Repository: flairNLP/flair

Issue:
token.get_embedding() with multiple embedding types returns in different order
`token.get_embedding()` does not necessarily return the embeddings in deterministic order across different environments.  

This is hugely problematic when trying to transfer a model to a different runtime environment (local vs remote) and/or when flair's embeddings are being fed into a different service.  Models are trained to expect certain sub-vectors in certain positions in the concatenated final vector.

`token.get_embedding()` sorts the embeddings by key when concatenating them into a single vector to return.  

```
embeddings = [
        self._embeddings[embed] for embed in sorted(self._embeddings.keys())
]
```

The keys come from `self.name` properties of the `Embedding` classes they came from.  However, these names are often just the paths to the model, which are hardly deterministic across environments, especially if we're loading our own weights.


A concrete example:

Locally:
```
stack = StackedEmbeddings [
        WordEmbeddings('glove'),  # self.name is cached path.  '/root/.gensim' or something.
        FlairEmbeddings('local/fine_tuned/embeddings.pt')
]
```
On `token.get_embedding()` this will put Flair first, since it comes first alphabetically when you sort their keys.


Deployment environment:
```
stack = StackedEmbeddings [
        WordEmbeddings('glove'),  # self.name is cached path.  '/root/.gensim' or something.
        FlairEmbeddings('/z/extra/resources/go/somewhere/different/embeddings.pt')
]
```
On `token.get_embedding()` this will put Glove first, since it comes first alphabetically when you sort their keys.


This will break a model trained on these embeddings when you try to deploy.  Once we tracked this down, our workaround is to override `self.name` after init.  Documentation on this functionality, or eliminating the need for this would be great.


Discussion:
@shoarora thanks for pointing this out. If you train a model such as a `SequenceTagger` or so with embeddings, the saved model will contain the original `self.name` of each embedding (since embeddings get packaged into the trained model), so we haven't encountered this error yet even though we prequently deploy models trained on one setup to another. 

But in the case as you describe where you set different paths in different environments and have a service that just consumes these embeddings, this will cause an error. One easy fix would be to have the `StackedEmbeddings` add a prefix to each embedding name in the constructor and so force the order to always be the same. I can put in a PR for this!

Resolving patch:
diff --git a/flair/embeddings.py b/flair/embeddings.py
--- a/flair/embeddings.py
+++ b/flair/embeddings.py
@@ -143,7 +143,8 @@ def __init__(self, embeddings: List[TokenEmbeddings]):
 
         # IMPORTANT: add embeddings as torch modules
         for i, embedding in enumerate(embeddings):
-            self.add_module("list_embedding_{}".format(i), embedding)
+            embedding.name = f"{str(i)}-{embedding.name}"
+            self.add_module(f"list_embedding_{str(i)}", embedding)
 
         self.name: str = "Stack"
         self.static_embeddings: bool = True


Test patch: