Sådan bygger du en billedklassificering med mere end 97% nøjagtighed

En klar og komplet plan for succes

Hvordan lærer du en computer at se på et billede og identificere det korrekt som en blomst? Hvordan lærer du en computer at se et billede af en blomst og derefter fortælle dig nøjagtigt hvilken blomsterart det er, når du ikke engang ved, hvilken art det er?

Lad mig vise dig!

Denne artikel tager dig gennem det grundlæggende ved at oprette en billedklassificering med PyTorch. Du kan forestille dig at bruge noget som dette i en telefonapp, der fortæller dig navnet på den blomst, dit kamera ser på. Hvis du ville, kan du træne denne klassifikator og derefter eksportere den til brug i en egen applikation.

Hvad du gør herfra, afhænger helt af dig og din fantasi.

Jeg sammensætter denne artikel til alle derude der er splinterny med alt dette og leder efter et sted at begynde. Det er op til dig at tage disse oplysninger, forbedre dem og gøre dem til dine egne!

Hvis du vil se den bærbare computer, kan du finde den her.

Da denne PyTorch-billedklassificering blev bygget som et afsluttende projekt for et Udacity-program, trækker koden kode fra Udacity, som igen trækker på den officielle PyTorch-dokumentation. Udacity leverede også en JSON-fil til etikettering. Denne fil kan findes i denne GitHub-repo.

Oplysninger om blomsterdatasættet findes her. Datasættet inkluderer en separat mappe for hver af de 102 blomsterklasser. Hver blomst er mærket som et nummer, og hver af de nummererede mapper har et antal .jpg-filer.

Lad os komme igang!

Foto af Annie Spratt på Unsplash

Da dette er et neuralt netværk, der bruger et større datasæt, end min CPU kunne håndtere på en rimelig tid, gik jeg videre og opsatte min billedklassificering i Google Colab. Colab er virkelig fantastisk, fordi det giver gratis GPU. (Hvis du er ny på Colab, kan du se denne artikel om, hvordan du kommer i gang med Google Colab!)

Fordi jeg brugte Colab, var jeg nødt til at starte med at importere PyTorch. Du behøver ikke at gøre dette, hvis du ikke bruger Colab.

*** OPDATERING! (01/29) *** Colab understøtter nu indbygget PyTorch !!! Du skal ikke have brug for at køre koden nedenfor, men jeg lader den stå, bare hvis nogen har problemer!

# Importer PyTorch, hvis du bruger Google Colab
# http://pytorch.org/
fra os.path-import findes
fra wheel.pep425tags importer 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 /'
accelerator = cuda_output [0] hvis der findes ('/ 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

Derefter, efter at have haft nogle problemer med Pillow (det er buggy i Colab!), Gik jeg bare videre og løb dette:

import PIL
print (PIL.PILLOW_VERSION)

Hvis du får noget under 5.3.0, skal du bruge rullemenuen under "Kørsel" til "Genstart kørselstid" og køre denne celle igen. Du skal være god til at gå!

Du ønsker at bruge GPU til dette projekt, som er utroligt nemt at konfigurere på Colab. Du går bare til dropdown-menuen “runtime”, vælger “Change runtime type” og vælger derefter “GPU” i rullemenuen til hardwareaccelerator!

Så kan jeg godt lide at løbe

train_on_gpu = fakkel.cuda.is_available ()
hvis ikke train_on_gpu:
    print ('Bummer! Træning på CPU ...')
andet:
    print ('Du er god til at gå! Træning på GPU ...')

bare for at sikre, at det fungerer. Kør derefter

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

til at definere enheden.

Efter dette skal du importere filerne. Der er masser af måder at gøre dette på, herunder montering af dit Google Drev, hvis du har dit datasæt gemt der, hvilket faktisk er virkelig enkelt. Selvom jeg ikke afsluttede med at finde ud af, at det var den mest nyttige løsning, inkluderer jeg det nedenfor, bare fordi det er så let og nyttigt.

fra google.colab-importdrev
drive.mount ( '/ indhold / GDrive')

Så ser du et link, klikke på det, tillade adgang, kopiere koden, der dukker op, indsæt det i boksen, tryk på Enter, og du er god til at gå! Hvis du ikke kan se dit drev i sideboksen til venstre, skal du bare trykke på "opdater" og det skal vises.

(Kør cellen, klik på linket, kopier koden på siden, indsæt den i boksen, tryk på enter, og du ser dette, når du har monteret dit drev med succes):

Det er faktisk super nemt!

Hvis du hellere vil downloade et delt link til zip-fil (denne afvikling bliver lettere og hurtigere til dette projekt), kan du bruge:

! 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 giver dig Udacity's blomsterdatasæt på få sekunder!

(Hvis du uploader små filer, kan du bare uploade dem direkte med en simpel kode. Hvis du vil, kan du også bare gå til venstre på skærmen og klikke på "upload filer", hvis du ikke gør det har lyst til at køre en enkel kode for at få fat i en lokal fil.)

Efter indlæsning af dataene importerede jeg bibliotekerne, jeg ville bruge:

% matplotlib inline
% config InlineBackend.figure_format = 'retina'
importtid
import json
importkopi
importer matplotlib.pyplot som plt
importer søfødte som sns
import numpy som np
import PIL
fra PIL-importbillede
fra samlinger importerer OrderedDict
import fakkel
fra fakkelimport nn, optim
fra torch.optim import lr_scheduler
fra torch.autograd import Variabel
import fakkelvision
fra torchvision import datasæt, modeller, transformer
fra torch.utils.data.sampler import SubsetRandomSampler
import torch.nn som nn
import fakkel.nn.funktionel som F

Dernæst kommer datatransformationerne! Du vil sørge for at bruge flere forskellige typer transformationer på dit træningssæt for at hjælpe dit program med at lære så meget som det kan. Du kan oprette en mere robust model ved at træne den på vippede, roterede og beskårne billeder.

Midlerne til, at der leveres standardafvigelser for at normalisere billedværdierne, før de overføres til vores netværk, men de kan også findes ved at se på middelværdien og standardafvigelsesværdierne for de forskellige dimensioner af billeddensorerne. Den officielle dokumentation er utroligt nyttig her!

For min billedklassificering 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])
    ])
}
# Indlæs datasættene med ImageFolder
image_datasets = {x: datasets.ImageFolder (os.path.join (data_dir, x),
                                          data_transforms [x])
                  for x i ['tog', 'gyldig']}
# Brug billedsdatasættene og togformerne til at definere dataloadere
batch_størrelse = 64
dataloaders = {x: torch.utils.data.DataLoader (image_datasets [x], batch_size = batch_size,
                                             shuffle = sandt, 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, definerede jeg også batchstørrelse, datalastere og klassnavne i koden ovenfor.

For at kigge meget hurtigt på dataene og kontrollere min enhed løb jeg:

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

Dernæst er vi nødt til at gøre nogle kortlægning fra etikettenummer og det faktiske blomsternavn. Udacity leverede en JSON-fil til, at denne kortlægning kunne udføres ganske enkelt.

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

For at teste datalasteren skal du køre:

billeder, labels = næste (iter (dataloaders ['train']))
rand_idx = np.random.randint (len (billeder))
# Print (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 ()]]))

Nu begynder det at blive endnu mere spændende! En række modeller i de sidste flere år er skabt af mennesker langt, langt mere kvalificerede end de fleste af os til genbrug i computervisionsproblemer. PyTorch gør det nemt at indlæse foruddannede modeller og bygge videre på dem, hvilket er nøjagtigt, hvad vi vil gøre for dette projekt. Valg af model er helt op til dig!

Nogle af de mest populære foruddannede modeller som ResNet, AlexNet og VGG kommer fra ImageNet Challenge. Disse foruddannede modeller giver andre mulighed for hurtigt at opnå banebrydende resultater i computervision uden at have brug for så store mængder computerkraft, tålmodighed og tid. Jeg havde faktisk gode resultater med DenseNet og besluttede at bruge DenseNet161, hvilket gav mig meget gode resultater relativt hurtigt.

Du kan hurtigt konfigurere dette ved at køre

model = models.densenet161 (foruddannet = sandt)

men det kan være mere interessant at give dig selv et valg af model, optimizer og planlægning. For at konfigurere et valg inden for arkitektur skal du køre

model_name = 'densenet' #vgg
hvis modelnavn == 'densenet':
    model = models.densenet161 (foruddannet = sandt)
    num_in_features = 2208
    print (model)
elif model_name == 'vgg':
    model = models.vgg19 (foruddannet = sandt)
    num_in_features = 25088
    print (model.classifier)
andet:
    print ("Ukendt model, vælg venligst 'densenet' eller 'vgg'")

som giver dig mulighed for hurtigt at oprette en alternativ model.

Derefter kan du begynde at opbygge din klassifikator ved hjælp af de parametre, der fungerer bedst for dig. Jeg gik videre og byggede

til parametre i model.parametre ():
    param.requires_grad = Falsk
def build_classifier (num_in_features, Hidden_layers, num_out_features):
   
    klassificering = nn.Sekvivalent ()
    hvis Hidden_layers == Ingen:
        classifier.add_module ('fc0', nn.Linear (num_in_features, 102))
    andet:
        layer_sizes = zip (skjult_layere [: - 1], skjult_layere [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 (lag_stø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))
        
    returklassifikator

som giver mulighed for en nem måde at ændre antallet af skjulte lag, som jeg bruger, samt hurtigt justere frafaldet. Du kan beslutte at tilføje yderligere ReLU- og dropout-lag for at finjustere din model.

Arbejd derefter med at træne dine klassificeringsparametre. Jeg besluttede at sikre mig, at jeg kun træner klassificeringsparametrene her, mens jeg havde frosset funktionsparametre. Du kan blive så kreativ, som du vil med din optimizer, kriterium og planlægning. Kriteriet er metoden, der bruges til at evaluere modeltilpasningen, optimeringsprogrammet er den optimeringsmetode, der bruges til at opdatere vægtene, og planlæggeren tilvejebringer forskellige metoder til justering af indlæringshastighed og trinstørrelse, der bruges under optimering.

Prøv så mange muligheder og kombinationer som du kan for at se, hvad der giver dig det bedste resultat. Du kan se al den officielle dokumentation her. Jeg anbefaler at tage et kig på det og tage dine egne beslutninger om, hvad du vil bruge. Du har ikke bogstaveligt talt et uendeligt antal muligheder her, men det føles helt sikkert som det, når du først begynder at lege!

Hidden_layers = Ingen
klassifikator = build_classifier (num_in_features, Hidden_layers, 102)
print (klassifikator)
# Træn kun klassificeringsparametrene, funktionsparametre er frosne
hvis modelnavn == 'densenet':
    model.classifier = klassifikator
    kriterium = nn.CrossEntropyLoss ()
    optimizer = optim.Adadelta (model.parameters ())
    sched = optim.lr_scheduler.StepLR (optimizer, step_size = 4)
elif model_name == 'vgg':
    model.classifier = klassifikator
    kriterium = nn.NLLTab ()
    optimizer = optim.Adam (model.classifier.parameters (), lr = 0,0001)
    sched = lr_scheduler.StepLR (optimizer, trinstørrelse = 4, gamma = 0,1)
andet:
    passere

Nu er det tid til at træne din model.

# Tilpasset fra https://pytorch.org/tutorials/beginner/transfer_learning_tutorial.html
def train_model (model, kriterium, optimizer, sched, num_epochs = 5):
    siden = time.time ()
best_model_wts = copy.deepcopy (model.state_dict ())
    best_acc = 0,0
for epoke inden for rækkevidde (num_epochs):
        print ('Epok {} / {}'. format (epok + 1, num_epochs))
        print ('-' * 10)
# Hver epoke har en trænings- og valideringsfase
        for fase i ['tog', 'gyldig']:
            hvis fase == 'tog':
                model.train () # Sæt model til træningstilstand
            andet:
                model.eval () # Indstil model til evalueringstilstand
running_loss = 0,0
            running_corrects = 0
# Iterate over data.
            til input, etiketter i dataloadere [fase]:
                input = input.to (enhed)
                labels = labels.to (enhed)
# Nul parametergradienterne
                optimizer.zero_grad ()
# Frem
                # spor historie, hvis kun i tog
                med fakkel.set_grad_enabled (fase == 'tog'):
                    output = model (input)
                    _, preds = fakkel.max (udgange, 1)
                    tab = kriterium (output, labels)
# Bagud + optimer kun, hvis du er i træningsfase
                    hvis fase == 'tog':
                        # Sched.step ()
                        loss.backward ()
                        
                        optimizer.step ()
# Statistikker
                running_loss + = loss.item () * input.size (0)
                running_corrects + = torch.sum (preds == labels.data)
epoch_loss = running_loss / datasæt_størrelser [fase]
            epoch_acc = running_corrects.double () / datasæt_størrelser [fase]
print ('{} Tab: {: .4f} Acc: {: .4f}'. -format (
                fase, epoch_loss, epoch_acc))
# Dybkopi modellen
            hvis fase == 'gyldig' og epoch_acc> best_acc:
                best_acc = epoch_acc
                best_model_wts = copy.deepcopy (model.state_dict ())
Print()
time_elapsed = time.time () - siden
    print ('Træning gennemført i {: .0f} m {: .0f} s'.format (
        time_elapsed // 60, time_elapsed% 60))
    print ('Best val Acc: {: 4f}'. format (best_acc))
# Indlæs bedste modelvægte
    model.load_state_dict (best_model_wts)
    
    retur model
epoker = 30
model.to (enhed)
model = train_model (model, kriterium, optimizer, sched, epoker)

Jeg ville være i stand til let at overvåge mine epoker og også holde styr på den tid, der var gået, da min model kørte. Koden ovenfor inkluderer begge dele, og resultaterne er ret gode! Du kan se, at modellen hurtigt lærer, og nøjagtigheden af ​​valideringssætet nåede hurtigt over 95% efter epoke 7!

Epok 1/30
----------
tog Tap: 2.4793 Acc: 0.4791
gyldigt tab: 0,9688 Acc: 0,8191

Epok 2/30
----------
tog Tap: 0.8288 Acc: 0.8378
gyldigt tab: 0,4714 Acc: 0,9010

Epok 3/30
----------
tog Tab: 0.5191 Acc: 0.8890
gyldigt tab: 0.3197 Acc: 0,9181

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

Epok 5/30
----------
tog Tab: 0.3401 Acc: 0.9214
gyldigt tab: 0.2486 Acc: 0.9401

Epok 6/30
----------
tog Tab: 0,3111 Acc: 0,9303
gyldigt tab: 0.2153 Acc: 0,9487

Epok 7/30
----------
tog Tap: 0.2987 Acc: 0.9298
gyldigt tab: 0.1969 Acc: 0.9584
...
Træning gennemført i 67m 43s
Bedste val Acc: 0,973105

Du kan se, at det tog lidt over en time at køre denne kode på Google Colab med GPU.

Nu er det tid til evaluering

model.eval ()
nøjagtighed = 0
for input, etiketter i dataloaders ['gyldig']:
    input, labels = input.to (enhed), labels.to (enhed)
    output = model (input)
    
    # Klasse med den højeste sandsynlighed er vores forudsagte klasse
    ligestilling = (labels.data == outputs.max (1) [1])
# Nøjagtighed = antal korrekte forudsigelser divideret med alle forudsigelser
    nøjagtighed + = ligestilling.type_as (lommelygte.FloatTensor ()). middel ()
    
print ("Testnøjagtighed: {: .3f}". format (nøjagtighed / len (dataloaders ['valid'])))
Testnøjagtighed: 0,973

Det er vigtigt at gemme dit kontrolpunkt

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

Du behøver ikke at gemme alle parametrene, men jeg inkluderer dem her som et eksempel. Dette checkpoint gemmer specifikt modellen med en foruddannet densenet161-arkitektur, men hvis du vil gemme dit checkpoint med to-valgmuligheden, kan du absolut gøre det. Juster blot inputstørrelsen og modellen.

Nu kan du indlæse dit kontrolpunkt. Hvis du sender dit projekt ind i Udacity-arbejdsområdet, kan tingene blive lidt vanskelige. Her er noget hjælp til fejlfinding af din kontrolpunktbelastning.

Du kan kontrollere dine nøgler ved at køre

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

Læg derefter og genopbyg din model!

def load_checkpoint (filepath):
    checkpoint = fakkel.load (filepath)
    model = checkpoint ['model']
    model.classifier = checkpoint ['classifier']
    model.load_state_dict (kontrolpunkt [ 'state_dict'])
    model.class_to_idx = checkpoint ['class_to_idx']
    optimizer = checkpoint ['optimizer']
    epoker = checkpoint ['epochs']
    
    til parametre i model.parametre ():
        param.requires_grad = Falsk
        
    return model, checkpoint ['class_to_idx']
model, class_to_idx = load_checkpoint ('checkpoint.pth')

Vil du fortsætte? Det er en god ide at lave noget forarbejdning af billeder og konklusion til klassificering. Gå videre og definér din billedsti og åbn et billede:

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

Behandl dit billede og kig på et behandlet billede:

def process_image (billede):
    '' 'Vægt, afgrøde og normaliserer et PIL-billede til en PyTorch-model,
        returnerer et Numpy array
    '''
    # Behandle et PIL-billede til brug i en PyTorch-model
    # tensor.numpy (). transponere (1, 2, 0)
    preprocess = transforms.Compose ([
        transforms.Resize (256),
        transforms.CenterCrop (224),
        transforms.ToTensor (),
        transformer.Normaliser (middelværdi = [0.485, 0.456, 0.406],
                             std = [0.229, 0.224, 0.225])
    ])
    image = forarbejdning (billede)
    returbillede
def imshow (billede, aks = Ingen, titel = Ingen):
    "" "Imshow for Tensor." ""
    hvis øksen er Ingen:
        fig, ax = plt.subplots ()
    
    # PyTorch-tensorer antager, at farvekanalen er den første dimension
    # men matplotlib antager, at den er den tredje dimension
    image = image.numpy (). transponere ((1, 2, 0))
    
    # Fortryd forarbejdning
    middel = n.array ([0,485, 0,456, 0,406])
    std = n.array ([0.229, 0.224, 0.225])
    image = std * image + middel
    
    # Billedet skal klippes mellem 0 og 1, eller det ser ud som støj, når det vises
    image = np.clip (billede, 0, 1)
    
    ax.imshow (billede)
    
    returøks
med Image.open ('flower_data / valid / 102 / image_08006.jpg') som billede:
    plt.imshow (billede)
model.class_to_idx = image_datasets ['train']. class_to_idx

Opret en funktion til forudsigelse:

def predict2 (image_path, model, topk = 5):
    '' 'Forudsige klassen (eller klasser) af et billede ved hjælp af en trænet dyb læringsmodel.
    '''
    
    # Implementér koden for at forudsige klassen fra en billedfil
    img = Image.open (image_path)
    img = process_image (img)
    
    # Konverter 2D-billede til 1D-vektor
    img = np.expand_dims (img, 0)
    
    
    img = fakkel. fra_nummer (img)
    
    model.eval ()
    input = Variabel (img) .to (enhed)
    logits = model.forward (input)
    
    ps = F.softmax (logits, dim = 1)
    topk = ps.cpu (). topk (topk)
    
    retur (e.data.numpy (). klemme (). tolist () for e i topk)

Når billederne er i det rigtige format, kan du skrive en funktion til at forudsige med din model. En almindelig praksis er at forudsige de øverste 5 eller deromkring (normalt kaldet top-KK) mest sandsynlige klasser. Du ønsker at beregne klassesandsynligheder og derefter finde KK's største værdier.

For at få de største KK-værdier i en tensor skal du bruge k.topk (). Denne metode returnerer både de højeste k-sandsynligheder og indekserne for disse sandsynligheder svarende til klasserne. Du skal konvertere fra disse indekser til de faktiske klassetiketter ved hjælp af class_to_idx, som du føjede til modellen eller fra den billedmappe, du brugte til at indlæse dataene. Sørg for at invertere ordbogen, så du også får en kortlægning fra indeks til klasse.

Denne metode skal tage en sti til et billede og et modelcheckpoint og derefter returnere sandsynligheder og klasser.

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

Jeg var temmelig tilfreds med, hvordan min model optrådte!

[0.9999195337295532, 1.4087702766119037e-05, 1.3897360986447893e-05, 1.1400215043977369e-05, 6.098791800468462e-06]
[12, 86, 7, 88, 40]
['peruviansk lilje', 'ørkenrose', 'konge-protea', 'magnolia', 'sværdlilje']

Grundlæggende er det næsten 100% sandsynligt, at det billede, jeg specificerede, er en peruansk lilje. Vil du kigge efter? Prøv at bruge matplotlib til at kortlægge sandsynlighederne for de fem bedste klasser i en søjlediagram sammen med input-billedet:

def view_classify (img_path, prob, klasser, kortlægning):
    '' 'Funktion til visning af et billede og det er forudsagte klasser.
    '''
    image = Image.open (img_path)
fig. (ax1, ax2) = plt.subplots (fig. størrelse = (6,10), ncols = 1, nrows = 2)
    flower_name = mapping [img_path.split ('/') [- 2]]
    ax1.set_title (flower_name)
    ax1.imshow (billede)
    ax1.axis ( 'off')
    
    y_pos = np.arange (len (prob))
    ax2.barh (y_pos, prob, align = 'center')
    ax2.set_yticks (y_pos)
    ax2.set_yticklabels (flower_names)
    ax2.invert_yaxis () # etiketter læses fra top til bund
    ax2.set_title ('Klassesandsynlighed')
view_classify (img_path, prober, klasser, cat_to_name)

Du skulle se noget lignende:

Jeg må sige, det er jeg ret tilfreds med! Jeg anbefaler at teste et par andre billeder for at se, hvor tæt dine forudsigelser er på en række billeder.

Nu er det tid til at lave en model til din egen og fortælle mig, hvordan det går i svarene nedenfor!

Foto af Pez González på Unsplash

Er du færdig med din dyb læring eller maskinlæringsmodel, men ved du ikke, hvad du skal gøre med det næste? Hvorfor ikke implementere det på internettet?

Få din model derude, så alle kan se den!

Tjek denne artikel for at lære, hvordan du implementerer din maskinlæringsmodel med Flask!