Miután elméletben felvázoltunk egy lehetőséget arra, hogyan lehet párhuzamos, kétnyelvű korpuszban egymásnak megfelelő nyelvi elemeket találni, ideje kipróbálni, hogyan működik az elképzelés a valóságban: egyfajta automatizált szótár-írásra szeretnénk a számítógépet rábírni.
Bemutattunk egy módszert, amely elméletileg képes megkeresni a kétnyelvű szövegben az egymásnak megfelelő szópárokat: a bootstrapping lényege az, hogy sokszor ismételve az algoritmust kialakul a teljes korpusz szintjén egy egyensúlyi állapot, amelyben minden szó a neki leginkább megfelelőhöz illeszkedik. Az algoritmusnak természetesen fogalma sincsen a jelentésekről, abból a naív feltételezésből indul ki, hogy ha egy szó sok angol nyelvű mondatban szerepel, akkor annak a magyar párja valószínűleg sok, azzal párhuzamos magyar mondatban is megjelenik.
A módszer viszonylag hibatűrő, mivel nem teljes bizonyosságra törekszik, csak valószínűségeket állapít meg: rossz minőségő bemeneti adat is adhat olyan eredményeket, amelyek alapján megkereshető a legkevésbbé rossz megoldás.
A nyelvi adat pedig nagyon is rossz minőségű: sok szó az egyik vagy a másik nyelvben eltűnik vagy megjelenik (segédszavak, kötőszavak, stb), sok szónak vannak szinonímái, és ugyanannak a szónak több alakja is létezik. A magyar esetében ez különösen így van, hiszen ragozó nyelvről van szó.
Ezért először is a bemenő adatokon érdemes elvégezni egy szótövezést (egy stemming-et). Érdekes megfigyelni, hogy míg enélkül egy magyar nyelvű szöveg 2-3 szor annyi szóformát tartalmaz, mint egy angol, a stemming után a szóformák száma nagyságrendileg megegyezik. Én ehhez az NLTK-ba beépített snowball stemmer-t használom (se magyarul, se angolul nem tökéletes)
A program és az eredmények előtt néhány szót kell ejteni az adatokról is: a próba a BME MOKK által létrehozott (és itt szabadon hozzáférhető: ftp://ftp.mokk.bme.hu/Hunglish2/ ) Hunglish korpusz egy kis részén történt, nevezetesen a számítógépes programok üzeneteit tartalmazó adatokkal, körülbelül 6 MB szöveggel. Ebben a szövegben nagyon gyakoriak a visszatérő szavak, így ez viszonylag hálás teszt-anyag. A programunk egy sqlite adatbázist generál, amely az egymásnak valamely mértékben megfelelő szópárokat, illetve a megfelelés valószínűségét tartalmazza (ez utóbbi a negyedik oszlopban szereplő tört).
Ezen kívül kapunk még két táblát arról is, hogy melyik szó összesen hányszor fordul elő a korpuszban -- ezek is fontos adatok:
A táblákba kicsit belelapozva látható, hogy az eredmények messze nem tökéletesek, de valamiféle rend felfedezhető bennük. Tapasztalat, hogy egy bizonyos korpuszméret alatt teljesen használhatatlanok az eredmények; 6MB körül már kezd bizony rend mutatkozni. Kérdés, vajon tízszer ekkora korpusz mit mutatna -- sajnos, a korpuszméret szűk keresztmetszet: 6 MB bemenő adatot feldolgozni körülbelül 3 órába tellik.
Most pedig lássuk magát a programot. Ez a fő ciklus, amely a lényegi munkát végző függvényt meghívja:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# a lenyegi resz itt tortenik: kiszamoljuk a t-matrixot, aztan ujraszamoljuk a kapott ertekekkel, | |
# mindezt 'hanyszor' alkalommal | |
mtrx={} | |
hanyszor=3 | |
while hanyszor > 0: | |
print "@" | |
mtrx=bootstrap(mtrx) | |
hanyszor-=1 | |
# itt kiirjuk egy sqlite filebe | |
kimutatas_sqlite(mtrx) |
Ez pedig a bootstrap() függvény, amely működését az előző posztban már bemutattuk. Gyakorlatilag az ott szereplő pszeudokód megvalósítása python nyelven.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# a bootstrap fuggveny csinalja a lenyegi munkat: szo-szo megfeleleseket keres, majd egy ezek valoszinuseget | |
# tartalmazo t-matrix-al ter vissza. A kiindulo ertekeket parameterkent kapja, vagyis a tobbszori ujraszamolas alapjan | |
# egyre pontosabb adatokat kaphatunk | |
def bootstrap(t_matrix): | |
t_matrix_uj={} # ez lesz a visszatero matrix | |
for i in range(max_id+1)[1:]: # annyiszor fut vegig a ciklus, ahany mondatunk van | |
print hanyszor, #ez azert van, hogy lassuk, eppen hol tart a ciklus | |
print i | |
# beolvassuk a ket mondatot a mondat-adatbazisbol | |
mondat1=mondat_olvas("nyelv1",i) | |
mondat2=mondat_olvas("nyelv2",i) | |
# a ket mondatot egyforma hosszura igazitjuk, hogy illeszkedjenek egymashoz (ez a permutaciok miatt kell) | |
(mondat1,mondat2)=kiegyenlit(mondat1,mondat2) | |
if (len(mondat1) > 20): continue ## ha tul hosszu a mondat, ugrunk -- nem birna ki a szamitogep | |
#letrehozzuk a masidok nyelvu mondat osszes(valoban nem) letezo elrendezeset (listakat tartalmo lista) | |
permutaciok=permutacio_csinal(mondat2) | |
# ebbe a listaba fog kerulni a minden egyes permutacio altal elert pontszam (ez mutatja meg, mennyire jo az adott elrendezes) | |
ertekek=[] | |
for j in range(len(permutaciok)): # ez a ciklus szamolja ki oket | |
ertekek.append(ertek_szamol(mondat1,permutaciok[j],t_matrix)) | |
ertekek=[x / sum(ertekek) for x in ertekek] #normalizaljuk az ertekeket | |
# ismet vegighaladunk az elrendezeseket | |
for j in range(len(permutaciok)): | |
# minden egyes szot szemugyre veszunk: az elredendezes altal elert ertekeket HOZZADJUK a t-matrix szo-szo mezojebe | |
for k in range(len(permutaciok[j])): | |
x=mondat1[k] # az oszlop az elso nyelv mondatanak k-szava | |
y=permutaciok[j][k] # a sor az adott j permutacio k-szava | |
ertek=ertekek[j] # az ertek, amelyet visszairunk, az ehhez a permutaciohoz tartozo pontszam | |
t_matrix_uj=matrix_add(t_matrix_uj,x,y,ertek) | |
# vegul az egesz matrixot soronkent normalizaljuk | |
t_matrix_uj=normalizal(t_matrix_uj) | |
return t_matrix_uj |
A teljes, működő program ezen a linken tekinthető meg -- a kódot többnyire kommentáltam.
(működni nem fog, mert bemenetként egy másik program által generált sqlite fájlt kér -- ha valaki nagyon szeretné otthon futtatni, kérésre elküldök mindent)
Már megjegyeztük, hogy az egyik szűk keresztmetszet a futási idő; a másik a mondatok hossza. Belátható, hogy egy bizonyos mondathossz után a lehetséges permutációk száma használhatatlanul nagyra nő. Ezért nem írjuk fel az összeset, csak egy részüket -- ily módon egy mintát veszünk az összes lehetséges változat halmazából, és reménykedünk, hogy az elég reprezentatív lesz. A harmadik problémás rész a szavak egymásnak való megfeleléseit tartalmazó mátrix mérete -- ennek nagy része amúgy is nullákat tartalmaz, amit egyszerűen felesleges tárolni. Jelenleg egy elég csúnya, barkács-megoldás szerepel a kódban, ennél elegánsabb módszernek is kell léteznie.
Nincsenek megjegyzések:
Megjegyzés küldése