Πώς να δημιουργήσετε έναν ταξινομητή εικόνας με ακρίβεια μεγαλύτερη από 97%

Ένα σαφές και ολοκληρωμένο σχέδιο επιτυχίας

Πώς διδάσκετε έναν υπολογιστή να εξετάζει μια εικόνα και να το αναγνωρίζει σωστά ως λουλούδι; Πώς διδάσκετε έναν υπολογιστή για να δείτε μια εικόνα ενός λουλουδιού και στη συνέχεια να σας πω ακριβώς ποιο είδος λουλουδιού είναι όταν ακόμα δεν ξέρετε ποιο είδος είναι;

Επιτρέψτε μου να σας δείξω!

Αυτό το άρθρο θα σας καθοδηγήσει στα βασικά στοιχεία της δημιουργίας ενός ταξινομητή εικόνας με το PyTorch. Μπορείτε να φανταστείτε χρησιμοποιώντας κάτι τέτοιο σε μια εφαρμογή τηλεφώνου που σας λέει το όνομα του λουλουδιού που κοιτάζει η μηχανή σας. Θα μπορούσατε, αν θέλετε, να εκπαιδεύσετε αυτόν τον ταξινομητή και στη συνέχεια να το εξάγετε για χρήση σε μια δική σας εφαρμογή.

Αυτό που κάνετε από εδώ εξαρτάται αποκλειστικά από εσάς και τη φαντασία σας.

Έβαλα αυτό το άρθρο μαζί για οποιονδήποτε εκεί έξω που είναι ολοκαίνουργιος σε όλα αυτά και ψάχνει για ένα μέρος για να ξεκινήσει. Εναπόκειται σε εσάς να λάβετε αυτές τις πληροφορίες, να τις βελτιώσετε και να το καταστήσετε το δικό σας!

Αν θέλετε να δείτε το σημειωματάριο, μπορείτε να το βρείτε εδώ.

Επειδή αυτός ο ταξινομητής εικόνας PyTorch χτίστηκε ως τελικό έργο για ένα πρόγραμμα Udacity, ο κώδικας βασίζεται σε κώδικα από την Udacity, ο οποίος με τη σειρά του βασίζεται στην επίσημη τεκμηρίωση του PyTorch. Η Udacity παρείχε επίσης ένα αρχείο JSON για χαρτογράφηση ετικετών. Αυτό το αρχείο μπορεί να βρεθεί σε αυτό το repo GitHub.

Μπορείτε να βρείτε πληροφορίες σχετικά με το σύνολο δεδομένων λουλουδιών εδώ. Το σύνολο δεδομένων περιλαμβάνει ένα ξεχωριστό φάκελο για κάθε μία από τις 102 κατηγορίες λουλουδιών. Κάθε λουλούδι χαρακτηρίζεται ως αριθμός και καθένας από τους αριθμημένους καταλόγους διαθέτει ένα αριθμό αρχείων .jpg.

Ας αρχίσουμε!

Φωτογραφία από την Annie Spratt στο Unsplash

Επειδή πρόκειται για ένα νευρωνικό δίκτυο που χρησιμοποιεί ένα μεγαλύτερο σύνολο δεδομένων από ό, τι η CPU μου μπορούσε να χειριστεί σε εύλογο χρονικό διάστημα, πήγα μπροστά και δημιούργησα τον ταξινομητή εικόνας μου στο Google Colab. Το Colab είναι πραγματικά εκπληκτικό επειδή παρέχει δωρεάν GPU. (Εάν είστε νέοι στην Colab, δείτε αυτό το άρθρο για να ξεκινήσετε με το Google Colab!)

Επειδή χρησιμοποιούσα την Colab, έπρεπε να ξεκινήσω εισάγοντας το PyTorch. Δεν χρειάζεται να το κάνετε αυτό εάν δεν χρησιμοποιείτε το Colab.

*** UPDATE! (01/29) *** Η Colab υποστηρίζει τώρα το εγγενές PyTorch !!! Δεν χρειάζεται να εκτελέσετε τον παρακάτω κώδικα, αλλά το αφήνω μόνο σε περίπτωση που κάποιος έχει προβλήματα!

# Εισαγάγετε PyTorch εάν χρησιμοποιείτε το Google Colab
# http://pytorch.org/
από την εισαγωγή os.path υπάρχει
από την εισαγωγή wheel.pep425tags get_abbr_impl, get_impl_ver, get_abi_tag
πλατφόρμα = '{} {} - {}'. (get_abbr_impl (), get_impl_ver (), get_abi_tag ())
cuda_output =! ldconfig -p | grep cudart.so | sed -e /.* \. \ ([0-9] * \) \. \ ([0-9] 2 /
accelerator = cuda_output [0] αν υπάρχει ('/ dev / nvidia0') άλλο 'cpu'
! pip install -q http://download.pytorch.org/whl/{accelerator}/torch-0.4.1-{platform}-linux_x86_64.whl torchvision
εισαγωγέας δάδα

Στη συνέχεια, μετά από κάποιο πρόβλημα με Pillow (είναι buggy στο Colab!), Πήγα ακριβώς μπροστά και έτρεξε αυτό:

εισαγωγή PIL
εκτύπωση (PIL.PILLOW_VERSION)

Αν έχετε κάτι κάτω από το 5.3.0, χρησιμοποιήστε το αναπτυσσόμενο μενού κάτω από το "Runtime" στο "Restart runtime" και εκτελέστε ξανά αυτό το κελί. Πρέπει να είσαι καλός για να φύγεις!

Θα θελήσετε να χρησιμοποιείτε GPU για αυτό το έργο, το οποίο είναι απίστευτα απλό στη δημιουργία του Colab. Απλά πηγαίνετε στο αναπτυσσόμενο μενού "Runtime", επιλέξτε "Change runtime type" και, στη συνέχεια, επιλέξτε "GPU" από το αναπτυσσόμενο μενού του επιταχυντή υλικού!

Τότε μου αρέσει να τρέχω

train_on_gpu = torch.cuda.is_available ()
αν όχι train_on_gpu:
    εκτύπωση ('Bummer! Training on CPU ...')
αλλού:
    print ('Είσαι καλό για να προχωρήσεις! Εκπαίδευση στη GPU ...')

απλά για να βεβαιωθείτε ότι λειτουργεί. Στη συνέχεια τρέξτε

συσκευή = torch.device ("cuda: 0" αν το torch.cuda.is_available () άλλο "cpu")

για να ορίσετε τη συσκευή.

Μετά από αυτό, εισαγάγετε τα αρχεία. Υπάρχουν πολλοί τρόποι για να το κάνετε αυτό, συμπεριλαμβανομένης της τοποθέτησης του Google Drive, εάν έχετε αποθηκεύσει το σύνολο δεδομένων σας, πράγμα που είναι πραγματικά απλό. Παρόλο που δεν καταλήξαμε να θεωρώ ότι είναι η πιο χρήσιμη λύση, το συμπεριλαμβάνω και παρακάτω, μόνο και μόνο επειδή είναι τόσο εύκολο και χρήσιμο.

από τη μονάδα εισαγωγής google.colab
drive.mount ('/ content / gdrive')

Στη συνέχεια, θα δείτε έναν σύνδεσμο, κάντε κλικ σε αυτό, θα επιτρέψετε την πρόσβαση, θα αντιγράψετε τον κώδικα που θα εμφανιστεί, θα τον επικολλήσετε στο κουτί, θα πατήσετε το πλήκτρο Enter και θα είναι καλό να πάτε! Εάν δεν βλέπετε τη μονάδα σας στο πλευρικό πλαίσιο αριστερά, απλώς πατήστε "ανανέωση" και θα εμφανιστεί.

(Εκτελέστε το κελί, κάντε κλικ στον σύνδεσμο, αντιγράψτε τον κώδικα στη σελίδα, επικολλήστε το στο κουτί, πατήστε enter και θα το δείτε όταν έχετε τοποθετήσει με επιτυχία τη μονάδα σας):

Είναι πραγματικά πολύ εύκολο!

Ωστόσο, εάν προτιμάτε να κάνετε λήψη ενός συνδέσμου αρχείου κοινόχρηστου αρχείου zip (ο οποίος είναι πιο εύκολος και ταχύτερος για αυτό το έργο), μπορείτε να χρησιμοποιήσετε:

! wget
!ανοίγω φερμουάρ

Για παράδειγμα:

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

Αυτό θα σας δώσει τα στοιχεία λουλουδιών της Udacity σε δευτερόλεπτα!

(Εάν μεταφορτώνετε μικρά αρχεία, μπορείτε απλά να τα ανεβάσετε απευθείας με κάποιο απλό κωδικό. Ωστόσο, αν θέλετε, μπορείτε επίσης να μεταβείτε στην αριστερή πλευρά της οθόνης και να κάνετε κλικ στο "upload files" αν δεν το κάνετε νιώστε σαν να τρέχετε κάποιο απλό κώδικα για να πάρετε ένα τοπικό αρχείο.)

Μετά τη φόρτωση των δεδομένων, εισήγαγα τις βιβλιοθήκες που ήθελα να χρησιμοποιήσω:

% matplotlib inline
% config InlineBackend.figure_format = 'αμφιβληστροειδής'
χρόνο εισαγωγής
εισαγωγή json
αντίγραφο εισαγωγής
εισαγωγή matplotlib.pyplot ως plt
να εισαγάγει το θαλάσσιο σκάφος ως sns
εισαγωγή numpy ως np
εισαγωγή PIL
από την εικόνα εισαγωγής PIL
από εισαγωγές συλλογών με εντολήDict
εισαγωγέας δάδα
από την εισαγωγή του φακού nn, βέλτιστη
από torch.optim import lr_scheduler
από torch.autograd Εισαγωγή Μεταβλητή
εισαγωγή torchvision
από torchvision σύνολα δεδομένων εισαγωγής, μοντέλα, μετατροπές
από το αρχείο torch.utils.data.sampler εισαγωγής SubsetRandomSampler
εισαγάγετε torch.nn ως nn
εισαγάγετε torch.nn.functional ως F

Ακολουθούν οι μετασχηματισμοί δεδομένων! Θέλετε να βεβαιωθείτε ότι χρησιμοποιείτε αρκετούς διαφορετικούς τύπους μετασχηματισμών στο εκπαιδευτικό σας σετ για να βοηθήσετε το πρόγραμμα σας να μάθει όσο το δυνατόν περισσότερο. Μπορείτε να δημιουργήσετε ένα πιο ισχυρό μοντέλο, εκπαιδεύοντάς το σε αναδιπλωμένες, περιστρεφόμενες και περικομμένες εικόνες.

Τα μέσα που παρέχουν τυπικές αποκλίσεις για την ομαλοποίηση των τιμών εικόνας προτού τα περάσουν στο δίκτυό μας, αλλά μπορούν επίσης να βρεθούν εξετάζοντας τις τιμές μέσης και τυπικής απόκλισης των διαφόρων διαστάσεων των τανυστή εικόνων. Η επίσημη τεκμηρίωση είναι εξαιρετικά χρήσιμη εδώ!

Για τον ταξινομητή εικόνας μου, το κράτησα απλά με:

data_transforms = {
    'τρένο': transforms.Compose ([
        μετασχηματισμοίRandomRotation (30),
        μετασχηματίζειRandomResizedCrop (224),
        transforms.RandomHorizontalFlip (),
        transforms.ToTensor (),
        Μετασχηματίζει ([0.485, 0.456, 0.406],
                             [0.229, 0.224, 0.225])
    ]),
    'έγκυρο': transforms.Compose ([
        μετασχηματισμοί.Resize (256),
        transforms.CenterCrop (224),
        transforms.ToTensor (),
        Μετασχηματίζει ([0.485, 0.456, 0.406],
                             [0.229, 0.224, 0.225])
    ])
}}
# Τοποθετήστε τα σύνολα δεδομένων με το ImageFolder
image_datasets = {x: datasets.ImageFolder (os.path.join (data_dir, x),
                                          data_transforms [x])
                  για το x στο ['τρένο', 'έγκυρο']]
