一、实验目的
- 通过本次实验熟悉朴素贝叶斯分类器的实现原理
- 了解混淆矩阵及其相关参数的意义
- 绘制ROC曲线,直观的了解实验结果
二、实验数据说明
在本次试验中我们使用了数据集wine.data
和数据集说明:wine.names
,其中,wine.data
总共有十四列数据,每列数据的含义在wine.names
中说明了:数据集和说明见下图:
在这里我们只需要知道每一行数据是一个样本,每一行对应的第一列的数据是这个样本的所属类别,其他的十三列数据都是关于酒的参数,在这个实验中我们要做的是建立朴素贝叶斯模型,在这个数据集上进行训练分析,得到我们的模型在这个数据集上的最终准确率。
二、代码框架
-
本次实验使用的函数框架如下:
1.dataread(filename) #将文件(wine.data)中的数据转换为数字类型存储在数组中并返回 2.classify0(data) #将数据按照标签分成三类 3.bayes_classificate(train_data,test_data,types,pre_y,test_y) #单次(十折中的一折)朴素贝叶斯分类 4.cross_check(types) #十次交叉验证 5.confusion_mat(test_y,pre_y) #使用python自带的库绘制计算混淆矩阵并绘图,输出参数 6.confusion_mat1(test_y,pre_y) #自制函数计算混淆矩阵,并输出参数
三、代码详细说明
-
dataread(filename)
函数功能:将数据读取到一个数组中def dataread(filename): data=[] f=open(filename,'r') for line in f.readlines(): data.append(line.replace('\n','').split(',')) data=[[eval(s) for s in d] for d in data] for d in data: if(len(d)<14): print("error") f.close() print("{}中总共记载了{}个样本:".format(filename,len(data))) return data
这里使用一个简单地for循环判断数据中是否有缺失值,data中存储的是以14个数字为一组的样本数据集。
-
classify0(data)
函数功能:对预处理后的数据进行分类def classify0(data): types=[[],[],[]] for d in data: types[d[0]-1].append(d[1:]) return types
-
bayes_classificate(train_data,test_data,types,pre_y,test_y)
函数功能:单次朴素贝叶斯分类def bayes_classificate(train_data,test_data,types,pre_y,test_y): ''' pre_y和test_y用来储存实际标签和预测标签,用于后面的参数计算 和混淆矩阵的生成 ''' data_num = sum([len(types[i]) for i in range(3)]) means=[np.mean(train_data[i],axis=0) for i in range(3)] #均值向量 std=[np.std(train_data[i],axis=0)for i in range(3)] #标准差 wrong_num = 0 for i in range(3): for t in test_data[i]: #两层循环:从每一类取每一个测试样本 my_type = [] for j in range(3): #由于数据集中所有的属性都是连续值, #连续值的似然估计可以按照高斯分布来计算: temp = np.log((2*math.pi) ** 0.5 * std[j]) temp += np.power(t - means[j], 2) / (2 * np.power(std[j], 2)) temp = np.sum(temp) temp = -1*temp+math.log(len(types[j])/data_num) my_type.append(temp) #这里将所有score保存 pre_type = my_type.index(max(my_type)) pre_y.append(pre_type) test_y.append(i) #取分值最大的为预测类别 if pre_type != i: #统计错误数 wrong_num+=1 return wrong_num
-
cross_check(types)
函数功能:十折交叉,对数据进行分层划分def cross_check(types): test_data=[[],[],[]] train_data=[[],[],[]] test_y=[] pre_y=[] test_len = [round(len(types[i]) / 10) for i in range(3)] print(test_len) data_num = sum([len(types[i]) for i in range(3)]) wrong_num = 0 for i in range(10): #十折交叉,并且对每一类数据分层 for j in range(3): #if (i+1)*test_len[j]>len(types[j]): if i==9: test_data[j] = np.mat(types[j][i*test_len[j]:]) train_data[j] = np.mat(types[j][:i*test_len[j]]) else: test_data[j] = np.mat(types[j][i*test_len[j]: (i+1)*test_len[j]]) train_data[j] = np.mat(types[j][:i*test_len[j]]+ types[j][(i+1)*test_len[j]:]) wrong_num+=bayes_classificate(train_data,test_data,types,pre_y,test_y) print("准确率:"+str(1-wrong_num/data_num)) #confusion_mat1(test_y,pre_y) #confusion_mat(test_y,pre_y)
-
confusion_mat(test_y,pre_y)
函数作用:根据预测结果使用相应的库绘制混淆矩阵的图形以及输出对应的参数#根据预测结果使用相应的库绘制混淆矩阵的图形以及输出对应的参数 def confusion_mat(test_y,pre_y): label = ['1', '2', '3'] # 生成混淆矩阵 conf_mat = confusion_matrix(test_y, pre_y) print("conf_mat:\n", conf_mat) print(classification_report(test_y,pre_y,target_names=label)) fig, ax = plt.subplots(figsize=(10, 8)) sns.heatmap(conf_mat, annot=True, fmt='d', xticklabels=label, yticklabels=label) plt.ylabel('实际结果', fontsize=18) plt.xlabel('预测结果', fontsize=18) plt.show()
-
confusion_mat1(test_y,pre_y)
函数功能:根据预测结果计算混淆矩阵的以及对应的参数# 单次试验求不同准则下的分类误差 def confusion_mat1(test_y,pre_y): confusion = [] for i in range(3): confusion.append([0] * 3) for i in range(len(test_y)): confusion[test_y[i]][pre_y[i]] += 1 count = np.sum(confusion) confusion=np.array(confusion) print("conf_mat:\n",confusion) precision = [round(confusion[i][i]/np.sum(confusion,axis=0)[i],2) for i in range(3)] recall = [round(confusion[i][i]/np.sum(confusion,axis=1)[i],2) for i in range(3)] accuracy = round(np.sum([confusion[i][i] for i in range(3)])/count,2) F1_score= [round(2*precision[i]*recall[i]/(precision[i]+recall[i]),2) for i in range(3)] support = np.sum(confusion,axis=1) print(" precision recall accuracy f1-score support") for i in range(3): print(" {} {} {} {} {} {}" .format(i+1,precision[i],recall[i],recall[i],F1_score[i],support[i])) print("avg/total: {} {} {} {} {}" .format(round(np.sum([precision[i]*support[i] for i in range(3)])/count,2), accuracy,accuracy,round(np.sum([F1_score[i]*support[i] for i in range(3)])/count,2), count))
在这里
confusion
是混淆矩阵,其中confusion[i][j]
表示实际类别为i+1
,预测类别为j+1
的样本的数量
四、实验步骤
1.基本要求
a).采用分层采样的方式将数据集划分为训练集和测试集。
test_len = [round(len(types[i]) / 10) for i in range(3)]
for i in range(10): #十折交叉,并且对每一类数据分层
for j in range(3):
#使用原先的分类标准类别为2的酒会有一个样本数据没有测试
#if (i+1)*test_len[j]>len(types[j]):
if i==9:
test_data[j] = np.mat(types[j][i*test_len[j]:])
train_data[j] = np.mat(types[j][:i*test_len[j]])
else:
test_data[j] = np.mat(types[j][i*test_len[j]:(i+1)*test_len[j]])
train_data[j] = np.mat(types[j][:i*test_len[j]]+
types[j][(i+1)*test_len[j]:])
进行分层采样的时候我们要做到的是,采样之后的测试集和训练集数据中不同类别的样本数量比例是一致的,都是和整个样本空间的比例一致,例如:如果样本空间中的比例为a:
b:
c=1:2:3,那么我们选择的训练集和测试集中三者的比例也应该是1:2:3
b)给定编写一个朴素贝叶斯分类器,对测试集进行预测,计算分类准确率。
经过a)分类之后我们已经将数据集按分层采样的要求分为了10份接下来,我们只要对每一份使用单次朴素贝叶斯分类bayes_classificate()
求出单次分类的错误个数,将十次的错误个数求和,再除以样本总数即为最终的错误率,用1-错误率
即为最终的结果
最终结果如下:
这里的[6,7,5]
是十次验证中,每个测试集不同类别样本的数量
2. 中级要求:使用测试集评估模型,得到混淆矩阵,精度,召回率,F值。
①.python中的confusion_matrix,classification_report
可以帮助我们很方便的得出题目要求的参数,同时使用seaborn
和matplotlib.pyplot
可以使混淆矩阵图形化更加直观具体,调用confusion_mat()
函数,结果如下图:
②使用自己定义的confusion_mat()
函数结果,如下图: