LeNet – это одна из первых успешных архитектур нейронных сетей для распознавания изображений, разработанная Яном Лекуном (Yann LeCun) и его коллегами в конце 1980-х годов. Эта архитектура была специально разработана для задачи распознавания рукописных цифр и символов, таких как те, что используются в почтовых индексах.
### Основные компоненты архитектуры LeNet:
1. Входной слой:
Входным слоем является изображение размером $32 \times 32$ пикселя. В случае работы с черно-белыми изображениями (например, рукописными цифрами), каждый пиксель представлен одним значением интенсивности.
2. Первый сверточный слой (Convolutional Layer):
После входного слоя следует первый сверточный слой, который применяет набор фильтров (или ядер свертки). Например, в оригинальной архитектуре LeNet использовалось 6 фильтров размером $5 \times 5$. Каждый фильтр применяется ко всему изображению, создавая карту признаков (feature map).
Формула для вычисления размера карты признаков после применения фильтра:
$$ \text{Размер карты признаков} = \frac{\text{размер изображения} - \text{размер фильтра} + 2 \cdot \text{пэддинг}}{stride} + 1 $$
3. Первый субдискретизирующий слой (Subsampling Layer):
Следующим шагом идет субдискретизация (также известная как пулинг). Обычно используется метод максимального пулинга ($max-pooling$), при котором выбирается максимальное значение из области, заданной окном пулинга. Это помогает уменьшить размер карт признаков и сделать сеть менее чувствительной к небольшим изменениям положения объектов в изображении.
4. Второй сверточный слой:
Аналогично первому, второй сверточный слой использует больше фильтров (в оригинальной версии LeNet их было 16) для извлечения более сложных признаков из карт признаков первого уровня.
5. Второй субдискретизирующий слой:
Еще раз применяется пулинг для уменьшения размеров карт признаков и снижения вычислительных затрат.
6. Полносвязанный слой (Fully Connected Layer):
Карты признаков преобразуются в одномерный вектор и подаются на полносвязанные слои. В оригинальной архитектуре LeNet использовались два полносвязанных слоя перед финальным классификатором.
7. Выходной слой:
Последний слой сети содержит столько же нейронов, сколько классов нужно различать. Для задачи классификации рукописных цифр выходного слоя будет содержать 10 нейронов (по одному на каждую цифру от 0 до 9).
### Важность LeNet
Хотя LeNet считается довольно простой сетью по современным стандартам, она заложила основу для многих последующих разработок в области глубокого обучения и компьютерного зрения. Многие современные архитектуры, такие как AlexNet, VGG, ResNet и другие, используют принципы, впервые продемонстрированные в LeNet.
Задача 1
В модель из предыдущего урока добавьте MaxPooling размером 2x2 после свёрточных слоёв. Вызовите функцию summary(), чтобы посмотреть на изменение количества параметров сети.
from tensorflow.keras import Sequential
from tensorflow.keras.layers import Conv2D, Flatten, Dense, MaxPooling2D
import matplotlib.pyplot as plt
import numpy as np
features_train = np.load('/datasets/fashion_mnist/train_features.npy')
target_train = np.load('/datasets/fashion_mnist/train_target.npy')
features_test = np.load('/datasets/fashion_mnist/test_features.npy')
target_test = np.load('/datasets/fashion_mnist/test_target.npy')
features_train = features_train.reshape(-1, 28, 28, 1) / 255.0
features_test = features_test.reshape(-1, 28, 28, 1) / 255.0
model = Sequential()
model.add(Conv2D(filters=4, kernel_size=(3, 3), padding='same',
activation="relu", input_shape=(28, 28, 1)))
model.add(Conv2D(filters=4, kernel_size=(3, 3), strides=2, padding='same',
activation="relu"))
model.add(MaxPooling2D(pool_size=(2, 2), strides=None, padding='valid'))
model.add(Flatten())
model.add(Dense(units=10, activation='softmax'))
model.compile(loss='sparse_categorical_crossentropy', optimizer='sgd', metrics=['acc'])
model.summary()
model.fit(features_train, target_train, epochs=1, verbose=1,
steps_per_epoch=1, batch_size=1)
Задача 2
Постройте архитектуру LeNet, изучив её описание в формате функции summary():
Скопировать код
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d (Conv2D) (None, 28, 28, 6) 156
_________________________________________________________________
average_pooling2d (AveragePo (None, 14, 14, 6) 0
_________________________________________________________________
conv2d_1 (Conv2D) (None, 10, 10, 16) 2416
_________________________________________________________________
average_pooling2d_1 (Average (None, 5, 5, 16) 0
_________________________________________________________________
flatten (Flatten) (None, 400) 0
_________________________________________________________________
dense (Dense) (None, 120) 48120
_________________________________________________________________
dense_1 (Dense) (None, 84) 10164
_________________________________________________________________
dense_2 (Dense) (None, 10) 850
=================================================================
Функция активации во всех слоях, кроме последнего, — гиперболический тангенс (англ. hyperbolic tangent). Сети с такой активацией обучаются лучше сетей с сигмоидой. Превзойти 'tanh' может только ReLU, но на момент разработки LeNet она ещё не применялась.
Вызовите функцию summary(). Запустите обучение на одном объекте, чтобы убедиться в работоспособности код.
from tensorflow.keras import Sequential
from tensorflow.keras.layers import Conv2D, Flatten, Dense, AvgPool2D
import matplotlib.pyplot as plt
import numpy as np
features_train = np.load('/datasets/fashion_mnist/train_features.npy')
target_train = np.load('/datasets/fashion_mnist/train_target.npy')
features_test = np.load('/datasets/fashion_mnist/test_features.npy')
target_test = np.load('/datasets/fashion_mnist/test_target.npy')
features_train = features_train.reshape(-1, 28, 28, 1) / 255.0
features_test = features_test.reshape(-1, 28, 28, 1) / 255.0
model = Sequential()
model.add(Conv2D(6, (5, 5), padding='same', activation='tanh',
input_shape=(28, 28, 1)))
model.add(AvgPool2D(pool_size=(2, 2)))
model.add(Conv2D(16, (5, 5), activation='tanh',
input_shape=(28, 28, 1)))
model.add(AvgPool2D(pool_size=(2, 2)))
model.add(Flatten())
model.add(Dense(units=120, activation='tanh'))
model.add(Dense(units=84, activation='tanh'))
model.add(Dense(units=10, activation='softmax'))
model.compile(loss='sparse_categorical_crossentropy', optimizer='sgd', metrics=['acc'])
model.summary()
model.fit(features_train, target_train, epochs=1, verbose=1,
steps_per_epoch=1, batch_size=1)