{ "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": 30, "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": "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": 31, "id": "d0970691", "metadata": {}, "outputs": [], "source": [ "pl_dict = set()\n", "with ZipFile('data/hunspell_pl.zip') as zipped_dictionary:\n", " with zipped_dictionary.open('hunspell_pl.txt') as dictionary_file:\n", " for line_bytes in dictionary_file:\n", " line = line_bytes.decode('utf-8')\n", " pl_dict.add(line.rstrip())" ] }, { "cell_type": "code", "execution_count": 32, "id": "economic-southeast", "metadata": {}, "outputs": [], "source": [ "def correct_text(text):\n", " words = text.split()\n", "\n", " result = []\n", " for word in words:\n", " if word in pl_dict:\n", " result.append((word, \"correct\"))\n", " else:\n", " result.append((word, \"incorrect\"))\n", "\n", " return result" ] }, { "cell_type": "code", "execution_count": 33, "id": "771a6c40", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[('kalend', 'incorrect'),\n", " ('kalendarz', 'correct'),\n", " ('kaledoński', 'correct'),\n", " ('kalejdoskopowy', 'correct'),\n", " ('kalendarium', 'correct')]" ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "correct_text(\"kalend kalendarz kaledoński kalejdoskopowy kalendarium\")" ] }, { "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": 34, "id": "built-sally", "metadata": {}, "outputs": [], "source": [ "def L1(w):\n", " letters = 'abcdefghijklmnopqrstuvwxyząćęłńóśźż'\n", " splits = [(w[:i], w[i:]) for i in range(len(w) + 1)]\n", " \n", " deletes = [L + R[1:] for L, R in splits if R]\n", " transposes = [L + R[1] + R[0] + R[2:] for L, R in splits if len(R) > 1]\n", " replaces = [L + c + R[1:] for L, R in splits if R for c in letters]\n", " inserts = [L + c + R for L, R in splits for c in letters]\n", " \n", " return set(deletes + transposes + replaces + inserts)" ] }, { "cell_type": "code", "execution_count": 35, "id": "dc3ffbfe", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['kaqendarz',\n", " 'kalenydarz',\n", " 'kalendadz',\n", " 'kalenżarz',\n", " 'kalendlrz',\n", " 'kalendaóz',\n", " 'kalvendarz',\n", " 'kalendarzv',\n", " 'katendarz',\n", " 'kolendarz']" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(L1(\"kalendarz\"))[:10]" ] }, { "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": 36, "id": "coordinated-cooperation", "metadata": {}, "outputs": [], "source": [ "def generate_suggestions(w):\n", " # Generate L1(w)\n", " L1_set = L1(w)\n", " # Generate S1(w)\n", " S1 = L1_set.intersection(pl_dict)\n", "\n", " # Generate L2(w)\n", " L2_set = set()\n", " for v in L1_set:\n", " L2_set.update(L1(v))\n", " \n", " # Generate S2(w)\n", " S2 = L2_set.intersection(pl_dict)\n", "\n", " # Combine S1 and S2 and return as list\n", " suggestions = S1.union(S2)\n", " return list(suggestions)" ] }, { "cell_type": "code", "execution_count": 37, "id": "e0c572ce", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['kalendarz', 'kalandar', 'kalendarzyk', 'arendarz']" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "generate_suggestions(\"kalendarz\")" ] } ], "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.10.14" }, "subtitle": "13,14. Korekta pisowni", "title": "Komputerowe wspomaganie tłumaczenia", "year": "2021" }, "nbformat": 4, "nbformat_minor": 5 }