KNN分类算法
KNN算法原理
根据K个邻居样本来判断当前样本属于哪个类别:K个最相似邻居中大多数所属类别即为当前样本的类别。但是对于数据量巨大或者高纬度的数据样本不太合适,数据量大的数据样本需要进行大量计算,而高纬度数据计算距离不具重要意义。
# knn算法
from sklearn.neighbors import KNeighborsClassifier
from sklearn.datasets import load_iris
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
import numpy as np
import joblib
iris = load_iris()
x,y = iris.data,iris.target
# print(x)
# print(y)
#划分数据集
x_train ,x_test, y_train,y_test = train_test_split(x,y,train_size=0.8,random_state=40)
#初始化标准化器
stander = StandardScaler()
#对x进行标准化处理
x_stand = stander.fit_transform(x_train)
#创建knn工具
estimater = KNeighborsClassifier(n_neighbors=7)
#训练
estimater.fit(x_stand,y_train)
#预测
y_predict = estimater.predict(x_test)
print('x_test:\n',x_test)
print('y_targer_names:\n',iris.target_names)
print('y_predict:\n',y_predict)
print('y_test:\n',y_test)
#评估1
res = y_predict == y_test
print(res)
score = np.sum(res)/len(res)
print(score)
#评估2
score1 = estimater.score(x_test,y_test)
print(score1)
#模型保存和加载
joblib.dump(estimater,'./mod/knn_iris.pkl')
# #推理(预测)
# x_new = [[5.1,3.5,1.4,0.2],
# [2.1,1.5,4.4,0.2],
# [5.1,5.5,1.4,5.2],
# [1.1,2.3,1.4,3.2]]
# x_new_stand = stander.transform(x_new)
# print(x_new_stand)
# y_detect = estimater.predict(x_new_stand)
# print('推理结果:',y_detect)
# print('种类:',iris.target_names[y_detect])
模型选择和调优
1.保留交叉验证
Train-Test-Split:将数据随机分到训练集和测试集中,可以自定义训练集和测试集的占比大小。对于某些特殊的数据集,可能造成同一类数据全部出现在训练集中,而其他类数据没有一个数据流入训练集,这样会造成模型训练失败(只能识别一种类型的数据)。
2.K-折交叉验证
K-Fold:将整个数据集分割成大小相同的K份,选择一个Fold用作训练,其余所有Fold用于测试。重复K次,直至每个Fold都被训练和验证过。模型的最终准确度是通过取k个模型验证数据的平均准确度来计算的。
3.分层K-折交叉验证
Stratified k-fold cross validation:对每个Fold进行分层训练和验证,假设原始数据有3类,占比为1:2:1,那么在每个Fold中也保证三类数据的占比为1:2:1。
from sklearn.model_selection import StratifiedKFold
from sklearn.datasets import load_iris
from sklearn.neighbors import KNeighborsClassifier
import numpy as np
x,y = iris = load_iris(return_X_y=True)
folder = StratifiedKFold(n_splits=5,shuffle=True,random_state=666)
iter = folder.split(x,y)
knn = KNeighborsClassifier(n_neighbors=7)
score_arr = []
for train_index , test_index in iter:
print(train_index,test_index,'-----------------')
model = knn.fit(x[train_index],y[train_index])
s = model.score(x[test_index],y[test_index])
score_arr.append(s)
print(score_arr)
print('平均准确率:',np.sum(score_arr)/(len(score_arr)))
4.超参搜索
Grid Searc:在KNN算法中,可以将n_neighbors作为可调参数传入,由Grid Search(网格搜索)验证并找出最优超参数值。
# 超参数的选择(网格搜素)
from sklearn.model_selection import GridSearchCV
from sklearn.neighbors import KNeighborsClassifier
from sklearn.datasets import load_iris
model = KNeighborsClassifier()
model = GridSearchCV(estimator=model,param_grid={'n_neighbors':[1,3,5,7,9]})
x,y = load_iris(return_X_y=True)
model.fit(x,y)
# best_params_ 最佳参数
# best_score_ 在训练集中的准确率
# best_estimator_ 最佳估计器
# cv_results_ 交叉验证过程描述
# best_index_最佳k在列表中的下标
print(model.best_params_)
print(model.best_score_)
print(model.best_estimator_)
# model.best_estimator_.predict()
print(model.cv_results_)
print(model.best_index_)
贝叶斯分类
1.理论
红色和蓝色分别表示两种事件的概率p1(x,y)和p2(x,y),当出现一个新数据点(x,y):
当p1(x,y)>p2(x,y),新数据点的类别为1;
当p1(x,y)<p2(x,y),新数据点的类别为2。
2.条件概率
在事件B发生的情况下,事件A发生的概率就是P(A∩B)除以P(B)。
𝑃(𝐴|𝐵)=𝑃(𝐴∩𝐵)/𝑃(𝐵)→𝑃(𝐴∩𝐵)=𝑃(𝐴|𝐵)𝑃(𝐵)
同理:
𝑃(𝐴∩𝐵)=𝑃(𝐵|𝐴)𝑃(𝐴)
故条件概率的计算公式为
𝑃(𝐴|𝐵)=𝑃(B|A)𝑃(𝐴)/𝑃(𝐵)
3.全概率公式
𝑃(𝐵)=𝑃(𝐵∩𝐴)+𝑃(𝐵∩𝐴′)
∵𝑃(𝐵∩𝐴)=𝑃(𝐵|𝐴)𝑃(𝐴)
∴𝑃(𝐵)=𝑃(𝐵|𝐴)𝑃(𝐴)+𝑃(𝐵|𝐴′)𝑃(𝐴′)
全概率公式为
P
(
A
∣
B
)
=
P
(
B
∣
A
)
P
(
A
)
P
(
B
∣
A
)
P
(
A
)
+
P
(
B
∣
A
,
)
P
(
A
,
)
P(A|B)=\frac{P(B|A)P(A)}{P(B|A)P(A)+P(B|A^,)P(A^,)}
P(A∣B)=P(B∣A)P(A)+P(B∣A,)P(A,)P(B∣A)P(A)
纹理 | 色泽 | 鼔声 | 类别 | |
---|---|---|---|---|
1 | 清晰 | 清绿 | 清脆 | 好瓜 |
2 | 模糊 | 乌黑 | 浊响 | 坏瓜 |
3 | 模糊 | 清绿 | 浊响 | 坏瓜 |
4 | 清晰 | 乌黑 | 沉闷 | 好瓜 |
5 | 清晰 | 清绿 | 浊响 | 好瓜 |
6 | 模糊 | 乌黑 | 沉闷 | 坏瓜 |
7 | 清晰 | 乌黑 | 清脆 | 好瓜 |
8 | 模糊 | 清绿 | 沉闷 | 好瓜 |
9 | 清晰 | 乌黑 | 浊响 | 坏瓜 |
10 | 模糊 | 清绿 | 清脆 | 好瓜 |
11 | 清晰 | 清绿 | 沉闷 | ? |
12 | 模糊 | 乌黑 | 浊响 | ? |
示例:
p(a|X) = p(X|a)* p(a)/p(X)
p(X|a) = p(x1,x2,x3...xn|a) = p(x1|a)*p(x2|a)*p(x3|a)...p(xn|a)
p(X) = p(x1,x2,x3...xn) = p(x1)*p(x2)*p(x3)...p(xn)
p(a|X) = p(x1|a)*p(x2|a)*p(x3|a)...p(xn|a) * p(a) / p(x1)*p(x2)*p(x3)...p(xn)
P(好瓜)=(好瓜数量)/所有瓜
P(坏瓜)=(坏瓜数量)/所有瓜
p(纹理清晰)=(纹理清晰数量)/所有瓜
p(纹理清晰|好瓜)= 好瓜中纹理清晰数量/好瓜数量
p(纹理清晰|坏瓜)= 坏瓜中纹理清晰数量/坏瓜数量
p(好瓜|纹理清晰,色泽清绿,鼓声沉闷)
=【p(好瓜)】*【p(纹理清晰,色泽清绿,鼓声沉闷|好瓜)】/【p(纹理清晰,色泽清绿,鼓声沉闷)】
=【p(好瓜)】*【p(纹理清晰|好瓜)*p(色泽清绿|好瓜)*p(鼓声沉闷|好瓜)】/【p(纹理清晰)*p(色泽清绿)*p(鼓声沉闷)】
p(坏瓜|纹理清晰,色泽清绿,鼓声沉闷)
=【p(坏瓜)*p(纹理清晰|坏瓜)*p(色泽清绿|坏瓜)*p(鼓声沉闷|坏瓜)】/【p(纹理清晰)*p(色泽清绿)*p(鼓声沉闷)】
从公式中判断"p(好瓜|纹理清晰,色泽清绿,鼓声沉闷)"和"p(坏瓜|纹理清晰,色泽清绿,鼓声沉闷)"时,因为它们的分母
值是相同的,[值都是p(纹理清晰)*p(色泽清绿)*p(鼓声沉闷)],所以只要计算它们的分子就可以判断是"好瓜"还是"坏瓜"之间谁大谁小了,所以没有必要计算分母
p(好瓜) = 6/10
p(坏瓜)=4/10
p(纹理清晰|好瓜) = 4/6
p(色泽清绿|好瓜) = 4/6
p(鼓声沉闷|好瓜) = 2/6
p(纹理清晰|坏瓜) = 1/4
p(色泽清绿|坏瓜) = 1/4
p(鼓声沉闷|坏瓜) = 1/4
把以上计算代入公式的分子
p(好瓜)*p(纹理清晰|好瓜)*p(色泽清绿|好瓜)*p(鼓声沉闷|好瓜) = 4/45
p(坏瓜)*p(纹理清晰|坏瓜)*p(色泽清绿|坏瓜)*p(鼓声沉闷|坏瓜) = 1/160
所以
p(好瓜|纹理清晰,色泽清绿,鼓声沉闷) > p(坏瓜|纹理清晰,色泽清绿,鼓声沉闷),
所以把(纹理清晰,色泽清绿,鼓声沉闷)的样本归类为好瓜
4.拉普拉斯平滑系数
为避免事件未出现的情况而导致的概率被估计为0,可以使用拉普拉斯平滑系数规避。
α通常取1,m为总特征数量
p(好瓜|纹理清晰,色泽淡白,鼓声沉闷)
=【p(好瓜)】*【p(纹理清晰|好瓜)*p(色泽淡白|好瓜)*p(鼓声沉闷|好瓜)】/【p(纹理清晰)*p(色泽淡白)*p(鼓声沉闷)】
p(坏瓜|纹理清晰,色泽淡白,鼓声沉闷)
=【p(坏瓜)】*【p(纹理清晰|坏瓜)*p(色泽淡白|坏瓜)*p(鼓声沉闷|坏瓜)】/【p(纹理清晰)*p(色泽淡白)*p(鼓声沉闷)】
p(纹理清晰|好瓜)= (4+1)/(6+3) # +1是因为防止零概率 +3是因为有3个特征(纹理,色泽,鼓声)
p(色泽淡白|好瓜)= (0+1)/(6+3)
p(鼓声沉闷|好瓜) = (2+1)/(6+3)
p(纹理清晰|坏瓜)= (1+1)/(4+3)
p(色泽淡白|坏瓜)= (0+1)/(4+3)
p(鼓声沉闷|坏瓜) = (1+1)/(4+3)
from sklearn.datasets import load_iris
from sklearn.naive_bayes import MultinomialNB
from sklearn.model_selection import train_test_split
import joblib
#实例化贝叶斯分类器
model = MultinomialNB()
# 加载鸢尾花数据集
x,y = load_iris(return_X_y=True)
# 划分数据集
x_train,x_test,y_train,y_test = train_test_split(x,y,train_size=0.8,random_state=666)
# 训练
model.fit(x_train,y_train)
# 评估
score = model.score(x_test,y_test)
print(score)
# 保存
joblib.dump(model,'mod/bys.bin')
import joblib
model1 = joblib.load('mod/bys.bin')
y_predect = model1.predict([[1,2,3,4]])
print(y_predect)
决策树分类
1.概念
1、决策节点
通过条件判断而进行分支选择的节点。如:将某个样本中的属性值(特征值)与决策节点上的值进行比较,从而判断它的流向。
2、叶子节点
没有子节点的节点,表示最终的决策结果。
3、决策树的深度
所有节点的最大层次数。
决策树具有一定的层次结构,根节点的层次数定为0,从下面开始每一层子节点层次数增加
4、决策树优点:
可视化 - 可解释能力-对算力要求低
5、 决策树缺点:
容易产生过拟合,所以不要把深度调整太大了。
2.基于基尼指数决策树的建立
基尼指数(Gini Index)是决策树算法中用于评估数据集纯度的一种度量,基尼指数衡量的是数据集的不纯度,或者说分类的不确定性。在构建决策树时,基尼指数被用来决定如何对数据集进行最优划分,以减少不纯度。
基尼指数的计算
对于一个二分类问题,如果一个节点包含的样本属于正类的概率是 §,则属于负类的概率是 (1-p)。那么,这个节点的基尼指数 (Gini§) 定义为:
G i n i ( p ) = 1 − p 2 − ( 1 − p ) 2 = 2 p ( 1 − p ) Gini(p) = 1 - p^2 - (1-p)^2 = 2p(1-p) Gini(p)=1−p2−(1−p)2=2p(1−p)
对于多分类问题,如果一个节点包含的样本属于第 k 类的概率是 p k p_k pk,则节点的基尼指数定义为:
G i n i ( p ) = 1 − ∑ k = 1 K p k 2 Gini(p) = 1 - \sum_{k=1}^{K} p_k^2 Gini(p)=1−k=1∑Kpk2
基尼指数的意义
- 当一个节点的所有样本都属于同一类别时,基尼指数为 0,表示纯度最高。
- 当一个节点的样本均匀分布在所有类别时,基尼指数最大,表示纯度最低。
决策树中的应用
在构建决策树时,我们希望每个内部节点的子节点能更纯,即基尼指数更小。因此,选择分割特征和分割点的目标是使子节点的平均基尼指数最小化。具体来说,对于一个特征,我们计算其所有可能的分割点对应的子节点的加权平均基尼指数,然后选择最小化这个值的分割点。这个过程会在所有特征中重复,直到找到最佳的分割特征和分割点。
例如,考虑一个数据集 (D),其中包含 (N) 个样本,特征 (A) 将数据集分割为 ∣ D 1 ∣ |D_1| ∣D1∣和 ∣ D 2 ∣ |D_2| ∣D2∣ ,则特征 (A) 的基尼指数为:
G i n i A = ∣ D 1 ∣ ∣ D ∣ G i n i ( D 1 ) + ∣ D 2 ∣ ∣ D ∣ G i n i ( D 2 ) Gini_A = \frac{|D_1|}{|D|} Gini(D_1) + \frac{|D_2|}{|D|} Gini(D_2) GiniA=∣D∣∣D1∣Gini(D1)+∣D∣∣D2∣Gini(D2)
其中 ∣ D 1 ∣ |D_1| ∣D1∣和 ∣ D 2 ∣ |D_2| ∣D2∣ 分别是子集 D 1 D_1 D1 和 D 2 D_2 D2 中的样本数量。
通过这样的方式,决策树算法逐步构建一棵树,每一层的节点都尽可能地减少基尼指数,最终达到对数据集的有效分类。
from sklearn.tree import DecisionTreeClassifier,export_graphviz
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
model = DecisionTreeClassifier(criterion='entropy')#entroopy信息熵的方式构建决策树,默认gini构建决策树
x,y = load_iris(return_X_y=True)
x_train,x_test,y_train,y_test = train_test_split(x,y,train_size=0.25)
scaler = StandardScaler()
scaler.fit(x_train)
x_train = scaler.transform(x_train)
model.fit(x_train,y_train)
x_test = scaler.transform(x_test)
rank = model.score(x_test,y_test)
print(rank)
y_predict = model.predict([[1,2,3,6],[2,5,1,3]])
print(y_predict)
# 决策过程可视化
export_graphviz(model,out_file='jueceshu/tree.dot',feature_names=['sepal length', 'sepal width', 'petal length', 'petal width'])
import pydotplus
from IPython.display import Image
# 读取 .dot 文件内容
dot_file_path = 'jueceshu/tree1.dot'
with open(dot_file_path, 'r') as f:
dot_data = f.read()
# 使用 pydotplus 将 .dot 文件内容转换为 PNG 图片
png_file_path = 'jueceshu/tree1.png'
graph = pydotplus.graph_from_dot_data(dot_data)
if graph is not None:
graph.write_png(png_file_path)
# 显示图片
Image(graph.create_png())
else:
print("Failed to create graph from dot data.")
随机森林
机器学习中有一种大类叫集成学习(Ensemble Learning),集成学习的基本思想就是将多个分类器组合,从而实现一个预测效果更好的集成分类器。集成算法可以说从一方面验证了中国的一句老话:三个臭皮匠,赛过诸葛亮。集成算法大致可以分为:Bagging,Boosting 和 Stacking 三大类型。
(1)每次有放回地从训练集中取出 n 个训练样本,组成新的训练集;
(2)利用新的训练集,训练得到M个子模型;
(3)对于分类问题,采用投票的方法,得票最多子模型的分类类别为最终的类别;
随机森林就属于集成学习,是通过构建一个包含多个决策树(通常称为基学习器或弱学习器)的森林,每棵树都在不同的数据子集和特征子集上进行训练,最终通过投票或平均预测结果来产生更准确和稳健的预测。这种方法不仅提高了预测精度,也降低了过拟合风险,并且能够处理高维度和大规模数据集
1.算法原理
- 随机: 特征随机,训练集随机
- 样本:对于一个总体训练集T,T中共有N个样本,每次有放回地随机选择n个样本。用这n个样本来训练一个决策树。
- 特征:假设训练集的特征个数为d,每次仅选择k(k<d)个来构建决策树。
- 森林: 多个决策树分类器构成的分类器, 因为随机,所以可以生成多个决策树
- 处理具有高维特征的输入样本,而且不需要降维
- 使用平均或者投票来提高预测精度和控制过拟合
import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn.feature_extraction import DictVectorizer
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
data = pd.read_csv('src/titanic/titanic.csv')
print(data)
data['age'].fillna(data['age'].mode()[0],inplace = True)
y = data[['survived']].to_numpy()
# print(y)
data.drop(['survived'],axis = 1,inplace = True)
# print(data.tail())
x = data[['age','sex','pclass']]
dict1 = data.to_dict(orient='records')
# dict1
vec = DictVectorizer(sparse=False)
x = vec.fit_transform(dict1)
# print(x)
# print(y)
#数据划分
x_train,x_test,y_train,y_test = train_test_split(x,y,train_size=0.25,random_state=666)
# 标准化
scaler = StandardScaler()
x_train = scaler.fit_transform(x_train)
# 训练模型
model = RandomForestClassifier(n_estimators=100 ,max_depth=8,criterion='gini')
model.fit(x_train,y_train)
# 模型评估
x_test = scaler.transform(x_test)
rank = model.score(x_test,y_test)
print(rank)