KWT-2024/lab/lab_03.ipynb

29 KiB
Raw Blame History

Logo 1

Komputerowe wspomaganie tłumaczenia

3. Terminologia [laboratoria]

Rafał Jaworski (2021)

Logo 2

import collections
lista1 = [3,4,5,4,4,7,8,7]
lista2 = [3,4,5,4,4,7,8,7]
print((collections.Counter(lista) + collections.Counter(lista2)).most_common(5))

Na dzisiejszych zajęciach zajmiemy się bliżej słownikami używanymi do wspomagania tłumaczenia. Oczywiście na rynku dostępnych jest bardzo wiele słowników w formacie elektronicznym. Wiele z nich jest gotowych do użycia w SDL Trados, memoQ i innych narzędziach CAT. Zawierają one setki tysięcy lub miliony haseł i oferują natychmiastową pomoc tłumaczowi.

Problem jednak w tym, iż często nie zawierają odpowiedniej terminologii specjalistycznej - używanej przez klienta zamawiającego tłumaczenie. Terminy specjalistyczne są bardzo częste w tekstach tłumaczonych ze względu na następujące zjawiska:

  • Teksty o tematyce ogólnej są tłumaczone dość rzadko (nikt nie tłumaczy pocztówek z pozdrowieniami z wakacji...)
  • Te same słowa mogą mieć zarówno znaczenie ogólne, jak i bardzo specjalistyczne (np. "dziedziczenie" w kontekście prawnym lub informatycznym)
  • Klient używa nazw lub słów wymyślonych przez siebie, np. na potrzeby marketingowe.

Nietrywialnymi zadaniami stają się: odnalezienie terminu specjalistycznego w tekście źródłowym oraz podanie prawidłowego tłumaczenia tego terminu na język docelowy

Brzmi prosto? Spróbujmy wykonać ręcznie tę drugą operację.

Ćwiczenie 1: Podaj tłumaczenie terminu "prowadnice szaf metalowych" na język angielski. Opisz, z jakich narzędzi skorzystałaś/eś.

Odpowiedź:

  • DeepL: metal cabinet slides / metal cabinet guides
  • Model GPT-3.5: metal cabinet slides / metal wardrobe rails.
  • Model GPT-4: guides for metal cabinets / metal cabinet guides
  • Google-translate: metal cabinet guides
  • www.tlumaczangielskopolski.pl: metal cabinet guides

W dalszych ćwiczeniach skupimy się jednak na odszukaniu terminu specjalistycznego w tekście. W tym celu będą potrzebne dwie operacje:

  1. Przygotowanie słownika specjalistycznego.
  2. Detekcja terminologii przy użyciu przygotowanego słownika specjalistycznego.

Zajmijmy się najpierw krokiem nr 2 (gdyż jest prostszy). Rozważmy następujący tekst:

text = " For all Java programmers:"
text += " This section explains how to compile and run a Swing application from the command line."
text += " For information on compiling and running a Swing application using NetBeans IDE,"
text += " see Running Tutorial Examples in NetBeans IDE. The compilation instructions work for all Swing programs"
text += " — applets, as well as applications. Here are the steps you need to follow:"
text += " Install the latest release of the Java SE platform, if you haven't already done so."
text += " Create a program that uses Swing components. Compile the program. Run the program."

Załóżmy, że posiadamy następujący słownik:

dictionary = ['program', 'application', 'applet', 'compile']

Ćwiczenie 2: Napisz program, który wypisze pozycje wszystkich wystąpień poszczególnych terminów specjalistycznych. Dla każdego terminu należy wypisać listę par (pozycja_startowa, pozycja końcowa).

import re
from pprint import pprint

def terminology_lookup():
    answer = {pattern:[] for pattern in dictionary}
    low_text = text.lower()
    for pattern in dictionary:
        offset = 0
        start = 0
        end = 0
        while True:
            match = (re.search(pattern,low_text[offset:]))
            if not match:
                break
            else:
                start += match.start()
                end = +match.end()
                offset += end

                answer[pattern].append((start,end))
    pprint(answer)
    #return answer

