forked from bfijalkowski/KWT-2024
324 lines
10 KiB
Plaintext
324 lines
10 KiB
Plaintext
{
|
|
"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",
|
|
"<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",
|
|
"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": 2,
|
|
"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": 33,
|
|
"id": "economic-southeast",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"[True, False, True]"
|
|
]
|
|
},
|
|
"execution_count": 33,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"from zipfile import ZipFile\n",
|
|
"def correct_text(text):\n",
|
|
" text = text.rstrip().split(\" \")\n",
|
|
" isCorrect = [False] * len(text)\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",
|
|
" bline = line_bytes.decode('utf-8').rstrip()\n",
|
|
" i = 0\n",
|
|
" for _ in range(len(text)):\n",
|
|
" if bline == text[i]:\n",
|
|
" isCorrect[i] = True\n",
|
|
" i += 1\n",
|
|
" \n",
|
|
" return isCorrect\n",
|
|
"\n",
|
|
"correct_text(\"Nadia jestr super\")"
|
|
]
|
|
},
|
|
{
|
|
"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": 62,
|
|
"id": "built-sally",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"{'coda',\n",
|
|
" 'doda',\n",
|
|
" 'koda',\n",
|
|
" 'moda',\n",
|
|
" 'roda',\n",
|
|
" 'soda',\n",
|
|
" 'wada',\n",
|
|
" 'weda',\n",
|
|
" 'wodan',\n",
|
|
" 'wola',\n",
|
|
" 'wota',\n",
|
|
" 'woza',\n",
|
|
" 'woła',\n",
|
|
" 'wóda',\n",
|
|
" 'węda'}"
|
|
]
|
|
},
|
|
"execution_count": 62,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"from zipfile import ZipFile\n",
|
|
"\n",
|
|
"def L1(w):\n",
|
|
" w.lower()\n",
|
|
" word_len = len(w)\n",
|
|
" words = 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",
|
|
" cost = 0\n",
|
|
" bline = line_bytes.decode('utf-8').rstrip().lower()\n",
|
|
" if len(bline) < word_len - 1 or len(bline) > word_len + 1:\n",
|
|
" continue\n",
|
|
" elif len(bline) == word_len:\n",
|
|
" for i in range(word_len):\n",
|
|
" if cost > 1:\n",
|
|
" continue\n",
|
|
" if w[i] != bline[i]:\n",
|
|
" cost += 1\n",
|
|
" if cost == 1:\n",
|
|
" words.add(bline)\n",
|
|
" else:\n",
|
|
" if w in bline:\n",
|
|
" words.add(bline)\n",
|
|
" \n",
|
|
" return words\n",
|
|
"\n",
|
|
"L1(\"woda\")"
|
|
]
|
|
},
|
|
{
|
|
"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": 69,
|
|
"id": "coordinated-cooperation",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"{'jeść', 'ujeść', 'zjeść'}"
|
|
]
|
|
},
|
|
"execution_count": 69,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"def generate_suggestions(w):\n",
|
|
" S1 = L1(w)\n",
|
|
" S2 = set()\n",
|
|
" for word in S1:\n",
|
|
" sugestions = L1(word)\n",
|
|
" for sug in sugestions:\n",
|
|
" S2.add(sug)\n",
|
|
" return set.union(S1, S2).difference({w})\n",
|
|
"\n",
|
|
"generate_suggestions(\"teść\")"
|
|
]
|
|
}
|
|
],
|
|
"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.12.1"
|
|
},
|
|
"subtitle": "13,14. Korekta pisowni",
|
|
"title": "Komputerowe wspomaganie tłumaczenia",
|
|
"year": "2021"
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 5
|
|
}
|