线性模型详解
线性模型是机器学习的基石:理解它,你就掌握了「模型如何从数据中学习参数」的核心逻辑。这篇从数学直觉出发,把线性回归和逻辑回归彻底讲清楚,并展示正则化如何防止过拟合。
线性回归:从直线拟合开始
问题:已知一批房屋的面积和价格数据,预测新房子的价格。
最朴素的假设:价格和面积之间是线性关系。
预测价格 ŷ = w₁×面积 + w₀其中 w₁ 是斜率(权重),w₀ 是截距(偏置)。多个特征时变成:
ŷ = w₁x₁ + w₂x₂ + ... + wₙxₙ + b损失函数:均方误差
「怎么知道这条直线画得好不好?」用**均方误差(MSE)**衡量:
MSE = (1/m) × Σ(ŷᵢ - yᵢ)²MSE 对每条样本的误差平方后求平均——平方有两个好处:让正负误差都变正数,并对大误差给予更大惩罚。
训练目标:找到一组 w 和 b,使 MSE 最小。
梯度下降:如何调参
想象你站在山上,闭眼找最低点的方法:每步都往坡最陡的方向(负梯度方向)迈一小步。
import numpy as np
def gradient_descent_demo(X, y, lr=0.01, epochs=1000):
m, n = X.shape
w = np.zeros(n)
b = 0.0
for epoch in range(epochs):
# 前向传播
y_pred = X @ w + b
# 计算梯度
error = y_pred - y
dw = (2/m) * X.T @ error
db = (2/m) * np.sum(error)
# 更新参数(沿负梯度方向)
w -= lr * dw
b -= lr * db
if epoch % 100 == 0:
mse = np.mean(error**2)
print(f"Epoch {epoch}: MSE = {mse:.4f}")
return w, b正规方程:一步到位的解析解
对于线性回归,存在直接求出最优参数的公式(正规方程):
w = (XᵀX)⁻¹ Xᵀy# 用正规方程求解,无需迭代
def normal_equation(X, y):
return np.linalg.pinv(X.T @ X) @ X.T @ y缺点:当特征数量很大时,矩阵求逆计算量为 O(n³),会非常慢。实践中特征超过几千时,梯度下降更划算。
sklearn 实战
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, r2_score
import numpy as np
# 生成示例数据
np.random.seed(42)
X = np.random.randn(200, 3)
y = 3*X[:,0] - 2*X[:,1] + 1.5*X[:,2] + np.random.randn(200)*0.5
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
model = LinearRegression()
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
print(f"RMSE: {mean_squared_error(y_test, y_pred)**0.5:.4f}")
print(f"R²: {r2_score(y_test, y_pred):.4f}") # R²=1 表示完美预测
print(f"权重: {model.coef_}") # 应该接近 [3, -2, 1.5]R² 系数(决定系数):0 到 1 之间,越接近 1 表示模型解释了越多的数据方差,比 RMSE 更直观。
逻辑回归:用「回归」做分类
名字带「回归」,但它是分类模型。核心思路:在线性回归的输出上套一个 Sigmoid 函数,把任意实数压缩到 (0, 1) 之间,解释为「属于正类的概率」。
z = w₁x₁ + w₂x₂ + ... + b (线性组合,和线性回归一样)
ŷ = sigmoid(z) = 1 / (1 + e⁻ᶻ) (把 z 压缩到 0~1)最终:ŷ ≥ 0.5 判为正类,ŷ < 0.5 判为负类(阈值可调)。
损失函数:交叉熵
分类不能用 MSE(梯度消失严重),用二元交叉熵:
Loss = -[y·log(ŷ) + (1-y)·log(1-ŷ)]直觉:真实标签是 1 时,惩罚预测概率低;真实标签是 0 时,惩罚预测概率高。
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report, confusion_matrix
from sklearn.datasets import load_breast_cancer
X, y = load_breast_cancer(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 标准化(逻辑回归对特征尺度敏感)
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)
model = LogisticRegression(max_iter=1000)
model.fit(X_train, y_train)
print(classification_report(y_test, model.predict(X_test)))
# 查看概率输出(不只是 0/1 标签)
proba = model.predict_proba(X_test)[:5]
print("前5条样本的类别概率:\n", proba)多分类:One-vs-Rest
逻辑回归本身是二分类,多分类(K 类)用 One-vs-Rest:训练 K 个二分类器,每个「当前类 vs 其他所有类」,预测时选置信度最高的类。
model = LogisticRegression(multi_class="ovr", max_iter=1000)
# sklearn 默认已经处理好多分类,直接用即可正则化:防止过拟合的数学手段
当特征很多时,模型可能会给噪声特征赋予很大权重,导致过拟合。正则化的思路是:在损失函数里加一个惩罚项,让权重保持「小而简洁」。
L2 正则(Ridge)
Loss_Ridge = MSE + λ × Σwᵢ²加上权重的平方和作为惩罚。λ(正则化强度)越大,权重被压得越小(但不会变 0)。
效果:缓解多重共线性,适合大多数场景。
L1 正则(Lasso)
Loss_Lasso = MSE + λ × Σ|wᵢ|加上权重的绝对值之和。
效果:倾向把不重要特征的权重压成精确的 0,等效做了特征选择。当你怀疑有大量无关特征时首选。
弹性网络(Elastic Net)
L1 + L2 的线性组合,两者兼顾:
from sklearn.linear_model import Ridge, Lasso, ElasticNet
# Ridge:L2 正则,alpha 即 λ
ridge = Ridge(alpha=1.0)
ridge.fit(X_train, y_train)
# Lasso:L1 正则,能把无关特征系数压为 0
lasso = Lasso(alpha=0.1)
lasso.fit(X_train, y_train)
print("非零特征数:", (lasso.coef_ != 0).sum())
# ElasticNet:同时用 L1 + L2
elastic = ElasticNet(alpha=0.1, l1_ratio=0.5)
elastic.fit(X_train, y_train)RidgeCV、LassoCV)自动搜索。四个模型的对比
| 模型 | 任务 | 特点 | 适用场景 |
|---|---|---|---|
| 线性回归 | 回归 | 无正则,基础 | 快速基线 |
| Ridge | 回归 | L2 正则,抗共线性 | 特征相关性高 |
| Lasso | 回归 | L1 正则,自动选特征 | 高维稀疏特征 |
| 逻辑回归 | 分类 | 输出概率,可解释 | 二分类基线 |
一句话小结
线性模型 = 线性变换 + 损失函数 + 梯度下降。逻辑回归加了 Sigmoid 和交叉熵;正则化在损失上加了一项「权重惩罚」。这套框架是后续所有模型(神经网络等)的基础原型。