{ "cells": [ { "cell_type": "markdown", "id": "fbe5ed8e", "metadata": { "origin_pos": 1 }, "source": [ "# Lazy Initialization\n", ":label:`sec_lazy_init`\n", "\n", "So far, it might seem that we got away\n", "with being sloppy in setting up our networks.\n", "Specifically, we did the following unintuitive things,\n", "which might not seem like they should work:\n", "\n", "* We defined the network architectures\n", " without specifying the input dimensionality.\n", "* We added layers without specifying\n", " the output dimension of the previous layer.\n", "* We even \"initialized\" these parameters\n", " before providing enough information to determine\n", " how many parameters our models should contain.\n", "\n", "You might be surprised that our code runs at all.\n", "After all, there is no way the deep learning framework\n", "could tell what the input dimensionality of a network would be.\n", "The trick here is that the framework *defers initialization*,\n", "waiting until the first time we pass data through the model,\n", "to infer the sizes of each layer on the fly.\n", "\n", "\n", "Later on, when working with convolutional neural networks,\n", "this technique will become even more convenient\n", "since the input dimensionality\n", "(e.g., the resolution of an image)\n", "will affect the dimensionality\n", "of each subsequent layer.\n", "Hence the ability to set parameters\n", "without the need to know,\n", "at the time of writing the code,\n", "the value of the dimension\n", "can greatly simplify the task of specifying\n", "and subsequently modifying our models.\n", "Next, we go deeper into the mechanics of initialization.\n" ] }, { "cell_type": "code", "execution_count": 1, "id": "6361eab8", "metadata": { "execution": { "iopub.execute_input": "2023-08-18T19:28:51.570624Z", "iopub.status.busy": "2023-08-18T19:28:51.570355Z", "iopub.status.idle": "2023-08-18T19:28:54.353918Z", "shell.execute_reply": "2023-08-18T19:28:54.352659Z" }, "origin_pos": 3, "tab": [ "pytorch" ] }, "outputs": [], "source": [ "import torch\n", "from torch import nn\n", "from d2l import torch as d2l" ] }, { "cell_type": "markdown", "id": "4b9bf2d8", "metadata": { "origin_pos": 6 }, "source": [ "To begin, let's instantiate an MLP.\n" ] }, { "cell_type": "code", "execution_count": 2, "id": "9f40f460", "metadata": { "execution": { "iopub.execute_input": "2023-08-18T19:28:54.358332Z", "iopub.status.busy": "2023-08-18T19:28:54.357625Z", "iopub.status.idle": "2023-08-18T19:28:54.363046Z", "shell.execute_reply": "2023-08-18T19:28:54.362240Z" }, "origin_pos": 8, "tab": [ "pytorch" ] }, "outputs": [], "source": [ "net = nn.Sequential(nn.LazyLinear(256), nn.ReLU(), nn.LazyLinear(10))" ] }, { "cell_type": "markdown", "id": "77114bd9", "metadata": { "origin_pos": 11 }, "source": [ "At this point, the network cannot possibly know\n", "the dimensions of the input layer's weights\n", "because the input dimension remains unknown.\n" ] }, { "cell_type": "markdown", "id": "203aec57", "metadata": { "origin_pos": 12, "tab": [ "pytorch" ] }, "source": [ "Consequently the framework has not yet initialized any parameters.\n", "We confirm by attempting to access the parameters below.\n" ] }, { "cell_type": "code", "execution_count": 3, "id": "4737af8b", "metadata": { "execution": { "iopub.execute_input": "2023-08-18T19:28:54.366909Z", "iopub.status.busy": "2023-08-18T19:28:54.366377Z", "iopub.status.idle": "2023-08-18T19:28:54.373758Z", "shell.execute_reply": "2023-08-18T19:28:54.372581Z" }, "origin_pos": 15, "tab": [ "pytorch" ] }, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net[0].weight" ] }, { "cell_type": "markdown", "id": "168c0bea", "metadata": { "origin_pos": 21 }, "source": [ "Next let's pass data through the network\n", "to make the framework finally initialize parameters.\n" ] }, { "cell_type": "code", "execution_count": 4, "id": "e9b0be3f", "metadata": { "execution": { "iopub.execute_input": "2023-08-18T19:28:54.377386Z", "iopub.status.busy": "2023-08-18T19:28:54.376838Z", "iopub.status.idle": "2023-08-18T19:28:54.404155Z", "shell.execute_reply": "2023-08-18T19:28:54.403267Z" }, "origin_pos": 23, "tab": [ "pytorch" ] }, "outputs": [ { "data": { "text/plain": [ "torch.Size([256, 20])" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "X = torch.rand(2, 20)\n", "net(X)\n", "\n", "net[0].weight.shape" ] }, { "cell_type": "markdown", "id": "5d13a508", "metadata": { "origin_pos": 26 }, "source": [ "As soon as we know the input dimensionality,\n", "20,\n", "the framework can identify the shape of the first layer's weight matrix by plugging in the value of 20.\n", "Having recognized the first layer's shape, the framework proceeds\n", "to the second layer,\n", "and so on through the computational graph\n", "until all shapes are known.\n", "Note that in this case,\n", "only the first layer requires lazy initialization,\n", "but the framework initializes sequentially.\n", "Once all parameter shapes are known,\n", "the framework can finally initialize the parameters.\n" ] }, { "cell_type": "markdown", "id": "65243de2", "metadata": { "origin_pos": 27, "tab": [ "pytorch" ] }, "source": [ "The following method\n", "passes in dummy inputs\n", "through the network\n", "for a dry run\n", "to infer all parameter shapes\n", "and subsequently initializes the parameters.\n", "It will be used later when default random initializations are not desired.\n" ] }, { "cell_type": "code", "execution_count": 5, "id": "7fb03640", "metadata": { "execution": { "iopub.execute_input": "2023-08-18T19:28:54.407725Z", "iopub.status.busy": "2023-08-18T19:28:54.407182Z", "iopub.status.idle": "2023-08-18T19:28:54.411940Z", "shell.execute_reply": "2023-08-18T19:28:54.411063Z" }, "origin_pos": 29, "tab": [ "pytorch" ] }, "outputs": [], "source": [ "@d2l.add_to_class(d2l.Module) #@save\n", "def apply_init(self, inputs, init=None):\n", " self.forward(*inputs)\n", " if init is not None:\n", " self.net.apply(init)" ] }, { "cell_type": "markdown", "id": "1465b435", "metadata": { "origin_pos": 31 }, "source": [ "## Summary\n", "\n", "Lazy initialization can be convenient, allowing the framework to infer parameter shapes automatically, making it easy to modify architectures and eliminating one common source of errors.\n", "We can pass data through the model to make the framework finally initialize parameters.\n", "\n", "\n", "## Exercises\n", "\n", "1. What happens if you specify the input dimensions to the first layer but not to subsequent layers? Do you get immediate initialization?\n", "1. What happens if you specify mismatching dimensions?\n", "1. What would you need to do if you have input of varying dimensionality? Hint: look at the parameter tying.\n" ] }, { "cell_type": "markdown", "id": "a17bbb9b", "metadata": { "origin_pos": 33, "tab": [ "pytorch" ] }, "source": [ "[Discussions](https://discuss.d2l.ai/t/8092)\n" ] } ], "metadata": { "language_info": { "name": "python" }, "required_libs": [] }, "nbformat": 4, "nbformat_minor": 5 }