Slik bygger du en bildeklassifiserer med mer enn 97% nøyaktighet

En klar og komplett plan for å lykkes

Hvordan lærer du en datamaskin å se på et bilde og identifisere det riktig som en blomst? Hvordan lærer du en datamaskin å se et bilde av en blomst og deretter fortelle deg nøyaktig hvilken blomsterart det er når du ikke vet hvilken art det er?

La meg vise deg!

Denne artikkelen tar deg gjennom det grunnleggende om å lage en bildeklassifiserer med PyTorch. Du kan tenke deg å bruke noe slikt i en telefon-app som forteller deg navnet på blomsten kameraet ditt ser på. Hvis du ønsket, kan du trene denne klassifisereren og eksportere den for bruk i en egen applikasjon.

Hva du gjør herfra, avhenger helt av deg og fantasien.

Jeg setter sammen denne artikkelen for alle der ute som er helt nye for alt dette og leter etter et sted å begynne. Det er opp til deg å ta denne informasjonen, forbedre den og gjøre den til din egen!

Hvis du vil vise notatboken, kan du finne den her.

Fordi denne PyTorch-bildeklassifisereren ble bygget som et sluttprosjekt for et Udacity-program, trekker koden på kode fra Udacity, som igjen trekker til den offisielle PyTorch-dokumentasjonen. Udacity ga også en JSON-fil for kartlegging av etiketter. Den filen finner du i denne GitHub-repoen.

Informasjon om blomsterdatasettet finner du her. Datasettet inkluderer en egen mappe for hver av de 102 blomsterklassene. Hver blomst er merket som et nummer, og hvert av de nummererte katalogene har et antall .jpg-filer.

La oss komme i gang!

Foto av Annie Spratt på Unsplash

Fordi dette er et nevralt nettverk som bruker et større datasett enn CPU-en min kunne håndtere på rimelig tid, gikk jeg videre og satte opp bildeklassifiseringen min i Google Colab. Colab er virkelig fantastisk fordi den gir gratis GPU. (Hvis du er ny på Colab, sjekk ut denne artikkelen om hvordan du kommer i gang med Google Colab!)

Fordi jeg brukte Colab, trengte jeg å starte med å importere PyTorch. Du trenger ikke å gjøre dette hvis du ikke bruker Colab.

*** OPPDATER! (01/29) *** Colab støtter nå innfødt PyTorch !!! Du burde ikke trenge å kjøre koden nedenfor, men jeg lar den være i tilfelle noen har problemer!

# Importer PyTorch hvis du bruker Google Colab
# http://pytorch.org/
fra os.path import eksisterer
fra Wheel.pep425tags import get_abbr_impl, get_impl_ver, get_abi_tag
platform = '{} {} - {}' .format (get_abbr_impl (), get_impl_ver (), get_abi_tag ())
cuda_output =! ldconfig -p | grep cudart.so | sed -e 's /.* \. \ ([0-9] * \) \. \ ([0-9] * \) $ / cu \ 1 \ 2 /'
akselerator = cuda_output [0] hvis det finnes ('/ dev / nvidia0') ellers 'cpu'
! pip install -q http://download.pytorch.org/whl/{accelerator}/torch-0.4.1-{platform}-linux_x86_64.whl torchvision
import fakkel

Så, etter å ha hatt litt problemer med Pillow (det er buggy i Colab!), Gikk jeg bare videre og kjørte dette:

importer PIL
print (PIL.PILLOW_VERSION)

Hvis du får noe under 5.3.0, bruk rullegardinmenyen under "Kjøretid" for å "starte kjøretid på nytt" og kjøre denne cellen på nytt. Du skal være god å gå!

Du vil bruke GPU til dette prosjektet, som er utrolig enkelt å sette opp på Colab. Du går bare til rullegardinmenyen “runtime”, velger “endre runtime type” og velger “GPU” i rullegardinmenyen for maskinvareakselerator!

