Интуитивное понимание 1D, 2D и 3D сверток в сверточных нейронных сетях

Ответы:

417

Хочу пояснить картинкой из C3D .

Короче говоря, направление свертки и форма вывода важны!

введите описание изображения здесь

↑↑↑↑↑ 1D Convolutions - Базовая ↑↑↑↑↑

  • всего 1 направление (ось времени) для расчета конверсии
  • вход = [W], фильтр = [k], выход = [W]
  • пример) вход = [1,1,1,1,1], фильтр = [0,25,0,5,0,25], выход = [1,1,1,1,1]
  • форма вывода - это одномерный массив
  • пример) сглаживание графика

tf.nn.conv1d code Пример игрушки

import tensorflow as tf
import numpy as np

sess = tf.Session()

ones_1d = np.ones(5)
weight_1d = np.ones(3)
strides_1d = 1

in_1d = tf.constant(ones_1d, dtype=tf.float32)
filter_1d = tf.constant(weight_1d, dtype=tf.float32)

in_width = int(in_1d.shape[0])
filter_width = int(filter_1d.shape[0])

input_1d   = tf.reshape(in_1d, [1, in_width, 1])
kernel_1d = tf.reshape(filter_1d, [filter_width, 1, 1])
output_1d = tf.squeeze(tf.nn.conv1d(input_1d, kernel_1d, strides_1d, padding='SAME'))
print sess.run(output_1d)

введите описание изображения здесь

↑↑↑↑↑ 2D Convolutions - Basic ↑↑↑↑↑

  • 2 -направление (x, y) для вычисления conv
  • форма вывода - это 2D- матрица
  • input = [W, H], filter = [k, k] output = [W, H]
  • пример) Sobel Egde Fllter

tf.nn.conv2d - Пример игрушки

ones_2d = np.ones((5,5))
weight_2d = np.ones((3,3))
strides_2d = [1, 1, 1, 1]

in_2d = tf.constant(ones_2d, dtype=tf.float32)
filter_2d = tf.constant(weight_2d, dtype=tf.float32)

in_width = int(in_2d.shape[0])
in_height = int(in_2d.shape[1])

filter_width = int(filter_2d.shape[0])
filter_height = int(filter_2d.shape[1])

input_2d   = tf.reshape(in_2d, [1, in_height, in_width, 1])
kernel_2d = tf.reshape(filter_2d, [filter_height, filter_width, 1, 1])

output_2d = tf.squeeze(tf.nn.conv2d(input_2d, kernel_2d, strides=strides_2d, padding='SAME'))
print sess.run(output_2d)

введите описание изображения здесь

↑↑↑↑↑ 3D Convolutions - Базовая ↑↑↑↑↑

  • 3 -направление (x, y, z) для вычисления conv
  • форма вывода - это 3D- объем
  • input = [W, H, L ], filter = [k, k, d ] output = [W, H, M]
  • d <L важно! для создания объемного вывода
  • пример) C3D

tf.nn.conv3d - Пример игрушки

ones_3d = np.ones((5,5,5))
weight_3d = np.ones((3,3,3))
strides_3d = [1, 1, 1, 1, 1]

in_3d = tf.constant(ones_3d, dtype=tf.float32)
filter_3d = tf.constant(weight_3d, dtype=tf.float32)

in_width = int(in_3d.shape[0])
in_height = int(in_3d.shape[1])
in_depth = int(in_3d.shape[2])

filter_width = int(filter_3d.shape[0])
filter_height = int(filter_3d.shape[1])
filter_depth = int(filter_3d.shape[2])

input_3d   = tf.reshape(in_3d, [1, in_depth, in_height, in_width, 1])
kernel_3d = tf.reshape(filter_3d, [filter_depth, filter_height, filter_width, 1, 1])

output_3d = tf.squeeze(tf.nn.conv3d(input_3d, kernel_3d, strides=strides_3d, padding='SAME'))
print sess.run(output_3d)

введите описание изображения здесь

↑↑↑↑↑ 2D-свертки с 3D-вводом - LeNet, VGG, ..., ↑↑↑↑↑

  • Несмотря на то, что вход 3D, например, 224x224x3, 112x112x32
  • output-shape - это не 3D- объем, а 2D- матрица
  • потому что глубина фильтра = L должна соответствовать входным каналам = L
  • 2 -направление (x, y) для вычисления conv! не 3D
  • input = [W, H, L ], filter = [k, k, L ] output = [W, H]
  • форма вывода - это 2D- матрица
  • что, если мы хотим обучить N фильтров (N - количество фильтров)
  • тогда выходная форма представляет собой (сложенную 2D) матрицу 3D = 2D x N.

