为什么说线性模型是深度学习的基石?从 Scikit-Learn 谈起

为什么说线性模型是深度学习的基石?从 Scikit-Learn 谈起

很多刚接触深度学习的人,容易陷入一个误区。大家往往一上来就去研究复杂的网络结构,觉得基础的线性模型太简单,甚至有点过时。但如果你去拆解那些庞大的网络,你会发现,它们最后输出结果的那一层,往往还是最基础的线性模型。不理解这些基础,就很难真正看懂深度学习。这篇文章我们就从大家最熟悉的 Scikit-Learn 库开始,把线性模型这条线彻底理顺。

预测连续值,从房价预测说起

假设我们要预测房价。影响房价的因素有很多,比如面积、房间数、到地铁的距离。线性模型的做法非常直白,给每个因素分配一个权重,然后相乘相加,最后加上一个基础底价。这就是线性回归的全部秘密。

理论上,只要特征和权重没有限制,线性回归的输出范围就可以是从负无穷到正无穷。Scikit-Learn 实现这个过程只需要几行代码。假设数据已经划分成训练集和测试集。

from sklearn.linear_model import LinearRegression

# 假设 X 是房屋特征,y 是房价
model = LinearRegression()
model.fit(X_train, y_train)
predictions = model.predict(X_test)

这看起来很简单,但在实际工程中,我们很容易遇到一个大坑,那就是过拟合。如果你的数据里不仅有面积,还有“前房主养的宠物数量”这种毫无关联的特征,模型为了在训练集上拿高分,可能会强行给宠物特征分配一个权重。这导致模型“死记硬背”了训练数据,在预测新房时一塌糊涂。

为了解决这个问题,我们需要给模型戴上紧箍咒,也就是正则化。常见的紧箍咒有两种。

L2 正则化在 Scikit-Learn 里对应 Ridge 模型。它的核心思想是惩罚过大的权重,让所有权重整体变小,避免模型过度依赖某一个特征。

L1 正则化对应 Lasso 模型。它的做法更狠。当正则化强度足够大时,L1 会把那些没什么用的特征权重直接压缩成零。这相当于模型在训练的同时,自动帮你做了一次特征筛选。

预测二分类,给线性模型装上开关

刚才我们预测了房价,但现实中更多的问题是分类。比如预测明天会不会下雨,或者一封邮件是不是垃圾邮件。结果只有两种,要么下雨,要么不下。

这时候线性回归就行不通了。因为我们需要的是一个概率,一个必须在 0 到 1 之间的数字。

为了解决这个问题,我们需要给线性模型装上一个转换器,这就是 Sigmoid 函数。

你可以把 Sigmoid 想象成一个数值压缩机,它把无界的实数映射到 0 和 1 之间。不管线性模型算出来的结果是一个极大的正数,还是一个极小的负数,只要经过 Sigmoid 处理,最终都会被挤压到 0 和 1 之间。如果输出是 0.8,我们就直觉地认为,明天有 80% 的概率会下雨。

Scikit-Learn 把它封装成了 LogisticRegression。

from sklearn.linear_model import LogisticRegression

# 假设 X 是天气特征,y 是是否下雨
clf = LogisticRegression()
clf.fit(X_train, y_train)

# 预测类别
label = clf.predict(X_test)
# 预测概率
probability = clf.predict_proba(X_test)

拿到概率后,模型通常会以 0.5 为界限,大于 0.5 就判定为下雨,小于 0.5 就判定为不下雨。这个阈值并不是死的,实际业务中可以根据需求调整。假设正类是欺诈,在欺诈检测中就可以调低阈值,宁可误报也不漏报。

逻辑回归虽然名字里带着回归,但它其实是一个分类模型。它的本质,就是先做了一个普通的线性加权计算,然后在最后一步套上了一个 Sigmoid 函数。

这种“先线性加权,再套一个非线性函数”的结构,就是后来神经网络里最基本的一个神经元。

多分类的常用方案,为什么深度学习偏爱 Softmax

现实世界的问题往往比二分类更复杂。比如我们要识别一张图片里的动物,答案可能是猫、狗、鸟、马等十种当中的一种。

