Некоторые части будут отредактированы и доработаны для лучшего понимания, однако я подтверждаю, что следующий пост основан на учебнике TensorFlow, представленном в:



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

Введение

Основные факты об этом сообщении.

Этот пост посвящен пониманию базовой нейронной сети, касающейся данных изображения. Это можно рассматривать как следующий шаг к последней публикации; загрузка изображения в tf.data. Однако в наборе данных flowers_photo не было тестового набора данных, поэтому мы будем использовать изображения CIFAR.

Кроме того, мы не будем использовать tf.data для загрузки изображений. Вместо этого мы просто загрузим изображения напрямую в (train_images, train_labels), (test_images, test_labels). Мы сохраним эту часть простой, чтобы сосредоточиться на CNN.

Этот пост предназначен для студентов бакалавриата, которые будут / будут испытывать трудности на шестой неделе. Надеюсь, это спасет вас, ребята! 👊

Что мы делаем дальше?

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

II. Техническая настройка

from __future__ import absolute_import, division, print_function, unicode_literals
try:
  # %tensorflow_version only exists in Colab.
  %tensorflow_version 2.x
except Exception:
  pass
import tensorflow as tf
from tensorflow.keras import datasets, layers, models
import matplotlib.pyplot as plt

Нам нравятся эти строки? PLZ Да! Вы должны знать, что вам не нужно знать слово в слово.

  • из tensorflow.keras import наборы данных, слои, модели: мы будем использовать последовательный API keras для моделирования
  • import matplotlib.pyplot как plt: мы будем рисовать графики, чтобы показать некоторые изображения.

III. Набор данных CIFAR10

Основная информация о наборе данных CIFAR10.

Он имеет 60K 32 * 32 цветных изображений 10 классов по 6K изображений на класс. У нас есть 50K обучающих изображений и 10K тестовых изображений. У нас есть набор данных, в котором изображения уже разделены на 6 групп (по 1К изображений в каждой); 5 партий для обучения и 1 партия для теста с произвольным смешением классов. Они уже закончили предварительную обработку! 👍

У нас есть 10 классов с именами; 'airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck'.

Сначала мы загрузим наш набор данных cifar10.

(train_images, train_labels), (test_images, test_labels) = datasets.cifar10.load_data()

Затем мы нормализуем значения пикселей до диапазона от 0 до 1.

train_images, test_images = train_images / 255.0, test_images / 255.0

Что такое значения пикселей?

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

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

Каждый R, G, B может иметь число от 0 до 255, а цветной пиксель сохраняется в векторе строк размером 1 на 3 (R, G, B). Вот почему у цветного изображения color_channels равно 3.

Затем у цветного изображения есть 3 сетки (по одной для каждого RGB) с размером image_height и image_width.

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

Покажите картинки

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

class_names = ['airplane', 'automobile', 'bird', 'cat', 'deer',
               'dog', 'frog', 'horse', 'ship', 'truck']
plt.figure(figsize=(10,10))
for i in range(10):
    plt.subplot(5,5,i+1)
    plt.xticks([])
    plt.yticks([])
    plt.grid(False)
    plt.imshow(train_images[i], cmap=plt.cm.binary)
    # The CIFAR labels happen to be arrays, 
    # which is why you need the extra index
    plt.xlabel(class_names[train_labels[i][0]])
plt.show()

IV. Создайте сверточную базу

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

model = tf.keras.Sequential([
    tf.keras.layers.Conv2D(32, (3, 3), activation='relu', input_shape=(32, 32, 3)),
    tf.keras.layers.MaxPooling2D((2,2)),
    tf.keras.layers.Conv2D(64, (3, 3), activation='relu'),
    tf.keras.layers.MaxPooling2D((2, 2)),
    tf.keras.layers.Conv2D(64, (3, 3), activation='relu'),
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(64,activation='relu'),
    tf.keras.layers.Dense(10,activation='softmax')
])

Однако мы можем использовать метод model.add, чтобы пропустить повторяющиеся слои tf.keras.layers. Идея состоит в том, что после создания свободной последовательной модели мы будем добавлять слои.

model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(32, 32, 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'))

ii) Что такое свертка и MaxPooling?

1 - Свертка: процесс применения ядра или фильтра к изображению.

