PyTorch深度学习实践概论笔记10-卷积神经网络基础篇

上一讲PyTorch深度学习实践概论笔记9-SoftMax分类器介绍了多分类问题如何解决,这一讲来介绍卷积神经网络。

PyTorch深度学习实践概论笔记10-卷积神经网络基础篇_第1张图片

回顾一下之前的内容,先看全连接神经网络吧。

0 Revision: Fully Connected Neural Network

PyTorch深度学习实践概论笔记10-卷积神经网络基础篇_第2张图片

之前的网络里面用的都是线性层,如果一个网络全都由线性层串行连接起来,就叫做全连接网络。在线性层里面输入和每一个输出值之间都存在权重,即每一个输入节点都要参与到下一层输出节点的计算上,这样的线性层也叫全连接层(Fully Connected)

1 Convolutional Neural Network

PyTorch深度学习实践概论笔记10-卷积神经网络基础篇_第3张图片

在做图像处理时,卷积神经网络把图像按照原始的空间结构保存,能保留原始的空间信息。输入的三维图像经过Convolution层之后依旧是三维张量。接下来做一个2x2的subsampling,下采样之后通道数不变、宽度和高度变小(减少数据量,降低运算量)。最终的输出是一个10维的向量,需要经过Fully Connected(全连接层)。

前面的卷积层和下采样层叫做特征提取(Feature Extraction),后面的全连接层做的操作最后输出10维向量叫做分类器(Classification)

1.1 Convolution

PyTorch深度学习实践概论笔记10-卷积神经网络基础篇_第4张图片

如上图,我们看到的彩色图像一般都是RGB的图像,有三通道。 (老师举了物理的光敏电阻的例子,刘老师真的是全能!)(栅格图像和矢量图像)

1.1.1 Convolution – Single Input Channel

接下来看卷积的运算过程,看单一通道如何卷积。

PyTorch深度学习实践概论笔记10-卷积神经网络基础篇_第5张图片

Input是1x5x5,卷积核是3*3,将上面红色方框和卷积核对应元素相乘再求和。其他类似。

PyTorch深度学习实践概论笔记10-卷积神经网络基础篇_第6张图片

1.1.2 Convolution – 3 Input Channels

接下来看3通道如何卷积。

PyTorch深度学习实践概论笔记10-卷积神经网络基础篇_第7张图片

图示如下:

PyTorch深度学习实践概论笔记10-卷积神经网络基础篇_第8张图片

上图的卷积核也叫过滤器。上图也可以画成:

PyTorch深度学习实践概论笔记10-卷积神经网络基础篇_第9张图片

输入一个RGB的三通道,经过一系列卷积后,输出1通道。 

1.1.3 Convolution – N Input Channels

对于N通道的卷积操作。

PyTorch深度学习实践概论笔记10-卷积神经网络基础篇_第10张图片

这是使用一个过滤器的情况。如果想要输出是m个通道怎么办呢?

1.1.4 Convolution – N Input Channels and M Output Channels

这是使用m个过滤器的情况,输入N通道,输出M通道。

PyTorch深度学习实践概论笔记10-卷积神经网络基础篇_第11张图片

准备m个卷积核,每一个卷积核对原始输入卷积完之后通道数都是1,然后把这些通道拼接(cat)起来。每一个卷积核通道数量要求和输入通道一样,这里都为n卷积核的数量和输出通道数是一样的,这里为m。卷积核的大小自己定,与图像大小无关,这里是3*3。

共享权重机制:对每一个图像块操作用的是相同的卷积核。

1.2 Convolutional Layer

PyTorch深度学习实践概论笔记10-卷积神经网络基础篇_第12张图片

可以把m个卷积核拼成4维张量:m(卷积核个数)*n(通道数)*width*height,我们构建卷积层,它的权重就是这样的一个维度。

代码如下:

import torch


in_channels, out_channels= 5, 10  #上面的n,m

width, height = 100, 100 #输入图像的大小

kernel_size = 3 #卷积核大小为3*3

batch_size = 1
#torch.randn()函数指生成服从正态分布的随机数

input = torch.randn(batch_size,

    		in_channels,

    		width, 

    		height)
#卷积核也可以是长方形的      

