监督学习是机器学习最广泛应用的范式。本文深入讲解从经典线性模型到现代梯度提升树的核心算法,包括数学原理、实现细节与调优策略。
1. 线性回归 (Linear Regression)
1.1 最小二乘法
假设目标 $y$ 与特征 $X$ 呈线性关系:$y = Xw + b$
目标是最小化均方误差 (MSE):
$$J(w) = \frac{1}{2n}\sum_{i=1}^{n}(y_i - \hat{y}_i)^2 = \frac{1}{2n}(y - Xw)^T(y - Xw)$$
解析解(正规方程):
$$w = (X^TX)^{-1}X^Ty$$
import numpy as np
# 正规方程实现
class LinearRegressionNormal:
def fit(self, X, y):
X_b = np.c_[np.ones((X.shape[0], 1)), X] # 添加偏置列
self.w = np.linalg.inv(X_b.T @ X_b) @ X_b.T @ y
def predict(self, X):
X_b = np.c_[np.ones((X.shape[0], 1)), X]
return X_b @ self.w
# 梯度下降实现
class LinearRegressionGD:
def __init__(self, lr=0.01, epochs=1000):
self.lr = lr
self.epochs = epochs
def fit(self, X, y):
n, m = X.shape
self.w = np.zeros(m + 1)
X_b = np.c_[np.ones((n, 1)), X]
for _ in range(self.epochs):
grad = (2/n) * X_b.T @ (X_b @ self.w - y)
self.w -= self.lr * grad
def predict(self, X):
X_b = np.c_[np.ones((X.shape[0], 1)), X]
return X_b @ self.w
1.2 梯度下降变体
| 方法 | 更新方式 | 优点 | 缺点 |
|---|---|---|---|
| 批量 GD | 全数据集计算梯度 | 稳定收敛 | 大数据集慢 |
| 随机 GD | 单样本更新 | 速度快,可逃离局部最优 | 波动大 |
| 小批量 GD | mini-batch (32-512) | 平衡速度与稳定性 | 需调 batch size |
# sklearn 实际使用解析解(小规模)或 SGD(大规模)
from sklearn.linear_model import LinearRegression, SGDRegressor
# 解析解
model = LinearRegression().fit(X_train, y_train)
# 大规模数据用 SGD
model = SGDRegressor(max_iter=1000, tol=1e-3, penalty='l2', alpha=0.0001)
model.fit(X_train, y_train)
2. 逻辑回归 (Logistic Regression)
2.1 Sigmoid 与对数几率
逻辑回归虽名"回归",实为分类算法。通过 Sigmoid 函数将线性输出映射到 (0,1) 概率:
$$\sigma(z) = \frac{1}{1 + e^{-z}}$$
$$P(y=1|x) = \sigma(w^Tx + b) = \frac{1}{1 + e^{-(w^Tx+b)}}$$
2.2 对数损失与梯度
交叉熵损失(对数损失):
$$J(w) = -\frac{1}{n}\sum_{i=1}^{n}[y_i\log(\hat{y}_i) + (1-y_i)\log(1-\hat{y}_i)]$$
梯度:
$$\frac{\partial J}{\partial w} = \frac{1}{n}X^T(\hat{y} - y)$$
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler
# 逻辑回归需先标准化
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
model = LogisticRegression(
penalty='l2', # 正则化类型
C=1.0, # 正则化强度倒数,越大正则越弱
solver='lbfgs', # 优化算法
max_iter=1000,
class_weight='balanced' # 处理类别不平衡
)
model.fit(X_train_scaled, y_train)
# 预测概率
proba = model.predict_proba(X_test_scaled)[:, 1]
2.3 多项逻辑回归(多分类)
# softmax 多分类
model = LogisticRegression(
multi_class='multinomial', # softmax 而非 one-vs-rest
solver='lbfgs',
max_iter=1000
)
3. 决策树 (Decision Tree)
3.1 分裂准则
信息增益(ID3):基于熵的减少
$$H(S) = -\sum p_i \log_2(p_i)$$
$$IG(S, A) = H(S) - \sum_{v \in Values(A)} \frac{|S_v|}{|S|}H(S_v)$$
基尼指数(CART):
$$Gini(S) = 1 - \sum p_i^2$$
from sklearn.tree import DecisionTreeClassifier, export_text, plot_tree
import matplotlib.pyplot as plt
model = DecisionTreeClassifier(
criterion='gini', # 'gini' 或 'entropy'
max_depth=5, # 最大深度,防过拟合
min_samples_split=20, # 分裂最小样本数
min_samples_leaf=10, # 叶子最小样本数
max_features='sqrt', # 每次分裂考虑的特征数
random_state=42
)
model.fit(X_train, y_train)
# 可视化
tree_rules = export_text(model, feature_names=list(X.columns))
print(tree_rules)
plt.figure(figsize=(20, 10))
plot_tree(model, feature_names=X.columns, filled=True, rounded=True)
plt.savefig('tree.png')
3.2 特征重要性
importances = pd.Series(model.feature_importances_, index=X.columns)
importances.sort_values(ascending=False).plot(kind='barh')
4. 集成学习
4.1 Bagging:随机森林
Bagging(Bootstrap Aggregating):有放回抽样训练多个模型,投票/平均预测。
from sklearn.ensemble import RandomForestClassifier, RandomForestRegressor
# 随机森林分类
rf = RandomForestClassifier(
n_estimators=200, # 树的数量
max_depth=10,
min_samples_split=5,
max_features='sqrt', # 每棵树随机选 sqrt(n_features) 个特征
bootstrap=True, # 有放回抽样
oob_score=True, # 使用 out-of-bag 样本评估
n_jobs=-1, # 并行
random_state=42
)
rf.fit(X_train, y_train)
print(f"OOB Score: {rf.oob_score_:.3f}")
# 特征重要性
importances = pd.DataFrame({
'feature': X.columns,
'importance': rf.feature_importances_
}).sort_values('importance', ascending=False)
4.2 Boosting:AdaBoost
每轮迭代调整样本权重,使先前错分的样本获得更多关注。
from sklearn.ensemble import AdaBoostClassifier
ada = AdaBoostClassifier(
estimator=DecisionTreeClassifier(max_depth=1), # 弱分类器(决策树桩)
n_estimators=100,
learning_rate=1.0
)
ada.fit(X_train, y_train)
4.3 Gradient Boosting
每棵新树拟合前面所有树的残差(负梯度):
$$F_m(x) = F_{m-1}(x) + \eta \cdot h_m(x)$$
from sklearn.ensemble import GradientBoostingClassifier
gb = GradientBoostingClassifier(
n_estimators=100,
learning_rate=0.1, # 收缩系数,越小越稳健
max_depth=3, # 树深度,通常 3-8
subsample=0.8, # 每次迭代抽样比例(随机梯度提升)
random_state=42
)
gb.fit(X_train, y_train)
4.4 XGBoost
工业界最常用的梯度提升框架,优化了计算效率和正则化。
import xgboost as xgb
# 原生接口
dtrain = xgb.DMatrix(X_train, label=y_train)
dtest = xgb.DMatrix(X_test, label=y_test)
params = {
'objective': 'binary:logistic', # 或 'multi:softmax', 'reg:squarederror'
'max_depth': 6,
'learning_rate': 0.1,
'subsample': 0.8,
'colsample_bytree': 0.8,
'reg_alpha': 0.1, # L1
'reg_lambda': 1.0, # L2
'eval_metric': 'auc'
}
model = xgb.train(
params,
dtrain,
num_boost_round=100,
evals=[(dtrain, 'train'), (dtest, 'test')],
early_stopping_rounds=10
)
# sklearn 风格接口
from xgboost import XGBClassifier
xgb_model = XGBClassifier(
n_estimators=100,
max_depth=6,
learning_rate=0.1,
subsample=0.8,
colsample_bytree=0.8,
reg_alpha=0.1,
random_state=42,
n_jobs=-1
)
xgb_model.fit(X_train, y_train,
eval_set=[(X_test, y_test)],
early_stopping_rounds=10,
verbose=False)
4.5 LightGBM
微软提出的高效梯度提升框架,使用直方图算法和 Leaf-wise 生长策略。
import lightgbm as lgb
lgb_model = lgb.LGBMClassifier(
n_estimators=100,
max_depth=-1, # 无限制,由 min_child_samples 控制
learning_rate=0.1,
num_leaves=31, # 叶子节点数,控制复杂度
subsample=0.8,
colsample_bytree=0.8,
reg_alpha=0.1,
random_state=42
)
lgb_model.fit(X_train, y_train,
eval_set=[(X_test, y_test)],
callbacks=[lgb.early_stopping(10, verbose=False)])
XGBoost vs LightGBM vs CatBoost:
| 特性 | XGBoost | LightGBM | CatBoost |
|---|---|---|---|
| 树生长 | Level-wise | Leaf-wise | Symmetric |
| 类别特征 | 需预处理 | 需预处理 | 原生支持 |
| 速度 | 快 | 更快 | 快 |
| 内存 | 较低 | 更低 | 中等 |
| 过拟合 | 中等 | 需注意 | 较低 |
5. 支持向量机 (SVM)
5.1 最大间隔分类器
SVM 寻找使间隔最大化的超平面。引入松弛变量处理非线性可分:
$$\min_{w,b,\xi} \frac{1}{2}|w|^2 + C\sum\xi_i$$
约束:$y_i(w^Tx_i + b) \geq 1 - \xi_i$
from sklearn.svm import SVC, LinearSVC
# 线性 SVM(大规模数据)
linear_svc = LinearSVC(C=1.0, max_iter=5000)
linear_svc.fit(X_train_scaled, y_train)
# 核 SVM
rbf_svc = SVC(
kernel='rbf', # 'linear', 'poly', 'rbf', 'sigmoid'
C=1.0, # 惩罚系数
gamma='scale', # 核函数系数
probability=True # 启用概率输出(训练 Platt Scaling,较耗时)
)
rbf_svc.fit(X_train_scaled, y_train)
# 预测概率
proba = rbf_svc.predict_proba(X_test_scaled)
5.2 核技巧
| 核函数 | 公式 | 适用场景 |
|---|---|---|
| 线性 | $K(x, y) = x^Ty$ | 高维稀疏数据(文本) |
| 多项式 | $K(x, y) = (\gamma x^Ty + r)^d$ | 图像处理 |
| RBF | $K(x, y) = \exp(-\gamma|x-y|^2)$ | 通用,非线性边界 |
| Sigmoid | $\tanh(\gamma x^Ty + r)$ | 类似神经网络 |
注意:SVM 对特征缩放敏感,必须标准化。
6. K 近邻 (KNN)
from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier(
n_neighbors=5,
weights='distance', # 'uniform' 等权重,'distance' 按距离加权
metric='minkowski', # 距离度量
p=2 # p=2 为欧氏距离,p=1 为曼哈顿
)
knn.fit(X_train_scaled, y_train) # KNN 实际是惰性学习,fit 只存储数据
7. 朴素贝叶斯
基于特征条件独立假设的概率分类器,文本分类首选。
from sklearn.naive_bayes import GaussianNB, MultinomialNB
# 连续特征用高斯分布
model = GaussianNB()
# 离散计数特征用多项式分布(如词频)
model = MultinomialNB(alpha=1.0) # Laplace 平滑
model.fit(X_train, y_train)
8. 算法选择速查
| 场景 | 推荐算法 | 理由 |
|---|---|---|
| 快速基线 | 逻辑回归 | 快、可解释、概率输出 |
| 小规模高维 | 线性 SVM、Lasso | 在高维空间表现好 |
| 表格数据竞赛 | XGBoost/LightGBM | 通常 SOTA |
| 需可解释 | 决策树、逻辑回归 | 规则透明 |
| 类别不平衡 | 调 class_weight 的 GBM | 内置处理 |
| 大规模实时 | 线性模型 + SGD | O(1) 预测,增量训练 |
| 缺失值多 | XGBoost、LightGBM | 自动处理缺失 |
| 非结构化数据 | 神经网络 | 图像/文本/语音 |
9. 超参数调优
9.1 网格搜索与随机搜索
from sklearn.model_selection import GridSearchCV, RandomizedSearchCV
from scipy.stats import uniform, randint
# 网格搜索
param_grid = {
'max_depth': [3, 5, 7],
'learning_rate': [0.01, 0.1, 0.3],
'n_estimators': [100, 200]
}
grid = GridSearchCV(
XGBClassifier(random_state=42),
param_grid,
cv=5,
scoring='f1_macro',
n_jobs=-1,
verbose=1
)
grid.fit(X_train, y_train)
print(f"最优参数: {grid.best_params_}")
# 随机搜索(更高效)
param_dist = {
'max_depth': randint(3, 10),
'learning_rate': uniform(0.01, 0.3),
'subsample': uniform(0.6, 0.4),
'colsample_bytree': uniform(0.6, 0.4)
}
random = RandomizedSearchCV(
XGBClassifier(random_state=42),
param_dist,
n_iter=50, # 采样 50 组参数
cv=5,
scoring='f1_macro',
n_jobs=-1,
random_state=42
)
9.2 Optuna 贝叶斯优化
import optuna
def objective(trial):
params = {
'max_depth': trial.suggest_int('max_depth', 3, 10),
'learning_rate': trial.suggest_float('learning_rate', 0.01, 0.3),
'n_estimators': trial.suggest_int('n_estimators', 50, 300),
'subsample': trial.suggest_float('subsample', 0.6, 1.0),
'colsample_bytree': trial.suggest_float('colsample_bytree', 0.6, 1.0),
'reg_alpha': trial.suggest_float('reg_alpha', 0, 1),
'reg_lambda': trial.suggest_float('reg_lambda', 0, 1)
}
model = XGBClassifier(**params, random_state=42, n_jobs=-1)
scores = cross_val_score(model, X_train, y_train, cv=5, scoring='f1_macro')
return scores.mean()
study = optuna.create_study(direction='maximize')
study.optimize(objective, n_trials=100, show_progress_bar=True)
print(f"最优参数: {study.best_params}")
print(f"最优得分: {study.best_value:.3f}")
总结
| 算法 | 类型 | 核心思想 | 调参重点 |
|---|---|---|---|
| 线性回归 | 回归 | 最小二乘 | 无(或正则化强度) |
| 逻辑回归 | 分类 | 对数几率、交叉熵 | C(正则化)、solver |
| 决策树 | 两者 | 递归分裂最优特征 | max_depth、min_samples |
| 随机森林 | 两者 | Bagging + 随机特征 | n_estimators、max_depth |
| XGBoost | 两者 | 梯度提升 + 正则化 | n_estimators、eta、max_depth、subsample |
| SVM | 分类 | 最大间隔 + 核技巧 | C、kernel、gamma |
| 朴素贝叶斯 | 分类 | 条件独立假设 | alpha(平滑系数) |
实际工程中,XGBoost/LightGBM 是结构化数据的首选;逻辑回归/SVM 适合快速基线与可解释需求;随机森林 是稳健的多用途选择。超参数调优先用随机搜索缩小范围,再用贝叶斯优化精细搜索。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。