面对多分类问题,传统的 Scikit-Learn 机器学习模型通常会采用两种策略。

OvR 的思路是,每次拿一个类别出来,和其他所有类别比。比如识别十种动物,就要训练十个模型,分别计算“属于猫”和“不属于猫”的置信度,最后取置信度最高的类别。

OvO 则更碎,每次拿两个类别单挑。猫和狗比一次,猫和鸟比一次,十种动物就要训练几十个小模型,最后靠投票决定结果。

这两种策略不仅是某些算法的默认设计,比如 Scikit-Learn 中 LinearSVC 默认采用一对多训练,SVC 内部默认采用一对一训练,在特定场景下依然有效。但在深度学习里,我们通常不这么干。

在单标签多分类任务中,深度学习采用了一种更优雅的标配方案,也就是 Softmax。

如果说 Sigmoid 是把一个数字压缩成概率,那么 Softmax 就是把一组数字变成一张完整的概率分布。你可以把它想象成一场按指数规则放大的分蛋糕游戏。不管模型原始算出来的分数是多少,经过 Softmax 处理后,十个类别的概率加起来必须等于一。指数运算会拉大分数差距,让原本得分高的类别占据绝大部分概率,得分低的类别几乎分不到蛋糕。

在深度学习的代码里,我们不需要像 Scikit-Learn 那样去拆分模型。我们只需要在线性模型的最后一层,设置十个输出节点,然后接上一个 Softmax 函数,模型就能端到端地直接输出十个类别的概率。

从 Scikit-Learn 到深度学习,基石到底体现在哪

很多人可能会问,既然线性模型这么简单,为什么我们说它是深度学习的基石?

其实,当你打开 PyTorch 或 TensorFlow,构建一个神经网络时,最基础的组件全连接层,它的核心就是一个线性变换。它做的事情,依然是特征乘以权重,再加上偏置。

无论前面的网络有多深,用了卷积还是注意力机制,最终要把提取到的特征映射到具体的分类结果时,靠的依然是最后一层线性计算,再加上我们前面提到的 Sigmoid 或 Softmax。

不仅如此,深度学习里最核心的训练过程反向传播,不管中间经过了多少层复杂的激活函数,线性层的权重和偏置依然是反向传播重点更新的参数。

实战避坑指南,线性模型没那么简单

理论看起来很完美,但在实际跑代码时,线性模型有几个非常容易翻车的地方。这里分享三个新手最常踩的坑。

第一个坑,忘记做特征缩放。
在使用梯度下降优化时,特征尺度差异会导致收敛变慢、权重更新不均衡。假设你的特征里,房屋面积是几十平方米,而房间数只有几个。如果不做处理,模型在训练时就会觉得面积更重要,从而带偏了权重。
在 Scikit-Learn 里,一定要配合 StandardScaler 使用,把所有特征拉到同一个起跑线上。

from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

注意测试集只能用 transform,不能重新 fit,否则会引入训练集之外的信息,造成数据泄露。

第二个坑,遇到类别不平衡数据。
假设你要预测信用卡欺诈,一万条数据里只有十条是欺诈。如果模型偷懒,把所有数据都预测为正常,它也能拿到百分之九十九的准确率。这在业务上是完全无效的。
在使用 LogisticRegression 时,记得加上 class_weight 参数,让模型强制关注少数类。除了调整权重,实际业务中还可以结合过采样或调整分类阈值来优化。

clf = LogisticRegression(class_weight='balanced')

第三个坑,误解正则化参数。
在 Scikit-Learn 的逻辑回归里,正则化强度通常由参数 C 控制。这里有一个反直觉的设计:C 其实是正则化强度的倒数。所以 C 的值越大,代表正则化越弱,模型越容易过拟合;C 的值越小,正则化才越强。调参时如果不小心,很容易把方向搞反。

今天我们走完了线性模型的进化之路。从预测连续值的线性回归,到加上 Sigmoid 的逻辑回归,再到处理多分类的 Softmax。无论网络结构怎么变,核心始终是线性计算与概率映射。掌握了这些,你就有了一张继续深入神经网络的地图。