Repository: flairNLP/flair

Issue:
generate_text() throws error with small temperatures
The 'generate_text()' method of the LanguageModel class throws an overflow error in certain cases, especially when the 'temperature' variable is set to be small. 

**To Reproduce**
```python
from flair.embeddings import FlairEmbeddings

language_model = FlairEmbeddings('news-forward').lm

language_model.generate_text(temperature=0.01, number_of_characters=100)
```




Discussion:

Resolving patch:
diff --git a/flair/models/language_model.py b/flair/models/language_model.py
--- a/flair/models/language_model.py
+++ b/flair/models/language_model.py
@@ -60,9 +60,9 @@ def __init__(self,
 
     def init_weights(self):
         initrange = 0.1
-        self.encoder.weight.data.uniform_(-initrange, initrange)
-        self.decoder.bias.data.fill_(0)
-        self.decoder.weight.data.uniform_(-initrange, initrange)
+        self.encoder.weight.detach().uniform_(-initrange, initrange)
+        self.decoder.bias.detach().fill_(0)
+        self.decoder.weight.detach().uniform_(-initrange, initrange)
 
     def set_hidden(self, hidden):
         self.hidden = hidden
@@ -85,7 +85,7 @@ def forward(self, input, hidden, ordered_sequence_lengths=None):
         return decoded.view(output.size(0), output.size(1), decoded.size(1)), output, hidden
 
     def init_hidden(self, bsz):
-        weight = next(self.parameters()).data
+        weight = next(self.parameters()).detach()
         return (Variable(weight.new(self.nlayers, bsz, self.hidden_size).zero_()),
                 Variable(weight.new(self.nlayers, bsz, self.hidden_size).zero_()))
 
@@ -111,14 +111,14 @@ def get_representation(self, strings: List[str], detach_from_lm=True):
     def repackage_hidden(self, h):
         """Wraps hidden states in new Variables, to detach them from their history."""
         if type(h) == torch.Tensor:
-            return Variable(h.data)
+            return Variable(h.detach())
         else:
             return tuple(self.repackage_hidden(v) for v in h)
 
     def initialize(self, matrix):
         in_, out_ = matrix.size()
         stdv = math.sqrt(3. / (in_ + out_))
-        matrix.data.uniform_(-stdv, stdv)
+        matrix.detach().uniform_(-stdv, stdv)
 
     @classmethod
     def load_language_model(cls, model_file: Union[Path, str]):
@@ -201,7 +201,7 @@ def save(self, file: Path):
 
         torch.save(model_state, str(file), pickle_protocol=4)
 
-    def generate_text(self, prefix: str = '', number_of_characters: int = 1000, temperature: float = 0.6, break_on_suffix = None) -> str:
+    def generate_text(self, prefix: str = '', number_of_characters: int = 1000, temperature: float = 1.0, break_on_suffix = None) -> str:
 
         if prefix == '':
             prefix = '\n'
@@ -223,8 +223,6 @@ def generate_text(self, prefix: str = '', number_of_characters: int = 1000, temp
             hidden = self.init_hidden(1)
             prediction, _, hidden = self.forward(input, hidden)
 
-            hidden = hidden
-
             input = torch.tensor(self.dictionary.get_idx_for_item(prefix[-1])).unsqueeze(0).unsqueeze(0)
 
             for i in range(number_of_characters):
@@ -232,11 +230,25 @@ def generate_text(self, prefix: str = '', number_of_characters: int = 1000, temp
                 if torch.cuda.is_available():
                     input = input.cuda()
 
+                # get predicted weights
                 prediction, _, hidden = self.forward(input, hidden)
-                prediction /= temperature
-                word_weights = prediction.squeeze().data.div(1.0).exp().cpu()
+                prediction = prediction.squeeze().detach()
+
+                # divide by temperature
+                prediction = prediction.div(temperature)
+
+                # to prevent overflow problem with small temperature values, substract largest value from all
+                # this makes a vector in which the largest value is 0
+                max = torch.max(prediction)
+                prediction -= max
+
+                # compute word weights with exponential function
+                word_weights = prediction.exp().cpu()
+
+                # sample multinomial distribution for next character
                 word_idx = torch.multinomial(word_weights, 1)[0]
-                input = word_idx.clone().detach().unsqueeze(0).unsqueeze(0)
+
+                input = word_idx.detach().unsqueeze(0).unsqueeze(0)
                 word = idx2item[word_idx].decode('UTF-8')
                 characters.append(word)
 
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
@@ -24,7 +24,7 @@
 
 
 def to_scalar(var):
-    return var.view(-1).data.tolist()[0]
+    return var.view(-1).detach().tolist()[0]
 
 
 def argmax(vec):
@@ -142,8 +142,8 @@ def __init__(self,
         if self.use_crf:
             self.transitions = torch.nn.Parameter(
                 torch.randn(self.tagset_size, self.tagset_size))
-            self.transitions.data[self.tag_dictionary.get_idx_for_item(START_TAG), :] = -10000
-            self.transitions.data[:, self.tag_dictionary.get_idx_for_item(STOP_TAG)] = -10000
+            self.transitions.detach()[self.tag_dictionary.get_idx_for_item(START_TAG), :] = -10000
+            self.transitions.detach()[:, self.tag_dictionary.get_idx_for_item(STOP_TAG)] = -10000
 
         if torch.cuda.is_available():
             self.cuda()
@@ -463,8 +463,8 @@ def _viterbi_decode(self, feats):
         for feat in feats:
             next_tag_var = forward_var.view(1, -1).expand(self.tagset_size, self.tagset_size) + self.transitions
             _, bptrs_t = torch.max(next_tag_var, dim=1)
-            bptrs_t = bptrs_t.squeeze().data.cpu().numpy()
-            next_tag_var = next_tag_var.data.cpu().numpy()
+            bptrs_t = bptrs_t.squeeze().detach().cpu().numpy()
+            next_tag_var = next_tag_var.detach().cpu().numpy()
             viterbivars_t = next_tag_var[range(len(bptrs_t)), bptrs_t]
             viterbivars_t = autograd.Variable(torch.FloatTensor(viterbivars_t))
             if torch.cuda.is_available():
@@ -474,8 +474,8 @@ def _viterbi_decode(self, feats):
             backpointers.append(bptrs_t)
 
         terminal_var = forward_var + self.transitions[self.tag_dictionary.get_idx_for_item(STOP_TAG)]
-        terminal_var.data[self.tag_dictionary.get_idx_for_item(STOP_TAG)] = -10000.
-        terminal_var.data[self.tag_dictionary.get_idx_for_item(START_TAG)] = -10000.
+        terminal_var.detach()[self.tag_dictionary.get_idx_for_item(STOP_TAG)] = -10000.
+        terminal_var.detach()[self.tag_dictionary.get_idx_for_item(START_TAG)] = -10000.
         best_tag_id = argmax(terminal_var.unsqueeze(0))
 
         best_path = [best_tag_id]
diff --git a/flair/trainers/language_model_trainer.py b/flair/trainers/language_model_trainer.py
--- a/flair/trainers/language_model_trainer.py
+++ b/flair/trainers/language_model_trainer.py
@@ -167,7 +167,7 @@ def __init__(self,
                  optimizer_state: dict = None
                  ):
         self.model: LanguageModel = model
-        self.optimzer: Optimizer = optimizer
+        self.optimizer: Optimizer = optimizer
         self.corpus: TextCorpus = corpus
         self.test_mode: bool = test_mode
 
@@ -211,7 +211,7 @@ def train(self,
 
             epoch = self.epoch
             best_val_loss = self.loss
-            optimizer = self.optimzer(self.model.parameters(), lr=learning_rate, **kwargs)
+            optimizer = self.optimizer(self.model.parameters(), lr=learning_rate, **kwargs)
             if self.optimizer_state is not None:
                 optimizer.load_state_dict(self.optimizer_state)
 
@@ -224,6 +224,7 @@ def train(self,
                                                                  factor=anneal_factor,
                                                                  patience=patience)
 
+            train_data = None
             for split in range(1 + self.split, max_splits + 1):
 
                 # after pass over all splits, increment epoch count
@@ -235,9 +236,10 @@ def train(self,
                 for group in optimizer.param_groups:
                     learning_rate = group['lr']
 
-                train_slice = self.corpus.get_next_train_slice()
+                if train_data is None or len(self.corpus.train_files) > 1:
+                    train_slice = self.corpus.get_next_train_slice()
+                    train_data = self._batchify(train_slice, mini_batch_size)
 
-                train_data = self._batchify(train_slice, mini_batch_size)
                 log.info('\t({:%H:%M:%S})'.format(datetime.datetime.now()))
 
                 # go into train mode


Test patch:
diff --git a/tests/test_language_model.py b/tests/test_language_model.py
--- a/tests/test_language_model.py
+++ b/tests/test_language_model.py
@@ -18,3 +18,14 @@ def test_train_resume_language_model_training(resources_path, results_base_path,
     assert (corpus.train_files is not None)
     assert (corpus.valid is not None)
     assert (len(corpus.train_files) == 2)
+
+
+def test_generate_text_with_small_temperatures():
+
+    from flair.embeddings import FlairEmbeddings
+
+    language_model = FlairEmbeddings('news-forward-fast').lm
+
+    text = language_model.generate_text(temperature=0.01, number_of_characters=100)
+    assert (text is not None)
+    assert (len(text) >= 100)
\ No newline at end of file