1.2 机器学习基础
虽然现代 AI 应用以大语言模型为核心,但机器学习基础仍然是理解 LLM 原理、评估模型性能、构建混合系统的关键。本文档涵盖 ML 核心概念、常见算法、模型训练全流程以及关键理论。
一、机器学习概述
1. 什么是机器学习
传统编程是规则驱动:人工编写规则,程序按规则执行。
传统编程:输入 + 规则 → 输出
机器学习:输入 + 输出 → 规则(模型)机器学习让计算机从数据中自动学习规律,无需显式编程规则。
- 训练(Training):用历史数据优化模型参数
- 推理(Inference):用训练好的模型对新数据做预测
应用场景:推荐系统、图像识别、自然语言处理、异常检测、搜索排序、自动驾驶
2. 三大学习范式
监督学习(Supervised Learning)
训练数据包含输入和标签(正确答案),模型学习从输入到标签的映射。
分类(Classification):预测离散类别
- 垃圾邮件识别:邮件 → 垃圾 / 正常
- 情感分析:文本 → 正面 / 负面 / 中性
- 图像分类:图片 → 猫 / 狗 / ...
回归(Regression):预测连续值
- 房价预测:房屋特征 → 价格
- 销量预测:历史数据 → 未来销量
- 股票趋势:市场指标 → 价格走势
典型算法:线性回归、逻辑回归、决策树、随机森林、SVM、神经网络
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、自编码器
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$
- 优化方法:梯度下降(迭代优化)或正规方程(解析解)
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})]$$
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):衡量不纯度
- 剪枝:限制树的深度,防止过拟合
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 策略,逐步提升 |
| XGBoost | GBDT 的工程优化版 | 速度快,Kaggle 竞赛常用 |
| LightGBM | 基于直方图的 GBDT | 大数据集效率高 |
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(最常用)
- 线性核:
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 个邻居的类别进行投票,无需训练过程(惰性学习)。
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(特征)}$$
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 | 输出层(多分类) |
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. 数据准备
数据收集
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()) # 查看前几行数据清洗
# --- 缺失值处理 ---
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)
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"))数据划分
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 的数据清洗可以理解为新时代的特征工程。
特征提取
# 数值特征:直接使用或计算统计量
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)特征转换
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)特征编码
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"])特征选择
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
需要可解释性 → 决策树 / 逻辑回归
文本分类 → 朴素贝叶斯 / 逻辑回归
复杂非线性关系 → 神经网络 / 集成学习超参数调优
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_}")训练技巧
# 早停(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. 模型评估
分类问题指标
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 | 不受阈值和类别比例影响 |
回归问题指标
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越好,表示模型解释了多少方差交叉验证
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. 模型保存与部署
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|$
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$
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 的优势:
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. 交叉验证深入
交叉验证的目的是更可靠地评估模型的泛化能力,避免因单次划分导致的评估偏差。
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:房价预测(回归)
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:泰坦尼克号生存预测(分类)
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:手写数字识别(图像分类)
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:信用卡欺诈检测(不平衡数据)
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 应用以大语言模型为核心,但机器学习基础在以下方面不可或缺:
- 理解 LLM 原理:神经网络、损失函数、梯度下降、训练流程是理解 LLM 工作原理的基础
- 小模型补充:某些场景用传统 ML 更高效(简单分类、异常检测、实时推荐),成本远低于 LLM
- 数据处理思维:特征工程、数据清洗的思维方式在 AI 应用(如 RAG 数据准备)中通用
- 评估方法:精确率、召回率、F1、AUC 等评估指标在 LLM 评估中同样适用
- 混合系统:实际应用常结合 LLM + 传统 ML(如 RAG 中的向量相似度搜索、意图分类路由)
学习建议:掌握核心概念和常用算法即可,无需过度深入数学推导,重点是理解原理和实践应用。