You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何绑定torch.nn.Embedding与EmbeddingBag的嵌入权重?

问题描述

需要在神经网络中绑定两个嵌入层的权重:一个是处理顺序敏感Token嵌入的torch.nn.Embedding,另一个是处理顺序无关Token嵌入的torch.nn.EmbeddingBag。手动实现类EmbeddingBag结构时遇到数值稳定性问题,因此希望使用官方的torch.nn.EmbeddingBag,但尝试的权重绑定方法无法正常工作——反向传播后两个层的梯度不一致,代码如下:

#!/usr/bin/env python3

import torch
import torch.nn as nn

if __name__ == "__main__":
    V, max_seq, padding_idx, emb_dim, B = 10, 100, 1, 512, 32

    # 创建嵌入层并初始化EmbeddingBag
    emb_layer = nn.Embedding(V, emb_dim, padding_idx=padding_idx)
    emb_bag = nn.EmbeddingBag.from_pretrained(emb_layer.weight, freeze=False, padding_idx=padding_idx)

    tokens = torch.randint(0, V, (B, max_seq))
    y = torch.randn((B, emb_dim))
    loss = nn.MSELoss()

    # 通过EmbeddingBag反向传播
    y_ = emb_bag(tokens)
    l = loss(y_, y)
    assert emb_bag.weight.grad is None
    l.backward()
    assert emb_bag.weight.grad is not None

    # 断言失败:emb_layer的梯度为空,且两者梯度不一致
    assert emb_layer.weight.grad is not None and \
        torch.allclose(emb_bag.weight.grad, emb_layer.weight.grad)
解决方案

问题核心是EmbeddingBag.from_pretrained会创建新的参数张量,而非直接共享emb_layer.weight的内存空间,因此反向传播的梯度只会更新emb_bag的权重,无法同步到emb_layer。以下两种方法可实现真正的权重绑定:

方法1:直接赋值weight属性

让emb_bag的weight属性直接指向emb_layer的weight张量,实现内存共享:

import torch
import torch.nn as nn

if __name__ == "__main__":
    V, max_seq, padding_idx, emb_dim, B = 10, 100, 1, 512, 32

    emb_layer = nn.Embedding(V, emb_dim, padding_idx=padding_idx)
    # 初始化EmbeddingBag时不使用from_pretrained
    emb_bag = nn.EmbeddingBag(V, emb_dim, padding_idx=padding_idx)
    # 绑定权重
    emb_bag.weight = emb_layer.weight

    tokens = torch.randint(0, V, (B, max_seq))
    y = torch.randn((B, emb_dim))
    loss = nn.MSELoss()

    y_ = emb_bag(tokens)
    l = loss(y_, y)
    l.backward()

    # 断言会成功
    assert emb_layer.weight.grad is not None
    assert torch.allclose(emb_bag.weight.grad, emb_layer.weight.grad)

方法2:使用register_parameter共享参数

通过register_parameter方法将emb_bag的weight注册为emb_layer.weight的引用,确保参数共享:

import torch
import torch.nn as nn

if __name__ == "__main__":
    V, max_seq, padding_idx, emb_dim, B = 10, 100, 1, 512, 32

    emb_layer = nn.Embedding(V, emb_dim, padding_idx=padding_idx)
    emb_bag = nn.EmbeddingBag(V, emb_dim, padding_idx=padding_idx)
    # 注册共享参数
    emb_bag.register_parameter('weight', emb_layer.weight)

    tokens = torch.randint(0, V, (B, max_seq))
    y = torch.randn((B, emb_dim))
    loss = nn.MSELoss()

    y_ = emb_bag(tokens)
    l = loss(y_, y)
    l.backward()

    assert emb_layer.weight.grad is not None
    assert torch.allclose(emb_bag.weight.grad, emb_layer.weight.grad)
原理说明

两种方法都让emb_layer和emb_bag的weight指向同一个张量对象,反向传播时梯度会直接更新这个共享张量,因此两个层的梯度完全一致,实现了真正的权重绑定。

内容的提问来源于stack exchange,提问作者erip

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 10:21:39