Iti0210lab112

Allikas: Lambda

Klassifitseerimine: otsustuspuu ja närvivõrk

Sissejuhatus

Kodutöös proovime otsustuspuu ja närvivõrguga andmete klassifitseerimist. Katsetamiseks on andmestik autode mõnedest parameetritest, iga auto kohta on antud hinnang, kas see on hea või halb ost. Andmestikul on mõningad eripärad: see on sünteetiline, ehk siis mitte korjatud päris klientidelt, vaid tekitatud mingi statistika alusel. Teiseks ei ole andmestik tasakaalus.

Vali masinõppeks sobiv tarkvarapakett. Allolev juhend eeldab, et kasutad Pythonit, muude vahenditega tuleb iseseisvalt hakkama saada.

Soovitatud masinõppe teegid (vali üks):

Ülesanne koosneb mõnedest katsetest, mille tulemustest tuleb koostada logi või raport ja see moodles esitada.

Andmete kirjeldus

Väljavõtted täielikust kirjeldusest:

   Car Evaluation Database was derived from a simple hierarchical
   decision model originally developed for the demonstration of DEX
   (M. Bohanec, V. Rajkovic: Expert system for decision
   making. Sistemica 1(1), pp. 145-157, 1990.). The model evaluates
   cars according to the following concept structure:

   CAR                      car acceptability
   . PRICE                  overall price
   . . buying               buying price
   . . maint                price of the maintenance
   . TECH                   technical characteristics
   . . COMFORT              comfort
   . . . doors              number of doors
   . . . persons            capacity in terms of persons to carry
   . . . lug_boot           the size of luggage boot
   . . safety               estimated safety of the car

Statistika:

   Number of Instances: 1728
   (instances completely cover the attribute space)

   Number of Attributes: 6

Tunnused ja nende väärtused:

   Attribute Values:

   buying       v-high, high, med, low
   maint        v-high, high, med, low
   doors        2, 3, 4, 5-more
   persons      2, 4, more
   lug_boot     small, med, big
   safety       low, med, high

   Missing Attribute Values: none

Klassid (päris andmetes on "vgood", mitte "v-good"):

   class      N          N[%]
   -----------------------------
   unacc     1210     (70.023 %) 
   acc        384     (22.222 %) 
   good        69     ( 3.993 %) 
   v-good      65     ( 3.762 %) 


Ettevalmistus

Andmed lae alla siit.

Kõigepealt tuleb andmed sisse lugeda. Käsitsi ei ole seda keeruline teha, aga näiteks Anacondaga tuleb kaasa pandas pakett:

import pandas

# veergude nimed võetud kirjeldusest
features = ["buying", "maint", "doors", "persons", "lug_boot", "safety"]        
                                                                                
car_data = pandas.read_csv("car.data",                                          
    header=None,                                                                
    names=features + ["class"])                                                 

Tulemuseks on DataFrame tüüpi objekt. See formaat on scikit-learn paketi poolt hästi toetatud ning paljud meetodid oskavad seda otse sisse süüa.

Kategooriate teisendamine numbriteks

Osad masinõppe tehnikad nõuavad juba oma olemuselt numbrilist sisendit (närvivõrk), teiste puhul võib see tuleneda implementatsioonist. Korrektne viis kategooriate teisendamiseks on need asendada umbes nii:

Algandmed:

 uksi
 2
 2
 4
 2
 5more

Teisendatud:

 uksi_2  uksi_4  uksi_5more
 1       0       0
 1       0       0
 0       1       0
 1       0       0
 0       0       1

Lihtsalt numbritega asendamine on ka võimalik (näiteks, low - 0, med - 0.5, high - 1.0), aga üldiselt on abstraktsete nähtuste, nagu suur, väike, punane või roheline, ühele numbrilisele skaalale paigutamine kahtlane idee.

pandas paketiga:

X_text = car_data.loc[:, features]

X = pandas.get_dummies(X_text)                                                   
print(X.columns)                                                                

y = car_data["class"]

Otsustuspuu

Jaotame andmed treening- ja testandmeteks. Selleks on juba valmisfunktsioon. Esialgu teeme andmed lihtsalt kaheks suureks tükiks: 70% treeninguks ja 30% testiks:

from sklearn.model_selection import train_test_split
# no shuffling
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, shuffle=False)

Nüüd treenime otsustuspuu (kui tahad täpsemalt uurida, vt juhend):

from sklearn import tree
dtc=tree.DecisionTreeClassifier()
dtc.fit(X_train, y_train)

# puu on treenitud, vaatame kiirelt, kui hästi ta testandmeid klassifitseerib:
dtc.score(X_test, y_test)

Täpsus on üsna madal. Mis juhtus? Kui vaatad sisendfaili, siis on seal read sorteeritud. Test- ja treeningandmeteks tükeldamisel satuvad ühte poolde rohkem ühesugused näited ja teise poolde teistsugused. See ei ole hea mõte, sest see tähendab, et treenime tahtlikult ebasobivate andmetega. Lubame nüüd train_test_split meetodil read juhuslikult ära segada:

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3)

Puu uuesti treenimisel peaks tulema palju parem klassifitseerimise täpsus.

Tegeleme nüüd teise küsimusega: saadud täpsus on keskmine üle kõigi klasside, mis siis kui meid huvitavad just need autod, mis on "väga head"?

from sklearn.metrics import precision_recall_fscore_support

y_predicted = dtc.predict(X_test)
precision, recall, _, _ = precision_recall_fscore_support(                          
        y_predicted, y_test, average=None, labels=["vgood"])

Siin precision ütleb meile, kui palju autosid, mille kohta otsustuspuu ütles "väga hea" ehk "vgood", olid õigesti klassifitseeritud - kui see arv ei ole 1.0, oli meil valepositiivseid tulemusi. recall on aga number, mis ütleb, kui suur osa "väga häid" autosid kogu testandmestikust üles leiti. Teisiti öeldes, kas leidus ka valenegatiivseid tulemusi.

Närvivõrk

Jätkame sama treening- ja testandmete jaotusega, et erinevaid tehnikaid omavahel otse võrrelda.

Esimene kiire test (põhjaliku närvivõrkude õpetuse leiad siit).

from sklearn import neural_network
nnc = neural_network.MLPClassifier(hidden_layer_sizes=(10,))
nnc.fit(X_train, y_train)
nnc.score(X_test, y_test)

Ainukese kasutatud parameetriga hidden_layer_sizes ütlesime ette, et võrk peaks koosnema sisenditest (leitakse automaatselt), 10 neuroniga peidetud kihist ning väljundkihist (tekitatakse automaatselt sõltuvalt treeningandmetest). Võimalik, et treenimisel kaebab närvivõrk, et tal ei olnud piisavalt aega kaalude konvergeerumiseks. Siis lisame iteratsioone:

nnc = neural_network.MLPClassifier(hidden_layer_sizes=(10,), max_iter=1000)

Närvivõrgu võib teha ka ambitsioonikama, näiteks lisame mitu kihti, mis on sama suured kui sisendkiht:

n_features = X_train.shape[1]
nnc_shape = (n_features, n_features, 10)
nnc = neural_network.MLPClassifier(hidden_layer_sizes=nnc_shape, max_iter=1000)

Proovi, kas klassifitseerimise täpsuse skoor kasvas, võrreldes väiksema võrguga.

Otsustuspuu puhul proovisime ka, kui täpselt ühte konkreetset klassi suudetakse leida. Kordame seda katset:

y_nnc = nnc.predict(X_test)
precision, recall, _, _ = precision_recall_fscore_support(y_nnc, y_test, average=None, labels=["vgood"])

Andmete balansseerimine

Katsetame hüpoteesi, et andmete tasakaalustatus mõjutab seda, kui edukalt kõige haruldlasemat klassi "vgood" leitakse. Ehk siis, võibolla läheb närvivõrk mõnikord lihtsama vastupanu teed ja kipub ennustama muid klasse, kuna "vgood"-i niikuinii eriti ei esine. Testandmeid me ei puutu, muudame ainult treeningandmeid, mille pealt närvivõrku õpetatakse. Paneme kõikide vähem esinenud klasside kohta ridu juurde, nii et neid oleks võrdselt "unacc" klassiga ja klasside jaotus oleks 25% iga klassi kohta.

Tükeldame treeningandmed:

X1 = X_train[y_train == "unacc"]
X2 = X_train[y_train == "acc"]
X3 = X_train[y_train == "good"]
X4 = X_train[y_train == "vgood"]
y1 = y_train[y_train == "unacc"]
y2 = y_train[y_train == "acc"]
y3 = y_train[y_train == "good"]
y4 = y_train[y_train == "vgood"]

Genereerime juhuslikult juurde samasuguseid ridu, kui andmetes juba esineb (uusi "juhtumeid" ei leiutata):

from sklearn.utils import resample

biggest_class = X1.shape[0]

X2r, y2r = resample(X2, y2, n_samples=biggest_class)
X3r, y3r = resample(X3, y3, n_samples=biggest_class)
X4r, y4r = resample(X4, y4, n_samples=biggest_class)

print(X4r.shape)

Paneme tükid uuesti kokku. Seejuures "unacc" klassi näiteid ei puudutatud.

X_balanced = pandas.concat([X1, X2r, X3r, X4r])
y_balanced = pandas.concat([y1, y2r, y3r, y4r])

Treeni nüüd närvivõrk uuesti *_balanced andmetega ja vaata, mida annab score meetod ning kas paraneb klassi "vgood" leidmine.

Lahenduse esitamine

Tekita oma tegevustest logi, kus on näha kõikide katsetuste numbrilised tulemused (klassifitseerimise tulemused, precision, recall). Kõiki käske/programmikoodi ära logisse kopeeri - asenda see lühikese selgitusega, mida tehti.

Mida veel saab teha

Need viited on huvi pärast edasi uurimiseks, seda osa pole kohustuslik teha.

  1. Otsustuspuu joonistamine
  2. Ristvalideerimine