卷积神经网络二:卷积神经网络原理简介与实现

卷积神经网络中,用卷积来代替全连接。由卷积层、汇聚层(池化层)以及最后套一个全连接层组成。

卷积层

卷积层有两个重要的特点:局部连接和权重共享。
局部连接是指卷积层中的每一个神经元都只和下一层的某个局部窗口内的神经元相连,构成一个局部连接网络,而全连接层中每个神经元都和下一层的所有神经元相连。
权重共享是指滤波器(卷积核)wL对于第L层所有的神经元都是相同的。如下图所示,所有同颜色连接的权重都是相同的。



卷积层的作用是提取一个局部区域的特征,不同的卷积核相当于不同的特征提取器。

汇聚层(池化层)

汇聚层,更普遍的叫法是池化层,pooling layer,作用主要是进行特征选择,降低特征数量,从而减少参数数量。卷积层虽然可以显著减少网络中的连接数量,但神经元个数并没有减少,如果卷积层后直接接一个分类器,那么分类器的输入维度依然很高,很容易出现过拟合,为了解决这个问题,所以需要加上汇聚层,从而降低特征维度,避免过拟合。常用的池化方法有最大池化(max pooling)和平均池化(avg pooling)。



全连接层

卷积神经网络在经过卷积和池化操作后最后连接一个全连接层用来进行分类。(根据下游任务的分类也可能变为目标提取和语义分割等任务)
一个典型的卷积神经网络结构如下:



这构成了一个前向传播网络,然后pytorch框架可以通过自动求导,进行误差反向传播,利用梯度下降等优化算法,调整参数w和b,(在卷积神经网络中,其实就是就是卷积核中的权重以及偏置),进而进行学习,当误差缩小到一个可以接受的范围内后或者学习到指定的步数后,停止学习,保存下调整后的参数,而且就是我们所学习到的模型。
至于参数的具体学习过程,可以参考很多的深度学习书籍。这里就不展开了。

代码实现

下面我们介绍一个典型的卷积神经网络:LeNet-5
LeNet-5提出的比较早,算是早期一个非常成功的卷积神经网络,典型应用就是基于LetNet-5的手写数字识别系统。LeNet-5的网络结构如下图所示:



首先输入一幅32 * 32 * 1的图像(默认是灰度图),采用5 * 5的卷积核进行卷积运算,把通道数变成6,长宽变成(32-5)/1 + 1 = 28,所以输出28 * 28 * 6的结果,经过最大池化操作,大小减半,变成14 * 14 * 6。第二层以14 * 14 * 6作为输入,采用5 * 5的卷积核进行卷积运算,把通道数变为16,长宽变成(14-5)/1 + 1 = 10,因此输出为10 * 10 * 16,再经过最大池化操作变成5 * 5 * 16。最后以5 * 5 * 16为输入,进行全连接运算,5 * 5 * 16=400,经过全连接运算后,输出10个类别,进行softmax打分,得分最高的就是输出的结果。
我们用手写数字数据集mnist和图像分类数据集cifar-10来分别训练一下LeNet-5网络。
minist和cifar-10都可以采用pytorch自带的torchvision.datasets进行下载。下面的程序展示了如何获取minst和cifar-10,并展示。

import torch.nn as nn
import torch
import torch.optim as optim
import torchvision.datasets as datasets
import torchvision.transforms as transforms
import matplotlib.pyplot as plt

# 加载MNIST数据集
train_dataset = datasets.MNIST(root='./data', train=True, transform=transforms.ToTensor(), download=True)
test_dataset = datasets.MNIST(root='./data', train=False, transform=transforms.ToTensor())
# 展示MNIST数据集
fig = plt.figure(figsize=(20,20))
for i in range(1,11):
    ax = fig.add_subplot(1,10,i)
    ax.imshow(train_dataset.data[i-1],cmap='gray')
    ax.axis('off') # 关闭坐标轴
    ax.set_title(train_dataset.targets[i-1].item())
plt.show()
# 加载cifar10数据集
'''下载训练集 CIFAR-10 10分类训练集'''
train_dataset_cifar = datasets.CIFAR10('./data', train=True, transform=transforms.ToTensor(), download=True)
classes=('plane', 'car', 'bird', 'cat','deer', 'dog', 'frog', 'horse', 'ship', 'truck')
# 展示cifar10数据集
fig = plt.figure(figsize=(20,20))
for i in range(1,11):
    ax = fig.add_subplot(1,10,i)
    ax.imshow(train_dataset_cifar.data[i-1],cmap='gray')
    ax.axis('off') # 关闭坐标轴
    ax.set_title(classes[train_dataset_cifar.targets[i-1]])
plt.show()

输出如下:




由于MINST输入是28*28的图片,而cifar10的输入是三通道的RGB图像,所以我们需要对LeNet-5网络进行微调。

