paddle 学习笔记

2021-04-09  本文已影响0人  半大人

paddlepaddle安装教程:
https://www.paddlepaddle.org.cn/documentation/docs/zh/install/index_cn.html

Tensor概念介绍 https://www.paddlepaddle.org.cn/documentation/docs/zh/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.html

1.1 Tensor

import paddle
#创建tensor
t = paddle.to_tensor([1,2,3,4,5,6],dtype="float64")
print(t)
#转换成numpy.array
arr = t.numpy()

#一些tensor的api
m=2
n=3
start = 1
end = 10
step = 2
num = 10
z = paddle.zeros([m, n])             # 创建数据全为0,shape为[m, n]的Tensor
o = paddle.ones([m, n])              # 创建数据全为1,shape为[m, n]的Tensor
f = paddle.full([m, n], 10)          # 创建数据全为10,shape为[m, n]的Tensor
a = paddle.arange(start, end, step)  # 创建从start到end,步长为step的Tensor
l = paddle.linspace(start, end, num) # 创建从start到end,元素个数固定为num的Tensor
print(z)
print(o)
print(f)
print(a)
print(l)

#对shape的操作
tensor = paddle.to_tensor([[[1, 2, 3, 4, 5],
                                   [6, 7, 8, 9, 10]],
                                  [[11, 12, 13, 14, 15],
                                   [16, 17, 18, 19, 20]],
                                  [[21, 22, 23, 24, 25],
                                   [26, 27, 28, 29, 30]]])
print("tensor shape:",tensor.shape)
#reshape
reshape_tensor = paddle.reshape(tensor,[2,5,3])
print("reshape_tensor shape:",reshape_tensor.shape)
#一些trick
print(paddle.reshape(tensor,[-1,1]))
print(paddle.reshape(tensor,[-1]))

#改变tensor的dtype
re_dtype = paddle.cast(tensor,dtype="float32")
print("re_dtype dtype:",re_dtype.dtype)
print("tensor dtype:",tensor.dtype)

#tensor 的place
#创建CPU上的Tensor:
cpu_tensor = paddle.to_tensor(1, place=paddle.CPUPlace())
print(cpu_tensor)
#创建GPU上的Tensor:
gpu_tensor = paddle.to_tensor(1, place=paddle.CUDAPlace(0))
print(gpu_tensor)

#切片的一些trick
#1D
tensor1D = paddle.reshape(tensor,[-1])
print("Interval of 3:", tensor1D[::3].numpy())
print("Reverse:", tensor1D[::-1].numpy())


#数学运算符
'''
x.abs()                       #逐元素取绝对值
x.ceil()                      #逐元素向上取整
x.floor()                     #逐元素向下取整
x.round()                     #逐元素四舍五入
x.exp()                       #逐元素计算自然常数为底的指数
x.log()                       #逐元素计算x的自然对数
x.reciprocal()                #逐元素求倒数
x.square()                    #逐元素计算平方
x.sqrt()                      #逐元素计算平方根
x.sin()                       #逐元素计算正弦
x.cos()                       #逐元素计算余弦
x.add(y)                      #逐元素相加
x.subtract(y)                 #逐元素相减
x.multiply(y)                 #逐元素相乘
x.divide(y)                   #逐元素相除
x.mod(y)                      #逐元素相除并取余
x.pow(y)                      #逐元素幂运算
x.max()                       #指定维度上元素最大值,默认为全部维度
x.min()                       #指定维度上元素最小值,默认为全部维度
x.prod()                      #指定维度上元素累乘,默认为全部维度
x.sum()                       #指定维度上元素的和,默认为全部维度
'''

#逻辑运算符
'''
x.isfinite()                  #判断tensor中元素是否是有限的数字,即不包括inf与nan
x.equal_all(y)                #判断两个tensor的全部元素是否相等,并返回shape为[1]的bool Tensor
x.equal(y)                    #判断两个tensor的每个元素是否相等,并返回shape相同的bool Tensor
x.not_equal(y)                #判断两个tensor的每个元素是否不相等
x.less_than(y)                #判断tensor x的元素是否小于tensor y的对应元素
x.less_equal(y)               #判断tensor x的元素是否小于或等于tensor y的对应元素
x.greater_than(y)             #判断tensor x的元素是否大于tensor y的对应元素
x.greater_equal(y)            #判断tensor x的元素是否大于或等于tensor y的对应元素
x.allclose(y)                 #判断tensor x的全部元素是否与tensor y的全部元素接近,并返回shape为[1]的bool Tensor
'''

#对bool型Tensor
'''
x.logical_and(y)              #对两个bool型tensor逐元素进行逻辑与操作
x.logical_or(y)               #对两个bool型tensor逐元素进行逻辑或操作
x.logical_xor(y)              #对两个bool型tensor逐元素进行逻辑亦或操作
x.logical_not(y)              #对两个bool型tensor逐元素进行逻辑非操作
'''

#线性代数相关
'''
x.cholesky()                  #矩阵的cholesky分解
x.t()                         #矩阵转置
x.transpose([1, 0])           #交换axis 0 与axis 1的顺序
x.norm('fro')                 #矩阵的Frobenius 范数
x.dist(y, p=2)                #矩阵(x-y)的2范数
x.matmul(y)                   #矩阵乘法
'''

#关于tensor跟多的api
#参考:https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/creation/Tensor_cn.html

1.2 tensor广播运算机制

飞桨的广播机制主要遵循如下规则(参考 Numpy 广播机制 ):

  1. 每个张量至少为一维张量
  2. 从后往前比较张量的形状,当前维度的大小要么相等,要么其中一个等于一,要么其中一个不存在
    A (4d array): ------ 8 x 1 x 6 x 1
    B (3d array): ---------- 7 x 1 x 5
    Result (4d array): 8 x 7 x 6 x 5
    两个张量进行广播语义后的结果张量的形状计算规则如下:
    如果两个张量的形状的长度不一致,那么需要在较小形状长度的矩阵向前添加1,直到两个张量的形状长度相等。
    保证两个张量形状相等之后,每个维度上的结果维度就是当前维度上较大的那个。

1.3 paddle自动微分机制

import paddle
from paddle.vision.models import vgg11
import paddle.nn.functional as F
import numpy as np
#创建前向计算模型
model = vgg11()
#数据准备
x = paddle.rand([1,3,224,224])
label = paddle.randint(0,1000)
#前向传播
predicts = model(x)
#计算损失
loss = F.cross_entropy(predicts,label)
#开始反向传播,计算梯度
loss.backward()
#设置优化器
optim = paddle.optimizer.Adam(learning_rate=0.001, parameters=model.parameters())
#更新参数
optim.step()

# 将b设置为需要计算梯度的属性,两种方式
b = paddle.to_tensor([1.0, 2.0, 3.0], stop_gradient=False) 
b.stop_gradient = False

2 模型开发

2.1 自带数据集

import paddle
print('视觉相关数据集:', paddle.vision.datasets.__all__)
print('自然语言相关数据集:', paddle.text.datasets.__all__)
from paddle.vision.transforms import ToTensor
#加载数据集
# 训练数据集 用ToTensor将数据格式转为Tensor
train_dataset = paddle.vision.datasets.MNIST(mode='train', transform=ToTensor())
# 验证数据集
val_dataset = paddle.vision.datasets.MNIST(mode='test', transform=ToTensor())

2.2 自定义数据集,个人感觉是一个生成器

通过上述的方法,你就定义了一个数据迭代器train_loader, 用于加载训练数据。通过batch_size=64设置了数据集的批大小为64,通过shuffle=True,在取数据前会打乱数据。此外,你还可以通过设置num_workers来开启多进程数据加载,提升加载速度。

DataLoader 默认用异步加载数据的方式来读取数据,一方面可以提升数据加载的速度,另一方面也会占据更少的内存。如果你需要同时加载全部数据到内存中,请设置use_buffer_reader=False。

