在职前端Leader学习/转行 AI Agent -DAY56

0 阅读4分钟

2026.09.16 11:16

学习路线

Python -> Python数据分析 -> LangChain -> LangGraph -> FastAPI + SQLAIchemy -> 机器学习 -> 神经网络 -> NLP -> Coze -> Dify -> 大模型应用基础 -> 大模型微调 -> 多模态 -> vibeCoding -> Hermes -> Docker

复习巩固总结

1. 随机梯度下降 SGD(Stochastic Gradient Descent)

每次迭代随机抽 1 个样本,只用这一条的梯度更新:

θjθjα(hθ(x(i))y(i))xj(i)\theta_j\leftarrow\theta_j-\alpha\bigl(h_\theta(x^{(i)})-y^{(i)}\bigr)\,x^{(i)}_j

ii 是这一步抽到的那个人。简单、高效,但不稳定:每步只看一条,碰上噪声样本容易被带偏

"""
案例:
    演示:随机梯度下降法 线性回归对象 完成 波士顿房价预测案例
"""
# 导包
from sklearn.preprocessing import StandardScaler        # 特征处理
from sklearn.model_selection import train_test_split    # 数据集划分
from sklearn.linear_model import LinearRegression       # 正规方程的回归模型
from sklearn.linear_model import SGDRegressor           # 梯度下降的回归模型
from sklearn.metrics import mean_absolute_error, mean_squared_error, root_mean_squared_error          # 均方误差评估
from sklearn.linear_model import Ridge, RidgeCV         # 岭回归
import pandas as pd                                     # 数据处理
import numpy as np                                      # 数组处理

# 1. 加载波士顿房价数据
data_url = "https://raw.githubusercontent.com/Csengupta1101/Datasets/main/BostonHousing.csv"
raw_df = pd.read_csv(data_url, storage_options={"User-Agent": "Mozilla/5.0"})
data = raw_df.drop(columns=["medv"]).values   # 前 13 列是特征
target = raw_df["medv"].values                # 最后一列房价是标签

# 2. 数据的预处理,按照 8:2 切分 训练集和测试集
x_train, x_test, y_train, y_test = train_test_split(data, target, test_size=0.2, random_state=23)

# 3. 特征工程(特征提取,特征预处理...)
# 3.1 创建标准化对象
transfer = StandardScaler()
# 3.2 对训练集进行标准化
x_train = transfer.fit_transform(x_train)
# 3.3 对测试集进行标准化
x_test = transfer.transform(x_test)

# 4. 模型训练
# estimator = LinearRegression(fit_intercept=True)  # 正规方程法 线性回归对象

# 4.1 创建 线性回归 随机梯度下降 模型对象
# 参1:fit_intercept:是否计算截距(Bias,偏置),默认是True
# 参2:learning_rate:学习率模式 -> 常量,即:不会发生改变
# 参3:eta0:学习率
estimator = SGDRegressor(fit_intercept=True, learning_rate="constant", eta0=0.01)
# 4.2 模型训练
estimator.fit(x_train, y_train)
# 4.3 打印模型计算出的 w(权重,weight)和 b(偏置,bias)
print(f'权重:{estimator.coef_}')
print(f'偏置:{estimator.intercept_}')

# 5. 模型预测
y_pre = estimator.predict(x_test)

# 6. 模型评估
# MSE: 均方误差,每个样本的误差平方和 / 样本总数
print(f'均方误差:{mean_squared_error(y_test, y_pre)}')            # 参1:测试集的真实标签,参2:测试集的预测标签

# RMSE: 均方根误差,均方误差的平方根
print(f'均方根误差:{root_mean_squared_error(y_test, y_pre)}')     # 参1:测试集的真实标签,参2:测试集的预测标签

# MAE: 平均绝对误差,每个样本的误差绝对值和 / 样本总数
print(f'平均绝对误差:{mean_absolute_error(y_test, y_pre)}')       # 参1:测试集的真实标签,参2:测试集的预测标签

2. 解决过拟合

  • L1 会让权重趋向 0,甚至等于 0。等于 0 的那些特征等于失效,所以 L1 能做特征筛选
  • L2 会让权重趋向 0,一般不等于 0(减弱影响,不删特征)。

2.1 L1正则化

J(w)=MSE(w)+αi=1nwiJ(w)=\mathrm{MSE}(w)+\alpha\sum_{i=1}^{n}\lvert w_i\rvert

α\alpha惩罚系数(sklearn 里叫 alpha)。越大,对特征权重罚得越狠,权重被往 0 拉的幅度越大。

使用 L1 的线性回归叫 Lasso

from sklearn.linear_model import Lasso
# 4. 定义函数,模拟:L1正则化
def dm04_l1_regularization():
    # ...

    # 4. 训练模型
    # 4.1 创建模型对象
    # estimator = LinearRegression()    # 正规方程 线性回归模型
    # 改为创建 L1正则化对象
    estimator = Lasso(alpha=0.1)  # alpha:正则化系数(惩罚系数),默认是1

    # ...

2.2 L2正则化

J(w)=MSE(w)+αi=1nwi2J(w)=\mathrm{MSE}(w)+\alpha\sum_{i=1}^{n}w_i^2

α\alpha:同样是惩罚系数。越大,对特征权重罚得越狠。

使用 L2 的线性回归叫岭回归

from sklearn.linear_model import Ridge
# 5. 定义函数,模拟:L2正则化
def dm05_l2_regularization():
    # ...

    # 4. 训练模型
    # 4.1 创建模型对象
    # estimator = Lasso(alpha=0.1)  # alpha:正则化系数(惩罚系数),默认是1
    # 改为创建 L2正则化对象
    # estimator = Ridge(alpha=0.1)
    estimator = Ridge(alpha=10)

    # ...

3. 概率相关知识回顾

3.1 联合概率

两个或多个事件同时发生的概率,记 P(AB)P(A\cap B)P(A,B)P(A,B)

只有在相互独立时才能拆成相乘:

P(AB)=P(A)P(B)(独立时)P(A\cap B)=P(A)\,P(B)\qquad\text{(独立时)}

课件:周一早上、周二早上都堵。若两天相互独立、每天早上堵车都是 0.70.7

P(周一早堵周二早堵)=0.7×0.7=0.49P(\text{周一早堵}\cap\text{周二早堵})=0.7\times 0.7=0.49

不独立就不能直接乘。比如「早上堵」和「中午堵」往往相关(早高峰堵了,中午更可能还堵),P(AB)P(A\cap B) 不一定等于 0.7×0.30.7\times 0.3

3.2 条件概率

条件概率:在事件 AA 已经发生的前提下,事件 BB 再发生的概率:

P(BA)=P(AB)P(A)P(B\mid A)=\frac{P(A\cap B)}{P(A)}

分母是「已知那件」的概率,分子是「两件一起发生」。乘法公式反过来说就是 P(AB)=P(A)P(BA)P(A\cap B)=P(A)\,P(B\mid A)

对照:

公式课件那组数(先假设独立)含义
联合P(AB)=P(A)P(B)P(A\cap B)=P(A)P(B)0.7×0.3=0.210.7\times 0.3=0.21早上中午都堵
条件P(BA)=P(AB)/P(A)P(B\mid A)=P(A\cap B)/P(A)0.21/0.7=0.30.21/0.7=0.3已知早上堵了,中午还堵

记法统一用 P(BA)P(B\mid A):竖线右边是已经知道的

3.3 对数函数

ab=Na^b=Na>0a>0a1a\neq 1),则 bb 叫做以 aa 为底 NN 的对数:

b=logaNb=\log_a N

例子:log10100=2\log_{10}100=2(因为 102=10010^2=100),log216=4\log_2 16=4(因为 24=162^4=16)。

三条运算性质

loga(MN)=logaM+logaNlogaMN=logaMlogaNloga(Mn)=nlogaM\begin{aligned} \log_a(MN)&=\log_a M+\log_a N\\ \log_a\frac{M}{N}&=\log_a M-\log_a N\\ \log_a(M^n)&=n\log_a M \end{aligned}

3.4 极大似然估计

核心思想:根据已经观测到的结果,反推模型里那个未知参数最像是多少。

未知参数 pp 先「产生」出数据(人、样本、对话),再用这些数据去估计 pp。正向是「参数 → 数据」,极大似然是倒过来「数据 → 参数」。

硬币例子

一枚不均匀的硬币,正面概率记 θ\theta,反面就是 1θ1-\theta。抛 6 次,现象

D={, 反, 反, 正, 正, 正}D=\{\text{正},\ \text{反},\ \text{反},\ \text{正},\ \text{正},\ \text{正}\}

4 正 2 反。每次投掷相互独立,所以整段序列的联合概率可以拆成相乘

P(Dθ)=P(θ)P(θ)P(θ)P(θ)P(θ)P(θ)=θ(1θ)(1θ)θθθ=θ4(1θ)2\begin{aligned} P(D\mid\theta) &=P(\text{正}\mid\theta)\,P(\text{反}\mid\theta)\,P(\text{反}\mid\theta)\,P(\text{正}\mid\theta)\,P(\text{正}\mid\theta)\,P(\text{正}\mid\theta)\\ &=\theta\cdot(1-\theta)\cdot(1-\theta)\cdot\theta\cdot\theta\cdot\theta =\theta^4(1-\theta)^2 \end{aligned}

P(Dθ)P(D\mid\theta) 是「参数取 θ\theta 时,碰巧看到这份数据的概率」。θ\theta 未知,这份 DD 已经发生了,于是把上式看成 θ\theta 的函数,叫似然函数

L(θ)=θ4(1θ)2L(\theta)=\theta^4(1-\theta)^2

问题变成:哪个 θ\thetaL(θ)L(\theta) 最大?——最能「解释」这 6 次结果的那个正面概率。

LL 求导,令导数为 0:

L(θ)=θ3(1θ)(46θ)\begin{aligned} L'(\theta)&=\theta^3(1-\theta)(4-6\theta) \end{aligned}

L(θ)=0L'(\theta)=0 给出三个根:

θ1=0,θ2=1,θ3=46=23\theta_1=0,\qquad \theta_2=1,\qquad \theta_3=\frac{4}{6}=\frac{2}{3}

θ=0\theta=011 时,L=0L=0(数据里正、反都出现过,说「永远正面」或「永远反面」完全解释不了 DD)。真正的极大值在

θ^=23\hat\theta=\frac{2}{3}

和直觉一致:6 次里 4 次正面,频率就是 4/64/6。伯努利试验的极大似然估计,就是样本里「成功」的比例。

似然 L(θ)=θ4(1θ)2L(\theta)=\theta^4(1-\theta)^2连乘。样本一多,0.7100000.7^{10000} 这类数会下溢成 00。对数能把乘法变加法,极大值的位置不变(log\log 单调递增),所以实际都改成最大化 logL\log L

L(θ)=θ4(1θ)2logL(θ)=4logθ+2log(1θ)L(\theta)=\theta^4(1-\theta)^2 \qquad\Rightarrow\qquad \log L(\theta)=4\log\theta+2\log(1-\theta)

连乘变成两项相加,求导不再用乘法法则硬拆:

ddθlogL=4θ21θ=04(1θ)=2θθ=23\frac{d}{d\theta}\log L=\frac{4}{\theta}-\frac{2}{1-\theta}=0 \quad\Rightarrow\quad 4(1-\theta)=2\theta \quad\Rightarrow\quad \theta=\frac{2}{3}

和直接对 LL 求导的结果一样,步骤短一截。逻辑回归里每条样本一个 σ(wTx)\sigma(w^T x)1σ(wTx)1-\sigma(w^T x),似然是 mm 项连乘,训练时优化的就是这个对数似然(再取负变成损失去最小化)。

4. 逻辑回归

逻辑回归(Logistic Regression)是一种分类模型:把线性回归的输出,当作逻辑回归的输入。模型吐出来的是 (0,1)(0,1) 之间的值(正类概率),不是房价那种连续标签。

逻辑回归 = 有监督学习,有特征、有标签,且标签是离散的(分类)= 适用于二分类。

数据 线性回归 预测值 Sigmoid 激活 概率[0,1] 阈值 A 或 B\text{数据}\ \xrightarrow{\text{线性回归}}\ \text{预测值}\ \xrightarrow{\text{Sigmoid 激活}}\ \text{概率}\in[0,1]\ \xrightarrow{\text{阈值}}\ \text{A 或 B}

Sigmoid 也叫 S 型函数,在神经网络里常当激活函数。线性回归吐出来的是任意实数,分类要的是 (0,1)(0,1) 之间的概率,中间就靠它压。

  1. 先按线性回归算出一个实数(可正可负、可很大很小)
  2. 丢进 Sigmoid,压成 (0,1)(0,1) 之间的概率
  3. 拿你写的阈值去切:比如 >0.6>0.6 判 A(正样本),否则判 B(负样本)

逻辑回归是解决二分类问题的利器:输出只有两边,是或不是

4.1 Sigmoid 函数

f(x)=11+exf(x)=\frac{1}{1+e^{-x}}

xx 这里是线性回归的预测值wTx+bw^T x+b,可正可负、可到 ±\pm\infty)。作用:把 (,+)(-\infty,+\infty) 映射到 (0,1)(0,1)

几个常用点:

