Differences

This shows you the differences between two versions of the page.

Link to this comparison view

sd-ca:teme:tema3-2018 [2018/05/09 21:42]
luca.istrate [Schelet de cod si Checker]
— (current)
Line 1: Line 1:
-===== Tema 3 - Optical Character Recognition pe cifre scrise de mână ===== 
  
-** Responsabili:​ ** 
-  *[[luca.istrate@mines-paristech.fr | Luca Istrate]] 
-  *[[andre_medar@yahoo.com | Andrei Medar]] 
- 
- 
-** Data publicării:​ 7 mai ** 
- 
-** Deadline: 20 mai, ora 23:55 ** 
- 
-===== Obiective ===== 
- 
-În urma realizării acestei teme: 
-  * veți învăța cum să adaptați structuri de date cunoscute la cerințe mai complicate 
-  * veți exersa lucrul colaborativ în echipe de câte doi 
-  * veți învăța ceva despre cum funcționează anumiți algoritmi de inteligență artificială 
- 
-===== Intro ===== 
- 
-Optical Character Recognition este un proces prin care se  
-transformă text scanat în text editabil. În cadrul acestei teme, 
-veți dezvolta un program care va aplica un algoritm de Machine ​ 
-Learning în acest scop. Algoritmul are doua  
-etape: Etapa de **învățare**,​ în care codul vostru va primi o serie  
-de imagini și va trebui să învețe să le recunoască;​ Etapa de  
-**prezicere**,​ unde codul vostru va primi o serie de imagini care  
-nu au mai fost vazute și va trebui să decidă ce cifră este  
-reprezentată de imagine. 
- 
- 
-===== Cerința ===== 
- 
-Va trebui să implementați o versiune puțin simplificată a algoritmului 
-Random Forest pentru clasificare. 
- 
-=== Reprezentarea Datelor === 
-Fiecare cifră este reprezentată ca o imagine de 28x28. Pentru simplitate, în loc să folosim 
-o matrice pentru a reprezenta o imagine, vom folosi un vector de lungime 784 (28 * 28). Pentru 
-etapa de învățare,​ fiecare vector va mai avea o dimensiune în plus, anume: v[0] va fi cifra  
-reprezentată de imagine. Deci, în etapa de învățare,​ fiecare vector va avea 785 de intrări. Pentru 
-predicția cifrei, vectorii nu vor avea răspunsul pe prima pozitie, deci vor avea 784 de intrări. 
- 
-Fiecare pixel din imagine este de fapt un întreg de la 0 la 255, imaginea fiind practic grey-scale. 
- 
-=== Terminologie === 
- 
-== Clasa == 
-În cazul nostru, cifra care trebuie prezisă. Termenul de clasă vine din faptul că această problema este 
-una de clasificare. 
- 
-== Sample == 
-Un vector (ca mai sus) care, în cazul nostru, reprezintă o imagine a unei cifre. 
- 
-== Dimensiune == 
-Index al vectorului (denumirea vine de la vectorii din algebră, un vector cu 3 dimensiuni fiind un vector 
-de lungime 3). 
- 
- 
-===== Descrierea algoritmului ===== 
- 
-=== Decision Tree === 
- 
-== Structura == 
-Un arbore de decizie este un arbore binar (în cazul nostru, pentru că parametrii sunt variabile continue). 
-Acesta este similar cu un arbore binar de căutare. Spre deosebire de BST, în nodurile care nu sunt frunze 
-avem două valori, un index de split (''​%%splitIdx%%''​) și o valoare de split (''​%%splitValue%%''​). Decizia de a merge în 
-stânga sau în dreapta este efectuată în felul următor: dacă ''​%%input[splitIndex] <= splitValue %%'',​ mergem în stânga, 
-daca nu, în dreapta. Astfel, singura diferență la nivelul nodurilor care nu sunt frunze este faptul câ inputul 
-primit la search într-un arbore de decizie este un vector, iar comparațiile în fiecare nod se fac pe dimensiunea 
-specificată în nod.  
- 
-Un nod frunză conține clasa prezisă pentru vectorul input. 
- 
- 
- 
-== Învățare == 
-Învățarea se face prin împărțirea setului de date după o anumită regulă, în mod recursiv. Când dintr-o împărțire 
-rezultă un set de date cu o singură clasă, atunci se creează o frunză cu clasa respectivă. Split-urile se efectuează 
-în felul următor: pentru fiecare dimensiune (aici apare o modificare la random forest, vezi mai jos, Random subspace 
-projection) și pentru fiecare valoare de pe dimensiunea respectivă se face un split. Pe fiecare din aceste split-uri se 
-aplică o metrică care trebuie minimizată/​maximizată (în funcție de metrică). Noi vom folosi o metrică numită Information Gain. 
- 
- 
-== Entropia ==  
-Information Gain se bazează pe noțiunea de entropie din Teoria Informației. 
-Entropia este definită astfel: 
- 
-{{https://​wikimedia.org/​api/​rest_v1/​media/​math/​render/​svg/​70b8bc9f2666c42790b5aa20b13a55bdc503dbb7?​.png?​}} 
- 
-Entropia unui set de teste în cazul nostru se măsoară în felul următor: fie $pi$ ($i$ de la $0$ la $9$) $=$ numărul de teste din set care are ca rezultat numărul i împărțit la numărul de teste. $pi$ este practic probabilitatea ca un test să aibă rezultatul $i$. 
- 
-<note warning> 
-**Atenție!** ​ 
- 
-În cazul în care $ pi = 0 $, nu se adaugă nimic la suma ($ \log_2 pi $ fiind nedefinit). 
-</​note>​ 
- 
- 
- 
-== Information Gain == 
-Information Gain este o metrică definită astfel: 
- 
-{{https://​wikimedia.org/​api/​rest_v1/​media/​math/​render/​svg/​264134245f420035af0c4c96bf1c66b9b106ff20?​.png?​}} 
- 
-{{https://​wikimedia.org/​api/​rest_v1/​media/​math/​render/​svg/​cedf76a71c803381440fd6cfd190b2311d0a116c?​.png?​}} 
- 
-Practic, pentru a calcula Information Gain pentru un anumit set de test samples și un index și o valoare de split, calculăm entropia părintelui,​ respectiv entropia copiiilor rezultați în urma acelui split și aplicăm formula din imagine. 
- 
-În cazul nostru, suma ponderată a entropiilor copiilor este: 
- 
-$$ \dfrac{n_{stanga} \cdot H(stanga) + n_{dreapta} \cdot H(dreapta)}{n} $$ 
- 
-Unde: 
- 
-  * $n_{stanga}$ = numărul de samples din copilul din stânga 
-  * $n_{dreapta}$ = numărul de samples din copilul dreapta 
-  * $n$ este numărul total de samples. Astfel $n = n_{stânga} + n_{dreapta}$ 
- 
- 
- 
-<​note>​ 
-Pentru mai multe detalii, vedeți 
-[[https://​en.wikipedia.org/​wiki/​Decision_tree_learning#​Information_gain | aici]]. 
-</​note>​ 
- 
-==Algoritm== 
-Se alege split-ul care maximizează Information Gain, se stochează indexul de split și valoarea de split în nodul respectiv, 
-după care mergem recursiv pe copii. În cazul în care toate split-urile au ca rezultat un copil care nu are niciun element, ​ 
-atunci vom face un nod frunză care are ca valoare clasa majoritară din setul de date. 
- 
-<note warning> 
-**Atenție!** 
- 
-Dacă printre splituri există unele care au un copil fără elemente, atunci considerăm că split-ul este prost, și nu-l luăm 
-în considerare. Un element devine frunză doar atunci când **toate** spliturile au un copil vid. 
-</​note>​ 
- 
- 
- 
-=== Bootstrap aggregation (bagging) === 
-Random forest este un algoritm care combină mai mulți decision tree pentru a da o predicție mai bună. Primul pas pentru 
-această combinare este antrenarea fiecărui din cei n decision trees cu seturi de date mai mici, alese în mod aleator ca 
-subseturi din setul inițial de training. Aceste subseturi se pot intersecta între ele. Va trebui să implementați generarea 
-acestor subseturi. 
- 
-=== Random subspace projection (feature bagging) === 
-Random subspace projection este o modificare adusă de Random forest la Decision trees-ii pe care îi folosește. Această 
-modificare constă în forțarea split-urilor doar pe anumite dimensiuni, selectate aleator. Astfel, la fiecare split, se 
-aleg mai întâi $\sqrt{num\_dimensiuni}$ dimensiuni, dupa care se încearcă split-uri si se maximizează information gain doar 
-pe acele dimensiuni. Dacă spre exemplu, un sample are $16$ dimensiuni, la fiecare split veți considera doar $4$ dintre ele. 
- 
-<​note>​ 
-Ca optimizare la selectarea unui split, puteți folosi ''​%%compute_unique%%''​ pentru a găsi valorile unice de pe dimensiunile 
-selectate. Astfel, în loc să verificați toate numerele de la $0$ la $255$, le veți verifica doar pe cele care apar. Acest lucru 
-crește viteza algoritmului cu mult, având în vedere faptul că, în general, pixelii vor fi aproape albi sau aproape negri. Valorile 
-intermediare apărând în mai puține locuri. 
-</​note>​ 
- 
-=== Schelet === 
-Aveți de implementat funcțiile cu TODO din randomForest.cpp și decisionTree.cpp. Nu aveți voie să modificați altceva. 
- 
-=== Note pentru implementare === 
- 
-    void make_leaf(const vector<​vector<​int>>​ &​samples,​ const bool is_single_class) 
- 
- 
-  * ar trebui să lucreze cu variabilele is_leaf și result din Node 
-  * dacă is_single_class este false, trebuie să găsiți clasa majoritară din setul de date primit. Dacă două clase apar la fel de des, o veți lua pe prima 
- 
-    bool same_class(const vector<​vector<​int>>​ &​samples) ​ 
- 
-  * întoarce true dacă toate sample-urile din samples au aceeași clasă. Altfel, întoarceți false 
- 
- 
-  * Exemplu: samples = {{1, 0, 0, 10....}, {1, 2, 5, 9....}, {1, 4, 99, 7...}}; ''​%%same_class(samples)%%''​ va întoarce true; 
-  * samples = {{1, 0, 0, 10...}, {0, 9, 7, 111...}}; ''​%%same_class(samples)%%''​ va întoarce false; 
- 
- 
-    float get_entropy_by_indexes(const vector<​vector<​int>>​ &​samples,​ const vector<​int>​ &​index) ​   ​ 
- 
-  * calculează entropia subsetului din samples format considerând doar sample-urile de forma samples[i], cu i element din index 
- 
-    vector<​int>​ compute_unique(const vector<​vector<​int>>​ &​samples,​ const int col) 
- 
-  * întoarce un vector care conține valorile unice care apar în matricea samples pe coloana col 
- 
-  * Exemplu: samples = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}}, col = 1; ''​%%compute_unique(samples,​ col)%%''​ va întoarce vectorul 
-  * {2, 6, 10} (nu contează ordinea) 
- 
-    pair<​vector<​int>,​ vector<​int>>​ get_split_as_indexes(const vector<​vector<​int>>​ &​samples,​ const int split_index,​ const int split_value) 
- 
-  * în funcție de split_index și split_value,​ întoarce doi vectori care conțin indecși în vectorul samples, astfel încât: 
-    * oricare ''​%%samples[i]%%''​ are ''​%%samples[i][split_index] <= split_value%%'',​ i aparținând primului rezultat returnat 
-    * oricare ''​%%samples[j]%%''​ are ''​%%samples[j][split_index > split_value%%'',​ j aparținând celui de-al doilea rezultat returnat 
-    * exemplu: pentru samples = {{1, 2, 3}, {5, 6, 7}, {0, 9, 7}}, split_index = 0, split_value = 2 
-    * ''​%%get_split_as_indexes(samples,​ split_index,​ split_value)%%''​ va întoarce perechea formată din {0, 2} si {1} 
- 
-    vector<​int>​ random_dimensions(const int size)  
- 
-  * selectează în mod aleator sqrt(size), rotunjit la întreg prin lipsa) dimensiuni. 
-  * dimensiunile trebuie să fie unice și să nu existe dimensiunea 0 (pentru că ar reprezenta răspunsul) 
-  * exemplu: random_dimensions(5) -> {1, 3} 
-  * explicație:​ 
-    * floor(sqrt(5)) = 2, selectăm în mod aleator două dimensiuni din mulțimea {1, 2, 3, 4} 
- 
-    int predict(const vector<​int>​ &image) const 
- 
-  * navigați arborele de decizie până ajungeți la o frunză și returnați valoarea din frunză 
-  * Exemplu: Pentru arborele: ​ 
- 
-                   (1, 10) 
-                    /  \ 
-                 ​(10) ​ (5) 
- 
-  * unde nodul(1, 10) este nod de decizie iar (10) și (5) noduri frunză. 1 reprezintă indexul de split, 10 reprezintă valoarea de split. 
-  * dacă avem image = {1, 15, 20, 7, 19} predict(image) va întoarce 5. ''​%%image[split_index]%%''​ $=$ 15, $15 \gt 10$, deci mergem în dreapta. Nodul din dreapta este nod frunză și are rezultatul 5. 
- 
- 
-    int predict(const vector<​int>​ &image) (din RandomForest) 
- 
-  * Pentru fiecare Decision Tree din Forest, află răspunsul prezis și în final, întoarce cel mai des întâlnit răspuns. 
- 
-    vector<​vector<​int>>​ get_random_samples(const vector<​vector<​int>>&​ samples, int num_to_return) 
- 
-  * pentru matricea samples, întoarce o submatrice cu num_to_return linii selectate random 
- 
-    pair<​int,​ int> find_best_split(const vector<​vector<​int>>​ &​samples,​ const vector<​int>​ &​dimensions) 
- 
-  * bazat pe dimensions, găsiți split-ul care maximizează information gain și întoarceți o pereche de forma (split_index,​ split_value) 
-  * dacă nu se găsește niciun split bun, atunci nodul trebuie să fie frunză. Recomandăm să întoarceți ceva care nu are sens, cum ar fi (-1, -1). 
- 
-    void train(const vector<​vector<​int>>​ &​samples) 
- 
-  * folosindu-vă de funcțiile descrise mai sus, implementați algoritmul de învățare pentru decision tree 
-  * pseudocod: 
-    * verificați dacă ați ajuns la un nod care trebuie să fie frunză 
-    * căutați cea mai bună valoare de split (nu uitați să folosiți random_dimensions 
-    * dacă nu a fost găsit niciun split bun, faceți frunza 
-    * dacă ați găsit un split, salvați split_index și split_value în nodul curent, creați copii pe baza split-ului și mergeți recursiv 
- 
- 
-<​note>​ 
-**Hint!** 
- 
-Folosiți-vă cât de mult puteți de funcții care operează cu indecși. Copierea matricelor este o operație scumpă. 
-</​note>​ 
- 
-===== Date de intrare ===== 
-Scheletul se ocupa de citirea datelor, deci voi nu trebuie sa implementati nimic. 
- 
-===== Date de ieșire ===== 
-Scheletul se ocupa de datele de iesire. 
-===== Schelet de cod si Checker ===== 
- 
- 
-=== Resurse === 
- 
-Scheletul si checkerul sunt disponibile {{:​sd-ca:​teme:​resurse_tema3.zip|aici}}. 
- 
-=== Detalii schelet === 
- 
-  * Puteti vedea definitiile structurilor si parametrii functiilor in fisierele ''​%%randomForest.h%%''​ si ''​%%decisionTree.h%%''​. 
-  * In structura arborelui folosim un smart pointer (''​%%shared_ptr%%''​). Acest tip de pointer se comporta la fel ca un pointer obisnuit, doar ca nu trebuie sa dati ''​%%free%%''​ sau ''​%%delete%%''​ pe el. Alocarea se face folosind functia ''​%%make_shared%%''​. Acesteia ii veti pasa aceeasi parametri pe care i-ati pasa constructorului:​ ''​%%shared_ptr<​Node>​ n = make_shared<​Node>​(...)%%''​. Am ales sa folosim ''​%%shared_ptr%%''​ pentru a va permite sa va concentrati mai mult pe implementare in loc sa va concentrati pe erori de memorie. 
-  * In functia ''​%%predict%%'',​ marimea vectorului dat ca parametru este cu $1$ mai mica decat la celelalte functii. In cazul ''​%%predict%%''​-ului,​ pe prima pozitie din vector nu va mai fi cifra care trebuie prezisa. Aveti grija cum navigati arborii! 
-===== Reguli pentru trimitere ===== 
- 
-Temele vor trebui trimise pe [[https://​elf.cs.pub.ro/​vmchecker/​ui/#​SD|vmchecker]]. ​ 
-**Atenție!** Temele trebuie trimise în secțiunea **Structuri de Date (CA)**. 
- 
-Arhiva trebuie să conțină: 
-  * sursele voastre (randomForest.cpp,​ decisionTree.cpp si alte surse, pe care le scrieti voi) 
-  * fisier **README** care să conțină detalii despre implementarea temei si despre modul in care ati impartit sarcinile. Va recomandam sa folositi un sistem de versionare al surselor (git). **Atentie,​** nu folositi repository-uri publice de git. 
-  * încercați să evitați folosirea altor fișiere sursa. Nu aveți voie să modificați Makefile-ul. Dacă totuși simțiți nevoia să modularizați mai mult, puteți să folosiți fisiere .h în care să scrieți și implementările 
- 
-===== Punctaj ===== 
- 
-  - **50p** get_split_as_indexes,​ same_class, random_dimensions,​ get_random_samples,​ compute_unique(cate **10p** fiecare) 
-  - **30p** Acuratetea algoritmului:​ > 85% -> **30p**, > 55% -> **20p**, > 25% -> **10p** 
-  - **10p** README + comentarii/​claritate cod (ATENȚIE! Fișierul README trebuie făcut explicit, cât să se înțeleagă ce ați făcut în sursă, dar fără comentarii inutile și detalii inutile) 
-  - **10p** pentru coding-style,​ proporțional cu punctajul obținut pe teste. De exemplu, pentru o temă care obține maxim pe teste, se pot obține **10p** dacă nu aveți erori de coding style. Pentru o temă care obține **40p** pe teste, se pot obține **5p** dacă nu aveți erori de coding style. 
-  - O temă care obține **0p** pe vmchecker este punctată cu **0**. ​ 
-  - O tema care nu compilează va fi punctată cu **0**. 
- 
-<note warning> 
-**Nu copiați!** ​ 
- 
-Toate soluțiile vor fi verificate folosind o unealtă de detectare a plagiatului. În cazul detectării unui astfel de caz, atât plagiatorul cât și autorul original (nu contează cine care este) vor primi punctaj **0** pe **toate temele**! 
- 
-De aceea, vă sfătuim să nu lăsați rezolvări ale temelor pe calculatoare partajate (la laborator etc), pe mail/liste de discuții/​grupuri etc. 
-</​note> ​ 
- 
-===== FAQ ===== 
- 
-  * **Q**: Putem folosi STL? \\ 
-  * **A**: Da, puteți să folosiți STL. 
sd-ca/teme/tema3-2018.1525891370.txt.gz · Last modified: 2018/05/09 21:42 by luca.istrate
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0