天天看点

卷积神经网络+用pytorch构建神经网络

目录

    • 1. 神经网络
      • 1.1 神经网络是怎么工作的?
      • 1.2 神经网络为什么要这样工作呢?
        • softmax回归
        • 损失
        • 梯度下降算法
        • 反向传播算法
    • 2.卷积神经网络
      • 2.1 与传统卷积神经网络的区别
      • 2.2 卷积神经网络工作原理
        • 卷积层
        • 激活函数——Relu
        • 池化计算
        • 全连接层
      • 2.3 卷积神经网络常见架构
    • 3. 使用Pytorch搭建神经网络
    • 4. 使用Pytorch实现卷积神经网络

1. 神经网络

1.1 神经网络是怎么工作的?

首先神经网络的结构包括三个层次:输入层、隐藏层和输出层。如下图所示:

卷积神经网络+用pytorch构建神经网络

其中每个圆圈代表一个神经元,输入层和隐藏层之间有很多箭头相连接,每个连接都有一个权值,同层的神经元没有连接,通常神经网络都是全l连接的,也就是所有层都相互有链接且中间没有断层。

第一层有4个输入结点,每个输入节点分别对应五个权值,则可以让输入的数据分别和权值作矩阵乘法最后再相加,得到的数据即为第二层,以此类推。最后会得到值Oj。

1.2 神经网络为什么要这样工作呢?

主要从两个方面来说:损失和优化

神经网络输出结果的分类问题最常用的方法是设置n个输出节点(n为类别的个数),将分类的结果看成概率事件,符合一定的概率分布。通常使用softmax回归将神经网络前向传播的到的结果也变成概率分布。

softmax回归

计算公式:

卷积神经网络+用pytorch构建神经网络

过程图示是这样的:

卷积神经网络+用pytorch构建神经网络

然后将得到的输出结果变成了概率输出:

卷积神经网络+用pytorch构建神经网络

损失

计算公式:

卷积神经网络+用pytorch构建神经网络

为了衡量神经网络预测的概率分布与真实概率分布之间的距离,要对结果进行one-hot编码:

卷积神经网络+用pytorch构建神经网络

最终结果是H(y) = -0.1log(0.1)

为了减少这个损失值,即找到损失函数的最小值,我们通常采用梯度下降算法,

梯度下降算法

之前有详细说过梯度下降算法,这里不再赘述——传送门。

反向传播算法

反向传播算法实际就是:我们使用链式求导法则,反向层层推进,计算出每一层神经节点的偏导数,然后使用梯度下降,不断调整每一个节点的权重,从而达到求得全局最小值的目的。

2.卷积神经网络

2.1 与传统卷积神经网络的区别

传统的多层神经网络只有输入层、隐藏层、输出层。其中隐藏层层数依情况而定。

首先什么是卷积?

对图像(不同的数据窗口数据)和滤波矩阵(一组固定的权重:因为每个神经元的多个权重固定,所以又可以看做一个恒定的滤波器filter)做内积(逐个元素相乘再求和)的操作就是卷积,也是卷积神经网络的名字来源。

卷积神经网络CNN,在原来多层神经网络上加入了更有效的特征学习部分,具体就是在全连接层之前增加了卷积层、激活层和池化层。卷积神经网络出现,使得神经网络层数得以加深,“深度” 学习由此而来。

2.2 卷积神经网络工作原理

如图:

卷积神经网络+用pytorch构建神经网络

新增的三个层次的作用分别是:

卷积层:通过在原始图像上平移来提取特征

激活层:增加非线性分割能力

池化层:减少学习的参数,降低网络的复杂度(最大池化和平均池化)

下边分别对这三个层次进行展开论述:

卷积层

四个参数:

  • 过滤器

    过滤器的大小是指设定的,一般都为3x3、5x5、7x7等

  • 深度

    输出深度(个数)是由卷积中的Filter的个数决定

    卷积神经网络+用pytorch构建神经网络
  • 步幅

    也叫步长,就是Filter移动的间隔大小,

  • 零填充

    主要为了解决步长移动与Filter大大小不合适,不能刚好移动到边缘的情况。

    卷积神经网络+用pytorch构建神经网络

激活函数——Relu

常见的激活函数有sigmod、softmax

relu等,但我们常用的是Relu,因为他可以减少梯度消失和梯度爆炸情况。

函数图像如下:

卷积神经网络+用pytorch构建神经网络

池化计算

池化层的作用主要是通过采样减少参数数量。常用的是最大值池化,也就是在n个样本中取最大值,作为新的样本值,如下图:

卷积神经网络+用pytorch构建神经网络

池化后深度不变。

全连接层

前面的卷积和池化相当于做特征工程,后面的全连接相当于做特征加权,最后的全连接层在整个卷积神经网络中起到 “分类器” 的作用。

2.3 卷积神经网络常见架构

卷积神经网络+用pytorch构建神经网络

3. 使用Pytorch搭建神经网络

1.先定义网络:写网络Net的Class,声明网络的实例net=Net(),

2.定义优化器opt=torch.optim.xxx(net.parameters(),lr=xxx),

3.再定义损失函数(自己写class或者直接用官方的,lossfunc=nn.MSELoss()或者其他。

4.在定义完之后,开始一次一次的循环:

①先清空优化器里的梯度信息,opt.zero_grad();

②再将input传入,output=net(input) ,正向传播

③算损失,loss=lossfunc(target,output) ##这里target就是参考标准值GT,需要自己准备,和之前传入的input一一对应

④误差反向传播,loss.backward()

⑤更新参数,opt.step()

import torch
import torch.nn.functional as F
from torch.autograd import Variable
import matplotlib.pyplot as plt

x = torch.unsqueeze(torch.linspace(-1,1,100),dim=1)
           #对输入的制定位置插入维度 1,无torch.unsqueeze,x是向量torch.Size([100]),
           #有之后x是torch.Size([100, 1])的矩阵
y = x**2+0.2*torch.rand(x.size())
           #x的平方,加噪声(从区间[0,1)的均匀分布中抽取的一组随机数,输出形状与x相同)
print(x,y,x.size())
x,y = Variable(x),Variable(y)  #将x,y张量转化为变量

##定义一个类,用来继承nn.Module
class Net(torch.nn.Module):
    def __init__(self,n_feature,n_hidden,n_output):#搭建层所要的初始信息,特征数,隐藏层单元数和输出单元数
        super(Net,self).__init__()#继承Net到模块,官方步骤
        self.hidden = torch.nn.Linear(n_feature,n_hidden)#从输入层到隐藏层的函数
        self.predict = torch.nn.Linear(n_hidden,n_output)#从隐藏层的输出层的函数
        
    def forward(self,x):#前向传播
        x = F.relu(self.hidden(x))#self.hidden(x)给x加权成为a,用激励函数将a变成特征b
        x = self.predict(x)#self.predict(b)给b加权,预测最终结果
        return x #返回最终预测值
net = Net(1,10,1) #只有一个输入单元,一个输出单元,隐藏单元定为10个
print(net)   #搭建神经网络完毕

plt.ion()
plt.show()

opt = torch.optim.SGD(net.parameters(),lr=0.5)#设置学习率为0.5,用随机梯度下降法优化net神经网络的参数
lossfunc = torch.nn.MSELoss() #设置损失函数为均方差函数
for t in range(100):#训练步数100
    prediction=net(x)#预测值
    loss=lossfunc(prediction,y)#预测值与真实值的误差
    opt.zero_grad()#梯度降0
    loss.backward()#反向传播
    opt.step()#梯度优化
    
    if t%5 == 0:#此时,打印一下图片
        plt.cla()#清除当前图形中的当前活动轴,其他轴不受影响。
        plt.scatter(x.data.numpy(),y.data.numpy())#numpy类型才支持plt,所以要转化
        plt.plot(x.data.numpy(),prediction.data.numpy(),'r-',lw=3)#拟合线图
        plt.text(0.5,0,'loss=%.4f'%loss.data[0],fontdict = {'size':10,'color':'red'})#在(0.5,0)的位置写loss的变化
        plt.pause(0.1)#间隔时间

plt.ioff()
plt.show()
           

4. 使用Pytorch实现卷积神经网络

主要实现图像分类:

import torch
from torch.autograd import Variable
import torch.nn as nn
import torch.nn.functional as F
from torchvision import transforms
from torch.utils.data import DataLoader
from torchvision.datasets import MNIST
from torch import optim


class CNN_net(nn.Module):
    def __init__(self):
        # 先运行nn.Module的初始化函数
        super(CNN_net, self).__init__()
        # 卷积层的定义,输入为1channel的灰度图,输出为4特征,每个卷积kernal为9*9
        self.conv = nn.Conv2d(1, 4, 9)
        # 均值池化
        self.pool = nn.AvgPool2d(2, 2)
        # 全连接后接softmax
        self.fc = nn.Linear(10*10*4, 10)
        self.softmax = nn.Softmax()

    def forward(self, x):
        # 卷积层,分别是二维卷积->sigmoid激励->池化
        out = self.conv(x)
        out = F.sigmoid(out)
        out = self.pool(out)
        print(out.size())
        # 将特征的维度进行变化(batchSize*filterDim*featureDim*featureDim->batchSize*flat_features)
        out = out.view(-1, self.num_flat_features(out))
        # 全连接层和softmax处理
        out = self.fc(out)
        out = self.softmax(out)
        return out
    def num_flat_features(self, x):
        # 四维特征,第一维是batchSize
        size = x.size()[1:]
        num_features = 1
        for s in size:
            num_features *= s
        return num_features

# 定义一个cnn网络
net = CNN_net()
print(net)

# 参数设置
learning_rate = 1e-3
batch_size = 100
epoches = 10

# MNIST图像数据的转换函数
trans_img = transforms.Compose([
        transforms.ToTensor()
    ])

# 下载MNIST的训练集和测试集
trainset = MNIST('./MNIST', train=True, transform=trans_img, download=True)
testset = MNIST('./MNIST', train=False, transform=trans_img, download=True)

# 构建Dataloader
trainloader = DataLoader(trainset, batch_size=batch_size, shuffle=True, num_workers=4)
testloader = DataLoader(testset, batch_size=batch_size, shuffle=False, num_workers=4)

# cost function的选用
criterian = nn.CrossEntropyLoss(size_average=False)

# 选用SGD来求解
opt = optim.SGD(net.parameters(), lr=learning_rate, momentum=0.95)

# 训练过程
for i in range(epoches):
    running_loss = 0.
    running_acc = 0.
    for (img, label) in trainloader:
        # 转换为Variable类型
        img = Variable(img)
        label = Variable(label)

        opt.zero_grad()

        # feedforward
        output = net(img)
        loss = criterian(output, label)
        # backward
        loss.backward()
        opt.step()

        # 记录当前的lost以及batchSize数据对应的分类准确数量
        running_loss += loss.data[0]
        _, predict = torch.max(output, 1)
        correct_num = (predict == label).sum()
        running_acc += correct_num.data[0]

    # 计算并打印训练的分类准确率
    running_loss /= len(trainset)
    running_acc /= len(trainset)

    print("[%d/%d] Loss: %.5f, Acc: %.2f" %(i+1, epoches, running_loss, 100*running_acc))

# 将当前模型设置到测试模式
net.eval()

# 测试过程
testloss = 0.
testacc = 0.
for (img, label) in testloader:
    # 转换为Variable类型
    img = Variable(img)
    label = Variable(label)

    # feedforward
    output = net(img)
    loss = criterian(output, label)

    # 记录当前的lost以及累加分类正确的样本数
    testloss += loss.data[0]
    _, predict = torch.max(output, 1)
    num_correct = (predict == label).sum()
    testacc += num_correct.data[0]

# 计算并打印测试集的分类准确率
testloss /= len(testset)
testacc /= len(testset)
print("Test: Loss: %.5f, Acc: %.2f %%" %(testloss, 100*testacc))
           

继续阅读