Layers tùy chỉnh
================
Một yếu tố đằng sau sự thành công của deep learning là sự sẵn có của một
loạt các lớp có thể được sáng tác theo những cách sáng tạo để thiết kế
kiến trúc phù hợp với nhiều nhiệm vụ khác nhau. Ví dụ, các nhà nghiên
cứu đã phát minh ra các lớp đặc biệt để xử lý hình ảnh, văn bản, lặp lại
dữ liệu tuần tự và thực hiện lập trình động. Sớm hay muộn, bạn sẽ gặp
hoặc phát minh ra một lớp chưa tồn tại trong khuôn khổ học sâu. Trong
những trường hợp này, bạn phải xây dựng một lớp tùy chỉnh. Trong phần
này, chúng tôi chỉ cho bạn như thế nào.
Các lớp không có tham số
------------------------
Để bắt đầu, chúng tôi xây dựng một lớp tùy chỉnh không có bất kỳ tham số
nào của riêng nó. Điều này sẽ trông quen thuộc nếu bạn nhớ lại giới
thiệu của chúng tôi để chặn trong :numref:`sec_model_construction`.
Lớp ``CenteredLayer`` sau chỉ đơn giản là 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 lớp cơ sở và thực
hiện hàm tuyên truyền chuyển tiếp.
.. raw:: html
.. raw:: html
.. code:: python
from mxnet import np, npx
from mxnet.gluon import nn
npx.set_np()
class CenteredLayer(nn.Block):
def __init__(self, **kwargs):
super().__init__(**kwargs)
def forward(self, X):
return X - X.mean()
.. raw:: html
.. raw:: html
.. code:: python
import torch
from torch import nn
from torch.nn import functional as F
class CenteredLayer(nn.Module):
def __init__(self):
super().__init__()
def forward(self, X):
return X - X.mean()
.. raw:: html
.. raw:: html
.. code:: python
import tensorflow as tf
class CenteredLayer(tf.keras.Model):
def __init__(self):
super().__init__()
def call(self, inputs):
return inputs - tf.reduce_mean(inputs)
.. raw:: html
.. raw:: html
Hãy để chúng tôi xác minh rằng lớp của chúng tôi hoạt động như dự định
bằng cách cung cấp một số dữ liệu thông qua nó.
.. raw:: html
.. raw:: html
.. code:: python
layer = CenteredLayer()
layer(np.array([1, 2, 3, 4, 5]))
.. parsed-literal::
:class: output
array([-2., -1., 0., 1., 2.])
.. raw:: html
.. raw:: html
.. code:: python
layer = CenteredLayer()
layer(torch.FloatTensor([1, 2, 3, 4, 5]))
.. parsed-literal::
:class: output
tensor([-2., -1., 0., 1., 2.])
.. raw:: html
.. raw:: html
.. code:: python
layer = CenteredLayer()
layer(tf.constant([1, 2, 3, 4, 5]))
.. parsed-literal::
:class: output
.. raw:: html
.. raw:: html
Bây giờ chúng ta có thể kết hợp layer của chúng ta như một component
trong việc xây dựng các mô hình phức tạp hơn.
.. raw:: html
.. raw:: html
.. code:: python
net = nn.Sequential()
net.add(nn.Dense(128), CenteredLayer())
net.initialize()
.. raw:: html
.. raw:: html
.. code:: python
net = nn.Sequential(nn.Linear(8, 128), CenteredLayer())
.. raw:: html
.. raw:: html
.. code:: python
net = tf.keras.Sequential([tf.keras.layers.Dense(128), CenteredLayer()])
.. raw:: html
.. raw:: html
Là một kiểm tra thêm sự tỉnh táo, chúng tôi có thể gửi dữ liệu ngẫu
nhiên thông qua mạng và kiểm tra xem trung bình có thực tế 0 không. Bởi
vì chúng ta đang xử lý các số điểm nổi, chúng ta vẫn có thể thấy một số
nonzero rất nhỏ do lượng tử hóa.
.. raw:: html
.. raw:: html
.. code:: python
Y = net(np.random.uniform(size=(4, 8)))
Y.mean()
.. parsed-literal::
:class: output
array(3.783498e-10)
.. raw:: html
.. raw:: html
.. code:: python
Y = net(torch.rand(4, 8))
Y.mean()
.. parsed-literal::
:class: output
tensor(-4.6566e-09, grad_fn=)
.. raw:: html
.. raw:: html
.. code:: python
Y = net(tf.random.uniform((4, 8)))
tf.reduce_mean(Y)
.. parsed-literal::
:class: output
.. raw:: html
.. raw:: html
Các lớp có tham số
------------------
Bây giờ chúng ta đã biết cách xác định các lớp đơn giản, chúng ta hãy
chuyển sang xác định các lớp với các tham số có thể được điều chỉnh
thông qua đào tạo. Chúng ta có thể sử dụng các chức năng tích hợp để tạo
các tham số, cung cấp một số chức năng dọn phòng cơ bản. Đặc biệt, họ
chi phối quyền truy cập, khởi tạo, chia sẻ, lưu và tải các tham số mô
hình. Bằng cách này, trong số các lợi ích khác, chúng tôi sẽ không cần
phải viết các thói quen serialization tùy chỉnh cho mọi lớp tùy chỉnh.
Bây giờ chúng ta hãy thực hiện phiên bản riêng của chúng ta của lớp được
kết nối hoàn toàn. Nhớ lại rằng lớp này yêu cầu hai tham số, một để đại
diện cho trọng lượng và một cho sự thiên vị. Trong triển khai này, chúng
tôi nướng trong kích hoạt ReLU dưới dạng mặc định. Lớp này yêu cầu nhập
đối số: ``in_units`` và ``units``, biểu thị số lượng đầu vào và đầu ra,
tương ứng.
.. raw:: html
.. raw:: html
.. code:: python
class MyDense(nn.Block):
def __init__(self, units, in_units, **kwargs):
super().__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(ctx=x.ctx)) + self.bias.data(
ctx=x.ctx)
return npx.relu(linear)
Tiếp theo, chúng tôi khởi tạo lớp ``MyDense`` và truy cập các tham số mô
hình của nó.
.. 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
.. raw:: html
.. code:: python
class MyLinear(nn.Module):
def __init__(self, in_units, units):
super().__init__()
self.weight = nn.Parameter(torch.randn(in_units, units))
self.bias = nn.Parameter(torch.randn(units,))
def forward(self, X):
linear = torch.matmul(X, self.weight.data) + self.bias.data
return F.relu(linear)
Tiếp theo, chúng tôi khởi tạo lớp ``MyLinear`` và truy cập các tham số
mô hình của nó.
.. code:: python
linear = MyLinear(5, 3)
linear.weight
.. parsed-literal::
:class: output
Parameter containing:
tensor([[-0.5447, -0.4223, -0.7320],
[-0.3532, 1.4021, 0.3656],
[ 0.4039, 0.0149, -1.7429],
[-0.9663, -2.5671, -1.0107],
[ 0.4001, -0.4510, 0.6005]], requires_grad=True)
.. raw:: html
.. raw:: html
.. code:: python
class MyDense(tf.keras.Model):
def __init__(self, units):
super().__init__()
self.units = units
def build(self, X_shape):
self.weight = self.add_weight(name='weight',
shape=[X_shape[-1], self.units],
initializer=tf.random_normal_initializer())
self.bias = self.add_weight(
name='bias', shape=[self.units],
initializer=tf.zeros_initializer())
def call(self, X):
linear = tf.matmul(X, self.weight) + self.bias
return tf.nn.relu(linear)
Tiếp theo, chúng tôi khởi tạo lớp ``MyDense`` và truy cập các tham số mô
hình của nó.
.. code:: python
dense = MyDense(3)
dense(tf.random.uniform((2, 5)))
dense.get_weights()
.. parsed-literal::
:class: output
[array([[ 0.09981945, -0.04308238, -0.06013313],
[-0.04979542, -0.02897512, 0.02753923],
[ 0.06066662, 0.09469151, -0.00839288],
[-0.00426127, -0.03299933, 0.01423264],
[-0.00832251, 0.03117967, 0.02648194]], dtype=float32),
array([0., 0., 0.], dtype=float32)]
.. raw:: html
.. raw:: html
Chúng ta có thể trực tiếp thực hiện các phép tính tuyên truyền chuyển
tiếp bằng cách sử dụng các lớp tùy chỉnh
.. raw:: html
.. raw:: html
.. 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
.. raw:: html
.. code:: python
linear(torch.rand(2, 5))
.. parsed-literal::
:class: output
tensor([[1.8603, 0.0000, 0.0000],
[1.9760, 0.0000, 0.0000]])
.. raw:: html
.. raw:: html
.. code:: python
dense(tf.random.uniform((2, 5)))
.. parsed-literal::
:class: output
.. raw:: html
.. raw:: html
Chúng ta cũng có thể xây dựng các mô hình bằng cách sử dụng lớp tùy
chỉnh Khi chúng ta có rằng chúng ta có thể sử dụng nó giống như lớp được
kết nối hoàn toàn tích hợp.
.. raw:: html
.. raw:: html
.. 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
.. code:: python
net = nn.Sequential(MyLinear(64, 8), MyLinear(8, 1))
net(torch.rand(2, 64))
.. parsed-literal::
:class: output
tensor([[0.],
[0.]])
.. raw:: html
.. raw:: html
.. code:: python
net = tf.keras.models.Sequential([MyDense(8), MyDense(1)])
net(tf.random.uniform((2, 64)))
.. parsed-literal::
:class: output
.. raw:: html
.. raw:: html
Tóm tắt
-------
- Chúng ta có thể thiết kế các lớp tùy chỉnh thông qua lớp lớp cơ bản.
Điều này cho phép chúng ta xác định các lớp mới linh hoạt hoạt hoạt
khác với bất kỳ lớp hiện có nào trong thư viện.
- Sau khi được xác định, các lớp tùy chỉnh có thể được gọi trong bối
cảnh và kiến trúc tùy ý.
- Các lớp có thể có các tham số cục bộ, có thể được tạo thông qua các
chức năng tích hợp.
Bài tập
-------
1. Thiết kế một lớp lấy đầu vào và tính toán giảm tensor, tức là nó trả
về :math:`y_k = \sum_{i, j} W_{ijk} x_i x_j`.
2. Thiết kế một lớp trả về một nửa hàng đầu của hệ số Fourier của dữ
liệu.
.. raw:: html
.. raw:: html
`Discussions `__
.. raw:: html
.. raw:: html
`Discussions `__
.. raw:: html
.. raw:: html
`Discussions `__
.. raw:: html
.. raw:: html