[NOT SOLVED] Training character level embeddings in FastText

Skinish: <p>Is it possible to load/train FastText weights and incorporate it in a pytorch model so that it can update character level weights and therefore train embeddings for unknown tokens also?</p>
<p>I had a look at <a href="https://github.com/facebookresearch/fastText/blob/master/python/doc/examples/FastTextEmbeddingBag.py" rel="nofollow noopener">this</a> but it seems that all the script does is to load the binary weights file into a word embedding matrix.</p>

dhpollack: <p>Are you training your fastText model with character data instead of word level data?  If you did that then I think you’d get the desired result.  But you’d probably have to preprocess the training data into a format that’s suitable for fastText.</p>

Skinish: <p>The problem is that I do not have access to a pytorch version of fastText, so that I am able to add the whole model as an embedding layer and train it. I have only trained fastText embeddings as described in <a href="https://github.com/facebookresearch/fastText" rel="nofollow noopener">here</a>. Is possible to use the <code>.bin</code> file to continue fine tuning the whole fastText model?</p>

dhpollack: <p>This might be easier if you explained with an example what you’ve done and what you haven’t done.  fastText basically creates an <code>nn.EmbeddingBag</code> weight matrix.  All the other stuff is basically lookups for the various components.  Have you compiled the fastText bindings for python?  If so, you can put the weight matrix into a normal <code>nn.EmbeddingBag</code>.</p>

dhpollack: <p>Below is an example of putting a fastText model into a dataset / dataloader.</p>
<aside class="onebox githubblob">
  <header class="source">
      <a href="https://github.com/dhpollack/fellowshipai/blob/master/data/europarl.py#L137" target="_blank" rel="nofollow noopener">github.com</a>
  </header>
  <article class="onebox-body">
    <h4><a href="https://github.com/dhpollack/fellowshipai/blob/master/data/europarl.py#L137" target="_blank" rel="nofollow noopener">dhpollack/fellowshipai/blob/master/data/europarl.py#L137</a></h4>
<pre class="onebox"><code class="lang-py"><ol class="start lines" start="127" style="counter-reset: li-counter 126 ;">
<li>    samples = torch.take(sentence, sample_idxes)  # num_samples x sample_len</li>
<li>    # make labels for samples</li>
<li>    labels = [self.label_encoder[label]] * num_samples</li>
<li>    labels = torch.LongTensor(labels)</li>
<li>    return samples, labels, num_samples</li>
<li>def shuffle_data(self):</li>
<li>    combined = list(zip(self.data, self.labels))</li>
<li>    random.shuffle(combined)</li>
<li>    self.data[:], self.labels[:] = zip(*combined)</li>
<li>
</li>
<li class="selected">class EUROPARLFT(data.Dataset):</li>
<li>splits = {"train": "txt_noxml/europarl.tokenized.all",</li>
<li>          "test": "test/europarl.tokenized.test",</li>
<li>          "split_train": "txt_noxml/europarl.tokenized.split.train",</li>
<li>          "split_valid": "txt_noxml/europarl.tokenized.split.valid",</li>
<li>          "dev_train": "txt_noxml/europarl.tokenized.trunc.train",</li>
<li>          "dev_test": "test/europarl.tokenized.trunc.test"}</li>
<li>splits_len = {"train": 10088220,</li>
<li>              "test": 21000,</li>
<li>              "split_train": 9988221,</li>
<li>              "split_valid": 100000,</li>
</ol></code></pre>


  </article>
  <div class="onebox-metadata">
    
    
  </div>
  <div style="clear: both"></div>
</aside>


Skinish: <p>But what does it actually do? Isn’t it just creating a lookup table for words? What about OOV words? Am I able to fine tune the character level embeddings with <code>nn.EmbeddingBag</code>?</p>

dhpollack: <p>If you train your fastText model with skipgrams then your lookup will also get partial words.  So if you have a skipgram of 3 then the word “there” returns [“there”, “the”, “her”, “ere”]… for unknown words, you won’t get the whole word for OOV, but you would get the components…  Now if your word has no components of any words then you’d get nothing, but that’s probably not going to happen if you train on enough data and the input is reasonable (i.e. same language as your train set).</p>

Skinish: <p>Makes sense. However, <code>nn.EmbeddingBag</code> is just a regular lookup table for word vectors, isn’t it? How does it generate embeddings for words that are not in the matrix?</p>

dhpollack: <p>So fastText doesn’t return anything for OOV.  But OOV when you have partial words would be super rare.  You are saying that the word you are looking up has no 3 letter sequence that is similar to any of the words in your training set?</p>

Skinish: <p>I will be more specific. Thanks for your help so far btw <img src="https://discuss.pytorch.org/images/emoji/apple/slight_smile.png?v=6" title=":slight_smile:" class="emoji" alt=":slight_smile:"></p>
<pre><code class="lang-auto">input_matrix = fasttext_model.get_input_matrix()  # numpy
num_emb, emb_dim = input_matrix.shape
self.embbag = nn.EmbeddingBag(num_emb, emb_dim)
</code></pre>
<p>What is inside this <code>input_matrix</code>?</p>
<p>word vectors for EVERY possible word and 3 letter sequence seen in train? or word vectors for words only? If the former, then the matrix will be too big to fit in memory, if the latter, OOV words that would have a fastText representation won’t have one, since wont be saved in the <code>self.embbag</code> matrix, correct?<br>
I hope I made myself clear</p>

dhpollack: <p>I think the former.  But you can also prune these matrices with fastText.  But having said that…</p>
<p>It’d be way easier for you to create a small dummy set, train a fastText model, then test it out yourself.  I’ve used fastText only a handful of times, so I wouldn’t really trust my own answers.  <img src="https://discuss.pytorch.org/images/emoji/apple/slight_smile.png?v=6" title=":slight_smile:" class="emoji" alt=":slight_smile:"></p>
<p>I am almost certain that fastText does not create a “not found” token.  You could do that yourself because creating an <code>EmbeddingBag</code> with <code>num_emb + 1</code> then copy the weight matrix from fastText over the appropriately.</p>
<pre><code class="lang-python">input_matrix = fasttext_model.get_input_matrix()  # numpy
num_emb, emb_dim = input_matrix.shape
self.embbag = nn.EmbeddingBag(num_emb + 1, emb_dim)
self.embbag.weight.data[:num_emb].copy_(torch.from_numpy(input_matrix))
</code></pre>
<p>I haven’t tried it but something like that might work.</p>

Skinish: <p>The point is that you are able to get the OOV embeddings using, for example, gensim. Check <a href="https://github.com/facebookresearch/fastText/issues/458" rel="nofollow noopener">this issue</a>. I’d just like to finetune these embeddings also</p>

dhpollack: <p>Ok, I think I get what you mean.  But I think that you should be asking the gensim people how they find out of word vectors.  Having said that.  I still think that for latin-based languages, it would be almost impossible to find a word that wasn’t composed of any smaller sub-sequences from other words.  And to get these subwords, you use the function <code>get_subwords</code>.  You’ll be able to get a representation of a word that is OOV, but still contains sub-sequences from other words.</p>

Skinish: <p>Thanks for your help. Let’s see if someone know how to finetune subword tokens.</p>

vinayak_athavale: <p>Hello, I haven’t tested this yet but i believe this should finetune the subword embeddings. <a href="https://github.com/facebookresearch/fastText/blob/master/python/doc/examples/FastTextEmbeddingBag.py" class="inline-onebox" rel="noopener nofollow ugc">fastText/FastTextEmbeddingBag.py at master · facebookresearch/fastText · GitHub</a></p>