仅更新Tensorflow中单词嵌入矩阵的一部分

仅更新Tensorflow中单词嵌入矩阵的一部分,tensorflow,word-embedding,Tensorflow,Word Embedding,假设我想在训练期间更新预先训练好的单词嵌入矩阵,有没有办法只更新单词嵌入矩阵的子集 我查看了Tensorflow API页面,发现: 创建一个优化器。 opt=GradientDescentOptimizerlearning_rate=0.1 计算变量列表的梯度。 grads_和_vars=opt.compute_gradientsloss, grads_和_vars是一个元组列表,梯度变量。你做什么就做什么 需要添加“渐变”部分,例如封盖等。 capped_grads_和_vars=[MyCa

假设我想在训练期间更新预先训练好的单词嵌入矩阵,有没有办法只更新单词嵌入矩阵的子集

我查看了Tensorflow API页面,发现:

创建一个优化器。 opt=GradientDescentOptimizerlearning_rate=0.1 计算变量列表的梯度。 grads_和_vars=opt.compute_gradientsloss, grads_和_vars是一个元组列表,梯度变量。你做什么就做什么 需要添加“渐变”部分,例如封盖等。 capped_grads_和_vars=[MyCappergv[0],gv[1]表示在grads_和_vars中的gv] 请优化器应用带封顶的渐变。 opt.apply_gradientscapped_grads_和_vars 但是,我如何将其应用于单词嵌入矩阵。假设我这样做:

word\u emb=tf.Variable0.2*tf.random\u uniform[syn0.shape[0],s['es'],minval=-1.0,maxval=1.0,dtype=tf.float32,name='word\u emb',trainable=False gather\u emb=tf.gatherword\u emb,索引假设我通过feed\u dict传递一些索引作为占位符 opt=tf.train.AdamOptimizer1e-4 梯度=opt.compute\u gradientsloss,gather\u emb 然后如何使用opt.apply_gradients和tf.scatter_update更新原始嵌入矩阵?此外,如果compute_gradient的第二个参数不是tf.变量,则tensorflow会抛出一个错误;DR:TensorFlow的默认实现将为word_emb生成稀疏更新,该更新仅修改参与正向传递的word_emb行

op相对于单词_emb的梯度是一个对象。该对象表示一个稀疏张量,除索引选择的行外,该张量处处为零。对opt.minimizeloss调用的调用,该调用调用tf.scatter_subword_emb,…*仅更新由索引选择的word_emb行

另一方面,如果要修改返回的tf.indexedlices,可以对其索引和值属性执行任意TensorFlow操作,并创建可传递给的新tf.indexedlices。例如,您可以使用MyCapper对渐变进行封顶,如示例中所示,使用以下调用:

grad, = opt.compute_gradients(loss, word_emb)
train_op = opt.apply_gradients(
    [tf.IndexedSlices(MyCapper(grad.values), grad.indices)])
类似地,您可以通过创建具有不同索引的新tf.indexedlices来更改将要修改的索引集


*一般来说,如果您只想更新TensorFlow中变量的一部分,可以使用,分别设置,将先前存储在变量中的值添加到+=或从-=中减去。

因为您只想选择要更新的元素,而不想更改渐变,所以可以执行以下操作

让索引_to_update是一个布尔张量,表示要更新的索引,并在链接中定义条目_stop_渐变,然后:

gather_emb = entry_stop_gradients(gather_emb, indices_to_update)

事实上,我也在努力解决这样的问题。在我的例子中,我需要训练一个带有w2v嵌入的模型,但并不是所有的标记都存在于嵌入矩阵中。因此,对于那些不在矩阵中的标记,我进行了随机初始化。当然,嵌入已经过培训的令牌不应该更新,因此我提出了这样一个解决方案:

class PartialEmbeddingsUpdate(tf.keras.layers.Layer):
def __init__(self, len_vocab, 
             weights,
            indices_to_update):
    super(PartialEmbeddingsUpdate, self).__init__()
    self.embeddings = tf.Variable(weights, name='embedding', dtype=tf.float32)
    self.bool_mask = tf.equal(tf.expand_dims(tf.range(0,len_vocab),1), tf.expand_dims(indices_to_update,0))
    self.bool_mask = tf.reduce_any(self.bool_mask,1)
    self.bool_mask_not = tf.logical_not(self.bool_mask)
    self.bool_mask_not = tf.expand_dims(tf.cast(self.bool_mask_not, dtype=self.embeddings.dtype),1)
    self.bool_mask = tf.expand_dims(tf.cast(self.bool_mask, dtype=self.embeddings.dtype),1)
    
def call(self, input):
    input = tf.cast(input, dtype=tf.int32)
    embeddings = tf.stop_gradient(self.bool_mask_not * self.embeddings) + self.bool_mask * self.embeddings
    return tf.gather(embeddings,input)

其中len_vocab-是您的词汇长度,权重-权重矩阵,其中一些权重不应更新,索引_to_更新-这些标记的索引应更新。之后,我应用了这个层,而不是tf.keras.layers.Embeddings。希望它能帮助所有遇到同样问题的人。

这里如何定义子集?只定义嵌入矩阵中的行子集。因为每一行都是一个单词嵌入向量,它只是原始单词嵌入矩阵中单词嵌入向量的子集,这是我想要实现的,但在TensorFlow中,你确定这是广告宣传的吗?请参阅我的问题和其中的链接。似乎出于某种原因,TensorFlow正在将IndexedLices转换为稠密张量,并且更新速度会减慢。如果嵌入变量是tf.gather或tf.nn.embedding\u lookup的直接参数,则它肯定会起作用。如果梯度反向传播通过没有专门用于处理IndexedSlice的梯度函数的任何操作,那么IndexedSlice将转换为稠密张量。我相信目前只有tf.concat具有这种专门化。我想知道这是否可以仅用于更新特定的字向量,就像我有预训练向量一样对于我的大多数单词,但有一些是新单词,需要训练。tf.scatter\u sub op似乎没有专门用于处理索引的梯度函数。我问了一个问题,我想你可能知道。