layers.Conv2D(32, (3, 3), activation='relu', input_shape=(32, 32, 3))
  • input_shape = (32,32,3)
    Мы рассмотрели это! у нас есть изображение размером image_height = 32, image_width = 32, с color_channels равным 3 (цвет).
  • Activation = ‘relu’
    relu
    - это сокращение от Выпрямленная линейная единица и наиболее часто используемая функция активации. Эта функция возвращает 0, если вход меньше или равен 0, и если вход положительный, то выход будет равен входу. Это облегчает решение «нелинейных» проблем.
    Если вы не знаете, какую функцию активации использовать, вас ждет relu.
  • (3,3): ядро ​​- сетка 3 * 3
  • 32 - количество фильтров ядра

[Пример] Для простоты мы возьмем изображение размером 6 на 6 пикселей с шкалой серого, в котором каждый пиксель имеет номер от 0 (черный ) до 255 (белый).

2*1 + 5*2+4*1 + 13*2 + 25*4 +15*2 +8*1+6*2+6*1=198

Тогда как насчет значений на полях? Нулевое заполнение может быть вариантом.
Представим, что матрица 2 на 2 окружена нулями. т.е.
0 0 0
0 1 0
0 8 2
0 * 1 + 0 * 2 + 0 * 1 + 0 * 2 + 1 * 4 + 0 * 2 + 0 * 1 + 8 * 2 + 2 * 1 = 4 + 16 + 2 = 22.
В кодировке мы можем заполнить нулями, присвоив padding = 'same'
т.е.
model.add (Layers.Conv2D (32, (3, 3) , padding = 'same', Activation = 'relu', input_shape = (32, 32, 3)))

Кроме того, у нас может быть 'no padding', который просто не будет учитывать значения на полях, не определяя переменную заполнения (как в нашем примере) или определяя padding = 'действительный'. В случае отсутствия заполнения мы видим, что размер вывода уменьшается до 4 на 4.

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

Можно сказать, что обучение - это процесс поиска лучшего ядра / фильтра 3 на 3. Каждое число в фильтрах концептуально совпадает с весами в линейной регрессии.

2 - MaxPooling: объединение максимального значения в запутанном изображении. Это метод понижающей дискретизации.

layers.MaxPooling2D((2, 2))

Мы создали запутанную сетку изображений, затем будем перемещаться по нашему ядру 2 на 2 и выбирать максимальное значение.
Также может быть назначена переменная stride, которая представляет собой количество пикселей, которые ядро ​​2 на 2 будет перемещать по запутанному изображению.

Вы видите, что размер нового изображения уменьшился вдвое?

IV-а. Краткое описание модели

model.summary () выведет это:

Как обычно, давайте интерпретируем числа, которые появляются в сводной таблице.

1 - model.add (Layers.Conv2D (32, (3, 3), padding = 'same', activate = 'relu', input_shape = (32, 32, 3)) → (Нет, 30,30,32), параметр # 896

  • Как мы и ожидали, наш ввод (32,32) был уменьшен до (30,30) для процесса без заполнения на 2 пикселя каждый.
  • 32 исходит из числа ядер. Каждое ядро ​​создаст одно запутанное изображение. Таким образом, всего будет 32 выходных канала сверток или карт функций.
  • Сколько у нас параметров?
    У нас есть ядро ​​размером 3 * 3 пикселя для каждого RGB. Итак, у нас есть 3 * 3 * 3 = 27 пикселей (весов) и 1 смещение для одного ядра. Подводя итог, (27 + 1) * 32 = 896 - это количество параметров.

2 —model.add (sizes.MaxPooling2D ((2, 2))) → (Нет, 15,15,32)

  • Мы сделали «понижающую дискретизацию» с помощью MaxPooling с сеткой 2 на 2, которая уменьшит размер изображения вдвое, сохранив количество функций; 32.

3 - model.add (Layers.Conv2D (64, (3, 3), activate = ’relu’)) → (Нет, 13,13,64), параметр # 18496

  • Выходные данные первого сверточного слоя были уменьшены с помощью предыдущего MaxPooling. Теперь субдискретизированная карта функций 15 на 15 снова будет проходить через фильтр свертки. Поскольку пиксели на полях удаляются из-за отсутствия заполнения, оно уменьшается до 13 на 13.
  • Этот сверточный слой имеет 64 ядра, которые имеют размер 3 на 3 пикселя.
  • Поскольку размер ввода был уменьшен, у нашего ИИ осталась некоторая емкость для дополнительных фильтров. Итак, во втором слое свертки у нас может быть 64 фильтра.
  • Сколько у нас параметров?
    Из первого сверточного слоя у нас есть 32 карты характеристик; наш 2-й сверточный слой принимает выходной канал из предыдущего сверточного слоя (или карту объектов) в качестве входных и выводит 64 объекта.
    У нас по-прежнему размер фильтра 3 на 3.
    Итак, у нас есть (3 * 3 * 32) веса и 1 смещение для каждого фильтра. Подводя итог, на втором уровне conv2d_7 есть параметры (3 * 3 * 32 + 1) * 64 = 18496.

Боковое примечание) 32 карты характеристик являются выходом 1-го сверточного слоя и входом 2-го сверточного слоя одновременно. Вы помните промежуточный продукт и конечный продукт из части «Введение в экономику ВВП»? Примените эту аналогию к CNN.

4 - model.add (sizes.MaxPooling2D ((2, 2))) → (None, 6,6,64)

  • Опять же, размер изображения уменьшился вдвое, сохранив те же 64 функции.

5 - model.add (sizes.Conv2D (64, (3, 3), activate = ’relu’)) → (None, 4,4,64) param # 36928

  • Здесь снова два пикселя от ширины изображения и высоты изображения удалены из-за отсутствия заполнения, и этот сверточный слой имеет 64 ядра, которые имеют размер 3 на 3 пикселя.
  • Сколько у нас параметров?
    Из второго сверточного слоя у нас есть 64 карты объектов на входе и 64 объекта на выходе. У нас по-прежнему размер фильтра 3 на 3.
    Итак, у нас есть (3 * 3 * 64) веса и 1 смещение для каждого фильтра. Подводя итог, на втором уровне conv2d_8 есть параметры (3 * 3 * 64 + 1) * 64 = 36928.

Наконец, 896 + 18496 + 36928 = 56320.

V. Добавьте плотные слои сверху

Мы должны провести классификацию, поэтому нам понадобится один или несколько плотных слоев. Поскольку плотный слой принимает в качестве вектора одномерный вектор, мы должны разбить и уложить выходные данные предыдущего сверточного слоя. Мы должны преобразовать трехмерный тензор (4,4,64) в одномерный тензор или вектор с 4 * 4 * 64 = 1024 элементами. Слой flatten () позволяет это.

Независимо от того, сколько у нас плотных слоев, у нас должен быть последний слой с 10 слоями (= количество классов) с функцией активации softmax.

model.add(layers.Flatten())
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(10, activation='softmax'))

V-а. Краткое описание модели

6 - model.add (sizes.Flatten ()) → (None, 1024)
Вход от последнего сверточного слоя имел выходной канал размером 4 * 4 и имел 64 канала. их. Сглаженный слой поместит все это как одномерный вектор, имеющий 4 * 4 * 64 элемента.

7 - model.add (Layers.Dense (64, Activation = 'relu')) → (None, 64) param # 65600
У нас есть 64 узла в этом плотном слое и 1024 входа от плоского слоя. Здесь у нас есть 1024 веса и 1 смещение для каждого узла. (1024 + 1) * 64 = 65600.

8 - model.add (Layers.Dense (10, activate = 'softmax')) → (None, 10), param # 650
У нас есть 10 узлов в этом плотном слое и 64 входы из предыдущего плотного слоя. (64 + 1) * 10 = 650.

Наконец, 896 + 18496 + 36928 + 65600 + 650 = 122570

VI. Скомпилируйте и обучите модель

model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])
history = model.fit(train_images, train_labels, epochs=10, 
                    validation_data=(test_images, test_labels))

VII. Оценить модель

test_loss, test_acc = model.evaluate(test_images,  test_labels, verbose=2)

У нас есть выход 10000 / 1–1 с - потеря: 0,8848 - точность: 0,7142.
Это не ужасно, но мало.

Продолжайте учиться и повышайте свою точность!

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