Skip to content

Instantly share code, notes, and snippets.

@kylemcdonald
Last active June 12, 2020 21:34
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save kylemcdonald/2d06dc736789f0b329e11d504e8dee9f to your computer and use it in GitHub Desktop.
Save kylemcdonald/2d06dc736789f0b329e11d504e8dee9f to your computer and use it in GitHub Desktop.
Recreating char-rnn from the spro/practical-pytorch tutorials.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import torch\n",
"import torch.nn as nn\n",
"import random\n",
"import time\n",
"import math\n",
"from IPython.display import clear_output\n",
"import matplotlib.pyplot as plt\n",
"\n",
"use_cuda = torch.cuda.is_available()\n",
"use_cuda"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"seq_length = 50\n",
"batch_size = 50\n",
"hidden_size = 128\n",
"epoch_count = 10\n",
"n_layers = 2\n",
"lr = 2e-3\n",
"input_filename = 'tiny-shakespeare.txt'"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"text = open(input_filename).read() #.lower()"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"chars = set(text)\n",
"chars_len = len(chars)\n",
"char_to_index = {}\n",
"index_to_char = {}\n",
"for i, c in enumerate(chars):\n",
" char_to_index[c] = i\n",
" index_to_char[i] = c"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[range(0, 3), range(3, 6), range(6, 9)]\n"
]
}
],
"source": [
"def time_since(since):\n",
" s = time.time() - since\n",
" m = math.floor(s / 60)\n",
" s -= m * 60\n",
" return '%dm %ds' % (m, s)\n",
"\n",
"def chunks(l, n):\n",
" for i in range(0, len(l) - n, n):\n",
" yield l[i:i + n]\n",
"\n",
"print(list(chunks(range(11), 3)))"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"torch.Size([51, 50])\n"
]
}
],
"source": [
"# convert all characters to indices\n",
"batches = [char_to_index[char] for char in text]\n",
"\n",
"# chunk into sequences of length seq_length + 1\n",
"batches = list(chunks(batches, seq_length + 1))\n",
"\n",
"# chunk sequences into batches\n",
"batches = list(chunks(batches, batch_size))\n",
"\n",
"# convert batches to tensors and transpose\n",
"batches = [torch.LongTensor(batch).transpose_(0,1) for batch in batches]\n",
"\n",
"# each batch is (sequence_length + 1) x batch_size\n",
"print(batches[0].size())"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"class RNN(nn.Module):\n",
" def __init__(self, input_size, hidden_size, output_size, n_layers, batch_size):\n",
" super(RNN, self).__init__()\n",
" self.input_size = input_size\n",
" self.hidden_size = hidden_size\n",
" self.output_size = output_size\n",
" self.n_layers = n_layers\n",
" self.batch_size = batch_size\n",
" \n",
" self.encoder = nn.Embedding(input_size, hidden_size)\n",
" self.cells = nn.GRU(hidden_size, hidden_size, n_layers)\n",
" self.decoder = nn.Linear(hidden_size, output_size)\n",
" \n",
" def forward(self, input, hidden):\n",
" input = self.encoder(input)\n",
" output, hidden = self.cells(input, hidden)\n",
" output = self.decoder(output.view(output.size(0) * output.size(1), output.size(2)))\n",
" return output, hidden\n",
" \n",
" def create_hidden(self):\n",
" # should this be small random instead of zeros\n",
" # should this also be stored in the class rather than being passed around?\n",
" return torch.zeros(self.n_layers, self.batch_size, self.hidden_size)"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"Duration: 0m 31s\n",
"Epoch: 10/10\n",
"Batch: 435/437, 139.46/s\n",
"Loss: 1.42\n",
"\n"
]
}
],
"source": [
"print_every = 5\n",
"\n",
"model = RNN(chars_len, hidden_size, chars_len, n_layers, batch_size)\n",
"optimizer = torch.optim.Adam(model.parameters(), lr=lr)\n",
"loss_function = nn.CrossEntropyLoss()\n",
"hidden = model.create_hidden()\n",
"\n",
"if use_cuda:\n",
" model = model.cuda()\n",
" hidden = hidden.cuda()\n",
"\n",
"start = time.time()\n",
"all_losses = []\n",
"\n",
"format_string = \\\n",
"\"\"\"\n",
"Duration: {duration}\n",
"Epoch: {epoch}/{epoch_count}\n",
"Batch: {batch}/{batch_count}, {batch_rate:.2f}/s\n",
"Loss: {loss:.2f}\n",
"\"\"\"\n",
"\n",
"try:\n",
" for epoch in range(1, epoch_count + 1):\n",
" random.shuffle(batches)\n",
" for batch, batch_tensor in enumerate(batches):\n",
" if use_cuda:\n",
" batch_tensor = batch_tensor.cuda()\n",
" \n",
" # reset the model\n",
" model.zero_grad()\n",
" \n",
" # everything except the last\n",
" input_variable = batch_tensor[:-1]\n",
" \n",
" # everything except the first, flattened\n",
" target_variable = batch_tensor[1:].view(-1)\n",
" \n",
" # prediction and calculate loss\n",
" output, _ = model(input_variable, hidden)\n",
" loss = loss_function(output, target_variable)\n",
"\n",
" # backprop and optimize\n",
" loss.backward()\n",
" optimizer.step()\n",
" \n",
" all_losses.append(loss.item())\n",
"\n",
" if print_every > 0 and batch % print_every == 0:\n",
" clear_output(wait=True)\n",
" batch_count = len(batches)\n",
" batch_rate = ((batch_count * (epoch - 1)) + batch) / (time.time() - start)\n",
" print(format_string.format(duration=time_since(start),\n",
" epoch=epoch,\n",
" epoch_count=epoch_count,\n",
" batch=batch,\n",
" batch_count=batch_count,\n",
" batch_rate=batch_rate,\n",
" loss=loss))\n",
" \n",
"except KeyboardInterrupt:\n",
" pass"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {
"image/png": {
"height": 248,
"width": 372
},
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"plt.plot(all_losses)\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
"def index_to_tensor(index):\n",
" tensor = torch.zeros(1, 1).long()\n",
" tensor[0,0] = index\n",
" return tensor\n",
"\n",
"# print(index_to_tensor(10))"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"def evaluate(prime_str='A', predict_len=100, temperature=0.8):\n",
" model.batch_size = 1\n",
" hidden = model.create_hidden()\n",
" \n",
" if use_cuda:\n",
" hidden = hidden.cuda()\n",
" \n",
" prime_tensors = [index_to_tensor(char_to_index[char]) for char in prime_str]\n",
" \n",
" if use_cuda:\n",
" prime_tensors = [tensor.cuda() for tensor in prime_tensors]\n",
"\n",
" for prime_tensor in prime_tensors[:-2]:\n",
" _, hidden = model(prime_tensor, hidden)\n",
" \n",
" inp = prime_tensors[-1]\n",
" predicted = prime_str\n",
" for p in range(predict_len):\n",
" if use_cuda:\n",
" inp = inp.cuda()\n",
" \n",
" output, hidden = model(inp, hidden)\n",
" \n",
" # Sample from the network as a multinomial distribution\n",
" output_dist = output.data.view(-1).div(temperature).exp()\n",
" top_i = torch.multinomial(output_dist, 1)[0]\n",
" \n",
" # Add predicted character to string and use as next input\n",
" predicted_char = index_to_char[top_i.item()]\n",
" predicted += predicted_char\n",
" inp = index_to_tensor(char_to_index[predicted_char])\n",
"\n",
" return predicted"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Thave numberland and justice of virtue;\n",
"Which on them spiteof and forsworn,\n",
"That sorrow to the news in against their eyes, and it not like proved\n",
"That all the revenge: would so long to this lips be this brother,\n",
"I one stard it.\n",
"\n",
"BUCKINGHAM:\n",
"It is near, or else say 'Will, sir, sir: the commonfessions leave one beauterly,\n",
"And his near to defend thine;\n",
"Sour tale his father, catched thee for the back\n",
"Upongment of live and posite the faults.\n",
"\n",
"Second Servant:\n",
"The more sworn about the fire to big and so \n"
]
}
],
"source": [
"print(evaluate('Th', 500, temperature=0.8))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"anaconda-cloud": {},
"kernelspec": {
"display_name": "torch-tf2",
"language": "python",
"name": "torch-tf2"
},
"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.6"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment