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

4 阅读3分钟

2026.09.14 10:22

学习路线

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

复习巩固总结

1. 梯度下降法

1.1 单变量

输入: 初始化位置 SS,每步距离为 aa(学习率)。输出:SS 走到山底。

  1. 令初始化位置为山的任意位置 SS
  2. 在当前位置环顾四周,如果四周都比 SS 高 → 返回 SS(已经在底);否则做第 3 步
  3. 在当前位置环顾四周,寻找坡度最陡的方向,令其为 xx 方向
  4. 沿着 xx 方向往下走,长度为 aa,到达新位置 SS'
  5. SS' 环顾四周:四周都比 SS' 高 → 返回 SS';否则回到第 3 步

aa 太大容易跨过谷底来回晃;aa 太小走得太慢。单变量时「最陡方向」就是导数的负方向(导数 >0>0 往左走,导数 <0<0 往右走)。

循环迭代:求当前点的梯度,更新当前权重。下一个点 = 当前点 − 学习率 × 损失对参数的偏导:

θi+1=θiαθiJ(θ)\theta_{i+1}=\theta_i-\alpha\frac{\partial}{\partial\theta_i}J(\theta)
  • α\alpha:学习率(步长)。不能太大也不能太小。机器学习里常见 0.0010.010.001\sim 0.01
  • 「当前点 − 学习率 × 损失函数」:减的不是 JJ 本身,是 JJθ\theta 的导数

举个例子

J(θ)=θ2J(\theta)=\theta^2,求导 J(θ)=2θJ'(\theta)=2\thetaθ=0\theta=0JJ 最小。起点 θ=1\theta=1,学习率 α=0.4\alpha=0.4。每次:

θθα(2θ)\theta\leftarrow\theta-\alpha\cdot(2\theta)
计算θ\theta
1初始化11
210.4×(2×1)=10.81-0.4\times(2\times 1)=1-0.80.20.2
30.20.4×(2×0.2)=0.20.160.2-0.4\times(2\times 0.2)=0.2-0.160.040.04
40.040.4×(2×0.04)=0.040.0320.04-0.4\times(2\times 0.04)=0.04-0.0320.0080.008
50.0080.4×(2×0.008)=0.0080.00640.008-0.4\times(2\times 0.008)=0.008-0.00640.00160.0016

NN 步时 θ\theta 已经极接近最优值 00J(θ)J(\theta) 也接近最小值。

1.2 多变量

单变量时梯度是一个数(导数);多个参数时梯度是向量:每个分量是对一个参数的偏导。更新公式不变,只是 θ\theta、梯度都变成向量。

举个栗子

J(θ)=θ12+θ22J(\theta)=\theta_1^2+\theta_2^2,求 θ1,θ2\theta_1,\theta_2 使 JJ 最小。最低点在 (0,0)(0,0)

Jθ1=2θ1,Jθ2=2θ2J=(2θ1, 2θ2)\frac{\partial J}{\partial\theta_1}=2\theta_1,\qquad \frac{\partial J}{\partial\theta_2}=2\theta_2 \qquad\Rightarrow\qquad \nabla J=(2\theta_1,\ 2\theta_2)

起点 (1,3)(1,3),学习率 α=0.1\alpha=0.1。下一步 = 当前点 - 学习率 ×\times 梯度:

(θ1,θ2)(θ1,θ2)α(2θ1, 2θ2)=(θ12αθ1, θ22αθ2)(\theta_1,\theta_2)\leftarrow(\theta_1,\theta_2)-\alpha(2\theta_1,\ 2\theta_2)=(\theta_1-2\alpha\theta_1,\ \theta_2-2\alpha\theta_2)
计算(θ1,θ2)(\theta_1,\theta_2)
起点初始化(1, 3)(1,\ 3)
1(1,3)0.1(2,6)=(10.2, 30.6)(1,3)-0.1\cdot(2,6)=(1-0.2,\ 3-0.6)(0.8, 2.4)(0.8,\ 2.4)
2(0.8,2.4)0.1(1.6,4.8)=(0.80.16, 2.40.48)(0.8,2.4)-0.1\cdot(1.6,4.8)=(0.8-0.16,\ 2.4-0.48)(0.64, 1.92)(0.64,\ 1.92)
\cdots继续减越来越靠近 (0,0)(0,0)
NNθ1,θ2\theta_1,\theta_2 已极接近最优JJ 也接近最小值

和单变量同一套路:每步按各偏导同时改所有参数,直到接近谷底。

1.3 手算 - 银行信贷案例

编号姓名x1x_1 月工资x2x_2 存款余额x3x_3 房产面积yy 授信额度
1张一6000120005530000
2张二8000100006545300
3张三7500160006046000
4赵六10000150007555500
5钱七9000210007058000
6孙八12000190008575400
7周九11000260009081000
8吴十13000320008076800

已知三件套:数据、假设函数、损失函数。权重 ww 这里写成 θ\theta

假设函数(bb 写成 θ0x0\theta_0 x_0,且 x0=1x_0=1):

hθ(x)=θ0x0+θ1x1+θ2x2+θ3x3+h_\theta(x)=\theta_0 x_0+\theta_1 x_1+\theta_2 x_2+\theta_3 x_3+\cdots

损失函数用均方误差,前面再除以 22,求导时平方带来的 22 能消掉:

J(θ)=12mi=1m(hθ(x(i))y(i))2J(\theta)=\frac{1}{2m}\sum_{i=1}^{m}\bigl(h_\theta(x^{(i)})-y^{(i)}\bigr)^2

m=8m=8hθ(x(i))h_\theta(x^{(i)}) 是第 ii 个样本的预测,y(i)y^{(i)} 是真实授信额度。

下个点 = 当前点 − 学习率 × 损失对参数的偏导:

θjθjαθjJ(θ)\theta_j\leftarrow\theta_j-\alpha\frac{\partial}{\partial\theta_j}J(\theta)

JJ 求导,拆开看第 jj 个分量(标量):

Jθj=1mi=1m(hθ(x(i))y(i))xj(i)\frac{\partial J}{\partial\theta_j} =\frac{1}{m}\sum_{i=1}^{m}\bigl(h_\theta(x^{(i)})-y^{(i)}\bigr)\,x^{(i)}_j

带入更新公式(8 个样本,在每个特征分量上求偏导再取平均):

θjθjα1mi=1m(hθ(x(i))y(i))xj(i)\theta_j\leftarrow\theta_j-\alpha\cdot\frac{1}{m}\sum_{i=1}^{m}\bigl(h_\theta(x^{(i)})-y^{(i)}\bigr)\,x^{(i)}_j

本例 j=0,1,2,3j=0,1,2,3(偏置 + 工资、存款、面积),m=8m=8j=0j=0x0(i)=1x^{(i)}_0=1,那一项就是误差本身的平均。

假设 θ0=θ1=θ2=θ3=1\theta_0=\theta_1=\theta_2=\theta_3=1α=0.001\alpha=0.001,即起始点为[1,1,1,1],只是为了能算出 hh。此时 h=1+x1+x2+x3h=1+x_1+x_2+x_3。偏置项:

(h(i)y(i))x0(i)=(1+x1+x2+x3y)1(h^{(i)}-y^{(i)})x^{(i)}_0=(1+x_1+x_2+x_3-y)\cdot 1
ii计算(hy)x0(h-y)x_0
1 张一(1+6000+12000+5530000)1(1+6000+12000+55-30000)\cdot 111944-11944
2 张二(1+8000+10000+6545300)1(1+8000+10000+65-45300)\cdot 127234-27234
3 张三(1+7500+16000+6046000)1(1+7500+16000+60-46000)\cdot 122439-22439
4 赵六同理30424-30424
5 钱七同理27929-27929
6 孙八同理44314-44314
7 周九同理43909-43909
8 吴十同理31719-31719

平均梯度(8 个数相加再除以 m=8m=8):

θ0=18i=18(h(i)y(i))x0(i)=29989\nabla\theta_0=\frac{1}{8}\sum_{i=1}^{8}(h^{(i)}-y^{(i)})x^{(i)}_0=-29989

更新偏置:

θ0θ0αθ0=10.001×(29989)=1+29.989=30.989\theta_0\leftarrow\theta_0-\alpha\nabla\theta_0=1-0.001\times(-29989)=1+29.989=30.989

同一批误差 (hy)(h-y),分别乘工资、存款、面积再平均:

θ1=1mi(h(i)y(i))x1(i)=305067937.5θ2=1mi(h(i)y(i))x2(i)=602021125θ3=1mi(h(i)y(i))x3(i)=2283290\begin{aligned} \nabla\theta_1&=\frac{1}{m}\sum_i(h^{(i)}-y^{(i)})x^{(i)}_1=-305067937.5\\ \nabla\theta_2&=\frac{1}{m}\sum_i(h^{(i)}-y^{(i)})x^{(i)}_2=-602021125\\ \nabla\theta_3&=\frac{1}{m}\sum_i(h^{(i)}-y^{(i)})x^{(i)}_3=-2283290 \end{aligned}

α=0.001\alpha=0.001,从 11 更新:

θ110.001×(305067937.5)=305068.94θ210.001×(602021125)=602022.125θ310.001×(2283290)=2284.29\begin{aligned} \theta_1&\leftarrow 1-0.001\times(-305067937.5)=305068.94\\ \theta_2&\leftarrow 1-0.001\times(-602021125)=602022.125\\ \theta_3&\leftarrow 1-0.001\times(-2283290)=2284.29 \end{aligned}

第 1 轮结束:θ=[1,1,1,1]\theta=[1,1,1,1] 变成

θ=[30.989, 305068.94, 602022.125, 2284.29]\theta=[30.989,\ 305068.94,\ 602022.125,\ 2284.29]

后面同样的步骤再迭代多轮,直到 θ\theta 稳定。工资、存款量级大,乘上误差后梯度会到上亿,一步就把 θ\theta 撑到几十万——所以线性回归里常要先做标准化 / 归一化,把各列特征拉到相近尺度。

1.4 算法分类

1.4.1 全梯度下降 FGD(Full Gradient Descent)

每次迭代用全部 mm 个样本的梯度(也叫批量梯度下降 Batch GD)。上面示例就是使用的这个,因为每次都用全部数据集,训练速度较慢,样本一多每走一步都要把全表扫一遍

θjθjα1mi=1m(hθ(x(i))y(i))xj(i)\theta_j\leftarrow\theta_j-\alpha\cdot\frac{1}{m}\sum_{i=1}^{m}\bigl(h_\theta(x^{(i)})-y^{(i)}\bigr)\,x^{(i)}_j
1.4.2 随机梯度下降 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 是这一步抽到的那个人。简单、高效,但不稳定:每步只看一条,碰上噪声样本容易被带偏

1.4.3 小批量梯度下降 mini-batch

每次从 mm 个样本里随机抽一小撮,个数记 bb

θjθjα1biB(hθ(x(i))y(i))xj(i)\theta_j\leftarrow\theta_j-\alpha\cdot\frac{1}{b}\sum_{i\in B}\bigl(h_\theta(x^{(i)})-y^{(i)}\bigr)\,x^{(i)}_j

BB 是这一批抽中的下标。课件把名字写成 mini-bantch,指的就是 mini-batch。

结合了 SGD 的「胆大」(走得快)和 FGD 的「心细」(方向相对稳),表现正好夹在两者中间。目前用得最多:既躲开了 FGD 算得慢、成本大,也躲开了纯 SGD 收敛晃。

  • b=1b=1:退化成 SGD
  • b=mb=m:退化成 FGD
1.4.4 随机平均梯度下降 SAG(Stochastic Average Gradient)

每次仍随机抽 1 个样本算新梯度,但更新时用的是:这个新梯度 + 以前抽过的样本所存梯度 的平均值(不是只看当前这一条)。

记每个样本各存一份梯度 g(i)g^{(i)}。抽到第 kk 个样本后,只重算 g(k)g^{(k)},其余格子不动,再拿列表里的均值去更新:

θθα1ng\theta\leftarrow\theta-\alpha\cdot\frac{1}{n}\sum g

比纯 SGD 稳(均值把噪声抹平),又不必像 FGD 每步扫完全部数据。标准实现会给全部 mm 个样本各留一格(没抽到过的先当 00);课件是「抽到几个就平均几个」,意思一样,只是列表慢慢变长。

训练初期往往不好、优化偏慢:一开始那些格子是 00,而 SAG 每轮更新都还掺着上一轮存下来的梯度,旧值要把新方向往回拉一把,等列表里的梯度慢慢被换新之后才顺起来。

1.4.5 对比
算法每步用几个样本优点缺点
FGD全部 mm方向准。§43 手算的就是这个数据大就慢
SGD随机 11简单、快。SGDRegressor不稳定,噪声样本会带偏
mini-batch随机 bb 个(1<b<m1<b<m快和稳折中,目前用得最多要选 bb
SAG11 个重算,用历史梯度均值更新后期接近 FGD 的稳、又省扫描初期慢(梯度先当 00,还掺旧值)

1.5 正规方程 vs 梯度下降

1.5.1 正规方程

w=(XTX)1XTyw=(X^T X)^{-1}X^T y,一次矩阵运算给出答案,不需要学习率一蹴而就

  • 适用:小数据、要算得比较准的场景(房屋那 4 行、信贷这 8 行用手都能推)
  • 缺点:运算量大;容易受噪声、特征强相关影响(课件「收到噪声」应为受到)
  • XTXX^T X 没有逆就解不了(特征线性相关等)
  • 求逆非常耗时,矩阵一大还可能内存溢出
  • 数据本身不是线性关系,这条闭式解用不了或效果差——它是给线性最小二乘准备的。非线性就要换模型;梯度下降可以继续用在别的可微损失上
1.5.2 梯度下降
  • 要选学习率 α\alpha,太大晃、太小爬
  • 要迭代:从某个起点反复走,直到 θ\theta 差不多停住
  • 特征很多也能用:不必求 (XTX)1(X^T X)^{-1},参数上亿也走得动
  • 更普适:迭代计算,适合嘈杂、大数据(课件「糟杂」即嘈杂)。各种可微的损失函数都能用;深度学习里参数动辄上亿,只能靠迭代找近似最优
1.5.3 对比
梯度下降正规方程
学习率要选 α\alpha不需要
怎么求迭代多步一次算完
特征很多 / 数据很大能用,深度学习主要靠它求逆太慢或没逆
噪声、特征相关仍能走(SGD 还会抖)容易被带偏,相关时可能没逆
适用损失各种可微损失基本是线性最小二乘
典型场景嘈杂、大数据、深度模型小数据、要精确的线性回归

2. 回归模型_评估方法

模型训完之后,还要看它在没见过的数据上预测得准不准。衡量的仍是预测值和真实值的差距,常用三个测评函数:MAE、MSE、RMSE。

2.1 平均绝对误差 MAE(Mean Absolute Error)

每个误差先取绝对值再平均,单位和 yy 一样,大误差不会被平方放大:

MAE=1ni=1nyiy^i\operatorname{MAE}=\frac{1}{n}\sum_{i=1}^{n}\lvert y_i-\hat y_i\rvert

2.2 均方误差 MSE(Mean Squared Error)

每个误差先平方再平均。大偏差会被放大,训练线性回归时最常用它当损失:

MSE=1ni=1n(yiy^i)2\operatorname{MSE}=\frac{1}{n}\sum_{i=1}^{n}(y_i-\hat y_i)^2

2.3 均方根误差 RMSE(Root Mean Squared Error)

MSE 再开平方根,就是 RMSE:

RMSE=1ni=1n(yiy^i)2=MSE\operatorname{RMSE}=\sqrt{\frac{1}{n}\sum_{i=1}^{n}(y_i-\hat y_i)^2}=\sqrt{\operatorname{MSE}}

RMSE 是 MSE 的平方根,某些情况下比 MSE 更有用:单位变回和 yy 相同(体重是 kg 就还是 kg,不会是 kg²),数字也更好跟原始标签对上看差了多少。

公式要点单位特点
MAE绝对误差的平均yy 相同不容易被个别离谱点拉爆
MSE平方误差的平均yy 的平方惩罚大误差,训练常用
RMSEMSE\sqrt{\text{MSE}}yy 相同评估时比 MSE 好看懂,但对异常点更敏感

2.4 三种指标对比

例子:真实点来自 y=2x+5+ey=2x+5+eee 是噪声),用直线 y^=2x+5\hat y=2x+5 去拟合。点会在直线上下乱跳,算出来大约:

