{ "metadata": { "kernelspec": { "language": "python", "display_name": "Python 3", "name": "python3" }, "language_info": { "name": "python", "version": "3.10.13", "mimetype": "text/x-python", "codemirror_mode": { "name": "ipython", "version": 3 }, "pygments_lexer": "ipython3", "nbconvert_exporter": "python", "file_extension": ".py" }, "kaggle": { "accelerator": "nvidiaTeslaT4", "dataSources": [ { "sourceId": 8513800, "sourceType": "datasetVersion", "datasetId": 5082663 } ], "dockerImageVersionId": 30699, "isInternetEnabled": true, "language": "python", "sourceType": "notebook", "isGpuEnabled": true } }, "nbformat_minor": 4, "nbformat": 4, "cells": [ { "cell_type": "markdown", "source": [ "# Seq2Seq Fiński --> Angielski\n", "https://pytorch.org/tutorials/intermediate/seq2seq_translation_tutorial.html" ], "metadata": {} }, { "cell_type": "code", "source": [ "from __future__ import unicode_literals, print_function, division\n", "from io import open\n", "import unicodedata\n", "import re\n", "import random\n", "\n", "import torch\n", "import torch.nn as nn\n", "from torch import optim\n", "import torch.nn.functional as F\n", "\n", "import numpy as np\n", "from torch.utils.data import TensorDataset, DataLoader, RandomSampler\n", "\n", "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")" ], "metadata": { "_uuid": "8f2839f25d086af736a60e9eeb907d3b93b6e0e5", "_cell_guid": "b1076dfc-b9ad-4769-8c92-a6c4dae69d19", "execution": { "iopub.status.busy": "2024-05-25T14:03:55.886451Z", "iopub.execute_input": "2024-05-25T14:03:55.887266Z", "iopub.status.idle": "2024-05-25T14:04:02.514594Z", "shell.execute_reply.started": "2024-05-25T14:03:55.887232Z", "shell.execute_reply": "2024-05-25T14:04:02.513697Z" }, "trusted": true }, "execution_count": 1, "outputs": [] }, { "cell_type": "code", "source": [ "torch.cuda.device_count()" ], "metadata": { "execution": { "iopub.status.busy": "2024-05-25T14:04:09.403445Z", "iopub.execute_input": "2024-05-25T14:04:09.403926Z", "iopub.status.idle": "2024-05-25T14:04:09.434533Z", "shell.execute_reply.started": "2024-05-25T14:04:09.403898Z", "shell.execute_reply": "2024-05-25T14:04:09.433678Z" }, "trusted": true }, "execution_count": 2, "outputs": [ { "execution_count": 2, "output_type": "execute_result", "data": { "text/plain": "2" }, "metadata": {} } ] }, { "cell_type": "markdown", "source": [ "### Konwersja słów na index" ], "metadata": {} }, { "cell_type": "code", "source": [ "SOS_token = 0\n", "EOS_token = 1\n", "\n", "class Lang:\n", " def __init__(self, name):\n", " self.name = name\n", " self.word2index = {}\n", " self.word2count = {}\n", " self.index2word = {0: \"SOS\", 1: \"EOS\"}\n", " self.n_words = 2 # Count SOS and EOS\n", "\n", " def addSentence(self, sentence):\n", " for word in sentence.split(' '):\n", " self.addWord(word)\n", "\n", " def addWord(self, word):\n", " if word not in self.word2index:\n", " self.word2index[word] = self.n_words\n", " self.word2count[word] = 1\n", " self.index2word[self.n_words] = word\n", " self.n_words += 1\n", " else:\n", " self.word2count[word] += 1" ], "metadata": { "execution": { "iopub.status.busy": "2024-05-25T14:04:14.014114Z", "iopub.execute_input": "2024-05-25T14:04:14.014490Z", "iopub.status.idle": "2024-05-25T14:04:14.024526Z", "shell.execute_reply.started": "2024-05-25T14:04:14.014461Z", "shell.execute_reply": "2024-05-25T14:04:14.023673Z" }, "trusted": true }, "execution_count": 3, "outputs": [] }, { "cell_type": "markdown", "source": [ "### Normalizacja tekstu" ], "metadata": {} }, { "cell_type": "code", "source": [ "# Turn a Unicode string to plain ASCII, thanks to\n", "# https://stackoverflow.com/a/518232/2809427\n", "def unicodeToAscii(s):\n", " return ''.join(\n", " c for c in unicodedata.normalize('NFD', s)\n", " if unicodedata.category(c) != 'Mn'\n", " )\n", "\n", "# Lowercase, trim, and remove non-letter characters\n", "def normalizeString(s):\n", " s = unicodeToAscii(s.lower().strip())\n", " s = re.sub(r\"([.!?])\", r\" \\1\", s)\n", " s = re.sub(r\"[^a-zA-Z!?]+\", r\" \", s)\n", " return s.strip()" ], "metadata": { "execution": { "iopub.status.busy": "2024-05-25T14:04:23.431898Z", "iopub.execute_input": "2024-05-25T14:04:23.432285Z", "iopub.status.idle": "2024-05-25T14:04:23.438688Z", "shell.execute_reply.started": "2024-05-25T14:04:23.432256Z", "shell.execute_reply": "2024-05-25T14:04:23.437569Z" }, "trusted": true }, "execution_count": 4, "outputs": [] }, { "cell_type": "markdown", "source": [ "### Wczytywanie danych (zmodyfikowane ze względu na ścieżkę w kaggle)" ], "metadata": {} }, { "cell_type": "code", "source": [ "# Zmodyfikowana wersja ze względu na użycie pojedynczego pliku przesłanego na Kaggle\n", "def readLangs(reverse=False):\n", " print(\"Reading lines...\")\n", " lang1=\"en\"\n", " lang2=\"fin\"\n", " # Read the file and split into lines\n", " lines = open('/kaggle/input/anki-en-fin/fin.txt', encoding='utf-8').\\\n", " read().strip().split('\\n')\n", "\n", " # Split every line into pairs and normalize\n", " pairs = [[normalizeString(s) for s in l.split('\\t')[:-1]] for l in lines] # +Usuwanie licencji CC z linii\n", "\n", " # Reverse pairs, make Lang instances\n", " if reverse:\n", " pairs = [list(reversed(p)) for p in pairs]\n", " input_lang = Lang(lang2)\n", " output_lang = Lang(lang1)\n", " else:\n", " input_lang = Lang(lang1)\n", " output_lang = Lang(lang2)\n", "\n", " return input_lang, output_lang, pairs" ], "metadata": { "execution": { "iopub.status.busy": "2024-05-25T14:12:25.385674Z", "iopub.execute_input": "2024-05-25T14:12:25.386029Z", "iopub.status.idle": "2024-05-25T14:12:25.394103Z", "shell.execute_reply.started": "2024-05-25T14:12:25.386002Z", "shell.execute_reply": "2024-05-25T14:12:25.392925Z" }, "trusted": true }, "execution_count": 14, "outputs": [] }, { "cell_type": "markdown", "source": [ "#### Ograniczenie do zdań max 10 słów, formy I am / You are / He is etc. bez interpunkcji" ], "metadata": {} }, { "cell_type": "code", "source": [ "MAX_LENGTH = 10\n", "\n", "eng_prefixes = (\n", " \"i am \", \"i m \",\n", " \"he is\", \"he s \",\n", " \"she is\", \"she s \",\n", " \"you are\", \"you re \",\n", " \"we are\", \"we re \",\n", " \"they are\", \"they re \"\n", ")\n", "\n", "def filterPair(p):\n", " return len(p[0].split(' ')) < MAX_LENGTH and \\\n", " len(p[1].split(' ')) < MAX_LENGTH and \\\n", " p[1].startswith(eng_prefixes)\n", "\n", "\n", "def filterPairs(pairs):\n", " return [pair for pair in pairs if filterPair(pair)]" ], "metadata": { "execution": { "iopub.status.busy": "2024-05-25T14:12:29.729786Z", "iopub.execute_input": "2024-05-25T14:12:29.730147Z", "iopub.status.idle": "2024-05-25T14:12:29.737013Z", "shell.execute_reply.started": "2024-05-25T14:12:29.730121Z", "shell.execute_reply": "2024-05-25T14:12:29.735886Z" }, "trusted": true }, "execution_count": 15, "outputs": [] }, { "cell_type": "code", "source": [ "def prepareData(reverse=False):\n", " input_lang, output_lang, pairs = readLangs(reverse)\n", " print(\"Read %s sentence pairs\" % len(pairs))\n", " pairs = filterPairs(pairs)\n", " print(\"Trimmed to %s sentence pairs\" % len(pairs))\n", " print(\"Counting words...\")\n", " for pair in pairs:\n", " input_lang.addSentence(pair[0])\n", " output_lang.addSentence(pair[1])\n", " print(\"Counted words:\")\n", " print(input_lang.name, input_lang.n_words)\n", " print(output_lang.name, output_lang.n_words)\n", " return input_lang, output_lang, pairs\n", "\n", "input_lang, output_lang, pairs = prepareData(True)\n", "print(random.choice(pairs))" ], "metadata": { "execution": { "iopub.status.busy": "2024-05-25T14:12:33.204103Z", "iopub.execute_input": "2024-05-25T14:12:33.204776Z", "iopub.status.idle": "2024-05-25T14:12:36.889693Z", "shell.execute_reply.started": "2024-05-25T14:12:33.204744Z", "shell.execute_reply": "2024-05-25T14:12:36.888700Z" }, "trusted": true }, "execution_count": 16, "outputs": [ { "name": "stdout", "text": "Reading lines...\nRead 72258 sentence pairs\nTrimmed to 5005 sentence pairs\nCounting words...\nCounted words:\nfin 3686\nen 1971\n['mina odotan joulua innolla', 'i am looking forward to christmas']\n", "output_type": "stream" } ] }, { "cell_type": "markdown", "source": [ "### Definicja modelu" ], "metadata": {} }, { "cell_type": "code", "source": [ "class EncoderRNN(nn.Module):\n", " def __init__(self, input_size, hidden_size, dropout_p=0.1):\n", " super(EncoderRNN, self).__init__()\n", " self.hidden_size = hidden_size\n", "\n", " self.embedding = nn.Embedding(input_size, hidden_size)\n", " self.gru = nn.GRU(hidden_size, hidden_size, batch_first=True)\n", " self.dropout = nn.Dropout(dropout_p)\n", "\n", " def forward(self, input):\n", " embedded = self.dropout(self.embedding(input))\n", " output, hidden = self.gru(embedded)\n", " return output, hidden" ], "metadata": { "execution": { "iopub.status.busy": "2024-05-25T14:12:52.383787Z", "iopub.execute_input": "2024-05-25T14:12:52.384131Z", "iopub.status.idle": "2024-05-25T14:12:52.391196Z", "shell.execute_reply.started": "2024-05-25T14:12:52.384104Z", "shell.execute_reply": "2024-05-25T14:12:52.390316Z" }, "trusted": true }, "execution_count": 17, "outputs": [] }, { "cell_type": "code", "source": [ "class DecoderRNN(nn.Module):\n", " def __init__(self, hidden_size, output_size):\n", " super(DecoderRNN, self).__init__()\n", " self.embedding = nn.Embedding(output_size, hidden_size)\n", " self.gru = nn.GRU(hidden_size, hidden_size, batch_first=True)\n", " self.out = nn.Linear(hidden_size, output_size)\n", "\n", " def forward(self, encoder_outputs, encoder_hidden, target_tensor=None):\n", " batch_size = encoder_outputs.size(0)\n", " decoder_input = torch.empty(batch_size, 1, dtype=torch.long, device=device).fill_(SOS_token)\n", " decoder_hidden = encoder_hidden\n", " decoder_outputs = []\n", "\n", " for i in range(MAX_LENGTH):\n", " decoder_output, decoder_hidden = self.forward_step(decoder_input, decoder_hidden)\n", " decoder_outputs.append(decoder_output)\n", "\n", " if target_tensor is not None:\n", " # Teacher forcing: Feed the target as the next input\n", " decoder_input = target_tensor[:, i].unsqueeze(1) # Teacher forcing\n", " else:\n", " # Without teacher forcing: use its own predictions as the next input\n", " _, topi = decoder_output.topk(1)\n", " decoder_input = topi.squeeze(-1).detach() # detach from history as input\n", "\n", " decoder_outputs = torch.cat(decoder_outputs, dim=1)\n", " decoder_outputs = F.log_softmax(decoder_outputs, dim=-1)\n", " return decoder_outputs, decoder_hidden, None # We return `None` for consistency in the training loop\n", "\n", " def forward_step(self, input, hidden):\n", " output = self.embedding(input)\n", " output = F.relu(output)\n", " output, hidden = self.gru(output, hidden)\n", " output = self.out(output)\n", " return output, hidden" ], "metadata": { "execution": { "iopub.status.busy": "2024-05-25T14:12:54.393953Z", "iopub.execute_input": "2024-05-25T14:12:54.394808Z", "iopub.status.idle": "2024-05-25T14:12:54.409000Z", "shell.execute_reply.started": "2024-05-25T14:12:54.394765Z", "shell.execute_reply": "2024-05-25T14:12:54.407827Z" }, "trusted": true }, "execution_count": 18, "outputs": [] }, { "cell_type": "code", "source": [ "class BahdanauAttention(nn.Module):\n", " def __init__(self, hidden_size):\n", " super(BahdanauAttention, self).__init__()\n", " self.Wa = nn.Linear(hidden_size, hidden_size)\n", " self.Ua = nn.Linear(hidden_size, hidden_size)\n", " self.Va = nn.Linear(hidden_size, 1)\n", "\n", " def forward(self, query, keys):\n", " scores = self.Va(torch.tanh(self.Wa(query) + self.Ua(keys)))\n", " scores = scores.squeeze(2).unsqueeze(1)\n", "\n", " weights = F.softmax(scores, dim=-1)\n", " context = torch.bmm(weights, keys)\n", "\n", " return context, weights\n", "\n", "class AttnDecoderRNN(nn.Module):\n", " def __init__(self, hidden_size, output_size, dropout_p=0.1):\n", " super(AttnDecoderRNN, self).__init__()\n", " self.embedding = nn.Embedding(output_size, hidden_size)\n", " self.attention = BahdanauAttention(hidden_size)\n", " self.gru = nn.GRU(2 * hidden_size, hidden_size, batch_first=True)\n", " self.out = nn.Linear(hidden_size, output_size)\n", " self.dropout = nn.Dropout(dropout_p)\n", "\n", " def forward(self, encoder_outputs, encoder_hidden, target_tensor=None):\n", " batch_size = encoder_outputs.size(0)\n", " decoder_input = torch.empty(batch_size, 1, dtype=torch.long, device=device).fill_(SOS_token)\n", " decoder_hidden = encoder_hidden\n", " decoder_outputs = []\n", " attentions = []\n", "\n", " for i in range(MAX_LENGTH):\n", " decoder_output, decoder_hidden, attn_weights = self.forward_step(\n", " decoder_input, decoder_hidden, encoder_outputs\n", " )\n", " decoder_outputs.append(decoder_output)\n", " attentions.append(attn_weights)\n", "\n", " if target_tensor is not None:\n", " # Teacher forcing: Feed the target as the next input\n", " decoder_input = target_tensor[:, i].unsqueeze(1) # Teacher forcing\n", " else:\n", " # Without teacher forcing: use its own predictions as the next input\n", " _, topi = decoder_output.topk(1)\n", " decoder_input = topi.squeeze(-1).detach() # detach from history as input\n", "\n", " decoder_outputs = torch.cat(decoder_outputs, dim=1)\n", " decoder_outputs = F.log_softmax(decoder_outputs, dim=-1)\n", " attentions = torch.cat(attentions, dim=1)\n", "\n", " return decoder_outputs, decoder_hidden, attentions\n", "\n", "\n", " def forward_step(self, input, hidden, encoder_outputs):\n", " embedded = self.dropout(self.embedding(input))\n", "\n", " query = hidden.permute(1, 0, 2)\n", " context, attn_weights = self.attention(query, encoder_outputs)\n", " input_gru = torch.cat((embedded, context), dim=2)\n", "\n", " output, hidden = self.gru(input_gru, hidden)\n", " output = self.out(output)\n", "\n", " return output, hidden, attn_weights" ], "metadata": { "execution": { "iopub.status.busy": "2024-05-25T14:13:00.670299Z", "iopub.execute_input": "2024-05-25T14:13:00.670758Z", "iopub.status.idle": "2024-05-25T14:13:00.687695Z", "shell.execute_reply.started": "2024-05-25T14:13:00.670720Z", "shell.execute_reply": "2024-05-25T14:13:00.686610Z" }, "trusted": true }, "execution_count": 19, "outputs": [] }, { "cell_type": "code", "source": [ "def indexesFromSentence(lang, sentence):\n", " return [lang.word2index[word] for word in sentence.split(' ')]\n", "\n", "def tensorFromSentence(lang, sentence):\n", " indexes = indexesFromSentence(lang, sentence)\n", " indexes.append(EOS_token)\n", " return torch.tensor(indexes, dtype=torch.long, device=device).view(1, -1)\n", "\n", "def tensorsFromPair(pair):\n", " input_tensor = tensorFromSentence(input_lang, pair[0])\n", " target_tensor = tensorFromSentence(output_lang, pair[1])\n", " return (input_tensor, target_tensor)\n", "\n", "def get_dataloader(batch_size):\n", " input_lang, output_lang, pairs = prepareData(True)\n", "\n", " n = len(pairs)\n", " input_ids = np.zeros((n, MAX_LENGTH), dtype=np.int32)\n", " target_ids = np.zeros((n, MAX_LENGTH), dtype=np.int32)\n", "\n", " for idx, (inp, tgt) in enumerate(pairs):\n", " inp_ids = indexesFromSentence(input_lang, inp)\n", " tgt_ids = indexesFromSentence(output_lang, tgt)\n", " inp_ids.append(EOS_token)\n", " tgt_ids.append(EOS_token)\n", " input_ids[idx, :len(inp_ids)] = inp_ids\n", " target_ids[idx, :len(tgt_ids)] = tgt_ids\n", "\n", " train_data = TensorDataset(torch.LongTensor(input_ids).to(device),\n", " torch.LongTensor(target_ids).to(device))\n", "\n", " train_sampler = RandomSampler(train_data)\n", " train_dataloader = DataLoader(train_data, sampler=train_sampler, batch_size=batch_size)\n", " return input_lang, output_lang, train_dataloader" ], "metadata": { "execution": { "iopub.status.busy": "2024-05-25T14:22:08.183866Z", "iopub.execute_input": "2024-05-25T14:22:08.184711Z", "iopub.status.idle": "2024-05-25T14:22:08.194870Z", "shell.execute_reply.started": "2024-05-25T14:22:08.184675Z", "shell.execute_reply": "2024-05-25T14:22:08.193965Z" }, "trusted": true }, "execution_count": 31, "outputs": [] }, { "cell_type": "code", "source": [ "def train_epoch(dataloader, encoder, decoder, encoder_optimizer,\n", " decoder_optimizer, criterion):\n", "\n", " total_loss = 0\n", " for data in dataloader:\n", " input_tensor, target_tensor = data\n", "\n", " encoder_optimizer.zero_grad()\n", " decoder_optimizer.zero_grad()\n", "\n", " encoder_outputs, encoder_hidden = encoder(input_tensor)\n", " decoder_outputs, _, _ = decoder(encoder_outputs, encoder_hidden, target_tensor)\n", "\n", " loss = criterion(\n", " decoder_outputs.view(-1, decoder_outputs.size(-1)),\n", " target_tensor.view(-1)\n", " )\n", " loss.backward()\n", "\n", " encoder_optimizer.step()\n", " decoder_optimizer.step()\n", "\n", " total_loss += loss.item()\n", "\n", " return total_loss / len(dataloader)" ], "metadata": { "execution": { "iopub.status.busy": "2024-05-25T14:16:38.894580Z", "iopub.execute_input": "2024-05-25T14:16:38.895410Z", "iopub.status.idle": "2024-05-25T14:16:38.902142Z", "shell.execute_reply.started": "2024-05-25T14:16:38.895382Z", "shell.execute_reply": "2024-05-25T14:16:38.900953Z" }, "trusted": true }, "execution_count": 22, "outputs": [] }, { "cell_type": "code", "source": [ "import time\n", "import math\n", "\n", "def asMinutes(s):\n", " m = math.floor(s / 60)\n", " s -= m * 60\n", " return '%dm %ds' % (m, s)\n", "\n", "def timeSince(since, percent):\n", " now = time.time()\n", " s = now - since\n", " es = s / (percent)\n", " rs = es - s\n", " return '%s (- %s)' % (asMinutes(s), asMinutes(rs))" ], "metadata": { "execution": { "iopub.status.busy": "2024-05-25T14:16:43.069584Z", "iopub.execute_input": "2024-05-25T14:16:43.069953Z", "iopub.status.idle": "2024-05-25T14:16:43.075972Z", "shell.execute_reply.started": "2024-05-25T14:16:43.069926Z", "shell.execute_reply": "2024-05-25T14:16:43.075033Z" }, "trusted": true }, "execution_count": 23, "outputs": [] }, { "cell_type": "code", "source": [ "def train(train_dataloader, encoder, decoder, n_epochs, learning_rate=0.001,\n", " print_every=100, plot_every=100):\n", " start = time.time()\n", " plot_losses = []\n", " print_loss_total = 0 # Reset every print_every\n", " plot_loss_total = 0 # Reset every plot_every\n", "\n", " encoder_optimizer = optim.Adam(encoder.parameters(), lr=learning_rate)\n", " decoder_optimizer = optim.Adam(decoder.parameters(), lr=learning_rate)\n", " criterion = nn.NLLLoss()\n", "\n", " for epoch in range(1, n_epochs + 1):\n", " loss = train_epoch(train_dataloader, encoder, decoder, encoder_optimizer, decoder_optimizer, criterion)\n", " print_loss_total += loss\n", " plot_loss_total += loss\n", "\n", " if epoch % print_every == 0:\n", " print_loss_avg = print_loss_total / print_every\n", " print_loss_total = 0\n", " print('%s (%d %d%%) %.4f' % (timeSince(start, epoch / n_epochs),\n", " epoch, epoch / n_epochs * 100, print_loss_avg))\n", "\n", " if epoch % plot_every == 0:\n", " plot_loss_avg = plot_loss_total / plot_every\n", " plot_losses.append(plot_loss_avg)\n", " plot_loss_total = 0\n", "\n", " showPlot(plot_losses)" ], "metadata": { "execution": { "iopub.status.busy": "2024-05-25T14:20:58.574148Z", "iopub.execute_input": "2024-05-25T14:20:58.574520Z", "iopub.status.idle": "2024-05-25T14:20:58.583203Z", "shell.execute_reply.started": "2024-05-25T14:20:58.574492Z", "shell.execute_reply": "2024-05-25T14:20:58.582230Z" }, "trusted": true }, "execution_count": 24, "outputs": [] }, { "cell_type": "code", "source": [ "import matplotlib.pyplot as plt\n", "plt.switch_backend('agg')\n", "import matplotlib.ticker as ticker\n", "import numpy as np\n", "%matplotlib inline\n", "\n", "def showPlot(points):\n", " plt.figure()\n", " fig, ax = plt.subplots()\n", " # this locator puts ticks at regular intervals\n", " loc = ticker.MultipleLocator(base=0.2)\n", " ax.yaxis.set_major_locator(loc)\n", " plt.plot(points)" ], "metadata": { "execution": { "iopub.status.busy": "2024-05-25T14:21:00.586018Z", "iopub.execute_input": "2024-05-25T14:21:00.586719Z", "iopub.status.idle": "2024-05-25T14:21:00.592633Z", "shell.execute_reply.started": "2024-05-25T14:21:00.586683Z", "shell.execute_reply": "2024-05-25T14:21:00.591636Z" }, "trusted": true }, "execution_count": 25, "outputs": [] }, { "cell_type": "markdown", "source": [ "### Ewaluacja" ], "metadata": {} }, { "cell_type": "code", "source": [ "def evaluate(encoder, decoder, sentence, input_lang, output_lang):\n", " with torch.no_grad():\n", " input_tensor = tensorFromSentence(input_lang, sentence)\n", "\n", " encoder_outputs, encoder_hidden = encoder(input_tensor)\n", " decoder_outputs, decoder_hidden, decoder_attn = decoder(encoder_outputs, encoder_hidden)\n", "\n", " _, topi = decoder_outputs.topk(1)\n", " decoded_ids = topi.squeeze()\n", "\n", " decoded_words = []\n", " for idx in decoded_ids:\n", " if idx.item() == EOS_token:\n", " decoded_words.append('')\n", " break\n", " decoded_words.append(output_lang.index2word[idx.item()])\n", " return decoded_words, decoder_attn" ], "metadata": { "execution": { "iopub.status.busy": "2024-05-25T14:21:01.858691Z", "iopub.execute_input": "2024-05-25T14:21:01.859612Z", "iopub.status.idle": "2024-05-25T14:21:01.866857Z", "shell.execute_reply.started": "2024-05-25T14:21:01.859574Z", "shell.execute_reply": "2024-05-25T14:21:01.865732Z" }, "trusted": true }, "execution_count": 26, "outputs": [] }, { "cell_type": "code", "source": [ "def evaluateRandomly(encoder, decoder, n=10):\n", " for i in range(n):\n", " pair = random.choice(pairs)\n", " print('Input sentence: ', pair[0])\n", " print('Target (true) translation:' , pair[1])\n", " output_words, _ = evaluate(encoder, decoder, pair[0], input_lang, output_lang)\n", " output_sentence = ' '.join(output_words)\n", " print('Output sentence: ', output_sentence)\n", " print('')" ], "metadata": { "execution": { "iopub.status.busy": "2024-05-25T14:39:52.474985Z", "iopub.execute_input": "2024-05-25T14:39:52.475327Z", "iopub.status.idle": "2024-05-25T14:39:52.481801Z", "shell.execute_reply.started": "2024-05-25T14:39:52.475304Z", "shell.execute_reply": "2024-05-25T14:39:52.480957Z" }, "trusted": true }, "execution_count": 36, "outputs": [] }, { "cell_type": "markdown", "source": [ "# Wykorzystanie zdefiniowanych wyżej funkcji" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "### Trenowanie modelu" ], "metadata": {} }, { "cell_type": "code", "source": [ "hidden_size = 128\n", "batch_size = 32\n", "\n", "input_lang, output_lang, train_dataloader = get_dataloader(batch_size)\n", "\n", "encoder = EncoderRNN(input_lang.n_words, hidden_size).to(device)\n", "decoder = AttnDecoderRNN(hidden_size, output_lang.n_words).to(device)\n", "\n", "train(train_dataloader, encoder, decoder, 80, print_every=5, plot_every=5)" ], "metadata": { "execution": { "iopub.status.busy": "2024-05-25T14:22:39.754370Z", "iopub.execute_input": "2024-05-25T14:22:39.754740Z", "iopub.status.idle": "2024-05-25T14:26:53.707012Z", "shell.execute_reply.started": "2024-05-25T14:22:39.754714Z", "shell.execute_reply": "2024-05-25T14:26:53.705969Z" }, "trusted": true }, "execution_count": 32, "outputs": [ { "name": "stdout", "text": "Reading lines...\nRead 72258 sentence pairs\nTrimmed to 5005 sentence pairs\nCounting words...\nCounted words:\nfin 3686\nen 1971\n0m 21s (- 5m 21s) (5 6%) 1.9364\n0m 36s (- 4m 17s) (10 12%) 1.0355\n0m 51s (- 3m 45s) (15 18%) 0.6313\n1m 7s (- 3m 21s) (20 25%) 0.3787\n1m 22s (- 3m 1s) (25 31%) 0.2243\n1m 37s (- 2m 42s) (30 37%) 0.1371\n1m 52s (- 2m 25s) (35 43%) 0.0903\n2m 7s (- 2m 7s) (40 50%) 0.0668\n2m 23s (- 1m 51s) (45 56%) 0.0538\n2m 38s (- 1m 34s) (50 62%) 0.0471\n2m 53s (- 1m 18s) (55 68%) 0.0410\n3m 8s (- 1m 2s) (60 75%) 0.0381\n3m 23s (- 0m 47s) (65 81%) 0.0343\n3m 38s (- 0m 31s) (70 87%) 0.0342\n3m 54s (- 0m 15s) (75 93%) 0.0322\n4m 9s (- 0m 0s) (80 100%) 0.0307\n", "output_type": "stream" } ] }, { "cell_type": "code", "source": [ "evaluateRandomly(encoder, decoder)" ], "metadata": { "execution": { "iopub.status.busy": "2024-05-25T15:48:23.131435Z", "iopub.execute_input": "2024-05-25T15:48:23.131948Z", "iopub.status.idle": "2024-05-25T15:48:23.213007Z", "shell.execute_reply.started": "2024-05-25T15:48:23.131911Z", "shell.execute_reply": "2024-05-25T15:48:23.211856Z" }, "trusted": true }, "execution_count": 121, "outputs": [ { "name": "stdout", "text": "Input sentence: olen hyvin hyvin vihainen\nTarget (true) translation: i m very very angry\nOutput sentence: i am very angry today \n\nInput sentence: han valehtelee\nTarget (true) translation: he s lying\nOutput sentence: he is telling a lie \n\nInput sentence: olen myohassa\nTarget (true) translation: i m late\nOutput sentence: i m late \n\nInput sentence: han on linja autonkuljettaja\nTarget (true) translation: he is a bus driver\nOutput sentence: he is a bus driver \n\nInput sentence: mukava tavata sinut taas\nTarget (true) translation: i m glad to see you again\nOutput sentence: i m glad to see you again \n\nInput sentence: olet kuumeessa\nTarget (true) translation: you re running a fever\nOutput sentence: you re so predictable \n\nInput sentence: anteeksi mutta unohdin tehda laksyt\nTarget (true) translation: i m sorry i forgot to do my homework\nOutput sentence: i m sorry i forgot to do my homework \n\nInput sentence: mina olen tyoton\nTarget (true) translation: i m unemployed\nOutput sentence: i m unemployed \n\nInput sentence: olen taynna\nTarget (true) translation: i am full\nOutput sentence: i am full of french \n\nInput sentence: ma kuolen nalkaan !\nTarget (true) translation: i m dying of hunger\nOutput sentence: i m dying of hunger \n\n", "output_type": "stream" } ] }, { "cell_type": "code", "source": [ "def showAttention(input_sentence, output_words, attentions):\n", " fig = plt.figure()\n", " ax = fig.add_subplot(111)\n", " cax = ax.matshow(attentions.cpu().numpy(), cmap='bone')\n", " fig.colorbar(cax)\n", "\n", " # Set up axes\n", " ax.set_xticklabels([''] + input_sentence.split(' ') +\n", " [''], rotation=90)\n", " ax.set_yticklabels([''] + output_words)\n", "\n", " # Show label at every tick\n", " ax.xaxis.set_major_locator(ticker.MultipleLocator(1))\n", " ax.yaxis.set_major_locator(ticker.MultipleLocator(1))\n", "\n", " plt.show()\n", "\n", "\n", "def evaluateAndShowAttention(input_sentence):\n", " input_sentence = normalizeString(input_sentence)\n", " output_words, attentions = evaluate(encoder, decoder, input_sentence, input_lang, output_lang)\n", " print('input =', input_sentence)\n", " print('output =', ' '.join(output_words))\n", " showAttention(input_sentence, output_words, attentions[0, :len(output_words), :])\n", "\n", "def translate(input_sentence, tokenized=False):\n", " input_sentence = normalizeString(input_sentence)\n", " output_words, attentions = evaluate(encoder, decoder, input_sentence, input_lang, output_lang)\n", " if tokenized:\n", " if \"\" in output_words:\n", " output_words.remove(\"\")\n", " return output_words\n", " return ' '.join(output_words)" ], "metadata": { "execution": { "iopub.status.busy": "2024-05-25T15:30:41.253325Z", "iopub.execute_input": "2024-05-25T15:30:41.253734Z", "iopub.status.idle": "2024-05-25T15:30:41.264515Z", "shell.execute_reply.started": "2024-05-25T15:30:41.253703Z", "shell.execute_reply": "2024-05-25T15:30:41.263376Z" }, "trusted": true }, "execution_count": 99, "outputs": [] }, { "cell_type": "code", "source": [ "translate(\"Meillä on nälkä\", tokenized=True)" ], "metadata": { "execution": { "iopub.status.busy": "2024-05-25T14:58:53.639252Z", "iopub.execute_input": "2024-05-25T14:58:53.639963Z", "iopub.status.idle": "2024-05-25T14:58:53.654186Z", "shell.execute_reply.started": "2024-05-25T14:58:53.639932Z", "shell.execute_reply": "2024-05-25T14:58:53.653028Z" }, "trusted": true }, "execution_count": 76, "outputs": [ { "execution_count": 76, "output_type": "execute_result", "data": { "text/plain": "['we', 'are', 'hungry']" }, "metadata": {} } ] }, { "cell_type": "code", "source": [ "evaluateAndShowAttention('Olet liian naivi')" ], "metadata": { "execution": { "iopub.status.busy": "2024-05-25T14:46:05.023218Z", "iopub.execute_input": "2024-05-25T14:46:05.024277Z", "iopub.status.idle": "2024-05-25T14:46:05.426793Z", "shell.execute_reply.started": "2024-05-25T14:46:05.024227Z", "shell.execute_reply": "2024-05-25T14:46:05.424993Z" }, "trusted": true }, "execution_count": 44, "outputs": [ { "name": "stdout", "text": "input = olet liian naivi\noutput = you re too naive \n", "output_type": "stream" }, { "name": "stderr", "text": "/tmp/ipykernel_34/2052950992.py:8: UserWarning: FixedFormatter should only be used together with FixedLocator\n ax.set_xticklabels([''] + input_sentence.split(' ') +\n/tmp/ipykernel_34/2052950992.py:10: UserWarning: FixedFormatter should only be used together with FixedLocator\n ax.set_yticklabels([''] + output_words)\n", "output_type": "stream" }, { "output_type": "display_data", "data": { "text/plain": "
", "image/png": "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N/nIaIO0cOFC5eTkuH/+7bffPO5rPXfunF5//XUzQoOfUSEGuJo1ayotLU1//etfVb58eW3atEm1atXSrl271Lx58z99eoFV5ObmauLEiVq4cKH279/v8QtNko4fP25SZOabN2+eunfvrrCwMHenwUi/fv2KKarA9cd7f+12uzZu3KhatWpJkg4dOqSqVatyi0oJxGBUgDty5MhV9yBKl1dmYZHm/xo9erRmzZqlZ555RiNGjNCLL76offv26R//+IdGjhxpdnimujLJkfD+3B9rBGoG66BlGuASEhK0ZMkS9895SXDWrFlKTEw0K6yA8+GHH2rmzJl65plnVLp0afXo0UOzZs3SyJEjWXfSwIULF+RwODw2wMqoEAPcmDFj1LFjR23dulWXLl3S5MmTtXXrVq1evVrffvut2eEFjKysLPdSWxERETp16pQk6W9/+5teeuklM0MLKGfPntXzzz+vhQsX6tixY1e9ThsQVkaFGOBuvfVWbdy4UZcuXVLjxo319ddfKyoqSmvWrFGLFi3MDi9gVK9eXQcPHpQk1a5dW19//bUkad26dQoLCzMztIAydOhQrVixQtOnT1dYWJhmzZql0aNHq2rVqpo/f77Z4QWMZcuWafHixVq8eLGcTqfS09PdPy9btszs8OAnTKpBiTBs2DDZ7Xa98MILSktLU+/evRUXF6f9+/fr6aef1rhx48wOMSDUqFFD8+fPV9u2bWW327VhwwbVqVNH77//vj766COe4CAV6IZ7m81GNV0CkRADUGHGcux2ux8jCV5r1qzRmjVrVLduXR7meoWIiAht3bpVNWrUUPXq1fXZZ5+pZcuW2rt3rxo3bqwzZ86YHSJgGsYQA1CFChX+dAapy+Xib6leJCYmMukoH7Vq1dLevXtVo0YNxcfHa+HChWrZsqW++OILVahQwezwAsa5c+e0e/fufB8BtWXLFtWsWZO1X0sgEmIAWrlypdkhBIXFixerY8eOKlOmjBYvXuz12HvvvbeYogpsycnJ2rRpk26//XYNGzZMnTp10tSpU3Xx4kVNmDDB7PACRk5Ojlq1aqWMjAy1bNnSvX/r1q1q1qyZ9u/fT0IsgWiZBoGTJ09q9uzZ2rZtmySpYcOGevjhhxUZGWlyZOa68lmR3sZ9qKSN/frrr1q/fr3q1KmjJk2amB1OQOnatauioqI0depU977hw4dr48aN+uqrr0yMDP5CQgxwP/74o+6++26Fh4e7/6a6bt06nT9/Xl9//bWaN29ucoQINunp6UpPT9fhw4c9liSTpDlz5pgUVeBZsmSJHnzwQR08eFClS5eWy+VSzZo1NX78eHXt2tXs8OAHJMQA16ZNG9WpU0czZ850P+Xi0qVL6t+/v/bs2aPvvvvO5AgRTEaPHq2XX35ZCQkJqlKlylVj1Z9//rlJkQWe3NxcVa9eXTNmzNDf//53rVy5Uvfdd5+ysrIUGhpqdnjwAxJigCtbtqz+7//+T/Hx8R77t27dqoSEBJ07d86kyMw3ZcoUPfLIIwoPD9eUKVO8Hvvkk08WU1SBrUqVKnr99dfVp08fs0MJCs8++6z27t2rRYsW6aGHHlJYWJimT59udljwEybVBDi73a79+/dflRAzMzNVvnx5k6IKDBMnTlSvXr0UHh6uiRMnGh5ns9lIiP+Rk5OjW265xewwgka/fv3UsmVLHThwQIsWLeKm/BKOCjHAPfnkk/r88881fvx49y+yVatW6bnnntN9992nSZMmmRsggsrzzz+viIgIlrMrhBYtWqh8+fLKysrS9u3bzQ4HfkSFGODGjx8vm82mvn376tKlS5KkMmXKaODAgZZffSUlJaVAx9lsNr355pt+jiY4XLhwQe+++66WL1+uJk2aqEyZMh6vc+vF1fr27aunn35ar776qtmhwM+oEINE3o3C0uW1Oq+77jqTIzLfHXfcUaDjbDabVqxY4edogoO374zvKX/Hjx/XW2+9pUcffVQxMTFmhwM/IiECACCedgEAgCQSIgAAkkiIQSU7O1ujRo1Sdna22aEENL6nguF7Khi+J+tgDDGIOBwORUZG6tSpUzz2yQu+p4LheyoYvifroEIEAEAkRAAAJHFjviGn06nff/9d5cuX/9OH9RYXh8Ph8b/IH99TwfA9FUwgfk8ul0unT59W1apVvT76zFcXLlxQTk6Oz+8TGhqq8PDwIojIvxhDNPDbb78pNjbW7DAAwFBmZqaqV6/ul/e+cOGCbrzxRmVlZfn8XjExMdq7d2/AJ0UqRANWXzgb/hAYnYZAd/zEcbNDCHgOh0NxNWv69fdUTk6OsrKytH//fp8mEzkcDtWoUUM5OTkkxGAVKG1SlBz8O1UwzOQsuOL4dyqifHlF+JB4nUHUhGRSDQAAokIEAHjhcrnky1STYJqmQkIEABhy/eePL+cHC1qmAACIChEA4IXTdXnz5fxgQUIEABiy0hgiLVMAAESFCADwwuly+XQvYTDdh0hCBAAYomUKAIDFUCECAAxZqUIkIQIADDGGCACArFUhMoYIAICoEAEAXlhpLVMSIgDAkJWWbqNlCgCAqBABAN74OKlGQTSphoQIADBkpdsuaJkCACAqRACAF1a6D5GECAAwZKWESMsUAABRIQIAvLDSpBoSIgDAkJVapiREAIAhKy3dxhgiAACiQgQAeGGltUxJiAAAQy75Ng4YRPmQlikAABIVIgDAC2aZAgAga92HGJAt0/nz5+v6669Xdna2x/7OnTurT58+kqTp06erdu3aCg0NVf369fX++++7j9u3b59sNps2btzo3nfy5EnZbDZlZGTke83s7Gw5HA6PDQBgHQGZEB944AHl5uZq8eLF7n2HDx/WkiVL9NBDD+nzzz/XU089pWeeeUabN2/Wo48+quTkZK1cufKarzl27FhFRka6t9jY2KL4KAAQ1PJapr5swSIgE2LZsmXVs2dPvffee+59H3zwgWrUqKG2bdtq/PjxevDBB/X444+rXr16SklJUZcuXTR+/Phrvubw4cN16tQp95aZmVkUHwUAglpey9SXLVgEZEKUpAEDBujrr7/WgQMHJElz587Vgw8+KJvNpm3btql169Yex7du3Vrbtm275uuFhYXJbrd7bAAA6wjYSTXNmjVT06ZNNX/+fLVv315btmzRkiVLCnRuSMjlPH9lqX7x4kW/xAkAJZqvbU8qxKLRv39/zZ07V++9956SkpLc43oNGjTQqlWrPI5dtWqVGjZsKEm64YYbJEkHDx50v37lBBsAQMG4iuBPsAjYClGSevbsqWeffVYzZ87U/Pnz3fufe+45de3aVc2aNVNSUpK++OILffbZZ1q+fLmky2OQf/3rXzVu3DjdeOONOnz4sEaMGGHWxwCAoGWlpdsCukKMjIzUfffdp4iICHXu3Nm9v3Pnzpo8ebLGjx+vv/zlL3rnnXf03nvvqW3btu5j5syZo0uXLqlFixYaMmSIXn311eL/AACAoBHQFaIkHThwQL169VJYWJjH/oEDB2rgwIGG5zVo0ECrV6/22BdM038BIBCwUk0AOHHihDIyMpSRkaG3337b7HAAwJJIiAGgWbNmOnHihF577TXVr1/f7HAA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ChEA4IWV7kMkIQIADFlplikJEQBgyEoJkTFEAABEhQgA8MJKFSIJEQBgyEoJkZYpAACiQgQAeHG5Qrz2WyeCqUIkIQIAjFlo6Ta/tkxtNlu+28cff+w+Jjc3VxMnTlTjxo0VHh6uihUrqmPHjlq1apXHe+Xm5mrcuHGKj49X2bJlValSJbVq1UqzZs3y50cAAFhEkVeIJ06cUJkyZRQRESFJeu+993T33Xd7HFOhQgVJl0vp7t27a/ny5XrjjTd05513yuFwaNq0aWrbtq0++eQTde7cWZI0evRovfPOO5o6daoSEhLkcDj0448/6sSJE+73/f333xUVFaXSpSl8AaAoWGkt0yLJHJcuXdKyZcs0d+5cffHFF/rhhx/UtGlTSZeTX0xMTL7nLVy4UJ9++qkWL16sTp06ufe/++67OnbsmPr376+77rpL5cqV0+LFi/X444/rgQcecB+Xd408M2fO1PTp09W7d2/169dPjRs3LoqPBwCWxSzTAvr555/1zDPPqHr16urbt69uuOEGrVy58qpEZWTBggWqV6+eRzLM88wzz+jYsWP65ptvJEkxMTFasWKFjhw5Yvh+zz//vCZPnqxt27apefPmat68uaZMmeL1nDzZ2dlyOBweGwDAOgqdEI8dO6bJkyerefPmSkhI0J49e/T222/r4MGDevvtt5WYmOhxfI8ePRQREeGx7d+/X5K0Y8cONWjQIN/r5O3fsWOHJGnChAk6cuSIYmJi1KRJEz322GP66quvPM4JDw9Xt27dtGTJEh04cEB9+/bV3LlzVa1aNXXu3Fmff/65Ll26lO/1xo4dq8jISPcWGxtb2K8GAEqcy4t7+7YFi0InxLfeektDhgxRRESEdu3apc8//1xdunRRaGhovsdPnDhRGzdu9NiqVq3qfr2g5XTDhg21efNmff/993rooYd0+PBhderUSf3798/3+KioKA0ZMkQbNmzQP//5T61Zs0ZdunTR5s2b8z1++PDhOnXqlHvLzMwsUFwAUJLltUx92YJFoccQH3nkEZUuXVrz58/XX/7yF913333q06eP2rZtq5CQq/NrTEyM6tSpk+971atXT9u2bcv3tbz99erVc+8LCQnRzTffrJtvvllDhgzRBx98oD59+ujFF1/UjTfe6HH+6dOn9emnn+r999/Xd999p9tvv139+vVTw4YN871eWFiYwsLCCvQdAIBVMIboRdWqVTVixAjt2LFDS5cuVWhoqLp06aKaNWtq2LBh2rJlS4Hfq3v37tq5c6e++OKLq1578803df311+uuu+4yPD8vuZ09e1bS5VszvvrqK/Xs2VPR0dEaN26c7rzzTu3Zs0fp6enq27evYSULALA2nybV3HLLLXrnnXeUlZWlN954Qxs3blTTpk31888/u485efKksrKyPLa8BNa9e3f9z//8j/r166fZs2dr3759+umnn/Too49q8eLFmjVrlsqVKydJuv/++zVx4kT98MMP+vXXX5WRkaFBgwapXr16io+PlySNGTNGPXr0UPny5bV8+XL98ssvevHFF1WjRg1fPiYAWJaVWqY2VxFH+/vvvysiIkJ2u102W/5PWR47dqyGDRsm6fItG5MmTdLcuXO1c+dOhYeHKzExUS+99JJat27tPmfmzJn66KOPtHnzZp06dUoxMTFq166dRo0apZo1a0qS9u3bp5iYGIWHh/v8ORwOhyIjI31+HwCFE0y/QM2S9/vp1KlTstvtfr1GxYoxstmuvXZyuZw6cSLLr7EWlSJPiCUFCREwB7+S/hwJ0T9Y0gUAYMzX2yaC6LYLEiIAwNDlpdessXQbz0MEAEBUiAAALy6P6VrjPkQSIgDAkJUSIi1TAABEhQgA8MLXxbmDaXFvEiIAwNDljqcvLdMiC8XvSIgAAEO+jgEyhggAQJChQgQAGLJShUhCBAAY8zWhBVFCpGUKAICoEAEAXrjklJT/o/wKdn7wVIgkRACAISuNIdIyBQBAVIgAAC+sVCGSEAEAhqyUEGmZAgAgKkQAgBdWqhBJiAAAQ5efVuHDbRckRABASWClCpExRAAARIUIAPDGQmuZkhABAIZ8XXotmJZuo2UKAICoEAEAXjDLFAAAMcsUAADLoUI0EEx/qwFKEofDYXYIAS/vOyqu31NW+X1IQjRw+vRps0MALCkyMtLsEILG6dOn/fZ9hYaGKiYmRllZWT6/V0xMjEJDQ4sgKv+yuayS+gvJ6XTq999/V/ny5WWzXfuAclFyOByKjY1VZmam7Ha72eEELL6nguF7KphA/J5cLpdOnz6tqlWrKiTEfyNfFy5cUE5Ojs/vExoaqvDw8CKIyL+oEA2EhISoevXqZoeRL7vdHjD/YQYyvqeC4XsqmED7noqjkg4PDw+KRFZUmFQDAIBIiAAASCIhBpWwsDClpqYqLCzM7FACGt9TwfA9FQzfk3UwqQYAAFEhAgAgiYQIAIAkEiIAAJJIiAAASCIhAgAgiYQIAIAkEiIAAJJIiAAASJL+H5bvJJHifT/eAAAAAElFTkSuQmCC" }, "metadata": {} } ] }, { "cell_type": "code", "source": [ "evaluateAndShowAttention('Olen todella pahoillani')" ], "metadata": { "execution": { "iopub.status.busy": "2024-05-25T14:46:10.394969Z", "iopub.execute_input": "2024-05-25T14:46:10.395671Z", "iopub.status.idle": "2024-05-25T14:46:10.793392Z", "shell.execute_reply.started": "2024-05-25T14:46:10.395630Z", "shell.execute_reply": "2024-05-25T14:46:10.791940Z" }, "trusted": true }, "execution_count": 45, "outputs": [ { "name": "stdout", "text": "input = olen todella pahoillani\noutput = i am truly sorry \n", "output_type": "stream" }, { "name": "stderr", "text": "/tmp/ipykernel_34/2052950992.py:8: UserWarning: FixedFormatter should only be used together with FixedLocator\n ax.set_xticklabels([''] + input_sentence.split(' ') +\n/tmp/ipykernel_34/2052950992.py:10: UserWarning: FixedFormatter should only be used together with FixedLocator\n ax.set_yticklabels([''] + output_words)\n", "output_type": "stream" }, { "output_type": "display_data", "data": { "text/plain": "
", "image/png": "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3367lixZogceeMDgCI339NNP6/z58/rwww8VGRmp9957z+n1jRs3asCAAQZFh/LEoBpUGIcPH9bixYt1+PBhpaSkKCIiQp9++qkaNGigG264wejwvEJ0dLQeffRRjRo1ymn/rFmzlJqa6qgaATMiIaJC2LBhg3r06KFOnTrpq6++Unp6uho3bqxp06Zp69atjhGCZhcUFKS9e/fq+uuvd9r//fffq0WLFrpw4YJBkXmfX3/9VV988YUOHjwoSWratKnuuOMOVa5c2eDIUF5oMvUBhw4d0rp163Ty5EnZbDan1yZOnGhQVN5l7NixeuGFF5SUlKRq1ao59t92222aO3eugZF5l6ioKKWlpV2WEL/88ksmmv/OqlWrNHjwYJ06dcppf82aNbVw4UL17NnToMhQnkiIXi41NVWPP/64atasqcjISKdRlRaLhYT4m927d2vZsmWX7Y+IiLjsS83MRo8erSeffFI7d+5Ux44dJRX2iS1ZskQpKSkGR+cdNm3apL59+6pXr14aPXq0oqOjJUn79u3TzJkz1bdvX23YsEE333yzwZGirNFk6uUaNmyooUOH6plnnjE6FK9Wv359vfvuu+rYsaOqVaum//f//p8aN26slStXasyYMTp8+LDRIXqNlStXaubMmY7+wujoaD311FP629/+ZnBk3uHuu+9WVFSUXn/99WJff+yxx5SZmalPPvnkKkeG8kZC9HKhoaHauXOnGjdubHQoXm3MmDHasmWL3nvvPTVt2lTbt2/XiRMnFB8fr/j4eCUnJxsdInxEjRo1tGHDBsdKNX+0a9cudenSRWfOnLnKkaG8sVKNl7vvvvv0+eefGx2G15syZYqaN2+uqKgo/fLLL4qJidEtt9yijh07avz48UaH53Xy8/P13//+VxkZGU4bLl+p5o/CwsIYfFRB0Yfo5a6//npNmDBB33zzjVq2bKmAgACn14vmkpldYGCgUlNTNWHCBO3Zs0e//PKL2rZtqyZNmhgdmlc5dOiQBg0apE2bNjntt9vtrGX6myZNmmjt2rVKTEws9vW0tDT+XlVQNJl6uWuvvdblaxaLRUeOHLmK0cDXderUSZUqVdLYsWNVp06dy5a+a926tUGReY/Zs2frhRde0FtvvaW7777b6bXVq1crISFBzz77rNPjoVAxkBDhs0rzhTRr1qxyjMR3VK1aVdu2bVPz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2fT888+rfv36CgoKUps2bbRmzRrHeUePHpXFYtGKFSvUpUsXBQcH61//+pcGDhyo3r1768UXX1TdunXVrFkzPf/882rRosVl927Tpo0mTJhQbFx5eXmyWq1OGwCYXVGF6MnmK65qQjx+/LgGDBigQYMGKT09XevXr1efPn1kt9uVkpKimTNnasaMGdq1a5e6d++uXr166dChQ07XGDt2rEaMGKH09HR1795dkpSWlqYDBw7oiy++0P/+7/86rv/dd985ztuxY4d27dqlxMTEYmObOnWqwsLCHFtUVFT5fRAA4CuK1jL1ZPMRFvtVTN/bt29Xu3btdPToUTVs2NDptXr16mnYsGF69tlnHfvat2+vm266SfPmzdPRo0d17bXXas6cORoxYoTjmIEDB2rNmjXKyMhQYGCgY//dd9+tRo0a6dVXX5UkPfnkk9q9e7fWrVtXbGx5eXnKy8tz/Gy1WkmKKGMWowPwCb40TN8oVqtVYWFhOnfunEJDQ8v1Hrfccr8qVQq44utcunRRX331brnGWlauaoXYunVr3X777WrZsqXuu+8+paam6syZM7JarTp27Jg6derkdHynTp2Unp7utC82Nvay67Zs2dIpGUrSkCFD9M477+jChQvKz8/XsmXLNGjQIJexBQUFKTQ01GkDAJjHVU2I/v7++uKLL/Tpp58qJiZGr7zyipo1a6YffvihxNeoWrVqifb17NlTQUFBWrlypT7++GNdvHhRffv29Sh+ADAb+hDLkcViUadOnTR58mTt2LFDgYGBSktLU926dbVx40anYzdu3KiYmJgruk+lSpWUkJCgxYsXa/Hixerfv78qV65cFm8BAEzDTAnxqs5D3LJli9LS0nTnnXcqIiJCW7ZsUXZ2tqKjo/XUU08pOTlZ1113ndq0aaPFixdr586d+te//nXF9xs8eLCio6Ml6bJkCwDA713VhBgaGqqvvvpKc+bMkdVqVcOGDTVz5kz16NFD3bt317lz5zR69GidPHlSMTExWrVqlZo0aXLF92vSpIk6duyo06dPKy4urgzfCQCYg5km5l/VUaZXm91uV5MmTTR06FAlJSWV6tyiEVZA2WGUaUkwyvTPXc1Rph073uPxKNNNm1b6xCjTCrt0W3Z2tpYvX66srCyXcw8BAChSYRNiRESEatasqQULFig8PNzocADAJ5mpybTCJkRf+kMAAG9lpoTo04t7AwBQVipshQgAKAOerkfqQxUiCREA4JL9t1+enO8rSIgAAJfsdptHU2F8aRoNfYgAAIgKEQDghplGmZIQAQAumSkh0mQKAICoEAEAbpipQiQhAgDc8GyUqcQoUwAAfAoVIgDAJZpMAQCQTLV0G02mAACIChEA4IZdnq1H6jv1IQkRAOAGfYgAAIjFvQEAMB0qRACASzSZAgAgcyVEmkwBABAVIgDADSpEAAD0fwnRk+1KzJs3T40aNVJwcLDi4uL07bffujw2NTVVnTt3Vnh4uMLDw9WtWze3x7tCQgQAeJUVK1YoKSlJycnJ2r59u1q3bq3u3bvr5MmTxR6/fv16DRgwQOvWrdPmzZsVFRWlO++8Uz/99FOp7mux+1I9exVZrVaFhYUZHQYqFIvRAfgEX5q3ZpSi76dz584pNDS0XO9xQ0wn+ftfee9aQcEl7d23sVSxxsXF6aabbtLcuXMlSTabTVFRURo+fLjGjh1bgnsWKDw8XHPnzlV8fHyJY6VCBAC4ZC+DX1Jhgv39lpeXV+z98vPztW3bNnXr1s2xz8/PT926ddPmzZtLFPP58+d18eJF1ahRo1TvlYQIXDV2thJtqIiioqIUFhbm2KZOnVrscadOnVJBQYFq167ttL927drKysoq0b2eeeYZ1a1b1ymplgSjTAEALpXVKNPMzEynJtOgoCCPYyvOtGnTtHz5cq1fv17BwcGlOpeECABwqawSYmhoaIn6EGvWrCl/f3+dOHHCaf+JEycUGRnp9twZM2Zo2rRp+vLLL9WqVatSx0qTKQDApaLFvT3ZSiMwMFDt2rVTWlqaY5/NZlNaWpo6dOjg8ryXXnpJ//jHP7RmzRrFxsZe0XulQgQAeJWkpCQlJCQoNjZW7du315w5c5Sbm6vExERJUnx8vOrVq+foh/znP/+piRMnatmyZWrUqJGjrzEkJEQhISElvi8JEQDgkhEr1fTr10/Z2dmaOHGisrKy1KZNG61Zs8Yx0CYjI0N+fv/XwPnaa68pPz9fffv2dbpOcnKyJk2aVOL7Mg/RBeYhAsbgK+nPXc15iE2axHo8D/HQoa3lGmtZoQ8RAADRZAoAcMNMi3uTEAEArtkleZLUfCcf0mQKAIBEhQgAcMMum+weLExvl+8s1k5CBAC4ZKY+RJpMAQAQFSIAwC3PKkRfGlVDQgQAuGSmJlMSIgDApcIFuj0YVFPKxb2NRB8iAACiQgQAuEGTKQAAMldCpMkUAABRIQIA3LHbPVzL1HcqRBIiAMAl+2+/PDnfV9BkCgCAqBABAG6YaR4iCREA4JKZRpmSEAEALpkpIdKHCACAqBABAG6YqUIkIQIAXDJTQqTJFAAAUSECANworBCvfOqEL1WIJEQAgGsmWrqtXJtMLRZLsdvy5csdxxQUFGj27Nlq2bKlgoODFR4erh49emjjxo1O1yooKNC0adPUvHlzVa5cWTVq1FBcXJzeeOON8nwLAACTKPMK8cyZMwoICFBISIgkafHixbrrrrucjqlevbqkwlK6f//++vLLLzV9+nTdfvvtslqtmjdvnrp27ar33ntPvXv3liRNnjxZr7/+uubOnavY2FhZrVZt3bpVZ86ccVz32LFjioiIUKVKFL4AUBbMtJZpmWSOS5cu6bPPPtOSJUv08ccfa8uWLWrdurWkwuQXGRlZ7Hnvvvuu3n//fa1atUo9e/Z07F+wYIF+/vlnDR48WHfccYeqVq2qVatWaejQobrvvvscxxXdo0hqaqpee+01PfTQQ0pISFDLli3L4u0BgGkxyrSEdu/erdGjR6t+/fqKj49XrVq1tG7dussSlSvLli1T06ZNnZJhkdGjR+vnn3/WF198IUmKjIzU2rVrlZ2d7fJ6zzzzjFJSUpSenq4bb7xRN954o15++WW35xTJy8uT1Wp12gAA5lHqhPjzzz8rJSVFN954o2JjY3XkyBG9+uqrOn78uF599VV16NDB6fgBAwYoJCTEacvIyJAkHTx4UNHR0cXep2j/wYMHJUmzZs1Sdna2IiMj1apVK/3973/Xp59+6nROcHCw+vXrp9WrV+unn35SfHy8lixZonr16ql3795auXKlLl26VOz9pk6dqrCwMMcWFRVV2o8GACqcwsW9Pdt8RakT4iuvvKKRI0cqJCRE33//vVauXKk+ffooMDCw2ONnz56tnTt3Om1169Z1vF7ScjomJkZ79uzRN998o0GDBunkyZPq2bOnBg8eXOzxERERGjlypLZv365///vf2rx5s/r06aM9e/YUe/y4ceN07tw5x5aZmVmiuACgIitqMvVk8xWl7kN89NFHValSJb355pu64YYbdO+99+rhhx9W165d5ed3eX6NjIzU9ddfX+y1mjZtqvT09GJfK9rftGlTxz4/Pz/ddNNNuummmzRy5Ei9/fbbevjhh/Xcc8/p2muvdTo/JydH77//vt566y199dVX6tKlixISEhQTE1Ps/YKCghQUFFSizwAAzII+RDfq1q2r8ePH6+DBg1qzZo0CAwPVp08fNWzYUGPHjtXevXtLfK3+/fvr0KFD+vjjjy97bebMmbrmmmt0xx13uDy/KLnl5uZKKpya8emnn+qBBx5Q7dq1NW3aNN1+++06cuSI0tLSFB8f77KSBQCYm0eDajp27KjXX39dWVlZmj59unbu3KnWrVtr9+7djmPOnj2rrKwsp60ogfXv31/33HOPEhIStHDhQh09elS7du3SY489plWrVumNN95Q1apVJUl9+/bV7NmztWXLFv34449av369hg0bpqZNm6p58+aSpClTpmjAgAGqVq2avvzySx04cEDPPfecGjRo4MnbBADTMlOTqcVextEeO3ZMISEhCg0NlcVS/FOWp06dqrFjx0oqnLIxZ84cLVmyRIcOHVJwcLA6dOigCRMmqFOnTo5zUlNT9c4772jPnj06d+6cIiMjddttt2nSpElq2LChJOno0aOKjIxUcHCwx+/DarUqLCzM4+sAKB1f+gI1StH307lz5xQaGlqu9wgPj5TFcuW1k91u05kzWeUaa1kp84RYUZAQAWPwlfTnSIjlgyVdAACueTptwoemXZAQAQAuFS69Zo6l23geIgAAokIEALhR2KdrjnmIJEQAgEtmSog0mQIAICpEAIAbni7O7UuLe5MQAQAuFbZ4etJkWmahlDsSIgDAJU/7AOlDBADAx1AhAgBcMlOFSEIEALjmaULzoYRIkykAAKJCBAC4YZdNUvGP8ivZ+b5TIZIQAQAumakPkSZTAABEhQgAcMNMFSIJEQDgkpkSIk2mAACIChEA4IaZKkQSIgDApcKnVXgw7YKECACoCMxUIdKHCACAqBABAO6YaC1TEiIAwCVPl17zpaXbaDIFAEBUiAAANxhlCgCAGGUKAIDpUCG64Ev/qgEqEqvVanQIXq/oM7pa31Nm+T4kIbqQk5NjdAiAKYWFhRkdgs/Iyckpt88rMDBQkZGRysrK8vhakZGRCgwMLIOoypfFbpbUX0o2m03Hjh1TtWrVZLFceYdyWbJarYqKilJmZqZCQ0ONDsdr8TmVDJ9TyXjj52S325WTk6O6devKz6/8er4uXLig/Px8j68TGBio4ODgMoiofFEhuuDn56f69esbHUaxQkNDveZ/TG/G51QyfE4l422f09WopIODg30ikZUVBtUAACASIgAAkkiIPiUoKEjJyckKCgoyOhSvxudUMnxOJcPnZB4MqgEAQFSIAABIIiECACCJhAgAgCQSIgAAkkiIAABIIiECACCJhAgAgCQSIgAAkqT/Dz82QwJ9FeKgAAAAAElFTkSuQmCC" }, "metadata": {} } ] }, { "cell_type": "code", "source": [ "evaluateAndShowAttention('Olet minun isäni')" ], "metadata": { "execution": { "iopub.status.busy": "2024-05-25T14:46:13.190403Z", "iopub.execute_input": "2024-05-25T14:46:13.191025Z", "iopub.status.idle": "2024-05-25T14:46:13.613486Z", "shell.execute_reply.started": "2024-05-25T14:46:13.190997Z", "shell.execute_reply": "2024-05-25T14:46:13.612143Z" }, "trusted": true }, "execution_count": 46, "outputs": [ { "name": "stdout", "text": "input = olet minun isani\noutput = you re my father \n", "output_type": "stream" }, { "name": "stderr", "text": "/tmp/ipykernel_34/2052950992.py:8: UserWarning: FixedFormatter should only be used together with FixedLocator\n ax.set_xticklabels([''] + input_sentence.split(' ') +\n/tmp/ipykernel_34/2052950992.py:10: UserWarning: FixedFormatter should only be used together with FixedLocator\n ax.set_yticklabels([''] + output_words)\n", "output_type": "stream" }, { "output_type": "display_data", "data": { "text/plain": "
", "image/png": "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QDabTfv379ehQ4c0duxYvfXWW/LzYyno5oaE6CUqKir0+eefq1evXsyYrEF0dLT+/Oc/KzY21t2heLSafsRNJpNsNhuTan4kKytLb775psON+RMmTNCECRPcHBkaCwnRw50/f15paWlavXq1JOngwYPq2bOn7r33XnXr1k0zZ850c4Se4YMPPtDzzz+vpUuXKioqyt3heKzjx487fb9Hjx5NFAngeaj5PdzMmTP12WefKTc3V0FBQfb9iYmJysrKcmNkniUpKUm5ubnq1auX2rZtqw4dOjhsqNSjRw+nG6S//OUvqqiosL/+4osvHO7XPHfunJ599ll3hIZGRoXo4Xr06KGsrCxdfvnlatu2rT777DP17NlThw8f1mWXXSaz2ezuED1CVQVtJCUlpYki8Q779u1TQUGBww+/JN10001uishz/Pje35CQEO3cuVM9e/aUVDmjuWvXrnQvN0MMRnm40tLSajflS1JZWRkzKX+AhFc7R48e1S233KLdu3fbxw4l2f8u8SNf/d5fagbfQZeph6u6ibpK1Q/X8uXLNWzYMHeF5RF+WB2bzWanGyrdf//9uvTSS1VSUqLWrVtr79692rJli+Lj45Wbm+vu8AC3okL0cPPmzdOvfvUr7du3TxcvXtSLL76offv2adu2bfrHP/7h7vDcqn379vaurXbt2tVYMTN70lFeXp42bdqksLAw+fn5yc/PT1deeaUyMjJ033336dNPP3V3iIDbkBA93JVXXqmdO3dq/vz5GjRokD744ANddtllysvLM1xr0Vds2rTJPmFm8+bNbo7GO1gsFvujoMLCwvTll1+qX79+6tGjhw4cOODm6DzHxo0bFRoaKqnyvsScnBzt2bNHknTmzBk3RobGxKQaNBvfffeddu3aVeNTHJgsUmnEiBF68MEHNXbsWE2cOFGnT5/WY489pldffVX5+fn2H31fVpsb7ul1aJ5IiB6oLmNezhYh9iXZ2dlKTk6u9kBXiR+vH9q4caPKysr061//WocPH9aNN96ogwcPqmPHjsrKytI111zj7hABtyEheiA/P7+fnEHK2JijPn36aNSoUZozZ47Cw8PdHY5X+frrr9W+fXtmLf/AuXPndOTIkRqHJfbu3asePXo4LLiP5oGE6IHqMlnm6quvbsRIvEdISIg+/fRT9erVy92heBWz2axNmzapf//+PD3l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" }, "metadata": {} } ] }, { "cell_type": "code", "source": [ "evaluateAndShowAttention('Hän on opettaja')" ], "metadata": { "execution": { "iopub.status.busy": "2024-05-25T14:46:15.840433Z", "iopub.execute_input": "2024-05-25T14:46:15.841005Z", "iopub.status.idle": "2024-05-25T14:46:16.232003Z", "shell.execute_reply.started": "2024-05-25T14:46:15.840973Z", "shell.execute_reply": "2024-05-25T14:46:16.230365Z" }, "trusted": true }, "execution_count": 47, "outputs": [ { "name": "stdout", "text": "input = han on opettaja\noutput = he is a teacher \n", "output_type": "stream" }, { "name": "stderr", "text": "/tmp/ipykernel_34/2052950992.py:8: UserWarning: FixedFormatter should only be used together with FixedLocator\n ax.set_xticklabels([''] + input_sentence.split(' ') +\n/tmp/ipykernel_34/2052950992.py:10: UserWarning: FixedFormatter should only be used together with FixedLocator\n ax.set_yticklabels([''] + output_words)\n", "output_type": "stream" }, { "output_type": "display_data", "data": { "text/plain": "
", "image/png": "iVBORw0KGgoAAAANSUhEUgAAAcUAAAHOCAYAAADpBhJHAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuNSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/xnp5ZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAx5klEQVR4nO3deVxU9f7H8feAAioMriwqgpZL7lsalmmFWT2ybDUqF8qysl8p9TC95VqJWZlZloqa2i21bLmWSwuKXdMs8Wq5pOYGWihKikJCMvP7g2Fuc8UjBnhmOK8nj/O4zpmzfObE5TOf7/l+v8fmdDqdAgAA8jM7AAAAvAVJEQAAF5IiAAAuJEUAAFxIigAAuJAUAQBwISkCAOBCUgQAwIWkCACAC0kRAAAXkiIAAC4kRQAAXKqYHQAAcyxZskQffPCB0tPTVVBQ4PHepk2bTIoKMBeVImBB06ZNU0JCgsLDw/Wf//xHXbp0UZ06dbR3717deOONZocHmMbGo6MA62nRooXGjh2r+Ph4hYSEaMuWLWrSpInGjBmj7Oxsvfnmm2aHCJiCShGwoPT0dHXr1k2SVK1aNZ08eVKS1L9/fy1cuNDM0ABTkRQBC4qIiFB2drYkqVGjRvruu+8kSfv27RONR7AykiJgQddee62WLl0qSUpISNDw4cPVq1cv9evXT7fddpvJ0QHm4Z4iYEEOh0MOh0NVqhR1QF+0aJHWrVunpk2basiQIQoICDA5QsAcJEUAOI/CwkJt27ZNLVu2dH+RQOXEf13AIn788Ue1bt1afn5++vHHHw23DQ4OVlRUlKpWrXqRovNun332me644w4tWLBA9913n9nhoAJRKQIW4efnp8zMTIWFhcnPz082m82wU01oaKhmzJihfv36XcQovdNtt92m9evXq02bNvrqq6/MDgcViKQIWMSBAwfUqFEj2Ww2HThwwHDb/Px8ffjhh0pOTtb+/fsvToBe6ujRo2rYsKE+/fRT3XLLLdq7d68aNmxodlioIDSfAhYRHR1d4r/P5bHHHlNaWlpFhuQTFi5cqNatW+uGG25Q9+7d9e6772rUqFFmh4UKQqUIWFheXl6Jc5+2bdvWpIi8T6dOnTRw4EA98cQTeueddzR58mTt2LHD7LBQQUiKgAVlZWUpISFBK1asKPH9wsLCixyRd9q6das6deqkQ4cOqW7dujp16pTCw8O1atUqde3a1ezwUAEYvA9Y0LBhw3T8+HFt2LBB1apV08qVKzV//nw1bdrUPagf0vz583X99derbt26kop65fbt21fz5s0zNzBUGCpFwIIiIyP1r3/9S126dJHdbtfGjRvVrFkzLV26VJMnT9batWvNDtF0hYWFatiwoaZNm6a77rrLvX7FihW67777lJmZySQHlRCVImBBubm5CgsLkyTVqlVLWVlZkqQ2bdrwLEWXI0eO6NFHH9Wtt97qsb53795KTExUZmamSZGhIlEpAhZ0+eWX64UXXlDv3r11yy23qGbNmkpKStK0adO0ZMkS7dmzx+wQAVOQFAEL+uc//6kzZ85o0KBBSktL0w033KDs7GwFBARo3rx5DNg/hwMHDig3N1ctWrSQnx8NbZURSRGA8vLy9PPPP6tRo0buTiVWNnfuXB0/flyJiYnudQ8//LDmzJkjSWrevLm++OILRUVFmRUiKghfdQALmjBhgvLy8tyvq1evro4dO6pGjRqaMGGCiZF5h1mzZqlWrVru1ytXrtQ777yjBQsW6IcfflDNmjU1fvx4EyNERaFSBCzI399fv/32m7uzTbFjx44pLCzM8uMU69Spo9TUVLVp00aS9OijjyorK0tLliyRJKWmpiohIUH79u0zM0xUACpFwIKcTqdsNttZ67ds2aLatWubEJF3+eOPP2S3292v161bp6uvvtr9ukmTJvQ+raSY+xSwkFq1aslms8lms6lZs2YeibGwsFCnTp3SI488YmKE3iE6OlppaWmKjo7W0aNHtW3bNl155ZXu9zMzMxUaGmpihKgoJEXAQqZOnSqn06kHHnhA48eP9/jDHhAQoJiYGMXGxpoYoXcYOHCghg4dqm3btmnVqlVq0aKFOnXq5H5/3bp1at26tYkRoqKQFAELGThwoCSpcePGuvLKK3mK/DmMGDFCeXl5+vjjjxUREaEPP/zQ4/1vv/1W8fHxJkWHikRHG8CC6GgDlIyviYAFneu7cH5+PvN5/sUff/yhr776Srt27ZIkNWvWTL169VK1atVMjgwVhaQIWMi0adMkSTabTbNnz1ZwcLD7vcLCQn3zzTdq0aKFWeF5laVLl2rw4ME6evSox/q6detqzpw56tOnj0mRoSLRfApYSOPGjSUVTVfWsGFD+fv7u98r7mgzYcIEyz8rcN26derZs6duueUW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AAALolIEABjieYoAALhYKSnSfAoAgAuVIgDAkJUqRZIiAMCYU1JZk5pv5ESaTwEAKEalCAAw5JRDTpVt8hKnfGOcIkkRAGDISvcUaT4FAMCFShEAcB5lrxR9pacNSREAYMhKzackRQCAoaIJwcvY0YYJwQEA8C1UigAAQzSfAgDgYqWkSPMpAAAuVIoAAGNOZznMfeoblSJJEQBgyOn6KesxfAHNpwAAuFApAgAMWWmcIkkRAGDISr1PSYoAAENWSorcUwQAwIVKEQBgyEqVIkkRAGDISkmR5lMAAFyoFAEAhooqxbINqfCVSpGkCAAwZqFp3iq8+dRms5W4LFq0yL1NYWGhXnvtNbVp00ZBQUGqVauWbrzxRn377bcexyosLNSkSZPUokULVatWTbVr11bXrl01e/bsiv4YAAALqJBK8ffff1fVqlUVHBwsSXrnnXd0ww03eGxTs2ZNSUUl9T333KOvv/5aL7/8sq677jrl5ORo+vTp6tmzpz788EP17dtXkjR+/HjNnDlTb775pjp37qycnBxt3LhRv//+u/u4v/76q8LCwlSlCkUwAJQHK819Wm6Z48yZM/riiy80b948ffbZZ9qwYYPatWsnqSgBRkRElLjfBx98oCVLlmjp0qXq06ePe/2sWbN07NgxDR48WL169VKNGjW0dOlSPfbYY7rrrrvc2xWfo1hycrLefvtt3X///Ro4cKDatGlTXh8RACyJ3qcX4KefftJTTz2lhg0basCAAapXr55Wr159VrI6l/fff1/NmjXzSIjFnnrqKR07dkxfffWVJCkiIkKrVq1SVlbWOY/3zDPP6PXXX9eOHTvUsWNHdezYUdOmTTPc56/y8/OVk5PjsQAArOFvJcVjx47p9ddfV8eOHdW5c2ft3btXb731ln777Te99dZbio2N9dg+Pj5ewcHBHkt6erokadeuXbrssstKPE/x+l27dkmSpkyZoqysLEVERKht27Z65JFHtGLFCo99goKC1K9fPy1btkyHDh3SgAEDNG/ePDVo0EB9+/bVJ598ojNnzpzzsyUlJSk0NNS9REVF/Z1LBACVRtGE4GVffMHfSopvvPGGhg0bpuDgYP3yyy/65JNPdPvttysgIKDE7V977TVt3rzZY6lfv777/dKW1S1bttTWrVv13Xff6YEHHtCRI0fUp08fDR48uMTtw8LCNGzYMG3atEn/+te/tH79et1+++3aunXrOc8xatQonThxwr1kZGSUKjYAqKyKm0/LuviCv3VP8eGHH1aVKlW0YMECtWrVSnfccYf69++vnj17ys/v7DwbERGhSy+9tMRjNWvWTDt27CjxveL1zZo1c6/z8/PT5Zdfrssvv1zDhg3TP//5T/Xv31/PPvusGjdu7LH/yZMntWTJEr377rv65ptv1KNHDw0cOFAtW7Y852cLDAxUYGDgea8BAFgF9xTPo379+nruuee0a9curVy5UgEBAbr99tsVHR2tkSNHatu2baU+1j333KPdu3frs88+O+u9V199VXXq1FGvXr3OuX9xgsvNzZVUNGxjxYoVuvfeexUeHq5Jkybpuuuu0969e5WSkqIBAwacs6IFAFhbmTvadOvWTTNnzlRmZqZefvllbd68We3atdNPP/3k3ub48ePKzMz0WIqT2D333KPbbrtNAwcO1Jw5c7R//379+OOPGjJkiJYuXarZs2erRo0akqQ777xTr732mjZs2KADBw4oNTVVQ4cOVbNmzdSiRQtJ0sSJExUfH6+QkBB9/fXX2rlzp5599lk1atSorB8VACzJSs2nNmcFRPrrr78qODhYdrtdNlvJT2tOSkrSyJEjJRUN55g6darmzZun3bt3KygoSLGxsRo9erSuvPJK9z7JyclauHChtm7dqhMnTigiIkLXXnutxo0bp+joaEnS/v37FRERoaCgoHL5LDk5OQoNDZVkO+dnQRFfuZFutmrVQswOwSfk5dHz+3yK/z6dOHFCdru9wo5fq1aEbLay1VBOp0O//55ZYbGWlwpJipUJSbH0SIqlQ1IsHZLi+ZEUyx/TvgAAjJXHF14f+dJMUgQAGCqaos0a07zxPEUAAFyoFAEAhoq6nlhjnCJJEQBgyEpJkeZTAABcqBQBAIbKY7iVrwzZIikCAAwVtXyWtfm0XEKpcCRFAICh8rgfyD1FAAB8DJUiAMCQlSpFkiIAwFh5JDQfSYo0nwIA4EKlCAAw5JRDUtmeEuQrc5+SFAEAhqx0T5HmUwAAXKgUAQCGrFQpkhQBAIaslBRpPgUAwIVKEQBgyEqVIkkRAGCo6AkXZRySQVIEAFQGVqoUuacIAIALlSIAwJiF5j4lKQIADJXHFG2+Ms0bzacAALhQKQIADNH7FAAAF3qfAgBgQVSK5/HfbzdOX+k8BS/nK9+YzZaTk2N2CF6v+BpdjN8pq/zekhTP4+TJk395ZY1fClSs06dPmR2CTwgNDTU7BJ9x8uTJCrleAQEBioiIUGZmZrkcLyIiQgEBAeVyrIpic1ol/f9NDodDv/76q0JCQmSzle1Gc3nJyclRVFSUMjIyZLfbzQ7Ha3GdSofrVDreeJ2cTqdOnjyp+vXry8+vYu6GnT59WgUFBeVyrICAAAUFBZXLsSoKleJ5+Pn5qWHDhmaHUSK73e41/+f0Zlyn0uE6lY63XaeKrqiDgoK8PpGVJzraAADgQlIEAMCFpOiDAgMDNXbsWAUGBpodilfjOpUO16l0uE7WQEcbAABcqBQBAHAhKQIA4EJSBADAhaQIAIALSREAABeSIgAALiRFAABcSIoAALj8P/bLSA8+8gedAAAAAElFTkSuQmCC" }, "metadata": {} } ] }, { "cell_type": "code", "source": [ "torch.save(encoder.state_dict(), \"encoder.pt\")\n", "torch.save(decoder.state_dict(), \"decoder.pt\")" ], "metadata": { "execution": { "iopub.status.busy": "2024-05-25T14:50:24.773506Z", "iopub.execute_input": "2024-05-25T14:50:24.774464Z", "iopub.status.idle": "2024-05-25T14:50:24.795895Z", "shell.execute_reply.started": "2024-05-25T14:50:24.774430Z", "shell.execute_reply": "2024-05-25T14:50:24.794979Z" }, "trusted": true }, "execution_count": 48, "outputs": [] }, { "cell_type": "markdown", "source": [ "# BLEU score" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "Jako że korzystaliśmy z okrojonej wersji zbioru danych, słownik nie zawiera wszystkich słów pojawiających się w przykładach więc do ewaluacji wykorzystujemy część przykładów z treningu" ], "metadata": {} }, { "cell_type": "code", "source": [ "import pandas as pd\n", "\n", "\n", "def filter_rows(row):\n", " return len(row[\"English\"].split(' '))\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
EnglishFinnishattribution
38027i m very serious about thisolen hyvin tosissani tastaCC-BY 2.0 (France) Attribution: tatoeba.org #2...
3803i m not tireden ole vasynytCC-BY 2.0 (France) Attribution: tatoeba.org #1...
26924i m not married eitherminakaan en ole naimisissaCC-BY 2.0 (France) Attribution: tatoeba.org #6...
32009he s sleeping like a babyhan nukkuu kuin pikkuvauvaCC-BY 2.0 (France) Attribution: tatoeba.org #2...
21339i m joking of coursese oli vitsi tietenkinCC-BY 2.0 (France) Attribution: tatoeba.org #2...
\n" }, "metadata": {} } ] }, { "cell_type": "code", "source": [ "test_section[\"English_tokenized\"] = test_section[\"English\"].apply(lambda x: x.split())" ], "metadata": { "execution": { "iopub.status.busy": "2024-05-25T15:29:07.110416Z", "iopub.execute_input": "2024-05-25T15:29:07.110816Z", "iopub.status.idle": "2024-05-25T15:29:07.117378Z", "shell.execute_reply.started": "2024-05-25T15:29:07.110786Z", "shell.execute_reply": "2024-05-25T15:29:07.116136Z" }, "trusted": true }, "execution_count": 96, "outputs": [] }, { "cell_type": "code", "source": [ "test_section.head()[\"English_tokenized\"]" ], "metadata": { "execution": { "iopub.status.busy": "2024-05-25T15:29:10.203170Z", "iopub.execute_input": "2024-05-25T15:29:10.203540Z", "iopub.status.idle": "2024-05-25T15:29:10.212993Z", "shell.execute_reply.started": "2024-05-25T15:29:10.203511Z", "shell.execute_reply": "2024-05-25T15:29:10.211937Z" }, "trusted": true }, "execution_count": 97, "outputs": [ { "execution_count": 97, "output_type": "execute_result", "data": { "text/plain": "38027 [i, m, very, serious, about, this]\n3803 [i, m, not, tired]\n26924 [i, m, not, married, either]\n32009 [he, s, sleeping, like, a, baby]\n21339 [i, m, joking, of, course]\nName: English_tokenized, dtype: object" }, "metadata": {} } ] }, { "cell_type": "code", "source": [ "test_section[\"English_translated\"] = test_section[\"Finnish\"].apply(lambda x: translate(x, tokenized=True))" ], "metadata": { "execution": { "iopub.status.busy": "2024-05-25T15:30:53.183117Z", "iopub.execute_input": "2024-05-25T15:30:53.183937Z", "iopub.status.idle": "2024-05-25T15:30:56.313012Z", "shell.execute_reply.started": "2024-05-25T15:30:53.183902Z", "shell.execute_reply": "2024-05-25T15:30:56.312202Z" }, "trusted": true }, "execution_count": 100, "outputs": [] }, { "cell_type": "code", "source": [ "test_section.head()" ], "metadata": { "execution": { "iopub.status.busy": "2024-05-25T15:31:06.745381Z", "iopub.execute_input": "2024-05-25T15:31:06.746471Z", "iopub.status.idle": "2024-05-25T15:31:06.771839Z", "shell.execute_reply.started": "2024-05-25T15:31:06.746417Z", "shell.execute_reply": "2024-05-25T15:31:06.770679Z" }, "trusted": true }, "execution_count": 101, "outputs": [ { "execution_count": 101, "output_type": "execute_result", "data": { "text/plain": " English ... English_translated\n38027 i m very serious about this ... [i, m, in, french]\n3803 i m not tired ... [i, not, tired]\n26924 i m not married either ... [i, m, not, married, either]\n32009 he s sleeping like a baby ... [he, is, as, a, pianist]\n21339 i m joking of course ... [i, m, joking, of, course]\n\n[5 rows x 5 columns]", "text/html": "
\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
EnglishFinnishattributionEnglish_tokenizedEnglish_translated
38027i m very serious about thisolen hyvin tosissani tastaCC-BY 2.0 (France) Attribution: tatoeba.org #2...[i, m, very, serious, about, this][i, m, in, french]
3803i m not tireden ole vasynytCC-BY 2.0 (France) Attribution: tatoeba.org #1...[i, m, not, tired][i, not, tired]
26924i m not married eitherminakaan en ole naimisissaCC-BY 2.0 (France) Attribution: tatoeba.org #6...[i, m, not, married, either][i, m, not, married, either]
32009he s sleeping like a babyhan nukkuu kuin pikkuvauvaCC-BY 2.0 (France) Attribution: tatoeba.org #2...[he, s, sleeping, like, a, baby][he, is, as, a, pianist]
21339i m joking of coursese oli vitsi tietenkinCC-BY 2.0 (France) Attribution: tatoeba.org #2...[i, m, joking, of, course][i, m, joking, of, course]
\n
" }, "metadata": {} } ] }, { "cell_type": "code", "source": [ "candidate_corpus = test_section[\"English_translated\"].values\n", "references_corpus = test_section[\"English_tokenized\"].values.tolist()\n", "x = candidate_corpus.tolist()\n", "y = [[el] for el in references_corpus]\n", "#print(references_corpus[:5])\n", "#print(candidate_corpus[:5])" ], "metadata": { "execution": { "iopub.status.busy": "2024-05-25T15:43:29.441911Z", "iopub.execute_input": "2024-05-25T15:43:29.442752Z", "iopub.status.idle": "2024-05-25T15:43:29.447799Z", "shell.execute_reply.started": "2024-05-25T15:43:29.442721Z", "shell.execute_reply": "2024-05-25T15:43:29.446877Z" }, "trusted": true }, "execution_count": 118, "outputs": [] }, { "cell_type": "code", "source": [ "y[:5]" ], "metadata": { "execution": { "iopub.status.busy": "2024-05-25T15:43:30.474463Z", "iopub.execute_input": "2024-05-25T15:43:30.475080Z", "iopub.status.idle": "2024-05-25T15:43:30.482690Z", "shell.execute_reply.started": "2024-05-25T15:43:30.475039Z", "shell.execute_reply": "2024-05-25T15:43:30.481686Z" }, "trusted": true }, "execution_count": 119, "outputs": [ { "execution_count": 119, "output_type": "execute_result", "data": { "text/plain": "[[['i', 'm', 'very', 'serious', 'about', 'this']],\n [['i', 'm', 'not', 'tired']],\n [['i', 'm', 'not', 'married', 'either']],\n [['he', 's', 'sleeping', 'like', 'a', 'baby']],\n [['i', 'm', 'joking', 'of', 'course']]]" }, "metadata": {} } ] }, { "cell_type": "code", "source": [ "from torchtext.data.metrics import bleu_score\n", "\n", "bleu_score(x, y)" ], "metadata": { "execution": { "iopub.status.busy": "2024-05-25T15:43:36.654035Z", "iopub.execute_input": "2024-05-25T15:43:36.654953Z", "iopub.status.idle": "2024-05-25T15:43:36.916617Z", "shell.execute_reply.started": "2024-05-25T15:43:36.654906Z", "shell.execute_reply": "2024-05-25T15:43:36.915429Z" }, "trusted": true }, "execution_count": 120, "outputs": [ { "execution_count": 120, "output_type": "execute_result", "data": { "text/plain": "0.5885258316993713" }, "metadata": {} } ] } ] }