paddle学习笔记一

前言

本篇文章以手写数字识别为例,快速上手paddle。
文章内容包含:

  • 从paddle中加载mnist数据集
  • 用class快速搭建模型
  • 选择优化器,加入正则化项
  • 训练过程中打印训练集的损失,验证集的损失及准确率,early_stop
  • 模型的保存与加载
  • 将训练损失和测试损失作图

文中API的使用可以查询官网:https://www.paddlepaddle.org.cn/documentation/docs/zh/api_cn/index_cn.html

导入模块

import paddle
from paddle import fluid
import numpy as np 
import matplotlib.pyplot as plt

MNIST数据集加载

#从paddle中加载数据集
train_set = paddle.dataset.mnist.train() #返回训练数据的reader creator(generator)
test_set  = paddle.dataset.mnist.test() #测试用数据

#通过下面几行代码可以看到训练集中有60000条数据
#测试集中有10000条数据
#data 为一个tuple,data[0]为一张被reshape成了(28*28,)维的图片,data[1]为其对应的标签
#data[0]的值在[-1, 1],是经过归一化处理后的
i = 0
for data in test_set():
    i += 1
print(i)
print(type(data))
print(type(data[0]))
print(data[0].shape)

#训练样本乱序、生成批次数据
train_set = fluid.io.shuffle(train_set, 60000)
train_reader = fluid.io.batch(train_set, 100)
#train_reader() 为生成器
for data in train_reader():
    print(data[0]) #请自行检查data
    break
print(type(data))
print(len(data))

定义模型结构

用定义类的方式定义模型
这里先随便定义个卷积网络
此部分代码中用到的API可从这里查询:
https://www.paddlepaddle.org.cn/documentation/docs/zh/api_cn/dygraph_cn.html

class MNIST(fluid.dygraph.Layer):
    def __init__(self):
        super(MNIST, self).__init__()
        #定义一个卷积层
        self.conv1 = fluid.dygraph.Conv2D(num_channels=1, num_filters=8, filter_size=3, stride=1, padding=1)
        #再定义一个卷积层
        self.conv2 = fluid.dygraph.Conv2D(num_channels=8, num_filters=16, filter_size=3, stride=1, padding=1)
        #定义个池化层
        self.pool = fluid.dygraph.Pool2D(pool_size=2, pool_stride=2, pool_type='max')
        #定义两个全连接层
        self.fc1 = fluid.dygraph.Linear(input_dim=784, output_dim=100, act='relu')
        self.fc2 = fluid.dygraph.Linear(input_dim=100, output_dim=10)
    # 定义网络的前向计算过程
    def forward(self, inputs, label=None):
        #将输入的shape=(batch_size, 28*28)reshape成正确的形状
        x = fluid.layers.reshape(inputs, [-1, 1, 28, 28]) #paddle 中为 channel_first
        x = self.conv1(x)
        #在卷积之后加入BN层,之后接relu激活函数
        x = fluid.dygraph.BatchNorm(8, act='relu')(x)
        x = self.pool(x)
        x = self.conv2(x)
        x = fluid.dygraph.BatchNorm(16, act='relu')(x)
        x = self.pool(x)
        x = fluid.layers.reshape(x, [-1, 784])
        x = self.fc1(x)
        x = fluid.dygraph.Dropout(p=0.5)(x)
        x = self.fc2(x) #这里没有加入softmax
        return x

开始训练

NUM_EPOCH = 50   #训练总次数
batch_sz = 100   #批次大小
init_lr = 0.001  #初始学习率
patience = 3     #early——stop
use_gpu = True   #是否使用GPU
params_path = "./model_data/mnist"  #定义保存路径
place = fluid.CPUPlace() if not use_gpu else fluid.CUDAPlace(0)
with fluid.dygraph.guard(place=place):
#    params_dict, opt_dict = fluid.load_dygraph(params_path)
    model = MNIST() 
#    model.load_dict(params_dict)
    #尝试不同的优化器。 前边的数字是在测试集上的最好的准确率,后面是epoch
    #同一个优化器每次训练在测试集上的表现略有不同
    opt = fluid.optimizer.SGDOptimizer(learning_rate=init_lr, 
          parameter_list=model.parameters())  #0.929  34
    # opt = fluid.optimizer.MomentumOptimizer(learning_rate=init_lr, momentum=0.9, 
         # parameter_list=model.parameters()) #0.974 23
    # opt = fluid.optimizer.AdagradOptimizer(learning_rate=init_lr, 
         # parameter_list=model.parameters())  #0.953 38
    # opt = fluid.optimizer.RMSPropOptimizer(learning_rate=init_lr, 
         # parameter_list=model.parameters())   #0.983 10
    # opt = fluid.optimizer.AdamOptimizer(learning_rate=init_lr, 
         # parameter_list=model.parameters())   #0.982 9

#正则化
    #飞桨支持为所有参数加上统一的正则化项,也支持为特定的参数添加正则化项。前者的实现如下代码所示,
    #仅在优化器中设置regularization参数即可实现。使用参数regularization_coeff调节正则化项的权重,
    #权重越大时,对模型复杂度的惩罚越高。
    opt = fluid.optimizer.AdamOptimizer(learning_rate=init_lr, 
                                        #regularization=fluid.regularizer.L2Decay(regularization_coeff=0.1),
                                        parameter_list=model.parameters())
    #total_steps = (int(60000//BATCH_SIZE) + 1) * EPOCH_NUM
    #lr = fluid.dygraph.PolynomialDecay(0.01, total_steps, 0.001) 学习率多项式衰减
    #可将init_lr 替换为lr从而加入 学习率多项式衰减
#    optimizer.set_dict(opt_dict)
    
    train_loss = [] #将训练集每个batch的loss放在这里
    test_loss = []  #将测试的的loss放在这里
    test_acc = []   #测试集上的准去率
    best_acc = 0    #early_stop用到的

    for epoch in range(1, NUM_EPOCH+1):

        model.train() # 训练模式 和 测试模式 中模型部分算子的表现不相同
        train_set = fluid.io.shuffle(train_set, 60000)  #随机打乱数据
        for batch_id, batch in enumerate(fluid.io.batch(train_set, batch_size=batch_sz)()):
            image_data = np.array([x[0] for x in batch], dtype='float32')
            label_data = np.array([x[1] for x in batch], dtype='int').reshape(len(image_data), 1)
            # 转换成paddle需要的格式
            image = fluid.dygraph.to_variable(image_data)
            label = fluid.dygraph.to_variable(label_data)

            preds = model(image) #执行一次模型的前向传播
            loss = fluid.layers.softmax_with_cross_entropy(preds, label) #softmax --> 计算损失
            avg_loss = fluid.layers.mean(loss)
            #每过一定次数将结果打印出来
            if batch_id % 100 == 0:
            #f-string 格式化字符串以 f 开头,后面跟着字符串,字符串中的表达式用大括号 {} 包起来,它会将变量或表达式计算后的值替换进去
                print(f'epoch: {epoch:4d} | batch_id = {batch_id:4d} | batch_loss = {avg_loss.numpy()[0]:4.3f}')
                train_loss.append(avg_loss.numpy()[0])
            # 反向传播,优化参数
            avg_loss.backward()
            opt.minimize(avg_loss)
            model.clear_gradients()

        model.eval() #测试模式
        acc = []
        avg_loss = []
        for batch_id, batch in enumerate(fluid.io.batch(test_set, batch_size=batch_sz)()):
            image_data = np.array([x[0] for x in batch], dtype='float32')
            label_data = np.array([x[1] for x in batch], dtype='int').reshape(len(image_data), 1)
            
            image = fluid.dygraph.to_variable(image_data)
            label = fluid.dygraph.to_variable(label_data)
            preds = model(image)
            loss = fluid.layers.softmax_with_cross_entropy(preds, label)
            avg_loss.append(fluid.layers.mean(loss).numpy())         #所有batch存起来求平均
            acc.append(fluid.layers.accuracy(preds, label).numpy())  #所有batch存起来求平均
        #打印出测试集的loss 和 acc
        print(f'test_data loss: {np.array(avg_loss).mean()}, test_data acc: {np.array(acc).mean()}')
        test_acc.append(np.array(acc).mean())
        test_loss.append(np.array(avg_loss).mean())
        # 设定early_stop    当连续patience次best_acc没有更新则停止循环    
        if best_acc <= test_acc[-1]:
            best_acc = test_acc[-1]
            early_stop = 0

            #如果test——acc变好则保存模型
            # 保存模型参数和优化器的参数
            fluid.save_dygraph(model.state_dict(), './model_data/mnist_epoch{}'.format(epoch))
            fluid.save_dygraph(opt.state_dict(), './model_data/mnist_epoch{}'.format(epoch))

        else:
            early_stop += 1
            if early_stop == patience:
                print(f'early_stop at epoch = {epoch}')
                break
        print()

    print('Done!')
    
#将训练集和测试集的损失画出来
train_x = np.arange(1, len(train_loss)+1)
test_x = np.arange(6, len(train_loss)+1, 6)

plt.plot(train_x, train_loss, 'red')
plt.plot(test_x, test_loss, 'blue')

总结

paddle中dataset读出来的是generator,在数据量非常大的时候可以显著节省内存。

本文将paddle的多篇文章总结为一篇,方便日后的查阅。

感谢阅读!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值