четвер, 27 травня 2021 р.

Побудова та навчання нейронної мережі для розпізнавання рукописних цифр

Mathedemo
Keras - це потужна, проста у використанні бібліотека Python, що надає високорівневі будівельні блоки для конструювання моделей глибокого навчання.

Побудова нейронної мережі складається з наступних етапів

  • Підготовка і завантаження даних
  • Визначення архітектури моделі
  • Компіляція моделі
  • Навчання моделі
  • Оцінювання моделі
  • Збирання моделі
  • Застосування моделі
Встановлення Keras

Встановлюємо пакет TensorFlow.

 pip install tensorflow 
Потім встановлюємо пакет Keras.
 pip install keras 

Основи роботи з Keras покажемо на конкретному прикладі. Спроектуємо та навчимо нейронну мережу для розпізнавання рукописних чисел.

Підготовка і завантаження даних

Нагадаємо що нейронна мережа визначається конкретними значеннями вагових коефіцієнтів кожного нейрона. Процес навчання нейронної мережі полягає у підгонці цих вагових коефіцієнтів на основі тестових даних, так щоб кожний образ на вході правильно класифікувався на виході. Ми будемо використовувати готовий набір (dataset) для напівтонових зображень рукописних цифр із відомого набору даних MNIST, який містить 60 000 навчальних зображень ($28 \times 28$ пікселів) і 10 000 контрольних зображень такого вигляду

Цей набір даних уже входить в Keras у вигляді чотирьох масивів i його можна завантажити
from keras.datasets import mnist
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()
Процес завантаження може займати деякий час.

Вихідні дані зберігаються в тривимірному масиві $(60000, 28, 28)$ типу np.uint8, значеннями в якому є числа в інтервалі $[0, 255]$. Такі масиви даних в бібліотеці TensorFlow називаються тензорами. Хоча, в математиці під тензорами розуміються дещо складніші об'єкти.

Переглядаємо, наприклад, друге зображення

cv2.imshow('1',train_images[1])
Бачимо, що на зображені цифра $0$. Відповідна тестова мітка з масиву train_labels, можна перевірити, також має значення $0$
train_labels[1]
0
Ми перетворимо тривимірний масив в двовимірний масив $(60000, 28 \times 28)$ типу float32, а потім нормалізуємо його щоб отримати значеннями компонент в інтервалі [0, 1].
train_images = train_images.reshape((60000, 28 * 28))
train_images = train_images.astype('float32') / 255
test_images = test_images.reshape((10000, 28 * 28))
test_images = test_images.astype('float32') / 255 
Нормалізація необхідна щоб наблизити числові значення пікселів до значень вагових коефіцієнтів, що пришвидшить збіжність розв'язку.

Отже, кожне зображення подається вектором довжини 784 і саме компоненти цього вектора будуть подаватися вхідним нейронам.

Тепер підготовлюємо мітки, тобто завантажимо масиви із відгуками на кожне тестове зображення.

from keras.utils import to_categorical
train_labels = to_categorical(train_labels)
test_labels = to_categorical(test_labels) 
Далі, передамо нейронній мережі навчальні дані train_images і train_labels. В результаті цього мережа навчиться зіставляти зображення з мітками. Потім ми запропонуємо мережі класифікувати зображення в test_images і перевіримо точність класифікації по мітках з test_labels.

Архітектура моделі

У Keras є два основні типи моделей: послідовна модель (Sequential class) та клас Model, що використовується з функціональним API. Ми будемо використовувати клас Sequential, тому що наша модель є простою послідовністю шарів (layers) - вхідного шару який складається із $28 \cdot 28=784$ нейронів, тобто кожному пікселю зображення відповідає один нейрон. Інформація про вхідний шар міститься в описі прихованого шару, який створюється методом .add(). Шар фактично є функцією, на вхід якої подається тензор і на виході також отримується тензор, можливо іншого розміру. Ми будемо використовувати щільні, повно-зв'язні( Dense) шари в яких кожен нейрон одного шару з'єднаний з кожним нейроном наступного шару. Прихований шар містить $512$ нейронів а вихідний шар містить $10$ класифікаційних нейронів.

За функції активації нейронів ми будемо використовувати функції relu у прихованому шарі і softmax в останньому шарі.

from keras import models
from keras import layers
''' Визначення типу моделі '''
network = models.Sequential()
''' Визначення прихованого  шару '''
network.add(layers.Dense(512, activation='relu', input_shape=(28 * 28,)))
network.add(layers.Dense(10, activation='softmax'))
Опцію input_shape можна опустити, тоді розмір вхідних даних визначиться із форми навчальних даних.

Компіляція мережі

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

  • оптимізатор(optimizer) - конкретний алгоритм, який буде оновлювати ваги в процесі навчання моделі; Оптимізатори які доступні в Keras :RMSProp, sgd, Adagrad, Adadelta,Arguments.
  • функцію втрат(loss), яку оптимізатор використовує для оцінювання якості своєї роботи на навчальних даних і, відповідно, як коригувати навчання в правильному напрямку; Доступні цільові функції
     categorical_crossentropy, logcosh, mean_squared_error, hinge,binary_crossentropy, poisson  
  • метрики для оцінки якості проведеного навчання. Метрики оцінюють результати навчання за такими параметрами як точність, коректність, повнота. Доступні метрики
    
    accuracy, binary_accuracy,categorical_accuracy,
    sparse_categorical_accuracy,cosine_proximity 
Показники якості схожі з цільовими функціями, але вони використовуються не для навчання моделі а для оцінки вже навченої моделі.

Рекомендується використовувати такі поєднання цільових функцій, оптимізаторів та метрик: для задач класифікації із багатьма класами

model.compile(optimizer='rmsprop',
              loss='categorical_crossentropy',
              metrics=['accuracy'])
Для бінарних задач класифікації із двома класами
model.compile(optimizer='rmsprop',
              loss='binary_crossentropy',
              metrics=['accuracy'])
Після визначення цих параметрів процес компіляції виконується методом .compile():
 network.compile(optimizer='rmsprop',loss='categorical_crossentropy',metrics=['accuracy'])

Навчання моделі

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

network.fit(train_images, train_labels, epochs=5, batch_size=128)
.......
58368/60000 [============================>.] - ETA: 0s - loss: 0.0367 - accuracy: 0.9891
59392/60000 [============================>.] - ETA: 0s - loss: 0.0367 - accuracy: 0.9890
60000/60000 [==============================] - 3s 55us/step - loss: 0.0369 - accuracy: 0.9890 
На навчальних даних ми досягнули точності на навчальних даних 0.9890.

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

Збільшення числа епох до 6 змінює точність до 0.9918. З оптимізатором sgd точність 0.9058. З оптимізатором Adagrad точність 0.9831. З оптимізатором Adadelta точність 0.9852.

Перевірка моделі

Після того як модель навчена, її потрібно перевірити на контрольному наборі даних, які ще не пред'являлися моделі. Перевіримо як модель розпізнає контрольний набір.

test_loss, test_acc = network.evaluate(test_images, test_labels)
32/10000 [..............................] - ETA: 4s
 2016/10000 [=====>........................] - ETA: 0s
 3712/10000 [==========>...................] - ETA: 0s
 5440/10000 [===============>..............] - ETA: 0s
 7168/10000 [====================>.........] - ETA: 0s
 9184/10000 [==========================>...] - ETA: 0s
10000/10000 [==============================] - 0s 29us/step
test_acc: 0.9801999926567078 

Збереження моделі

Команда model.save(filepath) зберігає модель Keras в одному файлі HDF5, який буде містити:

  • архітектуру моделі, що дозволяє відновити модель
  • ваги моделі
  • конфігурація тренувань (функцію втрат, оптимізатор)
  • стан оптимізатора, що дозволяє відновити навчання саме там, де ви зупинилися.
У нашому випадку зберігається модель з іменем network у файлі my_model.h5:
 network.save('my_model.h5') 
Після виконання цієї команди в поточному каталозі має з'явитися файл my_model.h5!

При потребі можна зберегти лише архітектуру моделі, наприклад у .json форматі

json_string = model.to_json() 
або лише значення вагових коефіцієнтів
model.save_weights('my_model_weights.h5')
Ці коефіцієнти пізніше можна завантажити в моделі з іншими архітектурами.

Робота із моделлю

Завантажуємо попередньо збережену модель

from keras.models import load_model
model = load_model('my_model.h5')
Для класифікації зображення із тестового файлу (наприклад test.png) із цифрою зберігаємо його в каталозі із виконуваним файлом . Завантажуємо його в OpenCV як напівтонове зображення і переформатовується до того розміру на якому відбувалося тренування нашої моделі
tst=255-cv2.imread('test.png',0)# чому відняли від 255?
tst=cv2.resize(tst, (28, 28))
tst = tst.reshape((1, 28 * 28))
tst=tst.astype('float32') / 255 
Підготовленні данні передаємо функції model.predict(tst). На виході отримаємо список списків із один елементом -- списком довжини 10, у якому на $i$-й позиції знаходиться ймовірність того що на вхідному зображенні є число $i.$ Нам потрібна позиція з максимальною ймовірністю.
pred=list(model.predict(tst)[0])
print(pred.index(max(pred)))
В результаті отримаємо число, яке нейронна мережа розпізнала на зображенні.

Немає коментарів:

Дописати коментар