V OSA sisukord |
5.6 KEELETÖÖTLUS
Kuigi tundub, et arvutid tegelevad peamiselt arvude ja matemaatiliste tehetega, pole see kaugeltki nii. Kui mõtleme enda tegevuse peale, siis suure osa ajast otsime me veebist enda jaoks vajalikku materjali, kirjutame tekstitöötlusprogrammi abil mitmesuguseid tekste või suhtleme tuttavatega. Ka arvuti ei võta teksti kui lihtsalt sümbolite jada, nii oskab ta sageli aru saada meie otsingumustrist ja pakkuda vastuseks ka sünonüüme või samu sõnu teises käändes sisaldavaid tekste, parandada õigekirjavigasid jne. Leidub hulgaliselt tõlkimisprogramme (näiteks kõigile kasutatavad Google Translate, TÜ masintõlge). Arvutit saame juhtida hääle abil ja talle teksti dikteerida (kõnetuvastus, näiteks http://bark.phon.ioc.ee/webtrans) ning tekste lugemise asemel ka kuulata (kõnesüntees, näiteks https://www.eki.ee/heli).
Tekstiga tegelemiseks sobib Python väga hästi. Teksti tõsisemaks töötlemiseks vajame küll spetsiaalseid lisamooduleid, mis oskaksid morfoloogilist analüüsi (sõnavormide põhjal sõna algvormi leidmist) ja sünteesi (sõnade algvormide põhjal sõnavormide koostamist), tunneksid keele süntaksit (kuidas võivad sõnad lauses paikneda), semantikat (ehk seda, mida tekst tegelikult tähendab) jms. Sellised vahendeid on palju, levinumateks näideteks on loomuliku keele töötluseks moodul NLTK või eesti keele jaoks mõeldud EstNLTK. Samas saame tekstist rohkem infot saada ka ilma erivahenditeta.
Sõnestamine ja sagedusloend
Tekst koosneb sõnadest ning teksti uurimist võimegi alustada selle sõnestamisest ehk sõnadeks jagamisest. Tegelikult kasutatakse mõistet “sõne” (i. k. string) sageli laiemas tähenduses ning sõltuvalt kontekstist loetakse sõnedeks lisaks sõnadele ka arvud, kirjavahemärgid jne, kuid momendil pole see väga tähtis. Püüame tekstist paremat pilti saada, leides selles olevad sagedasemad sõnavormid. Võtame aluseks ühe teksti (meie näites Arvo Valtoni “Kanaromaani”) ja püüame seda sõnestada. Selleks loeme failist ridade kaupa teksti, tükeldame iga rea tühikute järgi sõnadeks ning lisame sõnavormide järjendisse.
# Avame faili fail = open("valton_kanaromaan.txt", "r", encoding="utf-8") # Järjend sõnavormide jaoks sonavormid = [] for rida in fail: #print(rida) # Eemaldame reavahetused jms rea algusest ja lõpust rida = rida.strip() # Töötleme rida sel juhul, kui see pole tühi if rida != "": # Tükeldame rea tühikute kohalt sõnedeks sonad = rida.split() # Iga tüki lisame sõnavormide järjendisse, kui seda seal veel pole for sona in sonad: if sona not in sonavormid: sonavormid.append(sona) # Sulgeme faili fail.close() print(sonavormid)
Saadud järjendil on mitu puudust:
- järjendis eristatakse suuri ja väikseid tähti (kuigi vahel on see vajalik, näiteks nimede puhul);
- järjendis esinevad sõnad koos kirjavahemärkidega (nii on seal nii sõna “maailma” kui ka “maailma,”);
- me ei tea midagi sõnavormide esinemissageduse kohta.
Seega parem lahenduskäik oleks järgmine: väiketähestame teksti, kustutame ära kõik kirjavahemärgid (teeme selleks praegu suhteliselt lihtsakoelise funktsiooni) ning asendame järjendi sõnastikuga, kus võtmeks on sõnavorm, väärtuseks aga tema esinemissagedus. Nii saame sagedusloendi, millest trükime välja suurima esinemissagedusega sõnavormid.
def eemaldaPunktuatsioon(tekst): tekst = tekst.replace(".", "") tekst = tekst.replace(",", "") tekst = tekst.replace("!", "") tekst = tekst.replace("?", "") return tekst # Avame faili fail = open("valton_kanaromaan.txt", "r", encoding="utf-8") # Sõnastik sõnavormide jaoks sonavormid = {} for rida in fail: #print(rida) # Eemaldame reavahetused jms rea algusest ja lõpust rida = rida.strip() # Väiketähestame teksti rida = rida.lower() # Eemaldame punktuatsiooni rida = eemaldaPunktuatsioon(rida) # Töötleme rida sel juhul, kui see pole tühi if rida != "": # Tükeldame rea tühikute kohalt sõnedeks sonad = rida.split() # Kui sellise võtmega elementi sõnastikus veel pole, # lisame selle koos väärtusega 1 # (selleks momendiks on seda vormi esinenud tekstis 1 kord), # kui aga on, suurendame selle võtmega elemendi väärtust ühe võrra for sona in sonad: if sona not in sonavormid: sonavormid[sona] = 1 else: sonavormid[sona] += 1 # Sulgeme faili fail.close() print("Sõnavormide arv:", len(sonavormid)) loendur = 1 for vorm in sorted(sonavormid, key=sonavormid.get, reverse=True): print(loendur, vorm, sonavormid[vorm]) loendur += 1 if loendur > 30: break
Tähtsamad sõnavormid
Sagedusloendit vaadates näeme, et kuigi seal esinevad sõnad “tsee”, “kana” ja “kukk” (mis on just sellele tekstile iseloomulikud), ei ütle enamik seal olevatest sõnadest (nt “oli”, “ja”, “aga”) meile teksti kohta otseselt midagi. Kuni sagedusloend sisaldab sõnu, mis on keeles üldiselt kõrge sagedusega, ei saa me head pilti sellest, millised sõnad on käesolevale tekstile iseloomulikud. Seega tuleks meil jätta alles vaid need sõnavormid, mis pole kogu eesti keeles sagedased. Kuidas seda teha? Üheks võimaluseks on kasutada sagedaste sõnavormide loendit (eesti_keele_sonavormid.txt, kopeeritud https://keeleressursid.ee/et/83-article/clutee-lehed/256-sagedusloendid): võtta sealt mingi arv sagedasemaid eestikeelseid sõnavorme ning jätta oma teksti sagedusloendisse alles vaid sõnad, mis sagedaste sõnavormide loendis ei esine. Teeme seda järgnevas näites.
def eemaldaPunktuatsioon(tekst): tekst = tekst.replace(".", "") tekst = tekst.replace(",", "") tekst = tekst.replace("!", "") tekst = tekst.replace("?", "") return tekst eesti_keele_sagedasemad = [] # Avame eesti keele sagedasemate sõnavormide faili fail = open("eesti_keele_sonavormid.txt", "r", encoding="utf-8") for rida in fail: # Tükeldame rea tühiku järgi, failis on esimesel kohal sõnavormi sagedus, # teisel kohal sõnavorm ise rida = rida.split() eesti_keele_sagedasemad.append(rida[1]) # Võtame arvesse vaid sagedasemaid eesti keele sõnavorme if len(eesti_keele_sagedasemad) > 200: break fail.close() #print(eesti_keele_sagedasemad) # Avame faili fail = open("valton_kanaromaan.txt", "r", encoding="utf-8") # Sõnastik sõnavormide jaoks sonavormid = {} for rida in fail: # Eemaldame reavahetused jms rea algusest ja lõpust rida = rida.strip() # Väiketähestame teksti rida = rida.lower() # Eemaldame punktuatsiooni rida = eemaldaPunktuatsioon(rida) # Töötleme rida sel juhul, kui see pole tühi if rida != "": # Tükeldame rea tühikute kohalt sõnedeks sonad = rida.split() # Kui sellise võtmega elementi sõnastikus veel pole, # lisame selle koos väärtusega 1 # (selleks momendiks on seda vormi esinenud tekstis 1 kord), # kui aga on, suurendame selle võtmega elemendi väärtust ühe võrra for sona in sonad: # Lisame sagedusloendisse vaid sõnad, mis ei esine if sona not in eesti_keele_sagedasemad: if sona not in sonavormid: sonavormid[sona] = 1 else: sonavormid[sona] += 1 # Sulgeme faili fail.close() print("Sõnavormide arv:", len(sonavormid)) loendur = 1 for vorm in sorted(sonavormid, key=sonavormid.get, reverse=True): print(loendur, vorm, sonavormid[vorm]) loendur += 1 if loendur > 30: break
Kirjavahemärkide kustutamine oli meie eelmises programmis pisut kohmakas: asenduskäsuga pidime iga märgi eraldi eemaldama. Parem lahendus oleks kasutada regulaaravaldist -- sõnesid kirjeldavat üldistavat mustrit --, mis võimaldaks ühe käsuga leida üles kõik kirjavahemärgid ja eemaldada need. Samuti võimaldavad regulaaravaldised mugavalt otsida mingile mallile vastavaid sümbolijärjendeid, näiteks neid, mille teine täht on vokaal ning milles on neli tähte. Regulaaravaldiste kasutamist Pythonis tutvustatakse lähemalt siin.
Edasiarenduseks
Kui koguksime andmeid paljude erinevast žanrist või erinevatel teemadel tekstide kohta, tekiksid meil sagedasemate sõnavormidega sõnastikud iga tekstiklassi kohta (kultuuriartiklid, majandusartiklid ja spordiartiklid või ilukirjandus, ajakirjandus ja seadused). Seejärel oleks võimalik uusi tekste sõnavara kattuvuse alusel klassifitseerida ühte või teise rubriiki kuuluvateks.
Tegelesime praegu sõnavormide, mitte algvormide ehk lemmadega. Nii saime sagedusloendisse eraldi nii sõnad “kana” ja “kanad” kui sõnad “muna” ja “mune”, kuigi sisulises mõttes on tegemist sama mõistega. Reeglina kasutatakse siiski algvorme ning nende saamiseks vajaksime rohkem keeletöötlusvahendeid, näiteks morfoloogilist analüsaatorit.
Kui sooviksime tuvastada teksti keelt, võiksime toimida samamoodi, andes ette erinevate keelte sõnastikke. Keeletuvastuseks on olemas aga ka lihtsam moodus, mis töötab päris hästi, kui võimalikke keeli pole väga palju. Nimelt on igas keeles tähtede sagedus veidi erinev, näiteks eesti keeles esineb kõige rohkem tähte “a”, inglise keeles aga on kõige sagedasem täht “e” ning “a” on alles kolmandal kohal peale tähte “t” (ingl https://en.wikipedia.org/wiki/Letter_frequency). Nii võiksime ainuüksi mõne tekstirea analüüsi järel öelda, millise keele tekstiga on tõenäoliselt tegemist, ning aluseks poleks vaja sõnade sagedusloendeid, vaid erinevate keelte tähtede sagedusloendeid.
V OSA sisukord |