Câu trả lời:
Tôi muốn giải thích bằng hình ảnh từ C3D .
Tóm lại, hướng phức hợp & hình dạng đầu ra rất quan trọng!
↑ - D ↑ ↑ ↑ ↑ ↑
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 nhiều nếp cuộn - 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)
↑ - ↑
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)
Các biến đổi 2D với đầu vào 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)
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)
- Liên kết gốc: LINK
- Tác giả: Martin Görner
- Twitter: @martin_gorner
- Google +: plus.google.com/+MartinGorne
↑ ↑ ↑ Biến đổi 1D với đầu vào 1D ↑
Các biến đổi 1D với đầu vào 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
, sau đó là → đối với hàng 1+stride
. Bản thân sự biến đổi là bất biến dịch chuyển, vậy tại sao hướng của tích chập lại là vấn đề?
Sau câu trả lời từ @runhani, tôi sẽ bổ sung thêm một vài chi tiết để giải thích rõ ràng hơn một chút và sẽ cố gắng giải thích điều này nhiều hơn một chút (và tất nhiên với các ví dụ từ TF1 và TF2).
Một trong những phần bổ sung chính mà tôi bao gồm là,
tf.Variable
Đây là cách bạn có thể thực hiện tích chập 1D bằng TF 1 và TF 2.
Và cụ thể, dữ liệu của tôi có các hình dạng sau,
[batch size, width, in channels]
(ví dụ 1, 5, 1
)[width, in channels, out channels]
(ví dụ 5, 1, 4
)[batch size, width, out_channels]
(ví dụ 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)
Đó là cách ít hoạt động hơn với TF2 vì TF2 không cần Session
và variable_initializer
chẳng hạn.
Vì vậy, chúng ta hãy hiểu điều này đang làm gì bằng cách sử dụng một ví dụ làm mịn tín hiệu. Ở bên trái, bạn có bản gốc và ở bên phải, bạn có đầu ra của Convolution 1D có 3 kênh đầu ra.
Nhiều kênh về cơ bản là nhiều đại diện tính năng của một đầu vào. Trong ví dụ này, bạn có ba biểu diễn thu được bởi ba bộ lọc khác nhau. Kênh đầu tiên là bộ lọc làm mịn có trọng số bằng nhau. Thứ hai là một bộ lọc có trọng số giữa bộ lọc hơn các ranh giới. Bộ lọc cuối cùng làm ngược lại với bộ lọc thứ hai. Vì vậy, bạn có thể thấy các bộ lọc khác nhau này mang lại các hiệu ứng khác nhau như thế nào.
Phép tích chập 1D đã được sử dụng thành công cho nhiệm vụ phân loại câu .
Chuyển sang tích chập 2D. Nếu bạn là một người học sâu, khả năng bạn chưa xem qua phép tích chập 2D là ... bằng không. Nó được sử dụng trong CNN để phân loại hình ảnh, phát hiện đối tượng, v.v. cũng như trong các bài toán NLP liên quan đến hình ảnh (ví dụ như tạo chú thích hình ảnh).
Hãy thử một ví dụ, tôi có một nhân chập với các bộ lọc sau đây,
Và cụ thể, dữ liệu của tôi có các hình dạng sau,
[batch_size, height, width, 1]
(ví dụ 1, 340, 371, 1
)[height, width, in channels, out channels]
(ví dụ 3, 3, 1, 3
)[batch_size, height, width, out_channels]
(ví dụ 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')
Ở đây bạn có thể thấy kết quả được tạo ra bởi mã trên. Hình ảnh đầu tiên là bản gốc và theo đồng hồ, bạn có đầu ra của bộ lọc thứ nhất, bộ lọc thứ 2 và bộ lọc 3.
Trong ngữ cảnh nếu tích chập 2D, thì việc hiểu ý nghĩa của nhiều kênh này sẽ dễ dàng hơn nhiều. Giả sử bạn đang thực hiện nhận dạng khuôn mặt. Bạn có thể nghĩ đến (đây là một sự đơn giản hóa rất phi thực tế nhưng hiểu được điểm trên) mỗi bộ lọc đại diện cho mắt, miệng, mũi, v.v. Vì vậy, mỗi bản đồ đối tượng sẽ là một biểu diễn nhị phân về việc đối tượng địa lý đó có trong hình ảnh bạn cung cấp hay không . Tôi không nghĩ rằng tôi cần phải nhấn mạnh rằng đối với một mô hình nhận dạng khuôn mặt, đó là những tính năng rất có giá trị. Thông tin thêm trong bài viết này .
Đây là một minh họa cho những gì tôi đang cố gắng trình bày.
Phép tích chập 2D rất phổ biến trong lĩnh vực học sâu.
CNN (Convolution Neural Networks) sử dụng hoạt động tích chập 2D cho hầu hết các tác vụ thị giác máy tính (ví dụ: Phân loại hình ảnh, phát hiện đối tượng, phân loại video).
Ngày càng khó để minh họa điều gì đang xảy ra khi số lượng chiều tăng lên. Nhưng với sự hiểu biết tốt về cách hoạt động của tích chập 1D và 2D, sẽ rất dễ dàng để khái quát sự hiểu biết đó thành tích chập 3D. Vì vậy, đây đi.
Và cụ thể, dữ liệu của tôi có các hình dạng sau,
[batch size, height, width, depth, in channels]
(ví dụ 1, 200, 200, 200, 1
:)[height, width, depth, in channels, out channels]
(ví dụ 5, 5, 5, 1, 3
)[batch size, width, height, width, depth, out_channels]
(ví dụ 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')
Tính năng tích chập 3D đã được sử dụng khi phát triển các ứng dụng học máy liên quan đến dữ liệu LIDAR (Phát hiện và đo sáng) có bản chất là 3 chiều.
Được rồi, bạn sắp hoàn tất. Hãy giữ lấy. Hãy xem sải chân và đệm là gì. Chúng khá trực quan nếu bạn nghĩ về chúng.
Nếu bạn sải bước qua một hành lang, bạn sẽ đến đó nhanh hơn với ít bước hơn. Nhưng nó cũng có nghĩa là bạn quan sát xung quanh ít hơn so với khi bạn đi ngang qua phòng. Bây giờ chúng ta hãy củng cố sự hiểu biết của chúng ta với một bức tranh đẹp! Hãy hiểu những điều này thông qua tích chập 2D.
Khi bạn sử dụng tf.nn.conv2d
ví dụ, bạn cần đặt nó dưới dạng vectơ gồm 4 phần tử. Không có lý do gì để bị đe dọa bởi điều này. Nó chỉ chứa các bước theo thứ tự sau.
Chuyển đổi 2D - [batch stride, height stride, width stride, channel stride]
. Ở đây, sải bước hàng loạt và sải bước kênh mà bạn chỉ cần đặt thành một (Tôi đã triển khai mô hình học sâu trong 5 năm và chưa bao giờ phải đặt chúng thành bất cứ điều gì ngoại trừ một). Vì vậy, bạn chỉ cần thực hiện 2 bước.
Chuyển đổi 3D - [batch stride, height stride, width stride, depth stride, channel stride]
. Ở đây bạn chỉ lo lắng về chiều cao / chiều rộng / chiều sâu.
Bây giờ, bạn nhận thấy rằng bất kể sải chân của bạn nhỏ đến mức nào (tức là 1), thì việc giảm kích thước không thể tránh khỏi xảy ra trong quá trình tích chập (ví dụ: chiều rộng là 3 sau khi xoay một hình ảnh rộng 4 đơn vị). Điều này là không mong muốn, đặc biệt là khi xây dựng mạng nơ-ron tích chập sâu. Đây là nơi đệm đến để giải cứu. Có hai loại đệm được sử dụng phổ biến nhất.
SAME
và VALID
Dưới đây bạn có thể thấy sự khác biệt.
Lời cuối cùng : Nếu bạn rất tò mò, bạn có thể tự hỏi. Chúng tôi vừa thả một quả bom vào việc giảm kích thước hoàn toàn tự động và bây giờ đang nói về việc có những bước tiến khác nhau. Nhưng điều tốt nhất về sải chân là bạn kiểm soát được khi nào và làm thế nào để giảm kích thước.
Tóm lại, Trong CNN 1D, hạt nhân di chuyển theo 1 hướng. Dữ liệu đầu vào và đầu ra của CNN 1D là 2 chiều. Chủ yếu được sử dụng trên dữ liệu Chuỗi thời gian.
Trong CNN 2D, hạt nhân di chuyển theo 2 hướng. Dữ liệu đầu vào và đầu ra của CNN 2D là 3 chiều. Chủ yếu được sử dụng trên dữ liệu Hình ảnh.
Trong 3D CNN, hạt nhân di chuyển theo 3 hướng. Dữ liệu đầu vào và đầu ra của 3D CNN là 4 chiều. Chủ yếu được sử dụng trên dữ liệu Hình ảnh 3D (MRI, CT Scans).
Bạn có thể tìm thêm thông tin chi tiết tại đây: https://medium.com/@xzz201920/conv1d-conv2d-and-conv3d-8a59182c4d6