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