南京大学 LLM开发基础(一)前向反向传播搭建

主要围绕深度学习中 前向反向传播的搭建原理。

目录

[1. 大语言模型导言](#1. 大语言模型导言)

[编码 + Attention + 基本组件](#编码 + Attention + 基本组件)

[Decoder-only 架构](#Decoder-only 架构)

[2. 前向传播](#2. 前向传播)

[3. 反向传播](#3. 反向传播)

[Autograd -> 计算图 DAG](#Autograd -> 计算图 DAG)

[hw0:手搓一个线性层 Manual_Linear 并手动计算梯度](#hw0:手搓一个线性层 Manual_Linear 并手动计算梯度)


1. 大语言模型导言

https://njudeepengine.github.io/llm-course-lecture/2025/lecture1.html#1

硬件 -> 编程基础+深度学习框架 -> 神经网络架构 -> 模型的开发部署 -> 实际应用场景

编码 + Attention + 基本组件

语言模型,源于如何建模自然语言?比如如何解释"什么是南京大学"。

把"南京大学"输入模型,就需要一个编码器encoder去转换这四个字。

如何认识单词?编码:one-hot编码, word2vec(上下文完形填空训练), tokenization

经典向量投射例子 king - man = queen - woman

如何理解语言 ?多次 Attention 算两两之间字词的意思关系。

基础组件(积木):残差(residual), layernorm, attention, softmax, positional embedding

大规模训练 多卡多机:需要分布式运行

Decoder-only 架构

在BERT时代 虽然上下文完形填空 和生成 过程有些矛盾 ,但当时有一些训练效果(比只预测下文好训练 decoder-only每个位置所能接触的信息比其他架构少,要预测下一个token难度更高 ,但上限更高)。

后来模型参数量提高后 显卡和数据比较多后 GPT能力大大增强。

目前生成能力 还是以 GPT 为代表的Decoder-only架构为主。

下游任务 zero/few-shot 泛化性能好。

Encoder的双向注意力会存在低秩 问题,这可能会削弱模型表达能力,就生成任务 而言,引入双向注意力 并无实质好处。而Encoder-Decoder架构之所以能够在某些场景下表现更好,大概只是因为它多了一倍参数 。所以,在同等参数量、同等推理成本下,Decoder-only架构就是最优选择了。

2. 前向传播

前向传播:从输入到输出,计算预测结果

矩阵乘法 与 元素乘法

python 复制代码
# 矩阵乘法
y = x@w
y = x.matmul(w)
y = torch.matmul(x, w)

# 元素乘法
y = x*w
y = x.mul(w)
y = torch.mul(x, w)

层的堆叠 x是128个样本 每个样本20维度;m1 20->30 m2 30->40.

python 复制代码
m1 = nn.Linear(20, 30)
m2 = nn.Linear(30, 40)
x = torch.randn(128, 20)
y1 = m1(x)
y2 = m2(y1)

3. 反向传播

https://njudeepengine.github.io/llm-course-lecture/2025/lecture3.html#1

反向传播:从输出到输入,计算梯度,更新参数

核心思想:通过链式法则计算复合函数的导数

  • 目标 :找到使损失函数最小的参数Y=XW 找那个W
  • 方法 :梯度下降,需要计算损失函数对每个参数的梯度
  • 挑战 :深度学习模型是复合函数,直接计算梯度困难
  • 解决 :反向传播算法,针对优化目标 J(θ) 按层"回退",一层一层求偏导

很多层进行 复合函数

Autograd -> 计算图 DAG

非叶子节点 记录 grad_fn 描述生成该tensor的运算方式,即描述反向传播时如何计算梯度。

print tensor 时既包含张量值 也包含grad_fn。

python 复制代码
import torch

x = torch.ones(2, 2, requires_grad=True)
y = x + 2
z = y * y * 3
out = z.mean()

print(x.grad_fn)  # None,因为x是叶子张量
print(y.grad_fn)  # <AddBackward0>,表示加法操作
print(z.grad_fn)  # <MulBackward0>,表示乘法操作
print(out.grad_fn) # <MeanBackward0>,表示 mean 操作

Autograd的无感知使用

backward() 启动反向传播计算;之前清空梯度;之后更新梯度

python 复制代码
optimizer = torch.optim.SGD(net.parameters(), lr=0.01)

for epoch in range(100):
    # 前向传播
    y = net(x)
    loss = criterion(y, target)
    
    # 反向传播
    optimizer.zero_grad()  # 清零梯度
    loss.backward()        # 计算梯度
    optimizer.step()       # 更新参数

with torch.no_grad(): 上下文内不记录计算图,不分配 grad_fn/中间量,因此这些计算不参与 loss.backward()。

用途: 纯推理/验证(配合 model.eval())。

比如用于模型蒸馏 小模型学大模型的输出。两者都要前向操作 然后更新小模型需要grad 大模型不需要grad

hw0:手搓一个线性层 Manual_Linear 并手动计算梯度

A0 Onboarding | LLM-Assignment-Doc

我们进一步定义一个标量损失函数 loss = (Y ** 2).sum()

首先初始化 权重矩阵W(也要同步给测试的输入) W_grad 梯度初始化为零矩阵。

python 复制代码
class ManualLinear:
    def __init__(self, in_dim, out_dim, device=None, dtype=torch.float32):
        # 初始化权重矩阵 W,随机值,形状为 [输入维度, 输出维度]
        # requires_grad=False 是关键!因为我们手动计算梯度,不需要autograd帮忙
        self.W = torch.randn(in_dim, out_dim, device=device, dtype=dtype, requires_grad=False)
    
        # 初始化一个全0的张量,用来存储我们手动计算出的权重梯度 dL/dW
        self.W_grad = torch.zeros_like(self.W)

进一步,完整的 forward 过程即:

使用 Y = X @ w @进行矩阵乘法

python 复制代码
def forward(self, x):
    # 保存输入x,反向传播计算 dL/dW = X^T @ (dL/dY) 时会用到!
    self.input = x
    return x @ self.W

backward 的计算过程:

首先关于梯度/偏导 需要满足矩阵shape一致性:

分母布局求导(主流深度学习框架默认) 每行为每个分母对应梯度

此外 由于乘法求导等于转置 可进一步化为

在实现的过程中 由于输入x 是三维的;第一个维度为批量大小b,所以需要合并前两维(展平)

python 复制代码
def backward(self, grad_output):
    # grad_output 就是从后面一层传回来的梯度 dL/dY, 形状是 [b, h, e]
    # 获取输入X的维度,以便后续reshape操作
    b, h, d = self.input.shape

    # 我们需要把 batch 和 h 维度合并,看作一个大的"样本"维度。
    # 将输入X从 [b, h, d] 重塑为 [b*h, d]
    # 这相当于把多个样本在批次维度上拼接起来
    x_flat = self.input.reshape(-1, d)
    
    # 同样,将上游梯度 grad_output 从 [b, h, e] 重塑为 [b*h, e]
    grad_out_flat = grad_output.reshape(-1, self.W.shape[1])
    
    # 现在执行矩阵乘法: [d, b*h] @ [b*h, e] -> [d, e]
    # x_flat.T 的形状是 [d, b*h]
    # grad_out_flat 的形状是 [b*h, e]
    # 它们相乘的结果正好是 [d, e],和权重W的形状一致!
    # 这个操作在数学上等价于:对batch中的每一个样本计算 x_i^T @ (dL/dY_i),然后把所有结果加起来。
    self.W_grad = x_flat.T @ grad_out_flat

    grad_input = grad_output @ self.W.T
    
    return grad_input # 将梯度返回,用于前一层的反向传播

step 根据学习率 梯度下降更新;减去学习率lr * 梯度W

python 复制代码
# 梯度下降更新
def step(self, lr=1e-2):
    self.W -= lr * self.W_grad

Manual_Linear调用 Y**2 对 Y 偏导就是 2Y;作为上一层的grad_output 送给backward

python 复制代码
manual_linear = ManualLinear(d, e, device=device, dtype=dtype)
y_manual = manual_linear.forward(x)
loss_manual = y_manual.pow(2).sum()
grad_output = 2 * y_manual
grad_input_manual = manual_linear.backward(grad_output)

使用pytest 比较与 PyTorch AutogradLinear 自动求导机制与手动计算的一致性。

手动计算时 randn生成的W权重复制过来

.requires_grad 梯度跟踪的X 和 Y 随后才能用 .grad

计算平方和损失 .backward()自动计算梯度; 得到对X和W的梯度

三个assert断言 前向传播结果,对X和W的梯度

python 复制代码
# 创建一个使用PyTorch自动求导的线性层实例
autograd_linear = AutogradLinear(d, e, device=device, dtype=dtype)

# 关键步骤:确保两个模型的初始权重完全一致,才能进行公平比较
with torch.no_grad():
    # 将手动线性层的权重数值复制到自动求导线性层的权重中
    autograd_linear.W.copy_(manual_linear.W)

# 准备用于自动求导的输入数据
# .requires_grad_():要求对这个新张量进行梯度跟踪,这样autograd才能计算关于它的梯度

x_autograd = x.clone().detach().requires_grad_()

y_autograd = autograd_linear(x_autograd)  # x->y

loss_autograd = y_autograd.pow(2).sum()  # 平方和损失

loss_autograd.backward() # 反向传播 自动计算梯度

grad_input_autograd = x_autograd.grad # X梯度

grad_weight_autograd = autograd_linear.W.grad # W梯度

# ========== 验证阶段:对比手动实现 vs 自动求导 ==========

# 断言1:验证前向传播结果是否一致
torch.testing.assert_close(y_manual, y_autograd, rtol=1e-4, atol=1e-4)

# 断言2:验证输入梯度是否一致
# 检验手动实现的 dL/dx = grad_output @ W^T 公式是否正确
torch.testing.assert_close(grad_input_manual, grad_input_autograd, rtol=1e-4, atol=1e-4)

# 断言3:验证权重梯度是否一致
# 检验手动实现的 dL/dW = x^T @ grad_output 公式是否正确
torch.testing.assert_close(manual_linear.W_grad, grad_weight_autograd, rtol=1e-4, atol=1e-4)
相关推荐
GPU实战笔记10 分钟前
云端 GPU 训练的账户余额提醒:它能说明什么,不能说明什么?
深度学习·算法·成本管理·gpu云计算·云端训练
回眸&啤酒鸭10 分钟前
【回眸】OpenSwarm 多智能体协作系统实战指南
大数据·前端·人工智能
博图光电19 分钟前
Libra 27105相关技术参数
人工智能·数码相机
IT_陈寒44 分钟前
SpringBoot自动配置差点让我加班到凌晨
前端·人工智能·后端
yumgpkpm1 小时前
Acceldata ODP(Open Data Platform)3.3.6.4(RHEL9)保姆级完整安装手册
大数据·人工智能·hive·hadoop·kafka·hbase·cloudera
程序员cxuan1 小时前
腾讯又来一王炸,开源版 WorkBuddy 太夯了!
人工智能·后端·程序员
邓工说电1 小时前
智慧断路器安全吗?数据加密、离线保护与合规认证全解读
大数据·数据库·人工智能·智能断路器·炜晔科技
阿里云大数据AI技术1 小时前
Lance 数据检索怎么选,当然阿里云 Milvus 向量湖
人工智能
出海客1 小时前
跨境电商多语言客服知识库怎么建:资料结构、检索边界与人工升级
大数据·人工智能
xsd202411182 小时前
从自主导航到视觉读表:一台工业巡检机器人的全栈技术链路拆解
人工智能