目录
层和块:nn.Sequential
参数管理
卷积
填充和步幅:调整数据的维度
多输入多输出通道
池化
Lenet
import torch from torch import nn from torch.nn import functional as F net = nn.Sequential(nn.Linear(20, 256), nn.ReLU(), nn.Linear(256, 10))
nn.Sequential
定义了一种特殊的Module
,即在PyTorch中表示一个块的类。两个全连接层都是Linear
类的实例,Linear
类本身就是Module
的子类。forward
函数:将列表中的每个块连接在一起,将每个块的输出作为下一个块的输入。
class MLP(nn.Module): # 用模型参数声明层。这里,声明两个全连接的层 def __init__(self): # 调用`MLP`的父类`Block`的构造函数来执行必要的初始化。 # 这样,在类实例化时也可以指定其他函数参数,例如模型参数`params`(稍后将介绍) super().__init__() self.hidden = nn.Linear(20, 256) # 隐藏层 self.out = nn.Linear(256, 10) # 输出层 # 定义模型的正向传播,即如何根据输入`X`返回所需的模型输出 def forward(self, X): # 注意,这里使用ReLU的函数版本,其在nn.functional模块中定义。 return self.out(F.relu(self.hidden(X)))
层也是块。
一个块可以由许多层组成。
一个块可以由许多块组成。
块可以包含代码。
块负责大量的内部处理,包括参数初始化和反向传播。
层和块的顺序连接由Sequential
块处理。
Q:为什么需要常数参数?
平移不变性:不管检测对象出现在图像中的哪个位置,神经网络的前面几层应该对相同的图像区域具有相似的反应。
局部性:神经网络的前面几层应该只探索输入图像中的局部区域,而不过度在意图像中相隔较远区域的关系。
总结:
二维卷积层的核心计算是二维互相关运算。最简单的形式是,对二维输入数据和卷积核执行互相关操作,然后添加一个偏置。
可以从数据中学习卷积核的参数和偏移。
import torch from torch import nn from d2l import torch as d2l def corr2d(X, K): #@save """计算二维互相关运算。""" h, w = K.shape Y = torch.zeros((X.shape[0] - h + 1, X.shape[1] - w + 1)) #输出的宽度和高度 for i in range(Y.shape[0]): for j in range(Y.shape[1]): Y[i, j] = (X[i:i + h, j:j + w] * K).sum() return Y
二维卷积层:
class Conv2D(nn.Module): def __init__(self, kernel_size): super().__init__() self.weight = nn.Parameter(torch.rand(kernel_size)) self.bias = nn.Parameter(torch.zeros(1)) def forward(self, x): #前向运算:x和weight互相关运算再加偏移 return corr2d(x, self.weight) + self.bias
学习卷积核:
# 构造一个二维卷积层,它具有1个输出通道和形状为(1,2)的卷积核 conv2d = nn.Conv2d(1,1, kernel_size=(1, 2), bias=False) # 这个二维卷积层使用四维输入和输出格式(批量大小、通道、高度、宽度), # 其中批量大小和通道数都为1 X = X.reshape((1, 1, 6, 8)) Y = Y.reshape((1, 1, 6, 7)) for i in range(10): Y_hat = conv2d(X) l = (Y_hat - Y) ** 2 conv2d.zero_grad() l.sum().backward() # 迭代卷积核 conv2d.weight.data[:] -= 3e-2 * conv2d.weight.grad if (i + 1) % 2 == 0: print(f'batch {i+1}, loss {l.sum():.3f}')
全连接层的问题所在:权重W的高取决于输入的宽
应用多层卷积时,常常丢失边缘像素,解决这个问题的简单方法即填充:在输入图像的边界行或列填充元素(通常填充元素是 0 )。填充可以增加输出的高度和宽度
每次滑动元素的数量称为步幅。步幅可以减小输出的高和宽。
多输入多输出通道可以用来扩展卷积层的模型。
每个输入通道有独立的二维卷积核,所有通道结果相加得到一个输出通道结果。
可以认为每个输出通道在识别特定的模式。有独立的三维卷积核。
1×1卷积层:卷积核的高和宽为1。不识别空间模式信息,等价于把输入拉成一个向量。通常用于调整网络层的通道数量和控制模型复杂性。
池化层:降低卷积层对位置的敏感性。
与卷积层类似,池化层运算符由一个固定形状的窗口组成,该窗口根据其步幅大小在输入的所有区域上滑动,为固定形状窗口(称为池化窗口)遍历的每个位置计算一个输出。
不同于卷积层中的输入与卷积核之间的互相关计算,池化层不包含参数。 池运算符是确定性的,通常计算池化窗口中所有元素的最大值(最大池化层)或平均值(平均池化层)。输入通道数 = 输出通道数
卷积编码器:由两个卷积层组成;
全连接层密集块:由三个全连接层组成。
每个卷积块中的基本单元是一个卷积层、一个 sigmoid 激活函数和平均汇聚层。
CAT VS DOG
先想了一下Lenet的模型但是由于对colab不熟悉,对数据集的处理和对模型的训练还没成功,也不知道对不对,这几天假期抓紧研究一下。
import os import random from PIL import Image from torch.utils.data import Dataset random.seed(1) DogCat_label = {"dog": 0, "cat": 1} # Dataset class DogCatDataset(Dataset): def __init__(self, data_dir, transform=None): # data_dir:数据集所在路径;transform: 数据预处理 self.label_name = {"dog": 0, "cat": 1} # data_info放图片路径和标签 self.data_info = self.get_img_info(data_dir) self.transform = transform def __getitem__(self, index): path_img, label = self.data_info[index] img = Image.open(path_img).convert('RGB') if self.transform is not None: img = self.transform(img) return img, label def __len__(self): return len(self.data_info) def get_img_info(data_dir): data_info = list() for # 遍历类别(待完善) for # 遍历图片(待完善) for label = data_info.append() return data_info
class LeNet(nn.Module): def __init__(self, classes): super(LeNet, self).__init__() # 待完善 # self.conv1 = nn.Conv2d() # self.conv2 = nn.Conv2d() # self.fc1 = nn.Linear() # self.fc2 = nn.Linear() # self.fc3 = nn.Linear() def forward(self, x): out = F.Sigmoid(self.conv1(x)) out = F.max_pool2d(out, 2) out = F.Sigmoid(self.conv2(out)) out = F.max_pool2d(out, 2) out = F.Sigmoid(self.fc1(out)) out = F.Sigmoid(self.fc2(out)) out = self.fc3(out) return out def initialize_weights(self): for m in self.modules(): if isinstance(m, nn.Conv2d): nn.init.xavier_normal_(m.weight.data) if m.bias is not None: m.bias.data.zero_() elif isinstance(m, nn.BatchNorm2d): m.weight.data.fill_(1) m.bias.data.zero_() elif isinstance(m, nn.Linear): nn.init.normal_(m.weight.data, 0, 0.1) m.bias.data.zero_() class LeNet2(nn.Module): def __init__(self, classes): super(LeNet2, self).__init__() self.features = nn.Sequential( # 待完善 # nn.Conv2d(), # nn.Sigmoid(), # nn.MaxPool2d(), # nn.Conv2d(), # nn.Sigmoid(), # nn.MaxPool2d() ) self.classifier = nn.Sequential( # 待完善 # nn.Linear(), # nn.Sigmoid(), # nn.Linear(), # nn.Sigmoid(), # nn.Linear() ) def forward(self, x): # x = self.features(x) # x = self.classifier(x) return x