{ "cells": [ { "cell_type": "markdown", "id": "d5faec71", "metadata": { "origin_pos": 0 }, "source": [ "# Quản lý tham số\n", "\n", "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. \n", "\n", "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: \n", "\n", "* Truy cập các tham số để gỡ lỗi, chẩn đoán và trực quan hóa.\n", "* Khởi tạo tham số.\n", "* Chia sẻ các thông số trên các thành phần mô hình khác nhau.\n", "\n", "(**Chúng tôi bắt đầu bằng cách tập trung vào MLP với một lớp ẩn. **)\n" ] }, { "cell_type": "code", "execution_count": 1, "id": "dd723278", "metadata": { "execution": { "iopub.execute_input": "2022-04-26T12:35:18.450524Z", "iopub.status.busy": "2022-04-26T12:35:18.450273Z", "iopub.status.idle": "2022-04-26T12:35:19.175048Z", "shell.execute_reply": "2022-04-26T12:35:19.174217Z" }, "origin_pos": 2, "tab": [ "pytorch" ] }, "outputs": [ { "data": { "text/plain": [ "tensor([[0.0251],\n", " [0.0025]], grad_fn=)" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import torch\n", "from torch import nn\n", "\n", "net = nn.Sequential(nn.Linear(4, 8), nn.ReLU(), nn.Linear(8, 1))\n", "X = torch.rand(size=(2, 4))\n", "net(X)" ] }, { "cell_type": "markdown", "id": "82b8aa33", "metadata": { "origin_pos": 4 }, "source": [ "## [**Truy cập tham số**]\n", "\n", "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.\n" ] }, { "cell_type": "code", "execution_count": 2, "id": "6b5d7596", "metadata": { "execution": { "iopub.execute_input": "2022-04-26T12:35:19.178447Z", "iopub.status.busy": "2022-04-26T12:35:19.178055Z", "iopub.status.idle": "2022-04-26T12:35:19.183970Z", "shell.execute_reply": "2022-04-26T12:35:19.183150Z" }, "origin_pos": 6, "tab": [ "pytorch" ] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "OrderedDict([('weight', tensor([[-0.1270, 0.2448, 0.3073, -0.3091, 0.0385, -0.2082, 0.2561, -0.1132]])), ('bias', tensor([-0.1036]))])\n" ] } ], "source": [ "print(net[2].state_dict())" ] }, { "cell_type": "markdown", "id": "a6716d2d", "metadata": { "origin_pos": 8 }, "source": [ "Đầ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. \n", "\n", "### [**Tham số được nhắm mục tiêu**]\n", "\n", "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ố đó.\n" ] }, { "cell_type": "code", "execution_count": 3, "id": "889abb28", "metadata": { "execution": { "iopub.execute_input": "2022-04-26T12:35:19.186929Z", "iopub.status.busy": "2022-04-26T12:35:19.186463Z", "iopub.status.idle": "2022-04-26T12:35:19.192260Z", "shell.execute_reply": "2022-04-26T12:35:19.191484Z" }, "origin_pos": 10, "tab": [ "pytorch" ] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "Parameter containing:\n", "tensor([-0.1036], requires_grad=True)\n", "tensor([-0.1036])\n" ] } ], "source": [ "print(type(net[2].bias))\n", "print(net[2].bias)\n", "print(net[2].bias.data)" ] }, { "cell_type": "markdown", "id": "9d47bf80", "metadata": { "origin_pos": 12, "tab": [ "pytorch" ] }, "source": [ "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. \n", "\n", "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ó.\n" ] }, { "cell_type": "code", "execution_count": 4, "id": "1b40ee3f", "metadata": { "execution": { "iopub.execute_input": "2022-04-26T12:35:19.195246Z", "iopub.status.busy": "2022-04-26T12:35:19.194906Z", "iopub.status.idle": "2022-04-26T12:35:19.200202Z", "shell.execute_reply": "2022-04-26T12:35:19.199454Z" }, "origin_pos": 14, "tab": [ "pytorch" ] }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net[2].weight.grad == None" ] }, { "cell_type": "markdown", "id": "ec57773a", "metadata": { "origin_pos": 15 }, "source": [ "### [**Tất cả các thông số tại một lần**]\n", "\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.\n" ] }, { "cell_type": "code", "execution_count": 5, "id": "e8039a2c", "metadata": { "execution": { "iopub.execute_input": "2022-04-26T12:35:19.203051Z", "iopub.status.busy": "2022-04-26T12:35:19.202710Z", "iopub.status.idle": "2022-04-26T12:35:19.207707Z", "shell.execute_reply": "2022-04-26T12:35:19.206892Z" }, "origin_pos": 17, "tab": [ "pytorch" ] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "('weight', torch.Size([8, 4])) ('bias', torch.Size([8]))\n", "('0.weight', torch.Size([8, 4])) ('0.bias', torch.Size([8])) ('2.weight', torch.Size([1, 8])) ('2.bias', torch.Size([1]))\n" ] } ], "source": [ "print(*[(name, param.shape) for name, param in net[0].named_parameters()])\n", "print(*[(name, param.shape) for name, param in net.named_parameters()])" ] }, { "cell_type": "markdown", "id": "e87707aa", "metadata": { "origin_pos": 19 }, "source": [ "Đ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.\n" ] }, { "cell_type": "code", "execution_count": 6, "id": "647b6fd7", "metadata": { "execution": { "iopub.execute_input": "2022-04-26T12:35:19.210912Z", "iopub.status.busy": "2022-04-26T12:35:19.210336Z", "iopub.status.idle": "2022-04-26T12:35:19.216188Z", "shell.execute_reply": "2022-04-26T12:35:19.215418Z" }, "origin_pos": 21, "tab": [ "pytorch" ] }, "outputs": [ { "data": { "text/plain": [ "tensor([-0.1036])" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net.state_dict()['2.bias'].data" ] }, { "cell_type": "markdown", "id": "c746e757", "metadata": { "origin_pos": 23 }, "source": [ "### [**Thu thập các thông số từ các khối lồng nhựa**]\n", "\n", "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.\n" ] }, { "cell_type": "code", "execution_count": 7, "id": "32d339df", "metadata": { "execution": { "iopub.execute_input": "2022-04-26T12:35:19.219192Z", "iopub.status.busy": "2022-04-26T12:35:19.218850Z", "iopub.status.idle": "2022-04-26T12:35:19.229339Z", "shell.execute_reply": "2022-04-26T12:35:19.228545Z" }, "origin_pos": 25, "tab": [ "pytorch" ] }, "outputs": [ { "data": { "text/plain": [ "tensor([[-0.5720],\n", " [-0.5720]], grad_fn=)" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def block1():\n", " return nn.Sequential(nn.Linear(4, 8), nn.ReLU(),\n", " nn.Linear(8, 4), nn.ReLU())\n", "\n", "def block2():\n", " net = nn.Sequential()\n", " for i in range(4):\n", " # Nested here\n", " net.add_module(f'block {i}', block1())\n", " return net\n", "\n", "rgnet = nn.Sequential(block2(), nn.Linear(4, 1))\n", "rgnet(X)" ] }, { "cell_type": "markdown", "id": "a5d244ab", "metadata": { "origin_pos": 27 }, "source": [ "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.**]\n" ] }, { "cell_type": "code", "execution_count": 8, "id": "2bb14218", "metadata": { "execution": { "iopub.execute_input": "2022-04-26T12:35:19.232333Z", "iopub.status.busy": "2022-04-26T12:35:19.231990Z", "iopub.status.idle": "2022-04-26T12:35:19.236361Z", "shell.execute_reply": "2022-04-26T12:35:19.235679Z" }, "origin_pos": 29, "tab": [ "pytorch" ] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Sequential(\n", " (0): Sequential(\n", " (block 0): Sequential(\n", " (0): Linear(in_features=4, out_features=8, bias=True)\n", " (1): ReLU()\n", " (2): Linear(in_features=8, out_features=4, bias=True)\n", " (3): ReLU()\n", " )\n", " (block 1): Sequential(\n", " (0): Linear(in_features=4, out_features=8, bias=True)\n", " (1): ReLU()\n", " (2): Linear(in_features=8, out_features=4, bias=True)\n", " (3): ReLU()\n", " )\n", " (block 2): Sequential(\n", " (0): Linear(in_features=4, out_features=8, bias=True)\n", " (1): ReLU()\n", " (2): Linear(in_features=8, out_features=4, bias=True)\n", " (3): ReLU()\n", " )\n", " (block 3): Sequential(\n", " (0): Linear(in_features=4, out_features=8, bias=True)\n", " (1): ReLU()\n", " (2): Linear(in_features=8, out_features=4, bias=True)\n", " (3): ReLU()\n", " )\n", " )\n", " (1): Linear(in_features=4, out_features=1, bias=True)\n", ")\n" ] } ], "source": [ "print(rgnet)" ] }, { "cell_type": "markdown", "id": "c2c71199", "metadata": { "origin_pos": 31 }, "source": [ "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.\n" ] }, { "cell_type": "code", "execution_count": 9, "id": "f27557be", "metadata": { "execution": { "iopub.execute_input": "2022-04-26T12:35:19.239735Z", "iopub.status.busy": "2022-04-26T12:35:19.239167Z", "iopub.status.idle": "2022-04-26T12:35:19.245341Z", "shell.execute_reply": "2022-04-26T12:35:19.244628Z" }, "origin_pos": 33, "tab": [ "pytorch" ] }, "outputs": [ { "data": { "text/plain": [ "tensor([-0.0210, 0.2743, 0.0433, -0.1879, -0.3919, -0.1085, 0.0300, -0.4445])" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rgnet[0][1][0].bias.data" ] }, { "cell_type": "markdown", "id": "8c9a51da", "metadata": { "origin_pos": 35 }, "source": [ "## Khởi tạo tham số\n", "\n", "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.\n" ] }, { "cell_type": "markdown", "id": "12e7d32d", "metadata": { "origin_pos": 37, "tab": [ "pytorch" ] }, "source": [ "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.\n" ] }, { "cell_type": "markdown", "id": "063c0ccf", "metadata": { "origin_pos": 39 }, "source": [ "### [** Khởi tạo tích hợp**]\n", "\n", "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.\n" ] }, { "cell_type": "code", "execution_count": 10, "id": "32a380e1", "metadata": { "execution": { "iopub.execute_input": "2022-04-26T12:35:19.248764Z", "iopub.status.busy": "2022-04-26T12:35:19.248291Z", "iopub.status.idle": "2022-04-26T12:35:19.255424Z", "shell.execute_reply": "2022-04-26T12:35:19.254766Z" }, "origin_pos": 41, "tab": [ "pytorch" ] }, "outputs": [ { "data": { "text/plain": [ "(tensor([-0.0152, -0.0023, -0.0003, 0.0055]), tensor(0.))" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def init_normal(m):\n", " if type(m) == nn.Linear:\n", " nn.init.normal_(m.weight, mean=0, std=0.01)\n", " nn.init.zeros_(m.bias)\n", "net.apply(init_normal)\n", "net[0].weight.data[0], net[0].bias.data[0]" ] }, { "cell_type": "markdown", "id": "dca1a5ef", "metadata": { "origin_pos": 43 }, "source": [ "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).\n" ] }, { "cell_type": "code", "execution_count": 11, "id": "21bdca7e", "metadata": { "execution": { "iopub.execute_input": "2022-04-26T12:35:19.258414Z", "iopub.status.busy": "2022-04-26T12:35:19.257965Z", "iopub.status.idle": "2022-04-26T12:35:19.264590Z", "shell.execute_reply": "2022-04-26T12:35:19.263956Z" }, "origin_pos": 45, "tab": [ "pytorch" ] }, "outputs": [ { "data": { "text/plain": [ "(tensor([1., 1., 1., 1.]), tensor(0.))" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def init_constant(m):\n", " if type(m) == nn.Linear:\n", " nn.init.constant_(m.weight, 1)\n", " nn.init.zeros_(m.bias)\n", "net.apply(init_constant)\n", "net[0].weight.data[0], net[0].bias.data[0]" ] }, { "cell_type": "markdown", "id": "4b527c69", "metadata": { "origin_pos": 47 }, "source": [ "[**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.\n" ] }, { "cell_type": "code", "execution_count": 12, "id": "1840b55f", "metadata": { "execution": { "iopub.execute_input": "2022-04-26T12:35:19.267628Z", "iopub.status.busy": "2022-04-26T12:35:19.267173Z", "iopub.status.idle": "2022-04-26T12:35:19.273609Z", "shell.execute_reply": "2022-04-26T12:35:19.272923Z" }, "origin_pos": 49, "tab": [ "pytorch" ] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tensor([ 0.1975, 0.2599, -0.1798, 0.4012])\n", "tensor([[42., 42., 42., 42., 42., 42., 42., 42.]])\n" ] } ], "source": [ "def xavier(m):\n", " if type(m) == nn.Linear:\n", " nn.init.xavier_uniform_(m.weight)\n", "def init_42(m):\n", " if type(m) == nn.Linear:\n", " nn.init.constant_(m.weight, 42)\n", "\n", "net[0].apply(xavier)\n", "net[2].apply(init_42)\n", "print(net[0].weight.data[0])\n", "print(net[2].weight.data)" ] }, { "cell_type": "markdown", "id": "088ef198", "metadata": { "origin_pos": 51 }, "source": [ "### [** Initializationtùy chỉnh**]\n", "\n", "Đô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 $w$ bằng cách sử dụng phân phối lạ sau: \n", "\n", "$$\n", "\\begin{aligned}\n", " w \\sim \\begin{cases}\n", " U(5, 10) & \\text{ with probability } \\frac{1}{4} \\\\\n", " 0 & \\text{ with probability } \\frac{1}{2} \\\\\n", " U(-10, -5) & \\text{ with probability } \\frac{1}{4}\n", " \\end{cases}\n", "\\end{aligned}\n", "$$\n" ] }, { "cell_type": "markdown", "id": "6edfde54", "metadata": { "origin_pos": 53, "tab": [ "pytorch" ] }, "source": [ "Một lần nữa, chúng tôi triển khai một hàm `my_init` để áp dụng cho `net`.\n" ] }, { "cell_type": "code", "execution_count": 13, "id": "4778225b", "metadata": { "execution": { "iopub.execute_input": "2022-04-26T12:35:19.276753Z", "iopub.status.busy": "2022-04-26T12:35:19.276276Z", "iopub.status.idle": "2022-04-26T12:35:19.284029Z", "shell.execute_reply": "2022-04-26T12:35:19.283352Z" }, "origin_pos": 56, "tab": [ "pytorch" ] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Init weight torch.Size([8, 4])\n", "Init weight torch.Size([1, 8])\n" ] }, { "data": { "text/plain": [ "tensor([[-9.0877, -7.0061, 0.0000, 0.0000],\n", " [ 0.0000, 0.0000, 5.4938, -0.0000]], grad_fn=)" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def my_init(m):\n", " if type(m) == nn.Linear:\n", " print(\"Init\", *[(name, param.shape)\n", " for name, param in m.named_parameters()][0])\n", " nn.init.uniform_(m.weight, -10, 10)\n", " m.weight.data *= m.weight.data.abs() >= 5\n", "\n", "net.apply(my_init)\n", "net[0].weight[:2]" ] }, { "cell_type": "markdown", "id": "90a4c6e1", "metadata": { "origin_pos": 58 }, "source": [ "Lưu ý rằng chúng tôi luôn có tùy chọn đặt tham số trực tiếp.\n" ] }, { "cell_type": "code", "execution_count": 14, "id": "a6be1133", "metadata": { "execution": { "iopub.execute_input": "2022-04-26T12:35:19.287030Z", "iopub.status.busy": "2022-04-26T12:35:19.286516Z", "iopub.status.idle": "2022-04-26T12:35:19.292349Z", "shell.execute_reply": "2022-04-26T12:35:19.291708Z" }, "origin_pos": 60, "tab": [ "pytorch" ] }, "outputs": [ { "data": { "text/plain": [ "tensor([42.0000, -6.0061, 1.0000, 1.0000])" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net[0].weight.data[:] += 1\n", "net[0].weight.data[0, 0] = 42\n", "net[0].weight.data[0]" ] }, { "cell_type": "markdown", "id": "feb3dbd4", "metadata": { "origin_pos": 63 }, "source": [ "## [**Tham số Tied**]\n", "\n", "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.\n" ] }, { "cell_type": "code", "execution_count": 15, "id": "4da9ccc1", "metadata": { "execution": { "iopub.execute_input": "2022-04-26T12:35:19.295367Z", "iopub.status.busy": "2022-04-26T12:35:19.294898Z", "iopub.status.idle": "2022-04-26T12:35:19.303559Z", "shell.execute_reply": "2022-04-26T12:35:19.302888Z" }, "origin_pos": 65, "tab": [ "pytorch" ] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tensor([True, True, True, True, True, True, True, True])\n", "tensor([True, True, True, True, True, True, True, True])\n" ] } ], "source": [ "# We need to give the shared layer a name so that we can refer to its\n", "# parameters\n", "shared = nn.Linear(8, 8)\n", "net = nn.Sequential(nn.Linear(4, 8), nn.ReLU(),\n", " shared, nn.ReLU(),\n", " shared, nn.ReLU(),\n", " nn.Linear(8, 1))\n", "net(X)\n", "# Check whether the parameters are the same\n", "print(net[2].weight.data[0] == net[4].weight.data[0])\n", "net[2].weight.data[0, 0] = 100\n", "# Make sure that they are actually the same object rather than just having the\n", "# same value\n", "print(net[2].weight.data[0] == net[4].weight.data[0])" ] }, { "cell_type": "markdown", "id": "d11eebe0", "metadata": { "origin_pos": 67, "tab": [ "pytorch" ] }, "source": [ "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.\n" ] }, { "cell_type": "markdown", "id": "2b909980", "metadata": { "origin_pos": 68 }, "source": [ "## Tóm tắt\n", "\n", "* 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.\n", "* Chúng ta có thể sử dụng khởi tạo tùy chỉnh.\n", "\n", "## Bài tập\n", "\n", "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.\n", "1. 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.\n", "1. 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.\n", "1. Tại sao chia sẻ các thông số là một ý tưởng tốt?\n" ] }, { "cell_type": "markdown", "id": "5170ceea", "metadata": { "origin_pos": 70, "tab": [ "pytorch" ] }, "source": [ "[Discussions](https://discuss.d2l.ai/t/57)\n" ] } ], "metadata": { "language_info": { "name": "python" } }, "nbformat": 4, "nbformat_minor": 5 }