MAE3.73,MSE22.53,RMSE4.75\operatorname{MAE}\approx 3.73,\quad \operatorname{MSE}\approx 22.53,\quad \operatorname{RMSE}\approx 4.75

22.534.75\sqrt{22.53}\approx 4.75,RMSE 就是 MSE 开方。MAE 和 RMSE 数值接近,都说明整体误差不大;MAE 或 RMSE 越小,误差越小

RMSE 公式里有平方,大误差会被放大,所以大多数情况下 RMSE>MAE\operatorname{RMSE}>\operatorname{MAE}。两个误差分别是 1133 时:

MAE=1+32=2,RMSE=12+322=102=52.236\operatorname{MAE}=\frac{1+3}{2}=2,\qquad \operatorname{RMSE}=\sqrt{\frac{1^2+3^2}{2}}=\sqrt{\frac{10}{2}}=\sqrt{5}\approx 2.236

RMSE 会放大「错得比较狠」的那些样本;MAE 只是绝对误差的平均,每个点一视同仁。

结论: MAE、RMSE 都能反映真实误差(课件「反应」即反映),但 RMSE 对异常点更敏感。数据里有个别离谱点时,RMSE 会明显被拉高,MAE 动得少一些。

2.5 再对比:误差被放大之后

还是橙色直线 y^=2x+5\hat y=2x+5,但真实点改成在直线上再叠一层越来越大的波动(y=y+sin(x)exp(x/20)+ey=y+\sin(x)\cdot\exp(x/20)+e,意思是):

y=2x+5+sin(x)ex/20+ey=2x+5+\sin(x)\cdot e^{x/20}+e

ex/20e^{x/20}xx 变大,后面的点会离直线越来越远。算出来大约:

MAE19.14,MSE1147.83,RMSE33.88\operatorname{MAE}\approx 19.14,\quad \operatorname{MSE}\approx 1147.83,\quad \operatorname{RMSE}\approx 33.88

和上一张(MAE≈3.73,RMSE≈4.75)比,三个数都变大了。RMSE 几乎是 MAE 的两倍(33.88/19.141.7733.88/19.14\approx 1.77),因为后半段那些飞出去的点被平方放大了。

MAE 和 RMSE 都能反映真实误差,但不能说 RMSE 更好,也不该只盯着 RMSE 把误差压下去:

  • 若训练时只把 RMSE 压得很低,说明模型把异常点(噪声)也拟合得很紧
  • 这样很容易过拟合
  • 所以评估要几个指标一起看,不要只信一个数

3. 线性回归

利用回归方程(函数)对一个或多个自变量(特征值)和因变量(目标值)之间关系进行建模的一种分析方式。

类型公式说明例子
一元线性回归y=wx+by = wx + b目标值只与一个特征有关身高 xx → 体重 yy
多元线性回归y=w1x1+w2x2+w3x3++by = w_1x_1 + w_2x_2 + w_3x_3 + \cdots + b目标值与多个特征有关房价
名称公式含义
最小二乘J(w,b)=i=1m(h(x(i))y(i))2J(w,b)=\sum_{i=1}^{m}(h(x^{(i)})-y^{(i)})^2每个样本误差的平方和
MSE 均方误差J(w,b)=1mi=1m(h(x(i))y(i))2J(w,b)=\dfrac{1}{m}\sum_{i=1}^{m}(h(x^{(i)})-y^{(i)})^2每个样本误差的平方和 / 样本总数
MAE 平均绝对误差J(w,b)=1mi=1mh(x(i))y(i)J(w,b)=\dfrac{1}{m}\sum_{i=1}^{m}\|h(x^{(i)})-y^{(i)}\|每个样本误差的绝对值和 / 样本总数

3.1 一元线性回归 - 使用正规方程LinearRegression

数学模型:Y=kX+bY = kX + b

# 导包
from sklearn.linear_model import LinearRegression

# 案例:演示线性回归API入门

# 1. 准备数据
x_train = [[160], [166], [172], [174], [180]]  # 训练集的特征
y_train = [56.3, 60.6, 65.1, 68.5, 75]         # 训练集的标签
x_test = [[176]]                               # 测试集的特征

# 2. 数据的预处理,这里不需要
# 3. 特征工程(特征提取,特征预处理),这里不需要

# 4. 模型训练
# 4.1 创建模型对象
estimator = LinearRegression()
# 4.2 具体的训练动作
estimator.fit(x_train, y_train)
# 4.3 因为是线性回归模型,我们可以查看下:斜率(w,权重),截距(b,偏置)
print(f'斜率(w,权重):{estimator.coef_},截距(b,偏置):{estimator.intercept_}')
# 0.92942177  -93.27346938775517