f(0)=11+1=0.5,x+  f(x)1,x  f(x)0f(0)=\frac{1}{1+1}=0.5,\qquad x\to+\infty\ \Rightarrow\ f(x)\to 1,\qquad x\to-\infty\ \Rightarrow\ f(x)\to 0
  • 单调递增:线性预测越大,概率越大,不会扭回来
  • 拐点x=0, y=0.5x=0,\ y=0.5:曲线在这儿从下凸换成上凸,也是默认阈值常取 0.50.5 的原因
  • 两端饱和:离 0 很远时曲线几乎贴着 0 或 1,梯度很小

导数:

f(x)=f(x)(1f(x))f'(x)=f(x)\bigl(1-f(x)\bigr)

4.2 损失函数

线性回归用 MSE 看「差了多少」;逻辑回归输出的是概率,损失改成对数损失(二分类交叉熵)。它就是极大似然估计里似然取 log\log 再加负号:最大化「看到这些标签」的概率,等价于把这个损失压下去。

Loss(w)=i=1m[yilogpi+(1yi)log(1pi)]\operatorname{Loss}(w)=-\sum_{i=1}^{m}\Bigl[y_i\log p_i+(1-y_i)\log(1-p_i)\Bigr]
pi=sigmoid(wTx(i)+b)p_i=\operatorname{sigmoid}(w^T x^{(i)}+b)

编码:AA(正样本)y=1\to y=1BB(负样本)y=0\to y=0yy 是真实标签,预估的是 pip_i

每个样本模型都给出两个数:pp(是 A 的概率)和 1p1-p(是 B 的概率)。损失只盯真实那一类对应的那个概率:越大越好。外面的负号把「越大越好」翻成「越小越好」,才能当损失。

yy 只能是 0011,两项里永远只活一项:

真实活下来的那项含义
y=1y=1(正样本)logp-\log p真实是 A,希望 pp 靠近 11
y=0y=0(负样本)log(1p)-\log(1-p)真实是 B,希望 pp 靠近 00

例如:

特征回归预测值 zz逻辑回归 p[0,1]p\in[0,1]预测(阈值 0.60.6真实
12.3, 20.0, 1612.3,\ 20.0,\ 1682.482.40.400.40BA
9.4, 21.1, 7.29.4,\ 21.1,\ 7.289.189.10.680.68AB
34.4, 18.7, 8.134.4,\ 18.7,\ 8.180.280.20.410.41BA
10.2, 16.0, 12.510.2,\ 16.0,\ 12.581.381.30.550.55BB
5.6, 10.0, 6.35.6,\ 10.0,\ 6.390.490.40.710.71AA
iipip_iyiy_i代入这一项
10.400.40111log0.40+(11)log(10.40)1\cdot\log 0.40+(1-1)\log(1-0.40)log0.40\log 0.40
20.680.68000log0.68+(10)log(10.68)0\cdot\log 0.68+(1-0)\log(1-0.68)log0.32\log 0.32
30.410.41111log0.41+(11)log(10.41)1\cdot\log 0.41+(1-1)\log(1-0.41)log0.41\log 0.41
40.550.55000log0.55+(10)log(10.55)0\cdot\log 0.55+(1-0)\log(1-0.55)log0.45\log 0.45
50.710.71111log0.71+(11)log(10.71)1\cdot\log 0.71+(1-1)\log(1-0.71)log0.71\log 0.71

总损失是这些数求和再取负:

Loss=(log0.40+log0.32+log0.41+log0.45+log0.71)\operatorname{Loss}=-\bigl(\log 0.40+\log 0.32+\log 0.41+\log 0.45+\log 0.71\bigr)

4.3 案例_癌症预测

正类要自己对上业务,一般将类别数量少的当做正例,但不要默认少数类就是正例。

"""
案例:
    演示逻辑回归模型实现 癌症预测

逻辑回归模型介绍:
    概述:
        属于有监督学习,即:有特征,有标签,且表示离散的
        主要适用于:二分类
    原理:
        把线性回归处理后的预测值 -> 通过 Sigmoid 激活函数,映射到 (0, 1) 概率 -> 基于自定义的阈值,结合概率来分类
    损失函数:
        极大似然估计函数的 负数形式

回顾:机器学习项目流程
    1. 加载数据
    2. 数据预处理
    3. 特征工程(预处理...)
    4. 模型训练
    5. 模型预测
    6. 模型评估
"""
# 导包
from pathlib import Path
import numpy as np
import pandas as pd
from sklearn.linear_model import LogisticRegression     # 逻辑回归模型
from sklearn.preprocessing import StandardScaler        # 标准化
from sklearn.model_selection import train_test_split    # 训练集和测试集分割
from sklearn.metrics import accuracy_score              # 模型评估

# 1. 加载数据
data_path = Path(__file__).resolve().parent / 'data' / 'breast-cancer-wisconsin.csv'
data = pd.read_csv(data_path)
data.info()  # 查看数据信息

# 2. 数据预处理
# 2.1 把 ? 替换成 np.nan
# 参1:要被替换的值  参2:用来替换的值  参3:是否替换源数据,默认为False
data.replace('?', np.nan, inplace=True)
# 2.2 缺失值处理 -> 删除
# 参1:axis=0,表示删除包含缺失值的行  参2:inplace=True,表示替换源数据
data.dropna(axis=0, inplace=True)
# 2.3 打印处理后的信息
data.info()

# 3. 特征工程(提取,预处理...)
# 3.1 提取特征之提取特征和标签
x = data.iloc[:, 1:-1]
y = data.Class            # 获取最后一列
# 3.2 查看下特征 和 标签
print(x.head())
print(y.head())
print(x.shape, y.shape)
# 3.3 切割训练集和测试集
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=23)
# 3.4 特征工程:标准化
# 3.4.1 创建标准化对象
transfer = StandardScaler()
# 3.4.2 对训练集进行标准化,训练 + 标准化
x_train = transfer.fit_transform(x_train)
# 3.4.3 对测试集进行标准化,标准化
x_test = transfer.transform(x_test)

# 4. 模型训练
# 4.1 创建模型对象 -> 逻辑回归模型
estimator = LogisticRegression()
# 4.2 训练模型
estimator.fit(x_train, y_train)

# 5. 模型预测
y_pre = estimator.predict(x_test)
print(f'预测值:{y_pre}')

# 6. 模型评估
# 正确率(准确率),公式为:预测对的 / 样本总数
print(f'预测前评估,正确率:{estimator.score(x_test, y_test)}')  # 训练集的特征,标签
print(f'预测后评估,正确率:{accuracy_score(y_test, y_pre)}')    # 测试集的标签,预测值

# 思考:逻辑回归模型能用准确率来评测吗?
# 答:可以,但是结果不精确,因为逻辑回归模型主要用于二分类,即:A类还是B类,不能说 97%的A类,3%的B类
# 所以要通过混淆矩阵来评估,即:精确率,召回率,F1值(F1-score),ROC曲线,AUC值

相关知识点:

  • solver: 怎么把交叉熵压下去(优化器),不是换模型。
solver适合正则
liblinear小数据更快(坐标下降那一类)L1、L2
sag大数据更快(随机平均梯度,DAY9 的 SAG)L2,或不要正则
saga大数据,sag 的改进版L1、L2,或不要正则
  • penalty'l1''l2',和岭回归 / Lasso 同一套:L2 把权重往 0 缩,L1 能缩到正好 0。
  • C正则强弱的倒数。损失里是 Loss+1C×\operatorname{Loss} + \frac{1}{C}\times 正则项。
    • C 越大,罚得越轻,越接近不正则(容易过拟合)
    • C 越小,罚得越狠,权重越扁

4.4 混淆矩阵

意思
True真(预测对了)
False假 / 伪(预测错了)
Positive正例(模型说是正类)
Negative反例(模型说是负类)
预测值(正例)预测值(反例)
真实值(正例)真正例 TP(True Positive)伪反例 FN(False Negative)
真实值(假,反例)伪正例 FP(False Positive)真反例 TN(True Negative)
  • TP:真是正的,也预测成正的
  • FN:真是正的,却预测成负的(漏检)
  • FP:真是负的,却预测成正的(误报)
  • TN:真是负的,也预测成负的

4.5 精确率/召回率/F1-score

4.5.1 精确率 Precision

也叫查准率:在模型喊出来的正例里,有多少真是正例。癌症里恶性当正例,问的就是——报成恶性的那些人里,到底几个真是恶性。看混淆矩阵预测正例那一列(TP 和 FP),和 FN、TN 无关:

P=TPTP+FPP=\frac{\mathrm{TP}}{\mathrm{TP}+\mathrm{FP}}
4.5.2 召回率 Recall

也叫查全率:所有真实正例里,被找出来的占多少。恶性当正例时问的是——病人里有没有漏掉的,能不能把恶性都报出来。

看混淆矩阵真实正例那一行(TP 和 FN),和 FP、TN 无关:

R=TPTP+FNR=\frac{\mathrm{TP}}{\mathrm{TP}+\mathrm{FN}}
4.5.3 F1-score

精确率和召回率都要看、又经常对着干时,用一个数反映综合能力:F1。它是两者的调和平均(不是算术平均 (P+R)/2(P+R)/2):

F1=2PRP+R\mathrm{F1}=\frac{2\cdot P\cdot R}{P+R}

调和平均的脾气:有一个很低,F1 就被拖下去;两个都高,F1 才高。一边 100%、一边 0%,F1 是 0——不会出现「平均 50% 还不错」的错觉。

4.5.4 举个栗子

10 个样本:6 个恶性、4 个良性,恶性当正例。恒等式:TP+FN+FP+TN=10\mathrm{TP}+\mathrm{FN}+\mathrm{FP}+\mathrm{TN}=10。真实正例一行必须加起来是 6,真实反例一行必须是 4。

精确率=TPTP+FP,召回率=TPTP+FN,F1=2精确率召回率精确率+召回率\text{精确率}=\frac{\mathrm{TP}}{\mathrm{TP}+\mathrm{FP}},\qquad \text{召回率}=\frac{\mathrm{TP}}{\mathrm{TP}+\mathrm{FN}},\qquad \mathrm{F1}=\frac{2\cdot\text{精确率}\cdot\text{召回率}}{\text{精确率}+\text{召回率}}

模型 A:恶性对了 3 个,良性对了 4 个。

6 个恶性里只找对 3 个 → TP=3\mathrm{TP}=3,剩下 3 个漏掉 → FN=3\mathrm{FN}=3。4 个良性全对 → TN=4\mathrm{TN}=4,没有把良性说成恶性 → FP=0\mathrm{FP}=0

预测正例预测假例
真实正例(6 恶)TP =3=3FN =3=3
真实假例(4 良)FP =0=0TN =4=4
精确率=33+0=1,召回率=33+3=12,F1=21121+12=23\text{精确率}=\frac{3}{3+0}=1,\qquad \text{召回率}=\frac{3}{3+3}=\frac12,\qquad \mathrm{F1}=\frac{2\cdot 1\cdot \tfrac12}{1+\tfrac12}=\frac23

查出来的全是真恶性(精确率 1),但一半恶性漏了(召回率 0.5)。

4.5.5 代码实现
"""
案例:
    演示混淆矩阵,精确率,召回率,F1值

回顾:逻辑回归
    概述:
        属于有监督学习,即:有特征,有标签,且标签是离散的
        适用于二分类
    评估:
        精确率,召回率,F1值

混淆矩阵:
    概述:
        用来描述 真实值 和 预测值 之间关系的
    图解:
                        预测标签(正例)    预测标签(反例)
        真实标签(正例)    真正例(TP)      伪反例(FN)
        真实标签(反例)    伪正例(FP)      真反例(TN)
    单词:
        True:真
        False:假
        Positive:正例
        Negative:反例
    结论:
        1. 模型使用 分类少的 充当正例
        2. 精确率 = 真正例 在 预测正例中的占比,即:TP / (TP + FP)
        3. 召回率 = 真正例 在 真实正例中的占比,即:TP / (TP + FN)
        4. F1值 = 2 * (精确率 * 召回率) / (精确率 + 召回率)
"""
# 导包
import pandas as pd
from sklearn.metrics import confusion_matrix, precision_score, recall_score, f1_score  # 混淆矩阵, 精确率, 召回率, F1值

# 需求:已知有10个样本,6个恶性肿瘤(正例),4个良性肿瘤(反例)
# 模型A预测结果为:预测对了3个恶性肿瘤,预测对了4个良性肿瘤
# 模型B预测结果为:预测对了6个恶性肿瘤,预测对了1个良性肿瘤
# 请针对于上述的数据集,搭建混淆矩阵,分别计算模型A,模型B的 精确率,召回率,F1值

# 1. 定义变量,记录:样本数据
y_train = ['恶性', '恶性', '恶性', '恶性', '恶性', '恶性', '良性', '良性', '良性', '良性']

# 2. 定义变量,记录:模型A的预测结果
y_pre_A = ['恶性', '恶性', '恶性', '良性', '良性', '良性', '良性', '良性', '良性', '良性']

# 3. 定义变量,记录:模型B的预测结果
y_pre_B = ['恶性', '恶性', '恶性', '恶性', '恶性', '恶性', '恶性', '恶性', '恶性', '良性']

# 4. 用标签标记 正例,反例
label = ['恶性', '良性']
df_label = ['恶性(正例)', '良性(反例)']

# 5. 针对于 真实值(y_train)和 模型A的预测结果(y_pre_A),搭建混淆矩阵
cm_A = confusion_matrix(y_train, y_pre_A, labels=label)
print(f'模型A的混淆矩阵:\n{cm_A}')

# 6. 为了测试结果更好看,把上述的混淆矩阵转换成 DataFrame
df_A = pd.DataFrame(cm_A, index=df_label, columns=df_label)
print(f'模型A的混淆矩阵的 DataFrame 形式:\n{df_A}')

# 7. 针对于 真实值(y_train)和 模型B的预测结果(y_pre_B),搭建混淆矩阵
cm_B = confusion_matrix(y_train, y_pre_B, labels=label)
print(f'模型B的混淆矩阵:\n{cm_B}')

# 8. 为了测试结果更好看,把上述的混淆矩阵转换成 DataFrame
df_B = pd.DataFrame(cm_B, index=df_label, columns=df_label)
print(f'模型B的混淆矩阵的 DataFrame 形式:\n{df_B}')

# 9. 计算A模型的 精确率,召回率,F1值
# 参1:真实值  参2:预测值  参3:正例的标签
print(f'模型A的精确率:{precision_score(y_train, y_pre_A, pos_label='恶性')}')
# 参1:真实值  参2:预测值  参3:正例的标签
print(f'模型A的召回率:{recall_score(y_train, y_pre_A, pos_label='恶性')}')
# 参1:真实值  参2:预测值  参3:正例的标签
print(f'模型A的F1值:{f1_score(y_train, y_pre_A, pos_label='恶性')}')

# 10. 计算B模型的 精确率,召回率,F1值
print(f'模型B的精确率:{precision_score(y_train, y_pre_B, pos_label='恶性')}')
print(f'模型B的召回率:{recall_score(y_train, y_pre_B, pos_label='恶性')}')
print(f'模型B的F1值:{f1_score(y_train, y_pre_B, pos_label='恶性')}')

4.6 案例_电信用户流失预测

"""
案例:
    通过逻辑回归算法,针对于电信用户数据建模,进行流失预测分析
"""
# 导包
import numpy as np
import pandas as pd
from pathlib import Path

# 1. 定义函数,演示:数据的预处理
def dm01_data_preprocess():
    # pass
    # 1. 读取csv文件,获取到df对象
    data_path = Path(__file__).resolve().parent / 'data' / 'churn.csv'
    churn_df = pd.read_csv(data_path)
    # 2. 查看(处理前)数据集
    # churn_df.info()
    # print(churn_df.head())
    # 3. 因为 Churn 和 gender 列是字符串,所以需要进行one-hot编码(热编码处理)
    churn_df = pd.get_dummies(churn_df, columns=['Churn', 'gender'])
    # 4. 查看(处理后)的数据集
    # churn_df.info()
    # print(churn_df.head())
    # 5. 删除one-hot编码后,冗余的列
    # 参1:要删除的列,参2:axis=1,表示删除列,参3:inplace=True,表示直接修改原数据
    churn_df.drop(['Churn_No', 'gender_Male'], axis=1, inplace=True)
    # 6. 修改列名,将Churn_Yes -> flag,充当标签列
    churn_df.rename(columns={'Churn_Yes': 'flag'}, inplace=True)
    churn_df.info()
    print(churn_df.head())  # False -> 不流失,True -> 流失
    # 7. 查案数据值的分布
    print(churn_df['flag'].value_counts())  # False: 5174, True: 1869

# 2. 定义函数,演示:数据的可视化

# 3. 定义函数,演示:逻辑回归算法的模型训练,预测,评估

# 4. 测试
if __name__ == '__main__':
    dm01_data_preprocess()

机器学习

63. 逻辑回归_电信用户流失预测_数据可视化

# 2. 定义函数,演示:数据的可视化
def dm02_data_visualization():
    # 1. 读取csv文件,获取到df对象
    churn_df = pd.read_csv(Path(__file__).resolve().parent / 'data' / 'churn.csv')
    # 2. 对object类型的列(数据)做 one-hot 编码处理
    churn_df = pd.get_dummies(churn_df, columns=['Churn', 'gender'])
    # 3. 删除one-hot编码后,冗余的列
    churn_df.drop(['Churn_No', 'gender_Male'], axis=1, inplace=True)
    # 4. 修改列名,将Churn_Yes -> flag,充当标签列
    churn_df.rename(columns={'Churn_Yes': 'flag'}, inplace=True)
    # 6. 查看数据值的分布
    print(churn_df['flag'].value_counts())
    # 7. 查看列名,方便我们一会儿抽取特征
    """
    Index(['Partner_att', 'Dependents_att', 'landline', 'internet_att',
       'internet_other', 'StreamingTV', 'StreamingMovies', 'Contract_Month',
       'Contract_1YR', 'PaymentBank', 'PaymentCreditcard', 'PaymentElectronic',
       'MonthlyCharges', 'TotalCharges', 'flag', 'gender_Female'],
      dtype='object')
    """
    print(churn_df.columns)
    # 7. 数据的可视化,绘制计数柱状图
    # 参1:数据集;参2:x的列名(月度会员);参3:hue表示分组,根据分组进行绘制,这里是:是否流失(False -> 不流失,True -> 流失)
    sns.countplot(data=churn_df, x='Contract_Month', hue='flag')
    plt.show()

64. 模型训练预测评估

# 3. 定义函数,演示:逻辑回归算法的模型训练,预测,评估
def dm03_logistic_regression():
    # 1. 加载数据集
    churn_df = pd.read_csv(Path(__file__).resolve().parent / 'data' / 'churn.csv')

    # 2. 数据的预处理
    # 2.1 对object类型的列(数据)做 one-hot 编码处理
    churn_df = pd.get_dummies(churn_df, columns=['Churn', 'gender'])
    # 2.2 删除one-hot编码后,冗余的列
    churn_df.drop(['Churn_No', 'gender_Male'], axis=1, inplace=True)
    # 2.3 修改列名,将Churn_Yes -> flag,充当标签列
    churn_df.rename(columns={'Churn_Yes': 'flag'}, inplace=True)
    # 2.4 提取特征列和标签列
    # x的特征列:月度会员,是否有互联网服务,是否是电子支付
    x = churn_df[['Contract_Month', 'internet_other', 'PaymentElectronic']]
    y = churn_df['flag']  # False -> 不流失,True -> 流失
    # 2.5 划分训练集和测试集
    x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=23)

    # 3. 特征工程(例如:特征提取,特征预处理 -> 归一化,标准化,...),暂不处理

    # 4. 模型训练
    # 4.1 创建(逻辑回归)模型对象
    estimator = LogisticRegression()
    # 4.2 模型训练
    estimator.fit(x_train, y_train)

    # 5. 模型预测
    y_pre = estimator.predict(x_test)
    print(f"预测值:{y_pre}")

    # 6. 模型评估
    print(f"准确率:{estimator.score(x_test, y_test)}")  # 预测前 0.7679205110007097
    print(f"准确率:{accuracy_score(y_test, y_pre)}")    # 预测后 0.7679205110007097

    print(f"精确率:{precision_score(y_test, y_pre)}")   # 0.580769230769230
    print(f"召回率:{recall_score(y_test, y_pre)}")      # 0.4092140921409214
    print(f"F1值:{f1_score(y_test, y_pre)}")           # 0.48012718600953896

    # macro avg:宏平均,即:不考虑样本权重,直接求平均,适用于:数据均衡的情况
    # weighted avg:样本权重平均,即:考虑样本权重,求平均,适用于:数据不均衡的情况
    print(f"分类评估报告:\n{classification_report(y_test, y_pre)}")

拓展QA:

问:classification_report 里的 macro avg 和 weighted avg 都是怎么算的?

报告每一行先算好这一类当正例时的精确率、召回率、F1,两行平均才得到 avg。本次测试集:

precisionrecallf1-scoresupport
False(不流失)0.810.900.851040
True(流失)0.580.410.48369
合计1409

precision_score / recall_score / f1_score 默认只报 True 那一行(0.58 / 0.41 / 0.48)。

macro avg:两类各一份,简单平均,不管谁人多。 适合类别比较均衡时。

0.81+0.582=0.70,0.90+0.412=0.65,0.85+0.482=0.67\frac{0.81+0.58}{2}=0.70,\quad \frac{0.90+0.41}{2}=0.65,\quad \frac{0.85+0.48}{2}=0.67

F1 的 macro 是两个 F1 再平均,不是先平均 P、R 再套 F1 公式。

weighted avg:按 support 加权。 不均衡时多数类会把分数拉高。

0.81×1040+0.58×36914090.75\frac{0.81\times 1040+0.58\times 369}{1409}\approx 0.75
0.90×1040+0.41×36914090.77\frac{0.90\times 1040+0.41\times 369}{1409}\approx 0.77
0.85×1040+0.48×36914090.75\frac{0.85\times 1040+0.48\times 369}{1409}\approx 0.75

加权召回率一定等于准确率(这里都是 0.77)。流失是少数类,看「流失抓得好不好」盯 True 那一行或 macro,别只看 weighted。

65. 决策树_简介

65.1 大纲

ID3 / C4.5 绑在一起:都用信息熵系选特征;CART 另走 Gini(基尼值)。后面是分类案例、回归树、剪枝。

决策树简介
├── ID3 / C4.5(信息熵系)
│   ├── ID3决策树 ── 信息增益
│   └── C4.5决策树 ── 信息增益率
├── CART决策树 ── Gini,基尼值
├── 案例:泰坦尼克号生存预测
├── CART回归树
└── 决策树剪枝

65.2 生活中的决策树

决策树就是一串 if-else:内部节点是问特征,叶子是给结论。从根往下走,每条边一个取值,走到叶子就是预测。

例子:女孩相亲。先问年龄,再问长相、收入、是不是公务员,最后只剩「见」或「不见」。

年龄
├── <=30 → 长相
│   ├── 帅或中等 → 收入
│   │   ├── 高 → 见
│   │   ├── 中等 → 是否公务员
│   │   │   ├── 是 → 见
│   │   │   └── 不是 → 不见
│   │   └── 低 → 不见
│   └── 丑 → 不见
└── >30 → 不见

在树上走一遍:26 岁(≤30)→ 挺帅 → 收入中等 → 是公务员(税务局)→ 。年龄 >30、长相丑、收入低,这三处直接「不见」,后面的特征根本不问。

下表三个人走一遍:

年龄长相收入是否公务员怎么走预测值
2828≤30 → 帅 → 收入高
3030≤30 → 丑,后面不问不见
39中等39>30,后面不问不见

第二、三行收入再高、是不是公务员都没用:树在前面已经剪掉这条路了。机器学习里的决策树就是自动学出「先问谁、怎么分叉」。

65.3 树形结构和建立过程

决策树是一种树形结构。相亲那棵树对上三个名词:

名词是什么例子
内部节点某个特征上的判断年龄、长相、收入、是否公务员
分支一次判断结果的出口≤30 / >30、帅或中等 / 丑、高 / 中等 / 低、是 / 不是
叶子节点一种分类结果见、不见

从根走到叶子 = 对一条样本做完分类。

建立过程三步

  1. 特征选择:先选分类能力强的特征当根、当内部节点。相亲树把「年龄」放最上面,因为 >30 直接不见,这一刀切得干净。后面 ID3 用信息增益、C4.5 用信息增益率、CART 用基尼值,都是在比「谁分类更强」。
  2. 决策树生成:按选中的特征一层层分叉,直到叶子能给出类别。
  3. 剪枝:树太深、问题问得太细,容易把训练集噪声也学进去(过拟合)。剪掉一些枝,让树简单一点,泛化更好。

66. 信息熵简介

建树第一步要选分类能力强的特征。ID3 用的尺子是信息熵:这堆样本的标签有多乱。越乱熵越大,切一刀之后熵掉得越多,这个特征越好。

66.1 熵是什么

熵 Entropy:信息论里对随机变量不确定度的度量。

  • 熵越大:不确定度越高,平均要带的信息越多
  • 熵越小:越确定。全是同一类时熵为 00(纯)

决策树要的是:切完以后子节点尽量纯,熵尽量小。

66.2 公式

H=pilog2pi 分类占比×log2(分类占比)H=-\sum p_i\log_2 p_i \qquad\ \sum -\text{分类占比}\times\log_2(\text{分类占比})

pip_i 是第 ii 类出现的概率(占比)。log2\log_2 所以单位是 bit。全 kk 类等概率时熵最大,等于 log2k\log_2 k

有几类就加几项即可。约定:0log0=00\log 0=0(纯节点里没有的那一类不贡献熵)。

66.3 栗子:字符串 α\alpha / β\beta

把字母当类别,8 个字符算占比。

栗子 1-1 α=\alpha= ABCDEFGH,8 种各 1/81/8

H(α)=(18log218)×8=(18×(3))×8=3H(\alpha)=\Bigl(-\frac18\log_2\frac18\Bigr)\times 8 =\Bigl(-\frac18\times(-3)\Bigr)\times 8=3

栗子 1-2 β=\beta= AAAABBCD → A 四个、B 两个、C / D 各一个:

pA=12,pB=14,pC=pD=18p_A=\frac12,\quad p_B=\frac14,\quad p_C=p_D=\frac18
H(β)=(12log212)+(14log214)+(18log218)×2=121+142+1832=0.5+0.5+0.75=1.75\begin{aligned} H(\beta) &=\Bigl(-\frac12\log_2\frac12\Bigr) +\Bigl(-\frac14\log_2\frac14\Bigr) +\Bigl(-\frac18\log_2\frac18\Bigr)\times 2\\ &=\frac12\cdot 1+\frac14\cdot 2+\frac18\cdot 3\cdot 2 =0.5+0.5+0.75=1.75 \end{aligned}

