Opinnäytetyö · Haaga-Helia

Vehicle Classifier

Tutkin opinnäytetyössäni, soveltuuko siirto-oppiminen ajoneuvojen luokitteluun liikennekuvista, kun laskentaresursseja on vähän. Toteutin PyTorchilla prototyypin, joka tunnistaa bussit, autot, kuorma-autot ja moottoripyörät.

89,25 %

Paras tarkkuus validointikuvista

83,75 %

Tarkkuus jo ensimmäisellä koulutuksella

568

Koulutuskuvaa per luokka

4/5

Opinnäytetyön arvosana

Mitä tein

Aihe on ajankohtainen, sillä tekoälyn ymmärtäminen madaltaa kynnystä hyödyntää sitä käytännössä, esimerkiksi liikennesuunnittelussa. Työ rajattiin yksittäisiin kuviin, reaaliaikaista videota ei käsitelty.

  • Tavoite

    Toteuttaa prototyyppi, joka luokittelee liikennekuvat neljään luokkaan: henkilöautot, bussit, kuorma-autot ja moottoripyörät. Tavoitteena oli vähintään 80 % tarkkuus tavallisella kuluttajatietokoneella.

  • Tietoperusta

    Kävin läpi koneoppimisen, syväoppimisen ja siirto-oppimisen peruskäsitteet sekä konvoluutioneuroverkkojen toiminnan. Lisäksi tarkastelin ResNet-mallia ja sen soveltuvuutta tehtävään.

  • Data

    Käytin MIO-TCD-datasetin 50 000 kuvan osajoukkoa Kagglesta. Jokaisesta luokasta valitsin 568 koulutuskuvaa ja 100 validointikuvaa, jotka pidin omissa kansioissaan.

  • Toteutus

    Toteutin mallin PyTorchilla. ResNet18:n kaikki kerrokset jäädytettiin ja vain viimeinen kerros korvattiin omalla neljän luokan kerroksella ja koulutettiin.

Koodia

Tässä projektin keskeiset osat. Koko lähdekoodi, mukaan lukien esikäsittelyskriptit ja visualisoinnit, löytyy GitHubista.

Malli ja siirto-oppiminen

Esikoulutettu verkko jäädytetään ja viimeinen kerros korvataan. Vain se koulutetaan.

# Ladataan esikoulutettu mallimodel = models.resnet18(weights='DEFAULT')# Jäädytetään kaikki kerroksetfor param in model.parameters():    param.requires_grad = False# Korvataan viimeinen kerros neljälle ajoneuvoluokallenum_classes = 4model.fc = nn.Linear(model.fc.in_features, num_classes)criterion = nn.CrossEntropyLoss()# Optimoidaan vain uutta viimeistä kerrostaoptimizer = optim.Adam(model.fc.parameters(), lr=0.001)

Harjoitussilmukka

Jokaisella kierroksella (epoch) malli käy koulutuskuvat läpi erissä, laskee häviön ja päivittää painot.

num_epochs = 5for epoch in range(num_epochs):    model.train()    running_loss = 0.0    for images, labels in train_loader:        images, labels = images.to(device), labels.to(device)        optimizer.zero_grad()        outputs = model(images)        loss = criterion(outputs, labels)        loss.backward()        # gradientit (backpropagation)        optimizer.step()       # Adam päivittää painot        running_loss += loss.item()    epoch_loss = running_loss / len(train_loader)    train_losses.append(epoch_loss)

Tulokset: ensimmäinen koulutus

Oletusarvoilla (oppimisnopeus 0,001, eräkoko 32, 5 epochia) malli saavutti 83,75 %:n tarkkuuden validointikuvista ja ylitti 80 %:n tavoitteen. Koulutushäviö laski tasaisesti 1,0037:stä 0,3999:ään, eikä ylisovittumista (overfitting) näkynyt. Koulutus tehtiin tavallisella kuluttajatietokoneella ja vain 568 kuvalla per luokka.

Confusion matrix näyttää, missä malli on vahva. Moottoripyörällä on uniikki profiili, joten se tunnistettiin parhaiten. Suurin virhe oli kuorma-autojen sekoittuminen busseihin: 15 kuorma-autoa luokiteltiin busseiksi.

Confusion matrix ensimmäisestä koulutuksesta
Confusion matrix: rivit ovat oikeita luokkia, sarakkeet mallin ennusteita.
Koulutushäviö epochittain ensimmäisellä koulutuksella
Koulutushäviö laski tasaisesti jokaisella kierroksella.

Luokkakohtainen tarkkuus: kuinka suuri osa luokan validointikuvista tunnistettiin oikein.

94 %

Moottoripyörä

92 %

Bussi

76 %

Henkilöauto

73 %

Kuorma-auto

Esimerkkiennusteet

Jokaisesta luokasta kolme oikein (vihreä) ja yksi väärin luokiteltu kuva (punainen). Yksi henkilöauto meni kuorma-autosta, vaikka se on ihmissilmälle selvästi auto. Samalla osa erittäin epätarkoista kuvista tunnistettiin oikein.

Vehicle Classifier -ennusteita: oikein ja väärin luokiteltuja ajoneuvokuvia
Alin rivi näyttää virheelliset ennusteet.

Hyperparametrien optimointi

Muutin yhtä hyperparametria kerrallaan, jotta jokaisen vaikutus näkyi selvästi. Vertailukohta oli ensimmäisen koulutuksen 83,75 %.

TestitapausLearning rateBatchEpochitTarkkuus %Häviö
Pohjatulos0,00132583,750.3999
Batch size 80,0018586,750,5096
Batch size 160,00116587,250,4133
Batch size 640,00164582,750,4239
Batch size 1280,001128581,250,5078
Learning rate 0,010,0132589,250,3210
Learning rate 0,00010,000132572,250,8727
Learning rate 0,000010,0000132537,751,3529
Epochit 100,001321086,500,2836
Epochit 150,001321587,250.2425
Epochit 250,001322587,500,2147
Epochit 500,001325088,250,1548

Oppimisnopeus

Malli oli herkkä askelkoolle. Nopeus 0.01 antoi parhaan tuloksen (89,25 %), kun taas 0.00001 ei oppinut viidessä epochissa juuri mitään (37,75 %).

Eräkoko

Pienempi eräkoko 16 paransi tarkkuutta (87,25 %). Suuret koot 64 ja 128 oppivat liian hitaasti viidessä epochissa.

Koulutuskierrokset

15 epochista 50:een tarkkuus nousi vain yhden prosenttiyksikön, mutta häviö putosi lähes puoleen (0.2425 → 0.1548). Malli ei oppinut paljon uutta, mutta varmuus kasvoi.

Parhaiden arvojen yhdistäminen ei toiminut

Oletin, että oppimisnopeus 0,01, eräkoko 16 ja 25 epochia yhdessä antaisivat parhaan mallin. Tarkkuus jäi kuitenkin 86,75 %:iin, eikä se ylittänyt parasta yksittäistä tulosta.

Häviökuvaaja paljasti syyn: häviö hyppeli epäsäännöllisesti koko koulutuksen ajan. Suuri oppimisnopeus ja pieni eräkoko yhdessä tekivät painojen päivityksistä liian voimakkaita. Hyperparametrit eivät siis ole toisistaan riippumattomia.

Epätasainen koulutushäviö yhdistetyillä hyperparametreilla
Koulutushäviö yhdistetyillä parhailla arvoilla: epävakaa koulutus.

Johtopäätökset ja oppiminen

Paras tulos 89,25 % ylitti 80 %:n tavoitteen lähes kymmenellä prosenttiyksiköllä. Raskainkin koulutus, 50 epochia, vei vain muutaman minuutin tavallisella tietokoneella. Siirto-oppiminen osoittautui tehokkaaksi tavaksi toteuttaa malli rajallisilla resursseilla.

Tuloksista voi poimia kaksi konfiguraatiota: nopeaan prototyyppiin oppimisnopeus 0,01 oletusarvoilla, ja vakaampaan tulokseen oletusnopeus 0,001 ja 25 epochia.

Jatkokehitys olisi reaaliaikainen videotunnistus ja koulutusdatan kasvattaminen 568 kuvasta esimerkiksi 5 000:een per luokka. Tuloksia ei voi suoraan yleistää suurempaan luokkamäärään tai videokuvaan.

Aloitin ilman aiempaa kokemusta tekoälymallien kehittämisestä. Työssä opin kouluttamaan ensimmäisen koneoppimismallini, käyttämään PyTorchia käytännössä ja ymmärtämään hyperparametrien vaikutuksen.

Opin myös, ettei pelkkä tarkkuusprosentti riitä: confusion matrix ja häviökuvaaja paljastivat asioita, jotka luku yksin piilotti. Matkalla ratkaisin esimerkiksi CUDA-asennuksen ja kirjoitin skriptin, joka siirsi validointikuvat erilleen koulutuskuvista.