Skip to content

Instantly share code, notes, and snippets.

@loristns
Last active February 24, 2018 18:34
Show Gist options
  • Save loristns/70716b15cb757e9e30070e168385d267 to your computer and use it in GitHub Desktop.
Save loristns/70716b15cb757e9e30070e168385d267 to your computer and use it in GitHub Desktop.
Generate cats names using PyTorch.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Générer des noms de chats avec un LSTM avec PyTorch.\n",
"\n",
"Dans ce notebook nous allons essayer de générer des prénoms (que l'on pourraient donner à un chat) en utilisant un LSTM.\n",
"\n",
"## Préparer les données\n",
"\n",
"On commence par récupérer les données :"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"['abby', 'angel', 'annie', 'baby', 'bailey', 'bandit', 'bear', 'bella', 'bob', 'boo']\n"
]
}
],
"source": [
"names = \"Abby Angel Annie Baby Bailey Bandit Bear Bella Bob Boo Boots Bubba Buddy Buster Cali Callie Casper Charlie Chester Chloe Cleo Coco Cookie Cuddles Daisy Dusty Felix Fluffy Garfield George Ginger Gizmo Gracie Harley Jack Jasmine Jasper Kiki Kitty Leo Lilly Lily Loki Lola Lucky Lucy Luna Maggie Max Mia Midnight Milo Mimi Miss kitty Missy Misty Mittens Molly Muffin Nala Oliver Oreo Oscar Patches Peanut Pepper Precious Princess Pumpkin Rascal Rocky Sadie Salem Sam Samantha Sammy Sasha Sassy Scooter Shadow Sheba Simba Simon Smokey Snickers Snowball Snuggles Socks Sophie Spooky Sugar Tiger Tigger Tinkerbell Toby Trouble Whiskers Willow Stephany Versie Fernanda Kathlyn Sachiko Johanne Wendolyn Reynaldo Gregorio Porfirio Claudio Hanh Etta Kum Lettie Agatha Josette Rosie Earlene Philip Patty Fae Alvin Maryann Krystle Justine Wm Coy Lonnie Bernice Howard Alisha Rachell Abbie Charolette Ignacio Reena Elena Winford Taisha Melia Quinn Mollie Zonia Nicolle Fabiola Keith Teisha Wilburn Senaida Marlin Leanora Pamella Natasha Ollie Lorri Odell Enrique Tashina Jaunita Meryl China Colene Sterling Devora Bonnie Jenine Dee Bethel Lashon Kellye Cassy Bernardina Frances Carlton Cristie Stanton Corliss Britt Krysten Cassie Cherish Cori Vella Eugenie Justina Jami Lavonda Lucas Eric Felicitas\"\n",
"names = names.lower().split()\n",
"\n",
"print(names[:10])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"L'objectif est maintenant de transformer chaque mot en un vecteur (matrice d'une seule colonne) dans lequel chaque ligne représentera l'index d'une lettre du mot dans l'alphabet. "
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"import torch\n",
"\n",
"alphabet = \"abcdefghijklmnopqrstuvwxyz\"\n",
"\n",
"def name_to_vector(name):\n",
" vector = torch.zeros(len(name))\n",
" \n",
" for position, char in enumerate(name):\n",
" vector[position] = alphabet.index(char)\n",
" \n",
" return vector.long()"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"\n",
" 1\n",
" 14\n",
" 1\n",
"[torch.LongTensor of size 3]"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"name_to_vector('bob')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Le modèle\n",
"\n",
"Le modèle que nous allons utiliser sera simplement composé d'un LSTM qui jouera le rôle \"d'encodeur\" en prenant une séquence de valeur aléatoire, puis d'un second LSTM qui servira de \"décodeur\", puis finalement d'un layer d'activation log_softmax."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"from torch import nn\n",
"from torch.nn import functional as F\n",
"from torch.autograd import Variable\n",
"\n",
"\n",
"class LSTMGen(nn.Module):\n",
" def __init__(self, input_size, hidden_size):\n",
" super(LSTMGen, self).__init__()\n",
" \n",
" self.hidden_size = hidden_size\n",
" \n",
" self.lstm = nn.LSTM(input_size, hidden_size, 2)\n",
" \n",
" def forward(self, input, hidden):\n",
" output = self.lstm(input, hidden)[0]\n",
" seq_len = output.size()[0]\n",
" output = output.view(seq_len, self.hidden_size) # On redimensionne `output`\n",
" output = F.log_softmax(output, dim=0)\n",
" return output\n",
" \n",
" def init_hidden(self):\n",
" return [Variable(torch.zeros(2, 1, self.hidden_size)) for _ in range(2)]\n",
" \n",
" def generate(self, output_len):\n",
" random_input = Variable(torch.randn(output_len, 1, 1))\n",
" model_hidden = self.init_hidden()\n",
"\n",
" model_output = self.forward(random_input, model_hidden)\n",
"\n",
" generated_name = \"\"\n",
" for row in model_output:\n",
" char_index = int(row.max(0)[1])\n",
" generated_name += alphabet[char_index]\n",
"\n",
" return generated_name\n",
"\n",
"\n",
"model = LSTMGen(1, 26)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'wwknn'"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Testons le modèle non entrainé...\n",
"model.generate(5)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## L'entrainement\n",
"\n",
"Avant d'écrire le script d'entrainement, nous allons juste définir une fonction gérant l'affichage d'information durant l'entrainement."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"import time\n",
"\n",
"def print_info(start_time, epoch, loss):\n",
" run_min, run_sec = divmod(time.time() - start_time, 60)\n",
" \n",
" print(\"\\n[{}m {}s]\".format(round(run_min), round(run_sec)))\n",
" print(\"\\t Epoch {} - Loss: {}\".format(epoch, round(float(loss), 4)))\n",
" print(\"\\t \" + ' - '.join([model.generate(ln) for ln in range(3, 9)]))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Nous pouvons désormais entrainer notre réseau."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"[0m 0s]\n",
"\t Epoch 0 - Loss: 3.2568\n",
"\t pnn - pmnn - pmnnn - mmcnnn - mmmnnnn - pmmtnnnn\n",
"\n",
"[0m 40s]\n",
"\t Epoch 100 - Loss: 3.0543\n",
"\t cun - cuny - cugyy - cugtyy - cugtyye - cugtyyee\n",
"\n",
"[1m 30s]\n",
"\t Epoch 200 - Loss: 2.9117\n",
"\t cun - cuny - curty - curtyy - cugtyyy - cugtyyyd\n",
"\n",
"[2m 27s]\n",
"\t Epoch 300 - Loss: 3.3506\n",
"\t wan - wuly - wulty - wultyy - wuvtyyy - wuvtyyyd\n",
"\n",
"[3m 33s]\n",
"\t Epoch 400 - Loss: 3.2602\n",
"\t wun - wuly - wumky - wumkyy - wumkyyy - wugkiyyd\n",
"\n",
"[4m 32s]\n",
"\t Epoch 500 - Loss: 3.0369\n",
"\t wun - wuly - wulty - wulkyy - wugkyyd - wugkiydd\n",
"\n",
"[5m 30s]\n",
"\t Epoch 600 - Loss: 3.0483\n",
"\t wun - wuny - wulty - wugtyy - wugtyyy - wugkiyys\n",
"\n",
"[6m 20s]\n",
"\t Epoch 700 - Loss: 2.8337\n",
"\t wun - wuly - wulty - wurtyy - wumtiyy - wugtiyys\n",
"\n",
"[7m 9s]\n",
"\t Epoch 800 - Loss: 2.9701\n",
"\t wun - wuny - wulty - wurtyy - wugtiyy - wugiiyys\n",
"\n",
"[8m 12s]\n",
"\t Epoch 900 - Loss: 3.2689\n",
"\t wun - wury - wurty - wurtyy - wurtiyy - wumtiyys\n"
]
}
],
"source": [
"import random\n",
"from torch import optim\n",
"\n",
"n_epoch = 1000\n",
"info_every = 100\n",
"learning_rate = 1e-3\n",
"\n",
"loss_fn = nn.CrossEntropyLoss()\n",
"optimizer = optim.RMSprop(model.parameters(), lr=learning_rate)\n",
"errors = [] # On stockera l'historique des erreurs dans une liste pour pouvoir l'afficher ensuite.\n",
"\n",
"start_time = time.time()\n",
"\n",
"for epoch in range(n_epoch):\n",
" optimizer.zero_grad()\n",
" \n",
" for name in random.sample(names, 100): # On entraine par mini-batches de 100 exemples\n",
" model_hidden = model.init_hidden()\n",
" name_x = Variable(torch.randn(len(name), 1, 1)) # Les valeurs données en entrée au réseau sont aléatoires.\n",
" name_y = Variable(name_to_vector(name)) # On converti le nom que le réseau doit prédire en un vecteur.\n",
" name_y_pred = model(name_x, model_hidden) # La prédiction du réseau\n",
" \n",
" error = loss_fn(name_y_pred, name_y)\n",
" error.backward() # On calcule les gradients\n",
" \n",
" errors.append(float(error))\n",
" optimizer.step()\n",
" \n",
" if epoch % info_every == 0:\n",
" print_info(start_time, epoch, error)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Affichons maintenant la courbe d'apprentissage."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[<matplotlib.lines.Line2D at 0x7f78acb38e10>]"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/png": "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\n",
"text/plain": [
"<matplotlib.figure.Figure at 0x7f78b88fef98>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"import matplotlib.pyplot as plt\n",
"%matplotlib inline\n",
"\n",
"plt.figure()\n",
"plt.plot(errors)"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"a\n",
"wi\n",
"wun\n",
"wuly\n",
"wulty\n",
"wultyy\n",
"wultiyd\n",
"wugtiyds\n",
"wugtiydss\n",
"wugkiydsss\n"
]
}
],
"source": [
"for ln in range(1, 11):\n",
" print(model.generate(ln))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Conclusion\n",
"\n",
"La génération finale est intéressante, les résultats sont assez cohérant et ne ressemblent pas à une chaine de caractère complètement aléatoire. Cependant, le réseau semble fournir toujours la même séquence ce qui est bien dommage.\n",
"\n",
"Il semblerait que le réseau termine par faire abstraction de son entrée aléatoire et hardcode en lui un nom \"passe-partout\" qui malgré son apparence moyenne est le mieux à même à réduire le taux d'erreur.\n",
"\n",
"La solution serait un modèle prédisant lettre par lettre le mot en tenant compte de la lettre précédente au lieu de prédire le mot instantanément et à partir d'une séquence aléatoire."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"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.5.2"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment