Skip to content

1.2 机器学习基础

虽然现代 AI 应用以大语言模型为核心,但机器学习基础仍然是理解 LLM 原理、评估模型性能、构建混合系统的关键。本文档涵盖 ML 核心概念、常见算法、模型训练全流程以及关键理论。


一、机器学习概述

1. 什么是机器学习

传统编程是规则驱动:人工编写规则,程序按规则执行。

传统编程:输入 + 规则 → 输出
机器学习:输入 + 输出 → 规则(模型)

机器学习让计算机从数据中自动学习规律,无需显式编程规则。

  • 训练(Training):用历史数据优化模型参数
  • 推理(Inference):用训练好的模型对新数据做预测

应用场景:推荐系统、图像识别、自然语言处理、异常检测、搜索排序、自动驾驶

2. 三大学习范式

监督学习(Supervised Learning)

训练数据包含输入和标签(正确答案),模型学习从输入到标签的映射。

分类(Classification):预测离散类别

  • 垃圾邮件识别:邮件 → 垃圾 / 正常
  • 情感分析:文本 → 正面 / 负面 / 中性
  • 图像分类:图片 → 猫 / 狗 / ...

回归(Regression):预测连续值

  • 房价预测:房屋特征 → 价格
  • 销量预测:历史数据 → 未来销量
  • 股票趋势:市场指标 → 价格走势

典型算法:线性回归、逻辑回归、决策树、随机森林、SVM、神经网络

python
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split

# 监督学习基本范式
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
model = LogisticRegression()
model.fit(X_train, y_train)           # 用标签数据训练
predictions = model.predict(X_test)   # 对新数据预测

无监督学习(Unsupervised Learning)

训练数据只有输入,没有标签,模型自己发现数据中的结构和规律。

聚类(Clustering):发现数据分组

  • 用户分群:按行为特征将用户分为不同群体
  • 图像分割:将图像分为不同区域

降维(Dimensionality Reduction):减少特征数量

  • PCA:保留方差最大的方向
  • t-SNE:高维数据可视化

异常检测(Anomaly Detection):识别异常数据点

  • 信用卡欺诈检测
  • 服务器故障预警

典型算法:K-Means、层次聚类、DBSCAN、PCA、自编码器

python
from sklearn.cluster import KMeans

# 无监督学习:不需要标签
kmeans = KMeans(n_clusters=3)
kmeans.fit(X)                   # 只有输入,没有标签
clusters = kmeans.labels_       # 模型自动发现的分组

强化学习(Reinforcement Learning)

智能体(Agent)通过与环境交互,根据奖励信号学习最优策略。

核心要素

  • 状态(State):环境的当前情况
  • 动作(Action):智能体可以采取的操作
  • 奖励(Reward):动作的反馈信号
  • 策略(Policy):状态到动作的映射

典型算法:Q-Learning、DQN、PPO

AI 应用关联:RLHF(Reinforcement Learning from Human Feedback)是训练大语言模型对齐人类偏好的关键技术——ChatGPT、Claude 等模型都经过 RLHF 训练。


二、常见机器学习算法

1. 线性回归(Linear Regression)

最简单的回归算法,拟合一条直线(或超平面)来预测连续值。

原理:$y = w_1x_1 + w_2x_2 + ... + w_nx_n + b$

核心概念

  • 权重(Weight):每个特征的重要程度
  • 偏置(Bias):截距项
  • 损失函数:均方误差 MSE = $\frac{1}{n}\sum(y_{pred} - y_{true})^2$
  • 优化方法:梯度下降(迭代优化)或正规方程(解析解)
python
import numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error, r2_score

# 生成示例数据
np.random.seed(42)
X = np.random.rand(100, 3)  # 100个样本,3个特征
y = 3 * X[:, 0] + 2 * X[:, 1] - X[:, 2] + np.random.randn(100) * 0.1

# 训练
model = LinearRegression()
model.fit(X, y)

# 查看学习到的参数
print(f"权重: {model.coef_}")        # 接近 [3, 2, -1]
print(f"偏置: {model.intercept_}")

# 评估
y_pred = model.predict(X)
print(f"MSE: {mean_squared_error(y, y_pred):.4f}")
print(f"R²:  {r2_score(y, y_pred):.4f}")

适用场景:简单的数值预测、理解特征与目标之间的线性关系

2. 逻辑回归(Logistic Regression)

虽然名字带"回归",但它是分类算法。使用 Sigmoid 函数将线性输出映射到 [0, 1] 区间,表示概率。

原理

  • 线性部分:$z = wx + b$
  • Sigmoid 变换:$\sigma(z) = \frac{1}{1 + e^{-z}}$,输出值在 [0, 1] 之间
  • 当输出 > 0.5 归为正类,否则归为负类

损失函数:交叉熵(Cross-Entropy) $$L = -\frac{1}{n}\sum[y\log(\hat{y}) + (1-y)\log(1-\hat{y})]$$

python
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report

# 加载鸢尾花数据集(多分类)
iris = load_iris()
X_train, X_test, y_train, y_test = train_test_split(
    iris.data, iris.target, test_size=0.2, random_state=42
)

# 训练
model = LogisticRegression(max_iter=200)
model.fit(X_train, y_train)

# 预测概率
probas = model.predict_proba(X_test[:3])
print(f"前3个样本的分类概率:\n{probas}")

# 评估
y_pred = model.predict(X_test)
print(classification_report(y_test, y_pred, target_names=iris.target_names))

适用场景:二分类(可扩展到多分类),如垃圾邮件分类、点击率预测、情感分析

3. 决策树(Decision Tree)

通过一系列 if-then 规则进行分层决策,像一棵倒着生长的树。

核心概念

  • 分裂标准:选择哪个特征、什么阈值来分裂节点
    • 信息增益(Information Gain):基于熵的减少
    • 基尼系数(Gini Impurity):衡量不纯度
  • 剪枝:限制树的深度,防止过拟合
python
from sklearn.tree import DecisionTreeClassifier, export_text
from sklearn.datasets import load_iris

iris = load_iris()
model = DecisionTreeClassifier(max_depth=3, random_state=42)
model.fit(iris.data, iris.target)

# 打印决策规则(可解释性强)
tree_rules = export_text(model, feature_names=iris.feature_names)
print(tree_rules)
# |--- petal width (cm) <= 0.80
# |   |--- class: 0 (setosa)
# |--- petal width (cm) >  0.80
# |   |--- petal width (cm) <= 1.75
# |   |   |--- class: 1 (versicolor)
# |   |--- petal width (cm) >  1.75
# |   |   |--- class: 2 (virginica)

# 特征重要性
for name, importance in zip(iris.feature_names, model.feature_importances_):
    print(f"{name}: {importance:.4f}")

改进算法

算法原理特点
随机森林(Random Forest)多棵决策树投票Bagging 策略,减少过拟合
GBDT每棵树拟合前一棵的残差Boosting 策略,逐步提升
XGBoostGBDT 的工程优化版速度快,Kaggle 竞赛常用
LightGBM基于直方图的 GBDT大数据集效率高
python
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier

# 随机森林
rf = RandomForestClassifier(n_estimators=100, max_depth=5, random_state=42)
rf.fit(X_train, y_train)
print(f"随机森林准确率: {rf.score(X_test, y_test):.4f}")

# 梯度提升树
gb = GradientBoostingClassifier(n_estimators=100, learning_rate=0.1, max_depth=3)
gb.fit(X_train, y_train)
print(f"GBDT 准确率: {gb.score(X_test, y_test):.4f}")

4. 支持向量机(SVM)

寻找最优超平面来分隔不同类别,使得间隔(margin)最大。

核心概念

  • 支持向量:距离分隔超平面最近的样本点
  • 间隔最大化:找到使两类之间间隔最大的超平面
  • 核技巧(Kernel Trick):将数据映射到高维空间,处理非线性问题
    • 线性核:linear
    • 多项式核:poly
    • 高斯径向基核:rbf(最常用)
python
from sklearn.svm import SVC
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline

# SVM 对特征缩放敏感,通常需要标准化
svm_pipeline = Pipeline([
    ('scaler', StandardScaler()),        # 标准化
    ('svm', SVC(kernel='rbf', C=1.0))   # 高斯核 SVM
])

svm_pipeline.fit(X_train, y_train)
print(f"SVM 准确率: {svm_pipeline.score(X_test, y_test):.4f}")

适用场景:小样本、高维数据分类(如文本分类、图像识别)

5. K-近邻(K-NN)

根据最近的 K 个邻居的类别进行投票,无需训练过程(惰性学习)。

python
from sklearn.neighbors import KNeighborsClassifier

