交叉验证
由于数据集的划分有不确定性,若随机划分的样本正好处于某类特殊样本,则得到的训练模型所预测的结果的可信度将受到质疑。所以需要进行多次交叉验证,把样本空间中的所有样本均分成n份,使用不同的训练集训练模型,对不同的测试集进行测试时输出指标得分。sklearn提供了交叉验证相关API:
import sklearn.model_selection as ms
ms.cross_val_score(模型, 输入集, 输出集, cv=折叠数, scoring=指标名)->指标值数组
案例:使用交叉验证,输出分类器的精确度:
# 划分训练集和测试集
train_x, test_x, train_y, test_y = \ms.train_test_split(x, y, test_size=0.25, random_state=7)
# 朴素贝叶斯分类器
model = nb.GaussianNB()
# 交叉验证
# 精确度
ac = ms.cross_val_score( model, train_x, train_y, cv=5, scoring='accuracy')
print(ac.mean())
#用训练集训练模型
model.fit(train_x, train_y)
交叉验证指标
-
精确度(accuracy):分类正确的样本数/总样本数
-
查准率(precision_weighted):针对每一个类别,预测正确的样本数比上预测出来的样本数
-
召回率(recall_weighted):针对每一个类别,预测正确的样本数比上实际存在的样本数
-
f1得分(f1_weighted):
2x查准率x召回率/(查准率+召回率)
在交叉验证过程中,针对每一次交叉验证,计算所有类别的查准率、召回率或者f1得分,然后取各类别相应指标值的平均数,作为这一次交叉验证的评估指标,然后再将所有交叉验证的评估指标以数组的形式返回调用者。
# 交叉验证
# 精确度
ac = ms.cross_val_score( model, train_x, train_y, cv=5, scoring='accuracy')
print(ac.mean())
# 查准率
pw = ms.cross_val_score( model, train_x, train_y, cv=5, scoring='precision_weighted')
print(pw.mean())
# 召回率
rw = ms.cross_val_score( model, train_x, train_y, cv=5, scoring='recall_weighted')
print(rw.mean())
# f1得分
fw = ms.cross_val_score( model, train_x, train_y, cv=5, scoring='f1_weighted')
print(fw.mean())
混淆矩阵
每一行和每一列分别对应样本输出中的每一个类别,行表示实际类别,列表示预测类别。
A类别 | B类别 | C类别 | |
---|---|---|---|
A类别 | 5 | 0 | 0 |
B类别 | 0 | 6 | 0 |
C类别 | 0 | 0 | 7 |
上述矩阵即为理想的混淆矩阵。不理想的混淆矩阵如下:
A类别 | B类别 | C类别 | |
---|---|---|---|
A类别 | 3 | 1 | 1 |
B类别 | 0 | 4 | 2 |
C类别 | 0 | 0 | 7 |
查准率 = 主对角线上的值 / 该值所在列的和
召回率 = 主对角线上的值 / 该值所在行的和
获取模型分类结果的混淆矩阵的相关API:
import sklearn.metrics as sm
sm.confusion_matrix(实际输出, 预测输出)->混淆矩阵
案例:输出分类结果的混淆矩阵。
#输出混淆矩阵并绘制混淆矩阵图谱
cm = sm.confusion_matrix(test_y, pred_test_y)
print(cm)
mp.figure('Confusion Matrix', facecolor='lightgray')
mp.title('Confusion Matrix', fontsize=20)
mp.xlabel('Predicted Class', fontsize=14)
mp.ylabel('True Class', fontsize=14)
mp.xticks(np.unique(pred_test_y))
mp.yticks(np.unique(test_y))
mp.tick_params(labelsize=10)
mp.imshow(cm, interpolation='nearest', cmap='jet')
mp.show()
分类报告
sklearn.metrics提供了分类报告相关API,不仅可以得到混淆矩阵,还可以得到交叉验证查准率、召回率、f1得分的结果,可以方便的分析出哪些样本是异常样本。
# 获取分类报告
cr = sm.classification_report(实际输出, 预测输出)
案例:输出分类报告:
# 获取分类报告
cr = sm.classification_report(test_y, pred_test_y)
print(cr)
决策树分类
决策树分类模型会找到与样本特征匹配的叶子节点然后以投票的方式进行分类。在样本文件中统计了小汽车的常见特征信息及小汽车的分类,使用这些数据基于决策树分类算法训练模型预测小汽车等级。
汽车价格 | 维修费用 | 车门数量 | 载客数 | 后备箱 | 安全性 | 汽车级别 |
---|---|---|---|---|---|---|
案例:基于决策树分类算法训练模型预测小汽车等级。
- 读取文本数据,对每列进行标签编码,基于随机森林分类器进行模型训练,进行交叉验证。
import numpy as np
import sklearn.preprocessing as sp
import sklearn.ensemble as se
import sklearn.model_selection as msdata = np.loadtxt('../data/car.txt', delimiter=',', dtype='U10')
data = data.T
encoders = []
train_x, train_y = [],[]
for row in range(len(data)):encoder = sp.LabelEncoder()if row < len(data) - 1:train_x.append(encoder.fit_transform(data[row]))else:train_y = encoder.fit_transform(data[row])encoders.append(encoder)
train_x = np.array(tr ain_x).T
# 随机森林分类器
model = se.RandomForestClassifier(max_depth=6, n_estimators=200, random_state=7)
print(ms.cross_val_score(model, train_x, train_y, cv=4, scoring='f1_weighted').mean())
model.fit(train_x, train_y)
- 自定义测试集,使用已训练的模型对测试集进行测试,输出结果。
data = [['high', 'med', '5more', '4', 'big', 'low', 'unacc'],['high', 'high', '4', '4', 'med', 'med', 'acc'],['low', 'low', '2', '4', 'small', 'high', 'good'],['low', 'med', '3', '4', 'med', 'high', 'vgood']]data = np.array(data).T
test_x, test_y = [],[]
for row in range(len(data)):encoder = encoders[row]if row < len(data) - 1:test_x.append(encoder.transform(data[row]))else:test_y = encoder.transform(data[row])
test_x = np.array(test_x).T
pred_test_y = model.predict(test_x)
print((pred_test_y == test_y).sum() / pred_test_y.size)
print(encoders[-1].inverse_transform(test_y))
print(encoders[-1].inverse_transform(pred_test_y))
验证曲线
验证曲线:模型性能 = f(超参数)
验证曲线所需API:
train_scores, test_scores = ms.validation_curve(model, # 模型 输入集, 输出集, 'n_estimators', #超参数名np.arange(50, 550, 50), #超参数序列cv=5 #折叠数
)
train_scores的结构:
超参数取值 | 第一次折叠 | 第二次折叠 | 第三次折叠 | 第四次折叠 | 第五次折叠 |
---|---|---|---|---|---|
50 | 0.91823444 | 0.91968162 | 0.92619392 | 0.91244573 | 0.91040462 |
100 | 0.91968162 | 0.91823444 | 0.91244573 | 0.92619392 | 0.91244573 |
… | … | … | … | … | … |
test_scores的结构与train_scores的结构相同。
案例:在小汽车评级案例中使用验证曲线选择较优参数。
# 获得关于n_estimators的验证曲线
model = se.RandomForestClassifier(max_depth=6, random_state=7)
n_estimators = np.arange(50, 550, 50)
train_scores, test_scores = ms.validation_curve(model, train_x, train_y, 'n_estimators', n_estimators, cv=5)
print(train_scores, test_scores)
train_means1 = train_scores.mean(axis=1)
for param, score in zip(n_estimators, train_means1):print(param, '->', score)mp.figure('n_estimators', facecolor='lightgray')
mp.title('n_estimators', fontsize=20)
mp.xlabel('n_estimators', fontsize=14)
mp.ylabel('F1 Score', fontsize=14)
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')
mp.plot(n_estimators, train_means1, 'o-', c='dodgerblue', label='Training')
mp.legend()
mp.show()
# 获得关于max_depth的验证曲线
model = se.RandomForestClassifier(n_estimators=200, random_state=7)
max_depth = np.arange(1, 11)
train_scores, test_scores = ms.validation_curve(model, train_x, train_y, 'max_depth', max_depth, cv=5)
train_means2 = train_scores.mean(axis=1)
for param, score in zip(max_depth, train_means2):print(param, '->', score)mp.figure('max_depth', facecolor='lightgray')
mp.title('max_depth', fontsize=20)
mp.xlabel('max_depth', fontsize=14)
mp.ylabel('F1 Score', fontsize=14)
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')
mp.plot(max_depth, train_means2, 'o-', c='dodgerblue', label='Training')
mp.legend()
mp.show()
学习曲线
学习曲线:模型性能 = f(训练集大小)
学习曲线所需API:
_, train_scores, test_scores = ms.learning_curve(model, # 模型 输入集, 输出集, train_sizes=[0.9, 0.8, 0.7], # 训练集大小序列cv=5 # 折叠数
)
train_scores的结构:
案例:在小汽车评级案例中使用学习曲线选择训练集大小最优参数。
# 获得学习曲线
model = se.RandomForestClassifier( max_depth=9, n_estimators=200, random_state=7)
train_sizes = np.linspace(0.1, 1, 10)
_, train_scores, test_scores = ms.learning_curve(model, x, y, train_sizes=train_sizes, cv=5)
test_means = test_scores.mean(axis=1)
for size, score in zip(train_sizes, train_means):print(size, '->', score)
mp.figure('Learning Curve', facecolor='lightgray')
mp.title('Learning Curve', fontsize=20)
mp.xlabel('train_size', fontsize=14)
mp.ylabel('F1 Score', fontsize=14)
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')
mp.plot(train_sizes, test_means, 'o-', c='dodgerblue', label='Training')
mp.legend()
mp.show()
案例:预测工人工资收入。
读取adult.txt,针对不同形式的特征选择不同类型的编码器,训练模型,预测工人工资收入。
- 自定义标签编码器,若为数字字符串,则使用该编码器,保留特征数字值的意义。
class DigitEncoder():def fit_transform(self, y):return y.astype(int)def transform(self, y):return y.astype(int)def inverse_transform(self, y):return y.astype(str)
- 读取文件,整理样本数据,对样本矩阵中的每一列进行标签编码。
num_less, num_more, max_each = 0, 0, 7500
data = []txt = np.loadtxt('../data/adult.txt', dtype='U20', delimiter=', ')
for row in txt:if(' ?' in row):continueelif(str(row[-1]) == '<=50K'):num_less += 1data.append(row)elif(str(row[-1]) == '>50K'):num_more += 1data.append(row)data = np.array(data).T
encoders, x = [], []
for row in range(len(data)):if str(data[row, 0]).isdigit():encoder = DigitEncoder()else:encoder = sp.LabelEncoder()if row < len(data) - 1:x.append(encoder.fit_transform(data[row]))else:y = encoder.fit_transform(data[row])encoders.append(encoder)
- 划分训练集与测试集,基于朴素贝叶斯分类算法构建学习模型,输出交叉验证分数,验证测试集。
x = np.array(x).T
train_x, test_x, train_y, test_y = ms.train_test_split(x, y, test_size=0.25, random_state=5)
model = nb.GaussianNB()
print(ms.cross_val_score(model, x, y, cv=10, scoring='f1_weighted').mean())
model.fit(train_x, train_y)
pred_test_y = model.predict(test_x)
print((pred_test_y == test_y).sum() / pred_test_y.size)
- 模拟样本数据,预测收入级别。
data = [['39', 'State-gov', '77516', 'Bachelors','13', 'Never-married', 'Adm-clerical', 'Not-in-family','White', 'Male', '2174', '0', '40', 'United-States']]
data = np.array(data).T
x = []
for row in range(len(data)):encoder = encoders[row]x.append(encoder.transform(data[row]))
x = np.array(x).T
pred_y = model.predict(x)
print(encoders[-1].inverse_transform(pred_y))
支持向量机(SVM)
支持向量机原理
-
寻求最优分类边界
正确:对大部分样本可以正确地划分类别。
泛化:最大化支持向量间距。
公平:与支持向量等距。
简单:线性,直线或平面,分割超平面。
-
基于核函数的升维变换
通过名为核函数的特征变换,增加新的特征,使得低维度空间中的线性不可分问题变为高维度空间中的线性可分问题。
线性核函数:linear,不通过核函数进行维度提升,仅在原始维度空间中寻求线性分类边界。
基于线性核函数的SVM分类相关API:
import sklearn.svm as svm model = svm.SVC(kernel='linear') model.fit(train_x, train_y)
案例:对simple2.txt中的数据进行分类。
import numpy as np import sklearn.model_selection as ms import sklearn.svm as svm import sklearn.metrics as sm import matplotlib.pyplot as mp x, y = [], [] data = np.loadtxt('../data/multiple2.txt', delimiter=',', dtype='f8') x = data[:, :-1] y = data[:, -1] train_x, test_x, train_y, test_y = \ms.train_test_split(x, y, test_size=0.25, random_state=5) # 基于线性核函数的支持向量机分类器 model = svm.SVC(kernel='linear') model.fit(train_x, train_y) n = 500 l, r = x[:, 0].min() - 1, x[:, 0].max() + 1 b, t = x[:, 1].min() - 1, x[:, 1].max() + 1 grid_x = np.meshgrid(np.linspace(l, r, n),np.linspace(b, t, n)) flat_x = np.column_stack((grid_x[0].ravel(), grid_x[1].ravel())) flat_y = model.predict(flat_x) grid_y = flat_y.reshape(grid_x[0].shape) pred_test_y = model.predict(test_x) cr = sm.classification_report(test_y, pred_test_y) print(cr) mp.figure('SVM Linear Classification', facecolor='lightgray') mp.title('SVM Linear Classification', fontsize=20) mp.xlabel('x', fontsize=14) mp.ylabel('y', fontsize=14) mp.tick_params(labelsize=10) mp.pcolormesh(grid_x[0], grid_x[1], grid_y, cmap='gray') mp.scatter(test_x[:, 0], test_x[:, 1], c=test_y, cmap='brg', s=80) mp.show()
多项式核函数:poly,通过多项式函数增加原始样本特征的高次方幂
y=x1+x2y=x12+2x1x2+x22y=x13+3x12x2+3x1x22+x23y = x_1+x_2 \\ y = x_1^2 + 2x_1x_2 + x_2^2 \\ y = x_1^3 + 3x_1^2x_2 + 3x_1x_2^2 + x_2^3 y=x1?+x2?y=x12?+2x1?x2?+x22?y=x13?+3x12?x2?+3x1?x22?+x23?
案例,基于多项式核函数训练sample2.txt中的样本数据。# 基于线性核函数的支持向量机分类器 model = svm.SVC(kernel='poly', degree=3) model.fit(train_x, train_y)
径向基核函数:rbf,通过高斯分布函数增加原始样本特征的分布概率
案例,基于径向基核函数训练sample2.txt中的样本数据。
# 基于径向基核函数的支持向量机分类器 # C:正则强度 # gamma:正态分布曲线的标准差 model = svm.SVC(kernel='rbf', C=600, gamma=0.01) model.fit(train_x, train_y)
样本类别均衡化
通过类别权重的均衡化,使所占比例较小的样本权重较高,而所占比例较大的样本权重较低,以此平均化不同类别样本对分类模型的贡献,提高模型性能。(上采样或下采样)
样本类别均衡化相关API:
model = svm.SVC(kernel='linear', class_weight='balanced')
model.fit(train_x, train_y)
案例:修改线性核函数的支持向量机案例,基于样本类别均衡化读取imbalance.txt训练模型。
... ...
... ...
data = np.loadtxt('../data/imbalance.txt', delimiter=',', dtype='f8')
x = data[:, :-1]
y = data[:, -1]
train_x, test_x, train_y, test_y = \ms.train_test_split(x, y, test_size=0.25, random_state=5)
# 基于线性核函数的支持向量机分类器
model = svm.SVC(kernel='linear', class_weight='balanced')
model.fit(train_x, train_y)
... ...
... ...环境一般,暖气不热,空调还好,总体可以。 好评。 56%
房间漂亮,床非常舒服。非常干净卫生。强烈建议入住! 好评。99%
置信概率
根据样本与类边界的距离远近,对其预测类别的可信程度进行量化,离边界越近的样本,置信概率越低,反之,离边界越远的样本,置信概率高。
获取每个样本的置信概率相关API:
# 在获取模型时,给出超参数probability=True
model = svm.SVC(kernel='rbf', C=600, gamma=0.01, probability=True)
预测结果 = model.predict(输入样本矩阵)
# 调用model.predict_proba(样本矩阵)可以获取每个样本的置信概率矩阵
置信概率矩阵 = model.predict_proba(输入样本矩阵)
置信概率矩阵格式如下:
类别1 | 类别2 | |
---|---|---|
样本1 | 0.8 | 0.2 |
样本2 | 0.9 | 0.1 |
样本3 | 0.5 | 0.5 |
案例:修改基于径向基核函数的SVM案例,新增测试样本,输出每个测试样本的执行概率,并给出标注。
# 整理测试样本
prob_x = np.array([[2, 1.5],[8, 9],[4.8, 5.2],[4, 4],[2.5, 7],[7.6, 2],[5.4, 5.9]])
pred_prob_y = model.predict(prob_x)
probs = model.predict_proba(prob_x)
print(probs)# 绘制每个测试样本,并给出标注
mp.scatter(prob_x[:,0], prob_x[:,1], c=pred_prob_y, cmap='jet_r', s=80, marker='D')
for i in range(len(probs)):mp.annotate('{}% {}%'.format(round(probs[i, 0] * 100, 2),round(probs[i, 1] * 100, 2)),xy=(prob_x[i, 0], prob_x[i, 1]),xytext=(12, -12),textcoords='offset points',horizontalalignment='left',verticalalignment='top',fontsize=9,bbox={
'boxstyle': 'round,pad=0.6','fc': 'orange', 'alpha': 0.8})