第三周作业:卷积神经网络(Part1)

目录

层和块:nn.Sequential

参数管理

卷积

填充和步幅:调整数据的维度

多输入多输出通道

池化

Lenet


层和块:nn.Sequential

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:为什么需要常数参数?

卷积

  1. 平移不变性:不管检测对象出现在图像中的哪个位置,神经网络的前面几层应该对相同的图像区域具有相似的反应。

  2. 局部性:神经网络的前面几层应该只探索输入图像中的局部区域,而不过度在意图像中相隔较远区域的关系。

总结:

  • 二维卷积层的核心计算是二维互相关运算。最简单的形式是,对二维输入数据和卷积核执行互相关操作,然后添加一个偏置。

  • 可以从数据中学习卷积核的参数和偏移。

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。不识别空间模式信息,等价于把输入拉成一个向量。通常用于调整网络层的通道数量和控制模型复杂性。

池化

池化层:降低卷积层对位置的敏感性。

与卷积层类似,池化层运算符由一个固定形状的窗口组成,该窗口根据其步幅大小在输入的所有区域上滑动,为固定形状窗口(称为池化窗口)遍历的每个位置计算一个输出。

不同于卷积层中的输入与卷积核之间的互相关计算,池化层不包含参数。 池运算符是确定性的,通常计算池化窗口中所有元素的最大值(最大池化层)或平均值(平均池化层)。输入通道数 = 输出通道数

Lenet

卷积编码器:由两个卷积层组成; 

全连接层密集块:由三个全连接层组成。

每个卷积块中的基本单元是一个卷积层、一个 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

你可能感兴趣的:(神经网络,深度学习)