机器学习基础
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
算法步骤:
- 随机初始化K个中心点
- 分配每个样本到最近的中心
- 更新中心为类内样本均值
- 重复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 (主成分分析)
通过正交变换将数据投影到方差最大的方向。
步骤:
- 数据中心化
- 计算协方差矩阵
- 特征值分解
- 选择前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 分类指标
混淆矩阵:
| 预测正例 | 预测负例 | |
|---|---|---|
| 实际正例 | TP | FN |
| 实际负例 | FP | TN |
核心指标:
准确率 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。
解决方案:
- 使用ReLU激活函数
# ReLU梯度: x>0时为1, x≤0时为0
nn.ReLU()
- 批归一化(Batch Normalization)
nn.BatchNorm1d(num_features)
- 残差连接(ResNet)
class ResidualBlock(nn.Module):
def forward(self, x):
return x + self.conv_block(x) # 跳跃连接
- LSTM/GRU (RNN专用)
梯度爆炸:
梯度连乘时值>1导致指数增长。
解决方案:
- 梯度裁剪
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
权重正则化
降低学习率
面试题5: PCA和t-SNE的区别与应用场景
答案:
| 特性 | PCA | t-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维