Я начал свой путь в машинном обучении в прошлом году с этого фантастического курса по машинному обучению от Стэнфордского университета на Coursera (2,5 миллиона записались на данный момент и 113 000 + 4,9/5 отзывов! Только что вау! Если вы только начинаете свой путь в машинном обучении, я очень рекомендую этот базовый курс.
Один из способов, который я нашел очень эффективным в изучении ML, — это вернуться к предыдущим задачам по программированию и решить их с помощью новых навыков и фреймворков. В этом посте я буду описывать исследование этого задания на основе Keras, а также упражнение по добавлению ваших собственных рукописных образцов и просмотру этой работы. Нет большей радости, чем учиться на практике, так что давайте углубимся.
Примечание. Я использую colab (сокращение от collaboratory — среда блокнота Jupyter, для использования которой не требуется настройка. Блокнот colab для всего кода в этой статье можно найти здесь.
Часть 1: Загрузка и анализ обучающих данных:
Следите в коллабе здесь.
ex3data1.mat представляет собой обучающий набор рукописных цифр (20px X 20px), полученный из MNIST. В ex3data1.mat есть 5000 обучающих примеров, где каждый обучающий пример представляет собой изображение цифры в градациях серого размером 20 на 20 пикселей. Каждый пиксель представлен числом с плавающей запятой, указывающим интенсивность оттенков серого в этом месте. Сетка пикселей 20 на 20 разворачивается в 400-мерный вектор. Каждый из этих обучающих примеров становится отдельной строкой в нашей матрице данных X. Вторая часть обучающей выборки — это 5000-мерный вектор y, который содержит метки для обучающей выборки.
data = spio.loadmat('ex3data1.mat', squeeze_me=True) #Images X_train = data['X'] print(X_train.shape) #Labels Y_train = data['y'] print(Y_train.shape) #Note that 0 is encoded as 10 print(Y_train) (5000, 400) (5000,) [10 10 10 ... 9 9 9]
Каждое изображение в X разворачивается в массив форм (1, 400) из массива форм (20, 20). Чтобы прочитать его правильно, нам нужно будет транспонировать массив изображений после изменения его формы обратно в (20, 20).
Например. скажем, изображение 4x4:
a b c d a b c d a b c d a b c d
развертывание его в массив формы (1, 400) приводит к:
aaaabbbbccccdddd
преобразование его в (4,4) приводит к:
a a a a b b b b c c c c d d d d
и, наконец, выполнив транспонирование, мы получаем изображение обратно: -
a b c d a b c d a b c d a b c d
Давайте попробуем этот пример ниже: -
a = np.array([['a', 'b', 'c', 'd'], ['a', 'b', 'c', 'd'], ['a', 'b', 'c', 'd'], ['a', 'b', 'c', 'd']]) print(a) # unroll image b = np.transpose(a).reshape(1,4*4) print(b) # re-create image print(np.transpose(b.reshape(4, 4))) [['a' 'b' 'c' 'd'] ['a' 'b' 'c' 'd'] ['a' 'b' 'c' 'd'] ['a' 'b' 'c' 'd']] [['a' 'a' 'a' 'a' 'b' 'b' 'b' 'b' 'c' 'c' 'c' 'c' 'd' 'd' 'd' 'd']] [['a' 'b' 'c' 'd'] ['a' 'b' 'c' 'd'] ['a' 'b' 'c' 'd'] ['a' 'b' 'c' 'd']]
Давайте посмотрим на первое изображение в X_train:-
Обратите внимание, что цифра 0 соответствует метке 10.
# let's take the 1st image in the training set image = np.transpose(X_train[0].reshape(20,20)) plt.figure(figsize = (1,1)) plt.imshow(image, cmap='gray') print(Y_train[0])
Давайте попробуем построить подмножество случайно перемешанных изображений:
width, height, columns, rows = 20, 20, 10, 10 fig=plt.figure(figsize=(rows, columns)) # sample and shuffle 10% of the training data image_samples = pd.DataFrame(X_train).sample(frac=0.1) for i in range(0, rows*columns): image = np.transpose(image_samples[i:i+1].values.reshape(width, height)) fig.add_subplot(rows, columns, i+1) plt.imshow(image, cmap='gray') plt.show()
Далее мы горячим кодированием метки разделяем на 10 классов.
# one-hot encode the target (0 is encoded as 10 in original dataset) Y_train = np.where(Y_train==10,0,Y_train) Y_train_one_hot = keras.utils.to_categorical(Y_train, 10) print(Y_train_one_hot.shape) print(Y_train[0],' --> ', Y_train_one_hot[0]) print(Y_train[1],' --> ', Y_train_one_hot[1]) print(Y_train[2],' --> ', Y_train_one_hot[2]) (5000, 10) 0 --> [1. 0. 0. 0. 0. 0. 0. 0. 0. 0.] 0 --> [1. 0. 0. 0. 0. 0. 0. 0. 0. 0.] 0 --> [1. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
Давайте перезагрузим данные, перемешаем, а затем разделим их на обучающий и проверочный наборы (мы будем использовать разделение 80:20).
def reload_with_split(display_test_image=False): # Reload data data = spio.loadmat('ex3data1.mat', squeeze_me=True) #Images X_train = data['X'] print(X_train.shape) #Labels Y_train = data['y'] print(Y_train.shape) # We will need to shuffle this data before splitting as it's sorted by labels s = np.arange(0, len(X_train), 1) np.random.shuffle(s) X_train_shuffled, Y_train_shuffled = X_train[s], Y_train[s] #Training data: Images X_train = X_train_shuffled[:4000] print(X_train.shape) #Training data: Labels Y_train = Y_train_shuffled[:4000] print(Y_train.shape) X_val = X_train_shuffled[4000:] print(X_val.shape) Y_val = Y_train_shuffled[4000:] print(Y_val.shape) #Convert label for 0 to 0 (from 10) Y_train = np.where(Y_train==10,0,Y_train) #One-hot encode the labels. Y_train_one_hot = keras.utils.to_categorical(Y_train, 10) print(Y_train_one_hot.shape) #Same for validation labels Y_val = np.where(Y_val==10,0,Y_val) Y_val_one_hot = keras.utils.to_categorical(Y_val, 10) print(Y_val_one_hot.shape) if display_test_image: # let's take the first image in the validation set (this should be different # every time you run this cell). image = np.transpose(X_val[0].reshape(20,20)) plt.figure(figsize = (1,1)) plt.imshow(image, cmap='gray') print(Y_val[0]) return [X_train, Y_train_one_hot, X_val, Y_val_one_hot] X_train, Y_train_one_hot, X_val, Y_val_one_hot = reload_with_split() (5000, 400) (5000,) (4000, 400) (4000,) (1000, 400) (1000,) (4000, 10) (1000, 10) 5
Часть 2: Обучите глубокую нейронную сеть для классификации:
Следите в коллабе здесь.
В этой статье перечислены все тесты производительности для DNN и CNN. Для DNN попробуем 6-слойную NN 400–2500–2000–1500–1000–500–10, как подробно описано в этой статье. Мы будем использовать ReLu между плотными слоями и активацию Softmax для создания окончательных вероятностей.
from keras.layers import Dense, Activation from keras.models import Sequential model_dnn = Sequential([ Dense(400, input_shape=(400,)), Activation('relu'), Dense(2500), Activation('relu'), Dense(2000), Activation('relu'), Dense(1500), Activation('relu'), Dense(1000), Activation('relu'), Dense(500), Activation('relu'), Dense(10), Activation('softmax') ]) model_dnn.summary() reload_with_split() model_dnn.compile( loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'] ) model_dnn.fit( X_train, Y_train_one_hot, batch_size=512, epochs=100, validation_data=(X_val, Y_val_one_hot), verbose=1 )
Разве с Керасом не все так просто :). Узнайте больше о Keras здесь.
После обучения для 100 эпох мы получаем точность 100 % (переобучение) и точность проверки ~94 %. Давайте посмотрим, как наша модель работает с реальными данными.
Часть 3. Создание собственного тестового рукописного набора данных:
Напишите цифры от 0 до 9 на листе бумаги и сфотографируйте это. Затем вы можете создать отдельные кадры для каждой из цифр, которые вы написали, и назвать их по порядку. В моем случае я назвал их 1.png, 2.png и так далее…
Давайте проверим нашу DNN на наших рукописных данных:
import cv2 #Assuming images are stored as 1.png, 2.png and so on def test_dnn_model(): num_px=20 for i in range(1,10): image = cv2.imread(str(i) + '.png', 0) image_rs = cv2.resize(image,(num_px,num_px)) # training images are grayscale and normalized image_rs = (255-image_rs)/255 image_rs = np.transpose(image_rs).reshape(1,400,) my_predicted_number = model_dnn.predict(image_rs,batch_size=1, verbose=False) confidence_array = np.argsort(-my_predicted_number[0]) print('Real: '+ str(i) +', Predicted number:' + str(confidence_array[0]) + ' Also likely: ' + str(confidence_array[1]) + ' or ' + str(confidence_array[2])) def display_handwritten_images(): width, height, columns, rows = 20, 20, 3, 3 fig=plt.figure(figsize=(rows, columns)) for i in range(1, rows*columns+1): image = cv2.imread(str(i) + '.png', 0) image_rs = cv2.resize(image,(width,height)) # training images are grayscale and normalized image = (255-image_rs)/255 fig.add_subplot(rows, columns, i) plt.imshow(image,cmap='gray') plt.show() test_dnn_model() display_handwritten_images() Real: 1, Predicted number:2 Also likely: 3 or 8 Real: 2, Predicted number:2 Also likely: 3 or 8 Real: 3, Predicted number:3 Also likely: 5 or 9 Real: 4, Predicted number:2 Also likely: 3 or 8 Real: 5, Predicted number:5 Also likely: 8 or 3 Real: 6, Predicted number:5 Also likely: 8 or 0 Real: 7, Predicted number:2 Also likely: 3 or 8 Real: 8, Predicted number:2 Also likely: 3 or 8 Real: 9, Predicted number:2 Also likely: 3 or 8
← Вот как выглядят наши рукописные изображения.
Итак, как мы справились с нашим рукописным набором данных с только что обученной DNN — 30% точность (40%, если мы рассматриваем 2-ю и 3-ю наивысшие вероятности). УРА!!! Что здесь случилось ? (Добро пожаловать в реальный мир :-)).
- Статистическое отклонение набора данных для обучения/проверки и новые данные. Рукописные изображения, которые у меня есть, взяты из совершенно другого статистического набора. Модели реального мира требуют постоянного сбора новых данных и обучения для поддержания точности.
- Наша точность проверки при разделении 80:20 составляет всего 94 %, что довольно мало (мы также подгоняли к нашим обучающим данным в DNN).
Давайте попробуем обучить сверточную нейронную сеть и посмотрим, сможем ли мы добиться большего успеха:
Часть 4. Обучите модель классификации с помощью сверточной нейронной сети (CNN).
Следите в коллабе здесь.
from keras.layers import Conv2D, MaxPooling2D, Flatten, Dropout from keras.models import Sequential model_cnn = Sequential([ Conv2D(32, (3,3), activation='relu', input_shape=(20,20,1)), Conv2D(64, (3,3), activation='relu'), MaxPooling2D(2,2), Dropout(0.25), Flatten(), Dense(128, activation='relu'), Dropout(0.25), Dense(10, activation='softmax') ]) model_cnn.summary() reload_with_split() model_cnn.compile( loss='categorical_crossentropy', optimizer='adadelta', metrics=['accuracy'] ) model_cnn.fit( X_train.reshape(4000,20,20,1), Y_train_one_hot, batch_size=1024, epochs=40, validation_data=(X_val.reshape(1000,20,20,1), Y_val_one_hot), verbose=1 )
Обучение этой модели приводит к повышению точности проверки:
Output: Train on 4000 samples, validate on 1000 samples Epoch 1/40 4000/4000 [==============================] - 7s 2ms/step - loss: 2.1836 - acc: 0.2563 - val_loss: 1.8490 - val_acc: 0.6260 ... Epoch 40/40 4000/4000 [==============================] - 0s 50us/step - loss: 0.0758 - acc: 0.9742 - val_loss: 0.1211 - val_acc: 0.9650
Попробуем классифицировать наши рукописные изображения:
def test_cnn_model(): num_px=20 for i in range(1,10): image = cv2.imread(str(i) + '.png', 0) image_rs = cv2.resize(image,(num_px,num_px)) # training images are grayscale and normalized image_rs = (255-image_rs)/255 image_rs = np.transpose(image_rs).reshape(1,20,20).reshape(1, 20,20, 1) my_predicted_number = model_cnn.predict(image_rs,batch_size=1, verbose=False) confidence_array = np.argsort(-my_predicted_number[0]) print('Real: '+ str(i) +', Predicted number:' + str(confidence_array[0]) + ' Also likely: ' + str(confidence_array[1]) + ' or ' + str(confidence_array[2])) test_cnn_model() Real: 1, Predicted number:1 Also likely: 8 or 6 Real: 2, Predicted number:2 Also likely: 3 or 8 Real: 3, Predicted number:3 Also likely: 5 or 2 Real: 4, Predicted number:4 Also likely: 9 or 8 Real: 5, Predicted number:5 Also likely: 3 or 8 Real: 6, Predicted number:5 Also likely: 0 or 6 Real: 7, Predicted number:3 Also likely: 7 or 2 Real: 8, Predicted number:8 Also likely: 5 or 3 Real: 9, Predicted number:4 Also likely: 9 or 8
Неплохо! Мы получили точность около 60% (90% с учетом 2-го и 3-го наивысших вероятностей). Давайте посмотрим, что мы сделали не так:
6 › Очень близко к 5 с 3-й догадкой, классифицирующей это как 6. Возможно, здесь нам нужно больше обучающих данных. То же самое для 7.
9 › Наш образ несколько ущербен. Изменение размера и преобразование в оттенки серого явно выбили из верхней части 9. Это действительно 4 в изображении!
Давайте попробуем получить больше обучающих данных и посмотрим, что у нас получится. В следующем разделе мы будем тренироваться со всем набором данных MNIST и посмотрим, сможем ли мы повысить точность.
Часть 5. Обучение с полным набором данных MNIST:
Следите в коллабе здесь. Скопировано дословно из здесь.
from __future__ import print_function import keras from keras.datasets import mnist from keras.models import Sequential from keras.layers import Dense, Dropout, Flatten from keras.layers import Conv2D, MaxPooling2D from keras import backend as K batch_size = 128 num_classes = 10 epochs = 10 # we get to reasonable accuracy (~98% train/val) with 3 epochs # input image dimensions img_rows, img_cols = 28, 28 # the data, split between train and test sets (x_train, y_train), (x_test, y_test) = mnist.load_data() if K.image_data_format() == 'channels_first': x_train = x_train.reshape(x_train.shape[0], 1, img_rows, img_cols) x_test = x_test.reshape(x_test.shape[0], 1, img_rows, img_cols) input_shape = (1, img_rows, img_cols) else: x_train = x_train.reshape(x_train.shape[0], img_rows, img_cols, 1) x_test = x_test.reshape(x_test.shape[0], img_rows, img_cols, 1) input_shape = (img_rows, img_cols, 1) x_train = x_train.astype('float32') x_test = x_test.astype('float32') x_train /= 255 x_test /= 255 print('x_train shape:', x_train.shape) print(x_train.shape[0], 'train samples') print(x_test.shape[0], 'test samples') # convert class vectors to binary class matrices y_train = keras.utils.to_categorical(y_train, num_classes) y_test = keras.utils.to_categorical(y_test, num_classes) model_mnist_full = Sequential() model_mnist_full.add(Conv2D(32, kernel_size=(3, 3), activation='relu', input_shape=input_shape)) model_mnist_full.add(Conv2D(64, (3, 3), activation='relu')) model_mnist_full.add(MaxPooling2D(pool_size=(2, 2))) model_mnist_full.add(Dropout(0.25)) model_mnist_full.add(Flatten()) model_mnist_full.add(Dense(128, activation='relu')) model_mnist_full.add(Dropout(0.5)) model_mnist_full.add(Dense(num_classes, activation='softmax')) model_mnist_full.compile(loss=keras.losses.categorical_crossentropy, optimizer=keras.optimizers.Adadelta(), metrics=['accuracy']) model_mnist_full.fit(x_train, y_train, batch_size=batch_size, epochs=epochs, verbose=1, validation_data=(x_test, y_test)) score = model_mnist_full.evaluate(x_test, y_test, verbose=0) print('Test loss:', score[0]) print('Test accuracy:', score[1])
Мы получаем точность около 99% как на обучающих, так и на проверочных наборах! Давайте посмотрим, как мы работаем с нашими данными почерка:
def test_model_mnist_full(): num_px=28 # model is trained with 28x28 image size for i in range(1,10): image = cv2.imread(str(i) + '.png', 0) image_rs = cv2.resize(image,(num_px,num_px)) # training images are grayscale and normalized image_rs = (255-image_rs)/255.0 image_rs = image_rs.reshape(1,28,28).reshape(1, 28,28, 1) my_predicted_number = model_mnist_full.predict(image_rs,batch_size=1, verbose=False) confidence_array = np.argsort(-my_predicted_number[0]) print('Real: '+ str(i) +', Predicted number:' + str(confidence_array[0]) + ' Also likely: ' + str(confidence_array[1]) + ' or ' + str(confidence_array[2])) test_model_mnist_full() Real: 1, Predicted number:1 Also likely: 8 or 6 Real: 2, Predicted number:2 Also likely: 1 or 8 Real: 3, Predicted number:3 Also likely: 5 or 8 Real: 4, Predicted number:4 Also likely: 9 or 8 Real: 5, Predicted number:5 Also likely: 3 or 8 Real: 6, Predicted number:6 Also likely: 5 or 8 Real: 7, Predicted number:7 Also likely: 2 or 3 Real: 8, Predicted number:8 Also likely: 2 or 3 Real: 9, Predicted number:4 Also likely: 9 or 8
Вуаля, точность 90%! (100% с учетом вторичного ответа и проблемы с нашим 9).
Мы прошли хорошее упражнение по пересмотру задания по программированию из замечательного курса, адаптации его к реальному варианту использования (ваши собственные данные почерка), повышению точности за счет большего количества обучающих данных, узнали, как легко создавать и прототипировать модели с помощью Keras. и, что более важно, получили удовольствие при этом! Надеюсь, вам понравилось учиться и экспериментировать так же, как и мне!
Учиться весело!