Інтуїтивне розуміння 1D, 2D і 3D згортків у конволюційних нейронних мережах


Відповіді:


415

Я хочу пояснити картиною із C3D .

Коротше кажучи, важливий складний напрямок та форма виходу !

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

↑↑↑↑↑ 1D згортки - основні ↑↑↑↑↑

  • всього 1 -направлення (часова вісь) для обчислення конв
  • вхід = [Вт], фільтр = [к], вихід = [Вт]
  • ex) вхід = [1,1,1,1,1], фільтр = [0,25,0,5,0,25], вихід = [1,1,1,1,1]
  • форма виводу - це 1D масив
  • приклад) згладжування графіка

tf.nn.conv1d код Приклад іграшки

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 згортки - основні ↑↑↑↑↑

  • 2 -направлення (x, y) для обчислення конв
  • форма виходу - 2D матриця
  • вхід = [Вт, Н], фільтр = [к, к] вихід = [Вт, Н]
  • приклад) 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 згортки - основні ↑↑↑↑↑

  • 3 -направлення (x, y, z) для обчислення конв
  • форма виводу - 3D об'єм
  • input = [W, H, L ], фільтр = [k, k, d ] вихід = [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
  • форма виводу - це не 3D об'єм, а 2D матриця
  • тому що глибина фільтра = L повинна відповідати вхідним каналам = L
  • 2 -направлення (x, y) для обчислення conv! не 3D
  • input = [W, H, L ], фільтр = [k, k, L ] вихід = [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, вхід має форму 3D, як показано на малюнку вище.
  • він обчислює глибинну фільтрацію
  • input = [W, H, L], фільтр = [1,1, L] вихід = [W, H]
  • Форма складеного виводу - матриця 3D = 2D x N.

tf.nn.conv2d - окремий корпус 1x1 конв

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-входами)

введіть тут опис зображення - Оригінальне посилання: LINK
- Автор: Martin Görner
- Twitter: @martin_gorner
- Google +: plus.google.com/+MartinGorne

Бонусні 1D згортки з 2D входом

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

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

  • Навіть вхід 2D колишній) 20x14
  • форма виводу не 2D , а 1D матриця
  • тому що висота фільтра = L повинна відповідати вхідній висоті = L
  • 1 -направлення (x) для обчислення conv! не 2D
  • input = [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

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

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

Підсумок

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


18
Беручи до уваги вашу працю та ясність у поясненнях, результатів у 8 є занадто менше.
користувач3282777

3
2-конв. З 3d входом - приємний штрих. Я б запропонував редагувати, щоб включити 1d conv з 2d входом (наприклад, багатоканальний масив) і порівняти їх різницю з 2d conv з 2d входом.
SumNeuron

2
Дивовижна відповідь!
Бен

Чому напрямок конв у 2d ↲. Я бачив джерела, які стверджують, що напрямок для рядка 1, потім для рядка 1+stride. Сама згортання є інваріантною за зміною, тому чому має значення напрям згортки?
Мінь Трієт

Дякую за запитання Так! сама згортання є інваріантною. тому для розрахунку напрямок conv не має значення. (Ви можете обчислити 2d conv з двома великими множеннями матриць. caffe Framework вже зробив), але для розуміння краще пояснити напрям conv. тому що 2d conv з 3d входом заплутано без напрямку. ^^
runhani

6

Після відповіді від @runhani я додаю ще кілька деталей, щоб зробити пояснення трохи більш зрозумілими, і спробую пояснити це дещо більше (і, звичайно, з прикладами з TF1 і TF2).

Одним з головних додаткових бітів, які я включаю, є:

  • Акцент на додатках
  • Використання tf.Variable
  • Чіткіше пояснення входів / ядер / виходів 1D / 2D / 3D згортка
  • Наслідки кроку / накладки

1D згортка

Ось як можна зробити 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 згортки

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

2D згортка

Off до 2D згортки. Якщо ви людина, яка глибоко навчається, шанси на те, що ви не натрапили на 2D згортку, це… ну нуль. Застосовується в CNN для класифікації зображень, виявлення об'єктів тощо, а також у проблемах NLP, що включають зображення (наприклад, генерація підписів зображень).

Спробуємо на прикладі, тут я отримав ядро ​​згортки з такими фільтрами,

  • Ядро виявлення країв (вікно 3x3)
  • Ядро розмиття (вікно 3х3)
  • Загострити ядро ​​(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 згортка, набагато простіше зрозуміти, що означають ці кілька каналів. Скажіть, ви робите розпізнавання обличчя. Ви можете придумати (це дуже нереально спрощення, але отримує крапку) кожен фільтр являє собою око, рот, ніс тощо. Отже, щоб кожна карта функцій була двійковим зображенням того, чи є ця функція на зображенні, яке ви надали . Я не думаю, що мені потрібно наголошувати, що для моделі розпізнавання обличчя це дуже цінні риси. Більше інформації в цій статті .

Це ілюстрація того, що я намагаюся сформулювати.

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

Застосування поглибленого навчання 2D згортки

2D згортка дуже поширена у царині глибокого навчання.

CNN (Convolution Neural Networks) використовують 2D операцію згортання майже для всіх завдань комп'ютерного зору (наприклад, класифікація зображень, виявлення об'єктів, класифікація відео).

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-згортки

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

Нижче ви бачите різницю.

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

Заключне слово : Якщо ви дуже цікаві, вам може бути цікаво. Ми просто скинули бомбу на цілком автоматичне зменшення розмірів і тепер говоримо про різні успіхи. Але найкраще в кроці - це ви контролюєте, де і як зменшуються розміри.


2
  1. CNN 1D, 2D або 3D посилається на напрям згортки, а не на розмір введення чи фільтра.

  2. Для 1-го канального вводу CNN2D дорівнює CNN1D - це довжина ядра = вхідна довжина. (Напрямок 1 конв.)


2

Підсумовуючи це, в 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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.