IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 人工智能 -> 【神经网络9】卷积神经网络(CNN) -> 正文阅读

[人工智能]【神经网络9】卷积神经网络(CNN)

九、卷积神经网络(CNN)

卷积神经网络(ConvolutionalNeuralNetwork,CNN)是一种深度前馈神经网络,目前在图片分类、图片检索、目标检测、目标分割、目标跟踪、视频分类、姿态估计等图像视频相关领域中已有很多较为成功的应用。

9.1 全连接层

拉平为一个列向量

全连接层(Fully Connected Layer)可以简单地理解为前面章节中提到的神经网络的一个隐藏层,它包含权重向量W激活函数
具体来说,对于一张 32 ? 32 ? 3 32*32*3 32?32?3的图片(宽和高均为32个像素,有RGB三个通道,可以将其理解为一个的 32 ? 32 ? 3 32*32*3 32?32?3矩阵),要通过全连接层,首先要将其拉伸为3072*1的向量作为神经网络隐藏层的输入,然后该向量与权重向量W做点乘操作,再将点乘后的结果作为激活函数(如Sigmoid或tanh)的输入,最终,激活函数输出的结果便是全连接层的最终结果。操作过程如图所示,其中activation中蓝色圆圈的值表示所有3072个输入和10维权重向量W点乘的结果。

image-20220511080531667

当完成激活(activation)后的结果为一维向量时,通常将该结果称为特征向量(或激活向量);当激活后的结果为二维向量时,通常称为特征层(feature map,有时也称为激活层,activation map)。由于后面要介绍的卷积层也需要经过激活函数,因此卷积操作得到的结果通常被称为“特征层”。

9.2 卷积层

卷积运算:两矩阵对应元素相乘相加得到的值

卷积层(Convolution Layer)与全连接层不同,它保留了输入图像的空间特征,即对于一张 32 ? 32 ? 3 32*32*3 32?32?3的图片而言,卷积层的输入就是的 32 ? 32 ? 3 32*32*3 32?32?3矩阵,不需要做任何改变。在卷积层中,我们引入了一个新的概念:卷积核kernel(常简称为卷积,有时也称为滤波器filter)。卷积的大小可以在实际需要时自定义其长和宽(常见的卷积神经网络中通常将其设置为 1 ? 1 、 3 ? 3 、 5 ? 5 1*1、3*3、5*5 1?13?35?5等),其通道个数一般设置为与输入图片通道数量一致

必要的概念已经介绍完毕,接下来我们讲一下卷积的过程:让卷积(核)在输入图片上依次进行滑动,滑动方向为从左到右,从上到下;每滑动一次,卷积(核)就与其滑窗位置对应的输入图片x做一次点积计算并得到一个数值。

这里需要提到另外一个概念:步长(stride)。步长是指卷积在输入图片上移动时需要移动的像素数,如步长为1时,卷积每次只移动1个像素,计算过程不会跳过任何一个像素,而步长为2时,卷积每次移动2个像素。

9.2.1 一维卷积

image-20220511082708393

为方便大家理解,我们先来看一下一维卷积的情况,如图a所示,输入是一个 1 ? 7 1*7 1?7维的向量及其对应的数值,我们定义一维卷积,其卷积大小为 1 ? 3 1*3 1?3(数值分别为“10,5,11”),那么经过第一次卷积操作(卷积与其对应的输入做点积)后我们可以得到 10 ? 5 + 5 ? 2 + 11 ? 6 = 126 10*5+5*2+11*6=126 10?5+5?2+11?6=126,所以这里的A对应的数值即为126。在这个例子里,我们定义步长为1,所以接下来卷积移动一个格子(在图像中一个步长可以理解为一个像素),如图b所示,可以计算得到B的数值为160。以此类推,最终得到一个 1 ? 5 1*5 1?5维的向量。
卷积每次滑动覆盖的格子范围在图像处理中被称为“感受野”,这个名词在后文中还会用到。图中所示的“感受野”为 1 ? 3 1*3 1?3

image-20220511082742213

接下来,我们可以扩展到如图8-3所示的步长为2的情况,同样是 1 ? 7 1*7 1?7的输入向量,每次移动两个格子,即卷积从“5, 2,6”移动到“6,10,7”,然后再移动到“7,12,8”,完成所有的卷积操作之后(与步长为1不同),这里最终将得到一个 1 ? 3 1*3 1?3的向量。

9.2.2 二维卷积

看完了一维卷积的计算之后,我们再来学习下二维卷积的计算。对于一个 7 ? 7 7*7 7?7的图片,我们定义一个 3 ? 3 3*3 3?3的卷积,步长分别为1和2,读者可以先自行思考一下其计算过程,如果你已经想好了,请参考前图和下图,看与你的想法是否一致。我们可以看出,步长为1时,输出的特征层(feature map,有时也称为激活层activation map)大小为 5 ? 5 5*5 5?5,而步长为2时,则为 3 ? 3 3*3 3?3。那么,当步长为3时,输出的卷积层大小是多少呢?答案是:会有错误,对于一个 7 ? 7 7*7 7?7的图片不能使用步长为3的 3 ? 3 3*3 3?3卷积。(做卷积的时候必须要涉及到全部的元素

  • 步长为1

image-20220511093112939

  • 步长为2

image-20220511093240421

image-20220511093410229

图c的步长为1

如图a所示,输入为一张 32 ? 32 ? 3 32*32*3 32?32?3的图,kernel大小为 5 ? 5 ? 3 5*5*3 5?5?3(这里的感受野为 3 ? 3 3*3 3?3),那么每一次滑动都将带来卷积和输入图片 5 ? 5 ? 3 = 75 5*5*3=75 5?5?3=75点乘的计算量,完成整个图片的卷积后最终将生成一张 28 ? 28 ? 1 28*28*1 28?28?1的新图片b),即特征层(feature map)。类似地,我们再定义一个卷积(通常可以理解为不同卷积完成不同的任务),这时特征层将产生2个通道。接下来,我们连续堆叠6个不同的卷积(kerne/filter)结果,最终特征层将得到6个通道,而这就可以理解为一张 28 ? 28 ? 6 28*28*6 28?28?6的新图片(如图c)。

9.2.3 卷积神经网络

介绍完了卷积层,接下来我们看看什么是卷积神经网络。如下图所示,卷积神经网络是由一系列卷积层经过激活来得到的。接下来我们看一种更为通用的卷积形式,在 7 ? 7 7*7 7?7的输入图片周边做1个像素的填充(pad=1),如右图所示,步长为1,kernel为 3 ? 3 3*3 3?3的卷积输出的特征层将为 7 ? 7 7*7 7?7。我们在这里给出通用卷积层的计算公式:输入图像为 W 1 ? H 1 ? D 1 W1*H1*D1 W1?H1?D1(字母分别表示图像的宽、高、channel),卷积层的参数中kernel大小为 F ? F F*F F?F,步长为S,pad大小为P,kernel个数为K,那么经过卷积后,输出图像的宽、高、channel分别为:
W 2 = W 1 ? F + 2 P S + 1 H 2 = H 1 ? F ? 2 P S + 1 D 2 = K W_2 = \frac{W_1 - F + 2P}{S} + 1 \\ H_2 = \frac{H_1 - F - 2P}{S} + 1 \\ D_2 = K W2?=SW1??F+2P?+1H2?=SH1??F?2P?+1D2?=K
接下来看一个例子

  • 输入为 32 ? 32 ? 3 32*32*3 32?32?3
  • kernel个数为10,大小为 5 ? 5 5*5 5?5
  • 步长为1
  • pad为2

那么根据以上计算公式可以得出 ( 32 ? 5 + 2 ? 2 ) / 1 + 1 = 32 (32-5+2*2)/1+1=32 32?5+2?2/1+1=32,因此我们可以得知输出的特征层大小为 32 ? 32 ? 10 32*32*10 32?32?10。与此同时,我们也可以得到每个kernel对应的参数个数 5 ? 5 ? 3 + 1 = 76 5*5*3+1=76 5?5?3+1=76(+1表示bias),因此该层卷积最终的参数个数为 76 ? 10 = 760 76*10=760 76?10=760
至此,卷积层的基本运算已介绍完毕,那么卷积层的参数是如何与第7章介绍的传统神经网络参数对应的呢?实际上,卷积层学习的关键就是几个kernel。在上例中, 76 ? 10 = 760 76*10=760 76?10=760
可以对应到传统神经网络中的w0~wn,而输入x1~xn则是输入图片。与传统神经网络不同的是,卷积层的计算是含有空间信息的

9.3 池化层

池化层对原始特征层的信息进行压缩

9.3.1 池化(pooling)

池化(pooling)是对图片进行压缩(降采样)的一种方法,池化的方法有很多,如(网格里面求最大值)max pooling(求网格的平均值)average pooling等。池化层也有操作参数,我们假设输入图像为 W 1 ? H 1 ? D 1 W1*H1*D1 W1?H1?D1(字母分别表示图像的宽、高、channel),池化层的参数中,池化kernel的大小为F*F,步长为S,那么经过池化后输出的图像的宽、高、channel分别为:
W 2 = W 1 ? F S + 1 H 2 = H 1 ? F S + 1 D 2 = K W_2 = \frac{W_1 - F}{S} + 1 \\ H_2 = \frac{H_1 - F}{S} + 1 \\ D_2 = K W2?=SW1??F?+1H2?=SH1??F?+1D2?=K

image-20220511102223157

通常情况下F=2,S=2。如上图所示,一个 4 ? 4 4*4 4?4的特征层经过池化filter= 2 ? 2 2*2 2?2,stride=2的最大池化操作后可以得到一个 2 ? 2 2*2 2?2的特征层。

import torch.nn as nn
import torch.nn.functional as F

class Net(nn.Module):
    def __init__(self): #在这里定义卷积神经网络需要的元素
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(3, 6, 5) #定义第一个卷积层
        self.pool = nn.MaxPool2d(2, 2) #池化层
        self.conv2 = nn.Conv2d(6, 16, 5) #定义第二个卷积层
        self.fc1 = nn.Linear(16 * 5 * 5, 120) #全连接层
        self.fc2 = nn.Linear(120, 84) #全连接层
        self.fc3 = nn.Linear(84, 10) #最后一个全连接层用作10分类

    def forward(self, x): #使用__init__中的定义,构建卷积神经网络结构
        x = self.pool(F.relu(self.conv1(x))) #第一个卷积层首先要经过relu做激活,然后使用前面定义好的nn.MaxPool2d(2, 2)方法做池化
        x = self.pool(F.relu(self.conv2(x))) #第二个卷积层也要经过relu做激活,然后使用前面定义好的nn.MaxPool2d(2, 2)方法做池化
        x = x.view(-1, 16 * 5 * 5) #对特征层tensor维度进行变换
        x = F.relu(self.fc1(x)) #卷积神经网络的特征层经过第一次全连接层操作,然后再通过relu层激活
        x = F.relu(self.fc2(x)) #卷积神经网络的特征层经过第二次全连接层操作,然后再通过relu层激活
        x = self.fc3(x) #卷积神经网络的特征层经过最后一次全连接层操作,得到最终要分类的结果(10类标签)
        return x

net = Net()

9.4 批规范层

避免过拟合

让学习的过程早一点结束

