Відповіді:
Я хочу пояснити картиною із C3D .
Коротше кажучи, важливий складний напрямок та форма виходу !
↑↑↑↑↑ 1D згортки - основні ↑↑↑↑↑
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 згортки - основні ↑↑↑↑↑
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 згортки - основні ↑↑↑↑↑
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)
- Оригінальне посилання: LINK
- Автор: Martin Görner
- Twitter: @martin_gorner
- Google +: plus.google.com/+MartinGorne
↑↑↑↑↑ 1D Звитки з 1D входом ↑↑↑↑↑
↑↑↑↑↑ 1D Звитки з 2D входом ↑↑↑↑↑
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
Ось як можна зробити 1D згортку, використовуючи 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 згортка успішно використовується для завдання класифікації речень .
Off до 2D згортки. Якщо ви людина, яка глибоко навчається, шанси на те, що ви не натрапили на 2D згортку, це… ну нуль. Застосовується в CNN для класифікації зображень, виявлення об'єктів тощо, а також у проблемах NLP, що включають зображення (наприклад, генерація підписів зображень).
Спробуємо на прикладі, тут я отримав ядро згортки з такими фільтрами,
А якщо конкретніше, мої дані мають такі форми,
[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-го фільтра.
У контексті, якщо 2D згортка, набагато простіше зрозуміти, що означають ці кілька каналів. Скажіть, ви робите розпізнавання обличчя. Ви можете придумати (це дуже нереально спрощення, але отримує крапку) кожен фільтр являє собою око, рот, ніс тощо. Отже, щоб кожна карта функцій була двійковим зображенням того, чи є ця функція на зображенні, яке ви надали . Я не думаю, що мені потрібно наголошувати, що для моделі розпізнавання обличчя це дуже цінні риси. Більше інформації в цій статті .
Це ілюстрація того, що я намагаюся сформулювати.
2D згортка дуже поширена у царині глибокого навчання.
CNN (Convolution Neural Networks) використовують 2D операцію згортання майже для всіх завдань комп'ютерного зору (наприклад, класифікація зображень, виявлення об'єктів, класифікація відео).
Зараз стає все важче проілюструвати, що відбувається зі збільшенням кількості розмірів. Але з хорошим розумінням того, як працює 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')
3D-згортка була використана при розробці програм машинного навчання за участю даних LIDAR (Light Detection and Ranging), які мають тривимірний характер.
Гаразд, ти майже там. Тож тримайся. Давайте подивимось, що таке крок і підкладка. Вони досить інтуїтивні, якщо подумати над ними.
Якщо ви проїжджаєте через коридор, ви швидше потрапляєте туди за менше кроків. Але це також означає, що ви спостерігали менше оточення, ніж якщо б ви проходили по кімнаті. Давайте тепер підкріпимо наше розуміння і гарною картиною! Давайте розберемося в них через 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 ядро рухається в 1 напрямку. Вхідні та вихідні дані 1D CNN є двовимірними. В основному використовується на даних часових рядів.
У 2D CNN ядро рухається у двох напрямках. Вхідні та вихідні дані 2D CNN є тривимірними. В основному використовується для даних зображень.
У 3D CNN ядро рухається в 3 напрямках. Вхідні та вихідні дані 3D CNN - 4 мірні. В основному використовується для даних 3D-зображень (МРТ, КТ).
Більш детальну інформацію ви можете знайти тут: https://medium.com/@xzz201920/conv1d-conv2d-and-conv3d-8a59182c4d6