В этом руководстве мы собираемся взять необработанные изображения, которые уже были помечены для нас, а затем передать их сверточной нейронной сети для классификации.

На изображениях либо собака(и), либо кошка(и).

После того, как вы загрузили и извлекли данные с https://www.kaggle.com/c/dogs-vs-cats-redux-kernels-edition/data, вы готовы начать.

У нас есть данные, но мы не можем просто вставлять необработанные изображения прямо через нашу свёрточную нейронную сеть. Во-первых, нам нужно, чтобы все изображения были одинакового размера, а затем мы также, вероятно, захотим просто оттенить их оттенками серого. Кроме того, метки «кошка» и «собака» бесполезны, мы хотим, чтобы они были одноразовыми массивами.

Интересно, что мы можем приближаться к тому времени, когда наши данные, возможно, не должны быть одинакового размера. Глядя на исследовательский блог TensorFlow: https://research.googleblog.com/2017/02/announcing-tensorflow-fold-deep.html

«TensorFlow Fold упрощает реализацию моделей глубокого обучения, которые работают с данными разного размера и структуры».

Увлекательно… но пока мы будем делать это по старинке.

Требования к пакету numpy (pip install numpy) tqdm (pip install tqdm)

Я буду использовать версию TensorFlow для графического процессора вместе с tflearn.

Чтобы установить версию TensorFlow для процессора, просто выполните команду pip install tensorflow. Чтобы установить версию TensorFlow для графического процессора, вам нужно получить все зависимости и тому подобное.

Руководства по установке TensorFlow:



TensorFlow для Windows: https://www.youtube.com/watch?v=r7-WPbx8VuY

Использование TensorFlow и концептуальные руководства:

Введение в глубокое обучение с помощью нейронных сетей: https://pythonprogramming.net/neural-networks-machine-learning-tutorial

Введение в TensorFlow: https://pythonprogramming.net/tensorflow-introduction-machine-learning-tutorial/

Введение в сверточные нейронные сети: https://pythonprogramming.net/convolutional-neural-network-cnn-machine-learning-tutorial/

Учебное пособие по сверточной нейронной сети в TensorFlow: https://pythonprogramming.net/cnn-tensorflow-convolutional-nerual-network-machine-learning-tutorial/

Наконец, я буду использовать https://pythonprogramming.net/tflearn-machine-learning-tutorial/. После установки TensorFlow выполните pip install tflearn.

Во-первых, мы получим импорт и константы для предварительной обработки:

import cv2                 # working with, mainly resizing, images
import numpy as np         # dealing with arrays
import os                  # dealing with directories
from random import shuffle # mixing up or currently ordered data that might lead our network astray in training.
from tqdm import tqdm      # a nice pretty percentage bar for tasks. Thanks to viewer Daniel Bühler for this suggestion

TRAIN_DIR = '../input/train'
TEST_DIR = '../input/test'
IMG_SIZE = 50
LR = 1e-3

MODEL_NAME = 'dogsvscats-{}-{}.model'.format(LR, '2conv-basic') # just so we remember which saved model is which, sizes must match

Теперь наша первая задача — преобразовать изображения и метки в информацию массива, которую мы можем передать через нашу сеть. Для этого нам понадобится вспомогательная функция для преобразования имени изображения в массив.

Наши изображения помечены как «cat.1» или «dog.3» и так далее, поэтому мы можем просто разделить собаку/кошку, а затем преобразовать в массив следующим образом:

def label_img(img):
    word_label = img.split('.')[-3]
    # conversion to one-hot array [cat,dog]
    #                            [much cat, no dog]
    if word_label == 'cat': return [1,0]
    #                             [no cat, very doggo]
    elif word_label == 'dog': return [0,1]

Теперь мы можем создать еще одну функцию для полной обработки обучающих изображений и их меток в массивы:

def create_train_data():
    training_data = []
    for img in tqdm(os.listdir(TRAIN_DIR)):
        label = label_img(img)
        path = os.path.join(TRAIN_DIR,img)
        img = cv2.imread(path,cv2.IMREAD_GRAYSCALE)
        img = cv2.resize(img, (IMG_SIZE,IMG_SIZE))
        training_data.append([np.array(img),np.array(label)])
    shuffle(training_data)
    np.save('train_data.npy', training_data)
    return training_data

Один из моих зрителей познакомил меня с модулем tqdm, это действительно хороший, красивый способ измерить, где вы находитесь в процессе, а не распечатывать данные через определенные промежутки времени… и т. д., он дает индикатор выполнения. Супер аккуратно.

В любом случае, вышеприведенная функция преобразует данные для нас в данные массива изображения и его метки.

Когда мы просмотрим все изображения, мы их перемешиваем, а затем сохраняем. Shuffle изменяет переменную на месте, поэтому здесь нет необходимости переопределять ее.

С помощью этой функции мы и сохраним, и вернем данные массива. Таким образом, если мы просто изменим структуру нейронной сети, а не что-то с изображениями, например размер изображения и т. д., то мы можем просто загрузить файл массива и сэкономить время обработки. Пока мы здесь, мы могли бы также сделать функцию для обработки данных тестирования. Это фактические данные тестов соревнований, а НЕ данные, которые мы будем использовать для проверки точности нашего алгоритма во время тестирования. Эти данные не имеют метки.

def process_test_data():
    testing_data = []
    for img in tqdm(os.listdir(TEST_DIR)):
        path = os.path.join(TEST_DIR,img)
        img_num = img.split('.')[0]
        img = cv2.imread(path,cv2.IMREAD_GRAYSCALE)
        img = cv2.resize(img, (IMG_SIZE,IMG_SIZE))
        testing_data.append([np.array(img), img_num])
        
    shuffle(testing_data)
    np.save('test_data.npy', testing_data)
    return testing_data

Теперь мы можем запустить обучение:

train_data = create_train_data()
# If you have already created the dataset:
#train_data = np.load('train_data.npy')
100%|██████████| 25000/25000 [00:42<00:00, 591.72it/s]

Сверточная нейронная сеть

Далее мы готовы определить нашу нейронную сеть:

import tflearn
from tflearn.layers.conv import conv_2d, max_pool_2d
from tflearn.layers.core import input_data, dropout, fully_connected
from tflearn.layers.estimator import regression

convnet = input_data(shape=[None, IMG_SIZE, IMG_SIZE, 1], name='input')

convnet = conv_2d(convnet, 32, 5, activation='relu')
convnet = max_pool_2d(convnet, 5)

convnet = conv_2d(convnet, 64, 5, activation='relu')
convnet = max_pool_2d(convnet, 5)

convnet = fully_connected(convnet, 1024, activation='relu')
convnet = dropout(convnet, 0.8)

convnet = fully_connected(convnet, 2, activation='softmax')
convnet = regression(convnet, optimizer='adam', learning_rate=LR, loss='categorical_crossentropy', name='targets')

model = tflearn.DNN(convnet, tensorboard_dir='log')

Здесь у нас есть хорошая двухслойная сверточная нейронная сеть с полностью связанным слоем, а затем выходным слоем. Обсуждается, полезен ли полносвязный слой. все равно оставлю.

Эта точная сеть была достаточно хороша для распознавания рукописных цифр 28x28. Посмотрим, как обстоят дела с кошками и собаками в разрешении 50x50.

Теперь не всегда будет так, что вы тренируете сеть каждый раз заново. Может быть сначала вы просто хотите посмотреть, как тренируются 3 эпохи, но потом, после 3, может быть, вы закончили, а может, вы хотите увидеть около 5 эпох. Мы хотим сохранять нашу модель после каждого сеанса и перезагружать ее, если у нас есть сохраненная версия, поэтому я добавлю это:

if os.path.exists('{}.meta'.format(MODEL_NAME)):
    model.load(MODEL_NAME)
    print('model loaded!')

Теперь давайте разделим данные обучения и тестирования:

train = train_data[:-500]
test = train_data[-500:]

Теперь данные обучения и данные тестирования помечены как наборы данных. Обучающие данные — это то, что мы будем использовать для нейронной сети, а тестовые данные — это то, что мы собираемся использовать для проверки результатов. Тестовые данные будут «вне выборки», то есть данные тестирования будут использоваться только для проверки точности сети, а не для ее обучения.

У нас также есть «тестовые» изображения, которые мы скачали. ЭТИ изображения вообще не помечены, и именно их мы отправим на Kaggle для участия в конкурсе.

Далее мы собираемся создать наши массивы данных. По какой-то причине типичная логика numpy, например:

array[:,0] и array[:,1] НЕ работали у меня здесь. Не уверен, что я делаю неправильно, поэтому вместо этого я делаю это, чтобы разделить свои функции и метки:

X = np.array([i[0] for i in train]).reshape(-1,IMG_SIZE,IMG_SIZE,1)
Y = [i[1] for i in train]
test_x = np.array([i[0] for i in test]).reshape(-1,IMG_SIZE,IMG_SIZE,1)
test_y = [i[1] for i in test]

Теперь укладываемся на 2 эпохи:

model.fit({'input': X}, {'targets': Y}, n_epoch=2, validation_set=({'input': test_x}, {'targets': test_y}), 
    snapshot_step=50000, show_metric=True, run_id=MODEL_NAME)
Training Step: 765  | total loss: 11.61817 | time: 122.757s
| Adam | epoch: 002 | loss: 11.61817 - acc: 0.4954 -- iter: 24448/24500
Training Step: 766  | total loss: 11.57166 | time: 124.075s
| Adam | epoch: 002 | loss: 11.57166 - acc: 0.4974 | val_loss: 11.65108 - val_acc: 0.4940 -- iter: 24500/24500
--

Хм... не похоже, что мы вообще куда-то продвинулись.

Мы могли бы продолжать попытки, но, если вы не добились прогресса в точности в первые 3 эпохи, вы, вероятно, вообще этого не сделаете, если только это не связано с переоснащением ... по крайней мере, по моему опыту.

Ну что теперь?

Размер имеет значение

Нам понадобится большая сеть

Во-первых, нам нужно сбросить экземпляр графа, так как мы делаем это в непрерывной среде:

