Discuţie PHP – Extragere automată a cuvintelor şi frazelor cheie dintr-un text dat

Created at: aprilie 23, 2015; Last update: aprilie 28, 2015

Uau… nu am mai scris de mult timp un tutorial în română. Mă miră că mai ştiu româneşte. Apropo de română, acest tutorial este tocmai despre limba română sau mai bine zis despre cum am început construrea unui “motor” de extragere a cuvintelor cheie din text.

Atenţionez că acesta nu este un script pe care să îl foloseşti cu copy/paste, ci o discuţie principială ce foloseşte PHP drept cod exemplificator.

Multă lume ar putea întreba “Păi de ce să construieşti aşa ceva? Nu există deja un script sau un api pentru asta?”. Există şi vă invit să căutaţi aşa ceva pentru a-l aplica şi la limba română.

Dar până atunci, să vorbim despre cum am lucrat eu. Poate mă puteţi ajuta la schimbarea codului.

Întâi să vorbim principial despre ce am vrut să fac şi care sunt problemele pe care am încercat să le abordez. Este vorba de un extractor de cuvinte şi fraze cheie din text. Cu alte cuvinte, dai un text scriptului şi acesta ar trebui să găsească cuvintele cheie şi frazele cheie şi să ordoneze totul în funcţie de numărul de apariţii (cuvinte) şi de scorul pe care frazele îl obţin. În funcţie de aceşti parametri să scoată în faţă cuvintele şi frazele cheie care definesc cel mai bine textul care a fost dat.

Care este problema? Ce face specială aceasta întreprindere? Faptul că vorbim de limba română şi nu de limba engleză, chiar dacă presupun că acelaşi script s-ar putea aplica la fel de bine şi pentru limba engleză. Ce este specific limbii române şi altor limbi asemenea ei? Faptul că mare parte din afixe de aici sunt sudate în cuvânt şi nu cum se întâmplă în limba engleză, să fie separate de cuvânt. Adică (pe linia “mai catolic decat Papa”) româna e “mai flexionară” decât engleza. Din păcate nu ştiu să explic exact ce vreau să spun. Poate se găseşte cineva să o facă în comentarii, motiv pentru care îi mulţumesc.

Un al doilea punct pe care doresc să îl subliniez este faptul că ce va face acest script nu este neapărat mulţumitor ochiului vizitatorului site-ului, rezultatele vizibile putând descuraja. Cu toate acestea, rezultatele invizibile fac ca o aplicaţie bazată pe un astfel de script să funcţioneze. Cu alte cuvinte, rostul scriptului nu este acela de a afişa în “limbaj uman”, cu flexionările de rigoare, cuvintele şi frazele cheie, ci, în corelaţie cu un script de căutare care să funcţioneze pe acelaşi principiu ca acest script, să afişeze articolele cele mai reprezentative pentru ceea ce caută un vizitator al site-ului.

Aşadar, să începem.

Presupunând ca avem un text pe care îl asignăm unei variabile numite $text încep prin a-l curăţa de diacritice şi de a transforma toate caracterele în litere mici. Pentru asta am o funcţie care primeşte textul şi returnează textul curăţat:

Nu vă uitaţi la înşiruirea de litere ciudate… Funcţia este în mare parte luată din CodeIgniter.

După ce am curăţat textul, rămâne să îl împart în “fraze”, prin fraze înţelegând mai degrabă unităţi ce iniţial au fost despărţite prin semne de punctuaţie. Fac asta printr-o altă funcţie:

Acum toate frazele sunt despărţite prin coloana verticală (semnul “pipe” în engleză). Acest pas mă ajută să sparg în continuare textul în frazele componente, realizând un array cu acestea:

După ce am făcut asta, iau fiecare bloc-frază şi îl sparg la rândului lui în entităţile (cuvinte, cifre etc.) componente, având în acelaşi timp grijă ca dacă am o entitate nouă să o păstrez într-o listă de cuvinte pe care ulterior să o folosesc la căutarea acestora într-un dicţionar al limbii române:

OK… ce se întâmplă cu $words şi care este faza cu “dicţionarul limbii române”. Ei bine, trebuie cumva să gasim forma de bază (rădăcina) a cuvintelor pornind de la forma flexionată a lor. “Dicţionarul limbii române” îl putem construi de la zero, presupun că printr-o interfaţă în care să adaugi fiecare cuvânt în parte cu formele flexionare. Avantajul unei astfel de abordări ar fi acela că astfel vei avea un dicţionar specific aplicaţiei tale care să nu stocheze cuvinte nenecesare precum regionalisme, cuvinte învechite sau, cine ştie, neologisme chiar. Spre exemplu, la un test făcut de mine pe o ştire, “NATO” a fost găsit ca fiind o flexiune a cuvântului “nata”… care nu ştiu ce înseamnă.

Dacă însă doreşti, poţi oricând să te bucuri de ceea ce numim “open source”, mai exact de xml-ul cu toate fonemele de pe dexonline.ro (http://wiki.dexonline.ro/wiki/Protocol_de_exportare_a_datelor). Eu aşa am făcut.

Folosind un script precum cel de jos, am luat xml-ul (care, apropo, are cam 250MB) şi l-am exportat într-un tabel MySQL:

…Sper să nu te legi de cod…

Întorcându-ne la scriptul nostru…

Ziceam ceva de $words şi de dicţionar. Ei bine, cu cuvintele pe care le-am scos, încercăm să aflăm care este cuvântul rădăcină al acestora, interogând dicţionarul:

$word_ids va conţine toate cuvintele, cheia elementului fiind cuvântul, iar valoare fiind id-ul cuvântului rădăcină aferent cuvântului din cheie.

De asemenea, putem deja să facem un nou array cuvintele rădăcină, punând id-ul drept cheie pentru cuvânt. Vom avea nevoie de ele mai încolo:

Acum, că avem un dicţionar şi cuvinte de bază, putem să facem rost şi de “cuvintele zgomot”. Cuvintele zgomot sunt acele cuvinte care nu au o importanţă semantică luate individual, ci luate prin alăturarea lor altor cuvinte (conjuncţii, adverbe etc.):

Eu am scos câteva, dar s-ar putea ca id-urile să difere:

Întorcându-ne la blocurile noastre de text, pentru a începe analiza (mai rapidă a) acestora vom avea nevoie să transformăm cuvintele în id-urile rădăcinilor lor, numărând de asemenea numărul de apariţii a acestora:

Acum $blocks_as_ids va conţine id-urile cuvintelor din blocurile de text. Iar $word_appearances va conţine numărul de apariţii ale cuvintelor.

În contextul unui text de dimensiuni mari nu ne vor interesa prea mult cuvintele care apar o singură dată. Aşadar, să le scoatem pe acestea din array-ul nostru de cuvinte:

Acestea reprezintă cuvintele candidate la Oscarul căutărilor.

Apoi vom lua fiecare bloc de text si vom incerca să facem toate combinaţiile posibile pentru a afla frazele candidat la Oscarul căutărilor:

Vom exclude din aceste fraze frazele care au un singur cuvânt în componenţă:

Acum, vom afla frecvenţa cu care cuvintele apar în combinaţii de cuvinte. Cu cât frecvenţa este mai mare cu atât şansele ca cuvântul respectiv să faca parte din frază sunt mai mari:

Apoi calculăm scorul frazelor în funcţie de numărul de apariţii al cuvintelor componente şi frecvenţa acestora în combinaţii de cuvinte:

Acum sa refacem fraza ca sa avem cuvintele radacina (la o adică te poţi opri aici, avand tot ce îţi trebuie pentru ca scriptul să funcţioneze automat, dar dacă vrei să vezi şi ce fraze au ieşit ar trebui să transformi id-urile cuvintelor în limbaj uman):

Şi ca să şi arate bine, cam aşa ni se înfăţişează:

Cam asta a fost tot… Încă o dată te rog să nu te cramponezi în felul cum am scris codul… E doar o discuţie amicală.

Lasă un răspuns

Your email address will not be published. Required fields are marked *

No spam? * Time limit is exhausted. Please reload CAPTCHA.