NLP bot za pet dana

9 min

U porastu je broj servisa koji nude neki način obrade unetog teksta i jednostavan način pripreme tj. programiranja odgovora; što se često koristi za izradu botova. Međutim, odlučio sam da ih zanemarim i zaronim dublje u NLP, te pokušam da iskodiran… nešto smisleno. Kao, na primer, bota koji crpi znanje iz tekstualnog fajla i daje odgovore na pitanja u vezi teksta. Za pet dana. Jednostavno, zar ne?

Pre nego što nastavim: nisam nikakav ekspert za NLP i siguran sam da ima stvari koje nisam razumeo, niti ih ovde ispravno predstavljam. Ako nekome zbog ovog teksta padne mačka sa terase ili počne da sanja elektronske ovce, tja.

Svaki početak je… lak

Pošto je vremenski rok kratak, problemu sam pristupio inženjerski: pokušaću da za kratko vreme dođem do dovoljno dobrog rezultata; pažljivo birajući čemu mogu da se posvetim više, a šta jednostavno moram da zanemarim.

Ispostavilo se da je početak iznenađujuće lak. Naime, postoji priličan broj zrelih NLP toolikita: OpenNLP, Stanford NLP, LingPipe, GATE… Izabrao sam Stanfordov: pisan je u Javi, aktivno se razvija, a čini mi se da je veliki broj Python biblioteka upravo zasnovan na ovom toolkitu.

Šta je Stanford NLP? Pojednostavljeno, to je Java bibiloteka koja parsira tekst (engleski, ali i drugi jezici su donekle podržani) i izvodi analizu rečenica i reči od kojih se tekst sastoji. Analiziranjem teksta se rečenicama i rečima pripisuju tkzv. anotacije - meta-podaci vezani za konkretnu analizu. Drugim rečima, uzmeš tekst, propustiš ga kroz NLP i dobiješ skup rečenica i reči označenih anotacijama.

Kakve sve analize možemo zahtevati od NLP toolikita? Evo nekih:

Ovo je deo svih analiza teksta koji se mogu obaviti, ceo spisak tkzv. anotatora se može naći na zvaničnom sajtu.

Rad sa Stanford NLP bibliotekom i aktiviranje željenih anotatora je vrlo jednostavno:

Properties props = new Properties();
props.setProperty("annotators", "tokenize, ssplit, pos, lemma, ner, parse, dcoref");
StanfordCoreNLP pipeline = new StanfordCoreNLP(props);

String text = "...";
Annotation document = new Annotation(text);

// izvrši sve anotatore na tekstu
pipeline.annotate(document);

Sada kada sam našao moćan alat za analizu teksta, podelio sam problem na sledeće celine:

Weka i klasifikacija

Klasifikacija u NLP znači pripisivanje jedne od predefinisanih klasa analiziranoj rečenici ili reči. Jedna od klasifikacija ulazne rečenice je određivanje da li je ona pitanje (question), izraz (expression) ili tvrdnja (assertion). Program za odgovore bi trebalo da se drugačije ponaša i odgovara za svaku od ovih klasa ulaznog teksta.

Da bi implementirali ovakvu klasifikaciju, u NLP se koristi data-mining. Pisanja determinističkih pravila za određivanje kategorija je često nemoguće ili je nepraktično. Umesto toga se definiše tkzv. klasifajer (classifier) koji se trenira pripremljenim skupom podataka dovoljno velikim dok ne počne da daje rezultate sa zadovoljavajućom tačnošću. Stanford NLP nudi mogućnost pravljenja klasifajera, međutim (ovo sada već postaje simptomatično), izabrao sam da koristim moćan Weka data-mining toolkit.

Za svaku klasifikaciju je potrebno imati pripremljen data set već utvrđenih klasifikacija. Svaka od klasifikacija je uvezana sa skupom parametara (karakteristika, atributa) izvedenih iz prirode onoga šta se klasifikuje. Primer koji se često koristi je klasifikacija cvetova na osnovu Iris data seta. Ovaj data set sadrži 150 instanci (redova) cvetova; cvet je opisan sa 4 karakteristike (dimenzije latica i čašice) i oznakom jedne od tri klase kojoj cvet Irisa pripada. Kada Weka učita ove podatke, tj. kada se model istrenira, moguće je uraditi klasifikaciju novog primerka cveta samo na osnovu ulaznih parametera, tj. njegovih dimenzija.

Laički rečeno, klasifajer treniramo da ga “naučimo” vezama između ulaza (parametri) i rezultata (klasa). Jednom naučen, klasifajer primenjuje naučeno “znanje” na nove ulazne podatke da bi ih (manje-više) uspešno klasifikovao. Očigledno, da bi klasifikacija radila potrebno je da uopšte postoji korelacija ulaznih parametara i klasa. I to je deo gde Weka pomaže, pružajući pomoć u analizi data setova i korelacija korišćenjem raznih statističkih algoritama.

Da se vratim na problem: za klasifikaciju vrste rečenice potrebno je izabrati ulazne parametre koji bi bili u korelaciji sa klasama. Problem nije nov, ovaj naučni rad predlaže sledeći set ulaznih parametara:

Svi ovi parametre se dobijaju analizom teksta Stanford NLP bibliotekom. Nadalje je lako. Našao sam ili napravio data set rečenica za svaku od klasifikaciju, a onda napravio program da sve to analizira i konačno napravi finalni dataset spreman za Weku, tekstualni arff fajl. Jednom napravljen, finalni fajl se učitava u program, trenira se model i nadalje klasifikacija radi.

Klasifikujemo i dalje

Druga klasifikacija koju sam radio je za tipove pitanja i njihovu semantiku. Postoji klasifikacija po tome na šta se pitanje odnosi, kao i trening data setovi sa već klasifikovanim pitanjima. Međutim, nisam našao nikakav smislen set ulaznih parametara sa kojim bih toliko detaljno trenirao Weka model, pa sam klasifikaciju pitanja sveo na 6 glavnih kategorija, a za ulazne parametre uzeo sledeće:

Ovo nije potkrepljeno nekim naučnim dokazom da postoji korelacija; tako da ovde ima još dosta prostora za unapređenje.

Odgovaranje

Najteži deo je upravo pronalaženje pravog odgovora. Sada kada imam svu potrebnu analizu i teksta i pitanja, konačno mogu da se bacim na suštinu aplikacije. Nažalost, nisam našao neki dovoljno jednostavni primer ili naučni rad koji bi mi ovde pomogao. Pošto se rok bližio, rešio sam probam da poredim pitanje sa svakom rečenicom teksta, odredim nekakav koeficijent “sličnosti” - koliko rečenica odgovara pitanju i na kraju izaberem najbolje kandidate za odgovor. Evo šta sam sve poredio.

Bag-Of-Words: Stanford NLP biblioteka može da izdvoji set važnih reči iz rečenice, odvajajući ih of tkzv. stop-words i sličnih reči koje ne doprinose značenju rečenice (semantika). Ako pitanje i rečenica sadrže iste reči, verovatnoća je da se bave sličnom temom.

Triplets: jedna od analiza rečenice je određivanje tkzv. tripleta: subjekat, objekat i glagol. Ovim se rečenica praktično svodi na triple koji određuje značenje. Poređem tripleta pitanja i rečenice, prilično može da se nagovesti da li rečenica odgovara pitanju!

Keywords & word distance: nastavlja se na premisu Bag-Of-Words. Prvi korak je da se iz teksta izdvoje ključne reči (keywords). Stanford NLP ne daje ovu mogućnost, verovatno pošto ne postoji jasna definicija šta je to zapravo ključna reč. To bi mogle biti reči iz tripleta, na primer; no pristupio sam (opet!) malo drugačije. Kako bilo, izdvajanje ključnih reči i nije toliki problem. Međutim, za razliku od ranije, sada želim da ih uporedim na neki pametniji način, a ne samo da poredim da li su njihove leme isti stringovi.

I za poređenje sličnosti značenja reči postoji rešenje! Prinston univerzitet održava poznatu WordNet leksičku bazu engleskog jezika, dostupnu svima. Jedna od stvari koja se može uraditi pomoću ove baze je da se “izmere” reči i njihova sličnost. Laički, moguće je napisati funkciju koja poredi dve reči i vraća koeficijent koliko su one semantički slične. Ovakvim poređenjem po značaju možemo pronaći vezu između različitih reči, a koje imaju slično značenje. Tako sam i poredio ključne reči.

Tripleti, opet. Ovaj put računam triplete i njihovu zavisnost uz pomoć WordNeta. Zapravo, izračunavam više koeficijenata: frekfenciju lema, poredim pojedinačno objekte, pa zatim poredim parove objekat-subjekat, sinonime, antonime… Daleko da sam smislio sve ovo, ali bar razumem šta se dešava:) Rezultat je realan broj koji je suma svih koeficijenata i predstavlja meru koliko se rečenica i pitanje poklapaju.

Na kraju sve ove rezultate poređenja množim težinskim koeficijentima (koje sam potpuno proizvoljno i nenaučno izabrao) i zbrajam u finalni skor. Sve rečenice teksta se ovako ocene, sortiraju po skoru i izaberu tri koje najviše odgovaraju.

Evo primera kako program radi (za tekst o informacijama o kompaniji):

> How many projects you have developed?
We have more then 8000 projects.

> What do you develop?
We develop financial apps.
We develop medical apps.

> How many employees works in your company?
We have 800 employees.

> Where is your office?
We are located in Novi Beograd.

> In which countries you also work?
We also have offices in Switzerland, Germany and Serbia.

> What are you?
We are a software engineering company.
Internship is payed.
We are located in Novi Beograd.

Okej, očigledno nije savršeno, ali je iznenađujuće dovoljno dobro. Kod je na GitHub.

Kada bih imao još vremena…

NLP razvija zavisnost, bez šale, lako bih mogao da se ovime bavim duže vreme. Na kraju ovih pet dana imam puno ideja kuda bih dalje i kako bi se program mogao razvijati i fino podešavati.

Toliko za sada, vreme je da bota stavim na spavanje.

🧧
Nisam definisan svojim stavovima. Stavove usvajamo, menjamo, nadograđujemo, ali oni ne čine nas same. Manje je važno da li se slažemo, koliko da se razumemo.