机器学习 第三周上机作业
一、作业要求
- 实现手写朴素贝叶斯算法。
- 使用五折交叉验证评估算法,报告每折的准确率和计算平均准确率。
K折交叉验证(K-fold CrossValidation)
初始采样分割成K个子样本,一个单独的子样本被保留作为验证模型的数据,其他K-1个样本用来训练。交叉验证重复K次,每个子样本验证一次,平均K次的结果或者使用其它结合方式,最终得到一个单一估测。
二、代码实现
"""导入库"""
import numpy as np
import pandas as pd
from sklearn import datasets
from sklearn.model_selection import KFold
1. 写在前面:
X_train, X_test, y_train, y_test:
它们都是5维的列表,每一维代表第k折的训练集-测试集:
X_train[i]是第i+1个训练集,有120个元素,每一个元素是一个4维特征向量;
X_test [i]是第i+1个测试集,有30 个元素,每一个元素是一个4维特征向量;
y_train[i]是第i+1个训练集的标签,有120个元素,每一个元素是一个标签;
y_test [i]是第i+1个测试集的标签,有30 个元素,每一个元素是一个标签。
也就是说,我把5个训练集测试集合并在一起了。这样方便后面测试。
i=0,1,2,3,4
感觉这样麻烦了,单个测试也挺好……代码看起来有点打脑壳。
计算条件概率
"""计算条件概率"""
def get_feature_matrix(X_train0, y_train0, k, i):
"""
:param X_train0: 训练集的集合
:param y_train0: 训练集标签的集合
:param k: 第k个训练集
:param i: 第k个训练集的第i+1个特征
:return:
"""
Xi = X[:, i] # 原始训练集第i+1列,即所有样本的某个特征构成的向量
feature = X_train0[k][:, i] # 训练集第i个特征(特征向量)
label = y_train0[k][:] # 训练集标签(向量)
feature_category = np.unique(Xi) # 找出特征的所有取值,这里必须用原始数据,这样获得的特征才完整!!
label_category = np.unique(y) # 找出标签的所有取值,其实就是[0 1 2]
feature_num = feature_category.shape[0] # 训练集特征的数目
label_num = label_category.shape[0] # 训练集特征的数目标签数目,其实就是3
label_each_num = np.zeros(label_num) # 初始化,label_each_num[0]代表标签0在训练集中出现的次数
label_list = list(label) # 转化为列表,便于调用count函数来计数
for i in range(label_num): # 统计每个标签出现的次数
label_each_num[i] = label_list.count(label_category[i])
# 生成PPT那样的表格
df = pd.DataFrame(data=np.full((feature_num, label_num), 0),
index=feature_category, columns=label_category)
for i in range(m): # 计数
row0 = feature[i]
col0 = label[i]
df.loc[row0, col0] = df.loc[row0, col0] + 1
for i in range(feature_num): # 计算条件概率
for j in range(label_num):
df.iloc[i, j] = (df.iloc[i, j] + 1) / (label_each_num[j] + 3) # 拉普拉斯平滑
return df, label_category, label_each_num
其实就是算一下条件概率。用表格(dataframe)的形式储存,通过索引获取某个概率,方便后续计算。一个特征对应一个表格,4维特征就有4个条件概率表格。以第1个训练集的第1个特征为例,输出如下:
0 | 1 | 2 | |
---|---|---|---|
4.3 | 0.043478 | 0.018868 | 0.018868 |
4.4 | 0.130435 | 0.018868 | 0.018868 |
4.5 | 0.086957 | 0.018868 | 0.018868 |
… | … | … | … |
7.9 | 0.043478 | 0.018868 | 0.037736 |
其中,4.3,4.4,…,7.9代表第一个特征的取值;0,1,2代表三种标签。每个训练集都有4个这样的表格。
此外,这里用到了拉普拉斯平滑,如果测试集某个特征在训练集中没有出现过,那么概率就是0了。根据条件独立假设,概率相乘,最终这个特征出现的概率都变成了0,但实际情况并不一定是这样。
这里,在分子加1,分母加上3(也就是标签的数目),这就保证即使存在某个特征的取值为0,但我们也不会把其概率算成0。
2. 返回预测的标签向量
# 返回预测的标签向量
def get_H(testk, py, df01, df02, df03, df04, label_category):
"""
:param testk:第i个测试集
:param py:先验概率(向量)
:param df01:特征1的条件概率表格
:param df02:特征2的条件概率表格
:param df03:特征3的条件概率表格
:param df04:特征4的条件概率表格
:param label_category:标签类型,即[0 1 2]
:return:返回预测的标签向量
"""
H_list = []
for i in label_category:
Hi = py[i] * \
df01.loc[testk[0], i] * \
df02.loc[testk[1], i] * \
df03.loc[testk[2], i] * \
df04.loc[testk[3], i]
H_list.append(Hi)
return H_list.index(max(H_list)) # 返回列表最大数的索引(即预测的标签)
3. 计算分类准确率
# 计算分类准确率
def Score(test_index_list0, H0, y0, k):
"""
:param test_index_list0:测试集索引构成的列表
:param H0:预测标签向量
:param y0:原始数据标签向量
:param k:第k个测试集
:return:分类准确率
"""
index = test_index_list0[k]
count = 0
for i in range(len(H0)):
if H0[i] == y0[index[i]]: # 计数
count = count + 1
return count / len(H0)
4. 主函数
if __name__ == '__main__':
# 鸢尾花数据集
X, y = datasets.load_iris(return_X_y=True) # (150, 4) (150,)
# 5折交叉验证
kf = KFold(n_splits=5)
X_train, X_test, y_train, y_test = [], [], [], []
test_index_list = [] # 测试集索引列表
# train_index与test_index是索引
for train_index, test_index in kf.split(X):
X_train.append(X[train_index])
X_test.append(X[test_index])
y_train.append(y[train_index])
y_test.append(y[test_index])
test_index_list.append(test_index)
m = X_train[0].shape[0] # m = 120,训练集样本个数
Score_list = []
for num in range(5):
df1, _, _ = get_feature_matrix(X_train, y_train, num, 0)
df2, _, _ = get_feature_matrix(X_train, y_train, num, 1)
df3, _, _ = get_feature_matrix(X_train, y_train, num, 2)
df4, label_category, label_each_num = get_feature_matrix(X_train, y_train, num, 3)
Py = label_each_num / m # 计算先验概率
H_list = []
for i in range(30):
test = X_test[num][i]
H = get_H(test, Py, df1, df2, df3, df4, label_category)
H_list.append(H)
score = Score(test_index_list, H_list, y, num)
Score_list.append(score)
print("第{}个测试集分类正确率:{}".format(num+1, score))
print("平均分类准确率:{}".format(sum(Score_list) / 5))
label_each_num就是三维向量,每一维代表每个标签出现的次数。除以m就是标签出现的频率了。上面get_feature_matrix函数返回了label_each_num,其实对于某个训练集,label_each_num都是固定了的,而我每次要计算4个表格,每次都要返回相同的label_each_num。感觉有点多余……这里还有待改进。
三、输出结果:
第1个测试集分类正确率:1.0
第2个测试集分类正确率:0.9666666666666667
第3个测试集分类正确率:0.8666666666666667
第4个测试集分类正确率:0.9333333333333333
第5个测试集分类正确率:0.8666666666666667
平均分类准确率:0.9266666666666665
学了一段时间Python了,下次尝试用面向对象的风格写代码,过程化的语句看起来不直观。
个人作业,转载需注明网址。谢谢。
https://blog.csdn.net/Wolf_AgOH/article/details/115254433