Ответы:
Хочу пояснить картинкой из C3D .
Короче говоря, направление свертки и форма вывода важны!
↑↑↑↑↑ 1D Convolutions - Базовая ↑↑↑↑↑
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 ↑↑↑↑↑
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 - Базовая ↑↑↑↑↑
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, ..., ↑↑↑↑↑
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)
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, ..., ↑↑↑↑↑
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)
- Исходная ссылка: ССЫЛКА
- Автор: Мартин Гёрнер
- Twitter: @martin_gorner
- Google +: plus.google.com/+MartinGorne
↑↑↑↑↑ 1D свертки с входом 1D ↑↑↑↑↑
↑↑↑↑↑ 1D свертки с двумерным вводом ↑↑↑↑↑
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()
1
, затем → для строки 1+stride
. Сама свертка инвариантна к сдвигу, так почему же направление свертки имеет значение?
После ответа от @runhani я добавляю еще несколько деталей, чтобы сделать объяснение немного более ясным, и попытаюсь объяснить это немного подробнее (и, конечно, с примерами из TF1 и TF2).
Одна из основных дополнительных составляющих, которые я включаю:
tf.Variable
Вот как можно сделать одномерную свертку с использованием TF 1 и TF 2.
И, чтобы быть конкретным, мои данные имеют следующие формы:
[batch size, width, in channels]
(например 1, 5, 1
)[width, in channels, out channels]
(например 5, 1, 4
)[batch size, width, out_channels]
(например 1, 5, 4
)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]]])}))
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-свертке. Если вы занимаетесь глубоким обучением, шансы, что вы не сталкивались с двумерной сверткой,… ну, почти ноль. Он используется в CNN для классификации изображений, обнаружения объектов и т. Д., А также в задачах НЛП, связанных с изображениями (например, генерация подписи к изображениям).
Давайте попробуем пример, у меня есть ядро свертки со следующими фильтрами:
И, чтобы быть конкретным, мои данные имеют следующие формы:
[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
)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)})
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-го фильтра.
В контексте двумерной свертки гораздо легче понять, что означают эти множественные каналы. Допустим, вы делаете распознавание лиц. Вы можете представить себе (это очень нереалистичное упрощение, но передает суть), каждый фильтр представляет глаз, рот, нос и т. Д. Таким образом, каждая карта функций будет двоичным представлением того, присутствует ли эта функция на изображении, которое вы предоставили. , Не думаю, что нужно подчеркивать, что для модели распознавания лиц это очень ценные функции. Больше информации в этой статье .
Это иллюстрация того, что я пытаюсь сформулировать.
Двумерная свертка очень распространена в сфере глубокого обучения.
CNN (сверточные нейронные сети) используют операцию двумерной свертки почти для всех задач компьютерного зрения (например, для классификации изображений, обнаружения объектов, классификации видео).
Теперь становится все труднее проиллюстрировать, что происходит, по мере увеличения количества измерений. Но с хорошим пониманием того, как работает свертка 1D и 2D, очень просто обобщить это понимание до свертки 3D. Итак, начнем.
И, чтобы быть конкретным, мои данные имеют следующие формы:
[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
)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))})
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')
Трехмерная свертка использовалась при разработке приложений машинного обучения с использованием данных 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
Ниже вы можете увидеть разницу.
Последнее слово : если вам очень любопытно, возможно, вам интересно. Мы просто сбросили бомбу на автоматическое уменьшение размеров и теперь говорим о разных успехах. Но самое лучшее в шаге - это то, что вы контролируете, когда, где и как уменьшаются размеры.
Таким образом, в 1D CNN ядро движется в одном направлении. Входные и выходные данные 1D CNN двумерны. В основном используется для данных временных рядов.
В 2D CNN ядро движется в 2 направлениях. Входные и выходные данные 2D CNN трехмерны. В основном используется для данных изображения.
В 3D CNN ядро движется в трех направлениях. Входные и выходные данные 3D CNN четырехмерны. В основном используется для данных 3D-изображений (МРТ, КТ).
Вы можете найти более подробную информацию здесь: https://medium.com/@xzz201920/conv1d-conv2d-and-conv3d-8a59182c4d6