【问题标题】:How to tie word embedding and softmax weights in keras?如何在 keras 中绑定词嵌入和 softmax 权重?
【发布时间】:2018-04-16 04:19:00
【问题描述】:

对于 NLP 和视觉语言问题中的各种神经网络架构来说,将初始词嵌入层的权重与输出 softmax 的权重联系起来很常见。通常这会提高句子生成质量。 (参见示例here

在 Keras 中,通常使用 Embedding 类嵌入词嵌入层,但是似乎没有简单的方法将此层的权重与输出 softmax 联系起来。有人会碰巧知道如何实现吗?

【问题讨论】:

  • 我不确定我是否正确理解了这个问题。你想预测嵌入向量作为输出而不是单词的 one-hot 编码吗?是这个问题吗?
  • 不,我认为问题是如何确保输入标记的嵌入向量与输出标记的嵌入向量相同,以及如何在模型学习时一起更新它们。这应该是可能的,因为输入词汇与输出词汇相同。它应该会有所帮助,因为要训练的参数更少。问题之一是他们想使用Embedding()作为输入(因为这是学习嵌入的标准),但对于输出他们使用Dense()
  • 你看过我的回答了吗?
  • 是的,这就是我想要做的,最近的一些语言建模论文发现这可以显着提升模型性能

标签: machine-learning neural-network nlp deep-learning keras


【解决方案1】:

正如您可能读到的here,您只需将trainable 标志设置为False。例如

aux_output = Embedding(..., trainable=False)(input)
....
output = Dense(nb_of_classes, .. ,activation='softmax', trainable=False)

【讨论】:

  • 冻结权重在使用预训练的词嵌入时很有用,但如果我想学习词嵌入,它就不起作用
  • 您可以将trainable 标志设置为False
  • 但提问者只想将输入和输出嵌入绑定在一起,以便它们在训练期间保持不变。 trainable=False 对此没有帮助,因为这意味着嵌入是永远固定的。是的,它们是相同的,但提问者也想学习嵌入,这意味着 trainable 必须是 True
  • 如您所见 - 提问者想要冻结 softmax 层(因为他接受了答案:))
  • 绑定权重意味着将参数减半并保持同步;不冻结两者。想象一下,您的模型只有一个未初始化的嵌入和一个未初始化的密集。冻结两者意味着零学习正在进行。这不是我们想要的答案。
【解决方案2】:

请注意Press and Wolf 不建议将权重冻结到一些预训练的权重,而是将它们绑定。这意味着,要确保在训练期间输入和输出权重始终相同(在同步的意义上)。

在典型的 NLP 模型(例如语言建模/翻译)中,您有一个大小为 V 的输入维度(词汇表)和一个大小为 H 的隐藏表示。然后,您从Embedding 层开始,它是一个矩阵VxH。输出层(可能)类似于Dense(V, activation='softmax'),它是一个矩阵H2xV。绑定权重时,我们希望这些矩阵相同(因此,H==H2)。 对于在 Keras 中执行此操作,我认为要走的路是通过共享层:

在您的模型中,您需要实例化一个共享嵌入层(维度为VxH),并将其应用于您的输入和输出。但是您需要对其进行转置,以获得所需的输出尺寸 (HxV)。因此,我们声明一个TiedEmbeddingsTransposed 层,它从给定层转置嵌入矩阵(并应用激活函数):

class TiedEmbeddingsTransposed(Layer):
    """Layer for tying embeddings in an output layer.
    A regular embedding layer has the shape: V x H (V: size of the vocabulary. H: size of the projected space).
    In this layer, we'll go: H x V.
    With the same weights than the regular embedding.
    In addition, it may have an activation.
    # References
        - [ Using the Output Embedding to Improve Language Models](https://arxiv.org/abs/1608.05859)
    """

    def __init__(self, tied_to=None,
                 activation=None,
                 **kwargs):
        super(TiedEmbeddingsTransposed, self).__init__(**kwargs)
        self.tied_to = tied_to
        self.activation = activations.get(activation)

    def build(self, input_shape):
        self.transposed_weights = K.transpose(self.tied_to.weights[0])
        self.built = True

    def compute_mask(self, inputs, mask=None):
        return mask

    def compute_output_shape(self, input_shape):
        return input_shape[0], K.int_shape(self.tied_to.weights[0])[0]

    def call(self, inputs, mask=None):
        output = K.dot(inputs, self.transposed_weights)
        if self.activation is not None:
            output = self.activation(output)
        return output


    def get_config(self):
        config = {'activation': activations.serialize(self.activation)
                  }
        base_config = super(TiedEmbeddingsTransposed, self).get_config()
        return dict(list(base_config.items()) + list(config.items()))

这一层的用法是:

# Declare the shared embedding layer
shared_embedding_layer = Embedding(V, H)
# Obtain word embeddings
word_embedding = shared_embedding_layer(input)
# Do stuff with your model
# Compute output (e.g. a vocabulary-size probability vector) with the shared layer:
output = TimeDistributed(TiedEmbeddingsTransposed(tied_to=shared_embedding_layer, activation='softmax')(intermediate_rep)

我已经在NMT-Keras 中对此进行了测试,并且可以正常训练。但是,当我尝试加载经过训练的模型时,它会出现错误,这与 Keras 加载模型的方式有关:它没有从 tied_to 加载权重。我发现了几个关于此的问题(123),但我还没有设法解决这个问题。如果有人对接下来要采取的步骤有任何想法,我会很高兴听到他们的声音:)

【讨论】:

    猜你喜欢
    • 2021-01-07
    • 2018-08-16
    • 2018-09-03
    • 2021-05-25
    • 2019-04-15
    • 1970-01-01
    • 2021-05-12
    • 1970-01-01
    • 2018-10-28
    相关资源
    最近更新 更多