conv2d - LeNet, VGG, ... для 1 фильтра

in_channels = 32 # 3 for RGB, 32, 64, 128, ... 
ones_3d = np.ones((5,5,in_channels)) # input is 3d, in_channels = 32
# filter must have 3d-shpae with in_channels
weight_3d = np.ones((3,3,in_channels)) 
strides_2d = [1, 1, 1, 1]

in_3d = tf.constant(ones_3d, dtype=tf.float32)
filter_3d = tf.constant(weight_3d, dtype=tf.float32)

in_width = int(in_3d.shape[0])
in_height = int(in_3d.shape[1])

filter_width = int(filter_3d.shape[0])
filter_height = int(filter_3d.shape[1])

input_3d   = tf.reshape(in_3d, [1, in_height, in_width, in_channels])
kernel_3d = tf.reshape(filter_3d, [filter_height, filter_width, in_channels, 1])

output_2d = tf.squeeze(tf.nn.conv2d(input_3d, kernel_3d, strides=strides_2d, padding='SAME'))
print sess.run(output_2d)

conv2d - LeNet, VGG, ... для N фильтров

in_channels = 32 # 3 for RGB, 32, 64, 128, ... 
out_channels = 64 # 128, 256, ...
ones_3d = np.ones((5,5,in_channels)) # input is 3d, in_channels = 32
# filter must have 3d-shpae x number of filters = 4D
weight_4d = np.ones((3,3,in_channels, out_channels))
strides_2d = [1, 1, 1, 1]

in_3d = tf.constant(ones_3d, dtype=tf.float32)
filter_4d = tf.constant(weight_4d, dtype=tf.float32)

in_width = int(in_3d.shape[0])
in_height = int(in_3d.shape[1])

filter_width = int(filter_4d.shape[0])
filter_height = int(filter_4d.shape[1])

input_3d   = tf.reshape(in_3d, [1, in_height, in_width, in_channels])
kernel_4d = tf.reshape(filter_4d, [filter_height, filter_width, in_channels, out_channels])

#output stacked shape is 3D = 2D x N matrix
output_3d = tf.nn.conv2d(input_3d, kernel_4d, strides=strides_2d, padding='SAME')
print sess.run(output_3d)

введите описание изображения здесь ↑↑↑↑↑ Бонус 1x1 в CNN - GoogLeNet, ..., ↑↑↑↑↑

  • 1x1 conv сбивает с толку, когда вы думаете, что это фильтр 2D-изображений, такой как sobel
  • для конверсии 1x1 в CNN вводится трехмерная форма, как на рисунке выше.
  • он вычисляет глубинную фильтрацию
  • input = [W, H, L], filter = [1,1, L] output = [W, H]
  • выходная сложенная форма - это матрица 3D = 2D x N.

tf.nn.conv2d - специальный случай 1x1 conv

in_channels = 32 # 3 for RGB, 32, 64, 128, ... 
out_channels = 64 # 128, 256, ...
ones_3d = np.ones((1,1,in_channels)) # input is 3d, in_channels = 32
# filter must have 3d-shpae x number of filters = 4D
weight_4d = np.ones((3,3,in_channels, out_channels))
strides_2d = [1, 1, 1, 1]

in_3d = tf.constant(ones_3d, dtype=tf.float32)
filter_4d = tf.constant(weight_4d, dtype=tf.float32)

in_width = int(in_3d.shape[0])
in_height = int(in_3d.shape[1])

filter_width = int(filter_4d.shape[0])
filter_height = int(filter_4d.shape[1])

input_3d   = tf.reshape(in_3d, [1, in_height, in_width, in_channels])
kernel_4d = tf.reshape(filter_4d, [filter_height, filter_width, in_channels, out_channels])

#output stacked shape is 3D = 2D x N matrix
output_3d = tf.nn.conv2d(input_3d, kernel_4d, strides=strides_2d, padding='SAME')
print sess.run(output_3d)

Анимация (2D Conv с 3D-входами)

введите описание изображения здесь - Исходная ссылка: ССЫЛКА
- Автор: Мартин Гёрнер
- Twitter: @martin_gorner
- Google +: plus.google.com/+MartinGorne

Бонусные 1D свертки с 2D вводом

введите описание изображения здесь ↑↑↑↑↑ 1D свертки с входом 1D ↑↑↑↑↑