knn = KNeighborsClassifier(n_neighbors=5, metric='euclidean')
knn.fit(X_train, y_train)
print(f"KNN 准确率: {knn.score(X_test, y_test):.4f}")

特点:简单直观,但预测时计算成本高(需要遍历所有训练样本)

AI 应用关联:向量数据库中的相似度搜索(如 RAG 中检索最相关的文档)本质上就是 K-NN 的思想。

6. 朴素贝叶斯(Naive Bayes)

基于贝叶斯定理和特征条件独立性假设:

$$P(类别|特征) = \frac{P(特征|类别) \times P(类别)}{P(特征)}$$

python
from sklearn.naive_bayes import MultinomialNB
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.pipeline import Pipeline

# 文本分类示例
texts = [
    "机器学习很有趣", "深度学习是AI的子领域",
    "今天天气真好", "明天会下雨吗",
    "神经网络可以识别图像", "PyTorch是深度学习框架",
    "周末去公园散步", "春天到了花开了",
]
labels = ["tech", "tech", "life", "life", "tech", "tech", "life", "life"]

model = Pipeline([
    ('tfidf', TfidfVectorizer()),
    ('nb', MultinomialNB())
])

model.fit(texts, labels)
print(model.predict(["Transformer模型很强大"]))  # ['tech']
print(model.predict(["今天心情不错"]))            # ['life']

优点:训练速度快,适合文本分类 适用场景:垃圾邮件过滤、情感分析、文档分类

7. 神经网络(Neural Network)

模拟人脑神经元连接,是深度学习的基础。

结构

  • 输入层:接收特征数据
  • 隐藏层:进行非线性变换(可以有多层)
  • 输出层:产出预测结果

单个神经元:$y = \text{activation}(\sum w_i x_i + b)$

激活函数

函数公式输出范围常用位置
Sigmoid$\frac{1}{1+e^{-x}}$(0, 1)输出层(二分类)
Tanh$\frac{e^x - e^{-x}}{e^x + e^{-x}}$(-1, 1)隐藏层
ReLU$\max(0, x)$[0, +∞)隐藏层(最常用)
Softmax$\frac{e^{x_i}}{\sum e^{x_j}}$(0, 1) 且和为1输出层(多分类)
python
from sklearn.neural_network import MLPClassifier

# 多层感知机
mlp = MLPClassifier(
    hidden_layer_sizes=(64, 32),   # 两个隐藏层:64和32个神经元
    activation='relu',              # ReLU 激活函数
    max_iter=500,
    random_state=42
)

mlp.fit(X_train, y_train)
print(f"神经网络准确率: {mlp.score(X_test, y_test):.4f}")

神经网络是下一节"深度学习基础"的起点,此处了解基本结构即可。


三、模型训练完整流程

一个完整的 ML 项目包含 5 个阶段:数据准备 → 特征工程 → 模型训练 → 模型评估 → 模型部署。

1. 数据准备

数据收集

python
import pandas as pd

# 从 CSV 文件加载
df = pd.read_csv("data.csv")

# 从公开数据集加载
from sklearn.datasets import fetch_california_housing
housing = fetch_california_housing(as_frame=True)
df = housing.frame
print(df.shape)       # 查看数据规模
print(df.head())      # 查看前几行

数据清洗

python
# --- 缺失值处理 ---
print(df.isnull().sum())             # 查看每列缺失数量

# 方式1:删除缺失行
df_cleaned = df.dropna()

# 方式2:填充
df["age"].fillna(df["age"].median(), inplace=True)    # 中位数填充
df["city"].fillna("Unknown", inplace=True)            # 固定值填充

# 方式3:插值(适合时间序列)
df["value"].interpolate(method='linear', inplace=True)

# --- 异常值检测 ---
import numpy as np

# Z-score 方法:|z| > 3 视为异常
z_scores = np.abs((df["price"] - df["price"].mean()) / df["price"].std())
df_no_outliers = df[z_scores < 3]

# IQR 方法(箱线图原理)
Q1 = df["price"].quantile(0.25)
Q3 = df["price"].quantile(0.75)
IQR = Q3 - Q1
mask = (df["price"] >= Q1 - 1.5 * IQR) & (df["price"] <= Q3 + 1.5 * IQR)
df_no_outliers = df[mask]

# --- 去重 ---
df.drop_duplicates(inplace=True)

数据探索(EDA)

python
import matplotlib.pyplot as plt
import seaborn as sns

# 基本统计
print(df.describe())

# 目标变量分布
plt.figure(figsize=(10, 4))
plt.subplot(1, 2, 1)
df["target"].hist(bins=30)
plt.title("目标变量分布")

# 特征相关性热力图
plt.subplot(1, 2, 2)
corr = df.corr()
sns.heatmap(corr, annot=True, cmap="coolwarm", fmt=".2f")
plt.title("特征相关性")
plt.tight_layout()
plt.show()

# 散点图:观察特征与目标的关系
sns.pairplot(df["feature1", "feature2", "feature3", "target"]("feature1", "feature2", "feature3", "target"))

数据划分

python
from sklearn.model_selection import train_test_split

X = df.drop("target", axis=1)
y = df["target"]

# 常见比例:训练集 70% / 验证集 15% / 测试集 15%
# 第一步:分出测试集
X_temp, X_test, y_temp, y_test = train_test_split(
    X, y, test_size=0.15, random_state=42
)

# 第二步:从剩余数据分出验证集
X_train, X_val, y_train, y_val = train_test_split(
    X_temp, y_temp, test_size=0.176, random_state=42  # 0.176 ≈ 0.15/0.85
)

print(f"训练集: {len(X_train)}, 验证集: {len(X_val)}, 测试集: {len(X_test)}")

注意:时间序列数据必须按时间顺序划分,不能随机打乱,否则会造成数据泄露(用未来数据预测过去)。


2. 特征工程

特征工程是传统 ML 中最重要的环节——在 LLM 时代,Prompt Engineering 和 RAG 的数据清洗可以理解为新时代的特征工程。

特征提取

python
# 数值特征:直接使用或计算统计量
df["price_per_sqft"] = df["price"] / df["area"]    # 构造新特征

# 文本特征
from sklearn.feature_extraction.text import TfidfVectorizer

# TF-IDF:将文本转为数值向量(理解 Embedding 的前置知识)
vectorizer = TfidfVectorizer(max_features=1000)
text_features = vectorizer.fit_transform(texts)

# 时间特征
df["year"] = pd.to_datetime(df["date"]).dt.year
df["month"] = pd.to_datetime(df["date"]).dt.month
df["dayofweek"] = pd.to_datetime(df["date"]).dt.dayofweek
df["is_weekend"] = df["dayofweek"].isin([5, 6]).astype(int)

特征转换

python
from sklearn.preprocessing import StandardScaler, MinMaxScaler

# 标准化(Standardization):均值为0,标准差为1
# 适用于:SVM、神经网络、逻辑回归等对尺度敏感的算法
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X_train)

# 归一化(Normalization):缩放到 [0, 1]
# 适用于:距离相关的算法(KNN)
normalizer = MinMaxScaler()
X_normalized = normalizer.fit_transform(X_train)

# 对数变换:处理右偏分布
import numpy as np
df["log_price"] = np.log1p(df["price"])  # log1p = log(1+x),避免 log(0)

特征编码

python
from sklearn.preprocessing import OneHotEncoder, LabelEncoder

# 独热编码(One-Hot):适用于无序类别(如城市、颜色)
encoder = OneHotEncoder(sparse_output=False)
city_encoded = encoder.fit_transform(df["city"]("city"))
# 北京 -> [1, 0, 0]
# 上海 -> [0, 1, 0]
# 广州 -> [0, 0, 1]

# 标签编码(Label Encoding):适用于有序类别(如等级)
le = LabelEncoder()
df["size_encoded"] = le.fit_transform(df["size"])  # S->0, M->1, L->2

# Pandas 的 get_dummies(快捷方式)
df_encoded = pd.get_dummies(df, columns=["city", "category"])

特征选择

python
from sklearn.feature_selection import SelectKBest, f_classif, RFE
from sklearn.ensemble import RandomForestClassifier

# 过滤法:按统计指标筛选 Top K 特征
selector = SelectKBest(f_classif, k=10)
X_selected = selector.fit_transform(X, y)
selected_features = X.columns[selector.get_support()]
print(f"选出的特征: {list(selected_features)}")

# 包装法:递归特征消除(RFE)
rfe = RFE(estimator=RandomForestClassifier(n_estimators=50), n_features_to_select=10)
rfe.fit(X, y)
print(f"RFE 选出的特征: {list(X.columns[rfe.support_])}")

# 嵌入法:用树模型的特征重要性
rf = RandomForestClassifier(n_estimators=100)
rf.fit(X, y)
importances = pd.Series(rf.feature_importances_, index=X.columns)
print(importances.sort_values(ascending=False).head(10))

3. 模型训练

选择算法

数据量小 + 特征少 → 逻辑回归 / SVM
数据量大 + 特征多 → 随机森林 / XGBoost
需要可解释性     → 决策树 / 逻辑回归
文本分类         → 朴素贝叶斯 / 逻辑回归
复杂非线性关系   → 神经网络 / 集成学习

超参数调优

python
from sklearn.model_selection import GridSearchCV, RandomizedSearchCV
from sklearn.ensemble import RandomForestClassifier

# 网格搜索:遍历所有参数组合(精确但慢)
param_grid = {
    'n_estimators': [50, 100, 200],
    'max_depth': [3, 5, 10, None],
    'min_samples_split': [2, 5, 10],
}

grid_search = GridSearchCV(
    RandomForestClassifier(random_state=42),
    param_grid,
    cv=5,               # 5折交叉验证
    scoring='accuracy',
    n_jobs=-1            # 使用所有CPU核心
)
grid_search.fit(X_train, y_train)

print(f"最佳参数: {grid_search.best_params_}")
print(f"最佳得分: {grid_search.best_score_:.4f}")

# 随机搜索:随机采样参数组合(快但不精确)
from scipy.stats import randint

param_dist = {
    'n_estimators': randint(50, 300),
    'max_depth': randint(3, 20),
    'min_samples_split': randint(2, 20),
}

random_search = RandomizedSearchCV(
    RandomForestClassifier(random_state=42),
    param_dist,
    n_iter=50,           # 随机尝试50种组合
    cv=5,
    scoring='accuracy',
    n_jobs=-1
)
random_search.fit(X_train, y_train)
print(f"最佳参数: {random_search.best_params_}")

训练技巧

python
# 早停(Early Stopping):在验证集性能不再提升时停止训练
from sklearn.ensemble import GradientBoostingClassifier

gb = GradientBoostingClassifier(
    n_estimators=500,
    validation_fraction=0.1,    # 10% 数据用于验证
    n_iter_no_change=10,        # 连续10轮无提升则停止
    tol=1e-4,
    random_state=42
)
gb.fit(X_train, y_train)
print(f"实际使用的树: {gb.n_estimators_}")  # 可能远小于500

# 集成学习:投票分类器
from sklearn.ensemble import VotingClassifier

voting = VotingClassifier(estimators=[
    ('rf', RandomForestClassifier(n_estimators=100)),
    ('gb', GradientBoostingClassifier(n_estimators=100)),
    ('svm', SVC(probability=True))
], voting='soft')  # soft:加权概率投票

voting.fit(X_train, y_train)
print(f"集成模型准确率: {voting.score(X_test, y_test):.4f}")

4. 模型评估

分类问题指标

python
from sklearn.metrics import (
    accuracy_score, precision_score, recall_score,
    f1_score, confusion_matrix, classification_report,
    roc_auc_score, roc_curve
)
import matplotlib.pyplot as plt

y_pred = model.predict(X_test)
y_proba = model.predict_proba(X_test)[:, 1]  # 正类概率

# 基本指标
print(f"准确率 (Accuracy):  {accuracy_score(y_test, y_pred):.4f}")
print(f"精确率 (Precision): {precision_score(y_test, y_pred):.4f}")
print(f"召回率 (Recall):    {recall_score(y_test, y_pred):.4f}")
print(f"F1 分数:            {f1_score(y_test, y_pred):.4f}")

# 完整报告
print(classification_report(y_test, y_pred))

# 混淆矩阵
#                预测正   预测负
# 实际正    [ TP       FN ]
# 实际负    [ FP       TN ]
cm = confusion_matrix(y_test, y_pred)
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues')
plt.xlabel('预测值')
plt.ylabel('真实值')
plt.title('混淆矩阵')

# ROC 曲线与 AUC
fpr, tpr, thresholds = roc_curve(y_test, y_proba)
auc = roc_auc_score(y_test, y_proba)

plt.figure()
plt.plot(fpr, tpr, label=f'ROC (AUC = {auc:.4f})')
plt.plot([0, 1], [0, 1], 'k--')  # 随机分类器的基线
plt.xlabel('假正率 (FPR)')
plt.ylabel('真正率 (TPR)')
plt.title('ROC 曲线')
plt.legend()
plt.show()

指标选择指南

场景重点指标原因
垃圾邮件过滤Precision宁可漏过垃圾邮件,也不要把正常邮件误判为垃圾
癌症筛查Recall宁可多查几个,也不要漏掉真正的患者
通用场景F1平衡精确率和召回率
样本不平衡AUC不受阈值和类别比例影响

回归问题指标

python
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
import numpy as np

y_pred = model.predict(X_test)

mse  = mean_squared_error(y_test, y_pred)       # 均方误差
rmse = np.sqrt(mse)                              # 均方根误差
mae  = mean_absolute_error(y_test, y_pred)       # 平均绝对误差
r2   = r2_score(y_test, y_pred)                  # R² 决定系数

print(f"MSE:  {mse:.4f}")
print(f"RMSE: {rmse:.4f}")   # 与目标变量同量纲,更直观
print(f"MAE:  {mae:.4f}")
print(f"R²:   {r2:.4f}")     # 越接近1越好,表示模型解释了多少方差

交叉验证

python
from sklearn.model_selection import cross_val_score, StratifiedKFold

# K 折交叉验证
scores = cross_val_score(model, X, y, cv=5, scoring='accuracy')
print(f"5折交叉验证: {scores}")
print(f"平均准确率: {scores.mean():.4f} ± {scores.std():.4f}")

# 分层K折:保持每折中各类别的比例
skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
scores = cross_val_score(model, X, y, cv=skf, scoring='f1_weighted')
print(f"分层5折 F1: {scores.mean():.4f} ± {scores.std():.4f}")

5. 模型保存与部署

python
import joblib
import pickle

# --- 保存模型 ---
# 方式1:joblib(推荐,对大 numpy 数组更高效)
joblib.dump(model, "model.pkl")

# 方式2:pickle
with open("model.pkl", "wb") as f:
    pickle.dump(model, f)

# --- 加载模型 ---
loaded_model = joblib.load("model.pkl")
predictions = loaded_model.predict(X_test)

# --- 用 FastAPI 部署为 API ---
from fastapi import FastAPI
from pydantic import BaseModel
import joblib

app = FastAPI()
model = joblib.load("model.pkl")

class PredictRequest(BaseModel):
    features: list[float]

@app.post("/predict")
async def predict(request: PredictRequest):
    import numpy as np
    X = np.array(request.features).reshape(1, -1)
    prediction = model.predict(X)[0]
    probability = model.predict_proba(X)[0].tolist()
    return {"prediction": int(prediction), "probability": probability}

四、关键概念深入

1. 过拟合与欠拟合

模型复杂度低 ──────────────────────────── 模型复杂度高
  欠拟合                最佳平衡点                过拟合
  训练差,测试差         训练好,测试好         训练好,测试差

过拟合(Overfitting)

  • 现象:训练集准确率 99%,测试集准确率 70%
  • 原因:模型过于复杂,记住了训练数据的噪声而非规律
  • 解决方法
    • 增加训练数据
    • 减少模型复杂度(减少层数、参数)
    • 正则化(L1/L2/Dropout)
    • 早停(Early Stopping)
    • 数据增强

欠拟合(Underfitting)

  • 现象:训练集和测试集准确率都很低
  • 原因:模型过于简单,无法捕捉数据中的规律
  • 解决方法
    • 增加模型复杂度
    • 增加更多特征
    • 减少正则化强度
    • 训练更多轮次

2. 正则化(Regularization)

正则化通过在损失函数中添加惩罚项,限制模型参数大小,防止过拟合。

L1 正则化(Lasso)

损失函数 + $\lambda \sum|w_i|$

python
from sklearn.linear_model import Lasso

# L1 会将不重要的特征权重压缩为 0 → 自动特征选择
lasso = Lasso(alpha=0.1)
lasso.fit(X_train, y_train)

# 查看哪些特征被选中(权重不为0)
selected = [(name, coef) for name, coef in zip(X.columns, lasso.coef_) if coef != 0]
print(f"L1 选中 {len(selected)} 个特征:")
for name, coef in selected:
    print(f"  {name}: {coef:.4f}")

L2 正则化(Ridge)

损失函数 + $\lambda \sum w_i^2$

python
from sklearn.linear_model import Ridge

# L2 使权重趋向于小但不为零 → 缓解多重共线性
ridge = Ridge(alpha=1.0)
ridge.fit(X_train, y_train)
print(f"L2 权重: {ridge.coef_}")  # 所有权重都较小,但不为零

