Skip to content

34. 经典机器学习基础

LLM/Agent 工程师必备:经典 ML 是深度学习的基石,面试中考察频率极高,且直接关联特征工程、模型评估等日常工程实践


一、线性回归与逻辑回归

1. ⭐ Q: 线性回归的最小二乘法解是怎么推导的?

模型:$\hat{y} = Xw + b$,简化为 $\hat{y} = X\theta$(将 b 并入 θ)

目标函数(MSE)

$$L(\theta) = \frac{1}{2n} |X\theta - y|^2 = \frac{1}{2n}(X\theta - y)^T(X\theta - y)$$

推导

$$\nabla_\theta L = \frac{1}{n} X^T(X\theta - y) = 0$$

$$X^T X \theta = X^T y$$

$$\theta^* = (X^T X)^{-1} X^T y$$

其中 $(X^T X)^{-1} X^T$ 称为伪逆矩阵(Moore-Penrose Pseudoinverse)。

python
import numpy as np

class LinearRegression:
    def __init__(self):
        self.theta = None

    def fit(self, X, y):
        # 添加偏置列
        X_b = np.c_[np.ones((X.shape[0], 1)), X]
        # 正规方程求解
        self.theta = np.linalg.pinv(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.theta

# 对比 sklearn
from sklearn.linear_model import LinearRegression
model = LinearRegression()
model.fit(X, y)
print(model.coef_, model.intercept_)

追问

  • 什么时候不能用正规方程? 当 $X^TX$ 不可逆(特征数 > 样本数,或存在共线性)时,需要加正则化:$\theta = (X^TX + \lambda I)^{-1}X^Ty$
  • 时间复杂度? 求逆 $O(d^3)$,当维度 d 很大时用梯度下降

2. ⭐⭐ Q: 逻辑回归的损失函数为什么用交叉熵而不是 MSE?

Sigmoid 函数:$\sigma(z) = \frac{1}{1+e^{-z}}$

MSE 损失:$L_{MSE} = \frac{1}{2}(\sigma(w^Tx) - y)^2$

交叉熵损失:$L_{CE} = -[y\log\sigma(w^Tx) + (1-y)\log(1-\sigma(w^Tx))]$

两个关键原因

① MSE 的梯度问题(非凸 + 梯度消失)

python
import numpy as np
import matplotlib.pyplot as plt

z = np.linspace(-6, 6, 200)
sigmoid = 1 / (1 + np.exp(-z))

# MSE 对 z 的梯度
mse_grad = (sigmoid - 1) * sigmoid * (1 - sigmoid)  # σ'(z) × (σ(z)-y)
# 当 σ(z)→1 且 y=1 时,(σ-y)→0 且 σ'→0,梯度双重衰减

# 交叉熵对 z 的梯度
ce_grad = sigmoid - 1  # 非常简洁!当 y=1 时
# ∂L_CE/∂z = σ(z) - y,梯度只与误差成正比,不会饱和

② 从最大似然角度推导交叉熵

$$P(y|x;w) = [\sigma(w^Tx)]^y [1-\sigma(w^Tx)]^{1-y}$$

$$\log L(w) = \sum_i [y_i\log\sigma(w^Tx_i) + (1-y_i)\log(1-\sigma(w^Tx_i))]$$

最大化对数似然 = 最小化交叉熵损失。从概率建模出发,交叉熵是自然结果

面试技巧:画出 MSE 和交叉熵的损失曲面,交叉熵是凸函数(对 LR 参数),MSE 对 LR 是非凸的。


3. ⭐⭐ Q: 逻辑回归从零推导,包括梯度下降更新公式

python
import numpy as np

class LogisticRegression:
    def __init__(self, lr=0.01, n_iters=1000):
        self.lr = lr
        self.n_iters = n_iters

    def sigmoid(self, z):
        return 1 / (1 + np.exp(-np.clip(z, -500, 500)))

    def fit(self, X, y):
        n_samples, n_features = X.shape
        self.w = np.zeros(n_features)
        self.b = 0

        for _ in range(self.n_iters):
            z = X @ self.w + self.b
            y_pred = self.sigmoid(z)
            # 梯度
            dw = (1/n_samples) * X.T @ (y_pred - y)
            db = (1/n_samples) * np.sum(y_pred - y)
            # 更新
            self.w -= self.lr * dw
            self.b -= self.lr * db

    def predict(self, X):
        z = X @ self.w + self.b
        return (self.sigmoid(z) >= 0.5).astype(int)

追问

  • LR 能处理非线性问题吗? 原始不能,需要手动做特征交叉/多项式特征
  • LR 多分类? 用 Softmax 回归(多项逻辑回归),输出层换成 softmax

4. ⭐⭐ Q: 逻辑回归 vs 线性回归,本质区别是什么?

维度线性回归逻辑回归
任务回归分类
输出$w^Tx+b$(实数)$\sigma(w^Tx+b) \in (0,1)$
损失MSE交叉熵
概率解释无直接概率意义输出是概率(经 Sigmoid 映射)
优化正规方程有闭式解无闭式解,迭代优化

本质:逻辑回归 = 线性回归 + Sigmoid 激活 + 交叉熵损失。名字叫"回归",实际是分类器。


二、决策树与集成学习

5. ⭐ Q: 信息增益和基尼系数有什么区别?

信息熵:$H(D) = -\sum_{k=1}^K p_k \log_2 p_k$

条件熵:$H(D|A) = \sum_{v} \frac{|D_v|}{|D|} H(D_v)$

信息增益:$IG(D, A) = H(D) - H(D|A)$(ID3 用)

信息增益比:$IGR(D,A) = \frac{IG(D,A)}{H_A(D)}$(C4.5 用,修正偏好多值特征)

基尼系数:$Gini(D) = 1 - \sum_{k=1}^K p_k^2$(CART 用)

python
import numpy as np
from collections import Counter

def entropy(y):
    counts = np.bincount(y)
    probs = counts[counts > 0] / len(y)
    return -np.sum(probs * np.log2(probs))

def gini(y):
    counts = np.bincount(y)
    probs = counts[counts > 0] / len(y)
    return 1 - np.sum(probs ** 2)

y = np.array([0, 0, 1, 1, 1])
print(f"Entropy: {entropy(y):.4f}")  # 0.9710
print(f"Gini:    {gini(y):.4f}")      # 0.4800

追问:为什么 CART 选基尼系数?计算快(不需要对数),且与信息增益在二分类下行为类似。


6. ⭐⭐ Q: ID3、C4.5、CART 三种决策树有什么区别?

特性ID3C4.5CART
划分标准信息增益信息增益比基尼系数/方差
树类型多叉树多叉树二叉树
连续值✅(二分法)
缺失值
剪枝悲观剪枝代价复杂度剪枝
任务分类分类分类+回归

7. ⭐⭐⭐ Q: Bagging 和 Boosting 的区别?各自的代表算法?

维度BaggingBoosting
采样并行,Bootstrap 有放回采样串行,关注错分样本
权重所有基学习器等权根据误差加权
偏差/方差降低方差降低偏差
过拟合不易过拟合容易过拟合(需调参)
代表随机森林GBDT, XGBoost, LightGBM

类比:Bagging 像「民主投票」——多个独立的专家投票;Boosting 像「师傅带徒弟」——每一轮针对上一轮的错误重点训练。


8. ⭐⭐ Q: 随机森林的"随机"体现在哪里?

两层随机性:

  1. 样本随机:每棵树用 Bootstrap 采样(有放回),约 63.2% 的样本被选中
  2. 特征随机:每个节点分裂时,只从 $m = \sqrt{d}$ 个特征中选最优(d 是总特征数)
python
from sklearn.ensemble import RandomForestClassifier

# 关键参数
rf = RandomForestClassifier(
    n_estimators=100,      # 树的数量
    max_features='sqrt',   # 每次选 sqrt(d) 个特征(分类)
    # max_features=0.3,    # 也可以指定比例
    max_depth=None,        # 不限深度(靠 Bagging 控制方差)
    oob_score=True,        # 用袋外样本评估
    random_state=42
)

追问:什么是 OOB Score?每棵树没被采样到的约 36.8% 样本,可作为该树的验证集,汇总后得到 OOB 误差估计(免费的交叉验证)。


9. ⭐⭐⭐ Q: GBDT 的梯度提升是怎么回事?

核心思想:每一棵新树拟合的是损失函数关于模型输出的负梯度(即残差的近似)。

$$F_m(x) = F_{m-1}(x) + \eta \cdot h_m(x)$$

其中 $h_m(x)$ 拟合 $-\frac{\partial L(y, F(x))}{\partial F(x)}\bigg|{F=F{m-1}}$

python
# 手写 GBDT(回归,MSE 损失)
class SimpleGBDT:
    def __init__(self, n_estimators=100, lr=0.1, max_depth=3):
        self.n_estimators = n_estimators
        self.lr = lr
        self.max_depth = max_depth

    def fit(self, X, y):
        from sklearn.tree import DecisionTreeRegressor
        self.trees = []
        # 初始预测:均值
        self.init_pred = np.mean(y)
        F = np.full(len(y), self.init_pred)

        for _ in range(self.n_estimators):
            # MSE 损失的负梯度 = 残差
            residual = y - F
            tree = DecisionTreeRegressor(max_depth=self.max_depth)
            tree.fit(X, residual)
            F += self.lr * tree.predict(X)
            self.trees.append(tree)

    def predict(self, X):
        F = np.full(X.shape[0], self.init_pred)
        for tree in self.trees:
            F += self.lr * tree.predict(X)
        return F

关键理解:对于 MSE 损失,负梯度恰好等于残差 $y - F(x)$;对于其他损失(如 Huber 损失),负梯度是残差的鲁棒近似。


10. ⭐⭐⭐ Q: XGBoost vs LightGBM vs CatBoost 的核心区别?

特性XGBoostLightGBMCatBoost
树生长level-wise(按层)leaf-wise(按叶)对称树
分裂算法pre-sorted / histogramhistogram(更快)histogram
类别特征需手动编码直接支持原生支持(Ordered TS)
缺失值自动处理自动处理自动处理
GPU支持支持支持
正则化L1+L2+树复杂度L1+L2L2+树对称性约束
速度最快中等
过拟合风险高(leaf-wise)低(有序TS)

LightGBM 的 leaf-wise vs level-wise

  • level-wise:同一层所有叶子都分裂 → 平衡但可能浪费
  • leaf-wise:选增益最大的叶子分裂 → 可能过深,需限制 max_depth

CatBoost 的 Ordered Target Statistics

python
# 普通 Target Encoding(有标签泄漏)
# CatBoost:对每个样本,只用排序在它之前的样本来计算目标统计
# 避免了 target leakage,无需额外的 CV fold
python
# 三者 API 对比
import xgboost as xgb
import lightgbm as lgb
from catboost import CatBoostClassifier

xgb_model = xgb.XGBClassifier(n_estimators=100, max_depth=6, learning_rate=0.1)
lgb_model = lgb.LGBMClassifier(n_estimators=100, max_depth=-1, learning_rate=0.1, num_leaves=31)
cat_model = CatBoostClassifier(iterations=100, depth=6, learning_rate=0.1, verbose=0)

面试技巧:说出 LightGBM 的 GOSS(基于梯度的单边采样)和 EFB(互斥特征捆绑)两个加速技术加分。


三、支持向量机 SVM

11. ⭐⭐ Q: SVM 的最大间隔直觉是什么?

核心思想:在所有能正确分类的超平面中,选择离最近样本最远的那一个。

$$\min_{w,b} \frac{1}{2}|w|^2 \quad \text{s.t.} \quad y_i(w^Tx_i + b) \geq 1$$

为什么最大化间隔? 间隔越大,对噪声的容忍度越高 → 泛化能力越强。

几何间隔:$\gamma = \frac{2}{|w|}$,所以最小化 $|w|^2$ 等价于最大化间隔。

类比:想象在马路中间画线,要让两侧的电线杆离线越远越好——这条线最"安全"。


12. ⭐⭐⭐ Q: 软间隔 SVM 是什么?对偶问题怎么来的?

硬间隔要求完全线性可分,现实中不可能。软间隔引入松弛变量:

$$\min_{w,b,\xi} \frac{1}{2}|w|^2 + C\sum_{i=1}^n \xi_i$$ $$\text{s.t.} \quad y_i(w^Tx_i + b) \geq 1 - \xi_i, \quad \xi_i \geq 0$$

C 的含义

  • C 大 → 对误分类惩罚大 → 间隔小,可能过拟合
  • C 小 → 容忍更多误分类 → 间隔大,可能欠拟合

对偶问题推导:引入拉格朗日乘子 $\alpha_i$:

$$L = \frac{1}{2}|w|^2 + C\sum\xi_i - \sum\alpha_i[y_i(w^Tx_i+b)-(1-\xi_i)] - \sum\mu_i\xi_i$$

对 w, b, ξ 求导并代入,得到对偶问题:

$$\max_\alpha \sum\alpha_i - \frac{1}{2}\sum_{i,j}\alpha_i\alpha_j y_iy_j x_i^Tx_j$$ $$\text{s.t.} \quad 0 \leq \alpha_i \leq C$$

对偶的好处:只涉及 $x_i^Tx_j$(内积),可以替换为核函数 $K(x_i, x_j)$。


13. ⭐⭐ Q: 核函数的作用是什么?常见的有哪些?

核心思想:将数据映射到高维空间使其线性可分,但不需要显式计算映射,只需计算内积。

$$K(x_i, x_j) = \phi(x_i)^T \phi(x_j)$$

核函数公式特点
线性核$x^Tx'$不升维,线性可分时用
多项式核$(x^Tx' + c)^d$有限维映射
RBF 高斯核$\exp(-\gamma|x-x'|^2)$无限维映射,最常用
Sigmoid 核$\tanh(\gamma x^Tx' + c)$类似神经网络
python
from sklearn.svm import SVC

# RBF 核(默认)
svm_rbf = SVC(kernel='rbf', C=1.0, gamma='scale')

# 线性核(高维数据,如文本)
svm_linear = SVC(kernel='linear', C=1.0)

# gamma 的含义:单个样本的影响范围
# gamma 大 → 影响范围小 → 决策边界复杂 → 过拟合
# gamma 小 → 影响范围大 → 决策边界平滑 → 欠拟合

14. ⭐⭐ Q: SVM vs 逻辑回归,什么时候选哪个?

维度SVM逻辑回归
损失Hinge Loss交叉熵
输出决策边界 + 支持向量概率
核技巧❌(需手动特征映射)
样本量小样本表现好大样本更稳定
稀疏性只依赖支持向量使用所有样本
可解释性一般系数可解释

经验法则

  • 特征数 >> 样本数 → LR 或 Linear SVM
  • 样本少 + 非线性 → RBF SVM
  • 需要概率输出 → LR(或 SVM + Platt Scaling)
  • 大规模数据 → LR(SVM 训练太慢)

四、降维与聚类

15. ⭐⭐ Q: PCA 的数学原理是什么?

目标:找到数据方差最大的方向(主成分),将 d 维数据投影到 k 维。

步骤

  1. 数据中心化:$X = X - \bar{X}$
  2. 计算协方差矩阵:$\Sigma = \frac{1}{n-1}X^TX$
  3. 特征值分解:$\Sigma = V\Lambda V^T$
  4. 取前 k 个特征向量:$W = [v_1, v_2, ..., v_k]$
  5. 投影:$Z = XW$

信息保留率:$\frac{\sum_{i=1}^k \lambda_i}{\sum_{i=1}^d \lambda_i}$

python
from sklearn.decomposition import PCA

pca = PCA(n_components=0.95)  # 保留 95% 方差
X_reduced = pca.fit_transform(X)
print(f"从 {X.shape[1]} 维降到 {X_reduced.shape[1]} 维")
print(f"各主成分方差占比: {pca.explained_variance_ratio_}")

追问:PCA 假设数据是线性结构,非线性数据用 Kernel PCA 或 t-SNE/UMAP。


16. ⭐⭐⭐ Q: SVD 和 PCA 的关系是什么?

PCA 通过协方差矩阵的特征值分解实现,但实际计算中常用 SVD(奇异值分解)

$$X = U\Sigma V^T$$

  • $U$:左奇异向量(样本在主成分空间的坐标)
  • $\Sigma$:奇异值($\sigma_i^2 / (n-1) = \lambda_i$)
  • $V$:右奇异向量(主成分方向)

直接对 X 做 SVD 即可得到 PCA 结果,无需显式计算协方差矩阵 $X^TX$(数值更稳定)。

python
import numpy as np

# 手动 PCA via SVD
X_centered = X - X.mean(axis=0)
U, S, Vt = np.linalg.svd(X_centered, full_matrices=False)
X_pca = U * S  # 等价于 X_centered @ Vt.T

# sklearn 内部就是用 SVD 实现的 PCA

17. ⭐ Q: K-means 的流程和局限性?

流程

  1. 随机初始化 k 个质心
  2. 分配:每个样本归入最近质心的簇
  3. 更新:重新计算每个簇的质心
  4. 重复 2-3 直到收敛
python
from sklearn.cluster import KMeans

kmeans = KMeans(n_clusters=3, init='k-means++', n_init=10, random_state=42)
labels = kmeans.fit_predict(X)

局限性

  • 需要预设 k
  • 对初始质心敏感 → K-means++ 解决
  • 只能发现球形簇(凸形),非球形不行
  • 对离群点敏感
  • 只适用于数值特征

18. ⭐⭐ Q: K-means++ 怎么改进初始化?

原始 K-means:随机选 k 个点 → 可能两个初始质心很近 → 收敛到局部最优

K-means++ 初始化

  1. 随机选第一个质心
  2. 对每个样本,计算它到最近质心的距离 $D(x)$
  3. 以概率 $\frac{D(x)^2}{\sum D(x')^2}$ 选择下一个质心(越远越可能被选中)
  4. 重复 2-3 直到选够 k 个

直觉:初始质心尽量分散开。


19. ⭐⭐ Q: DBSCAN 相比 K-means 有什么优势?

维度K-meansDBSCAN
簇形状球形任意形状
需要 k不需要
噪声处理好(自动识别噪声点)
参数kε(邻域半径), MinPts(最小点数)

核心概念

  • 核心点:ε 邻域内至少有 MinPts 个点
  • 边界点:不是核心点,但在某核心点的 ε 邻域内
  • 噪声点:既不是核心点也不是边界点
python
from sklearn.cluster import DBSCAN

dbscan = DBSCAN(eps=0.5, min_samples=5)
labels = dbscan.fit_predict(X)
# labels=-1 表示噪声点

20. ⭐⭐ Q: 如何选择最优的聚类数 k?

① 肘部法则(Elbow Method):画 k vs inertia(簇内平方和),找"拐点"

② 轮廓系数(Silhouette Score)

$$s(i) = \frac{b(i) - a(i)}{\max(a(i), b(i))}$$

  • $a(i)$:样本 i 到同簇其他点的平均距离(内聚度)
  • $b(i)$:样本 i 到最近异簇所有点的平均距离(分离度)
  • $s \in [-1, 1]$,越大越好
python
from sklearn.metrics import silhouette_score

scores = []
for k in range(2, 10):
    km = KMeans(n_clusters=k, random_state=42)
    labels = km.fit_predict(X)
    scores.append(silhouette_score(X, labels))

best_k = range(2, 10)[np.argmax(scores)]

五、偏差-方差权衡与模型选择

21. ⭐⭐ Q: 什么是偏差-方差分解?

$$E[(y - \hat{f}(x))^2] = \text{Bias}^2 + \text{Variance} + \text{Noise}$$

  • 偏差(Bias):模型预测的期望值与真实值的差距 → 欠拟合
  • 方差(Variance):模型对不同训练集的敏感程度 → 过拟合
  • 噪声(Irreducible Error):数据本身的随机性,无法消除
python
# 直观理解
# 高偏差低方差:每次射箭都偏左(系统性偏差,但很集中)
# 低偏差高方差:平均在靶心,但散布很广
# 高偏差高方差:又偏又散
# 低偏差低方差:理想状态,集中靶心
模型复杂度偏差方差风险
低(线性模型)欠拟合
高(深度决策树)过拟合
集成(RF/GBDT)最优

22. ⭐⭐ Q: 如何诊断过拟合和欠拟合?

python
from sklearn.model_selection import learning_curve
import matplotlib.pyplot as plt

train_sizes, train_scores, val_scores = learning_curve(
    estimator, X, y, cv=5, train_sizes=np.linspace(0.1, 1.0, 10)
)

# 欠拟合:两条线都在低处,且差距小
# 过拟合:训练分很高,验证分很低,差距大
# 理想:两条线收敛到较高的值,差距小

诊断清单

症状训练误差验证误差原因对策
欠拟合模型太简单增加特征/复杂度
过拟合模型太复杂正则化/更多数据/简化

23. ⭐ Q: K 折交叉验证怎么实现?为什么比留一法好?

python
from sklearn.model_selection import cross_val_score, KFold

kf = KFold(n_splits=5, shuffle=True, random_state=42)
scores = cross_val_score(model, X, y, cv=kf, scoring='accuracy')
print(f"Mean: {scores.mean():.4f}, Std: {scores.std():.4f}")

K 折 vs 留一法(LOO)

  • LOO:n 折,每次留 1 个样本 → 无偏但方差大,计算量巨大
  • 5/10 折:偏差和方差的平衡点

追问:分类任务用 分层 K 折(Stratified K-Fold),保证每折中各类别比例一致。


六、正则化

24. ⭐⭐⭐ Q: L1 正则化为什么能产生稀疏解?从数学和几何两个角度解释。

数学角度

$$L_{L1} = L_{original} + \lambda\sum|w_i|$$

L1 在 $w_i=0$ 处不可导,次梯度为 $[-1, 1]$。当 $\lambda$ 足够大时,最优解落在 $w_i=0$ 处。

几何角度(关键!面试加分):

L1 约束 $|w_1| + |w_2| \leq c$ 在二维空间是菱形,L2 约束 $w_1^2 + w_2^2 \leq c$ 是圆形

损失函数的等高线是椭圆。菱形有角点(尖角),椭圆更容易与角点相切 → $w_1$ 或 $w_2$ 为零。圆形没有角点 → 切点几乎不可能在轴上 → 不产生稀疏。

python
import numpy as np

# L1 稀疏演示
from sklearn.linear_model import Lasso, Ridge

# L1 (Lasso) 产生稀疏系数
lasso = Lasso(alpha=0.1).fit(X, y)
print(f"Lasso 非零系数: {np.sum(lasso.coef_ != 0)}/{len(lasso.coef_)}")

# L2 (Ridge) 不稀疏
ridge = Ridge(alpha=0.1).fit(X, y)
print(f"Ridge 非零系数: {np.sum(ridge.coef_ != 0)}/{len(ridge.coef_)}")

25. ⭐⭐ Q: L2 正则化的贝叶斯视角是什么?

频率学派:正则化是约束优化

贝叶斯学派:正则化 = 引入参数的先验分布

正则化等价先验公式
L1 (Lasso)Laplace 先验$p(w) \propto e^{-\lambda|w|}$
L2 (Ridge)高斯先验$p(w) \propto e^{-\lambda|w|^2}$

MAP 估计 = 最大后验 = 最大似然 + 先验 = 最小化损失 + 正则化

$$\hat{w}_{MAP} = \arg\max_w P(w|D) = \arg\max_w [P(D|w)P(w)]$$

$$= \arg\min_w [-\log P(D|w) - \log P(w)]$$

当 $P(w) = N(0, \frac{1}{\lambda}I)$ 时,$-\log P(w) = \frac{\lambda}{2}|w|^2$ → L2 正则化。


26. ⭐⭐ Q: 弹性网络(Elastic Net)是什么?什么时候用?

$$L = L_{original} + \lambda_1\sum|w_i| + \lambda_2\sum w_i^2$$

等价于 L1 + L2 的组合,用 l1_ratio 控制比例:

python
from sklearn.linear_model import ElasticNet

# l1_ratio=1 纯 L1, l1_ratio=0 纯 L2
en = ElasticNet(alpha=0.1, l1_ratio=0.5)

什么时候用 Elastic Net?

  • 特征间有共线性:Lasso 会随机选一个,Elastic Net 会一起选
  • 特征数远大于样本数
  • 想要稀疏但 Lasso 太激进

七、特征工程

27. ⭐ Q: 缺失值怎么处理?

方法适用场景代码
删除行缺失比例小(<5%)dropna()
均值/中位数填充数值特征,MCARSimpleImputer(strategy='median')
众数填充类别特征SimpleImputer(strategy='most_frequent')
KNN 填充特征间有关联KNNImputer(n_neighbors=5)
模型预测填充缺失有规律用其他特征训练模型预测
标记缺失缺失本身有意义增加 is_null
python
from sklearn.impute import SimpleImputer, KNNImputer
from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import IterativeImputer  # MICE 算法

# 基本填充
imp = SimpleImputer(strategy='median')
X_filled = imp.fit_transform(X)

# 高级填充(多重插补)
imp_iter = IterativeImputer(max_iter=10, random_state=42)
X_filled = imp_iter.fit_transform(X)

面试技巧:先判断缺失机制(MCAR/MAR/MNAR),再选方法。


28. ⭐ Q: 标准化和归一化有什么区别?

方法公式输出范围适用场景
标准化(Z-score)$\frac{x-\mu}{\sigma}$均值0,方差1大多数ML模型
归一化(Min-Max)$\frac{x-x_{min}}{x_{max}-x_{min}}$[0, 1]神经网络、距离模型
python
from sklearn.preprocessing import StandardScaler, MinMaxScaler

scaler = StandardScaler()  # 推荐,对异常值鲁棒
X_std = scaler.fit_transform(X)

mms = MinMaxScaler()  # 对异常值敏感
X_norm = mms.fit_transform(X)

什么时候必须标准化? SVM、KNN、PCA、梯度下降训练的模型。树模型不需要。


29. ⭐⭐ Q: 类别特征有哪些编码方式?

python
import pandas as pd
from sklearn.preprocessing import LabelEncoder, OrdinalEncoder, OneHotEncoder

# 1. Label Encoding(有序类别)
le = LabelEncoder()
# 低→高:小学<初中<高中<大学
encoded = le.fit_transform(['小学', '高中', '初中', '大学'])

# 2. One-Hot Encoding(无序类别)
ohe = OneHotEncoder(sparse=False)
# 红、绿、蓝 → [1,0,0], [0,1,0], [0,0,1]

# 3. Target Encoding(高基数类别)
# 用该类别下目标变量的均值替代
# 例:城市→该城市的平均房价

# 4. Frequency Encoding
# 用类别出现频率替代
freq = df['city'].value_counts(normalize=True)
df['city_freq'] = df['city'].map(freq)
编码适用问题
Label有序类别无序时引入虚假顺序
One-Hot低基数(<10)高基数时维度爆炸
Target高基数有标签泄漏风险,需正则化
Frequency高基数丢失类别语义

30. ⭐⭐ Q: 特征选择有哪些方法?

三类方法

① 过滤法(Filter):独立于模型,计算快

python
from sklearn.feature_selection import SelectKBest, mutual_info_classif

# 方差过滤
from sklearn.feature_selection import VarianceThreshold
sel = VarianceThreshold(threshold=0.01)

# 互信息
selector = SelectKBest(mutual_info_classif, k=10)
X_new = selector.fit_transform(X, y)

② 包装法(Wrapper):效果好但慢

python
from sklearn.feature_selection import RFE
from sklearn.ensemble import RandomForestClassifier

rfe = RFE(RandomForestClassifier(), n_features_to_select=10)
X_new = rfe.fit_transform(X, y)

③ 嵌入法(Embedded):模型自带特征重要性

python
from sklearn.feature_selection import SelectFromModel

sfm = SelectFromModel(RandomForestClassifier(), threshold='median')
X_new = sfm.fit_transform(X, y)

八、评估指标体系

31. ⭐ Q: 混淆矩阵和基本指标怎么算?

              预测正    预测负
实际正         TP        FN
实际负         FP        TN
指标公式含义
精确率 P$\frac{TP}{TP+FP}$预测为正的里面,真正的比例
召回率 R$\frac{TP}{TP+FN}$实际为正的里面,被找到的比例
F1$\frac{2PR}{P+R}$P 和 R 的调和平均
准确率$\frac{TP+TN}{N}$整体正确率

类比

  • 精确率:「宁缺毋滥」—— 搜索结果尽量准确
  • 召回率:「宁多勿少」—— 不漏掉任何一个相关结果
  • F1:精确率和召回率的平衡

32. ⭐⭐ Q: P-R 曲线和 ROC 曲线有什么区别?什么时候用哪个?

P-R 曲线:以召回率为横轴,精确率为纵轴

ROC 曲线:以 FPR(假正率)为横轴,TPR(真正率=召回率)为纵轴

$$FPR = \frac{FP}{FP + TN}, \quad TPR = \frac{TP}{TP + FN}$$

AUC:ROC 曲线下的面积,越大越好(0.5 = 随机,1.0 = 完美)

关键区别

  • 正负样本均衡 → 用 AUC-ROC
  • 正负样本不均衡 → 用 P-R 曲线和 AUC-PR(ROC 在不均衡时会过于乐观)
python
from sklearn.metrics import (
    precision_recall_curve, roc_curve, auc,
    average_precision_score, roc_auc_score
)

# ROC
fpr, tpr, _ = roc_curve(y_true, y_score)
roc_auc = auc(fpr, tpr)

# P-R
precision, recall, _ = precision_recall_curve(y_true, y_score)
ap = average_precision_score(y_true, y_score)

为什么 ROC 在不均衡时乐观? 当负样本远多于正样本时,FP 的增量对 FPR 影响很小(分母大),但对精确率影响大。


33. ⭐⭐ Q: 多分类指标怎么算?宏平均 vs 微平均 vs 加权平均?

平均方式公式特点
宏平均 Macro各类指标的简单平均每个类别权重相同,受小类影响大
微平均 Micro全局 TP/FP/FN 计算受大类主导,≈ 准确率
加权平均 Weighted按类别样本数加权考虑类别不平衡
python
from sklearn.metrics import classification_report

print(classification_report(y_true, y_pred, target_names=['cat','dog','bird']))
#          precision  recall  f1-score   support
# cat         0.90     0.85      0.87       100
# dog         0.80     0.90      0.85        80
# bird        0.95     0.80      0.87        60
# accuracy                        0.85       240
# macro avg   0.88     0.85      0.86       240
# weighted avg 0.88    0.85      0.86       240

34. ⭐⭐⭐ Q: 不均衡数据怎么处理?

数据层面

python
# 欠采样
from imblearn.under_sampling import RandomUnderSampler
rus = RandomUnderSampler(sampling_strategy=0.5)

# 过采样
from imblearn.over_sampling import SMOTE
smote = SMOTE(sampling_strategy=0.5)

# 组合
from imblearn.combine import SMOTETomek
smt = SMOTETomek()

算法层面

  • 调 class_weight:LogisticRegression(class_weight='balanced')
  • 调阈值:降低正类阈值(如 0.3)提高召回率
  • 使用 F1/AUC-PR 作为优化目标

经验

  • 比例 1:10 → class_weight 即可
  • 比例 1:100 → SMOTE + class_weight
  • 比例 1:1000+ → 考虑异常检测方法

九、经典 ML 面试高频题汇总

35. ⭐⭐ Q: 【高频 #1】为什么树模型不需要特征标准化?

:决策树的分裂依据是特征值的排序和阈值,不是数值大小。分裂条件 x < threshold 是序关系,不受缩放影响。而 SVM/KNN/LR 基于距离或梯度,必须标准化。


36. ⭐⭐ Q: 【高频 #2】梯度下降和牛顿法的区别?

维度梯度下降牛顿法
信息一阶导数一阶 + 二阶导数(Hessian)
更新$w - \eta\nabla L$$w - H^{-1}\nabla L$
收敛线性二阶(更快)
每步成本高(需计算 Hessian 逆)
适用大规模数据小规模、光滑优化

拟牛顿法(L-BFGS):近似 Hessian 逆,兼顾速度和效率,scipy.optimize 默认方法。


37. ⭐⭐ Q: 【高频 #3】特征相关性高(共线性)有什么影响?怎么解决?

影响

  • 线性回归系数不稳定(方差大),符号可能反转
  • 模型可解释性下降
  • 树模型/集成模型不受影响

解决

  • VIF(方差膨胀因子)检测:VIF > 10 有共线性
  • PCA 降维
  • L1 正则化(自动选一个)
  • 删除相关特征

38. ⭐⭐ Q: 【高频 #4】什么是模型的泛化能力?如何提升?

泛化能力 = 模型在未见过的数据上的表现。

提升方法

  1. 更多数据(最有效)
  2. 正则化(L1/L2/Dropout)
  3. 交叉验证选模型
  4. 特征工程(去噪、降维)
  5. 集成学习(Bagging 降方差、Boosting 降偏差)
  6. 早停(Early Stopping)

39. ⭐⭐⭐ Q: 【高频 #5】XGBoost 的二阶泰勒展开有什么好处?

XGBoost 的损失函数用二阶泰勒展开:

$$L^{(t)} \approx \sum_{i=1}^n [g_i f_t(x_i) + \frac{1}{2}h_i f_t^2(x_i)] + \Omega(f_t)$$

其中 $g_i = \partial_{\hat{y}i} L$,$h_i = \partial^2{\hat{y}_i} L$(一阶和二阶梯度)。

好处

  1. 通用性:只要损失函数二阶可导,就能用同一套框架(自定义损失)
  2. 精度:比一阶梯度下降更准
  3. 效率:Hessian 对角近似,计算量可控
  4. 正则化:自然引出树复杂度的正则项

40. ⭐⭐ Q: 【高频 #6】如何处理高基数类别特征?

高基数(如用户 ID、城市名有上千个不同值):

  1. Target Encoding(最常用)+ 正则化(加噪声或贝叶斯平滑)
  2. Frequency Encoding:用频率替代
  3. Embedding:学习低维向量表示
  4. Hash Encoding:哈希到固定维度(有冲突但实用)
  5. 聚合:将低频类别合并为"其他"
python
# Target Encoding 实现
def target_encode(train, col, target, smoothing=10):
    global_mean = train[target].mean()
    stats = train.groupby(col)[target].agg(['mean', 'count'])
    smooth = (stats['count'] * stats['mean'] + smoothing * global_mean) / (stats['count'] + smoothing)
    return train[col].map(smooth)

41. ⭐⭐⭐ Q: 【高频 #7】随机森林的特征重要性怎么计算?

① 基于不纯度(Gini Importance):每棵树中,特征用于分裂时带来的不纯度减少量的加权平均。缺点:偏向高基数特征。

② 基于排列(Permutation Importance):打乱某特征的值,观察模型性能下降多少。更可靠。

python
from sklearn.inspection import permutation_importance

# 不纯度重要性(内置)
rf = RandomForestClassifier().fit(X, y)
imp1 = rf.feature_importances_

# 排列重要性(推荐)
result = permutation_importance(rf, X_val, y_val, n_repeats=10)
imp2 = result.importances_mean

42. ⭐⭐⭐ Q: 【高频 #8】模型上线后效果下降,排查思路是什么?

  1. 数据分布漂移(Data Drift):特征分布变了 → 检测 PSI/KS
  2. 概念漂移(Concept Drift):特征和标签的关系变了
  3. 数据质量问题:缺失值增多、编码错误、特征缺失
  4. 线上/线下特征不一致:特征穿越(Label Leakage)、时间窗口不对
  5. 评估指标选择不当:线上指标和线下指标不一致
python
# PSI 检测分布漂移
def psi(expected, actual, buckets=10):
    breakpoints = np.percentile(expected, np.linspace(0, 100, buckets + 1))
    expected_pct = np.histogram(expected, breakpoints)[0] / len(expected)
    actual_pct = np.histogram(actual, breakpoints)[0] / len(actual)
    expected_pct = np.clip(expected_pct, 1e-4, None)
    actual_pct = np.clip(actual_pct, 1e-4, None)
    return np.sum((actual_pct - expected_pct) * np.log(actual_pct / expected_pct))

# PSI < 0.1 稳定, 0.1-0.25 需关注, > 0.25 显著漂移

43. ⭐⭐ Q: 【高频 #9】如何用 sklearn Pipeline 构建完整的 ML 工作流?

python
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.impute import SimpleImputer
from sklearn.ensemble import GradientBoostingClassifier

# 定义列类型
numeric_features = ['age', 'income']
categorical_features = ['city', 'gender']

# 分别处理数值和类别特征
numeric_transformer = Pipeline([
    ('imputer', SimpleImputer(strategy='median')),
    ('scaler', StandardScaler())
])

categorical_transformer = Pipeline([
    ('imputer', SimpleImputer(strategy='most_frequent')),
    ('encoder', OneHotEncoder(handle_unknown='ignore'))
])

# 组合
preprocessor = ColumnTransformer([
    ('num', numeric_transformer, numeric_features),
    ('cat', categorical_transformer, categorical_features)
])

# 完整 Pipeline
full_pipeline = Pipeline([
    ('preprocessor', preprocessor),
    ('classifier', GradientBoostingClassifier())
])

# 训练和预测
full_pipeline.fit(X_train, y_train)
score = full_pipeline.score(X_test, y_test)

# 防止数据泄漏!fit 只在训练集上调用

面试加分点:Pipeline 保证了数据处理的一致性,防止训练/推理时的特征工程不一致。


44. ⭐⭐⭐ Q: 【高频 #10】模型可解释性有哪些方法?

方法类型适用模型
特征重要性全局树模型
SHAP全局+局部任意模型
LIME局部任意模型
Partial Dependence全局任意模型
系数分析全局线性模型
决策树可视化全局决策树
python
import shap

# SHAP 值
explainer = shap.TreeExplainer(model)
shap_values = explainer.shap_values(X_test)

# 全局重要性
shap.summary_plot(shap_values, X_test)

# 单个样本解释
shap.force_plot(explainer.expected_value, shap_values[0], X_test.iloc[0])

面试技巧:SHAP 值基于博弈论的 Shapley 值,有坚实的理论基础(可加性、对称性、零贡献性),是目前最被认可的可解释性工具。


总结:经典 ML 面试核心在于能从数学推导、直觉解释、代码实现三个层面回答问题。重点掌握:逻辑回归推导、决策树原理、集成学习对比、SVM 核技巧、PCA/SVD、正则化(尤其是 L1 稀疏性的几何解释)、评估指标选择(不均衡场景)、特征工程实践。

LLM 应用 & Agent 开发面试准备