LOADING

加载过慢请开启缓存 浏览器默认开启

PaddlePaddle 1

PaddlePaddle

paddle安装

  • 从官方获取安装命令

飞桨PaddlePaddle-源于产业实践的开源深度学习平台

根据自己的芯片型号获得适当的安装命令。可以采用nvidia-smi来查看

cuda 安装教程

  • 创建conda环境
conda create -n paddle_env python=YOUR_PY_VER
activate paddle_env

对于国内用户无法连接到 Anaconda 官方源的可以按照以下命令添加清华源:

conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
conda config --set show_channel_urls yes

Tensor

1、Tensor的创建

可使用 paddle.to_tensor 创建任意维度的 Tensor

当创建张量时,您可以根据不同的需求和情况使用以下几种方法:

  1. 创建指定数据的张量:
  • 使用Python列表创建张量:
import paddle

data = [1, 2, 3, 4, 5]
tensor_data = paddle.to_tensor(data)
print(tensor_data)
  • 使用浮点数数组创建张量:
import numpy as np
import paddle

data_float = np.array([1.0, 2.0, 3.0, 4.0, 5.0])
tensor_data_float = paddle.to_tensor(data_float)
print(tensor_data_float)
  1. 创建指定形状的张量:
  • 创建全零张量:
shape = [2, 3]  # 2行3列的张量
tensor_zeros = paddle.zeros(shape)
print(tensor_zeros)
  • 创建全一张量:
shape = [3, 2]  # 3行2列的张量
tensor_ones = paddle.ones(shape)
print(tensor_ones)
  1. 创建指定区间的张量:
  • 创建指定区间和步长的张量:
tensor_range = paddle.arange(start = 1, end = 10, step = 2)
print(tensor_range)
  • 创建均匀间隔的张量:
tensor_linspace = paddle.linspace(start = 1, stop = 5, num = 5)
print(tensor_linspace)
  1. 创建指定对象的张量:
  • 从NumPy数组创建张量:
numpy_array = np.array([1, 2, 3])
tensor_from_numpy = paddle.to_tensor(numpy_array)
print(tensor_from_numpy)
  • 从Python列表创建张量:
import paddle

data_list = [3, 6, 9, 12]
tensor_from_list = paddle.to_tensor(data_list)
print(tensor_from_list)

2、Tensor 的属性

在PaddlePaddle中,Tensor具有以下属性:

  1. 形状(shape):
  • shape: 描述了Tensor在每个维度上的元素数量。
  • ndim: Tensor的维度数量,如向量的维度为1,矩阵的维度为2,Tensor可以有任意数量的维度。
  • axis或者dimension: Tensor的轴,即某个特定的维度。
  • size: Tensor中所有元素的个数。

示例代码:

import paddle

ndim_4_Tensor = paddle.ones([2, 3, 4, 5])

print("每个元素的数据类型:", ndim_4_Tensor.dtype)
print("维度数量:", ndim_4_Tensor.ndim)
print("Tensor的形状:", ndim_4_Tensor.shape)
print("第0个维度上的元素数量:", ndim_4_Tensor.shape[0])
print("最后一个维度上的元素数量:", ndim_4_Tensor.shape[-1])
  1. 重置 Tensor 形状(Reshape):
  • 改变Tensor的形状可以使用paddle.reshape
  • 在指定新的形状时可以使用-1或者0的技巧。

示例代码:

x = paddle.to_tensor([1, 2, 3])
y = paddle.reshape(x, [1, 3])
  1. 数据类型(dtype):
  • Tensor的数据类型dtype可以是boolfloat16float32float64等。
  • 所有元素的数据类型在同一个Tensor中是相同的。
  1. 设备位置(place):
  • 可以指定Tensor位于CPU、GPU或者固定内存上。
  • 可以通过paddle.CPUPlace()paddle.CUDAPlace(0)paddle.CUDAPinnedPlace()创建相应位置的Tensor。
cpu_tensor = paddle.to_tensor([1, 2, 3], place=paddle.CPUPlace())
gpu_tensor = paddle.to_tensor([1, 2, 3], place=paddle.CUDAPlace(0))
pin_memory_tensor = paddle.to_tensor([1, 2, 3], place=paddle.CUDAPinnedPlace())
  1. 名称(name):
  • Tensor还有一个名称属性name,用于标识该Tensor。
tensor_name = paddle.to_tensor([1, 2, 3], name="my_tensor").name

3、Tensor 的操作

在飞桨(PaddlePaddle)中,您可以通过索引和切片来访问或修改Tensor,以下是关于Tensor操作的详细介绍:

  1. 索引和切片

