赞
踩
iris以鸢尾花的特征作为数据来源,常用在分类操作中。该数据集由3种不同类型的鸢尾花的各50个样本数据构成。其中的一个种类与另外两个种类是线性可分离的,后两个种类是非线性可分离的。
该数据集包含了4个属性:
& Sepal.Length(花萼长度),单位是cm;
& Sepal.Width(花萼宽度),单位是cm;
& Petal.Length(花瓣长度),单位是cm;
& Petal.Width(花瓣宽度),单位是cm;
种类:Iris Setosa(山鸢尾)、Iris Versicolour(杂色鸢尾),以及Iris Virginica(维吉尼亚鸢尾)
数据集可以从网上下载
需要手动分割训练集和数据集。我将五分之四的数据集作为训练集对贝叶斯分类器进行训练,将剩余数据集作为测试集,采用训练好的贝叶斯分类器模型对其进行预测。训练集与测试集的数据是随机选取的。
下面只是写一个目录,只把用到的朴素贝叶斯分类器的核心思想记录了一下,其它详见西瓜书。
简化:“属性条件独立性假设”
分类过程:
数据集是从网上下载的.csv
文件,直接放在了项目里。
首先以4 : 1(训练集:测试集)的比例分割数据集:
因为每个类别都有50条数据,我初始是按照每个类别随机取10条数据作为测试集的想法做的,代码如下:
def split_dataset(data: pd.DataFrame): """ 分割训练集和测试集 :param data: iris dataset :return: trainSet, testSet """ # 随机采样 每个品种各取1/5作为测试集 test_index = random.sample(range(50), 10) test_index.extend(random.sample(range(50, 100), 10)) test_index.extend(random.sample(range(100, 150), 10)) print("测试集包括:") print(test_index) testSet = data.iloc[test_index] train_index = list(range(150)) for index in test_index: train_index.remove(index) trainSet = data.iloc[train_index] return trainSet, testSet
后来考虑了一下,为了使随机采样更具随机性,我选择了直接随机取样。把随机采样改为:
test_index = random.sample(range(150), 30)
# 类先验概率
# 拉普拉斯修正
num = train.shape[0]
prior_0 = np.log((train.loc[train.Species == Species[0]].shape[0]+1)/(num+3))
prior_1 = np.log((train.loc[train.Species == Species[1]].shape[0]+1)/(num+3))
prior_2 = np.log((train.loc[train.Species == Species[2]].shape[0]+1)/(num+3))
在这里,采用了拉普拉斯修正,进行“平滑”处理。
同时,为了避免数值下溢,对数值进行取对数处理。
使用train.loc[train.Species == Species[0]]
获得训练集中品种为"setosa"
的数据,使用.shape[0]
取得行数。
首先,因为鸢尾花数据集都是连续数据,所以要用高斯函数计算类条件概率,函数代码如下:
def continuous_attr_p(value, mean, var):
"""
:param value: xi
:param mean: 均值
:param var: 方差
:return: 类条件概率p
"""
p = 1 / ((2 * PI * var) ** 0.5) * math.exp(-(value - mean)**2 / (2 * var))
# print("p:")
# print(p)
return p
计算所有类条件概率的思路:根据物种类型,取出训练集中同属于一类的数据。对于这些数据中的每一列,计算它们的均值和方差,代入测试集数据x该列的值,计算类条件概率,判断这些数据属于哪个物种类型,将每列的类条件概率存放在定义好的字典中。属于"setosa"就存放在storage0中,以此类推。为了避免数值下溢,对数值进行取对数处理。
Species = ["setosa", "versicolor", "virginica"]
定义字典的代码:
storage0 = {"Sepal.Length": 0, "Sepal.Width": 0, "Petal.Length": 0, "Petal.Width": 0}
storage1 = {"Sepal.Length": 0, "Sepal.Width": 0, "Petal.Length": 0, "Petal.Width": 0}
storage2 = {"Sepal.Length": 0, "Sepal.Width": 0, "Petal.Length": 0, "Petal.Width": 0}
该思路实现的代码为:
for spes in Species:
train_dic = train.loc[train.Species == spes]
for column in train_dic.columns:
if column != 'Species': # 去除预测标签的影响
mean = np.mean(train_dic[column])
var = np.var(train_dic[column])
p = np.log(continuous_attr_p(x[column], mean, var))
if spes == Species[0]:
storage0[column] = p
elif spes == Species[1]:
storage1[column] = p
elif spes == Species[2]:
storage2[column] = p
定义一个后验概率数组,存放三个品种的后验概率。
post_p = np.zeros(3, np.float64)
post_p[0] = prior_0
post_p[1] = prior_1
post_p[2] = prior_2
因为先验概率和所有的类条件概率都取了对数,所以后验概率=类先验概率+所有的类条件概率之和。
for column in train.columns:
if column != 'Species':
post_p[0] = post_p[0] + storage0[column]
post_p[1] = post_p[1] + storage1[column]
post_p[2] = post_p[2] + storage2[column]
返回三者中的最大值的索引,索引与Species数组相对应,得到的即为该测试数据所属的类别:
post_p_i = np.argmax(post_p, axis=0)
print("post_p_i", post_p_i)
return post_p_i
Data = pd.read_csv('iris.csv')
# 划分数据集
train_set, test_set = split_dataset(Data)
# 存放分类器对测试集的分类结果
classify_result = []
for index, row in test_set.iterrows():
classify_result.append(Species[classifier(row, train_set)])
# 将分类结果添加到测试集中
test_set.insert(test_set.shape[1], 'forecast', classify_result)
使用seaborn
绘图工具
①以Species
为x
轴,使用sns.countplot()
绘制条形图,显示训练集中各物种的个数:
colors = sns.color_palette('pastel')
sns.countplot(x="Species", palette='pastel', data=train_set)
plt.show()
②统计测试集中分类正确和分类错误的数目:
# 分类正确的数目
df1 = test_set[['Species']]
df2 = test_set[['forecast']]
df = pd.concat([df1, df2], axis=1)
df['result'] = np.where(df['Species'] == df['forecast'], 'true', 'false')
colors = sns.color_palette('pastel')
sns.countplot(x="result", palette='pastel', data=df)
plt.show()
③以Sepal.Length
为x
轴,以Petal.Length
为y
轴绘制回归图,对比以Species
列为分类标准和以forecast
列为分类标准的图像的差异。
sns.lmplot(x='Sepal.Length', y='Petal.Length', hue='Species',
data=test_set, markers=['*', 'o', '+'])
plt.show()
sns.lmplot(x='Sepal.Length', y='Petal.Length', hue='forecast',
data=test_set, markers=['*', 'o', '+'])
plt.show()
采用准确率:分类正确的样本数/总样本数进行评估
TP = 0
for index, row in comparison.iterrows():
if row["Species"] == row['forecast']:
TP += 1
Accuracy = TP / 30
print('正确率: {:.2%}'.format(Accuracy))
注:下列输出结果为使用jupyter
运行后得到的结果。
随机选取的测试集标号:
可视化显示训练集中各物种的个数:
输出后验概率:post_p[0]为setosa
物种的后验概率,post_p[1]为versicolor
物种的后验概率,post_p[2]为virginica
物种的后验概率。取三者的最大值作post_p_i,即预测的类别,0为setosa
物种,1为versicolor
物种,2为virginica
物种。
下图仅为部分数据:
①统计测试集中分类正确和分类错误的数目:
②
应该得到的正确分类:
实际上得到的结果:
import numpy as np import random import math import pandas as pd import seaborn as sns import matplotlib.pyplot as plt # from torcheval.metrics import functional as TMF PI = math.pi Species = ["setosa", "versicolor", "virginica"] def split_dataset(data: pd.DataFrame): """ 分割训练集和测试集 :param data: iris dataset :return: trainSet, testSet """ # 随机采样 test_index = random.sample(range(150), 30) print("测试集包括:") print(test_index) testSet = data.iloc[test_index] train_index = list(range(150)) for index in test_index: train_index.remove(index) trainSet = data.iloc[train_index] return trainSet, testSet def continuous_attr_p(value, mean, var): """ :param value: xi :param mean: 均值 :param var: 方差 :return: 类条件概率p """ p = 1 / ((2 * PI * var) ** 0.5) * math.exp(-(value - mean) ** 2 / (2 * var)) # print("p:") # print(p) return p storage0 = {"Sepal.Length": 0, "Sepal.Width": 0, "Petal.Length": 0, "Petal.Width": 0} storage1 = {"Sepal.Length": 0, "Sepal.Width": 0, "Petal.Length": 0, "Petal.Width": 0} storage2 = {"Sepal.Length": 0, "Sepal.Width": 0, "Petal.Length": 0, "Petal.Width": 0} def classifier(x, train: pd.DataFrame): """ 朴素贝叶斯分类 鸢尾花数据集都是连续数据 :param x: 测试集的一条数据 :param train: 训练集模型 :return: 预测结果 """ # 类先验概率 # 拉普拉斯修正 num = train.shape[0] prior_0 = np.log((train.loc[train.Species == Species[0]].shape[0] + 1) / (num + 3)) prior_1 = np.log((train.loc[train.Species == Species[1]].shape[0] + 1) / (num + 3)) prior_2 = np.log((train.loc[train.Species == Species[2]].shape[0] + 1) / (num + 3)) # 计算所有列的类条件概率 for spes in Species: train_dic = train.loc[train.Species == spes] for column in train_dic.columns: if column != 'Species': # 去除预测标签的影响 mean = np.mean(train_dic[column]) var = np.var(train_dic[column]) p = np.log(continuous_attr_p(x[column], mean, var)) if spes == Species[0]: storage0[column] = p elif spes == Species[1]: storage1[column] = p elif spes == Species[2]: storage2[column] = p post_p = np.zeros(3, np.float64) post_p[0] = prior_0 post_p[1] = prior_1 post_p[2] = prior_2 for column in train.columns: if column != 'Species': post_p[0] = post_p[0] + storage0[column] post_p[1] = post_p[1] + storage1[column] post_p[2] = post_p[2] + storage2[column] print("post_p[0]", post_p[0]) print("post_p[1]", post_p[1]) print("post_p[2]", post_p[2]) post_p_i = np.argmax(post_p, axis=0) print("post_p_i", post_p_i) return post_p_i if __name__ == '__main__': Data = pd.read_csv('iris.csv') # 划分数据集 train_set, test_set = split_dataset(Data) # 显示训练集物种分布情况 # a=[] # for index, row in train_set.iterrows(): # if row["Species"] == Species[0]: # a.append(1) # elif row["Species"] == Species[1]: # a.append(2) # else: # a.append(3) # train_set.insert(train_set.shape[1], 'digit',a) colors = sns.color_palette('pastel') sns.countplot(x="Species", palette='pastel', data=train_set) plt.show() # 存放分类器对测试集的分类结果 classify_result = [] for index, row in test_set.iterrows(): classify_result.append(Species[classifier(row, train_set)]) # 将分类结果添加到测试集中 test_set.insert(test_set.shape[1], 'forecast', classify_result) sns.lmplot(x='Sepal.Length', y='Petal.Length', hue='Species', data=test_set, markers=['*', 'o', '+']) plt.show() sns.lmplot(x='Sepal.Length', y='Petal.Length', hue='forecast', data=test_set, markers=['*', 'o', '+']) plt.show() # 分类正确的数目 df1 = test_set[['Species']] df2 = test_set[['forecast']] df = pd.concat([df1, df2], axis=1) df['result'] = np.where(df['Species'] == df['forecast'], 'true', 'false') colors = sns.color_palette('pastel') sns.countplot(x="result", palette='pastel', data=df) plt.show() TP = 0 for index, row in test_set.iterrows(): if row["Species"] == row['forecast']: TP += 1 Accuracy = TP / 30 print('正确率: {:.2%}'.format(Accuracy))
通过本次实验,我对贝叶斯分类器有了更深刻的了解,清楚了朴素贝叶斯分类的基本流程,加深了对机器学习基本方法的理解与应用,提高了自身的代码技能和分析并解决问题的能力,为之后的实验和学习奠定了基础,受益匪浅。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。