Quản lý tham số
===============
Khi chúng tôi đã chọn một kiến trúc và đặt các siêu tham số của mình,
chúng tôi tiến hành vòng đào tạo, nơi mục tiêu của chúng tôi là tìm các
giá trị tham số giảm thiểu chức năng mất mát của chúng tôi. Sau khi đào
tạo, chúng tôi sẽ cần các thông số này để đưa ra dự đoán trong tương
lai. Ngoài ra, đôi khi chúng tôi sẽ muốn trích xuất các tham số để sử
dụng lại chúng trong một số bối cảnh khác, để lưu mô hình của chúng tôi
vào đĩa để nó có thể được thực thi trong phần mềm khác hoặc để kiểm tra
với hy vọng đạt được sự hiểu biết khoa học.
Hầu hết thời gian, chúng ta sẽ có thể bỏ qua các chi tiết nitty-gritty
về cách các tham số được khai báo và thao tác, dựa vào các khuôn khổ học
sâu để thực hiện việc nâng nặng. Tuy nhiên, khi chúng ta di chuyển ra
khỏi các kiến trúc xếp chồng lên nhau với các lớp tiêu chuẩn, đôi khi
chúng ta sẽ cần phải vào cỏ dại khai báo và thao tác các tham số. Trong
phần này, chúng tôi đề cập đến những điều sau:
- Truy cập các tham số để gỡ lỗi, chẩn đoán và trực quan hóa.
- Khởi tạo tham số.
- Chia sẻ các thông số trên các thành phần mô hình khác nhau.
Chúng tôi bắt đầu bằng cách tập trung vào MLP với một lớp ẩn.
.. raw:: html
.. raw:: html
.. code:: python
from mxnet import init, np, npx
from mxnet.gluon import nn
npx.set_np()
net = nn.Sequential()
net.add(nn.Dense(8, activation='relu'))
net.add(nn.Dense(1))
net.initialize() # Use the default initialization method
X = np.random.uniform(size=(2, 4))
net(X) # Forward computation
.. parsed-literal::
:class: output
array([[0.0054572 ],
[0.00488594]])
.. raw:: html
.. raw:: html
.. code:: python
import torch
from torch import nn
net = nn.Sequential(nn.Linear(4, 8), nn.ReLU(), nn.Linear(8, 1))
X = torch.rand(size=(2, 4))
net(X)
.. parsed-literal::
:class: output
tensor([[0.0251],
[0.0025]], grad_fn=
)
.. raw:: html
.. raw:: html
.. code:: python
import tensorflow as tf
net = tf.keras.models.Sequential([
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(4, activation=tf.nn.relu),
tf.keras.layers.Dense(1),
])
X = tf.random.uniform((2, 4))
net(X)
.. parsed-literal::
:class: output
.. raw:: html
.. raw:: html
Truy cập tham số
----------------
Hãy để chúng tôi bắt đầu với cách truy cập các tham số từ các mô hình mà
bạn đã biết. Khi một mô hình được định nghĩa thông qua lớp
``Sequential``, trước tiên chúng ta có thể truy cập bất kỳ lớp nào bằng
cách lập chỉ mục vào mô hình như thể nó là một danh sách. Các tham số
của mỗi lớp được đặt thuận tiện trong thuộc tính của nó. Chúng ta có thể
kiểm tra các tham số của lớp được kết nối hoàn toàn thứ hai như sau.
.. raw:: html
.. raw:: html
.. code:: python
print(net[1].params)
.. parsed-literal::
:class: output
dense1_ (
Parameter dense1_weight (shape=(1, 8), dtype=float32)
Parameter dense1_bias (shape=(1,), dtype=float32)
)
.. raw:: html
.. raw:: html
.. code:: python
print(net[2].state_dict())
.. parsed-literal::
:class: output
OrderedDict([('weight', tensor([[-0.1270, 0.2448, 0.3073, -0.3091, 0.0385, -0.2082, 0.2561, -0.1132]])), ('bias', tensor([-0.1036]))])
.. raw:: html
.. raw:: html
.. code:: python
print(net.layers[2].weights)
.. parsed-literal::
:class: output
[, ]
.. raw:: html
.. raw:: html
Đầu ra cho chúng ta biết một vài điều quan trọng. Đầu tiên, lớp kết nối
hoàn toàn này chứa hai tham số, tương ứng với trọng lượng và thành kiến
của lớp đó, tương ứng. Cả hai đều được lưu trữ dưới dạng phao chính xác
đơn (float32). Lưu ý rằng tên của các tham số cho phép chúng ta xác định
duy nhất các tham số của từng lớp, ngay cả trong một mạng chứa hàng trăm
lớp.
Tham số được nhắm mục tiêu
~~~~~~~~~~~~~~~~~~~~~~~~~~
Lưu ý rằng mỗi tham số được biểu diễn dưới dạng một đối tượng của lớp
tham số. Để làm bất cứ điều gì hữu ích với các tham số, trước tiên chúng
ta cần truy cập các giá trị số cơ bản. Có một số cách để làm điều này.
Một số đơn giản hơn trong khi những người khác nói chung hơn. Đoạn code
sau trích xuất sự thiên vị từ lớp mạng nơ-ron thứ hai, nó trả về một đối
tượng lớp tham số, và truy cập thêm giá trị của tham số đó.
.. raw:: html
.. raw:: html
.. code:: python
print(type(net[1].bias))
print(net[1].bias)
print(net[1].bias.data())
.. parsed-literal::
:class: output
Parameter dense1_bias (shape=(1,), dtype=float32)
[0.]
Tham số là các đối tượng phức tạp, chứa các giá trị, độ dốc và thông tin
bổ sung. Đó là lý do tại sao chúng ta cần yêu cầu giá trị một cách rõ
ràng.
Ngoài giá trị, mỗi tham số cũng cho phép chúng ta truy cập gradient. Bởi
vì chúng tôi chưa gọi backpropagation cho mạng này được nêu ra, nó ở
trạng thái ban đầu của nó.
.. code:: python
net[1].weight.grad()
.. parsed-literal::
:class: output
array([[0., 0., 0., 0., 0., 0., 0., 0.]])
.. raw:: html
.. raw:: html
.. code:: python
print(type(net[2].bias))
print(net[2].bias)
print(net[2].bias.data)
.. parsed-literal::
:class: output
Parameter containing:
tensor([-0.1036], requires_grad=True)
tensor([-0.1036])
Tham số là các đối tượng phức tạp, chứa các giá trị, độ dốc và thông tin
bổ sung. Đó là lý do tại sao chúng ta cần yêu cầu giá trị một cách rõ
ràng.
Ngoài giá trị, mỗi tham số cũng cho phép chúng ta truy cập gradient. Bởi
vì chúng tôi chưa gọi backpropagation cho mạng này được nêu ra, nó ở
trạng thái ban đầu của nó.
.. code:: python
net[2].weight.grad == None
.. parsed-literal::
:class: output
True
.. raw:: html
.. raw:: html
.. code:: python
print(type(net.layers[2].weights[1]))
print(net.layers[2].weights[1])
print(tf.convert_to_tensor(net.layers[2].weights[1]))
.. parsed-literal::
:class: output
tf.Tensor([0.], shape=(1,), dtype=float32)
.. raw:: html
.. raw:: html
Tất cả các thông số tại một lần
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Khi chúng ta cần thực hiện các thao tác trên tất cả các tham số, truy
cập chúng từng cái một có thể phát triển tẻ nhạt. Tình hình có thể phát
triển đặc biệt khó sử dụng khi chúng ta làm việc với các khối phức tạp
hơn (ví dụ, các khối lồng nhau), vì chúng ta cần đệ quy qua toàn bộ cây
để trích xuất các tham số của từng khối phụ. Dưới đây chúng tôi chứng
minh việc truy cập các tham số của lớp được kết nối hoàn toàn đầu tiên
so với truy cập tất cả các lớp.
.. raw:: html
.. raw:: html
.. code:: python
print(net[0].collect_params())
print(net.collect_params())
.. parsed-literal::
:class: output
dense0_ (
Parameter dense0_weight (shape=(8, 4), dtype=float32)
Parameter dense0_bias (shape=(8,), dtype=float32)
)
sequential0_ (
Parameter dense0_weight (shape=(8, 4), dtype=float32)
Parameter dense0_bias (shape=(8,), dtype=float32)
Parameter dense1_weight (shape=(1, 8), dtype=float32)
Parameter dense1_bias (shape=(1,), dtype=float32)
)
.. raw:: html
.. raw:: html
.. code:: python
print(*[(name, param.shape) for name, param in net[0].named_parameters()])
print(*[(name, param.shape) for name, param in net.named_parameters()])
.. parsed-literal::
:class: output
('weight', torch.Size([8, 4])) ('bias', torch.Size([8]))
('0.weight', torch.Size([8, 4])) ('0.bias', torch.Size([8])) ('2.weight', torch.Size([1, 8])) ('2.bias', torch.Size([1]))
.. raw:: html
.. raw:: html
.. code:: python
print(net.layers[1].weights)
print(net.get_weights())
.. parsed-literal::
:class: output
[, ]
[array([[ 0.7232258 , 0.78331023, 0.60271305, 0.8582216 ],
[-0.5968576 , 0.2452305 , 0.8569761 , -0.8358928 ],
[-0.29286593, -0.29781765, 0.63798755, -0.49257767],
[ 0.49835342, -0.22738558, 0.41689676, -0.38474053]],
dtype=float32), array([0., 0., 0., 0.], dtype=float32), array([[-0.8811922 ],
[ 0.5014286 ],
[-1.0506117 ],
[ 0.52643347]], dtype=float32), array([0.], dtype=float32)]
.. raw:: html
.. raw:: html
Điều này cung cấp cho chúng tôi một cách khác để truy cập các tham số
của mạng như sau.
.. raw:: html
.. raw:: html
.. code:: python
net.collect_params()['dense1_bias'].data()
.. parsed-literal::
:class: output
array([0.])
.. raw:: html
.. raw:: html
.. code:: python
net.state_dict()['2.bias'].data
.. parsed-literal::
:class: output
tensor([-0.1036])
.. raw:: html
.. raw:: html
.. code:: python
net.get_weights()[1]
.. parsed-literal::
:class: output
array([0., 0., 0., 0.], dtype=float32)
.. raw:: html
.. raw:: html
Thu thập các thông số từ các khối lồng nhựa
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Chúng ta hãy xem làm thế nào các quy ước đặt tên tham số hoạt động nếu
chúng ta tổ nhiều khối bên trong nhau. Đối với điều đó đầu tiên chúng ta
xác định một hàm tạo ra các khối (một nhà máy khối, có thể nói) và sau
đó kết hợp các khối bên trong nhưng lớn hơn.
.. raw:: html
.. raw:: html
.. code:: python
def block1():
net = nn.Sequential()
net.add(nn.Dense(32, activation='relu'))
net.add(nn.Dense(16, activation='relu'))
return net
def block2():
net = nn.Sequential()
for _ in range(4):
# Nested here
net.add(block1())
return net
rgnet = nn.Sequential()
rgnet.add(block2())
rgnet.add(nn.Dense(10))
rgnet.initialize()
rgnet(X)
.. parsed-literal::
:class: output
array([[-6.3465846e-09, -1.1096752e-09, 6.4161787e-09, 6.6354140e-09,
-1.1265507e-09, 1.3284951e-10, 9.3619388e-09, 3.2229084e-09,
5.9429879e-09, 8.8181435e-09],
[-8.6219423e-09, -7.5150686e-10, 8.3133251e-09, 8.9321128e-09,
-1.6740003e-09, 3.2405989e-10, 1.2115976e-08, 4.4926449e-09,
8.0741742e-09, 1.2075874e-08]])
.. raw:: html
.. raw:: html
.. code:: python
def block1():
return nn.Sequential(nn.Linear(4, 8), nn.ReLU(),
nn.Linear(8, 4), nn.ReLU())
def block2():
net = nn.Sequential()
for i in range(4):
# Nested here
net.add_module(f'block {i}', block1())
return net
rgnet = nn.Sequential(block2(), nn.Linear(4, 1))
rgnet(X)
.. parsed-literal::
:class: output
tensor([[-0.5720],
[-0.5720]], grad_fn=
)
.. raw:: html
.. raw:: html
.. code:: python
def block1(name):
return tf.keras.Sequential([
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(4, activation=tf.nn.relu)],
name=name)
def block2():
net = tf.keras.Sequential()
for i in range(4):
# Nested here
net.add(block1(name=f'block-{i}'))
return net
rgnet = tf.keras.Sequential()
rgnet.add(block2())
rgnet.add(tf.keras.layers.Dense(1))
rgnet(X)
.. parsed-literal::
:class: output
.. raw:: html
.. raw:: html
Bây giờ chúng tôi đã thiết kế mạng, chúng ta hãy xem nó được tổ chức như
thế nào.
.. raw:: html
.. raw:: html
.. code:: python
print(rgnet.collect_params)
print(rgnet.collect_params())
.. parsed-literal::
:class: output
32, Activation(relu))
(1): Dense(32 -> 16, Activation(relu))
)
(1): Sequential(
(0): Dense(16 -> 32, Activation(relu))
(1): Dense(32 -> 16, Activation(relu))
)
(2): Sequential(
(0): Dense(16 -> 32, Activation(relu))
(1): Dense(32 -> 16, Activation(relu))
)
(3): Sequential(
(0): Dense(16 -> 32, Activation(relu))
(1): Dense(32 -> 16, Activation(relu))
)
)
(1): Dense(16 -> 10, linear)
)>
sequential1_ (
Parameter dense2_weight (shape=(32, 4), dtype=float32)
Parameter dense2_bias (shape=(32,), dtype=float32)
Parameter dense3_weight (shape=(16, 32), dtype=float32)
Parameter dense3_bias (shape=(16,), dtype=float32)
Parameter dense4_weight (shape=(32, 16), dtype=float32)
Parameter dense4_bias (shape=(32,), dtype=float32)
Parameter dense5_weight (shape=(16, 32), dtype=float32)
Parameter dense5_bias (shape=(16,), dtype=float32)
Parameter dense6_weight (shape=(32, 16), dtype=float32)
Parameter dense6_bias (shape=(32,), dtype=float32)
Parameter dense7_weight (shape=(16, 32), dtype=float32)
Parameter dense7_bias (shape=(16,), dtype=float32)
Parameter dense8_weight (shape=(32, 16), dtype=float32)
Parameter dense8_bias (shape=(32,), dtype=float32)
Parameter dense9_weight (shape=(16, 32), dtype=float32)
Parameter dense9_bias (shape=(16,), dtype=float32)
Parameter dense10_weight (shape=(10, 16), dtype=float32)
Parameter dense10_bias (shape=(10,), dtype=float32)
)
.. raw:: html
.. raw:: html
.. code:: python
print(rgnet)
.. parsed-literal::
:class: output
Sequential(
(0): Sequential(
(block 0): Sequential(
(0): Linear(in_features=4, out_features=8, bias=True)
(1): ReLU()
(2): Linear(in_features=8, out_features=4, bias=True)
(3): ReLU()
)
(block 1): Sequential(
(0): Linear(in_features=4, out_features=8, bias=True)
(1): ReLU()
(2): Linear(in_features=8, out_features=4, bias=True)
(3): ReLU()
)
(block 2): Sequential(
(0): Linear(in_features=4, out_features=8, bias=True)
(1): ReLU()
(2): Linear(in_features=8, out_features=4, bias=True)
(3): ReLU()
)
(block 3): Sequential(
(0): Linear(in_features=4, out_features=8, bias=True)
(1): ReLU()
(2): Linear(in_features=8, out_features=4, bias=True)
(3): ReLU()
)
)
(1): Linear(in_features=4, out_features=1, bias=True)
)
.. raw:: html
.. raw:: html
.. code:: python
print(rgnet.summary())
.. parsed-literal::
:class: output
Model: "sequential_1"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
sequential_2 (Sequential) (2, 4) 80
dense_6 (Dense) (2, 1) 5
=================================================================
Total params: 85
Trainable params: 85
Non-trainable params: 0
_________________________________________________________________
None
.. raw:: html
.. raw:: html
Kể từ khi các lớp được phân cấp lồng nhau, chúng ta cũng có thể truy cập
chúng như thể lập chỉ mục thông qua các danh sách lồng nhau. Ví dụ,
chúng ta có thể truy cập khối chính đầu tiên, bên trong đó khối phụ thứ
hai và trong đó sự thiên vị của lớp đầu tiên, với như sau.
.. raw:: html
.. raw:: html
.. code:: python
rgnet[0][1][0].bias.data()
.. parsed-literal::
:class: output
array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
.. raw:: html
.. raw:: html
.. code:: python
rgnet[0][1][0].bias.data
.. parsed-literal::
:class: output
tensor([-0.0210, 0.2743, 0.0433, -0.1879, -0.3919, -0.1085, 0.0300, -0.4445])
.. raw:: html
.. raw:: html
.. code:: python
rgnet.layers[0].layers[1].layers[1].weights[1]
.. parsed-literal::
:class: output
.. raw:: html
.. raw:: html
Khởi tạo tham số
----------------
Bây giờ chúng ta đã biết cách truy cập các tham số, chúng ta hãy xem
cách khởi tạo chúng đúng cách. Chúng tôi đã thảo luận về sự cần thiết
phải khởi tạo thích hợp trong :numref:`sec_numerical_stability`. Khung
học sâu cung cấp các khởi tạo ngẫu nhiên mặc định cho các lớp của nó.
Tuy nhiên, chúng tôi thường muốn khởi tạo trọng lượng của mình theo các
giao thức khác nhau. Khung cung cấp các giao thức được sử dụng phổ biến
nhất và cũng cho phép tạo một trình khởi tạo tùy chỉnh.
.. raw:: html
.. raw:: html
Theo mặc định, MXNet khởi tạo các tham số trọng lượng bằng cách vẽ ngẫu
nhiên từ một phân phối thống nhất :math:`U(-0.07, 0.07)`, xóa các tham
số thiên vị về 0. mô-đun ``init`` của MXNet cung cấp nhiều phương pháp
khởi tạo cài sẵn.
.. raw:: html
.. raw:: html
Theo mặc định, PyTorch khởi tạo các ma trận trọng lượng và thiên vị đồng
đều bằng cách vẽ từ một phạm vi được tính toán theo kích thước đầu vào
và đầu ra. mô-đun ``nn.init`` của PyTorch cung cấp một loạt các phương
pháp khởi tạo cài sẵn.
.. raw:: html
.. raw:: html
Theo mặc định, Keras khởi tạo các ma trận trọng lượng đồng đều bằng cách
vẽ từ một phạm vi được tính theo kích thước đầu vào và đầu ra, và các
tham số thiên vị đều được đặt thành 0. TensorFlow cung cấp một loạt các
phương pháp khởi tạo cả trong mô-đun gốc và mô-đun
``keras.initializers``.
.. raw:: html
.. raw:: html
Khởi tạo tích hợp
~~~~~~~~~~~~~~~~~
Hãy để chúng tôi bắt đầu bằng cách gọi trên built-in initializers. Mã
dưới đây khởi tạo tất cả các tham số trọng lượng dưới dạng biến ngẫu
nhiên Gaussian với độ lệch chuẩn 0,01, trong khi các tham số thiên vị bị
xóa về 0.
.. raw:: html
.. raw:: html
.. code:: python
# Here `force_reinit` ensures that parameters are freshly initialized even if
# they were already initialized previously
net.initialize(init=init.Normal(sigma=0.01), force_reinit=True)
net[0].weight.data()[0]
.. parsed-literal::
:class: output
array([-0.00324057, -0.00895028, -0.00698632, 0.01030831])
.. raw:: html
.. raw:: html
.. code:: python
def init_normal(m):
if type(m) == nn.Linear:
nn.init.normal_(m.weight, mean=0, std=0.01)
nn.init.zeros_(m.bias)
net.apply(init_normal)
net[0].weight.data[0], net[0].bias.data[0]
.. parsed-literal::
:class: output
(tensor([-0.0152, -0.0023, -0.0003, 0.0055]), tensor(0.))
.. raw:: html
.. raw:: html
.. code:: python
net = tf.keras.models.Sequential([
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(
4, activation=tf.nn.relu,
kernel_initializer=tf.random_normal_initializer(mean=0, stddev=0.01),
bias_initializer=tf.zeros_initializer()),
tf.keras.layers.Dense(1)])
net(X)
net.weights[0], net.weights[1]
.. parsed-literal::
:class: output
(,
)
.. raw:: html
.. raw:: html
Chúng ta cũng có thể khởi tạo tất cả các tham số thành một giá trị không
đổi cho trước (ví dụ, 1).
.. raw:: html
.. raw:: html
.. code:: python
net.initialize(init=init.Constant(1), force_reinit=True)
net[0].weight.data()[0]
.. parsed-literal::
:class: output
array([1., 1., 1., 1.])
.. raw:: html
.. raw:: html
.. code:: python
def init_constant(m):
if type(m) == nn.Linear:
nn.init.constant_(m.weight, 1)
nn.init.zeros_(m.bias)
net.apply(init_constant)
net[0].weight.data[0], net[0].bias.data[0]
.. parsed-literal::
:class: output
(tensor([1., 1., 1., 1.]), tensor(0.))
.. raw:: html
.. raw:: html
.. code:: python
net = tf.keras.models.Sequential([
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(
4, activation=tf.nn.relu,
kernel_initializer=tf.keras.initializers.Constant(1),
bias_initializer=tf.zeros_initializer()),
tf.keras.layers.Dense(1),
])
net(X)
net.weights[0], net.weights[1]
.. parsed-literal::
:class: output
(,
)
.. raw:: html
.. raw:: html
Chúng tôi cũng có thể áp dụng các trình khởi tạo khác nhau cho các khối
nhất định Ví dụ, bên dưới chúng ta khởi tạo lớp đầu tiên với trình khởi
tạo Xavier và khởi tạo lớp thứ hai thành giá trị không đổi là 42.
.. raw:: html
.. raw:: html
.. code:: python
net[0].weight.initialize(init=init.Xavier(), force_reinit=True)
net[1].initialize(init=init.Constant(42), force_reinit=True)
print(net[0].weight.data()[0])
print(net[1].weight.data())
.. parsed-literal::
:class: output
[-0.17594433 0.02314097 -0.1992535 0.09509248]
[[42. 42. 42. 42. 42. 42. 42. 42.]]
.. raw:: html
.. raw:: html
.. code:: python
def xavier(m):
if type(m) == nn.Linear:
nn.init.xavier_uniform_(m.weight)
def init_42(m):
if type(m) == nn.Linear:
nn.init.constant_(m.weight, 42)
net[0].apply(xavier)
net[2].apply(init_42)
print(net[0].weight.data[0])
print(net[2].weight.data)
.. parsed-literal::
:class: output
tensor([ 0.1975, 0.2599, -0.1798, 0.4012])
tensor([[42., 42., 42., 42., 42., 42., 42., 42.]])
.. raw:: html
.. raw:: html
.. code:: python
net = tf.keras.models.Sequential([
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(
4,
activation=tf.nn.relu,
kernel_initializer=tf.keras.initializers.GlorotUniform()),
tf.keras.layers.Dense(
1, kernel_initializer=tf.keras.initializers.Constant(42)),
])
net(X)
print(net.layers[1].weights[0])
print(net.layers[2].weights[0])
.. parsed-literal::
:class: output
.. raw:: html
.. raw:: html
Initializationtùy chỉnh
~~~~~~~~~~~~~~~~~~~~~~~
Đôi khi, các phương pháp khởi tạo chúng ta cần không được cung cấp bởi
khung học sâu. Trong ví dụ dưới đây, chúng ta định nghĩa một bộ khởi tạo
cho bất kỳ tham số trọng lượng nào :math:`w` bằng cách sử dụng phân phối
lạ sau:
.. math::
\begin{aligned}
w \sim \begin{cases}
U(5, 10) & \text{ with probability } \frac{1}{4} \\
0 & \text{ with probability } \frac{1}{2} \\
U(-10, -5) & \text{ with probability } \frac{1}{4}
\end{cases}
\end{aligned}
.. raw:: html
.. raw:: html
Ở đây chúng ta định nghĩa một lớp con của lớp ``Initializer``. Thông
thường, chúng ta chỉ cần thực hiện hàm ``_init_weight`` mà lấy một đối
số tensor (``data``) và gán cho nó các giá trị khởi tạo mong muốn.
.. code:: python
class MyInit(init.Initializer):
def _init_weight(self, name, data):
print('Init', name, data.shape)
data[:] = np.random.uniform(-10, 10, data.shape)
data *= np.abs(data) >= 5
net.initialize(MyInit(), force_reinit=True)
net[0].weight.data()[:2]
.. parsed-literal::
:class: output
Init dense0_weight (8, 4)
Init dense1_weight (1, 8)
.. parsed-literal::
:class: output
array([[ 0. , -0. , -0. , 8.522827 ],
[ 0. , -8.828651 , -0. , -5.6012006]])
.. raw:: html
.. raw:: html
Một lần nữa, chúng tôi triển khai một hàm ``my_init`` để áp dụng cho
``net``.
.. code:: python
def my_init(m):
if type(m) == nn.Linear:
print("Init", *[(name, param.shape)
for name, param in m.named_parameters()][0])
nn.init.uniform_(m.weight, -10, 10)
m.weight.data *= m.weight.data.abs() >= 5
net.apply(my_init)
net[0].weight[:2]
.. parsed-literal::
:class: output
Init weight torch.Size([8, 4])
Init weight torch.Size([1, 8])
.. parsed-literal::
:class: output
tensor([[-9.0877, -7.0061, 0.0000, 0.0000],
[ 0.0000, 0.0000, 5.4938, -0.0000]], grad_fn=)
.. raw:: html
.. raw:: html
Ở đây chúng ta định nghĩa một lớp con của ``Initializer`` và thực hiện
hàm ``__call__`` trả về một tensor mong muốn cho hình dạng và kiểu dữ
liệu.
.. code:: python
class MyInit(tf.keras.initializers.Initializer):
def __call__(self, shape, dtype=None):
data=tf.random.uniform(shape, -10, 10, dtype=dtype)
factor=(tf.abs(data) >= 5)
factor=tf.cast(factor, tf.float32)
return data * factor
net = tf.keras.models.Sequential([
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(
4,
activation=tf.nn.relu,
kernel_initializer=MyInit()),
tf.keras.layers.Dense(1),
])
net(X)
print(net.layers[1].weights[0])
.. parsed-literal::
:class: output
.. raw:: html
.. raw:: html
Lưu ý rằng chúng tôi luôn có tùy chọn đặt tham số trực tiếp.
.. raw:: html
.. raw:: html
.. code:: python
net[0].weight.data()[:] += 1
net[0].weight.data()[0, 0] = 42
net[0].weight.data()[0]
.. parsed-literal::
:class: output
array([42. , 1. , 1. , 9.522827])
Lưu ý cho người dùng nâng cao: nếu bạn muốn điều chỉnh các tham số trong
phạm vi ``autograd``, bạn cần sử dụng ``set_data`` để tránh nhầm lẫn cơ
chế phân biệt tự động.
.. raw:: html
.. raw:: html
.. code:: python
net[0].weight.data[:] += 1
net[0].weight.data[0, 0] = 42
net[0].weight.data[0]
.. parsed-literal::
:class: output
tensor([42.0000, -6.0061, 1.0000, 1.0000])
.. raw:: html
.. raw:: html
.. code:: python
net.layers[1].weights[0][:].assign(net.layers[1].weights[0] + 1)
net.layers[1].weights[0][0, 0].assign(42)
net.layers[1].weights[0]
.. parsed-literal::
:class: output
.. raw:: html
.. raw:: html
Tham số Tied
------------
Thông thường, chúng tôi muốn chia sẻ các tham số trên nhiều lớp. Hãy để
chúng tôi xem làm thế nào để làm điều này một cách thanh lịch. Sau đây,
chúng tôi phân bổ một lớp dày đặc và sau đó sử dụng các tham số của nó
đặc biệt để đặt các thông số của một lớp khác.
.. raw:: html
.. raw:: html
.. code:: python
net = nn.Sequential()
# We need to give the shared layer a name so that we can refer to its
# parameters
shared = nn.Dense(8, activation='relu')
net.add(nn.Dense(8, activation='relu'),
shared,
nn.Dense(8, activation='relu', params=shared.params),
nn.Dense(10))
net.initialize()
X = np.random.uniform(size=(2, 20))
net(X)
# Check whether the parameters are the same
print(net[1].weight.data()[0] == net[2].weight.data()[0])
net[1].weight.data()[0, 0] = 100
# Make sure that they are actually the same object rather than just having the
# same value
print(net[1].weight.data()[0] == net[2].weight.data()[0])
.. parsed-literal::
:class: output
[ True True True True True True True True]
[ True True True True True True True True]
Ví dụ này cho thấy các tham số của lớp thứ hai và thứ ba được gắn. Chúng
không chỉ bằng nhau, chúng được đại diện bởi cùng một tensor chính xác.
Do đó, nếu chúng ta thay đổi một trong các tham số, cái kia cũng thay
đổi. Bạn có thể tự hỏi, khi tham số được gắn những gì sẽ xảy ra với
gradient? Vì các tham số mô hình chứa gradient, các gradient của lớp ẩn
thứ hai và lớp ẩn thứ ba được thêm vào với nhau trong quá trình truyền
ngược.
.. raw:: html
.. raw:: html
.. code:: python
# We need to give the shared layer a name so that we can refer to its
# parameters
shared = nn.Linear(8, 8)
net = nn.Sequential(nn.Linear(4, 8), nn.ReLU(),
shared, nn.ReLU(),
shared, nn.ReLU(),
nn.Linear(8, 1))
net(X)
# Check whether the parameters are the same
print(net[2].weight.data[0] == net[4].weight.data[0])
net[2].weight.data[0, 0] = 100
# Make sure that they are actually the same object rather than just having the
# same value
print(net[2].weight.data[0] == net[4].weight.data[0])
.. parsed-literal::
:class: output
tensor([True, True, True, True, True, True, True, True])
tensor([True, True, True, True, True, True, True, True])
Ví dụ này cho thấy các tham số của lớp thứ hai và thứ ba được gắn. Chúng
không chỉ bằng nhau, chúng được đại diện bởi cùng một tensor chính xác.
Do đó, nếu chúng ta thay đổi một trong các tham số, cái kia cũng thay
đổi. Bạn có thể tự hỏi, khi tham số được gắn những gì sẽ xảy ra với
gradient? Vì các tham số mô hình chứa gradient, các gradient của lớp ẩn
thứ hai và lớp ẩn thứ ba được thêm vào với nhau trong quá trình truyền
ngược.
.. raw:: html
.. raw:: html
.. code:: python
# tf.keras behaves a bit differently. It removes the duplicate layer
# automatically
shared = tf.keras.layers.Dense(4, activation=tf.nn.relu)
net = tf.keras.models.Sequential([
tf.keras.layers.Flatten(),
shared,
shared,
tf.keras.layers.Dense(1),
])
net(X)
# Check whether the parameters are different
print(len(net.layers) == 3)
.. parsed-literal::
:class: output
True
.. raw:: html
.. raw:: html
Tóm tắt
-------
- Chúng tôi có một số cách để truy cập, khởi tạo và buộc các tham số mô
hình.
- Chúng ta có thể sử dụng khởi tạo tùy chỉnh.
Bài tập
-------
1. Sử dụng mô hình ``FancyMLP`` được xác định trong
:numref:`sec_model_construction` và truy cập các tham số của các
lớp khác nhau.
2. Nhìn vào tài liệu mô-đun khởi tạo để khám phá các trình khởi tạo khác
nhau.
3. Xây dựng một MLP chứa một lớp tham số được chia sẻ và đào tạo nó.
Trong quá trình đào tạo, quan sát các thông số mô hình và gradient
của mỗi lớp.
4. Tại sao chia sẻ các thông số là một ý tưởng tốt?
.. raw:: html
.. raw:: html
`Discussions `__
.. raw:: html
.. raw:: html
`Discussions `__
.. raw:: html
.. raw:: html
`Discussions `__
.. raw:: html
.. raw:: html