Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

6.4 Многоканальные входы и выходы

В разделе 6.1.4.1 мы говорили о том, что каждое изображение состоит из нескольких каналов и что свёрточных слоёв может быть много. Например, цветное изображение имеет стандартные RGB-каналы — красный, зелёный и синий. Однако до сих пор мы рассматривали лишь упрощённый пример с одним входным и одним выходным каналом. Это позволяло считать вход, ядро и выход двумерными тензорами.

Когда мы добавляем каналы, и входы, и скрытые представления становятся трёхмерными тензорами. Например, каждое входное RGB-изображение имеет форму . Ось размером мы называем измерением канала (channel). В этом разделе мы подробнее рассмотрим ядра с несколькими входными и выходными каналами.

6.4.1 Несколько входных каналов

Когда вход содержит несколько каналов, нужно построить ядро с таким же числом входных каналов, как у входных данных, чтобы выполнять взаимную корреляцию. Пусть число входных каналов равно ; тогда у ядра тоже должно быть входных каналов. Если форма окна ядра равна , то при ядро можно считать двумерным тензором формы .

Однако при каждый входной канал ядра содержит тензор формы . Если объединить таких тензоров, получим ядро формы . Поскольку и вход, и ядро имеют по каналов, мы можем выполнить двумерную взаимную корреляцию двумерного тензора каждого входного канала с двумерным тензором соответствующего канала ядра, а затем просуммировать результаты по каналам (складываем результатов), получив один двумерный тензор. Это и есть результат двумерной взаимной корреляции для многоканального входа и ядра с несколькими входными каналами.

На рис. 6.4.1 показан пример двумерной взаимной корреляции с двумя входными каналами. Затенённая часть — первый элемент выхода, а также элементы входа и ядра, используемые для его вычисления: .

Рис. 6.4.1. Взаимная корреляция для двух входных каналов

Чтобы лучше понять эту операцию, реализуем многоканальную взаимную корреляцию. Коротко говоря, мы выполняем взаимную корреляцию для каждого канала и затем складываем результаты.

MXNet:

from d2l import mxnet as d2l
from mxnet import np, npx
npx.set_np()

PyTorch:

from d2l import torch as d2l
import torch

PaddlePaddle:

from d2l import paddle as d2l
import warnings
warnings.filterwarnings("ignore")
import paddle

MXNet, PyTorch, PaddlePaddle:

def corr2d_multi_in(X, K):
    # Перебираем нулевое измерение «X» и «K» (измерение каналов), затем складываем результаты
    return sum(d2l.corr2d(x, k) for x, k in zip(X, K))

TensorFlow:

from d2l import tensorflow as d2l
import tensorflow as tf

def corr2d_multi_in(X, K):
    # Перебираем нулевое измерение «X» и «K» (измерение каналов), затем складываем результаты
    return tf.reduce_sum([d2l.corr2d(x, k) for x, k in zip(X, K)], axis=0)

Построим входной тензор X и ядерный тензор K, соответствующие значениям с рис. 6.4.1, и проверим результат взаимной корреляции.

X = d2l.tensor([[[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]],
               [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]])
K = d2l.tensor([[[0.0, 1.0], [2.0, 3.0]], [[1.0, 2.0], [3.0, 4.0]]])

corr2d_multi_in(X, K)

6.4.2 Несколько выходных каналов

До сих пор, сколько бы входных каналов ни было, выходной канал у нас был один. Однако, как мы обсуждали в разделе 6.1.4.1, на каждом слое необходимо несколько выходных каналов. В самых популярных архитектурах нейронных сетей по мере углубления сети число выходных каналов часто увеличивают, одновременно уменьшая пространственное разрешение, чтобы получить большую глубину по каналам. Интуитивно можно считать каждый канал откликом на определённый признак. Реальность, впрочем, сложнее: каналы не обучаются независимо, а оптимизируются для совместного использования. Поэтому несколько выходных каналов — это не просто обучение нескольких одноканальных детекторов.

Обозначим число входных и выходных каналов через и , а высоту и ширину ядра — через и . Чтобы получить выход с несколькими каналами, для каждого выходного канала создадим ядерный тензор формы ; таким образом, ядро имеет форму . При взаимной корреляции каждый выходной канал сначала получает все входные каналы, а затем результат вычисляется с помощью ядра, соответствующего этому выходному каналу.

Ниже реализуем функцию взаимной корреляции, вычисляющую выход с несколькими каналами.

