<p>Be aware that <a href="http://%20%20%20[1]:%20https://arxiv.org/pdf/1608.05859.pdf" rel="noreferrer">Press and Wolf</a> dont't propose to freeze the weights to some pretrained ones, but tie them. That means, to ensure that input and output weights are always the same during training (in the sense of synchronized).</p>

<p>In a typical NLP model (e.g. language modelling/translation), you have an input dimension (vocabulary) of size <code>V</code> and a hidden representation size <code>H</code>. Then, you start with an <code>Embedding</code> layer, which is a matrix <code>VxH</code>. And the output layer is (probably) something like <code>Dense(V, activation='softmax')</code>, which is a matrix <code>H2xV</code>. When tying the weights, we want that those matrices are the same (therefore, <code>H==H2</code>).
For doing this in Keras, I think the way to go is via shared layers:</p>

<p>In your model, you need to instantiate a shared embedding layer (of dimension <code>VxH</code>), and apply it to either your input and output. But you need to transpose it, to have the desired output dimensions (<code>HxV</code>). So, we declare a <code>TiedEmbeddingsTransposed</code> layer, which transposes the embedding matrix from a given layer (and applies an activation function):</p>

<pre><code>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()))
</code></pre>

<p>The usage of this layer is:</p>

<pre><code># 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)
</code></pre>

<p>I have tested this in <a href="https://github.com/lvapeab/nmt-keras" rel="noreferrer">NMT-Keras</a> and it trains properly. But, as I try to load a trained model, it gets an error, related to the way Keras loads the models: it doesn't load the weights from the <code>tied_to</code>. I've found several questions regarding this (<a href="https://stackoverflow.com/questions/42015116/decoders-weights-of-autoencoder-with-tied-weights-in-keras">1</a>, <a href="https://stackoverflow.com/questions/47106830/keras-get-weights-returning-empty">2</a>, <a href="https://gist.github.com/dswah/c6b3e4d47d933b057aab32c9c29c4221#gistcomment-2298473" rel="noreferrer">3</a>), but I haven't managed to solve this issue. If someone have any ideas on the next steps to take, I'd be very glad to hear them :)</p>
How to tie word embedding and softmax weights in keras?
<p>Its commonplace for various neural network architectures in NLP and vision-language problems to tie the weights of an initial word embedding layer to that of an output softmax. Usually this produces a boost to sentence generation quality. (see example <a href="https://arxiv.org/pdf/1608.05859.pdf" rel="noreferrer">here</a>)</p>

<p>In Keras its typical to embed word embedding layers using the Embedding class, however there seems to be no easy way to tie the weights of this layer to the output softmax. Would anyone happen to know how this could be implemented ?</p>