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
mxnetpytorchtensorflow
.. 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
mxnetpytorchtensorflow
.. 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
mxnetpytorchtensorflow
.. 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
mxnetpytorchtensorflow
.. 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
mxnetpytorchtensorflow
.. 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
mxnetpytorchtensorflow
.. 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
mxnetpytorchtensorflow
.. 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
mxnetpytorchtensorflow
.. 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
mxnetpytorchtensorflow
.. 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
mxnetpytorchtensorflow
.. 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
mxnetpytorchtensorflow
.. 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
mxnetpytorchtensorflow
.. 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
mxnetpytorchtensorflow
.. 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
mxnetpytorchtensorflow
.. 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
mxnetpytorchtensorflow
.. 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
mxnetpytorchtensorflow
.. raw:: html
`Discussions `__ .. raw:: html
.. raw:: html
`Discussions `__ .. raw:: html
.. raw:: html
`Discussions `__ .. raw:: html
.. raw:: html