Это 4-й день моего участия в августовском испытании обновлений.Подробности о событии:Испытание августовского обновления
Deep Learning with Python for computer vision
В этой статье я изучаю «Глубокое обучение с помощью Python» (второе издание, Франсуа Шолле).Глава 5 Глубокое обучение для компьютерного зрения (Chapter 5. Deep learning for computer vision) Примечания.
Содержание статьи конвертировано из блокнотов Jupyter в Markdown, вы можете перейти на мойGitHubилиGiteeнайти оригинал.ipynb
ноутбук.
ты можешь пойти вЧитайте оригинальный текст этой книги онлайн на этом сайте(Английский). В то же время автор этой книги также оказал поддержкуJupyter notebooks.
Введение в сверточные нейронные сети
5.1 Introduction to convnets
Сверточные нейронные сети отлично справляются с проблемами компьютерного зрения.
Давайте сначала рассмотрим пример простейшей сверточной нейронной сети, обрабатывающей полносвязную сеть в главе 2 MNIST:
from tensorflow.keras import layers
from tensorflow.keras import models
model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)))
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.add(layers.Flatten())
model.add(layers.Dense(64, activation="relu"))
model.add(layers.Dense(10, activation="softmax"))
Здесь мы используем слой Conv2D дляinput_shape
да(image_height, image_width, image_channels)
в этом формате.
Выходные данные слоев Conv2D и MaxPooling2D являются трехмерными тензорами.(height, width, channels)
, высота и ширина будут уменьшаться слой за слоем, каналы управляются первым параметром Conv2D.
В последние три слоя мы помещаем последний слой Conv2D(3, 3, 64)
Тензор является исходным значением слова «сглаживание», но стандартный китайский перевод означает «сглаживание» до 1D. Затем следующие два плотных слоя аналогичны тем, что мы делали в главе 2, и, наконец, мы получаем 10-факторную классификацию.
Наконец, взгляните на структуру модели:
model.summary()
Model: "sequential_1"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d_3 (Conv2D) (None, 26, 26, 32) 320
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 13, 13, 32) 0
_________________________________________________________________
conv2d_4 (Conv2D) (None, 11, 11, 64) 18496
_________________________________________________________________
max_pooling2d_3 (MaxPooling2 (None, 5, 5, 64) 0
_________________________________________________________________
conv2d_5 (Conv2D) (None, 3, 3, 64) 36928
_________________________________________________________________
flatten_1 (Flatten) (None, 576) 0
_________________________________________________________________
dense_2 (Dense) (None, 64) 36928
_________________________________________________________________
dense_3 (Dense) (None, 10) 650
=================================================================
Total params: 93,322
Trainable params: 93,322
Non-trainable params: 0
_________________________________________________________________
Итак, сеть строится так, она еще очень простая, а затем обучается, что примерно так же, как и во второй главе (но обратите внимание, что форма решейпа другая):
# Load the TensorBoard notebook extension
# TensorBoard 可以可视化训练过程
%load_ext tensorboard
# Clear any logs from previous runs
!rm -rf ./logs/
# 在 MNIST 图像上训练卷积神经网络
import datetime
import tensorflow as tf
from tensorflow.keras.datasets import mnist
from tensorflow.keras.utils import to_categorical
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()
train_images = train_images.reshape((60000, 28, 28, 1))
train_images = train_images.astype('float32') / 255
test_images = test_images.reshape((10000, 28, 28, 1))
test_images = test_images.astype('float32') / 255
train_labels = to_categorical(train_labels)
test_labels = to_categorical(test_labels)
# 准备 TensorBoard
log_dir = "logs/fit/" + datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
tensorboard_callback = tf.keras.callbacks.TensorBoard(log_dir=log_dir, histogram_freq=1)
model.compile(optimizer='rmsprop',
loss="categorical_crossentropy",
metrics=['accuracy'])
model.fit(train_images, train_labels, epochs=5, batch_size=64,
callbacks=[tensorboard_callback])
Train on 60000 samples
Epoch 1/5
60000/60000 [==============================] - 36s 599us/sample - loss: 0.0156 - accuracy: 0.9953
Epoch 2/5
60000/60000 [==============================] - 33s 554us/sample - loss: 0.0127 - accuracy: 0.9960
Epoch 3/5
60000/60000 [==============================] - 31s 524us/sample - loss: 0.0097 - accuracy: 0.9971
Epoch 4/5
60000/60000 [==============================] - 32s 529us/sample - loss: 0.0092 - accuracy: 0.9974
Epoch 5/5
60000/60000 [==============================] - 31s 523us/sample - loss: 0.0095 - accuracy: 0.9971
Примечание. Здесь вы также можете визуализировать ситуацию обучения модели, отображаемую tensorboard:%tensorboard --logdir logs/fit
.
Посмотрим результаты в тестовом наборе:
test_loss, test_acc = model.evaluate(test_images, test_labels, verbose=2)
print(test_loss, test_acc)
вывод:
10000/1 - 1s - loss: 0.0172 - accuracy: 0.9926
0.03441549262946125 0.9926
свертка
Сверточная нейронная сеть
мы использовалиплотносвязный слойГлобальный паттерн изучается во всем пространстве входных признаков (в MNIST — во всех пикселях); здесь сверточный слой изучает локальный паттерн. То есть Dense изучает изображение целиком, а Conv изучает часть изображения. Например, в коде, который мы только что написали, мы изучили окно 3x3:
Эта сверточная нейронная сеть имеет два свойства:
-
Паттерн, изученный сверточной нейронной сетью, инвариантен к трансляции: после того, как сверточная нейронная сеть изучила определенный шаблон, она увидит тот же шаблон в другом месте и признает, что изучила его. Нет необходимости учиться снова. Для плотной сети, даже если она сталкивается с той же частичной частью, ее все равно необходимо заново изучить. Это свойство позволяет сверточным нейронным сетям эффективно использовать данные, и им требуется только меньшее количество обучающих выборок, чтобы лучше обобщать представления данных (запоминать каждую часть, а не отображать ее целиком).
-
Сверточные нейронные сети могут изучать пространственные иерархии паттернов: после того, как сверточная нейронная сеть изучит небольшой локальный паттерн в первом слое, следующий слой может использовать эти маленькие части для описания более крупных режимов. Затем, с еще несколькими слоями, сверточная нейронная сеть может изучать все более сложные и абстрактные визуальные понятия, в чем смысл следующей картинки:
сверточный слой
Трехмерный тензор, используемый для представления изображения в нашем примере, только что включает две пространственные оси: высоту, ширину и глубину по оси глубины (также называемую осью каналов).Для изображений RGB размер оси глубины равен 3, что соответствует 3 типам соответственно. Цвет: для изображений в градациях серого, таких как MNIST, глубина равна 1, и для представления значения градаций серого используется только одно число. Результат этого трехмерного тензора и выполненной над ним операции свертки называетсяfeature map(карта объектов).
Операция свертки извлекает небольшие фрагменты из входной карты объектов и применяет идентичное преобразование ко всем этим фрагментам для получения выходной карты объектов. Выходная карта объектов по-прежнему представляет собой трехмерный тензор: с шириной и высотой ее глубина может быть произвольной, размер глубины является параметром слоя, а каждый канал на оси глубины представляет собой фильтр. фильтр кодирует некоторый аспект входных данных, например, фильтр может кодировать понятие «вход содержит лицо».
Только что в примере MNIST первый сверточный слой принимает размеры(28, 28, 1)
Входная карта объектов , выводит размер(26, 26, 32)
карта характеристик. Этот выход содержит 32 фильтра, а канал на каждой оси глубины содержит 26x26 значений, называемых картой отклика фильтра на вход, которая представляет собой результат работы фильтра в разных позициях на входе. Вот почему карты объектов называются картами объектов: каждое измерение оси глубины является функцией (или фильтром), а двумерный тензорoutput[:, :, n]
представляет собой двумерный пространственный график отклика этого фильтра на входе.
Операция свертки
Про свертки, эммм, не понимаю сложное преобразование, в основном читал«Чжиху: Как объяснить свертки простым для понимания способом?»понять. Здесь мы в основном используем эту функцию:
Инициализация слоя Keras Conv2D записывается как:
Conv2D(output_depth, (window_height, window_width))
Он содержит два основных параметра операции свертки:
- Глубина выходной карты признаков: 32 и 64 только что использовались в нашем примере MNIST;
- Размер каждого блока (скользящего окна), извлекаемого из ввода: обычно 3x3 или 5x5;
Операция свертки проходит все возможные позиции, как скользящее окно, объединяя свойства каждого маленького блока во входных данных.(window_height, window_width, input_depth)
Выполняя скалярное произведение с матрицей весов для обучения, называемое ядром свертки, изменение дает вектор(output_depth, )
. Все такие векторы результатов объединяются для получения окончательного 3D-вывода.(height, width, output_depth)
, где каждое значение соответствует вводу, например скользящему окну 3x3, затемoutput[i, j, :]
отinput[i-1:i+1, j-1:j+1, :]
.
Для свертки и CNN вы можете перейти к этой статье:Convolutional Neural Networks - Basics, An Introduction to CNNs and Deep Learning
Обратите внимание, что ширина и высота нашего вывода могут отличаться от входной ширины и высоты из-за эффектов границ и шагов.
Граничные эффекты и заливка
Краевой эффект заключается в том, что размер матрицы, которую вы получаете после выполнения скользящего окна, уменьшается по кругу (край исчез). Например, если вы вводите изображение 5x5, вы можете удалить только 9 частей, взяв маленькие блоки 3x3, поэтому выходным результатом будет 3x3:
MNIST, который мы делали раньше, аналогичен входным данным 28x28 в начале, первый слой занимает 3x3, и результат 26x26.
Если это сокращение нежелательно, т. е. вы хотите, чтобы пространственные размеры выходных данных соответствовали входным, то требуется дополнение. Заполнение заключается в добавлении нескольких строк и столбцов к границе входного изображения, 1 круга для 3x3 и 2 кругов для 5x5:
Слой Keras Conv2D можно использоватьpadding
параметр для установки с использованием заполнения.padding
Может быть установлено на:
-
"valid"
(по умолчанию): без заполнения, берутся только «действительные» блоки. Например, на входной карте объектов 5×5 можно извлечь действительные местоположения тайлов 3×3; -
"same"
: выполните заполнение, чтобы сделать ширину и высоту вывода и ввода равными.
Шаг свертки
Шаг свертки - это то, насколько скользящее окно перемещается за раз. То, что мы делали раньше, - это шаг 1. Мы называем свертки с шагом больше 1Ступенчатая свертка(шаговая свертка), например, следующий шаг равен 2:
Однако на практике ступенчатая свертка используется нечасто.Для такого рода даунсемплинга (downsampling) карт признаков мы обычно используем максимальный пул.
Примечание:
понижение частоты дискретизации: выборка последовательности сэмплов с интервалами в несколько сэмплов, так что новая полученная последовательность представляет собой дискретизацию исходной последовательности с понижением частоты дискретизации.
From Энциклопедия Байду
максимальное объединение
Подобно ступенчатой свертке, максимальное объединение используется для понижения дискретизации карт объектов. В исходном примере MNIST после того, как мы использовали слой MaxPooling2D, размер карты объектов был уменьшен вдвое.
Максимальное объединение состоит в том, чтобы взять максимальное значение каждого канала из входной карты объектов в окне, а затем вывести его. Эта операция очень похожа на свертку, но применяемая функция является макс.
Для максимального объединения мы обычно используем окно 2x2 с шагом 2, что может уменьшить карту объектов в 2 раза. (Свертка обычно представляет собой окно 3x3 и шаг 1)
Если вы не используете максимальное объединение и складываете множество сверточных слоев напрямую, возникнут две проблемы:
- Размер карты признаков уменьшается медленно, а сзади слишком много параметров, что усугубит переобучение;
- Это не способствует изучению пространственной иерархии: оно не способствует видению более абстрактного целого.
В дополнение к максимальному объединению существует множество способов понижения частоты дискретизации: например, ступенчатая свертка, усреднение и тому подобное. Но в целом эффект максимального объединения лучше. Мы хотим знать, есть ли определенная функция. Если мы используем среднее значение, чтобы «уклониться» от этой функции, если мы используем ступенчатую свертку, мы можем пропустить эту информацию.
В целом, причины использования максимального объединения/другого понижения дискретизации заключаются в том, чтобы уменьшить количество элементов карты объектов, которые необходимо обработать, и наблюдать все большие и большие окна с помощью серии сверточных слоев (чем больше покрытие вы видите). ) все больше и больше пропорций исходного ввода), чтобы узнать пространственную иерархию.
Обучение сверточной нейронной сети с нуля на небольшом наборе данных
5.2 Training a convnet from scratch on a small dataset
Когда мы занимаемся компьютерным зрением, проблема, с которой мы часто сталкиваемся, заключается в обучении модели классификации изображений на очень маленьком наборе данных. эммм, "маленьких" здесь может быть от сотен до десятков тысяч.
От этого раздела к следующему мы собираемся обучить небольшую модель с нуля, использовать предварительно обученную сеть для извлечения признаков и точно настроить предварительно обученную сеть.Эти шаги можно комбинировать для решения небольших данные набор задач классификации изображений.
В этом разделе мы будем обучать маленькую модель с нуля классифицировать изображения кошек и собак. Без регуляризации игнорируйте проблему переобучения.
Скачать данные
Мы будем обучать модель, используя набор данных Dogs vs. Cats, который представляет собой коллекцию фотографий кошек и собак. Этот набор данных не встроен в Keras, мы можем скачать его с Kaggle:woohoo.cardreform.com/from/dogs-vs-from…
Скачай и разархивируй, (эммммм, он немного великоват, мой MBP не влезает, я просто распаковал его на мобильный жесткий диск, эммммм, я вспомнил, что должен купить новый твердотельный).
Затем создайте наборы данных, которые мы будем использовать: 1000 образцов для каждой кошки и собаки в обучающем наборе, по 500 для каждого набора для проверки и по 500 для каждого тестового набора. Программа для работы:
# 将图像复制到训练、验证和测试的目录
import os, shutil
original_dataset_dir = '/Volumes/WD/Files/dataset/dogs-vs-cats/dogs-vs-cats/train' # 原始数据集
base_dir = '/Volumes/WD/Files/dataset/dogs-vs-cats/cats_and_dogs_small' # 将要保存的较小数据集的位置
os.mkdir(base_dir)
# 创几个目录放划分后的训练、验证和测试集
train_dir = os.path.join(base_dir, 'train')
os.mkdir(train_dir)
validation_dir = os.path.join(base_dir, 'validation')
os.mkdir(validation_dir)
test_dir = os.path.join(base_dir, 'test')
os.mkdir(test_dir)
# 分开放猫狗
train_cats_dir = os.path.join(train_dir, 'cats')
os.mkdir(train_cats_dir)
validation_cats_dir = os.path.join(validation_dir, 'cats')
os.mkdir(validation_cats_dir)
test_cats_dir = os.path.join(test_dir, 'cats')
os.mkdir(test_cats_dir)
# 接下来把上面这段代码一遍,cat 改成 dog
...
# 复制猫的图片
fnames = [f'cat.{i}.jpg' for i in range(1000)] # 这里用了 f-String,要求 Python >= 3.6,老版本可以用 'cat.{}.jpg'.format(i)
for fname in fnames:
src = os.path.join(original_dataset_dir, fname)
dst = os.path.join(train_cats_dir, fname)
shutil.copyfile(src, dst)
fnames = [f'cat.{i}.jpg' for i in range(1000, 1500)]
for fname in fnames:
src = os.path.join(original_dataset_dir, fname)
dst = os.path.join(validation_cats_dir, fname)
shutil.copyfile(src, dst)
fnames = [f'cat.{i}.jpg' for i in range(1500, 2000)]
for fname in fnames:
src = os.path.join(original_dataset_dir, fname)
dst = os.path.join(test_cats_dir, fname)
shutil.copyfile(src, dst)
# 复制狗的图片: 和上面猫的类似,把 cat 改成 dog 就行了。
...
# 检查
print('total training cat images:', len(os.listdir(train_cats_dir)))
print('total validation cat images:', len(os.listdir(validation_cats_dir)))
print('total test cat images:', len(os.listdir(test_cats_dir)))
print('total training dog images:', len(os.listdir(train_dogs_dir)))
print('total validation dog images:', len(os.listdir(validation_dogs_dir)))
print('total test dog images:', len(os.listdir(test_dogs_dir)))
Выходной результат:
total training cat images: 1000
total validation cat images: 500
total test cat images: 500
total training dog images: 1000
total validation dog images: 500
total test dog images: 500
Создайте сеть
Почти во всех сверточных нейронных сетях мы постепенно увеличиваем глубину карты признаков и постепенно уменьшаем размер. Вот и в этот раз мы поступили так же.
Проблема, с которой мы столкнулись сейчас, — это бинарная классификация, поэтому последний слой использует 1-элементную сигмовидную активацию Dense:
# 将猫狗分类的小型卷积神经网络实例化
from tensorflow.keras import layers
from tensorflow.keras import models
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(128, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(128, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Flatten())
model.add(layers.Dense(512, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))
Взгляните на структуру сети:
model.summary()
Model: "sequential_4"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d_16 (Conv2D) (None, 148, 148, 32) 896
_________________________________________________________________
max_pooling2d_16 (MaxPooling (None, 74, 74, 32) 0
_________________________________________________________________
conv2d_17 (Conv2D) (None, 72, 72, 64) 18496
_________________________________________________________________
max_pooling2d_17 (MaxPooling (None, 36, 36, 64) 0
_________________________________________________________________
conv2d_18 (Conv2D) (None, 34, 34, 128) 73856
_________________________________________________________________
max_pooling2d_18 (MaxPooling (None, 17, 17, 128) 0
_________________________________________________________________
conv2d_19 (Conv2D) (None, 15, 15, 128) 147584
_________________________________________________________________
max_pooling2d_19 (MaxPooling (None, 7, 7, 128) 0
_________________________________________________________________
flatten_4 (Flatten) (None, 6272) 0
_________________________________________________________________
dense_8 (Dense) (None, 512) 3211776
_________________________________________________________________
dense_9 (Dense) (None, 1) 513
=================================================================
Total params: 3,453,121
Trainable params: 3,453,121
Non-trainable params: 0
_________________________________________________________________
Затем нам нужно скомпилировать сеть для выполнения бинарной классификации, поэтому функция потерь использует бинарную кроссэнтропию (binary cross entropy), а оптимизатор по-прежнему использует RMSprop (мы писали ранееoptimizer='rmsprop'
, на этот раз для передачи некоторых параметров, поэтому используйтеoptimizers.RMSprop
пример):
from tensorflow.keras import optimizers
model.compile(loss='binary_crossentropy',
optimizer=optimizers.RMSprop(lr=1e-4),
metrics=['acc'])
предварительная обработка данных
Нам нужно превратить эти изображения в тензоры чисел с плавающей запятой, чтобы передать их в нейронную сеть. Действуйте следующим образом:
- прочитать файл изображения
- Декодировать содержимое файла JPEG в пиксельную сетку RGB
- Преобразовать в плавающий тензор
- преобразовать значение пикселя из
[0, 255]
увеличить до[0, 1]
Keras предоставляет несколько инструментов для автоматизации этого:
# 使用 ImageDataGenerator 从目录中读取图像
from tensorflow.keras.preprocessing.image import ImageDataGenerator
train_datagen = ImageDataGenerator(rescale=1./255)
test_datagen = ImageDataGenerator(rescale=1./255)
train_generator = train_datagen.flow_from_directory(
train_dir,
target_size=(150, 150),
batch_size=20,
class_mode='binary') # 用二分类的标签
validation_generator = test_datagen.flow_from_directory(
validation_dir,
target_size=(150, 150),
batch_size=20,
class_mode='binary')
Found 2000 images belonging to 2 classes.
Found 1000 images belonging to 2 classes.
Получающиеся в результате train_generator и validation_generator — это своего рода генераторы в Python, своего рода ленивое вычисление. Этот генератор выдает одну партию за раз, поэтому он называется «генератором партии» и повторяет одну партию, чтобы увидеть:
for data_batch, labels_batch in train_generator:
print('data batch shape:', data_batch.shape)
print('labels batch shape:', labels_batch.shape)
print('labels_batch:', labels_batch)
break
data batch shape: (20, 150, 150, 3)
labels batch shape: (20,)
labels_batch: [1. 1. 1. 1. 1. 1. 1. 1. 1. 0. 1. 1. 1. 1. 0. 0. 1. 1. 0. 1.]
# 利用 batch 生成器拟合模型
history = model.fit_generator(
train_generator,
steps_per_epoch=100,
epochs=30,
validation_data=validation_generator,
validation_steps=50)
Беговые тренировки:
Epoch 1/30
100/100 [==============================] - 97s 967ms/step - loss: 0.6901 - acc: 0.5450 - val_loss: 0.6785 - val_acc: 0.5270
......
Epoch 30/30
100/100 [==============================] - 162s 2s/step - loss: 0.0460 - acc: 0.9885 - val_loss: 1.0609 - val_acc: 0.7150
Здесь, поскольку пакет считывается из генератора для подгонки, подгонка, которую мы обычно используем, изменяется наfit_generator
. Он передает генератор обучающих данных, количество раз, чтобы получить раунд от train_generator (steps_per_epoch), раунд, генератор набора проверки и количество раз, чтобы получить раунд от validation_generator (validation_steps).
steps_per_epoch = 训练集数据总数 / 构建generator时指定的batch_size
validation_steps похож на steps_per_epoch, но для набора проверки.
Сохраните обученную модель с помощью следующей строки кода:
# 保存模型
model.save('/Volumes/WD/Files/dataset/dogs-vs-cats/cats_and_dogs_small_1.h5')
Затем нарисуйте картину тренировочного процесса, чтобы увидеть:
# 绘制训练过程中的损失曲线和精度曲线
import matplotlib.pyplot as plt
acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(1, len(acc) + 1)
plt.plot(epochs, acc, 'bo-', label='Training acc')
plt.plot(epochs, val_acc, 'sr-', label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()
plt.figure()
plt.plot(epochs, loss, 'bo-', label='Training loss')
plt.plot(epochs, val_loss, 'sr-', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.show()
Неудивительно, что это переобучение, начиная примерно с 5-го раунда.
Затем мы используем увеличение данных, чтобы уменьшить переоснащение.
увеличение данных
Увеличение данных — это метод, обычно используемый для обработки изображений с помощью глубокого обучения.
Переобучение возникает из-за слишком малого количества данных для обучения (пока выборок достаточно, модель может просмотреть почти все, что возможно, и почти не ошибиться). Увеличение данных — это метод создания большего количества обучающих данных на основе существующих выборок, дополненных различными случайными преобразованиями, которые могут генерировать правдоподобные изображения.
В Keras мы можем завершить улучшение данных, установив несколько параметров при использовании ImageDataGenerator:
datagen = ImageDataGenerator(
rotation_range=40, # 随机旋转图片的范围,0~180
width_shift_range=0.2, # 随机水平移动的比例
height_shift_range=0.2, # 随机竖直移动的比例
shear_range=0.2, # 随机错切变换(shearing transformations)的角度
zoom_range=0.2, # 随机缩放的范围
horizontal_flip=True, # 是否做随机水平反转
fill_mode='nearest') # 填充新创建像素的方法
Найдите изображение, которое хотите улучшить, и попробуйте:
from tensorflow.keras.preprocessing import image
fnames = [os.path.join(train_cats_dir, fname) for
fname in os.listdir(train_cats_dir)]
img_path = fnames[3]
img = image.load_img(img_path, target_size=(150, 150)) # 读取图片
x = image.img_to_array(img) # shape (150, 150, 3)
x = x.reshape((1,) + x.shape) # shape (1, 150, 150, 3)
i=0
for batch in datagen.flow(x, batch_size=1):
plt.figure(i)
imgplot = plt.imshow(image.array_to_img(batch[0]))
i += 1
if i % 4 == 0:
break
plt.show()
Обратите внимание, что дополнение данных не приносит новой информации, оно просто смешивает существующую информацию. Таким образом, в случае очень небольшого количества данных одного улучшения данных недостаточно для устранения переобучения, поэтому нам также необходимо использовать Dropout перед плотным слоем.
# 定义一个包含 dropout 的新卷积神经网络
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(128, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(128, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Flatten())
model.add(layers.Dropout(0.5)) # ? 新增的 Dropout
model.add(layers.Dense(512, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))
model.compile(loss='binary_crossentropy',
optimizer=optimizers.RMSprop(lr=1e-4),
metrics=['acc'])
# 利用数据增强生成器来训练卷积神经网络
# 数据生成器
train_datagen = ImageDataGenerator(
rescale=1./255,
rotation_range=40,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,)
test_datagen = ImageDataGenerator(rescale=1./255) # 测试集不增强哦
train_generator = train_datagen.flow_from_directory(
train_dir,
target_size=(150, 150),
batch_size=32,
class_mode='binary')
validation_generator = test_datagen.flow_from_directory(
validation_dir,
target_size=(150, 150),
batch_size=32,
class_mode='binary')
# 训练
history = model.fit_generator(
train_generator,
steps_per_epoch=100,
epochs=100,
validation_data=validation_generator,
validation_steps=50)
# 保存模型
model.save('/Volumes/WD/Files/dataset/dogs-vs-cats/cats_and_dogs_small_2.h5')
Found 2000 images belonging to 2 classes.
Found 1000 images belonging to 2 classes.
Epoch 1/100
100/100 [==============================] - 142s 1s/step - loss: 0.6909 - acc: 0.5265 - val_loss: 0.6799 - val_acc: 0.5127
......
Epoch 100/100
100/100 [==============================] - 130s 1s/step - loss: 0.3140 - acc: 0.8624 - val_loss: 0.4295 - val_acc: 0.8274
# 绘制训练过程中的损失曲线和精度曲线
import matplotlib.pyplot as plt
acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(1, len(acc) + 1)
plt.plot(epochs, acc, 'bo-', label='Training acc')
plt.plot(epochs, val_acc, 'r-', label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()
plt.figure()
plt.plot(epochs, loss, 'bo-', label='Training loss')
plt.plot(epochs, val_loss, 'r-', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.show()
?После использования Data Augmentation and Dropout переоснащение стало намного лучше, а также повысилась точность.
Далее мы будем использовать некоторые методы для дальнейшей оптимизации модели.
【Продолжение следует】