.. raw:: html
.. raw:: html
.. raw:: html
Các tầng Tuỳ chỉnh
==================
.. raw:: html
Một trong những yếu tố dẫn đến thành công của học sâu là sự đa dạng của
các tầng. Những tầng này có thể được sắp xếp theo nhiều cách sáng tạo để
thiết kế nên những kiến trúc phù hợp với nhiều tác vụ khác nhau. Ví dụ,
các nhà nghiên cứu đã phát minh ra các tầng chuyên dụng để xử lý ảnh,
chữ viết, lặp trên dữ liệu tuần tự, thực thi quy hoạch động, v.v… Dù sớm
hay muộn, bạn cũng sẽ gặp (hoặc sáng tạo) một tầng không có trong Gluon.
Đối với những trường hợp như vậy, bạn cần xây dựng một tầng tuỳ chỉnh.
Phần này sẽ hướng dẫn bạn cách thực hiện điều đó.
.. raw:: html
Các tầng không có Tham số
-------------------------
.. raw:: html
Để bắt đầu, ta tạo một tầng tùy chỉnh (một Khối) không chứa bất kỳ tham
số nào. Bước này khá quen thuộc nếu bạn còn nhớ phần giới thiệu về
``Block`` của Gluon tại :numref:`sec_model_construction`. Lớp
``CenteredLayer`` chỉ đơn thuần trừ đi giá trị trung bình từ đầu vào của
nó. Để xây dựng nó, chúng ta chỉ cần kế thừa từ lớp ``Block`` và lập
trình phương thức ``forward``.
.. code:: python
from mxnet import gluon, np, npx
from mxnet.gluon import nn
npx.set_np()
class CenteredLayer(nn.Block):
def __init__(self, **kwargs):
super(CenteredLayer, self).__init__(**kwargs)
def forward(self, x):
return x - x.mean()
.. raw:: html
Hãy cùng xác thực rằng tầng này hoạt động như ta mong muốn bằng cách
truyền dữ liệu vào nó.
.. code:: python
layer = CenteredLayer()
layer(np.array([1, 2, 3, 4, 5]))
.. parsed-literal::
:class: output
array([-2., -1., 0., 1., 2.])
.. raw:: html
Chúng ta cũng có thể kết hợp tầng này như là một thành phần để xây dựng
các mô hình phức tạp hơn.
.. code:: python
net = nn.Sequential()
net.add(nn.Dense(128), CenteredLayer())
net.initialize()
.. raw:: html
Để kiểm tra thêm, chúng ta có thể truyền dữ liệu ngẫu nhiên qua mạng và
chứng thực xem giá trị trung bình đã về 0 hay chưa. Chú ý rằng vì đang
làm việc với các số thực dấu phẩy động, chúng ta sẽ thấy một giá trị
khác không *rất* nhỏ.
.. code:: python
y = net(np.random.uniform(size=(4, 8)))
y.mean()
.. parsed-literal::
:class: output
array(3.783498e-10)
.. raw:: html
.. raw:: html
.. raw:: html
.. raw:: html
.. raw:: html
Tầng có Tham số
---------------
.. raw:: html
Giờ đây ta đã biết cách định nghĩa các tầng đơn giản, hãy chuyển sang
việc định nghĩa các tầng chứa tham số có thể điều chỉnh được trong quá
trình huấn luyện. Để tự động hóa các công việc lặp lại, lớp
``Parameter`` và từ điển ``ParameterDict`` cung cấp một số tính năng
quản trị cơ bản. Cụ thể, chúng sẽ quản lý việc truy cập, khởi tạo, chia
sẻ, lưu và nạp các tham số mô hình. Bằng cách này, cùng với nhiều lợi
ích khác, ta không cần phải viết lại các thủ tục tuần tự hóa
(*serialization*) cho mỗi tầng tùy chỉnh mới.
.. raw:: html
Lớp ``Block`` chứa biến ``params`` với kiểu dữ liệu ``ParameterDict``.
Từ điển này ánh xạ các xâu kí tự biểu thị tên tham số đến các tham số mô
hình (thuộc kiểu ``Parameter``). ``ParameterDict`` cũng cung cấp hàm
``get`` giúp việc tạo tham số mới với tên và chiều cụ thể trở nên dễ
dàng.
.. code:: python
params = gluon.ParameterDict()
params.get('param2', shape=(2, 3))
params
.. parsed-literal::
:class: output
(
Parameter param2 (shape=(2, 3), dtype=)
)
.. raw:: html
Giờ đây chúng ta đã có tất cả các thành phần cơ bản cần thiết để tự tạo
một phiên bản tùy chỉnh của tầng ``Dense`` trong Gluon. Chú ý rằng tầng
này yêu cầu hai tham số: một cho trọng số và một cho hệ số điều chỉnh.
Trong cách lập trình này, ta sử dụng hàm kích hoạt mặc định là hàm ReLU.
Trong hàm ``__init__``, ``in_units`` và ``units`` biểu thị lần lượt số
lượng đầu vào và đầu ra.
.. code:: python
class MyDense(nn.Block):
# units: the number of outputs in this layer; in_units: the number of
# inputs in this layer
def __init__(self, units, in_units, **kwargs):
super(MyDense, self).__init__(**kwargs)
self.weight = self.params.get('weight', shape=(in_units, units))
self.bias = self.params.get('bias', shape=(units,))
def forward(self, x):
linear = np.dot(x, self.weight.data()) + self.bias.data()
return npx.relu(linear)
.. raw:: html
Việc đặt tên cho các tham số cho phép ta truy cập chúng theo tên thông
qua tra cứu từ điển sau này. Nhìn chung, bạn sẽ muốn đặt cho các biến
những tên đơn giản biểu thị rõ mục đích của chúng. Tiếp theo, ta sẽ khởi
tạo lớp ``MyDense`` và truy cập các tham số mô hình. Lưu ý rằng tên của
Khối được tự động thêm vào trước tên các tham số.
.. code:: python
dense = MyDense(units=3, in_units=5)
dense.params
.. parsed-literal::
:class: output
mydense0_ (
Parameter mydense0_weight (shape=(5, 3), dtype=)
Parameter mydense0_bias (shape=(3,), dtype=)
)
.. raw:: html
Ta có thể trực tiếp thực thi các phép tính truyền xuôi có sử dụng các
tầng tùy chỉnh.
.. code:: python
dense.initialize()
dense(np.random.uniform(size=(2, 5)))
.. parsed-literal::
:class: output
array([[0. , 0.01633355, 0. ],
[0. , 0.01581812, 0. ]])
.. raw:: html
Các tầng tùy chỉnh cũng có thể được dùng để xây dựng mô hình. Chúng có
thể được sử dụng như các tầng kết nối dày đặc được lập trình sẵn. Ngoại
lệ duy nhất là việc suy luận kích thước sẽ không được thực hiện tự động.
Để biết thêm chi tiết về cách thực hiện việc này, vui lòng tham khảo
`tài liệu MXNet `__.
.. code:: python
net = nn.Sequential()
net.add(MyDense(8, in_units=64),
MyDense(1, in_units=8))
net.initialize()
net(np.random.uniform(size=(2, 64)))
.. parsed-literal::
:class: output
array([[0.06508517],
[0.0615553 ]])
.. raw:: html
.. raw:: html
.. raw:: html
Tóm tắt
-------
.. raw:: html
- Ta có thể thiết kế các tầng tùy chỉnh thông qua lớp ``Block``. Điều
này cho phép ta định nghĩa một cách linh hoạt các tầng có cách hoạt
động khác với các tầng có sẵn trong thư viện.
- Một khi đã được định nghĩa, các tầng tùy chỉnh có thể được gọi trong
những bối cảnh và kiến trúc tùy ý.
- Các khối có thể có các tham số cục bộ, được lưu trữ dưới dạng đối
tượng ``ParameterDict`` trong mỗi thuộc tính ``params`` của Block.
.. raw:: html
Bài tập
-------
.. raw:: html
1. Thiết kế một tầng có khả năng học một phép biến đổi affine của dữ
liệu.
2. Thiết kế một tầng nhận đầu vào và tính toán phép giảm tensor, tức trả
về :math:`y_k = \sum_{i, j} W_{ijk} x_i x_j`.
3. Thiết kế một tầng trả về nửa đầu của các hệ số Fourier của dữ liệu.
Gợi ý: hãy tra cứu hàm ``fft`` trong MXNet.
.. 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 Lê Quang Nhật
- Nguyễn Văn Cường
- Phạm Hồng Vinh
- Lê Khắc Hồng Phúc
- Phạm Minh Đức
- Nguyễn Duy Du