KWT-2024/lab/lab_02.ipynb

14 KiB

Logo 1

Komputerowe wspomaganie tłumaczenia

2. Zaawansowane użycie pamięci tłumaczeń [laboratoria]

Rafał Jaworski (2021)

Logo 2

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 ice_lookup(sentence, prev_sentence, next_sentence):
    # Wyniki dopasowania ICE
    ice_matches = []

    # Iterujemy przez pamięć tłumaczeń, pomijając pierwszy i ostatni element dla bezpieczeństwa kontekstowego
    for index in range(1, len(translation_memory) - 1):
        # Pobieramy obecne, poprzednie i następne zdania z TM
        prev_tm_sentence, _ = translation_memory[index - 1]
        current_tm_sentence, current_tm_translation = translation_memory[index]
        next_tm_sentence, _ = translation_memory[index + 1]

        # Sprawdzamy, czy wszystkie trzy zdania zgadzają się z odpowiednikami w TM
        if (prev_tm_sentence == prev_sentence and current_tm_sentence == current_sentence  and next_tm_sentence == next_sentence):
            ice_matches.append(current_tm_translation)

    return ice_matches

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:

  1. $\forall_{x,y} d(x,y)\geqslant 0$
  2. $\forall_{x,y} d(x,y)=0 \Leftrightarrow x=y$
  3. $\forall_{x,y} d(x,y)=d(y,x)$
  4. $\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, ponieważ w tej funkcji interesuje nas tylko długość zdania, tzn. drugi warunek nie będzie spełniony

Przykład: kot != bok, a dla tej funkcji zwróci 0

Spełnione warunki: 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, spełnia wszystkie warunki

Sprawdzenie dla warunku 4

# x == y i y == z
print(sentence_distance("kot", "kot") + sentence_distance("kot", "kot") >= sentence_distance("kot", "kot"))

# x == y i y != z
print(sentence_distance("kot", "kot") + sentence_distance("kot", "pies") >= sentence_distance("kot", "pies"))

# x != y i y == z
print(sentence_distance("kot", "pies") + sentence_distance("pies", "pies") >= sentence_distance("kot", "pies"))

# x != y i y != z
print(sentence_distance("kot", "pies") + sentence_distance("pies", "kot") >= sentence_distance("kot", "kot"))
True
True
True
True

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ź:

  • Dystans Levenshteina jest zawsze nieujemny
  • Jeśli dwa ciągi są identyczne, nie potrzeba żadnych operacji do przekształcenia jednego w drugi
  • Dystans Levenshteina jest symetryczny, ponieważ liczba operacji wymaganych do przekształcenia ciągu A w ciąg B jest taka sama jak liczba operacji potrzebnych do przekształcenia ciągu B w ciąg A
  • Dystans Levenshteina spełnia nierówność trójkąta. Można to uzasadnić rozważając, że przekształcenie ciągu X w Y przez ciąg pośredni Z (najpierw przekształcając X w Z, a następnie Z w Y) nie będzie wymagać więcej operacji niż bezpośrednie przekształcenie X w Y

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")
2

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
levenshtein_similarity('Spróbuj wyłączyć i włączyć komputer', 'Nie próbuj wyłączać i włączać drukarki')
0.631578947368421

Ć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.

# Write a fuzzy_lookup function that will search the translation memory for all sentences whose Levenshtein similarity to the searched sentence is greater than or equal to a set threshold.
def fuzzy_lookup(sentence, threshold):
    fuzzy_matches = []

    # Iterujemy przez pamięć tłumaczeń
    for tm_sentence, tm_translation in translation_memory:
        # Sprawdzamy, czy podobieństwo Levenshteina jest większe niż próg
        if levenshtein_similarity(sentence, tm_sentence) >= threshold:
            fuzzy_matches.append(tm_translation)

    return fuzzy_matches