import tensorflow as tf
tf.reset_default_graph()
convnet = input_data(shape=[None, IMG_SIZE, IMG_SIZE, 1], name='input')
convnet = conv_2d(convnet, 32, 5, activation='relu')
convnet = max_pool_2d(convnet, 5)
convnet = conv_2d(convnet, 64, 5, activation='relu')
convnet = max_pool_2d(convnet, 5)
convnet = conv_2d(convnet, 32, 5, activation='relu')
convnet = max_pool_2d(convnet, 5)
convnet = conv_2d(convnet, 64, 5, activation='relu')
convnet = max_pool_2d(convnet, 5)

convnet = conv_2d(convnet, 32, 5, activation='relu')
convnet = max_pool_2d(convnet, 5)

convnet = conv_2d(convnet, 64, 5, activation='relu')
convnet = max_pool_2d(convnet, 5)

convnet = fully_connected(convnet, 1024, activation='relu')
convnet = dropout(convnet, 0.8)

convnet = fully_connected(convnet, 2, activation='softmax')
convnet = regression(convnet, optimizer='adam', learning_rate=LR, loss='categorical_crossentropy', name='targets')

model = tflearn.DNN(convnet, tensorboard_dir='log')



if os.path.exists('{}.meta'.format(MODEL_NAME)):
    model.load(MODEL_NAME)
    print('model loaded!')

train = train_data[:-500]
test = train_data[-500:]
X = np.array([i[0] for i in train]).reshape(-1,IMG_SIZE,IMG_SIZE,1)
Y = [i[1] for i in train]

test_x = np.array([i[0] for i in test]).reshape(-1,IMG_SIZE,IMG_SIZE,1)
test_y = [i[1] for i in test]

model.fit({'input': X}, {'targets': Y}, n_epoch=4, validation_set=({'input': test_x}, {'targets': test_y}), 
    snapshot_step=500, show_metric=True, run_id=MODEL_NAME)
Training Step: 1531  | total loss: 0.46830 | time: 106.652s
| Adam | epoch: 004 | loss: 0.46830 - acc: 0.7910 -- iter: 24448/24500
Training Step: 1532  | total loss: 0.45849 | time: 107.952s
| Adam | epoch: 004 | loss: 0.45849 - acc: 0.7947 | val_loss: 0.52884 - val_acc: 0.7240 -- iter: 24500/24500
--

НУ НУ НУ... Похоже, у нас есть победитель. С нейронными сетями размер имеет огромное значение. Мы перешли от явно неподдающихся обучению данных к явно поддающимся обучению данным, и это было всего 3 эпохи.

Если вы довольны моделью, сохраните ее:

In [13]:

model.save(MODEL_NAME)
INFO:tensorflow:/kaggle/working/dogsvscats-0.001-2conv-basic.model is not in all_model_checkpoint_paths. Manually adding it.

Теперь мы можем перезагрузить модель и продолжить обучение (здесь нам НЕ НУЖНО перезагружать модель, так как это непрерывно, и модель все еще находится в памяти, но если бы вы запускали это как программу, вы бы это сделали)

код ссылки

Вы можете быть слишком большим

Больше не всегда лучше, есть предел, по крайней мере, исходя из моего опыта. Более крупная сеть лучше и быстрее определяет ситуацию, но имеет тенденцию к переобучению обучающих данных. Вы можете использовать отсев (случайно устанавливает определенный процент узлов, которые не участвуют в сети для более надежных сетей), чтобы немного исправить это, но, похоже, есть предел.

Хорошо, что теперь? Посмотрим, как у нас получилось!

import matplotlib.pyplot as plt

# if you need to create the data:
test_data = process_test_data()
# if you already have some saved:
#test_data = np.load('test_data.npy')

fig=plt.figure()

for num,data in enumerate(test_data[:12]):
    # cat: [1,0]
    # dog: [0,1]
    
    img_num = data[1]
    img_data = data[0]
    
    y = fig.add_subplot(3,4,num+1)
    orig = img_data
    data = img_data.reshape(IMG_SIZE,IMG_SIZE,1)
    #model_out = model.predict([data])[0]
    model_out = model.predict([data])[0]
    
    if np.argmax(model_out) == 1: str_label='Dog'
    else: str_label='Cat'
        
    y.imshow(orig,cmap='gray')
    plt.title(str_label)
    y.axes.get_xaxis().set_visible(False)
    y.axes.get_yaxis().set_visible(False)
    plt.show()
100%|██████████| 12500/12500 [00:24<00:00, 510.43it/s]

Итак, мы допустили пару ошибок, но на самом деле не так уж и плохо!

Если вас это устраивает, давайте соревноваться!

with open('submission_file.csv','w') as f:
    f.write('id,label\n')
            
with open('submission_file.csv','a') as f:
    for data in tqdm(test_data):
        img_num = data[1]
        img_data = data[0]
        orig = img_data
        data = img_data.reshape(IMG_SIZE,IMG_SIZE,1)
        model_out = model.predict([data])[0]
        f.write('{},{}\n'.format(img_num,model_out[1]))
100%|██████████| 12500/12500 [00:59<00:00, 211.11it/s]