批规范化层(BatchNorm层)是2015年Ioffe和Szegedy等人提出的想法,主要是为了加速神经网络的收敛过程以及提高训练过程中的稳定性。虽然深度学习被证明有效,但它的训练过程始终需要经过精心调试,比如精心设置初始化参数、使用较小的学习率等。Ioffe和Szegedy等人进行了详细的分析,并给出了BatchNorm方法,在后面的很多实验中该方法均被证明非常有效(8.2.4节中介绍的ResNet就在重复使用该结构)。这里首先介绍一下batch的概念:在使用卷积神经网络处理图像数据时,往往是几张图片(如32张、64张、128张等)被同时输入到网络中一起进行前向计算,误差也是将该batch中所有图片的误差累计起来一起回传。BatchNorm方法其实就是对一个batch中的数据根据公式(8-1)做了归一化。
x ^ k = x k ? E [ x k ] V a r ( x k ) \widehat{x}_k = \frac{x_k - E[x_k]}{\sqrt{Var(x_k)}} x k?=Var(xk?) ?xk??E[xk?]?

… 一些常见卷积神经网络结构

9.6 VGG16实现Cifar10分类

########################################
#第1步:载入数据
########################################
import torch
import torchvision
import torchvision.transforms as transforms
#使用torchvision可以很方便地下载cifar10数据集,而torchvision下载的数据集为[0, 1]的PILImage格式,我们需要将张量Tensor归一化到[-1, 1]

transform = transforms.Compose(
    [transforms.ToTensor(), #将PILImage转换为张量
     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))] #将[0, 1]归一化到[-1, 1]
     )

trainset = torchvision.datasets.CIFAR10(root='./book/classifier_cifar10/data', #root表示cifar10的数据存放目录,使用torchvision可直接下载cifar10数据集,也可直接在https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz这里下载(链接来自cifar10官网)
                                        train=True,
                                        download=True, 
                                        transform=transform #按照上面定义的transform格式转换下载的数据
                                        )
trainloader = torch.utils.data.DataLoader(trainset, 
                                          batch_size=4, #每个batch载入的图片数量,默认为1
                                          shuffle=True, 
                                          num_workers=2 #载入训练数据所需的子任务数
                                          )

testset = torchvision.datasets.CIFAR10(root='./book/classifier_cifar10/data', 
                                       train=False,
                                       download=True, 
                                       transform=transform)
testloader = torch.utils.data.DataLoader(testset, 
                                         batch_size=4,
                                         shuffle=False, 
                                         num_workers=2)

cifar10_classes = ('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

########################################
#查看训练数据
#备注:该部分代码可以不放入主函数
########################################
# import numpy as np

# dataiter = iter(trainloader) #随机从训练数据中取一些数据
# images, labels = dataiter.next() 
# images.shape #(4L, 3L, 32L, 32L)  
# #我们可以看到images的shape是4*3*32*32,原因是上面载入训练数据trainloader时一个batch里面有4张图片

# torchvision.utils.save_image(images[1],"test.jpg") #我们仅随机保存images中的一张图片看看
# cifar10_classes[labels[j]] #打印label

########################################
#第2步:构建卷积神经网络
########################################
import math
import torch
import torch.nn as nn
import os

cfg = {'VGG16':[64, 64, 'M', 128, 128, 'M', 256, 256, 256, 'M', 512, 512, 512, 'M', 512, 512, 512, 'M']}

class VGG(nn.Module):
    def __init__(self, net_name):
        super(VGG, self).__init__()
        
        #构建网络的卷积层和池化层,最终输出命名features,原因是通常认为经过这些操作的输出为包含图像空间信息的特征层
        self.features = self._make_layers(cfg[net_name])
        
        #构建卷积层之后的全连接层以及分类器
        self.classifier = nn.Sequential(
            nn.Dropout(),
            nn.Linear(512, 512), #fc1
            nn.ReLU(True),
            nn.Dropout(),
            nn.Linear(512, 512), #fc2
            nn.ReLU(True),
            nn.Linear(512, 10), #fc3,最终cifar10的输出是10类
        )
        #初始化权重
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
                m.weight.data.normal_(0, math.sqrt(2. / n))
                m.bias.data.zero_()
    
    def forward(self, x):
        x = self.features(x) #前向传播的时候先经过卷积层和池化层
        x = x.view(x.size(0), -1)
        x = self.classifier(x) #再将features(得到网络输出的特征层)的结果拼接到分类器上
        return x
    
    def _make_layers(self, cfg):
        layers = []
        in_channels = 3
        for v in cfg:
            if v == 'M':
                layers += [nn.MaxPool2d(kernel_size=2, stride=2)]
            else:
                #conv2d = nn.Conv2d(in_channels, v, kernel_size=3, padding=1)
                #layers += [conv2d, nn.ReLU(inplace=True)]
                layers += [nn.Conv2d(in_channels, v, kernel_size=3, padding=1),
                           nn.BatchNorm2d(v),
                           nn.ReLU(inplace=True)]
                in_channels = v
        return nn.Sequential(*layers)

net = VGG('VGG16')

########################################
#第3步:定义损失函数和优化方法
########################################
import torch.optim as optim

#x = torch.randn(2,3,32,32)
#y = net(x)
#print(y.size())
criterion = nn.CrossEntropyLoss() #定义损失函数:交叉熵
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9) #定义优化方法:随机梯度下降

########################################
#第4步:卷积神经网络的训练
########################################
for epoch in range(5):  #训练数据集的迭代次数,这里cifar10数据集将迭代2次
    train_loss = 0.0
    for batch_idx, data in enumerate(trainloader, 0):
        #初始化
        inputs, labels = data #获取数据
        optimizer.zero_grad() #先将梯度置为0
        
        #优化过程
        outputs = net(inputs) #将数据输入到网络,得到第一轮网络前向传播的预测结果outputs
        loss = criterion(outputs, labels) #预测结果outputs和labels通过之前定义的交叉熵计算损失
        loss.backward() #误差反向传播
        optimizer.step() #随机梯度下降方法(之前定义)优化权重
        
        #查看网络训练状态
        train_loss += loss.item()
        if batch_idx % 2000 == 1999: #每迭代2000个batch打印看一次当前网络收敛情况
            print('[%d, %5d] loss: %.3f' % (epoch + 1, batch_idx + 1, train_loss / 2000))
            train_loss = 0.0
    
    print('Saving epoch %d model ...' % (epoch + 1))
    state = {
        'net': net.state_dict(),
        'epoch': epoch + 1,
    }
    if not os.path.isdir('checkpoint'):
        os.mkdir('checkpoint')
    torch.save(state, './checkpoint/cifar10_epoch_%d.ckpt' % (epoch + 1))

print('Finished Training')
  • 测试
########################################
#第5步:批量计算整个测试集预测效果
########################################
correct = 0
total = 0
with torch.no_grad():
    for data in testloader:
        images, labels = data
        outputs = net(images)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item() #当标记的label种类和预测的种类一致时认为正确,并计数

print('Accuracy of the network on the 10000 test images: %d %%' % (100 * correct / total))

#结果打印:Accuracy of the network on the 10000 test images: 73 %

########################################
#分别查看每个类的预测效果
########################################
class_correct = list(0. for i in range(10))
class_total = list(0. for i in range(10))
with torch.no_grad():

    for data in testloader:
        images, labels = data
        outputs = net(images)
        _, predicted = torch.max(outputs, 1)
        c = (predicted == labels).squeeze()
        for i in range(4):
            label = labels[i]
            class_correct[label] += c[i].item()
            class_total[label] += 1

for i in range(10):
    print('Accuracy of %5s : %2d %%' % (
        cifar10_classes[i], 100 * class_correct[i] / class_total[i])) 
  人工智能 最新文章
2022吴恩达机器学习课程——第二课(神经网
第十五章 规则学习
FixMatch: Simplifying Semi-Supervised Le
数据挖掘Java——Kmeans算法的实现
大脑皮层的分割方法
【翻译】GPT-3是如何工作的
论文笔记:TEACHTEXT: CrossModal Generaliz
python从零学(六)
详解Python 3.x 导入(import)
【答读者问27】backtrader不支持最新版本的
上一篇文章      下一篇文章      查看所有文章
加:2022-05-13 11:44:22  更:2022-05-13 11:47:12 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 5:27:37-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码