505 KiB
Komputerowe wspomaganie tłumaczenia
4,5. Klasyfikacja tematyczna (terminologii ciąg dalszy) [laboratoria]
Rafał Jaworski (2021)
Komputerowe wspomaganie tłumaczenia
Zajęcia 4 i 5 - klasyfikacja tematyczna (terminologii ciąg dalszy)
Na poprzednich zajęciach opracowaliśmy nasz własny ekstraktor terminologii. Mówiliśmy również, jak ważna jest ekstrakcja terminów specjalistycznych. Dziś zajmiemy się zagadnieniem, w jaki sposób wyciągnąć z tekstu terminy, które naprawdę są specjalistyczne.
Dlaczego nasze dotychczasowe rozwiązanie mogło nie spełniać tego warunku? Wykonajmy następujące ćwiczenie:
Ćwiczenie 1: Zgromadź korpus w języku angielskim składający się z co najmniej 100 dokumentów, z których każdy zawiera co najmniej 100 zdań. Wykorzystaj stronę https://opus.nlpl.eu/. Dobrze, aby dokumenty pochodziły z różnych dziedzin (np. prawo Unii Europejskiej, manuale programistyczne, medycyna). Ściągnięty korpus zapisz na swoim dysku lokalnym, nie załączaj go do niniejszego notatnika.
Taki korpus pozwoli nam zaobserwować, co się stanie, jeśli do ekstrakcji terminologii będziemy stosowali wyłącznie kryterium częstościowe. Aby wykonać odpowiedni eksperyment musimy uruchomić ekstraktor z poprzednich zajęć.
Ćwiczenie 2: Uruchom ekstraktor terminologii (wykrywacz rzeczowników) z poprzednich zajęć na każdym dokumencie z osobna. Jako wynik ekstraktora w każdym przypadku wypisz 5 najczęściej występujących rzeczowników. Wyniki działania komendy umieść w notatniku.
import spacy
from collections import Counter
nlp = spacy.load("en_core_web_sm")
documents = {
'bible': './books/bible.en',
'forgein_affair': './books/forgein_affair.en',
'justice': './books/justice.en'}
def extract_terms(path: str, k: int = 5):
with open(path, 'r') as f:
text = f.read()[:1000000]
doc = nlp(text.lower())
noun_counts = Counter([w.lemma_ for w in doc if w.pos_ == 'NOUN'])
return noun_counts.most_common(k)
extract_terms(documents['bible'])
[('man', 966), ('child', 943), ('son', 930), ('land', 805), ('day', 783)]
extract_terms(documents['forgein_affair'])
[('%', 654), ('country', 567), ('market', 312), ('year', 297), ('state', 251)]
extract_terms(documents['justice'])
[('project', 78), ('account', 40), ('information', 39), ('application', 34), ('victim', 28)]
Czy wyniki uzyskane w ten sposób to zawsze terminy specjalistyczne? Niestety może zdarzyć się, że w wynikach pojawią się rzeczowniki, które są po prostu częste w języku, a niekoniecznie charakterystyczne dla przetwarzanych przez nas tekstów. Aby wyniki ekstrakcji były lepsze, konieczne jest zastosowanie bardziej wyrafinowanych metod.
Jedną z tych metod jest znana z dyscypliny Information Retrieval technika zwana TF-IDF. Jej nazwa wywodzi się od Term Frequency Inverted Document Frequency. Według tej metody, dla każdego odnalezionego przez nas termu powinniśmy obliczyć czynnik TF-IDF, a następnie wyniki posortować malejąco po wartości tego czynnika.
Jak obliczyć czynnik TF-IDF? Czym jest TF, a czym jest IDF?
Zacznijmy od TF, bo ten czynnik już znamy. Jest to nic innego jak częstość wystąpienia terminu w tekście, który przetwarzamy. Idea TF-IDF skupia się na drugim czynniku - IDF. Słowo _inverted oznacza, że czynnik ten będzie odwrócony, czyli trafi do mianownika. W związku z tym TF-IDF to w istocie: $\frac{TF}{DF}$
Czym zatem jest document frequency? Jest to liczba dokumentów, w których wystąpił dany termin. Dokumenty w tym przypadku są rozumiane jako jednostki, na które podzielony jest korpus, nad którym pracujemy (dokładnie taki, jak korpus z ćwiczenia pierwszego).
Zastanówmy się nad sensem tego czynnika. Pamiętajmy, że naszym zadaniem jest ekstracja terminów z tylko jednego dokumentu na raz. Mamy jednak do dyspozycji wiele innych dokumentów, zawierających wiele innych słów i termów. Wartość TF-IDF jest tym większa, im częściej termin występuje w dokumencie, na którym dokonujemy ekstrakcji. Czynnik ten jednak zmniejsza się, jeśli słowo występuje w wielu różnych dokumentach. Zatem, popularne słowa będą miały wysoki czynnik DF i niski TF-IDF. Natomiast najwyższą wartość TF-IDF będą miały terminy, które są częste w przetwarzanym przez nas dokumencie, ale nie występują nigdzie indziej.
Ćwiczenie 3: Zaimplementuj czynnik TF-IDF i dokonaj ekstrakcji terminologii za jego pomocą, używając korpusu z ćwiczenia nr 1. Czy wyniki różnią się od tych uzyskanych tylko za pomocą TF?
from typing import List, Dict
def count_words(path: str):
with open(path, 'r') as f:
text = f.read()[:1000000]
doc = nlp(text.lower())
noun_counts = Counter([w.lemma_ for w in doc if w.pos_ == 'NOUN'])
return noun_counts
def tfidf_extract(document: str, documents: Dict[str, Counter], k: int = 5):
tf = documents[document]
rest = [d for d in documents.keys() if d != document]
from collections import Counter
from typing import Dict
import math
def tfidf_extract(document: str, documents: Dict[str, Counter], idf: Dict[str, float], k: int = 5):
tf = documents[document]
tfidf = {term: tf[term] * idf[term] for term in tf}
top_k_terms = sorted(tfidf.items(), key=lambda x: x[1], reverse=True)[:k]
return top_k_terms
def precompute_idf(documents: Dict[str, Counter]):
idf = {}
num_docs = len(documents)
all_terms = [term for doc in documents.values() for term in doc]
for term in all_terms:
num_docs_with_term = sum(1 for doc in documents.values() if term in doc)
idf[term] = math.log(num_docs / (1 + num_docs_with_term))
return idf
def load_dict(path: str):
with open(path, 'r') as f:
text = f.read()[:1000000]
doc = nlp(text.lower())
return Counter([w.lemma_ for w in doc if w.pos_ == 'NOUN'])
documents = {
'bible': './books/bible.en',
'forgein_affair': './books/forgein_affair.en',
'justice': './books/justice.en'}
loaded = {k: load_dict(v) for k, v in documents.items()}
idf = precompute_idf(loaded)
for doc_name in documents:
top_terms = tfidf_extract(doc_name, loaded, idf)
print(f"Top terms in {doc_name}: {top_terms}")
Top terms in bible: [('son', 377.0825505405929), ('offering', 282.6091803513906), ('father', 250.5774368108456), ('house', 149.61662489191266), ('priest', 145.96743891893917)] Top terms in forgein_affair: [('%', 265.17418070273953), ('market', 126.5051137297473), ('energy', 83.93127737839002), ('sector', 73.79464967568592), ('accession', 67.71267305406346)] Top terms in justice: [('mediation', 8.920232378379616), ('proceeding', 6.48744172973063), ('prosecutor', 4.054651081081644), ('promoter', 3.6491859729734797), ('interrogation', 2.8382557567571505)]
Teraz potrafimy już w lepszy sposób wyciągać terminy z dokumentów. Spróbujmy jeszcze czegoś widowiskowego - wygenerujmy tzw. chmurę słów z tekstu przy użyciu biblioteki WordCloud dla artykułu z BBC News (https://www.bbc.com/news/world-europe-56530714):
sudo pip install wordcloud
!pip install wordcloud
Collecting wordcloud Downloading wordcloud-1.9.3-cp311-cp311-macosx_10_9_x86_64.whl (172 kB) [2K [38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━[0m [32m172.4/172.4 kB[0m [31m2.1 MB/s[0m eta [36m0:00:00[0m[31m3.2 MB/s[0m eta [36m0:00:01[0m [?25hRequirement already satisfied: numpy>=1.6.1 in /Users/potoato/.pyenv/versions/3.11.0/lib/python3.11/site-packages (from wordcloud) (1.26.4) Requirement already satisfied: pillow in /Users/potoato/.pyenv/versions/3.11.0/lib/python3.11/site-packages (from wordcloud) (10.3.0) Requirement already satisfied: matplotlib in /Users/potoato/.pyenv/versions/3.11.0/lib/python3.11/site-packages (from wordcloud) (3.8.4) Requirement already satisfied: contourpy>=1.0.1 in /Users/potoato/.pyenv/versions/3.11.0/lib/python3.11/site-packages (from matplotlib->wordcloud) (1.2.1) Requirement already satisfied: cycler>=0.10 in /Users/potoato/.pyenv/versions/3.11.0/lib/python3.11/site-packages (from matplotlib->wordcloud) (0.12.1) Requirement already satisfied: fonttools>=4.22.0 in /Users/potoato/.pyenv/versions/3.11.0/lib/python3.11/site-packages (from matplotlib->wordcloud) (4.51.0) Requirement already satisfied: kiwisolver>=1.3.1 in /Users/potoato/.pyenv/versions/3.11.0/lib/python3.11/site-packages (from matplotlib->wordcloud) (1.4.5) Requirement already satisfied: packaging>=20.0 in /Users/potoato/.pyenv/versions/3.11.0/lib/python3.11/site-packages (from matplotlib->wordcloud) (24.0) Requirement already satisfied: pyparsing>=2.3.1 in /Users/potoato/.pyenv/versions/3.11.0/lib/python3.11/site-packages (from matplotlib->wordcloud) (3.1.2) Requirement already satisfied: python-dateutil>=2.7 in /Users/potoato/.pyenv/versions/3.11.0/lib/python3.11/site-packages (from matplotlib->wordcloud) (2.9.0.post0) Requirement already satisfied: six>=1.5 in /Users/potoato/.pyenv/versions/3.11.0/lib/python3.11/site-packages (from python-dateutil>=2.7->matplotlib->wordcloud) (1.16.0) Installing collected packages: wordcloud Successfully installed wordcloud-1.9.3 [1m[[0m[34;49mnotice[0m[1;39;49m][0m[39;49m A new release of pip available: [0m[31;49m22.3[0m[39;49m -> [0m[32;49m24.0[0m [1m[[0m[34;49mnotice[0m[1;39;49m][0m[39;49m To update, run: [0m[32;49mpip install --upgrade pip[0m
from wordcloud import WordCloud
text = """"This is where it happened," says Felipe Luis Codesal, opening the gate to a three-hectare field on his farm in Zamora, north-west Spain.
One night last November, a pack of wolves got through the fence surrounding the field and attacked Mr Codesal's sheep, many of which were pregnant. When he arrived the next morning, he found 11 animals had been killed. Over the following days, he says, another 36 sheep died from injuries sustained in that attack and miscarriages it triggered.
Mr Codesal fears that such attacks will become even more commonplace if a proposed change to laws protecting the Iberian wolf comes into force.
The leftist coalition government plans to prevent the Iberian wolf from being hunted anywhere by categorising it as an endangered species. The reform is yet to be implemented and could see changes.
Iberian wolves from the Iberian Wolf Centre in Robledo de Sanabria on February 21, 2020 in Zamora, Spain
image captionSpain has Europe's biggest wolf population: These Iberian wolves are kept at Zamora's Iberian wolf centre
"It's like in a nightclub when there's a fire," says Mr Codesal of the wolf attack. "There's a stampede and people get trodden on and hurt. This is the same."
He was not entitled to any compensation and estimates that the financial losses he suffered from this incident totalled around €12-14,000
"It's not even about the money," he says. "It's emotional, because the animals are part of my family."
A 'historic' change?
The region of Castilla y León is the habitat for most of Spain's wolves. Figures gathered by the local government showed that they killed 3,774 sheep and cows in the region in 2019.
Felipe Luis Codesal's farm is just north of the Duero river, which marks a natural border between north-west Spain and the rest of the country. Until now, it has been legal to hunt wolves north of the Duero, under a strict quota system, because that is where they are most prevalent.
South of the river they have been protected.
Conservationist groups have welcomed the government plan. When it was unveiled in February, the Ecologistas en Acción organisation hailed it as a "historic day".
But Mr Codesal, who is a member of the UPA association of smallholder farmers, warns the reform will ruin livestock owners by allowing the wolf population to spiral out of control and roam uncontrolled. The UPA is unconvinced by measures included in the plan to subsidise the installation of fences and the use of guard dogs in livestock farming areas.
Biggest wolf numbers in Europe
The Iberian wolf was close to being wiped out in the middle of the 20th Century. But it enjoyed a resurgence on the back of new hunting regulations introduced in the 1970s and the migration of Spaniards away from rural areas also encouraged its spread down from the north-western corner of the country.
In recent years, wolves have moved into areas such as the Guadarrama mountains north of Madrid and near the city of Ávila, to the west of the capital.
There are now some 2,500 Iberian wolves: around 2,000 are in Spain - the largest wolf population in western Europe - and the rest in Portugal.
"""
wordcloud = WordCloud(background_color="white", max_words=5000, contour_width=3, contour_color='steelblue')
wordcloud.generate(text)
wordcloud.to_image()
Ćwiczenie 4: Wykonaj chmurę słów dla całego korpusu z ćwiczenia nr 1.
def my_word_cloud(path: str):
with open(path, 'r') as f:
text = f.read()[:1000000]
doc = nlp(text.lower())
nouns = [w.lemma_ for w in doc if w.pos_ == 'NOUN']
wordcloud = WordCloud(background_color="white", max_words=1000, contour_width=3, contour_color='steelblue')
return wordcloud.generate(" ".join(nouns)).to_image()
my_word_cloud(documents['bible'])
my_word_cloud(documents['forgein_affair'])
my_word_cloud(documents['justice'])
Zastanówmy się nad jeszcze jednym zagadnieniem - jak pogrupować te terminy ze względu na dziedzinę? Zagadnienie to nosi nazwę klasyfikacji tematycznej. A dzięki pewnemu XIX-wiecznemu niemieckiemu matematykowi możliwe jest przeprowadzenie tego procesu automatycznie. Matematyk ten nosił nazwisko Peter Gustav Lejeune Dirichlet, a metoda klasyfikacji nazywa się LDA (Latent Dirichlet Allocation).