terminology_lookup()
{'applet': [(302, 308)],
 'application': [(80, 91), (153, 84), (300, 158)],
 'compile': [(56, 63), (497, 448)],
 'program': [(14, 21), (284, 277), (454, 177), (495, 48), (505, 17)]}

Zwykłe wyszukiwanie w tekście ma pewne wady. Na przykład, gdy szukaliśmy słowa "program", złapaliśmy przypadkiem słowo "programmer". Złapaliśmy także słowo "programs", co jest poprawne, ale niepoprawnie podaliśmy jego pozycję w tekście.

Żeby poradzić sobie z tymi problemami, musimy wykorzystać techniki przetwarzania języka naturalnego. Wypróbujmy pakiet spaCy:

pip3 install spacy

oraz

python3 -m spacy download en_core_web_sm

pip3 install spacy
Defaulting to user installation because normal site-packages is not writeable
Requirement already satisfied: spacy in /usr/local/lib/python3.9/dist-packages (3.4.1)
Requirement already satisfied: spacy-legacy<3.1.0,>=3.0.9 in /usr/local/lib/python3.9/dist-packages (from spacy) (3.0.10)
Requirement already satisfied: spacy-loggers<2.0.0,>=1.0.0 in /usr/local/lib/python3.9/dist-packages (from spacy) (1.0.3)
Requirement already satisfied: murmurhash<1.1.0,>=0.28.0 in /usr/local/lib/python3.9/dist-packages (from spacy) (1.0.8)
Requirement already satisfied: cymem<2.1.0,>=2.0.2 in /usr/local/lib/python3.9/dist-packages (from spacy) (2.0.6)
Requirement already satisfied: preshed<3.1.0,>=3.0.2 in /usr/local/lib/python3.9/dist-packages (from spacy) (3.0.7)
Requirement already satisfied: thinc<8.2.0,>=8.1.0 in /usr/local/lib/python3.9/dist-packages (from spacy) (8.1.1)
Requirement already satisfied: wasabi<1.1.0,>=0.9.1 in /usr/local/lib/python3.9/dist-packages (from spacy) (0.10.1)
Requirement already satisfied: srsly<3.0.0,>=2.4.3 in /usr/local/lib/python3.9/dist-packages (from spacy) (2.4.4)
Requirement already satisfied: catalogue<2.1.0,>=2.0.6 in /usr/local/lib/python3.9/dist-packages (from spacy) (2.0.8)
Requirement already satisfied: typer<0.5.0,>=0.3.0 in /usr/local/lib/python3.9/dist-packages (from spacy) (0.4.2)
Requirement already satisfied: pathy>=0.3.5 in /usr/local/lib/python3.9/dist-packages (from spacy) (0.6.2)
Requirement already satisfied: tqdm<5.0.0,>=4.38.0 in /usr/local/lib/python3.9/dist-packages (from spacy) (4.64.1)
Requirement already satisfied: numpy>=1.15.0 in /usr/local/lib/python3.9/dist-packages (from spacy) (1.21.6)
Requirement already satisfied: requests<3.0.0,>=2.13.0 in /usr/local/lib/python3.9/dist-packages (from spacy) (2.28.1)
Requirement already satisfied: pydantic!=1.8,!=1.8.1,<1.10.0,>=1.7.4 in /usr/local/lib/python3.9/dist-packages (from spacy) (1.9.2)
Requirement already satisfied: jinja2 in /usr/local/lib/python3.9/dist-packages (from spacy) (3.1.2)
Requirement already satisfied: setuptools in /usr/lib/python3/dist-packages (from spacy) (52.0.0)
Requirement already satisfied: packaging>=20.0 in /usr/local/lib/python3.9/dist-packages (from spacy) (21.3)
Requirement already satisfied: langcodes<4.0.0,>=3.2.0 in /usr/local/lib/python3.9/dist-packages (from spacy) (3.3.0)
Requirement already satisfied: pyparsing!=3.0.5,>=2.0.2 in /usr/lib/python3/dist-packages (from packaging>=20.0->spacy) (2.4.7)
Requirement already satisfied: smart-open<6.0.0,>=5.2.1 in /usr/local/lib/python3.9/dist-packages (from pathy>=0.3.5->spacy) (5.2.1)
Requirement already satisfied: typing-extensions>=3.7.4.3 in /usr/local/lib/python3.9/dist-packages (from pydantic!=1.8,!=1.8.1,<1.10.0,>=1.7.4->spacy) (4.3.0)
Requirement already satisfied: charset-normalizer<3,>=2 in /usr/local/lib/python3.9/dist-packages (from requests<3.0.0,>=2.13.0->spacy) (2.1.1)
Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.9/dist-packages (from requests<3.0.0,>=2.13.0->spacy) (3.4)
Requirement already satisfied: urllib3<1.27,>=1.21.1 in /usr/local/lib/python3.9/dist-packages (from requests<3.0.0,>=2.13.0->spacy) (1.26.12)
Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.9/dist-packages (from requests<3.0.0,>=2.13.0->spacy) (2022.9.14)
Requirement already satisfied: blis<0.10.0,>=0.7.8 in /usr/local/lib/python3.9/dist-packages (from thinc<8.2.0,>=8.1.0->spacy) (0.9.1)
Requirement already satisfied: confection<1.0.0,>=0.0.1 in /usr/local/lib/python3.9/dist-packages (from thinc<8.2.0,>=8.1.0->spacy) (0.0.1)
Requirement already satisfied: click<9.0.0,>=7.1.1 in /usr/local/lib/python3.9/dist-packages (from typer<0.5.0,>=0.3.0->spacy) (8.1.3)
Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.9/dist-packages (from jinja2->spacy) (2.1.1)
python3 -m spacy download en_core_web_sm
Defaulting to user installation because normal site-packages is not writeable
DEPRECATION: https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-3.4.1/en_core_web_sm-3.4.1-py3-none-any.whl#egg=en_core_web_sm==3.4.1 contains an egg fragment with a non-PEP 508 name pip 25.0 will enforce this behaviour change. A possible replacement is to use the req @ url syntax, and remove the egg fragment. Discussion can be found at https://github.com/pypa/pip/issues/11617
Collecting en-core-web-sm==3.4.1
  Downloading https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-3.4.1/en_core_web_sm-3.4.1-py3-none-any.whl (12.8 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 12.8/12.8 MB 45.8 MB/s eta 0:00:0000:010:01
[?25hRequirement already satisfied: spacy<3.5.0,>=3.4.0 in /usr/local/lib/python3.9/dist-packages (from en-core-web-sm==3.4.1) (3.4.1)
Requirement already satisfied: spacy-legacy<3.1.0,>=3.0.9 in /usr/local/lib/python3.9/dist-packages (from spacy<3.5.0,>=3.4.0->en-core-web-sm==3.4.1) (3.0.10)
Requirement already satisfied: spacy-loggers<2.0.0,>=1.0.0 in /usr/local/lib/python3.9/dist-packages (from spacy<3.5.0,>=3.4.0->en-core-web-sm==3.4.1) (1.0.3)
Requirement already satisfied: murmurhash<1.1.0,>=0.28.0 in /usr/local/lib/python3.9/dist-packages (from spacy<3.5.0,>=3.4.0->en-core-web-sm==3.4.1) (1.0.8)
Requirement already satisfied: cymem<2.1.0,>=2.0.2 in /usr/local/lib/python3.9/dist-packages (from spacy<3.5.0,>=3.4.0->en-core-web-sm==3.4.1) (2.0.6)
Requirement already satisfied: preshed<3.1.0,>=3.0.2 in /usr/local/lib/python3.9/dist-packages (from spacy<3.5.0,>=3.4.0->en-core-web-sm==3.4.1) (3.0.7)
Requirement already satisfied: thinc<8.2.0,>=8.1.0 in /usr/local/lib/python3.9/dist-packages (from spacy<3.5.0,>=3.4.0->en-core-web-sm==3.4.1) (8.1.1)
Requirement already satisfied: wasabi<1.1.0,>=0.9.1 in /usr/local/lib/python3.9/dist-packages (from spacy<3.5.0,>=3.4.0->en-core-web-sm==3.4.1) (0.10.1)
Requirement already satisfied: srsly<3.0.0,>=2.4.3 in /usr/local/lib/python3.9/dist-packages (from spacy<3.5.0,>=3.4.0->en-core-web-sm==3.4.1) (2.4.4)
Requirement already satisfied: catalogue<2.1.0,>=2.0.6 in /usr/local/lib/python3.9/dist-packages (from spacy<3.5.0,>=3.4.0->en-core-web-sm==3.4.1) (2.0.8)
Requirement already satisfied: typer<0.5.0,>=0.3.0 in /usr/local/lib/python3.9/dist-packages (from spacy<3.5.0,>=3.4.0->en-core-web-sm==3.4.1) (0.4.2)
Requirement already satisfied: pathy>=0.3.5 in /usr/local/lib/python3.9/dist-packages (from spacy<3.5.0,>=3.4.0->en-core-web-sm==3.4.1) (0.6.2)
Requirement already satisfied: tqdm<5.0.0,>=4.38.0 in /usr/local/lib/python3.9/dist-packages (from spacy<3.5.0,>=3.4.0->en-core-web-sm==3.4.1) (4.64.1)
Requirement already satisfied: numpy>=1.15.0 in /usr/local/lib/python3.9/dist-packages (from spacy<3.5.0,>=3.4.0->en-core-web-sm==3.4.1) (1.21.6)
Requirement already satisfied: requests<3.0.0,>=2.13.0 in /usr/local/lib/python3.9/dist-packages (from spacy<3.5.0,>=3.4.0->en-core-web-sm==3.4.1) (2.28.1)
Requirement already satisfied: pydantic!=1.8,!=1.8.1,<1.10.0,>=1.7.4 in /usr/local/lib/python3.9/dist-packages (from spacy<3.5.0,>=3.4.0->en-core-web-sm==3.4.1) (1.9.2)
Requirement already satisfied: jinja2 in /usr/local/lib/python3.9/dist-packages (from spacy<3.5.0,>=3.4.0->en-core-web-sm==3.4.1) (3.1.2)
Requirement already satisfied: setuptools in /usr/lib/python3/dist-packages (from spacy<3.5.0,>=3.4.0->en-core-web-sm==3.4.1) (52.0.0)
Requirement already satisfied: packaging>=20.0 in /usr/local/lib/python3.9/dist-packages (from spacy<3.5.0,>=3.4.0->en-core-web-sm==3.4.1) (21.3)
Requirement already satisfied: langcodes<4.0.0,>=3.2.0 in /usr/local/lib/python3.9/dist-packages (from spacy<3.5.0,>=3.4.0->en-core-web-sm==3.4.1) (3.3.0)
Requirement already satisfied: pyparsing!=3.0.5,>=2.0.2 in /usr/lib/python3/dist-packages (from packaging>=20.0->spacy<3.5.0,>=3.4.0->en-core-web-sm==3.4.1) (2.4.7)
Requirement already satisfied: smart-open<6.0.0,>=5.2.1 in /usr/local/lib/python3.9/dist-packages (from pathy>=0.3.5->spacy<3.5.0,>=3.4.0->en-core-web-sm==3.4.1) (5.2.1)
Requirement already satisfied: typing-extensions>=3.7.4.3 in /usr/local/lib/python3.9/dist-packages (from pydantic!=1.8,!=1.8.1,<1.10.0,>=1.7.4->spacy<3.5.0,>=3.4.0->en-core-web-sm==3.4.1) (4.3.0)
Requirement already satisfied: charset-normalizer<3,>=2 in /usr/local/lib/python3.9/dist-packages (from requests<3.0.0,>=2.13.0->spacy<3.5.0,>=3.4.0->en-core-web-sm==3.4.1) (2.1.1)
Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.9/dist-packages (from requests<3.0.0,>=2.13.0->spacy<3.5.0,>=3.4.0->en-core-web-sm==3.4.1) (3.4)
Requirement already satisfied: urllib3<1.27,>=1.21.1 in /usr/local/lib/python3.9/dist-packages (from requests<3.0.0,>=2.13.0->spacy<3.5.0,>=3.4.0->en-core-web-sm==3.4.1) (1.26.12)
Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.9/dist-packages (from requests<3.0.0,>=2.13.0->spacy<3.5.0,>=3.4.0->en-core-web-sm==3.4.1) (2022.9.14)
Requirement already satisfied: blis<0.10.0,>=0.7.8 in /usr/local/lib/python3.9/dist-packages (from thinc<8.2.0,>=8.1.0->spacy<3.5.0,>=3.4.0->en-core-web-sm==3.4.1) (0.9.1)
Requirement already satisfied: confection<1.0.0,>=0.0.1 in /usr/local/lib/python3.9/dist-packages (from thinc<8.2.0,>=8.1.0->spacy<3.5.0,>=3.4.0->en-core-web-sm==3.4.1) (0.0.1)
Requirement already satisfied: click<9.0.0,>=7.1.1 in /usr/local/lib/python3.9/dist-packages (from typer<0.5.0,>=0.3.0->spacy<3.5.0,>=3.4.0->en-core-web-sm==3.4.1) (8.1.3)
Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.9/dist-packages (from jinja2->spacy<3.5.0,>=3.4.0->en-core-web-sm==3.4.1) (2.1.1)
Installing collected packages: en-core-web-sm
Successfully installed en-core-web-sm-3.4.1
✔ Download and installation successful
You can now load the package via spacy.load('en_core_web_sm')
import spacy
nlp = spacy.load("en_core_web_sm")

doc = nlp(text)

for token in doc:
    print(token.lemma_)
 
for
all
Java
programmer
:
this
section
explain
how
to
compile
and
run
a
swing
application
from
the
command
line
.
for
information
on
compile
and
run
a
swing
application
use
NetBeans
IDE
,
see
Running
Tutorial
Examples
in
NetBeans
IDE
.
the
compilation
instruction
work
for
all
swing
program
—
applet
,
as
well
as
application
.
here
be
the
step
you
need
to
follow
:
install
the
late
release
of
the
Java
SE
platform
,
if
you
have
not
already
do
so
.
create
a
program
that
use
swing
component
.
compile
the
program
.
run
the
program
.

Sukces! Nastąpił podział tekstu na słowa (tokenizacja) oraz sprowadzenie do formy podstawowej każdego słowa (lematyzacja).

Ćwiczenie 3: Zmodyfikuj program z ćwiczenia 2 tak, aby zwracał również odmienione słowa. Na przykład, dla słowa "program" powinien znaleźć również "programs", ustawiając pozycje w tekście odpowiednio dla słowa "programs". Wykorzystaj właściwość idx tokenu.

import re

def terminology_lookup():
    answer = {pattern:[] for pattern in dictionary}

    for pattern in dictionary:
        for token in doc:
            if pattern in token.lemma_:
                answer[pattern].append((token.idx,token.idx+len(token.lemma_)))
    return answer

terminology_lookup()
{'program': [(14, 24), (291, 298), (468, 475), (516, 523), (533, 540)],
 'application': [(80, 91), (164, 175), (322, 333)],
 'applet': [(302, 308)],
 'compile': [(56, 63), (134, 141), (504, 511)]}

Teraz czas zająć się problemem przygotowania słownika specjalistycznego. W tym celu napiszemy nasz własny ekstraktor terminologii. Wejściem do ekstraktora będzie tekst zawierający specjalistyczną terminologię. Wyjściem - lista terminów.

Przyjmijmy następujące podejście - terminami specjalistycznymi będą najcześćiej występujące rzeczowniki w tekście. Wykonajmy krok pierwszy:

Ćwiczenie 4: Wypisz wszystkie rzeczowniki z tekstu. Wykorzystaj możliwości spaCy.

import spacy
def get_nouns(text):
    nlp = spacy.load("en_core_web_sm")
    doc = nlp(text)
    nouns = [token.text for token in doc if token.pos_ == "NOUN"]
    return nouns

get_nouns(text)
['programmers',
 'section',
 'Swing',
 'application',
 'command',
 'line',
 'information',
 'Swing',
 'application',
 'compilation',
 'instructions',
 'Swing',
 'programs',
 'applets',
 'applications',
 'steps',
 'release',
 'platform',
 'program',
 'Swing',
 'components',
 'program',
 'program']

Teraz czas na podliczenie wystąpień poszczególnych rzeczowników. Uwaga - różne formy tego samego słowa zliczamy razem jako wystąpienia tego słowa (np. "program" i "programs"). Najwygodniejszą metodą podliczania jest zastosowanie tzw. tally (po polsku "zestawienie"). Jest to słownik, którego kluczem jest słowo w formie podstawowej, a wartością liczba wystąpień tego słowa, wliczając słowa odmienione. Przykład gotowego tally:

tally = {"program" : 4, "component" : 1}

Ćwiczenie 5: Napisz program do ekstrakcji terminologii z tekstu według powyższych wytycznych.

from collections import Counter
import spacy

def extract_terms(text):
    nlp = spacy.load("en_core_web_sm")
    doc = nlp(text)
    tally = {}
    nouns = [token.lemma_ for token in doc if token.pos_ == "NOUN"]
    nouns_counts = Counter(nouns)
   
    for word, count in nouns_counts.items():
        tally.update({word: count})
    return tally

extract_terms(text)
{'programmer': 1,
 'section': 1,
 'swing': 4,
 'application': 3,
 'command': 1,
 'line': 1,
 'information': 1,
 'compilation': 1,
 'instruction': 1,
 'program': 4,
 'applet': 1,
 'step': 1,
 'release': 1,
 'platform': 1,
 'component': 1}

Ćwiczenie 6: Rozszerz powyższy program o ekstrację czasowników i przymiotników.

from pprint import pprint
from collections import Counter
import spacy

def extract_terms(text):
    
    nlp = spacy.load("en_core_web_sm")
    doc = nlp(text)
    
    nouns, verbs, adjectives = [], [], []
    tally = {"nouns": {}, "verbs": {}, "adjectives": {}}
    
    for token in doc:
        if token.pos_ == "NOUN":
            nouns.append(token.lemma_)
        elif token.pos_ == "VERB":
            verbs.append(token.lemma_)
        elif token.pos_ == "ADJ":
            adjectives.append(token.lemma_)
    
    nouns_counts = Counter(nouns)
    verbs_counts = Counter(verbs)
    adjectives_counts = Counter(adjectives)

    for word, count in nouns_counts.items():
         tally["nouns"].update({word: count})
 
    for word, count in verbs_counts.items():
         tally["verbs"].update({word: count})
      
    for word, count in adjectives_counts.items():
         tally["adjectives"].update({word: count})

    pprint(tally)
    #return tally

extract_terms(text)
{'adjectives': {'late': 1},
 'nouns': {'applet': 1,
           'application': 3,
           'command': 1,
           'compilation': 1,
           'component': 1,
           'information': 1,
           'instruction': 1,
           'line': 1,
           'platform': 1,
           'program': 4,
           'programmer': 1,
           'release': 1,
           'section': 1,
           'step': 1,
           'swing': 4},
 'verbs': {'compile': 3,
           'create': 1,
           'do': 1,
           'explain': 1,
           'follow': 1,
           'install': 1,
           'need': 1,
           'run': 3,
           'see': 1,
           'use': 2,
           'work': 1}}