# Χρησιμοποιώντας τα σύνολα δεδομένων εικόνων και τις μορφές κίνησης, ορίστε τους dataloaders
batch_size = 64
dataloaders = {x: torch.utils.data.DataLoader (image_datasets [x], batch_size = batch_size,
                                             shuffle = True, num_workers = 4)
              για το x στο ['τρένο', 'έγκυρο']]
class_names = class_names = image_datasets ['τρένο']
dataset_sizes = {x: len (image_datasets [x]) για το x στο ['τρένο', 'έγκυρο']]
class_names = class_names = image_datasets ['τρένο']

Όπως βλέπετε παραπάνω, επίσης ορίζω το μέγεθος παρτίδας, φορτωτές δεδομένων και ονόματα κλάσεων στον παραπάνω κώδικα.

Για να εξετάσω πολύ γρήγορα τα δεδομένα και να ελέγξω τη συσκευή μου, έτρεξα:

εκτύπωση (dataset_sizes)
εκτύπωση (συσκευή)
{'τρένο': 6552, 'έγκυρο': 818}
cuda: 0

Στη συνέχεια, πρέπει να κάνουμε κάποια χαρτογράφηση από τον αριθμό της ετικέτας και το πραγματικό όνομα λουλουδιού. Η Udacity παρείχε ένα αρχείο JSON για να γίνει αυτό το χαρτογράφηση απλά.

με ανοικτό ('cat_to_name.json', 'r') ως f:
    cat_to_name = json.load (f)

Για να ελέγξετε τον φορτωτή δεδομένων, εκτελέστε:

εικόνες, ετικέτες = επόμενο (iter (dataloaders ['τρένο']))
rand_idx = np.random.randint (len (εικόνες))
# Εκτύπωση (rand_idx)
εκτύπωση (ετικέτα: {}, κλάση: {}, όνομα: {} ". (ετικέτες [rand_idx] .item (),
                                               class_names [ετικέτες [rand_idx] .item ()],
                                               cat_to_name [class_names [ετικέτες [rand_idx] .item ()]]))

Τώρα αρχίζει να γίνεται ακόμα πιο συναρπαστικό! Ορισμένα μοντέλα τα τελευταία χρόνια έχουν δημιουργηθεί από ανθρώπους πολύ πιο εξειδικευμένους από τους περισσότερους από εμάς για επαναχρησιμοποίηση σε προβλήματα ηλεκτρονικής όρασης. Το PyTorch διευκολύνει την φόρτωση των προ-εκπαιδευμένων μοντέλων και την κατασκευή τους, κάτι ακριβώς πρόκειται να κάνουμε για αυτό το έργο. Η επιλογή του μοντέλου εξαρτάται αποκλειστικά από εσάς!

Μερικά από τα πιο δημοφιλή προ-εκπαιδευμένα μοντέλα, όπως το ResNet, το AlexNet και το VGG, προέρχονται από την ImageNet Challenge. Αυτά τα προ-εκπαιδευμένα μοντέλα επιτρέπουν σε άλλους να αποκτήσουν γρήγορα αποτελέσματα αιχμής στην όραση του υπολογιστή χωρίς να χρειάζονται τόσο μεγάλες ποσότητες ισχύος, υπομονής και χρόνου υπολογιστή. Είχα πραγματικά καλά αποτελέσματα με το DenseNet και αποφάσισα να χρησιμοποιήσω το DenseNet161, το οποίο μου έδωσε πολύ καλά αποτελέσματα σχετικά γρήγορα.

Μπορείτε γρήγορα να το ρυθμίσετε τρέχοντας

μοντέλο = models.densenet161 (pretrained = True)

αλλά ίσως είναι πιο ενδιαφέρον να δώσετε στον εαυτό σας μια επιλογή μοντέλου, βελτιστοποίησης και χρονοπρογραμματιστή. Για να δημιουργήσετε μια επιλογή στην αρχιτεκτονική, τρέξτε

model_name = 'densenet' #vgg
αν model_name == 'densenet':
    μοντέλο = models.densenet161 (pretrained = True)
    num_in_features = 2208
    εκτύπωση (μοντέλο)
elif model_name == 'vgg':
    μοντέλο = models.vgg19 (pretrained = True)
    num_in_features = 25088
    εκτύπωση (model.classifier)
αλλού:
    print ("Άγνωστο μοντέλο, επιλέξτε" densenet "ή" vgg ")

που σας επιτρέπει να ρυθμίσετε γρήγορα ένα εναλλακτικό μοντέλο.

Μετά από αυτό, μπορείτε να αρχίσετε να δημιουργείτε τον ταξινομητή σας, χρησιμοποιώντας τις παραμέτρους που λειτουργούν καλύτερα για εσάς. Πήγα μπροστά και χτίστηκα

για παράμετρο σε μοντέλο.παράμετρα ():
    param.requires_grad = Λάθος
def build_classifier (num_in_features, hidden_layers, num_out_features):
   
    ταξινομητής = nn.Sequential ()
    αν hidden_layers == Καμία:
        classifier.add_module ('fc0', nn.Linear (num_in_features, 102))
    αλλού:
        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))
        για το i, (h1, h2) στο enumerate (layer_sizes):
            classifier.add_module ('fc' + str (i + 1), nn.Linear (h1, h2))
            classifier.add_module ('relu' + str (i + 1), nn.ReLU ())
            classifier.add_module ('πτώση' + str (i + 1), nn.Dropout (.5))
        classifier.add_module ('έξοδος', nn.Linear (hidden_layers [-1], num_out_features))
        
    ταξινομητής επιστροφής

η οποία επιτρέπει έναν εύκολο τρόπο να αλλάξετε τον αριθμό των κρυφών επιπέδων που χρησιμοποιώ, καθώς και να ρυθμίσω γρήγορα την ταχύτητα εγκατάλειψης. Μπορεί να αποφασίσετε να προσθέσετε επιπλέον επίπεδα ReLU και dropouts για να βελτιώσετε με ακρίβεια το μοντέλο σας.

Στη συνέχεια, εργάζονται για την εκπαίδευση των παραμέτρων ταξινομητή. Αποφάσισα να βεβαιωθώ ότι έχω εκπαιδεύσει μόνο τις παραμέτρους ταξινομητή εδώ, ενώ οι παράμετροι χαρακτηριστικών έχουν παγώσει. Μπορείτε να έχετε όσο πιο δημιουργικό θέλετε με το βελτιστοποιητή, το κριτήριο και το χρονοδιάγραμμα. Το κριτήριο είναι η μέθοδος που χρησιμοποιείται για την αξιολόγηση της προσαρμογής του μοντέλου, ο βελτιστοποιητής είναι η μέθοδος βελτιστοποίησης που χρησιμοποιείται για την ενημέρωση των βαρών και ο χρονοπρογραμματιστής παρέχει διαφορετικές μεθόδους για τη ρύθμιση του ρυθμού εκμάθησης και του μεγέθους βήματος που χρησιμοποιήθηκε κατά τη διάρκεια της βελτιστοποίησης.