# 5. 模型评估
y_pre = estimator.predict(x_test)
print(f'模型预测:{y_pre}')  # 70.3047619
# 6. 模型评估
含义
fit_interceptLinearRegression 是否计算偏置 bb。默认 True;若数据已经中心化、不需要截距,可设 False
coef_拟合后的权重 ww(多个特征就是一排数)
intercept_拟合后的偏置 bb

内部用正规方程那套最小二乘一次求出 wwbb

3.2 多元线性回归 - 使用梯度下降SGDRegressor

h(w)=w1x1+w2x2+w3x3++b=wTx+bh(w) = w_1x_1 + w_2x_2 + w_3x_3 + \cdots + b = w^T x + b

正规方程法存在的问题:

  1. 运算量太大时,可能造成内存溢出XTXX^T X 要求和、求逆)
  2. 假设矩阵没有逆,则可能无解

这两种情况就改用梯度下降,波士顿房价预测示例如下:

"""
案例:
    演示:随机梯度下降法 线性回归对象 完成 波士顿房价预测案例

回顾:
    线性回归算法 属于 有监督学习之 有特征,有标签,且标签是连续的
    线性回归分类:
        一元线性回归:1个特征列,1个标签列
        多元线性回归:多个特征列,1个标签列
    线性回归大白话解释:
        它是用线性公式来描述 特征 和 标签 之间的关系的,方便做预测,公式如下:
        一元线性回归:y = w * x + b
        多元线性回归:y = w1 * x1 + w2 * x2 + ... + wn * xn + b = w的转置 * x + b
    如何衡量线性回归模型的好坏?
        思路:
            预测值和真实值之间的误差,误差越小,模型越好 => 损失函数
        具体的方案:
            1. 最小二乘法:         每个(样本)误差平方和
            2. 均方误差(MSE)      每个(样本)误差平方和 / 样本总数
            3. 均方根误差(RMSE)   每个(样本)误差平方和 / 样本总数 的 平方根
            4. 平均绝对误差(MAE)  每个(样本)误差绝对值和 / 样本总数
    如何让损失函数最小?
        思路1:梯度下降法
            全梯度下降(Full Gradient Descent,FGD)
            随机梯度下降(Stochastic Gradient Descent,SGD)
            小批量梯度下降(推荐,Mini-batch Gradient Descent,MBGD)
            随机平均梯度下降(Stochastic Average Gradient Descent,SAG)
        思路2:正规方程法
    机器学习开发流程:
        1. 加载数据
        2. 数据的预处理
        3. 特征工程(特征提取,特征预处理...)
        4. 模型训练
        5. 模型预测
        6. 模型评估
"""
# 导包
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:测试集的预测标签
参数 / 属性含义
loss损失函数。线性回归用平方损失。课件写 squared_loss,那是旧名字,现在写 squared_error
fit_intercept是否计算偏置,同上面
learning_rate学习率怎么变。'constant' 固定为 eta0;也可以随迭代变小
eta0学习率的起始值,默认 0.01
coef_ / intercept_同样是拟合后的 wwbb

在后续的深度学习中,学习率随迭代变小的一种策略叫 'invscaling'

ηt=η0t0.25\eta_t=\frac{\eta_0}{t^{0.25}}

eta = eta0 / pow(t, power_t=0.25)tt 是第几次更新,步子会越走越短,减轻后期晃。

大数据、特征很多、正规方程求不动逆时,改用这个。

4. 拟合

拟合(fitting):用在机器学习领域,用来表示模型对样本点的拟合情况。也就是模型在训练集和测试集上的表现情况。

情况训练集测试集曲线长什么样
欠拟合 under-fitting很差很差直线硬切弯数据,趋势都没学到
正好拟合 Just right平滑曲线跟着大趋势走,不追每个点
过拟合 over-fitting很好很差折线穿过几乎每个点,把噪声也记住了
  • 欠拟合产生的原因:模型过于简单
  • 过拟合产生的原因:模型太过于复杂、数据不纯、训练数据太少
  • 泛化(Generalization):模型在新数据集(非训练数据)上表现好坏的能力。具体的、个别的扩大为一般的能力。模型的拟合情况=泛化能力
  • 奥卡姆剃刀原则:给定两个具有相同泛化误差的模型,较简单的模型比较复杂的模型更可取

4.1 欠拟合

解决办法: 从数据、模型、算法三个角度想。

添加其他特征

有时欠拟合就是特征项不够,可以加别的特征项。三类常用手段:

  • 组合:把已有特征拼在一起(乘法 / 加法,见 DAY4 特征组合)
  • 泛化:把具体值做成更宽的表征
  • 相关性:把和目标相关、但还没进模型的特征加进来
