监督学习:回归与分类
核心:用「已知答案」的数据,反推出从特征到答案的映射。
监督学习占工业落地的 80%。这篇给出四类主力算法的心智模型、适用场景与最小代码。
一张表选算法
| 算法 | 类型 | 核心思想 | 何时首选 | 弱点 |
|---|---|---|---|---|
| 线性回归 | 回归 | 找一条直线/超平面拟合 | 需要可解释性、基线 | 只能表达线性关系 |
| 逻辑回归 | 分类 | 线性打分 + sigmoid 压到 0–1 | 二分类基线、需要概率 | 边界是线性的 |
| 决策树 | 两者 | 一系列 if-else 切分空间 | 需要规则可解释 | 极易过拟合 |
| KNN | 两者 | 看最近的 K 个邻居投票 | 小数据、无训练成本 | 预测慢、怕维度高 |
| SVM | 两者 | 找最大间隔的分界面 | 中小数据、高维稀疏 | 大数据慢、调核麻烦 |
默认起手式:先 逻辑回归 / 线性回归 做基线,再上树模型。
一、回归:预测一个连续数
线性回归
$$\hat y = w_1x_1 + w_2x_2 + \dots + b$$
python
from sklearn.linear_model import Ridge
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import make_pipeline
from sklearn.metrics import mean_absolute_error, r2_score
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
model = make_pipeline(StandardScaler(), Ridge(alpha=1.0))
model.fit(X_train, y_train)
pred = model.predict(X_test)
print(f"MAE={mean_absolute_error(y_test, pred):.3f} R²={r2_score(y_test, pred):.3f}")| 变体 | 加的正则 | 效果 |
|---|---|---|
| LinearRegression | 无 | 容易过拟合 |
| Ridge (L2) | $\lambda\sum w^2$ | 权重变小,默认选它 |
| Lasso (L1) | $\lambda\sum\lvert w\rvert$ | 部分权重变 0,可做特征选择 |
| ElasticNet | L1 + L2 | 介于两者之间 |
为什么用 pipeline
make_pipeline(StandardScaler(), model) 保证缩放只在训练集上 fit,预测时用同一套参数。手写 scaler.fit(X) 在合并数据上做,就是典型的数据泄漏。
回归任务的三个坑
| 坑 | 表现 | 处理 |
|---|---|---|
| 目标偏态 | 少数极大值主导损失 | 对 y 取 log(预测后 exp 回来) |
| 异常值 | MAE 正常但 RMSE 爆炸 | 检查并处理异常,或改用 MAE/Huber |
| 只看 R² | R²=0.95 但业务不可用 | 一定要看 MAE 的实际量级是否可接受 |
二、分类:预测一个类别
逻辑回归(名字叫回归,其实是分类)
$$p = \sigma(w^\top x + b) = \frac{1}{1+e^{-(w^\top x+b)}}$$
输出是概率,用 0.5(或业务最优阈值)判类。
python
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report, roc_auc_score
clf = make_pipeline(StandardScaler(), LogisticRegression(max_iter=1000, class_weight="balanced"))
clf.fit(X_train, y_train)
proba = clf.predict_proba(X_test)[:, 1]
print(classification_report(y_test, proba > 0.5, digits=3))
print(f"AUC={roc_auc_score(y_test, proba):.3f}")class_weight="balanced" 处理类别不平衡 —— 比上采样稳定得多。
为什么它至今仍是首选基线:训练快、输出概率、系数直接可解释(「该特征每增加 1 单位,对数几率增加 w」)。
决策树
python
from sklearn.tree import DecisionTreeClassifier, export_text
tree = DecisionTreeClassifier(max_depth=4, min_samples_leaf=20, random_state=42)
tree.fit(X_train, y_train)
print(export_text(tree, feature_names=list(X.columns), max_depth=3))它真正的价值是可解释性:能把规则打印出来给业务方看。但几乎不要在最终模型里用单棵树 —— 换成随机森林/GBDT,见 集成学习。
控制过拟合的三个旋钮:
| 参数 | 作用 | 建议 |
|---|---|---|
max_depth | 树的最大深度 | 3–10,从 4 起调 |
min_samples_leaf | 叶子最少样本数 | 20–100 |
min_samples_split | 分裂最少样本数 | 常与上一个配合 |
KNN
python
from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import StandardScaler
knn = make_pipeline(StandardScaler(), KNeighborsClassifier(n_neighbors=5, weights="distance"))
knn.fit(X_train, y_train)必须标准化,否则距离被量纲大的特征主导。适合小数据、低维、无训练时间要求(比如推荐召回)。
SVM
python
from sklearn.svm import SVC
svm = make_pipeline(StandardScaler(), SVC(kernel="rbf", C=1.0, gamma="scale", probability=True))在中小规模、高维稀疏(文本 TF-IDF)场景依然很强。数据超 10 万行就换树模型。
三、特征工程:收益最大的一步
| 类型 | 手法 | 说明 |
|---|---|---|
| 缺失 | 中位数/众数/「未知」类别 | 见 L1 清洗 |
| 数值缩放 | StandardScaler / MinMax | 线性模型、KNN、SVM、神经网络必须;树模型不需要 |
| 类别编码 | One-Hot(低基数);目标编码(高基数) | 目标编码必须用 CV 内均值,否则泄漏 |
| 偏态 | log1p / Box-Cox | 房价、收入这类长尾特征 |
| 派生特征 | 比率、差值、时间间隔、聚合统计 | 收益往往最大 |
| 分箱 | 等频分箱 + One-Hot | 捕捉非线性、抗异常 |
| 特征选择 | 方差过滤、互信息、L1、树的重要性 | 降维、降过拟合 |
派生特征的例子(比调参有用得多):
python
df["price_per_sqm"] = df["price"] / df["area"]
df["days_since_signup"] = (df["order_date"] - df["signup_date"]).dt.days
df["avg_30d"] = df.groupby("user_id")["amount"].transform(
lambda s: s.rolling("30D").mean())四、训练循环:不要一次上大模型
推荐的自上而下顺序:
| 轮次 | 做什么 | 目的 |
|---|---|---|
| 0 | 常数基线(预测众数/均值) | 建立下限,防止后面自欺 |
| 1 | 逻辑/线性回归 | 建立可解释基线 |
| 2 | 随机森林 | 看非线性是否带来收益 |
| 3 | LightGBM / XGBoost | 通常到这里就接近上限 |
| 4 | 特征工程迭代 | 真正的分数来源 |
| 5 | 融合 / 调参 | 边际收益递减,注意过拟合验证集 |
通过标准
下一步
→ 模型评估:先学会判断模型好坏,再谈提升