Elastic Net

结合 L1 和 L2 的优势:

python
from sklearn.linear_model import ElasticNet

# l1_ratio=0.5 表示 L1 和 L2 各占一半
enet = ElasticNet(alpha=0.1, l1_ratio=0.5)
enet.fit(X_train, y_train)

Dropout(神经网络专用)

训练时随机丢弃一部分神经元,防止过度依赖某些特征路径。这是深度学习中最常用的正则化手段,将在 1.3 深度学习基础中详细展开。

3. 交叉验证深入

交叉验证的目的是更可靠地评估模型的泛化能力,避免因单次划分导致的评估偏差。

python
from sklearn.model_selection import (
    KFold, StratifiedKFold, TimeSeriesSplit, cross_validate
)

# --- K折交叉验证 ---
kf = KFold(n_splits=5, shuffle=True, random_state=42)
# 数据被分为5份,每次用4份训练、1份验证,轮转5次

# --- 分层K折(分类任务推荐) ---
# 保持每折中各类别比例与整体一致
skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)

# --- 时间序列交叉验证 ---
# 保持时间顺序:训练集永远在验证集之前
# Fold 1: [Train] | [Val]
# Fold 2: [Train   Train] | [Val]
# Fold 3: [Train   Train   Train] | [Val]
tscv = TimeSeriesSplit(n_splits=5)

# --- 同时评估多个指标 ---
results = cross_validate(
    model, X, y,
    cv=StratifiedKFold(n_splits=5),
    scoring=['accuracy', 'precision_weighted', 'recall_weighted', 'f1_weighted'],
    return_train_score=True
)

for metric in ['accuracy', 'precision_weighted', 'recall_weighted', 'f1_weighted']:
    train_key = f'train_{metric}'
    test_key = f'test_{metric}'
    print(f"{metric}:")
    print(f"  训练: {results[train_key].mean():.4f} ± {results[train_key].std():.4f}")
    print(f"  验证: {results[test_key].mean():.4f} ± {results[test_key].std():.4f}")

K 值选择:通常 5 或 10。K 越大评估越准确但计算成本越高。


五、实战练习

练习1:房价预测(回归)

python
import pandas as pd
import numpy as np
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression, Ridge, Lasso
from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor
from sklearn.metrics import mean_squared_error, r2_score
from sklearn.pipeline import Pipeline

# 1. 加载数据
housing = fetch_california_housing(as_frame=True)
df = housing.frame
print(f"数据集大小: {df.shape}")
print(df.describe())

# 2. 数据划分
X = df.drop("MedHouseVal", axis=1)
y = df["MedHouseVal"]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 3. 多模型对比
models = {
    "线性回归": Pipeline([('scaler', StandardScaler()), ('model', LinearRegression())]),
    "Ridge":   Pipeline([('scaler', StandardScaler()), ('model', Ridge(alpha=1.0))]),
    "Lasso":   Pipeline([('scaler', StandardScaler()), ('model', Lasso(alpha=0.01))]),
    "随机森林": RandomForestRegressor(n_estimators=100, random_state=42),
    "GBDT":    GradientBoostingRegressor(n_estimators=100, random_state=42),
}

for name, model in models.items():
    model.fit(X_train, y_train)
    y_pred = model.predict(X_test)
    rmse = np.sqrt(mean_squared_error(y_test, y_pred))
    r2 = r2_score(y_test, y_pred)
    print(f"{name:8s} | RMSE: {rmse:.4f} | R²: {r2:.4f}")

练习2:泰坦尼克号生存预测(分类)

python
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.preprocessing import StandardScaler, LabelEncoder
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report

# 1. 加载数据(从 Kaggle 下载 titanic 数据集)
df = pd.read_csv("titanic_train.csv")

# 2. 数据清洗
df["Age"].fillna(df["Age"].median(), inplace=True)
df["Embarked"].fillna(df["Embarked"].mode()[0], inplace=True)
df.drop(["PassengerId", "Name", "Ticket", "Cabin"], axis=1, inplace=True)

# 3. 特征编码
df["Sex"] = LabelEncoder().fit_transform(df["Sex"])
df = pd.get_dummies(df, columns=["Embarked"])

# 4. 特征工程
df["FamilySize"] = df["SibSp"] + df["Parch"] + 1
df["IsAlone"] = (df["FamilySize"] == 1).astype(int)

