PYTHON liburua/Aplikazioak III: Testuak lantzen

Wikibookstik

12. Aplikazioak III: testuak lantzen[aldatu]

Python programazio-lengoaiaren erabilera oso zabalduta dago testuak prozesatzen dituen arloan. Lana asko errazten du arlo honetarako bereziki diseinatu zen NLTK liburutegiaren erabilerak.

Landuko dituzun testuak eskuratzeko, hainbat aukera dituzu gaur egun: i) zuk zeuk lortutako testua erabiltzea, ii) edukia Internetetik eskuratzea, eta iii) NLTK tresna multzoarekin eskaintzen diren testuak hartzea. Lehenengo bi kasuetan 7. eta 10. kapituluetan landu duguna oso baliagarria izango zaizu. Lortutako testu horiek NLTKko testuekin integratu ahal izango ditugu, gero bertako metodoekin erabili ahal izateko.

Kapitulu hau osatzeko, TAPE Testu-analisirako PERL erremintak liburua (http://ueu.org/download/liburua/PERL.BERRIA.osoa.pdf) kontsultatzea merezi du. Liburu horretan testuen gaineko ariketa asko daude, baina Python lengoaia erabili beharrean Perl lengoaia erabiltzen da hor.

Ariketa sinple batez hasiko gara.

Pentsa dezagun telebista-programa batean euskarazko hitzekin joko bat sortu behar dutela eta jakin nahi dutela euskaraz letra bakoitzak duen robabilitatea. Horretarako testu luze bat hartu behar da, eta etra bakoitzaren agerpen kopurua kontatu Pythoneko hiztegi-egitura batean. Bukaeran, maiztasun absolutuak jakinda eta letra guztiak kontatu baditugu, erraza izango da maiztasun erlatiboak (probabilitateak) ere kalkulatzea, ordenatzea eta inprimatzea.

Erabiltzen den testu-fitxategi luzea (wiki_eu_labur.txt) geroxeago deskribatuko dugu kapitulu honetan.

12.1. programa.

from google.colab import drive
drive.mount('/content/drive')
sst_home = 'drive/My Drive/Colab Notebooks/Python_UEU_20/kodea_izen_luzeak/kap12_testuak/'
hizt = dict()
alfa = "abcdefghijklmnñopqrstuvwxyz"
kont=0

fitx = 'wiki_eu_labur.txt'
for lerro in open(sst_home + fitx, encoding='utf-8').readlines():
   lerrox = lerro.lower()
   for k in lerrox:
      if (k in alfa):
         kont=kont+1
         if (k in hizt):
            hizt[k] += 1  # inkrem
         else:
            hizt[k] = 1   # 1 balioa

for j in sorted(hizt):
   print("%s \t %d \t %2.4f" % (j, hizt[j], hizt[j]*100/kont))
*xekuzioaren emaitza hau da:
a 	 78778 	 15.8231
b 	 11142 	 2.2379
c 	 2681 	 0.5385
d 	 14587 	 2.9299
e 	 57703 	 11.5900
f 	 3095 	 0.6217
g 	 11182 	 2.2460
h 	 8339 	 1.6749
i 	 45784 	 9.1960
j 	 2913 	 0.5851
k 	 24661 	 4.9533
l 	 18872 	 3.7906
m 	 8542 	 1.7157
n 	 34554 	 6.9404
o 	 29830 	 5.9916
p 	 6059 	 1.2170
q 	 141 	 0.0283
r 	 41902 	 8.4163
s 	 15150 	 3.0430
t 	 36044 	 7.2397
u 	 22583 	 4.5360
v 	 1315 	 0.2641
w 	 1509 	 0.3031
x 	 1642 	 0.3298
y 	 1065 	 0.2139
z 	 17715 	 3.5582
ñ 	 79 	 0.0159

Probabilitate horiek nahiko fidagarriak dira, baina testuetatik kalkulatu direnez, hitz deklinatuetako atzizkietan sarri azaltzen diren letrek ("a" atzizkia adibidez) behar baino agerpen gehiago jasoko dituzte. Probabilitate zehatzagoak behar izanez gero, hobe litzateke hiztegi batetik abiatzea eta ez testu-fitxategi batetik.

Ondoren testuak lantzeko teknikak eta adibideak hiru ataletan banatu ditugu: Interneteko erabilera-testuak eskuratzeko, NLTK paketearen erabilera bertako testuekin, eta NLTKren erabilera gure testuekin.

12.1. TESTUAK INTERNETETIK ESKURATZEA[aldatu]

Aurreko kapituluetan azaldutako hainbat modulu eta metodo erabilita, adibide sinple bat burutuko dugu, hainbat webguneren edukia arakatu eta parametro gisa pasatzen den hitzaren agerpen kopurua kalkulatu nahi dugu. 12.2a programan azaltzen da. Bertan ikus daitekeenez, "re" (espresio erregularrak) eta "urllib" moduluak erabiltzen dira lana errazteko. Lehenaren bitartez, bilaketak espresio erregularren bitartez egin daitezke; bigarrenaren bitartez, berriz, sareko edukiak eskura ditzakegu.

Webguneen URLak konstante moduan definituta daude zerrenda batean, baina hitza parametro gisa sartuko dugu.

12.2a programa. Hitz baten kontaketa zenbait webgunetan:

import re
import sys
from urllib.request import urlopen

# webgune baten testua irakurtzeko eta testua bilatzeko
def kontatu_url(web, hitz):
    maiz = 0
    web_testu = urlopen(web)
    for lerro in web_testu:
        lerro_deskod = lerro.decode('latin-1')
        espres = re.compile(r"("+hitz+".*)")
        maiz = maiz + len(re.findall(espres, lerro_deskod))
    return (maiz)

# webguneen zerrendako emaitzak lortu eta hiztegi batean gordetzeko
def kontatu_n_url(web_zerrenda, hitz):
    emaitzak = {}
    for i in web_zerrenda:
        emaitzak[i] = kontatu_url(i, hitz)
    return(emaitzak)

# hiztegiaren datuak inprimatzeko
def idatz_hizt (hizt):
    for gako, balio in hizt.items():
        print ("%s:\t %d" % (gako, balio))

# programa nagusia
# aztertzeko kanalak
kanalak = ["http://www.ueu.eus",
           "http://www.sustatu.eus",
           "http://www.berria.eus"]

#aztertzeko hitza edo hitz-hasiera
h = "euskara"
print(h, "hitzaren agerpenak: ")
idatz_hizt(kontatu_n_url(kanalak, h))

Programa horren exekuzioan agertuko dena honelako zerbait izango da:

euskara hitzaren agerpenak: 
http://www.ueu.eus:	 0
http://www.sustatu.eus:	 8
http://www.berria.eus:	 7

Espresio erregularra erabiltzen da hitzaren deklinabidea kontuan hartzeko. ".*" jarri dugu parametroaren atzean ondoren edozein karaktere ager daitekeela adierazteko. Parametroan "euskara" zehaztu beharrean "euskaraz" zehaztuz gero, agerpen kopurua jaitsiko da, baita espresio erregularrean aipatutako ".*" atzizkia kenduta ere. Edozein kasutan, geroago azalduko dugunez, espresio erregular sinpleekin ezin da lematizazio zuzen eta oso bat egin. Adibidez 'egin.*' espresioarekin ez da 'egiten' harrapatuko.

Webguneen beste ezaugarri interesgarri bat esteken agerpena da. Estekak baliatuz testu gehiago eskura daitezke, eta modu errekurtsiboan jarraituko bagenie, sarea arakatzeko robot moduko bat programatuko genuke. 12.2b. ariketan webgune bakoitzaren barruan estekak bilatu ditugu, eta esteken bitartez orri berriak eskuratu ditugu kontaketa burutzeko. Estekak lortzeko funtzioa eta deitzen dion programa-zatia bakarrik aldatu ditugu liburura.

12.2b programa. Estekak lortzeko funtzioa eta erabilera:

import re
import sys
from urllib.request import urlopen

def lortu_estekak(kanal):
    joateko = []
    espres_erreg = r"<a href=\"(http.+?)\"[>\s]"
    web_oso = urlopen(kanal)
    eduki = str(web_oso.read())
    estekak = re.findall(espres_erreg, eduki, re.I)
    egiaz_domeinu = kanal+r".+"
    egiaz_mota = r".*^(.(?!(pdf|jpg|docx?|png|gif|bmp|tiff)))*$"
    for esteka in estekak:
        domeinu_egoki = re.search(egiaz_domeinu, esteka)
        mota_egoki = re.search(egiaz_mota, esteka)
        if domeinu_egoki and mota_egoki:
            joateko.append(esteka)
    # bikoiztuak ezabatu
    joateko_multzo = set(joateko)
    joateko_multzo.discard(kanal) 	# kendu hasierakoa
    joateko = list(joateko_multzo)
    joateko.insert(0, kanal)		# gehitu hasierakoa hasieran
    return(joateko)


# webgune baten testua irakurtzeko eta testua bilatzeko
def kontatu_url(web, hitz):
    maiz = 0
    espres = re.compile(r"("+hitz+".*)")
    zerr = lortu_estekak(web)	# estekak lortu (jatorria ere)
    for url in zerr:
#        print(url)
        try:# erroreak saihestu
            web_testu = urlopen(url)
            for lerro in web_testu:
                lerro_deskod = lerro.decode('latin-1')
                maiz = maiz + len(re.findall(espres, lerro_deskod))
        except:
            continue
    return (maiz)

# webguneen zerrendako emaitzak lortu eta hiztegi batean gordetzeko
def kontatu_n_url(web_zerrenda, hitz):
    emaitzak = {}
    for i in web_zerrenda:
        print(i, ".......")
        emaitzak[i] = kontatu_url(i, hitz)
    return(emaitzak)

# hiztegiaren datuak inprimatzeko
def idatz_hizt (hizt):
    for gako, balio in hizt.items():
        print ("%s:\t %d" % (gako, balio))


# programa nagusia
# aztertzeko kanalak
kanalak = ["http://www.ueu.eus",
                "http://www.sustatu.eus",
                "http://www.berria.eus"]

#aztertzeko hitza edo hitz-hasiera
h = "euskara"
print(h, "hitzaren agerpenak: ")
idatz_hizt(kontatu_n_url(kanalak, h))

Programa horren exekuzioan agertuko dena halako zerbait izango da:

euskara hitzaren agerpenak:
http://www.ueu.eus .......
http://www.sustatu.eus .......
http://www.berria.eus .......
http://www.ueu.eus:	     57
http://www.sustatu.eus:	 10
http://www.berria.eus:	 9

12.2. NLTK TRESNAK[aldatu]

NLTK-k oso erraza egiten du testuarekin lan egitea, tokenizatzaileak, lematizatzaileak, entitate izendunen ezagutzaileak eta abar eskaintzen baititu erraz asko erabiltzeko. Tresna horietako batzuen erabilera ikusiko dugu atal honetako kasu praktikoen bidez.

Dena dela, luze joko luke aukera guztiak garatzeak. Adibidez, luzeegi joko luke grafikoak egiteko metodo guztiak lantzeak edo ikasketa automatikoaren bidez sailkatzaileak sortu ahal izateko metodo guztiak lantzeak. Izan ere, NLTK-k berak liburu oso bat eskaintzen du Interneten libreki eskuragarri helbide honetan: http://www.nltk.org/book/. Testuekin lan egin nahi izanez gero, eta are gehiago, hizkuntzaren prozesaketan (NLP, Natural Language Processing ingelesez) lan egin nahi izanez gero, oso gomendagarria da liburua irakurtzea, oso argi azalduta dagoen informazio asko baitauka.

Esan bezala, euskarazko antzeko beste liburu bat dago kontzeptu horiek lantzen dituena, https://www.buruxkak.eus/liburua/tape-testu-analisirako-perl-erremintak/478 baina kasu horretan Perl programazio-lengoaiaren bitartez landuta.

NLTK-k barneratzen dituen modulu batzuk, hauek dira:

Eginkizuna Moduluak Funtzionaltasuna
Testu-bildumak corpus Testu-bildumak eta lexikoak eskuratzea
Testu-bildumak corpus Testu-bildumak eta lexikoak eskuratzea
Testu-prozesaketa tokenize, stem Hitzetan banatzea (tokenizatzea) lematizazioa *
Agerkidetzen bilaketa collocations Neurri estatistikoak osagaien agerkidetzaren probabilitatea kalkulatzeko (t- test, chi-squared point-wise mutual information)
POS etiketatzea tag Desanbiguazioa testuingurua kontuan hartuta. Hainbat metodo: n-gram, backof,f Brill, HMM, TnT
Ikasketa automatikoa classify, cluster, tbl Hainbat metodo: erabaki-zuhaitzak, entropia maximoa, naive Bayes, EM, k-means
Azaleko sintaxia chunk Espresio erregularrak, n-gramak, izen propioak
Analisi sintaktikoa parse, ccg Hainbat aukera: chart feature- based baterakuntza probabilistikoa mendekotasunak...
Ebaluazio-metrikak metrics Doitasuna eta estaldura (precision recall) Komuztadura-koefizienteak..

Liburu honetan modulu sinpleenak erabiliko ditugu, eta erabili ahala hobeto ulertu ahal izango duzu. Testu-bilduma gehienak ingelesez daudenez, hasierako adibideak ingelesezko testuekin egingo ditugu.

12.2.1. Instalazioa eta baliabideen deskarga[aldatu]

NLTK-k hainbat pakete eta corpus dauzka eskura, erraz asko erabil daitezkeenak. Erabili aurretik baina, baliabideok deskargatu behar ditugu. Horretarako Python3-n bertan funtzio-dei bakar bat erabili behar da: nltk.download() 12.1. irudia. Nola deskarga dezakegu baliabideren bat NLTKtik.

IRUDIA????

Notebook honetan NLTK paketea erabili ahal izateko, beharrezkoa da lehenago paketea instalatzea. Hori honako komando hauen bidez lortuko dugu:

!pip install --upgrade pip
!pip install nltk
import nltk
nltk.download('brown')

Aukera egokia izan daiteke baliabide denak jaistea (denbora behar da, hobeto konexio azkarra duzunean), beste bat, behar ahala instalatzea. Zeure konputagailuara jaisten baduzu, jaitsitakoa (nltk_data karpeta) erro-katalogoan (edo beste katalogo zehatz batean: /usr/share edo /usr/local/share adibidez) jartzea gomendatzen da, geroago erraz aurkitu ahal izateko.

12.2.2. Ariketa: Corpusak, zenbaketak eta stopword-ak[aldatu]

Berriki deskargatu dugun corpus bat erabiliko dugu hurrengo adibidean. Brown Corpusa ingelesezko milioi bat hitz bildu zuen lehen testu-corpusa izan zen, 1961. urtean Brown Unibertsitatean sortua. 500 jatorri ezberdinetatik jasotako testuak dira eta generoaren arabera (news, fiction, humor, romance...) sailkatuta daude.

Kasu praktiko honetan, argumentu gisa genero horietako bi landuko ditugu, bi genero horietako hitz ohikoenak lortuko ditugu.

12.3a. programa. Maiztasun handieneko hitzak:

import sys

#nltk-ren corpusa inportimportatu: brown
from nltk.corpus import brown

#generoak jaso (ingelesez)
g1 = "news"
g2 = "science_fiction"

#lehen gaiaren corpusa jaso hitz zerrenda gisa
corp1 = brown.words(categories=g1)
#bigarren generoaren corpusa jaso hitz zerrenda gisa
corp2 = brown.words(categories=g2)

#corpusetan kontaketak egin minuskulak eta maiuskulak bereizi gabe
corp1_kontaketa = nltk.FreqDist(h.lower() for h in corp1)
corp2_kontaketa = nltk.FreqDist(h.lower() for h in corp2)

maiz1=corp1_kontaketa.most_common(10)
maiz2=corp2_kontaketa.most_common(10)

#kontaketak pantailaratu
print(g1+"-ren hitz ohikoenak:")
print(maiz1)
print(g2+"-ren hitz ohikoenak:")
print(maiz2)

#dotoreago
print()
print(g1+"-ren hitz ohikoenak:")
print (' '.join(str(p[0]) for p in maiz1))
print(g2+"-ren hitz ohikoenak:")
print (' '.join(str(p[0]) for p in maiz2))
news-ren hitz ohikoenak:
[('the', 6386), (',', 5188), ('.', 4030), ('of', 2861), ('and', 2186), ('to', 2144), ('a', 2130), ('in', 2020), ('for', 969), ('that', 829)]
science_fiction-ren hitz ohikoenak:
[(',', 791), ('.', 786), ('the', 723), ('of', 329), ('to', 306), ('and', 294), ('a', 236), ('``', 235), ("''", 235), ('was', 200)]

news-ren hitz ohikoenak:
the , . of and to a in for that
science_fiction-ren hitz ohikoenak:
, . the of to and a `` '' was

Adibide gisa, "news" eta "science_fiction" generoak pasatzen baditugu, ikus dezakegunez, 10 hitz ohikoenak pantailaratzen ditugunean, puntuazio-markak, deklinabideak, artikuluak eta preposizioak maizen agertzen diren hitzak direla ikus dezakegu:

news-ren hitz ohikoenak:
[('the', 6386), (',', 5188), ('.', 4030), ('of', 2861), ('and', 2186), ('to', 2144), ('a', 2130), ('in', 2020), ('for', 969), ('that', 829)]
science_fiction-ren hitz ohikoenak:
[(',', 791), ('.', 786), ('the', 723), ('of', 329), ('to', 306), ('and', 294), ('a', 236), ('``', 235), ("''", 235), ('was', 200)]

news-ren hitz ohikoenak:
the , . of and to a in for that
science_fiction-ren hitz ohikoenak:
, . the of to and a `` '' was

12.3a. programaren kodean hainbat kontu azpimarra dezakegu: • brown objektua inportatu da nltk.corpus klasetik. Eta objektu horren generoetako (kategoriak) testuetan words metodoa aplikatu dugu azpicorpus horietan hitzak lortzeko, hitz-mailako lanketa egin ahal izateko gero.

• Moduluko nltk.FreqDist() metodoaren bidez hitz-mailako maiztasunak lortu ditugu (bikote-zerrenda bat) eta zerrenda horren gainean most_common() metodoa aplikatu dugu maiztasun handieneko 10 hitzak lortzeko.

• Kontaketetan 6. kapituluan azaldu den comprehension moduko esleipenak (zerrenda bati esleipen iteratiboa) egin dira kodea trinkotzeko.

Horrekin ez ditugu genero bakoitzeko berezitasunak ikusten. Hain ohikoak diren eta normalean esanahi txikia duten hitz horiek baztertu ohi dira azterketa lexikografikoetan eta "stopword" zerrenda bat osatu ohi da baztertu nahi diren hitzekin. NLTK-k ere stopword-zerrenda bat eskaintzen du. Alda dezagun, beraz, aurreko adibideko kodea, hitz horiek eta puntuazio-markak kontuan har ez ditzan.

12.3b. programan dago bertsio berria. Bide batez zerrendaren idazketa dotoreagoa egin dugu eta 10 aukeratu beharrean 30 aukeratu dugu.

import nltk
nltk.download('stopwords')
[nltk_data] Downloading package stopwords to /root/nltk_data...
[nltk_data]   Unzipping corpora/stopwords.zip.

True

12.3b. MaiLetra lodiko testuaztasun handieneko hitz esanguratsuak.

import nltk,sys

#nltk-ren corpusa inportatu: brown
from nltk.corpus import brown

#generoak jaso (ingelesez)
g1 = "news"
g2 = "science_fiction"

#lehen gaiaren corpusa jaso hitz zerrenda gisa
corp1 = brown.words(categories=g1)
#bigarren generoaren corpusa jaso hitz zerrenda gisa
corp2 = brown.words(categories=g2)

#ingelesezko stopword-en corpusa jaso hitz zerrenda gisa
stopwords = nltk.corpus.stopwords.words('english')

#corpusetan kontaketak egin bi baldintzakin: alfabetoko karaktereekin osatuta egotea eta stopwordsetan ez agertzea.
corp1_kontaketa = nltk.FreqDist(h.lower() for h in corp1 if
                              h.isalpha() and h.lower() not in stopwords)
corp2_kontaketa = nltk.FreqDist(h.lower() for h in corp2 if
                             h.isalpha() and h.lower() not in stopwords)

maiz1=corp1_kontaketa.most_common(30)
maiz2=corp2_kontaketa.most_common(30)

print(g1+"-ren hitz ohikoenak:")
print (' '.join(str(p[0]) for p in maiz1))
print()
print(g2+"-ren hitz ohikoenak:")
print (' '.join(str(p[0]) for p in maiz2))
said would new one last two first state year president home also made time years three house week city may school could four day committee man members back government many

science_fiction-ren hitz ohikoenak:
would could said one time ekstrohm helva mercer long like know people hal mike ship back man jack first never make years earth made mind see take well course head

Oraingoan bai, hitz esanguratsuagoak jaso ditugu irteeran. Berrien hitzen artean “state” edo “president” bezalako hitzak ikusten ditugun bitartean, zientzia-fikzioan “mercer”, “ekstrohm” edo “helva” hitzak ditugu.

said would new one last two first state year president home also made time years three house week city may school could four day committee man members back government many

science_fiction-ren hitz ohikoenak:
would could said one time ekstrohm helva mercer long like know people hal mike ship back man jack first never make years earth made mind see take well course head

12.2.3. Ariketa: Lemak eta kategoriak[aldatu]

Tagger edo POS tagger, etiketatzaile esango diogu euskaraz, ezagutzen den tresna da, erabilienetako bat hizkuntza-prozesaketan. Tresna horren bitartez hitzaren kategoria gramatikala modu automatikoan lortzen da hitzaren testuingurua kontuan hartuta. Horrela euskarazko “zuen” hitzak bi etiketa edo kategoria izan ditzake testuinguruaren arabera: aditz laguntzailea (adibidez “esan zuen”) edo izenordaina (“zuen kontura”).

Brown Corpusa erabilita 12.4. programan kategoria eta lema ohikoenak inprimatuko ditugu lehenik, adjektibo ohikoenak gero, eta, azkenik, parametro gisa sartzen dugun izenarekin konbinatzen diren adjektiboak (ingelesezko etiketatzaile honen arabera izenek “NN” etiketa dute eta adjektiboek “JJ” etiketa).

12.4. programa. Lemak eta etiketak lantzen.

import nltk
nltk.download('universal_tagset')
import nltk,sys

#nltk-ren corpusa inportatu: brown
from nltk.corpus import brown

#izena jaso (ingelesez)
iz = "house"


#corpus etiketatua jaso
corp_etik = brown.tagged_words(tagset='universal')
stopwords = nltk.corpus.stopwords.words('english')
print("Hasi da analisia ...")

#etiketen eta lemen kontaketa
etik_kontaketa = nltk.FreqDist(et for (h,et) in corp_etik if
                              h.isalpha() and h.lower() not in stopwords)
lema_kontaketa = nltk.FreqDist(h for (h,et) in corp_etik if
                              h.isalpha() and h.lower() not in stopwords)

maiz_et=etik_kontaketa.most_common(10)
maiz_lem=lema_kontaketa.most_common(10)

print("Etiketa eta lema ohikoenak:")
print(maiz_et)
print(maiz_lem)

# adjektibo ohikoenak
adj_kontaketa = nltk.FreqDist(h for (h,et) in corp_etik if et == 'JJ')
maiz_adj=adj_kontaketa.most_common(10)
print()
print("ADJ ohikoenak:")
print(maiz_adj)

# ADJ-IZE bigramak

bigr=dict()
for esaldi_et in brown.tagged_sents():
    for (h1,et1), (h2,et2) in nltk.bigrams(esaldi_et):
       if (et2=='NN' and h2==iz and et1=='JJ'):
            print(h1, h2)
print ("Bukatu da")
Hasi da analisia ...
Etiketa eta lema ohikoenak:
[('NOUN', 260583), ('VERB', 123176), ('ADJ', 73116), ('ADV', 31549), ('ADP', 8576), ('NUM', 7195), ('DET', 1683), ('X', 1209), ('PRT', 888), ('PRON', 816)]
[('one', 2873), ('would', 2677), ('said', 1943), ('could', 1580), ('time', 1556), ('two', 1311), ('may', 1292), ('first', 1242), ('like', 1237), ('man', 1151)]

ADJ ohikoenak:
[]
two-family house
empty house
own house
small house
efficient house
1800-square-foot house
7-room house
two-story house
1200-square-foot house
new house
white house
new house
halfway house
old house
old house
shiny house
American house
royal house
private house
dark house
own house
open house
pleasant house
white house
Italian house
portable house
well-kept house
rich house
rich house
silent house
Tudor-style house
big house
big house
smallish house
old house
whole house
little house
big house
two-story house
entire house
old house
new house
white house
two-story house
big house
big house
open house
respectable house
handsome house
safe house
big house

Programa exekutatzen badugu, eta parametro gisa “house” izena sartu, hauxe da exekuzioaren emaitzaren hasiera (denbora apur bat behar du):

Hasi da analisia...
etiketa eta lema ohikoenak:
[(NOUN, 260583), (VERB, 123176), (ADJ, 73116), (ADV, 31549), (ADP, 8576),
(NUM, 7195), (DET, 1683), (X, 1209), (PRT, 888), (PRON, 816)]
[(one, 2873), (would, 2677), (said, 1943), (could, 1580), (time, 1556),
(two, 1311), (may, 1292), (first, 1242), (like, 1237), (man, 1151)]
ADJ ohikoenak:
[(other, 1627), (new, 1060), (first, 947), (more, 941), (such, 903),
(many, 896), (own, 750), (good, 693), (same, 679), (little, 673)]
two-family house
empty house
own house
small house

Zoritxarrez NLTKn ez dago integratuta euskararako halako etiketatzailerik (badaude baina beste programazio-lengoaia batzuetan; adib. hemen: http://ixa.eus/node/4450).

Ondoko metodoen bitartez jakin daiteke zein hizkuntzatarako dauden eskuragarri hainbat tresna:

print("Stopwords hizkuntza hauetarako:")
print(" ".join(nltk.corpus.stopwords.fileids()))
print()
print("Stemmer hizkuntza hauetarako:")
from nltk.stem import SnowballStemmer
print(" ".join(SnowballStemmer.languages))
Stopwords hizkuntza hauetarako:
arabic azerbaijani danish dutch english finnish french german greek hungarian indonesian italian kazakh nepali norwegian portuguese romanian russian slovene spanish swedish tajik turkish

Stemmer hizkuntza hauetarako:
arabic danish dutch english finnish french german hungarian italian norwegian porter portuguese romanian russian spanish swedish

12.2.4. Ariketa: antzeko hitzak lortzen[aldatu]

Batzuetan interesgarria da ortografikoki antzekoak diren hitzak lortzea, zuzentzaile ortografikoetan esaterako. Horretarako NLTK-k edit_distance metodoa eskaintzen du, edizio-distantzian oinarritzen dena. Distantzia horrek karaktere-aldaketak, ezabatzeak edo txertatzeak kontatzen ditu. Posible da karaktere kontsekutiboen arteko trukea ere kontuan hartzea (3. parametroan 1 jarriz). Beraz, bateko distantziara dauden hitzak oso antzekoak izango dira.

Proba egiteko, 12.5. programan edit_distance metodo hori erabiltzen dugu sartutako lema oker batetik gertuen dauden lemak (bateko distantzian daudenak) lortzeko. Brown Corpusetik bildutako lema-biltegia erabiltzen da bilaketa horretan.

Definitutako bilatu_antza funtzioan maiztasun handieneko lemen biltegia eta lema bat jasotzen dira parametro gisa, eta lema horrekin antzekotasun handiena duten biltegiko lemak lortzen dira.

12.5. programa. Antzeko lemak lortzen.

import nltk,sys

#nltk-ren corpusa inportatu: brown
from nltk.corpus import brown


def bilatu_antza(maiz_lem, lem):
    dmin = 10
    zerr = [];
    for l,m in maiz_lem:
        d = nltk.edit_distance(l, lem, 1)
        if d == 1 :
            zerr.append(l)
    return zerr

# corpus etiketatua jaso
corp_etik = brown.tagged_words(tagset='universal')

# lemen kontaketa
lemak = [h for (h,et) in corp_etik if h.isalpha()]
lema_kontaketa = nltk.FreqDist(lemak)

# maiztasun handienko 10.000 lema
maiz_lem = lema_kontaketa.most_common(10000)

# hitza eskatu eta ez badago corpusean antzekoenak lortu
lem = input("Sartu lema bat (zuzen ez badago ere): ")
if lem not in lemak:
    z = bilatu_antza(maiz_lem, lem)
else:
    z = lem
print("Antzekoenak:", z)

Ondoren, proba baten emaitza dago:

Sartu lema bat (zuzen ez badago ere): tken
Antzekoenak: ['then', 'taken', 'ten', 'token']

12.3. NLTK GURE TESTUEKIN ERABILTZEA[aldatu]

Aurreko kasu praktikoetan NLTKtik ekarritako corpusarekin lan egiten ikasi dugu. Oraingo honetan euskaraz idatzitako testu handi bat lortuko dugu eta testu horiek prozesatu beharko ditugu.

Testuen jabegoarekin arazorik ez edukitzeko, XVII. mendeko testu klasiko bat hautatu dugu: Pedro Agerreren (Axular) Gero. Armiarma guneari esker eskuragarri dugu (http://klasikoak.armiarma.eus/idazlanak/A/AxularGero.htm) RTF formatuan. Gure makinan gordetzean TXT formatuan gordetzeko esango diogu (AxularGero.txt fitxategia gure adibideetan).

Baina testu horretan gaur egungo euskara estandarrean ez dauden hitz asko agertzen dira. Kasu batzuetan estandarrez idatzitako testuak tratatu nahi izango ditugu. Lizentziarekin arazorik ez edukitzeko, Wikipediako testuetara jo dugu. Erabiliko dugun testua IXA taldearen zerbitzarian dago, Wikipediaren lizentzia berarekin: CC BY-SA. Helbide honetatik eskura dezakezu: http://ixa.eus/Ixa/Produktuak/1464941552 (bertsio laburrena eskuratu eta wiki_eu_labur.txt izenarekin erabili da ariketetan).

12.3.1. Ariketa: euskarazko n-gramen maiztasunak[aldatu]

Hizkuntzak modelatzeko (eta hizkuntzen arteko desberdintasunak kalkulatzeko) erabiltzen da n-gramen eredua. Testuetan elkarren segidan azaltzen diren n elementuzko konbinazio posible guztien maiztasunak kalkulatzen dira horrelakoetan. n-ren balioa 1 eta 5 artean egon ohi da (handiagoak ere erabiltzen dira) eta normalean “gramak” edo elementuak karaktereak izaten dira (hitzen n-gramak ere erabiltzen dira beste batzuetan, ariketa honetan bezala).

Ariketa honetan kalkulatu nahi dira zeintzuk diren maiztasun handieneko 10 n-gramak euskararako. Zeintzuk dira euskaraz gehien errepikatzen diren 3 hitzezko sekuentziak?

"Gero" liburua izango da gure corpusa (ahal izanez gero, handiago eta eguneratuago bat erabiltzea gomendagarriagoa izan daiteke).

12.6. programa. n-gramen sorkuntza eta erabilera:

import nltk
nltk.download('punkt')
[nltk_data] Downloading package punkt to /root/nltk_data...
[nltk_data]   Unzipping tokenizers/punkt.zip.

True
import nltk, sys, operator

#nltk-ren n-gramak
from nltk.util import ngrams


fitx = "AxularGero.txt"

# irakurri eta tokenizatu
f=open(sst_home + fitx, encoding='utf-8')
gordin=f.read() # unicode
tokenak = nltk.word_tokenize(gordin)
testu = nltk.Text(tokenak)
hitzak = [h.lower() for h in testu]	#hitzak letra xehez

nhizt = dict()

n=3
# hizkuntz ereduak (hitzak)
ngramak = ngrams(hitzak,n)
for g in ngramak:
  try:
    if g in nhizt:
       nhizt[g] += 1
    else:
       nhizt[g] = 1
  except:
    continue

# inprimaketa sailkatuta. Lehen m ngramak (10)
n = 0
m = 10
for ng in sorted(nhizt, key=nhizt.get, reverse=True):
  print(ng, nhizt[ng])
  n = n+1
  if n == m:
     break

12.6. programan NLTK moduluko ngrams metodoa erabiltzeaz gain hiztegi bateko sarrerak nola ordena daitezkeen ikus dezakegu. Aurretik azaldutako sorted metodoa erabili da hemen ere, baina hiztegi batean erabili ahal izateko bigarren parametro bat zehaztu behar izan dugu (key). Hiztegiaren gainean get metodoa aplikatuta balioaren arabera ordenatzea lortzen da. Horrez gain, reverse ere zehaztu dugu maiztasun handieneko n-gramak eskuratzeko, ez maiztasun txikienekoak. Bukatzeko, hitzak letra xehetara pasa ditugu (lower metodoaren bitartez) trigramak sortu baino lehen, horrela hitz bera ez da desberdin moduan tratatuko larri/xehe moduan egoteagatik.

Ariketa probatzeko aipatutako "AxularGero.txt" testua erabili dugu.

Lortzen den emaitza hauxe da

(',', 'eta', 'ez') 320
('.', 'eta', 'hala') 88
('erraiten', 'du', 'san') 84
(',', 'eta', 'bai') 78
('.', 'lib', '.') 62
('da', ',', 'eta') 60
('.', 'kap', '.') 60
('.', 'hala', 'erraiten') 60
('2', ')', '.') 56
('dioen', 'bezala', ':') 56

Ikusten denez, puntuazio-marka asko daude. Testuz osatutako hirukoteak interesgarriagoak izan daitezke. Ariketa gisa proposatzen da (ikus kapitulu bukaeran)

12.3.2. Ariketa: Zuzentzaile ortografiko sinple bat[aldatu]

Aurreko ariketetan ikasitakoarekin nahiko erraza gertatuko zaigu zuzentzaile ortografiko sinple bat programatzea. Wikipediako testutik abiatuko gara eta suposatuko dugu bertan idatzitako hitz guztiak ondo daudela. Hitz horiek zerrenda batean metatuko ditugu (letra xehez), eta zuzentzaile ortografikoak ondokoa egingo du: aztertzen duen hitza zerrendan badago, ontzat joko du, eta bestela, alfabetikoa bada, antzekoenak bilatzen saiatuko da. 12.7. programan dago kodea.

12.7. programa. Euskarazko zuzentzaile sinple bat:

import nltk, sys, operator

#nltk-ren n-gramak
from nltk.util import ngrams

# 12.5 ariketatik eratorria
def bilatu_antza(hzerr, hitz):
    zerr = [];
    for h in hzerr:
        d = nltk.edit_distance(h, hitz, 1)
        if d == 1 :
            zerr.append(h)
    return zerr

fitx1 = "wiki_eu_labur.txt" # corpusa
fitx2 = "proba_zuzen"    # zuzentzeko testua

# irakurri eta tokenizatu (12.6 ariketatik hartuta)
f1=open(sst_home + fitx1, encoding='utf-8')
gordin=f1.read()
tokenak = nltk.word_tokenize(gordin)
testu = nltk.Text(tokenak)
hitzguztiak = [h.lower() for h in testu     #hitzak letra xehez
                     if h.isalpha()]
hitzak=set(hitzguztiak)

f2=open(sst_home + fitx2, encoding='utf-8')
gordin2=f2.read()
tokenak2 = nltk.word_tokenize(gordin2)
testu2 = nltk.Text(tokenak2)
hitzak2 = [h.lower() for h in testu2     #hitzak letra xehez
                         if h.isalpha()]

for h in hitzak2:
    if h not in hitzak:
        print(h, bilatu_antza(hitzak,h))
    else:
        print(h)

Emaitzak jarraian ikus daitezke. Bi fitxategi erabiltzen dira, corpusa ("wiki_eu_labur.txt") eta zuzentzeko testua ("proba_zuzen").

hau
proba
bat
da
hauek
ondo
daude
baiina ['baiona', 'baina']
haek ['hark', 'hauek', 'haiek', 'hašek']
ezz ['ezb', 'erz', 'ez']
flexionatuak []
etxekoa ['etxeko']
etxetik
etxekoarekikoak []
andereño []
andereñoaren []
izarñoarekin []

12.3.3. Ariketa: Zuzentzaile ortografiko oso bat integratzen[aldatu]

Aurreko zuzentzailea, hala ere, ahula da, maiztasun txikiko hitzak (egondakoarekin, ibilgailua) ez-zuzentzat hartzen ditu eta, gainera, modu ez koherentean lan egiten du (kalean onartzen du baina kalea ez). Alde hori hobe daiteke corpus handiago batekin, baina beti muga batekin. Alde hori, eta aurreko ariketan aipatu dena, hobetzeko morfologia landu behar da, bai morfologian oinarritutako programa bat eginda (nahiko konplexua dena) bai eskura dagoen bat erabilita. hunspell programa erabiltzea da horretan erosoena, euskararako eskura dago-eta. Mozillan eta Libreofficen integratuta dagoen bera da. Gainera, software librea da.

Hunspell erabiliz hainbat hizkuntzatarako zuzentzaileak (eta analizatzaile morfologikoak) garatu dira. Kodeaz gain hizkuntza bakoitzeko bi fitxategi behar dira: lemen fitxategia (.dic luzapena duena) eta atzizkien fitxategia (.aff luzapenekoa). Fitxategi horiek eskuratzeko jo helbide honetara: http://xuxen.eus/eu/bertsioak#tab_hunspell. (eu_ES.dic eta eu_ES.aff fitxategiak).

Programa instalatzeko ondoko komandoa exekutatu behar da: ??? Arazoak daude hunspell paketea Colab-n eta PAWS-en instalatzeko ??? :-(

!pip install --upgrade pip
!pip install python3-hunspell
!pip install wheel
!pip install hunspell
!pip install spacy_hunspell

Modulua instalatuta eta aipatutako bi fitxategiak eskuratuta, aurreko ariketa dezente sinplifikatzen da eta, gainera, askoz hobea da lortzen den zuzentzailea.

Programa sinpleagoa da, "spell" metodoaz hitzaren zuzentasuna egiazta daitekeelako, eta "suggest" metodoaz proposamenak lortu.

12.8. programa. Euskarazko zuzenketa hunspell bitartez:

import nltk, sys, operator

import hunspell

#klasea kargatu
zuzentz = hunspell.HunSpell("eu_ES.dic", "eu_ES.aff")

fitx = "proba_zuzen"
# zuzentzeko testua
f=open(sst_home + fitx, encoding='utf-8')
gordin=f.read()
tokenak = nltk.word_tokenize(gordin)
testu = nltk.Text(tokenak)
hitzak = [h for h in testu if h.isalpha()]
for h in hitzak:
    #egiaztapena
    if not zuzentz.spell(h):
        #proposamena
        print("OKER:", h, "PROPO:", zuzentz.suggest(h))
        #lema
    else:
        print("ZUZEN:", h, "LEMA:", zuzentz.stem(h)[0])

Exekutatzeko komandoa eta emaitzak jarraian ikus daitezke. Bi fitxategi erabiltzen dira, corpusa eta zuzentzeko testua.

ZUZEN: Hau LEMA: hau ZUZEN: proba LEMA: proba ZUZEN: bat LEMA: bat ZUZEN: da LEMA: da ZUZEN: hauek LEMA: hauek ZUZEN: ondo LEMA: ondo ZUZEN: daude LEMA: daude OKER: baiina PROPO: ‘baiona’, ‘baina’, ‘bazina’, ‘baiena’, ‘balina’, ‘bagina’, ‘Bariaina’, ‘Baiona’, ‘ainarba’ OKER: haek PROPO: ‘hark’, ‘hasek’, ‘haxek’, ‘haiek’, ‘hauek’, ‘hagek’, ‘habek’, ‘Saek’, ‘HAEek’, ‘EHAek’, ‘EHAeek’ OKER: ezz PROPO: ‘zez’, ‘ez’, ‘ezaz’, ‘ezez’, ‘eziz’, ‘ezoz’, ‘eza’, ‘eze’, ‘ezi’ ZUZEN: flexionatuak LEMA: flexionatu ZUZEN: etxekoa LEMA: etxeko ZUZEN: etxetik LEMA: etxe ZUZEN: etxekoarekikoak LEMA: etxeko

Adibidean ikus daitekeenez, egiaztapenaz zein proposamenen eskaeraz gain beste eragiketa garrantzitsu bat egin daiteke: lematizazioa edo stemming. Horretarako stem metodoa erabili ohi da.

12.4 PROPOSATUTAKO ARIKETAK[aldatu]

1) 12.1. programaren bigarren bertsio bat egin behar duzu ts, tx eta tz fonemak bilduta kontatzeko, jokoaren fitxetan fonema horiek agertzen baitira.

2) Egin aurreko ariketaren hedapen bat, karaktere solteak kontatu beharrean karakterezko n-gramak kontatzeko. n-ren balioa parametro gisa jasoko da. Hitzen hasieran eta bukaeran zuriune bat gehituko da hitz-hasierako eta hitz-bukaerako n-gramak ere bereizi ahal izateko.

3) Osatu 12.2b. programa honako ezaugarri hauekin: •Esteken bitartez Internetetik jasotako webgune bakoitzarekin testu-corpus bat osatu, eta 12.1. programan kalkulatu diren probabilitateak kalkulatu testu guztien bildumarekin. •Lortutako emaitza horiek eta beste corpusetik lortutakoak konparatu eta kalkulatu desbiderapen tipikoa.

4) Egin 12.4. programan aldaketak, kategoria eta hitz desberdinetako adibideak lortzeko. Adibidez, lortu zeintzuk diren maizen agertzen diren izen-izen bikoteak eta zeintzuk diren small eta big hitzekin gehien agertzen diren izenak. Euskararako, hitzekin lan eginda, lortu zeintzuk diren polita eta itsusia hitzen aurretik maizen agertzen diren hitzak.

5) 12.6. programa hobetu behar duzu hizkuntza-eredutik lortzen diren trigramak (3 hitzeko sekuentziak) soilik hitzez osatuta egon daitezen (ez zenbakiak, ez puntuazioa). Horretarako, funtzio bat definitzea eta erabiltzea gomendatzen da. Kapitulu honetan erabilitako isalpha metodoa erabil daiteke horretarako, baina kontuan hartu behar da hiztegiko elementuak zerrendak direla eta metodoa zerrendako elementuei (karaktere-kateei) aplikatu behar zaiela.

6) 12.7. programa hobetu behar duzu hiru aldetatik: • Batetik, proposamen ez-zuzenen kopurua murritz liteke, ontzat hartzeko corpusean bi aldiz agertzea eskatuko bagenu (edo aurretik corpus osoa ohiko zuzentzaile batetik pasako bagenu). • Bestetik, erabili behar dugun bakoitzean corpusa kargatzea ez da ideia ona eraginkortasunaren aldetik, beraz, programa bitan banatu behar da: batetik, hitz zuzenen zerrenda osatuko da eta biltegiratuko da (pickeld paketea erabiltzea gomendatzen da). Bigarren programak corpusa eskuratu beharrean biltegiratutako zerrenda erabiliko du. • Proposamenak hitzaren maiztasunaren arabera ematea (maiztasun handienekoak aurretik).

7) hunspell erabilita lematiza ezazu Wikipediako corpusa eta lor itzazu maiztasun handieneko lemak eta lema-bikoteak.

8) Seigarren ariketa hobetu behar duzu proposamenak testuinguruaren arabera emateko. Horretarako, lehenik eta behin euskararen hitz-hirukoteen hizkuntza-eredu bat sortu behar duzu. Gero, zuzentzeko proposamenak jaso eta gero, aurreko eta ondoko hitzak (edo aurreko biak) kontuan hartuta, aukeratu behar duzu zein den probabilitate handienekoa hizkuntza-ereduaren arabera.