12 KiB
Komputerowe wspomaganie tłumaczenia
2. Zaawansowane użycie pamięci tłumaczeń [laboratoria]
Rafał Jaworski (2021)
Wiemy już, do czego służy pamięć tłumaczeń. Spróbujmy przeprowadzić mały research, którego celem będzie odkrycie, w jaki sposób do pamięci tłumaczeń podchodzą najwięksi producenci oprogramowania typu CAT.
Ćwiczenie 1: Wykonaj analizę funkcjonalności pamięci tłumaczeń w programach SDL Trados Studio 2021 oraz Kilgray memoQ. Dla obu programów wypisz funkcje, które są związane z TM oraz zaznacz, które funkcje są wspólne dla obu programów oraz których funkcji Tradosa brakuje w memoQ oraz odwrotnie.
Odpowiedź:
Jedną z funkcji dostępnych we wszystkich większych programach do wspomagania tłumaczenia jest znajdowanie bardzo pewnych dopasowań w pamięci tłumaczeń. Są one zwane **ICE** (In-Context Exact match) lub 101% match. Są to takie dopasowania z pamięci tłumaczeń, dla których nie tylko zdanie źródłowe z TM jest identyczne z tłumaczonym, ale także poprzednie zdanie źródłowe z TM zgadza się z poprzednim zdaniem tłumaczonym oraz następne z TM z następnym tłumaczonym.
Rozważmy przykładową pamięć tłumaczeń z poprzednich zajęć (można do niej dorzucić kilka przykładów):
translation_memory = [
('Wciśnij przycisk Enter', 'Press the ENTER button'),
('Sprawdź ustawienia sieciowe', 'Check the network settings'),
('Drukarka jest wyłączona', 'The printer is switched off'),
('Wymagane ponowne uruchomienie komputera', 'System restart required')
]
Ćwiczenie 2: Zaimplementuj funkcję ice_lookup, przyjmującą trzy parametry: aktualnie tłumaczone zdanie, poprzednio tłumaczone zdanie, następne zdanie do tłumaczenia. Funkcja powinna zwracać dopasowania typu ICE. Nie pozwól, aby doszło do błędów podczas sprawdzania pierwszego i ostatniego przykładu w pamięci (ze względu na brak odpowiednio poprzedzającego oraz następującego przykładu).
def exact_match(sentence):
for key, entry in enumerate(translation_memory):
if entry[0] == sentence:
return key, entry[1]
return None, None
def has_exact_match_on_index(index, sentence):
return translation_memory[index][0] == sentence
def ice_lookup(sentence, prev_sentence, next_sentence):
index, match = exact_match(sentence)
trans_length = len(translation_memory)
if index is None:
return []
if next_sentence \
and index < trans_length \
and not has_exact_match_on_index(index + 1, next_sentence):
return []
if prev_sentence \
and index > 0 \
and not has_exact_match_on_index(index - 1, prev_sentence):
return []
return [match]
Inną powszechnie stosowaną techniką przeszukiwania pamięci tłumaczeń jest tzw. fuzzy matching. Technika ta polega na wyszukiwaniu zdań z pamięci, które są tylko podobne do zdania tłumaczonego. Na poprzednich zajęciach wykonywaliśmy funkcję tm_lookup, która pozwalała na różnicę jednego słowa.
Zazwyczaj jednak funkcje fuzzy match posiadają znacznie szersze możliwości. Ich działanie opiera się na zdefiniowaniu funkcji $d$ dystansu pomiędzy zdaniami $x$ i $y$. Matematycznie, funkcja dystansu posiada następujące właściwości:
- $\forall_{x,y} d(x,y)\geqslant 0$
- $\forall_{x,y} d(x,y)=0 \Leftrightarrow x=y$
- $\forall_{x,y} d(x,y)=d(y,x)$
- $\forall_{x,y,z} d(x,y) + d(y,z)\geqslant d(x,z)$
Rozważmy następującą funkcję:
def sentence_distance(x,y):
return abs(len(y) - len(x))
Ćwiczenie 3: Czy to jest poprawna funkcja dystansu? Które warunki spełnia?
Odpowiedź: Nie. 1, 3, 4.
A teraz spójrzmy na taką funkcję:
def sentence_distance(x,y):
if (x == y):
return 0
else:
return 3
Ćwiczenie 4: Czy to jest poprawna funkcja dystansu? Które warunki spełnia?
Odpowiedź: Tak. 1, 2, 3, 4.
Wprowadźmy jednak inną funkcję dystansu - dystans Levenshteina. Dystans Levenshteina pomiędzy dwoma łańcuchami znaków definiuje się jako minimalną liczbę operacji edycyjnych, które są potrzebne do przekształcenia jednego łańcucha znaków w drugi. Wyróżniamy trzy operacje edycyjne:
- dodanie znaku
- usunięcie znaku
- zamiana znaku na inny
Ćwiczenie 5: Czy dystans Levenshteina jest poprawną funkcją dystansu? Uzasadnij krótko swoją odpowiedź sprawdzając każdy z warunków.
Odpowiedź: Tak.
- Liczba operacji wykonanych nie może być ujemna.
- Gdy x == y, nie są wymagane żadne operacje edycyjne, więc wynik funkcji to 0.
- Zmiana jednego łańcucha znaków w drugi, wymaga tyle samo operacji edycji, co zmiana drugiego w pierwszy. Studia -> Studiel = 2; Studiel -> Studia = 2; 2 == 2
- Istnieją trzy opcje
- Jeżeli x == y == z, więc 0 + 0 == 0
- Jeżeli x == y, x != z, a x -> z = n, to y -> z = n więc albo 0 + n == n, albo n + n > 0
- Jeżeli x != y != z to im z jest bliżej do x, tym jest dalej od y (jednostką odległości jest liczba przekształceń). Można by to przedstawić graficznie jako trójkąt (x, y, z). z stanowi punkt na pośredniej drodze pomiędzy x i y, która nie może być dłuższa niż droga bezpośrednia - wynika to z własności trójkąta. Studia -> Studiel = 2; Studiel -> udia = 4; udia -> Studia = 2; 2 + 4 > 2; 2 + 2 == 4
W Pythonie dostępna jest biblioteka zawierająca implementację dystansu Levenshteina. Zainstaluj ją w swoim systemie przy użyciu polecenia:
pip3 install python-Levenshtein
I wypróbuj:
from Levenshtein import distance as levenshtein_distance
levenshtein_distance("kotek", "kotki")
Funkcja ta daje nam możliwość zdefiniowania podobieństwa pomiędzy zdaniami:
def levenshtein_similarity(x,y):
return 1 - levenshtein_distance(x,y) / max(len(x), len(y))
Przetestujmy ją!
levenshtein_similarity('Program jest uruchomiony', 'Program jest uruchamiany')
0.9166666666666666
levenshtein_similarity('Spróbuj wyłączyć i włączyć komputer', 'Spróbuj włączyć i wyłączyć komputer')
0.9428571428571428
Ćwiczenie 6: Napisz funkcję fuzzy_lookup, która wyszuka w pamięci tłumaczeń wszystkie zdania, których podobieństwo Levenshteina do zdania wyszukiwanego jest większe lub równe od ustalonego progu.
def fuzzy_lookup(sentence, threshold):
results = []
for entry in translation_memory:
if levenshtein_similarity(entry[0], sentence) >= threshold:
results.append(entry[1])
return results