# 5. 训练与评估
X = df.drop("Survived", axis=1)
y = df["Survived"]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

rf = RandomForestClassifier(n_estimators=100, max_depth=5, random_state=42)
rf.fit(X_train, y_train)

y_pred = rf.predict(X_test)
print(classification_report(y_test, y_pred, target_names=["遇难", "幸存"]))

# 6. 特征重要性
importances = pd.Series(rf.feature_importances_, index=X.columns)
print("\n特征重要性:")
print(importances.sort_values(ascending=False))

练习3:手写数字识别(图像分类)

python
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
from sklearn.svm import SVC
from sklearn.neural_network import MLPClassifier
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.metrics import classification_report, accuracy_score

# 1. 加载 MNIST 简化版(8x8 图像)
digits = load_digits()
X, y = digits.data, digits.target
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 2. SVM 分类
svm = Pipeline([('scaler', StandardScaler()), ('svm', SVC(kernel='rbf'))])
svm.fit(X_train, y_train)
svm_pred = svm.predict(X_test)

# 3. 神经网络分类
mlp = Pipeline([
    ('scaler', StandardScaler()),
    ('mlp', MLPClassifier(hidden_layer_sizes=(128, 64), max_iter=300, random_state=42))
])
mlp.fit(X_train, y_train)
mlp_pred = mlp.predict(X_test)

# 4. 性能对比
print(f"SVM 准确率:    {accuracy_score(y_test, svm_pred):.4f}")
print(f"神经网络准确率: {accuracy_score(y_test, mlp_pred):.4f}")

练习4:信用卡欺诈检测(不平衡数据)

python
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report, roc_auc_score

# 1. 加载数据(Kaggle Credit Card Fraud 数据集)
df = pd.read_csv("creditcard.csv")
print(f"欺诈比例: {df['Class'].mean():.4%}")  # 通常不到 1%

X = df.drop("Class", axis=1)
y = df["Class"]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 2. 处理类别不平衡

# 方式1:class_weight 参数(让模型更关注少数类)
lr_balanced = LogisticRegression(class_weight='balanced', max_iter=1000)
lr_balanced.fit(X_train, y_train)

# 方式2:过采样(SMOTE)
from imblearn.over_sampling import SMOTE

smote = SMOTE(random_state=42)
X_train_resampled, y_train_resampled = smote.fit_resample(X_train, y_train)
print(f"过采样后正负比例: {pd.Series(y_train_resampled).value_counts().to_dict()}")

rf = RandomForestClassifier(n_estimators=100, random_state=42)
rf.fit(X_train_resampled, y_train_resampled)

# 3. 评估(不平衡数据用 AUC 和 F1,不要只看准确率)
for name, model in [("LR balanced", lr_balanced), ("RF+SMOTE", rf)]:
    y_pred = model.predict(X_test)
    y_proba = model.predict_proba(X_test)[:, 1]
    auc = roc_auc_score(y_test, y_proba)
    print(f"\n{name}:")
    print(f"AUC: {auc:.4f}")
    print(classification_report(y_test, y_pred, target_names=["正常", "欺诈"]))

六、学习资源

类型资源说明
课程吴恩达《机器学习》经典入门课程,讲解清晰
书籍《机器学习实战》代码实践导向,适合边学边做
书籍《统计学习方法》(李航)理论深入,适合进阶理解算法原理
工具scikit-learn 官方文档最常用的 ML 库,文档质量极高
实践Kaggle 竞赛真实数据集与竞赛,锻炼实战能力

七、ML 在 AI 应用开发中的定位

虽然现代 AI 应用以大语言模型为核心,但机器学习基础在以下方面不可或缺:

  1. 理解 LLM 原理:神经网络、损失函数、梯度下降、训练流程是理解 LLM 工作原理的基础
  2. 小模型补充:某些场景用传统 ML 更高效(简单分类、异常检测、实时推荐),成本远低于 LLM
  3. 数据处理思维:特征工程、数据清洗的思维方式在 AI 应用(如 RAG 数据准备)中通用
  4. 评估方法:精确率、召回率、F1、AUC 等评估指标在 LLM 评估中同样适用
  5. 混合系统:实际应用常结合 LLM + 传统 ML(如 RAG 中的向量相似度搜索、意图分类路由)

学习建议:掌握核心概念和常用算法即可,无需过度深入数学推导,重点是理解原理和实践应用。

坚持是一种品格