Da liker jeg å løpe

train_on_gpu = fakkel.cuda.is_available ()
hvis ikke train_on_gpu:
    print ('Bummer! Trening på CPU ...')
ellers:
    print ('Du er god å gå! Trening på GPU ...')

bare for å sikre at det fungerer. Så løp

device = torch.device ("cuda: 0" hvis torch.cuda.is_available () ellers "cpu")

å definere enheten.

Etter dette, importer filene. Det er mange måter å gjøre dette på, inkludert montering av Google Drive hvis du har datasettet ditt lagret der, som faktisk er veldig enkelt. Selv om jeg ikke hadde noe å finne på å være den mest nyttige løsningen, inkluderer jeg det nedenfor, bare fordi det er så enkelt og nyttig.

fra google.colab importstasjon
drive.mount ( '/ innhold / GDrive')

Så ser du en lenke, klikker på den, tillater tilgang, kopier koden som dukker opp, limer den i boksen, trykker enter og du er god til å gå! Hvis du ikke ser stasjonen din i sideboksen til venstre, er det bare å "oppdatere" og den skal dukke opp.

(Kjør cellen, klikk på lenken, kopier koden på siden, lim den inn i boksen, trykk enter, så ser du dette når du har montert harddisken):

Det er faktisk superenkelt!

Hvis du heller vil laste ned en delt zip-filkobling (denne avviklingen er enklere og raskere for dette prosjektet), kan du bruke:

! wget
! unzip

For eksempel:

! wget -cq https://s3.amazonaws.com/content.udacity-data.com/courses/nd188/flower_data.zip
! unzip -qq flower_data.zip

Det vil gi deg Udacitys blomsterdatasett på sekunder!

(Hvis du laster opp små filer, kan du bare laste dem opp direkte med en enkel kode. Hvis du vil, kan du også bare gå til venstre på skjermen og klikke på "last opp filer" hvis du ikke gjør det. føler for å kjøre noen enkle koder for å hente en lokal fil.)

Etter å ha lastet inn dataene, importerte jeg bibliotekene jeg ønsket å bruke:

% matplotlib inline
% config InlineBackend.figure_format = 'netthinne'
importtid
importer json
importkopi
importer matplotlib.pyplot som plt
importer sjøfødt som sns
importer numpy som np
importer PIL
fra PIL-importbilde
fra samlinger importerer OrderedDict
import fakkel
fra fakkelimport nn, optim
fra torch.optim import lr_scheduler
fra torch.autograd import Variabel
import fakkeltog
fra torchvision import datasett, modeller, transformasjoner
fra torch.utils.data.sampler import SubsetRandomSampler
import torch.nn som nn
import torch.nn.functional as F

Neste kommer datatransformasjonene! Du vil sørge for å bruke flere forskjellige typer transformasjoner på treningssettet ditt for å hjelpe programmet ditt å lære så mye det kan. Du kan lage en mer robust modell ved å trene den på vippede, roterte og beskjære bilder.

Midlene til at standardavvik blir gitt for å normalisere bildeverdiene før de sendes til vårt nettverk, men de kan også bli funnet ved å se på middelverdien og standardavvikverdiene for de forskjellige dimensjonene til bildetensorene. Den offisielle dokumentasjonen er utrolig nyttig her!

For min bildeklassifiserer holdt jeg det enkelt med:

data_transforms = {
    'tog': transforms.Compose ([
        transforms.RandomRotation (30),
        transforms.RandomResizedCrop (224),
        transforms.RandomHorizontalFlip (),
        transforms.ToTensor (),
        transforms.Normaliser ([0.485, 0.456, 0.406],
                             [0.229, 0.224, 0.225])
    ]),
    'valid': transforms.Compose ([
        transforms.Resize (256),
        transforms.CenterCrop (224),
        transforms.ToTensor (),
        transforms.Normaliser ([0.485, 0.456, 0.406],
                             [0.229, 0.224, 0.225])
    ])
}
# Last datasettene med ImageFolder
image_datasets = {x: datasett.ImageFolder (os.path.join (data_dir, x),
                                          data_transforms [x])
                  for x i ['tog', 'gyldig']}
