.. raw:: html
.. raw:: html
.. raw:: html
.. _sec_channels:
Đa kênh Đầu vào và Đầu ra
=========================
.. raw:: html
Mặc dù chúng ta đã mô tả mỗi tấm ảnh được tạo nên bởi nhiều kênh
(*channel*) (cụ thể, ảnh màu sử dụng hệ màu RGB tiêu chuẩn với các kênh
riêng biệt thể hiện lượng màu đỏ, xanh lá và xanh dương), nhưng cho đến
lúc này, ta vẫn đơn giản hóa tất cả các ví dụ tính toán với chỉ một kênh
đầu vào và một kênh đầu ra. Điều đó đã cho phép chúng ta coi các đầu
vào, các bộ lọc tích chập và các đầu ra như các mảng hai chiều.
.. raw:: html
Khi chúng ta thêm các kênh vào hỗn hợp ấy, đầu vào cùng với các lớp biểu
diễn ẩn của ta trở thành các mảng ba chiều. Chẳng hạn, mỗi ảnh RGB đầu
vào có dạng :math:`3\times h\times w`. Ta xem trục này là chiều kênh, có
kích thước là 3. Trong phần này, ta sẽ quan sát sâu hơn vào các bộ lọc
tích chập với đầu vào và đầu ra đa kênh.
.. raw:: html
Đa kênh Đầu vào
---------------
.. raw:: html
Khi dữ liệu đầu vào có nhiều kênh, ta cần xây dựng một bộ lọc tích chập
với cùng số kênh đầu vào như dữ liệu nhập, để nó có thể thực hiện tính
tương quan chéo với dữ liệu này. Giả sử số kênh dữ liệu đầu vào là
:math:`c_i`, ta sẽ cần số kênh đầu vào của bộ lọc tích chập là
:math:`c_i`. Nếu kích thước cửa sổ của bộ lọc tích chập là
:math:`k_h\times k_w`, thì khi :math:`c_i=1`, ta có thể xem bộ lọc tích
chập này đơn giản là một mảng hai chiều có kích thước
:math:`k_h\times k_w`.
.. raw:: html
.. raw:: html
.. raw:: html
Tuy nhiên, khi :math:`c_i>1`, chúng ta cần một bộ lọc chứa mảng có kích
thước :math:`k_h\times k_w` *cho mỗi kênh của đầu vào*. Gộp :math:`c_i`
mảng này lại ta được một bộ lọc tích chập kích thước
:math:`c_i\times k_h\times k_w`. Vì đầu vào và bộ lọc đều có :math:`c_i`
kênh, ta có thể thực hiện phép tương quan chéo trên từng cặp mảng hai
chiều của đầu vào và bộ lọc cho mỗi kênh, rồi cộng kết quả của
:math:`c_i` kênh lại để tạo ra một mảng hai chiều. Đây là kết quả của
phép tương quan chéo hai chiều giữa dữ liệu đầu vào đa kênh và kênh bộ
lọc tích chập *đa đầu vào*.
.. raw:: html
Trong :numref:`fig_conv_multi_in` minh hoạ một ví dụ về phép tương
quan chéo hai chiều với hai kênh đầu vào. Phần tô đậm là phần tử đầu ra
đầu tiên cùng các phần tử của mảng đầu vào và bộ lọc được sử dụng trong
phép tính đó:
:math:`(1\times1+2\times2+4\times3+5\times4)+(0\times0+1\times1+3\times2+4\times3)=56`.
.. raw:: html
.. _fig_conv_multi_in:
.. figure:: ../img/conv-multi-in.svg
Phép tính tương quan chéo với hai kênh đầu vào. Phần tô đậm là phần
tử đầu ra đầu tiên cùng các phần tử của mảng đầu vào và bộ lọc được
sử dụng trong phép tính đó:
:math:`(1\times1+2\times2+4\times3+5\times4)+(0\times0+1\times1+3\times2+4\times3)=56`.
.. raw:: html
Để thực sự hiểu được những gì đang xảy ra ở đây, chúng ta có thể tự lập
trình phép toán tương quan chéo với nhiều kênh đầu vào. Chú ý rằng tất
cả những gì chúng ta đang làm là thực hiện một phép tương quan chéo trên
mỗi kênh rồi cộng các kết quả lại bằng hàm ``add_n``.
.. code:: python
from d2l import mxnet as d2l
from mxnet import np, npx
npx.set_np()
def corr2d_multi_in(X, K):
# First, traverse along the 0th dimension (channel dimension) of X and K.
# Then, add them together by using * to turn the result list into a
# positional argument of the add_n function
return sum(d2l.corr2d(x, k) for x, k in zip(X, K))
.. raw:: html
Ta có thể tạo mảng đầu vào ``X`` và mảng bộ lọc ``K`` tương ứng với các
giá trị trong hình trên để kiểm chứng kết quả đầu ra.
.. code:: python
X = np.array([[[0, 1, 2], [3, 4, 5], [6, 7, 8]],
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]])
K = np.array([[[0, 1], [2, 3]], [[1, 2], [3, 4]]])
corr2d_multi_in(X, K)
.. parsed-literal::
:class: output
array([[ 56., 72.],
[104., 120.]])
.. raw:: html
.. raw:: html
.. raw:: html
.. raw:: html
.. raw:: html
Đa kênh Đầu ra
--------------
.. raw:: html
Cho đến nay, bất kể số lượng kênh đầu vào là bao nhiêu thì ta vẫn luôn
kết thúc với chỉ một kênh đầu ra. Tuy nhiên, như đã thảo luận trước đây,
hóa ra việc có nhiều kênh ở mỗi tầng là rất cần thiết. Trong các kiến
trúc mạng nơ-ron phổ biến nhất, ta thường tăng kích thước chiều kênh khi
tiến sâu hơn trong mạng, đồng thời giảm độ phân giải không gian để đánh
đổi với *chiều kênh* sâu hơn này. Theo trực giác, ta có thể xem mỗi kênh
tương ứng với một tập các đặc trưng khác nhau. Nhưng thực tế phức tạp
hơn một chút so với cách diễn giải theo trực giác này vì các biểu diễn
không được học độc lập mà được tối ưu hóa để có ích khi kết hợp với
nhau. Vì vậy, có thể việc phát hiện biên sẽ được học bởi một vài kênh
thay vì chỉ một kênh duy nhất.
.. raw:: html
Đặt :math:`c_i` và :math:`c_o` lần lượt là số lượng kênh đầu vào và đầu
ra, :math:`k_h` và :math:`k_w` lần lượt là chiều cao và chiều rộng của
bộ lọc. Để có được một đầu ra với nhiều kênh, ta có thể tạo một mảng bộ
lọc có kích thước :math:`c_i \times k_h \times k_w` cho mỗi kênh đầu ra.
Ta nối chúng lại dựa trên chiều kênh đầu ra đã biết, sao cho kích thước
của bộ lọc tích chập là :math:`c_o \times c_i \times k_h \times k_w`.
Trong các phép tính tương quan chéo, kết quả trên mỗi kênh đầu ra được
tính từ bộ lọc tích chập tương ứng với kênh đầu ra đó và lấy đầu vào từ
tất cả các kênh trong mảng đầu vào.
.. raw:: html
Ta lập trình một hàm tương quan chéo để tính đầu ra của nhiều kênh như
dưới đây.
.. code:: python
def corr2d_multi_in_out(X, K):
# Traverse along the 0th dimension of K, and each time, perform
# cross-correlation operations with input X. All of the results are merged
# together using the stack function
return np.stack([corr2d_multi_in(X, k) for k in K])
.. raw:: html
Ta tạo một bộ lọc tích chập với 3 kênh đầu ra bằng cách nối mảng bộ lọc
``K`` với ``K+1`` (cộng một cho mỗi phần tử trong ``K``) và ``K+2``.
.. code:: python
K = np.stack((K, K + 1, K + 2))
K.shape
.. parsed-literal::
:class: output
(3, 2, 2, 2)
.. raw:: html
Dưới đây, ta thực hiện các phép tính tương quan chéo trên mảng đầu vào
``X`` với mảng bộ lọc ``K``. Đầu ra sẽ gồm có 3 kênh. Kết quả của kênh
đầu tiên khớp với kết quả trước đây khi áp dụng bộ lọc đa kênh đầu vào
và một kênh đầu ra lên mảng đầu vào ``X``.
.. 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
.. raw:: html
Tầng Tích chập :math:`1\times 1`
--------------------------------
.. raw:: html
Thoạt nhìn, một phép tích chập :math:`1 \times 1`, tức
:math:`k_h = k_w = 1`, dường như không có nhiều ý nghĩa. Suy cho cùng,
một phép tích chập là để tính toán tương quan giữa các điểm ảnh liền kề.
Nhưng rõ ràng một phép tích chập :math:`1 \times 1` lại không làm như
vậy. Mặc dù vậy, chúng là các phép tính phổ biến đôi khi được sử dụng
khi thiết kế các mạng sâu phức tạp. Ta sẽ xem kỹ cách hoạt động của
chúng.
.. raw:: html
Do cửa sổ có kích thước tối thiểu nên so với các tầng tích chập lớn hơn,
phép tích chập :math:`1\times 1` mất đi khả năng nhận dạng các khuôn mẫu
chứa các tương tác giữa các phần tử liền kề theo chiều cao và chiều
rộng. Phép tích chập :math:`1\times 1` chỉ xảy ra trên chiều kênh.
.. raw:: html
:numref:`fig_conv_1x1` biểu diễn phép tính tương quan chéo sử dụng bộ
lọc tích chập :math:`1\times 1` với 3 kênh đầu vào và 2 kênh đầu ra. Lưu
ý rằng đầ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 là một tổ hợp tuyến tính của các phần tử *ở cùng một vị
trí* trong ảnh đầu vào. Bạn có thể xem tầng tích chập :math:`1\times 1`
như một tầng kết nối đầy đủ được áp dụng lên mỗi vị trí điểm ảnh đơn lẻ
để chuyển đổi :math:`c_i` giá trị đầu vào thành :math:`c_o` giá trị đầu
ra tương ứng. Bởi vì đây vẫn là một tầng tích chập nên các trọng số sẽ
được chia sẻ giữa các vị trí điểm ảnh. Do đó, tầng tích chập
:math:`1\times 1` cần tới :math:`c_o\times c_i` trọng số (cộng thêm các
hệ số điều chỉnh).
.. raw:: html
.. _fig_conv_1x1:
.. figure:: ../img/conv-1x1.svg
Phép tính tương quan chéo sử dụng bộ lọc tích chập :math:`1\times 1`
với 3 kênh đầu vào và 2 kênh đầu ra. Các đầu vào và các đầu ra có
cùng chiều cao và chiều rộng.
.. raw:: html
.. raw:: html
.. raw:: html
Hãy kiểm tra xem liệu nó có hoạt động trong thực tế: Ta sẽ lập trình một
phép tích chập :math:`1 \times 1` sử dụng một tầng kết nối đầy đủ. Vấn
đề duy nhất là ta cần phải điều chỉnh kích thước dữ liệu trước và sau
phép nhân ma trận.
.. 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)
Y = np.dot(K, X) # Matrix multiplication in the fully connected layer
return Y.reshape(c_o, h, w)
.. raw:: html
Khi thực hiện phép tích chập :math:`1\times 1`, hàm bên trên tương đương
với hàm tương quan chéo đã được lập trình ở ``corr2d_multi_in_out``.
.. code:: python
X = np.random.uniform(size=(3, 3, 3))
K = np.random.uniform(size=(2, 3, 1, 1))
Y1 = corr2d_multi_in_out_1x1(X, K)
Y2 = corr2d_multi_in_out(X, K)
np.abs(Y1 - Y2).sum() < 1e-6
.. parsed-literal::
:class: output
array(True)
.. raw:: html
Tóm tắt
-------
.. raw:: html
- Ta có thể sử dụng nhiều kênh để mở rộng các tham số mô hình của tầng
tích chập.
- Tầng tích chập :math:`1\times 1` khi được áp dụng lên từng điểm ảnh
tương đương với tầng kết nối đầy đủ giữa các kênh.
- Tầng tích chập :math:`1\times 1` thường được sử dụng để điều chỉnh số
lượng kênh giữa các tầng của mạng và để kiểm soát độ phức tạp của mô
hình.
.. raw:: html
Bài tập
-------
.. raw:: html
1. Giả sử rằng ta có hai bộ lọc tích chập có kích thước tương ứng là
:math:`k_1` và :math:`k_2` (không có tính phi tuyến ở giữa).
- Chứng minh rằng kết quả của phép tính có thể được biểu diễn bằng
chỉ một phép tích chập.
- Phép tích chập tương đương này có kích thước là bao nhiêu?
- Điều ngược lại có đúng không?
2. Giả sử kích thước của đầu vào là :math:`c_i\times h\times w` và một
bộ lọc tích chập có kích thước
:math:`c_o\times c_i\times k_h\times k_w`, đồng thời sử dụng đệm
:math:`(p_h, p_w)` và sải bước :math:`(s_h, s_w)`.
- Chi phí tính toán (phép nhân và phép cộng) cho lượt truyền xuôi là
bao nhiêu?
- Dung lượng bộ nhớ cho tính toán truyền xuôi là bao nhiêu?
- Dung lượng bộ nhớ cho tính toán truyền ngược là bao nhiêu?
- Chi phí tính toán cho lượt lan truyền ngược là bao nhiêu?
3. Số lượng tính toán sẽ tăng lên bao nhiêu lần nếu ta nhân đô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ì
xảy ra nếu ta gấp đôi phần đệm?
4. Nếu chiều cao và chiều rộng của bộ lọc tích chập là
:math:`k_h=k_w=1`, thì độ phức tạp của tính toán truyền xuôi là bao
nhiêu?
5. Các biến ``Y1`` và\ ``Y2`` trong ví dụ cuối cùng của mục này có giống
nhau không? Tại sao?
6. Khi cửa sổ tích chập không phải là :math:`1\times 1`, bạn sẽ lập
trình các phép tích chập sử dụng phép nhân ma trận như thế nào?
.. raw:: html
.. raw:: html
Thảo luận
---------
- `Tiếng Anh `__
- `Tiếng Việt `__
Những người thực hiện
---------------------
Bản dịch trong trang này được thực hiện bởi:
- Đoàn Võ Duy Thanh
- Nguyễn Mai Hoàng Long
- Nguyễn Lê Quang Nhật
- Lê Khắc Hồng Phúc
- Nguyễn Văn Cường
- Phạm Minh Đức
- Nguyễn Duy Du
- Phạm Hồng Vinh