diff --git a/.gitignore b/.gitignore index 0d20b6487c61e7d1bde93acf4a14b7a89083a16d..784978471ee703ed47795cc619d4a800cb19226f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.pyc +*.DS_Store diff --git a/README.md b/README.md index adc5b2b9c87a146ea59bd48be3dae6c68c2ce391..140ea18a0cc029dc93c7ecc12f5c7f5f4e02c6ce 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,131 @@ -# PaddlePaddle大规模分类库PLSC +# PLSC: PaddlePaddle大规模分类库 ## 简介 -PaddlePaddle大规模分类库PLSC (PaddlePaddle Large Scale Classification)是基于[飞桨平台](https://www.paddlepaddle.org.cn)开发的超大规模分类库,为用户提供从训练到部署的全流程大规模分类应用解决方案。 +PaddlePaddle大规模分类库(PLSC: PaddlePaddle Large Scale Classification)是 +基于[飞桨平台](https://www.paddlepaddle.org.cn)构建的超大规模分类库,为用 +户提供从训练到部署的大规模分类问题全流程解决方案。 -PLSC具备以下特点: +深度学习中用于解决多分类问题的深度神经网络的最后一层通常是全连接层+Softmax, +并采用交叉熵(Cross-Entropy)算法计算网络的损失函数。由于全连接层的参数量随着 +分类类别数的增长线性增长,当分类类别数相当大时,会面临下面两个主要挑战: + +1. 参数量过大,超出单个GPU卡的显存容量:假设分类网络最后一层隐层的输出维度为512, +那么当分类类别数为一百万时,最后一层全连接层参数的大小约为2GB(假设以32比特浮点 +数表示参数)。当分类问题的类别数为一亿时(例如,对自然界中的生物进行分类),则 +最后一层全连接层参数的大小接近200GB,远远超过当前GPU的显存容量。 + +2. 参数量较大,同步训练方式下通信开销较大:数据并行训练方式下,所有GPU卡之间需 +要同步参数的梯度信息,以完成参数值的同步更新。当参数数量较大时,参数的梯度信息 +数据量同样较大,从而导致参数梯度信息的通信开销较大,影响训练速度。 + +为了解决大规模分类问题,我们设计开发了PaddlePaddle大规模分类库PLCS,为用户提供 +从训练到部署的大规模分类问题全流程解决方案。 + +## 设计思想 + +解决大规模分类问题的核心思想是采用模型并行方案实现深度神经网络模型的全连接层以 +及之后的损失值计算。 + +首先,我们回顾大规模分类问题面临的两个主要挑战: + +1. 参数量过大,超出单个GPU卡的显存容量 + +2. 参数量较大,同步训练方式下通信开销较大 + +### 显存优化 + +为了解决显存不足的问题,PLSC采用模型并行设计,将深度神经网络的最后一层全连接层切 +分到各个GPU卡。全连接层天然地具有可切分属性,无外乎是一个矩阵乘法和加法(存在偏置 +项的情形下)。假设以100张GPU卡进行模型训练,当分类类别数目为一亿时,每张GPU卡上的 +全连接参数的大小约为2GB,这完全是可接受的。 + +对于全连接层计算,可以表示为矩阵乘法和加法,如下面的公示所示: + +![FC计算公示](images/fc_computing.png) + +其中,*W*和*b*全连接层参数,*X*是神经网络最后一层隐层的输出。将根据矩阵分块原理,全 +连接层计算又可以进一步地表示为下面的形式: + +![FC计算公示展开](images/fc_computing_block.png) + +这里,*n*是分块的块数。因此,我们可以将神经网络的最后一层全连接参数分布到多张GPU卡, +并在每张卡上分别完成全连接层的部分计算,从而实现整个全连接层的计算,并解决大规模分 +类问题面临的GPU显存空间不足的问题。 + +需要注意的是,由于将神经网络模型最后一层全连接层参数划分到多张GPU卡,因此需要汇总 +各个GPU上的*X*参数,得到全连接层的全局输入*X*’(可以通过集合通信allgather实现),并 +计算全连接层输出: + +![全局FC计算公示](images/fc_computing_block_global.png) + +### 通信优化 + +为了得到多分类任务的损失值,在完成全连接层计算后,通常会使用Softmax+交叉熵操作。 + +softmax的计算公示如下图所示: + +![softmax计算公示](images/softmax_computing.png) + +由于softmax计算是基于全类别的logit值的,因此需要进行全局同步,以计算分母项。这需 +要执行*N*次AllGather操作,这里*N*是参与训练的GPU卡数。这种全局通信方式的开销较大。 + +为了减少通信和计算代价,PLSC实现中仅同步其中的分母项。由于各个GPU卡上分母项是一个 +标量,所以可以显著降低通信开销。 + +## PLSC的特征: - 基于源于产业实践的开源深度学习平台[飞桨平台](https://www.paddlepaddle.org.cn) -- 包含大量的预训练模型 (TBD) -- 提供从训练到部署的全流程解决方案 (TBD) + + 飞桨是由百度研发的一款源于产业实践的开源深度学习平台,致力于让深度学习技术的创 + 新与应用更简单。PLSC基于飞桨平台研发,实现与飞桨平台的无缝链接,可以更好地服务 + 产业实践。 + +- 包含多种预训练模型 + 除了PLSC库源码,我们还发布了基于ResNet50模型、ResNet101模型、ResNet152模型的大 + 规模分类模型在多种数据集上的预训练模型,方便用户基于这些预训练模型进行下游任务 + 的fine-tuning。 + +- 提供从训练到部署的全流程解决方案 + PLSC库功能包括数据预处理、模型训练、验证和在线预测服务,提供从训练到部署的大规 + 模分类问题全流程解决方案,用户可以基于PLSC库快速、便捷地搭建大规模分类问题解决 + 方案。 + +## 预训练模型和性能 + +### 预训练模型 + +我们提供了下面的预训练模型,以帮助用户对下游任务进行fine-tuning。 + +| 模型 | 描述 | +| :--------------- | :------------- | +| [resnet50_distarcface_ms1m_v2](http://icm.baidu-int.com/user-center/account) | 该模型使用ResNet50网络训练,数据集为MS1M_v2,训练阶段使用的loss_type为'dist_arcface',预训练模型在lfw验证集上的验证精度为0.99817。 | + +### 训练性能 + +| 模型 | 训练集 | lfw | agendb_30 | cfp_ff | cfp_fp | +| :--------------- | :------------- | :------ | :----- | :------ | :---- | +| ResNet50 | MS1M-ArcFace | 0.99817 | 0.99827 | 0.99857 | 0.96314 | +| ResNet50 | CASIA | 0.9895 | 0.9095 | 0.99057 | 0.915 | + +备注:上述模型训练使用的loss_type为'dist_arcface'。更多关于ArcFace的内容请 +参考[ArcFace: Additive Angular Margin Loss for Deep Face Recognition](https://arxiv.org/abs/1801.07698) ## 使用教程 我们提供了一系列使用教程,来帮助用户完成使用PLSC大规模分类库进行训练、评估和部署。 -这一系列文档分为__快速入门__、__基础功能__、__预测部署__和__高级功能__四个部分,由浅入深地介绍PLSC大规模分类库的设计思路和使用方法。 +这一系列文档分为**快速入门**、**基础功能**、**预测部署**和**高级功能**四个部分, +由浅入深地介绍PLSC大规模分类库的使用方法。 ### 快速入门 * [安装说明](docs/installation.md) -* [训练/评估/部署](docs/usage.md) +* [训练和验证](docs/usage.md) ### 基础功能 * [API简介](docs/api_intro.md) * [自定义模型](docs/custom_models.md) -* [自定义Reader接口] ### 预测部署 diff --git a/docs/api_intro.md b/docs/api_intro.md index ad55c93630817e8420362eb6a9b96058b5afa6a9..63a52d73301d20c289fa354f1bb126e50556a83d 100644 --- a/docs/api_intro.md +++ b/docs/api_intro.md @@ -2,7 +2,8 @@ ## 默认配置参数 -PLSC大规模分类库提供了默认配置参数,用于设置训练、评估和模型相关的信息,如训练数据集目录、训练轮数等。 +PLSC大规模分类库提供了默认配置参数,用于设置训练、评估和模型相关的信息,如训练数 +据集目录、训练轮数等。 这些参数信息位于plsc.config模块中,下面给出这些参数的含义和默认值。 @@ -64,19 +65,19 @@ PLSC大规模分类库提供了默认配置参数,用于设置训练、评估 | set_checkpoint_dir(dir) | 设置用于加载的预训练模型的目录 | 类型为字符串 | | set_warmup_epochs(num) | 设置warmup的轮数 | 类型为int | | set_loss_type(loss_type) | 设置模型的loss类型 | 类型为字符串 | -| set_image_size(size) | 设置图像尺寸,格式为CHW | 类型为元组 | +| set_image_shape(size) | 设置图像尺寸,格式为CHW | 类型为元组 | | set_optimizer(optimizer) | 设置训练阶段的optimizer | Optimizer类实例 | | convert_for_prediction() | 将预训练模型转换为预测模型 | None | -| predict() | 离线预测接口,用于验证线上模型的正确性 | None | | test() | 模型评估 | None | | train() | 模型训练 | None | -备注:上述API均为PaddlePaddle大规模分类库PLSC的plsc.entry.Entry类的方法,需要通过该类的实例调用,例如: +备注:上述API均为PaddlePaddle大规模分类库PLSC的Entry类的方法,需要通过该类的实例 +调用,例如: -```shell -import plsc.entry as entry +```python +from plsc import Entry -ins = entry.Entry() +ins = Entry() ins.set_class_num(85742) ins.train() ``` diff --git a/docs/base64_preprocessor.md b/docs/base64_preprocessor.md index 13eee49db0c32883779e2f03db4d343ef68f67f0..b23666f0ad1375718b35ff978fab623a07ad33b6 100644 --- a/docs/base64_preprocessor.md +++ b/docs/base64_preprocessor.md @@ -37,7 +37,12 @@ dataset.part10 ## 工具使用方法 -工具位于tools目录下。 +工具位于tools目录下。使用该工具时,需要安装sqlite3模块,可以通过下面的命令安装: + +```shell +pip install sqlite3 +``` + 可以通过下面的命令行查看工具的使用帮助信息: ```python diff --git a/docs/custom_models.md b/docs/custom_models.md index 7a3e3896125d0371d4110d3c002dba41b71f6dc0..261a7ec212968d2c168562f036d8eb9281827a7b 100644 --- a/docs/custom_models.md +++ b/docs/custom_models.md @@ -2,12 +2,14 @@ 默认地,PaddlePaddle大规模分类库构建基于ResNet50模型的训练模型。 -PLSC提供了模型基类plsc.models.base_model.BaseModel,用户可以基于该基类构建自己的网络模型。用户自定义的模型类需要继承自该基类,并实现build_network方法,该方法用于构建用户自定义模型。 +PLSC提供了模型基类plsc.models.base_model.BaseModel,用户可以基于该基类构建自己的 +网络模型。用户自定义的模型类需要继承自该基类,并实现build_network方法,该方法用 +于构建用户自定义模型。 下面的例子给出如何使用BaseModel基类定义用户自己的网络模型, 以及如何使用。 ```python import paddle.fluid as fluid -import plsc.entry as entry +from plsc import Entry from plsc.models.base_model import BaseModel class ResNet(BaseModel): @@ -64,20 +66,22 @@ class ResNet(BaseModel): is_test=False if is_train else True) return emb + def conv_bn_layer( ... ... if __name__ == "__main__": - ins = entry.Entry() + ins = Entry() ins.set_model(ResNet()) ins.train() ``` -用户自定义模型类需要继承自基类BaseModel,并实现build_network方法,实现用户的自定义模型。 +用户自定义模型类需要继承自基类BaseModel,并实现build_network方法,实现用户的自定 +义模型。 build_network方法的输入如下: * input: 输入图像数据 * label: 图像类别 * is_train: 表示训练阶段还是测试/预测阶段 -build_network方法返回用户自定义组网的输出变量,BaseModel类的get_output方法将调用该方法获取用户自定义组网的输出,并自动在其后添加分布式FC层。 +build_network方法返回用户自定义组网的输出变量。 diff --git a/docs/distributed_params.md b/docs/distributed_params.md index 422261760e14e41ce0bb1c9a2a4f0f1eaa03bc52..3ddd8536262565d51e831be2f8a24b932339e6f2 100644 --- a/docs/distributed_params.md +++ b/docs/distributed_params.md @@ -2,20 +2,15 @@ ## 简介 -对于最后一层全连接层参数(W和b,假设参数b存在,否则,全连接参数仅为W),通常切分到所有训练GPU卡。例如, -假设训练阶段使用的GPU卡数为N,那么 - -$$W = [W_{1}, W_{2},..., W_{N}$$ -$$b = [b_{1}, b_{2},..., b_{N}$$ - -并且,参数$W_{i}$和$b_{i}$保存在第i个GPU。 +对于最后一层全连接层参数(W和b,假设参数b存在,否则,全连接参数仅为W),通常切分到 +所有训练GPU卡。那么,每个GPU卡上只保存部分全连接层参数。 当保存模型时,各个GPU卡的分布式参数均会得到保存。 -在热启动或fine-tuning阶段,如果训练GPU卡数和热启动前或者预训练阶段使用的GPU卡数不同时,需要 -对分布式参数进行转换,以保证分布式参数的数量和训练使用的GPU卡数相同。 +在热启动或fine-tuning阶段,如果训练GPU卡数和热启动前或者预训练阶段使用的GPU卡数 +不同时,需要对分布式参数进行转换,以保证分布式参数的数量和训练使用的GPU卡数相同。 -默认地,当使用plsc.entry.Entry.train()方法时,会自动进行分布式参数的转换。 +默认地,当使用train()方法时,会自动进行分布式参数的转换。 ## 工具使用方法 diff --git a/docs/export_for_infer.md b/docs/export_for_infer.md index 960ca42fc0f280c292ddc3a5b52f1dc5a0baaa92..0d24927a904f737de89bdc0298f87ee011188001 100644 --- a/docs/export_for_infer.md +++ b/docs/export_for_infer.md @@ -2,17 +2,21 @@ 通常,PaddlePaddle大规模分类库在训练过程中保存的模型只保存模型参数信息, 而不包括预测模型结构。为了部署PLSC预测库,需要将预训练模型导出为预测模型。 + 预测模型包括预测所需要的模型参数和模型结构,用于后续地预测任务(参见[C++预测库使用](./serving.md)) 可以通过下面的代码将预训练模型导出为预测模型: ```python -import plsc.entry as entry +from plsc import Entry if __name__ == "__main__": - ins = entry.Entry() + ins = Entry() ins.set_checkpoint_dir('./pretrain_model') ins.set_model_save_dir('./inference_model') ins.convert_for_prediction() ``` + +其中'./pretrain_model'目录为预训练模型目录,'./inference_model'为用于预测的模型 +目录。 diff --git a/docs/installation.md b/docs/installation.md index 212508597c8719e35b6dfb27a1615b11e45386b0..a33ed2cb9a40ac5672d8961ea85504c96163f8b0 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -29,7 +29,13 @@ conda install -c paddle paddlepaddle-gpu cudatoolkit=9.0 更多安装方式和信息请参考[PaddlePaddle安装说明](https://www.paddlepaddle.org.cn/documentation/docs/zh/beginners_guide/install/index_cn.html) -## 2. 安装大规模分类库 +## 2. 安装依赖包 + +```shell +pip install -r requirements.txt +``` + +## 3. 安装大规模分类库 ```shell pip install plsc diff --git a/docs/mixed_precision.md b/docs/mixed_precision.md deleted file mode 100755 index 7b3f6d08c7b4635818507c4ed6aba6c1f1ce347b..0000000000000000000000000000000000000000 --- a/docs/mixed_precision.md +++ /dev/null @@ -1,28 +0,0 @@ -# 混合精度训练 - -PLSC支持混合精度训练。使用混合精度训练可以提升训练的速度,同时减少训练使用的内存。 - -可以通过下面的代码设置开启混合精度训练: - -```python -from __future__ import print_function -import plsc.entry as entry - -def main(): - ins = entry.Entry() - ins.set_mixed_precision(True, 1.0) - ins.train() - -if __name__ == "__main__": - main() -``` -其中,`set_mixed_precision`函数介绍如下: - -| API | 描述 | 参数说明 | -| :------------------- | :--------------------| :---------------------- | -| set_mixed_precision(use_fp16, loss_scaling) | 设置混合精度训练 | `use_fp16`为是否开启混合精度训练,默认为False;`loss_scaling`为初始的损失缩放值,默认为1.0| - -- `use_fp16`:bool类型,当想要开启混合精度训练时,可将此参数设为True即可。 -- `loss_scaling`:float类型,为初始的损失缩放值,这个值有可能会影响混合精度训练的精度,建议设为默认值1.0。 - -为了提高混合精度训练的稳定性和精度,默认开启了动态损失缩放机制。更多关于混合精度训练的介绍可参考:[混合精度训练](https://arxiv.org/abs/1710.03740) diff --git a/docs/usage.md b/docs/usage.md index a2887804c0e817ffa190acfebb486ce807223dfa..99f3877cc7c5604138228db762e15cd9472227e0 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -1,6 +1,7 @@ -# 训练、评估和部署 +# 模型训练和评估 -PaddlePaddle大规模分类提供了从训练、评估到预测部署的全流程解决方案。本文档介绍如何使用PaddlePaddle大规模分类库快速完成训练、评估和预测部署。 +PaddlePaddle大规模分类提供了从训练、评估到预测部署的全流程解决方案。本文档介绍如 +何使用PaddlePaddle大规模分类库快速完成训练、评估和预测部署。 ## 数据准备 @@ -16,7 +17,11 @@ train_data/ `-- lfw.bin ``` -其中,*train_data*是用户数据的根目录,*agedb_30.bin*、*cfp_ff.bin*、*cfp_fp.bin*和*lfw.bin*分别是不同的验证数据集,且这些验证数据集不是全部必须的。本文档教程默认使用lfw.bin作为验证数据集,因此在浏览本教程时,请确保lfw.bin验证数据集可用。*images*目录包含JPEG格式的训练图像,*label.txt*中的每一行对应一张训练图像以及该图像的类别。 +其中,*train_data*是用户数据的根目录,*agedb_30.bin*、*cfp_ff.bin*、*cfp_fp.bin* +和*lfw.bin*分别是不同的验证数据集,且这些验证数据集不是全部必须的。本文档教程默认 +使用lfw.bin作为验证数据集,因此在浏览本教程时,请确保lfw.bin验证数据集可用。 +*images*目录包含JPEG格式的训练图像,*label.txt*中的每一行对应一张训练图像以及该 +图像的类别。 *label.txt*文件的内容示例如下: @@ -37,24 +42,27 @@ images/00000007.jpg 0 ### 训练代码 下面的例子给出使用PLSC完成大规模分类训练的脚本*train.py*: ```python -import plsc.entry as entry +from plsc import Entry if __name__ == "__main__": - ins = entry.Entry() + ins = Entry() ins.train() ``` -1. 从plsc包导入entry.Entry类,其是使用PLCS大规模分类库功能的接口类。 +1. 从plsc包导入Entry类,其是使用PLCS大规模分类库功能的接口类。 2. 生成Entry类的实例。 3. 调用Entry类的train方法,即可开始训练。 +默认地,训练阶段每个训练轮次的之后会使用验证集验证模型的效果,当没有验证数据集时, +可以使用*set_with_test(False)* API关闭验证功能。 + ### 开始训练 下面的例子给出如何使用上述脚本启动训练任务: ```shell python -m paddle.distributed.launch \ - --cluster_ips="127.0.0.1" \ + --cluster_node_ips="127.0.0.1" \ --node_ip="127.0.0.1" \ --selected_gpus=0,1,2,3,4,5,6,7 \ train.py @@ -62,31 +70,29 @@ python -m paddle.distributed.launch \ paddle.distributed.launch模块用于启动多机/多卡分布式训练任务脚本,简化分布式训练任务启动过程,各个参数的含义如下: -* cluster_ips: 参与训练的节点的ip地址列表,以逗号分隔; +* cluster_node_ips: 参与训练的节点的ip地址列表,以逗号分隔; * node_ip: 当前训练节点的ip地址; * selected_gpus: 每个训练节点所使用的gpu设备列表,以逗号分隔。 -## 模型评估 +## 模型验证 本教程中,我们使用lfw.bin验证数据集评估训练模型的效果。 -### 评估代码 +### 验证代码 -下面的例子给出使用PLSC完成大规模分类训练的脚本*val.py*: +下面的例子给出使用PLSC完成大规模分类验证的脚本*val.py*: ```python -import plsc.entry as entry +from plsc import Entry if __name__ == "__main__": - ins = entry.Entry() + ins = Entry() ins.set_checkpoint("output/0") ins.test() ``` -默认地,PLSC将训练脚本保存在'./ouput'目录下,并以pass_id作为区分不同训练轮次模型的子目录,例如'./output/0'目录下保存完成第一个轮次的训练后保存的模型。 - -在模型评估阶段,我们首先需要设置训练模型的目录,接着调用Entry类的test方法开始模型评估。 - -## 预测部署 +默认地,PLSC将训练脚本保存在'./ouput'目录下,并以pass作为区分不同训练轮次模型 +的子目录,例如'./output/0'目录下保存完成第一个轮次的训练后保存的模型。 -TBD +在模型评估阶段,我们首先需要设置训练模型的目录,接着调用Entry类的test方法开始模 +型验证。 diff --git a/images/fc_computing.png b/images/fc_computing.png new file mode 100644 index 0000000000000000000000000000000000000000..32db1fba463e1015434bfb4ebe5d7000474ffba4 Binary files /dev/null and b/images/fc_computing.png differ diff --git a/images/fc_computing_block.png b/images/fc_computing_block.png new file mode 100644 index 0000000000000000000000000000000000000000..56e949199ab01253e601f75fca9d2f761ea9f57a Binary files /dev/null and b/images/fc_computing_block.png differ diff --git a/images/fc_computing_block_global.png b/images/fc_computing_block_global.png new file mode 100644 index 0000000000000000000000000000000000000000..d4ab1ee0d854f1e231e7d0a19a21e976e9736739 Binary files /dev/null and b/images/fc_computing_block_global.png differ diff --git a/images/softmax_computing.png b/images/softmax_computing.png new file mode 100644 index 0000000000000000000000000000000000000000..3cee647d71c7ab8e5727020d43f39805cc564118 Binary files /dev/null and b/images/softmax_computing.png differ diff --git a/requirements.txt b/requirements.txt index 0b5ae2bb08b9efaf65bd0e7b97be29dd96ac68cc..19b094a1d4d9cd4b07a9cd6987bf503fb244208a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,6 @@ numpy>=1.12, <=1.16.4 ; python_version<"3.5" numpy>=1.12 ; python_version>="3.5" scipy>=0.19.0, <=1.2.1 ; python_version<"3.5" -paddlepaddle>=1.6.2 scipy ; python_version>="3.5" Pillow sklearn diff --git a/setup.py b/setup.py index 944a94f0e098194fb8a4c1637bc8ded3cb97a48b..8cafab03524421f9056be25459a4b1fe7a938b4e 100644 --- a/setup.py +++ b/setup.py @@ -23,8 +23,7 @@ from plsc.version import plsc_version REQUIRED_PACKAGES = [ - 'sklearn', 'easydict', 'paddlepaddle>=1.6.2', 'Pillow', - 'numpy', 'scipy' + 'sklearn', 'easydict', 'Pillow', 'numpy', 'scipy' ]