.. _sec_channels:
Nhiều kênh đầu vào và nhiều đầu ra
==================================
Mặc dù chúng tôi đã mô tả nhiều kênh bao gồm mỗi hình ảnh (ví dụ: hình
ảnh màu có các kênh RGB tiêu chuẩn để chỉ số lượng màu đỏ, xanh lá cây
và xanh dương) và các lớp phức tạp cho nhiều kênh trong
:numref:`subsec_why-conv-channels`, cho đến bây giờ, chúng tôi đã đơn
giản hóa tất cả các ví dụ số của mình bằng cách làm việc với chỉ một đầu
vào và một kênh đầu ra duy nhất. Điều này đã cho phép chúng tôi nghĩ về
các đầu vào của chúng tôi, hạt nhân phức tạp, và đầu ra mỗi cái là hàng
chục hai chiều.
Khi chúng tôi thêm các kênh vào hỗn hợp, các đầu vào và biểu diễn ẩn của
chúng tôi đều trở thành hàng chục ba chiều. Ví dụ, mỗi hình ảnh đầu vào
RGB có hình dạng :math:`3\times h\times w`. Chúng tôi đề cập đến trục
này, với kích thước 3, như kích thước \* kênh\*. Trong phần này, chúng
ta sẽ xem xét sâu hơn về các hạt nhân phức tạp với nhiều kênh đầu vào và
nhiều kênh đầu ra.
Nhiều kênh đầu vào
------------------
Khi dữ liệu đầu vào chứa nhiều kênh, chúng ta cần xây dựng một hạt nhân
phức tạp có cùng số kênh đầu vào với dữ liệu đầu vào, để nó có thể thực
hiện tương quan chéo với dữ liệu đầu vào. Giả sử rằng số kênh cho dữ
liệu đầu vào là :math:`c_i`, số kênh đầu vào của hạt nhân covolution
cũng cần phải là :math:`c_i`. Nếu hình dạng cửa sổ của hạt nhân phức tạp
của chúng ta là :math:`k_h\times k_w`, thì khi :math:`c_i=1`, chúng ta
có thể nghĩ về hạt nhân phức tạp của chúng ta như chỉ là một tensor hai
chiều của hình dạng :math:`k_h\times k_w`.
Tuy nhiên, khi :math:`c_i>1`, chúng ta cần một hạt nhân chứa một tensor
của hình dạng :math:`k_h\times k_w` cho *every* kênh đầu vào. Nối các số
lượng :math:`c_i` này lại với nhau mang lại một hạt nhân phức tạp của
hình dạng :math:`c_i\times k_h\times k_w`. Vì hạt nhân đầu vào và
convolution mỗi kênh có :math:`c_i` kênh, chúng ta có thể thực hiện một
hoạt động tương quan chéo trên tensor hai chiều của đầu vào và tensor
hai chiều của hạt nhân phức tạp cho mỗi kênh, thêm các kết quả
:math:`c_i` với nhau (tổng hợp qua các kênh) để mang lại một hai- tensor
chiều. Đây là kết quả của mối tương quan chéo hai chiều giữa đầu vào đa
kênh và hạt nhân phức tạp đa đầu vào kênh.
Trong :numref:`fig_conv_multi_in`, chúng tôi chứng minh một ví dụ về
mối tương quan chéo hai chiều với hai kênh đầu vào. Các phần bóng mờ là
phần tử đầu ra đầu tiên cũng như các phần tử tensor đầu vào và hạt nhân
được sử dụng cho tính toán đầu ra:
:math:`(1\times1+2\times2+4\times3+5\times4)+(0\times0+1\times1+3\times2+4\times3)=56`.
.. _fig_conv_multi_in:
.. figure:: ../img/conv-multi-in.svg
Cross-correlation computation with 2 input channels.
Để đảm bảo rằng chúng ta thực sự hiểu những gì đang xảy ra ở đây, chúng
ta có thể thực hiện các hoạt động tương quan chéo với nhiều kênh đầu vào
chính mình. Lưu ý rằng tất cả những gì chúng tôi đang làm là thực hiện
một hoạt động tương quan chéo trên mỗi kênh và sau đó thêm kết quả.
.. raw:: html
.. raw:: html
.. code:: python
from mxnet import np, npx
from d2l import mxnet as d2l
npx.set_np()
def corr2d_multi_in(X, K):
# First, iterate through the 0th dimension (channel dimension) of `X` and
# `K`. Then, add them together
return sum(d2l.corr2d(x, k) for x, k in zip(X, K))
.. raw:: html
.. raw:: html
.. code:: python
import torch
from d2l import torch as d2l
def corr2d_multi_in(X, K):
# First, iterate through the 0th dimension (channel dimension) of `X` and
# `K`. Then, add them together
return sum(d2l.corr2d(x, k) for x, k in zip(X, K))
.. raw:: html
.. raw:: html
.. code:: python
import tensorflow as tf
from d2l import tensorflow as d2l
def corr2d_multi_in(X, K):
# First, iterate through the 0th dimension (channel dimension) of `X` and
# `K`. Then, add them together
return tf.reduce_sum([d2l.corr2d(x, k) for x, k in zip(X, K)], axis=0)
.. raw:: html
.. raw:: html
Chúng ta có thể xây dựng tensor đầu vào ``X`` và tensor kernel ``K``
tương ứng với các giá trị trong :numref:`fig_conv_multi_in` đến xác
nhận đầu ra của hoạt động tương quan chéo.
.. raw:: html
.. raw:: html
.. code:: python
X = np.array([[[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]],
[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]])
K = np.array([[[0.0, 1.0], [2.0, 3.0]], [[1.0, 2.0], [3.0, 4.0]]])
corr2d_multi_in(X, K)
.. parsed-literal::
:class: output
array([[ 56., 72.],
[104., 120.]])
.. raw:: html
.. raw:: html
.. code:: python
X = torch.tensor([[[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]],
[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]])
K = torch.tensor([[[0.0, 1.0], [2.0, 3.0]], [[1.0, 2.0], [3.0, 4.0]]])
corr2d_multi_in(X, K)
.. parsed-literal::
:class: output
tensor([[ 56., 72.],
[104., 120.]])
.. raw:: html
.. raw:: html
.. code:: python
X = tf.constant([[[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]],
[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]])
K = tf.constant([[[0.0, 1.0], [2.0, 3.0]], [[1.0, 2.0], [3.0, 4.0]]])
corr2d_multi_in(X, K)
.. parsed-literal::
:class: output
.. raw:: html
.. raw:: html
.. _subsec_multi-output-channels:
Nhiều kênh đầu ra
-----------------
Bất kể số lượng kênh đầu vào, cho đến nay chúng tôi luôn kết thúc với
một kênh đầu ra. Tuy nhiên, như chúng ta đã thảo luận trong
:numref:`subsec_why-conv-channels`, hóa ra là điều cần thiết để có
nhiều kênh ở mỗi lớp. Trong các kiến trúc mạng thần kinh phổ biến nhất,
chúng tôi thực sự tăng kích thước kênh khi chúng tôi tăng cao hơn trong
mạng thần kinh, thường là lấy mẫu xuống để giảm độ phân giải không gian
cho độ sâu kênh \* lớn hơn\*. Trực giác, bạn có thể nghĩ về từng kênh là
phản hồi một số bộ tính năng khác nhau. Thực tế phức tạp hơn một chút so
với những giải thích ngây thơ nhất của trực giác này vì các đại diện
không được học độc lập nhưng khá tối ưu hóa để cùng hữu ích. Vì vậy, có
thể không phải là một kênh duy nhất học một máy dò cạnh mà là một số
hướng trong không gian kênh tương ứng với việc phát hiện các cạnh.
Biểu thị bởi :math:`c_i` và :math:`c_o` số lượng các kênh đầu vào và đầu
ra, tương ứng, và để :math:`k_h` và :math:`k_w` là chiều cao và chiều
rộng của hạt nhân. Để có được một đầu ra với nhiều kênh, chúng ta có thể
tạo một tensor hạt nhân của hình dạng :math:`c_i\times k_h\times k_w`
cho \* mỗi kênh đầu ra. Chúng tôi nối chúng trên kích thước kênh đầu ra,
để hình dạng của hạt nhân phức tạp là
:math:`c_o\times c_i\times k_h\times k_w`. Trong các hoạt động tương
quan chéo, kết quả trên mỗi kênh đầu ra được tính từ hạt nhân tích lũy
tương ứng với kênh đầu ra đó và lấy đầu vào từ tất cả các kênh trong
tensor đầu vào.
Chúng tôi thực hiện một hàm tương quan chéo để tính toán đầu ra của
nhiều kênh như hình dưới đây.
.. raw:: html
.. raw:: html
.. code:: python
def corr2d_multi_in_out(X, K):
# Iterate through the 0th dimension of `K`, and each time, perform
# cross-correlation operations with input `X`. All of the results are
# stacked together
return np.stack([corr2d_multi_in(X, k) for k in K], 0)
.. raw:: html
.. raw:: html
.. code:: python
def corr2d_multi_in_out(X, K):
# Iterate through the 0th dimension of `K`, and each time, perform
# cross-correlation operations with input `X`. All of the results are
# stacked together
return torch.stack([corr2d_multi_in(X, k) for k in K], 0)
.. raw:: html
.. raw:: html
.. code:: python
def corr2d_multi_in_out(X, K):
# Iterate through the 0th dimension of `K`, and each time, perform
# cross-correlation operations with input `X`. All of the results are
# stacked together
return tf.stack([corr2d_multi_in(X, k) for k in K], 0)
.. raw:: html
.. raw:: html
Chúng tôi xây dựng một hạt nhân phức tạp với 3 kênh đầu ra bằng cách nối
tensor kernel ``K`` với ``K+1`` (cộng với một cho mỗi phần tử trong
``K``) và ``K+2``.
.. raw:: html
.. raw:: html
.. code:: python
K = np.stack((K, K + 1, K + 2), 0)
K.shape
.. parsed-literal::
:class: output
(3, 2, 2, 2)
.. raw:: html
.. raw:: html
.. code:: python
K = torch.stack((K, K + 1, K + 2), 0)
K.shape
.. parsed-literal::
:class: output
torch.Size([3, 2, 2, 2])
.. raw:: html
.. raw:: html
.. code:: python
K = tf.stack((K, K + 1, K + 2), 0)
K.shape
.. parsed-literal::
:class: output
TensorShape([3, 2, 2, 2])
.. raw:: html
.. raw:: html
Dưới đây, chúng tôi thực hiện các thao tác tương quan chéo trên tensor
đầu vào ``X`` với tensor kernel ``K``. Bây giờ đầu ra chứa 3 kênh. Kết
quả của kênh đầu tiên phù hợp với kết quả của tensor đầu vào trước ``X``
và kênh đa đầu vào, hạt nhân kênh đơn đầu ra.
.. raw:: html
.. raw:: html
.. code:: python
corr2d_multi_in_out(X, K)
.. parsed-literal::
:class: output
array([[[ 56., 72.],
[104., 120.]],
[[ 76., 100.],
[148., 172.]],
[[ 96., 128.],
[192., 224.]]])
.. raw:: html
.. raw:: html
.. code:: python
corr2d_multi_in_out(X, K)
.. parsed-literal::
:class: output
tensor([[[ 56., 72.],
[104., 120.]],
[[ 76., 100.],
[148., 172.]],
[[ 96., 128.],
[192., 224.]]])
.. raw:: html
.. raw:: html
.. code:: python
corr2d_multi_in_out(X, K)
.. parsed-literal::
:class: output
.. raw:: html
.. raw:: html
:math:`1\times 1` Lớp phức tạp
------------------------------
Lúc đầu, một :math:`1 \times 1` convolution, tức là,
:math:`k_h = k_w = 1`, dường như không có ý nghĩa nhiều. Rốt cuộc, một
sự covolution tương quan các pixel liền kề. Một sự phức tạp
:math:`1 \times 1` rõ ràng là không. Tuy nhiên, chúng là những hoạt động
phổ biến đôi khi được bao gồm trong các thiết kế của các mạng sâu phức
tạp. Hãy để chúng tôi xem một số chi tiết những gì nó thực sự làm.
Bởi vì cửa sổ tối thiểu được sử dụng, sự phức tạp :math:`1\times 1` mất
khả năng của các lớp phức tạp lớn hơn để nhận ra các mẫu bao gồm các
tương tác giữa các phần tử liền kề trong chiều cao và chiều rộng. Tính
toán duy nhất của sự phức tạp :math:`1\times 1` xảy ra trên kích thước
kênh.
:numref:`fig_conv_1x1` cho thấy tính toán tương quan chéo bằng cách sử
dụng hạt nhân phức tạp :math:`1\times 1` với 3 kênh đầu vào và 2 kênh
đầu ra. Lưu ý rằng các đầu vào và đầu ra có cùng chiều cao và chiều
rộng. Mỗi phần tử trong đầu ra được bắt nguồn từ sự kết hợp tuyến tính
của các phần tử \* ở cùng một vị trí\* trong hình ảnh đầu vào. Bạn có
thể nghĩ về lớp ghép :math:`1\times 1` là cấu thành một lớp kết nối hoàn
toàn được áp dụng tại mọi vị trí pixel duy nhất để chuyển đổi các giá
trị đầu vào tương ứng :math:`c_i` thành các giá trị đầu ra :math:`c_o`.
Bởi vì đây vẫn là một lớp phức tạp, các trọng lượng được gắn trên vị trí
pixel. Do đó lớp phức tạp :math:`1\times 1` đòi hỏi
:math:`c_o\times c_i` trọng lượng (cộng với sự thiên vị).
.. _fig_conv_1x1:
.. figure:: ../img/conv-1x1.svg
The cross-correlation computation uses the :math:`1\times 1`
convolution kernel with 3 input channels and 2 output channels. The
input and output have the same height and width.
Hãy để chúng tôi kiểm tra xem điều này có hoạt động trong thực tế hay
không: chúng tôi thực hiện một sự phức tạp :math:`1 \times 1` bằng cách
sử dụng một lớp được kết nối hoàn toàn. Điều duy nhất là chúng ta cần
thực hiện một số điều chỉnh đối với hình dạng dữ liệu trước và sau khi
nhân ma trận.
.. raw:: html
.. raw:: html
.. code:: python
def corr2d_multi_in_out_1x1(X, K):
c_i, h, w = X.shape
c_o = K.shape[0]
X = X.reshape((c_i, h * w))
K = K.reshape((c_o, c_i))
# Matrix multiplication in the fully-connected layer
Y = np.dot(K, X)
return Y.reshape((c_o, h, w))
.. raw:: html
.. raw:: html
.. code:: python
def corr2d_multi_in_out_1x1(X, K):
c_i, h, w = X.shape
c_o = K.shape[0]
X = X.reshape((c_i, h * w))
K = K.reshape((c_o, c_i))
# Matrix multiplication in the fully-connected layer
Y = torch.matmul(K, X)
return Y.reshape((c_o, h, w))
.. raw:: html
.. raw:: html
.. code:: python
def corr2d_multi_in_out_1x1(X, K):
c_i, h, w = X.shape
c_o = K.shape[0]
X = tf.reshape(X, (c_i, h * w))
K = tf.reshape(K, (c_o, c_i))
# Matrix multiplication in the fully-connected layer
Y = tf.matmul(K, X)
return tf.reshape(Y, (c_o, h, w))
.. raw:: html
.. raw:: html
Khi thực hiện sự phức tạp :math:`1\times 1`, hàm trên tương đương với
hàm tương quan chéo được triển khai trước đó ``corr2d_multi_in_out``.
Hãy để chúng tôi kiểm tra điều này với một số dữ liệu mẫu.
.. raw:: html
.. raw:: html
.. code:: python
X = np.random.normal(0, 1, (3, 3, 3))
K = np.random.normal(0, 1, (2, 3, 1, 1))
Y1 = corr2d_multi_in_out_1x1(X, K)
Y2 = corr2d_multi_in_out(X, K)
assert float(np.abs(Y1 - Y2).sum()) < 1e-6
.. raw:: html
.. raw:: html
.. code:: python
X = torch.normal(0, 1, (3, 3, 3))
K = torch.normal(0, 1, (2, 3, 1, 1))
Y1 = corr2d_multi_in_out_1x1(X, K)
Y2 = corr2d_multi_in_out(X, K)
assert float(torch.abs(Y1 - Y2).sum()) < 1e-6
.. raw:: html
.. raw:: html
.. code:: python
X = tf.random.normal((3, 3, 3), 0, 1)
K = tf.random.normal((2, 3, 1, 1), 0, 1)
Y1 = corr2d_multi_in_out_1x1(X, K)
Y2 = corr2d_multi_in_out(X, K)
assert float(tf.reduce_sum(tf.abs(Y1 - Y2))) < 1e-6
.. raw:: html
.. raw:: html
Tóm tắt
-------
- Nhiều kênh có thể được sử dụng để mở rộng các tham số mô hình của lớp
phức tạp.
- Lớp kết nối :math:`1\times 1` tương đương với lớp kết nối hoàn toàn,
khi được áp dụng trên cơ sở trên mỗi pixel.
- Lớp phức tạp :math:`1\times 1` thường được sử dụng để điều chỉnh số
kênh giữa các lớp mạng và để kiểm soát độ phức tạp của mô hình.
Bài tập
-------
1. Giả sử rằng chúng ta có hai hạt nhân phức tạp có kích thước
:math:`k_1` và :math:`k_2`, tương ứng (không có phi tuyến tính ở
giữa).
1. Chứng minh rằng kết quả của hoạt động có thể được thể hiện bằng
một sự phức tạp duy nhất.
2. Chiều của sự phức tạp đơn tương đương là gì?
3. Cuộc trò chuyện có đúng không?
2. Giả sử một đầu vào của hình :math:`c_i\times h\times w` và một hạt
nhân phức tạp của hình :math:`c_o\times c_i\times k_h\times k_w`, đệm
của :math:`(p_h, p_w)`, và sải chân của :math:`(s_h, s_w)`.
1. Chi phí tính toán (nhân và bổ sung) cho việc tuyên truyền chuyển
tiếp là bao nhiêu?
2. Dấu chân bộ nhớ là gì?
3. Dấu chân bộ nhớ cho tính toán ngược là gì?
4. Chi phí tính toán cho việc truyền ngược là bao nhiêu?
3. Theo yếu tố nào số lượng tính toán tăng lên nếu chúng ta tăng gấp đôi
số lượng kênh đầu vào :math:`c_i` và số lượng kênh đầu ra
:math:`c_o`? Điều gì sẽ xảy ra nếu chúng ta tăng gấp đôi đệm?
4. Nếu chiều cao và chiều rộng của một hạt nhân phức tạp là
:math:`k_h=k_w=1`, độ phức tạp tính toán của sự lan truyền về phía
trước là gì?
5. Các biến ``Y1`` và ``Y2`` trong ví dụ cuối cùng của phần này có giống
hệt nhau không? Tại sao?
6. Làm thế nào bạn sẽ thực hiện các phức tạp bằng cách sử dụng phép nhân
ma trận khi cửa sổ phức tạp không phải là :math:`1\times 1`?
.. raw:: html
.. raw:: html
`Discussions `__
.. raw:: html
.. raw:: html
`Discussions `__
.. raw:: html
.. raw:: html
`Discussions `__
.. raw:: html
.. raw:: html