{ "cells": [ { "cell_type": "markdown", "id": "ordered-wrestling", "metadata": {}, "source": [ "![Logo 1](https://git.wmi.amu.edu.pl/AITech/Szablon/raw/branch/master/Logotyp_AITech1.jpg)\n", "
\n", "

Komputerowe wspomaganie tłumaczenia

\n", "

13,14. Korekta pisowni [laboratoria]

\n", "

Rafał Jaworski (2021)

\n", "
\n", "\n", "![Logo 2](https://git.wmi.amu.edu.pl/AITech/Szablon/raw/branch/master/Logotyp_AITech2.jpg)" ] }, { "cell_type": "markdown", "id": "featured-afghanistan", "metadata": {}, "source": [ "Współczesne programy typu CAT nie mogą obyć się bez korektora pisowni. Na bieżąco kontrolują one pisownię wyrazów po stronie docelowej, czyli tam, gdzie tłumacz wpisuje tłumaczenie. Jest to niezwykle istotne w sytuacji, gdy język docelowy nie jest dla tłumacza językiem ojczystym. Co więcej, badania wykazują, iż korekta pisowni wydatnie zmniejsza liczbę błędów w każdych scenariuszach." ] }, { "cell_type": "markdown", "id": "seventh-genre", "metadata": {}, "source": [ "Co poprawia korekta pisowni? Słowa. Tylko lub aż słowa. Program dokonujący korekty pisowni przegląda tekst słowo po słowie i sprawdza, czy należy ono do słownika. Jeśli nie, sygnalizowany jest błąd oraz, jeśli to możliwe, podawane sugestie poprawy. Co istotne, korektor pisowni nie zajmuje się szeregiem błędów, które mieszczą się w dziedzinie korekty gramatycznej, w tym:\n", "* interpunkcją\n", "* powtórzeniami wyrazów\n", "* stylistyką." ] }, { "cell_type": "markdown", "id": "sticky-society", "metadata": {}, "source": [ "Aby zaimplementować korektor pisowni bez wątpienia potrzebny jest słownik. Skorzystajmy ze słownika, który znajdziemy w folderze data, pochodzącego z narzędzia Hunspell. Jest on spakowany - użyjmy techniki czytania z archiwum zip bez rozpakowywania. Poniższy kod wypisze fragment ze środka słownika." ] }, { "cell_type": "code", "execution_count": null, "id": "familiar-terrace", "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "kalecząc\n", "kaledonidy\n", "kaledoński\n", "kalefaktor\n", "kalejdofon\n", "kalejdoskop\n", "kalejdoskopowość\n", "kalejdoskopowy\n", "kaleka\n", "kaleki\n", "kalema\n", "kalendarium\n", "kalendarz\n", "kalendarzowy\n", "kalendarzyk\n", "kalendy\n", "kalenica\n", "kalenicowy\n", "kalepin\n", "kalesonki\n", "kalesony\n" ] } ], "source": [ "from zipfile import ZipFile\n", "\n", "with ZipFile('data/hunspell_pl.zip') as zipped_dictionary:\n", " with zipped_dictionary.open('hunspell_pl.txt') as dictionary_file:\n", " count = 0\n", " for line_bytes in dictionary_file:\n", " count += 1\n", " if count >= 100000 and count <= 100020:\n", " line = line_bytes.decode('utf-8')\n", " print(line.rstrip())" ] }, { "cell_type": "code", "execution_count": null, "id": "bee601d9", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "id": "dominant-insurance", "metadata": {}, "source": [ "Miejmy na uwadze, że powyższy słownik zawiera tylko formy podstawowe słowa, np. zawiera słowo \"kalendarz\", ale nie zawiera \"kalendarze\", \"kalendarza\", \"kalendarzy\" itp. " ] }, { "cell_type": "markdown", "id": "single-brighton", "metadata": {}, "source": [ "Algorytm korekty pisowni na podstawie słownika powinien działać według następujących kroków:\n", "1. Wczytanie słownika do zbioru (set)\n", "2. Podział tekstu do korekty na słowa (podział po spacji)\n", "3. Dla każdego słowa wypisać, czy jest ono poprawne (znajduje się w słowniku) czy nie." ] }, { "cell_type": "markdown", "id": "needed-watson", "metadata": {}, "source": [ "### Ćwiczenie 1: Zaimplementuj podstawowy algorytm korekty pisowni według powyższych wytycznych." ] }, { "cell_type": "code", "execution_count": 11, "id": "economic-southeast", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[('auto', 1)]\n", "[('adam', 1), ('jest', 1), ('fajny', 1)]\n", "[('brak', 1), ('słów', 1)]\n", "[('chrzaszcz', 0)]\n" ] } ], "source": [ "from zipfile import ZipFile\n", "\n", "dictionary = set\n", "\n", "with ZipFile('data/hunspell_pl.zip') as zip_f:\n", " with zip_f.open('hunspell_pl.txt') as f:\n", " dictionary = set([line.strip().lower() for line in f.read().decode('utf-8').splitlines()])\n", "\n", "def correct_text(phrase):\n", " return [(word, 1) if word in dictionary\n", " else (word, 0)\n", " for word in phrase.lower().split()]\n", "\n", "# 0 - źle\n", "# 1 - dobrze\n", "print(correct_text('Auto'))\n", "print(correct_text('Adam jest fajny'))\n", "print(correct_text('Brak słów'))\n", "print(correct_text('Chrzaszcz'))" ] }, { "cell_type": "markdown", "id": "endless-slide", "metadata": {}, "source": [ "To jednak oczywiście nie wszystko. Do tej pory mamy funkcjonalność sygnalizowania słów błędnych, ale każdy dobry korektor pisowni potrafi podać sugestie poprawek. W tym celu musimy stawić czoła następującemu problemowi - wygenerowanie listy słów podobnych do danego słowa błędnego, które znajdują się w słowniku." ] }, { "cell_type": "markdown", "id": "adult-freight", "metadata": {}, "source": [ "W pierwszej kolejności musimy zdefiniować podobieństwo między wyrazami. Posłuży do tego dobrze nam znana odległość Levenshteina - wyrazy podobne to takie, dla których dystans Levenshteina jest niewielki (np. równy 1 lub 2). Teraz brakuje tylko algorytmu wyszukiwania wyrazów w danym słowniku, które znajdują się niedaleko (w sensie Levenshteina) danego błędnego słowa." ] }, { "cell_type": "markdown", "id": "everyday-things", "metadata": {}, "source": [ "Rozważmy następujący algorytm: dla danego słownika $D$ i błędnego słowa $w \\notin D$:\n", "1. Wygeneruj zbiór $L_1(w)$ wszystkich słów, których odległość Levenshteina od $w$ wynosi 1.\n", "2. Wyznacz zbiór $S_1(w)=L_1(w) \\cap D$\n", "3. Wyznacz zbiór $L_2(w)=\\bigcup_{v \\in L_1(w)} L_1(v)$\n", "4. Wyznacz zbiór $S_2(w)=L_2(w) \\cap D$\n", "5. Zwróć jako listę sugestii: $S_1 \\cup S_2$" ] }, { "cell_type": "markdown", "id": "industrial-convert", "metadata": {}, "source": [ "### Ćwiczenie 2: Napisz funkcję do generowania zbioru $L_1(w)$ - wszystkich słów znajdujących się w odległości Levenshteina 1 od danego słowa w." ] }, { "cell_type": "code", "execution_count": 5, "id": "built-sally", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "\n", "def levenshtein_distance(a, b):\n", " n, m = len(a), len(b)\n", " if n > m:\n", " a, b = b, a\n", " n, m = m, n\n", "\n", " current_row = np.arange(n + 1)\n", " for i in range(1, m + 1):\n", " previous_row, current_row = current_row, np.zeros(n + 1, dtype=int)\n", " current_row[0] = i\n", " for j in range(1, n + 1):\n", " insertions = previous_row[j] + 1\n", " deletions = current_row[j - 1] + 1\n", " substitutions = previous_row[j - 1] + (a[j - 1] != b[i - 1])\n", " current_row[j] = min(insertions, deletions, substitutions)\n", "\n", " return current_row[n]\n", "\n", "\n", "def L1(word, dictionary, max_distance=1):\n", " return [dict_word for dict_word in dictionary if levenshtein_distance(word, dict_word) <= max_distance]" ] }, { "cell_type": "markdown", "id": "wireless-uncle", "metadata": {}, "source": [ "### Ćwiczenie 3: Napisz funkcję do generowania sugestii poprawek dla danego słowa według opisanego wcześniej algorytmu." ] }, { "cell_type": "code", "execution_count": 6, "id": "coordinated-cooperation", "metadata": {}, "outputs": [], "source": [ "def generate_suggestions(word, dictionary):\n", " return L1(word, dictionary)" ] }, { "cell_type": "code", "execution_count": 8, "id": "0c7843bb", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['ato', 'alto', 'huto', 'luto', 'autko', 'autor', 'auto', 'aut']\n", "['adams', 'adm', 'dam', 'ada', 'edam', 'adad', 'adym', 'asam', 'adaś', 'adat', 'aram', 'adam']\n", "['fajny', 'fajno', 'tajny', 'farny']\n", "[]\n" ] } ], "source": [ "from zipfile import ZipFile\n", "\n", "dictionary = set\n", "\n", "with ZipFile('data/hunspell_pl.zip') as zip_f:\n", " with zip_f.open('hunspell_pl.txt') as f:\n", " dictionary = set([line.strip().lower() for line in f.read().decode('utf-8').splitlines()])\n", " \n", "print(generate_suggestions('auto', dictionary))\n", "print(generate_suggestions('adam', dictionary))\n", "print(generate_suggestions('fajny', dictionary))\n", "print(generate_suggestions('chrzazszcz', dictionary))" ] } ], "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.9" }, "subtitle": "13,14. Korekta pisowni", "title": "Komputerowe wspomaganie tłumaczenia", "year": "2021" }, "nbformat": 4, "nbformat_minor": 5 }