{ "cells": [ { "cell_type": "markdown", "id": "0a56c534", "metadata": { "origin_pos": 0 }, "source": [ "# Layers tùy chỉnh\n", "\n", "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. \n", "\n", "## (** Các lớp không có tham số**)\n", "\n", "Để 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.\n" ] }, { "cell_type": "code", "execution_count": 1, "id": "bcc7fdbb", "metadata": { "execution": { "iopub.execute_input": "2022-04-26T12:18:38.212754Z", "iopub.status.busy": "2022-04-26T12:18:38.212070Z", "iopub.status.idle": "2022-04-26T12:18:39.001334Z", "shell.execute_reply": "2022-04-26T12:18:39.000620Z" }, "origin_pos": 2, "tab": [ "pytorch" ] }, "outputs": [], "source": [ "import torch\n", "from torch import nn\n", "from torch.nn import functional as F\n", "\n", "\n", "class CenteredLayer(nn.Module):\n", " def __init__(self):\n", " super().__init__()\n", "\n", " def forward(self, X):\n", " return X - X.mean()" ] }, { "cell_type": "markdown", "id": "615beeea", "metadata": { "origin_pos": 4 }, "source": [ "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ó.\n" ] }, { "cell_type": "code", "execution_count": 2, "id": "505aef66", "metadata": { "execution": { "iopub.execute_input": "2022-04-26T12:18:39.006547Z", "iopub.status.busy": "2022-04-26T12:18:39.005978Z", "iopub.status.idle": "2022-04-26T12:18:39.017300Z", "shell.execute_reply": "2022-04-26T12:18:39.016714Z" }, "origin_pos": 6, "tab": [ "pytorch" ] }, "outputs": [ { "data": { "text/plain": [ "tensor([-2., -1., 0., 1., 2.])" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "layer = CenteredLayer()\n", "layer(torch.FloatTensor([1, 2, 3, 4, 5]))" ] }, { "cell_type": "markdown", "id": "55c97328", "metadata": { "origin_pos": 8 }, "source": [ "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.**]\n" ] }, { "cell_type": "code", "execution_count": 3, "id": "ed885d0f", "metadata": { "execution": { "iopub.execute_input": "2022-04-26T12:18:39.022434Z", "iopub.status.busy": "2022-04-26T12:18:39.022068Z", "iopub.status.idle": "2022-04-26T12:18:39.026172Z", "shell.execute_reply": "2022-04-26T12:18:39.025566Z" }, "origin_pos": 10, "tab": [ "pytorch" ] }, "outputs": [], "source": [ "net = nn.Sequential(nn.Linear(8, 128), CenteredLayer())" ] }, { "cell_type": "markdown", "id": "aeeb0983", "metadata": { "origin_pos": 12 }, "source": [ "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.\n" ] }, { "cell_type": "code", "execution_count": 4, "id": "03789323", "metadata": { "execution": { "iopub.execute_input": "2022-04-26T12:18:39.030483Z", "iopub.status.busy": "2022-04-26T12:18:39.030091Z", "iopub.status.idle": "2022-04-26T12:18:39.035969Z", "shell.execute_reply": "2022-04-26T12:18:39.035283Z" }, "origin_pos": 14, "tab": [ "pytorch" ] }, "outputs": [ { "data": { "text/plain": [ "tensor(-4.6566e-09, grad_fn=)" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Y = net(torch.rand(4, 8))\n", "Y.mean()" ] }, { "cell_type": "markdown", "id": "5ff95e2b", "metadata": { "origin_pos": 16 }, "source": [ "## [**Các lớp có tham số**]\n", "\n", "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. \n", "\n", "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.\n" ] }, { "cell_type": "code", "execution_count": 5, "id": "5d9613ae", "metadata": { "execution": { "iopub.execute_input": "2022-04-26T12:18:39.041329Z", "iopub.status.busy": "2022-04-26T12:18:39.040515Z", "iopub.status.idle": "2022-04-26T12:18:39.046777Z", "shell.execute_reply": "2022-04-26T12:18:39.045920Z" }, "origin_pos": 18, "tab": [ "pytorch" ] }, "outputs": [], "source": [ "class MyLinear(nn.Module):\n", " def __init__(self, in_units, units):\n", " super().__init__()\n", " self.weight = nn.Parameter(torch.randn(in_units, units))\n", " self.bias = nn.Parameter(torch.randn(units,))\n", " def forward(self, X):\n", " linear = torch.matmul(X, self.weight.data) + self.bias.data\n", " return F.relu(linear)" ] }, { "cell_type": "markdown", "id": "d8789b6c", "metadata": { "origin_pos": 21, "tab": [ "pytorch" ] }, "source": [ "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ó.\n" ] }, { "cell_type": "code", "execution_count": 6, "id": "2a1c6a66", "metadata": { "execution": { "iopub.execute_input": "2022-04-26T12:18:39.051138Z", "iopub.status.busy": "2022-04-26T12:18:39.050594Z", "iopub.status.idle": "2022-04-26T12:18:39.056286Z", "shell.execute_reply": "2022-04-26T12:18:39.055660Z" }, "origin_pos": 23, "tab": [ "pytorch" ] }, "outputs": [ { "data": { "text/plain": [ "Parameter containing:\n", "tensor([[-0.5447, -0.4223, -0.7320],\n", " [-0.3532, 1.4021, 0.3656],\n", " [ 0.4039, 0.0149, -1.7429],\n", " [-0.9663, -2.5671, -1.0107],\n", " [ 0.4001, -0.4510, 0.6005]], requires_grad=True)" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "linear = MyLinear(5, 3)\n", "linear.weight" ] }, { "cell_type": "markdown", "id": "f1684dc7", "metadata": { "origin_pos": 25 }, "source": [ "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**]\n" ] }, { "cell_type": "code", "execution_count": 7, "id": "a0ce72ff", "metadata": { "execution": { "iopub.execute_input": "2022-04-26T12:18:39.060481Z", "iopub.status.busy": "2022-04-26T12:18:39.060016Z", "iopub.status.idle": "2022-04-26T12:18:39.065443Z", "shell.execute_reply": "2022-04-26T12:18:39.064854Z" }, "origin_pos": 27, "tab": [ "pytorch" ] }, "outputs": [ { "data": { "text/plain": [ "tensor([[1.8603, 0.0000, 0.0000],\n", " [1.9760, 0.0000, 0.0000]])" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "linear(torch.rand(2, 5))" ] }, { "cell_type": "markdown", "id": "bbdf2dbb", "metadata": { "origin_pos": 29 }, "source": [ "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.\n" ] }, { "cell_type": "code", "execution_count": 8, "id": "e1c09e18", "metadata": { "execution": { "iopub.execute_input": "2022-04-26T12:18:39.069297Z", "iopub.status.busy": "2022-04-26T12:18:39.069095Z", "iopub.status.idle": "2022-04-26T12:18:39.075174Z", "shell.execute_reply": "2022-04-26T12:18:39.074382Z" }, "origin_pos": 31, "tab": [ "pytorch" ] }, "outputs": [ { "data": { "text/plain": [ "tensor([[0.],\n", " [0.]])" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net = nn.Sequential(MyLinear(64, 8), MyLinear(8, 1))\n", "net(torch.rand(2, 64))" ] }, { "cell_type": "markdown", "id": "e27d312b", "metadata": { "origin_pos": 33 }, "source": [ "## Tóm tắt\n", "\n", "* 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.\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 ý.\n", "* 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.\n", "\n", "## Bài tập\n", "\n", "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ề $y_k = \\sum_{i, j} W_{ijk} x_i x_j$.\n", "1. 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.\n" ] }, { "cell_type": "markdown", "id": "62d0e55c", "metadata": { "origin_pos": 35, "tab": [ "pytorch" ] }, "source": [ "[Discussions](https://discuss.d2l.ai/t/59)\n" ] } ], "metadata": { "language_info": { "name": "python" } }, "nbformat": 4, "nbformat_minor": 5 }