1.宏观意识
朴素贝叶斯,决策树,随机森林等等都是在预估器到生成模型这个环节,只是不同的算法而已,上次学习了KNN即生成预估器用KNN算法进行训练,生成模型,这次采用了朴素贝叶斯,决策树,随机森林等算法进行这个环节。
复习一个整个环节:1.加载数据(玩具,网络,CSV)2.数据清洗:pandas处理异常数据,缺失数据 3.数据预处理:生成实体化转化器,把文本,字典转换为计算机可识别文件并且计算机进行无量纲化,给予数据权重(归一化,标准化) 4.特征降维:为了简化计算,处理掉不用与多余的特征 5.划分数据集 6.生成预估器,按照算法生成模型 7 模型评估与保存
2. 朴素贝叶斯分类
2.1 贝叶斯分类与朴素贝叶斯
贝叶斯决策理论是一种统计决策理论,它利用概率论来指导决策过程即如果有0.6的概率是好瓜,0.4的概率是坏瓜,那我们判断大概率是好瓜
P(A)称为"先验概率",即在B事件发生之前,我们对A事件概率的一个判断。
P(A|B)称为"后验概率"),即在B事件发生之后,我们对A事件概率的重新评估。
P(B|A)/P(B)称为"可能性函数",这是一个调整因子,使得预估概率更接近真实概率。
决策规则:基于后验概率和其他可能的成本或损失函数来选择最优行动方案
简单一点:通过后验概率进行推理和指导,比如通过知道是好瓜的前提下,由于它的青绿,模糊,脆响的特征,对其他特征或者这个特征的瓜的好坏进行判断。
在条件彼此独立的情况下,再次公式实际化:(不独立不行,特征有关联就是相互影响)
P(X∣Cj)=P(X1∣Cj)⋅P(X2∣Cj)⋯P(Xn∣Cj)
也就是朴素贝叶斯的产生与应用:
C^=argmaxjP(Cj)∏i=1nP(Xi∣Cj) 即 C^=argmaxj(logP(Cj)+∑i=1nlogP(Xi∣Cj))
补充一下推导过程(了解即可):
先根据条件概率:
𝑃(𝐴|𝐵)=𝑃(𝐴∩𝐵)/𝑃(𝐵)和𝑃(𝐴∩𝐵)=𝑃(𝐴|𝐵)𝑃(𝐵)即𝑃(𝐴∩𝐵)=𝑃(𝐵|𝐴)𝑃(𝐴)
最终化简得到𝑃(𝐴|𝐵)=𝑃(B|A)𝑃(𝐴)/𝑃(𝐵)
再根据全概率公式:
𝑃(𝐵)=𝑃(𝐵∩𝐴)+𝑃(𝐵∩𝐴′)和𝑃(𝐵∩𝐴)=𝑃(𝐵|𝐴)𝑃(𝐴)即𝑃(𝐵)=𝑃(𝐵|𝐴)𝑃(𝐴)+𝑃(𝐵|𝐴′)𝑃(𝐴′)
再根据独立的全概率公式: P(X∣Cj)=P(X1∣Cj)⋅P(X2∣Cj)⋯P(Xn∣Cj)
其实也还好,就是普遍的概率论的应用,内核:相当于机器对每个数据按照权重进行分配再将他们相加得到普遍概率(权重就是概率)
2.2API的调用
sklearn.naive_bayes.MultinomialNB()
estimator.fit(x_train, y_train)
y_predict = estimator.predict(x_test)
说实话,这种预估器的API调用都挺简单的
2.3实例分析
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
from sklearn.naive_bayes import MultinomialNB
news =load_digits()
x_train, x_test, y_train, y_test = train_test_split(news.data, news.target)
estimator = MultinomialNB()
estimator.fit(x_train, y_train)
score = estimator.score(x_test, y_test)
print("准确率为:\n", score)
index=estimator.predict([[2,2,3,1,1,2,2,2,3,5,4,4,5,6,7,8,9,4,4,5,1,2,3,4,5,6,7,8,9,4,1,2,3,4,5,6,7,8,9,4,1,2,3,4,5,6,7,8,9,4,1,2,3,4,5,6,7,8,9,1,2,3,4,5]])
print("预测:\n",index,news.target_names,news.target_names[index])
这里数据集有六十四个特征,我就写了六十四个数字,也可以随机生成,后续复习会改进
老方法老步骤:导入数据集,划分数据集,标准化数据集,引入预计器生成模型,训练数据
2.4 朴素贝叶斯经典例题与分析(参考西瓜书第四章末尾例题与第七章末尾例题)
周志华老师的《机器学习》(西瓜书)是机器学习领域的经典入门教材之一
配套视频教程:【吃瓜教程】《机器学习公式详解》(南瓜书)与西瓜书公式推导_哔哩哔哩_bilibili
参考纸质版:https://github.com/datawhalechina/pumpkin-book?tab=readme-ov-file
2.4.1数学方法求解
P(色泽=青绿|好瓜=是)==0.375, P(色泽=青绿|好瓜=否)=-≈0.3335
P(根蒂 = 蜷缩|好瓜 = 是)== 0.3751,P(根蒂 = 蜷缩|好瓜 =否)=° 0.333
P(敲声=浊响|好瓜=是)==0.750,P(敵声 = 浊响|好瓜 = 否)=0.444
P(纹理=清晰|好瓜=是)==0.875, P(纹理= 清晰|好瓜 =否) =0.961
P(脐部=凹陷|好瓜=是)== 0.750,P(脐部= 凹陷|好瓜-香=0.222
P(触感=硬滑|好瓜=是)0.750,P(触感=硬滑|好瓜=否)=0.667
通过正态分布算密度和含糖量:
带入进去得到P(好瓜):相乘得到0.038 P坏瓜:相乘0.00028
2.4.2代码方法实现
import pandas as pd
import numpy as np
# 将数据集分别保存在excel表中的不同工作表中,用pandas导入,其余都用numpy来做
def load_data():
# 导入数据
train_data = pd.read_excel('data.xlsx', sheet_name='train')
test_data = pd.read_excel('data.xlsx', sheet_name='test')
# ['色泽', '根蒂', '敲声', '纹理', '脐部', '触感', '密度', '含糖率', '好瓜‘]
train_data = np.array(train_data)[:, 1:]
test_data = np.array(test_data)[:, 1:]
return train_data, test_data
# 训练贝叶斯分类器,其实就是计算离散属性的先验概率和条件概率、连续属性的均值和方差
def train_bayes(train_data): # 13行9列
# 先计算先验概率P(c),即好瓜和坏瓜的个数分别占总训练集样本个数的比例
good_num = 0
bad_num = 0 # 好瓜与坏瓜的个数,后面拉普拉斯修正也要用
for i in range(train_data.shape[0]): # 一行一行地看,shape[0]指行数
if train_data[i, -1] == "是":
good_num += 1
elif train_data[i, -1] == "否":
bad_num += 1
# 得到好瓜6个,坏瓜7个
# 计算先验概率
pc_good = (good_num + 1) / (train_data.shape[0] + 2) # 公式见西瓜书p153
pc_bad = (bad_num + 1) / (train_data.shape[0] + 2)
# 将分类结果的好瓜与坏瓜分开,典的第一个键值对保存该属性的取值个数,例如本训练集中色泽有三种取值(青绿,乌黑,浅白),就保存
# 保存每一个属性的取值个数是为了进行拉普拉斯修正
good_melon = [{'sumType': 0} for i in range(8)]
bad_melon = [{'sumType': 0} for i in range(8)]
# 计算条件概率P(xi | c),例如计算在好瓜中色泽为青绿的个数占好瓜总数的比例
for j in range(train_data.shape[1] - 3): # 一列一列地看,shape[1]指列数,最后三列不看
# 一行一行地看,这两行正反都一样
for i in range(train_data.shape[0]):
# 首先保证是好瓜
if train_data[i, -1] == "是":
# 如果字典数组中已经有了这个属性对应的值(如青绿)就直接加一
if train_data[i, j] in good_melon[j]:
good_melon[j][train_data[i, j]] += 1
else:
good_melon[j][train_data[i, j]] = 1 # 如果没有就创建一个键值对并赋值为1
good_melon[j]['sumType'] += 1 # 该属性增加一个取值
else: # 如果是坏瓜,把上面good_melon换成bad_melon就行
if train_data[i, j] in bad_melon[j]: # 如果字典数组中已经有了这个属性对应的值(如青绿)就直接加一
bad_melon[j][train_data[i, j]] += 1
else:
bad_melon[j][train_data[i, j]] = 1 # 如果没有就创建一个键值对并赋值为1
bad_melon[j]['sumType'] += 1 # 该属性增加一个取值
# 因为拉普拉斯修正中每一个属性的取值是整个训练集的取值,上面只是单独收集好瓜与坏瓜
for i in range(len(good_melon) - 2):
# if或者elif成立说明有属性只在好瓜和坏瓜中存在,要统一一下
if good_melon[i]['sumType'] > bad_melon[i]['sumType']:
# 统一属性取值个数
bad_melon[i]['sumType'] = good_melon[i]['sumType']
# 统一取值
key = good_melon[i].keys() - bad_melon[i].keys()
bad_melon[i][key] = 0
print(bad_melon[i][key])
elif good_melon[i]['sumType'] < bad_melon[i]['sumType']:
# 统一属性取值个数
good_melon[i]['sumType'] = bad_melon[i]['sumType']
# 统一取值
key = list(bad_melon[i].keys() - good_melon[i].keys())
for j in key:
good_melon[i][j] = 0
# 上面只是统计了个数,下面才是计算条件概率,直接用统计出来的数值除以好瓜或者坏瓜的个数
for i in range(train_data.shape[1] - 3): # 有train_data.shape[0] - 3个是离散属性,需要进行拉普拉斯修正
for key, value in good_melon[i].items(): # 遍历每一个键值对,好瓜
if key != "sumType": # 除了字典的第一个值
good_melon[i][key] = (good_melon[i][key] + 1) / (good_num + good_melon[i]['sumType'])
for key, value in good_melon[i].items(): # 遍历每一个键值对,坏瓜
if key != "sumType": # 除了字典的第一个值
bad_melon[i][key] = (bad_melon[i][key] + 1) / (bad_num + bad_melon[i]['sumType'])
# 以上是离散属性的先验概率和条件概率
# 下面是连续属性的均值和方差 -1是含糖率,-2是密度
good_melon[-1]['mean'] = np.mean(train_data[:6, -2], axis=0)
good_melon[-1]['var'] = np.var(train_data[:6, -2], axis=0)
bad_melon[-1]['mean'] = np.mean(train_data[6:, -2], axis=0)
bad_melon[-1]['var'] = np.var(train_data[6:, -2], axis=0)
good_melon[-2]['mean'] = np.mean(train_data[:6, -3], axis=0)
good_melon[-2]['var'] = np.var(train_data[:6, -3], axis=0)
bad_melon[-2]['mean'] = np.mean(train_data[6:, -3], axis=0)
bad_melon[-2]['var'] = np.var(train_data[6:, -3], axis=0)
# print(f'好瓜 {good_melon}')
# print(f'坏瓜 {bad_melon}')
# 结果如下: 好瓜[{'sumType': 3, '青绿': 0.4444444444444444, '乌黑': 0.3333333333333333, '浅白': 0.2222222222222222},
# { 'sumType': 3, '蜷缩': 0.6666666666666666, '稍蜷': 0.2222222222222222, '硬挺': 0.1111111111111111}, { 'sumType': 3,
# '浊响': 0.5555555555555556, '沉闷': 0.3333333333333333, '清脆': 0.1111111111111111}, { 'sumType': 3,
# '清晰': 0.7777777777777778, '模糊': 0.1111111111111111, '稍糊': 0.1111111111111111}, { 'sumType': 3,
# '凹陷': 0.6666666666666666, '稍凹': 0.2222222222222222, '平坦': 0.1111111111111111}, { 'sumType': 2, '硬滑': 0.75,
# '软粘': 0.25}, {'sumType': 0, 'means': 0.612, 'var': 0.01346433333333333}, { 'sumType': 0,
# 'means': 0.3116666666666667, 'var': 0.0072288888888888915}] 坏瓜[{'sumType': 3, '浅白': 0.5, '青绿': 0.3, '乌黑': 0.2},
# {'sumType': 3, '硬挺': 0.2, '蜷缩': 0.4, '稍蜷': 0.4}, { 'sumType': 3, '清脆': 0.2, '浊响': 0.5, '沉闷': 0.3}, {'sumType':
# 3, '模糊': 0.4, '稍糊': 0.4, '清晰': 0.2}, { 'sumType': 3, '平坦': 0.4, '凹陷': 0.3, '稍凹': 0.3}, {'sumType': 2,
# '硬滑': 0.6666666666666666, '软粘': 0.3333333333333333}, {'sumType': 0, 'mean': 0.508,
# 'var': 0.029915142857142855}, { 'sumType': 0, 'mean': 0.14714285714285716, 'var': 0.010841551020408164}]
return pc_good,pc_bad,good_melon, bad_melon
# 开始对测试集分类
def classify_bayes(pc_good,pc_bad,good_melon, bad_melon, test_data):
# 对每一个测试数据进行计算好瓜与坏瓜的概率
for i in range(test_data.shape[0]):
# 每一个测试数据都要先令其等于先验概率的对数,后面全部取对数直接相加
good_probability = np.log(pc_good)
bad_probability = np.log(pc_bad)
for j in range(test_data.shape[1] - 3): # 先处理离散属性
if test_data[i][j] in good_melon[j]: # 如果这个特征训练集没有就跳过
good_probability += np.log(good_melon[j][test_data[i][j]]) # 转化为对数相加
if test_data[i][j] in bad_melon[j]:
bad_probability += np.log(bad_melon[j][test_data[i][j]])
for j in range(test_data.shape[1] - 3, test_data.shape[1] - 1): # 处理连续属性
good_probability += np.log((2 * np.pi * good_melon[j]['var']) ** (-1 / 2)) + \
(-1 / 2) * ((test_data[i][j] - good_melon[j]['mean']) ** 2) / (
good_melon[j]['var'] ** (-2))
bad_probability += np.log((2 * np.pi * bad_melon[j]['var']) ** (-1 / 2)) + \
(-1 / 2) * ((test_data[i][j] - bad_melon[j]['mean']) ** 2) / (
bad_melon[j]['var'] ** (-2))
print(f'The positive probability of the sample {i + 1} is {good_probability}\n\
The negative probability of the sample {i + 1} is {bad_probability}')
if good_probability > bad_probability:
print(f'Lucky! The test data numbered {i + 1} is a good melon\n')
else:
print(f'Not good! The test data numbered {i + 1} is a bad melon\n')
if __name__ == "__main__":
train_data, test_data = load_data()
pc_good,pc_bad,good_melon, bad_melon = train_bayes(train_data)
classify_bayes(pc_good,pc_bad,good_melon, bad_melon, test_data)
多练吧,熟能生巧,我现在还差一些,这些没那么熟练
2.5拉普拉斯平滑系数
普拉斯平滑技术可以避免这种“零概率陷阱”,某些事件或特征可能从未出现过,这会导致它们的概率被估计为零。 比如计算判断新瓜(纹理清晰,色泽淡白,鼓声沉闷)是好和坏时,因为在样本中色泽淡白没有出现,导致出现0值,会影响计算结果,要采用拉普拉斯平滑系数(不用的话样本出现0了)
API:sklearn.naive_bayes.MultinomialNB()
estimator.fit(x_train, y_train)
y_predict = estimator.predict(x_test)
使用完整案例,上面的分西瓜已经使用了,这次采用鸢尾花数据写一个完整案例:
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.naive_bayes import MultinomialNB
news =load_iris()
x_train, x_test, y_train, y_test = train_test_split(news.data, news.target)
estimator = MultinomialNB()
estimator.fit(x_train, y_train)
score = estimator.score(x_test, y_test)
print("准确率为:\n", score)
index=estimator.predict([[2,2,3,1]])
print("预测:\n",index,news.target_names,news.target_names[index])
3.决策树
3.1决策树的构成:
决策节点 通过条件判断而进行分支选择的节点。如:将某个样本中的属性值(特征值)与决策节点上的值进行比较,从而判断它的流向。
叶子节点 没有子节点的节点,表示最终的决策结果。
决策树的深度 所有节点的最大层次数。
决策树具有一定的层次结构,根节点的层次数定为0,从下面开始每一层子节点层次数增加
3.2基于信息增益决策树的建立
3.2.1信息熵与信息增益
信息熵描述的是不确定性。信息熵越大,不确定性越大。信息熵的值越小,则D的纯度越高。
假设样本集合D共有N类,第k类样本所占比例为
,则D的信息熵为H(D)=−∑i=1npilog2pi
信息增益是一个统计量,用来描述一个属性区分数据样本的能力。信息增益越大,那么决策树就会越简洁。这里信息增益的程度用信息熵的变化程度来衡量, 信息增益公式:IG(A)=H(D)−H(D∣A)
职业 | 年龄 | 收入 | 学历 | 是否贷款 | |
---|---|---|---|---|---|
1 | 工人 | 36 | 5500 | 高中 | 否 |
2 | 工人 | 42 | 2800 | 初中 | 是 |
3 | 白领 | 45 | 3300 | 小学 | 是 |
4 | 白领 | 25 | 10000 | 本科 | 是 |
5 | 白领 | 32 | 8000 | 硕士 | 否 |
6 | 白领 | 28 | 13000 | 博士 | 是 |
3.2.2 信息增益决策树建立步骤
我机器学习考了三次,信息熵与信息增益每次都要算,算就对了,这个意义不是很大,代码不会考察,建议掌握上面朴素贝叶斯的纯手写