import torch.nn as nn
import torch
import torch.optim as optim
import torchvision.datasets as datasets
import torchvision.transforms as transforms
# 用于MINST数据集
class LeNet5(nn.Module):
    def __init__(self):
        super(LeNet5, self).__init__()
        # 第一个卷积模块
        self.layer1 = nn.Sequential(
            nn.Conv2d(1,6,5), # 输入通道数1,输出通道数6,卷积核大小5*5,所以输出28*28的二维矩阵(28-5)/1 + 1 = 24
            nn.MaxPool2d(2,2) # 最大池化操作后,长宽缩小一半,所以输出是12*12的矩阵
        )
        # 第二个卷积模块
        self.layer2 = nn.Sequential(
            nn.Conv2d(6,16,5), # 输入通道数6,输出通道数16,卷积大小5*5,所以输出10*10,(12-5)/1+1 = 8
            nn.MaxPool2d(2,2) # 最大池化操作后,长宽缩小一半,输出是5*5,所以输出元素是4*4*16=256
        )
        # 全连接模块
        self.layer3 = nn.Sequential(
            nn.Linear(256,120), # 全连接操作,将400个神经元输出为120个
            nn.Linear(120,84), # 全连接操作,将120个神经元输出为84个
            nn.Linear(84, 10) # 全连接操作,将84个神经元输出为10个,因为数字识别有是个数字,所以需要输出10个类别的打分值
        )

    def forward(self, x):
        x = self.layer1(x)
        x = self.layer2(x)
        x = x.view(x.size(0), -1) # 把卷积层的输出展平成一维向量
        x = self.layer3(x)
        return x
# 用于CIFAR10数据集
class LeNet5_image(nn.Module):
    def __init__(self):
        super(LeNet5_image, self).__init__()
        # 第一个卷积模块
        self.layer1 = nn.Sequential(
            nn.Conv2d(3,6,5), # 输入通道数3,输出通道数6,卷积核大小5*5,所以输出28*28的二维矩阵(32-5)/1 + 1 = 28
            nn.MaxPool2d(2,2), # 最大池化操作后,长宽缩小一半,所以输出是14*14的矩阵
        )
        # 第二个卷积模块
        self.layer2 = nn.Sequential(
            nn.Conv2d(6,16,5), # 输入通道数6,输出通道数16,卷积大小5*5,所以输出10*10,(14-5)/1+1 = 10
            nn.MaxPool2d(2,2), # 最大池化操作后,长宽缩小一半,输出是5*5,所以输出元素是5*5*16=400
        )
        # 全连接模块
        self.layer3 = nn.Sequential(
            nn.Linear(400,120), # 全连接操作,将400个神经元输出为120个
            nn.Linear(120,84), # 全连接操作,将120个神经元输出为84个
            nn.Linear(84, 10) # 全连接操作,将84个神经元输出为10个,因为数字识别有是个数字,所以需要输出10个类别的打分值
        )

    def forward(self, x):
        x = self.layer1(x)
        x = self.layer2(x)
        #print(x.shape)
        x = x.view(x.size(0), -1) # 把卷积层的输出展平成一维向量
        #print(x.shape)
        x = self.layer3(x)
        return x

训练代码如下:

def train(net, data_loader, test_loader, epochs=10):
    criterion = nn.CrossEntropyLoss()
    optimizer = optim.Adam(net.parameters())
    #optimizer = optim.SGD(net.parameters(), lr=3e-2, momentum=0.9)
    for epoch in range(epochs):
        total_loss = 0.0
        for i, (images, labels) in enumerate(data_loader):
            optimizer.zero_grad()
            outputs = net(images)
            loss = criterion(outputs, labels)
            total_loss += loss.item()
            loss.backward()
            optimizer.step()
            # if (i+1) % 100 == 0:
            #     print('Epoch [{}/{}], Step [{}/{}], Loss: {:.4f}'.format(epoch+1, 10, i+1, len(data_loader), loss.item()))
        print('Epoch {}, Loss: {:.4f}'.format(epoch+1, total_loss/len(data_loader)))
    # 测试模型
    net.eval()
    with torch.no_grad():
        correct = 0
        total = 0
        for images, labels in test_loader:
            outputs = net(images)
            _, predicted = torch.max(outputs.data, 1)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()
        print('Test Accuracy: {:.2f}%'.format(100 * correct / total))
    pass

经过10个epoch的训练,mnist数据集输出:

mnist:
Epoch 10, Loss: 0.0429
Test Accuracy: 98.49%

而对于cifar10数据集,训练50个epoch,输出:

Epoch 50, Loss: 0.2990
Test Accuracy: 60.20%

可以看到,对于mnist手写数字数据集精度非常高,而对于三通道的图像,LeNet-5的精度比较一般,下次我们来看一下真正让人认识到深度学习能力的冠军模型——AlexNet。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容