AI 系统学习指南
为有经验的程序员设计的人工智能完整学习路径 — 从数学基础到大模型应用,从理论理解到工程落地。
学习路线总览
基础筑基 (4-6周)
数学基础 + Python科学计算 + 数据处理。作为程序员你已有编程基础,重点补数学。
经典ML (4-6周)
监督/无监督学习 + 模型评估 + 特征工程 + Scikit-learn实战项目。
深度学习 & LLM (8-12周)
神经网络 + CNN/RNN + Transformer + 大模型应用 + RAG + Agent开发。这是目前就业市场最热的领域。
工程落地 & 求职 (4-6周)
MLOps + 模型部署 + 项目作品集 + 面试准备。你的工程经验在这里是巨大优势。
数学基础 — AI的底层语言
1.1 线性代数 核心
为什么线性代数是AI的基石?
神经网络的本质就是大量的矩阵乘法。一张图片是一个矩阵,一段文本经过编码后是一组向量,模型的权重也是矩阵。理解线性代数,你就能理解数据在模型中是如何流动和变换的。
1.1.1 向量(Vector)
向量是AI中最基本的数据单位。在机器学习中,每个数据样本都可以表示为一个向量。例如,一条房屋数据 [面积, 房间数, 楼层] = [120, 3, 5] 就是一个3维向量。
核心操作:
- 点积(Dot Product):衡量两个向量的相似度,是注意力机制(Attention)的核心
- 范数(Norm):向量的"长度",L1范数和L2范数在正则化中广泛使用
- 余弦相似度:用于文本相似度计算、推荐系统
Python
import numpy as np
# === 向量基础操作 ===
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
# 点积 — 两个向量的相似程度
# 在Transformer的Attention中: score = Q · K^T
dot_product = np.dot(a, b) # 1*4 + 2*5 + 3*6 = 32
# L2范数 — 向量的长度
# 在正则化(权重衰减)中防止过拟合
l2_norm = np.linalg.norm(a) # sqrt(1² + 2² + 3²) = 3.74
# 余弦相似度 — 忽略长度只看方向
# 在NLP中衡量两段文本的语义相似度
cosine_sim = np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
print(f"余弦相似度: {cosine_sim:.4f}") # 0.9746 — 非常相似
# === 实际应用示例:文本相似度 ===
# 假设这是两句话经过模型编码后的向量(embedding)
sentence_a = np.array([0.2, 0.8, 0.1, 0.5]) # "我喜欢猫"
sentence_b = np.array([0.3, 0.7, 0.2, 0.4]) # "我爱小猫"
sentence_c = np.array([0.9, 0.1, 0.8, 0.0]) # "今天天气很好"
sim_ab = np.dot(sentence_a, sentence_b) / (np.linalg.norm(sentence_a) * np.linalg.norm(sentence_b))
sim_ac = np.dot(sentence_a, sentence_c) / (np.linalg.norm(sentence_a) * np.linalg.norm(sentence_c))
print(f"'猫' vs '小猫' 相似度: {sim_ab:.4f}") # 高相似度
print(f"'猫' vs '天气' 相似度: {sim_ac:.4f}") # 低相似度
1.1.2 矩阵(Matrix)与矩阵运算
矩阵是向量的扩展。神经网络的每一层本质上就是一次矩阵乘法加一个非线性激活:y = f(Wx + b),其中W是权重矩阵。
关键运算:
- 矩阵乘法:神经网络前向传播的核心,GPU之所以适合深度学习就是因为它擅长并行矩阵运算
- 转置(Transpose):在Attention中计算Q·KT
- 逆矩阵:在最小二乘法求解线性回归中使用:w = (XTX)-1XTy
Python
import numpy as np
# === 矩阵乘法 = 神经网络的一层 ===
# 模拟一个简单的神经网络层
# 输入:batch_size=3, 特征数=4
X = np.array([
[1.0, 0.5, 0.3, 0.8], # 样本1
[0.2, 0.9, 0.7, 0.1], # 样本2
[0.6, 0.3, 0.5, 0.4], # 样本3
])
# 权重矩阵:4个输入特征 -> 2个输出特征
W = np.random.randn(4, 2) * 0.1 # 随机初始化,乘0.1防止值过大
b = np.zeros(2) # 偏置项
# 前向传播:y = X @ W + b(这就是线性层做的全部事情!)
output = X @ W + b
print(f"输入形状: {X.shape}") # (3, 4)
print(f"权重形状: {W.shape}") # (4, 2)
print(f"输出形状: {output.shape}") # (3, 2) — 3个样本,每个有2个输出特征
# === 理解矩阵形状变化(这在debug深度学习时极其重要) ===
# (batch, seq_len, d_model) @ (d_model, d_ff) = (batch, seq_len, d_ff)
# 例如 Transformer: (32, 512, 768) @ (768, 3072) = (32, 512, 3072)
batch_input = np.random.randn(32, 512, 768)
ff_weight = np.random.randn(768, 3072)
ff_output = batch_input @ ff_weight
print(f"Transformer FFN: {batch_input.shape} @ {ff_weight.shape} = {ff_output.shape}")
1.1.3 特征值与特征向量
当一个矩阵乘以某个向量后,该向量方向不变,只是长度缩放了,这个向量就是特征向量,缩放系数就是特征值。
AI中的应用:
- PCA降维:找到数据最重要的方向(最大特征值对应的特征向量)
- PageRank算法:Google搜索排名的数学基础
- 谱聚类:用图的拉普拉斯矩阵的特征向量做聚类
Python
import numpy as np
from sklearn.decomposition import PCA
import matplotlib
# matplotlib.use('Agg') # 服务器端
# === PCA: 特征值分解的经典应用 ===
# 假设有100个样本,每个有50个特征(高维数据)
np.random.seed(42)
X_high_dim = np.random.randn(100, 50)
# 用PCA降到2维 — 内部就是对协方差矩阵做特征值分解
pca = PCA(n_components=2)
X_low_dim = pca.fit_transform(X_high_dim)
print(f"降维前: {X_high_dim.shape}") # (100, 50)
print(f"降维后: {X_low_dim.shape}") # (100, 2)
print(f"保留信息比例: {pca.explained_variance_ratio_.sum():.2%}")
# === 手动理解特征值分解 ===
A = np.array([[4, 2], [1, 3]])
eigenvalues, eigenvectors = np.linalg.eig(A)
print(f"\n特征值: {eigenvalues}") # [5, 2] — 数据在这两个方向上的"能量"
print(f"特征向量:\n{eigenvectors}") # 两个方向
# 验证: A @ v = λ * v
for i in range(len(eigenvalues)):
v = eigenvectors[:, i]
lam = eigenvalues[i]
print(f"A @ v{i} = {A @ v}, λ*v{i} = {lam * v}") # 两者相等
1.2 微积分 核心
为什么需要微积分?
一句话概括:神经网络通过求导来学习。 训练模型的本质是找到一组参数使损失函数最小,而梯度下降就是用导数来指引参数更新方向的方法。
1.2.1 导数与梯度
导数告诉你"函数在某个点朝哪个方向增长最快"。对于多变量函数,所有偏导数组成的向量叫做梯度。梯度的反方向就是函数下降最快的方向。
Python
import numpy as np
# === 用数值方法理解导数 ===
def f(x):
return x**2 + 3*x + 1
# 导数的定义:f'(x) = lim(h->0) [f(x+h) - f(x)] / h
def numerical_derivative(func, x, h=1e-7):
return (func(x + h) - func(x - h)) / (2 * h) # 中心差分更精确
x = 2.0
print(f"f({x}) = {f(x)}")
print(f"f'({x}) = {numerical_derivative(f, x):.4f}") # 应该是 2*2+3 = 7
# === 梯度:多变量函数的导数 ===
# 损失函数通常依赖多个参数
def loss(w1, w2):
"""简化的损失函数:L = w1² + 2*w2² + w1*w2"""
return w1**2 + 2*w2**2 + w1*w2
def gradient(w1, w2):
"""手动求梯度:∂L/∂w1 = 2*w1 + w2, ∂L/∂w2 = 4*w2 + w1"""
dw1 = 2*w1 + w2
dw2 = 4*w2 + w1
return np.array([dw1, dw2])
# 在点(1, 1)处的梯度
grad = gradient(1, 1)
print(f"\n梯度 at (1,1): {grad}") # [3, 5] — 告诉我们w2方向变化更大
1.2.2 链式法则(Chain Rule) — 反向传播的数学基础
链式法则是整个深度学习训练的数学基石。反向传播(Backpropagation)就是链式法则在计算图上的应用。
Python
# === 链式法则的直觉 ===
# 如果 y = f(g(x)),那么 dy/dx = f'(g(x)) * g'(x)
#
# 类比编程:函数调用链
# result = outer(inner(x))
# 对x的影响 = outer对inner结果的影响 × inner对x的影响
# === 手写一个简单的自动求导引擎 ===
class Value:
"""微型自动求导引擎 — 这就是PyTorch autograd的简化版"""
def __init__(self, data, _children=(), _op=''):
self.data = data
self.grad = 0.0 # 梯度
self._backward = lambda: None # 反向传播函数
self._children = set(_children)
def __add__(self, other):
other = other if isinstance(other, Value) else Value(other)
out = Value(self.data + other.data, (self, other), '+')
def _backward():
self.grad += out.grad # ∂(a+b)/∂a = 1
other.grad += out.grad # ∂(a+b)/∂b = 1
out._backward = _backward
return out
def __mul__(self, other):
other = other if isinstance(other, Value) else Value(other)
out = Value(self.data * other.data, (self, other), '*')
def _backward():
self.grad += other.data * out.grad # ∂(a*b)/∂a = b
other.grad += self.data * out.grad # ∂(a*b)/∂b = a
out._backward = _backward
return out
def relu(self):
out = Value(max(0, self.data), (self,), 'ReLU')
def _backward():
self.grad += (1.0 if self.data > 0 else 0.0) * out.grad
out._backward = _backward
return out
def backward(self):
"""拓扑排序后反向传播 — 和PyTorch的loss.backward()一样"""
topo = []
visited = set()
def build_topo(v):
if v not in visited:
visited.add(v)
for child in v._children:
build_topo(child)
topo.append(v)
build_topo(self)
self.grad = 1.0
for v in reversed(topo):
v._backward()
# 使用示例:模拟一个神经元
x1 = Value(2.0) # 输入
w1 = Value(-3.0) # 权重
b = Value(6.0) # 偏置
# 前向传播: neuron = relu(x1*w1 + b)
n = (x1 * w1 + b).relu()
n.backward() # 反向传播
print(f"输出: {n.data}") # relu(-6+6) = relu(0) = 0
print(f"x1的梯度: {x1.grad}") # 输入的梯度
print(f"w1的梯度: {w1.grad}") # 权重的梯度 — 用来更新权重!
loss.backward() 时,PyTorch 就是在做同样的事情——沿着计算图反向传播梯度。你不需要手动求导,框架会自动完成,但理解原理能帮你调试梯度消失/爆炸等问题。
1.2.3 梯度下降(Gradient Descent)
有了梯度,我们就可以更新参数了:w = w - lr * gradient。这是所有深度学习优化的基础。
Python
import numpy as np
# === 可视化梯度下降过程 ===
def loss_fn(w):
"""模拟一个损失函数:L(w) = (w-3)² + 1"""
return (w - 3)**2 + 1
def grad_fn(w):
"""损失函数的梯度:dL/dw = 2(w-3)"""
return 2 * (w - 3)
# 梯度下降
w = 10.0 # 随机初始位置(离最优解 w=3 很远)
lr = 0.1 # 学习率
history = []
for step in range(30):
g = grad_fn(w)
w = w - lr * g # 核心公式!
history.append((w, loss_fn(w)))
if step % 5 == 0:
print(f"Step {step:2d}: w={w:.4f}, loss={loss_fn(w):.4f}, grad={g:.4f}")
# Step 0: w=8.6000, loss=32.3600, grad=14.0000
# Step 5: w=4.4694, loss=3.1592, grad=...
# ...逐渐逼近 w=3, loss=1
# === 不同学习率的影响 ===
for lr in [0.01, 0.1, 0.5, 1.0]:
w = 10.0
for _ in range(20):
w = w - lr * grad_fn(w)
status = "收敛" if abs(w - 3) < 0.1 else ("振荡" if abs(w-3) < 10 else "发散")
print(f"lr={lr}: 最终 w={w:.2f} ({status})")
# lr=0.01: 太慢,还没收敛
# lr=0.1: 刚好,接近最优
# lr=0.5: 太大,在最优解附近振荡
# lr=1.0: 发散了!
1.3 概率与统计 核心
AI中处处是概率
分类模型输出的是概率分布,语言模型预测下一个词的概率,贝叶斯方法用概率表达不确定性。理解概率是理解AI模型输出的基础。
1.3.1 概率分布
Python
import numpy as np
from scipy import stats
# === 正态分布(高斯分布) — AI中最常见的分布 ===
# 权重初始化、噪声建模、VAE的潜空间都假设正态分布
mu, sigma = 0, 1 # 标准正态分布
samples = np.random.normal(mu, sigma, 10000)
print(f"均值: {samples.mean():.4f} (理论值: {mu})")
print(f"标准差: {samples.std():.4f} (理论值: {sigma})")
# === Softmax — 将任意数值转换为概率分布 ===
# 这是分类模型最后一层做的事情
def softmax(logits):
"""将模型输出(logits)转换为概率"""
exp_logits = np.exp(logits - np.max(logits)) # 减max防溢出
return exp_logits / exp_logits.sum()
logits = np.array([2.0, 1.0, 0.1]) # 模型原始输出
probs = softmax(logits)
print(f"\n模型输出(logits): {logits}")
print(f"概率分布(softmax): {probs}")
print(f"概率总和: {probs.sum():.4f}") # 恒为1
# [0.659, 0.242, 0.099] — 模型认为第一类最可能
# === 交叉熵损失 — 分类任务的标准损失函数 ===
def cross_entropy(y_true, y_pred):
"""
衡量预测分布与真实分布的差异
y_true: one-hot标签, y_pred: 预测概率
"""
epsilon = 1e-12 # 防止log(0)
return -np.sum(y_true * np.log(y_pred + epsilon))
# 真实标签:第一类(即 [1, 0, 0])
y_true = np.array([1, 0, 0])
# 好的预测 vs 差的预测
good_pred = np.array([0.9, 0.05, 0.05])
bad_pred = np.array([0.1, 0.6, 0.3])
print(f"\n好的预测的损失: {cross_entropy(y_true, good_pred):.4f}") # 低
print(f"差的预测的损失: {cross_entropy(y_true, bad_pred):.4f}") # 高
1.3.2 贝叶斯定理
P(A|B) = P(B|A) · P(A) / P(B) — 用新证据更新我们的信念。在AI中,这是理解模型如何从数据中"学习"的哲学基础。
Python
# === 贝叶斯定理的直觉:垃圾邮件检测 ===
# 问题:收到一封包含"免费"这个词的邮件,它是垃圾邮件的概率是多少?
# 先验概率
P_spam = 0.3 # 30%的邮件是垃圾邮件
P_not_spam = 0.7 # 70%的邮件是正常邮件
# 似然
P_free_given_spam = 0.8 # 垃圾邮件中80%包含"免费"
P_free_given_not_spam = 0.1 # 正常邮件中10%包含"免费"
# 全概率公式
P_free = P_free_given_spam * P_spam + P_free_given_not_spam * P_not_spam
# 0.8 * 0.3 + 0.1 * 0.7 = 0.31
# 贝叶斯更新
P_spam_given_free = (P_free_given_spam * P_spam) / P_free
print(f"包含'免费'时是垃圾邮件的概率: {P_spam_given_free:.2%}") # 77.42%
# 先验30% → 后验77%,一个词就能大幅更新判断!
1.4 优化理论 重要
深度学习中的优化器
优化器决定了模型如何根据梯度更新参数。从最基础的SGD到现代的Adam,理解它们的区别能帮你选择合适的训练策略。
Python
import numpy as np
# === 从零实现主流优化器 ===
class SGD:
"""随机梯度下降 — 最基础的优化器"""
def __init__(self, lr=0.01):
self.lr = lr
def step(self, param, grad):
return param - self.lr * grad
class Momentum:
"""带动量的SGD — 像球滚下山坡,越滚越快"""
def __init__(self, lr=0.01, momentum=0.9):
self.lr = lr
self.momentum = momentum
self.velocity = 0
def step(self, param, grad):
self.velocity = self.momentum * self.velocity + grad
return param - self.lr * self.velocity
class Adam:
"""Adam — 目前最常用的优化器,自适应学习率"""
def __init__(self, lr=0.001, beta1=0.9, beta2=0.999, eps=1e-8):
self.lr = lr
self.beta1 = beta1 # 一阶矩衰减率
self.beta2 = beta2 # 二阶矩衰减率
self.eps = eps
self.m = 0 # 一阶矩(梯度的均值)
self.v = 0 # 二阶矩(梯度的方差)
self.t = 0 # 时间步
def step(self, param, grad):
self.t += 1
self.m = self.beta1 * self.m + (1 - self.beta1) * grad
self.v = self.beta2 * self.v + (1 - self.beta2) * grad**2
# 偏差修正(训练初期很重要)
m_hat = self.m / (1 - self.beta1**self.t)
v_hat = self.v / (1 - self.beta2**self.t)
return param - self.lr * m_hat / (np.sqrt(v_hat) + self.eps)
# === 对比三种优化器 ===
def noisy_gradient(w):
"""带噪声的梯度(模拟mini-batch随机性)"""
true_grad = 2 * (w - 5) # 最优解在w=5
return true_grad + np.random.randn() * 2 # 加噪声
for name, opt in [("SGD", SGD(0.1)), ("Momentum", Momentum(0.1)), ("Adam", Adam(0.1))]:
w = 0.0
for _ in range(100):
g = noisy_gradient(w)
w = opt.step(w, g)
print(f"{name:10s}: 最终 w = {w:.4f} (最优解: 5.0)")
# Adam通常收敛最快且最稳定
| 优化器 | 特点 | 适用场景 |
|---|---|---|
| SGD | 最基础,可能收敛慢 | 配合学习率调度可达到最优泛化 |
| SGD+Momentum | 加速收敛,减少震荡 | CV任务中广泛使用 |
| Adam | 自适应学习率,对超参不敏感 | NLP、Transformer的默认选择 |
| AdamW | Adam+权重衰减修正 | 大模型训练的标准选择 |
Python 科学计算与AI工具链
2.1 NumPy — 数值计算的基石 Python
为什么不用普通Python列表?
NumPy数组在内存中是连续存储的,支持SIMD指令集,批量操作比Python循环快100-1000倍。所有深度学习框架的底层都依赖类似NumPy的张量操作。
Python
import numpy as np
import time
# === 性能对比:NumPy vs 纯Python ===
size = 1_000_000
py_list = list(range(size))
np_array = np.arange(size)
# Python循环
start = time.time()
result_py = [x**2 for x in py_list]
py_time = time.time() - start
# NumPy向量化
start = time.time()
result_np = np_array**2
np_time = time.time() - start
print(f"Python循环: {py_time:.4f}s")
print(f"NumPy向量化: {np_time:.4f}s")
print(f"加速比: {py_time/np_time:.0f}x") # 通常50-200x
# === AI中最常用的NumPy操作 ===
# 1. 创建张量
zeros = np.zeros((3, 4)) # 全零矩阵(初始化偏置)
ones = np.ones((2, 3)) # 全一矩阵
random_normal = np.random.randn(3, 3) # 标准正态(初始化权重)
# 2. 形状操作 — 深度学习中最常见的操作之一
x = np.random.randn(32, 3, 224, 224) # batch的图像: (B, C, H, W)
print(f"原始形状: {x.shape}")
x_flat = x.reshape(32, -1) # 展平: (32, 3*224*224)
print(f"展平后: {x_flat.shape}") # (32, 150528)
x_transposed = x.transpose(0, 2, 3, 1) # (B,C,H,W) -> (B,H,W,C)
print(f"转置后: {x_transposed.shape}")
# 3. 广播机制 — 不同形状的数组如何运算
image = np.random.randn(224, 224, 3) # HWC格式的图片
mean = np.array([0.485, 0.456, 0.406]) # ImageNet均值 (3,)
std = np.array([0.229, 0.224, 0.225]) # ImageNet标准差
# 标准化:每个像素的RGB通道分别减均值除标准差
# (224,224,3) - (3,) 自动广播,等价于对每个像素都做同样操作
normalized = (image - mean) / std # 这就是图像预处理!
# 4. 索引和切片
data = np.random.randn(100, 10)
# 布尔索引(类似SQL的WHERE)
positive_rows = data[data[:, 0] > 0] # 第一列大于0的行
print(f"正值行数: {positive_rows.shape[0]}")
2.2 Pandas — 数据处理利器 Python
Python
import pandas as pd
import numpy as np
# === 机器学习的数据预处理流水线 ===
# 模拟一个真实数据集
np.random.seed(42)
n = 1000
df = pd.DataFrame({
'age': np.random.randint(18, 65, n),
'salary': np.random.normal(50000, 15000, n).astype(int),
'city': np.random.choice(['Beijing', 'Shanghai', 'Shenzhen', 'Hangzhou'], n),
'experience': np.random.randint(0, 30, n),
'purchased': np.random.choice([0, 1], n, p=[0.6, 0.4]) # 目标变量
})
# 故意制造一些缺失值(真实数据总有缺失)
df.loc[np.random.choice(n, 50), 'salary'] = np.nan
df.loc[np.random.choice(n, 30), 'age'] = np.nan
print("=== 数据概览 ===")
print(df.info())
print(f"\n缺失值:\n{df.isnull().sum()}")
# 1. 处理缺失值
df['salary'].fillna(df['salary'].median(), inplace=True) # 用中位数填充
df['age'].fillna(df['age'].mean(), inplace=True) # 用均值填充
# 2. 特征工程
df['salary_per_year'] = df['salary'] / (df['experience'] + 1) # 避免除0
df['age_group'] = pd.cut(df['age'], bins=[0, 25, 35, 50, 100],
labels=['青年', '中青年', '中年', '中老年'])
# 3. 独热编码(将分类变量转为数值)
df_encoded = pd.get_dummies(df, columns=['city'], drop_first=True)
# 4. 数据分析
print("\n=== 各城市购买率 ===")
print(df.groupby('city')['purchased'].mean().sort_values(ascending=False))
print("\n=== 相关性矩阵 ===")
numeric_cols = df.select_dtypes(include=[np.number]).columns
print(df[numeric_cols].corr()['purchased'].sort_values(ascending=False))
2.3 PyTorch — 深度学习框架 Python 最重要
为什么选PyTorch?
PyTorch已成为学术界和工业界的主流框架。几乎所有大模型(GPT、LLaMA、Stable Diffusion)都是用PyTorch构建的。它的动态计算图机制对程序员非常友好——用起来就像普通Python代码。
Python
import torch
import torch.nn as nn
# === PyTorch基础:张量(Tensor) ===
# Tensor就是支持GPU加速和自动求导的NumPy数组
# 创建张量
x = torch.randn(3, 4) # 随机张量
x_gpu = torch.randn(3, 4, device='cuda') if torch.cuda.is_available() else x # GPU张量
# 自动求导 — PyTorch最强大的功能
w = torch.randn(4, 2, requires_grad=True) # 需要计算梯度的参数
b = torch.zeros(2, requires_grad=True)
# 前向传播
y = x @ w + b # 线性层
loss = y.sum() # 假设这是损失
# 反向传播 — 一行代码完成所有梯度计算!
loss.backward()
print(f"w的梯度形状: {w.grad.shape}") # 和w一样: (4, 2)
print(f"b的梯度形状: {b.grad.shape}") # 和b一样: (2,)
# === 构建一个完整的神经网络 ===
class SimpleNet(nn.Module):
"""一个简单的分类网络"""
def __init__(self, input_dim, hidden_dim, output_dim):
super().__init__()
self.net = nn.Sequential(
nn.Linear(input_dim, hidden_dim), # 线性层
nn.ReLU(), # 激活函数
nn.Dropout(0.2), # 防过拟合
nn.Linear(hidden_dim, hidden_dim),
nn.ReLU(),
nn.Dropout(0.2),
nn.Linear(hidden_dim, output_dim),
)
def forward(self, x):
return self.net(x)
# 实例化
model = SimpleNet(input_dim=784, hidden_dim=256, output_dim=10)
print(f"模型参数量: {sum(p.numel() for p in model.parameters()):,}")
# === 完整的训练循环 ===
# 这是你会写无数遍的代码模板
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
criterion = nn.CrossEntropyLoss()
# 模拟数据
X_train = torch.randn(1000, 784)
y_train = torch.randint(0, 10, (1000,))
model.train()
for epoch in range(5):
# 前向传播
logits = model(X_train)
loss = criterion(logits, y_train)
# 反向传播
optimizer.zero_grad() # 清零梯度(必须!否则梯度会累加)
loss.backward() # 计算梯度
optimizer.step() # 更新参数
# 计算准确率
pred = logits.argmax(dim=1)
acc = (pred == y_train).float().mean()
print(f"Epoch {epoch}: loss={loss.item():.4f}, acc={acc:.2%}")
nn.Module 就是一个Python类,forward() 就是方法调用,训练循环就是一个for循环。没有什么黑魔法。你的OOP经验在这里直接适用:继承、组合、设计模式都可以用。
2.4 开发环境搭建
| 工具 | 用途 | 安装 |
|---|---|---|
| Miniconda | Python环境管理 | conda create -n ai python=3.11 |
| Jupyter Lab | 交互式开发,适合实验 | pip install jupyterlab |
| VS Code | 正式项目开发 | 安装Python + Jupyter扩展 |
| PyTorch | 深度学习框架 | pip install torch torchvision |
| Hugging Face | 预训练模型仓库 | pip install transformers datasets |
| Weights & Biases | 实验追踪 | pip install wandb |
Bash
# === 推荐的环境搭建步骤 ===
# 1. 安装 Miniconda
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
bash Miniconda3-latest-Linux-x86_64.sh
# 2. 创建AI学习环境
conda create -n ai python=3.11 -y
conda activate ai
# 3. 安装核心库
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
pip install numpy pandas matplotlib scikit-learn jupyter
pip install transformers datasets accelerate
pip install wandb tensorboard
# 4. GPU验证
python -c "import torch; print(f'CUDA可用: {torch.cuda.is_available()}')"
python -c "import torch; print(f'GPU: {torch.cuda.get_device_name(0)}')" 2>/dev/null
Jupyter Notebook —— AI 开发者的最爱
Jupyter Notebook 是一种交互式编程环境,允许你把代码、文字说明、图表混合在一个文档里。AI 研究论文的代码几乎都用 Jupyter 演示。你可以运行一个代码单元格,立刻看到结果,然后修改再运行,非常适合探索数据和调试模型。
经典机器学习
3.1 核心概念
Machine Learning 分类
├── 监督学习 (Supervised) ← 有标签数据
│ ├── 分类 (Classification) ← 输出离散类别
│ │ ├── 逻辑回归
│ │ ├── SVM
│ │ ├── 决策树/随机森林
│ │ └── 神经网络
│ └── 回归 (Regression) ← 输出连续数值
│ ├── 线性回归
│ ├── 多项式回归
│ └── 岭回归/Lasso
├── 无监督学习 (Unsupervised) ← 无标签数据
│ ├── 聚类 (Clustering)
│ │ ├── K-Means
│ │ └── DBSCAN
│ └── 降维 (Dimensionality Reduction)
│ ├── PCA
│ └── t-SNE / UMAP
└── 强化学习 (Reinforcement) ← 通过奖励学习
└── 见第八章
过拟合与欠拟合 — 机器学习的核心矛盾
这是你在AI领域会反复遇到的最重要概念:
- 欠拟合(Underfitting):模型太简单,训练集上表现就差。类比:用直线拟合曲线数据。
- 过拟合(Overfitting):模型"记住了"训练数据,但对新数据表现差。类比:背答案vs理解知识。
- 正好(Good fit):在训练集和测试集上都表现好。
Python
import numpy as np
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error
# === 直观理解过拟合 ===
np.random.seed(42)
X = np.sort(np.random.rand(30, 1) * 10, axis=0)
y = np.sin(X).ravel() + np.random.randn(30) * 0.3 # 带噪声的正弦曲线
# 训练集和测试集
X_train, X_test = X[:20], X[20:]
y_train, y_test = y[:20], y[20:]
for degree in [1, 4, 15]:
# 多项式特征
poly = PolynomialFeatures(degree)
X_poly_train = poly.fit_transform(X_train)
X_poly_test = poly.transform(X_test)
model = LinearRegression()
model.fit(X_poly_train, y_train)
train_err = mean_squared_error(y_train, model.predict(X_poly_train))
test_err = mean_squared_error(y_test, model.predict(X_poly_test))
status = "欠拟合" if degree == 1 else ("过拟合" if degree == 15 else "刚好")
print(f"degree={degree:2d}: 训练误差={train_err:.4f}, 测试误差={test_err:.4f} → {status}")
# degree=1: 训练误差高,测试误差也高 → 欠拟合
# degree=4: 训练误差低,测试误差也低 → 刚好
# degree=15: 训练误差极低,测试误差爆炸 → 过拟合
3.2 监督学习算法详解
3.2.1 线性回归 — 最基本的模型
Python
import numpy as np
from sklearn.linear_model import LinearRegression, Ridge, Lasso
# === 从零实现线性回归 ===
class MyLinearRegression:
"""用梯度下降实现线性回归 — 理解训练的本质"""
def __init__(self, lr=0.01, epochs=1000):
self.lr = lr
self.epochs = epochs
def fit(self, X, y):
n_samples, n_features = X.shape
self.weights = np.zeros(n_features)
self.bias = 0
for epoch in range(self.epochs):
# 前向传播:y_pred = X @ w + b
y_pred = X @ self.weights + self.bias
# 计算梯度(MSE损失对w和b的偏导)
dw = (2/n_samples) * X.T @ (y_pred - y)
db = (2/n_samples) * np.sum(y_pred - y)
# 更新参数
self.weights -= self.lr * dw
self.bias -= self.lr * db
return self
def predict(self, X):
return X @ self.weights + self.bias
# 生成数据
np.random.seed(42)
X = np.random.randn(100, 3)
true_w = np.array([2.0, -1.0, 0.5])
y = X @ true_w + 3.0 + np.random.randn(100) * 0.1
# 用我们自己的实现
my_model = MyLinearRegression(lr=0.1, epochs=1000).fit(X, y)
print(f"自己实现: w={my_model.weights}, b={my_model.bias:.2f}")
# 用sklearn验证
sk_model = LinearRegression().fit(X, y)
print(f"sklearn: w={sk_model.coef_}, b={sk_model.intercept_:.2f}")
# 两者结果应该非常接近
3.2.2 逻辑回归 — 分类的基石
名字叫"回归",实际上是最经典的分类算法。通过Sigmoid函数将线性输出映射到[0,1]概率。
Python
import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, classification_report
# === 理解逻辑回归 ===
def sigmoid(z):
"""将任意值压缩到(0,1) — 解释为概率"""
return 1 / (1 + np.exp(-z))
# Sigmoid的特性
z = np.array([-5, -2, 0, 2, 5])
print("z :", z)
print("sigmoid :", np.round(sigmoid(z), 3))
# z=-5→0.007, z=0→0.5, z=5→0.993
# 越大的z → 越接近1(越确定是正类)
# === 实际分类任务 ===
X, y = make_classification(n_samples=1000, n_features=20,
n_informative=10, 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)
y_pred = model.predict(X_test)
print(f"\n准确率: {accuracy_score(y_test, y_pred):.2%}")
print(classification_report(y_test, y_pred))
3.2.3 决策树与随机森林
Python
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_iris
from sklearn.model_selection import cross_val_score
# === 决策树 — 最直观的模型 ===
iris = load_iris()
X, y = iris.data, iris.target
# 单棵决策树(容易过拟合)
tree = DecisionTreeClassifier(max_depth=3, random_state=42)
scores = cross_val_score(tree, X, y, cv=5)
print(f"决策树: {scores.mean():.2%} ± {scores.std():.2%}")
# 随机森林(多棵树投票,大幅减少过拟合)
forest = RandomForestClassifier(n_estimators=100, max_depth=5, random_state=42)
scores = cross_val_score(forest, X, y, cv=5)
print(f"随机森林: {scores.mean():.2%} ± {scores.std():.2%}")
# === 特征重要性 — 理解模型在看什么 ===
forest.fit(X, y)
importance = forest.feature_importances_
for name, imp in sorted(zip(iris.feature_names, importance), key=lambda x: -x[1]):
bar = '█' * int(imp * 50)
print(f" {name:20s}: {imp:.3f} {bar}")
# 花瓣长度和宽度最重要,这与领域知识一致
3.2.4 支持向量机(SVM)
SVM的核心思想:找到一个超平面,使两类数据的间隔最大化。通过核技巧(kernel trick)可以处理非线性数据。
Python
from sklearn.svm import SVC
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
# SVM对特征缩放敏感,所以总是要标准化
svm_pipeline = Pipeline([
('scaler', StandardScaler()), # 标准化:均值0,方差1
('svm', SVC(kernel='rbf', C=1.0)) # RBF核,C是正则化参数
])
scores = cross_val_score(svm_pipeline, X, y, cv=5)
print(f"SVM(RBF): {scores.mean():.2%} ± {scores.std():.2%}")
# 不同核函数对比
for kernel in ['linear', 'poly', 'rbf']:
pipe = Pipeline([('scaler', StandardScaler()), ('svm', SVC(kernel=kernel))])
scores = cross_val_score(pipe, X, y, cv=5)
print(f" {kernel:8s}: {scores.mean():.2%}")
3.3 无监督学习
3.3.1 K-Means 聚类
Python
import numpy as np
from sklearn.cluster import KMeans, DBSCAN
from sklearn.datasets import make_blobs
# === 从零实现K-Means ===
class MyKMeans:
def __init__(self, k=3, max_iters=100):
self.k = k
self.max_iters = max_iters
def fit(self, X):
n_samples = X.shape[0]
# 随机初始化中心点
idx = np.random.choice(n_samples, self.k, replace=False)
self.centers = X[idx].copy()
for _ in range(self.max_iters):
# 1. 分配:每个点归到最近的中心
distances = np.linalg.norm(X[:, np.newaxis] - self.centers, axis=2)
self.labels = distances.argmin(axis=1)
# 2. 更新:重新计算中心
new_centers = np.array([X[self.labels == i].mean(axis=0) for i in range(self.k)])
# 3. 收敛检查
if np.allclose(new_centers, self.centers):
break
self.centers = new_centers
return self
# 生成聚类数据
X, y_true = make_blobs(n_samples=300, centers=4, cluster_std=0.6, random_state=42)
# 使用自己的实现
my_km = MyKMeans(k=4).fit(X)
print(f"自实现K-Means: 找到 {len(np.unique(my_km.labels))} 个类")
# 如何选择K?—— 肘部法则
inertias = []
for k in range(1, 10):
km = KMeans(n_clusters=k, random_state=42, n_init=10).fit(X)
inertias.append(km.inertia_)
print(f" K={k}: inertia={km.inertia_:.1f}")
# inertia在K=4时出现明显拐点("肘部")
3.4 模型评估 重要
不要只看准确率!
在不平衡数据集上(如欺诈检测:99%正常,1%欺诈),一个永远预测"正常"的模型也有99%准确率。必须理解更全面的评估指标。
| 指标 | 含义 | 何时关注 |
|---|---|---|
| Accuracy | 总体正确率 | 类别均衡时 |
| Precision | 预测为正的有多少真是正的 | 关注误报(如垃圾邮件) |
| Recall | 真正为正的有多少被找出来 | 关注漏报(如癌症检测) |
| F1-Score | Precision和Recall的调和平均 | 需要平衡两者时 |
| AUC-ROC | 分类阈值无关的整体性能 | 比较不同模型 |
Python
from sklearn.metrics import (confusion_matrix, classification_report,
roc_auc_score, precision_recall_curve)
from sklearn.model_selection import cross_val_score, GridSearchCV
import numpy as np
# === 混淆矩阵 — 最直观的评估方式 ===
y_true = np.array([1,1,1,1,1,0,0,0,0,0])
y_pred = np.array([1,1,1,0,0,0,0,0,1,1])
cm = confusion_matrix(y_true, y_pred)
print("混淆矩阵:")
print(f" TN={cm[0][0]} FP={cm[0][1]}")
print(f" FN={cm[1][0]} TP={cm[1][1]}")
print(f"\n{classification_report(y_true, y_pred)}")
# === 交叉验证 — 可靠的模型评估 ===
from sklearn.datasets import make_classification
from sklearn.ensemble import RandomForestClassifier
X, y = make_classification(n_samples=1000, random_state=42)
model = RandomForestClassifier(n_estimators=100, random_state=42)
# 5折交叉验证
scores = cross_val_score(model, X, y, cv=5, scoring='f1')
print(f"\n5折交叉验证 F1: {scores.mean():.4f} ± {scores.std():.4f}")
# === 超参数搜索 ===
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=3, scoring='f1', n_jobs=-1)
grid_search.fit(X, y)
print(f"最优参数: {grid_search.best_params_}")
print(f"最优F1: {grid_search.best_score_:.4f}")
3.5 集成学习 — 三个臭皮匠赛过诸葛亮
Python
from sklearn.ensemble import (GradientBoostingClassifier, AdaBoostClassifier,
VotingClassifier, StackingClassifier)
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
from sklearn.tree import DecisionTreeClassifier
import xgboost as xgb # pip install xgboost
# === XGBoost — 结构化数据的王者 ===
# 在Kaggle竞赛中,XGBoost/LightGBM几乎统治了所有表格数据竞赛
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)
xgb_model = xgb.XGBClassifier(
n_estimators=100,
max_depth=6,
learning_rate=0.1,
subsample=0.8, # 每棵树用80%数据
colsample_bytree=0.8, # 每棵树用80%特征
eval_metric='logloss',
random_state=42,
)
xgb_model.fit(X_train, y_train)
print(f"XGBoost准确率: {xgb_model.score(X_test, y_test):.2%}")
# === 模型融合(Stacking) ===
estimators = [
('rf', RandomForestClassifier(n_estimators=100)),
('xgb', xgb.XGBClassifier(n_estimators=100, eval_metric='logloss')),
('svm', SVC(probability=True)),
]
stacking = StackingClassifier(
estimators=estimators,
final_estimator=LogisticRegression(), # 用逻辑回归融合
cv=5
)
scores = cross_val_score(stacking, X, y, cv=5, scoring='accuracy')
print(f"Stacking准确率: {scores.mean():.2%}")
深度学习
4.1 神经网络基础
从生物到数学
一个人工神经元做三件事:(1) 接收输入的加权和,(2) 加一个偏置,(3) 通过激活函数输出。数学表达:y = activation(Wx + b)。多个神经元排列成层,多层堆叠就是深度神经网络。
激活函数 — 引入非线性
没有激活函数,无论多少层的神经网络都等价于一个线性变换。激活函数让网络能够拟合任意复杂的函数。
Python
import torch
import torch.nn as nn
import numpy as np
# === 主流激活函数对比 ===
x = torch.linspace(-5, 5, 100)
activations = {
'Sigmoid': torch.sigmoid(x), # 输出(0,1),用于二分类输出层
'Tanh': torch.tanh(x), # 输出(-1,1),RNN中常用
'ReLU': torch.relu(x), # max(0,x),最常用,计算简单
'LeakyReLU': nn.LeakyReLU(0.1)(x), # 解决ReLU的"死神经元"问题
'GELU': nn.GELU()(x), # Transformer中的标准选择
'SiLU/Swish': nn.SiLU()(x), # LLaMA等现代模型使用
}
for name, y in activations.items():
print(f"{name:12s}: x=-2 → {y[20]:.3f}, x=0 → {y[50]:.3f}, x=2 → {y[80]:.3f}")
# === 为什么ReLU比Sigmoid好? ===
# 1. Sigmoid的梯度在两端趋近0(梯度消失),深层网络无法学习
# 2. ReLU的正区间梯度恒为1,梯度可以顺畅传播
# 3. ReLU计算简单(一个max操作),速度快
# === 从零构建多层感知器(MLP) ===
class MLP(nn.Module):
def __init__(self, layer_sizes):
"""
layer_sizes: [784, 512, 256, 10] 表示4层网络
"""
super().__init__()
layers = []
for i in range(len(layer_sizes) - 1):
layers.append(nn.Linear(layer_sizes[i], layer_sizes[i+1]))
if i < len(layer_sizes) - 2: # 最后一层不加激活
layers.append(nn.GELU())
layers.append(nn.LayerNorm(layer_sizes[i+1])) # 层归一化
layers.append(nn.Dropout(0.1))
self.network = nn.Sequential(*layers)
def forward(self, x):
return self.network(x)
model = MLP([784, 512, 256, 128, 10])
print(f"模型结构:\n{model}")
print(f"\n总参数量: {sum(p.numel() for p in model.parameters()):,}")
4.2 卷积神经网络(CNN) — 图像处理的基石
核心思想:局部感知 + 参数共享
卷积核(filter)像一个小窗口在图像上滑动,检测局部特征(边缘、纹理、形状)。浅层检测简单特征(线条),深层组合出复杂特征(人脸、物体)。
输入图像 (3,224,224)
↓ Conv2d(3→64, 3×3) + ReLU + MaxPool
特征图 (64,112,112) ← 检测边缘、颜色
↓ Conv2d(64→128, 3×3) + ReLU + MaxPool
特征图 (128,56,56) ← 检测纹理、角点
↓ Conv2d(128→256, 3×3) + ReLU + MaxPool
特征图 (256,28,28) ← 检测部件(眼睛、轮子)
↓ Conv2d(256→512, 3×3) + ReLU + MaxPool
特征图 (512,14,14) ← 检测整体对象
↓ AdaptiveAvgPool → Flatten
向量 (512)
↓ Linear(512→num_classes)
输出 (num_classes) ← 分类概率
Python
import torch
import torch.nn as nn
import torch.nn.functional as F
# === 理解卷积操作 ===
# 手动卷积
image = torch.randn(1, 1, 8, 8) # (batch, channels, H, W)
# 边缘检测核
edge_kernel = torch.tensor([
[-1, -1, -1],
[-1, 8, -1],
[-1, -1, -1]
], dtype=torch.float32).reshape(1, 1, 3, 3)
# 手动卷积
edges = F.conv2d(image, edge_kernel, padding=1)
print(f"输入: {image.shape} → 边缘检测后: {edges.shape}")
# === 现代CNN架构:类ResNet ===
class ResidualBlock(nn.Module):
"""残差块 — ResNet的核心创新
关键洞察:让网络学习"残差"(变化量)比学习完整映射更容易
output = F(x) + x ← 即使F学不好,至少有x兜底
"""
def __init__(self, channels):
super().__init__()
self.conv1 = nn.Conv2d(channels, channels, 3, padding=1)
self.bn1 = nn.BatchNorm2d(channels)
self.conv2 = nn.Conv2d(channels, channels, 3, padding=1)
self.bn2 = nn.BatchNorm2d(channels)
def forward(self, x):
residual = x # 保存输入
out = F.relu(self.bn1(self.conv1(x)))
out = self.bn2(self.conv2(out))
out = F.relu(out + residual) # 残差连接!
return out
class SimpleCNN(nn.Module):
"""一个实用的图像分类网络"""
def __init__(self, num_classes=10):
super().__init__()
self.features = nn.Sequential(
# Stage 1
nn.Conv2d(3, 64, 3, padding=1),
nn.BatchNorm2d(64),
nn.ReLU(),
nn.MaxPool2d(2),
ResidualBlock(64),
# Stage 2
nn.Conv2d(64, 128, 3, padding=1),
nn.BatchNorm2d(128),
nn.ReLU(),
nn.MaxPool2d(2),
ResidualBlock(128),
# Stage 3
nn.Conv2d(128, 256, 3, padding=1),
nn.BatchNorm2d(256),
nn.ReLU(),
nn.AdaptiveAvgPool2d(1), # 全局平均池化 → (B, 256, 1, 1)
)
self.classifier = nn.Linear(256, num_classes)
def forward(self, x):
x = self.features(x)
x = x.view(x.size(0), -1) # 展平
return self.classifier(x)
model = SimpleCNN(num_classes=10)
dummy = torch.randn(2, 3, 32, 32) # 2张32x32的RGB图片
output = model(dummy)
print(f"输入: {dummy.shape} → 输出: {output.shape}") # (2, 10)
print(f"参数量: {sum(p.numel() for p in model.parameters()):,}")
4.3 RNN & LSTM — 序列建模
核心思想:隐藏状态传递信息
RNN通过隐藏状态ht在时间步之间传递信息。但朴素RNN有严重的梯度消失问题(长序列中早期信息会被"遗忘"),LSTM通过门控机制解决了这个问题。
Python
import torch
import torch.nn as nn
# === 从零理解RNN ===
class SimpleRNN(nn.Module):
"""手写RNN — 理解隐藏状态的传递"""
def __init__(self, input_size, hidden_size):
super().__init__()
self.hidden_size = hidden_size
# RNN只有两个矩阵:处理输入的和处理隐藏状态的
self.W_ih = nn.Linear(input_size, hidden_size) # 输入→隐藏
self.W_hh = nn.Linear(hidden_size, hidden_size) # 隐藏→隐藏
def forward(self, x):
# x shape: (batch, seq_len, input_size)
batch_size, seq_len, _ = x.shape
h = torch.zeros(batch_size, self.hidden_size) # 初始隐藏状态
outputs = []
for t in range(seq_len):
# h_t = tanh(W_ih @ x_t + W_hh @ h_{t-1})
h = torch.tanh(self.W_ih(x[:, t]) + self.W_hh(h))
outputs.append(h)
return torch.stack(outputs, dim=1), h # (batch, seq_len, hidden), (batch, hidden)
# === LSTM — 序列任务的可靠选择 ===
class TextClassifier(nn.Module):
"""基于LSTM的文本分类器"""
def __init__(self, vocab_size, embed_dim, hidden_dim, num_classes):
super().__init__()
self.embedding = nn.Embedding(vocab_size, embed_dim)
self.lstm = nn.LSTM(embed_dim, hidden_dim, num_layers=2,
batch_first=True, dropout=0.3, bidirectional=True)
self.classifier = nn.Linear(hidden_dim * 2, num_classes) # *2因为双向
def forward(self, x):
emb = self.embedding(x) # (batch, seq_len, embed_dim)
output, (h_n, c_n) = self.lstm(emb) # h_n: (4, batch, hidden)
# 拼接正向和反向的最后隐藏状态
hidden = torch.cat([h_n[-2], h_n[-1]], dim=1)
return self.classifier(hidden)
model = TextClassifier(vocab_size=10000, embed_dim=128, hidden_dim=256, num_classes=5)
dummy_text = torch.randint(0, 10000, (8, 50)) # 8个样本,每个50个token
output = model(dummy_text)
print(f"输入: {dummy_text.shape} → 输出: {output.shape}") # (8, 5)
4.4 Transformer — 现代AI的基石 最重要
为什么Transformer改变了一切?
2017年Google的论文"Attention Is All You Need"提出了Transformer架构。它解决了RNN的两大问题:(1) 无法并行计算,(2) 长距离依赖难以捕捉。几乎所有现代大模型(GPT、BERT、LLaMA、Stable Diffusion)都基于Transformer。
Transformer 架构(以GPT为例,仅Decoder部分)
输入 tokens: ["我", "喜欢", "学习", "AI"]
↓
[Token Embedding] + [Positional Encoding] ← 词义 + 位置信息
↓
┌─────────────────────────────────────┐
│ Transformer Block (× N层) │
│ │
│ ┌─────────────────────────────┐ │
│ │ Multi-Head Self-Attention │ │ ← 每个token关注所有其他token
│ │ Q = XW_Q, K = XW_K, V = XW_V │ │
│ │ Attention = softmax(QK^T/√d)V │ │
│ └──────────┬──────────────────┘ │
│ ↓ + Residual + LayerNorm│ ← 残差连接
│ ┌─────────────────────────────┐ │
│ │ Feed-Forward Network │ │ ← 两层MLP + 激活函数
│ │ FFN(x) = GELU(xW₁+b₁)W₂+b₂ │ │
│ └──────────┬──────────────────┘ │
│ ↓ + Residual + LayerNorm│
└─────────────────────────────────────┘
↓
[Linear + Softmax]
↓
下一个词的概率分布 → 预测 "很"
Python
import torch
import torch.nn as nn
import torch.nn.functional as F
import math
# === 从零实现 Self-Attention — Transformer的核心 ===
class SelfAttention(nn.Module):
"""
自注意力机制 — 让每个token"看到"序列中的所有其他token
类比:开会时每个人都能听到所有人发言,然后各自总结出最相关的信息
"""
def __init__(self, d_model, n_heads):
super().__init__()
assert d_model % n_heads == 0
self.d_model = d_model
self.n_heads = n_heads
self.d_k = d_model // n_heads # 每个头的维度
self.W_q = nn.Linear(d_model, d_model) # Query: "我在找什么?"
self.W_k = nn.Linear(d_model, d_model) # Key: "我有什么信息?"
self.W_v = nn.Linear(d_model, d_model) # Value: "我的具体内容是什么?"
self.W_o = nn.Linear(d_model, d_model) # 输出投影
def forward(self, x, mask=None):
batch_size, seq_len, _ = x.shape
# 1. 线性投影得到Q, K, V
Q = self.W_q(x) # (B, seq_len, d_model)
K = self.W_k(x)
V = self.W_v(x)
# 2. 拆分成多个头 (B, seq_len, d_model) → (B, n_heads, seq_len, d_k)
Q = Q.view(batch_size, seq_len, self.n_heads, self.d_k).transpose(1, 2)
K = K.view(batch_size, seq_len, self.n_heads, self.d_k).transpose(1, 2)
V = V.view(batch_size, seq_len, self.n_heads, self.d_k).transpose(1, 2)
# 3. 计算注意力分数: score = Q @ K^T / sqrt(d_k)
scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(self.d_k)
# scores shape: (B, n_heads, seq_len, seq_len)
# scores[i][h][a][b] = token_a对token_b的关注程度
# 4. 因果掩码(GPT风格:只能看到之前的token)
if mask is not None:
scores = scores.masked_fill(mask == 0, float('-inf'))
# 5. Softmax归一化 → 注意力权重
attn_weights = F.softmax(scores, dim=-1)
# 6. 加权求和
context = torch.matmul(attn_weights, V) # (B, n_heads, seq_len, d_k)
# 7. 合并多头
context = context.transpose(1, 2).contiguous().view(batch_size, seq_len, self.d_model)
return self.W_o(context)
# === 完整的 Transformer Block ===
class TransformerBlock(nn.Module):
def __init__(self, d_model, n_heads, d_ff, dropout=0.1):
super().__init__()
self.attention = SelfAttention(d_model, n_heads)
self.norm1 = nn.LayerNorm(d_model)
self.norm2 = nn.LayerNorm(d_model)
self.ffn = nn.Sequential(
nn.Linear(d_model, d_ff),
nn.GELU(),
nn.Linear(d_ff, d_model),
nn.Dropout(dropout),
)
self.dropout = nn.Dropout(dropout)
def forward(self, x, mask=None):
# Pre-Norm 架构(LLaMA/GPT-3风格)
attn_out = self.attention(self.norm1(x), mask)
x = x + self.dropout(attn_out) # 残差连接
ffn_out = self.ffn(self.norm2(x))
x = x + self.dropout(ffn_out) # 残差连接
return x
# === Mini GPT — 一个完整的语言模型 ===
class MiniGPT(nn.Module):
def __init__(self, vocab_size, d_model=256, n_heads=8, n_layers=6,
max_seq_len=512, d_ff=1024):
super().__init__()
self.token_emb = nn.Embedding(vocab_size, d_model)
self.pos_emb = nn.Embedding(max_seq_len, d_model)
self.blocks = nn.ModuleList([
TransformerBlock(d_model, n_heads, d_ff) for _ in range(n_layers)
])
self.ln_f = nn.LayerNorm(d_model)
self.head = nn.Linear(d_model, vocab_size, bias=False)
# 因果掩码(下三角矩阵)
self.register_buffer('causal_mask',
torch.tril(torch.ones(max_seq_len, max_seq_len)).unsqueeze(0).unsqueeze(0))
def forward(self, idx):
B, T = idx.shape
tok_emb = self.token_emb(idx) # (B, T, d_model)
pos_emb = self.pos_emb(torch.arange(T, device=idx.device)) # (T, d_model)
x = tok_emb + pos_emb
mask = self.causal_mask[:, :, :T, :T]
for block in self.blocks:
x = block(x, mask)
x = self.ln_f(x)
logits = self.head(x) # (B, T, vocab_size)
return logits
# 创建模型
model = MiniGPT(vocab_size=32000)
dummy = torch.randint(0, 32000, (2, 128))
logits = model(dummy)
print(f"MiniGPT - 输入: {dummy.shape} → 输出: {logits.shape}")
print(f"参数量: {sum(p.numel() for p in model.parameters()):,}")
# 生成文本(最简单的贪心解码)
def generate(model, start_tokens, max_new_tokens=50):
model.eval()
tokens = start_tokens.clone()
for _ in range(max_new_tokens):
logits = model(tokens[:, -512:]) # 取最后512个token
next_token_logits = logits[:, -1, :] # 最后一个位置的预测
next_token = next_token_logits.argmax(-1, keepdim=True)
tokens = torch.cat([tokens, next_token], dim=1)
return tokens
4.5 训练技巧 实战
Batch Normalization vs Layer Normalization
- BatchNorm:在batch维度上归一化,CNN中常用。依赖batch size,推理时用移动平均。
- LayerNorm:在特征维度上归一化,Transformer的标配。不依赖batch size,更稳定。
学习率调度(Learning Rate Schedule)
Python
import torch.optim as optim
model = MiniGPT(vocab_size=32000)
optimizer = optim.AdamW(model.parameters(), lr=3e-4, weight_decay=0.1)
# Cosine退火 + Warmup — 大模型训练的标准方案
total_steps = 10000
warmup_steps = 1000
def lr_lambda(step):
if step < warmup_steps:
return step / warmup_steps # 线性warmup
progress = (step - warmup_steps) / (total_steps - warmup_steps)
return 0.5 * (1 + math.cos(math.pi * progress)) # cosine退火
scheduler = optim.lr_scheduler.LambdaLR(optimizer, lr_lambda)
# 训练循环中使用
# for step in range(total_steps):
# loss = train_step()
# optimizer.step()
# scheduler.step() # 每步更新学习率
防止过拟合的工具箱
| 技术 | 原理 | 代码 |
|---|---|---|
| Dropout | 随机"关闭"部分神经元 | nn.Dropout(0.1) |
| Weight Decay | L2正则化,限制权重大小 | AdamW(weight_decay=0.01) |
| 数据增强 | 对训练数据做随机变换 | transforms.RandomCrop |
| Early Stopping | 验证集性能不再提升时停止 | 手动实现或用回调 |
| Label Smoothing | 软化标签,防止过度自信 | CrossEntropyLoss(label_smoothing=0.1) |
混合精度训练 — 实际工程必备
Python
from torch.cuda.amp import autocast, GradScaler
# 混合精度:用FP16加速计算,FP32保持梯度精度
scaler = GradScaler()
for batch in dataloader:
optimizer.zero_grad()
with autocast(): # 自动将合适的操作转为FP16
output = model(batch['input'])
loss = criterion(output, batch['target'])
scaler.scale(loss).backward() # 缩放loss防止FP16下溢
scaler.step(optimizer)
scaler.update()
# 效果:训练速度提升50-100%,显存占用减半
自然语言处理 & 大语言模型
5.1 NLP基础
文本如何变成数字?
计算机无法直接处理文字,需要将文本转换为数值表示。这个过程经历了几代演进:
| 方法 | 时代 | 原理 | 局限 |
|---|---|---|---|
| One-Hot | 最早期 | 每个词一个维度 | 维度灾难,无语义信息 |
| Word2Vec | 2013 | 通过上下文学习词向量 | 静态向量,一词一义 |
| BERT Embedding | 2018 | 上下文相关的动态表示 | 计算量大 |
| LLM Embedding | 2023+ | 大模型的内部表示 | 当前最先进 |
Python
# === Tokenization — 文本预处理的第一步 ===
from transformers import AutoTokenizer
# 加载GPT-2的tokenizer(BPE算法)
tokenizer = AutoTokenizer.from_pretrained("gpt2")
text = "人工智能正在改变世界"
tokens = tokenizer.encode(text)
decoded = [tokenizer.decode([t]) for t in tokens]
print(f"原文: {text}")
print(f"Token IDs: {tokens}")
print(f"Token文本: {decoded}")
print(f"Token数量: {len(tokens)}")
# === BPE(Byte Pair Encoding)的直觉 ===
# 不是按"词"切分,而是按"频率最高的子串"切分
# "unhappiness" → ["un", "happiness"] 或 ["un", "happ", "iness"]
# 好处:
# 1. 词表大小可控(通常32K-100K)
# 2. 能处理任何文本(包括代码、罕见词、新词)
# 3. 比字符级更高效,比词级更灵活
# === Word2Vec — 词向量的里程碑 ===
# "king" - "man" + "woman" ≈ "queen"
# 这说明词向量捕捉到了语义关系
import gensim.downloader as api
# model = api.load("word2vec-google-news-300")
# similar = model.most_similar(positive=["king", "woman"], negative=["man"])
# print(similar[0]) # ('queen', 0.71)
5.2 预训练模型 — BERT与GPT
预训练的核心思想
在大量无标签文本上自监督学习,模型学到了通用的语言理解能力,然后在特定任务上微调。这个范式革命性地降低了AI应用的门槛。
BERT (Encoder) GPT (Decoder) ├── 双向注意力 ├── 单向(从左到右)注意力 ├── 填空任务(MLM): 预测[MASK]位置 ├── 下一个词预测: 自回归生成 ├── 适合:分类、NER、QA ├── 适合:文本生成、对话 └── 代表:BERT, RoBERTa, DeBERTa └── 代表:GPT系列, LLaMA, Claude
Python
from transformers import pipeline, AutoModel, AutoTokenizer
import torch
# === 用 Hugging Face 快速体验预训练模型 ===
# 1. 情感分析(BERT风格)
classifier = pipeline("sentiment-analysis")
result = classifier("I love learning AI, it's fascinating!")
print(f"情感分析: {result}")
# 2. 文本生成(GPT风格)
generator = pipeline("text-generation", model="gpt2")
result = generator("The future of AI is", max_length=50, num_return_sequences=1)
print(f"文本生成: {result[0]['generated_text']}")
# 3. 提取句子向量(Embedding)
# 这在RAG中至关重要
from sentence_transformers import SentenceTransformer
embed_model = SentenceTransformer("all-MiniLM-L6-v2")
sentences = [
"机器学习是AI的一个子领域",
"深度学习使用多层神经网络",
"今天天气很好适合外出"
]
embeddings = embed_model.encode(sentences)
print(f"\n句子向量维度: {embeddings.shape}") # (3, 384)
# 计算相似度
from sklearn.metrics.pairwise import cosine_similarity
sim_matrix = cosine_similarity(embeddings)
print(f"AI句1 vs AI句2: {sim_matrix[0][1]:.3f}") # 高
print(f"AI句1 vs 天气句: {sim_matrix[0][2]:.3f}") # 低
5.3 大语言模型(LLM) 原理深入
LLM是如何工作的?
大语言模型本质上就是一个超大的Transformer Decoder。它做的事情在数学上很简单:给定前面的所有文字,预测下一个最可能的词。但当模型足够大(几百亿到万亿参数)、数据足够多时,这种简单的目标涌现出了惊人的能力。
模型规模与涌现能力
| 模型 | 参数量 | 特点 |
|---|---|---|
| GPT-2 | 1.5B | 能写连贯文本 |
| GPT-3 | 175B | In-context Learning涌现 |
| GPT-4 | ~1.8T(推测) | 多模态,强推理 |
| LLaMA 3 | 8B-405B | 开源标杆 |
| Claude 3.5 | 未公开 | 长上下文,强推理 |
| DeepSeek V3 | 671B(MoE) | 性价比极高的中国模型 |
Python
# === 使用 Hugging Face 加载开源LLM ===
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
# 加载一个小型开源模型(本地运行)
model_name = "TinyLlama/TinyLlama-1.1B-Chat-v1.0"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(
model_name,
torch_dtype=torch.float16, # 半精度节省显存
device_map="auto" # 自动分配到可用设备
)
# 对话模板
messages = [
{"role": "system", "content": "你是一个有帮助的AI助手。"},
{"role": "user", "content": "用简单的话解释什么是Transformer?"}
]
# 生成回复
input_text = tokenizer.apply_chat_template(messages, tokenize=False)
inputs = tokenizer(input_text, return_tensors="pt").to(model.device)
with torch.no_grad():
outputs = model.generate(
**inputs,
max_new_tokens=256,
temperature=0.7, # 控制随机性:0=确定性,1=更随机
top_p=0.9, # 核采样:只从概率最高的90%中选
repetition_penalty=1.1
)
response = tokenizer.decode(outputs[0][inputs['input_ids'].shape[1]:], skip_special_tokens=True)
print(response)
# === 理解生成参数 ===
# temperature: 控制softmax的"尖锐程度"
# - 低温(0.1): 接近贪心解码,输出确定,适合代码/数学
# - 高温(1.0): 更随机,输出多样,适合创意写作
# top_p (nucleus sampling): 只从累积概率达到p的最小词集中采样
# top_k: 只从概率最高的k个词中选择
5.4 Prompt Engineering 实战
提示工程 — 用语言"编程"
Prompt Engineering是当前最高性价比的AI技能。不需要训练模型,只需要学会如何跟模型"说话"。
Python
# === 使用 OpenAI/Claude API ===
# pip install anthropic (Claude API)
# pip install openai (OpenAI API)
import anthropic # 以Claude为例
client = anthropic.Anthropic(api_key="your-api-key")
# === 基础 Prompt 技巧 ===
# 1. 角色设定(System Prompt)
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
system="你是一位资深Python工程师,擅长代码审查。请用中文回复。",
messages=[{"role": "user", "content": "请审查这段代码:def add(a,b): return a+b"}]
)
# 2. Few-Shot Prompting(给几个例子)
few_shot_prompt = """
请将以下文本分类为"正面"或"负面"。
示例:
文本:"这个产品太棒了!" → 正面
文本:"服务态度很差。" → 负面
文本:"质量一般般。" → 负面
请分类:
文本:"性价比超高,推荐!" →
"""
# 3. Chain-of-Thought(让模型展示思考过程)
cot_prompt = """
请一步一步思考以下问题:
一个水箱有两个管道。进水管每分钟注入3升水,排水管每分钟排出1升水。
如果水箱初始有10升水,容量为50升,多少分钟后水箱会满?
让我们一步一步来想:
"""
# 4. 结构化输出(让模型返回特定格式)
structured_prompt = """
分析以下代码的bug,以JSON格式返回结果:
```python
def find_max(lst):
max_val = 0
for i in lst:
if i > max_val:
max_val = i
return max_val
```
返回格式:
{
"bugs": [{"line": ..., "issue": ..., "fix": ...}],
"severity": "low/medium/high"
}
"""
# 5. 工具调用(Function Calling) — AI Agent的基础
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
tools=[{
"name": "search_database",
"description": "搜索产品数据库",
"input_schema": {
"type": "object",
"properties": {
"query": {"type": "string", "description": "搜索关键词"},
"category": {"type": "string", "enum": ["电子", "服装", "食品"]}
},
"required": ["query"]
}
}],
messages=[{"role": "user", "content": "帮我找一下最新的iPhone手机"}]
)
# 模型会返回tool_use,你的代码执行工具,再把结果传回模型
5.5 RAG — 检索增强生成 热门
为什么需要RAG?
LLM有两个致命问题:(1) 知识有截止日期,(2) 会"幻觉"——一本正经地编造信息。RAG通过在生成前检索相关知识,让模型基于真实资料回答问题。
RAG 工作流程
用户问题: "公司最新的请假政策是什么?"
↓
[1. Embedding] 将问题转为向量
↓
[2. 检索] 在向量数据库中搜索最相关的文档
↓ 找到: company_policy_2024.pdf 的第3章
[3. 增强] 将检索到的文档拼接到Prompt中
↓
[4. 生成] LLM基于文档内容生成回答
↓
回答: "根据2024年最新政策,年假为15天..."(附来源引用)
Python
# === 从零构建一个RAG系统 ===
# pip install chromadb sentence-transformers
import chromadb
from sentence_transformers import SentenceTransformer
import numpy as np
# 1. 准备向量模型
embed_model = SentenceTransformer("all-MiniLM-L6-v2")
# 2. 创建向量数据库
client = chromadb.Client()
collection = client.create_collection("knowledge_base")
# 3. 准备知识库文档
documents = [
"Python是一种解释型编程语言,由Guido van Rossum创建于1991年。",
"PyTorch是Facebook开发的深度学习框架,支持动态计算图。",
"Transformer架构由Google在2017年的论文'Attention Is All You Need'中提出。",
"BERT是一种双向预训练语言模型,擅长文本理解任务。",
"GPT系列模型使用自回归方式生成文本,是单向Transformer。",
"RAG将检索和生成结合,解决了大模型知识过时和幻觉问题。",
"向量数据库如Chroma、Pinecone、Milvus用于存储和检索文本嵌入。",
"LangChain是一个用于构建LLM应用的框架,提供链式调用和Agent能力。",
]
# 4. 文档入库(实际项目中需要分块处理长文档)
collection.add(
documents=documents,
ids=[f"doc_{i}" for i in range(len(documents))],
)
# 5. 检索函数
def retrieve(query, top_k=3):
results = collection.query(query_texts=[query], n_results=top_k)
return results['documents'][0]
# 6. RAG完整流程
def rag_answer(question):
# 检索
relevant_docs = retrieve(question)
context = "\n".join(f"- {doc}" for doc in relevant_docs)
# 构建增强后的Prompt
prompt = f"""基于以下参考资料回答用户问题。如果资料中没有相关信息,请说明。
参考资料:
{context}
用户问题:{question}
请基于参考资料回答:"""
print(f"问题: {question}")
print(f"检索到 {len(relevant_docs)} 条相关文档")
print(f"增强Prompt:\n{prompt}")
# 实际应用中,这里调用LLM API
return prompt
rag_answer("什么是Transformer?")
# === 进阶:文档分块策略 ===
def chunk_text(text, chunk_size=500, overlap=100):
"""滑动窗口分块 — 长文档需要切分"""
chunks = []
start = 0
while start < len(text):
end = start + chunk_size
chunk = text[start:end]
chunks.append(chunk)
start = end - overlap # 重叠以保持上下文连贯
return chunks
# 实际项目中还需要考虑:
# - 按段落/章节分块(语义完整性更好)
# - Metadata附带来源信息
# - Re-ranking(对检索结果二次排序)
# - Hybrid Search(向量检索 + 关键词检索结合)
5.6 微调(Fine-Tuning) 热门
什么时候需要微调?
- Prompt Engineering无法满足需求时
- 需要模型掌握特定领域知识或风格
- 对延迟或成本有要求(微调小模型替代大模型API)
Python
# === LoRA 微调 — 当前最主流的微调方法 ===
# 核心思想:不修改原始权重,只训练很小的"适配器"
#
# 原始:Y = XW (W是巨大的权重矩阵,如 4096×4096)
# LoRA:Y = X(W + BA) 其中 B∈R^{4096×r}, A∈R^{r×4096}, r=8
# 只训练A和B,参数量从 16M 降到 65K (减少99.6%)
# pip install peft transformers datasets trl
from peft import LoraConfig, get_peft_model, TaskType
from transformers import AutoModelForCausalLM, AutoTokenizer, TrainingArguments
from trl import SFTTrainer
from datasets import Dataset
# 1. 加载基座模型
model_name = "TinyLlama/TinyLlama-1.1B-Chat-v1.0"
model = AutoModelForCausalLM.from_pretrained(model_name, torch_dtype="auto")
tokenizer = AutoTokenizer.from_pretrained(model_name)
# 2. 配置LoRA
lora_config = LoraConfig(
task_type=TaskType.CAUSAL_LM,
r=8, # LoRA秩(越大能力越强,但参数越多)
lora_alpha=16, # 缩放系数
lora_dropout=0.05,
target_modules=["q_proj", "v_proj"], # 只在注意力的Q和V上加LoRA
)
# 3. 应用LoRA
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
# "trainable params: 524,288 || all params: 1,100,524,288 || trainable%: 0.048%"
# 只训练不到0.05%的参数!
# 4. 准备训练数据(对话格式)
train_data = Dataset.from_dict({
"text": [
"<|system|>你是一个编程助手。<|user|>Python如何读文件?<|assistant|>使用open()函数:with open('file.txt', 'r') as f: content = f.read()",
"<|system|>你是一个编程助手。<|user|>什么是列表推导式?<|assistant|>[expr for item in iterable if condition],例如 [x**2 for x in range(10) if x > 3]",
# ... 更多训练样本
]
})
# 5. 训练配置
training_args = TrainingArguments(
output_dir="./lora_output",
num_train_epochs=3,
per_device_train_batch_size=4,
gradient_accumulation_steps=4,
learning_rate=2e-4,
fp16=True,
logging_steps=10,
save_strategy="epoch",
)
# 6. 开始训练
trainer = SFTTrainer(
model=model,
train_dataset=train_data,
args=training_args,
dataset_text_field="text",
max_seq_length=512,
)
# trainer.train()
# 7. 保存和加载LoRA权重(只有几MB!)
# model.save_pretrained("./my_lora_adapter")
# 部署时:base_model + LoRA adapter = 微调后的模型
- Full Fine-Tuning:所有参数都训练,效果最好但需要大量GPU。适合大公司。
- LoRA/QLoRA:只训练少量参数,单卡就能跑。当前最主流,性价比最高。
- Prefix Tuning:在输入前加可学习的前缀。参数更少但效果一般。
- RLHF/DPO:基于人类偏好的对齐训练。用于让模型更有帮助、更安全。
5.7 AI Agent — 让LLM使用工具 最前沿
什么是AI Agent?
AI Agent = LLM + 记忆 + 工具 + 规划能力。不再只是问答,而是能自主执行多步骤任务的智能体。这是当前AI应用最热的方向。
AI Agent 架构
用户任务: "分析这个CSV数据并生成报告"
↓
┌──────────────────────────────────────┐
│ Agent (LLM作为"大脑") │
│ │
│ 1. 规划: 拆解任务为多个步骤 │
│ 2. 推理: 决定下一步使用哪个工具 │
│ 3. 行动: 调用工具执行操作 │
│ 4. 观察: 分析工具返回的结果 │
│ 5. 循环: 重复2-4直到任务完成 │
│ │
│ ┌──────────┐ ┌────────┐ ┌────────┐ │
│ │ 代码执行 │ │ 搜索 │ │ 文件 │ │
│ │ (Python) │ │ (Web) │ │ (R/W) │ │
│ └──────────┘ └────────┘ └────────┘ │
│ 工具 (Tools) │
│ │
│ [短期记忆: 对话历史] │
│ [长期记忆: 向量数据库] │
└──────────────────────────────────────┘
Python
# === 从零实现一个 ReAct Agent ===
# ReAct = Reasoning + Acting (推理+行动交替进行)
import json
import re
class SimpleAgent:
"""一个最简化的AI Agent实现"""
def __init__(self, llm_call_fn):
self.llm = llm_call_fn # LLM调用函数
self.tools = {}
self.memory = [] # 对话历史
def register_tool(self, name, func, description):
self.tools[name] = {"func": func, "desc": description}
def run(self, task, max_steps=5):
"""执行任务的主循环"""
# 构建系统提示
tools_desc = "\n".join(
f"- {name}: {info['desc']}" for name, info in self.tools.items()
)
system_prompt = f"""你是一个AI助手,可以使用以下工具来完成任务:
{tools_desc}
请按以下格式回复:
Thought: 我需要思考下一步该做什么
Action: tool_name
Action Input: tool的输入参数
(等待工具返回结果后继续)
Observation: 工具返回的结果
... (可以重复多次)
Final Answer: 最终回答
"""
self.memory.append({"role": "system", "content": system_prompt})
self.memory.append({"role": "user", "content": task})
for step in range(max_steps):
# 调用LLM
response = self.llm(self.memory)
# 解析LLM的输出
if "Final Answer:" in response:
answer = response.split("Final Answer:")[-1].strip()
print(f"\n[最终回答] {answer}")
return answer
# 解析Action
action_match = re.search(r'Action: (\w+)', response)
input_match = re.search(r'Action Input: (.+)', response)
if action_match and input_match:
tool_name = action_match.group(1)
tool_input = input_match.group(1).strip()
print(f"[Step {step+1}] 使用工具: {tool_name}({tool_input})")
# 执行工具
if tool_name in self.tools:
result = self.tools[tool_name]["func"](tool_input)
observation = f"Observation: {result}"
else:
observation = f"Observation: 工具 {tool_name} 不存在"
self.memory.append({"role": "assistant", "content": response})
self.memory.append({"role": "user", "content": observation})
return "达到最大步数,任务未完成"
# === 注册工具 ===
def calculator(expression):
try:
return str(eval(expression)) # 注意:实际项目中不要用eval
except:
return "计算错误"
def search_web(query):
# 模拟搜索
fake_results = {
"python版本": "Python最新稳定版是3.12",
"transformer": "Transformer由Google在2017年提出",
}
for key, val in fake_results.items():
if key in query.lower():
return val
return f"搜索'{query}'的结果: 未找到相关信息"
agent = SimpleAgent(llm_call_fn=lambda msgs: "模拟LLM回复")
agent.register_tool("calculator", calculator, "执行数学计算")
agent.register_tool("search", search_web, "搜索互联网信息")
# === 生产级Agent框架 ===
# 实际开发中推荐使用成熟框架:
# - LangChain: 最流行,生态丰富,但有时过于抽象
# - LlamaIndex: 专注于RAG,数据连接能力强
# - CrewAI: 多Agent协作框架
# - AutoGen: 微软出品,Agent对话框架
计算机视觉
6.1 图像处理基础
Python
import torch
import torchvision
import torchvision.transforms as T
from PIL import Image
# === 图像预处理 — CV任务的标准流程 ===
# 所有预训练模型都要求统一的输入格式
# 训练时的数据增强
train_transform = T.Compose([
T.RandomResizedCrop(224), # 随机裁剪缩放到224x224
T.RandomHorizontalFlip(), # 随机水平翻转
T.ColorJitter(0.2, 0.2, 0.2, 0.1), # 随机颜色扰动
T.ToTensor(), # PIL Image → Tensor, 范围[0,1]
T.Normalize( # ImageNet标准化
mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225]
),
])
# 推理时不需要增强
val_transform = T.Compose([
T.Resize(256),
T.CenterCrop(224),
T.ToTensor(),
T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])
# === 使用预训练模型进行图像分类 ===
from torchvision.models import resnet50, ResNet50_Weights
model = resnet50(weights=ResNet50_Weights.DEFAULT)
model.eval()
# 加载和预处理图片
# img = Image.open("cat.jpg")
# img_tensor = val_transform(img).unsqueeze(0) # 加batch维度
# 推理
# with torch.no_grad():
# logits = model(img_tensor)
# pred = logits.argmax(dim=1)
# print(f"预测类别: {pred.item()}")
# === 迁移学习 — CV实战的标准做法 ===
# 用预训练的backbone,只替换最后的分类头
import torch.nn as nn
def create_transfer_model(num_classes):
model = resnet50(weights=ResNet50_Weights.DEFAULT)
# 冻结backbone(不训练,用预训练的特征提取能力)
for param in model.parameters():
param.requires_grad = False
# 替换分类头
model.fc = nn.Sequential(
nn.Linear(2048, 512),
nn.ReLU(),
nn.Dropout(0.3),
nn.Linear(512, num_classes)
)
return model
# 只需要少量数据就能在新任务上达到好效果
custom_model = create_transfer_model(num_classes=5)
trainable = sum(p.numel() for p in custom_model.parameters() if p.requires_grad)
total = sum(p.numel() for p in custom_model.parameters())
print(f"可训练参数: {trainable:,} / {total:,} ({trainable/total:.1%})")
6.2 目标检测
目标检测 = 分类 + 定位。不仅要识别图像中有什么,还要框出它们的位置。
| 模型 | 类型 | 特点 | 适用场景 |
|---|---|---|---|
| YOLO v8/v9 | 单阶段 | 速度极快,实时检测 | 监控、自动驾驶 |
| DETR | 基于Transformer | 端到端,无需NMS | 高精度需求 |
| SAM | 分割一切 | 零样本分割 | 通用分割 |
| Grounding DINO | 开放词汇 | 文本描述→检测 | 灵活的目标检测 |
Python
# === YOLO v8 — 当前最流行的目标检测模型 ===
# pip install ultralytics
from ultralytics import YOLO
# 加载预训练模型
model = YOLO("yolov8n.pt") # nano版本,最快
# 推理
# results = model("image.jpg")
# for r in results:
# for box in r.boxes:
# cls = int(box.cls[0])
# conf = float(box.conf[0])
# xyxy = box.xyxy[0].tolist() # [x1, y1, x2, y2]
# print(f"类别: {model.names[cls]}, 置信度: {conf:.2f}, 位置: {xyxy}")
# 训练自定义检测器(只需准备标注数据)
# model.train(data="custom_dataset.yaml", epochs=100, imgsz=640)
6.3 图像生成 — Diffusion Models 热门
扩散模型的核心思想
前向过程:逐步给图片加噪声,直到变成纯噪声。反向过程:学习如何从噪声中恢复出图片。训练好后,从随机噪声开始去噪,就能"生成"新图片。
扩散模型 (Diffusion Model)
前向过程(加噪声): 图片 → ... → 纯噪声
x₀ → x₁ → x₂ → ... → x_T
反向过程(去噪声): 纯噪声 → ... → 图片
x_T → ... → x₂ → x₁ → x₀
训练目标:学习在每一步预测需要去除的噪声 ε
Loss = ||ε - ε_θ(x_t, t)||² (预测噪声与真实噪声的MSE)
Python
# === Stable Diffusion 使用 ===
# pip install diffusers
from diffusers import StableDiffusionPipeline
import torch
# 加载模型
pipe = StableDiffusionPipeline.from_pretrained(
"runwayml/stable-diffusion-v1-5",
torch_dtype=torch.float16,
)
pipe = pipe.to("cuda")
# 文生图
# image = pipe(
# prompt="a serene landscape with mountains and a lake, digital art",
# negative_prompt="blurry, low quality",
# num_inference_steps=30,
# guidance_scale=7.5, # CFG: 越大越贴合prompt,但可能过饱和
# ).images[0]
# image.save("output.png")
# === 简化版 Diffusion 模型实现 ===
import torch.nn as nn
class SimpleDiffusion(nn.Module):
"""极简扩散模型 — 理解核心原理"""
def __init__(self, n_steps=1000, beta_start=1e-4, beta_end=0.02):
super().__init__()
# 噪声调度
betas = torch.linspace(beta_start, beta_end, n_steps)
alphas = 1 - betas
alphas_cumprod = torch.cumprod(alphas, dim=0)
self.register_buffer('betas', betas)
self.register_buffer('alphas_cumprod', alphas_cumprod)
self.register_buffer('sqrt_alphas_cumprod', torch.sqrt(alphas_cumprod))
self.register_buffer('sqrt_one_minus_alphas_cumprod', torch.sqrt(1 - alphas_cumprod))
def add_noise(self, x_0, t, noise=None):
"""前向过程:q(x_t | x_0) = √ᾱ_t * x_0 + √(1-ᾱ_t) * ε"""
if noise is None:
noise = torch.randn_like(x_0)
sqrt_alpha = self.sqrt_alphas_cumprod[t].reshape(-1, 1, 1, 1)
sqrt_one_minus = self.sqrt_one_minus_alphas_cumprod[t].reshape(-1, 1, 1, 1)
return sqrt_alpha * x_0 + sqrt_one_minus * noise, noise
# 训练循环(伪代码):
# for batch in dataloader:
# t = torch.randint(0, n_steps, (batch_size,)) # 随机时间步
# noisy, noise = diffusion.add_noise(batch, t) # 加噪
# predicted_noise = unet(noisy, t) # UNet预测噪声
# loss = F.mse_loss(predicted_noise, noise) # 损失
# loss.backward()
6.4 多模态模型 前沿
多模态模型能同时理解和生成文本、图像、音频、视频。这是AI的发展趋势——统一的智能系统。
| 模型 | 能力 | 典型应用 |
|---|---|---|
| CLIP (OpenAI) | 图文对齐 | 图片搜索、零样本分类 |
| GPT-4V / Claude Vision | 图片理解 | 图像描述、OCR、图表分析 |
| Stable Diffusion | 文生图 | 艺术创作、设计 |
| Sora | 文生视频 | 视频创作 |
| Whisper | 语音转文字 | 会议记录、字幕 |
Python
# === CLIP — 理解图文关系 ===
# pip install open_clip_torch
import open_clip
import torch
from PIL import Image
model, _, preprocess = open_clip.create_model_and_transforms('ViT-B-32', pretrained='openai')
tokenizer = open_clip.get_tokenizer('ViT-B-32')
# 图片和文本编码到同一个向量空间
# image = preprocess(Image.open("cat.jpg")).unsqueeze(0)
texts = tokenizer(["a cat", "a dog", "a car"])
# with torch.no_grad():
# image_features = model.encode_image(image)
# text_features = model.encode_text(texts)
# similarity = (image_features @ text_features.T).softmax(dim=-1)
# print(f"相似度: {similarity}")
# # [0.95, 0.04, 0.01] — 最像"a cat"
AI工程实践
7.1 MLOps — AI项目的DevOps
MLOps 生命周期 数据 → 特征工程 → 训练 → 评估 → 部署 → 监控 ↑ │ └────────── 反馈循环(持续改进) ←────────┘ 关键工具栈: ├── 数据版本管理: DVC, Delta Lake ├── 实验追踪: Weights & Biases, MLflow ├── 模型注册: MLflow Model Registry ├── 流水线编排: Airflow, Kubeflow, Prefect ├── 模型服务: TorchServe, Triton, vLLM ├── 监控告警: Prometheus + Grafana, Arize AI └── 特征商店: Feast, Tecton
Python
# === Weights & Biases — 实验追踪 ===
# pip install wandb
import wandb
# 初始化实验
wandb.init(
project="my-ai-project",
config={
"model": "resnet50",
"lr": 3e-4,
"batch_size": 32,
"epochs": 10,
"optimizer": "adamw",
}
)
# 在训练循环中记录指标
for epoch in range(10):
train_loss = 0.5 - epoch * 0.04 # 模拟
val_acc = 0.7 + epoch * 0.025
wandb.log({
"epoch": epoch,
"train/loss": train_loss,
"val/accuracy": val_acc,
"lr": 3e-4 * (0.95 ** epoch),
})
# wandb.finish()
# 登录 wandb.ai 查看漂亮的可视化面板
# === MLflow — 模型注册和管理 ===
# pip install mlflow
import mlflow
with mlflow.start_run():
mlflow.log_params({"lr": 3e-4, "model": "bert-base"})
mlflow.log_metrics({"accuracy": 0.92, "f1": 0.89})
# mlflow.pytorch.log_model(model, "model") # 保存模型
7.2 模型部署 实战
Python
# === 方案1:FastAPI 部署推理服务 ===
# pip install fastapi uvicorn
from fastapi import FastAPI
from pydantic import BaseModel
import torch
app = FastAPI()
# 加载模型(启动时只加载一次)
# model = torch.load("model.pt")
# model.eval()
class PredictRequest(BaseModel):
text: str
class PredictResponse(BaseModel):
label: str
confidence: float
@app.post("/predict", response_model=PredictResponse)
async def predict(request: PredictRequest):
# 预处理
# inputs = tokenizer(request.text, return_tensors="pt")
# 推理
# with torch.no_grad():
# output = model(**inputs)
# 后处理
return PredictResponse(label="positive", confidence=0.95)
# 启动: uvicorn app:app --host 0.0.0.0 --port 8000
Python
# === 方案2:vLLM — LLM高效推理服务器 ===
# pip install vllm
# vLLM支持PagedAttention,比普通推理快2-4倍
# 启动服务器(命令行):
# python -m vllm.entrypoints.openai.api_server \
# --model meta-llama/Llama-3-8B-Instruct \
# --tensor-parallel-size 2 \
# --max-model-len 4096
# 客户端调用(兼容OpenAI API格式)
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8000/v1", api_key="dummy")
response = client.chat.completions.create(
model="meta-llama/Llama-3-8B-Instruct",
messages=[{"role": "user", "content": "Hello!"}],
max_tokens=100,
)
print(response.choices[0].message.content)
Dockerfile
# === Docker 部署 ===
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
# 下载模型(构建时缓存)
RUN python -c "from transformers import AutoModel; AutoModel.from_pretrained('bert-base-uncased')"
EXPOSE 8000
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]
7.3 分布式训练
当模型太大放不下一张GPU,或数据太多训练太慢时,需要分布式训练。
| 策略 | 原理 | 适用场景 |
|---|---|---|
| Data Parallel (DP) | 每张卡放完整模型,数据切分 | 模型放得下单卡 |
| Distributed DP (DDP) | DP的改进,通信更高效 | 多卡训练的标准方案 |
| FSDP | 模型参数也切分到多卡 | 模型放不下单卡 |
| Pipeline Parallel | 模型按层切分到不同卡 | 超大模型 |
| Tensor Parallel | 单层内部切分 | 超大模型 |
| DeepSpeed ZeRO | 优化器状态/梯度/参数逐步切分 | 万亿参数模型 |
Python
# === 使用 HuggingFace Accelerate 简化分布式训练 ===
# pip install accelerate
from accelerate import Accelerator
accelerator = Accelerator(mixed_precision="fp16")
# 正常创建模型、优化器、数据加载器
model = create_model()
optimizer = torch.optim.AdamW(model.parameters(), lr=3e-4)
dataloader = create_dataloader()
# 一行代码转为分布式!
model, optimizer, dataloader = accelerator.prepare(model, optimizer, dataloader)
# 训练循环几乎不变
for batch in dataloader:
outputs = model(**batch)
loss = outputs.loss
accelerator.backward(loss) # 替代 loss.backward()
optimizer.step()
optimizer.zero_grad()
# 启动: accelerate launch --num_processes 4 train.py
前沿方向与职业发展
8.1 强化学习(Reinforcement Learning)
Agent在环境中通过试错来学习最优策略。AlphaGo、机器人控制、RLHF(大模型对齐)都基于RL。
强化学习框架
┌──────────────┐
│ Agent │
│ (策略 π) │
└──┬───────┬───┘
action │ ↑ reward + state
↓ │
┌──────────────┐
│ Environment │
└──────────────┘
核心概念:
- State(s): 环境状态
- Action(a): Agent的动作
- Reward(r): 环境的反馈
- Policy(π): 状态→动作的映射(这就是要学的东西)
- Value(V): 状态的长期价值
Python
# === Q-Learning — 最经典的RL算法 ===
import numpy as np
# 一个简单的网格世界
# S . . .
# . # . .
# . . . G (S=起点, G=终点, #=障碍)
class GridWorld:
def __init__(self):
self.grid_size = 4
self.state = (0, 0)
self.goal = (3, 3)
self.obstacles = {(1, 1)}
def reset(self):
self.state = (0, 0)
return self.state
def step(self, action):
# 0=上, 1=下, 2=左, 3=右
moves = [(-1,0), (1,0), (0,-1), (0,1)]
new_state = (
max(0, min(3, self.state[0] + moves[action][0])),
max(0, min(3, self.state[1] + moves[action][1]))
)
if new_state in self.obstacles:
new_state = self.state
self.state = new_state
if self.state == self.goal:
return self.state, 10, True # 到达目标,奖励10
return self.state, -0.1, False # 每步小惩罚,鼓励快速到达
# Q-Learning
env = GridWorld()
q_table = np.zeros((4, 4, 4)) # state_x, state_y, action
lr = 0.1
gamma = 0.99 # 折扣因子:多看重未来回报
epsilon = 0.1 # 探索率
for episode in range(1000):
state = env.reset()
for _ in range(50):
# ε-greedy策略:大部分时候选最优,偶尔随机探索
if np.random.random() < epsilon:
action = np.random.randint(4)
else:
action = q_table[state[0], state[1]].argmax()
next_state, reward, done = env.step(action)
# Q值更新:Q(s,a) ← Q(s,a) + lr * [r + γ*max(Q(s')) - Q(s,a)]
old_q = q_table[state[0], state[1], action]
next_max_q = q_table[next_state[0], next_state[1]].max()
q_table[state[0], state[1], action] = old_q + lr * (reward + gamma * next_max_q - old_q)
state = next_state
if done:
break
# 打印学到的策略
symbols = ['↑', '↓', '←', '→']
print("\n学到的最优策略:")
for i in range(4):
row = ""
for j in range(4):
if (i,j) == (3,3): row += " G "
elif (i,j) in {(1,1)}: row += " # "
else: row += f" {symbols[q_table[i,j].argmax()]} "
print(row)
8.2 图神经网络(GNN)
当数据天然具有图结构时(社交网络、分子结构、知识图谱),GNN能利用节点之间的关系学习更好的表示。
Python
# === PyTorch Geometric — GNN框架 ===
# pip install torch-geometric
import torch
import torch.nn.functional as F
from torch_geometric.nn import GCNConv
from torch_geometric.datasets import Planetoid
# 加载Cora引用网络数据集
# dataset = Planetoid(root='/tmp/Cora', name='Cora')
# data = dataset[0] # 2708个论文节点,5429条引用边,7个类别
class GCN(torch.nn.Module):
"""图卷积网络 — 节点分类"""
def __init__(self, num_features, num_classes):
super().__init__()
self.conv1 = GCNConv(num_features, 64) # 图卷积层
self.conv2 = GCNConv(64, num_classes)
def forward(self, x, edge_index):
# x: 节点特征 (num_nodes, num_features)
# edge_index: 边列表 (2, num_edges)
x = self.conv1(x, edge_index) # 聚合邻居信息
x = F.relu(x)
x = F.dropout(x, p=0.5, training=self.training)
x = self.conv2(x, edge_index)
return F.log_softmax(x, dim=1)
# GNN的核心思想:消息传递(Message Passing)
# 每个节点通过聚合邻居节点的信息来更新自己的表示
# h_v^(k) = UPDATE(h_v^(k-1), AGGREGATE({h_u^(k-1) : u ∈ N(v)}))
8.3 AI安全与伦理
随着AI能力增强,安全问题越来越重要。这不仅是技术问题,也关系到AI从业者的职业责任。
- 对抗攻击:微小的输入扰动让模型判断错误(自动驾驶安全隐患)
- 数据隐私:联邦学习、差分隐私等技术保护用户数据
- 模型偏见:训练数据中的偏见会被模型放大(性别、种族偏见)
- 大模型安全:Prompt注入攻击、越狱(Jailbreak)、有害输出
- AI对齐:如何确保AI的行为符合人类意图和价值观
8.4 职业转型路径 关键
适合有经验程序员的AI岗位
AI/LLM 应用工程师
开发AI驱动的应用产品。RAG系统、Agent开发、API集成。
门槛最低,你的工程经验直接适用。
MLOps/AI平台工程师
构建和维护AI基础设施。模型训练平台、推理服务、监控系统。
最契合你的背景。
机器学习工程师(MLE)
端到端的ML系统开发。数据管道、模型训练、部署优化。
需要更深的ML理论基础。
AI研究工程师
实现和优化前沿论文的算法。需要较强的数学功底。
门槛最高但成长空间最大。
建议的转型策略
- 边学边做(3-6个月):完成本文档的学习,同时做2-3个完整项目
- 作品集构建:
- 项目1:基于RAG的企业知识库问答系统(展示LLM应用能力)
- 项目2:微调一个垂直领域的小模型(展示模型训练能力)
- 项目3:完整的ML Pipeline + 部署(展示工程化能力)
- 面试准备:
- ML基础:常见算法原理、过拟合、评估指标
- 深度学习:反向传播、Transformer细节、训练技巧
- 系统设计:如何设计推荐系统/搜索系统/RAG系统
- 编码:LeetCode中等题 + ML相关的代码题
推荐学习资源
| 类型 | 资源 | 说明 |
|---|---|---|
| 课程 | Andrew Ng - Machine Learning (Coursera) | ML入门经典 |
| 课程 | Stanford CS231n | 计算机视觉 |
| 课程 | Stanford CS224n | NLP |
| 课程 | fast.ai | 实战导向,适合程序员 |
| 书籍 | 《动手学深度学习》(d2l.ai) | 李沐,理论+代码,有中文版 |
| 书籍 | Hands-On Machine Learning (Aurélien Géron) | 工程导向的ML圣经 |
| 实践 | Kaggle竞赛 | 实战练兵,简历加分项 |
| 社区 | Hugging Face | 模型仓库 + 教程 + 社区 |
| 论文 | Papers With Code | 论文+代码+排行榜 |
AI领域变化极快,但基础不变。Transformer论文才2017年,LLM爆发才2022年,但线性代数和概率论已经存在上百年。打好基础,理解原理,新技术出来时你就能快速上手。
你的10年编程经验不是包袱,而是翅膀。AI领域最缺的不是写模型的人,而是能把模型变成产品的人。带着工程思维进入AI,你会比纯ML背景的人更有竞争力。
行动起来,最好的学习时间就是现在。
AI 系统学习指南 · 为10年+经验程序员设计
持续更新 · 理论 + 代码 + 实战
Built with passion for learning · 2024-2026