Pytorch实现CNN_含验证集

一、CNN的结构

输入层---->[卷积层*N---->池化层]*M---->全连接层

在这里插入图片描述

二、卷积、池化和训练

卷积运算过程:
以为5* 5的image和 3* 3的filter,stride=1,Relu为激活函数,为例。
在这里插入图片描述
feature_map中第一个元素的计算公式:
在这里插入图片描述
feature_map[0][0]=Relu(1 * 1+1 * 0+1 * 1+0 * 0+1* 1+1 * 0+0 * 1+0 * 0+1 * 1)
=Relu(4)
= 4
feature_map[0][1]=Relu(1 * 1+ 1 * 0+ 0 * 1 + 1 * 0 + 1 * 1+ 1 * 0 + 0 * 1 + 1 * 0 +1* 1) =Relu(3)
= 3
以此类推可以得到frature_map的矩阵。

和全连接神经网络不同,有局部连接和权值共享的思想:每层神经元只和上一层部分神经元相连,且filter的权值对于上一层所有神经元都是一样的。

池化的过程
Pooling层主要的作用是下采样,进一步减少参数数量。
以常用的Max Pooling为例(也可以用Mean Pooling 取平均值)。以2*2的filter,以stride=1进行扫描,取扫描区域中的最大值作样本值。
在这里插入图片描述
全连接层
其实就是在池化层后面加了全连接神经网络。
这里需要将池化层的输出展平,作为全连接层的输入。

卷积层的训练
训练算法依然是反向传播算法。

池化层误差传递
在卷积神经网络的训练中,Pooling层需要做的仅仅是将误差项传递到上一层,而没有梯度的计算。下一层的误差项的值会原封不动的传递到上一层对应区块中的最大值所对应的神经元,而其他神经元的误差项的值都是0。
(对于mean pooling,下一层的误差项的值会平均分配到上一层对应区块中的所有神经元)

二、用CNN实现 Mnist手写数据集分类

整体流程:
卷积( Conv2d ) -> 激励函数( ReLU ) -> 池化, 向下采样 ( MaxPooling ) -> 再来一遍 -> 展平多维的卷积成的特征图 -> 接入全连接层 ( Linear ) -> 输出。
我用工具NN-SVG,画了下面这张图:
在这里插入图片描述
超参数 Hyper Parameters

EPOCH = 1  # 训练次数
BATCH_SIZE = 50 # 每一批训练的数据量
LR = 0.001  # 学习率 learning_rate
DOWNLOAD_MNIST = False   

Mnist数据集
我们从mnist中下载黑白的手写数字图片。它是 1 * 28 * 28
(若是RGB图片,为 3* 28 *28)
TIP:像素的值需要从区间[0,255] 压缩到 [0,1]。

train_data = torchvision.datasets.MNIST(
    root='./mnist/',  # 保存的位置
           train = True,  # 训练集
                   transform = torchvision.transforms.ToTensor(),  # 将(o,255)的像素值压缩到 (0,1)
                               download = DOWNLOAD_MNIST,  # 下载完成后 DOWNLOAD_MNIST=Flase
)

CNN模型

class CNN(nn.Module):
    def __init__(self):
        super(CNN,self).__init__()
        self.covn1=nn.Sequential(
            nn.Conv2d(           # (1,28,28)
                in_channels=1,      #黑白为1 RGB为3
                out_channels=16,    #filter的个数
                kernel_size=5,      #filter为 5*5
                stride=1,           #步长为1
                padding=2,          # padding=(kernal_size-stride)/2=(5-1)/2
            ),                   # ->(16,28,28)
            nn.ReLU(),           # ->(16,28,28)
            nn.MaxPool2d(kernel_size=2),   #2*2的filter扫描选择最大值   ->(16,14,14)
        )
        self.conv2=nn.Sequential(   #(16,14,14)
            nn.Conv2d(16,32,5,1,2),   # ->(32,14,14)
            nn.ReLU(),                # ->(32,14,14)
            nn.MaxPool2d(2)           # ->(32,7,7)
        )
        self.out=nn.Linear(32*7*7,10)   # 全连接神经网络将它展平  0~9共有十个分类

    def forward(self,x):
        x=self.covn1(x)
        x=self.conv2(x)      #(batch,32,7,7)
        x=x.view(x.size(0),-1)   #(batch,32*7*7)
        output=self.out(x)
        return output
print(cnn)

在这里插入图片描述
训练
将 y 都用 Variable 包起来, 然后放入 cnn 中计算 output, 最后再计算误差.

optimizer=torch.optim.Adam(cnn.parameters(),lr=LR)
loss_func=nn.CrossEntropyLoss()

#traning and testing
for epoch in range(EPOCH):
    for step,(x,y) in enumerate(train_loader):
        b_x=Variable(x)
        b_y=Variable(y)

        output=cnn(b_x)
        loss=loss_func(output,b_y)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

训练完成后选择十个数字进行测试

test_output=cnn(test_x[:10])
pred_y=torch.max(test_output,1)[1].data.numpy().squeeze()
print(pred_y, 'prediction number')
print(test_y[:10].numpy(), 'real number')

最后老师让修改代码,分清楚了测试集、验证集、训练集。
总共2000张照片,前1600作为测试集,200张验证,200张测试。
所谓验证集就是在训练过程中进行准确率的测试。
注意这里图片是随机的,我用random.shuffle()函数来实现。

import torch
import torch.nn as nn
from torch.autograd import Variable
import torch.utils.data as Data
import torchvision
from sklearn.utils import shuffle
import random

# torch.manual_seed(1)
# Hyper Parameters
EPOCH = 2  # 迭代次数
BATCH_SIZE = 50   # 批数据大小
LR = 0.001  # 学习率 learning_rate
DOWNLOAD_MNIST = False

# Mnist 手写数字
train_data = torchvision.datasets.MNIST(
    root='./mnist/',  # 保存的位置
           train = True,  # 训练集
                   transform = torchvision.transforms.ToTensor(),  # 将图片转化为tensor的形式,且/255归一化到[0,1.0]之间
                               download = DOWNLOAD_MNIST,  # 下载完成后 DOWNLOAD_MNIST=Flase
)

#设置测试集
test_data = torchvision.datasets.MNIST(root='./mnist/', train = False)

# 训练数据加载器:批训练 50samples, 1 channel, 28x28 ,(50, 1, 28, 28),
train_loader = Data.DataLoader(dataset=train_data, batch_size=BATCH_SIZE, shuffle=True)

test_x = Variable(torch.unsqueeze(test_data.test_data, dim=1), volatile=True).type(torch.FloatTensor)[
         :2000] / 255.  # shape from (2000, 28, 28) to (2000, 1, 28, 28), value in range(0,1)
test_y = test_data.test_labels[:2000]

class CNN(nn.Module):
    def __init__(self):
        super(CNN,self).__init__()
        self.covn1=nn.Sequential(
            nn.Conv2d(           # (1,28,28)
                in_channels=1,      #灰度图像为1 RGB为3
                out_channels=16,    #filter有16个,输出的维度为16
                kernel_size=5,      #filter为 5*5
                stride=1,           #步长为1
                padding=2,          # padding=(kernal_size-stride)/2=(5-1)/2 卷积出来图像尺寸不变
            ),                   # ->(16,28,28)
            nn.ReLU(),           # ->(16,28,28)
            nn.MaxPool2d(kernel_size=2),   #2*2的filter扫描选择最大值  ->(16,14,14)
        )
        self.conv2=nn.Sequential(   #(16,14,14)
            nn.Conv2d(16,32,5,1,2),   # ->(32,14,14)
            nn.ReLU(),                # ->(32,14,14)
            nn.MaxPool2d(2)           # ->(32,7,7)
        )
        self.out=nn.Linear(32*7*7,10)   # flatten ;0~9共有10个分类

    def forward(self,x):
        x=self.covn1(x)
        x=self.conv2(x)
        x=x.view(x.size(0),-1)   #降维
        output=self.out(x)
        return output

cnn=CNN()
print(cnn)
#定义优化器

optimizer=torch.optim.Adam(cnn.parameters(),lr=LR)
#定义损失函数
loss_func=nn.CrossEntropyLoss()

#traning
for epoch in range(EPOCH):
    random.seed(epoch)
    random.shuffle(train_data.train_data)
    random.seed(epoch)
    random.shuffle(train_data.train_labels)
    train_loader = Data.DataLoader(dataset=train_data, batch_size=BATCH_SIZE, shuffle=True)
    random.seed(epoch)
    random.shuffle(test_data.test_data)
    random.seed(epoch)
    random.shuffle(test_data.test_labels)
    test_x = Variable(torch.unsqueeze(test_data.test_data, dim=1), volatile=True).type(torch.FloatTensor)[
            :2000] / 255.  # shape from (2000, 28, 28) to (2000, 1, 28, 28), value in range(0,1)
    test_y = test_data.test_labels[:2000]
    for step,(x,y) in enumerate(train_loader):
        b_x=Variable(x)
        b_y=Variable(y)

        output=cnn(b_x)
        loss=loss_func(output,b_y)
        optimizer.zero_grad()  #把梯度置零,也就是把loss关于weight的导数变成0
        loss.backward()    # caculate gradient
        optimizer.step()   # apply gradient
        if step==1600:
            break
    validation_output = cnn(test_x[1600:1800])
    val_y = torch.max(validation_output, 1)[1].data.numpy().squeeze()
    print('validation :', val_y)
    print('real number:', test_y[1600:1800].numpy())
    b=0
    for i in range(200):
        if val_y[i] == test_y[1600:1800].numpy()[i]:
            b += 1
    val_accuracy = float(b / 200)
    print('| val accuracy: %.2f' % val_accuracy)

test_output=cnn(test_x[1800:2000])
pred_y=torch.max(test_output,1)[1].data.numpy().squeeze()
print('prediction :',pred_y)
print('real number:',test_y[1800:2000].numpy())
a=0
for i in range(200):
    if pred_y[i]==test_y[1800:2000].numpy()[i]:
        a+=1
accuracy=float(a/200)
print('| test accuracy: %.2f' % accuracy)
  • 4
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值