Universitatea babeş-bolyai cluj-napoca facultatea de matematicǎ Şi informaticǎ specializarea informatică



Yüklə 465,96 Kb.
səhifə13/14
tarix03.01.2019
ölçüsü465,96 Kb.
#89267
1   ...   6   7   8   9   10   11   12   13   14

Implementare

După ce au fost evidențiate conceptele care stau la baza funcționării clasificatorului naiv Bayes, implementarea acestuia se desfășoară natural, principalele decizii și alegeri fiind determinate strict de modul în care dorim să arate și să se comporte sistemul în final.

Scopul nostru este acela de a realiza o aplicație care să reușească printr-un design atractiv să îndeplinească cerințele utilizatorului. Performanța reprezintă de asemenea o caracteristică importantă, întrucât stă la baza încrederii pe care un personaj uman o poate acorda unui sistem soft, iar independența de platformă constituie un plus în momentul în care ne raportăm la distribuția pe scară largă a produsului pe care îl concepem.

Acestea fiind spuse, am ales ca limbaj de programare Java, un limbaj orientat obiect, puternic tipizat. Un avantaj al acestuia este reprezentat de posibilitatea rulării unui program compilat în prealabil, pe orice platformă care are instalată o mașină virtuală Java (Java Virtual Machine – JVM). Acest lucru este posibil datorită faptului că în urma compilării unor surse Java obținem un format standard, numit cod de octeți, care constituie o cale de mijloc între codul mașină (o caracteristică specifică tipului de calculator) și codul sursă.

Pentru a putea oferi utilizatorului o perspectivă clară asupra performanțelor sistemului, am ales utilizarea unui sistem de gestiune a bazelor de date relaționale, mai exact MySQL, care este de asemenea independent de platformă, alături de Hibernate, care reprezintă o soluție de mapare obiect-relațională (Object-Relational Mapping - ORM) pentru Java, oferind de asemenea și facilități de interogare a datelor.

În acest sens, am avut nevoie de o bază de date nouă, pe care am denumit-o clasificatornaivbayes. Aceasta conține la rândul ei trei tabele: acuratete, functiitinta, probabilitaticonditionale. Vom prezenta structura acestora în cele ce urmează:



  • acuratete

Coloană

Tip

Nul

Cheie primară

dataOraTestării

datetime

Nu

Da

categorii

varchar(500)

Nu

Nu

microPrecizie

double

Nu

Nu

microSensibilitate

double

Nu

Nu

microScorF1

double

Nu

Nu

macroPrecizie

double

Nu

Nu

macroSensibilitate

double

Nu

Nu

macroScorF1

double

Nu

Nu

acuratete

double

Nu

Nu

observatii

varchar(500)

Nu

Nu




  • functiitinta

Coloană

Tip

Nul

Cheie primară

categorie

varchar(50)

Nu

Da

probabilitate

double

Nu

Nu




  • probabilitaticonditionale

Coloană

Tip

Nul

Cheie primară

cuvant

varchar(50)

Nu

Da

categorie

varchar(50)

Nu

Da

probabilitate

double

Nu

Nu

În urma procesului de antrenare, vor rezulta probabilitățile condiționale, respectiv funcțiile țintă, care vor fi utile ulterior în cadrul procesului de testare, respectiv de clasificare a unor documente noi. Pentru a nu fi nevoie ca la fiecare pornire a aplicației să se inițieze procesul de antrenare al sistemului, este necesară salvarea acestor informații obținute în cadrul unei baze de date. Astfel, utilizatorul va putea testa sistemul, respectiv clasifica documente noi, pe baza unei antrenări făcute anterior, sau va putea alege opțiunea de antrenare a sistemului în momentul respectiv, dacă dorește acest lucru.

Pentru a putea ține o evidență clară asupra comportamentului programului în funcție de setările făcute, sau având în vedere diverse categorii ale documentelor folosite pentru procesul de antrenare, acuratețile obținute în urma testărilor au fost salvate într-un tabel. Acestea sunt utilizate ulterior pentru reprezentarea unor grafice grupate pe categorii.

În continuare, vom prezenta succint clasele cele mai importante folosite în realizarea funcționalității acestui sistem:



PreprocesatorText.java::

Ne oferă posibilitatea de a preprocesa o listă de cuvinte, punând la dispoziția utilizatorului trei setări care pot fi activate, respectiv dezactivate, după voia acestuia:



  • Eliminare stop-words – această tehnică este deseori utilizată pentru filtrarea documentelor de tip text și constă în eliminarea cuvintelor cele mai uzuale din vocabularul englez, acestea fiind preluate în cadrul algoritmului din fișierul stopwords.txt;

  • Aplicare Stemmer – acest termen este utilizat în morfologia lingvistică, precum și în tehnicile de regăsire a informațiilor, caracterizându-se prin reducerea cuvintelor la o formă mai scurtă, datorită eliminării inflexiunilor și a sufixelor comune din punct de vedere morfologic;

  • Aplicare Lemmatizer – este un proces asemănător Stemmer-ului, având un grad de dificultate mai ridicat, datorită faptului că în acest caz cuvintele sunt reduse la rădăcina lor, rezultatele obținute fiind la rândul lor cuvinte reale.

Document.java:

Pentru a instanția un astfel de obiect avem nevoie de calea către un fișier text, categoria din care acesta face parte și de un obiect instanță a clasei PreprocesatorText. Cuvintele din cadrul documentului sunt găsite prin intermediul unor separatori prestabiliți aflați în fișierul separatori.txt, acestea fiind ulterior supuse unei preprocesări. În cele din urmă se obține un container de forma Dicționar care conține toate cuvintele preprocesate din fișier, alături de numărul de apariții corespunzător.



GeneratorDocumente.java:

Are rolul de a genera în mod automat și aleator documentele pentru antrenare, respectiv pentru testare, respectând raportul 2/3 1/3. Pe baza unui folder, care este oferit ca parametru constructorului, vor fi obținute două liste, care conțin căile documentelor din folderul respectiv. Una dintre acestea va corespunde fișierelor pentru antrenare, iar cealaltă fișierelor pentru testare. Fiecărei categorii în parte îi va corespunde o instanță a clasei GeneratorDocumente.



FunctieTinta.java:

Reprezintă o categorie, în care va putea fi ulterior clasificat un document nou. Reține denumirea categoriei, precum și probabilitatea inițială a acesteia.



ProbabilitateConditionala.java:

Constituie probabilitatea condițională asignată unui cuvânt, în raport cu o anumită categorie. În urma antrenării sistemului, pentru fiecare cuvânt din cadrul vocabularului final va rezulta un număr egal de probabilități, deci și un număr egal de instanțe ale clasei ProbabilitateCondițională, acesta fiind egal cu numărul de categorii folosite.



Acuratete.java:

Reprezintă feedback-ul pe care îl obținem în urma antrenării sistemului, fiind relevant tocmai datorită faptului că putem vizualiza calitatatea algoritmului și comportamentul acestuia în funcție de setările folosite, de categoriile observate, de numărul acestora precum și de bazele de date pentru antrenare utilizate. Fiecare instanță a acestei clase conține informații relevante despre realizarea unui anumit proces de testare care corespunde la rândul său unui proces concret de antrenare, precum: categoriile, data și ora testării, micro-precizia, micro-sensibilitatea, micro-scorF1, macro-precizia, macro-sensibilitatea, macro-scorF1, acuratețea și nu în ultimul rând observațiile despre setările folosite în momentul antrenării.



Vocabular.java:

Un obiect corespunzător acestei clase va reprezenta fie vocabularul cuvintelor pentru o anumită categorie, fie vocabularul final, care le cuprinde pe celelalte. Aceste informații sunt reținute sub forma unui Dicționar. De asemenea, această clasă ne oferă posibilitatea de a elimina din cadrul vocabularului acele cuvinte al căror număr de apariție nu se încadrează într-un interval prestabilit.



AcurateteHibernateDAO.java,

FunctieTintaHibernateDAO.java,

ProbabilitateCondiționalăHibernateDAO.java:

Aceste clase asigură persistența obiectelor corespunzătoare claselor Acuratete, FunctieTinta și ProbabilitateConditionala în cadrul bazei de date, prin intermediul operațiilor de tip CRUD.



Antrenare.java:

După cum ne sugerează și numele, în cadrul acestei clase se desfășoară antrenarea sistemului, fiind apelate mai multe metode din clasele prezentate anterior. Termenul antrenare se referă în cazul de față la determinarea funcțiilor țintă și a probabilităților condiționale care sunt necesare ulterior pentru clasificările documentelor noi. Pentru a înțelege mai bine acest proces, vom prezenta pe scurt pașii urmați.

Pentru ca antrenarea să poată fi realizată, avem nevoie de o listă de obiecte instanțe ale clasei GeneratorDocumente. Cu ajutorul acesteia, preluând doar acele documente corespunzătoare antrenării, putem să identificăm vocabularele pentru fiecare categorie în parte, urmând ca acestea să fie mai apoi integrate într-un vocabular complet. În funcție de setările alese de către utilizator, cuvintele pot fi obținute prin aplicarea unui stemmer sau a unui lemmatizer putând fi eliminate dintre acestea și cuvintele uzuale. De asemenea, din vocabularul mare pot fi eliminate cuvintele cu frecvența cuprinsă într-un anumit interval.

În cazul în care nu beneficiem de anumite cunoștințe anterioare, probabilităților inițiale corespunzătoare funcțiilor țintă sunt evaluate la aceeași valoare, aceasta fiind calculată astfel: .

Urmează calcularea probabilităților condiționale pentru fiecare cuvânt în parte, acest lucru realizându-se prin intermediul metodei:

calculeazaProbabilitatiConditionale (List listaGeneratoareDocumente) care este prezentată mai jos:


private void calculeazaProbabilitatiConditionale(List listaGeneratoareDocumente) {

for (String cuvant : this.vocabularToateCategoriile.getDictionarCuvantFrecventa().keySet()){

Map categorieProbabilitate = new HashMap();

Double marimeVocabular = new Double(this.vocabularToateCategoriile.getDictionarCuvantFrecventa().size());

for (GeneratorDocumente generatorDocumente : listaGeneratoareDocumente) {

String categorie = generatorDocumente.getDenumireFolder();

Vocabular vocabularCategorie = this.dictionarCategorieVocabular.get(categorie);

Integer nk = vocabularCategorie.getDictionarCuvantFrecventa().get(cuvant);



if (nk == null) {

nk = new Integer(0);

}

Double n = new Double(vocabularCategorie.getDictionarCuvantFrecventa().size());



Double probabilitate = (new Double(nk) + 1.0)/(n + marimeVocabular);

categorieProbabilitate.put(categorie, probabilitate);

}

this.probabilitatiConditionale.put(cuvant, categorieProbabilitate);

}

}


Ulterior, probabilitățile condiționale, alături de funcțiile țintă sunt salvate în baza de date, prin apelul metodelor: stergeIncarcaFunctiiTintaBD(), stergeIncarcaProbabilitatileBD().

ClasificatorNaivBayes.java:

După ce a fost realizată antrenarea sistemului, putem să clasificăm un document nou în una din categoriile care au fost învățate, cu ajutorul probabilităților condiționale și a funcțiilor țintă preluate din baza de date. Desigur, acest document este supus inițial unui PreprocesatorText, urmând ca pentru fiecare categorie în parte să se calculeze probabilitatea ca fișierul text respectiv să îi corespundă. În cele din urmă, se alege ca rezultat final acea categorie cu probabilitatea cea mai mare:


private Map calculeazaProduseProbabilitatiCategorii(Document document) {

this.produsuriProbabilitati = new HashMap();

for (FunctieTinta functieTinta : this.functiiTinta) {

Double probabilitateFunctieTinta = functieTinta.getProbabilitate();

Double produs = probabilitateFunctieTinta;

for (String cuvant : document.getDictionarCuvantFrecventaPrelucrat().keySet()) {

Map dictionarCategorieProbabilitate = this.probabilitatiConditionale.get(cuvant);



if (dictionarCategorieProbabilitate != null) {

Double probabilitateCategorie = dictionarCategorieProbabilitate.get(functieTinta.getCategorie());

produs = produs * probabilitateCategorie;

}

}



this.produsuriProbabilitati.put(functieTinta.getCategorie(), produs);

}

return produsuriProbabilitati;

}
public String clasificaDocument(Document document) {

this.produsuriProbabilitati = calculeazaProduseProbabilitatiCategorii(document);

Double probabilitateMaxima = 0.0;



for (Map.Entry entry : this.produsuriProbabilitati.entrySet()) {

if (probabilitateMaxima.compareTo(entry.getValue()) == -1) {

probabilitateMaxima = entry.getValue();

}

}

for (Map.Entry entry : this.produsuriProbabilitati.entrySet()) {



if (probabilitateMaxima.compareTo(entry.getValue()) == 0) {

return entry.getKey();

}

}



return "";

}
Această clasă ne mai oferă o funcționalitate, și anume aceea de a calcula pentru fiecare categorie în parte, probabilitatea corespunzătoare documentului nou oferit spre clasificare. Această abordare este utilă, tocmai datorită faptului că ne oferă posibilitatea de a vizualiza, pe lângă clasa aleasă de către sistem și probabilitatea pe baza căreia a fost luată această decizie, alături de probabilitățile corespunzătoare celorlalte clase.

În acest sens, pot fi identificate asemănările, respectiv deosebirile dintre categorii, prin studierea variațiilor valorilor probabilistice de la o categorie la alta. Metoda corespunzătoare acestei abordări este: calculeazaProbabilitatiCategorii(Document document).

Testare.java:

Această clasă ne oferă mai multe funcționalități în ceea ce privește evaluarea sistemului, în urma procesului de antrenare. Cu ajutorul unei instanțe a clasei ClasificatorNaivBayes, documentele corespunzătoare testării sunt clasificate, rezultatele obținute fiind mai apoi comparate cu adevăratele categorii care ar fi trebuit alese.

Informațiile obținute sunt relevante pentru construirea matricei confuziei, sau a erorilor cum mai este denumită, care permite vizualizarea performanțelor algoritmului. Coloanele corespund instanțelor categoriilor prezise, iar rândurile corespund instanțelor din categoriile corecte. Metoda implicată în rezolvarea acestei funcționalități este apelată ulterior în cadrul metodei principale, care cu ajutorul matricei confuziei instanțiază un obiect al clasei Acuratete. Atât acuratețea, cât și matricea confuziei sunt salvate în cadrul unor fișiere, astfel încât la pornirea viitoare a aplicației, utilizatorul să poată vedea aceste informații.
public void testeazaAlgoritm(List listaGeneratoareDocumente) {

int nrDocumenteClasificate = 0;

Map> matriceaConfuziei = this.calculeazaMatriceaConfuziei(listaGeneratoareDocumente);

Double sensibilitateTotal = 0.0;

Double precizieTotal = 0.0;

Double scorF1Total = 0.0;

int adevaratPozitiveTotal = 0;

int falsPozitiveTotal = 0;

int adevaratNegativeTotal = 0;

int falsNegativeTotal = 0;

for (String denumireCategorie : this.preiaDenumiriCategorii(listaGeneratoareDocumente)) {

//numarul de elemente clasificate corect pentru aceasta categorie



int adevaratPozitive = matriceaConfuziei.get(denumireCategorie).get(denumireCategorie);

//numarul de elemente din alte categorii care au fost clasificate ca facand parte din categoria de fata



int falsPozitive = 0;

for (String key : matriceaConfuziei.keySet()) {

if (!key.equals(denumireCategorie)) {

falsPozitive = falsPozitive + matriceaConfuziei.get(key).get(denumireCategorie);

}

}

//numarul de elemente din acesta categorie care au fost clasificate ca facand parte din alte categorii



int falsNegative = 0;

Map hashMap = matriceaConfuziei.get(denumireCategorie);



for (String key : hashMap.keySet()) {

if (!key.equals(denumireCategorie)) {

falsNegative = falsNegative + hashMap.get(key);

}

}

//numarul de elemente care au fost clasificate corect, ca facand parte din alte categorii



int adevaratNegative = 0;

for (String key : matriceaConfuziei.keySet()) {

if (!key.equals(denumireCategorie)) {

for (String key1 : matriceaConfuziei.get(key).keySet()) {

if (!key1.equals(denumireCategorie)) {

adevaratNegative = adevaratNegative + matriceaConfuziei.get(key).get(key1);

}

}

}



}

if (nrDocumenteClasificate == 0) {

nrDocumenteClasificate = nrDocumenteClasificate + adevaratNegative + adevaratPozitive + falsNegative + falsPozitive;

}

adevaratPozitiveTotal += adevaratPozitive;



falsPozitiveTotal += falsPozitive;

falsNegativeTotal += falsNegative;

adevaratNegativeTotal += adevaratNegative;

Double sensibilitate = 0.0;

Double precizie = 0.0;

Double scorF1 = 0.0;



if ((adevaratPozitive + falsNegative) > 0) {

sensibilitate = new Double(adevaratPozitive)/(adevaratPozitive + falsNegative);

}

if ((adevaratPozitive + falsPozitive) > 0) {

precizie = new Double(adevaratPozitive)/(adevaratPozitive + falsPozitive);

}

if ((2 * adevaratPozitive + falsPozitive + falsNegative) > 0) {

scorF1 = new Double(2 * adevaratPozitive)/(2 * adevaratPozitive + falsPozitive + falsNegative);

}

sensibilitateTotal = sensibilitateTotal + sensibilitate;



precizieTotal = precizieTotal + precizie;

scorF1Total = scorF1Total + scorF1;

}

Double macroSensibilitate = sensibilitateTotal/listaGeneratoareDocumente.size();



Double macroPrecizie = precizieTotal/listaGeneratoareDocumente.size();

Double macroScorF1 = scorF1Total/listaGeneratoareDocumente.size();

Double microSensibilitate = new Double(adevaratPozitiveTotal)/(adevaratPozitiveTotal + falsPozitiveTotal);

Double microPrecizie = new Double(adevaratPozitiveTotal)/(adevaratPozitiveTotal + falsNegativeTotal);

Double microScorF1 = 2/(1/microSensibilitate + 1/microPrecizie);

Double acuratete = new Double(adevaratPozitiveTotal)/(nrDocumenteClasificate);

DateFormat df = new SimpleDateFormat("yy/MM/dd HH:mm:ss");

Calendar calobj = Calendar.getInstance();

Acuratete ac = new Acuratete(this.preiaDenumiriCategorii(listaGeneratoareDocumente).toString(), df.format(calobj.getTime()), microPrecizie, microSensibilitate, microScorF1, macroPrecizie, macroSensibilitate, macroScorF1, acuratete, observatii);

acurateteHibernateDAO.saveAcuratete(ac);

Serializable.serializeazaObiect("src/fisiere/acuratete.txt", ac);

Serializable.serializeazaObiect("src/fisiere/matriceaConfuziei.txt", matriceaConfuziei);

}


    1. Yüklə 465,96 Kb.

      Dostları ilə paylaş:
1   ...   6   7   8   9   10   11   12   13   14




Verilənlər bazası müəlliflik hüququ ilə müdafiə olunur ©muhaz.org 2024
rəhbərliyinə müraciət

gir | qeydiyyatdan keç
    Ana səhifə


yükləyin