2024-04-13 08:20:53 +02:00
{
"cells": [
{
"cell_type": "markdown",
"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> 13,14. <i>Korekta pisowni</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",
"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",
2024-05-25 14:53:24 +02:00
"execution_count": 6,
"metadata": {},
2024-04-13 08:20:53 +02:00
"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",
2024-05-25 14:53:24 +02:00
"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",
2024-04-13 08:20:53 +02:00
" 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",
2024-05-25 14:53:24 +02:00
"execution_count": 9,
2024-04-13 08:20:53 +02:00
"metadata": {},
2024-05-25 14:53:24 +02:00
"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"
]
}
],
2024-04-13 08:20:53 +02:00
"source": [
2024-05-25 14:53:24 +02:00
"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)"
2024-04-13 08:20:53 +02:00
]
},
{
"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",
2024-05-25 14:53:24 +02:00
"execution_count": 11,
2024-04-13 08:20:53 +02:00
"metadata": {},
2024-05-25 14:53:24 +02:00
"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ł
]
}
],
2024-04-13 08:20:53 +02:00
"source": [
"def L1(w):\n",
2024-05-25 14:53:24 +02:00
" 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)"
2024-04-13 08:20:53 +02:00
]
},
{
"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",
2024-05-25 14:53:24 +02:00
"execution_count": 15,
2024-04-13 08:20:53 +02:00
"metadata": {},
2024-05-25 14:53:24 +02:00
"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"
]
}
],
2024-04-13 08:20:53 +02:00
"source": [
2024-05-25 14:53:24 +02:00
"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)"
2024-04-13 08:20:53 +02:00
]
}
],
"metadata": {
"author": "Rafał Jaworski",
"email": "rjawor@amu.edu.pl",
"kernelspec": {
2024-05-25 14:53:24 +02:00
"display_name": "Python 3 (ipykernel)",
2024-04-13 08:20:53 +02:00
"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",
2024-05-25 14:53:24 +02:00
"version": "3.7.0"
2024-04-13 08:20:53 +02:00
},
"subtitle": "13,14. Korekta pisowni",
"title": "Komputerowe wspomaganie tłumaczenia",
"year": "2021"
},
"nbformat": 4,
"nbformat_minor": 5
}