Created
May 2, 2017 19:40
-
-
Save ShigekiKarita/a2dea387d0d4cd7c3f3388d4986e7547 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| """ | |
| PTB RNNLM model | |
| according to tf: https://www.tensorflow.org/tutorials/recurrent#run_the_code | |
| perplexity 120 is nice. 80 is great. | |
| """ | |
| from functools import partial | |
| import torch | |
| import torch.nn as nn | |
| import torch.nn.init as init | |
| from torch.autograd import Variable | |
| def default_uniform(x): | |
| s = 1.0 / (x.size(0) ** 0.5) | |
| x.uniform_(-s, s) | |
| def init_weights(net, weight_init=default_uniform, | |
| tanh_weight_gain=init.calculate_gain("tanh"), | |
| bias_init=partial(init.constant, val=0.0), | |
| forget_bias_init=partial(init.constant, val=1.0)): | |
| """ | |
| Args: | |
| net (nn.Module): nn.RNN like module | |
| weight_init: its input is torch.FloatTensor | |
| """ | |
| for name, p in net.named_parameters(): | |
| if name.startswith("bias"): | |
| bias_init(p.data) | |
| if isinstance(net, (nn.LSTM, nn.LSTMCell)): | |
| n = p.size(0) | |
| forget_bias_init(p.data[n // 4:n // 2]) | |
| elif name.startswith("weight"): | |
| weight_init(p.data) | |
| # NOTE: according to init.calculate_gain, tanh requires 5/3 gain from sigmoid. | |
| if isinstance(net, (nn.LSTM, nn.LSTMCell)): | |
| n = p.size(0) | |
| p.data[n // 2:n // 4 * 3] *= tanh_weight_gain | |
| if isinstance(net, (nn.GRU, nn.GRUCell)): | |
| n = p.size(0) | |
| p.data[n // 2:] *= tanh_weight_gain | |
| def clip_weights(parameters, norm=1.0, dim=1): | |
| """ | |
| Args: | |
| parameters (generator): generator of nn.Parameter | |
| norm (float): | |
| Notes: | |
| blocks implementation (as a optimizer) | |
| https://github.com/mila-udem/blocks/blob/master/blocks/algorithms/__init__.py#L717 | |
| """ | |
| assert dim == 0 or dim == 1 | |
| for p in parameters: | |
| if p.dim() == 2: | |
| column_norm = p.data.norm(p=2, dim=dim) | |
| rescale = column_norm > norm | |
| over = rescale.float() * column_norm / norm | |
| under = (rescale == 0).float() | |
| denom = over + under | |
| p.data.div_(denom.expand_as(p)) | |
| def add_weight_noise(parameters, stddev=0.075): | |
| """ | |
| Args: | |
| parameters: | |
| stddev: | |
| """ | |
| for p in parameters: | |
| p.data = torch.normal(p.data, stddev) | |
| class RNNModel(nn.Module): | |
| """Container module with an encoder, a recurrent module, and a decoder.""" | |
| def __init__(self, rnn_type, ntoken, ninp, nhid, nlayers, dropout=0.5, tie_weights=False, weight_noise=0.0075, max_norm=1.0): | |
| super(RNNModel, self).__init__() | |
| self.drop = nn.Dropout(dropout) | |
| self.encoder = nn.Embedding(ntoken, ninp) | |
| if rnn_type in ['LSTM', 'GRU']: | |
| self.rnn = getattr(nn, rnn_type)(ninp, nhid, nlayers, dropout=dropout) | |
| else: | |
| try: | |
| nonlinearity = {'RNN_TANH': 'tanh', 'RNN_RELU': 'relu'}[rnn_type] | |
| except KeyError: | |
| raise ValueError( """An invalid option for `--model` was supplied, | |
| options are ['LSTM', 'GRU', 'RNN_TANH' or 'RNN_RELU']""") | |
| self.rnn = nn.RNN(ninp, nhid, nlayers, nonlinearity=nonlinearity, dropout=dropout) | |
| self.decoder = nn.Linear(nhid, ntoken) | |
| self.init_weights() | |
| # Optionally tie weights as in: | |
| # "Using the Output Embedding to Improve Language Models" (Press & Wolf 2016) | |
| # https://arxiv.org/abs/1608.05859 | |
| # and | |
| # "Tying Word Vectors and Word Classifiers: A Loss Framework for Language Modeling" (Inan et al. 2016) | |
| # https://arxiv.org/abs/1611.01462 | |
| if tie_weights: | |
| if nhid != ninp: | |
| raise ValueError('When using the tied flag, nhid must be equal to emsize') | |
| self.decoder.weight = self.encoder.weight | |
| self.rnn_type = rnn_type | |
| self.nhid = nhid | |
| self.nlayers = nlayers | |
| self.weight_noise = weight_noise | |
| self.max_norm = max_norm | |
| def init_weights(self): | |
| initializer = default_uniform | |
| # initializer = init.xavier_normal | |
| # initializer = init.xavier_uniform | |
| # initializer = init.orthogonal | |
| # initializer = partial(init.uniform, a=-0.01, b=0.01) | |
| # initializer = partial(init.normal, mean=0.0, std=0.01) | |
| init_weights(self.rnn, initializer, tanh_weight_gain=1.0) | |
| init_weights(self.encoder, initializer) | |
| init_weights(self.decoder, initializer) | |
| def forward(self, input, hidden): | |
| if self.training: | |
| add_weight_noise(self.rnn.parameters(), self.weight_noise) | |
| # clip_weights(self.parameters(), self.max_norm) | |
| # emb = self.drop(self.encoder(input)) | |
| emb = self.encoder(input) | |
| output, hidden = self.rnn(emb, hidden) | |
| # output = self.drop(output) | |
| decoded = self.decoder(output.view(output.size(0)*output.size(1), output.size(2))) | |
| return decoded.view(output.size(0), output.size(1), decoded.size(1)), hidden | |
| def init_hidden(self, bsz): | |
| weight = next(self.parameters()).data | |
| if self.rnn_type == 'LSTM': | |
| return (Variable(weight.new(self.nlayers, bsz, self.nhid).zero_()), | |
| Variable(weight.new(self.nlayers, bsz, self.nhid).zero_())) | |
| else: | |
| return Variable(weight.new(self.nlayers, bsz, self.nhid).zero_()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment