Цели

· Что такое классификация изображений?

· Как это работает?

· Как это делается в Python?

· Почему это важно?

Введение

Tensor Flow 2.0 - это бесплатная библиотека программного обеспечения с открытым исходным кодом с множеством действительно интересных API для выполнения различных задач. Одним из них является классификация изображений.

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

Для наших целей мы собираемся использовать наборы данных для обучения и тестирования, найденные на Kaggle (https://www.kaggle.com/puneet6060/intel-image-classification), предоставленные Intel. Эти наборы данных включают тысячи изображений шести различных категорий: здания, леса, ледники, горы, моря и улицы. Мы собираемся использовать эти наборы данных, чтобы увидеть, сможем ли мы построить точную модель, чтобы правильно маркировать изображения в одной из шести категорий. Прежде всего, давайте немного углубимся в концепцию того, как классификация изображений работает в TF.

Как работает классификация изображений в TF

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

В конце концов, изображение - это просто матрица чисел, определяющая его особенности. Вы хотите иметь возможность разбить эти функции в формате, подходящем для нашей модели. Таким образом, первая часть построения модели CNN - это извлечь и понять эти особенности ваших изображений с помощью операции свертки. Эта операция работает путем создания матрицы фильтра (также известной как ядро) определенного размера (обычно меньше размера матрицы изображения), которая перемещается по матрице изображения и инкапсулирует часть функций вашего изображения. Он выполняет операцию умножения матриц для охваченных функций и выводит результат в свернутую функцию (которая представляет собой другую матрицу, которая содержит все выходные данные), которая может быть меньше, больше или того же размера, что и матрица вашего изображения (просто зависит от того, какой размер фильтр есть). Затем фильтр перемещается по остальной части изображения и повторяет этот процесс до тех пор, пока он не сможет полностью покрыть матрицу изображения и свернутая матрица признаков не заполнится. После того, как этот процесс выполнен для одного изображения, он переходит к следующему и строит другую свернутую матрицу признаков. В итоге вы получаете еще один слой нейронной сети, содержащий информацию об особенностях ваших изображений (например, края, линии, цвета и т. Д.). Вы можете использовать операции свертки несколько раз с разными размерами ядра, чтобы извлекать различные уровни информации о функциях, тем самым создавая несколько слоев свертки. Это может быть трудно понять просто с помощью слов, но вот анимация, которая показывает процесс на небольшой матрице изображения (желтый - это фильтр, проходящий через зеленую матрицу изображения):

В дополнение к слоям свертки, CNN также, как правило, имеют слои объединения, которые уменьшают размер ранее созданных свернутых функций. Прохождение модели через каждую созданную свернутую функцию для каждого изображения может быть очень затратным с точки зрения вычислений, особенно если свернутые функции имеют большой размер. Чтобы учесть это, мы можем использовать уровни объединения, чтобы уменьшить размерность, тем самым уменьшив мощность, необходимую для обработки данных. По сути, мы указываем размер фильтра, который проходит через свернутую матрицу признаков таким же образом, как и операция свертки, но на этот раз нет перекрывающихся мест, где фильтр зависает, и вместо того, чтобы брать результат умножения матрицы, он принимает либо объединенное среднее значение свернутых функций, либо максимальное значение и помещает его в новую матрицу указанного размера. Вы делаете это для каждого изображения и создаете новый слой в своей нейронной сети. Таким образом, вы меняете специфику характеристик изображения в пользу менее затратной в вычислительном отношении модели. Каждый раз, когда вы создаете сверточный слой, вы можете создать объединенный слой, который будет следовать за ним и уменьшить его размерность. Опять же, вот изображение, которое поможет понять этот процесс:

После процесса свертывания функций вашего изображения, объединения их, свертывания этих функций, объединения их и т. Д. Вы, наконец, можете достичь точки, в которой, по вашему мнению, вы преобразовали матрицы входного изображения в размер, который инкапсулирует извлечение таких функций, как возможно, но без излишних вычислительных затрат. В этот момент вам нужно сгладить ваши матрицы (это делается путем определения сглаженного слоя) до одного вектора-столбца, чтобы мы могли передать его через нейронную сеть с прямой связью и применить обратное распространение через каждую итерацию обучения (выполнено через указание плотного слоя; будет показано в коде). Обратное распространение - это метод обучения нейронных сетей с использованием метода градиентного спуска. При обучении модели вы также указываете количество эпох, через которые должны пройти ваши данные. Одна эпоха означает, что ваша модель тренирует каждую часть информации в ваших данных только один раз. Если вы увеличите его до двух, модель обучит ваши данные один раз, затем обновит веса различных функций в зависимости от того, как они работали в первую эпоху, а затем обучит данные вашей обновленной модели. Чтобы точно применить веса, вы хотите прогнать свою модель по серии эпох, чтобы эффективно различать важные и не очень важные особенности.

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

Процесс в Python

В этом проекте я использую Python через Google Colab. Github с кодом находится здесь: https://github.com/brh2hd/Image-Classification-with-Tensor-Flow

Как мы упоминали ранее, наборы данных включают тысячи изображений зданий, лесов, ледников, гор, морей и улиц. Обозначим их как 0, 1, 2, 3, 4 и 5 соответственно. К сожалению, zip-файлы слишком велики для того, чтобы загружать их на Github, поэтому вы можете скачать их прямо с сайта Kaggle, с которого я их получил: https://www.kaggle.com/puneet6060/intel-image-classification . В этом посте я использую только набор данных поезда, так как я разбил его на поезд и набор проверки для моделирования.

Вы хотите скачать zip-архив, но не распаковывайте его на локальный диск. Сделайте это в Google Colab вот так:

#Uploading zip file
from google.colab import files
uploaded = files.upload()
#Unzipping images
!unzip seg_train.zip

После импорта пакетов и распаковки изображений вы заметите, что все изображения находятся в отдельных папках в зависимости от класса. Я импортировал каждый класс изображений отдельно и очищал их отдельно, удаляя все значения «Нет». Все фотографии были пронумерованы странно (т.е. первое изображение в папке для зданий было 4, затем 9, затем 22 и т. Д.). Здесь я покажу очистку и обработку одного из классов изображений, остальные находятся на гитхабе.

#Importing all images of buildings, appending them, and removing none values
Buildings = []
for i in range(1,20055):
    Buildings.append(cv.imread("seg_train/buildings/" + str(i) + ".jpg"))

Buildings = np.array(Buildings) 
Buildings2 = [x for x in Buildings if x is not None]
#Creating a Label for each of the building images
Buildings_Labels = np.repeat(0,2190)
print(len(Buildings_Labels))
print(Buildings_Labels)

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

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

#Creating a Label for each of the building images
Buildings_Labels = np.repeat(0,2190)
print(len(Buildings_Labels))
print(Buildings_Labels)

Вы повторяете этот процесс для каждого из других классов изображений (код на Github), а затем объединяете их все вместе (изображения и метки).

#Combining all the labels into one array
Labels = np.concatenate([Buildings_Labels,Forests_Labels,Glaciers_Labels,Mountains_Labels,Seas_Labels,Streets_Labels])

Также давайте определим, что обозначают ярлыки.

#Defining what are labels stand for
def get_classlabel(class_code):
    labels = {2:'glacier', 4:'sea', 0:'buildings', 1:'forest', 5:'street', 3:'mountain'}
    
    return labels[class_code]

Затем давайте построим случайное подмножество из 25 всех изображений после того, как они были объединены.

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

#Our images need to be resized in their array form in order to be used by our models
print("Shape of Images:",Images.shape)
print("Shape of Labels:",Labels.shape)
Shape of Images: (14028,)
Shape of Labels: (14028,)

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

#Resizing all our images
Images2 = []
for i in range(0,14028):
  image = cv.resize(Images[i],(150,150))
  Images2.append(image)
Images3 = np.array(Images2)
#Suitable size
Images3.shape
  
(14028, 150, 150, 3)

Теперь, когда наши данные можно использовать, давайте попробуем моделировать. Первым шагом в моделировании сверточной нейронной сети является определение всех операций свертки с использованием функции Layers.Conv2D и наших уровней объединения с использованием функции Layers.MaxPooling2D. Мы используем максимальный пул по сравнению со средним пулом. В функции Layers.Conv2D мы указываем сначала наши входные числа, затем размер нашей матрицы фильтров, функцию активации, используемую для классификации (ReLu обычно используется до конца), а затем форму наших входных данных. В функции объединения все, что мы указываем, - это размер фильтра объединения.

#First atttempt at layering
model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(150, 150, 3)))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))

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

model.summary()
Model: "sequential_2"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_6 (Conv2D)            (None, 148, 148, 32)      896       
_________________________________________________________________
max_pooling2d_4 (MaxPooling2 (None, 74, 74, 32)        0         
_________________________________________________________________
conv2d_7 (Conv2D)            (None, 72, 72, 64)        18496     
_________________________________________________________________
max_pooling2d_5 (MaxPooling2 (None, 36, 36, 64)        0         
_________________________________________________________________
conv2d_8 (Conv2D)            (None, 34, 34, 64)        36928     
=================================================================
Total params: 56,320
Trainable params: 56,320
Non-trainable params: 0
_________________________________________________________________

Следующий шаг - сгладить каждую из наших матриц изображений до одного вектора-столбца (с помощью функции сглаживания) и пропустить их через нейронную сеть с обратным распространением (с плотной функцией). Обратите внимание, что для последнего слоя мы используем другую функцию активации. Softmax лучше всего использовать для последнего слоя. Здесь лучше всего работает другой алгоритм классификатора.

#Flattening our matrices
model.add(layers.Flatten())
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(6, activation='softmax'))

Вот обновленная сводка модели:

model.summary()
Model: "sequential_2"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_6 (Conv2D)            (None, 148, 148, 32)      896       
_________________________________________________________________
max_pooling2d_4 (MaxPooling2 (None, 74, 74, 32)        0         
_________________________________________________________________
conv2d_7 (Conv2D)            (None, 72, 72, 64)        18496     
_________________________________________________________________
max_pooling2d_5 (MaxPooling2 (None, 36, 36, 64)        0         
_________________________________________________________________
conv2d_8 (Conv2D)            (None, 34, 34, 64)        36928     
_________________________________________________________________
flatten_2 (Flatten)          (None, 73984)             0         
_________________________________________________________________
dense_4 (Dense)              (None, 64)                4735040   
_________________________________________________________________
dense_5 (Dense)              (None, 6)                 390       
=================================================================
Total params: 4,791,750
Trainable params: 4,791,750
Non-trainable params: 0
_________________________________________________________________

Затем оптимизируем наши параметры. В качестве показателя тестирования мы используем точность.

#Optimization Parameters
model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

Наконец, мы обучаем нашу модель предсказанию надписей для изображений. Здесь мы используем только 10 эпох из-за вычислительных затрат, и мы установили разделение валидации на 0,30. С разделением валидации каждая эпоха, по сути, уже имеет набор поездов и тестов.

#Training our model with a validation split
trained = model.fit(Images3,Labels,epochs=10,validation_split=0.30)
Train on 9819 samples, validate on 4209 samples
Epoch 1/10
9819/9819 [==============================] - 344s 35ms/sample - loss: 1.3712 - accuracy: 0.4887 - val_loss: 10.3954 - val_accuracy: 0.0000e+00

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

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

model2 = models.Sequential()
model2.add(layers.Conv2D(200, (3, 3), activation='relu', input_shape=(150, 150, 3)))
model2.add(layers.Conv2D(180,kernel_size=(3,3),activation='relu'))
model2.add(layers.MaxPooling2D((5, 5)))
model2.add(layers.Conv2D(180,kernel_size=(3,3),activation='relu'))
model2.add(layers.Conv2D(140,kernel_size=(3,3),activation='relu'))
model2.add(layers.Conv2D(100,kernel_size=(3,3),activation='relu'))
model2.add(layers.Conv2D(50,kernel_size=(3,3),activation='relu'))
model2.add(layers.MaxPool2D(5,5))

model2.add(layers.Flatten())
model2.add(layers.Dense(180, activation='relu'))
model2.add(layers.Dense(100,activation='relu'))
model2.add(layers.Dense(50,activation='relu'))
model2.add(layers.Dropout(rate=0.5))
model2.add(layers.Dense(6, activation='softmax'))

model2.compile(optimizer=Optimizer.Adam(lr=0.0001),loss='sparse_categorical_crossentropy',metrics=['accuracy'])

model2.summary()

Из-за вычислительных затрат я просто проведу 3 эпохи.

trained2 = model.fit(Images4,Labels2,epochs=3,validation_split=0.30)

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

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

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

Заключение

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

Так почему же так важна классификация изображений? Это просто для того, чтобы я мог различать изображения кошек и собак? Хотя мы рассмотрели простую его версию, классификация изображений может быть очень сложной и выполнять множество сложных задач. Думаю об этом. Когда вы пытаетесь разблокировать свой iPhone X с помощью функции распознавания лиц, что он делает? Он классифицирует изображение вашего лица по ярлыку, что это вы. Вы когда-нибудь загружали изображение на Facebook, и оно дает вам возможность отметить человека на лице? Как он распознает, что небольшая часть изображения - это лицо? Правильно, классификация изображений (более сложная, но все же есть). Куда бы вы ни пошли, будет какая-то классификация изображений; будь то ваши телефоны, спутники, компьютеры и т. д. Он стал неотъемлемой частью нашего общества, и изучение только его основ может быть очень полезным.

Ссылки:

















Https://www.kaggle.com/uzairrj/beg-tut-intel-image-classification-93-76-accur/data

📝 Прочтите этот рассказ позже в Журнале.

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