通过索引或切片方式可以访问或修改Tensor。在飞桨中,遵循标准的Python索引规则与Numpy索引规则。

  • 索引从0开始,如果下标为负数,则从尾部开始计算。
  • 利用冒号:分隔切片参数,start:stop:step来进行切片操作,其中start、stop、step均可缺省。
print(x[:, 1:])
  1. 赋值与修改

请小心使用索引或切片修改Tensor,在原地修改该Tensor的数值,原值不会被保存。若修改后的Tensor参与梯度计算,仅会使用修改后的数值。

x[1, 0] = 5
  1. Tensor广播

飞桨中Tensor的广播机制遵循Numpy的广播规则:

  • 每个Tensor至少为一维Tensor。
  • 从最后一个维度向前比较两个Tensor的形状,满足以下条件可以进行广播:两个Tensor的维度大小相等、其中一个Tensor的维度等于1、其中一个Tensor的维度不存在。
import paddle

# 创建形状为(1, 2)和(2, 2)的两个Tensor
x = paddle.to_tensor([[1, 2]])
y = paddle.to_tensor([[3, 4], [5, 6]])

# 进行广播操作
result = x + y
print(result)  # 输出: Tensor(shape=[2, 2], dtype=int32, place=CUDAPlace(0), stop_gradient=True, value=[[4, 6], [6, 8]])
  1. Tensor运算

飞桨中的Tensor支持多种逐元素运算,如绝对值、取整、指数运算、求倒数、平方、正弦、余弦、相加、相减、相乘、相除等操作。此外,还可以判断元素是否有限、元素是否相等、元素大小关系等。

import paddle

# 创建形状为(2, 2)的Tensor
x = paddle.full(shape=[2, 2], fill_value=2)
y = paddle.full(shape=[2, 2], fill_value=3)

# 逐元素相加
result_add = paddle.add(x, y)
print(result_add)  # 输出: Tensor(shape=[2, 2], dtype=int32, place=CUDAPlace(0), stop_gradient=True, value=[[5, 5], [5, 5]])

# 判断两个Tensor的全部元素是否相等
result_equal_all = x.equal_all(y)
print(result_equal_all)  # 输出: Tensor(shape=[1], dtype=bool, place=CUDAPlace(0), stop_gradient=True, value=[False])

数据集定义与加载

1、定义数据集

在PaddlePaddle中定义数据集通常有两种方式:直接加载内置数据集和自定义数据集。

1.1 直接加载内置数据集

飞桨框架在 paddle.vision.datasetspaddle.text 目录下内置了一些经典数据集可直接调用.

PaddlePaddle提供了内置数据集,你可以直接调用这些数据集。举例来说,以MNIST数据集为例,你可以这样加载内置数据集:

from paddle.vision.datasets import MNIST
from paddle.vision.transforms import Normalize

transform = Normalize(mean=[127.5], std=[127.5], data_format='CHW')
train_dataset = MNIST(mode='train', transform=transform)
test_dataset = MNIST(mode='test', transform=transform)

print('train images: ', len(train_dataset), ', test images: ', len(test_dataset))

1.2 使用paddle.io.Dataset自定义数据集

如果你有自己的数据,可以通过继承paddle.io.Dataset类来实现自定义数据集。你需要完成以下步骤:

import os
import cv2
import numpy as np
from paddle.io import Dataset
from paddle.vision.transforms import Normalize

class MyDataset(Dataset):
    def __init__(self, data_dir, label_path, transform=None):
        super().__init__()
        self.data_list = []
        with open(label_path, encoding='utf-8') as f:
            for line in f.readlines():
                image_path, label = line.strip().split('\t')
                image_path = os.path.join(data_dir, image_path)
                self.data_list.append([image_path, label])
        self.transform = transform

    def __getitem__(self, index):
        image_path, label = self.data_list[index]
        image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
        image = image.astype('float32')
        if self.transform is not None:
            image = self.transform(image)
        label = int(label)
        return image, label

    def __len__(self):
        return len(self.data_list)

transform = Normalize(mean=[127.5], std=[127.5], data_format='CHW')

train_custom_dataset = MyDataset('mnist/train', 'mnist/train/label.txt', transform)
test_custom_dataset = MyDataset('mnist/val', 'mnist/val/label.txt', transform)

print('train_custom_dataset images: ', len(train_custom_dataset), ', test_custom_dataset images: ', len(test_custom_dataset))

2、迭代读取数据集

