02 基于sklearn的机械学习-KNN算法、模型选择与调优(交叉验证、朴素贝叶斯算法、拉普拉斯平滑)、决策树(信息增益、基尼指数)、随机森林
文章目录
- KNN算法
- 距离
- 1. 欧氏距离(Euclidean Distance)
- 2. 曼哈顿距离(Manhattan Distance)
- 3. 切比雪夫距离(Chebyshev Distance)
- 4. 闵可夫斯基距离(Minkowski Distance)
- 5. 余弦相似度(Cosine Similarity)
- 6. 杰卡德相似度(Jaccard Similarity)
- 7. 马氏距离(Mahalanobis Distance)
- KNN算法
- 模型保存与加载
- 模型选择与调优
- 交叉验证
- 1 保留交叉验证(Train-Test Split)
- 2 K-折交叉验证(K-fold)
- 3 分层K-折交叉验证(Stratified k-fold)
- 超参数搜索 GridSearchCV
- 朴素贝叶斯算法
- 1.条件概率
- 2. 全概率公式
- 3. 贝叶斯定理
- 4 朴素贝叶斯推断
- 5. 拉普拉斯平滑
- 决策树-分类
- 基于信息增益决策树的建立
- (1) 信息熵
- (2) 信息增益
- (3) 信息增益决策树建立步骤
- 基于基尼指数决策树的建立
- 决策树sklearn API
- 随机森林
KNN算法
距离
在机器学习中,样本距离是衡量数据点之间相似性或差异性的核心概念,广泛应用于分类、聚类、降维等算法中。
1. 欧氏距离(Euclidean Distance)
最常见的两点或多点之间的距离表示方法,指在m维空间中两个点之间的真实距离,或者向量的自然长度(即该点到原点的距离)。在二维和三维空间中的欧氏距离就是两点之间的实际距离。
在 n 维空间中,两个点欧氏距离为:
d(A,B)=(x1−y1)2+(x2−y2)2+⋯+(xn−yn)2d(A,B) = \sqrt{(x_1 - y_1)^2 + (x_2 - y_2)^2 + \dots + (x_n - y_n)^2}\ d(A,B)=(x1−y1)2+(x2−y2)2+⋯+(xn−yn)2
公式可简化为向量形式(若 A 和 B 表示为向量):
d(A,B)=∥A−B∥2d(A,B) = \| A - B \|_2\ d(A,B)=∥A−B∥2
2. 曼哈顿距离(Manhattan Distance)
两点在网格状坐标系中走过的最短路径(只能沿坐标轴方向移动),又称 “城市街区距离”。
在 n 维空间中,A 与 B 之间的曼哈顿距离公式为:
d(A,B)=∣x1−y1∣+∣x2−y2∣+⋯+∣xn−yn∣d(A,B) = |x_1 - y_1| + |x_2 - y_2| + \dots + |x_n - y_n| d(A,B)=∣x1−y1∣+∣x2−y2∣+⋯+∣xn−yn∣
公式可简化为向量形式:
d(A,B)=∥A−B∥1d(A,B) = \| A - B \|_1 d(A,B)=∥A−B∥1
3. 切比雪夫距离(Chebyshev Distance)
两点在各维度上差值的最大值,相当于 “国际象棋中国王的移动距离”。
d(x,y)=maxi=1n∣xi−yi∣d(\mathbf{x}, \mathbf{y}) = \max_{i=1}^{n} |x_i - y_i| d(x,y)=i=1maxn∣xi−yi∣
公式可通过数学变换与曼哈顿距离关联(需借助坐标转换),但核心是对 “最大维度差异” 的直接度量。
4. 闵可夫斯基距离(Minkowski Distance)
欧氏距离和曼哈顿距离的广义形式,通过参数 p 控制距离度量的阶数。
d(x,y)=(∑i=1n∣xi−yi∣p)1/pd(\mathbf{x}, \mathbf{y}) = \left( \sum_{i=1}^{n} |x_i - y_i|^p \right)^{1/p} d(x,y)=(i=1∑n∣xi−yi∣p)1/p
- 当 (p=1) 时,退化为曼哈顿距离;
- 当 (p=2) 时,即为欧氏距离;
- 当 (p \to \infty) 时,收敛于切比雪夫距离。
5. 余弦相似度(Cosine Similarity)
计算两向量夹角的余弦值,衡量方向的相似性(而非大小)。
cosine(x,y)=x⋅y∣∣x∣∣⋅∣∣y∣∣=∑i=1nxiyi∑i=1nxi2⋅∑i=1nyi2\text{cosine}(\mathbf{x}, \mathbf{y}) = \frac{\mathbf{x} \cdot \mathbf{y}}{||\mathbf{x}|| \cdot ||\mathbf{y}||} = \frac{\sum_{i=1}^{n} x_i y_i}{\sqrt{\sum_{i=1}^{n} x_i^2} \cdot \sqrt{\sum_{i=1}^{n} y_i^2}} cosine(x,y)=∣∣x∣∣⋅∣∣y∣∣x⋅y=∑i=1nxi2⋅∑i=1nyi2∑i=1nxiyi
余弦相似度的结果范围是 [-1, 1],具体含义如下:
- θ = 0°:(\cos(\theta) = 1) → 两向量方向完全相同(相似度最高);
- θ = 90°:(\cos(\theta) = 0) → 两向量垂直,方向无关;
- θ = 180°:(\cos(\theta) = -1) → 两向量方向完全相反(相似度最低)。
6. 杰卡德相似度(Jaccard Similarity)
用于比较两个集合的相似性,定义为交集与并集的比值。
对于两个集合 A 和 B,杰卡德相似度为:
J(A,B)=∣A∩B∣∣A∪B∣J(A, B) = \frac{|A \cap B|}{|A \cup B|}\ J(A,B)=∣A∪B∣∣A∩B∣
杰卡德距离为:
d(A,B)=1−J(A,B)d(A, B) = 1 - J(A, B) d(A,B)=1−J(A,B)
杰卡德相似度的结果范围是 [0, 1],具体含义如下:
- J = 1:两个集合完全相同(交集 = 并集);
- J = 0:两个集合没有任何共同元素(交集为空);
- 数值越接近 1,说明两个集合的重叠度越高,相似性越强。
7. 马氏距离(Mahalanobis Distance)
考虑数据分布协方差的距离度量,可消除特征间相关性的影响,且对尺度不敏感。
对于两个数据点 x 和 y,若它们来自同一分布(协方差矩阵为 S,则两者之间的马氏距离为:
d(x,y)=(x−y)TS−1(x−y)d(\mathbf{x}, \mathbf{y}) = \sqrt{(\mathbf{x} - \mathbf{y})^T S^{-1} (\mathbf{x} - \mathbf{y})} d(x,y)=(x−y)TS−1(x−y)
KNN算法
K 近邻(K-Nearest Neighbors, KNN)是一种基本且常用的监督学习算法,可用于分类和回归任务。其核心思想是:给定一个训练数据集,对于新的输入实例,在训练数据集中找到与该实例最邻近的 k 个实例,然后基于这 k 个实例的信息来进行预测。
- 计算待预测样本与训练集中所有样本的距离。
- 选择距离最小的 k 个样本。
- 根据这 k 个样本进行分类,则预测样本属于k个样本中样本数最多的那一类。
缺点:
- 对于大规模的数据集,计算量过大
- 维度灾难–对于高维数据,最近邻与最远邻的距离差异缩小,导致模型性能下降。
- K 值选择困难
- K 值过小:模型过拟合,对噪声敏感(如 K=1 时易受离群点影响)。
- K 值过大:模型欠拟合,忽略局部特征(如 K=n 时退化为全局多数表决)。
模型保存与加载
import joblib
# 保存模型
joblib.dump(estimator, "my_ridge.pkl")
# 加载模型
estimator = joblib.load("my_ridge.pkl")
#使用模型预测
y_test=estimator.predict([[0.4,0.2,0.4,0.7]])
print(y_test)
模型选择与调优
交叉验证
交叉验证的核心思想是将原始数据集划分为多个子集,轮流将其中一个子集作为测试集,其余作为训练集,多次训练和评估模型后取平均值作为最终性能指标。
交叉验证主要解决以下问题:
- 传统的单次训练测试分割可能导致结果不稳定
- 模型在特定测试集上可能过拟合
- 充分利用有限的数据进行模型评估
1 保留交叉验证(Train-Test Split)
保留交叉验证(Hold-Out Cross Validation,又称 Train-Test Split)是最直观、最简单的模型验证方法,核心思想是将原始数据集一次性分割为两个互斥的子集:训练集(Train Set)和测试集(Test Set),用训练集训练模型,用测试集评估模型的泛化能力。
常见比例为训练集 70%-80%,测试集 20%-30%。
sklearn.model_selection.train_test_split
test_size
:测试集占比(如 0.2 表示 20% 数据作为测试集)。random_state
:随机种子,固定后分割方式不变,保证实验可复现。stratify
:用于类别不平衡数据(如二分类中正负样本比例悬殊),设置为y
时,训练集和测试集会保持与原始数据相同的类别比例(避免测试集某类样本过少导致评估失真)。
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
x,y = load_iris(return_X_y=True)
x_train,x_test,y_train,y_test = train_test_split(x,y,test_size=0.2,random_state=22,shuffle=True,stratify=y)
#stratify=y :按照y的分布进行分层print(y_test)
[1 2 0 0 0 0 0 2 1 0 1 2 1 1 2 1 0 2 0 1 1 1 1 0 2 2 2 0 2 2]
优点
- 简单高效:仅分割一次,计算成本低(尤其适合大数据集)。
- 直观易懂:直接模拟 “训练→测试” 的真实场景,结果易解释。
缺点
- 结果稳定性差:评估结果受分割方式影响大(不同随机种子可能导致测试集分布差异,进而影响指标)。
- 数据利用率低:测试集未参与训练,对于小数据集,可能浪费有效信息(训练数据不足导致模型欠拟合)。
- 对小数据集不友好:若数据量小,测试集可能无法代表整体数据分布(如极端值集中在测试集),导致评估失真。
2 K-折交叉验证(K-fold)
将数据集划分为K 个互不重叠的子集(折),每个分区被称为 一个”Fold”。一个Fold被用作验证集,其余的K-1个Fold被用作训练集,重复 K 次。最终模型性能是 K 次评估结果的平均值。
from sklearn.model_selection import KFold
n_splits
:折数,通常取 5 或 10shuffle
:是否在划分前打乱数据random_state
:随机种子,确保结果可重现
from sklearn.model_selection import KFold,StratifiedKFold
from sklearn.datasets import load_iris
x,y = load_iris(return_X_y=True)
k = KFold(n_splits=5,shuffle=True,random_state=22)
re = k.split(x,y)
# print(re)# 迭代器
# print(next(re))for train_index,test_index in re:x_train,x_test = x[train_index],x[test_index]y_train,y_test = y[train_index],y[test_index]print(y_test)
[0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2]
[0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2]
[0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2]
[0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2]
缺点:对不平衡数据集不友好(可能导致某类样本在测试集中缺失)
随机划分可能导致某些折中某类别样本极少甚至缺失,尤其是当某类别样本占比较低时(如欺诈检测中欺诈样本通常不足 1%)。这种不平衡会导致:
- 训练集和测试集分布不一致
- 模型评估指标不可靠(如准确率虚高)
- 对少数类别的预测能力无法得到有效验证
3 分层K-折交叉验证(Stratified k-fold)
分层 K - 折交叉验证(Stratified K-Fold Cross-Validation)是 K - 折交叉验证的改进版本,特别适用于分类问题,尤其是类别不平衡的数据集。其核心思想是在每次划分时保持每个折中各类别样本的比例与原始数据集一致,从而确保评估结果的可靠性。
from sklearn.model_selection import StratifiedKFold
strat_k_fold=sklearn.model_selection.StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
- n_splits划分为几个折叠
- shuffle是否在拆分之前被打乱(随机化),False则按照顺序拆
- random_state随机因子
from sklearn.model_selection import StratifiedKFold
from sklearn.datasets import load_iris
x,y = load_iris(return_X_y=True)
k = StratifiedKFold(n_splits=5,shuffle=True,random_state=22)
re = k.split(x,y)
# print(re)# 迭代器
# print(next(re))for train_index,test_index in re:x_train,x_test = x[train_index],x[test_index]y_train,y_test = y[train_index],y[test_index]print(y_test)
[0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2]
[0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2]
[0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2]
[0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2]
[0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2]
超参数搜索 GridSearchCV
在机器学习中,超参数是模型训练前需要人为设定的参数(如 KNN 中的k
值、决策树的max_depth
等)。超参数搜索是寻找最优超参数组合的过程,而 **网格搜索(Grid Search)**是最常用的方法之一。
GridSearchCV API
class sklearn.model_selection.GridSearchCV(estimator, param_grid)
GridSearchCV(estimator, # 模型(如KNN、RandomForest等)param_grid, # 超参数网格(字典形式)score=None, # 评估指标cv=None, # 交叉验证折数或策略...
)
estimator
-
含义:需要优化的模型对象。
from sklearn.neighbors import KNeighborsClassifier estimator = KNeighborsClassifier() # 创建KNN模型
param_grid
-
含义:待搜索的超参数组合,格式为字典的列表。
param_grid = {'n_neighbors': [3, 5, 7, 9], # K值'weights': ['uniform', 'distance'], # 权重计算方式'p': [1, 2] # 距离度量(1=曼哈顿,2=欧氏) }
cv
-
含义:交叉验证的折数或自定义策略。
-
常用值
- 整数:指定 K 折(如
cv=5
) - 交叉验证生成器:如
StratifiedKFold
(处理类别不平衡)
from sklearn.model_selection import StratifiedKFold cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
- 整数:指定 K 折(如
网格搜索结束后,可以通过以下属性获取结果:
- 最佳参数组合:
grid_search.best_params_
- 最佳模型:
grid_search.best_estimator_
- 最佳交叉验证分数:
grid_search.best_score_
- 所有参数组合的结果:
grid_search.cv_results_
from sklearn.model_selection import GridSearchCV
from sklearn.neighbors import KNeighborsClassifier
from sklearn.datasets import load_irisiris = load_iris()
x,y = load_iris(return_X_y=True)knn = KNeighborsClassifier()
model = GridSearchCV(knn,param_grid={'n_neighbors':[1,2,3,4,5,6,7,8,9,10]},cv = 5)
model.fit(x,y)# 查看结果
print("最佳参数:",model.best_params_)
print("最佳结果:",model.best_score_)
print("最佳模型:",model.best_estimator_)
# print("交叉验证结果:",model.cv_results_)#model本身是个模型
# 使用模型
y_predict = model.predict([[5.1,3.5,1.4,0.2]])
print("预测结果为:",y_predict,iris.target_names[y_predict])
最佳参数: {'n_neighbors': 6}
最佳结果: 0.9800000000000001
最佳模型: KNeighborsClassifier(n_neighbors=6)
预测结果为: [0] ['setosa']
朴素贝叶斯算法
1.条件概率
条件概率是概率论中的核心概念,用于描述在已知某一事件发生的情况下,另一事件发生的概率。
设 A 和 B 是样本空间Ω中的两个事件,且 (P(B) > 0),则在事件 B 发生的条件下,事件 A 发生的条件概率记为 (P(A|B)),定义为:𝑃(𝐴|𝐵)=𝑃(𝐴∩𝐵)/𝑃(𝐵)
𝑃(𝐴∩𝐵)=𝑃(𝐴|𝐵)𝑃(𝐵)
𝑃(𝐴∩𝐵)=𝑃(𝐵|𝐴)𝑃(𝐴)
𝑃(𝐴|𝐵)=𝑃(B|A)𝑃(𝐴)/𝑃(𝐵)
2. 全概率公式
假定样本空间S,是两个事件A与A’的和。
事件B可以划分成两个部分:𝑃(𝐵)=𝑃(𝐵∩𝐴)+𝑃(𝐵∩𝐴′)
因为𝑃(𝐵∩𝐴)=𝑃(𝐵|𝐴)𝑃(𝐴)
所以𝑃(𝐵)=𝑃(𝐵|𝐴)𝑃(𝐴)+𝑃(𝐵|𝐴′)𝑃(𝐴′)
将这个公式代入上一节的条件概率公式,就得到了条件概率的另一种写法:
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)
3. 贝叶斯定理
贝叶斯定理描述了条件概率之间的关系
其中:
- P(A):“先验概率”,即在B事件发生之前,我们对A事件概率的一个判断。
- P(A|B):“后验概率”(Posterior probability),即在B事件发生之后,我们对A事件概率的重新评估。
- P(B|A)/P(B):“可能性函数”,这是一个调整因子,使得预估概率更接近真实概率。
- 后验概率 = 先验概率x调整因子
4 朴素贝叶斯推断
朴素贝叶斯分类器的关键假设是特征之间的条件独立性,即给定类别 a ,特征 xix_ixi 和 xjx_jxj (其中 i≠ji \neq ji=j 相互独立。)
因此,我们可以将联合概率 P(X|a) 分解为各个特征的概率乘积:
P(X∣a)=P(x1,x2,...,xn∣a)=P(x1∣a)P(x2∣a)...P(xn∣a)P(X|a) = P(x_1, x_2, ..., x_n|a) = P(x_1|a)P(x_2|a)...P(x_n|a)P(X∣a)=P(x1,x2,...,xn∣a)=P(x1∣a)P(x2∣a)...P(xn∣a)
将这个条件独立性假设应用于贝叶斯公式,我们得到:
P(a∣X)=P(x1∣a)P(x2∣a)...P(xn∣a)P(a)P(X)P(a|X) = \frac{P(x_1|a)P(x_2|a)...P(x_n|a)P(a)}{P(X)}P(a∣X)=P(X)P(x1∣a)P(x2∣a)...P(xn∣a)P(a)
5. 拉普拉斯平滑
某些事件或特征可能从未出现过,这会导致它们的概率被估计为零。
解决:P(xi∣y)=Ny,xi+αNy+αnP(x_i|y) = \frac{N_{y,x_i} + \alpha}{N_y + \alpha n}P(xi∣y)=Ny+αnNy,xi+α
- α\alphaα:平滑系数(通常为1)
- nnn:特征取值数
sklearn.naive_bayes.MultinomialNB()
estimator.fit(x_train, y_train)
y_predict = estimator.predict(x_test)
【贝叶斯对鸢尾花的分类】
from sklearn.naive_bayes import MultinomialNB
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
iris = load_iris()
x,y = load_iris(return_X_y=True)x_train,x_test,y_train,y_test = train_test_split(x,y,test_size=0.2,random_state=22)
model = MultinomialNB()
#训练:统计先验概率
model.fit(x_train,y_train)
score = model.score(x_test,y_test)
print("准确率:",score)x_new = [[5.1,3.5,1.4,0.2],[1,2,4,3]]
y_new = model.predict(x_new)
print("预测结果为:",y_new)
print("预测结果为:",model.predict(x_new))
print(iris.target_names[y_new])
准确率: 0.6666666666666666
预测结果为: [0 2]
预测结果为: [0 2]
['setosa' 'virginica']
【贝叶斯对葡萄酒的分类】
from sklearn.datasets import load_wine
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import train_test_split
from sklearn.naive_bayes import MultinomialNB# 加载数据集
wine = load_wine()
x,y = load_wine(return_X_y=True)# 无量纲化处理--多项式朴素贝叶斯要求输入特征非负
transfer = MinMaxScaler()
x=transfer.fit_transform(x)# 数据集的划分
x_train,x_test,y_train,y_test = train_test_split(x,y,test_size=0.2,random_state=22)# 创建模型
model = MultinomialNB(alpha=1.0)# 模型训练
model.fit(x_train,y_train)# 模型评估
print("准确率:",model.score(x_test,y_test))
y_predict = model.predict(x_test)
print(y_predict)
准确率: 0.8888888888888888
[1 2 2 2 0 1 0 1 1 1 1 1 0 0 1 0 0 0 2 2 1 1 2 1 1 2 1 1 2 2 0 0 0 1 0 2]
决策树-分类
决策树依据特征对数据进行划分,其结构类似于树,由节点和分支构成。
- 根节点:是决策树的起始点,包含全部的训练样本。
- 内部节点:代表一个特征上的测试,通过这个测试将样本划分到不同的子节点。
- 分支:是节点之间的连接,体现了测试的结果。
- 叶节点:表示最终的预测类别。
具体通过以下步骤构建:
- 特征选择:从所有特征中选择一个最优特征作为当前节点的分裂依据,衡量 “最优” 的指标有:
- 信息增益(ID3 算法):基于信息熵(衡量数据混乱程度),选择使分裂后信息熵减少最多的特征(信息增益 = 父节点熵 - 子节点熵的加权和)。
- 信息增益比(C4.5 算法):解决信息增益偏向多值特征的问题,用信息增益除以特征自身的熵。
- 基尼指数(CART 算法):衡量数据不纯度(值越小,纯度越高),选择使分裂后基尼指数下降最多的特征。
- 递归分裂:用选中的特征和分裂点将数据划分为子数据集,对每个子数据集重复步骤 1,直到满足停止条件(如子数据集样本数小于阈值、树深度达到上限、无法进一步降低不纯度等)。
- 预测: 新样本从根节点开始,根据其特征值沿着树的分支向下移动,直到到达某个叶节点。该叶节点的值(类别标签或目标均值)就是预测结果。
优缺点:
- 优点: 易于理解和解释(可视化),对数据的预处理要求相对较低(如不需要特征缩放、能处理缺失值、能处理数值和类别特征),计算速度相对较快。
- 缺点: 非常容易过拟合(树太深、太复杂),对训练数据的小变化可能非常敏感(高方差),单独一棵树的预测能力通常有限。
基于信息增益决策树的建立
信息增益(Information Gain)基于信息论中的熵(Entropy)概念,衡量特征分割后数据集纯度提升的程度。
职业 | 年龄 | 收入 | 学历 | 是否贷款 | |
---|---|---|---|---|---|
1 | 工人 | 36 | 5500 | 高中 | 否 |
2 | 工人 | 42 | 2800 | 初中 | 是 |
3 | 白领 | 45 | 3300 | 小学 | 是 |
4 | 白领 | 25 | 10000 | 本科 | 是 |
5 | 白领 | 32 | 8000 | 硕士 | 否 |
6 | 白领 | 28 | 13000 | 博士 | 是 |
(1) 信息熵
在机器学习中,信息熵常被用来评估数据的纯度 —— 熵值越低,数据越 “纯”(不确定性越低);熵值越高,数据越 “混乱”(不确定性越高)。
假设样本集合D共有N类,第k类样本所占比例为Pk,则D的信息熵为
(2) 信息增益
信息增益是一个统计量,用来描述一个属性区分数据样本的能力。信息增益越大,那么决策树就会越简洁。信息增益表示使用某个特征分裂数据集后,信息熵的减少量。信息增益公式:
信息增益 = 初始熵 - 加权熵
(3) 信息增益决策树建立步骤
a.上表根据是否贷款把样本分成2类样本,"是"占4/6=2/3, "否"占2/6=1/3,
所以
b.计算各个属性的信息增益
c.划分属性
对比属性信息增益发现,"收入"和"学历"相等,并且是最高的,所以我们就可以选择"学历"或"收入"作为第一个
决策树的节点, 接下来我们继续重复1,2的做法继续寻找合适的属性节点
基于基尼指数决策树的建立
基于基尼指数(Gini Index)的决策树是 CART(Classification and Regression Tree)算法中用于分类任务的核心模型。与基于信息熵的 ID3/C4.5 算法不同,它使用基尼指数衡量数据不纯度,以此指导特征选择和节点分裂。
对于一个二分类问题,如果一个节点包含的样本属于正类的概率是 (p),则属于负类的概率是 (1-p)。那么,这个节点的基尼指数定义为:
Gini(p)=1−p2−(1−p)2=2p(1−p)Gini(p) = 1 - p^2 - (1-p)^2 = 2p(1-p) Gini(p)=1−p2−(1−p)2=2p(1−p)
对于多分类问题,如果一个节点包含的样本属于第 k 类的概率是 pkp_kpk,则节点的基尼指数定义为:
Gini(p)=1−∑k=1Kpk2Gini(p) = 1 - \sum_{k=1}^{K} p_k^2 Gini(p)=1−k=1∑Kpk2
基尼指数用于衡量数据集中类别的 “混乱程度”(不纯度),取值范围为(0, 1):
- 基尼指数 = 0:所有样本属于同一类别(完全纯);
- 基尼指数 = 1:样本类别分布最均匀(最混乱)。
【例子】
在计算时,工资和平台的计算方式有明显的不同。因为工资只有两个取值0和1,而平台有三个取值0,1,2。所以在计算时,需要将平台的每一个取值都单独进行计算。比如:当平台=0时,将数据集分为两部分,第一部分是平台=0,第二部分是平台!=0(分母是5的原因)。
根据基尼指数最小准则, 我们优先选择工资或者平台=0作为D的第一特征。
我们选择工资作为第一特征,那么当工资=1时,工作=好,无需继续划分。当工资=0时,需要继续划分。
当工资=0时,继续计算基尼指数:
当平台=0时,基尼指数=0,可以优先选择。
同时,当平台=0时,工作都是好,无需继续划分,当平台=1,2时,工作都是不好,也无需继续划分。直接把1,2放到树的一个结点就可以。
决策树sklearn API
class sklearn.tree.DecisionTreeClassifier(…)
- criterion:用于衡量划分质量的函数,
- 当criterion取值为"gini"时采用 基尼不纯度(Gini impurity)算法构造决策树,
- 当criterion取值为"entropy”时采用信息增益( information gain)算法构造决策树.
- max_depth:树的最大深度,能够防止模型过拟合。
- min_samples_split:拆分内部节点所需的最小样本数。
- min_samples_leaf:叶节点所需的最小样本数。
- max_features:在寻找最佳划分时要考虑的特征数量。
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifierx,y = load_iris(return_X_y=True)
x_train,x_test,y_train,y_test = train_test_split(x,y,test_size=0.2,random_state=0)model = DecisionTreeClassifier()
model.fit(x_train,y_train)
print(model.score(x_test,y_test))y_predict = model.predict([[4.3, 2.6 ,1.5, 1.3]])
print(y_predict)
1.0
[1]
导出决策树:
# 导出已经构建好的决策树
from sklearn.tree import export_graphviz
export_graphviz(model,out_file='../src/tree.dot',feature_names=iris.feature_names)
随机森林
随机森林是一种集成学习 (Ensemble Learning) 算法,属于 Bagging (Bootstrap Aggregating) 类型。它通过构建多棵决策树并将它们的预测结果进行组合(分类:投票;回归:平均)来获得最终的预测结果。其核心在于引入双重随机性来降低单棵决策树的方差和过拟合风险,从而提高模型的泛化能力和鲁棒性。
步骤如下:
- 样本随机:从原始训练集中有放回地抽样(bootstrap 抽样),生成多个不同的子训练集(每个子数据集大小与原数据集相同)。
- 特征随机:每棵树在每个节点分裂时,从所有特征中随机选择一部分(如
sqrt(n_features)
)作为候选特征,仅用这些特征判断分裂。 - 构建多棵树:用每个子数据集和随机选择的特征训练一棵决策树(不剪枝,保留多样性)。
- 结果聚合:
- 分类任务:所有树投票,得票最多的类别为最终结果;
- 回归任务:所有树的预测值取平均作为最终结果。
class sklearn.ensemble.RandomForestClassifier
- n_estimators:森林中决策树的数量(树越多性能可能越好,但计算成本越高)
- criterion:决策树分裂的评价标准
"gini"
(基尼指数)"entropy"
(信息熵)
- max_depth:单棵树的最大深度(控制过拟合,
None
表示不限制)
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScalerx,y = load_iris(return_X_y=True)
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=0)# 数据预处理
scanler = StandardScaler()
s_train = scanler.fit_transform(x_train)
s_test = scanler.transform(x_test)# n_estimators: 树的数量
# max_depth: 树的最大深度
model = RandomForestClassifier(n_estimators=100, max_depth=15, random_state=0)# 训练模型
model.fit(s_train, y_train)# 模型评估
print(model.score(s_test, y_test))# 预测
y_predict = model.predict([[1.3,2.5,1.1,0.5]])
print(y_predict)
优缺点:
- 优点:
- 高准确率: 通常比单棵决策树或其他单一模型表现更好。
- 鲁棒性强: 对噪声和异常值不太敏感。
- 不易过拟合: 双重随机性有效降低了方差。
- 能处理高维特征: 特征随机性使其在高维数据中表现良好。
- 提供特征重要性: 可以评估特征对预测的贡献程度。
- 内置OOB评估: 方便进行模型验证。
- 缺点:
- 模型解释性差: 相比单棵决策树,随机森林是一个“黑盒”模型,难以直观理解预测逻辑。
- 训练和预测速度较慢: 需要构建和存储多棵树,预测时需要遍历所有树。
- 内存消耗较大: 存储多棵树需要更多内存。
- 参数更多: 需要调优的参数比单棵决策树多(如
n_estimators
,max_features
)。