HiHuo
首页
博客
手册
工具
关于
首页
博客
手册
工具
关于
  • 技术面试完全指南

    • 技术面试完全指南
    • 8年面试官告诉你:90%的简历在第一轮就被刷掉了
    • 刷了500道LeetCode,终于明白大厂算法面试到底考什么
    • 高频算法题精讲-双指针与滑动窗口
    • 03-高频算法题精讲-二分查找与排序
    • 04-高频算法题精讲-树与递归
    • 05-高频算法题精讲-图与拓扑排序
    • 06-高频算法题精讲-动态规划
    • Go面试必问:一道GMP问题,干掉90%的候选人
    • 08-数据库面试高频题
    • 09-分布式系统面试题
    • 10-Kubernetes与云原生面试题
    • 11-系统设计面试方法论
    • 前端面试高频题
    • AI 与机器学习面试题
    • 行为面试与软技能

机器学习基础

1. 监督学习

监督学习是机器学习中最常见的学习方式,通过标注数据学习输入到输出的映射关系。

1.1 分类算法

逻辑回归 (Logistic Regression)

逻辑回归通过Sigmoid函数将线性回归的输出映射到(0,1)区间,用于二分类问题。

核心公式:

h(x) = σ(wx + b) = 1 / (1 + e^(-wx-b))

损失函数(交叉熵):

L = -[y·log(h(x)) + (1-y)·log(1-h(x))]

代码示例:

from sklearn.linear_model import LogisticRegression
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split