# Definer dataladere ved hjelp av bildedatasettene og togformene
batch_size = 64
dataloaders = {x: torch.utils.data.DataLoader (image_datasets [x], batch_size = batch_size,
                                             shuffle = True, num_workers = 4)
              for x i ['tog', 'gyldig']}
class_names = image_datasets ['train'] .klasser
dataset_sizes = {x: len (image_datasets [x]) for x i ['train', 'valid']}
class_names = image_datasets ['train'] .klasser

Som du kan se ovenfor, definerte jeg også batchstørrelse, datalastere og klassenavn i koden over.

For å se veldig raskt på dataene og sjekke enheten min, løp jeg:

print (dataset_sizes)
print (enhet)
{'tog': 6552, 'gyldig': 818}
cuda: 0

Deretter må vi gjøre noen kartlegging fra etikettnummeret og det faktiske blomsternavnet. Udacity ga en JSON-fil for at denne kartleggingen enkelt kunne gjøres.

med åpen ('cat_to_name.json', 'r') som f:
    cat_to_name = json.load (f)

For å teste datalasteren, kjør:

bilder, etiketter = neste (iter (dataloaders ['train']))
rand_idx = np.random.randint (len (bilder))
# Skriv ut (rand_idx)
print ("label: {}, class: {}, name: {}". format (labels [rand_idx] .item (),
                                               class_names [etiketter [rand_idx] .item ()],
                                               cat_to_name [class_names [etiketter [rand_idx] .item ()]]))

Nå begynner det å bli enda mer spennende! En rekke modeller i løpet av de siste årene har blitt skapt av mennesker langt, langt mer kvalifiserte enn de fleste av oss for gjenbruk i datamaskinens synsproblemer. PyTorch gjør det enkelt å laste ferdigtrente modeller og bygge videre på dem, og det er akkurat det vi skal gjøre for dette prosjektet. Valg av modell er helt opp til deg!

Noen av de mest populære ferdigtrente modellene, som ResNet, AlexNet og VGG, kommer fra ImageNet Challenge. Disse forhåndsopplærte modellene lar andre raskt oppnå banebrytende resultater i datasyn uten å trenge så store datamaskiner, tålmodighet og tid. Jeg hadde faktisk gode resultater med DenseNet og bestemte meg for å bruke DenseNet161, noe som ga meg veldig gode resultater relativt raskt.

Du kan raskt konfigurere dette ved å løpe

modell = modeller.densenet161 (pretrained = True)

men det kan være mer interessant å gi deg selv et valg av modell, optimizer og planlegger. For å sette opp et valg innen arkitektur, kjør

model_name = 'densenet' #vgg
hvis modellnavn == 'densenet':
    modell = modeller.densenet161 (pretrained = True)
    num_in_features = 2208
    print (modell)
elif model_name == 'vgg':
    model = models.vgg19 (pretrained = True)
    num_in_features = 25088
    print (model.classifier)
ellers:
    print ("Ukjent modell, velg 'densenet' eller 'vgg'")

som lar deg raskt sette opp en alternativ modell.

Etter det kan du begynne å bygge klassifiseringen din ved å bruke parametrene som fungerer best for deg. Jeg gikk videre og bygde

for param i modell.parametre ():
    param.requires_grad = Falske
