【机器学习】朴素贝叶斯Python实现

机器学习 第三周上机作业

一、作业要求

  1. 实现手写朴素贝叶斯算法。
  2. 使用五折交叉验证评估算法,报告每折的准确率和计算平均准确率。

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个特征为例,输出如下:

012
4.30.0434780.0188680.018868
4.40.1304350.0188680.018868
4.50.0869570.0188680.018868
7.90.0434780.0188680.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.02个测试集分类正确率:0.96666666666666673个测试集分类正确率:0.86666666666666674个测试集分类正确率:0.93333333333333335个测试集分类正确率:0.8666666666666667
平均分类准确率:0.9266666666666665

学了一段时间Python了,下次尝试用面向对象的风格写代码,过程化的语句看起来不直观。

个人作业,转载需注明网址。谢谢。
https://blog.csdn.net/Wolf_AgOH/article/details/115254433

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值