在飞桨(PaddlePaddle)框架中,使用paddle.io.DataLoader来定义数据读取器是一种推荐的方式,它可以实现对数据集的多进程读取,同时自动进行批次划分的工作。下面我将详细解释您提供的代码示例中关于迭代处理数据的部分:

2.1 使用paddle.io.DataLoader定义数据读取器

在这个部分,您首先定义并初始化了一个数据读取器train_loader,用于加载训练数据集train_custom_dataset。在初始化数据读取器时,您设置了一些常用的字段:

  • batch_size:每批次读取的样本数,这里设置为64,表示每次读���64个样本。
  • shuffle:指定是否打乱样本顺序,设置为True表示在取数据时打乱样本顺序,以降低过拟合风险。
  • drop_last:是否丢弃不完整的批次样本,设置为True表示丢弃因数据集样本数不能被batch_size整除而产生的最后一个不完整的batch。
  • num_workers:用于设置加载数据的子进程个数,设置为大于0时开启多进程异步加载数据,可提升数据读取速度。

接着,您使用enumerate(train_loader())来迭代读取数据,train_loader()返回一个可迭代对象,每次迭代返回一个批次的数据。在循环内部,您将每个批次的数据解包成imageslabels,然后打印出当前批次的batch_id、训练数据的形状和标签数据的形状。

from paddle.io import DataLoader, Dataset  
import paddle  

# 假设 train_custom_dataset 已经被定义并实例化  
# train_custom_dataset = MyCustomDataset(...)  

# 使用 DataLoader 加载数据  
train_loader = paddle.io.DataLoader(  
    train_custom_dataset,  
    batch_size=64,  
    shuffle=True,  
    num_workers=1,  
    drop_last=True  
)  

# 迭代读取数据  
for batch_id, (images, labels) in enumerate(train_loader):  
    print("batch_id: {}, 训练数据shape: {}, 标签数据shape: {}".format(batch_id, images.shape, labels.shape))

2.2 自定义采样器

在飞桨框架中,提供了多种采样器来实现更灵活的数据加载方式。这里演示了几种不同的采样器的用法:

  • 批采样器BatchSampler:在示例中定义了一个批采样器bs,设置了采样数据集源、采样批大小、是否乱序等参数,然后将其传入paddle.io.DataLoader中的batch_sampler参数来获取采样数据。
  • 顺序采样器SequenceSampler和随机采样器RandomSampler:展示了如何使用这两种采样器并打印采样结果。
  • 分布式批采样器DistributedBatchSampler:用于分布式训练情况下的批量采样,示例中展示了分布式采样器的使用方法。
from paddle.io import Sampler  

class CustomSampler(Sampler):  
    def __init__(self, data_source, indices):  
        self.data_source = data_source  
        self.indices = indices  

    def __iter__(self):  
        return iter(self.indices)  

    def __len__(self):  
        return len(self.indices)  

# 假设 indices 是您自定义的索引列表  
indices = [i for i in range(len(train_custom_dataset))]  # 示例:简单地使用所有索引  

# 使用自定义采样器  
custom_sampler = CustomSampler(train_custom_dataset, indices)  
train_loader_with_custom_sampler = paddle.io.DataLoader(  
    train_custom_dataset,  
    batch_size=64,  
    sampler=custom_sampler,  
    num_workers=1  
)  

# 迭代读取数据(使用自定义采样器)  
for batch_id, (images, labels) in enumerate(train_loader_with_custom_sampler):  
    print("batch_id: {}, 训练数据shape: {}, 标签数据shape: {}".format(batch_id, images.shape, labels.shape))

Transforms 数据预处

1、paddle.vision.transforms 介绍

飞桨框架在 paddle.vision.transforms 下内置了数十种图像数据处理方法,可以通过以下代码查看

paddle.vision.transforms.__all__

在 PaddlePaddle 中,paddle.vision.transforms 模块提供了一系列用于图像处理和数据增强的转换函数,这些函数可以帮助您对数据进行预处理、增强和转换,以供神经网络训练或推断使用。下面是一些常用的转换函数:

[‘BaseTransform’, ‘Compose’, ‘Resize’, ‘RandomResizedCrop’, ‘CenterCrop’, ‘RandomHorizontalFlip’, ‘RandomVerticalFlip’, ‘Transpose’, ‘Normalize’, ‘BrightnessTransform’, ‘SaturationTransform’, ‘ContrastTransform’, ‘HueTransform’, ‘ColorJitter’, ‘RandomCrop’, ‘Pad’, ‘RandomRotation’, ‘Grayscale’, ‘ToTensor’, ‘to_tensor’, ‘hflip’, ‘vflip’, ‘resize’, ‘pad’, ‘rotate’, ‘to_grayscale’, ‘crop’, ‘center_crop’, ‘adjust_brightness’, ‘adjust_contrast’, ‘adjust_hue’, ‘normalize’]

  1. Compose:将多个转换函数组合成一个序列,按顺序依次对数据进行转换。
  2. RandomHorizontalFlip:以一定的概率随机水平翻转输入图像,可用于数据增强。
  3. RandomVerticalFlip:以一定的概率随机垂直翻转输入图像,也是数据增强的一种方式。
  4. Resize:调整输入图像的大小,可以设置目标尺寸或调整比例。
  5. RandomResizedCrop:随机裁剪并调整大小,常用于数据增强和训练时的输入处理。
  6. ColorJitter:随机颜色抖动,用于增加数据多样性。
  7. Normalize:标准化图像数据,减去均值并除以标准差,通常用于输入数据预处理。

这些转换函数可以按需组合使用,以实现对图像数据的预处理和增强。通过使用适当的转换函数,您可以提高训练模型的数据多样性、模型的泛化能力和性能。

要使用 PaddlePaddle 中的图像处理和数据增强的转换函数,通常需要按照以下步骤进行:

  1. 导入必要的库和模块:
import paddle
from paddle.vision.transforms import Compose, RandomHorizontalFlip, Resize, Normalize
  1. 创建转换函数序列(Compose):
transform = Compose([
    RandomHorizontalFlip(),  # 随机水平翻转
    Resize(size=256),  # 调整大小为256x256
    Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])  # 标准化
])
  1. 应用转换函数到数据集:
    假设您有一个数据集 dataset,您可以按如下方式应用转换函数:
dataset_transformed = dataset.transform(transform)
  1. 创建数据加载器(DataLoader)并迭代数据:
    最后,您可以使用创建好的转换后的数据集来初始化 DataLoader,并迭代读取数据用于模型的训练或推断:
data_loader = paddle.io.DataLoader(dataset_transformed, batch_size=32, shuffle=True)
for data in data_loader:
    # 在这里处理每个批次的数据,例如输入模型进行训练

2、在数据集中应用数据预处理操作

  • 在框架内置数据集中应用

train_dataset = paddle.vision.datasets.MNIST(mode='train', transform=transform)
  • 在自定义的数据集中应用

对于自定义的数据集,可以在数据集中将定义好的数据处理方法传入 __init__ 函数,将其定义为自定义数据集类的一个属性,然后在 __getitem__ 中将其应用到图像上,如下述代码所示:

import os
import cv2
import numpy as np
from paddle.io import Dataset
class MyDataset(Dataset):
    """
    步骤一:继承 paddle.io.Dataset 类
    """
    def __init__(self, data_dir, label_path, transform=None):
        """
        步骤二:实现 __init__ 函数,初始化数据集,将样本和标签映射到列表中
        """
        super().__init__()
        self.data_list = []
        with open(label_path,encoding='utf-8') as f:
            for line in f.readlines():
                image_path, label = line.strip().split('\t')
                image_path = os.path.join(data_dir, image_path)
                self.data_list.append([image_path, label])
        # 2. 传入定义好的数据处理方法,作为自定义数据集类的一个属性
        self.transform = transform
    def __getitem__(self, index):
        """
        步骤三:实现 __getitem__ 函数,定义指定 index 时如何获取数据,并返回单条数据(样本数据、对应的标签)
        """
        image_path, label = self.data_list[index]
        image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
        image = image.astype('float32')

        # 3. 应用数据处理方法到图像上
        if self.transform is not None:
            image = self.transform(image)
        label = int(label)
        return image, label

    def __len__(self):
        """
        步骤四:实现 __len__ 函数,返回数据集的样本总数
        """
        return len(self.data_list)

# 1. 定义随机旋转和改变图片大小的数据处理方法
transform = Compose([RandomRotation(10), Resize(size=32)])

custom_dataset = MyDataset('mnist/train','mnist/train/label.txt', transform)

模型组网

本节介绍了如何在飞桨框架中使用内置的LeNet模型以及Paddle.nn模块的不同方式构建神经网络,包括Sequential方式和SubclassLayer方式,以满足不同复杂度的网络需求。

1、直接使用内置模型

飞桨框架目前在 paddle.vision.models 下内置了计算机视觉领域的一些经典模型,只需一行代码即可完成网络构建和初始化,适合完成一些简单的深度学习任务,满足深度学习初阶用户感受模型的输入和输出形式、了解模型的性能。

paddle.vision.models.__all__

以 LeNet 模型为例,可通过如下代码组网:

# 模型组网并初始化网络
lenet = paddle.vision.models.LeNet(num_classes=10)
# 可视化模型组网结构和参数
paddle.summary(lenet,(1, 1, 28, 28))
---------------------------------------------------------------------------
 Layer (type)       Input Shape          Output Shape         Param #    
===========================================================================
   Conv2D-1       [[1, 1, 28, 28]]      [1, 6, 28, 28]          60       
    ReLU-1        [[1, 6, 28, 28]]      [1, 6, 28, 28]           0       
  MaxPool2D-1     [[1, 6, 28, 28]]      [1, 6, 14, 14]           0       
   Conv2D-2       [[1, 6, 14, 14]]     [1, 16, 10, 10]         2,416     
    ReLU-2       [[1, 16, 10, 10]]     [1, 16, 10, 10]           0       
  MaxPool2D-2    [[1, 16, 10, 10]]      [1, 16, 5, 5]            0       
   Linear-1          [[1, 400]]            [1, 120]           48,120     
   Linear-2          [[1, 120]]            [1, 84]            10,164     
   Linear-3          [[1, 84]]             [1, 10]              850      
===========================================================================
Total params: 61,610
Trainable params: 61,610
Non-trainable params: 0
---------------------------------------------------------------------------
Input size (MB): 0.00
Forward/backward pass size (MB): 0.11
Params size (MB): 0.24
Estimated Total Size (MB): 0.35
---------------------------------------------------------------------------


{'total_params': 61610, 'trainable_params': 61610}

2、Paddle.nn

  • 使用 paddle.nn.Sequential 组网:构建顺序的线性网络结构(如 LeNet、AlexNet 和 VGG)时,可以选择该方式。相比于 Layer 方式 ,Sequential 方式可以用更少的代码完成线性网络的构建。
from paddle import nn
# 使用 paddle.nn.Sequential 构建 LeNet 模型
lenet_Sequential = nn.Sequential(
    nn.Conv2D(1, 6, 3, stride=1, padding=1),
    nn.ReLU(),
    nn.MaxPool2D(2, 2),
    nn.Conv2D(6, 16, 5, stride=1, padding=0),
    nn.ReLU(),
    nn.MaxPool2D(2, 2),
    nn.Flatten(),
    nn.Linear(400, 120),
    nn.Linear(120, 84), 
    nn.Linear(84, 10)
)

# 可视化模型组网结构和参数
paddle.summary(lenet_Sequential,(1, 1, 28, 28))
  • 使用 paddle.nn.Layer 组网(推荐):构建一些比较复杂的网络结构时,可以选择该方式。相比于 Sequential 方式,Layer 方式可以更灵活地组建各种网络结构。Sequential 方式搭建的网络也可以作为子网加入 Layer 方式的组网中。

构建一些比较复杂的网络结构时,可以选择该方式,组网包括三个步骤:

  1. 创建一个继承自 paddle.nn.Layer的类;
  2. 在类的构造函数 __init__ 中定义组网用到的神经网络层(layer);
  3. 在类的前向计算函数 forward 中使用定义好的 layer 执行前向计算。

仍然以 LeNet 模型为例,使用 paddle.nn.Layer 组网的代码如下:

# 使用 Subclass 方式构建 LeNet 模型

class LeNet(nn.Layer):
    def __init__(self, num_classes=10):
        super().__init__()
        self.num_classes = num_classes
        # 构建 features 子网,用于对输入图像进行特征提取
        self.features = nn.Sequential(
            nn.Conv2D(1, 6, 3, stride=1, padding=1),
            nn.ReLU(),
            nn.MaxPool2D(2, 2),
            nn.Conv2D(6, 16, 5, stride=1, padding=0),
            nn.ReLU(),
            nn.MaxPool2D(2, 2))
        # 构建 linear 子网,用于分类

        if num_classes > 0:
            self.linear = nn.Sequential(
                nn.Linear(400, 120),
                nn.Linear(120, 84), 
                nn.Linear(84, num_classes)
            )

    # 执行前向计算
    def forward(self, inputs):
        x = self.features(inputs)
        if self.num_classes > 0:
            x = paddle.flatten(x, 1)
            x = self.linear(x)
        return x 

lenet_SubClass = LeNet()
# 可视化模型组网结构和参数
params_info = paddle.summary(lenet_SubClass,(1, 1, 28, 28))
print(params_info)