def build_classifier (num_in_features, hidden_layers, num_out_features):
   
    klassifiserer = nn.Sekvens ()
    hvis skjulte_spillere == Ingen:
        classifier.add_module ('fc0', nn.Linear (num_in_features, 102))
    ellers:
        layer_sizes = zip (Hidden_layers [: - 1], Hidden_layers [1:])
        classifier.add_module ('fc0', nn.Linear (num_in_features, Hidden_layers [0]))
        classifier.add_module ('relu0', nn.ReLU ())
        classifier.add_module ('drop0', nn.Dropout (.6))
        classifier.add_module ('relu1', nn.ReLU ())
        classifier.add_module ('drop1', nn.Dropout (.5))
        for i, (h1, h2) i enumerate (lagstørrelser):
            classifier.add_module ('fc' + str (i + 1), nn. Lineær (h1, h2))
            classifier.add_module ('relu' + str (i + 1), nn.ReLU ())
            classifier.add_module ('drop' + str (i + 1), nn.Dropout (.5))
        classifier.add_module ('output', nn.Linear (Hidden_layers [-1], num_out_features))
        
    retur klassifiserer

som gjør det enkelt å endre antallet skjulte lag som jeg bruker, samt raskt justere frafallet. Du kan bestemme deg for å legge til flere ReLU- og frafallslag for å finpusse modellen.

Deretter jobber du med å trene klassifiseringsparametrene. Jeg bestemte meg for å forsikre meg om at jeg bare trente klassifiseringsparametrene her mens jeg hadde frosne funksjonsparametere. Du kan bli så kreativ som du vil med optimaliseringsprogrammet, kriteriet og planleggeren. Kriteriet er metoden som er brukt for å evaluere modelltilpasning, optimalisatoren er optimaliseringsmetoden som brukes for å oppdatere vektene, og planleggeren gir forskjellige metoder for å justere læringsfrekvensen og trinnstørrelsen som ble brukt under optimaliseringen.

Prøv så mange alternativer og kombinasjoner du kan for å se hva som gir deg det beste resultatet. Du kan se all den offisielle dokumentasjonen her. Jeg anbefaler å ta en titt på det og ta dine egne beslutninger om hva du vil bruke. Du har ikke bokstavelig talt et uendelig antall alternativer her, men det føles helt sikkert som det når du begynner å leke!

Hidden_layers = Ingen
klassifiserer = build_classifier (num_in_features, Hidden_layers, 102)
print (klassifiserer)
# Tren bare klassifiseringsparametere, funksjonsparametere er frosset
hvis modellnavn == 'densenet':
    model.classifier = klassifiserer
    kriterium = nn.CrossEntropyLoss ()
    optimizer = optim.Adadelta (modell.parametre ())
    sched = optim.lr_scheduler.StepLR (optimizer, step_size = 4)
elif model_name == 'vgg':
    model.classifier = klassifiserer
    kriterium = nn.NLLTap ()
    optimizer = optim.Adam (model.classifier.parameters (), lr = 0,0001)
    sched = lr_scheduler.StepLR (optimizer, trinnstørrelse = 4, gamma = 0,1)
ellers:
    passere

Nå er det på tide å trene modellen din.

# Tilpasset fra https://pytorch.org/tutorials/beginner/transfer_learning_tutorial.html
def train_model (modell, kriterium, optimizer, sched, num_epochs = 5):
    siden = time.time ()
best_model_wts = copy.deepcopy (model.state_dict ())
    best_acc = 0,0
for epoke innen rekkevidde (num_epochs):
        print ('Epoke {} / {}'. format (epoke + 1, num_epochs))
        print ('-' * 10)
# Hver epoke har en trenings- og valideringsfase
        for fase i ['tog', 'gyldig']:
            hvis fase == 'tog':
                model.train () # Sett modell til treningsmodus
            ellers:
                model.eval () # Sett modell til evalueringsmodus
running_loss = 0,0
            running_corrects = 0
# Iterate over data.
            for innganger, etiketter i dataloadere [fase]:
                innganger = innganger.til (enhet)
                labels = labels.to (enhet)
# Null parametergradienter
                optimizer.zero_grad ()
# Fremover
                # spor historie hvis bare i tog
                med fakkel.set_grad_enabled (fase == 'tog'):
                    utganger = modell (innganger)
                    _, preds = fakkel.max (utganger, 1)
                    tap = kriterium (output, labels)
# Bakover + optimaliser bare hvis du er i treningsfase
                    hvis fase == 'tog':
                        # Sched.step ()
                        loss.backward ()
                        
                        optimizer.step ()
# Statistikk
                running_loss + = loss.item () * input.size (0)
                running_corrects + = torch.sum (preds == labels.data)
epoch_loss = running_loss / dataset_sizes [phase]
            epoch_acc = running_corrects.double () / datasett_størrelser [fase]
print ('{} Tap: {: .4f} Acc: {: .4f}'. -format (
                fase, epoch_loss, epoch_acc))
# Dypkopi modellen
            hvis fase == 'valid' og epoch_acc> best_acc:
                best_acc = epoch_acc
                best_model_wts = copy.deepcopy (model.state_dict ())
skrive ut()
time_elapsed = time.time () - siden
    print ('Trening fullført i {: .0f} m {: .0f} s'.format (
        time_elapsed // 60, time_elapsed% 60))
    print ('Best val Acc: {: 4f}'. format (best_acc))
# Last inn beste modellvekter
    model.load_state_dict (best_model_wts)
    
    returmodell
epoker = 30
model.to (enhet)
modell = train_model (modell, kriterium, optimizer, sched, epoker)

Jeg ønsket å være i stand til å overvåke epokene mine enkelt og også følge med på tiden som gikk da modellen gikk. Koden over inkluderer begge deler, og resultatene er ganske bra! Du kan se at modellen raskt lærer og nøyaktigheten på valideringssettet nådde raskt over 95% innen epoke 7!

Epok 1/30
----------
tog Tap: 2.4793 Acc: 0.4791
gyldig tap: 0,9688 Akk: 0,8191

Epok 2/30
----------
tog Tap: 0.8288 Acc: 0.8378
gyldig tap: 0,4714 Akk: 0,9010

Epok 3/30
----------
tog Tap: 0.5191 Acc: 0.8890
gyldig tap: 0.3197 Akk: 0,9181

Epok 4/30
----------
tog Tap: 0.4064 Acc: 0.9095
gyldig tap: 0.2975 Akk: 0,9169

Epok 5/30
----------
tog Tap: 0.3401 Acc: 0.9214
gyldig tap: 0.2486 Akk: 0.9401

Epok 6/30
----------
tog Tap: 0.3111 Acc: 0.9303
gyldig tap: 0.2153 Akk: 0,9487

Epok 7/30
----------
tog Tap: 0.2987 Acc: 0.9298
gyldig tap: 0.1969 Akk: 0,9584
...
Trening fullført i 67m 43s
Best val Acc: 0,973105

Du kan se at det tok litt over en time å kjøre denne koden på Google Colab med GPU.

Nå er det tid for evaluering

model.eval ()
nøyaktighet = 0
for innganger, etiketter i dataloaders ['valid']:
    innganger, etiketter = innganger.til (enhet), etiketter.til (enhet)
    utganger = modell (innganger)
    
    # Klasse med høyest sannsynlighet er vår spådde klasse
    likhet = (labels.data == outputs.max (1) [1])
# Nøyaktighet = antall riktige prediksjoner delt på alle spådommer
    nøyaktighet + = likhet.type_as (fakkelen. flyterensor ()). middel ()
    
print ("Test nøyaktighet: {: .3f}". format (nøyaktighet / len (dataloaders ['valid'])))
Testnøyaktighet: 0,973

Det er viktig å lagre sjekkpunktet ditt

model.class_to_idx = image_datasets ['train']. class_to_idx
checkpoint = {'input_size': 2208,
              'output_size': 102,
              'epoker': epoker,
              'batch_size': 64,
              'modell': models.densenet161 (pretrained = True),
              'klassifiserer': klassifiserer,
              'scheduler': sched,
              'optimizer': optimizer.state_dict (),
              'state_dict': model.state_dict (),
              'class_to_idx': model.class_to_idx
             }
   
torch.save (checkpoint, 'checkpoint.pth')

Du trenger ikke lagre alle parameterne, men jeg inkluderer dem her som et eksempel. Dette sjekkpunktet lagrer modellen spesielt med en ferdig trent densenet161-arkitektur, men hvis du vil lagre sjekkpunktet ditt med to-valg, kan du absolutt gjøre det. Bare juster inngangsstørrelse og modell.

Nå kan du laste inn sjekkpunktet ditt. Hvis du sender inn prosjektet ditt i Udacity-arbeidsområdet, kan ting bli litt vanskelig. Her er litt hjelp med feilsøking av kontrollpunktbelastningen.

Du kan sjekke nøklene dine ved å løpe

ckpt = fakkel.load ('checkpoint.pth')
ckpt.keys ()

Last deretter inn og gjenoppbygg modellen!

def load_checkpoint (filepath):
    sjekkpunkt = lommelykt (filepath)
    modell = sjekkpunkt ['modell']
    model.classifier = checkpoint ['classifier']
    model.load_state_dict (kontrollpunkt [ 'state_dict'])
    model.class_to_idx = sjekkpunkt ['class_to_idx']
    optimizer = checkpoint ['optimizer']
    epoker = sjekkpunkt ['epoker']
    
    for param i modell.parametre ():
        param.requires_grad = Falske
        
    returmodell, sjekkpunkt ['class_to_idx']
modell, class_to_idx = load_checkpoint ('checkpoint.pth')

Vil du fortsette? Det er en god idé å gjøre litt forbehandling av bilder og slutninger for klassifisering. Gå videre og definer bildebanen din og åpne et bilde:

image_path = 'flower_data / valid / 102 / image_08006.jpg'
img = Image.open (image_path)

Behandle bildet ditt og se på et behandlet bilde:

def process_image (image):
    '' 'Vekter, avlinger og normaliserer et PIL-bilde for en PyTorch-modell,
        returnerer en numpy matrise
    '''
    # Behandle et PIL-bilde for bruk i en PyTorch-modell
    # tensor.numpy (). transponere (1, 2, 0)
    preprocess = transforms.Compose ([
        transforms.Resize (256),
        transforms.CenterCrop (224),
        transforms.ToTensor (),
        transformerer.Normaliser (gjennomsnitt = [0.485, 0.456, 0.406],
                             std = [0.229, 0.224, 0.225])
    ])
    image = preprosess (bilde)
    returbilde
def imshow (bilde, øks = Ingen, tittel = Ingen):
    "" "Imshow for Tensor." ""
    hvis øksa er Ingen:
        fig, ax = plt.subplots ()
    
    # PyTorch-tensorer antar at fargekanalen er den første dimensjonen
    # men matplotlib antar er den tredje dimensjonen
    image = image.numpy (). transponere ((1, 2, 0))
    
    # Angre forbehandling
    middel = n.gr.gruppe ([0.485, 0.456, 0.406])
    std = n.array ([0.229, 0.224, 0.225])
    image = std * image + middel
    
    # Bildet må klippes mellom 0 og 1, eller det ser ut som støy når det vises
    image = np.clip (bilde, 0, 1)
    
    ax.imshow (bilde)
    
    returøks
med Image.open ('flower_data / valid / 102 / image_08006.jpg') som bilde:
    plt.imshow (bilde)
model.class_to_idx = image_datasets ['train']. class_to_idx

Lag en funksjon for prediksjon:

def predict2 (image_path, model, topk = 5):
    '' 'Forutsi klassen (eller klasser) av et bilde ved hjelp av en trent dyp læringsmodell.
    '''
    
    # Implementerer koden for å forutsi klassen fra en bildefil
    img = Image.open (image_path)
    img = prosess_bilde (img)
    
    # Konverter 2D-bilde til 1D-vektor
    img = np.expand_dims (img, 0)
    
    
    img = fakkel.fra_nummer (img)
    
    model.eval ()
    innganger = Variabel (img) .to (enhet)
    logits = model.forward (innganger)
    
    ps = F.softmax (logits, dim = 1)
    topk = ps.cpu (). topk (topk)
    
    retur (e.data.numpy (). klem (). tolist () for e i topk)

Når bildene er i riktig format, kan du skrive en funksjon for å gjøre spådommer med modellen din. En vanlig praksis er å forutsi topp 5 eller så (vanligvis kalt topp-KK) mest sannsynlige klasser. Du vil beregne klassesannsynlighetene og deretter finne KKs største verdier.

For å få topp KK største verdier i en tensor, bruk k.topk (). Denne metoden returnerer både de høyeste k-sannsynlighetene og indeksene for de sannsynlighetene som tilsvarer klassene. Du må konvertere fra disse indeksene til de faktiske klassetikettene ved å bruke class_to_idx, som du la til modellen eller fra bildemappen du brukte til å laste inn dataene. Sørg for å invertere ordboken slik at du også får en kartlegging fra indeks til klasse.

Denne metoden skal ta en vei til et bilde og et modellkontrollpunkt, og deretter returnere sannsynlighetene og klassene.

img_path = 'flower_data / valid / 18 / image_04252.jpg'
sonder, klasser = forutsi2 (img_path, model.to (enhet))
print (probs)
print (klasser)
flower_names = [cat_to_name [class_names [e]] for e in classes]
print (flower_names)

Jeg var ganske fornøyd med hvordan modellen min presterte!

[0.9999195337295532, 1.4087702766119037e-05, 1.3897360986447893e-05, 1.1400215043977369e-05, 6.098791800468462e-06]
[12, 86, 7, 88, 40]
['peruansk lilje "," ørkenrose "," konge protea "," magnolia "," sverdlilje "]

I utgangspunktet er det nesten 100% sannsynlig at bildet jeg spesifiserte er en peruansk lilje. Vil du ta en titt? Prøv å bruke matplotlib for å plotte sannsynlighetene for de fem beste klassene i et søylediagram sammen med inngangsbildet:

def view_classify (img_path, prob, klasser, kartlegging):
    '' 'Funksjon for visning av et bilde og det er forutsagte klasser.
    '''
    image = Image.open (img_path)
fig, (ax1, ax2) = plt.-tomter (fig. størrelse = (6,10), ncols = 1, nrows = 2)
    flower_name = kartlegging [img_path.split ('/') [- 2]]
    ax1.set_title (flower_name)
    ax1.imshow (bilde)
    ax1.axis ( 'av')
    
    y_pos = np.arange (len (prob))
    ax2.barh (y_pos, prob, align = 'sentrum')
    ax2.set_yticks (y_pos)
    ax2.set_yticklabels (flower_names)
    ax2.invert_yaxis () # etiketter leses fra topp til bunn
    ax2.set_title ('Klassesannsynlighet')
view_classify (img_path, prober, klasser, cat_to_name)

Du bør se noe slikt:

Jeg må si, det er jeg ganske fornøyd med! Jeg anbefaler å teste noen få andre bilder for å se hvor nær forutsigelsene dine ligger på en rekke bilder.

Nå er det på tide å lage en egen modell og gi meg beskjed om hvordan det går i svarene nedenfor!

Foto av Pez González på Unsplash

Er du ferdig med din dype læring eller maskinlæringsmodell, men vet du ikke hva du skal gjøre med den neste? Hvorfor ikke distribuere det til internett?

Få din modell der ute slik at alle kan se den!

Sjekk ut denne artikkelen for å lære hvordan du distribuerer maskinen din læringsmodell med Flask!