Δοκιμάστε όσο το δυνατόν περισσότερες επιλογές και συνδυασμούς για να δείτε τι σας δίνει το καλύτερο αποτέλεσμα. Μπορείτε να δείτε όλα τα επίσημα έγγραφα εδώ. Σας συνιστώ να ρίξετε μια ματιά σε αυτό και να κάνετε τις δικές σας αποφάσεις για το τι θέλετε να χρησιμοποιήσετε. Δεν έχετε κυριολεκτικά έναν άπειρο αριθμό επιλογών εδώ, αλλά σίγουρα αισθάνεται σαν να το ξεκινήσετε να παίζετε!

hidden_layers = Καμία
ταξινομητής = build_classifier (num_in_features, hidden_layers, 102)
εκτύπωση (ταξινομητής)
# Εκπαιδεύστε μόνο τις παραμέτρους ταξινομητή, οι παράμετροι χαρακτηριστικών παγώνουν
αν model_name == 'densenet':
    model.classifier = ταξινομητής
    κριτήριο = nn.CrossEntropyLoss ()
    optimizer = optim.Adadelta (μοντέλο.παράμετρα ())
    sched = optim.lr_scheduler.StepLR (optimizer, step_size = 4)
elif model_name == 'vgg':
    model.classifier = ταξινομητής
    κριτήριο = nn.NLLLoss ()
    βελτιστοποιητής = optim.Adam (μοντέλο.κλασσικός.παράμετρος (), lr = 0,0001)
    sched = lr_scheduler.StepLR (βελτιστοποιητής, step_size = 4, γάμμα = 0,1)
αλλού:
    πέρασμα

Τώρα ήρθε η ώρα να εκπαιδεύσετε το μοντέλο σας.

# Προσαρμοσμένη από τη διεύθυνση https://pytorch.org/tutorials/beginner/transfer_learning_tutorial.html
def model_model (μοντέλο, κριτήριο, βελτιστοποιητής, γραφή, num_epochs = 5):
    δεδομένου ότι = time.time ()
best_model_wts = copy.deepcopy (model.state_dict ())
    best_acc = 0,0
για την εποχή στην περιοχή (num_epochs):
        εκτύπωση (μορφή 'Epoch {} / {}'. (εποχή + 1, num_epochs))
        εκτύπωση ('-' * 10)
# Κάθε εποχή έχει μια φάση εκπαίδευσης και επικύρωσης
        για φάση σε ['τρένο', 'έγκυρο']:
            εάν φάση == 'τρένο':
                model.train () # Ορισμός μοντέλου σε λειτουργία εκπαίδευσης
            αλλού:
                model.eval () # Ορίστε μοντέλο για να αξιολογήσετε τη λειτουργία
running_loss = 0.0
            running_corrects = 0
# Επαναλάβετε τα δεδομένα.
            για εισόδους, ετικέτες σε dataloaders [φάση]:
                είσοδοι = inputs.to (συσκευή)
                ετικέτες = labels.to (συσκευή)
# Καταργήστε την κλίση των παραμέτρων
                optimizer.zero_grad ()
# Προώθηση
                # ιστορία ίχνους, αν είναι μόνο σε τρένο
                με torch.set_grad_enabled (φάση == 'τρένο'):
                    έξοδοι = μοντέλο (είσοδοι)
                    _, preds = torch.max (εξόδους, 1)
                    απώλεια = κριτήριο (αποτελέσματα, ετικέτες)
# Επιστροφή + βελτιστοποίηση μόνο αν βρίσκεται σε φάση κατάρτισης
                    εάν φάση == 'τρένο':
                        # sched.step ()
                        loss.backward ()
                        
                        optimizer.step ()
# Στατιστικά στοιχεία
                run_loss + = loss.item () * inputs.size (0)
                running_corrects + = torch.sum (πριν == ετικέτες.data)
epoch_loss = running_loss / dataset_sizes [στάδιο]
            epoch_acc = running_corrects.double () / dataset_sizes [στάδιο]
print ('{} Απώλεια: {: .4f} Acc: {: .4f}'.
                φάση, epoch_loss, epoch_acc))
# Αντιγράψτε βαθιά το μοντέλο
            εάν φάση == 'έγκυρη' και epoch_acc> best_acc:
                best_acc = epoch_acc
                best_model_wts = copy.deepcopy (model.state_dict ())
Τυπώνω()
time_elapsed = time.time () - από τότε
    print ('Εκπαίδευση ολοκληρώθηκε στο {: .0f} m {: .0f} s'.format (
        time_elapsed // 60, time_elapsed% 60))
    εκτύπωση ('Καλύτερη φόρμα Acc: {4f}' (best_acc))
# Τοποθετήστε τα καλύτερα βάρη μοντέλων
    model.load_state_dict (best_model_wts)
    
    μοντέλο επιστροφής
εποχές = 30
model.το (συσκευή)
μοντέλο = μοντέλο τρένου (μοντέλο, κριτήριο, βελτιστοποιητής, προγραμματισμός, εποχές)

Ήθελα να είμαι σε θέση να παρακολουθώ τις εποχές μου εύκολα και επίσης να παρακολουθώ τον χρόνο που πέρασε καθώς το μοντέλο μου έτρεχε. Ο παραπάνω κώδικας περιλαμβάνει και τα δύο, και τα αποτελέσματα είναι αρκετά καλά! Μπορείτε να δείτε ότι το μοντέλο μαθαίνει γρήγορα και ότι η ακρίβεια στην επικύρωση που τέθηκε γρήγορα έφτασε πάνω από 95% από την εποχή 7!

Εποχή 1/30
----------
απώλεια αμαξοστοιχίας: 2.4793 Acc: 0.4791
έγκυρη απώλεια: 0,9688 Acc: 0,8191

Εποχή 2/30
----------
απώλεια αμαξοστοιχίας: 0,8288 Acc: 0,8378
έγκυρη απώλεια: 0.4714 Acc: 0.9010

Εποχή 3/30
----------
απώλεια αμαξοστοιχίας: 0.5191 Acc: 0.8890
έγκυρη απώλεια: 0,3197 Acc: 0,9181

Εποχή 4/30
----------
απώλεια αμαξοστοιχίας: 0.4064 Acc: 0.9095
έγκυρη απώλεια: 0,2975 Acc: 0,9169

Εποχή 5/30
----------
απώλεια αμαξοστοιχίας: 0.3401 Acc: 0.9214
ισχύουσα απώλεια: 0.2486 Acc: 0.9401

Εποχή 6/30
----------
απώλεια αμαξοστοιχίας: 0,3111 Acc: 0,9303
έγκυρη απώλεια: 0.2153 Acc: 0.9487

Εποχή 7/30
----------
απώλεια αμαξοστοιχίας: 0.2987 Acc: 0.9298
ισχύουσα απώλεια: 0.1969 Acc: 0.9584
...
Η εκπαίδευση ολοκληρώθηκε σε 67m 43s
Καλύτερη τιμή Acc: 0,973105

Μπορείτε να δείτε ότι η εκτέλεση αυτού του κώδικα στο Google Colab με GPU πήρε λίγο περισσότερο από μία ώρα.

Τώρα είναι ώρα για αξιολόγηση

model.eval ()
ακρίβεια = 0
για εισόδους, ετικέτες σε dataloaders ['έγκυρο']:
    εισόδους, ετικέτες = inputs.to (συσκευή), labels.to (συσκευή)
    έξοδοι = μοντέλο (είσοδοι)
    
    # Κλάση με την υψηλότερη πιθανότητα είναι η προβλεπόμενη τάξη μας
    ισότητα = (ετικέτες.data == outputs.max (1) [1])
# Ακρίβεια = αριθμός σωστών προβλέψεων διαιρούμενο με όλες τις προβλέψεις
    ακρίβεια + = equality.type_as (torch.FloatTensor ()) mean ()
    
εκτύπωση ("Ακρίβεια δοκιμής: {: .3f}". (ακρίβεια /
Ακρίβεια δοκιμής: 0,973

Είναι σημαντικό να αποθηκεύσετε το σημείο ελέγχου

"." "" "" "" "" ""
σημείο ελέγχου = {'input_size': 2208,
              'output_size': 102,
              «εποχές»: εποχές,
              'batch_size': 64,
              'μοντέλο': models.densenet161 (pretrained = true),
              "ταξινομητής": ταξινομητής,
              'scheduler': προγραμματισμένο,
              'optimizer': optimizer.state_dict (),
              'state_dict': model.state_dict (),
              'class_to_idx': model.class_to_idx
             }}
   
torch.save (σημείο ελέγχου, 'checkpoint.pth')

Δεν χρειάζεται να αποθηκεύσετε όλες τις παραμέτρους, αλλά τις συμπεριλαμβάνω εδώ ως παράδειγμα. Αυτό το σημείο ελέγχου αποθηκεύει ειδικά το μοντέλο με μια προ-εκπαιδευμένη αρχιτεκτονική densenet161, αλλά αν θέλετε να αποθηκεύσετε το σημείο ελέγχου σας με την επιλογή δύο επιλογών, μπορείτε να το κάνετε απολύτως. Απλά ρυθμίστε το μέγεθος και το μοντέλο εισόδου.

Τώρα μπορείτε να φορτώσετε το σημείο ελέγχου. Εάν υποβάλετε το έργο σας στον εργασιακό χώρο Udacity, τα πράγματα μπορούν να πάρουν λίγο δύσκολο. Ακολουθεί κάποια βοήθεια για την αντιμετώπιση προβλημάτων στο φορτίο ελέγχου.

Μπορείτε να ελέγξετε τα κλειδιά σας εκτελώντας

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

Στη συνέχεια, φορτώστε και ανοικοδομήστε το μοντέλο σας!

def load_checkpoint (filepath):
    checkpoint = torch.load (filepath)
    μοντέλο = σημείο ελέγχου ['μοντέλο']
    model.classifier = σημείο ελέγχου ['ταξινομητής']
    model.load_state_dict (σημείο ελέγχου ['state_dict'])
    model.class_to_idx = σημείο ελέγχου ['class_to_idx']
    optimizer = σημείο ελέγχου ['optimizer']
    εποχές = σημείο ελέγχου ['εποχές']
    
    για παράμετρο σε μοντέλο.παράμετρα ():
        param.requires_grad = Λάθος
        
    μοντέλο επιστροφής, σημείο ελέγχου ['class_to_idx']
μοντέλο, class_to_idx = load_checkpoint ('checkpoint.pth')

Θέλετε να συνεχίσετε; Είναι καλή ιδέα να κάνετε κάποια προεπεξεργασία εικόνων και συμπεράσματα για ταξινόμηση. Προχωρήστε και ορίστε τη διαδρομή της εικόνας σας και ανοίξτε μια εικόνα:

image_path = 'λουλούδια_δικές / έγκυρες / 102 / image_08006.jpg'
img = Image.open (εικόνα_διαδρομή)

Επεξεργαστείτε την εικόνα σας και ρίξτε μια ματιά σε μια επεξεργασμένη εικόνα:

def process_image (εικόνα):
    '' 'Κλίμακα, καλλιέργειες, και ομαλοποιεί μια εικόνα PIL για ένα μοντέλο PyTorch,
        επιστρέφει έναν πίνακα Numpy
    '' '
    # Επεξεργαστείτε μια εικόνα PIL για χρήση σε ένα μοντέλο PyTorch
    # tensor.numpy () μεταφέρετε (1, 2, 0)
    preprocess = transforms.Compose ([
        μετασχηματισμοί.Resize (256),
        transforms.CenterCrop (224),
        transforms.ToTensor (),
        Μετασχηματίζει (μέσος όρος = [0.485, 0.456, 0.406],
                             std = [0.229, 0.224, 0.225])
    ])
    image = προεπεξεργασία (εικόνα)
    επιστροφή εικόνας
def imshow (εικόνα, άξονας = Καμία, τίτλος = Κανένας):
    "" "Imshow για Tensor." "" "
    αν το τσεκούρι είναι Καμία:
        σύμβολο, ax = plt.subplots ()
    
    Οι # tensors PyTorch υποθέτουν ότι το κανάλι χρώματος είναι η πρώτη διάσταση
    # αλλά το matplotlib υποθέτει ότι είναι η τρίτη διάσταση
    image = image.numpy () μεταφέρετε ((1, 2, 0))
    
    # Αναίρεση προεπεξεργασίας
    μέσος όρος = np.array ([0.485, 0.456, 0.406])
    std = np.array ([0.229, 0.224, 0.225])
    image = std * εικόνα + μέση τιμή
    
    # Η εικόνα πρέπει να αποκοπεί μεταξύ 0 και 1 ή μοιάζει με θόρυβο όταν εμφανίζεται
    image = np.clip (εικόνα, 0, 1)
    
    ax.imshow (εικόνα)
    
    επιστροφή τσεκούρι
με το Image.open ('flower_data / valid / 102 / image_08006.jpg') ως εικόνα:
    plt.imshow (εικόνα)
"." "" "" "" "" ""

Δημιουργία συνάρτησης για πρόβλεψη:

πρόβλεψη def 2 (path_path, μοντέλο, topk = 5):
    '' 'Προβλέψτε την τάξη (ή τάξεις) μιας εικόνας χρησιμοποιώντας ένα εκπαιδευμένο μοντέλο βαθιάς μάθησης.
    '' '
    
    # Εφαρμογή του κώδικα για την πρόβλεψη της κλάσης από ένα αρχείο εικόνας
    img = Image.open (εικόνα_διαδρομή)
    img = process_image (img)
    
    # Μετατροπή 2D εικόνας σε 1D φορέα
    img = np.expand_dims (img, 0)
    
    
    img = torch.from_numpy (img)
    
    model.eval ()
    είσοδοι = μεταβλητή (img) .to (συσκευή)
    logits = model.forward (είσοδοι)
    
    ps = F.softmax (logits, dim = 1)
    topk = ps.cpu () κορυφή (topk)
    
    επιστροφή (e.data.numpy (), συμπίεση (), tolist () για e στην κορυφή)

Μόλις οι εικόνες είναι στη σωστή μορφή, μπορείτε να γράψετε μια λειτουργία για να κάνετε προβλέψεις με το μοντέλο σας. Μια κοινή πρακτική είναι να προβλέψουμε τα κορυφαία 5 περίπου (συνήθως αποκαλούμενα top-KK) πιο πιθανά μαθήματα. Θα θελήσετε να υπολογίσετε τις πιθανότητες κλάσης και στη συνέχεια να βρείτε τις μεγαλύτερες τιμές KK.

Για να πάρετε τις κορυφαίες τιμές KK μεγαλύτερες σε tensor χρησιμοποιήστε k.topk (). Αυτή η μέθοδος επιστρέφει τόσο τις υψηλότερες πιθανότητες k όσο και τους δείκτες αυτών των πιθανοτήτων που αντιστοιχούν στις τάξεις. Πρέπει να μετατρέψετε από αυτούς τους δείκτες στις πραγματικές ετικέτες κλάσης χρησιμοποιώντας class_to_idx, τις οποίες προσθέσατε στο μοντέλο ή από το φάκελο εικόνων που χρησιμοποιήσατε για τη φόρτωση των δεδομένων. Βεβαιωθείτε ότι έχετε αντιστρέψει το λεξικό, ώστε να πάρετε μια χαρτογράφηση από το ευρετήριο στην κλάση επίσης.

Αυτή η μέθοδος θα πρέπει να ακολουθήσει μια πορεία προς μια εικόνα και ένα σημείο ελέγχου μοντέλου και στη συνέχεια να επιστρέψει τις πιθανότητες και τις κλάσεις.

img_path = 'λουλούδι_δώρα / έγκυρο / 18 / image_04252.jpg'
πρότυπα, τάξεις = πρόβλεψη2 (img_path, model.to (συσκευή))
εκτύπωση (probs)
εκτύπωση (τάξεις)
flower_names = [cat_to_name [class_names [e]] για e σε κατηγορίες]
εκτύπωση (όνομα_χθύου)

Ήμουν πολύ ευχαριστημένος με το πώς έκανε το μοντέλο μου!

[0.9999195337295532, 1.4087702766119037ε-05, 1.3897360986447893ε-05, 1.1400215043977369e-05, 6.098791800468462e-06]
[12, 86, 7, 88, 40]
[«περουβιανός κρίνος», «έρημος-τριαντάφυλλο», «βασιλιάς πρωτεΐνη», «μανόλιας», «κρίνος σπαθί»]

Βασικά, είναι σχεδόν 100% πιθανό ότι η εικόνα που είχα δηλώσει είναι ένα περουβιανό κρίνο. Θέλετε να ρίξετε μια ματιά; Δοκιμάστε να χρησιμοποιήσετε το matplotlib για να σχεδιάσετε τις πιθανότητες για τις πέντε κορυφαίες κατηγορίες σε γραμμική γραφική παράσταση μαζί με την εικόνα εισόδου:

def view_classify (img_path, prob, κλάσεις, χαρτογράφηση):
    '' 'Λειτουργία για την προβολή μιας εικόνας και των προβλεπόμενων τάξεων.
    '' '
    image = Image.open (img_path)
εικ., (ax1, ax2) = plt.subplots (figsize = (6,10), ncols = 1, nrows = 2)
    flower_name = χαρτογράφηση [img_path.split ('/') [- 2]]
    ax1.set_title (flower_name)
    ax1.imshow (εικόνα)
    ax1.axis ('εκτός')
    
    y_pos = np.arange (len (prob))
    ax2.barh (y_pos, prob, ευθυγράμμιση = 'κέντρο')
    ax2.set_yticks (y_pos)
    ax2.set_yticklabels (flower_names)
    ax2.invert_yaxis () # ετικέτες που διαβάζονται από πάνω προς κάτω
    ax2.set_title ('Πιθανότητα κλάσης')
view_classify (img_path, probs, κλάσεις, cat_to_name)

Θα πρέπει να δείτε κάτι τέτοιο:

Έχω να πω, είμαι πολύ χαρούμενος με αυτό! Σας συνιστούμε να δοκιμάσετε μερικές άλλες εικόνες για να δείτε πόσο κοντά οι προβλέψεις σας είναι σε μια ποικιλία εικόνων.

Τώρα ήρθε η ώρα να κάνετε ένα δικό σας μοντέλο και επιτρέψτε μου να ξέρω πώς πηγαίνει στις παρακάτω απαντήσεις!

Φωτογραφία από τον Pez González στο Unsplash

Έχετε τελειώσει το μοντέλο σας για μαθήματα εκμάθησης ή μηχανικής μάθησης, αλλά δεν ξέρετε τι να κάνετε στη συνέχεια; Γιατί να μην το αναπτύξετε στο Διαδίκτυο;

Πάρτε το μοντέλο σας εκεί έξω ώστε ο καθένας να το δει!

Ελέγξτε αυτό το άρθρο για να μάθετε πώς να αναπτύξετε το μοντέλο εκμάθησης μηχανών σας με το Flask!