Repository: flairNLP/flair

Issue:
RuntimeError: storage has wrong size when loading en-sentiment
When trying to load the **en-sentiment model**, I get the error:

RuntimeError: storage has wrong size: expected -1862414276 got 22700

I am able to load other models such as **de-offensive-language** and the **ner** SequenceTagger.

Code:
```
from flair.models import TextClassifier
from flair.data import Sentence
classifier = TextClassifier.load('en-sentiment')
```
Possibly related to issue https://github.com/zalandoresearch/flair/issues/172 ?
But the fix for that was merged back in November? https://github.com/zalandoresearch/flair/pull/194

Have tried deleting and redownloading the model.

Platform: Windows 10, Torch 1.0 (cpu), Python 3.6.4 (Anaconda)


Discussion:
Hi @rookie32 - yes it is the same bug as reported in #194, namely that very large models (over 2GB) cannot be de-serialized in certain windows and mac setups. This is a known issue in the pickle library which is used by pytorch to serialize. 

We currently have a workaround in place in which we make sure that models do not go over 2GB in size, but overlooked this with the en-sentiment model. We will fix this for the next release!
Here also the link to the corresponding [pickle issue](https://bugs.python.org/issue24658).
I am facing the same issue on my windows machine (windows 10, 8 GB RAM). Do we have a fix for this issue?

Yes, we are working on this at the moment. We will push an updated version with the upcoming 0.4.1 release, within the next weeks.
It's pretty weird to me that this hasn't been solved, as the discussion about this bug in pickle was reported in 2015.

Anyways, I created a temporary workaround. 

**Disclaimer: this will use a big amount of RAM. If there's a more memory efficient solution that doesn't change the way Torch serializes the models (i.e. doesn't change `serialization.py`), let us know**

Step 1:
Create a `text_classifier.py` file, which should be a copy of `flair/models/text_classification_model.py`

Step 2:
Replace the `_load_state` function with the following two functions: 
https://gist.github.com/highway11git/1fa64621ea1b04e6494bf6672a750b24

Step 3:
Instead of using `from flair.models import TextClassifier` in your project, import the `TextClassifier` class from your newly created file. Like so: `from text_classifier import TextClassifier`
Hi @highway11git thanks very much for this! We'll test it and see if we can directly integrate it. Perhaps we could add a special flag to the model loader to allow users to specify which method they want to use to load a model, with the default being the current one.
I've updated the gist:
1. `load_big_file` now uses mmap instead of BytesIO, which makes it _much more memory efficient_
2. `_load_state` now also uses the `load_big_file` function if the user doesn't use cuda

Thanks for testing it, and keep us up to date on any progress.
Cool, thanks! We'll test the solution and let you know!

Resolving patch:
diff --git a/flair/file_utils.py b/flair/file_utils.py
--- a/flair/file_utils.py
+++ b/flair/file_utils.py
@@ -11,6 +11,7 @@
 import re
 from urllib.parse import urlparse
 
+import mmap
 import requests
 
 # from allennlp.common.tqdm import Tqdm
@@ -22,6 +23,20 @@
 CACHE_ROOT = os.path.expanduser(os.path.join('~', '.flair'))
 
 
+def load_big_file(f):
+    """
+    Workaround for loading a big pickle file. Files over 2GB cause pickle errors on certin Mac and Windows distributions.
+    :param f:
+    :return:
+    """
+    logger.info(f'loading big file {f}')
+    with open(f, 'r+b') as f_in:
+        # mmap seems to be much more memory efficient
+        bf = mmap.mmap(f_in.fileno(), 0)
+        f_in.close()
+    return bf
+
+
 def url_to_filename(url: str, etag: str = None) -> str:
     """
     Converts a url into a filename in a reversible way.
diff --git a/flair/models/sequence_tagger_model.py b/flair/models/sequence_tagger_model.py
--- a/flair/models/sequence_tagger_model.py
+++ b/flair/models/sequence_tagger_model.py
@@ -238,10 +238,14 @@ def _load_state(cls, model_file: Union[str, Path]):
         # https://docs.python.org/3/library/warnings.html#temporarily-suppressing-warnings
         with warnings.catch_warnings():
             warnings.filterwarnings("ignore")
+            # load_big_file is a workaround by https://github.com/highway11git to load models on some Mac/Windows setups
+            # see https://github.com/zalandoresearch/flair/issues/351
             if torch.cuda.is_available():
-                state = torch.load(str(model_file))
+                f = flair.file_utils.load_big_file(str(model_file))
+                state = torch.load(f)
             else:
-                state = torch.load(str(model_file), map_location={'cuda:0': 'cpu'})
+                f = flair.file_utils.load_big_file(str(model_file))
+                state = torch.load(f, map_location={'cuda:0': 'cpu'})
         return state
 
     def forward_loss(self, sentences: Union[List[Sentence], Sentence], sort=True) -> torch.tensor:
diff --git a/flair/models/text_classification_model.py b/flair/models/text_classification_model.py
--- a/flair/models/text_classification_model.py
+++ b/flair/models/text_classification_model.py
@@ -140,10 +140,14 @@ def _load_state(cls, model_file: Union[str, Path]):
         # https://docs.python.org/3/library/warnings.html#temporarily-suppressing-warnings
         with warnings.catch_warnings():
             warnings.filterwarnings("ignore")
+            # load_big_file is a workaround by https://github.com/highway11git to load models on some Mac/Windows setups
+            # see https://github.com/zalandoresearch/flair/issues/351
             if torch.cuda.is_available():
-                state = torch.load(str(model_file))
+                f = flair.file_utils.load_big_file(str(model_file))
+                state = torch.load(f)
             else:
-                state = torch.load(str(model_file), map_location={'cuda:0': 'cpu'})
+                f = flair.file_utils.load_big_file(str(model_file))
+                state = torch.load(f, map_location={'cuda:0': 'cpu'})
         return state
 
     def forward_loss(self, sentences: Union[List[Sentence], Sentence]) -> torch.tensor:


Test patch: