乐趣区

关于chrome:看完这篇我就不信还有人不懂卷积神经网络

什么是神经网络?
在介绍卷积神经网络之前,咱们先回顾一下神经网络的基本知识📖。就目前而言,神经网络是深度学习算法的外围,咱们所熟知的很多深度学习算法的背地其实都是神经网络。神经网络由节点层组成,通常蕴含一个输出层、一个或多个暗藏层和一个输入层,咱们所说的几层神经网络通常指的是暗藏层的个数,因为输出层和输入层通常是固定的。节点之间相互连接并具备相干的权重和阈值。如果节点的输入高于指定的阈值,则激活该节点并将数据发送到网络的下一层。否则,没有数据被传递到网络的下一层。对于节点激活的过程有没有感觉十分类似?没错,这其实就是生物的神经元产生神经激动的过程的模仿。

神经网络类型多样,实用于不同的利用场景。例如,循环神经网络(RNN)通常用于自然语言解决和语音辨认,而卷积神经网络(ConvNets 或 CNN)则罕用于分类和计算机视觉工作。在 CNN 产生之前,辨认图像中的对象须要手动的特征提取办法。当初,卷积神经网络为图像分类和对象辨认工作提供了一种更具可扩展性的办法,它利用线性代数的原理,特地是矩阵乘法,来辨认图像中的模式。然而,CNN 的计算要求很高,通常须要图形处理单元 (GPU) 来训练模型,否则训练速度很慢。
什么是卷积神经网络?
卷积神经网络是一种基于卷积计算的前馈神经网络。与一般的神经网络相比,它具备部分连贯、权值共享等长处,使其学习的参数量大幅升高,且网络的收敛速度也更快。同时,卷积运算能更好地提取图像的特色。卷积神经网络的根本组件有卷积层、池化层和全连贯层。卷积层是卷积网络的第一层,其后能够跟着其余卷积层或池化层,最初一层是全连贯层。越往后的层辨认图像越大的局部,较早的层专一于简略的特色,例如色彩和边缘。随着图像数据在 CNN 的各层中后退,它开始辨认物体的较大元素或形态,直到最终辨认出预期的物体。

上面将简略介绍这几种根本组件的相干原理和作用。
卷积层
卷积层是 CNN 的外围组件,其作用是提取样本的特色。它由三个局部组成,即输出数据、过滤器和特色图。在计算机外部,图像以像素矩阵的模式存储。若输出数据是一个 RGB 图像,则由 3D 像素矩阵组成,这意味着输出将具备三个维度——高度、宽度和深度。过滤器,也叫卷积核、特色检测器,其本质是一个二维(2-D)权重矩阵,它将在图像的感触野中挪动,查看特色是否存在。
卷积核的大小不一,但通常是一个 3×3 的矩阵,这也决定了感触野的大小。不同卷积核提取的图像特色也不同。从输出图像的像素矩阵的左上角开始,卷积核的权重矩阵与像素矩阵的对应区域进行点积运算,而后挪动卷积核,反复该过程,直到卷积核扫过整个图像。这个过程就叫做卷积。卷积运算的最终输入就称为特色图、激活图或卷积特色。

如上图所示☝,特色图中的每个输入值不用连贯到输出图像中的每个像素值,它只须要连贯到利用过滤器的感触野。因为输入数组不须要间接映射到每个输出值,卷积层(以及池化层)通常被称为“局部连贯”层。这种个性也被形容为部分连贯。
当卷积核在图像上挪动时,其权重是放弃不变的,这被称为权值共享。一些参数,如权重值,是在训练过程中通过反向流传和梯度降落的过程进行调整的。在开始训练神经网络之前,须要设置三个影响输入体积大小的超参数:

过滤器的数量:影响输入的深度。例如,三个不同的过滤器将产生三个不同的特色图,从而产生三个深度。

步长(Stride):卷积核在输出矩阵上挪动的间隔或像素数。尽管大于等于 2 的步长很少见,但较大的步长会产生较小的输入。

零填充(Zero-padding):通常在当过滤器不适宜输出图像时应用。这会将输出矩阵之外的所有元素设置为零,从而产生更大或雷同大小的输入。有三种类型的填充:

Valid padding:这也称为无填充。在这种状况下,如果维度不对齐,则抛弃最初一个卷积。
Same padding:此填充确保输入层与输出层具备雷同的大小。
Full padding:这种类型的填充通过在输出的边界增加零来减少输入的大小。

在每次卷积操作之后,CNN 的特色图通过激活函数(Sigmoid、ReLU、Leaky ReLU 等)的变换,从而对输入做非线性的映射,以晋升网络的表达能力。

当 CNN 有多个卷积层时,前面的层能够看到后面层的感触野内的像素。例如,假如咱们正在尝试确定图像是否蕴含自行车。咱们能够把自行车视为整机的总和,即由车架、车把、车轮、踏板等组成。自行车的每个独自局部在神经网络中形成了一个较低级别的模式,各局部的组合则代表了一个更高级别的模式,这就在 CNN 中创立了一个特色层次结构。

池化层
为了缩小特色图的参数量,进步计算速度,增大感触野,咱们通常在卷积层之后退出池化层(也称降采样层)。池化能进步模型的容错能力,因为它能在不损失重要信息的前提下进行特色降维。这种降维的过程一方面使得模型更加关注全局特色而非部分特色,另一方面具备肯定的避免过拟合的作用。池化的具体实现是将感触域中的值通过聚合函数后失去的后果作为输入。池化有两种次要的类型:

最大池化(Max pooling):当过滤器在输出中挪动时,它会抉择具备最大值的像素发送到输入数组。与均匀池化相比,这种办法的应用频率更高。

均匀池化(Average pooling):当过滤器在输出中挪动时,它会计算感触域内的平均值以发送到输入数组。

