TensorFlow中tf.nn.embedding_lookup是否可微?反向传播机制解析
tf.nn.embedding_lookup的反向传播与梯度问题 我来给你捋明白这个疑问——其实哪怕tf.nn.embedding_lookup看起来是“硬索引查找”,它的反向传播逻辑TensorFlow已经帮你处理得明明白白,完全不影响embedding矩阵的训练更新。
先拆解前向传播的本质
首先,你用tf.get_variable创建的embedding_matrix是一个可训练的参数矩阵,形状一般是[词汇表大小/类别总数, 嵌入维度]——每一行对应一个类别/词的嵌入向量。
tf.nn.embedding_lookup(embedding_matrix, input_ids)做的事情非常直白:把输入的整数id序列,逐个映射到embedding_matrix里对应的行。比如输入input_ids = [3, 7],输出就是[embedding_matrix[3], embedding_matrix[7]],本质就是个索引取值操作。
反向传播:梯度如何更新embedding矩阵?
那损失的梯度怎么传到embedding_matrix上呢?其实逻辑很简单:
- 假设损失对lookup输出的梯度是
dy(形状和输出一致,比如[批量大小, 嵌入维度]),那么embedding_matrix的梯度就是把每个dy[i]累加到embedding_matrix[input_ids[i]]这一行。 - 举个具体例子:如果
input_ids是[3,7],对应的dy是[[0.2, 0.5], [0.1, 0.3]],那embedding_matrix[3]的梯度就是[0.2,0.5],embedding_matrix[7]的梯度就是[0.1,0.3],其他所有行的梯度都是0。
TensorFlow内部会自动完成这个稀疏累加的操作,你完全不用手动实现——这和全连接层的梯度更新逻辑本质相通,只不过这里是只更新被选中的嵌入行,是一种稀疏的梯度更新。
关于输入id的梯度
你问到的“针对输入id的梯度”,这里要明确:input_ids一般是离散的整数类别数据,它本身不是可训练的参数,也不是连续的数值型张量,所以从数学上来说,离散变量的导数是没有定义的。
在TensorFlow的默认逻辑里,对input_ids的梯度会被视为0,或者说不会计算这个梯度。如果你的场景需要对“类别选择”这类操作求梯度(比如某些生成式模型),那通常会用Gumbel-Softmax这类连续近似的方法,但这已经超出了tf.nn.embedding_lookup的原生使用场景了。
总结一下:tf.nn.embedding_lookup的反向传播通过稀疏更新embedding矩阵实现,仅对被id选中的嵌入行累加梯度;而输入id是离散值,无有效梯度(除非用连续近似手段)。
内容的提问来源于stack exchange,提问作者Animesh Karnewar

