{ "cells": [ { "cell_type": "code", "execution_count": null, "id": "a11d642b", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "输入图片: torch.Size([1, 1, 32, 32])\n", "卷积核权重: torch.Size([8, 1, 3, 3])\n", "输出特征图: torch.Size([1, 8, 30, 30])\n" ] } ], "source": [ "import torch\n", "import torch.nn as nn\n", "\n", "\n", "# 1 张 灰度图,每张大小 32x32。\n", "# N C H W\n", "images = torch.randn(1, 1, 32, 32)\n", "# 8 个卷积核,每个卷积核看 1 个输入通道,常见的卷积核大小是 3x3 或 5x5。\n", "#参数:(输入通道数, 输出通道数, 卷积核大小)\n", "conv = nn.Conv2d(1, 8, kernel_size=3) #一层卷积\n", "features = conv(images)\n", "\n", "print(\"输入图片:\", images.shape) #[1, 1, 32, 32]\n", "print(\"卷积核权重:\", conv.weight.shape) # [8, 1, 3, 3] 3*3*8+8\n", "print(\"输出特征图:\", features.shape) # [1, 8, 30, 30]" ] }, { "cell_type": "code", "execution_count": 2, "id": "975e93b9", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "全连接参数量: 1200010\n", "卷积层参数量: 28\n", "结论: 卷积层靠局部连接和权重共享,把参数量明显压小。\n" ] } ], "source": [ "import torch\n", "from torch import nn\n", "# 1. 超参数写死:只比较参数量,不做训练。\n", "image_channels = 3\n", "image_height = 200\n", "image_width = 200\n", "class_count = 10\n", "# 2. 全连接层:需要把整张图片拍平成一个超长向量。\n", "full_connection = nn.Linear(image_channels * image_height * image_width,\n", "class_count)\n", "full_connection_params = sum(param.numel() for param in\n", "full_connection.parameters())\n", "# 3. 卷积层:只看 3x3 局部窗口,参数量和图片宽高无关。\n", "convolution = nn.Conv2d(in_channels=3, out_channels=1, kernel_size=3)\n", "convolution_params = sum(param.numel() for param in convolution.parameters())\n", "# 4. 打印对比结果。\n", "print(\"全连接参数量:\", full_connection_params)\n", "print(\"卷积层参数量:\", convolution_params)\n", "print(\"结论: 卷积层靠局部连接和权重共享,把参数量明显压小。\")\n", "\n" ] }, { "cell_type": "code", "execution_count": 3, "id": "859b6fff", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "卷积参数:\n", " weight 数量: 9\n", " bias 数量: 1\n", " 输出形状: torch.Size([1, 1, 2, 2])\n", "全连接参数:\n", " weight 数量: 64\n", " bias 数量: 4\n", " 输出形状: torch.Size([4])\n" ] } ], "source": [ "import torch\n", "from torch import nn\n", "\n", "# 1. 模拟一张 1 通道 4x4 ‘假图片’。\n", "image = torch.randn(1, 1, 4, 4)\n", "\n", "# 2. 卷积层:3x3 卷积核有 9 个权重,再加 1 个 bias。\n", "conv = nn.Conv2d(in_channels=1, out_channels=1, kernel_size=3)\n", "conv_out = conv(image)\n", "\n", "print(\"卷积参数:\")\n", "print(\" weight 数量:\", conv.weight.numel())\n", "print(\" bias 数量:\", conv.bias.numel())\n", "print(\" 输出形状:\", conv_out.shape)\n", "\n", "# 3. 全连接层:把 4x4 图片拍平成 16 维,再输出 4 维。\n", "flat_image = torch.flatten(image)\n", "fc = nn.Linear(16, 4)\n", "fc_out = fc(flat_image)\n", "\n", "print(\"全连接参数:\")\n", "print(\" weight 数量:\", fc.weight.numel())\n", "print(\" bias 数量:\", fc.bias.numel())\n", "print(\" 输出形状:\", fc_out.shape)" ] }, { "cell_type": "code", "execution_count": null, "id": "f90e3a5d", "metadata": {}, "outputs": [], "source": [ "import torch\n", "from torch import nn\n", "\n", "# 1. 模拟一张 1 通道 4x4 ‘假图片’。\n", "image = torch.randn(1, 1, 4, 4)\n", "\n", "# 2. 卷积层:5*5 卷积核。\n", "conv = nn.Conv2d(in_channels=1, out_channels=1, kernel_size=5)\n", "conv_out = conv(image)\n", "print(conv_out)" ] }, { "cell_type": "code", "execution_count": 6, "id": "fa39666b", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tensor([[[[-0.5894, -0.1350],\n", " [ 0.4633, 0.5073]]]], grad_fn=)\n", "torch.Size([1, 1, 2, 2])\n" ] } ], "source": [ "import torch\n", "from torch import nn\n", "\n", "# 1. 模拟一张 1 通道 4x4 ‘假图片’。\n", "image = torch.randn(1, 1, 4, 4)\n", "\n", "# 2. 卷积层:5*5 卷积核。\n", "conv = nn.Conv2d(in_channels=1, \n", " out_channels=1, \n", " kernel_size=5,\n", " padding=1)\n", "conv_out = conv(image)\n", "print(conv_out)\n", "print(conv_out.shape)" ] }, { "cell_type": "code", "execution_count": 7, "id": "c27d0b28", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "无填充输出: torch.Size([1, 1, 2, 2])\n", "有填充输出: torch.Size([1, 1, 4, 4])\n" ] } ], "source": [ "import torch\n", "\n", "# 模拟一张 1 通道 4x4 ‘假图片’。\n", "data = torch.randn(1, 1, 4, 4)\n", "\n", "# 对比有无 padding 的结果\n", "# padding=0: 输出 2×2\n", "conv_no_pad = torch.nn.Conv2d(in_channels=1, out_channels=1, \n", " kernel_size=3,padding=0)\n", "result_no_pad = conv_no_pad(data)\n", "print(f\"无填充输出: {result_no_pad.shape}\") # [1, 1, 2, 2]\n", "\n", "# padding=1: 输出 4×4(补一圈零,尺寸不变)\n", "conv_pad = torch.nn.Conv2d(in_channels=1, out_channels=1, kernel_size=3,padding=1)\n", "result_pad = conv_pad(data)\n", "print(f\"有填充输出: {result_pad.shape}\") # [1, 1, 4, 4]" ] }, { "cell_type": "code", "execution_count": 8, "id": "b7be42fd", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "输入形状: torch.Size([1, 3, 32, 32])\n", "stride=1 输出: torch.Size([1, 8, 32, 32])\n", "stride=2 输出: torch.Size([1, 8, 16, 16])\n" ] } ], "source": [ "import torch\n", "from torch import nn\n", "\n", "# 1. 模拟一张 RGB 商品图。\n", "image = torch.randn(1, 3, 32, 32)\n", "\n", "# 2. stride=1:卷积核移动更密,输出尺寸保持 32x32。\n", "conv_stride_1 = nn.Conv2d(3, 8, kernel_size=3, stride=1, padding=1)\n", "\n", "# 3. stride=2:卷积核每次跳 2 格,输出尺寸减半。\n", "conv_stride_2 = nn.Conv2d(3, 8, kernel_size=3, stride=2, padding=1)\n", "\n", "# 4. 打印输出形状,观察 stride 对空间尺寸的影响。\n", "print(\"输入形状:\", image.shape) #1, 3, 32, 32\n", "print(\"stride=1 输出:\", conv_stride_1(image).shape) #1, 8, 32, 32\n", "print(\"stride=2 输出:\", conv_stride_2(image).shape) #1, 8, 16, 16" ] }, { "cell_type": "code", "execution_count": 9, "id": "177a7758", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "输入图片形状: torch.Size([1, 3, 32, 32])\n", "卷积权重形状: torch.Size([16, 3, 3, 3])\n", "输出特征图形状: torch.Size([1, 16, 30, 30])\n" ] } ], "source": [ "import torch\n", "from torch import nn\n", "\n", "# 1. 定义卷积层:输入 RGB 三通道,输出 16 组特征。\n", "conv = nn.Conv2d(in_channels=3, out_channels=16, kernel_size=3)\n", "\n", "# 2. 模拟 1 张 32x32 商品图。\n", "images = torch.randn(1, 3, 32, 32)\n", "features = conv(images)\n", "\n", "# 3. 重点看权重形状:[输出通道数, 输入通道数, 卷积核高, 卷积核宽]。\n", "print(\"输入图片形状:\", images.shape) #1, 3, 32, 32\n", "print(\"卷积权重形状:\", conv.weight.shape) #16, 3, 3, 3\n", "print(\"输出特征图形状:\", features.shape) #1, 16, 30, 30" ] }, { "cell_type": "code", "execution_count": null, "id": "cd5604e0", "metadata": {}, "outputs": [], "source": [ "import torch\n", "import torch.nn.functional as F\n", "from torch import nn\n", "\n", "class ResBlock(nn.Module):\n", " def __init__(self,c):\n", " super().__init__()\n", " self.conv1 = torch.nn.Conv2d(c, c, 3, padding=1)\n", " self.conv2 = torch.nn.Conv2d(c, c, 3, padding=1)\n", "\n", " def forward(self, x):\n", " y = F.relu(self.conv1(x))\n", " y = self.conv2(y)\n", " return F.relu(y + x)\n", "\n", "if __name__ == '__main__':\n", " x = torch.randn(1, 3, 32, 32)\n", " net = ResBlock(3)\n", " out = net(x)\n", " print(out)\n", " print(out.shape)" ] }, { "cell_type": "code", "execution_count": 11, "id": "e0c98f24", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tensor([[[[-0.0675, -0.2037, 0.0171, ..., 0.4069, 0.5789, 0.2718],\n", " [ 0.2190, -0.2025, -0.0752, ..., 0.1766, 0.2935, 0.4443],\n", " [ 0.2035, 0.2333, 0.1905, ..., -0.0450, 0.3506, 0.2333],\n", " ...,\n", " [-0.4011, 0.3804, -0.2089, ..., 0.1364, 0.1076, -0.1391],\n", " [-0.1936, -0.0422, -0.0826, ..., 0.1392, 0.1757, 0.2660],\n", " [ 0.1471, 0.1258, -0.1912, ..., -0.0734, 0.2551, 0.2739]],\n", "\n", " [[ 0.7543, 0.2415, 0.0671, ..., 0.3817, 0.1778, 0.3153],\n", " [-0.0306, 0.4632, 0.2696, ..., -0.2719, -0.1206, 0.2695],\n", " [ 0.1162, 0.3065, 0.3385, ..., 0.0064, 0.2785, 0.5351],\n", " ...,\n", " [ 0.6120, 0.2779, -0.1715, ..., 0.2900, 0.4495, 0.5793],\n", " [ 0.3929, 0.2231, 0.4754, ..., 0.0822, 0.7376, -0.0432],\n", " [ 0.1821, 0.1866, 0.4352, ..., 0.1818, 0.3094, 0.5141]],\n", "\n", " [[ 0.1182, -0.2091, -0.2565, ..., -0.1608, -0.2723, -0.0040],\n", " [-0.4285, -0.1973, -0.2409, ..., -0.0442, -0.1848, -0.2102],\n", " [-0.1080, -0.0223, -0.3410, ..., -0.4098, 0.0375, 0.1220],\n", " ...,\n", " [ 0.0412, -0.6477, 0.0764, ..., -0.4107, -0.2269, -0.0131],\n", " [-0.0586, -0.2387, -0.0483, ..., -0.2497, -0.1536, -0.0121],\n", " [ 0.0596, -0.2647, -0.2079, ..., -0.0137, -0.0346, 0.0276]],\n", "\n", " ...,\n", "\n", " [[-0.1513, -0.4924, -0.3291, ..., -0.0858, 0.3325, -0.1445],\n", " [ 0.4556, 0.0310, -0.4014, ..., 0.3085, -0.1313, 0.0024],\n", " [ 0.0458, -0.0948, -0.3584, ..., 0.5850, -0.1029, -0.0403],\n", " ...,\n", " [-0.2512, -1.0865, -0.0465, ..., 0.3306, 0.0660, 0.0341],\n", " [-0.0380, -0.0944, -0.0224, ..., -0.0754, -0.3096, 0.2728],\n", " [-0.0409, 0.1720, 0.1746, ..., 0.2054, 0.1020, 0.0853]],\n", "\n", " [[ 0.2407, -0.1370, -0.2752, ..., 0.4636, 0.2640, 0.4261],\n", " [ 0.4146, 0.1510, 0.1223, ..., 0.0529, 0.1967, 0.2584],\n", " [ 0.1815, 0.6670, 0.3959, ..., 0.0048, 0.0204, 0.0871],\n", " ...,\n", " [ 0.0746, 0.2185, -0.3571, ..., 0.1158, -0.0269, -0.2000],\n", " [ 0.0969, -0.2937, -0.2306, ..., 0.4424, 0.6800, 0.3173],\n", " [ 0.3723, 0.2909, -0.0732, ..., 0.0284, 0.1223, 0.2113]],\n", "\n", " [[-0.5429, -0.0432, -0.0599, ..., 0.0307, 0.1396, 0.0065],\n", " [ 0.4744, 0.0020, 0.4407, ..., -0.2063, 0.0455, 0.2949],\n", " [ 0.1040, 0.3706, 0.1427, ..., -0.4794, 0.0436, -0.2792],\n", " ...,\n", " [-0.1091, 0.1286, 0.0920, ..., -0.2311, -0.0640, -0.2771],\n", " [-0.1871, -0.0525, -0.0609, ..., -0.0685, -0.1604, -0.0677],\n", " [ 0.3317, -0.3073, -0.2099, ..., -0.1886, 0.0815, 0.1345]]]],\n", " grad_fn=)\n", "torch.Size([1, 128, 30, 30])\n" ] } ], "source": [ "class Net(torch.nn.Module):\n", " def __init__(self):\n", " super(Net, self).__init__()\n", " self.conv1 = torch.nn.Conv2d(3, 64, 3, 1)\n", " self.resblock = ResBlock(64)\n", " self.conv2 = torch.nn.Conv2d(64, 128, 1, 1)\n", " def forward(self, x):\n", " out = self.conv1(x)\n", " x = self.resblock(out)\n", " y = self.conv2(x)\n", " return y\n", "if __name__ == '__main__':\n", " x = torch.randn(1, 3, 32, 32)\n", " net = Net()\n", " out = net(x)\n", " print(out)\n", " print(out.shape)" ] }, { "cell_type": "code", "execution_count": null, "id": "77a94535", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "输入图片形状: torch.Size([5, 3, 32, 32])\n", "卷积权重形状: torch.Size([16, 3, 3, 3])\n", "输出特征图形状: torch.Size([5, 16, 30, 30])\n", "weight 参数量: 432\n", "bias 参数量: 16\n", "总参数量: 448\n" ] } ], "source": [ "import torch\n", "from torch import nn\n", "# 1. 定义卷积层:输入 RGB 三通道,输出 16 组特征。\n", "conv = nn.Conv2d(in_channels=3, out_channels=16, kernel_size=3)\n", "# 2. 模拟 10 张 32x32 商品图。\n", "images = torch.randn(5, 3, 32, 32)\n", "features = conv(images)\n", "# 3. 重点看权重形状:[输出通道数, 输入通道数, 卷积核高, 卷积核宽]。\n", "print(\"输入图片形状:\", images.shape)\n", "print(\"卷积权重形状:\", conv.weight.shape)\n", "print(\"输出特征图形状:\", features.shape)\n", "# 4. 参数量计算:权重 16 * 3 * 3 * 3,再加 16 个 bias。\n", "weight_count = conv.weight.numel()\n", "bias_count = conv.bias.numel()\n", "print(\"weight 参数量:\", weight_count)# 16 * 3 * 3 * 3 = 432\n", "print(\"bias 参数量:\", bias_count) # 16\n", "print(\"总参数量:\", weight_count + bias_count)" ] }, { "cell_type": "code", "execution_count": 3, "id": "040eba4a", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "池化前: torch.Size([1, 3, 32, 32])\n", "池化后: torch.Size([1, 3, 16, 16])\n" ] } ], "source": [ "import torch\n", "from torch import nn\n", "\n", "# 1. 模拟 1 张图片经过卷积后得到的特征图。\n", "features = torch.randn(1, 3, 32, 32)\n", "\n", "# 2. 最大池化:每个 2x2 小窗口取最大值,宽高减半。\n", "max_pool = nn.MaxPool2d(kernel_size=2)\n", "pooled_features = max_pool(features)\n", "\n", "# 3. 池化不改变通道数,只压缩空间尺寸。\n", "print(\"池化前:\", features.shape) #1, 3, 32, 32\n", "print(\"池化后:\", pooled_features.shape) #1, 3, 16, 16" ] }, { "cell_type": "code", "execution_count": null, "id": "687a5f7c", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tensor([[0.1005, 0.0953, 0.1032, 0.1047, 0.0979, 0.1016, 0.0990, 0.0962, 0.1005,\n", " 0.1011]], grad_fn=)\n", "输出形状: torch.Size([1, 10])\n" ] } ], "source": [ "import torch\n", "import torch.nn as nn\n", "import torch.nn.functional as F\n", "\n", "class SimpleCNN(nn.Module):\n", " def __init__(self, num_classes=10):\n", " super(SimpleCNN, self).__init__()\n", "\n", " #特征提取器 \n", " #Conv → ReLU → Pool 重复堆叠\n", " #卷积提取局部模式,ReLU 引入非线性,池化降低空间分辨率并扩大有效视野。\n", " self.conv1 = nn.Sequential(\n", " # 第一个卷积块\n", " # 卷积核的大小 用奇数, 3*3 ,5*5\n", " nn.Conv2d(3, 16, kernel_size=3, padding=1),\n", " nn.ReLU(),\n", " nn.MaxPool2d(kernel_size=2),\n", " )\n", "\n", " self.conv2 = nn.Sequential(\n", " # 第二个卷积块\n", " nn.Conv2d(16, 32, kernel_size=3, padding=1),\n", " nn.ReLU(),\n", " nn.MaxPool2d(kernel_size=2),\n", " )\n", "\n", " self.conv3 = nn.Sequential( \n", " # 第三个卷积块\n", " nn.Conv2d(32, 64, kernel_size=3),\n", " nn.ReLU()\n", " )\n", "\n", " self.conv4 = nn.Sequential( \n", " # 第三个卷积块\n", " nn.Conv2d(64, 128, kernel_size=3),\n", " nn.ReLU()\n", " )\n", " # 1 128 4 4\n", " #N C H W -> N V\n", " #分类器 处理卷积层提炼后的高层特征,最终输出类别得分\n", " #Flatten 特征图拉成一维向量\n", " #Linear映射到类别数维度\n", " #Softmax:推理时转成概率分布\n", " self.fc = nn.Sequential(\n", " # 展平层\n", " nn.Flatten(),\n", " # 全连接层\n", " nn.Linear(128 * 4 * 4, num_classes),\n", " nn.Softmax(dim=1)\n", " )\n", "\n", " def forward(self, x):\n", " conv_out = self.conv1(x)\n", " conv_out = self.conv2(conv_out)\n", " conv_out = self.conv3(conv_out)\n", " conv_out = self.conv4(conv_out)\n", " out = self.fc(conv_out)\n", " return out\n", "\n", "\n", "# 测试代码\n", "if __name__ == \"__main__\":\n", " data = torch.randn(1, 3, 32, 32)\n", " model = SimpleCNN()\n", " output = model(data)\n", " print(output)\n", " print(f\"输出形状: {output.shape}\")\n" ] } ], "metadata": { "kernelspec": { "display_name": "gitcode", "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.11.15" } }, "nbformat": 4, "nbformat_minor": 5 }