α\alpha 8 种还均匀,H=3H=3β\beta 只有 4 种且 A 占一半,H=1.75H=1.75更乱的熵更大

67. 信息增益计算

熵只说「这堆有多乱」。信息增益说:用特征 AA 切一刀,乱的程度掉了多少。掉得越多,这个特征越适合当节点。ID3 就挑增益最大的那个。

67.1 概念和公式

特征 AA 对训练集 DD 的信息增益:

g(D,A)=H(D)H(DA)g(D,A)=H(D)-H(D\mid A)
信息增益=信息熵条件熵\text{信息增益}=\text{信息熵}-\text{条件熵}

H(D)H(D) 是切之前整堆标签的熵;H(DA)H(D\mid A)已知 AA 取了哪个值之后,各子集熵再按样本占比加权平均。差就是「这一刀买到的确定度」。

67.2 条件熵

AAnn 个取值,第 vv 个取值对应子集 DvD^v(里面 Dv|D^v| 条),该子集标签熵仍用 66.2

H(DA)=v=1nDvDH(Dv)H(D\mid A)=\sum_{v=1}^{n}\frac{|D^v|}{|D|}\,H(D^v)

条件熵 = 各分支样本占比 × 该分支信息熵,再相加

展开 H(Dv)H(D^v) 时别漏负号:

H(Dv)=kCkvDvlog2CkvDvH(D^v)=-\sum_{k}\frac{C^{kv}}{\lvert D^v\rvert}\log_2\frac{C^{kv}}{\lvert D^v\rvert}

CkvC^{kv}:子集 DvD^v 里第 kk 类有几条。

67.3 六个样本走一遍

特征 aa目标值
α\alphaA
α\alphaA
β\betaB
α\alphaA
β\betaB
α\alphaB

根上 3A + 3B。按 aa 劈开:α\alpha 枝 AAAB(3A 1B,4 条),β\beta 枝 BB(2B,2 条)。

        3A 3B
       /      \
     α/        \β
  3A 1B        2B

1. 信息熵 H(D)H(D) 两类各 3/6=1/23/6=1/2

H(D)=(12log212)×2=1H(D)=\Bigl(-\frac12\log_2\frac12\Bigr)\times 2=1

2. 条件熵 H(Da)H(D\mid a)

α\alpha3/43/4 是 A、1/41/4 是 B:

H(Dα)=34log23414log2140.81H(D^\alpha)=-\frac34\log_2\frac34-\frac14\log_2\frac14\approx 0.81

β\beta 枝纯 B:

H(Dβ)=22log222=0H(D^\beta)=-\frac22\log_2\frac22=0

α\alpha4/64/6β\beta2/62/6

H(Da)=46×0.81+26×0=0.54H(D\mid a)=\frac46\times 0.81+\frac26\times 0=0.54

3. 信息增益

g(D,a)=10.54=0.46g(D,a)=1-0.54=0.46

β\beta 枝已经纯了(熵 0),乱只剩 α\alpha 那 4 条里混进来的 1 个 B。增益 0.460.46 就是这一刀从「对半乱」收到的确定度。

68. ID3决策树_搭建

68.1 ID3 构建流程

  1. 算每个特征的信息增益
  2. 增益最大的那个特征把数据集拆成子集
  3. 该特征当作当前节点
  4. 子集上用剩下的特征重复 1~3

直到子集纯了、没特征了、或增益太小,就停成叶子。

68.2 论坛流失:性别 vs 活跃度

某个论坛 15 条客户,问:性别、活跃度哪个对流失影响更大(谁当根节点)。is_lost=1 流失(正样本 5 条),=0 未流失(负样本 10 条)。

uingenderact_infois_lost
10
20
31
40
50
60
71
80
91
100
110
121
131
140
150

先数格子(后面加权用):

流失 1未流失 0合计
整体51015
358
257
活跃度高066
活跃度中145
活跃度低404
68.2.1 整堆熵 H(D)H(D)
H(D)=515log25151015log210150.918H(D)=-\frac{5}{15}\log_2\frac{5}{15}-\frac{10}{15}\log_2\frac{10}{15}\approx 0.918
68.2.2 性别的信息增益

男 8 条、女 7 条:

H()=38log23858log2580.954H()=27log22757log2570.863\begin{aligned} H(\text{男})&=-\frac38\log_2\frac38-\frac58\log_2\frac58\approx 0.954\\ H(\text{女})&=-\frac27\log_2\frac27-\frac57\log_2\frac57\approx 0.863 \end{aligned}
H(D性别)=815×0.954+715×0.8630.912H(D\mid\text{性别})=\frac{8}{15}\times 0.954+\frac{7}{15}\times 0.863\approx 0.912
g(D,性别)=0.9180.9120.0065g(D,\text{性别})=0.918-0.912\approx 0.0065

性别几乎切不开:男、女两边都还是「少数流失、多数留下」。

68.2.3 活跃度的信息增益

高 6 条全是 0(纯),低 4 条全是 1(纯),只有「中」5 条里 1 流失 4 未流失:

H()=0,H()=0,H()=15log21545log2450.722H(\text{高})=0,\quad H(\text{低})=0,\quad H(\text{中})=-\frac15\log_2\frac15-\frac45\log_2\frac45\approx 0.722
H(D活跃度)=6150+5150.722+41500.241H(D\mid\text{活跃度})=\frac{6}{15}\cdot 0+\frac{5}{15}\cdot 0.722+\frac{4}{15}\cdot 0\approx 0.241
g(D,活跃度)=0.9180.2410.677g(D,\text{活跃度})=0.918-0.241\approx 0.677
68.2.4 比较、当根、长树

0.6770.00650.677\gg 0.0065活跃度对流失影响比性别大,根节点用活跃度。低 / 高已经纯,直接当叶子;只有「中」还要再问性别。

「中」这 5 条:

uingenderis_lost
20
80
100
60
71

女 3 条全是 0;男 2 条 1:1。课件右边那个空圈就是 中 → 男,不是漏画「女」。

活跃度
├── 低 → 流失 (1)          ← 4 条全是 1,纯
├── 中 → 性别              ← 1 流失 / 4 未流失,继续切
│   ├── 女 → 非流失 (0)    ← 3 条全是 0,纯
│   └── 男 → 不纯          ← uin6=0、uin7=1,没特征了
└── 高 → 非流失 (0)        ← 6 条全是 0,纯

多数类也打不成(1:1)。这就是两个特征能搭完的整棵 ID3。

68.3 练习

1. 下列关于决策树的概念描述错误的是?

  • A)决策树算法需要构建树结构
  • B)决策树上的每一个节点代表一个判断条件
  • C)决策树上的每一个叶节点代表一种分类结果
  • D)通过决策树不能明确特征的重要性程度

答案:D。