# 1. 定义函数,模拟:欠拟合
def dm01_under_fitting():
    # pass
    # 1. 准备数据
    # 1.1 指定随机种子,则每次生成(噪声)的数据都是固定的
    np.random.seed(23)

    # 1.2. 随机生成x轴 100 个数据,模拟:特征
    # 参1:x轴最小值  参2:x轴最大值  参3:生成个数
    x = np.random.uniform(-3, 3, size=100)

    # 1.3. 基于x轴值,通过线性公式,生成y轴 100 个数据,模拟:标签
    # 线性公式:y = kx + b = 0.5 * x ** 2 + x + 2 + 噪声
    # 参1:平均值  参2:标准差  参3:生成个数
    y = 0.5 * x ** 2 + x + 2 + np.random.normal(0, 1, size=100)

    # 1.4 查看生成的 x轴(特征)和 y轴(标签)的数据
    # print(f'x轴(特征):{x}')
    # print(f'y轴(标签):{y}')

    # 2. 数据预处理,把x轴(特征)转成 多行1列的形式
    X = x.reshape(-1, 1)
    # print(f'处理后的特征:{X}')

    # 3. 特征工程,这里不做了,直接用100条数据,先训练,后预测

    # 4. 训练模型
    # 4.1 创建模型对象
    estimator = LinearRegression()  # 正规方程 线性回归模型
    # 4.2 模型训练
    estimator.fit(X, y)             # 参1:处理后的特征数据  参2:标签数据

    # 5. 模型预测
    y_pre = estimator.predict(X)    # 处理后的特征数据

    # 6. 模型评估
    print(f'均方误差:{mean_squared_error(y, y_pre)}')        # 参1:真实值,参2:预测值

    # 7. 绘图
    plt.scatter(x, y)                # 以散点图的形式绘制 真实值
    plt.plot(x, y_pre, color='red')  # 以线图的形式绘制 预测值
    plt.show()

4.2 正好拟合

添加多项式特征项

模型过于简单时的常用套路:线性模型加上二次项或三次项,让模型能弯、泛化能力更强,示例如下:

# 2. 定义函数,模拟:恰好拟合
# def dm01_under_fitting():
def dm02_just_fitting():
    # ...

    # 2. 数据预处理,把x轴(特征)转成 多行1列的形式
    # 2.1 把上述的x轴(特征)转成 多行1列的形式,即:[[1], [2], [3], ..., [100]]
    X = x.reshape(-1, 1)
    # print(f'处理后的特征:{X}')

    # 2.2 因为目前特征列只有1列,模型过于简单,会出现欠拟合的问题,我们增加1列 特征列,从而增加模型的复杂度
    # 即:把数据从 [[1], [2], [3], ..., [100]] => [[1, 1], [2, 4], [3, 9], ..., [100, 10000]]
    X2 = np.hstack([X, X ** 2])  # 该函数作用:横向拼接,即:拼接2个数组,拼接后数组的行数不变,拼接后数组的列数等于拼接前的列数之和

    # ...

    # 7. 绘图
    plt.scatter(x, y)                  # 以散点图的形式绘制 真实值
    # np.sort(x):对x轴(特征)进行排序,默认是:升序
    # np.argsort(x):对x轴(特征)进行排序,返回的是排序后的索引
    # 例如:排序前x轴是 [11, 33, 22] -> 对应索引:[0, 1, 2]
    # 排序后:x轴是 [11, 22, 33] -> 对应索引:[0, 2, 1]
    # plt.plot(x, y_pre, color='red')  # 以线图的形式绘制 预测值
    plt.plot(np.sort(x), y_pre[np.argsort(x)], color='red')
    plt.show() 

4.3 过拟合

解决办法:

重新清洗数据

异常点过多、数据不纯的地方再处理一遍(异常值检测、去噪声),少让模型去记脏特征。

增大训练量

原来那点数据被训练得太过了;样本变多,同样复杂度的模型更难把噪声当成规律,过拟合会缓解。

正则化

专门用来压过拟合,机器学习和深度学习里都大量用。做法如 正则化

减少特征维度,防止维灾难

特征太多、样本太少时,空间被撑得很空,学不充分,泛化差。降维(少特征)等于把模型从「太复杂」往回拉。

4.3.1 代码示例
# 3. 定义函数,模拟:过拟合
def dm03_over_fitting():
    # ...

    # 2.2 因为目前特征列只有1列,模型过于简单,为了模拟过拟合,我们增加9列 特征列,从而增加模型的复杂度
    # 即:把数据从 [[1], [2], [3], ..., [100]] => [[1, 1², 1³, ..., 1⁹], [2, 4, 8, ..., 512], [3, 9, 27, ..., 729], ..., [100, 10000, 1000000, ..., 1000000000]]
    # 该函数作用:横向拼接,即:拼接多个数组,拼接后数组的行数不变,拼接后数组的列数等于拼接前的列数之和
    # X2 = np.hstack([X, X ** 2])
    X3 = np.hstack([X, X ** 2, X ** 3, X ** 4, X ** 5, X ** 6, X ** 7, X ** 8, X ** 9])

    # ...
4.3.2 正则化

训练时有些特征会把模型搞复杂,或某个特征异常值较多。正则化就是尽量减弱这个特征的影响(甚至删掉)。

做法:在损失函数里加正则项,压住异常点把 ww 拉得过大 / 过小。分成 L1、L2 两种。

4.3.2.1 L1 正则化

在损失里加上各权重绝对值之和(L1 范数):

J(w)=MSE(w)+αi=1nwiJ(w)=\mathrm{MSE}(w)+\alpha\sum_{i=1}^{n}\lvert w_i\rvert
  • α\alpha惩罚系数(sklearn 里叫 alpha)。越大,对特征权重罚得越狠,权重被往 0 拉的幅度越大。这里的 α\alpha 不是梯度下降那节的学习率。
  • L1 会让权重趋向 0,甚至等于 0。等于 0 的那些特征等于失效,所以 L1 能做特征筛选

使用 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

    # ...
4.3.2.2 L2 正则化