введите описание изображения здесь ↑↑↑↑↑ 1D свертки с двумерным вводом ↑↑↑↑↑

  • Хотя вход 2D, например) 20x14
  • output-shape не 2D , а 1D матрица
  • потому что высота фильтра = L должна соответствовать входной высоте = L
  • 1 -направление (x) для вычисления конверсии! не 2D
  • вход = [W, L ], фильтр = [k, L ] выход = [W]
  • форма вывода - 1D матрица
  • что, если мы хотим обучить N фильтров (N - количество фильтров)
  • тогда выходная форма представляет собой (сложенную 1D) матрицу 2D = 1D x N.

Бонус C3D

in_channels = 32 # 3, 32, 64, 128, ... 
out_channels = 64 # 3, 32, 64, 128, ... 
ones_4d = np.ones((5,5,5,in_channels))
weight_5d = np.ones((3,3,3,in_channels,out_channels))
strides_3d = [1, 1, 1, 1, 1]

in_4d = tf.constant(ones_4d, dtype=tf.float32)
filter_5d = tf.constant(weight_5d, dtype=tf.float32)

in_width = int(in_4d.shape[0])
in_height = int(in_4d.shape[1])
in_depth = int(in_4d.shape[2])

filter_width = int(filter_5d.shape[0])
filter_height = int(filter_5d.shape[1])
filter_depth = int(filter_5d.shape[2])

input_4d   = tf.reshape(in_4d, [1, in_depth, in_height, in_width, in_channels])
kernel_5d = tf.reshape(filter_5d, [filter_depth, filter_height, filter_width, in_channels, out_channels])

output_4d = tf.nn.conv3d(input_4d, kernel_5d, strides=strides_3d, padding='SAME')
print sess.run(output_4d)

sess.close()

Ввод и вывод в Tensorflow

введите описание изображения здесь

введите описание изображения здесь

Резюме

введите описание изображения здесь

runhani
источник
18
Учитывая ваш труд и ясность в объяснениях, 8 голосов слишком мало.
user3282777
3
Двухмерное преобразование с трехмерным вводом - приятный штрих. Я бы предложил отредактировать, чтобы включить 1d conv с 2d входом (например, многоканальный массив) и сравнить их разницу с 2d conv с 2d входом.
SumNeuron
2
Замечательный ответ!
Бен
Почему направление свертки в 2d ↲. Я видел источники, которые утверждают, что направление для строки 1, затем для строки 1+stride. Сама свертка инвариантна к сдвигу, так почему же направление свертки имеет значение?
Минь Чиет
Спасибо Вам за Ваш вопрос. Да! свертка сама по себе инвариантна относительно сдвига. так что для вычисления направление свертки не имеет значения. (Вы можете вычислить 2d свертку с двумя большими матричными умножениями. Фреймворк caffe уже сделал), но для понимания лучше объяснить направление свертки. потому что 2d conv с 3d вводом сбивает с толку без направления. ^^
runhani
6

После ответа от @runhani я добавляю еще несколько деталей, чтобы сделать объяснение немного более ясным, и попытаюсь объяснить это немного подробнее (и, конечно, с примерами из TF1 и TF2).

Одна из основных дополнительных составляющих, которые я включаю:

  • Акцент на приложениях
  • Использование tf.Variable
  • Более четкое объяснение входов / ядер / выходов 1D / 2D / 3D свертки
  • Эффекты шага / заполнения

1D свертка

Вот как можно сделать одномерную свертку с использованием TF 1 и TF 2.

И, чтобы быть конкретным, мои данные имеют следующие формы:

  • 1D вектор - [batch size, width, in channels](например 1, 5, 1)
  • Ядро - [width, in channels, out channels](например 5, 1, 4)
  • Выход - [batch size, width, out_channels](например 1, 5, 4)

Пример TF1

import tensorflow as tf
import numpy as np

inp = tf.placeholder(shape=[None, 5, 1], dtype=tf.float32)
kernel = tf.Variable(tf.initializers.glorot_uniform()([5, 1, 4]), dtype=tf.float32)
out = tf.nn.conv1d(inp, kernel, stride=1, padding='SAME')

with tf.Session() as sess:
  tf.global_variables_initializer().run()
  print(sess.run(out, feed_dict={inp: np.array([[[0],[1],[2],[3],[4]],[[5],[4],[3],[2],[1]]])}))

Пример TF2

import tensorflow as tf
import numpy as np