import paddle
from paddle.io import Dataset
BATCH_SIZE = 64
BATCH_NUM = 20
IMAGE_SIZE = (28, 28)
CLASS_NUM = 10
class MyDataset(Dataset):
    """
    步骤一:继承paddle.io.Dataset类
    """
    def __init__(self, num_samples):
        """
        步骤二:实现构造函数,定义数据集大小
        """
        super(MyDataset, self).__init__()
        self.num_samples = num_samples

    def __getitem__(self, index):
        """
        步骤三:实现__getitem__方法,定义指定index时如何获取数据,并返回单条数据(训练数据,对应的标签)
        """
        data = paddle.uniform(IMAGE_SIZE, dtype='float32')
        label = paddle.randint(0, CLASS_NUM-1, dtype='int64')
        print(index)
        return data, label

    def __len__(self):
        """
        步骤四:实现__len__方法,返回数据集总数目
        """
        return self.num_samples
# 测试定义的数据集
custom_dataset = MyDataset(BATCH_SIZE * BATCH_NUM)
print('=============custom dataset=============')
for data, label in custom_dataset:
    print(data.shape, label.shape)
    break

#paddle.io.DataLoader完成数据的加载
train_loader = paddle.io.DataLoader(custom_dataset, batch_size=BATCH_SIZE, shuffle=True)
# 如果要加载内置数据集,将 custom_dataset 换为 train_dataset 即可
for batch_id, data in enumerate(train_loader()):
    x_data = data[0]
    y_data = data[1]
    print(x_data.shape)
    print(y_data.shape)
    break

2.2 飞桨框架内置图像数据集的预处理

你可以同构以下方式随机调整图像的亮度、对比度、饱和度,并调整图像的大小,对图像的其他调整

import paddle
print('数据处理方法:', paddle.vision.transforms.__all__)

#对图片数据集进行预处理
from paddle.vision.transforms import Compose, Resize, ColorJitter
# 定义想要使用的数据增强方式,这里包括随机调整亮度、对比度和饱和度,改变图片大小
transform = Compose([ColorJitter(), Resize(size=32)])
# 通过transform参数传递定义好的数据增强方法即可完成对自带数据集的增强
train_dataset = paddle.vision.datasets.MNIST(mode='train', transform=transform)

在自定义数据集中完成自定义数据增强

import paddle
from paddle.io import Dataset
from paddle.vision.transforms import Compose, Resize

BATCH_SIZE = 64
BATCH_NUM = 20

IMAGE_SIZE = (28, 28)
CLASS_NUM = 10

class MyDataset(Dataset):
    def __init__(self, num_samples):
        super(MyDataset, self).__init__()
        self.num_samples = num_samples
        # 在 `__init__` 中定义数据增强方法,此处为调整图像大小
        self.transform = Compose([Resize(size=32)])

    def __getitem__(self, index):
        data = paddle.uniform(IMAGE_SIZE, dtype='float32')
        # 在 `__getitem__` 中对数据集使用数据增强方法
        data = self.transform(data.numpy())

        label = paddle.randint(0, CLASS_NUM-1, dtype='int64')

        return data, label

    def __len__(self):
        return self.num_samples

# 测试定义的数据集
custom_dataset = MyDataset(BATCH_SIZE * BATCH_NUM)

print('=============custom dataset=============')
for data, label in custom_dataset:
    print(data.shape, label.shape)
    break

2.3模型组网

paddle.nn api大全

Sequential 组网

import paddle
# Sequential形式组网
mnist = paddle.nn.Sequential(
    paddle.nn.Flatten(),
    paddle.nn.Linear(784, 512),
    paddle.nn.ReLU(),
    paddle.nn.Dropout(0.2),
    paddle.nn.Linear(512, 10)
)

SubClass 组网

import paddle
# Layer类继承方式组网
class Mnist(paddle.nn.Layer):
    def __init__(self):
        super(Mnist, self).__init__()
        self.flatten = paddle.nn.Flatten()
        self.linear_1 = paddle.nn.Linear(784, 512)
        self.linear_2 = paddle.nn.Linear(512, 10)
        self.relu = paddle.nn.ReLU()
        self.dropout = paddle.nn.Dropout(0.2)

    def forward(self, inputs):
        y = self.flatten(inputs)
        y = self.linear_1(y)
        y = self.relu(y)
        y = self.dropout(y)
        y = self.linear_2(y)
        return y
mnist_2 = Mnist()

内置网络

import paddle
print('飞桨框架内置模型:', paddle.vision.models.__all__)
lenet = paddle.vision.models.LeNet()

paddle.summary(lenet, (64, 1, 28, 28))

