一、KNN 算法
- 原理
K-近邻算法(K-Nearest Neighbors,简称KNN),根据K个邻居样本的类别来判断当前样本的类别;
如果一个样本在特征空间中的k个最相似(最邻近)样本中的大多数属于某个类别,则该类本也属于这个类别
比如: 有10000个样本,选出7个到样本A的距离最近的,然后这7个样本中假设:类别1有2个,类别2有3个,类别3有2个.那么就认为A样本属于类别2,因为它的7个邻居中 类别2最多(近朱者赤近墨者黑)
- 距离度量
- 明可夫斯基距离:通用的距离度量公式。
- 欧式距离:明可夫斯基距离的特殊情况,常用于连续变量的距离计算。
- 曼哈顿距离:也是明可夫斯基距离的特殊情况,在一些场景中更具实际意义。
- 优缺点
- 优点:简单直观,易于理解和实现。
- 缺点:对于大规模数据集计算量大;对高维数据效果不佳;需要选择合适的 k 值和距离度量。
- API
sklearn.neighbors.KNeighborsClassifier(n_neighbors=5, algorithm='auto')
fit(x, y)
:使用 X 作为训练数据和 y 作为目标数据进行训练。predict(X)
:预测提供的数据。
- 示例代码(鸢尾花分类)
python
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier
# 1)获取数据
iris = load_iris()
# 2)划分数据集
x_train, x_test, y_train, y_test = train_test_split(iris.data, iris.target, random_state=22)
# 3)特征工程:标准化
transfer = StandardScaler()
x_train = transfer.fit_transform(x_train)
x_test = transfer.transform(x_test)
# 4)KNN算法预估器
estimator = KNeighborsClassifier(n_neighbors=7)
estimator.fit(x_train, y_train)
# 5)模型评估
y_predict = estimator.predict(x_test)
print("y_predict:\n", y_predict)
print("直接比对真实值和预测值:\n", y_test == y_predict)
score = estimator.score(x_test, y_test)
print("准确率为:\n", score)
二、模型选择与调优
1 交叉验证
(1) 保留交叉验证HoldOut
HoldOut Cross-validation(Train-Test Split)
在这种交叉验证技术中,整个数据集被随机地划分为训练集和验证集。根据经验法则,整个数据集的近70%被用作训练集,其余30%被用作验证集。也就是我们最常使用的,直接划分数据集的方法。
优点:很简单很容易执行。
缺点1:不适用于不平衡的数据集。假设我们有一个不平衡的数据集,有0类和1类。假设80%的数据属于 “0 “类,其余20%的数据属于 “1 “类。这种情况下,训练集的大小为80%,测试数据的大小为数据集的20%。可能发生的情况是,所有80%的 “0 “类数据都在训练集中,而所有 “1 “类数据都在测试集中。因此,我们的模型将不能很好地概括我们的测试数据,因为它之前没有见过 “1 “类的数据。
缺点2:一大块数据被剥夺了训练模型的机会。
在小数据集的情况下,有一部分数据将被保留下来用于测试模型,这些数据可能具有重要的特征,而我们的模型可能会因为没有在这些数据上进行训练而错过。
(2) K-折交叉验证(K-fold)
(K-fold Cross Validation,记为K-CV或K-fold)
K-Fold交叉验证技术中,整个数据集被划分为K个大小相同的部分。每个分区被称为 一个”Fold”。所以我们有K个部分,我们称之为K-Fold。一个Fold被用作验证集,其余的K-1个Fold被用作训练集。
该技术重复K次,直到每个Fold都被用作验证集,其余的作为训练集。
模型的最终准确度是通过取k个模型验证数据的平均准确度来计算的。
(3) 分层k-折交叉验证Stratified k-fold
Stratified k-fold cross validation,
K-折交叉验证的变种, 分层的意思是说在每一折中都保持着原始数据中各个类别的比例关系,比如说:原始数据有3类,比例为1:2:1,采用3折分层交叉验证,那么划分的3折中,每一折中的数据类别保持着1:2:1的比例,这样的验证结果更加可信。
(4) 其它验证
去除p交叉验证) 留一交叉验证) 蒙特卡罗交叉验证 时间序列交叉验证
(5)API
from sklearn.model_selection import StratifiedKFold
说明:普通K折交叉验证和分层K折交叉验证的使用是一样的 只是引入的类不同
from sklearn.model_selection import KFold
使用时只是KFold这个类名不一样其他代码完全一样
strat_k_fold=sklearn.model_selection.StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
n_splits划分为几个折叠 shuffle是否在拆分之前被打乱(随机化),False则按照顺序拆分 random_state随机因子
indexs=strat_k_fold.split(X,y)
返回一个可迭代对象,一共有5个折叠,每个折叠对应的是训练集和测试集的下标
然后可以用for循环取出每一个折叠对应的X和y下标来访问到对应的测试数据集和训练数据集 以及测试目标集和训练目标集
for train_index, test_index in indexs:
X[train_index] y[train_index] X[test_index ] y[test_index ]
(6)示例
from sklearn.datasets import load_iris
from sklearn.model_selection import StratifiedKFold
from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import StandardScaler
# 加载数据
iris = load_iris()
X = iris.data
y = iris.target
# 初始化分层k-折交叉验证器
#n_splits划分为几个折叠
#shuffle是否在拆分之前被打乱(随机化),False则按照顺序拆分
#random_state随机因子
strat_k_fold = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
# 创建一个K近邻分类器实例
knn = KNeighborsClassifier(n_neighbors=7)
# 进行交叉验证
accuracies = []
for train_index, test_index in strat_k_fold.split(X, y):
print(train_index, test_index)
X_train, X_test = X[train_index], X[test_index]
y_train, y_test = y[train_index], y[test_index]
# 数据预处理(标准化)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
# 使用K近邻分类器进行训练
knn.fit(X_train_scaled, y_train)
# 输出每次折叠的准确性得分
score = knn.score(X_test,y_test)
print(score)
accuracies.append(score)#把分数添加到外面列表中
print(sum(accuracies)/len(accuracies))#平均得分
#使用StratifiedKFold来创建5个折叠,每个折叠中鸢尾花数据集的类别分布与整体数据集的分布一致。然后我们对每个折叠进行了训练和测试,计算了分类器的准确性。
2 超参数搜索
超参数搜索也叫网格搜索(Grid Search)
比如在KNN算法中,k是一个可以人为设置的参数,所以就是一个超参数。网格搜索能自动的帮助我们找到最好的超参数值。
3 sklearn API
class sklearn.model_selection.GridSearchCV(estimator, param_grid) 说明: 同时进行交叉验证(CV)、和网格搜索(GridSearch),GridSearchCV实际上也是一个估计器(estimator),同时它有几个重要属性: best_params_ 最佳参数 best_score_ 在训练集中的准确率 best_estimator_ 最佳估计器 cv_results_ 交叉验证过程描述 best_index_最佳k在列表中的下标 参数: estimator: scikit-learn估计器实例 param_grid:以参数名称(str)作为键,将参数设置列表尝试作为值的字典 示例: {"n_neighbors": [1, 3, 5, 7, 9, 11]} cv: 确定交叉验证切分策略,值为: (1)None 默认5折 (2)integer 设置多少折 如果估计器是分类器,使用"分层k-折交叉验证(StratifiedKFold)"。在所有其他情况下,使用KFold。
4 示例-鸢尾花分类
用KNN算法对鸢尾花进行分类,添加网格搜索和交叉验证
# 用KNN算法对鸢尾花进行分类,添加网格搜索和交叉验证
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import GridSearchCV
def knn_iris_gscv():
# 1)获取数据
iris = load_iris()
# 2)划分数据集
x_train, x_test, y_train, y_test = train_test_split(iris.data, iris.target, random_state=22)
# 3)特征工程:标准化
transfer = StandardScaler()
x_train = transfer.fit_transform(x_train)
x_test = transfer.transform(x_test)
# 4)KNN算法预估器, 这里就不传参数n_neighbors了,交给GridSearchCV来传递
estimator = KNeighborsClassifier()
# 加入网格搜索与交叉验证, GridSearchCV会让k分别等于1,2,5,7,9,11进行网格搜索偿试。cv=10表示进行10次交叉验证
estimator = GridSearchCV(estimator, param_grid={"n_neighbors": [1, 3, 5, 7, 9, 11]}, cv=10)
estimator.fit(x_train, y_train)
# 5)模型评估
# 方法1:直接比对真实值和预测值
y_predict = estimator.predict(x_test)
print("y_predict:\n", y_predict)
print("直接比对真实值和预测值:\n", y_test == y_predict)
# 方法2:计算准确率
score = estimator.score(x_test, y_test)
print("在测试集中的准确率为:\n", score) #0.9736842105263158
# 最佳参数:best_params_
print("最佳参数:\n", estimator.best_params_) #{'n_neighbors': 3}, 说明k=3时最好
# 最佳结果:best_score_
print("在训练集中的准确率:\n", estimator.best_score_) #0.9553030303030303
# 最佳估计器:best_estimator_
print("最佳估计器:\n", estimator.best_estimator_) # KNeighborsClassifier(n_neighbors=3)
# 交叉验证结果:cv_results_
print("交叉验证过程描述:\n", estimator.cv_results_)
#最佳参数组合的索引:最佳k在列表中的下标
print("最佳参数组合的索引:\n",estimator.best_index_)
#通常情况下,直接使用best_params_更为方便
return None
knn_iris_gscv()
三、朴素贝叶斯算法
- 原理:基于贝叶斯定理和条件独立性假设,通过计算样本属于各个类别的概率来进行分类。
- 公式推导
- 拉普拉斯平滑系数:避免因某些特征在训练集中未出现而导致概率为零的情况。
- API
sklearn.naive_bayes.MultinomialNB()
fit(x_train, y_train)
:训练模型。predict(x_test)
:进行预测。
- 示例代码(鸢尾花分类)
python
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.naive_bayes import MultinomialNB
# 1)获取数据
news = load_iris()
# 2)划分数据集
x_train, x_test, y_train, y_test = train_test_split(news.data, news.target)
# 3)特征工程:不用做标准化
# 4)朴素贝叶斯算法预估器流程
estimator = MultinomialNB()
estimator.fit(x_train, y_train)
# 5)模型评估
score = estimator.score(x_test, y_test)
print("准确率为:\n", score)
# 6)预测
index = estimator.predict([[2, 2, 3, 1]])
print("预测:\n", index, news.target_names, news.target_names[index])
四、决策树算法
1 概念
1、决策节点 通过条件判断而进行分支选择的节点。如:将某个样本中的属性值(特征值)与决策节点上的值进行比较,从而判断它的流向。
2、叶子节点 没有子节点的节点,表示最终的决策结果。
3、决策树的深度 所有节点的最大层次数。
决策树具有一定的层次结构,根节点的层次数定为0,从下面开始每一层子节点层次数增加
4、决策树优点:
可视化 - 可解释能力-对算力要求低
5、 决策树缺点:
容易产生过拟合,所以不要把深度调整太大了
是动物 | 会飞 | 有羽毛 | |
---|---|---|---|
1麻雀 | 1 | 1 | 1 |
2蝙蝠 | 1 | 1 | 0 |
3飞机 | 0 | 1 | 0 |
4熊猫 | 1 | 0 | 0 |
是否为动物
是动物 | 会飞 | 有羽毛 | |
---|---|---|---|
1麻雀 | 1 | 1 | 1 |
2蝙蝠 | 1 | 1 | 0 |
4熊猫 | 1 | 0 | 0 |
是否会飞
是动物 | 会飞 | 有羽毛 | |
---|---|---|---|
1麻雀 | 1 | 1 | 1 |
2蝙蝠 | 1 | 1 | 0 |
是否有羽毛
是动物 | 会飞 | 有羽毛 | |
---|---|---|---|
1麻雀 | 1 | 1 | 1 |
2 基于信息增益决策树的建立
信息增益决策树倾向于选择取值较多的属性,在有些情况下这类属性可能不会提供太多有价值的信息,算法只能对描述属性为离散型属性的数据集构造决策树。
根据以下信息构建一棵预测是否贷款的决策树。我们可以看到有4个影响因素:职业,年龄,收入和学历。
职业 | 年龄 | 收入 | 学历 | 是否贷款 | |
---|---|---|---|---|---|
1 | 工人 | 36 | 5500 | 高中 | 否 |
2 | 工人 | 42 | 2800 | 初中 | 是 |
3 | 白领 | 45 | 3300 | 小学 | 是 |
4 | 白领 | 25 | 10000 | 本科 | 是 |
5 | 白领 | 32 | 8000 | 硕士 | 否 |
6 | 白领 | 28 | 13000 | 博士 | 是 |
(1) 信息熵
信息熵描述的是不确定性。信息熵越大,不确定性越大。信息熵的值越小,则D的纯度越高。
假设样本集合D共有N类,第k类样本所占例为Pk
,则D的信息熵为
(2) 信息增益
信息增益是一个统计量,用来描述一个属性区分数据样本的能力。信息增益越大,那么决策树就会越简洁。这里信息增益的程度用信息熵的变化程度来衡量, 信息增益公式:
。
(3) 信息增益决策树建立步骤
第一步,计算根节点的信息熵
上表根据是否贷款把样本分成2类样本,"是"占4/6=2/3, "否"占2/6=1/3,
所以
第二步,计算属性的信息增益
<1> "职业"属性的信息增益IG(D,"职业")
在职业中,工人占1/3, 工人中,是否代款各占1/2, 所以有
,
在职业中,白领占2/3, 白领中,是贷款占3/4, 不贷款占1/4, 所以有
所以有
最后得到职业属性的信息增益为:
<2>" 年龄"属性的信息增益(以35岁为界)
<3> "收入"属性的信息增益(以10000为界,大于等于10000为一类)
<4> "学历"属性的信息增益(以高中为界, 大于等于高中的为一类)
注意: 以上年龄使用35为界,收入使用10000为界,学历使用高中为界,实计API使用中,会有一个参数"深度", 属性中具体以多少为界会被根据深度调整。
第三步, 划分属性
对比属性信息增益发现,"收入"和"学历"相等,并且是最高的,所以我们就可以选择"学历"或"收入"作为第一个
决策树的节点, 接下来我们继续重复1,2的做法继续寻找合适的属性节点
3 基于基尼指数决策树的建立(了解)
基尼指数(Gini Index)是决策树算法中用于评估数据集纯度的一种度量,基尼指数衡量的是数据集的不纯度,或者说分类的不确定性。在构建决策树时,基尼指数被用来决定如何对数据集进行最优划分,以减少不纯度。
基尼指数的计算
对于一个二分类问题,如果一个节点包含的样本属于正类的概率是 (p),则属于负类的概率是 (1-p)。那么,这个节点的基尼指数 (Gini(p)) 定义为:
$Gini(p) = 1 - p^2 - (1-p)^2 = 2p(1-p) $
对于多分类问题,如果一个节点包含的样本属于第 k 类的概率是 $p_k$,则节点的基尼指数定义为:
$ Gini(p) = 1 - \sum_{k=1}^{K} p_k^2 $
基尼指数的意义
-
当一个节点的所有样本都属于同一类别时,基尼指数为 0,表示纯度最高。
-
当一个节点的样本均匀分布在所有类别时,基尼指数最大,表示纯度最低。
决策树中的应用
在构建决策树时,我们希望每个内部节点的子节点能更纯,即基尼指数更小。因此,选择分割特征和分割点的目标是使子节点的平均基尼指数最小化。具体来说,对于一个特征,我们计算其所有可能的分割点对应的子节点的加权平均基尼指数,然后选择最小化这个值的分割点。这个过程会在所有特征中重复,直到找到最佳的分割特征和分割点。
例如,考虑一个数据集 (D),其中包含 (N) 个样本,特征 (A) 将数据集分割为 $|D_1|$和 $|D_2|$ ,则特征 (A) 的基尼指数为:
$ Gini_A = \frac{|D_1|}{|D|} Gini(D_1) + \frac{|D_2|}{|D|} Gini(D_2) $
其中 $|D_1|$和 $|D_2|$ 分别是子集 $D_1$ 和 $D_2$ 中的样本数量。
通过这样的方式,决策树算法逐步构建一棵树,每一层的节点都尽可能地减少基尼指数,最终达到对数据集的有效分类。
4 sklearn API
class sklearn.tree.DecisionTreeClassifier(....)
参数:
criterion "gini" "entropy” 默认为="gini"
当criterion取值为"gini"时采用 基尼不纯度(Gini impurity)算法构造决策树,
当criterion取值为"entropy”时采用信息增益( information gain)算法构造决策树.
max_depth int, 默认为=None 树的最大深度
# 可视化决策树
function sklearn.tree.export_graphviz(estimator, out_file="iris_tree.dot", feature_names=iris.feature_names)
参数:
estimator决策树预估器
out_file生成的文档
feature_names节点特征属性名
功能:
把生成的文档打开,复制出内容粘贴到"http://webgraphviz.com/"中,点击"generate Graph"会生成一个树型的决策树图
5 示例
示例1:鸢尾花分类
用决策树对鸢尾花进行分类
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.tree import DecisionTreeClassifier, export_graphviz
# 1)获取数据集
iris = load_iris()
# 2)划分数据集
x_train, x_test, y_train, y_test = train_test_split(iris.data, iris.target, random_state=22)
#3)标准化
transfer = StandardScaler()
x_train = transfer.fit_transform(x_train)
x_test = transfer.transform(x_test)
# 4)决策树预估器
estimator = DecisionTreeClassifier(criterion="entropy")
estimator.fit(x_train, y_train)
# 5)模型评估,计算准确率
score = estimator.score(x_test, y_test)
print("准确率为:\n", score)
# 6)预测
index=estimator.predict([[2,2,3,1]])
print("预测:\n",index,iris.target_names,iris.target_names[index])
# 可视化决策树
export_graphviz(estimator, out_file="iris_tree.dot", feature_names=iris.feature_names)
准确率为:
0.8947368421052632
示例2:坦尼克号乘客生存
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction import DictVectorizer
from sklearn.tree import DecisionTreeClassifier, export_graphviz
# 1、获取数据
titanic = pd.read_csv("src/titanic/titanic.csv")
titanic.head()
# 筛选特征值和目标值
x = titanic[["pclass", "age", "sex"]]
y = titanic["survived"]
#2、数据处理
# 1)缺失值处理, 因为其中age有缺失值。
x["age"].fillna(x["age"].mean(), inplace=True)
# 2) 转换成字典, 因为其中数据必须为数字才能进行决策树,所在先转成字典,后面又字典特征抽取,这样之后的数据就会是数字了, 鸢尾花的数据本来就全部是数字,所以不需要这一步。
"""
x.to_dict(orient="records") 这个方法通常用于 Pandas DataFrame 对象,用来将 DataFrame 转换为一个列表,其中列表的每一个元素是一个字典,对应于 DataFrame 中的一行记录。字典的键是 DataFrame 的列名,值则是该行中对应的列值。
假设你有一个如下所示的 DataFrame x:
A B C
0 1 4 7
1 2 5 8
2 3 6 9
执行 x.to_dict(orient="records"),你会得到这样的输出:
[
{'A': 1, 'B': 4, 'C': 7},
{'A': 2, 'B': 5, 'C': 8},
{'A': 3, 'B': 6, 'C': 9}
]
"""
x = x.to_dict(orient="records")
# 3)、数据集划分
x_train, x_test, y_train, y_test = train_test_split(x, y, random_state=22)
# 4)、字典特征抽取
transfer = DictVectorizer()
x_train = transfer.fit_transform(x_train) #稀疏矩阵
x_test = transfer.transform(x_test)
# 3)决策树预估器
estimator = DecisionTreeClassifier(criterion="entropy", max_depth=8)
estimator.fit(x_train, y_train)
# 4)模型评估
# 方法1:直接比对真实值和预测值
y_predict = estimator.predict(x_test)
print("y_predict:\n", y_predict)
print("直接比对真实值和预测值:\n", y_test == y_predict)
# 方法2:计算准确率
score = estimator.score(x_test, y_test)
print("准确率为:\n", score)
# 6)预测
x_test = transfer.transform([{'pclass': '1rd', 'age': 22.0, 'sex': 'female'}])
index=estimator.predict(x_test)
print("预测1:\n",index)#[1] 头等舱的就可以活下来
x_test = transfer.transform([{'pclass': '3rd', 'age': 22.0, 'sex': 'female'}])
index=estimator.predict(x_test)
print("预测2:\n",index)#[0] 3等舱的活不下来
# 可视化决策树
export_graphviz(estimator, out_file="titanic_tree.dot", feature_names=transfer.get_feature_names_out())