在损失里加上各权重的平方和(L2 范数的平方,3.3):

J(w)=MSE(w)+αi=1nwi2J(w)=\mathrm{MSE}(w)+\alpha\sum_{i=1}^{n}w_i^2
  • α\alpha:同样是惩罚系数。越大,对特征权重罚得越狠。
  • L2 会让权重趋向 0,一般不等于 0(减弱影响,不删特征)。

图像是抛物线 y=x2y=x^2。离 0 越远斜率越陡(P1),越靠近 0 斜率越缓(P2),所以会往 0 靠,但钉不死在 0 上。

使用 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)

    # ...

机器学习

52. L1和L2正则化解释

52.6 拓展:底层实现

这里的「调参」不是网格搜索怎么选 alpha,而是:惩罚系数 α\alpha 在更新 ww 时底层到底加了什么、每一步怎么把权重往 0 拉。前面损失已经写成「MSE + 正则项」,优化时对整段 JJ 求导,梯度多出来的那一块就是正则在干活。偏置 bb 一般不加正则(sklearn 里 intercept_ 不进 L1/L2)。下面学习率仍用 η\eta,避免和惩罚系数 α\alpha 撞名。

52.6.1 L1 底层实现逻辑

损失:

J(w)=MSE(w)+αiwiJ(w)=\mathrm{MSE}(w)+\alpha\sum_i\lvert w_i\rvert

w\lvert w\rvert 的导数是符号函数(w>0w>011w<0w<01-1)。w=0w=0 处不可导,次梯度是 [1,1][-1,1];课件写成 00,意思是钉在原点时不再往两边推。梯度下降一步:

wwη(MSEw+αsign(w))w\leftarrow w-\eta\Bigl(\frac{\partial\mathrm{MSE}}{\partial w}+\alpha\cdot\operatorname{sign}(w)\Bigr)

α\alpha 越大,每步额外往 00 推的那一截 ηα\eta\alpha 越长。数据梯度(MSE 那项)如果不够大,这一截会把 ww 整段削过 0。实现上等价于先按 MSE 走半步得到 zz,再做软阈值(把绝对值小于门槛的直接打成 0):

w=sign(z)max(zηα, 0)w=\operatorname{sign}(z)\cdot\max\bigl(\lvert z\rvert-\eta\alpha,\ 0\bigr)
  • zηα\lvert z\rvert\le\eta\alphaww 变成 正好 0 → 这个特征被关掉(特征筛选)
  • z>ηα\lvert z\rvert>\eta\alpha:绝对值缩小 ηα\eta\alpha,符号不变 → 还留着,但影响变弱

sklearn 的 Lasso 常用坐标下降:一次只动一个 wiw_i,对这一维做上面的软阈值,扫完全部权重再重复,直到收敛。没有像岭回归那样一步求逆的闭式解,因为绝对值不可导、损失也不是严格光滑的碗。

α\alpha 调大:门槛 ηα\eta\alpha 升高,更多 wiw_i 被打成 0,模型更稀疏。Lasso(alpha=0.1) 就是把这个门槛设成 0.10.1 量级去拟合。

52.6.2 L2 底层实现逻辑

损失:

J(w)=MSE(w)+αiwi2J(w)=\mathrm{MSE}(w)+\alpha\sum_i w_i^2

平方项对 ww 的导数是 2αw2\alpha w(处处可导,0 处导数也是 0)。梯度下降一步:

wwη(MSEw+2αw)=(12ηα)wηMSEww\leftarrow w-\eta\Bigl(\frac{\partial\mathrm{MSE}}{\partial w}+2\alpha w\Bigr)=(1-2\eta\alpha)\,w-\eta\frac{\partial\mathrm{MSE}}{\partial w}

前面那个 (12ηα)(1-2\eta\alpha) 就是权重衰减:每步先把当前 ww 按比例缩小一点,再减去数据梯度。α\alpha 越大,缩小得越狠。因为乘的是小于 11 的正数,只能越乘越小,到不了正好 0(除非本来就是 0)。离 0 越远(ww 很大),2αw2\alpha w 越大、拉得越猛;靠近 0 时拉力变弱,所以会贴着 0,钉不死。

岭回归还有闭式解,正规方程里给 XTXX^T X 加上 αI\alpha I

w=(XTX+αI)1XTyw=(X^T X+\alpha I)^{-1}X^T y

对角加 α\alpha 之后矩阵更好求逆(特征相关、XTXX^T X 没逆时也能解),这就是 L2 既能压过拟合、又能补正规方程没逆的原因。Ridge(alpha=10)alpha=0.1 加在对角上的数更大,权重被压得更扁。

52.6.3 L1 和 L2 底层实现对比
L1(Lasso)L2(Ridge)
正则项αwi\alpha\sum\lvert w_i\rvertαwi2\alpha\sum w_i^2
ww 多出来的梯度αsign(w)\alpha\cdot\operatorname{sign}(w)(常数力)2αw2\alpha w(和 ww 成正比)
每步在干什么按固定步长往 0 削,过门槛就打成 0先按比例缩小 ww,再减数据梯度
能到 0 吗能,正好 0 → 特征筛选一般不能,只是趋近 0
闭式解没有(坐标下降 / 软阈值)有,w=(XTX+αI)1XTyw=(X^T X+\alpha I)^{-1}X^T y
α\alpha 调大更多权重变 0,更稀疏所有权重一起变小,仍都在
图像直觉V 字,拐角在 0,能卡在原点抛物线,0 处切线水平,滑不到死点

