.. _sec_nin: Сеть в сети (NiN) ================= LeNet, AlexNet и VGG имеют общую схему проектирования: они извлекают признаки, используя *пространственную* структуру, посредством последовательности свёрточных и пулинг-слоёв, а затем обрабатывают полученные представления с помощью полносвязных слоёв. Улучшения, внесённые в LeNet моделями AlexNet и VGG, заключаются преимущественно в том, как эти более поздние сети расширяют и углубляют указанные два модуля. Такой подход порождает две серьезные проблемы. Во-первых, полносвязные слои в конце архитектуры потребляют огромное количество параметров. Например, даже простая модель, такая как VGG-11, требует массивной матрицы, занимающей почти 400 МБ оперативной памяти в одинарной точности (FP32). Это является серьезным препятствием для вычислений, особенно на мобильных и встроенных устройствах. В конце концов, даже флагманские смартфоны оснащаются не более чем 8 ГБ оперативной памяти. На момент создания VGG этот показатель был на порядок меньше (в iPhone 4S было всего 512 МБ). Таким образом, было бы трудно оправдать выделение большей части памяти под классификатор изображений. Во-вторых, также невозможно добавить полносвязные слои на более ранних этапах сети для повышения степени нелинейности: это разрушило бы пространственную структуру и потребовало бы, возможно, ещё больше памяти. Блоки *network in network* (*NiN*) :cite:`Lin.Chen.Yan.2013` предлагают альтернативу, способную решить обе проблемы с помощью одной простой стратегии. Они были предложены на основе очень простого наблюдения: (i) использовать :math:`1 \times 1` свёртки для добавления локальных нелинейностей к активациям каналов и (ii) использовать глобальный пулинг для агрегации по всем позициям в последнем слое представления. Обратите внимание, что глобальный пулинг не был бы эффективным, если бы не были добавлены нелинейности. Давайте разберёмся в этом подробнее. .. raw:: latex \diilbookstyleinputcell .. code:: python import torch from torch import nn from d2l import torch as d2l Блоки NiN --------- Вспомним :numref:`subsec_1x1`. В нём мы говорили, что входные и выходные данные слоёв свёртки представляют собой четырёхмерные тензоры, оси которых соответствуют примеру, каналу, высоте и ширине. Также отметим, что входные и выходные данные полносвязных слоёв обычно являются двумерными тензорами, соответствующими примеру и признаку. Идея NiN заключается в применении полносвязного слоя к каждой позиции пикселя (для каждой высоты и ширины). Получающуюся :math:`1 \times 1` свёртку можно рассматривать как полносвязный слой, действующий независимо на каждой позиции пикселя. :numref:`fig_nin` иллюстрирует основные структурные различия между VGG и NiN, а также их блоков. Обратите внимание как на различия в блоках NiN (за начальной свёрткой следуют :math:`1 \times 1` свёрток, тогда как VGG сохраняет :math:`3 \times 3` свёрток), так и на финальный этап, где нам больше не нужен гигантский полносвязный слой. .. _fig_nin: .. figure:: ../img/nin.svg :width: 600px Сравнение архитектур VGG и NiN, а также их блоков. .. raw:: latex \diilbookstyleinputcell .. code:: python def nin_block(out_channels, kernel_size, strides, padding): return nn.Sequential( nn.LazyConv2d(out_channels, kernel_size, strides, padding), nn.ReLU(), nn.LazyConv2d(out_channels, kernel_size=1), nn.ReLU(), nn.LazyConv2d(out_channels, kernel_size=1), nn.ReLU()) Модель NiN ---------- NiN использует те же начальные размеры свёрток, что и AlexNet (он был предложен вскоре после этого). Размеры ядер равны :math:`11\times 11`, :math:`5\times 5` и :math:`3\times 3` соответственно, а количество выходных каналов совпадает с таковыми в AlexNet. Каждый блок NiN завершается слоем максимального пулинга с шагом 2 и формой окна :math:`3\times 3`. Второе существенное отличие NiN от AlexNet и VGG заключается в том, что NiN полностью избегает использования полносвязных слоёв. Вместо этого NiN использует блок NiN с числом выходных каналов, равным числу классов меток, за которым следует слой *глобального* усредняющего пулинга, в результате чего получается вектор логитов. Такая конструкция значительно сокращает количество требуемых параметров модели, хотя и ценой возможного увеличения времени обучения. .. raw:: latex \diilbookstyleinputcell .. code:: python class NiN(d2l.Classifier): def __init__(self, lr=0.1, num_classes=10): super().__init__() self.save_hyperparameters() self.net = nn.Sequential( nin_block(96, kernel_size=11, strides=4, padding=0), nn.MaxPool2d(3, stride=2), nin_block(256, kernel_size=5, strides=1, padding=2), nn.MaxPool2d(3, stride=2), nin_block(384, kernel_size=3, strides=1, padding=1), nn.MaxPool2d(3, stride=2), nn.Dropout(0.5), nin_block(num_classes, kernel_size=3, strides=1, padding=1), nn.AdaptiveAvgPool2d((1, 1)), nn.Flatten()) self.net.apply(d2l.init_cnn) Мы создаём пример данных, чтобы посмотреть **форму вывода каждого блока**. .. raw:: latex \diilbookstyleinputcell .. code:: python NiN().layer_summary((1, 1, 224, 224)) Обучение -------- Как и ранее, мы используем Fashion-MNIST для обучения модели с тем же оптимизатором, что и при обучении AlexNet и VGG. .. raw:: latex \diilbookstyleinputcell .. code:: python model = NiN(lr=0.05) trainer = d2l.Trainer(max_epochs=10, num_gpus=1) data = d2l.FashionMNIST(batch_size=128, resize=(224, 224)) model.apply_init([next(iter(data.get_dataloader(True)))[0]], d2l.init_cnn) trainer.fit(model, data) Итоги ----- NiN содержит значительно меньше параметров, чем AlexNet и VGG. Это обусловлено прежде всего тем, что в нём отсутствуют огромные полносвязные слои. Вместо этого он использует глобальный пулинг по среднему значению для агрегации данных по всем позициям изображения после последнего этапа основной части сети. Это устраняет необходимость в дорогостоящих (обучаемых) операциях снижения размерности, заменяя их простым усреднением. Что удивило исследователей в то время, так это то, что операция усреднения не ухудшила точность. Обратите внимание, что усреднение по представлению с низким разрешением (с большим количеством каналов) также способствует повышению степени инвариантности к сдвигам, которую может обрабатывать сеть. Выбор меньшего числа свёрток с широкими ядрами и замена их на :math:`1 \times 1` свёрток дополнительно способствует сокращению количества параметров. Это позволяет моделировать значительное количество нелинейностей в пределах каналов для любого заданного положения. Как :math:`1 \times 1` свёртки, так и глобальный пулинг оказали существенное влияние на последующие архитектуры свёрточных нейронных сетей. Упражнения ---------- 1. Почему в каждом блоке NiN используется два свёрточных слоя :math:`1\times 1`? Увеличьте их количество до трёх. Уменьшите до одного. Что изменится? 2. Что изменится, если заменить свёртки :math:`1 \times 1` на свёртки :math:`3 \times 3`? 3. Что произойдет, если заменить глобальный средний пулинг на полносвязный слой (скорость, точность, количество параметров)? 4. Вычислите использование ресурсов для NiN. 1. Сколько параметров? 2. Каков объём вычислений? 3. Какой объём памяти требуется во время обучения? 4. Какой объём памяти требуется при предсказании? 5. Какие могут возникнуть проблемы при одномоментном сведении представления :math:`384 \times 5 \times 5` к представлению :math:`10 \times 5 \times 5`? 6. Используйте структурные решения, примененные в VGG для создания VGG-11, VGG-16 и VGG-19, для проектирования семейства сетей, аналогичных NiN. `Обсуждения `__