Як побудувати класифікатор зображення з точністю понад 97%

Ясний і повний план успіху

Як ви вчите комп’ютер дивитись на зображення і правильно ідентифікувати його як квітку? Як ви навчите комп’ютер бачити зображення квітки, а потім точно розповісти, який це вид квітки, коли навіть ви не знаєте, який це вид?

Дозвольте мені показати вам!

Ця стаття ознайомить вас з основами створення класифікатора зображень за допомогою PyTorch. Ви можете уявити собі щось подібне в телефонному додатку, який повідомляє назву квітки, на яку дивиться ваша камера. Ви можете, якщо хочете, навчити цей класифікатор, а потім експортувати його для використання у власній програмі.

Що ви робите звідси, повністю залежить від вас і вашої фантазії.

Я складаю цю статтю для всіх, хто є новим у всьому цьому і шукає місце для початку. Вам належить взяти цю інформацію, вдосконалити її та зробити її власною!

Якщо ви хочете переглянути ноутбук, його можна знайти тут.

Оскільки цей класифікатор зображень PyTorch був побудований як остаточний проект для програми Udacity, код використовує код з Udacity, який, у свою чергу, спирається на офіційну документацію PyTorch. Udacity також надав файл JSON для відображення міток. Цей файл можна знайти в цьому репо-файлі GitHub.

Інформацію про набір даних про квітки можна знайти тут. Набір даних включає окрему папку для кожного з 102 класових квітів. Кожна квітка позначена цифрою, і кожен із пронумерованих каталогів містить декілька файлів .jpg.

Давайте розпочнемо!

Фото Енні Спратт на знімку

Оскільки це нейронна мережа, що використовує більший набір даних, ніж мій процесор міг би обробляти в будь-який розумний проміжок часу, я пішов вперед і встановив свій класифікатор зображень у Google Colab. Colab по-справжньому приголомшливий, тому що він пропонує безкоштовний GPU. (Якщо ви новачок у Colab, перегляньте цю статтю про початок роботи з Google Colab!)

Оскільки я використовував Colab, мені потрібно було почати з імпорту PyTorch. Вам не потрібно цього робити, якщо ви не використовуєте Colab.

*** ОНОВЛЕННЯ! (01/29) *** Colab тепер підтримує рідний PyTorch !!! Вам не потрібно запускати код нижче, але я залишаю його на випадок, якщо у когось є проблеми!

# Імпортуйте PyTorch, якщо використовуєте Google Colab
# http://pytorch.org/
існує імпорт os.path
з wheel.pep425тегів імпортувати get_abbr_impl, get_impl_ver, get_abi_tag
platform = '{} {} - {}'. формат (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], якщо існує ('/ dev / nvidia0') else 'cpu'
! pip install -q http://download.pytorch.org/whl/{accelerator}/torch-0.4.1-{platform}-linux_x86_64.whl torchvision
імпорт факел

Потім, маючи деякі проблеми з Подушкою (вона в Колабі баггі!), Я просто пішов вперед і побіг цього:

імпорт PIL
друк (PIL.PILLOW_VERSION)

Якщо у вас є щось нижче, ніж 5.3.0, скористайтеся спадною меню в розділі "Час виконання" до "Перезапустити час виконання" та запустіть цю клітинку ще раз. Вам слід добре піти!

Ви хочете використовувати GPU для цього проекту, який надзвичайно просто налаштувати на Colab. Ви просто переходите до випадаючого меню "runtime", вибираєте "змінити тип виконання", а потім випадаєте "GPU" у спадному меню апаратного прискорювача!

Тоді я люблю бігати

train_on_gpu = torch.cuda.is_available ()
якщо не поїзд_on_gpu:
    print ("Bummer! Training on CPU ...")
ще:
    print ("Вам добре піти! Навчання на GPU ...")

просто щоб переконатися, що це працює. Потім бігайте

device = torch.device ("cuda: 0" if torch.cuda.is_available () else "cpu")

визначити пристрій.

Після цього імпортуйте файли. Існує маса способів зробити це, зокрема встановити Google Диск, якщо у вас зберігається набір даних, що насправді дуже просто. Хоча я і не зрозумів, що це найкорисніше рішення, я включаю це нижче, лише тому, що це так просто і корисно.

з імпортного диска google.colab
drive.mount ('/ content / gdrive')

Потім ви побачите посилання, натисніть на нього, дозвольте доступ, скопіюйте код, що з’явиться, вставте його у вікно, натисніть клавішу Enter, і вам буде добре! Якщо ви не бачите свій привід у бічній коробці ліворуч, просто натисніть «оновити», і він повинен з’явитися.

(Запустіть клітинку, натисніть посилання, скопіюйте код на сторінку, вставте її у вікно, натисніть клавішу Enter, і ви побачите це, коли ви успішно змонтували диск):

Насправді це дуже просто!

Однак якщо ви краще скачаєте спільне поштове посилання (це завершується простіше та швидше для цього проекту), ви можете використовувати:

! wget
! розпакуйте

Наприклад:

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

Це дасть вам набір даних про квітки Udacity за лічені секунди!

(Якщо ви завантажуєте невеликі файли, ви можете просто завантажити їх безпосередньо за допомогою простого коду. Однак, якщо ви хочете, ви також можете просто перейти в ліву частину екрана і натиснути «завантажити файли», якщо ви цього не зробите відчуваю, як запустити простий код, щоб схопити локальний файл.)

Після завантаження даних я імпортував бібліотеки, які хотів використовувати:

% matplotlib inline
% config InlineBackend.figure_format = 'сітківка'
час імпорту
імпортувати json
імпортувати копію
імпортувати matplotlib.pyplot як plt
імпортувати моренароджених як sns
імпортувати numpy як np
імпорт PIL
з зображення імпорту PIL
з колекцій імпорту OrdersDict
імпорт факел
від імпорту факела nn, оптим
з torch.optim import lr_scheduler
з torch.autograd Varia Variable
імпортний факел
з наборів даних імпорту факелів, моделей, перетворень
з імпорту torch.utils.data.sampler SubsetRandomSampler
імпорт torch.nn як nn
імпорт torch.nn.функціональний як F

Далі йде перетворення даних! Ви хочете переконатися, що ви використовуєте кілька різних типів перетворень на своєму навчальному наборі, щоб допомогти вашій програмі навчитися якнайбільше. Можна створити більш надійну модель, навчаючи її на перевернутих, обернутих і обрізаних зображеннях.

Це означає, що стандартні відхилення передбачені для нормалізації значень зображення перед передачею їх в нашу мережу, але їх можна також знайти, переглянувши середні та стандартні значення відхилень різних розмірів тензорів зображень. Офіційна документація тут неймовірно корисна!

Для мого класифікатора зображень я просто:

data_transforms = {
    'поїзд': transforms.Compose ([
        Перетворення: Випадкове обертання (30),
        transforms.RandomResizedCrop (224),
        transforms.RandomHorizontalFlip (),
        transforms.ToTensor (),
        перетворення.Нормалізувати ([0.485, 0.456, 0.406],
                             [0,229, 0,224, 0,225])
    ]),
    'дійсний': transforms.Compose ([
        перетворення. Розмір (256),
        трансформації.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),
                                          дані_трансформи [x])
                  для x in ['train', 'valid']}
# Використовуючи набори даних зображень та форми поїздів, визначте завантажувачі даних
batch_size = 64
dataloaders = {x: torch.utils.data.DataLoader (image_datasets [x], batch_size = batch_size,
                                             перетасувати = Правда, число_робочих = 4)
              для x in ['train', 'valid']}
class_names = image_datasets ["поїзд"]
skupek_imates = {x: len (image_datasets [x]) for x in ['train', 'valid']}
class_names = image_datasets ["поїзд"]

Як ви бачите вище, я також визначив розмір партії, завантажувачі даних та назви класів у наведеному вище коді.

Щоб дуже швидко переглянути дані та перевірити свій пристрій, я побіг:

друк (розмір даних)
друк (пристрій)
{'поїзд': 6552, 'дійсний': 818}
куда: 0

Далі нам потрібно зробити кілька зіставлення з номера етикетки та фактичної назви квітки. Udacity надав файл JSON для цього відображення.

з відкритим ('cat_to_name.json', 'r') як f:
    cat_to_name = json.load (f)

Щоб перевірити завантажувач даних, запустіть:

images, labels = next (iter (завантажувачі даних ['поїзд']))
rand_idx = np.random.randint (len (зображення))
# Друк (rand_idx)
print ("label: {}, class: {}, name: {}". формат (мітки [rand_idx] .item (),
                                               назви класів [мітки [rand_idx] .item ()],
                                               cat_to_name [class_names [мітки [rand_idx] .item ()]]))

Тепер він починає ще більше захоплювати! Ряд моделей за останні кілька років були створені людьми, набагато більш кваліфікованими, ніж більшість із нас, для повторного використання у комп’ютерному зорі. PyTorch дозволяє легко завантажувати заздалегідь підготовлені моделі та будувати на них, саме це ми робимо для цього проекту. Вибір моделі повністю залежить від вас!

Деякі з найпопулярніших попередньо підготовлених моделей, такі як ResNet, AlexNet та VGG, походять із виклику ImageNet. Ці заздалегідь підготовлені моделі дозволяють іншим швидко отримувати передові результати в комп’ютерному зорі, не потребуючи такої великої кількості енергії, терпіння та часу на комп’ютері. Я фактично мав чудові результати з DenseNet і вирішив використовувати DenseNet161, який дав мені дуже хороші результати порівняно швидко.

Ви можете швидко налаштувати це, запустивши

model = models.densenet161 (перевірено = True)

але може бути цікавіше вибрати собі модель, оптимізатор та планувальник. Щоб встановити вибір в архітектурі, запустіть

model_name = 'thicknet' #vgg
якщо model_name == 'thicknet':
    model = models.densenet161 (перевірено = True)
    num_in_features = 2208
    друк (модель)
elif model_name == 'vgg':
    model = models.vgg19 (перевірено = True)
    num_in_features = 25088
    друк (модель.класифікатор)
ще:
    print ("Невідома модель, будь ласка, оберіть" thicknet "або" vgg ")

що дозволяє швидко налаштувати альтернативну модель.

Після цього ви можете почати будувати свій класифікатор, використовуючи параметри, які найкраще підходять для вас. Я пішов вперед і будував

для парам у моделі.parameters ():
    param.requires_grad = Неправдивий
def build_classifier (num_in_features, hidden_layers, num_out_features):
   
    класифікатор = nn.послідовний ()
    якщо приховані_шарі == Немає:
        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) у перерахунку (шари_розміри):
            classifier.add_module ('fc' + str (i + 1), nn.Linear (h1, h2))
            classifier.add_module ('relu' + str (i + 1), nn.ReLU ())
            classifier.add_module ('drop' + str (i + 1), nn.Dropout (.5))
        classifier.add_module ('вихід', nn.Linear (приховані_шари [-1], число_визначення))
        
    класифікатор повернення

що дозволяє простий спосіб змінити кількість прихованих шарів, які я використовую, а також швидко скорегувати швидкість випадання. Ви можете вирішити додати додаткові шари ReLU та випадаючі, щоб більш тонко відточити вашу модель.

Далі попрацюйте з навчанням параметрів класифікатора. Я вирішив переконатися, що тут я лише навчив параметри класифікатора, але параметри функції заморожені. За допомогою оптимізатора, критерію та планувальника ви можете бути настільки креативними, як хочете. Критерієм є метод, який використовується для оцінки відповідності моделі, оптимізатор - це метод оптимізації, який використовується для оновлення ваг, а планувальник надає різні методи коригування швидкості навчання та розміру кроків, що використовуються під час оптимізації.

