深度学习进阶:自然语言处理|3.4 QA|用 SimpleCBOW 讲清楚 backward 为什么有的 return,有的不 return

结论:dW 是当前层参数的责任,留下来更新参数;dx 是当前层输入的责任,传回给制造这个输入的前一层。

1. 先看一个数字例子

只看 CBOW 里的这一句:

python 复制代码
score = h * W_out

假设:

text 复制代码
h = 2
W_out = 3
score = 6

后面传回来:

text 复制代码
ds = 10

意思是:score 对 loss 的影响是 10。

算 dW_out

text 复制代码
score = h * W_out
score = 2 * W_out

所以:

text 复制代码
W_out 每变 1,score 变 2
score 对 loss 的影响是 10

得到:

text 复制代码
dW_out = 2 * 10 = 20

dW_out 是当前层参数 W_out 的责任。

所以:

text 复制代码
存进 self.grads,之后更新 W_out。
不传给前一层。

算 dh

text 复制代码
score = h * W_out
score = h * 3

所以:

text 复制代码
h 每变 1,score 变 3
score 对 loss 的影响是 10

得到:

text 复制代码
dh = 3 * 10 = 30

dh 是输入 h 的责任。

但 h 是前面的 in_layer 算出来的。

所以:

text 复制代码
return dh,传回给 in_layer。

最短记法:

text 复制代码
dW_out = 20:当前层的 W_out 要改,所以留下。
dh = 30:前一层给的 h 有责任,所以传回去。

2. SimpleCBOW 的前向

代码位置:ch03/simple_cbow.py

python 复制代码
h0 = self.in_layer0.forward(contexts[:, 0])
h1 = self.in_layer1.forward(contexts[:, 1])
h = (h0 + h1) * 0.5
score = self.out_layer.forward(h)
loss = self.loss_layer.forward(score, target)

图:

text 复制代码
contexts[:,0] ──> in_layer0: MatMul(W_in) ──> h0 ──┐
                                                     ├─> h ──> out_layer: MatMul(W_out) ──> score ──> loss
contexts[:,1] ──> in_layer1: MatMul(W_in) ──> h1 ──┘

MatMul 的公式只有一个:

text 复制代码
out = xW

所以它反向时会同时算两个梯度:

python 复制代码
dx = np.dot(dout, W.T)
dW = np.dot(self.x.T, dout)
self.grads[0][...] = dW
return dx

2. dW 和 dx 的区别

对 out = xW 来说:

text 复制代码
dW:loss 对 W 的梯度
    用来更新当前层的 W
    存到 self.grads

dx:loss 对 x 的梯度
    x 是前一层的输出
    要 return 给前一层继续反传

图:

text 复制代码
前一层输出 x ──> [ 当前层:out = xW ] ──> 后一层

反向:

前一层  <── dx ── [ 当前层 ] <── dout ── 后一层
                 │
                 └── dW 存入 self.grads,之后给优化器更新 W

一句话:

text 复制代码
dW 留在本层,用来改本层参数;dx 返回出去,用来通知前一层。

3. SimpleCBOW 的反向

代码:

python 复制代码
def backward(self, dout=1):
    ds = self.loss_layer.backward(dout)
    da = self.out_layer.backward(ds)
    da *= 0.5
    self.in_layer1.backward(da)
    self.in_layer0.backward(da)
    return None

逐行看:

text 复制代码
loss_layer.backward(dout)

得到:

text 复制代码
ds = loss 对 score 的梯度

因为 score 是 out_layer 的输出,所以 ds 要传给 out_layer。


text 复制代码
out_layer.backward(ds)

out_layer 是:

text 复制代码
score = h W_out

它会算:

text 复制代码
dW_out:更新 W_out 用,存入 out_layer.grads
da:loss 对 h 的梯度,return 出来

图:

text 复制代码
h ──> out_layer(W_out) ──> score ──> loss

反向:

h <── da ── out_layer <── ds
             │
             └── dW_out 存入 self.grads

这里必须 return da,因为 h 前面还有 in_layer0 和 in_layer1。


text 复制代码
self.in_layer1.backward(da)
self.in_layer0.backward(da)

in_layer0 / in_layer1 是:

text 复制代码
h0 = contexts[:,0] W_in
h1 = contexts[:,1] W_in

它们会算:

text 复制代码
dW_in:更新 W_in 用,存入 in_layer.grads
dcontexts:loss 对 contexts 的梯度,return 出来

但是这里返回的 dcontexts 没人接:

python 复制代码
self.in_layer1.backward(da)
self.in_layer0.backward(da)

原因:contexts 是输入数据,不是前一层的输出。

图:

text 复制代码
contexts ──> in_layer(W_in) ──> h

反向:

contexts <── dcontexts ── in_layer <── da
                         │
                         └── dW_in 存入 self.grads

dW_in 有用:更新词向量。

dcontexts 没用:不会更新输入数据,也没有更前面的层。

4. 为什么 SimpleCBOW 最后 return None

训练时调用方式是:

python 复制代码
loss = model.forward(batch_x, batch_t)
model.backward()
optimizer.update(model.params, model.grads)

优化器只看:

text 复制代码
model.params
model.grads

不看:

text 复制代码
model.backward() 的返回值

所以 SimpleCBOW.backward() 最后:

python 复制代码
return None

5. 最短判断规则

text 复制代码
当前层参数梯度:dW/db
    -> 存 self.grads
    -> 不 return
    -> 给 optimizer 更新参数

当前层输入梯度:dx
    -> 如果前面还有层,就 return
    -> 如果前面没有层,或者没人接,就不用管

放到 CBOW:

text 复制代码
out_layer.backward(ds)
    dW_out 留下
    da return,传给 in_layer

in_layer.backward(da)
    dW_in 留下
    dcontexts return 了也没人用

SimpleCBOW.backward()
    整个模型的梯度都已经写入 self.grads
    外部不需要返回值
    return None
相关推荐
冬奇Lab1 小时前
一天一个开源项目(第252篇):Lightpanda —— 用 Zig 从零写的 AI 专用无头浏览器,内存省 16 倍、速度快 9 倍
人工智能·开源·资讯
小豆庄1 小时前
本地视频打码:多模型检测、帧间追踪与18种模糊的选型
opencv·目标检测·ai·自然语言处理·音视频·视频
在努力的前端小白1 小时前
基于FastAPI+LangChain+LangGraph+Agent+多模态的AI Agent 有渔渔具网站
人工智能·langchain·vue·fastapi
锋行天下2 小时前
Langgraph-cli 本地构建失败,改用服务器docker构建的流程
人工智能·前端框架·全栈
释厄6232 小时前
群体投票双通道狭隘论——AI泡沫出清后的主角确权
人工智能
IT古董2 小时前
《Agent开发工程师成长指南》- 第4章 第2节:Prompt设计五要素——任务、角色、上下文、约束与输出格式
大数据·人工智能·prompt
霸道流氓气质2 小时前
AI 流式对话与实时聊天平台:SSE 解码竞态、有状态会话隔离与背压取消的 Vue3 + Spring AI 生产级实战
java·人工智能·spring
孟健3 小时前
我把9个AI助理迁到了Grok Bot,连远程服务器都省了
人工智能·aigc·ai编程
AI+程序员在路上3 小时前
RV1126B 上支持的模型及全流程应用--从CNN及语音到 LLMVLM 与跨平台选型
linux·c语言·人工智能
AI备案指南-满满3 小时前
算法备案审核不通过,一般是什么原因?
java·人工智能·算法·大模型备案·算法备案·大模型登记