Bár ismeretes, hogy már létező
megoldást újra megírni nem igazán érdemes, talán alapvető
eljárások esetében mégis más a helyzet: tanulni és megérteni
szerintem sok dolgot így lehet a legjobban. Tehát, egy klasszikus
keyword-extraction eljárással és egy hasznos python könyvtárral
való ismerkedésem dokumentálása következik.
1. A probléma: szeretnénk
kideríteni, hogy egy rakás szöveges dokumentum, mondjuk egy weblap
oldalai körülbelül miről
szólnak. Építhetünk tag-felhőt vagy minden oldalhoz
odarendelhetjük a legjellemzőbb három taget (a Számítógépes
Nyelvészeten is van ilyen, a cikk alján látható!). Ki szeretnénk nyerni a kulcsszavakat, keyword
extractiont -t csinálni.
2. A legjellemzőbb
kifejezések nyilván gyakran fordulnak elő -- elég megszámolni a
szavak előfordulását. Sajnos a természetes nyelv alattommos, és
hatalmas túlsúlyba kerülnek a semmire sem használható
segédszavak. Ismert a stopword-szűrés technika, de ez sem
megoldás mindenre.
3.
Kifinomultabb a TF-IDF módszer (term frequency / inverse document
freqency), mint a keyword kinyerés elterjedt eszköze (leírás itt
és itt).
Az eljárás lényege, hogy egy dokumentum jellemző kulcsszavait nem
csak a dokumentumhoz képest, hanem más (lehetőleg hasonló)
dokumentumokhoz képest is vizsgáljuk: ha az index.hu újságírói
szívesen használnak bizonyos kifejezéseket, akkor ezt figyelembe
kell vennünk a kulcsszavak keresése közben, és az ilyen
"általában gyakori" kifejezéseket "le kell
pontoznunk".
4. A TF-IDF modell
gyakorlati megvalósításához érdemes röviden áttekinteni a
dokumentum-vektortér modellt.
Minden
dokumentumot vektorként ábrázolunk, amely vektornak az egyes elemei az egyes kifejezések előfordulásainak számát
tartalmazzák.
Nyilvánvaló,
hogy ehhez szükségünk van egy lexikonra is. Pl, ha a lexikonunk
így néz ki:
1: szép
2: csúnya
3: kutya
akkor a "szép
kutya" szöveget tartalmazó dokumentumot a következő vektor
reprezentálja:
(1,0,1)
A valóságban természetesen több ezer dimenziós vektorokkal lesz dolgunk. A vektorokká alakítást elsősorban rendelkezésre álló matematikai eszköztár indokolja -- ezt fogjuk mi is kihasználni a python kód megírásakor.
5. Minden
dokumentumot vektoronként ábrázolunk az n-dimenziós
vektorterünkben, majd ezekből a vektorokból d x n kiterjedésű
mátrixot írunk fel. Nevezzük ezt TF-mátrixnak. Valahogy így
1.
dokumentum: 1 3 5 0 1
2.
dokumentum: 5 1 1 0 0
3. dokumentum:
8 5 3 1 0
A mátrix oszlopai
az adott dokumentumban az adott kifejezés előfordulási
gyakoriságát tartalmazzák.
6.
Létrehozunk egy document freqency vektort, amely azt fogja
tartalmazni, hogy melyik kifejezés hány dokumentumban fordul elő.
Vagyis, azt derítjük ki, hogy mennyire szokványos az adott
kifejezés az egész szettre nézve. Ezt például megoldhatjuk úgy,
hogy a TF mátrixból csinálunk egy másik ugyanolyan kiterjedésű
mátrixot; az új mátrix elemei 0 értéket tartalmaznak, ha a TF mátrixban 0 található az adott helyen, 1-et, ha nem 0.
Ezek után oszloponként összeadjuk az elemeket, és meg is kaptuk a
document freqency vektort. A példában pl.
(3, 3, 3, 1, 1)
7. Az
inverse document frequency értéke wikipediás definíció szerint
log(dokumentumok
száma/kifejezés előfordulása)
Amennyiben nincs hatalmas mennyiségű adatunk, a logaritmus
befejezése nem biztos, hogy szükséges (hiszen itt a nagyságrendi
viszonyok kezdenek számítani), számolhatunk egyszerű reciprokkal
is. A lényeg: az inverz
érték
azt jelenti, hogy minél gyakoribb egy kifejezés, annál kisebb
lesz a kapott érték.
8. Ezek után nincs más hátra, mint elvégezni a TF * IDF
műveletet: minden dokumentum minden egyes kifejezésének
előfordulási értékét megszorozni az adott kifejezés IDF értékével. Ily módon a sok dokumentumban előforduló kifejezések lejjebb csúsznak a
ranglistán, míg a csak az adott dokumentumra jellemző kifejezések
jobb pontszámot érnek el.
9. A mátrixxá alakítás itt mutatja meg az igazi előnyét: a
fenti művelet egyetlen mátrix-szorzás, nevezetesen a TF mátrixot
szozzuk meg egy másikkal. Ez a másik mátrix nem más, mint az a
diagonális mátrix, amelyet az IDF vektor elemeiből állítunk elő.
Az eredményként kapott mátrix elemei az egyes dokumentumok
kifejezéseinek relatív fontosságát fogják tartalmazni.
(mint ismeretes, diagonális mátrixxal szorozni gyakorlatilag annyit
tesz, mint minden egyes oszlop elemeit egyenként végigszorozni a
diagonális mátrix adott oszlopának egyes elemével).
Felmerülhet a kérdés, hogy miért kell diagonális mátrixxal
bohóckodni, miért nem lehet oszloponként szorozni a TF mátrixot
IDF vektor soron következő elemével (skalárszorzatként). Lehet,
sőt, érdemes is, mert gyorsabb, az eredmény pedig ugyanaz. Csak az első megoldás papíron
jobban néz ki;)
10. A python numpy könyvtára bevezet egy array nevű adattípust
-- ezek n-dimenziós mátrixok. Az adattípus rendkívül rugalmasan
kezelhető, teljesen illeszkedik a python sztenderd típusaihoz,
minden mátrixalgebrai művelet elvégezhető rajta, és nem
utolsósorban nagyon gyors adatkezelést tesz lehetővé (az ezért
felelős részek C-ben íródtak). Ízelítő itt.
A lexikon elkészítése nem túl izgalmas: a dokumentumszett minden
előforduló új kifejezését betesszük a lexikon nevű listába. A TF
mátrix létrehozása is rendkívül egyszerű (nullákkal töltjük
fel):
tf=zeros((len(doks), len(lexikon)), dtype=int16)
Ezek után újra végigszaladunk az összes dokumentumon, és
összeszámoljuk az előforduló kifejezéseket.
A DF vektort így hozzuk létre:
f=lambda x: 0 if x==0 else 1
df=array([f(x) for x in tf.flat]).reshape(tf.shape).sum(axis=0)
idf=[1/x for x in df]
A sum(axis=0) oszloponként számolja össze az egyeseket, amelyeket
az f=lambda x: 0 if x==0 else 1 mini-fügvénnyel állítunk elő a
TF függvény elemeiből. Hogy ne kelljen két egymásba ágyazott
ciklust használni, a tf.flat kifejezéssel egyszerűen "kiterítjük"
az elemeit, majd a reshape(tf.shape) kifejezéssel újra összerakjuk.
Az IDF vektor előállításához a triviális reciprok-formulát
(1/x) alkalmazzuk, majd a TF mátrixot oszloponként szorozzuk az IDF
elemeivel:
tfidf=tf*idf
Figyelem! Ez nem mátix-szorzás! (az a dot() fügvénnyel csinálnánk)
10+1. A TF vektort normalizálhatnánk is. Ez azt jelenti, hogy a
vektor hosszát 1 egység hosszúságúra nyessük -- ennek az volna az értelme,
hogy a különböző hosszúságú dokumentumok ne okozzanak
kavarodást a rendszerünkben.
Nincsenek megjegyzések:
Megjegyzés küldése