.. 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