PaddlePaddle
paddle安装
- 从官方获取安装命令
飞桨PaddlePaddle-源于产业实践的开源深度学习平台
根据自己的芯片型号获得适当的安装命令。可以采用nvidia-smi
来查看
- 创建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
当创建张量时,您可以根据不同的需求和情况使用以下几种方法:
- 创建指定数据的张量:
- 使用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)
- 创建指定形状的张量:
- 创建全零张量:
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)
- 创建指定区间的张量:
- 创建指定区间和步长的张量:
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)
- 创建指定对象的张量:
- 从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具有以下属性:
- 形状(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])
- 重置 Tensor 形状(Reshape):
- 改变Tensor的形状可以使用
paddle.reshape
。 - 在指定新的形状时可以使用
-1
或者0
的技巧。
示例代码:
x = paddle.to_tensor([1, 2, 3])
y = paddle.reshape(x, [1, 3])
- 数据类型(dtype):
- Tensor的数据类型dtype可以是
bool
、float16
、float32
、float64
等。 - 所有元素的数据类型在同一个Tensor中是相同的。
- 设备位置(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())
- 名称(name):
- Tensor还有一个名称属性
name
,用于标识该Tensor。
tensor_name = paddle.to_tensor([1, 2, 3], name="my_tensor").name
3、Tensor 的操作
在飞桨(PaddlePaddle)中,您可以通过索引和切片来访问或修改Tensor,以下是关于Tensor操作的详细介绍:
- 索引和切片
通过索引或切片方式可以访问或修改Tensor。在飞桨中,遵循标准的Python索引规则与Numpy索引规则。
- 索引从0开始,如果下标为负数,则从尾部开始计算。
- 利用冒号
:
分隔切片参数,start:stop:step
来进行切片操作,其中start、stop、step均可缺省。
print(x[:, 1:])
- 赋值与修改
请小心使用索引或切片修改Tensor,在原地修改该Tensor的数值,原值不会被保存。若修改后的Tensor参与梯度计算,仅会使用修改后的数值。
x[1, 0] = 5
- 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]])
- 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.datasets 和 paddle.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()
返回一个可迭代对象,每次迭代返回一个批次的数据。在循环内部,您将每个批次的数据解包成images
和labels
,然后打印出当前批次的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’]
Compose
:将多个转换函数组合成一个序列,按顺序依次对数据进行转换。RandomHorizontalFlip
:以一定的概率随机水平翻转输入图像,可用于数据增强。RandomVerticalFlip
:以一定的概率随机垂直翻转输入图像,也是数据增强的一种方式。Resize
:调整输入图像的大小,可以设置目标尺寸或调整比例。RandomResizedCrop
:随机裁剪并调整大小,常用于数据增强和训练时的输入处理。ColorJitter
:随机颜色抖动,用于增加数据多样性。Normalize
:标准化图像数据,减去均值并除以标准差,通常用于输入数据预处理。
这些转换函数可以按需组合使用,以实现对图像数据的预处理和增强。通过使用适当的转换函数,您可以提高训练模型的数据多样性、模型的泛化能力和性能。
要使用 PaddlePaddle 中的图像处理和数据增强的转换函数,通常需要按照以下步骤进行:
- 导入必要的库和模块:
import paddle
from paddle.vision.transforms import Compose, RandomHorizontalFlip, Resize, Normalize
- 创建转换函数序列(Compose):
transform = Compose([
RandomHorizontalFlip(), # 随机水平翻转
Resize(size=256), # 调整大小为256x256
Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) # 标准化
])
- 应用转换函数到数据集:
假设您有一个数据集dataset
,您可以按如下方式应用转换函数:
dataset_transformed = dataset.transform(transform)
- 创建数据加载器(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 方式的组网中。
构建一些比较复杂的网络结构时,可以选择该方式,组网包括三个步骤:
- 创建一个继承自
paddle.nn.Layer
的类; - 在类的构造函数
__init__
中定义组网用到的神经网络层(layer); - 在类的前向计算函数
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)