A、C 就是 65.3 原话:要搭树,叶子给类别。B 把「节点」说成判断条件——有 C 把叶节点单列时,这里的节点多半指内部节点(根、分叉),和课件「内部节点 = 特征上的判断」一致;真把叶子也算进去,B 才不严谨。D 反了:ID3 用信息增益比谁重要,68.2 活跃度增益远大于性别,就是在明确重要性。

2. 下列关于熵和信息熵的描述错误的是?

  • A)熵越大,系统的混乱程度越小
  • B)信息熵是用来描述信息的完整性和有序性的
  • C)信息的有序状态越一致、数据越集中,信息熵越小,反之越大

答案:A。

66.1:熵越大越乱、越不确定;越小越确定。A 把大小说反了。C 就是这件事:越集中越纯,熵越小。B 用「完整性」不标准,熵量的是不确定度;「有序」和 C 对得上,不是这题要抓的错。

3. 下列关于信息增益的描述正确的是?(多选)

  • A)表达的是在用某个特征对数据集进行分裂
  • B)是 ID3 算法中的核心
  • C)消除的不确定性越大,信息增益越小,表示这个特征越不重要
  • D)对类别数较多的特征比较青睐

答案:ABD。

  • A:67.1 增益就是「用特征 AA 切一刀,熵掉了多少」
  • B:67.4 / 68.1 每次拿增益最大的特征当节点
  • C 反了:消除的不确定度越大 → 增益越大 → 特征越重要
  • D:取值特别多的特征(极端:每条样本一个 ID)一切就纯,增益虚高。这是 ID3 的毛病,所以大纲里 C4.5 改用信息增益率压这个偏好

69. C4.5树_信息增益率

69.1 ID3 的不足

ID3 只看信息增益,会偏向取值种类多的特征68.3 第 3 题 D 就是这句话。极端情况:特征几乎是每条样本一个 ID,一切全纯,增益虚高,树却学不到能泛化的规则。

六个样本:

特征 bb特征 aa目标值
1α\alphaA
2α\alphaA
3β\betaB
4α\alphaA
5β\betaB
6α\alphaB

aa 只有 2 个取值(α/β\alpha/\beta),bb6 个取值(1~6,一条一个)。

aa 切:就是 67.3 那棵,α\alpha 枝 3A1B 还不纯,增益 0.460.46

bb 切:六个取值各一条,每条都纯,条件熵 00,增益 =H(D)=1=H(D)=1,比 0.460.46 大,ID3 会选 bb 当根

            根(按 b 切)
     /   /   /   \   \   \
    1   2   4    3   5   6
    └─┬─┘        └───┬───┘
    目标值 A       目标值 B

1、2、4 → A;3、5、6 → B。训练集上几乎全对。

  1. 用它分裂,会得到一棵深度为 2 的树(根 + 叶子),训练集准确率可以非常高
  2. 整棵树过于依赖这一个特征,新来一个没见过的编号就不会了 → 过拟合

C4.5 就是冲着这个毛病来的:不用裸增益,改用信息增益率,把「取值多」这件事惩罚掉。

69.2 信息增益率

信息增益率=信息增益特征熵=信息增益×1特征熵\text{信息增益率}=\frac{\text{信息增益}}{\text{特征熵}} =\text{信息增益}\times\frac{1}{\text{特征熵}}
Gain_Ratio(D,a)=Gain(D,a)IV(a)\mathrm{Gain\_Ratio}(D,a)=\frac{\mathrm{Gain}(D,a)}{\mathrm{IV}(a)}

IV(a)\mathrm{IV}(a) 叫固有值 / 特征熵 / split information:把特征 aa取值分布当随机变量,算它的熵(看的是枝有多碎,不是标签乱不乱):

IV(a)=v=1nDvDlog2DvD\mathrm{IV}(a)=-\sum_{v=1}^{n}\frac{\lvert D^v\rvert}{\lvert D\rvert}\log_2\frac{\lvert D^v\rvert}{\lvert D\rvert}

取值越多、分得越碎,IV\mathrm{IV} 越大(一条一个编号时 IV=log2n\mathrm{IV}=\log_2 n)。

惩罚系数=1IV(a)\text{惩罚系数}=\frac{1}{\mathrm{IV}(a)}

取值个数多 → IV\mathrm{IV} 大 → 1/IV1/\mathrm{IV} → 乘上去增益率被压;取值少则反过来。本质就是:增益 ÷\div 特征的内在信息,给裸增益加一个惩罚。

接到 69.1 那 6 条,数字走一遍见 69.3

69.3 六个样本走一遍

同一张表。67.3 已经算过 H(D)=1H(D)=1g(D,a)=0.46g(D,a)=0.46。这里补特征熵,再除出增益率。

Gain_Ratio(D,a)=Gain(D,a)IV(a)\mathrm{Gain\_Ratio}(D,a)=\frac{\mathrm{Gain}(D,a)}{\mathrm{IV}(a)}

IV\mathrm{IV} 看的是特征取值碎不碎,不是标签乱不乱。

特征 aa

α\alpha 4 条、β\beta 2 条。

  1. 信息增益(67.3):H(D)=1H(D)=1,条件熵 0.540.54,增益 10.54=0.461-0.54=0.46
  2. 特征熵:
IV(a)=46log24626log226=0.92\mathrm{IV}(a)=-\frac46\log_2\frac46-\frac26\log_2\frac26=0.92
  1. 增益率:0.46/0.92=0.50.46/0.92=0.5

特征 bb

1~6 各 1 条,每枝都纯,条件熵 00

  1. 信息增益:16×0=11-6\times 0=1(ID3 会因此选 bb
  2. 特征熵:六条取值均匀,就是 log26\log_2 6
IV(b)=16log216×6=2.58\mathrm{IV}(b)=-\frac16\log_2\frac16\times 6=2.58
  1. 增益率:1/2.58=0.391/2.58=0.39

0.5>0.390.5>0.39,C4.5 选 特征 aa 当分裂特征。bb 裸增益最大,但枝太碎,IV\mathrm{IV} 把增益率压下去了。

69.4 练习

1. 下列关于信息增益率的说法错误的是?

  • A. 能有效缓解信息增益所带来的弊端
  • B. 是在信息增益的基础上除以当前特征的固有值
  • C. 是 C4.5 算法中的核心
  • D. 倾向于选择取值多的特征

答案:D

  • A / B / C:69.1 / 69.2 增益率 =Gain/IV=\mathrm{Gain}/\mathrm{IV},用来压 ID3「取值多增益虚高」
  • D 说反了:那是信息增益的毛病;增益率会惩罚取值碎的特征,69.3 选的是 aa 不是 bb

2. 在机器学习中,信息增益率是用来衡量什么的?

  • A. 属性对分类的贡献
  • B. 分类的准确度
  • C. 样本的数量
  • D. 计算机存储空间的使用率

答案:A

增益率是选特征的尺子,量的是这一刀对分类有多大用,不是准确率、条数或占磁盘。