全连贯层
全连贯层通常位于网络的末端,其构造正如其名。如前所述,输出图像的像素值在局部连贯层中并不间接连贯到输入层。然而,在全连贯层中,输入层中的每个节点都间接连贯到前一层中的节点,特色图在这里被开展成一维向量。
该层依据通过前几层提取的特色及其不同的过滤器执行分类工作。尽管卷积层和池化层偏向于应用 ReLu 函数,但 FC 层通常用 Softmax 激活函数对输出进行适当分类,产生 [0, 1] 之间的概率值。
自定义卷积神经网络进行手写数字辨认

导包

import time
import numpy as np
import torch
import torch.nn.functional as F
from torchvision import datasets
from torchvision import transforms
from torch.utils.data import DataLoader

if torch.cuda.is_available():

torch.backends.cudnn.deterministic = True

复制代码

设置参数 & 加载数据集

SETTINGS

Device

device = torch.device(“cuda:3” if torch.cuda.is_available() else “cpu”)

Hyperparameters

random_seed = 1
learning_rate = 0.05
num_epochs = 10
batch_size = 128

Architecture

num_classes = 10

MNIST DATASET

Note transforms.ToTensor() scales input images

to 0-1 range

train_dataset = datasets.MNIST(root=’data’,

                           train=True, 
                           transform=transforms.ToTensor(),
                           download=True)

test_dataset = datasets.MNIST(root=’data’,

                          train=False, 
                          transform=transforms.ToTensor())

train_loader = DataLoader(dataset=train_dataset,

                      batch_size=batch_size, 
                      shuffle=True)

test_loader = DataLoader(dataset=test_dataset,

                     batch_size=batch_size, 
                     shuffle=False)

Checking the dataset

for images, labels in train_loader:

print('Image batch dimensions:', images.shape)
print('Image label dimensions:', labels.shape)
break

复制代码

模型定义

MODEL

class ConvNet(torch.nn.Module):

def __init__(self, num_classes):
    super(ConvNet, self).__init__()
    
    # calculate same padding:
    # (w - k + 2*p)/s + 1 = o
    # => p = (s(o-1) - w + k)/2
    
    # 28x28x1 => 28x28x8
    self.conv_1 = torch.nn.Conv2d(in_channels=1,
                                  out_channels=8,
                                  kernel_size=(3, 3),
                                  stride=(1, 1),
                                  padding=1) # (1(28-1) - 28 + 3) / 2 = 1
    # 28x28x8 => 14x14x8
    self.pool_1 = torch.nn.MaxPool2d(kernel_size=(2, 2),
                                     stride=(2, 2),
                                     padding=0) # (2(14-1) - 28 + 2) = 0
    # 14x14x8 => 14x14x16
    self.conv_2 = torch.nn.Conv2d(in_channels=8,
                                  out_channels=16,
                                  kernel_size=(3, 3),
                                  stride=(1, 1),
                                  padding=1) # (1(14-1) - 14 + 3) / 2 = 1                 
    # 14x14x16 => 7x7x16                             
    self.pool_2 = torch.nn.MaxPool2d(kernel_size=(2, 2),
                                     stride=(2, 2),
                                     padding=0) # (2(7-1) - 14 + 2) = 0

    self.linear_1 = torch.nn.Linear(7*7*16, num_classes)

    for m in self.modules():
        if isinstance(m, torch.nn.Conv2d) or isinstance(m, torch.nn.Linear):
            m.weight.data.normal_(0.0, 0.01)
            m.bias.data.zero_()
            if m.bias is not None:
                m.bias.detach().zero_()
    
    
def forward(self, x):
    out = self.conv_1(x)
    out = F.relu(out)
    out = self.pool_1(out)

    out = self.conv_2(out)
    out = F.relu(out)
    out = self.pool_2(out)
    
    logits = self.linear_1(out.view(-1, 7*7*16))
    probas = F.softmax(logits, dim=1)
    return logits, probas

torch.manual_seed(random_seed)
model = ConvNet(num_classes=num_classes)

model = model.to(device)

optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)
复制代码

模型训练

def compute_accuracy(model, data_loader):

correct_pred, num_examples = 0, 0
for features, targets in data_loader:
    features = features.to(device)
    targets = targets.to(device)
    logits, probas = model(features)
    _, predicted_labels = torch.max(probas, 1)
    num_examples += targets.size(0)
    correct_pred += (predicted_labels == targets).sum()
return correct_pred.float()/num_examples * 100

start_time = time.time()
for epoch in range(num_epochs):

model = model.train()
for batch_idx, (features, targets) in enumerate(train_loader):
    
    features = features.to(device)
    targets = targets.to(device)

    ### FORWARD AND BACK PROP
    logits, probas = model(features)
    cost = F.cross_entropy(logits, targets)
    optimizer.zero_grad()
    
    cost.backward()
    
    ### UPDATE MODEL PARAMETERS
    optimizer.step()
    
    ### LOGGING
    if not batch_idx % 50:
        print ('Epoch: %03d/%03d | Batch %03d/%03d | Cost: %.4f' 
               %(epoch+1, num_epochs, batch_idx, 
                 len(train_loader), cost))

model = model.eval()
print('Epoch: %03d/%03d training accuracy: %.2f%%' % (
      epoch+1, num_epochs, 
      compute_accuracy(model, train_loader)))

print('Time elapsed: %.2f min' % ((time.time() - start_time)/60))

print(‘Total Training Time: %.2f min’ % ((time.time() – start_time)/60))
复制代码

模型评估

with torch.set_grad_enabled(False): # save memory during inference

print('Test accuracy: %.2f%%' % (compute_accuracy(model, test_loader)))

复制代码
输入如下👇

Test accuracy: 97.97%

退出移动版