KWT-2024/lab/lab_15.ipynb

402 lines
146 KiB
Plaintext
Raw Permalink Normal View History

2024-04-13 08:20:53 +02:00
{
2024-06-15 19:14:48 +02:00
"cells": [
{
"cell_type": "markdown",
"id": "marine-termination",
"metadata": {
"id": "marine-termination"
},
"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> 15. <i>Korekta gramatyczna</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": {
"id": "featured-afghanistan"
},
"source": [
"Ostatnią z omawianych przez nas technik stosowaną podczas wspomagania tłumaczenia jest korekta gramatyczna. Automatyczna korekta gramatyczna tekstu to ambitne zadanie odnalezienia możliwych błędów niezwiązanych bezpośrednio z pisownią. Są to między innymi:\n",
"* błędy gramatyczne\n",
"* źle użyte słowa\n",
"* złe połączenia wyrazowe\n",
"* błędy interpunkcyjne\n",
"* kolokwializmy\n",
"* redundancja (np. \"tylko i wyłącznie\")"
]
},
{
"cell_type": "markdown",
"id": "peaceful-kingston",
"metadata": {
"id": "peaceful-kingston"
},
"source": [
"Warto zwrócić uwagę, iż systemy do korekcji gramatycznej można traktować jako klasyfikatory binarne. Przyjmijmy, że odpowiedź pozytywna korektora to wykrycie błędu w tekście, natomiast negatywna - brak błędu. Wówczas rozróżniamy dwa typy pomyłek: false positive oraz false negative. False positive to tzw. \"fałszywy alarm\" - zbyt duża ich ilość spowoduje wydłużenie czasu pracy użytkownika przez konieczność analizowania zgłoszeń, które w istocie błędami nie są. Co jednak jeszcze gorsze, zbyt duża ilość false positives powoduje spadek zaufania użytkownika do systemu oraz drastyczny spadek satysfakcji z używania systemu. Te drugie błędy - false negatives - to z kolei faktyczne błędy w tekście, które nie zostały wyłapane przez system korekty. Stare polskie porzekadło głosi, że \"czego oko nie widzi, tego sercu nie żal\". Niestety jednak problem pojawia się, kiedy dostrzeże to jakieś inne oko... Wysoka liczba false negatives wprawdzie skraca czas korekty (sic!), ale odbywa się to kosztem jakości całego procesu. Idealnie zatem byłoby zminimalizować zarówno liczbę false positives, jak i false negatives. Jak jednak łatwo się domyślić, nie jest to zawsze możliwe. Korektor gramatyczny, który jest bardzo restrykcyjny i raportuje wiele błędów, będzie miał tendencję do popełniania false positives. Natomiast korektor bardziej pobłażliwy niechybnie popełni wiele false negatives. Co zatem jest ważniejsze? Praktyka wskazuje, że oba parametry mają podobną wagę, ale jednak odrobinę ważniejsze jest powstrzymanie się od false positives."
]
},
{
"cell_type": "markdown",
"id": "soviet-highland",
"metadata": {
"id": "soviet-highland"
},
"source": [
"Do najbardziej popularnych narzędzi wspomagających korektę gramatyczną tekstu należą Grammarly oraz LanguageTool. Na dzisiejszych zajęciach zajmiemy się tym drugim. LanguageTool został pierwotnie napisany jako praca dyplomowa Daniela Nabera, a następnie intensywnie rozwijany wspólnie z Marcinem Miłkowskim. Aż do dziś projekt jest ciągle rozwijany, zwiększana jest liczba obsługiwanych języków oraz dokładność działania."
]
},
{
"cell_type": "markdown",
"id": "arbitrary-reconstruction",
"metadata": {
"id": "arbitrary-reconstruction"
},
"source": [
"LanguageTool jest systemem opartym na regułach. W dobie wszechobecnej sztucznej inteligencji opartej na uczeniu maszynowym rozwiązanie to wydaje się nieco przestarzałe. Jednak to właśnie reguły stanowią o sile LanguageToola - pozwalają one na zwiększenie precyzji korektora poprzez minimalizację false positives. Warto wspomnieć, iż liczne reguły LanguageToola dotyczą również korekty pisowni. Czyni to z LanguageToola kompletne narzędzie do korekty tekstu. Polecam przejrzenie zestawu reguł LanguageToola dla języka angielskiego: https://community.languagetool.org/rule/list?lang=en"
]
},
{
"cell_type": "markdown",
"id": "piano-satellite",
"metadata": {
"id": "piano-satellite"
},
"source": [
"Czas uruchomić to narzędzie. Skorzystajmy z Pythona."
]
},
{
"cell_type": "raw",
"id": "academic-crest",
"metadata": {
"id": "academic-crest"
},
"source": [
"pip3 install language_tool_python"
]
},
2024-04-13 08:20:53 +02:00
{
2024-06-15 19:14:48 +02:00
"cell_type": "markdown",
"id": "italian-cheese",
"metadata": {
"id": "italian-cheese"
},
"source": [
"Następnie możemy użyć LanguageToola w programie Pythonowym: (przykład zaczerpnięty z oficjalnego tutoriala: https://pypi.org/project/language-tool-python/)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "relative-anaheim",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "relative-anaheim",
"outputId": "d2c3754c-d0f5-457a-ace7-3caba62cb729"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Collecting language_tool_python\n",
" Downloading language_tool_python-2.8-py3-none-any.whl (35 kB)\n",
"Requirement already satisfied: pip in /usr/local/lib/python3.10/dist-packages (from language_tool_python) (23.1.2)\n",
"Requirement already satisfied: requests in /usr/local/lib/python3.10/dist-packages (from language_tool_python) (2.31.0)\n",
"Requirement already satisfied: tqdm in /usr/local/lib/python3.10/dist-packages (from language_tool_python) (4.66.4)\n",
"Requirement already satisfied: wheel in /usr/local/lib/python3.10/dist-packages (from language_tool_python) (0.43.0)\n",
"Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.10/dist-packages (from requests->language_tool_python) (3.3.2)\n",
"Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.10/dist-packages (from requests->language_tool_python) (3.7)\n",
"Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.10/dist-packages (from requests->language_tool_python) (2.0.7)\n",
"Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.10/dist-packages (from requests->language_tool_python) (2024.6.2)\n",
"Installing collected packages: language_tool_python\n",
"Successfully installed language_tool_python-2.8\n"
]
},
{
"output_type": "stream",
"name": "stderr",
"text": [
"Downloading LanguageTool 6.4: 100%|██████████| 246M/246M [00:03<00:00, 64.0MB/s]\n",
"INFO:language_tool_python.download_lt:Unzipping /tmp/tmpze2q21r3.zip to /root/.cache/language_tool_python.\n",
"INFO:language_tool_python.download_lt:Downloaded https://www.languagetool.org/download/LanguageTool-6.4.zip to /root/.cache/language_tool_python.\n"
]
},
{
"output_type": "stream",
"name": "stdout",
"text": [
"[Match({'ruleId': 'EN_A_VS_AN', 'message': 'Use “an” instead of a if the following word starts with a vowel sound, e.g. an article, an hour.', 'replacements': ['an'], 'offsetInContext': 16, 'context': 'A sentence with a error in the Hitchhikers Guide tot he ...', 'offset': 16, 'errorLength': 1, 'category': 'MISC', 'ruleIssueType': 'misspelling', 'sentence': \"A sentence with a error in the Hitchhiker's Guide tot he Galaxy\"}),\n",
" Match({'ruleId': 'TOT_HE', 'message': 'Did you mean “to the”?', 'replacements': ['to the'], 'offsetInContext': 43, 'context': '... with a error in the Hitchhikers Guide tot he Galaxy', 'offset': 50, 'errorLength': 6, 'category': 'TYPOS', 'ruleIssueType': 'misspelling', 'sentence': \"A sentence with a error in the Hitchhiker's Guide tot he Galaxy\"})]\n"
]
}
],
"source": [
"!pip install language_tool_python\n",
"import language_tool_python\n",
"import pprint\n",
"tool = language_tool_python.LanguageTool('en-US')\n",
"\n",
"text = 'A sentence with a error in the Hitchhikers Guide tot he Galaxy'\n",
"\n",
"pp = pprint.PrettyPrinter(depth=2)\n",
"errors = tool.check(text)\n",
"pp.pprint(errors)"
]
},
{
"cell_type": "markdown",
"id": "gorgeous-million",
"metadata": {
"id": "gorgeous-million"
},
"source": [
"Przeanalizujmy format zwracanego wyniku. Otrzymujemy listę obiektów Match - zawiadomień o potencjalnym błędzie. Razem z każdym błędem otrzymujemy m.in. identyfikator użytej reguły, opis błędu, rekomendancję poprawy, kontekst."
]
},
{
"cell_type": "markdown",
"id": "reasonable-cornwall",
"metadata": {
"id": "reasonable-cornwall"
},
"source": [
"### Ćwiczenie 1: Użyj LanguageTool do znalezienia jak największej liczby prawdziwych błędów na swoim ulubionym portalu internetowym. Skorzystaj z poznanych wcześniej technik web scrapingu. Uwaga - LanguageTool najprawdopodobniej oznaczy nazwy własne jako literówki - ten typ błędu nie powinien być brany pod uwagę."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "sound-teaching",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "sound-teaching",
"outputId": "d3f9388e-c6ce-4a42-c709-67c8329e76fe"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"[{'Error': 'Nietypowa kombinacja małych i dużych liter. Czy nie powinno być: \"szukajszukajsympatiagrypromocjaogłoszeniapromocjarabatywiadomościsportpremiumkup\"?', 'Context': 'Onet Jesteś na bieżąco SzukajSzukajSympatiaGrypromocjaOgłoszeniapromocjaRabatyWiadomościSportPremiumKup Onet PremiumBusiness InsiderForbes Wome...', 'Suggestion': 'szukajszukajsympatiagrypromocjaogłoszeniapromocjarabatywiadomościsportpremiumkup', 'Rule ID': 'NIETYPOWA_KOMBINACJA_DUZYCH_I_MALYCH_LITER'}, {'Error': 'Nietypowa kombinacja małych i dużych liter. Czy nie powinno być: \"premiumbusiness\"?', 'Context': '...cjaRabatyWiadomościSportPremiumKup Onet PremiumBusiness InsiderForbes WomenNational GeographicA...', 'Suggestion': 'premiumbusiness', 'Rule ID': 'NIETYPOWA_KOMBINACJA_DUZYCH_I_MALYCH_LITER'}, {'Error': 'Nietypowa kombinacja małych i dużych liter. Czy nie powinno być: \"insiderforbes\"?', 'Context': '...ściSportPremiumKup Onet PremiumBusiness InsiderForbes WomenNational GeographicArchineaSprawy ...', 'Suggestion': 'insiderforbes', 'Rule ID': 'NIETYPOWA_KOMBINACJA_DUZYCH_I_MALYCH_LITER'}, {'Error': 'Nietypowa kombinacja małych i dużych liter. Czy nie powinno być: \"womennational\"?', 'Context': '...mKup Onet PremiumBusiness InsiderForbes WomenNational GeographicArchineaSprawy KryminalneWszy...', 'Suggestion': 'womennational', 'Rule ID': 'NIETYPOWA_KOMBINACJA_DUZYCH_I_MALYCH_LITER'}, {'Error': 'Nietypowa kombinacja małych i dużych liter. Czy nie powinno być: \"geographicarchineasprawy\"?', 'Context': '...iumBusiness InsiderForbes WomenNational GeographicArchineaSprawy KryminalneWszystkieDie WeltPrzegląd Spo...', 'Suggestion': 'geographicarchineasprawy', 'Rule ID': 'NIETYPOWA_KOMBINACJA_DUZYCH_I_MALYCH_LITER'}, {'Error': 'Nietypowa kombinacja małych i dużych liter. Czy nie powinno być: \"kryminalnewszystkiedie\"?', 'Context': '... WomenNational GeographicArchineaSprawy KryminalneWszystkieDie WeltPrzegląd SportowyNowa Technika Wojs...', 'Suggestion': 'kryminalnewszystkiedie', 'Rule ID': 'NIETYPOWA_KOMBINACJA_DUZYCH_I_MALYCH_LITER'}, {'Error': 'Nietypowa kombinacja małych i dużych liter. Czy nie powinno być: \"weltprzegląd\"?', 'Context': '...icArchineaSprawy KryminalneWszystkieDie WeltPrzegląd SportowyNowa Technika WojskowaBildPrósz...', 'Suggestion': 'weltprzegląd', 'Rule ID': 'NIETYPOWA_KOMBINACJA_DUZYCH_I_MALYCH_LITER'}, {'Error': 'Nietypowa kombinacja małych i dużych liter. Czy nie powinno być: \"sportowynowa\"?', 'Context': '...awy KryminalneWszystkieDie WeltPrzegląd SportowyNowa Technika WojskowaBildPrószyński i S-kaO...', 'Suggestion': 'sportowynowa', 'Rule ID': 'NIETYPOWA_KOMBINACJA_DUZYCH_I_MALYCH_LITER'}, {'Error': 'Nietypowa kombinacja małych i dużych liter. Czy nie powinno być: \"wojskowabildprószyński\"?', 'Context': '...eDie WeltPrzegląd SportowyNowa Technika WojskowaBildPrószyński i S-kaOnetPoliticoAuto ŚwiatNowa Europa...', 'Suggestion': 'wojskowabildprószyński', 'Rule ID': 'NIETYPOWA_KOMBINACJA_DUZYCH_I_MALYCH_LITER'}, {'Error': 'Wykryto prawdopodobny błąd pisowni', 'Context': '...yNowa Technika WojskowaBildPrószyński i S-kaOnetPoliticoAuto ŚwiatNowa Europa WschodniaNowa GazietaN...', 'Suggestion': '', 'Rule ID': 'MORFOLOGIK_RULE_PL_PL'}, {'Error': 'Nietypowa kombinacja małych i dużych liter. Czy nie powinno być: \"światnowa\"?', 'Context': '...waBildPrószyński i S-kaOnetPoliticoAuto ŚwiatNowa Europa WschodniaNowa GazietaNewsweekFor...', 'Suggestion': 'światnowa', 'Rule ID': 'NIETYPOWA_KOMBINACJA_DUZYCH_I_MALYCH_LITER'}, {'Error': 'Nietypowa kombinacja małych i dużych liter. Czy nie powinno być: \"wschodnianowa\"?', 'Context': '...i S-kaOnetPoliticoAuto ŚwiatNowa Europa WschodniaNowa GazietaNewsweekForbesKomputer ŚwiatNews...', 'Suggestion': 'wschodnianowa', 'Rule ID': 'NIETYPOWA_KOMBINACJA_DUZYCH_I_MALYCH_LITER'}, {'Error': 'Nietypowa kombinacja małych i dużych liter. Czy nie powinno być: \"gazietanewsweekforbeskomputer\"?', 'Context': '...ticoAuto ŚwiatNowa Europa WschodniaNowa GazietaNewsweekForbesKomputer ŚwiatNewsweek Learnin
]
}
],
"source": [
"import requests\n",
"from bs4 import BeautifulSoup\n",
"\n",
"def get_page_text(url):\n",
" page = requests.get(url)\n",
" soup = BeautifulSoup(page.content, 'html.parser')\n",
" for script_or_style in soup(['script', 'style']):\n",
" script_or_style.extract()\n",
" text = soup.get_text()\n",
" text = ' '.join(text.split())\n",
" return text\n",
"\n",
"def find_errors(website_url):\n",
" text = get_page_text(website_url)\n",
"\n",
" tool = language_tool_python.LanguageTool('pl-PL')\n",
" matches = tool.check(text)\n",
" errors = [match for match in matches if match.ruleId not in ['MORFOLOGIK_RULE_PL_PROPER_NOUN', 'MORFOLOGIK_RULE_PL_UNKNOWN']]\n",
"\n",
" formatted_errors = []\n",
" for error in errors:\n",
" formatted_error = {\n",
" \"Error\": error.message,\n",
" \"Context\": error.context,\n",
" \"Suggestion\": ', '.join(error.replacements),\n",
" \"Rule ID\": error.ruleId\n",
" }\n",
" formatted_errors.append(formatted_error)\n",
"\n",
" return formatted_errors\n",
"\n",
"website_url = 'https://www.onet.pl'\n",
"errors = find_errors(website_url)\n",
"print(errors)"
]
},
{
"cell_type": "markdown",
"id": "coupled-extra",
"metadata": {
"id": "coupled-extra"
},
"source": [
"### Ćwiczenie 2: Napisz skrypt, który poszuka błędów w komentarzach klasy Javowej (zwykłych // oraz w javadocach). Uruchom ten skrypt na źródłach wybranego opensourcowego projektu w Javie."
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "settled-armor",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "settled-armor",
"outputId": "6762f9fb-33b9-48e3-d5f9-72a876e05ccb"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Requirement already satisfied: textblob in /usr/local/lib/python3.10/dist-packages (0.17.1)\n",
"Requirement already satisfied: nltk>=3.1 in /usr/local/lib/python3.10/dist-packages (from textblob) (3.8.1)\n",
"Requirement already satisfied: click in /usr/local/lib/python3.10/dist-packages (from nltk>=3.1->textblob) (8.1.7)\n",
"Requirement already satisfied: joblib in /usr/local/lib/python3.10/dist-packages (from nltk>=3.1->textblob) (1.4.2)\n",
"Requirement already satisfied: regex>=2021.8.3 in /usr/local/lib/python3.10/dist-packages (from nltk>=3.1->textblob) (2024.5.15)\n",
"Requirement already satisfied: tqdm in /usr/local/lib/python3.10/dist-packages (from nltk>=3.1->textblob) (4.66.4)\n"
]
}
],
"source": [
"!pip install textblob\n",
"\n",
"import os\n",
"import re\n",
"from textblob import TextBlob\n",
"\n",
"def open_file(file_path):\n",
" with open(file_path, 'r', encoding='utf-8') as file:\n",
" return file.readlines()\n",
"\n",
"\n",
"def check_comments(lines):\n",
" comments = []\n",
" in_javadoc = False\n",
" comment_block = []\n",
"\n",
" for line in lines:\n",
" line = line.strip()\n",
" if line.startswith('/**'):\n",
" in_javadoc = True\n",
" comment_block.append(line)\n",
" elif line.startswith('*/') and in_javadoc:\n",
" in_javadoc = False\n",
" comment_block.append(line)\n",
" comments.append('\\n'.join(comment_block))\n",
" comment_block = []\n",
" elif in_javadoc or line.startswith('*'):\n",
" comment_block.append(line)\n",
" elif line.startswith('//'):\n",
" comments.append(line)\n",
"\n",
" return comments\n",
"\n",
"def find_errors(comments):\n",
" errors = []\n",
" for comment in comments:\n",
" words = re.findall(r'\\b\\w+\\b', comment)\n",
" for word in words:\n",
" if not TextBlob(word).correct() == word:\n",
" errors.append(f\"Misspelled word '{word}' in comment:\\n{comment}\\n\")\n",
"\n",
" if comment.startswith('/**'):\n",
" if '@param' not in comment and '()' in comment:\n",
" errors.append(f\"Missing @param tag in comment:\\n{comment}\\n\")\n",
" if '@return' not in comment and 'return' in comment:\n",
" errors.append(f\"Missing @return tag in comment:\\n{comment}\\n\")\n",
"\n",
" return errors\n",
"\n",
"def correct_java_grammar(java_file_path):\n",
" comments = check_comments(open_file(java_file_path))\n",
" return find_errors(comments)\n"
]
},
{
"cell_type": "code",
"source": [
"import requests\n",
"from bs4 import BeautifulSoup\n",
"\n",
"def get_page_text(url):\n",
" page = requests.get(url)\n",
" soup = BeautifulSoup(page.content, 'html.parser')\n",
" for script_or_style in soup(['script', 'style']):\n",
" script_or_style.extract()\n",
" text = soup.get_text()\n",
" return text\n",
"\n",
"def extract_java_class(text):\n",
" start_index = text.find('/**')\n",
" end_index = text.find('}', start_index) + 1\n",
" return text[start_index:end_index]\n",
"\n",
"def split_into_lines(java_class_text):\n",
" return java_class_text.split('\\n')\n",
"\n",
"url = \"https://www.tutorialspoint.com/java/java_documentation.htm\"\n",
"page_text = get_page_text(url)\n",
"java_class_text = split_into_lines(extract_java_class(page_text))\n",
"\n",
"print(find_errors(check_comments(java_class_text)))"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "3QwSdAGIRGk5",
"outputId": "95b3c95f-1385-4cd4-e751-cf25c91fa963"
},
"id": "3QwSdAGIRGk5",
"execution_count": 8,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"['Misspelled word \\'Zara\\' in comment:\\n/**\\n* The HelloWorld program implements an application that\\n* simply displays \"Hello World!\" to the standard output.\\n*\\n* @author Zara Ali\\n* @version 1.0\\n* @since 2014-03-31\\n*/\\n', 'Misspelled word \\'Ali\\' in comment:\\n/**\\n* The HelloWorld program implements an application that\\n* simply displays \"Hello World!\" to the standard output.\\n*\\n* @author Zara Ali\\n* @version 1.0\\n* @since 2014-03-31\\n*/\\n']\n"
]
}
]
}
],
"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.8.10"
},
"subtitle": "15. Korekta gramatyczna",
"title": "Komputerowe wspomaganie tłumaczenia",
"year": "2021",
"colab": {
"provenance": []
2024-04-13 08:20:53 +02:00
}
},
2024-06-15 19:14:48 +02:00
"nbformat": 4,
"nbformat_minor": 5
}