Спробуйте якомога більше варіантів та комбінацій, щоб побачити, що дає найкращий результат. Ви можете ознайомитись з усією офіційною документацією тут. Рекомендую поглянути на це та прийняти власні рішення щодо того, що ви хочете використовувати. Тут ви буквально не маєте нескінченної кількості варіантів, але це впевнено, як колись починаєте грати!

hidden_layers = Немає
classifier = build_classifier (num_in_features, hidden_layers, 102)
друк (класифікатор)
# Тренуйте лише параметри класифікатора, параметри функції заморожені
якщо model_name == 'thicknet':
    model.classifier = класифікатор
    критерій = nn.CrossEntropyLoss ()
    optimizer = optim.Adadelta (model.parameters ())
    sched = optim.lr_scheduler.StepLR (оптимізатор, step_size = 4)
elif model_name == 'vgg':
    model.classifier = класифікатор
    критерій = nn.NLLLoss ()
    optimizer = optim.Adam (model.classifier.parameters (), lr = 0,0001)
    sched = lr_scheduler.StepLR (оптимізатор, step_size = 4, гамма = 0,1)
ще:
    пропуск

Зараз саме час тренувати свою модель.

# Адаптовано з https://pytorch.org/tutorials/beginner/transfer_learning_tutorial.html
def train_model (модель, критерій, оптимізатор, графік, num_epochs = 5):
    since = time.time ()
best_model_wts = copy.deepcopy (model.state_dict ())
    best_acc = 0,0
для епохи в діапазоні (num_epochs):
        print ("Епоха {} / {}". формат (епоха + 1, число_епох))
        print ('-' * 10)
# Кожна епоха має етап навчання та перевірки
        для етапу в "поїзд", "дійсний"]:
            якщо фаза == 'поїзд':
                model.train () # Встановити модель в режим навчання
            ще:
                model.eval () # Встановити модель для оцінки режиму
running_loss = 0,0
            running_corrects = 0
# Ітерація над даними.
            для входів, міток у завантажувачі даних [фаза]:
                inputs = inputs.to (пристрій)
                labels = labels.to (пристрій)
# Нульові градієнти параметрів
                optimizer.zero_grad ()
# Вперед
                # історія треків, якщо тільки у поїзді
                з torch.set_grad_enabled (фаза == 'поїзд'):
                    Виходи = модель (входи)
                    _, preds = torch.max (виходи, 1)
                    втрата = критерій (результати, мітки)
# Назад + оптимізуйте лише у фазі тренувань
                    якщо фаза == 'поїзд':
                        # sched.step ()
                        loss.backward ()
                        
                        optimizer.step ()
# Статистика
                running_loss + = loss.item () * inputs.size (0)
                running_corrects + = torch.sum (preds == labels.data)
epoch_loss = running_loss / skuter_size [фаза]
            epoch_acc = running_corrects.double () / database_sensions [фаза]
print ('{} Втрата: {: .4f} Acc: {: .4f}'. формат (
                фаза, epoch_loss, epoch_acc))
# Глибока копія моделі
            якщо фаза == "дійсна" та epoch_acc> best_acc:
                best_acc = epoch_acc
                best_model_wts = copy.deepcopy (model.state_dict ())
