【问题标题】:Tensorflow - Train only a subset of embedding matrixTensorflow - 仅训练嵌入矩阵的一个子集
【发布时间】:2018-09-26 02:08:38
【问题描述】:

我有一个嵌入矩阵e,定义如下

e = tf.get_variable(name="embedding", shape=[n_e, d], 
              initializer=tf.contrib.layers.xavier_initializer(uniform=False))

其中n_e 是指实体的数量,d 是潜在维度的数量。对于此示例,假设 d=10。

培训:

optimizer = tf.train.GradientDescentOptimizer(0.01)
grads_and_vars = optimizer.compute_gradients(loss)
train_op = optimizer.apply_gradients(grads_and_vars, global_step=global_step)

模型在训练后保存。 稍后,会添加新实体(例如 2),从而生成 n_e_new。现在我想重新训练模型,但保留已训练实体的嵌入,即仅重新训练 delta(2 个新实体)。

我加载保存的e

init_e = np.zeros((n_e_new, d), dtype=np.float32)
r = list(range(n_e_new - 2))
init_e[r, :] = # load e from saved model

e = tf.get_variable(name="embedding", initializer=init_e)
gather_e = tf.nn.embedding_lookup(e, [n_e, n_e+1])

培训:

optimizer = tf.train.GradientDescentOptimizer(0.01)
grads_and_vars = optimizer.compute_gradients(loss, gather_e)
train_op = optimizer.apply_gradients(grads_and_vars, global_step=global_step)

compute_gradients 出现错误: NotImplementedError: ('尝试优化不支持的类型', )

我了解gather_ecompute_gradients 的第二个参数不是变量,但无法弄清楚如何实现此部分训练/更新。

P.S - 我也看过 this post,但似乎也找不到解决方案。

编辑: 代码示例(按照@meruf 建议的方法):

if new_data_available:
    e = tf.get_variable(name="embedding", shape=[n_e_new, 1, d],
              initializer=tf.contrib.layers.xavier_initializer(uniform=False))
    e_old = tf.get_variable(name="embedding_old", initializer=<load e from saved model>, trainable=False)
    e_new = tf.concat([e_old, e], 0)

else:
    e = tf.get_variable(name="embedding", shape=[n_e, d], 
              initializer=tf.contrib.layers.xavier_initializer(uniform=False))

查找如下:

if new_data_available:
    var_p = tf.nn.embedding_lookup(e_new, indices)
else:
    var_p = tf.nn.embedding_lookup(e, indices)

loss = #some operations on var_p and other variabes that are a result of the lookup above

问题是当new_data_available 为真时,ee_new 在每个时期都不会改变。它们保持不变。

【问题讨论】:

标签: python tensorflow


【解决方案1】:

您不应该在优化器级别更改代码。你可以很容易地告诉 tensorflow 哪个变量是可训练的。

我们来看看tf.getVariable()的定义,

tf.get_variable(
name,
shape=None,
dtype=None,
initializer=None,
regularizer=None,
trainable=True,
collections=None,
caching_device=None,
partitioner=None,
validate_shape=True,
use_resource=None,
custom_getter=None,
constraint=None
)

这里trainable参数表示该参数是否可训练。当您不想训练参数时,请将其设为 false。

为您的情况制作 2 组变量。一个是trainable=True,另一个是trainable=false

假设您有 100 个预训练变量和 10 个新变量要训练。现在将预训练变量加载到A,将新变量加载到B

注意: 有关实现细节,您应该查看tf.cond 函数以了解运行时决策。主要用于查找。因为现在您的新 B 嵌入的索引从 0 开始。但是您可能已经在数据集或程序中从# of pretrained embedding+1 分配了它们。因此,在 tensorflow 中,您可以做出运行时决策

伪代码

if index_number is >= number of pretrained embedding
    index_number = index_number - number of pretrained embedding
    look_up on B matrix
else
    look_up on A matrix

An Ipython Notebook of the example. (slightly different than the example given here.)

更新:

让我们看一个例子我的意思,

首先加载库

import tensorflow as tf

声明占位符

y_ = tf.placeholder(tf.float32, [None, 2])
x = tf.placeholder(tf.int32, [None])
z = tf.placeholder(tf.bool, []) # is the example in the x contains new data or not

创建网络

e = tf.get_variable(name="embedding", shape=[5,10],initializer=tf.contrib.layers.xavier_initializer(uniform=False))
e_old = tf.get_variable(name="embedding1", shape=[5,10],initializer=tf.contrib.layers.xavier_initializer(uniform=False),trainable=False)
out = tf.cond(z,lambda : e, lambda : e_old)
lookup = tf.nn.embedding_lookup(out,x)
W = tf.get_variable(name="weight", shape=[10,2],initializer=tf.contrib.layers.xavier_initializer(uniform=False))
l = tf.nn.relu(tf.matmul(lookup,W))
y = tf.nn.softmax(l)

计算损失

cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y), reduction_indices=[1]))

优化损失

train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)

加载并运行图表

sess = tf.InteractiveSession()
tf.global_variables_initializer().run()

打印初始化值

我们正在打印这些值,以便我们稍后检查我们的值是否发生变化。

e_out_tf,e_out_old_tf = sess.run([e,e_old])



print("New Data ", e_out_tf)
print("Old Data", e_out_old_tf)




 ('New Data ', array([[-0.38952214, -0.37217963,  0.11370762, -0.13024905,  0.11420489,
            -0.09138191,  0.13781562, -0.1624797 , -0.27410012, -0.5404499 ],
           [-0.0065698 ,  0.04728106,  0.53637034, -0.13864517, -0.36171854,
             0.40325132,  0.7172644 , -0.28067762, -0.0258827 , -0.5615116 ],
           [-0.17240004,  0.3765518 ,  0.4658525 ,  0.16545495, -0.37515178,
            -0.39557686, -0.50662124, -0.06570222, -0.3605038 ,  0.13746035],
           [ 0.19647208, -0.16588202,  0.5739292 ,  0.43803877, -0.05350745,
             0.71350956,  0.39937392, -0.45939735,  0.09050641, -0.18077391],
           [-0.05588558,  0.7295865 ,  0.42288807,  0.57227516,  0.7268311 ,
            -0.1194113 ,  0.28589466,  0.09422033, -0.10094754,  0.3942643 ]],
          dtype=float32))
    ('Old Data', array([[ 0.5308224 , -0.14003026, -0.7685277 ,  0.06644323, -0.02585996,
            -0.1713268 ,  0.04987739,  0.01220775,  0.33571896,  0.19891626],
           [ 0.3288728 , -0.09298109,  0.14795913,  0.21343362,  0.14123142,
            -0.19770677,  0.7366793 ,  0.38711038,  0.37526497,  0.440099  ],
           [-0.29200613,  0.4852043 ,  0.55407804, -0.13675605, -0.2815263 ,
            -0.00703347,  0.31396288, -0.7152872 ,  0.0844975 ,  0.4210107 ],
           [ 0.5046112 ,  0.3085646 ,  0.19497707, -0.5193338 , -0.0429871 ,
            -0.5231836 , -0.38976955, -0.2300536 , -0.00906788, -0.1689194 ],
           [-0.1231837 ,  0.54029703,  0.45702592, -0.07886257, -0.6420077 ,
            -0.24090563, -0.02165782, -0.44103763, -0.20914222,  0.40911582]],
          dtype=float32))

测试用例

现在我们将测试我们的理论,如果 1. 不可训练变量的变化与否 2. 可训练变量的变化与否。 我们声明了一个额外的占位符 z 来指示我们的输入是否包含 new dataold data

这里,index 0 包含 新数据,如果 zTrue,则可以训练。

feed_dict={x: [0],z:True}
lookup_tf = sess.run([lookup], feed_dict=feed_dict)

检查该值是否与上述值匹配。

print(lookup_tf)


[array([[-0.38952214, -0.37217963,  0.11370762, -0.13024905,  0.11420489,
        -0.09138191,  0.13781562, -0.1624797 , -0.27410012, -0.5404499 ]],
      dtype=float32)]

我们将发送 z=True 以指示您要查找的嵌入。

因此,当您发送批次时,请确保该批次仅包含旧数据或新数据。

feed_dict={x: [0], y_: [[0,1]], z:True} 
_, = sess.run([train_step], feed_dict=feed_dict)


lookup_tf = sess.run([lookup], feed_dict=feed_dict)

训练后,让我们检查一下它的行为是否正常。

print(lookup_tf)


[array([[-0.559212  , -0.362611  ,  0.06011545, -0.02056453,  0.26133284,
        -0.24933788,  0.18598196, -0.00602196, -0.12775017, -0.6666256 ]],
      dtype=float32)]

请参阅索引 0 包含可训练的新数据,并且由于 SGD 更新而与之前的值相比发生了变化。

让我们尝试相反的方法

feed_dict={x: [0], y_: [[0,1]], z:False} 
lookup_tf = sess.run([lookup], feed_dict=feed_dict)
print(lookup_tf)
_, = sess.run([train_step], feed_dict=feed_dict)
lookup_tf = sess.run([lookup], feed_dict=feed_dict)
print(lookup_tf)


[array([[ 0.5308224 , -0.14003026, -0.7685277 ,  0.06644323, -0.02585996,
        -0.1713268 ,  0.04987739,  0.01220775,  0.33571896,  0.19891626]],
      dtype=float32)]
[array([[ 0.5308224 , -0.14003026, -0.7685277 ,  0.06644323, -0.02585996,
        -0.1713268 ,  0.04987739,  0.01220775,  0.33571896,  0.19891626]],
      dtype=float32)]

【讨论】:

  • 我知道 TensorFlow 变量的 trainable 参数,但想知道是否有一种方法可以只包含一个变量并使其一部分不可训练。由于在新实体到达时使用预先训练的嵌入和重新训练是一种常见的用例,我认为必须有一种通用的方法来解决这个问题。我将尝试您的解决方案,看看它是否适合我。谢谢。
  • 我尝试使用 2 个不同的变量(A 具有可训练的假,B 具有可训练的真)并在张量 C(A 与 B 连接)上进行查找。然后根据 C 计算损失。但是,我面临的问题是变量 B 在训练期间从未更新。它保持不变。我该如何解决这个问题?
  • @meruf - 在原始帖子中添加了代码。请帮忙。
  • 别忘了查看我分享的笔记本。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-06-18
  • 2020-04-16
  • 1970-01-01
  • 1970-01-01
  • 2016-06-20
  • 2017-02-16
  • 2019-07-11
相关资源
最近更新 更多