KWT-2024/lab/lab_13-14.ipynb

317 lines
10 KiB
Plaintext
Raw Normal View History

2024-04-13 08:20:53 +02:00
{
"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",
2024-06-22 12:05:36 +02:00
"execution_count": null,
2024-04-13 08:20:53 +02:00
"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())"
]
},
2024-06-22 12:05:36 +02:00
{
"cell_type": "code",
"execution_count": null,
"id": "bee601d9",
"metadata": {},
"outputs": [],
"source": []
},
2024-04-13 08:20:53 +02:00
{
"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",
2024-06-22 12:05:36 +02:00
"execution_count": 11,
2024-04-13 08:20:53 +02:00
"id": "economic-southeast",
"metadata": {},
2024-06-22 12:05:36 +02:00
"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"
]
}
],
2024-04-13 08:20:53 +02:00
"source": [
2024-06-22 12:05:36 +02:00
"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'))"
2024-04-13 08:20:53 +02:00
]
},
{
"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",
2024-06-22 12:05:36 +02:00
"execution_count": 5,
2024-04-13 08:20:53 +02:00
"id": "built-sally",
"metadata": {},
"outputs": [],
"source": [
2024-06-22 12:05:36 +02:00
"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]"
2024-04-13 08:20:53 +02:00
]
},
{
"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",
2024-06-22 12:05:36 +02:00
"execution_count": 6,
2024-04-13 08:20:53 +02:00
"id": "coordinated-cooperation",
"metadata": {},
"outputs": [],
"source": [
2024-06-22 12:05:36 +02:00
"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))"
2024-04-13 08:20:53 +02:00
]
}
],
"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",
2024-06-22 12:05:36 +02:00
"version": "3.11.9"
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
}