print ()
time_elapsed = time.time () - з тих пір
    print ("Навчання завершено в {: .0f} m {: .0f} s'.format (
        time_elapsed // 60, time_elapsed% 60))
    print ("Найкращий вал Acc: {: 4f}". формат (best_acc))
# Завантажте найкращі ваги моделей
    model.load_state_dict (best_model_wts)
    
    модель повернення
епохи = 30
model.to (пристрій)
модель = train_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
...
Тренування завершені в 67 м 43-х
Кращий val Acc: 0,973105

Ви можете бачити, що запуск цього коду в Google Colab з GPU зайняв трохи більше години.

Настав час для оцінки

model.eval ()
точність = 0
для входів, міток у завантажувачі даних ['дійсний']:
    входи, мітки = inputs.to (пристрій), labels.to (пристрій)
    Виходи = модель (входи)
    
    # Клас з найбільшою ймовірністю - це наш прогнозований клас
    рівність = (labels.data == outputs.max (1) [1])
# Точність = кількість правильних прогнозів, поділених на всі прогнози
    точність + = рівність.type_as (факел.FloatTensor ()). mean ()
    
print ("Точність тесту: {: .3f}". формат (точність / len (завантажувачі даних ['дійсний'])))
Точність тесту: 0,973

Важливо зберегти контрольний пункт

model.class_to_idx = image_datasets ['поїзд']. class_to_idx
контрольна точка = {'input_size': 2208,
              'output_size': 102,
              'епохи': епохи,
              'batch_size': 64,
              'модель': models.densenet161 (перевірено = True),
              'класифікатор': класифікатор,
              'планувальник': планується,
              'оптимізатор': optimizer.state_dict (),
              'state_dict': model.state_dict (),
              'class_to_idx': model.class_to_idx
             }
   
torch.save (контрольний пункт, 'checkpoint.pth')

Вам не потрібно зберігати всі параметри, але я включаю їх як приклад. Цей контрольно-пропускний пункт спеціально зберігає модель із заздалегідь підготовленою архітектурою щільної161, але якщо ви хочете зберегти свою контрольну точку за допомогою варіанту з двома виборами, ви можете абсолютно це зробити. Просто відрегулюйте розмір введення та модель.

Тепер ви можете завантажити ваші контрольно-пропускні пункти. Якщо ви надсилаєте проект у робочу область Udacity, все може стати дещо складним. Ось довідка щодо усунення несправностей із завантаженням на контрольній точці.

Ви можете перевірити свої ключі, запустивши

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

Потім завантажте і відновіть свою модель!

Def load_checkpoint (filepath):
    checkpoint = torch.load (filepath)
    model = контрольна точка ['модель']
    model.classifier = контрольна точка ['класифікатор']
    model.load_state_dict (контрольна точка ['state_dict'])
    model.class_to_idx = контрольна точка ['class_to_idx']
    optimizer = контрольна точка ['optimizer']
    epochs = контрольна точка ['epochs']
    
    для парам у моделі.parameters ():
        param.requires_grad = Неправдивий
        
    повернути модель, контрольна точка ['class_to_idx']
модель, class_to_idx = load_checkpoint ('checkpoint.pth')

Хочете продовжувати? Добре зайнятися попередньою обробкою зображення та висновком для класифікації. Вперед і визначте свій шлях до зображення та відкрийте зображення:

image_path = 'flower_data / дійсний / 102 / image_08006.jpg'
img = Image.open (image_path)

Обробіть своє зображення та подивіться на оброблене зображення:

визначити_файл (зображення):
    '' 'Масштаби, посіви та нормалізує зображення PIL для моделі PyTorch,
        повертає масив Numpy
    '' '
    # Обробити зображення PIL для використання у моделі PyTorch
    # tensor.numpy (). транспонировать (1, 2, 0)
    preprocess = transforms.Compose ([
        перетворення. Розмір (256),
        трансформації.CenterCrop (224),
        transforms.ToTensor (),
        перетворення.Нормалізувати (середнє значення = [0,485, 0,456, 0,406],
                             std = [0,229, 0,224, 0,225])
    ])
    image = попередня обробка (зображення)
    повернути зображення
def imshow (зображення, ax = Немає, назва = None):
    "" "Imshow для тензора." ""
    якщо сокира немає:
        інжир, сокира = plt.subplots ()
    
    # Тенори 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 / дійсний / 102 / image_08006.jpg') як зображення:
    plt.imshow (зображення)
model.class_to_idx = image_datasets ['поїзд']. class_to_idx

Створіть функцію для прогнозування:

def predict2 (image_path, модель, topk = 5):
    '' 'Прогнозуйте клас (або класи) зображення, використовуючи навчену модель глибокого навчання.
    '' '
    
    # Реалізуйте код для прогнозування класу з файлу зображення
    img = Image.open (image_path)
    img = process_image (img)
    
    # Перетворити 2D зображення в 1D вектор
    img = np.expand_dims (img, 0)
    
    
    img = torch.from_numpy (img)
    
    model.eval ()
    inputs = Змінна (img) .to (пристрій)
    logits = model.forward (входи)
    
    ps = F.softmax (logits, dim = 1)
    topk = ps.cpu (). topk (topk)
    
    return (e.data.numpy (). вичавити (). tolist () для e in topk)

Після того як зображення будуть у правильному форматі, ви можете написати функцію для прогнозування відповідно до вашої моделі. Однією поширеною практикою є прогнозування 5-ти найкращих (зазвичай їх називають топ-KK) найбільш ймовірних класів. Вам потрібно буде обчислити ймовірності класу, а потім знайти найбільші значення KK.

Щоб отримати найбільші значення KK у тензорі, використовуйте k.topk (). Цей метод повертає як найвищі k ймовірності, так і індекси тих ймовірностей, що відповідають класам. Вам потрібно перетворити з цих індексів у фактичні мітки класу за допомогою class_to_idx, які ви додали до моделі або з папки зображень, яку використовували для завантаження даних. Не забудьте інвертувати словник, щоб ви також отримали відображення від індексу до класу.

Цей метод повинен пройти шлях до зображення та контрольної точки моделі, а потім повернути ймовірності та класи.

img_path = 'flower_data / дійсний / 18 / image_04252.jpg'
probs ,lasses = predict2 (img_path, model.to (пристрій))
друк (проб)
друк (заняття)
flower_names = [cat_to_name [class_names [e]] для e в класах]
друк (імена квітів)

Я був дуже задоволений тим, як працювала моя модель!

[0.9999195337295532, 1.4087702766119037e-05, 1.3897360986447893e-05, 1.1400215043977369e-05, 6.098791800468462e-06]
[12, 86, 7, 88, 40]
['перуанська лілія', 'пустельна троянда', 'король протея', 'магнолія', 'лілія меча']

В основному, майже на 100% є ймовірність, що вказане нами зображення - це перуанська лілія. Хочете поглянути? Спробуйте використовувати matplotlib для побудови ймовірностей для п’яти перших класів у гістограмі разом із вхідним зображенням:

def view_classify (img_path, prob, class, mapping):
    '' 'Функція для перегляду зображення та його передбачувані класи.
    '' '
    image = Image.open (img_path)
fig, (ax1, ax2) = plt.subplots (figsize = (6,10), ncols = 1, nrows = 2)
    ім'я квітки = відображення [img_path.split ('/') [- 2]]
    ax1.set_title (ім'я квітки)
    ax1.imshow (зображення)
    ax1.axis ('вимкнено')
    
    y_pos = np.arange (len (prob))
    ax2.barh (y_pos, prob, align = 'центр')
    ax2.set_yticks (y_pos)
    ax2.set_yticklabels (назви квітів)
    ax2.invert_yaxis () # мітки читаються зверху вниз
    ax2.set_title ("Імовірність класу")
view_classify (img_path, probs, класи, cat_to_name)

Ви повинні побачити щось подібне:

Я повинен сказати, що я дуже задоволений цим! Я рекомендую протестувати кілька інших зображень, щоб побачити, наскільки близькі ваші прогнози щодо різних зображень.

Настав час зробити власну модель і повідомте мені, як це відбувається у відповідях нижче!

Фото Пеза Гонсалеса на знімку

Ви закінчили свою модель глибокого навчання або машинного навчання, але не знаєте, що з цим робити далі? Чому б не розгорнути його в Інтернеті?

Дістаньте свою модель там, щоб кожен міг її бачити!

Перегляньте цю статтю, щоб дізнатися, як розгорнути модель машинного навчання за допомогою колби!