inp = np.array([[[0],[1],[2],[3],[4]],[[5],[4],[3],[2],[1]]]).astype(np.float32)
kernel = tf.Variable(tf.initializers.glorot_uniform()([5, 1, 4]), dtype=tf.float32)
out = tf.nn.conv1d(inp, kernel, stride=1, padding='SAME')
print(out)

С TF2 гораздо меньше работы, так как TF2 не нужен Sessionи variable_initializerнапример.

Как это могло бы выглядеть в реальной жизни?

Итак, давайте разберемся, что это делает, на примере сглаживания сигнала. Слева вы получили оригинал, а справа вы получили результат Convolution 1D, который имеет 3 выходных канала.

введите описание изображения здесь

Что означают несколько каналов?

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

Приложения глубокого обучения одномерной свертки

1D свертка успешно использовалась для задачи классификации предложений .

2D свертка

Переход к 2D-свертке. Если вы занимаетесь глубоким обучением, шансы, что вы не сталкивались с двумерной сверткой,… ну, почти ноль. Он используется в CNN для классификации изображений, обнаружения объектов и т. Д., А также в задачах НЛП, связанных с изображениями (например, генерация подписи к изображениям).

Давайте попробуем пример, у меня есть ядро ​​свертки со следующими фильтрами:

  • Ядро обнаружения краев (окно 3x3)
  • Ядро размытия (окно 3x3)
  • Заточка ядра (окно 3x3)

И, чтобы быть конкретным, мои данные имеют следующие формы:

  • Изображение (черно-белое) - [batch_size, height, width, 1](например 1, 340, 371, 1)
  • Ядро (также известное как фильтры) - [height, width, in channels, out channels](например 3, 3, 1, 3)
  • Выходные данные (также известные как карты функций) - [batch_size, height, width, out_channels](например 1, 340, 371, 3)

Пример TF1,

import tensorflow as tf
import numpy as np
from PIL import Image

im = np.array(Image.open(<some image>).convert('L'))#/255.0

kernel_init = np.array(
    [
     [[[-1, 1.0/9, 0]],[[-1, 1.0/9, -1]],[[-1, 1.0/9, 0]]],
     [[[-1, 1.0/9, -1]],[[8, 1.0/9,5]],[[-1, 1.0/9,-1]]],
     [[[-1, 1.0/9,0]],[[-1, 1.0/9,-1]],[[-1, 1.0/9, 0]]]
     ])

inp = tf.placeholder(shape=[None, image_height, image_width, 1], dtype=tf.float32)
kernel = tf.Variable(kernel_init, dtype=tf.float32)
out = tf.nn.conv2d(inp, kernel, strides=[1,1,1,1], padding='SAME')

with tf.Session() as sess:
  tf.global_variables_initializer().run()
  res = sess.run(out, feed_dict={inp: np.expand_dims(np.expand_dims(im,0),-1)})

Пример TF2

import tensorflow as tf
import numpy as np
from PIL import Image

im = np.array(Image.open(<some image>).convert('L'))#/255.0
x = np.expand_dims(np.expand_dims(im,0),-1)

kernel_init = np.array(
    [
     [[[-1, 1.0/9, 0]],[[-1, 1.0/9, -1]],[[-1, 1.0/9, 0]]],
     [[[-1, 1.0/9, -1]],[[8, 1.0/9,5]],[[-1, 1.0/9,-1]]],
     [[[-1, 1.0/9,0]],[[-1, 1.0/9,-1]],[[-1, 1.0/9, 0]]]
     ])

kernel = tf.Variable(kernel_init, dtype=tf.float32)

out = tf.nn.conv2d(x, kernel, strides=[1,1,1,1], padding='SAME')

Как это могло бы выглядеть в реальной жизни?

Здесь вы можете увидеть результат, полученный с помощью приведенного выше кода. Первое изображение является оригинальным и идет по часовой стрелке, у вас есть выходы 1-го фильтра, 2-го фильтра и 3-го фильтра. введите описание изображения здесь

Что означают несколько каналов?

В контексте двумерной свертки гораздо легче понять, что означают эти множественные каналы. Допустим, вы делаете распознавание лиц. Вы можете представить себе (это очень нереалистичное упрощение, но передает суть), каждый фильтр представляет глаз, рот, нос и т. Д. Таким образом, каждая карта функций будет двоичным представлением того, присутствует ли эта функция на изображении, которое вы предоставили. , Не думаю, что нужно подчеркивать, что для модели распознавания лиц это очень ценные функции. Больше информации в этой статье .

Это иллюстрация того, что я пытаюсь сформулировать.

введите описание изображения здесь

Приложения глубокого обучения 2D-свертки

Двумерная свертка очень распространена в сфере глубокого обучения.

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

3D свертка

Теперь становится все труднее проиллюстрировать, что происходит, по мере увеличения количества измерений. Но с хорошим пониманием того, как работает свертка 1D и 2D, очень просто обобщить это понимание до свертки 3D. Итак, начнем.

И, чтобы быть конкретным, мои данные имеют следующие формы:

  • 3D данные (LIDAR) - [batch size, height, width, depth, in channels](например 1, 200, 200, 200, 1)
  • Ядро - [height, width, depth, in channels, out channels](например 5, 5, 5, 1, 3)
  • Выход - [batch size, width, height, width, depth, out_channels](например 1, 200, 200, 2000, 3)

Пример TF1

import tensorflow as tf
import numpy as np

tf.reset_default_graph()

inp = tf.placeholder(shape=[None, 200, 200, 200, 1], dtype=tf.float32)
kernel = tf.Variable(tf.initializers.glorot_uniform()([5,5,5,1,3]), dtype=tf.float32)
out = tf.nn.conv3d(inp, kernel, strides=[1,1,1,1,1], padding='SAME')

with tf.Session() as sess:
  tf.global_variables_initializer().run()
  res = sess.run(out, feed_dict={inp: np.random.normal(size=(1,200,200,200,1))})

Пример TF2

import tensorflow as tf
import numpy as np

x = np.random.normal(size=(1,200,200,200,1))
kernel = tf.Variable(tf.initializers.glorot_uniform()([5,5,5,1,3]), dtype=tf.float32)
out = tf.nn.conv3d(x, kernel, strides=[1,1,1,1,1], padding='SAME') 

Приложения для глубокого обучения 3D-свертки

Трехмерная свертка использовалась при разработке приложений машинного обучения с использованием данных LIDAR (Light Detection and Ranging), которые по своей природе являются трехмерными.

Что ... больше жаргона ?: Шаг и отступы

Хорошо, ты почти у цели. Так что держись. Посмотрим, что такое stride и padding. Если задуматься, они довольно интуитивны.

Если вы перейдете через коридор, вы доберетесь туда быстрее, сделав меньше шагов. Но это также означает, что вы наблюдали меньше окружающих, чем если бы вы шли через комнату. Давайте теперь подкрепим наше понимание красивой картинкой! Давайте разберемся с этим с помощью 2D-свертки.

Понимание шага

Шаг свертки

Когда вы используете, tf.nn.conv2dнапример, вам нужно установить его как вектор из 4 элементов. Нет причин пугаться этого. Он просто содержит шаги в следующем порядке.

  • 2D свертка - [batch stride, height stride, width stride, channel stride]. Здесь пакетный шаг и шаг канала вы просто установили на один (я внедрял модели глубокого обучения в течение 5 лет, и мне никогда не приходилось настраивать их ни на что, кроме одного). Таким образом, у вас остается только 2 шага.

  • 3D свертка - [batch stride, height stride, width stride, depth stride, channel stride]. Здесь вы беспокоитесь только о шагах высоты / ширины / глубины.

Понимание заполнения

Теперь вы замечаете, что независимо от того, насколько мал ваш шаг (т.е. 1), во время свертки происходит неизбежное уменьшение размеров (например, ширина равна 3 после свертки изображения шириной 4 единицы). Это нежелательно, особенно при построении нейронных сетей с глубокой сверткой. Здесь на помощь приходит набивка. Есть два наиболее часто используемых типа заполнения.

  • SAME и VALID

Ниже вы можете увидеть разницу.

введите описание изображения здесь

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

thushv89
источник
2
  1. CNN 1D, 2D или 3D относится к направлению свертки, а не к измерению входа или фильтра.

  2. Для 1-канального входа CNN2D равно CNN1D: длина ядра = длина входа. (1 направление конв.)

Джерри Лю
источник
2

Таким образом, в 1D CNN ядро ​​движется в одном направлении. Входные и выходные данные 1D CNN двумерны. В основном используется для данных временных рядов.

В 2D CNN ядро ​​движется в 2 направлениях. Входные и выходные данные 2D CNN трехмерны. В основном используется для данных изображения.

В 3D CNN ядро ​​движется в трех направлениях. Входные и выходные данные 3D CNN четырехмерны. В основном используется для данных 3D-изображений (МРТ, КТ).

Вы можете найти более подробную информацию здесь: https://medium.com/@xzz201920/conv1d-conv2d-and-conv3d-8a59182c4d6

zz x
источник