一句话:L1 用「砍一刀」调参,不重要的特征直接砍掉;L2 用「按比例缩」调参,特征都留着,只是声音变小。两种 α\alpha 都不是学习率,学率 η\eta 管步子多大,α\alpha 管往 0 拉多狠。

53. 逻辑回归大纲

线性回归的标签是连续的(房价、授信额度);逻辑回归的标签是离散的(是/否、A/B)。它仍是有监督学习:有特征、有标签,但干的是分类,而且课件这一章主要讲二分类

53.1 它是什么、怎么判

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

原理一条链:

数据 线性回归 预测值 Sigmoid 激活 概率[0,1] 阈值 A 或 B\text{数据}\ \xrightarrow{\text{线性回归}}\ \text{预测值}\ \xrightarrow{\text{Sigmoid 激活}}\ \text{概率}\in[0,1]\ \xrightarrow{\text{阈值}}\ \text{A 或 B}
  1. 先按线性回归算出一个实数(可正可负、可很大很小)
  2. 丢进 Sigmoid,压成 (0,1)(0,1) 之间的概率
  3. 拿你写的阈值去切:课件例子是 >0.6>0.6 判 A(正样本),否则判 B(负样本)

阈值不一定是 0.50.5,业务上可以自己定。概率本身是连续的,分类结果是离散的——名字里带「回归」,干的却是分类。

53.2 大纲

模块内容
逻辑回归简介应用场景、数学知识(Sigmoid、概率)
逻辑回归原理线性式 + Sigmoid + 阈值如何变成二分类
逻辑回归 API 和案例sklearn 怎么调、跑通一个小例子
分类问题评估混淆矩阵、精确率、召回率、F1-score、AUC、ROC
电信客户流失预测整套流程落到真实业务

评估里两个最常用的分数先记公式(后面展开):

精确率 Precision=TPTP+FP,召回率 Recall=TPTP+FN\text{精确率 Precision}=\frac{\mathrm{TP}}{\mathrm{TP}+\mathrm{FP}},\qquad \text{召回率 Recall}=\frac{\mathrm{TP}}{\mathrm{TP}+\mathrm{FN}}

精确率:预测成正的里面,有多少真是正的。召回率:真正的正样本里,有多少被找出来了。

53.3 混淆矩阵先认格子

四个英文拆开记:

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

精确率分母是「预测为正」那一列(TP+FP);召回率分母是「真实为正」那一行(TP+FN)。F1 是两者的调和平均,AUC / ROC 看阈值从 0 扫到 1 时整体分得开不开。

54. 逻辑回归_简介

大纲里这一块是两件事:用在哪数学上靠什么函数把线性预测变成概率

54.1 应用场景

逻辑回归是解决二分类问题的利器:输出只有两边,是或不是。课件四个例子:

场景正 / 负
预测疾病阳性 / 不是阳性
银行信贷放贷 / 不放贷
情感分析正面 / 负面
广告点击率点击 / 不点击

同类的还有:垃圾邮件与否、客户流失与否、刷卡欺诈与否。只要标签是两档离散值,就可以先拿逻辑回归试。

54.2 数学基础:Sigmoid 函数

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

54.2.1 公式与作用
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
54.2.2 数学性质
  • 单调递增:线性预测越大,概率越大,不会扭回来
  • 拐点x=0, y=0.5x=0,\ y=0.5:曲线在这儿从下凸换成上凸,也是默认阈值常取 0.50.5 的原因
  • 两端饱和:离 0 很远时曲线几乎贴着 0 或 1,梯度很小

图上看:横轴是线性预测值,纵轴是概率 [0,1][0,1]。默认阈值 0.50.5 时,f(x)>0.5f(x)>0.5正样本f(x)<0.5f(x)<0.5负样本(对应 x>0x>0 / x<0x<0)。

54.2.3 导数(后面求梯度会用)
f(x)=f(x)(1f(x))f'(x)=f(x)\bigl(1-f(x)\bigr)

求导不用再碰指数,只要当前的 f(x)f(x)f=0.5f=0.5 时导数最大(0.250.25);靠近 0 或 1 时导数接近 0——这也是两端饱和、学得慢的原因。

手推一眼:令 u=1+exu=1+e^{-x},则 f=u1f=u^{-1}

f=u2(ex)=ex(1+ex)2=11+exex1+ex=f(1f)f'=-u^{-2}\cdot(-e^{-x})=\frac{e^{-x}}{(1+e^{-x})^2}=\frac{1}{1+e^{-x}}\cdot\frac{e^{-x}}{1+e^{-x}}=f\cdot(1-f)
54.2.4 和逻辑回归怎么接上

原理还是那条链,课件强调中间那一步:

把(线性回归处理后的)值 → 通过 Sigmoid 激活函数映射到 [0,1][0,1] 之间 → 结合阈值,划分正负样本。

这就是逻辑回归在干的二分类。输出的概率可以读成条件概率:给定这组特征,属于正类的把握有多大,记 P(y=1x)P(y=1\mid x)

「A 发生的情况下,B 发生的概率,记作 P(BA)P(B\mid A)」。条件概率的定义是:

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

逻辑回归里 A 是「看到了这组特征 xx」,B 是「标签为正」,要的就是 P(x)P(\text{正}\mid x)