A / N: Эта статья предполагает хорошее понимание архитектуры сверточной нейронной сети. Для предыстории, зацените это.

A / N: Код, используемый в этой статье, основан на туториале l от Sentdex. Приветствуется базовое понимание Кераса.

Вы кошка 🐱 или собака 🐶 человек?

Для некоторых из нас мы твердо стоим на одной стороне этого вопроса. Кошки милые, подвижные и независимые пушистые комочки, а собаки игривые, представительные и оборонительные! A̵l̵s̵o̵, ̵ ̵t̵h̵e̵y̵’̵r̵e̵ ̵w̵a̵y̵ ̵b̵e̵t̵t̵e̵r̵ ̵😉̵.̵

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

А теперь представьте, что вы копируете этот процесс на компьютер. Я попытался это сделать, но тут же наткнулся на препятствия.

Логика кажется проверенной, но эта программа явно не надежна. Как нам искать усы? Как нам искать хвост? Для нас это легко, но как заставить компьютер сканировать эти функции?

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

Так зачем использовать обычный код?

С глубоким обучением в этом нет необходимости.

Сверточные нейронные сети упрощают этот процесс.

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

С помощью CNN я уменьшил это число до 70.

Давайте сначала освежимся интуицией, лежащей в основе CNN.

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

Используя искусственный интеллект через CNN, мы преодолеваем эту проблему, позволяя им учиться! Наша нейронная сеть учится на данных, предоставляемых нашим набором данных. Итеративно корректируя значения ядра для уменьшения потерь, наша модель учится распознавать особенности изображений. Обнаруженные особенности из входных изображений помогают сети предсказать, из каких животных состоит изображение. Так, например, если на изображении были обнаружены усы, наша сверточная нейронная сеть, скорее всего, предскажет, что смотрит на кошку.

А теперь давайте построим это!

Для начала воспользуемся набором данных Кошки и собаки от Kaggle. Я скачал его как папку PetImages. Он состоит из 12499 фотографий собак и кошек каждая.

Теперь нам нужно сделать эти изображения доступными для нашего будущего кода (для нашей CNN).

Загрузка и предварительная обработка данных

Во-первых, давайте импортируем следующие библиотеки.

import numpy as np
import os
import cv2

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

DATADIR = “/Users/joshua/Desktop/PetImages”
CATEGORIES = [“Dog”, “Cat”]

IMG_SIZE - это размер фотографий, до которых они будут уменьшены. Наши обучающие данные будут представлять собой список фотографий с измененным размером, представленных в пикселях, вместе с их классом (кошка или собака) в форме 0 или 1.

IMG_SIZE = 50
training_data = []

Теперь давайте заставим функцию загружать и предварительно обрабатывать наши данные.

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

def create_training_data():
    for category in CATEGORIES:
        path = os.path.join(DATADIR, category)
        class_num = CATEGORIES.index(category)
        for img in os.listdir(path):
            try:
                img_array = cv2.imread(os.path.join(path, img),                         cv2.IMREAD_GRAYSCALE)
                new_array = cv2.resize(img_array, (IMG_SIZE, IMG_SIZE))
                training_data.append([new_array, class_num])
            except Exception as e:
                pass

Давайте разберемся, как работает create_training_data ().

for category in CATEGORIES:
        path = os.path.join(DATADIR, category)
        class_num = CATEGORIES.index(category)

Для каждой категории «Собака» и «Кошка» определены переменные path и class_num.

С 'path = os.path.join (DATADIR, category)' функция 'os.path.join' объединяет обе предоставленные строки в один путь к каталогу файлов. . В моем случае "/ Users / joshua / Desktop / PetImages" и "Cat" или "Dog" объединены. Это выведет:

/Users/joshua/Desktop/PetImages/Dog
/Users/joshua/Desktop/PetImages/Cat

Теперь это создает прямой путь к двум подпапкам, Dog и Cat, в нашем наборе данных PetImages, поэтому наш код может получить доступ к обеим категориям фотографий!

Теперь в этот цикл for вложен еще один цикл for. Это означает, что этот цикл выполняется дважды, один раз как для данных в подпапке Dog, так и для подпапки Cat.

Наш код зацикливается на каждой фотографии внутри каждой подпапки. То есть каждая фотография собаки или каждая фотография кошки.

for img in os.listdir(path):

Для каждой из этих фотографий мы пытаемся сделать их массивом (img_array) в виде изображения в градациях серого (cv2.IMREAD_GRAYSCALE ).

Функция ‘os.path.join’ снова используется для доступа к каждому фрагменту данных в подпапке. ‘/ Users / joshua / Desktop / PetImages / Cat / 1’ получит доступ к первой фотографии кошки (1.jpg).

Затем размер массива изображений изменяется в соответствии с размерами нашей переменной IMG_SIZE из предыдущего (IMG_SIZE = 50, поэтому фотография становится 50 на 50). Затем этот массив new_array добавляется к нашим обучающим данным вместе с номером его класса, определенным в нашем первом цикле for. Помните, что этот номер класса (class_num) указывает, является ли фотография собакой или кошкой, 0 или 1.

Если мы сталкиваемся с файлом, который нельзя добавить к нашим обучающим данным, мы игнорируем его (pass).

try:
                img_array = cv2.imread(os.path.join(path, img),                         cv2.IMREAD_GRAYSCALE)
                new_array = cv2.resize(img_array, (IMG_SIZE, IMG_SIZE))
                training_data.append([new_array, class_num])
except Exception as e:
                pass

Давайте теперь свяжем все вместе.

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

Теперь мы можем вызвать нашу функцию, чтобы фактически создать наши обучающие данные, запустив код внутри нее.

create_training_data()

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

Чтобы этого избежать, нам нужно перемешать наши обучающие данные.

import random
random.shuffle(training_data)

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

Элементы нашего списка обучающих данных разделены массивом изображений и номером класса. Массив соответствует функциям, а номер класса - это метка. Мы сортируем каждый элемент training_data и добавляем их функции и метки к X и y.

X = []
y = []
for features, label in training_data:
    X.append(features)
    y.append(label)

Затем преобразование X и y в массивы numpy и изменение формы X:

X = np.array(X).reshape(-1, IMG_SIZE, IMG_SIZE, 1)
y = np.array(y)

Чтобы получить доступ к этим переменным из разных программ, мы можем сохранить их и их данные как файлы pickle.

import pickle
pickle_out = open(“X.pickle”, “wb”)
pickle.dump(X, pickle_out)
pickle_out.close()
pickle_out = open(“y.pickle”, “wb”)
pickle.dump(y, pickle_out)
pickle_out.close()

Теперь у нас загружен весь наш набор данных, представленный переменными X и y для, соответственно, их функций и меток . Эти списки были сохранены на нашем компьютере в виде файлов рассола (X.pickle, y.pickle).

Создание CNN

Хорошо! Теперь, когда все наши данные предварительно обработаны и готовы к работе, давайте создадим нашу настоящую нейронную сеть!

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

import tensorflow as tf
from tensorflow.keras.datasets import cifar10
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout, Activation, Flatten, Conv2D, MaxPooling2D
import pickle

После этого, чтобы использовать наши данные, загрузим файлы рассола X и y. (rb относится к чтению файлов)

X = pickle.load(open(“X.pickle”, “rb”))
y = pickle.load(open(“y.pickle”, “rb”))

Для дальнейшей предварительной обработки мы разделим наши характеристики (значения пикселей) на 255, чтобы получить значения только между 0 и 1.

X = X/255.0

Теперь мы можем приступить к созданию нашей нейронной сети! Мы будем использовать последовательную модель.

model = Sequential()

Затем мы добавим наши первые сверточные и объединяющие слои:

model.add(Conv2D(64, (3,3), input_shape = X.shape[1:]))
model.add(Activation(“relu”))
model.add(MaxPooling2D(pool_size = (2, 2)))

Мы используем 64 ядра, каждое с размерами 3 на 3. Наш вход - это наши функции, X, потому что мы учим нашу нейронную сеть отображать их на метку y.

Затем они проходят активацию ReLU, а затем объединяются в максимальное количество для каждого локального рецептивного поля 2x2 в картах функций.

Это будет повторяться еще два раза, потому что после отдельной оптимизации модели это привело к более высокой точности. Следите за новостями, чтобы в будущем увидеть статьи по оптимизации и поиску нейронной архитектуры 😉.

for l in range(2):
    model.add(Conv2D(64 , (3,3)))
    model.add(Activation(“relu”))
    model.add(MaxPooling2D(pool_size = (2, 2)))

Теперь мы сведем наши объединенные карты в один слой, который будет использоваться в нашем плотно связанном слое. Затем эти нейроны подвергаются активации ReLU.

model.add(Flatten())
model.add(Dense(64))
model.add(Activation(“relu”))

А теперь наш выходной слой, который состоит из последнего нейрона! Это представляет собой предсказание того, что входное изображение будет кошкой или собакой.

model.add(Dense(1))
model.add(Activation(‘sigmoid’))

Давайте скомпилируем нашу модель и подгоним ее под нашу функцию и отметим данные. Мы используем пакет размером 32, чтобы передавать 32 выборки данных за раз. Кроме того, validation_split представляет 30% наших данных, которые разделяются на данные тестирования.

model.compile(loss = “binary_crossentropy”, 
    optimizer = “adam”, 
    metrics = [‘accuracy’])
model.fit(X, y, batch_size=32, epochs=10, validation_split = 0.3)

И сэкономьте!

model.save(‘64x3-CNN.model’)

Вот как выглядит вся модель:

import tensorflow as tf
from tensorflow.keras.datasets import cifar10
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout, Activation, Flatten, Conv2D, MaxPooling2D
from tensorflow.keras.callbacks import TensorBoard
import pickle
X = pickle.load(open(“X.pickle”, “rb”))
y = pickle.load(open(“y.pickle”, “rb”))
X = X/255.0
model = Sequential()
model.add(Conv2D(64, (3,3), input_shape = X.shape[1:]))
model.add(Activation(“relu”))
model.add(MaxPooling2D(pool_size = (2, 2)))
for l in range(2):
 model.add(Conv2D(64 , (3,3)))
 model.add(Activation(“relu”))
 model.add(MaxPooling2D(pool_size = (2, 2)))
model.add(Flatten())
model.add(Dense(64))
model.add(Activation(“relu”))
model.add(Dense(1))
model.add(Activation(‘sigmoid’)) # softmax??
model.compile(loss = “binary_crossentropy”, 
 optimizer = “adam”, 
 metrics = [‘accuracy’])
model.fit(X, y, batch_size=32, epochs=10, validation_split = 0.3)
model.save(‘64x3-CNN.model’)

И… беги! Вот мои последние показатели после 10 эпох.

loss: 0.2443 — accuracy: 0.8954 — val_loss: 0.4773 — val_accuracy: 0.8051

Увидеть нашу сеть в действии!

Мы создали свой CNN, но пока не видели, чтобы он что-то предсказывал. Давайте сделаем это сейчас и посмотрим на нашу модель на практике!

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

Сначала мы импортируем эти библиотеки:

import cv2
import tensorflow as tf

Прогноз нашей модели представлен как 0 или 1, поэтому мы создадим эту переменную, чтобы позже помочь нам преобразовать это число прогноза в «Кошка» или «Собака».

CATEGORIES = ["Dog", "Cat"]

Эта функция предварительно обработает наше изображение, чтобы оно подходило для нашей ConvNet.

def prepare(filepath):
    IMG_SIZE = 50
    img_array = cv2.imread(filepath, cv2.IMREAD_GRAYSCALE)
    new_array = cv2.resize(img_array, (IMG_SIZE, IMG_SIZE)) 
    return new_array.reshape(-1, IMG_SIZE, IMG_SIZE, 1)

Теперь мы загрузим нашу модель, сохраненную при обучении ранее:

model = tf.keras.models.load_model(“64x3-CNN.model”)

Давайте теперь воспользуемся нашей моделью, чтобы предсказать, что это за изображение! Мы будем использовать нашу функцию prepare для нашей фотографии, и наша модель предскажет, какое это животное, в виде 0 или 1. Это значение будет сохранено в нашей переменной прогноза.

prediction = model.predict([prepare('dog.jpg')])

Наша прогнозирующая переменная представляет собой двумерный массив, поэтому, чтобы получить значение 0 или 1, мы будем использовать это.

prediction[0][0]

Результатом будет 0, но мы не можем напрямую сказать нам, является ли фотография кошкой или собакой. Давайте исправим это с помощью нашей предыдущей переменной «CATEGORIES». Мы говорим нашему коду напечатать элемент в списке с индексом 0.

print(CATEGORIES[int(prediction[0][0])])

Это выводит "Dog"! Это означает, что наш код успешно распознан dog.jpg!

Вот и все! Это весь код, который нам нужен, чтобы использовать возможности ИИ. Мы научили компьютер распознавать кошек и собак по иностранным фотографиям с точностью 80% в 70 строках кода. Несмотря на то, что в начале статьи это казалось невозможным, нам все равно удалось!

Но это далеко не все для приложения CNN - представьте, как мы могли бы масштабировать эту технологию для еще большего важных задач! Распознавание преступников по видеозаписям, обнаружение аномалий в органах - этот список можно продолжать.

Разрешив компьютерам распознавать и классифицировать изображения, мы открыли дверь к безграничному количеству решений реальных проблем. Люди только начинают находить области для использования сверточных нейронных сетей - где еще это возможно?

Ключевые выводы

  • CNN упрощает и обеспечивает классификацию цифровых изображений
  • Обучив нашу модель на наборе данных "Кошки и собаки", мы научили ее различать эти два
  • Tensorflow и Keras позволяют лаконично программировать нейронную сеть.
  • Мы достигли точности валидации 80%!

Если вам понравилась эта статья, не стесняйтесь обращаться ко мне в LinkedIn или по адресу [email protected]. Спасибо за внимание!