# 生成数据
X, y = make_classification(n_samples=1000, n_features=20, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

# 训练模型
model = LogisticRegression(max_iter=1000)
model.fit(X_train, y_train)

# 预测
accuracy = model.score(X_test, y_test)
print(f"准确率: {accuracy:.4f}")

支持向量机 (SVM)

SVM寻找最优超平面,最大化分类间隔。核心思想是通过核函数将数据映射到高维空间。

目标函数:

min 1/2||w||² + C∑ξᵢ
s.t. yᵢ(w·xᵢ + b) ≥ 1 - ξᵢ

常用核函数:

  • 线性核: K(x, x') = x·x'
  • RBF核: K(x, x') = exp(-γ||x-x'||²)
  • 多项式核: K(x, x') = (x·x' + c)^d

决策树 (Decision Tree)

决策树通过递归划分特征空间,常用信息增益或基尼系数作为划分标准。

信息增益:

Gain(D, A) = Entropy(D) - ∑(|Dᵥ|/|D|)·Entropy(Dᵥ)
Entropy(D) = -∑pᵢ·log₂(pᵢ)

基尼系数:

Gini(D) = 1 - ∑pᵢ²

随机森林 (Random Forest)

随机森林是决策树的集成学习方法,通过Bagging和特征随机选择提高泛化能力。

from sklearn.ensemble import RandomForestClassifier

rf = RandomForestClassifier(
    n_estimators=100,    # 树的数量
    max_depth=10,        # 最大深度
    min_samples_split=5, # 最小分裂样本数
    random_state=42
)
rf.fit(X_train, y_train)
print(f"特征重要性: {rf.feature_importances_}")

1.2 回归算法

线性回归

目标函数:

y = wx + b
损失函数: MSE = 1/n ∑(yᵢ - ŷᵢ)²

正规方程求解:

w = (XᵀX)⁻¹Xᵀy

岭回归 (Ridge Regression)

添加L2正则化防止过拟合:

L = MSE + λ∑wᵢ²

Lasso回归

添加L1正则化,可进行特征选择:

L = MSE + λ∑|wᵢ|

2. 无监督学习

2.1 聚类算法

K-Means

算法步骤:

  1. 随机初始化K个中心点
  2. 分配每个样本到最近的中心
  3. 更新中心为类内样本均值
  4. 重复2-3直到收敛

目标函数:

J = ∑∑||xᵢ - μₖ||²
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt

kmeans = KMeans(n_clusters=3, random_state=42)
labels = kmeans.fit_predict(X)
centers = kmeans.cluster_centers_

# 可视化
plt.scatter(X[:, 0], X[:, 1], c=labels)
plt.scatter(centers[:, 0], centers[:, 1], marker='x', s=200, c='red')
plt.show()

DBSCAN

基于密度的聚类,可发现任意形状的簇,无需指定K值。

核心参数:

  • eps: 邻域半径
  • min_samples: 核心点最小邻居数
from sklearn.cluster import DBSCAN

dbscan = DBSCAN(eps=0.5, min_samples=5)
labels = dbscan.fit_predict(X)

2.2 降维算法

PCA (主成分分析)

通过正交变换将数据投影到方差最大的方向。

步骤:

  1. 数据中心化
  2. 计算协方差矩阵
  3. 特征值分解
  4. 选择前k个主成分
from sklearn.decomposition import PCA

pca = PCA(n_components=2)
X_reduced = pca.fit_transform(X)
print(f"解释方差比: {pca.explained_variance_ratio_}")

t-SNE

非线性降维,适合可视化高维数据。

from sklearn.manifold import TSNE

tsne = TSNE(n_components=2, perplexity=30, random_state=42)
X_embedded = tsne.fit_transform(X)

3. 强化学习

3.1 Q-Learning

Q值更新公式:

Q(s,a) ← Q(s,a) + α[r + γ·max Q(s',a') - Q(s,a)]
  • α: 学习率
  • γ: 折扣因子
  • r: 即时奖励

3.2 DQN (Deep Q-Network)

使用神经网络近似Q值函数,引入经验回放和目标网络。

关键技术:

  • Experience Replay: 打破数据相关性
  • Target Network: 稳定训练

3.3 PPO (Proximal Policy Optimization)

目标函数:

L = E[min(rₜ(θ)·Âₜ, clip(rₜ(θ), 1-ε, 1+ε)·Âₜ)]

其中 rₜ(θ) = πθ(aₜ|sₜ) / πθ_old(aₜ|sₜ)

4. 评估指标

4.1 分类指标

混淆矩阵:

预测正例预测负例
实际正例TPFN
实际负例FPTN

核心指标:

准确率 Accuracy = (TP + TN) / (TP + TN + FP + FN)
精确率 Precision = TP / (TP + FP)
召回率 Recall = TP / (TP + FN)
F1-Score = 2 × (Precision × Recall) / (Precision + Recall)

ROC-AUC:

  • ROC曲线: TPR vs FPR
  • AUC: ROC曲线下面积,越接近1越好
from sklearn.metrics import classification_report, roc_auc_score, confusion_matrix

y_pred = model.predict(X_test)
y_prob = model.predict_proba(X_test)[:, 1]

print(classification_report(y_test, y_pred))
print(f"AUC: {roc_auc_score(y_test, y_prob):.4f}")
print(f"混淆矩阵:\n{confusion_matrix(y_test, y_pred)}")

4.2 回归指标

MAE (平均绝对误差) = 1/n ∑|yᵢ - ŷᵢ|
MSE (均方误差) = 1/n ∑(yᵢ - ŷᵢ)²
RMSE (均方根误差) = √MSE
R² (决定系数) = 1 - ∑(yᵢ - ŷᵢ)² / ∑(yᵢ - ȳ)²

5. 过拟合与欠拟合

5.1 过拟合 (Overfitting)

表现:

  • 训练集表现好,测试集表现差
  • 模型过于复杂,学习到噪声

原因:

  • 模型复杂度过高
  • 训练数据太少
  • 训练时间过长

解决方案:

正则化

L1正则化:

from sklearn.linear_model import Lasso
model = Lasso(alpha=0.1)  # alpha控制正则化强度

L2正则化:

from sklearn.linear_model import Ridge
model = Ridge(alpha=1.0)

Dropout

import torch.nn as nn

class Net(nn.Module):
    def __init__(self):
        super().__init__()
        self.fc1 = nn.Linear(100, 50)
        self.dropout = nn.Dropout(0.5)  # 50%的神经元随机失活
        self.fc2 = nn.Linear(50, 10)

    def forward(self, x):
        x = torch.relu(self.fc1(x))
        x = self.dropout(x)
        x = self.fc2(x)
        return x

Early Stopping

from sklearn.model_selection import train_test_split

X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2)

best_score = 0
patience = 10
counter = 0

for epoch in range(100):
    model.fit(X_train, y_train)
    val_score = model.score(X_val, y_val)

    if val_score > best_score:
        best_score = val_score
        counter = 0
    else:
        counter += 1

    if counter >= patience:
        print(f"Early stopping at epoch {epoch}")
        break

其他方法:

  • 数据增强
  • 集成学习
  • 简化模型

5.2 欠拟合 (Underfitting)

表现:

  • 训练集和测试集表现都差
  • 模型过于简单

解决方案:

  • 增加模型复杂度
  • 增加特征
  • 减少正则化
  • 增加训练时间

6. 高频面试题

面试题1: 解释偏差-方差权衡

答案:

偏差-方差权衡是机器学习中的核心概念:

总误差分解:

E[(y - ŷ)²] = Bias² + Variance + Irreducible Error
  • 偏差(Bias): 模型预测的期望值与真实值的差距,反映模型的拟合能力

    • 高偏差→欠拟合→模型过于简单
  • 方差(Variance): 模型在不同训练集上预测值的变化程度,反映模型的稳定性

    • 高方差→过拟合→模型过于复杂
  • 不可约误差: 数据本身的噪声

权衡策略:

  • 简单模型(如线性回归): 高偏差、低方差
  • 复杂模型(如深度神经网络): 低偏差、高方差
  • 目标: 找到偏差和方差的最佳平衡点

面试题2: L1和L2正则化的区别

答案:

特性L1正则化(Lasso)L2正则化(Ridge)
公式λ∑|wᵢ|λ∑wᵢ²
梯度λ·sign(w)2λw
特征选择可将部分权重压缩到0权重趋近但不等于0
稀疏性产生稀疏解不产生稀疏解
应用场景特征选择,高维稀疏数据多重共线性,防止过拟合
求解无解析解,需数值优化有解析解

代码对比:

from sklearn.linear_model import Lasso, Ridge

# L1正则化 - 部分系数为0
lasso = Lasso(alpha=0.1)
lasso.fit(X_train, y_train)
print(f"非零系数数量: {np.sum(lasso.coef_ != 0)}")

# L2正则化 - 系数接近0但不为0
ridge = Ridge(alpha=0.1)
ridge.fit(X_train, y_train)
print(f"系数范围: [{ridge.coef_.min():.4f}, {ridge.coef_.max():.4f}]")

面试题3: 如何选择合适的K值(K-Means)

答案:

方法1: 肘部法则(Elbow Method)

from sklearn.cluster import KMeans
import matplotlib.pyplot as plt

inertias = []
K_range = range(1, 11)

for k in K_range:
    kmeans = KMeans(n_clusters=k, random_state=42)
    kmeans.fit(X)
    inertias.append(kmeans.inertia_)

plt.plot(K_range, inertias, 'bo-')
plt.xlabel('K值')
plt.ylabel('簇内误差平方和')
plt.title('肘部法则')
plt.show()

方法2: 轮廓系数(Silhouette Score)

from sklearn.metrics import silhouette_score

for k in range(2, 11):
    kmeans = KMeans(n_clusters=k, random_state=42)
    labels = kmeans.fit_predict(X)
    score = silhouette_score(X, labels)
    print(f"K={k}, Silhouette Score={score:.4f}")

轮廓系数范围[-1, 1],越接近1越好。

方法3: Gap Statistic

比较聚类结果与随机数据的差异。

选择建议:

  • 结合业务场景
  • 考虑计算成本
  • 可视化验证
  • 交叉验证

面试题4: 梯度消失和梯度爆炸的原因及解决方案

答案:

梯度消失:

链式法则导致梯度连乘:

∂L/∂w₁ = ∂L/∂a_n × ∂a_n/∂a_{n-1} × ... × ∂a_2/∂a_1 × ∂a_1/∂w₁

当激活函数导数<1时(如Sigmoid: σ'(x)≤0.25),连乘导致梯度趋近0。

解决方案:

  1. 使用ReLU激活函数
# ReLU梯度: x>0时为1, x≤0时为0
nn.ReLU()
  1. 批归一化(Batch Normalization)
nn.BatchNorm1d(num_features)
  1. 残差连接(ResNet)
class ResidualBlock(nn.Module):
    def forward(self, x):
        return x + self.conv_block(x)  # 跳跃连接
  1. LSTM/GRU (RNN专用)

梯度爆炸:

梯度连乘时值>1导致指数增长。

解决方案:

  1. 梯度裁剪
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
  1. 权重正则化

  2. 降低学习率

面试题5: PCA和t-SNE的区别与应用场景

答案:

特性PCAt-SNE
类型线性降维非线性降维
原理最大化方差保持局部结构
计算复杂度O(n²d)O(n²)
可解释性主成分有明确意义无明确意义
全局结构保持良好不保证
局部结构可能丢失保持良好
距离保持欧氏距离概率分布
确定性确定随机(不同运行结果不同)

应用场景:

PCA:

  • 数据预处理和特征提取
  • 数据压缩
  • 噪声过滤
  • 需要可解释性
  • 数据量大时

t-SNE:

  • 高维数据可视化
  • 聚类结果展示
  • 探索性数据分析
  • 对局部结构敏感的任务

代码示例:

from sklearn.decomposition import PCA
from sklearn.manifold import TSNE
from sklearn.datasets import load_digits
import matplotlib.pyplot as plt

# 加载手写数字数据集
digits = load_digits()
X, y = digits.data, digits.target

# PCA降维
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X)

# t-SNE降维
tsne = TSNE(n_components=2, random_state=42)
X_tsne = tsne.fit_transform(X)

# 可视化对比
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))

ax1.scatter(X_pca[:, 0], X_pca[:, 1], c=y, cmap='tab10')
ax1.set_title('PCA')

ax2.scatter(X_tsne[:, 0], X_tsne[:, 1], c=y, cmap='tab10')
ax2.set_title('t-SNE')

plt.show()

选择建议:

  • 先用PCA快速降维和去噪
  • 再用t-SNE进行可视化
  • 大数据集先PCA到50维,再t-SNE到2维