{
"cells": [
{
"cell_type": "markdown",
"id": "f2160c1d-37bb-482b-a270-daa13c612f7c",
"metadata": {},
"source": [
"# 卷积神经网络\n",
"\n",
"目录\n",
"\n",
"- 图像卷积\n",
"- 填充和步幅\n",
"- 多输入多输出通道\n",
"- 池化层\n",
"- 卷积神经网络(LeNet)"
]
},
{
"cell_type": "markdown",
"id": "6846ad05-7209-4c31-9378-b9b532a3667e",
"metadata": {
"jp-MarkdownHeadingCollapsed": true,
"slideshow": {
"slide_type": "-"
},
"tags": []
},
"source": [
"## 图像卷积\n",
"\n",
"互相关运算"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "ac9478e0-6433-4cf7-917f-600f08edc110",
"metadata": {
"origin_pos": 3,
"tab": [
"pytorch"
]
},
"outputs": [],
"source": [
"import torch\n",
"from torch import nn\n",
"from d2l import torch as d2l\n",
"\n",
"def corr2d(X, K): \n",
" \"\"\"计算二维互相关运算\"\"\"\n",
" h, w = K.shape\n",
" Y = torch.zeros((X.shape[0] - h + 1, X.shape[1] - w + 1))\n",
" for i in range(Y.shape[0]):\n",
" for j in range(Y.shape[1]):\n",
" Y[i, j] = (X[i:i + h, j:j + w] * K).sum()\n",
" return Y"
]
},
{
"cell_type": "markdown",
"id": "314a155d-9475-4a87-950d-c14bbcad6d44",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"验证上述二维互相关运算的输出"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "3f8076da-f6b1-4b35-ba49-c31cb1314304",
"metadata": {
"origin_pos": 6,
"tab": [
"pytorch"
]
},
"outputs": [
{
"data": {
"text/plain": [
"tensor([[19., 25.],\n",
" [37., 43.]])"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"X = torch.tensor([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]])\n",
"K = torch.tensor([[0.0, 1.0], [2.0, 3.0]])\n",
"corr2d(X, K)"
]
},
{
"cell_type": "markdown",
"id": "aab13ec8-679c-41b7-a686-98527d50a6f7",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"实现二维卷积层"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "8c2795ba-6b7e-4d65-a718-7834d515eebf",
"metadata": {
"origin_pos": 9,
"tab": [
"pytorch"
]
},
"outputs": [],
"source": [
"class Conv2D(nn.Module):\n",
" def __init__(self, kernel_size):\n",
" super().__init__()\n",
" self.weight = nn.Parameter(torch.rand(kernel_size))\n",
" self.bias = nn.Parameter(torch.zeros(1))\n",
"\n",
" def forward(self, x):\n",
" return corr2d(x, self.weight) + self.bias"
]
},
{
"cell_type": "markdown",
"id": "9a4b28c4-b74d-47f0-a549-2b2d7d350e55",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"卷积层的一个简单应用:\n",
"检测图像中不同颜色的边缘"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "2763c05e-3af6-48ab-8660-e7ffff81bbf5",
"metadata": {
"origin_pos": 12,
"tab": [
"pytorch"
]
},
"outputs": [
{
"data": {
"text/plain": [
"tensor([[1., 1., 0., 0., 0., 0., 1., 1.],\n",
" [1., 1., 0., 0., 0., 0., 1., 1.],\n",
" [1., 1., 0., 0., 0., 0., 1., 1.],\n",
" [1., 1., 0., 0., 0., 0., 1., 1.],\n",
" [1., 1., 0., 0., 0., 0., 1., 1.],\n",
" [1., 1., 0., 0., 0., 0., 1., 1.]])"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"X = torch.ones((6, 8))\n",
"X[:, 2:6] = 0\n",
"X"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "c3d67537-eb49-4136-b5bd-51cc13eaafb2",
"metadata": {
"origin_pos": 15,
"tab": [
"pytorch"
]
},
"outputs": [],
"source": [
"K = torch.tensor([[1.0, -1.0]])"
]
},
{
"cell_type": "markdown",
"id": "965ca956-b67c-451f-9dd4-8ab725165f1a",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"输出`Y`中的1代表从白色到黑色的边缘,-1代表从黑色到白色的边缘"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "5cf508d4-c35a-428d-9bb1-96fc32ae5864",
"metadata": {
"origin_pos": 17,
"tab": [
"pytorch"
]
},
"outputs": [
{
"data": {
"text/plain": [
"tensor([[ 0., 1., 0., 0., 0., -1., 0.],\n",
" [ 0., 1., 0., 0., 0., -1., 0.],\n",
" [ 0., 1., 0., 0., 0., -1., 0.],\n",
" [ 0., 1., 0., 0., 0., -1., 0.],\n",
" [ 0., 1., 0., 0., 0., -1., 0.],\n",
" [ 0., 1., 0., 0., 0., -1., 0.]])"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Y = corr2d(X, K)\n",
"Y"
]
},
{
"cell_type": "markdown",
"id": "7bc03775-24f6-4d2a-b2aa-e870717de626",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"卷积核`K`只可以检测垂直边缘"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "39d014be-40db-4d15-8caf-591a2710845c",
"metadata": {
"origin_pos": 19,
"tab": [
"pytorch"
]
},
"outputs": [
{
"data": {
"text/plain": [
"tensor([[0., 0., 0., 0., 0.],\n",
" [0., 0., 0., 0., 0.],\n",
" [0., 0., 0., 0., 0.],\n",
" [0., 0., 0., 0., 0.],\n",
" [0., 0., 0., 0., 0.],\n",
" [0., 0., 0., 0., 0.],\n",
" [0., 0., 0., 0., 0.],\n",
" [0., 0., 0., 0., 0.]])"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"corr2d(X.t(), K)"
]
},
{
"cell_type": "markdown",
"id": "87ad3bb5-f1b5-4788-9db8-2c1c27cffbfa",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"学习由`X`生成`Y`的卷积核"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "dd75f3b8-6a8e-4956-8f9c-f39ac8ceb791",
"metadata": {
"origin_pos": 22,
"tab": [
"pytorch"
]
},
"outputs": [],
"source": [
"# 构造一个二维卷积层,它具有1个输出通道和形状为(1,2)的卷积核\n",
"conv2d = nn.Conv2d(1,1, kernel_size=(1, 2), bias=False)\n",
"\n",
"# 这个二维卷积层使用四维输入和输出格式(批量大小、通道、高度、宽度),\n",
"# 其中批量大小和通道数都为1\n",
"X = X.reshape((1, 1, 6, 8))\n",
"Y = Y.reshape((1, 1, 6, 7))\n",
"lr = 3e-2"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "0d28ad4a-bf04-4352-b0c4-c5d5ec11646a",
"metadata": {
"origin_pos": 22,
"slideshow": {
"slide_type": "slide"
},
"tab": [
"pytorch"
]
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"epoch 2, loss 8.836\n",
"epoch 4, loss 1.692\n",
"epoch 6, loss 0.370\n",
"epoch 8, loss 0.097\n",
"epoch 10, loss 0.031\n"
]
}
],
"source": [
"for i in range(10):\n",
" Y_hat = conv2d(X)\n",
" l = (Y_hat - Y) ** 2\n",
" conv2d.zero_grad()\n",
" l.sum().backward()\n",
" # 迭代更新卷积核\n",
" conv2d.weight.data[:] -= lr * conv2d.weight.grad\n",
" if (i + 1) % 2 == 0:\n",
" print(f'epoch {i+1}, loss {l.sum():.3f}')"
]
},
{
"cell_type": "markdown",
"id": "eaeb6855-12e3-40b0-acc8-71cadbf9f55e",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"所学的卷积核的权重张量"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "b473f6f7-6fca-4aa6-8c77-87c57b7f9b5b",
"metadata": {
"origin_pos": 26,
"tab": [
"pytorch"
]
},
"outputs": [
{
"data": {
"text/plain": [
"tensor([[ 1.0015, -0.9692]])"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"conv2d.weight.data.reshape((1, 2))"
]
},
{
"cell_type": "markdown",
"id": "5879af59-0af7-4128-a750-84d5ce60f25d",
"metadata": {
"jp-MarkdownHeadingCollapsed": true,
"slideshow": {
"slide_type": "-"
},
"tags": []
},
"source": [
"## 填充和步幅\n",
"\n",
"在所有侧边填充1个像素"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "979a995a-96f9-4d41-8442-2d32af9074bf",
"metadata": {
"origin_pos": 2,
"tab": [
"pytorch"
]
},
"outputs": [],
"source": [
"import torch\n",
"from torch import nn\n",
"\n",
"\n",
"def comp_conv2d(conv2d, X):\n",
" \"\"\"初始化卷积层权重,并对输入和输出提高和缩减相应的维数\"\"\"\n",
" X = X.reshape((1, 1) + X.shape) # 批量大小和通道数都是1\n",
" Y = conv2d(X)\n",
" return Y.reshape(Y.shape[2:]) # 省略前两个维度:批量大小和通道"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "f9f0ed64-7d08-4788-bf0b-932b9ec4803b",
"metadata": {
"origin_pos": 2,
"slideshow": {
"slide_type": "slide"
},
"tab": [
"pytorch"
]
},
"outputs": [
{
"data": {
"text/plain": [
"torch.Size([8, 8])"
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# 注意,每边都填充了1行或1列,因此总共添加了2行或2列\n",
"conv2d = nn.Conv2d(1, 1, kernel_size=3, padding=1)\n",
"X = torch.rand(size=(8, 8))\n",
"comp_conv2d(conv2d, X).shape"
]
},
{
"cell_type": "markdown",
"id": "cf74250e-eb1b-47df-9141-f931fafc3af0",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"填充不同的高度和宽度"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "545bfb03-c86e-4f29-9f2f-940c7ec49aaf",
"metadata": {
"origin_pos": 6,
"tab": [
"pytorch"
]
},
"outputs": [
{
"data": {
"text/plain": [
"torch.Size([8, 8])"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"conv2d = nn.Conv2d(1, 1, kernel_size=(5, 3), padding=(2, 1))\n",
"comp_conv2d(conv2d, X).shape"
]
},
{
"cell_type": "markdown",
"id": "6a8ce394-d251-438d-a6ad-0ae688a17a91",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"将高度和宽度的步幅设置为2"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "15d25626-14d6-49c2-a7d2-f5b32d4ae621",
"metadata": {
"origin_pos": 10,
"tab": [
"pytorch"
]
},
"outputs": [
{
"data": {
"text/plain": [
"torch.Size([4, 4])"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"conv2d = nn.Conv2d(1, 1, kernel_size=3, padding=1, stride=2)\n",
"comp_conv2d(conv2d, X).shape"
]
},
{
"cell_type": "markdown",
"id": "64f13f70-7ba6-4c68-adc4-951c77fb7b6c",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"一个稍微复杂的例子"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "7aae79d5-a75d-4769-bac5-52c0dd6c2cd4",
"metadata": {
"origin_pos": 14,
"tab": [
"pytorch"
]
},
"outputs": [
{
"data": {
"text/plain": [
"torch.Size([2, 2])"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"conv2d = nn.Conv2d(1, 1, kernel_size=(3, 5), padding=(0, 1), stride=(3, 4))\n",
"comp_conv2d(conv2d, X).shape"
]
},
{
"cell_type": "markdown",
"id": "b0ade0d5-8c2e-42d3-874d-bb9880c89dab",
"metadata": {
"jp-MarkdownHeadingCollapsed": true,
"slideshow": {
"slide_type": "-"
},
"tags": []
},
"source": [
"## 多输入多输出通道\n",
"\n",
"实现一下多输入通道互相关运算"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "9be9ab73-d95c-4a89-acfe-ac912e33e831",
"metadata": {
"origin_pos": 3,
"tab": [
"pytorch"
]
},
"outputs": [],
"source": [
"import torch\n",
"from d2l import torch as d2l\n",
"\n",
"def corr2d_multi_in(X, K):\n",
" # 先遍历“X”和“K”的第0个维度(通道维度,忽略批量),再把它们加在一起\n",
" return sum(d2l.corr2d(x, k) for x, k in zip(X, K))"
]
},
{
"cell_type": "markdown",
"id": "5cb1a6f4-9155-4c6e-b452-6f0575ac6bc3",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"验证互相关运算的输出"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "ffc69b60-744c-4a12-8f3c-ffb93993e6b8",
"metadata": {
"origin_pos": 6,
"tab": [
"pytorch"
]
},
"outputs": [
{
"data": {
"text/plain": [
"tensor([[ 56., 72.],\n",
" [104., 120.]])"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"X = torch.tensor([[[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]],\n",
" [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]])\n",
"K = torch.tensor([[[0.0, 1.0], [2.0, 3.0]], [[1.0, 2.0], [3.0, 4.0]]])\n",
"\n",
"corr2d_multi_in(X, K)"
]
},
{
"cell_type": "markdown",
"id": "2b26766f-0d38-4dec-b444-49564c7b1706",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"计算多个通道的输出的互相关函数"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "f973b848-4341-43b5-bdeb-dde75fc953e9",
"metadata": {
"origin_pos": 10,
"tab": [
"pytorch"
]
},
"outputs": [
{
"data": {
"text/plain": [
"torch.Size([3, 2, 2, 2])"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def corr2d_multi_in_out(X, K):\n",
" # 迭代“K”的第0个维度,每次都对输入“X”执行互相关运算。\n",
" # 最后将所有结果都叠加在一起\n",
" return torch.stack([corr2d_multi_in(X, k) for k in K], 0)\n",
"\n",
"K = torch.stack((K, K + 1, K + 2), 0)\n",
"K.shape"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "8a5ef08c-2688-4551-9fe4-4fe83476b568",
"metadata": {
"origin_pos": 12,
"slideshow": {
"slide_type": "slide"
},
"tab": [
"pytorch"
]
},
"outputs": [
{
"data": {
"text/plain": [
"tensor([[[ 56., 72.],\n",
" [104., 120.]],\n",
"\n",
" [[ 76., 100.],\n",
" [148., 172.]],\n",
"\n",
" [[ 96., 128.],\n",
" [192., 224.]]])"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"corr2d_multi_in_out(X, K)"
]
},
{
"cell_type": "markdown",
"id": "8b5646a7-4eaf-4c73-ae23-a6b0ae4b3667",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"1x1卷积"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "fd63dcb7-8f02-46e9-8696-8cd24313460d",
"metadata": {
"origin_pos": 18,
"tab": [
"pytorch"
]
},
"outputs": [],
"source": [
"def corr2d_multi_in_out_1x1(X, K):\n",
" c_i, h, w = X.shape\n",
" c_o = K.shape[0]\n",
" X = X.reshape((c_i, h * w))\n",
" K = K.reshape((c_o, c_i))\n",
" # 全连接层中的矩阵乘法\n",
" Y = torch.matmul(K, X)\n",
" return Y.reshape((c_o, h, w))"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "18e97ece-4c09-4273-9e7b-a0934046c97c",
"metadata": {
"origin_pos": 18,
"slideshow": {
"slide_type": "slide"
},
"tab": [
"pytorch"
]
},
"outputs": [],
"source": [
"X = torch.normal(0, 1, (3, 3, 3))\n",
"K = torch.normal(0, 1, (2, 3, 1, 1))\n",
"\n",
"Y1 = corr2d_multi_in_out_1x1(X, K)\n",
"Y2 = corr2d_multi_in_out(X, K)\n",
"assert float(torch.abs(Y1 - Y2).sum()) < 1e-6"
]
},
{
"cell_type": "markdown",
"id": "64c48e4b-8c94-4de9-bd8d-e03512695e7e",
"metadata": {
"jp-MarkdownHeadingCollapsed": true,
"slideshow": {
"slide_type": "-"
},
"tags": []
},
"source": [
"## 池化层\n",
"\n",
"池化也有译成汇聚。实现池化层的前向传播"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "7ce8507e-5ba3-427a-9066-d1031580472d",
"metadata": {
"origin_pos": 3,
"tab": [
"pytorch"
]
},
"outputs": [],
"source": [
"import torch\n",
"from torch import nn\n",
"from d2l import torch as d2l\n",
"\n",
"def pool2d(X, pool_size, mode='max'):\n",
" p_h, p_w = pool_size\n",
" Y = torch.zeros((X.shape[0] - p_h + 1, X.shape[1] - p_w + 1))\n",
" for i in range(Y.shape[0]):\n",
" for j in range(Y.shape[1]):\n",
" if mode == 'max':\n",
" Y[i, j] = X[i: i + p_h, j: j + p_w].max()\n",
" elif mode == 'avg':\n",
" Y[i, j] = X[i: i + p_h, j: j + p_w].mean()\n",
" return Y"
]
},
{
"cell_type": "markdown",
"id": "fd4f9890-9159-4d0f-92bf-629c91f522ac",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"验证二维最大汇聚层的输出"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "4d6aca2f-7a2b-4e4e-a33f-51cfeabe3149",
"metadata": {
"origin_pos": 6,
"tab": [
"pytorch"
]
},
"outputs": [
{
"data": {
"text/plain": [
"tensor([[4., 5.],\n",
" [7., 8.]])"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"X = torch.tensor([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]])\n",
"pool2d(X, (2, 2))"
]
},
{
"cell_type": "markdown",
"id": "da22e032-1759-4359-836e-36ecfd643583",
"metadata": {
"slideshow": {
"slide_type": "-"
}
},
"source": [
"验证平均汇聚层"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "2939723a-f3ea-4f70-b291-cf5dbfd8fe30",
"metadata": {
"origin_pos": 8,
"tab": [
"pytorch"
]
},
"outputs": [
{
"data": {
"text/plain": [
"tensor([[2., 3.],\n",
" [5., 6.]])"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"pool2d(X, (2, 2), 'avg')"
]
},
{
"cell_type": "markdown",
"id": "c0cb77fd-3e7b-4d1d-a616-63c6100d2feb",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"填充和步幅"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "a574a0ed-4184-4ad5-8f16-b802bea2bb99",
"metadata": {
"origin_pos": 11,
"tab": [
"pytorch"
]
},
"outputs": [
{
"data": {
"text/plain": [
"tensor([[[[ 0., 1., 2., 3.],\n",
" [ 4., 5., 6., 7.],\n",
" [ 8., 9., 10., 11.],\n",
" [12., 13., 14., 15.]]]])"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"X = torch.arange(16, dtype=torch.float32).reshape((1, 1, 4, 4))\n",
"X"
]
},
{
"cell_type": "markdown",
"id": "0209afc0-8eb4-4c58-a5c6-efdc1988e0fd",
"metadata": {
"slideshow": {
"slide_type": "-"
}
},
"source": [
"深度学习框架中的步幅与汇聚窗口的大小相同"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "b66fd222-9233-4dc9-bbce-87925a86498b",
"metadata": {
"origin_pos": 15,
"tab": [
"pytorch"
]
},
"outputs": [
{
"data": {
"text/plain": [
"tensor([[[[10.]]]])"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"pool2d = nn.MaxPool2d(3)\n",
"pool2d(X)"
]
},
{
"cell_type": "markdown",
"id": "0c169cd1-b0d6-4760-88e2-0ad530c51cfc",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"填充和步幅可以手动设定"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "4fdbb845-7f4b-4649-96c0-3c0300b6021a",
"metadata": {
"origin_pos": 19,
"tab": [
"pytorch"
]
},
"outputs": [
{
"data": {
"text/plain": [
"tensor([[[[ 5., 7.],\n",
" [13., 15.]]]])"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"pool2d = nn.MaxPool2d(3, padding=1, stride=2)\n",
"pool2d(X)"
]
},
{
"cell_type": "markdown",
"id": "7b83b34f-526b-48b1-b5ff-963c84b57471",
"metadata": {
"slideshow": {
"slide_type": "-"
}
},
"source": [
"设定一个任意大小的矩形汇聚窗口,并分别设定填充和步幅的高度和宽度"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "fc4d58c9-df77-4765-8344-948d66e9d55f",
"metadata": {
"origin_pos": 25,
"tab": [
"pytorch"
]
},
"outputs": [
{
"data": {
"text/plain": [
"tensor([[[[ 5., 7.],\n",
" [13., 15.]]]])"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"pool2d = nn.MaxPool2d((2, 3), stride=(2, 3), padding=(0, 1))\n",
"pool2d(X)"
]
},
{
"cell_type": "markdown",
"id": "6a771a4c-bda2-48e6-bd31-d8d4eec503ad",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"汇聚层在每个输入通道上单独运算"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "8fc17adc-68a2-4395-8ca4-db4c853eae72",
"metadata": {
"origin_pos": 29,
"tab": [
"pytorch"
]
},
"outputs": [
{
"data": {
"text/plain": [
"tensor([[[[ 0., 1., 2., 3.],\n",
" [ 4., 5., 6., 7.],\n",
" [ 8., 9., 10., 11.],\n",
" [12., 13., 14., 15.]],\n",
"\n",
" [[ 1., 2., 3., 4.],\n",
" [ 5., 6., 7., 8.],\n",
" [ 9., 10., 11., 12.],\n",
" [13., 14., 15., 16.]]]])"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"X = torch.cat((X, X + 1), 1)\n",
"X"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "4622f1b6-0352-446f-8a67-c336f42249d2",
"metadata": {
"origin_pos": 33,
"tab": [
"pytorch"
]
},
"outputs": [
{
"data": {
"text/plain": [
"tensor([[[[ 5., 7.],\n",
" [13., 15.]],\n",
"\n",
" [[ 6., 8.],\n",
" [14., 16.]]]])"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"pool2d = nn.MaxPool2d(3, padding=1, stride=2)\n",
"pool2d(X)"
]
},
{
"cell_type": "markdown",
"id": "a71866f9-e712-467a-9e0d-3832db22e4ad",
"metadata": {
"slideshow": {
"slide_type": "-"
},
"tags": []
},
"source": [
"## 卷积神经网络(LeNet)\n",
"\n",
"LeNet(LeNet-5)由两个部分组成:\n",
"卷积编码器和全连接层密集块"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "10bf89f8-9718-4d30-bceb-f64ccd57d7e8",
"metadata": {
"origin_pos": 2,
"tab": [
"pytorch"
]
},
"outputs": [],
"source": [
"import torch\n",
"from torch import nn\n",
"from d2l import torch as d2l\n",
"\n",
"net = nn.Sequential(\n",
" nn.Conv2d(1, 6, kernel_size=5, padding=2), nn.Sigmoid(),\n",
" nn.AvgPool2d(kernel_size=2, stride=2),\n",
" nn.Conv2d(6, 16, kernel_size=5), nn.Sigmoid(),\n",
" nn.AvgPool2d(kernel_size=2, stride=2),\n",
" nn.Flatten(),\n",
" nn.Linear(16 * 5 * 5, 120), nn.Sigmoid(),\n",
" nn.Linear(120, 84), nn.Sigmoid(),\n",
" nn.Linear(84, 10))"
]
},
{
"cell_type": "markdown",
"id": "b108595b-2b06-4670-b55e-4096d2c97e7d",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"检查模型"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "fca54cef-3f57-4901-b1e7-4039c9050a53",
"metadata": {
"origin_pos": 6,
"tab": [
"pytorch"
]
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Conv2d output shape: \t torch.Size([1, 6, 28, 28])\n",
"Sigmoid output shape: \t torch.Size([1, 6, 28, 28])\n",
"AvgPool2d output shape: \t torch.Size([1, 6, 14, 14])\n",
"Conv2d output shape: \t torch.Size([1, 16, 10, 10])\n",
"Sigmoid output shape: \t torch.Size([1, 16, 10, 10])\n",
"AvgPool2d output shape: \t torch.Size([1, 16, 5, 5])\n",
"Flatten output shape: \t torch.Size([1, 400])\n",
"Linear output shape: \t torch.Size([1, 120])\n",
"Sigmoid output shape: \t torch.Size([1, 120])\n",
"Linear output shape: \t torch.Size([1, 84])\n",
"Sigmoid output shape: \t torch.Size([1, 84])\n",
"Linear output shape: \t torch.Size([1, 10])\n"
]
}
],
"source": [
"X = torch.rand(size=(1, 1, 28, 28), dtype=torch.float32)\n",
"for layer in net:\n",
" X = layer(X)\n",
" print(layer.__class__.__name__,'output shape: \\t',X.shape)"
]
},
{
"cell_type": "markdown",
"id": "97b8c6d4-e9ff-4a47-94f9-9f12348683c3",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"LeNet在Fashion-MNIST数据集上的表现"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "f0f07725-b2d3-4461-92f3-e8465153bd96",
"metadata": {
"origin_pos": 9,
"tab": [
"pytorch"
]
},
"outputs": [],
"source": [
"batch_size = 256\n",
"train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size=batch_size)"
]
},
{
"cell_type": "markdown",
"id": "4b457e3e-b2b0-4bab-9821-7ed7ce2e3f7f",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"对\n",
"`evaluate_accuracy`函数进行轻微的修改"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "43a68342-901c-4d8e-8066-c3f6b0f32a2a",
"metadata": {
"origin_pos": 13,
"tab": [
"pytorch"
]
},
"outputs": [],
"source": [
"def evaluate_accuracy_gpu(net, data_iter, device=None): \n",
" \"\"\"使用GPU计算模型在数据集上的精度\"\"\"\n",
" if isinstance(net, nn.Module):\n",
" net.eval() # 设置为评估模式\n",
" if not device:\n",
" device = next(iter(net.parameters())).device\n",
" metric = d2l.Accumulator(2) # 正确预测的数量,总预测的数量\n",
" with torch.no_grad():\n",
" for X, y in data_iter:\n",
" if isinstance(X, list): # BERT微调所需(之后介绍)\n",
" X = [x.to(device) for x in X]\n",
" else:\n",
" X = X.to(device)\n",
" y = y.to(device)\n",
" metric.add(d2l.accuracy(net(X), y), y.numel())\n",
" return metric[0] / metric[1]"
]
},
{
"cell_type": "markdown",
"id": "0fb6644e-bfe3-47da-acdc-e223e632801d",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"为了使用GPU,我们还需要一点小改动"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "c7b9b01a-3c00-4e6d-b991-06c081e442bf",
"metadata": {
"origin_pos": 16,
"tab": [
"pytorch"
]
},
"outputs": [],
"source": [
"def train_ch6(net, train_iter, test_iter, num_epochs, lr, device):\n",
" \"\"\"用GPU训练模型(在第六章定义)\"\"\"\n",
" def init_weights(m):\n",
" if type(m) == nn.Linear or type(m) == nn.Conv2d:\n",
" nn.init.xavier_uniform_(m.weight)\n",
" net.apply(init_weights)\n",
" print('training on', device)\n",
" net.to(device)\n",
" optimizer = torch.optim.SGD(net.parameters(), lr=lr)\n",
" loss = nn.CrossEntropyLoss()\n",
" animator = d2l.Animator(xlabel='epoch', xlim=[1, num_epochs],\n",
" legend=['train loss', 'train acc', 'test acc'])\n",
" timer, num_batches = d2l.Timer(), len(train_iter)\n",
" for epoch in range(num_epochs):\n",
" metric = d2l.Accumulator(3) # 训练损失之和,训练准确率之和,样本数\n",
" net.train()\n",
" for i, (X, y) in enumerate(train_iter):\n",
" timer.start()\n",
" optimizer.zero_grad()\n",
" X, y = X.to(device), y.to(device)\n",
" y_hat = net(X)\n",
" l = loss(y_hat, y)\n",
" l.backward()\n",
" optimizer.step()\n",
" with torch.no_grad():\n",
" metric.add(l * X.shape[0], d2l.accuracy(y_hat, y), X.shape[0])\n",
" timer.stop()\n",
" train_l = metric[0] / metric[2]\n",
" train_acc = metric[1] / metric[2]\n",
" if (i + 1) % (num_batches // 5) == 0 or i == num_batches - 1:\n",
" animator.add(epoch + (i + 1) / num_batches,\n",
" (train_l, train_acc, None))\n",
" test_acc = evaluate_accuracy_gpu(net, test_iter)\n",
" animator.add(epoch + 1, (None, None, test_acc))\n",
" print(f'loss {train_l:.3f}, train acc {train_acc:.3f}, '\n",
" f'test acc {test_acc:.3f}')\n",
" print(f'{metric[2] * num_epochs / timer.sum():.1f} examples/sec '\n",
" f'on {str(device)}')"
]
},
{
"cell_type": "markdown",
"id": "a6c9ffd8-fc3a-4480-bb2a-8ec164f7fe37",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"训练和评估LeNet-5模型"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "103901ca-60e4-4694-96fd-02f56736c134",
"metadata": {
"origin_pos": 19,
"tab": [
"pytorch"
],
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"loss 0.472, train acc 0.823, test acc 0.786\n",
"3560.3 examples/sec on cpu\n"
]
},
{
"data": {
"image/svg+xml": [
"\n",
"\n",
"\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"lr, num_epochs = 0.9, 10\n",
"train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.16"
}
},
"nbformat": 4,
"nbformat_minor": 5
}