Обычно мы используем MNIST или CIFAR-10 в качестве вводных учебников для классификации изображений с использованием глубокого обучения, потому что данные подготавливаются другими, а некоторые даже загружают все данные в одну функцию, поэтому ее очень просто запустить.Но после запуска, кажется, что они не освоили весь процесс классификации изображений, потому что не прошли стадию обработки данных, поэтому нельзя говорить о процессе реализации классификации глубокого обучения. Сегодня я хотел бы поделиться с вами двумя более близкими к реальности классификационными проектами, начиная с анализа и обработки данных и заканчивая использованием Keras в качестве инструмента для тщательного освоения задач классификации изображений.
Два проекта классификации: классификация дорожных знаков и классификация счетов. Классификация дорожных знаков имеет приложения в проектах без водителя или связанных с дорожным движением, а задача классификации билетов более актуальна для жизни.В то же время этот проект также является подзадачей большого проекта, которым я сейчас занимаюсь. Эти две задачи по классификации являются очень практичными проектами. Я надеюсь, что после этих двух практических задач я смогу освоить инструмент Keras и создать общую основу для классификации изображений. В будущем я также смогу заняться другими проектами по классификации изображений.
Давайте сначала поговорим о среде конфигурации:
- Python 3.5
- Keras==2.0.1, бэкэнд TesensorFlow, обучение ЦП
1. Классификация дорожных знаков
Первый — посмотреть на данные, чтобы увидеть, сколько типов дорожных знаков мы хотим распознать и сколько изображений каждого типа. Открыв его, этот набор данных о дорожных знаках помог нам разделить обучающий набор и набор данных.
Имя каждой папки является ее меткой.
Количество изображений логотипов каждой категории от десятков до десятков, что является небольшим набором данных, а общее количество категорий составляет 62.
Затем мы начинаем использовать Keras в качестве инструмента для создания общей основы для классификаторов изображений.
Создание CNN
Сеть, которая использует глубокое обучение для классификации изображений, должна быть сверточной нейронной сетью, но сейчас существует так много типов CNN, какой из них лучше всего справится с нашей задачей классификации логотипа? Никто не узнает до эксперимента. Вообще говоря, это мудрый выбор, чтобы выбрать самую простую и самую классическую сеть и запустить ее, чтобы увидеть эффект классификации.Тогда LeNet должен быть наиболее подходящим для вышеуказанных требований.Он прост в реализации и вполне классический. Тогда мы сначала напишем отдельный файл lenet.py, а потом реализуем улучшенную версию класса LeNet.
# import the necessary packages
from keras.models import Sequential
from keras.layers.convolutional import Conv2D
from keras.layers.convolutional import MaxPooling2D
from keras.layers.core import Activation
from keras.layers.core import Flatten
from keras.layers.core import Dense
from keras import backend as K
class LeNet:
@staticmethod
def build(width, height, depth, classes):
# initialize the model
model = Sequential()
inputShape = (height, width, depth)
# if we are using "channels last", update the input shape
if K.image_data_format() == "channels_first": #for tensorflow
inputShape = (depth, height, width)
# first set of CONV => RELU => POOL layers
model.add(Conv2D(20, (5, 5),padding="same",input_shape=inputShape))
model.add(Activation("relu"))
model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
#second set of CONV => RELU => POOL layers
model.add(Conv2D(50, (5, 5), padding="same"))
model.add(Activation("relu"))
model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
# first (and only) set of FC => RELU layers
model.add(Flatten())
model.add(Dense(500))
model.add(Activation("relu"))
# softmax classifier
model.add(Dense(classes))
model.add(Activation("softmax"))
# return the constructed network architecture
return model
скопировать код
Где conv2d означает выполнение свертки, maxpooling2d означает выполнение максимального объединения, Activation означает определенный тип функции активации, Flatten layer используется для «сглаживания» входных данных для перехода от convolutional слоя к полносвязному слою, Dense означает полносвязный слой (500 нейронов). .
Парсер параметров и инициализация некоторых параметров
Сначала мы определяем парсер параметров.
# set the matplotlib backend so figures can be saved in the background
import matplotlib
matplotlib.use("Agg")
# import the necessary packages
from keras.preprocessing.image import ImageDataGenerator
from keras.optimizers import Adam
from sklearn.model_selection import train_test_split
from keras.preprocessing.image import img_to_array
from keras.utils import to_categorical
from imutils import paths
import matplotlib.pyplot as plt
import numpy as np
import argparse
import random
import cv2
import os
import sys
sys.path.append('..')
from net.lenet import LeNet
def args_parse():
# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-dtest", "--dataset_test", required=True,
help="path to input dataset_test")
ap.add_argument("-dtrain", "--dataset_train", required=True,
help="path to input dataset_train")
ap.add_argument("-m", "--model", required=True,
help="path to output model")
ap.add_argument("-p", "--plot", type=str, default="plot.png",
help="path to output accuracy/loss plot")
args = vars(ap.parse_args())
return args
скопировать код
Нам также нужно установить некоторые параметры для обучения, такие как периоды обучения, batch_szie и т. д. Эти параметры не задаются произвольно, например, значение batch_size зависит от объема памяти вашего компьютера, чем больше объем памяти, тем больше может быть значение batch_size. Другой пример: norm_size (размер нормализации изображения) основан на наборе данных, который вы получаете после анализа, потому что большинство изображений в нашем наборе данных находятся в этом диапазоне, поэтому я думаю, что 32 должно быть более подходящим. , но не так ли? самое подходящее? Это еще предстоит выяснить опытным путем, может 64 лучше?
# initialize the number of epochs to train for, initial learning rate,
# and batch size
EPOCHS = 35
INIT_LR = 1e-3
BS = 32
CLASS_NUM = 62
norm_size = 32
скопировать код
загрузить данные
Далее нам нужно прочитать изображение и соответствующую информацию этикетки.
def load_data(path):
print("[INFO] loading images...")
data = []
labels = []
# grab the image paths and randomly shuffle them
imagePaths = sorted(list(paths.list_images(path)))
random.seed(42)
random.shuffle(imagePaths)
# loop over the input images
for imagePath in imagePaths:
# load the image, pre-process it, and store it in the data list
image = cv2.imread(imagePath)
image = cv2.resize(image, (norm_size, norm_size))
image = img_to_array(image)
data.append(image)
# extract the class label from the image path and update the
# labels list
label = int(imagePath.split(os.path.sep)[-2])
labels.append(label)
# scale the raw pixel intensities to the range [0, 1]
data = np.array(data, dtype="float") / 255.0
labels = np.array(labels)
# convert the labels from integers to vectors
labels = to_categorical(labels, num_classes=CLASS_NUM)
return data,labels
скопировать код
Функция возвращает изображение и соответствующую ему метку.
тренироваться
def train(aug,trainX,trainY,testX,testY,args):
# initialize the model
print("[INFO] compiling model...")
model = LeNet.build(width=norm_size, height=norm_size, depth=3, classes=CLASS_NUM)
opt = Adam(lr=INIT_LR, decay=INIT_LR / EPOCHS)
model.compile(loss="categorical_crossentropy", optimizer=opt,
metrics=["accuracy"])
# train the network
print("[INFO] training network...")
H = model.fit_generator(aug.flow(trainX, trainY, batch_size=BS),
validation_data=(testX, testY), steps_per_epoch=len(trainX) // BS,
epochs=EPOCHS, verbose=1)
# save the model to disk
print("[INFO] serializing network...")
model.save(args["model"])
# plot the training loss and accuracy
plt.style.use("ggplot")
plt.figure()
N = EPOCHS
plt.plot(np.arange(0, N), H.history["loss"], label="train_loss")
plt.plot(np.arange(0, N), H.history["val_loss"], label="val_loss")
plt.plot(np.arange(0, N), H.history["acc"], label="train_acc")
plt.plot(np.arange(0, N), H.history["val_acc"], label="val_acc")
plt.title("Training Loss and Accuracy on traffic-sign classifier")
plt.xlabel("Epoch #")
plt.ylabel("Loss/Accuracy")
plt.legend(loc="lower left")
plt.savefig(args["plot"])
скопировать код
Здесь мы используем оптимизатор Адама, а поскольку эта задача является задачей мультиклассификации, можно использовать категориальную кроссэнтропию (categorical_crossentropy). Но если нужно выполнить только две задачи классификации, функцию потерь следует заменить бинарной кросс-энтропийной функцией потерь (бинарная кросс-энтропия).
основная функция
#python train.py --dataset_train ../../traffic-sign/train --dataset_test ../../traffic-sign/test --model traffic_sign.model
if __name__=='__main__':
args = args_parse()
train_file_path = args["dataset_train"]
test_file_path = args["dataset_test"]
trainX,trainY = load_data(train_file_path)
testX,testY = load_data(test_file_path)
# construct the image generator for data augmentation
aug = ImageDataGenerator(rotation_range=30, width_shift_range=0.1,
height_shift_range=0.1, shear_range=0.2, zoom_range=0.2,
horizontal_flip=True, fill_mode="nearest")
train(aug,trainX,trainY,testX,testY,args)
скопировать код
Перед формальным обучением мы также использовали технологию увеличения данных (ImageDataGenerator) для выполнения увеличения данных в нашем небольшом наборе данных (произвольное вращение, перемещение, переворачивание, вырезание и т. д. на изображениях набора данных) для повышения способности модели к обобщению.
Код обучения был написан, а затем начните обучение (нормализованный размер изображения — 32, размер_пакета — 32, а эпохи — 35).
python train.py --dataset_train ../../traffic-sign/train --dataset_test ../../traffic-sign/test --model traffic_sign.model
скопировать код
Тренировочный процесс:
Потери и точность:
От тренировочного эффекта точность составляет около 94%, и эффект хороший.
Предсказать одно изображение
Теперь, когда у нас есть обученная модель traffic_sign.model, мы пишем predict.py, скрипт, предназначенный для прогнозирования.
# import the necessary packages
from keras.preprocessing.image import img_to_array
from keras.models import load_model
import numpy as np
import argparse
import imutils
import cv2
norm_size = 32
def args_parse():
# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-m", "--model", required=True,
help="path to trained model model")
ap.add_argument("-i", "--image", required=True,
help="path to input image")
ap.add_argument("-s", "--show", action="store_true",
help="show predict image",default=False)
args = vars(ap.parse_args())
return args
def predict(args):
# load the trained convolutional neural network
print("[INFO] loading network...")
model = load_model(args["model"])
#load the image
image = cv2.imread(args["image"])
orig = image.copy()
# pre-process the image for classification
image = cv2.resize(image, (norm_size, norm_size))
image = image.astype("float") / 255.0
image = img_to_array(image)
image = np.expand_dims(image, axis=0)
# classify the input image
result = model.predict(image)[0]
#print (result.shape)
proba = np.max(result)
label = str(np.where(result==proba)[0])
label = "{}: {:.2f}%".format(label, proba * 100)
print(label)
if args['show']:
# draw the label on the image
output = imutils.resize(orig, width=400)
cv2.putText(output, label, (10, 25),cv2.FONT_HERSHEY_SIMPLEX,
0.7, (0, 255, 0), 2)
# show the output image
cv2.imshow("Output", output)
cv2.waitKey(0)
#python predict.py --model traffic_sign.model -i ../2.png -s
if __name__ == '__main__':
args = args_parse()
predict(args)
скопировать код
Идея написания кода в скрипте предсказания такова: парсер параметров - "загрузить обученную модель -" прочитать в картинке информацию - "предсказание" - "показать эффект предсказания". Стоит отметить, что параметр -s используется для визуализации результатов, с его слов мы можем видеть введенные нами картинки и результаты классификации, предсказанные моделью, которая очень интуитивно понятна. Если вам нужно получить только результат классификации, можно обойтись и без -s.
Прогноз для одного изображения:
python predict.py --model traffic_sign.model -i ../2.png -s
скопировать код
На этом задача классификации трафика завершена.
Вот источник набора данных для этого проекта:
вы можете нажатьздесьЗагрузите набор данных. На странице загрузки есть много наборов данных, но вам нужно загрузить только два файла из каталога BelgiumTS for Classification (обрезанные изображения):
- BelgiumTSC_Training (171.3MBytes)
- BelgiumTSC_Testing (76.5MBytes)
Стоит отметить, что формат изображения исходного набора данных — ppm, это очень старый формат сохранения изображений, и многие инструменты больше не поддерживают его. Это также означает, что мы не можем легко просматривать изображения в этих папках.
Чтобы решить эту проблему, я использовал opencv для повторного преобразования этих изображений в формат png, чтобы мы могли интуитивно видеть изображения данных.
Скрипт преобразования находится вздесь
В то же время я также передаю преобразованный набор данных вОблако БайдуДа, детскую обувь, которая не хочет переделываться, можно получить самостоятельно.
2. Классификация векселей
Сначала проанализируйте задачу и наблюдайте за данными. Наша задача классификации на этот раз — классификация счетов, Сейчас у нас всего 14 типов счетов, Наша задача — обучить модель классифицировать их один за другим. Сначала взгляните на изображение билета.
Всего существует 14 видов купюр, а название картинки является ее меткой.
Билеты хранятся в папке, показанной ниже.
Затем мы смотрим на ситуацию с каждым типом данных изображения, чтобы увидеть, сколько данных доступно.
В некоторых счетах меньше данных, всего дюжина или около того.
Некоторые купюры больше, их сотни
Если такое распределение данных напрямую использовать для обучения, эффект может быть не очень хорошим (это проблема дисбаланса), но это проблема, которую необходимо учитывать при последующей настройке модели, поэтому пока отложим ее. Затем мы продолжаем использовать приведенную выше структуру классификации изображений для выполнения этой задачи классификации билетов.
Метод хранения набора данных на этот раз отличается от хранения данных задачи классификации дорожных знаков, Этот набор данных не делит данные на две папки: train и test, поэтому функция, которую мы пишем при чтении данных в коде должны реагировать соответствующим образом Модификация: Сначала мы читаем все картинки, а затем используем функцию sklearn «train_test_split», чтобы разделить набор данных на обучающий набор и тестовый набор в определенной пропорции.
Я написал функцию load_data2() для размещения этого хранилища данных.
def load_data2(path):
print("[INFO] loading images...")
data = []
labels = []
# grab the image paths and randomly shuffle them
imagePaths = sorted(list(paths.list_images(path)))
random.seed(42)
random.shuffle(imagePaths)
# loop over the input images
for imagePath in imagePaths:
# load the image, pre-process it, and store it in the data list
image = cv2.imread(imagePath)
image = cv2.resize(image, (norm_size, norm_size))
image = img_to_array(image)
data.append(image)
# extract the class label from the image path and update the
# labels list
label = int(imagePath.split(os.path.sep)[-2])
labels.append(label)
# scale the raw pixel intensities to the range [0, 1]
data = np.array(data, dtype="float") / 255.0
labels = np.array(labels)
# partition the data into training and testing splits using 75% of
# the data for training and the remaining 25% for testing
(trainX, testX, trainY, testY) = train_test_split(data,
labels, test_size=0.25, random_state=42)
# convert the labels from integers to vectors
trainY = to_categorical(trainY, num_classes=CLASS_NUM)
testY = to_categorical(testY, num_classes=CLASS_NUM)
return trainX,trainY,testX,testY
скопировать код
Мы использовали артефакт train_test_split в sklearn для разделения набора данных, что очень удобно. Можно видеть, что возвращаемое значение load_data2() представляет собой изображение обучающего набора и аннотацию + изображение тестового набора и аннотацию.
В основной функции можно внести лишь несколько изменений для выполнения этой задачи классификации счетов.
if __name__=='__main__':
args = args_parse()
file_path = args["dataset"]
trainX,trainY,testX,testY = load_data2(file_path)
# construct the image generator for data augmentation
aug = ImageDataGenerator(rotation_range=30, width_shift_range=0.1,
height_shift_range=0.1, shear_range=0.2, zoom_range=0.2,
horizontal_flip=True, fill_mode="nearest")
train(aug,trainX,trainY,testX,testY,args)
скопировать код
Затем установите некоторые параметры, такие как нормализованный размер изображения 64 * 64, и обучите 35 эпох. После настройки параметров приступаем к обучению.
python train.py --dataset ../../invoice_all/train --model invoice.model
скопировать код
Тренировочный процесс не долгий, минут десять. Процесс обучения выглядит следующим образом:
Построив кривые Loss и Accuracy, можно увидеть, что точность нашей обученной модели может достигать 97%. По-прежнему очень приятно, что вы можете выйти за пределы этой точности, используя сеть LeNet напрямую.
Наконец, используйте обученную модель, чтобы предсказать один билет и увидеть эффект:
Прогноз верен, и задача классификации билетов глубокого обучения выполнена!
3. Резюме
Мы используем Keras для создания общей основы для классификаторов изображений на основе LeNet и используем ее для успешного выполнения двух практических задач классификации. Наконец, давайте поговорим о некоторых улучшениях в нашей существующей модели. Во-первых, подходит ли нормализованный размер изображения? Например, в задаче классификации купюр изображение нормализуется до 64, что может быть немного мало, если изменить размер на 128 или 256, эффект может быть лучше, во-вторых, можно рассматривать более глубокие сети, такие как VGG. , GoogLeNet и т. д.; В-третьих, часть улучшения данных может быть выполнена еще раз.
Полный код и тестовые изображения можно найти по адресумой гитхабполучено на.
Использованная литература: