Pytorch LeNet 2:手写体字符识别实现

Pytroch LeNet介绍章节,我们介绍了LeNet的各层网络结构的输入和输出,以及LeNet的前馈过程实现,本章节我们使用Mnist数据集,训练LeNet网络,进一步熟悉这个网络

Mnist数据集

数据官网地址

只介绍跟我们训练相关的数据
Mnist是手写字体,图片样式如下

数据包含四个文件:

train-images-idx3-ubyte: training set images #训练集文件
train-labels-idx1-ubyte: training set labels #训练接标注
t10k-images-idx3-ubyte:  test set images  #测试解文件
t10k-labels-idx1-ubyte:  test set labels  #测试解标注文件

训练集包含60000张样本数,测试集包含10000张样本数据

使用python读取训练接标注

训练集标注文件的格式为(train-labels-idx1-ubyte):

[offset] [type]          [value]          [description] 
0000     32 bit integer  0x00000801(2049) magic number (MSB first) 
0004     32 bit integer  60000            number of items 
0008     unsigned byte   ??               label 
0009     unsigned byte   ??               label 
........ 
xxxx     unsigned byte   ??               label
The labels values are 0 to 9.

读取代码:

    labels_path = os.path.join(path,'train-labels.idx1-ubyte')
    with open(labels_path, 'rb') as lbpath:
        #'>II' 大端模式(big-endian),
        magic, n = struct.unpack('>II',lbpath.read(8))#读取8个字节
        labels = np.fromfile(lbpath,dtype=np.uint8)
        print(magic,n,labels)

读取前8个字节,前四个字节是magic number[0x00000801(2049)];后四个字节存储标注文件数据[60000]

labels表示读取的60000张图片的标注结果

打印结果

2049 60000 [5 0 4 ... 5 6 8]

使用python读取训练集图片

训练集图片数据格式 (train-images-idx3-ubyte):


[offset] [type]          [value]          [description] 
0000     32 bit integer  0x00000803(2051) magic number 
0004     32 bit integer  60000            number of images 
0008     32 bit integer  28               number of rows 
0012     32 bit integer  28               number of columns 
0016     unsigned byte   ??               pixel 
0017     unsigned byte   ??               pixel 
........ 
xxxx     unsigned byte   ??               pixel

读取代码如下:

images_path = os.path.join(path,'train-images.idx3-ubyte')
with open(images_path, 'rb') as imgpath:
        #2051 60000 28 28
        magic, num, rows, cols = struct.unpack('>IIII', imgpath.read(16))
       images = np.fromfile(imgpath,dtype=np.uint8).reshape(len(labels), 28x28)

读取前16个字节,获取魔幻数字magic[2051],图片数目num[60000],图片大小[28,28]
然后读取图片字节数据,并转换成60000个28*28的图片数据,每行代表一张图片数据

日志打印结果

2051 60000 28 28
(60000, 784)

使用pytorch显示Mnist图片

def show(images,labels):
    fig, ax = plt.subplots(
        nrows=2,
        ncols=5,
        sharex=True,
        sharey=True, )
 
    ax = ax.flatten()
    for i in range(10):
         #根据i获取labels中与i相同的数据的位置,然后通过这个位置id获取对应的images图片数据
        img = images[labels == i][0].reshape(28, 28)
 
        ax[i].imshow(img, cmap='Greys', interpolation='nearest')
 
    ax[0].set_xticks([])
    ax[0].set_yticks([])
    plt.tight_layout()
    plt.show()

显示结果为:

Mnist

使用pytorch Lenet训练Mnist

pytorch加载Mnist和LeNet网络

torchvision,其中包含了针对Imagenet、CIFAR10、MNIST等常用数据集的数据加载器(data loaders),还有对图片数据变形的操作,即torchvision.datasetstorch.utils.data.DataLoader
我们使用trochvision加载Mnist,来进行网络的训练,关键方法如下

网络初始化
#初始化网络
def getLenet(self):

    #getdataload
    #use already define Lenet
    net = LeNet(self.dataImgChannel).to(self.device)

    loss_fuc = nn.CrossEntropyLoss() 
    optimizer = optim.Adam(net.parameters(),lr = 0.01, weight_decay = 0.005) 
    return net,loss_fuc,optimizer
数据加载
    #定义数据加载器
    def getDataLoader(self):
        #use gpu to load and train data

        #define transform:
        #1:resize MNIST data to 32x32, so adapter to LeNet struct
        #2:transform PIL.Image to  torch.FloatTensor
        resize = 32
        normalize = transforms.Normalize((0.5,0.5,0.5), (0.5,0.5,0.5))
        if self.dataImgChannel==1:
            normalize = transforms.Normalize((0.5), (0.5))
        elif self.dataImgChannel==3:
            normalize = transforms.Normalize((0.5,0.5,0.5), (0.5,0.5,0.5))

        transform = transforms.Compose(
            [transforms.Resize(size=(resize, resize)),
             transforms.ToTensor(),#将图片转换成(C,H, W)的Tensor格式,且/255归一化到[0,1.0]之间
             normalize])#对每个通道通过(image-mean)/std将数据转换到[-1,1]之间
        if dataType=='Mnist':
            train_data = torchvision.datasets.MNIST(root=self.dataRoot,    #data dir 
                                                    train=True,               #it is train data
                                                    transform=transform,      #use defined transform
                                                    download=True)            #use local data
            test_data = torchvision.datasets.MNIST(root=self.dataRoot,
                                                    train=False,
                                                    transform=transform,
                                                    download=True)
        elif dataType=='Cifar':
             train_data = torchvision.datasets.CIFAR10(root='./datas/cifar', train=True, download=True, transform=transform)
             test_data = torchvision.datasets.CIFAR10(root='./datas/cifar', train=False, download=True, transform=transform)
        else:
            print('dataType not in[Mnist,Cifar]')
            return

        #define DataLoder,and shuffle data
        train_loader = torch.utils.data.DataLoader(dataset = train_data, batch_size =1000 , shuffle = True)
        test_loader = torch.utils.data.DataLoader(dataset = test_data, batch_size = 1000, shuffle = False)


        return train_loader,test_loader

这里我们使用默认的数据数据集加载器,后续章节会分析怎么自定义数据加载器

代码解释:

  1. 定义transform,将MNIST图片尺寸从28x28 转换到 32x32,将数据格式转换成pytorch处理的Tensor;LeNet的分类器是全连接层,因此必须固定输入。也可以处理28x28的图片,需要将全连接层的输入改成对应的输入个数
  2. 定义数据加载器train_data,和test_data,根据你自己的电脑配置设置合适的batch_size, epochs和batch_size的关系请参考epochs对训练结果的影响
  3. 使用Pytorch Lenet-5 1:网络结构介绍中定义的网络,初始化网络结构,并送入到gpu
  4. 定义损失函数,使用交叉熵损失函数
  5. 定于优化器,使用Adam优化器
训练网络
    #开始训练
    def train_net(self,net,loss_fuc,optimizer):
        #getdataloader
        train_loader,test_loader= self.getDataLoader()
        #Star train
        
        adjust_lr_scheduler = lr_scheduler.StepLR(optimizer, step_size=self.step_size, gamma=0.1)#定义学习率衰减函数
        print('start train with epoch:',self.EPOCH)
        iteration = 0
        for epoch in range(self.EPOCH):
            sum_loss = 0
            #数据读取
            for i,data in enumerate(train_loader):
                inputs,labels = data
                #有GPU则将数据置入GPU加速

                inputs, labels = inputs.to(self.device), labels.to(self.device)   
                # 梯度清零
                optimizer.zero_grad()
                # 传递损失 + 更新参数
                output = net(inputs)
                loss = loss_fuc(output,labels)
                loss.backward()
                optimizer.step()
                iteration = iteration+1

                sum_loss += loss.item()
                if iteration%20==0:#每100次Iteration,测试一次测试数据
                    lr = optimizer.param_groups[0]["lr"]#get current lr
                    #print(lr)
                    print('###iteration[:%d],[Epoch:%d],[Lr:%.08f] train sum_loss: [%.03f] -> avg loss[%.03f]' % (iteration,epoch, lr, sum_loss, sum_loss / 20))
                    #用网络测试验证数据
                    #保存模型
                    sum_loss = 0.0
            adjust_lr_scheduler.step()#更新学习率
            self.test_net(self.device,test_loader,net)
            if epoch%30==0 or epoch==self.EPOCH:
                dummy_input = torch.randn(1, 1, 32, 32).to(self.device)   # 生成张量
                save_model(net, optimizer, epoch+1, 'lenet', 'mnist', dummy_input)


使用以上代码训练网络,并打印测试准确度

1: 每20次iteration,打印一次损失值;一次iteration会处理batch_size张图片
2: 每30次epoch,保存一次模型中间变量

打印训练结果:

start train with epoch: 100
###iteration[:100],[Epoch:1],[Lr:0.00100000] train loss: 1.003
avg accuracy:89%
....
###iteration[:2920],[Epoch:16],[Lr:0.00010000] train loss: 0.051
avg accuracy:98%


如上,训练在epoch 10左右开始收敛,因为图片特征比较明显,准去率较高

测试网络

训练过程中,调用了test_data(device,test_loader,net)测试网络,其具体实现如下:

    #测试验证数据
    def test_net(self,device,test_loader,net):
        correct = 0
        total = 0
        with torch.no_grad():
            for data in test_loader:
                test_inputs, labels = data
                test_inputs, labels = test_inputs.to(self.device), labels.to(self.device)
                #输出的是batch_size张图片的10个分类值
                outputs_test = net(test_inputs)
                #输出每个数据得分最高的那个分类id
                predict_value, predicted_label = torch.max(outputs_test.data, 1)  
                #统计test_loader中图片的总个数
                total += labels.size(0) 
                #统计test_loader中 正确分类的个数
                correct += (predicted_label == labels).sum()
        print('test data avg accuracy:%d%%' % ((100 * correct // total)))

测试输出:

test data avg accuracy:96%

源码

Pytorch/net/Lenet

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值