conv_layer = torch.nn.Conv2d(in_channels,

    		out_channels,

    		kernel_size=kernel_size)

#输入传入卷积层,得到输出
output = conv_layer(input)

print(input.shape)  #torch.Size([1,5,100,100])

print(output.shape)  #torch.Size([1,10,98,98])

print(conv_layer.weight.shape)
#卷积层权重的形状torch.Size([10,5,3,3])
#10是输出通道的数量,5是输入通道的数量,3,3是卷积核的大小

卷积层并不在于输入的张量的宽度和高度,因为我们是复用的权重,只是把图像遍历一遍,图像大输出就大,图像小,输出就小。所以卷积层对输入图像的宽度和高度没有要求,只对输入通道数有要求。定义一个卷积层,需要确定输入通道,输出通道,卷积核的大小。 

接下来介绍卷积层中常见的参数

1.2.1 Convolutional Layer – padding

如何保证卷积之后图像宽高不变?

padding=1

PyTorch深度学习实践概论笔记10-卷积神经网络基础篇_第13张图片

这是有规律可循的:

  • 如果卷积核是3*3,3/2=1,则在原矩阵外补1圈0,padding=1
  • 如果卷积核是5*5,5/2=2,则在原矩阵外补2圈0,padding=2

代码如下:

import torch

input = [3,4,6,5,7, 
         2,4,6,8,2, 
         1,6,7,8,4, 
         9,7,4,6,2, 
         3,7,5,4,1]

#把输入变成(batch_size,channel,width,height)
input = torch.Tensor(input).view(1, 1, 5, 5)

#构建卷积层,输入和输出都是1个通道,卷积核3*3
#如果bias=True,卷积完之后会给每一个通道加上一个偏置量,不需要加偏置量就bias=False
#padding=1,当卷积核为3*3时,保证输出还是5*5
conv_layer = torch.nn.Conv2d(1, 1, kernel_size=3, padding=1, bias=False)

#构建卷积核(输出通道数,输入通道数,宽度,高度)
kernel = torch.Tensor([1,2,3,4,5,6,7,8,9]).view(1, 1, 3, 3)

#把做出来的张量赋值给卷积层的权重.data,把卷积层的权重初始化
conv_layer.weight.data = kernel.data

output = conv_layer(input)

print(output)

输出结果为:

PyTorch深度学习实践概论笔记10-卷积神经网络基础篇_第14张图片

1.2.2 Convolutional Layer – stride=2

介绍一个新概念,stride(步长),索引时每次移动两格。

PyTorch深度学习实践概论笔记10-卷积神经网络基础篇_第15张图片

代码如下:

import torch

input = [3,4,6,5,7, 
         2,4,6,8,2, 
         1,6,7,8,4, 
         9,7,4,6,2, 
         3,7,5,4,1]

input = torch.Tensor(input).view(1, 1, 5, 5)

conv_layer = torch.nn.Conv2d(1, 1, kernel_size=3, stride=2, bias=False)

kernel = torch.Tensor([1,2,3,4,5,6,7,8,9]).view(1, 1, 3, 3)

conv_layer.weight.data = kernel.data

output = conv_layer(input)

print(output)

1.3 Max Pooling Layer

PyTorch深度学习实践概论笔记10-卷积神经网络基础篇_第16张图片

下采样用的比较多的是MaxPooling(最大池化层)。最大池化层是没有权重的,2*2的maxpooling默认stride=2,就是把图像分成2*2的一个组,在每个组里面找最大值。所以做maxpooling的时候只能把一个通道拿出来做maxpooling,通道之间不会去找最大值,所以通道数量不会发生改变,但是如果用2*2的maxpooling图像大小会缩成原来的一半

代码如下:

import torch

input = [3,4,6,5, 
        2,4,6,8, 
        1,6,7,8, 
        9,7,4,6, 
        ]

input = torch.Tensor(input).view(1, 1, 4, 4)
#当kernel_size=2时,maxpooling默认的步长也是2

maxpooling_layer = torch.nn.MaxPool2d(kernel_size=2)

output = maxpooling_layer(input)

print(output)

2 A Simple Convolutional Neural Network

PyTorch深度学习实践概论笔记10-卷积神经网络基础篇_第17张图片

第一个最大池化做一个就行,因为它没有权重,跟sigmoid和relu一样没有权重的,但是有权重的必须每一个层单独做一个实例。

2.1 Revision: Fully Connected Neural Network

PyTorch深度学习实践概论笔记10-卷积神经网络基础篇_第18张图片

代码如下:

class Net(torch.nn.Module):

    def __init__(self):

    	super(Net, self).__init__()

    	self.conv1 = torch.nn.Conv2d(1, 10, kernel_size=5)

    	self.conv2 = torch.nn.Conv2d(10, 20, kernel_size=5)

    	self.pooling = torch.nn.MaxPool2d(2)

    	self.fc = torch.nn.Linear(320, 10)

    def forward(self, x):

    	# Flatten data from (n, 1, 28, 28) to (n, 784)

    	batch_size = x.size(0)

    	x = F.relu(self.pooling(self.conv1(x)))  #先池化再激活区别不大

    	x = F.relu(self.pooling(self.conv2(x)))

    	x = x.view(batch_size, -1) # flatten

    	x = self.fc(x)

    	return x
     

model = Net()
print(model)

输出结果如下:

Net(
  (conv1): Conv2d(1, 10, kernel_size=(5, 5), stride=(1, 1))
  (conv2): Conv2d(10, 20, kernel_size=(5, 5), stride=(1, 1))
  (pooling): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  (fc): Linear(in_features=320, out_features=10, bias=True)
)

2.2 How to use GPU

2.2.1 How to use GPU – 1. Move Model to GPU

步骤1.把模型迁移到GPU

PyTorch深度学习实践概论笔记10-卷积神经网络基础篇_第19张图片

代码如下:

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
#把整个模型的参数,缓存,所有的模块都放到cuda里面,转成cuda tensor
model.to(device)

如果装的是支持cuda版本的pyTorch,available就是True,默认是cuda 0;只有cpu就是false。如果有多个显卡,不同的任务可以使用不同的显卡。model.to(device)把整个模型的参数,缓存,所有的模块都放到cuda里面,转成cuda tensor

2.2.2 How to use GPU – 2. Move Tensors to GPU

步骤2.把计算张量迁移到GPU

计算的时候要把用来计算的张量也迁移到GPU,主要是输入和对应的输出,把inputs和target都迁移到device上,注意迁移数据的device和模型的device要在同一块显卡上,比如把模型放在第0块显卡,数据放在第一块显卡,是没法工作的。

训练代码如下:

def train(epoch):

    running_loss = 0.0

    for batch_idx, data in enumerate(train_loader, 0):

    	inputs, target = data
     	#加入下面这行,把每一步的inputs和targets迁移到GPU

    	inputs, target = inputs.to(device), target.to(device)

    	optimizer.zero_grad()
     

    	# forward + backward + update

    	outputs = model(inputs)

    	loss = criterion(outputs, target)

    	loss.backward()

    	optimizer.step()
     

    	running_loss += loss.item()

    	if batch_idx % 300 == 299:

    		print('[%d, %5d] loss: %.3f' % (epoch + 1, batch_idx + 1, running_loss / 2000))

    		running_loss = 0.0

测试代码如下:

def test():

    correct = 0

    total = 0

    with torch.no_grad():

    	for data in test_loader:

    	    inputs, target = data
     	    #加入下面这行,把每一步的inputs和targets迁移到GPU

    	    inputs, target = inputs.to(device), target.to(device)

    	    outputs = model(inputs)

    	    _, predicted = torch.max(outputs.data, dim=1)

    	    total += target.size(0)

    	    correct += (predicted == target).sum().item()

    print('Accuracy on test set: %d %% [%d/%d]' % (100 * correct / total, correct, total))

2.3 Results

老师结果如下:

PyTorch深度学习实践概论笔记10-卷积神经网络基础篇_第20张图片

不难看出,相比PyTorch深度学习实践概论笔记9-SoftMax分类器中全连接网络97%的准确率,卷积神经网络的准确率为98%,看似只上升了一个百分点,但是错误率由3%降到了2%,错误率降低了三分之一,可以对模型进行改造,追求更好的准确率。

整体代码如下:

import torch
from torchvision import transforms
from torchvision import datasets
from torch.utils.data import DataLoader
import torch.nn.functional as F  # 用Relu函数
import torch.optim as optim  # 优化器优化

batch_size = 64
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))])
# transform:把图像转化成图像张量
train_dataset = datasets.MNIST(root='../data',
                               train=True,
                               download=True,
                               transform=transform)  # 训练数据集
train_loader = DataLoader(train_dataset,
                          shuffle=True,
                          batch_size=batch_size)
test_dataset = datasets.MNIST(root='../data',
                              train=False,
                              download=True,
                              transform=transform)
test_loader = DataLoader(test_dataset,
                         shuffle=False,
                         batch_size=batch_size)
#import torch
#import torch.nn.functional as F
class Net(torch.nn.Module):
    def __init__(self):
        super(Net,self).__init__()
        self.conv1=torch.nn.Conv2d(1,10,kernel_size=5)
        self.conv2=torch.nn.Conv2d(10,20,kernel_size=5)
        self.pooling=torch.nn.MaxPool2d(2)
        self.fc=torch.nn.Linear(320,10)
        
    def forward(self,x):
        #Flatten data from (n,1,28,28) to (n,784)
        batch_size=x.size(0)
        x=F.relu(self.pooling(self.conv1(x)))#先把输入做卷积,然后做池化,然后做relu
        x=F.relu(self.pooling(self.conv2(x)))
        x=x.view(batch_size,-1)#Flatten,变成想要的全连接网络需要的输入
        x=self.fc(x)#用全连接层做变换
        return x#因为要做交叉熵损失,最后一层不做激活
model=Net()
criterion = torch.nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.5)
 

def train(epoch):
    running_loss = 0.0
    for batch_idx, data in enumerate(train_loader, 0):
        inputs, target = data
        #加入下面这行,把每一步的inputs和targets迁移到GPU
    	inputs, target = inputs.to(device), target.to(device)
        optimizer.zero_grad()  # 优化器,输入之前清零
        # forward + backward + updat
        outputs = model(inputs)
        loss = criterion(outputs, target)
        loss.backward()
        optimizer.step()
        running_loss += loss.item()
        if batch_idx % 300 == 299:  # 每300轮输出一次
            print('[%d,%5d] loss:%.3f' % (epoch + 1, batch_idx + 1, running_loss / 300))
            running_loss = 0.0


def test():
    correct = 0  # 正确多少
    total = 0  # 总数多少
    with torch.no_grad():  # 测试不用算梯度
        for data in test_loader:  # 从test_loader拿数据
            images, labels = data
            #加入下面这行,把每一步的inputs和targets迁移到GPU
    	    inputs, target = inputs.to(device), target.to(device)
            outputs = model(images)  # 拿完数据做预测
            _, predicted = torch.max(outputs.data, dim=1)  # 沿着第一个维度找最大值的下标,返回值有两个,因为是10列嘛,返回值
            # 返回值一个是每一行的最大值,另一个是最大值的下标(每一个样本就是一行,每一行有10个量)(行是第0个维度,列是第1个维度)
            total += labels.size(0)  # 取size元组的第0个元素(N,1),
            correct += (predicted == labels).sum().item()  # 推测出来的分类与label是否相等,真就是1,假就是0,求完和之后把标量拿出来
    print('Accuracy on test set:%d %%' % (100 * correct / total))


# 训练
if __name__ == '__main__':
    for epoch in range(10):
        train(epoch)
        test() #训练一轮,测试一轮 

我的输出结果如下:

PyTorch深度学习实践概论笔记10-卷积神经网络基础篇_第21张图片

效果不是很理想,卷积网络设计的有点简单。 

3 Exercise 10-1

看一下练习题。

• Try a more complex CNN:(尝试更复杂的CNN)

        • Conv2d Layer *3

        • ReLU Layer * 3

        • MaxPooling Layer * 3

        • Linear Layer * 3

• Try different configuration of this CNN:(尝试不同的CNN配置)

        • Compare their performance.

(练习的解答之后会完善,请看评论区。)

说明:记录学习笔记,如果错误欢迎指正!写文章不易,转载请联系我。

你可能感兴趣的:(DL框架,AI,笔记,cnn,深度学习,神经网络,机器学习,pytorch)