Arvutiteaduse instituut
  1. Kursused
  2. 2016/17 kevad
  3. Programmeerimise alused II (LTAT.TK.001)
EN
Logi sisse

Programmeerimise alused II 2016/17 kevad

  • Kursuse info
  • 1. Kahemõõtmeline järjend
  • 2. Kahekordne tsükkel
  • 3. Andmestruktuurid
  • 4. Viitamine ja muteerimine
  • 5. Testimine ja silumine. Rekursioon

5.1 Testimine ja silumine 5.2 Rekursioon: sissejuhatus 5.3 Rekursioonist detailsemalt 5.4 Silmaring: Keeletöötlus

  • 6. Rekursioon II
  • 7. Projekt?
  • Korraldajad
V OSA sisukord

5.4 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
  • Arvutiteaduse instituut
  • Loodus- ja täppisteaduste valdkond
  • Tartu Ülikool
Tehniliste probleemide või küsimuste korral kirjuta:

Kursuse sisu ja korralduslike küsimustega pöörduge kursuse korraldajate poole.
Õppematerjalide varalised autoriõigused kuuluvad Tartu Ülikoolile. Õppematerjalide kasutamine on lubatud autoriõiguse seaduses ettenähtud teose vaba kasutamise eesmärkidel ja tingimustel. Õppematerjalide kasutamisel on kasutaja kohustatud viitama õppematerjalide autorile.
Õppematerjalide kasutamine muudel eesmärkidel on lubatud ainult Tartu Ülikooli eelneval kirjalikul nõusolekul.
Tartu Ülikooli arvutiteaduse instituudi kursuste läbiviimist toetavad järgmised programmid:
euroopa sotsiaalfondi logo