2.4 训练与预测

飞桨框架提供了两种训练与预测的方法,一种是用paddle.Model对模型进行封装,通过高层API如Model.fit()、Model.evaluate()、Model.predict()等完成模型的训练与预测;另一种就是基于基础API常规的训练方式。

import paddle
from paddle.vision.transforms import ToTensor

# 加载数据集
train_dataset = paddle.vision.datasets.MNIST(mode='train', transform=ToTensor())
test_dataset = paddle.vision.datasets.MNIST(mode='test', transform=ToTensor())
# 定义网络结构
mnist = paddle.nn.Sequential(
    paddle.nn.Flatten(1, -1),
    paddle.nn.Linear(784, 512),
    paddle.nn.ReLU(),
    paddle.nn.Dropout(0.2),
    paddle.nn.Linear(512, 10)
)

#使用paddle.Model对模型进行封装,进行训练与预测
model= paddle.Model(mnist)
#设置,优化器,训练参数,损失函数,评估值
model.prepare(optimizer=paddle.optimizer.Adam(parameters=model.parameters()),loss=paddle.nn.CrossEntropyLoss(),metrics=paddle.metric.Accuracy())
#使用数据集训练模型
model.fit(train_dataset,epochs=1,batch_size=64,verbose=1)
#使用测试集合对模型进行验证
eval_result = model.evaluate(test_dataset,verbose=1)
print(eval_result)
#使用model.predict进行预测
test_result = model.predict(test_dataset)



#基础API训练模型
train_loader = paddle.io.DataLoader(train_dataset,batch_size=64,shuffle=True)
#设置参数为可训练(猜测)
mnist.train()
#迭代次数
epochs = 1
#设置优化器
optim = paddle.optimizer.Adam(parameters=mnist.parameters())
#设置损失函数
loss_fn = paddle.nn.CrossEntropyLoss()
#开始训练
for epoch in range(epochs):
    for batch_id,data in enumerate(train_loader()):
        x_data = data[0] #数据
        y_data = data[1]  #标签
        predicts = mnist(x_data)  #预测结果
        #计算损失
        loss = loss_fn(predicts,y_data)
        #反向传播
        loss.backward()
        # 计算准确率 等价于 prepare 中metrics的设置
        acc = paddle.metric.accuracy(predicts, y_data)
        #输出训练信息
        if (bach_id+1)%900 == 0:
            print("\repoch: {}, batch_id: {}, loss is: {}, acc is: {}".format(epoch, batch_id+1, loss.numpy(), acc.numpy()))
        #更新参数
        optim.step()
        #梯度清零
        optim.clear_grad()


#拆解Model.evaluate()-- 用基础API验证模型
# 加载测试数据集
test_loader = paddle.io.DataLoader(test_dataset, batch_size=64, drop_last=True)
loss_fn = paddle.nn.CrossEntropyLoss()
mnist.eval()
for batch_id, data in enumerate(test_loader()):
    x_data = data[0]            # 测试数据
    y_data = data[1]            # 测试数据标签
    predicts = mnist(x_data)    # 预测结果
    # 计算损失与精度
    loss = loss_fn(predicts, y_data)
    acc = paddle.metric.accuracy(predicts, y_data)
    # 打印信息
    if (batch_id+1) % 30 == 0:
        print("batch_id: {}, loss is: {}, acc is: {}".format(batch_id+1, loss.numpy(), acc.numpy()))

### 拆解Model.predict()-- 用基础API测试模型
# 加载测试数据集
test_loader = paddle.io.DataLoader(test_dataset, batch_size=64, drop_last=True)
mnist.eval()
for batch_id, data in enumerate(test_loader()):
    x_data = data[0]
    predicts = mnist(x_data)
    # 获取预测结果
print("predict finished")

资源配置

就是,指定那几个GPU在训练时,使用,详细参考下面链接
https://www.paddlepaddle.org.cn/documentation/docs/zh/guides/02_paddle2.0_develop/06_device_cn.html

自定义loss

class SelfDefineLoss(paddle.nn.Layer):
   """
   1. 继承paddle.nn.Layer
   """
   def __init__(self):
       """
       2. 构造函数根据自己的实际算法需求和使用需求进行参数定义即可
       """
       super(SelfDefineLoss, self).__init__()

   def forward(self, input, label):
       """
       3. 实现forward函数,forward在调用时会传递两个参数:input和label
           - input:单个或批次训练数据经过模型前向计算输出结果
           - label:单个或批次训练数据对应的标签数据
           接口返回值是一个Tensor,根据自定义的逻辑加和或计算均值后的损失
       """
       # 使用Paddle中相关API自定义的计算逻辑
       # output = xxxxx
       # return output




class SelfDefineMetric(paddle.metric.Metric):
    """
    1. 继承paddle.metric.Metric
    """
    def __init__(self):
        """
        2. 构造函数实现,自定义参数即可
        """
        super(SelfDefineMetric, self).__init__()

    def name(self):
        """
        3. 实现name方法,返回定义的评估指标名字
        """
        return '自定义评价指标的名字'

    def compute(self, ...)
        """
        4. 本步骤可以省略,实现compute方法,这个方法主要用于`update`的加速,可以在这个方法中调用一些paddle实现好的Tensor计算API,编译到模型网络中一起使用低层C++ OP计算。
        """
        return 自己想要返回的数据,会做为update的参数传入。

    def update(self, ...):
        """
        5. 实现update方法,用于单个batch训练时进行评估指标计算。
        - 当`compute`类函数未实现时,会将模型的计算输出和标签数据的展平作为`update`的参数传入。
        - 当`compute`类函数做了实现时,会将compute的返回结果作为`update`的参数传入。
        """
        return acc value

    def accumulate(self):
        """
        6. 实现accumulate方法,返回历史batch训练积累后计算得到的评价指标值。
        每次`update`调用时进行数据积累,`accumulate`计算时对积累的所有数据进行计算并返回。
        结算结果会在`fit`接口的训练日志中呈现。
        """
        # 利用update中积累的成员变量数据进行计算后返回
        return accumulated acc value

    def reset(self):
        """
        7. 实现reset方法,每个Epoch结束后进行评估指标的重置,这样下个Epoch可以重新进行计算。
        """
        # do reset action

自定义Metric

from paddle.metric import Metric

class Precision(Metric):
    """
    Precision (also called positive predictive value) is the fraction of
    relevant instances among the retrieved instances. Refer to
    https://en.wikipedia.org/wiki/Evaluation_of_binary_classifiers
    Noted that this class manages the precision score only for binary
    classification task.

    ......
    """

    def __init__(self, name='precision', *args, **kwargs):
        super(Precision, self).__init__(*args, **kwargs)
        self.tp = 0  # true positive
        self.fp = 0  # false positive
        self._name = name

    def update(self, preds, labels):
        """
        Update the states based on the current mini-batch prediction results.
        Args:
            preds (numpy.ndarray): The prediction result, usually the output
               of two-class sigmoid function. It should be a vector (column
               vector or row vector) with data type: 'float64' or 'float32'.
           labels (numpy.ndarray): The ground truth (labels),
               the shape should keep the same as preds.
               The data type is 'int32' or 'int64'.
        """
        if isinstance(preds, paddle.Tensor):
            preds = preds.numpy()
        elif not _is_numpy_(preds):
            raise ValueError("The 'preds' must be a numpy ndarray or Tensor.")
        if isinstance(labels, paddle.Tensor):
            labels = labels.numpy()
        elif not _is_numpy_(labels):
            raise ValueError("The 'labels' must be a numpy ndarray or Tensor.")

        sample_num = labels.shape[0]
        preds = np.floor(preds + 0.5).astype("int32")

        for i in range(sample_num):
            pred = preds[i]
            label = labels[i]
            if pred == 1:
                if pred == label:
                    self.tp += 1
                else:
                    self.fp += 1

    def reset(self):
        """
        Resets all of the metric state.
        """
        self.tp = 0
        self.fp = 0

    def accumulate(self):
        """
        Calculate the final precision.

        Returns:
           A scaler float: results of the calculated precision.
        """
        ap = self.tp + self.fp
        return float(self.tp) / ap if ap != 0 else .0

    def name(self):
        """
        Returns metric name
        """
        return self._name

自定义 callback

fit接口的callback参数支持传入一个Callback类实例,用来在每轮训练和每个batch训练前后进行调用,可以通过callback收集到训练过程中的一些数据和参数,或者实现一些自定义操作。

class SelfDefineCallback(paddle.callbacks.Callback):
    """
    1. 继承paddle.callbacks.Callback
    2. 按照自己的需求实现以下类成员方法:
        def on_train_begin(self, logs=None)                 训练开始前,`Model.fit`接口中调用
        def on_train_end(self, logs=None)                   训练结束后,`Model.fit`接口中调用
        def on_eval_begin(self, logs=None)                  评估开始前,`Model.evaluate`接口调用
        def on_eval_end(self, logs=None)                    评估结束后,`Model.evaluate`接口调用
        def on_predict_begin(self, logs=None)               预测测试开始前,`Model.predict`接口中调用
        def on_predict_end(self, logs=None)                 预测测试结束后,`Model.predict`接口中调用
        def on_epoch_begin(self, epoch, logs=None)          每轮训练开始前,`Model.fit`接口中调用
        def on_epoch_end(self, epoch, logs=None)            每轮训练结束后,`Model.fit`接口中调用
        def on_train_batch_begin(self, step, logs=None)     单个Batch训练开始前,`Model.fit`和`Model.train_batch`接口中调用
        def on_train_batch_end(self, step, logs=None)       单个Batch训练结束后,`Model.fit`和`Model.train_batch`接口中调用
        def on_eval_batch_begin(self, step, logs=None)      单个Batch评估开始前,`Model.evalute`和`Model.eval_batch`接口中调用
        def on_eval_batch_end(self, step, logs=None)        单个Batch评估结束后,`Model.evalute`和`Model.eval_batch`接口中调用
        def on_predict_batch_begin(self, step, logs=None)   单个Batch预测测试开始前,`Model.predict`和`Model.test_batch`接口中调用
        def on_predict_batch_end(self, step, logs=None)     单个Batch预测测试结束后,`Model.predict`和`Model.test_batch`接口中调用
    """
    def __init__(self):
        super(SelfDefineCallback, self).__init__()
    # 按照需求定义自己的类成员方法


#一个实际例子
class ModelCheckpoint(Callback):
    def __init__(self, save_freq=1, save_dir=None):
        self.save_freq = save_freq
        self.save_dir = save_dir

    def on_epoch_begin(self, epoch=None, logs=None):
        self.epoch = epoch

    def _is_save(self):
        return self.model and self.save_dir and ParallelEnv().local_rank == 0

    def on_epoch_end(self, epoch, logs=None):
        if self._is_save() and self.epoch % self.save_freq == 0:
            path = '{}/{}'.format(self.save_dir, epoch)
            print('save checkpoint at {}'.format(os.path.abspath(path)))
            self.model.save(path)

    def on_train_end(self, logs=None):
        if self._is_save():
            path = '{}/final'.format(self.save_dir)
            print('save checkpoint at {}'.format(os.path.abspath(path)))
            self.model.save(path)

参数存储载入(训练调优)

# save
paddle.save(layer.state_dict(), "linear_net.pdparams")
paddle.save(adam.state_dict(), "adam.pdopt")

# load
layer_state_dict = paddle.load("linear_net.pdparams")
opt_state_dict = paddle.load("adam.pdopt")
layer.set_state_dict(layer_state_dict)
adam.set_state_dict(opt_state_dict)

模型&参数存储载入(训练部署)

# save
path = "example.model/linear"
paddle.jit.save(layer, path)

# load
path = "example.model/linear"
loaded_layer = paddle.jit.load(path)
#使用加载模型进行预测
loaded_layer.eval()
x = paddle.randn([1, IMAGE_SIZE], 'float32')
pred = loaded_layer(x)

#此外, paddle.jit.save 同时保存了模型和参数,如果你只需要从存储结果中载入模型的参数,可以使用 paddle.load 接口载入,返回所存储模型的state_dict,示例如下:
# load
path = "example.model/linear"
state_dict = paddle.load(path)
# inference
layer.set_state_dict(state_dict, use_structured_name=False)
layer.eval()


VisualDL 工具

上一篇下一篇

猜你喜欢

热点阅读