{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "improved-register",
   "metadata": {},
   "source": [
    "![Logo 1](https://git.wmi.amu.edu.pl/AITech/Szablon/raw/branch/master/Logotyp_AITech1.jpg)\n",
    "<div class=\"alert alert-block alert-info\">\n",
    "<h1> Komputerowe wspomaganie tłumaczenia </h1>\n",
    "<h2> 8. <i>Wykorzystanie tłumaczenia automatycznego we wspomaganiu tłumaczenia</i> [laboratoria]</h2> \n",
    "<h3>Rafał Jaworski (2021)</h3>\n",
    "</div>\n",
    "\n",
    "![Logo 2](https://git.wmi.amu.edu.pl/AITech/Szablon/raw/branch/master/Logotyp_AITech2.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "hungarian-davis",
   "metadata": {},
   "source": [
    "W dzisiejszych czasach, niezwykle ważną techniką wspomagania tłumaczenia jest użycie tłumaczenia maszynowego. Tekst źródłowy do tłumaczenia jest najpierw tłumaczony całkowicie autommatycznie, a następnie tłumacz ludzki dokonuje korekty wyniku. Technologia tłumaczenia maszynowego jest już na tyle dojrzała, że oferuje bardzo wysoką jakość wyników. Coraz częstsze stają się scenariusze, w których ludzka korekta to niemal całkowicie machinalne (sic!) zatwierdzanie wyników tłumaczenia maszynowego. Na dzisiejszych zajęciach poznamy techniki ewaluacji tłumaczenia maszynowego oraz sprawdzania jego przydatności w procesie wspomagania tłumaczenia ludzkiego."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "posted-commons",
   "metadata": {},
   "source": [
    "Jakość tłumaczenia maszynowego możemy oceniać na dwóch niezależnych płaszczyznach: dokładność i płynność. Płynność jest subiektywnie odbieranym odczuciem, że czytany tekst jest napisany językiem naturalnym i zrozumiałym. Systemy tłumaczenia maszynowego oparte na uczeniu głębokim z wykorzystaniem sieci neuronowych osiągają duży stopień płynności tłumaczenia. Niestety jednak ich dokładność nie zawsze jest równie wysoka."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "referenced-implement",
   "metadata": {},
   "source": [
    "Dokładność tłumaczenia maszynowego jest parametrem, który łatwiej zmierzyć. Wartość takich pomiarów daje obraz tego, jaka jest faktyczna jakość tłumaczenia maszynowego i jaka jest jego potencjalna przydatność we wspomaganiu tłumaczenia."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "disturbed-january",
   "metadata": {},
   "source": [
    "Najczęściej stosowaną techniką oceny tłumaczenia maszynowego jest ocena BLEU. Do obliczenia tej oceny potrzebny jest wynik tłumaczenia maszynowego oraz referencyjne tłumaczenie ludzkie wysokiej jakości."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dental-combination",
   "metadata": {},
   "source": [
    "### Ćwiczenie 1: Zaimplementuj program do obliczania oceny BLEU dla korpusu w folderze data. Użyj implementacji BLEU z pakietu nltk. Dodatkowe wymaganie techniczne - napisz program tak, aby nie musiał rozpakwowywać pliku zip z korpusem na dysku."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "d4f068df",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/opt/anaconda3/lib/python3.11/site-packages/nltk/translate/bleu_score.py:552: UserWarning: \n",
      "The hypothesis contains 0 counts of 3-gram overlaps.\n",
      "Therefore the BLEU score evaluates to 0, independently of\n",
      "how many N-gram overlaps of lower order it contains.\n",
      "Consider using lower n-gram order or use SmoothingFunction()\n",
      "  warnings.warn(_msg)\n",
      "/opt/anaconda3/lib/python3.11/site-packages/nltk/translate/bleu_score.py:552: UserWarning: \n",
      "The hypothesis contains 0 counts of 4-gram overlaps.\n",
      "Therefore the BLEU score evaluates to 0, independently of\n",
      "how many N-gram overlaps of lower order it contains.\n",
      "Consider using lower n-gram order or use SmoothingFunction()\n",
      "  warnings.warn(_msg)\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "3.984587822441638e-156"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import zipfile\n",
    "import nltk.translate.bleu_score as bleu\n",
    "import string\n",
    "\n",
    "def remove_punctuation(text):\n",
    "    text_without_punctuations = text.translate(str.maketrans('', '', string.punctuation))\n",
    "    sentences = text_without_punctuations.split('\\n')\n",
    "    return [[word.lower() for word in sentence.split()] for sentence in sentences if sentence != '']\n",
    "\n",
    "def calculate_bleu():\n",
    "    zip = zipfile.ZipFile('data/corpus_corrected.zip')\n",
    "    files = {name: remove_punctuation(zip.read(name).decode('utf-8'))\n",
    "            for name in zip.namelist()}\n",
    "        \n",
    "    corpus_de_human, corpus_de_nmt = files['corpus_de_human.txt'], files['corpus_de_nmt.txt']\n",
    "    \n",
    "    return bleu.corpus_bleu(corpus_de_human, corpus_de_nmt)\n",
    "\n",
    "calculate_bleu()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "jewish-ethics",
   "metadata": {},
   "source": [
    "### Ćwiczenie 2: Oblicz wartość bleu na różnych fragmentach przykładowego korpusu (np. na pierwszych 100 zdaniach, zdaniach 500-600). Czy w jakimś fragmencie korpusu jakość tłumaczenia znacząco odbiega od średniej?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "lasting-rolling",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0 to 100 - 4.97555004481153e-232\n",
      "500 to 600 - 5.956707985683837e-232\n",
      "800 to 900 - 4.774461089627919e-232\n",
      "200 to 300 - 5.56331772444502e-232\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/opt/anaconda3/lib/python3.11/site-packages/nltk/translate/bleu_score.py:552: UserWarning: \n",
      "The hypothesis contains 0 counts of 2-gram overlaps.\n",
      "Therefore the BLEU score evaluates to 0, independently of\n",
      "how many N-gram overlaps of lower order it contains.\n",
      "Consider using lower n-gram order or use SmoothingFunction()\n",
      "  warnings.warn(_msg)\n"
     ]
    }
   ],
   "source": [
    "\n",
    "def analyze_bleu(start_sentence_index, finish_sentence_index):\n",
    "    zip = zipfile.ZipFile('data/corpus_corrected.zip')\n",
    "    files = {name: remove_punctuation(zip.read(name).decode('utf-8'))\n",
    "            for name in zip.namelist()}\n",
    "        \n",
    "    corpus_de_human, corpus_de_nmt = files['corpus_de_human.txt'][start_sentence_index:finish_sentence_index], files['corpus_de_nmt.txt'][start_sentence_index:finish_sentence_index]\n",
    "    \n",
    "    return bleu.corpus_bleu(corpus_de_human, corpus_de_nmt)\n",
    "\n",
    "\n",
    "print(\"0 to 100 - \"+str(analyze_bleu(0, 100)))\n",
    "print(\"500 to 600 - \"+str(analyze_bleu(500, 600)))\n",
    "print(\"800 to 900 - \"+str(analyze_bleu(800, 900)))\n",
    "print(\"200 to 300 - \"+str(analyze_bleu(200, 300)))\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "listed-bikini",
   "metadata": {},
   "source": [
    "Inną metodą oceny jakości tłumaczenia maszynowego jest parametr WER - Word Error Rate. Definiuje się on w następujący sposób:\n",
    "\n",
    "$WER = \\frac{S+D+I}{N}=\\frac{S+D+I}{S+D+C}$\n",
    "\n",
    "gdzie:\n",
    " * S - liczba substytucji (słów)\n",
    " * D - liczba usunięć\n",
    " * I - liczba wstawień\n",
    " * C - liczba poprawnych śłów\n",
    " * N - liczba słów w tłumaczeniu referencyjnym (N=S+D+C)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fb4f02ae",
   "metadata": {},
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "conscious-cookbook",
   "metadata": {},
   "source": [
    "Miara ta jest zwykle używana w do oceny systemów automatycznego rozpoznawania mowy, jednak w kontekście wspomagania tłumaczenia może być rozumiana jako wielkość nakładu pracy tłumacza nad poprawieniem tłumaczenia maszynowego."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "split-palace",
   "metadata": {},
   "source": [
    "### Ćwiczenie 3: Oblicz wartość WER dla przykładowego korpusu. Skorzystaj z gotowej implementacji WER."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "occupied-swing",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.17355216569308377"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from jiwer import wer\n",
    "import zipfile\n",
    "\n",
    "def calculate_wer():\n",
    "    ourZip = zipfile.ZipFile('data/corpus_corrected.zip')\n",
    "    files = {name: remove_punctuation(ourZip.read(name).decode('utf-8'))\n",
    "            for name in ourZip.namelist()}\n",
    "        \n",
    "    corpus_de_human, corpus_de_nmt = files['corpus_de_human.txt'], files['corpus_de_nmt.txt']\n",
    "\n",
    "    sum_wer = 0\n",
    "    for human_sent, nmt_sent in zip(corpus_de_human, corpus_de_nmt):\n",
    "        sum_wer+= wer(\" \".join(human_sent), \" \".join(nmt_sent))\n",
    "\n",
    "    return sum_wer/(len(corpus_de_human))\n",
    "\n",
    "calculate_wer()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "stretch-wound",
   "metadata": {},
   "source": [
    "Poza wymienionymi powyżej, stosować można jeszcze inne miary oparte na porównywaniu tłumaczenia maszynowego z ludzkim. Przypomnijmy sobie jedną, którą stosowaliśmy wcześniej."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "abstract-wilderness",
   "metadata": {},
   "source": [
    "### Ćwiczenie 4: Oblicz średnią wartość dystansu Levenshteina pomiędzy zdaniami przetłumaczonymi automatycznie oraz przez człowieka. Użyj implementacji z ćwiczeń nr 2."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "id": "immediate-element",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2.653"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import Levenshtein\n",
    "\n",
    "def calculate_levenshtein():\n",
    "    ourZip = zipfile.ZipFile('data/corpus_corrected.zip')\n",
    "    files = {name: remove_punctuation(ourZip.read(name).decode('utf-8'))\n",
    "            for name in ourZip.namelist()}\n",
    "        \n",
    "    corpus_de_human, corpus_de_nmt = files['corpus_de_human.txt'], files['corpus_de_nmt.txt']\n",
    "\n",
    "    sum_disatnce = 0\n",
    "    for human_element, nmt_element in zip(corpus_de_human, corpus_de_nmt):\n",
    "        sum_disatnce+=  Levenshtein.distance(human_element, nmt_element)\n",
    "\n",
    "    return sum_disatnce/(len(corpus_de_human))\n",
    "\n",
    "calculate_levenshtein()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "filled-burton",
   "metadata": {},
   "source": [
    "A teraz sprawdźmy coś jeszcze. W danych przykładowego korpusu znajduje się także angielski tekst źródłowy. Teoretycznie, dobre tłumaczenie niemieckie powinno zawierać jak najwięcej słów z angielskiego źródła. Wykonajmy najstępujący eksperyment:"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "grateful-recruitment",
   "metadata": {},
   "source": [
    "### Ćwiczenie 5: Dla każdej trójki zdań z korpusu przykładowego wykonaj następujące kroki:\n",
    " * Przetłumacz każde angielskie słowo na niemiecki przy użyciu modułu PyDictionary.\n",
    " * Sprawdź, które z niemieckich tłumaczeń zawiera więcej spośród tych przetłumaczonych słów - automatyczne, czy ludzkie.\n",
    "Następnie wypisz statystyki zbiorcze. Które tłumaczenie zawiera więcej słownikowych tłumaczeń słów ze źródła?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "descending-easter",
   "metadata": {},
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'remove_punctuation' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "Cell \u001b[0;32mIn[1], line 45\u001b[0m\n\u001b[1;32m     38\u001b[0m     \u001b[38;5;28mprint\u001b[39m(human_sum)\n\u001b[1;32m     41\u001b[0m         \u001b[38;5;66;03m# tranlsations = [PyDictionary().translate(word, 'de') for word in element]\u001b[39;00m\n\u001b[0;32m---> 45\u001b[0m analyze_translations()\n",
      "Cell \u001b[0;32mIn[1], line 19\u001b[0m, in \u001b[0;36manalyze_translations\u001b[0;34m()\u001b[0m\n\u001b[1;32m     17\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21manalyze_translations\u001b[39m():\n\u001b[1;32m     18\u001b[0m     ourZip \u001b[38;5;241m=\u001b[39m zipfile\u001b[38;5;241m.\u001b[39mZipFile(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mdata/corpus_corrected.zip\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[0;32m---> 19\u001b[0m     files \u001b[38;5;241m=\u001b[39m {name: remove_punctuation(ourZip\u001b[38;5;241m.\u001b[39mread(name)\u001b[38;5;241m.\u001b[39mdecode(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mutf-8\u001b[39m\u001b[38;5;124m'\u001b[39m))\n\u001b[1;32m     20\u001b[0m             \u001b[38;5;28;01mfor\u001b[39;00m name \u001b[38;5;129;01min\u001b[39;00m ourZip\u001b[38;5;241m.\u001b[39mnamelist()}\n\u001b[1;32m     22\u001b[0m     corpus_de_human, corpus_de_nmt, corpus_en \u001b[38;5;241m=\u001b[39m files[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mcorpus_de_human.txt\u001b[39m\u001b[38;5;124m'\u001b[39m], files[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mcorpus_de_nmt.txt\u001b[39m\u001b[38;5;124m'\u001b[39m], files[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mcorpus_en.txt\u001b[39m\u001b[38;5;124m'\u001b[39m]\n\u001b[1;32m     24\u001b[0m     nmt_sum \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m0\u001b[39m\n",
      "Cell \u001b[0;32mIn[1], line 19\u001b[0m, in \u001b[0;36m<dictcomp>\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m     17\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21manalyze_translations\u001b[39m():\n\u001b[1;32m     18\u001b[0m     ourZip \u001b[38;5;241m=\u001b[39m zipfile\u001b[38;5;241m.\u001b[39mZipFile(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mdata/corpus_corrected.zip\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[0;32m---> 19\u001b[0m     files \u001b[38;5;241m=\u001b[39m {name: remove_punctuation(ourZip\u001b[38;5;241m.\u001b[39mread(name)\u001b[38;5;241m.\u001b[39mdecode(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mutf-8\u001b[39m\u001b[38;5;124m'\u001b[39m))\n\u001b[1;32m     20\u001b[0m             \u001b[38;5;28;01mfor\u001b[39;00m name \u001b[38;5;129;01min\u001b[39;00m ourZip\u001b[38;5;241m.\u001b[39mnamelist()}\n\u001b[1;32m     22\u001b[0m     corpus_de_human, corpus_de_nmt, corpus_en \u001b[38;5;241m=\u001b[39m files[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mcorpus_de_human.txt\u001b[39m\u001b[38;5;124m'\u001b[39m], files[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mcorpus_de_nmt.txt\u001b[39m\u001b[38;5;124m'\u001b[39m], files[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mcorpus_en.txt\u001b[39m\u001b[38;5;124m'\u001b[39m]\n\u001b[1;32m     24\u001b[0m     nmt_sum \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m0\u001b[39m\n",
      "\u001b[0;31mNameError\u001b[0m: name 'remove_punctuation' is not defined"
     ]
    }
   ],
   "source": [
    "from PyDictionary import PyDictionary\n",
    "import zipfile\n",
    "import re\n",
    "\n",
    "def transalate(word_list):\n",
    "     transalted_words = {}\n",
    "     for word in word_list:\n",
    "        try:\n",
    "            translation = PyDictionary().translate(word, 'German')\n",
    "            if translation:\n",
    "                transalted_words[word] = translation\n",
    "        except Exception as e:\n",
    "            print('Exception')\n",
    "\n",
    "     return transalted_words\n",
    "\n",
    "def analyze_translations():\n",
    "    ourZip = zipfile.ZipFile('data/corpus_corrected.zip')\n",
    "    files = {name: remove_punctuation(ourZip.read(name).decode('utf-8'))\n",
    "            for name in ourZip.namelist()}\n",
    "        \n",
    "    corpus_de_human, corpus_de_nmt, corpus_en = files['corpus_de_human.txt'], files['corpus_de_nmt.txt'], files['corpus_en.txt']\n",
    "\n",
    "    nmt_sum = 0\n",
    "    human_sum = 0\n",
    "\n",
    "    for human_element, nmt_element, element  in zip(corpus_de_human, corpus_de_nmt, corpus_en):\n",
    "        transalted_words = transalate(element)\n",
    "\n",
    "        # words = set(re.findall(r'\\w+', nmt_element.lower()))\n",
    "        nmt_sum += sum(1 for word in nmt_element if transalted_words.get(word.lower()))\n",
    "\n",
    "        # words = set(re.findall(r'\\w+', human_element.lower()))\n",
    "        human_sum += sum(1 for word in human_element if transalted_words.get(word.lower()))\n",
    "\n",
    "\n",
    "    print(nmt_sum)\n",
    "    print(human_sum)\n",
    "\n",
    "\n",
    "        # tranlsations = [PyDictionary().translate(word, 'de') for word in element]\n",
    "        \n",
    "\n",
    "\n",
    "analyze_translations()"
   ]
  }
 ],
 "metadata": {
  "author": "Rafał Jaworski",
  "email": "rjawor@amu.edu.pl",
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "lang": "pl",
  "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.11.7"
  },
  "subtitle": "8. Wykorzystanie tłumaczenia automatycznego we wspomaganiu tłumaczenia",
  "title": "Komputerowe wspomaganie tłumaczenia",
  "year": "2021"
 },
 "nbformat": 4,
 "nbformat_minor": 5
}