def corr2d_multi_in_out(X, K):
    # Перебираем нулевое измерение «K» и для каждого ядра выполняем взаимную корреляцию с входом «X».
    # Затем объединяем все результаты в один тензор
    return d2l.stack([corr2d_multi_in(X, k) for k in K], 0)

Соединив ядерный тензор K с K+1 (каждый элемент K увеличен на ) и K+2, построим ядро с выходными каналами.

K = d2l.stack((K, K + 1, K + 2), 0)
K.shape

Теперь выполним взаимную корреляцию входного тензора X и ядерного тензора K. Выход теперь содержит канала; результат первого канала совпадает с ранее полученным результатом для входа X с несколькими входными и одним выходным каналом.

corr2d_multi_in_out(X, K)

6.4.3 Свёрточные слои

Свёртка , то есть при , на первый взгляд кажется бессмысленной. Ведь суть свёртки — эффективно выделять корреляции между соседними пикселями, а свёртка этим явно не занимается. Тем не менее она очень популярна и часто входит в проекты сложных глубоких сетей. Разберём подробнее, какую роль она играет.

Поскольку окно минимально, свёртка утрачивает присущую свёрточным слоям способность выявлять взаимодействие соседних элементов по высоте и ширине. Фактически все вычисления свёртки происходят по каналам.

На рис. 6.4.2 показана взаимная корреляция с ядром при входных и выходных каналах. Здесь вход и выход имеют одинаковые высоту и ширину, а каждый элемент выхода — линейная комбинация элементов входа в той же позиции. Свёрточный слой можно рассматривать как полносвязный слой, применяемый в каждой позиции пикселя, который преобразует входных значений в выходных. Поскольку это по-прежнему свёрточный слой, веса одинаковы для всех пикселей. При этом свёрточный слой требует весов размерности и, кроме того, смещения.

Рис. 6.4.2. Взаимная корреляция с ядром при 3 входных и 2 выходных каналах. Вход и выход имеют одинаковые высоту и ширину

Теперь реализуем свёртку с помощью полносвязного слоя. Обратите внимание, что нам нужно изменить форму входных и выходных данных.

def corr2d_multi_in_out_1x1(X, K):
    c_i, h, w = X.shape
    c_o = K.shape[0]
    X = d2l.reshape(X, (c_i, h * w))
    K = d2l.reshape(K, (c_o, c_i))
    # Матричное умножение, как в полносвязном слое
    Y = d2l.matmul(K, X)
    return d2l.reshape(Y, (c_o, h, w))

При выполнении свёртки приведённая функция эквивалентна ранее реализованной функции взаимной корреляции corr2d_multi_in_out. Проверим это на примере данных.

MXNet, PyTorch, PaddlePaddle:

X = d2l.normal(0, 1, (3, 3, 3))
K = d2l.normal(0, 1, (2, 3, 1, 1))

TensorFlow:

X = d2l.normal((3, 3, 3), 0, 1)
K = d2l.normal((2, 3, 1, 1), 0, 1)
Y1 = corr2d_multi_in_out_1x1(X, K)
Y2 = corr2d_multi_in_out(X, K)
assert float(d2l.reduce_sum(d2l.abs(Y1 - Y2))) < 1e-6

Резюме

  • Несколько входных и выходных каналов позволяют расширять модель свёрточного слоя.
  • При применении к каждому пикселю свёрточный слой эквивалентен полносвязному слою.
  • Свёрточные слои обычно используют, чтобы менять число каналов в слое сети и управлять сложностью модели.

Упражнения

  1. Предположим, что у нас есть две свёрточные операции с ядрами размеров и (без нелинейной функции активации между ними).
    1. Докажите, что эти операции можно представить одной свёрткой.
    2. Какова размерность эквивалентного одного ядра?
    3. А верно ли обратное?
  2. Пусть вход имеет форму , ядро — , заполнение — , шаг — .
    1. Каковы вычислительные затраты прямого распространения (умножения и сложения)?
    2. Каков объём занимаемой памяти?
    3. Каков объём памяти при обратном распространении?
    4. Каковы вычислительные затраты обратного распространения?
  3. Если удвоить число входных и выходных каналов, насколько вырастет число вычислений? А что, если удвоить заполнение?
  4. Если высота и ширина ядра равны , какова вычислительная сложность прямого распространения?
  5. Совпадают ли полностью переменные Y1 и Y2 из последнего примера этого раздела? Почему?
  6. Как реализовать свёртку через матричное умножение, если окно свёртки не ?

MXNet: Обсуждение

PyTorch: Обсуждение

TensorFlow: Обсуждение

PaddlePaddle: Обсуждение