{ "cells": [ { "cell_type": "markdown", "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", "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", "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", "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": 6, "metadata": {}, "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", "plik_zip = 'data/hunspell_pl.zip'\n", "plik_txt = 'hunspell_pl.txt'\n", "\n", "with ZipFile(plik_zip) as zipped_dictionary:\n", " with zipped_dictionary.open(plik_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", "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", "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", "metadata": {}, "source": [ "### Ćwiczenie 1: Zaimplementuj podstawowy algorytm korekty pisowni według powyższych wytycznych." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "'To' - nok\n", "'jest' - ok\n", "'przykładowy' - ok\n", "'tekst' - ok\n", "'do' - ok\n", "'sprawdzenia' - nok\n", "'pisowni.' - nok\n" ] } ], "source": [ "import zipfile\n", "\n", "def load_dictionary(zip_filepath, dictionary_filename):\n", " with zipfile.ZipFile(zip_filepath, 'r') as zip_ref:\n", " with zip_ref.open(dictionary_filename) as file:\n", " words = set(word.strip().decode('utf-8') for word in file.readlines())\n", " return words\n", "\n", "def correct_text(text, dictionary):\n", " words = text.split()\n", " results = []\n", " for word in words:\n", " if word in dictionary:\n", " results.append(f\"'{word}' - ok\")\n", " else:\n", " results.append(f\"'{word}' - nok\")\n", " return results\n", "\n", "dictionary = load_dictionary(plik_zip, plik_txt)\n", "\n", "text_to_check = \"To jest przykładowy tekst do sprawdzenia pisowni.\"\n", "results = correct_text(text_to_check, dictionary)\n", "\n", "for result in results:\n", " print(result)" ] }, { "cell_type": "markdown", "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", "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", "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", "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": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'przykbad', 'przykłay', 'przukład', 'crzykład', 'przykqład', 'wprzykład', 'przykładi', 'przykłtad', 'yprzykład', 'przykłsd', 'przykłvad', 'przxykład', 'przykzład', 'przykładg', 'pyrzykład', 'przyoład', 'prjykład', 'przeykład', 'przykkład', 'przikład', 'przyksad', 'sprzykład', 'pozykład', 'przyxkład', 'przyiład', 'pzrzykład', 'pwrzykład', 'jprzykład', 'przykła', 'grzykład', 'przykvad', 'przykładh', 'przykłae', 'przcykład', 'przmykład', 'plrzykład', 'przyckład', 'wrzykład', 'prvykład', 'erzykład', 'pzzykład', 'jrzykład', 'przykvład', 'przykpad', 'przmkład', 'przykłkd', 'przyeład', 'przyzład', 'przykłas', 'xprzykład', 'przykcład', 'przdkład', 'przykeład', 'przytkład', 'przykłap', 'przvkład', 'zrzykład', 'przynład', 'przfkład', 'przykłrd', 'mrzykład', 'przyktład', 'przyskład', 'frzykład', 'przykłnad', 'przykłaj', 'przykłqd', 'przyhkład', 'purzykład', 'przwykład', 'przykłod', 'przbykład', 'ptrzykład', 'przykłao', 'przykłal', 'porzykład', 'perzykład', 'przrykład', 'przkkład', 'przykłag', 'pqzykład', 'przykław', 'przykfład', 'prcykład', 'przyktad', 'praykład', 'przlykład', 'przykładk', 'tprzykład', 'drzykład', 'ppzykład', 'pruzykład', 'przyfład', 'przykmad', 'przymład', 'przykhad', 'prznykład', 'mprzykład', 'przgkład', 'przyzkład', 'pxrzykład', 'przykładm', 'przykłpd', 'przykzad', 'przykyad', 'przwkład', 'qrzykład', 'pirzykład', 'przekład', 'przykłado', 'srzykład', 'przykłabd', 'przhkład', 'iprzykład', 'przykłwad', 'przgykład', 'xrzykład', 'rrzykład', 'przykładz', 'pvzykład', 'prgzykład', 'pryzykład', 'przbkład', 'przyaład', 'fprzykład', 'przykłgd', 'prhykład', 'przykoad', 'nrzykład', 'przhykład', 'pezykład', 'przykładl', 'przsykład', 'przykłgad', 'przykłamd', 'przykłhd', 'prszykład', 'przyrład', 'przykaład', 'przykłads', 'phzykład', 'przykłayd', 'nprzykład', 'przydład', 'przykuad', 'pvrzykład', 'przyekład', 'przykładc', 'przykładr', 'lrzykład', 'orzykład', 'prrzykład', 'przybkład', 'przykdład', 'brzykład', 'przykłjad', 'prznkład', 'przykłlad', 'przykładw', 'zprzykład', 'przyhład', 'przykwład', 'przakład', 'przykmład', 'przykłaid', 'ptzykład', 'przyykład', 'eprzykład', 'pfrzykład', 'pgzykład', 'przykłkad', 'przyjład', 'pgrzykład', 'pkzykład', 'przykłxad', 'dprzykład', 'gprzykład', 'pfzykład', 'przykłmd', 'przykiad', 'rprzykład', 'przypład', 'przykłakd', 'przykłfd', 'przyuład', 'pmrzykład', 'przykłsad', 'prhzykład', 'przpykład', 'przyyład', 'prgykład', 'przykładx', 'przykłade', 'przvykład', 'przykłhad', 'przykłzd', 'przykłnd', 'prsykład', 'przqkład', 'vrzykład', 'przykłld', 'przykoład', 'przjykład', 'psrzykład', 'pyzykład', 'przykłmad', 'pryykład', 'przylład', 'przykłid', 'prqzykład', 'przykłiad', 'przykłazd', 'przykłbd', 'przykłaf', 'przckład', 'pmzykład', 'prjzykład', 'przykłatd', 'hprzykład', 'pxzykład', 'kprzykład', 'przykładp', 'prczykład', 'pdzykład', 'prmzykład', 'przykłaz', 'przuykład', 'przykłxd', 'prziykład', 'przykbład', 'przykłau', 'priykład', 'pszykład', 'przzkład', 'przykładt', 'proykład', 'przrkład', 'przymkład', 'pzykład', 'prezykład', 'przykłald', 'uprzykład', 'przqykład', 'przykłafd', 'prvzykład', 'przykłapd', 'przykłand', 'przykyład', 'przfykład', 'pizykład', 'przykwad', 'hrzykład', 'przykławd', 'przykładu', 'pczykład', 'prazykład', 'prlzykład', 'przykłud', 'przlkład', 'przykłada', 'przykjład', 'prnykład', 'przyikład', 'przywkład', 'pwzykład', 'przyknad', 'prfykład', 'przykłbad', 'parzykład', 'przykłdad', 'prztkład', 'pjrzykład', 'przykrad', 'przzykład', 'przyvkład', 'przjkład', 'puzykład', 'pjzykład', 'pcrzykład', 'przykładb', 'przykłaqd', 'przykpład', 'prxzykład', 'prpykład', 'przykłab', 'prdzykład', 'przylkład', 'prmykład', 'przykładf', 'irzykład', 'yrzykład', 'przynkład', 'przyakład', 'pnrzykład', 'przykłqad', 'pruykład', 'przokład', 'przyvład', 'przpkład', 'prozykład', 'pbzykład', 'przykiład', 'prwykład', 'przykłak', 'pbrzykład', 'przykładq', 'przykłfad', 'przykjad', 'przyukład', 'przykłyad', 'przykłtd', 'przdykład', 'przykad', 'przykłard', 'prpzykład', 'przykłdd', 'przykłai', 'preykład', 'prtzykład', 'przykłwd', 'przykłax', 'przoykład', 'przykłcad', 'cprzykład', 'przyxład', 'krzykład', 'przykłead', 'przykłaud', 'przydkład', 'przykłvd', 'przykłcd', 'przykłah', 'przykłyd', 'przykłaa', 'pprzykład', 'prztykład', 'aprzykład', 'przykłagd', 'przypkład', 'przyrkład', 'przxkład', 'przykłoad', 'przykłasd', 'prwzykład', 'przykłd', 'przyklład', 'prxykład', 'przykłam', 'przykłaod', 'vprzykład', 'przygkład', 'rzykład', 'przykłady', 'prdykład', 'przaykład', 'prbykład', 'przykłaxd', 'przkład', 'przykłajd', 'przykłac', 'przyfkład', 'prtykład', 'przykead', 'przybład', 'lprzykład', 'przykgad', 'prrykład', 'przyknład', 'przykłaq', 'qprzykład', 'przykłpad', 'pnzykład', 'pqrzykład', 'plzykład', 'przykqad', 'oprzykład', 'przykxad', 'przyokład', 'przytład', 'przykkad', 'przykcad', 'przykłuad', 'przykłrad', 'przykłaed', 'przkykład', 'przykładj', 'przykłjd', 'przysład', 'przykładd', 'przykład', 'prlykład', 'przykaad', 'przykxład', 'przykłahd', 'przykładn', 'pazykład', 'prfzykład', 'przyksład', 'przyqład', 'przyqkład', 'prqykład', 'przyład', 'prnzykład', 'przykgład', 'przykdad', 'przykłed', 'przykłar', 'przycład', 'przykłan', 'przykłaad', 'przykłat', 'przskład', 'pkrzykład', 'przykłav', 'trzykład', 'przygład', 'przykładv', 'prbzykład', 'pdrzykład', 'przykrład', 'prkzykład', 'przykfad', 'przykłacd', 'przykuład', 'przyklad', 'arzykład', 'przykłavd', 'przyjkład', 'przywład', 'prykład', 'prizykład', 'urzykład', 'prkykład', 'bprzykład', 'przykłzad', 'przykhład', 'phrzykład'}\n" ] } ], "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", " inserts = [L + c + R for L, R in splits for c in letters]\n", " replaces = [L + c + R[1:] for L, R in splits if R for c in letters]\n", " \n", " return set(deletes + inserts + replaces)\n", "\n", "word = \"przykład\"\n", "L1_set = L1(word)\n", "print(L1_set)" ] }, { "cell_type": "markdown", "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": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "'To' - nok (sugerowane poprawki: T, Tb, Tt, Tot, o, no, to, oo, Tc, Tl, Ta, co, do, po, bo, Th, ko, Te, eo, go, Tm, ho, Tob, Tom, Ti, Tr)\n", "'jest' - ok\n", "'przykładowy' - ok\n", "'tekst' - ok\n", "'do' - ok\n", "'sprawdzenia' - nok (brak sugestii)\n", "'pisowni.' - nok (sugerowane poprawki: pisownia)\n" ] } ], "source": [ "def generate_suggestions(text, dictionary):\n", " words = text.split()\n", " results = []\n", " for word in words:\n", " if word in dictionary:\n", " results.append(f\"'{word}' - ok\")\n", " else:\n", " suggestions = L1(word)\n", " valid_suggestions = [s for s in suggestions if s in dictionary]\n", " if valid_suggestions:\n", " suggestion_str = \", \".join(valid_suggestions)\n", " results.append(f\"'{word}' - nok (sugerowane poprawki: {suggestion_str})\")\n", " else:\n", " results.append(f\"'{word}' - nok (brak sugestii)\")\n", " return results\n", "\n", "text_to_check = \"To jest przykładowy tekst do sprawdzenia pisowni.\"\n", "results = generate_suggestions(text_to_check, dictionary)\n", "\n", "for result in results:\n", " print(result)" ] } ], "metadata": { "author": "Rafał Jaworski", "email": "rjawor@amu.edu.pl", "kernelspec": { "display_name": "Python 3 (ipykernel)", "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.7.0" }, "subtitle": "13,14. Korekta pisowni", "title": "Komputerowe wspomaganie tłumaczenia", "year": "2021" }, "nbformat": 4, "nbformat_minor": 5 }