{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"#hide\n",
"from utils import *"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Making our RNN state of the art"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"#hide\n",
"from fastai2.text.all import *\n",
"path = untar_data(URLs.HUMAN_NUMBERS)\n",
"lines = L()\n",
"with open(path/'train.txt') as f: lines += L(*f.readlines())\n",
"with open(path/'valid.txt') as f: lines += L(*f.readlines())\n",
"text = ' . '.join([l.strip() for l in lines])\n",
"tokens = text.split(' ')\n",
"vocab = L(*tokens).unique()\n",
"word2idx = {w:i for i,w in enumerate(vocab)}\n",
"nums = L(word2idx[i] for i in tokens)\n",
"\n",
"def group_chunks(ds, bs):\n",
" m = len(ds) // bs\n",
" new_ds = L()\n",
" for i in range(m): new_ds += L(ds[i + m*j] for j in range(bs))\n",
" return new_ds"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"sl,bs = 16,64\n",
"seqs = L((tensor(nums[i:i+sl]), tensor(nums[i+1:i+sl+1])) for i in range(0,len(nums)-sl-1,sl))\n",
"cut = int(len(seqs) * 0.8)\n",
"dls = DataLoaders.from_dsets(group_chunks(seqs[:cut], bs), group_chunks(seqs[cut:], bs), bs=bs, drop_last=True, shuffle=False)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Multilayer RNNs"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### The model"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"class LMModel5(Module):\n",
" def __init__(self, vocab_sz, n_hidden, n_layers):\n",
" self.i_h = nn.Embedding(vocab_sz, n_hidden)\n",
" self.rnn = nn.RNN(n_hidden, n_hidden, n_layers, batch_first=True)\n",
" self.h_o = nn.Linear(n_hidden, vocab_sz)\n",
" self.h = torch.zeros(n_layers, bs, n_hidden)\n",
" \n",
" def forward(self, x):\n",
" res,h = self.rnn(self.i_h(x), self.h)\n",
" self.h = h.detach()\n",
" return self.h_o(res)\n",
" \n",
" def reset(self): self.h.zero_()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
\n",
" \n",
" \n",
" | epoch | \n",
" train_loss | \n",
" valid_loss | \n",
" accuracy | \n",
" time | \n",
"
\n",
" \n",
" \n",
" \n",
" | 0 | \n",
" 3.048115 | \n",
" 2.622384 | \n",
" 0.434001 | \n",
" 00:02 | \n",
"
\n",
" \n",
" | 1 | \n",
" 2.136388 | \n",
" 1.763967 | \n",
" 0.471191 | \n",
" 00:02 | \n",
"
\n",
" \n",
" | 2 | \n",
" 1.689246 | \n",
" 1.898718 | \n",
" 0.364746 | \n",
" 00:02 | \n",
"
\n",
" \n",
" | 3 | \n",
" 1.443545 | \n",
" 1.747440 | \n",
" 0.480387 | \n",
" 00:01 | \n",
"
\n",
" \n",
" | 4 | \n",
" 1.271023 | \n",
" 1.870939 | \n",
" 0.479980 | \n",
" 00:02 | \n",
"
\n",
" \n",
" | 5 | \n",
" 1.101259 | \n",
" 1.794428 | \n",
" 0.495361 | \n",
" 00:02 | \n",
"
\n",
" \n",
" | 6 | \n",
" 0.948380 | \n",
" 1.769644 | \n",
" 0.511149 | \n",
" 00:02 | \n",
"
\n",
" \n",
" | 7 | \n",
" 0.822373 | \n",
" 1.800406 | \n",
" 0.535400 | \n",
" 00:01 | \n",
"
\n",
" \n",
" | 8 | \n",
" 0.731188 | \n",
" 1.914065 | \n",
" 0.522461 | \n",
" 00:01 | \n",
"
\n",
" \n",
" | 9 | \n",
" 0.662659 | \n",
" 1.987547 | \n",
" 0.525798 | \n",
" 00:02 | \n",
"
\n",
" \n",
" | 10 | \n",
" 0.613053 | \n",
" 2.022102 | \n",
" 0.527751 | \n",
" 00:02 | \n",
"
\n",
" \n",
" | 11 | \n",
" 0.577007 | \n",
" 2.068472 | \n",
" 0.526530 | \n",
" 00:02 | \n",
"
\n",
" \n",
" | 12 | \n",
" 0.551144 | \n",
" 2.113533 | \n",
" 0.521566 | \n",
" 00:02 | \n",
"
\n",
" \n",
" | 13 | \n",
" 0.535356 | \n",
" 2.123089 | \n",
" 0.523600 | \n",
" 00:02 | \n",
"
\n",
" \n",
" | 14 | \n",
" 0.526783 | \n",
" 2.122413 | \n",
" 0.524333 | \n",
" 00:02 | \n",
"
\n",
" \n",
"
"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"learn = Learner(dls, LMModel5(len(vocab), 64, 2), loss_func=CrossEntropyLossFlat(), metrics=accuracy, cbs=ModelReseter)\n",
"learn.fit_one_cycle(15, 3e-3)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Handling exploding or disappearing activations"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## LSTM"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Building an LSTM from scratch"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"class LSTMCell(Module):\n",
" def __init__(self, ni, nh):\n",
" self.forget_gate = nn.Linear(ni + nh, nh)\n",
" self.input_gate = nn.Linear(ni + nh, nh)\n",
" self.cell_gate = nn.Linear(ni + nh, nh)\n",
" self.output_gate = nn.Linear(ni + nh, nh)\n",
"\n",
" def forward(self, input, state):\n",
" h,c = state\n",
" h = torch.stack([x, input], dim=1)\n",
" forget = torch.sigmoid(self.forget_gate(h))\n",
" c = c * forget\n",
" inp = torch.sigmoid(self.input_gate(h))\n",
" cell = torch.tanh(self.cell_gate(h))\n",
" c = c + inp * cell\n",
" out = torch.sigmoid(self.output_gate(h))\n",
" h = outgate * torch.tanh(c)\n",
" return h, (h,c)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"class LSTMCell(Module):\n",
" def __init__(self, ni, nh):\n",
" self.ih = nn.Linear(ni,4*nh)\n",
" self.hh = nn.Linear(nh,4*nh)\n",
"\n",
" def forward(self, input, state):\n",
" h,c = state\n",
" #One big multiplication for all the gates is better than 4 smaller ones\n",
" gates = (self.ih(input) + self.hh(h)).chunk(4, 1)\n",
" ingate,forgetgate,outgate = map(torch.sigmoid, gates[:3])\n",
" cellgate = gates[3].tanh()\n",
"\n",
" c = (forgetgate*c) + (ingate*cellgate)\n",
" h = outgate * c.tanh()\n",
" return h, (h,c)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Training a language model using LSTMs"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"class LMModel6(Module):\n",
" def __init__(self, vocab_sz, n_hidden, n_layers):\n",
" self.i_h = nn.Embedding(vocab_sz, n_hidden)\n",
" self.rnn = nn.LSTM(n_hidden, n_hidden, n_layers, batch_first=True)\n",
" self.h_o = nn.Linear(n_hidden, vocab_sz)\n",
" self.h = [torch.zeros(2, bs, n_hidden) for _ in range(n_layers)]\n",
" \n",
" def forward(self, x):\n",
" res,h = self.rnn(self.i_h(x), self.h)\n",
" self.h = [h_.detach() for h_ in h]\n",
" return self.h_o(res)\n",
" \n",
" def reset(self): \n",
" for h in self.h: h.zero_()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
" \n",
" \n",
" | epoch | \n",
" train_loss | \n",
" valid_loss | \n",
" accuracy | \n",
" time | \n",
"
\n",
" \n",
" \n",
" \n",
" | 0 | \n",
" 3.031346 | \n",
" 2.749381 | \n",
" 0.279215 | \n",
" 00:03 | \n",
"
\n",
" \n",
" | 1 | \n",
" 2.219651 | \n",
" 2.084450 | \n",
" 0.204753 | \n",
" 00:03 | \n",
"
\n",
" \n",
" | 2 | \n",
" 1.659518 | \n",
" 1.685639 | \n",
" 0.479574 | \n",
" 00:03 | \n",
"
\n",
" \n",
" | 3 | \n",
" 1.410550 | \n",
" 1.666663 | \n",
" 0.509440 | \n",
" 00:03 | \n",
"
\n",
" \n",
" | 4 | \n",
" 1.204062 | \n",
" 1.606485 | \n",
" 0.541829 | \n",
" 00:03 | \n",
"
\n",
" \n",
" | 5 | \n",
" 1.021459 | \n",
" 1.529109 | \n",
" 0.592448 | \n",
" 00:03 | \n",
"
\n",
" \n",
" | 6 | \n",
" 0.785871 | \n",
" 1.340280 | \n",
" 0.642008 | \n",
" 00:03 | \n",
"
\n",
" \n",
" | 7 | \n",
" 0.547519 | \n",
" 1.271710 | \n",
" 0.688802 | \n",
" 00:03 | \n",
"
\n",
" \n",
" | 8 | \n",
" 0.339775 | \n",
" 1.216605 | \n",
" 0.753825 | \n",
" 00:03 | \n",
"
\n",
" \n",
" | 9 | \n",
" 0.197550 | \n",
" 1.218557 | \n",
" 0.743652 | \n",
" 00:02 | \n",
"
\n",
" \n",
" | 10 | \n",
" 0.114297 | \n",
" 1.253571 | \n",
" 0.751139 | \n",
" 00:03 | \n",
"
\n",
" \n",
" | 11 | \n",
" 0.071301 | \n",
" 1.314827 | \n",
" 0.752686 | \n",
" 00:03 | \n",
"
\n",
" \n",
" | 12 | \n",
" 0.049507 | \n",
" 1.307375 | \n",
" 0.765462 | \n",
" 00:03 | \n",
"
\n",
" \n",
" | 13 | \n",
" 0.038810 | \n",
" 1.287779 | \n",
" 0.767741 | \n",
" 00:03 | \n",
"
\n",
" \n",
" | 14 | \n",
" 0.033738 | \n",
" 1.292951 | \n",
" 0.767985 | \n",
" 00:03 | \n",
"
\n",
" \n",
"
"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"learn = Learner(dls, LMModel6(len(vocab), 64, 2), loss_func=CrossEntropyLossFlat(), metrics=accuracy, cbs=ModelReseter)\n",
"learn.fit_one_cycle(15, 1e-2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Regularizing an LSTM"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Dropout"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"class Dropout(Module):\n",
" def __init__(self, p): self.p = p\n",
" def forward(self, x):\n",
" if self.training: return x\n",
" mask = x.new(*x.shape).bernoulli_(1-p)\n",
" return x * mask.div_(1-p)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### AR and TAR regularization"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Training a regularized LSTM"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [],
"source": [
"class LMModel7(Module):\n",
" def __init__(self, vocab_sz, n_hidden, n_layers, p):\n",
" self.i_h = nn.Embedding(vocab_sz, n_hidden)\n",
" self.rnn = nn.LSTM(n_hidden, n_hidden, n_layers, batch_first=True)\n",
" self.drop = nn.Dropout(p)\n",
" self.h_o = nn.Linear(n_hidden, vocab_sz)\n",
" self.h = [torch.zeros(2, bs, n_hidden) for _ in range(n_layers)]\n",
" \n",
" def forward(self, x):\n",
" raw,h = self.rnn(self.i_h(x), self.h)\n",
" out = self.drop(raw)\n",
" self.h = [h_.detach() for h_ in h]\n",
" return self.h_o(out),raw,out\n",
" \n",
" def reset(self): \n",
" for h in self.h: h.zero_()"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [],
"source": [
"learn = Learner(dls, LMModel7(len(vocab), 64, 2, 0.4), loss_func=CrossEntropyLossFlat(), \n",
" metrics=accuracy, cbs=[ModelReseter, RNNRegularizer(alpha=2, beta=1)])"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
" \n",
" \n",
" | epoch | \n",
" train_loss | \n",
" valid_loss | \n",
" accuracy | \n",
" time | \n",
"
\n",
" \n",
" \n",
" \n",
" | 0 | \n",
" 3.145553 | \n",
" 2.495994 | \n",
" 0.437581 | \n",
" 00:03 | \n",
"
\n",
" \n",
" | 1 | \n",
" 2.333189 | \n",
" 1.674463 | \n",
" 0.491862 | \n",
" 00:03 | \n",
"
\n",
" \n",
" | 2 | \n",
" 1.678753 | \n",
" 1.500536 | \n",
" 0.553955 | \n",
" 00:03 | \n",
"
\n",
" \n",
" | 3 | \n",
" 1.111904 | \n",
" 1.040109 | \n",
" 0.748779 | \n",
" 00:03 | \n",
"
\n",
" \n",
" | 4 | \n",
" 0.707829 | \n",
" 0.773369 | \n",
" 0.807699 | \n",
" 00:02 | \n",
"
\n",
" \n",
" | 5 | \n",
" 0.465899 | \n",
" 0.621159 | \n",
" 0.829346 | \n",
" 00:03 | \n",
"
\n",
" \n",
" | 6 | \n",
" 0.335249 | \n",
" 0.649926 | \n",
" 0.839193 | \n",
" 00:03 | \n",
"
\n",
" \n",
" | 7 | \n",
" 0.254418 | \n",
" 0.586989 | \n",
" 0.841064 | \n",
" 00:03 | \n",
"
\n",
" \n",
" | 8 | \n",
" 0.205191 | \n",
" 0.527288 | \n",
" 0.850179 | \n",
" 00:02 | \n",
"
\n",
" \n",
" | 9 | \n",
" 0.172876 | \n",
" 0.460011 | \n",
" 0.868652 | \n",
" 00:02 | \n",
"
\n",
" \n",
" | 10 | \n",
" 0.151452 | \n",
" 0.500604 | \n",
" 0.860677 | \n",
" 00:03 | \n",
"
\n",
" \n",
" | 11 | \n",
" 0.136872 | \n",
" 0.480342 | \n",
" 0.863525 | \n",
" 00:03 | \n",
"
\n",
" \n",
" | 12 | \n",
" 0.127576 | \n",
" 0.496534 | \n",
" 0.858398 | \n",
" 00:03 | \n",
"
\n",
" \n",
" | 13 | \n",
" 0.122187 | \n",
" 0.475931 | \n",
" 0.867025 | \n",
" 00:03 | \n",
"
\n",
" \n",
" | 14 | \n",
" 0.119538 | \n",
" 0.490366 | \n",
" 0.861165 | \n",
" 00:03 | \n",
"
\n",
" \n",
"
"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"learn = TextLearner(dls, LMModel7(len(vocab), 64, 2, 0.4), loss_func=CrossEntropyLossFlat(), \n",
" metrics=accuracy)\n",
"learn.fit_one_cycle(15, 1e-2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Conclusion"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"jupytext": {
"split_at_heading": true
},
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.4"
}
},
"nbformat": 4,
"nbformat_minor": 2
}