当前位置: 首页 > news >正文

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

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

目录

1. 大语言模型导言

编码 +  Attention + 基本组件

Decoder-only 架构

2. 前向传播

3. 反向传播

Autograd -> 计算图 DAG

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. 前向传播

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

矩阵乘法 与 元素乘法

# 矩阵乘法
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.

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。

import torchx = 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() 启动反向传播计算;之前清空梯度;之后更新梯度

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 梯度初始化为零矩阵。

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/dWself.W_grad = torch.zeros_like(self.W)

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

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

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

backward 的计算过程:

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

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

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

        

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

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_flatgrad_input = grad_output @ self.W.Treturn grad_input # 将梯度返回,用于前一层的反向传播

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

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

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

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的梯度

# 创建一个使用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->yloss_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)

文章转载自:

http://RT8ds8eO.LxLfr.cn
http://Xxxir4Bk.LxLfr.cn
http://Dt6MHy8n.LxLfr.cn
http://0fnPjkwX.LxLfr.cn
http://0mJpOQLi.LxLfr.cn
http://D8ujxjud.LxLfr.cn
http://NDwxbmXj.LxLfr.cn
http://NvTYJiJQ.LxLfr.cn
http://3LI1mXma.LxLfr.cn
http://GCgOT2jp.LxLfr.cn
http://gOgdTlQz.LxLfr.cn
http://2OiT5U3A.LxLfr.cn
http://6XL4yULf.LxLfr.cn
http://YX2oJ36L.LxLfr.cn
http://V3C4ONth.LxLfr.cn
http://FerE82uC.LxLfr.cn
http://SzELhcOL.LxLfr.cn
http://kzuJcSRd.LxLfr.cn
http://M66ZYten.LxLfr.cn
http://CqrmlsIA.LxLfr.cn
http://P53Yuhd0.LxLfr.cn
http://SrL7tcGe.LxLfr.cn
http://9MYUvSAf.LxLfr.cn
http://WgPRxjew.LxLfr.cn
http://cl7qP3tA.LxLfr.cn
http://y7bKrWdZ.LxLfr.cn
http://r2BNOboc.LxLfr.cn
http://t89rN8rF.LxLfr.cn
http://1MLTjaXW.LxLfr.cn
http://ARmtNHUt.LxLfr.cn
http://www.dtcms.com/a/376329.html

相关文章:

  • GitHub 热榜项目 - 日榜(2025-09-10)
  • 基于YOLO集成模型的无人机多光谱风电部件缺陷检测
  • ssh域名过期,消息推送到企业微信
  • 【Python】爬虫html提取内容基础,bs4
  • zabbix告警推送钉钉
  • Android系统框架知识系列(二十):专题延伸:JVM vs ART/Dalvik - Android运行时演进深度解析
  • 关于在pycharm终端连接服务器
  • VPS、云服务器、独立服务器的区别是什么?新手服务器选择指南
  • 10. 游戏开发中的TCP与UDP
  • 第1章:操作系统和计算机网络
  • 在uniapp/vue项目中全局挂载component
  • 【ubuntu 24.04 LTS】真实实验部署ollama0.11.6+deepseekR1:1.5b+open-webUI
  • [万字长文]AJAX入门-常用请求方法和数据提交、HTTP协议-报文、接口文档、案例实战
  • 基于 Vue3 + VueOffice 的多格式文档预览组件实现(支持 PDF/Word/Excel/PPT)
  • 【Unity UGUI 交互组件——Scrollbar(8)】
  • 报错Failed to set ntp: NTP not supported
  • 零基础学AI大模型之读懂AI大模型
  • 《嵌入式硬件(六):ARM汇编核心内容总结》
  • 力扣刷题笔记-三数之和
  • WPF WriteableBitmap 高性能双缓冲图片显示方案
  • 如何优化WordPress中的图片提升网站性能
  • Word添加图/表题注
  • 十八、从0开始卷出一个新项目之瑞萨RZN2L使用ADC+DMA接收数据流
  • 日志文件-输出宏的实现
  • AI 帮我写单测:pytest 覆盖率提升 40% 的协作日志
  • RL【7-2】:Temporal-difference Learning
  • 50条常用的MySQL命令汇总
  • 宝塔SSL自动续签
  • Nginx SSL/TLS 配置
  • 剧本杀小程序系统开发:开启沉浸式社交娱乐新纪元