Browse Source

Merge branch 'develop' of https://github.com/PaddlePaddle/PaddleX into develop_qh

FlyingQianMM 5 years ago
parent
commit
8d0a99304d

+ 42 - 0
docs/apis/datasets/dataset_convert.md

@@ -0,0 +1,42 @@
+# 数据集转换
+## labelme2voc
+```python
+pdx.tools.labelme2voc(image_dir, json_dir, dataset_save_dir)
+```
+将LabelMe标注的数据集转换为VOC数据集。
+
+> **参数**
+> > * **image_dir** (str): 图像文件存放的路径。
+> > * **json_dir** (str): 与每张图像对应的json文件的存放路径。
+> > * **dataset_save_dir** (str): 转换后数据集存放路径。
+
+## 其它数据集转换
+### easydata2imagenet
+```python
+pdx.tools.easydata2imagenet(image_dir, json_dir, dataset_save_dir)
+```
+### easydata2voc
+```python
+pdx.tools.easydata2voc(image_dir, json_dir, dataset_save_dir)
+```
+### easydata2coco
+```python
+pdx.tools.easydata2coco(image_dir, json_dir, dataset_save_dir)
+```
+### easydata2seg
+```python
+pdx.tools.easydata2seg(image_dir, json_dir, dataset_save_dir)
+```
+### labelme2coco
+```python
+pdx.tools.labelme2coco(image_dir, json_dir, dataset_save_dir)
+```
+### labelme2seg
+```python
+pdx.tools.labelme2seg(image_dir, json_dir, dataset_save_dir)
+```
+### jingling2seg
+```python
+pdx.tools.jingling2seg(image_dir, json_dir, dataset_save_dir)
+```
+

+ 1 - 0
docs/apis/datasets/index.rst

@@ -29,3 +29,4 @@ PaddleX目前支持主流的CV数据集格式和 `EasyData <https://ai.baidu.com
    classification.md
    detection.md
    semantic_segmentation.md
+   dataset_convert.md

+ 30 - 5
docs/apis/visualize.md

@@ -5,7 +5,7 @@ PaddleX提供了一系列模型预测和结果分析的可视化函数。
 ```
 paddlex.det.visualize(image, result, threshold=0.5, save_dir='./')
 ```
-将目标检测/实例分割模型预测得到的Box框和Mask在原图上进行可视化
+将目标检测/实例分割模型预测得到的Box框和Mask在原图上进行可视化
 
 ### 参数
 > * **image** (str): 原图文件路径。  
@@ -77,7 +77,7 @@ pdx.det.draw_pr_curve(gt=gt, pred_bbox=bbox, save_dir='./insect')
 ```
 paddlex.seg.visualize(image, result, weight=0.6, save_dir='./')
 ```
-将语义分割模型预测得到的Mask在原图上进行可视化
+将语义分割模型预测得到的Mask在原图上进行可视化
 
 ### 参数
 > * **image** (str): 原图文件路径。  
@@ -99,11 +99,11 @@ pdx.det.visualize('city.png', result, save_dir='./')
 ```
 paddlex.slim.visualize(model, sensitivities_file)
 ```
-利用此接口,可以分析在不同的`eval_metric_loss`参数下,模型被裁剪的比例情况。可视化结果纵轴为eval_metric_loss参数值,横轴为对应的模型被裁剪的比例
+利用此接口,可以分析在不同的`eval_metric_loss`参数下,模型被裁剪的比例情况。可视化结果纵轴为eval_metric_loss参数值,横轴为对应的模型被裁剪的比例
 
 ### 参数
->* **model**: 使用PaddleX加载的模型
->* **sensitivities_file**: 模型各参数在验证集上计算得到的参数敏感度信息文件
+>* **model** (paddlex.cv.models): 使用PaddleX加载的模型
+>* **sensitivities_file** (str): 模型各参数在验证集上计算得到的参数敏感度信息文件
 
 ### 使用示例
 > 点击下载示例中的[模型](https://bj.bcebos.com/paddlex/models/vegetables_mobilenet.tar.gz)和[sensitivities_file](https://bj.bcebos.com/paddlex/slim_prune/mobilenetv2.sensitivities)
@@ -113,3 +113,28 @@ model = pdx.load_model('vegetables_mobilenet')
 pdx.slim.visualize(model, 'mobilenetv2.sensitivities', save_dir='./')
 # 可视化结果保存在./sensitivities.png
 ```
+
+## 可解释性结果可视化
+```
+paddlex.interpret.visualize(img_file, 
+                            model, 
+                            dataset=None, 
+                            algo='lime',
+                            num_samples=3000, 
+                            batch_size=50,
+                            save_dir='./')
+```
+将模型预测结果的可解释性可视化,目前只支持分类模型。
+
+### 参数
+>* **img_file** (str): 预测图像路径。
+>* **model** (paddlex.cv.models): paddlex中的模型。
+>* **dataset** (paddlex.datasets): 数据集读取器,默认为None。
+>* **algo** (str): 可解释性方式,当前可选'lime'和'normlime'。
+>* **num_samples** (int): LIME用于学习线性模型的采样数,默认为3000。
+>* **batch_size** (int): 预测数据batch大小,默认为50。
+>* **save_dir** (str): 可解释性可视化结果(保存为png格式文件)和中间文件存储路径。 
+
+
+### 使用示例
+> 对预测可解释性结果可视化的过程可参见[代码](https://github.com/PaddlePaddle/PaddleX/blob/develop/tutorials/interpret/interpret.py)。

+ 367 - 0
docs/appendix/datasets.md

@@ -0,0 +1,367 @@
+# 数据集格式说明
+
+---
+## 图像分类ImageNet
+
+图像分类ImageNet数据集包含对应多个标签的图像文件夹、标签文件及图像列表文件。
+参考数据文件结构如下:
+```
+./dataset/  # 数据集根目录
+|--labelA  # 标签为labelA的图像目录
+|  |--a1.jpg
+|  |--...
+|  └--...
+|
+|--...
+|
+|--labelZ  # 标签为labelZ的图像目录
+|  |--z1.jpg
+|  |--...
+|  └--...
+|
+|--train_list.txt  # 训练文件列表文件
+|
+|--val_list.txt  # 验证文件列表文件
+|
+└--labels.txt  # 标签列表文件
+
+```
+其中,相应的文件名可根据需要自行定义。
+
+`train_list.txt`和`val_list.txt`文本以空格为分割符分为两列,第一列为图像文件相对于dataset的相对路径,第二列为图像文件对应的标签id(从0开始)。如下所示:
+```
+labelA/a1.jpg 0
+labelZ/z1.jpg 25
+...
+```
+
+`labels.txt`: 每一行为一个单独的类别,相应的行号即为类别对应的id(行号从0开始),如下所示:
+```
+labelA
+labelB
+...
+```
+[点击这里](https://bj.bcebos.com/paddlex/datasets/vegetables_cls.tar.gz),下载蔬菜分类分类数据集。  
+在PaddleX中,使用`paddlex.cv.datasets.ImageNet`([API说明](../apis/datasets/classification.html#imagenet))加载分类数据集。
+
+## 目标检测VOC
+目标检测VOC数据集包含图像文件夹、标注信息文件夹、标签文件及图像列表文件。
+参考数据文件结构如下:
+```
+./dataset/  # 数据集根目录
+|--JPEGImages  # 图像目录
+|  |--xxx1.jpg
+|  |--...
+|  └--...
+|
+|--Annotations  # 标注信息目录
+|  |--xxx1.xml
+|  |--...
+|  └--...
+|
+|--train_list.txt  # 训练文件列表文件
+|
+|--val_list.txt  # 验证文件列表文件
+|
+└--labels.txt  # 标签列表文件
+
+```
+其中,相应的文件名可根据需要自行定义。
+
+`train_list.txt`和`val_list.txt`文本以空格为分割符分为两列,第一列为图像文件相对于dataset的相对路径,第二列为标注文件相对于dataset的相对路径。如下所示:
+```
+JPEGImages/xxx1.jpg Annotations/xxx1.xml
+JPEGImages/xxx2.jpg Annotations/xxx2.xml
+...
+```
+
+`labels.txt`: 每一行为一个单独的类别,相应的行号即为类别对应的id(行号从0开始),如下所示:
+```
+labelA
+labelB
+...
+```
+[点击这里](https://bj.bcebos.com/paddlex/datasets/insect_det.tar.gz),下载昆虫检测数据集。  
+在PaddleX中,使用`paddlex.cv.datasets.VOCDetection`([API说明](../apis/datasets/detection.html#vocdetection))加载目标检测VOC数据集。
+
+## 目标检测和实例分割COCO
+目标检测和实例分割COCO数据集包含图像文件夹及图像标注信息文件。
+参考数据文件结构如下:
+```
+./dataset/  # 数据集根目录
+|--JPEGImages  # 图像目录
+|  |--xxx1.jpg
+|  |--...
+|  └--...
+|
+|--train.json  # 训练相关信息文件
+|
+└--val.json  # 验证相关信息文件
+
+```
+其中,相应的文件名可根据需要自行定义。
+
+`train.json`和`val.json`存储与标注信息、图像文件相关的信息。如下所示:
+
+```
+{
+  "annotations": [
+    {
+      "iscrowd": 0,
+      "category_id": 1,
+      "id": 1,
+      "area": 33672.0,
+      "image_id": 1,
+      "bbox": [232, 32, 138, 244],
+      "segmentation": [[32, 168, 365, 117, ...]]
+    },
+    ...
+  ],
+  "images": [
+    {
+      "file_name": "xxx1.jpg",
+      "height": 512,
+      "id": 267,
+      "width": 612
+    },
+    ...
+  ]
+  "categories": [
+    {
+      "name": "labelA",
+      "id": 1,
+      "supercategory": "component"
+    }
+  ]
+}
+```
+其中,每个字段的含义如下所示:
+
+| 域名 | 字段名 | 含义 | 数据类型 | 备注 |
+|:-----|:--------|:------------|------|:-----|
+| annotations | id | 标注信息id | int | 从1开始 |
+| annotations | iscrowd      | 标注框是否为一组对象 | int | 只有0、1两种取值 |
+| annotations | category_id  | 标注框类别id | int |  |
+| annotations | area         | 标注框的面积 | float |  |
+| annotations | image_id     | 当前标注信息所在图像的id | int |  |
+| annotations | bbox         | 标注框坐标 | list | 长度为4,分别代表x,y,w,h |
+| annotations | segmentation | 标注区域坐标 | list | list中有至少1个list,每个list由每个小区域坐标点的横纵坐标(x,y)组成 |
+| images          | id                | 图像id | int | 从1开始 |
+| images   | file_name         | 图像文件名 | str |  |
+| images      | height            | 图像高度 | int |  |
+| images       | width             | 图像宽度 | int |  |
+| categories  | id            | 类别id | int | 从1开始 |
+| categories | name          | 类别标签名 | str |  |
+| categories | supercategory | 类别父类的标签名 | str |  |
+
+
+[点击这里](https://bj.bcebos.com/paddlex/datasets/garbage_ins_det.tar.gz),下载垃圾实例分割数据集。  
+在PaddleX中,使用`paddlex.cv.datasets.COCODetection`([API说明](../apis/datasets/detection.html#cocodetection))加载COCO格式数据集。
+
+## 语义分割数据
+语义分割数据集包含原图、标注图及相应的文件列表文件。
+参考数据文件结构如下:
+```
+./dataset/  # 数据集根目录
+|--images  # 原图目录
+|  |--xxx1.png
+|  |--...
+|  └--...
+|
+|--annotations  # 标注图目录
+|  |--xxx1.png
+|  |--...
+|  └--...
+|
+|--train_list.txt  # 训练文件列表文件
+|
+|--val_list.txt  # 验证文件列表文件
+|
+└--labels.txt  # 标签列表
+
+```
+其中,相应的文件名可根据需要自行定义。
+
+`train_list.txt`和`val_list.txt`文本以空格为分割符分为两列,第一列为图像文件相对于dataset的相对路径,第二列为标注图像文件相对于dataset的相对路径。如下所示:
+```
+images/xxx1.png annotations/xxx1.png
+images/xxx2.png annotations/xxx2.png
+...
+```
+
+`labels.txt`: 每一行为一个单独的类别,相应的行号即为类别对应的id(行号从0开始),如下所示:
+```
+background
+labelA
+labelB
+...
+```
+
+标注图像为单通道图像,像素值即为对应的类别,像素标注类别需要从0开始递增(一般第一个类别为`background`),
+例如0,1,2,3表示有4种类别,标注类别最多为256类。其中可以指定特定的像素值用于表示该值的像素不参与训练和评估(默认为255)。
+
+[点击这里](https://bj.bcebos.com/paddlex/datasets/optic_disc_seg.tar.gz),下载视盘语义分割数据集。  
+在PaddleX中,使用`paddlex.cv.datasets.SegReader`([API说明](../apis/datasets/semantic_segmentation.html#segdataset))加载语义分割数据集。
+
+
+## 图像分类EasyDataCls
+
+图像分类EasyDataCls数据集包含存放图像和json文件的文件夹、标签文件及图像列表文件。
+参考数据文件结构如下:
+```
+./dataset/  # 数据集根目录
+|--easydata  # 存放图像和json文件的文件夹
+|  |--0001.jpg
+|  |--0001.json
+|  |--0002.jpg
+|  |--0002.json
+|  └--...
+|
+|--train_list.txt  # 训练文件列表文件
+|
+|--val_list.txt  # 验证文件列表文件
+|
+└--labels.txt  # 标签列表文件
+
+```
+其中,图像文件名应与json文件名一一对应。   
+
+每个json文件存储于`labels`相关的信息。如下所示:
+```
+{"labels": [{"name": "labelA"}]}
+```
+其中,`name`字段代表对应图像的类别。  
+
+`train_list.txt`和`val_list.txt`文本以空格为分割符分为两列,第一列为图像文件相对于dataset的相对路径,第二列为json文件相对于dataset的相对路径。如下所示:
+```
+easydata/0001.jpg easydata/0001.json
+easydata/0002.jpg easydata/0002.json
+...
+```
+
+`labels.txt`: 每一行为一个单独的类别,相应的行号即为类别对应的id(行号从0开始),如下所示:
+```
+labelA
+labelB
+...
+```
+[点击这里](https://ai.baidu.com/easydata/),可以标注图像分类EasyDataCls数据集。  
+在PaddleX中,使用`paddlex.cv.datasets.EasyDataCls`([API说明](../apis/datasets/classification.html#easydatacls))加载分类数据集。
+
+
+## 目标检测和实例分割EasyDataDet
+
+目标检测和实例分割EasyDataDet数据集包含存放图像和json文件的文件夹、标签文件及图像列表文件。
+参考数据文件结构如下:
+```
+./dataset/  # 数据集根目录ß
+|--easydata  # 存放图像和json文件的文件夹
+|  |--0001.jpg
+|  |--0001.json
+|  |--0002.jpg
+|  |--0002.json
+|  └--...
+|
+|--train_list.txt  # 训练文件列表文件
+|
+|--val_list.txt  # 验证文件列表文件
+|
+└--labels.txt  # 标签列表文件
+
+```
+其中,图像文件名应与json文件名一一对应。   
+
+每个json文件存储于`labels`相关的信息。如下所示:
+```
+"labels": [{"y1": 18, "x2": 883, "x1": 371, "y2": 404, "name": "labelA", 
+            "mask": "kVfc0`0Zg0<F7J7I5L5K4L4L4L3N3L3N3L3N2N3M2N2N2N2N2N2N1O2N2O1N2N1O2O1N101N1O2O1N101N10001N101N10001N10001O0O10001O000O100000001O0000000000000000000000O1000001O00000O101O000O101O0O101O0O2O0O101O0O2O0O2N2O0O2O0O2N2O1N1O2N2N2O1N2N2N2N2N2N2M3N3M2M4M2M4M3L4L4L4K6K5J7H9E\\iY1"}, 
+           {"y1": 314, "x2": 666, "x1": 227, "y2": 676, "name": "labelB",
+            "mask": "mdQ8g0Tg0:G8I6K5J5L4L4L4L4M2M4M2M4M2N2N2N3L3N2N2N2N2O1N1O2N2N2O1N1O2N2O0O2O1N1O2O0O2O0O2O001N100O2O000O2O000O2O00000O2O000000001N100000000000000000000000000000000001O0O100000001O0O10001N10001O0O101N10001N101N101N101N101N2O0O2N2O0O2N2N2O0O2N2N2N2N2N2N2N2N2N3L3N2N3L3N3L4M2M4L4L5J5L5J7H8H;BUcd<"}, 
+           ...]}
+```
+其中,list中的每个元素代表一个标注信息,标注信息中字段的含义如下所示: 
+
+| 字段名 | 含义 | 数据类型 | 备注 |
+|:--------|:------------|------|:-----|
+| x1 | 标注框左下角横坐标 | int | |
+| y1 | 标注框左下角纵坐标 | int | |
+| x2 | 标注框右上角横坐标 | int | |
+| y2 | 标注框右上角纵坐标 | int | |
+| name | 标注框中物体类标 | str | |
+| mask | 分割区域布尔型numpy编码后的字符串 | str | 该字段可以不存在,当不存在时只能进行目标检测 |
+
+`train_list.txt`和`val_list.txt`文本以空格为分割符分为两列,第一列为图像文件相对于dataset的相对路径,第二列为json文件相对于dataset的相对路径。如下所示:
+```
+easydata/0001.jpg easydata/0001.json
+easydata/0002.jpg easydata/0002.json
+...
+```
+
+`labels.txt`: 每一行为一个单独的类别,相应的行号即为类别对应的id(行号从0开始),如下所示:
+```
+labelA
+labelB
+...
+```
+
+[点击这里](https://ai.baidu.com/easydata/),可以标注图像分类EasyDataDet数据集。  
+在PaddleX中,使用`paddlex.cv.datasets.EasyDataDet`([API说明](../apis/datasets/detection.html#easydatadet))加载分类数据集。
+
+## 语义分割EasyDataSeg
+
+语义分割EasyDataSeg数据集包含存放图像和json文件的文件夹、标签文件及图像列表文件。
+参考数据文件结构如下:
+```
+./dataset/  # 数据集根目录ß
+|--easydata  # 存放图像和json文件的文件夹
+|  |--0001.jpg
+|  |--0001.json
+|  |--0002.jpg
+|  |--0002.json
+|  └--...
+|
+|--train_list.txt  # 训练文件列表文件
+|
+|--val_list.txt  # 验证文件列表文件
+|
+└--labels.txt  # 标签列表文件
+
+```
+其中,图像文件名应与json文件名一一对应。   
+
+每个json文件存储于`labels`相关的信息。如下所示:
+```
+"labels": [{"y1": 18, "x2": 883, "x1": 371, "y2": 404, "name": "labelA", 
+            "mask": "kVfc0`0Zg0<F7J7I5L5K4L4L4L3N3L3N3L3N2N3M2N2N2N2N2N2N1O2N2O1N2N1O2O1N101N1O2O1N101N10001N101N10001N10001O0O10001O000O100000001O0000000000000000000000O1000001O00000O101O000O101O0O101O0O2O0O101O0O2O0O2N2O0O2O0O2N2O1N1O2N2N2O1N2N2N2N2N2N2M3N3M2M4M2M4M3L4L4L4K6K5J7H9E\\iY1"}, 
+           {"y1": 314, "x2": 666, "x1": 227, "y2": 676, "name": "labelB",
+            "mask": "mdQ8g0Tg0:G8I6K5J5L4L4L4L4M2M4M2M4M2N2N2N3L3N2N2N2N2O1N1O2N2N2O1N1O2N2O0O2O1N1O2O0O2O0O2O001N100O2O000O2O000O2O00000O2O000000001N100000000000000000000000000000000001O0O100000001O0O10001N10001O0O101N10001N101N101N101N101N2O0O2N2O0O2N2N2O0O2N2N2N2N2N2N2N2N2N3L3N2N3L3N3L4M2M4L4L5J5L5J7H8H;BUcd<"}, 
+           ...]}
+```
+其中,list中的每个元素代表一个标注信息,标注信息中字段的含义如下所示: 
+
+| 字段名 | 含义 | 数据类型 | 备注 |
+|:--------|:------------|------|:-----|
+| x1 | 标注框左下角横坐标 | int | |
+| y1 | 标注框左下角纵坐标 | int | |
+| x2 | 标注框右上角横坐标 | int | |
+| y2 | 标注框右上角纵坐标 | int | |
+| name | 标注框中物体类标 | str | |
+| mask | 分割区域布尔型numpy编码后的字符串 | str | 该字段必须存在 |
+
+`train_list.txt`和`val_list.txt`文本以空格为分割符分为两列,第一列为图像文件相对于dataset的相对路径,第二列为json文件相对于dataset的相对路径。如下所示:
+```
+easydata/0001.jpg easydata/0001.json
+easydata/0002.jpg easydata/0002.json
+...
+```
+
+`labels.txt`: 每一行为一个单独的类别,相应的行号即为类别对应的id(行号从0开始),如下所示:
+```
+labelA
+labelB
+...
+```
+
+[点击这里](https://ai.baidu.com/easydata/),可以标注图像分类EasyDataSeg数据集。  
+在PaddleX中,使用`paddlex.cv.datasets.EasyDataSeg`([API说明](../apis/datasets/semantic_segmentation.html#easydataseg))加载分类数据集。
+

+ 28 - 0
docs/appendix/how_to_convert_dataset.md

@@ -0,0 +1,28 @@
+# 数据集转换
+
+当前PaddleX GUI支持ImageNet格式的图像分类数据集、VOC格式的目标检测数据集、COCO格式的实例分割数据集、Seg格式的语义分割的数据集,当使用LabelMe、EasyData、标注精灵这3个工具标注数据时,PaddleX提供了相应接口可将数据转换成与PaddleX GUI想适配的数据集,使用方式如下所示:
+
+```python
+import paddlex as pdx
+
+# 该接口实现LabelMe数据集到VOC数据集的转换。
+# image_dir为图像文件存放的路径。
+# json_dir为与每张图像对应的json文件的存放路径。
+# dataset_save_dir为转换后数据集存放路径。
+pdx.tools.labelme2voc(image_dir='labelme_imgs',
+                      json_dir='labelme_jsons',
+                      dataset_save_dir='voc_dataset')
+```
+
+可替换labelme2voc实现不同数据集间的转换,目前提供的转换接口如下:  
+
+| 接口      | 转换关系 |
+| :-------- | :------- |
+| labelme2voc  | LabelMe数据集转换为VOC数据集   |
+| labelme2coco  | LabelMe数据集转换为COCO数据集   |
+| labelme2seg  | LabelMe数据集转换为Seg数据集  |
+| easydata2imagenet | EasyData数据集转换为ImageNet数据集  |
+| easydata2voc | EasyData数据集转换为VOC数据集  |
+| easydata2coco | EasyData数据集转换为COCO数据集  |
+| easydata2seg | EasyData数据集转换为Seg数据集  |
+| jingling2seg | 标注精灵数据集转换为Seg数据集  |

+ 3 - 0
docs/appendix/index.rst

@@ -8,6 +8,9 @@
 
    model_zoo.md
    metrics.md
+   how_to_convert_dataset.md
+   datasets.md
+
 
 * PaddleX版本: v0.1.7
 * 项目官网: http://www.paddlepaddle.org.cn/paddle/paddlex  

+ 1 - 203
docs/datasets.md

@@ -1,204 +1,2 @@
 # 数据集格式说明
-
----
-## 图像分类ImageNet
-
-图像分类ImageNet数据集包含对应多个标签的图像文件夹、标签文件及图像列表文件。
-参考数据文件结构如下:
-```
-./dataset/  # 数据集根目录
-|--labelA  # 标签为labelA的图像目录
-|  |--a1.jpg
-|  |--...
-|  └--...
-|
-|--...
-|
-|--labelZ  # 标签为labelZ的图像目录
-|  |--z1.jpg
-|  |--...
-|  └--...
-|
-|--train_list.txt  # 训练文件列表文件
-|
-|--val_list.txt  # 验证文件列表文件
-|
-└--labels.txt  # 标签列表文件
-
-```
-其中,相应的文件名可根据需要自行定义。
-
-`train_list.txt`和`val_list.txt`文本以空格为分割符分为两列,第一列为图像文件相对于dataset的相对路径,第二列为图像文件对应的标签id(从0开始)。如下所示:
-```
-labelA/a1.jpg 0
-labelZ/z1.jpg 25
-...
-```
-
-`labels.txt`: 每一行为一个单独的类别,相应的行号即为类别对应的id(行号从0开始),如下所示:
-```
-labelA
-labelB
-...
-```
-[点击这里](https://bj.bcebos.com/paddlex/datasets/vegetables_cls.tar.gz),下载蔬菜分类分类数据集
-在PaddleX中,使用`paddlex.cv.datasets.ImageNet`([API说明](./apis/datasets.html#imagenet))加载分类数据集
-
-## 目标检测VOC
-目标检测VOC数据集包含图像文件夹、标注信息文件夹、标签文件及图像列表文件。
-参考数据文件结构如下:
-```
-./dataset/  # 数据集根目录
-|--JPEGImages  # 图像目录
-|  |--xxx1.jpg
-|  |--...
-|  └--...
-|
-|--Annotations  # 标注信息目录
-|  |--xxx1.xml
-|  |--...
-|  └--...
-|
-|--train_list.txt  # 训练文件列表文件
-|
-|--val_list.txt  # 验证文件列表文件
-|
-└--labels.txt  # 标签列表文件
-
-```
-其中,相应的文件名可根据需要自行定义。
-
-`train_list.txt`和`val_list.txt`文本以空格为分割符分为两列,第一列为图像文件相对于dataset的相对路径,第二列为标注文件相对于dataset的相对路径。如下所示:
-```
-JPEGImages/xxx1.jpg Annotations/xxx1.xml
-JPEGImages/xxx2.jpg Annotations/xxx2.xml
-...
-```
-
-`labels.txt`: 每一行为一个单独的类别,相应的行号即为类别对应的id(行号从0开始),如下所示:
-```
-labelA
-labelB
-...
-```
-[点击这里](https://bj.bcebos.com/paddlex/datasets/insect_det.tar.gz),下载昆虫检测数据集
-在PaddleX中,使用`paddlex.cv.datasets.VOCDetection`([API说明](./apis/datasets.html#vocdetection))加载目标检测VOC数据集
-
-## 目标检测和实例分割COCO
-目标检测和实例分割COCO数据集包含图像文件夹及图像标注信息文件。
-参考数据文件结构如下:
-```
-./dataset/  # 数据集根目录
-|--JPEGImages  # 图像目录
-|  |--xxx1.jpg
-|  |--...
-|  └--...
-|
-|--train.json  # 训练相关信息文件
-|
-└--val.json  # 验证相关信息文件
-
-```
-其中,相应的文件名可根据需要自行定义。
-
-`train.json`和`val.json`存储与标注信息、图像文件相关的信息。如下所示:
-
-```
-{
-  "annotations": [
-    {
-      "iscrowd": 0,
-      "category_id": 1,
-      "id": 1,
-      "area": 33672.0,
-      "image_id": 1,
-      "bbox": [232, 32, 138, 244],
-      "segmentation": [[32, 168, 365, 117, ...]]
-    },
-    ...
-  ],
-  "images": [
-    {
-      "file_name": "xxx1.jpg",
-      "height": 512,
-      "id": 267,
-      "width": 612
-    },
-    ...
-  ]
-  "categories": [
-    {
-      "name": "labelA",
-      "id": 1,
-      "supercategory": "component"
-    }
-  ]
-}
-```
-每个字段的含义如下所示:
-
-| 域名 | 字段名 | 含义 | 数据类型 | 备注 |
-|:-----|:--------|:------------|------|:-----|
-| annotations | id | 标注信息id | int | 从1开始 |
-| annotations | iscrowd      | 标注框是否为一组对象 | int | 只有0、1两种取值 |
-| annotations | category_id  | 标注框类别id | int |  |
-| annotations | area         | 标注框的面积 | float |  |
-| annotations | image_id     | 当前标注信息所在图像的id | int |  |
-| annotations | bbox         | 标注框坐标 | list | 长度为4,分别代表x,y,w,h |
-| annotations | segmentation | 标注区域坐标 | list | list中有至少1个list,每个list由每个小区域坐标点的横纵坐标(x,y)组成 |
-| images          | id                | 图像id | int | 从1开始 |
-| images   | file_name         | 图像文件名 | str |  |
-| images      | height            | 图像高度 | int |  |
-| images       | width             | 图像宽度 | int |  |
-| categories  | id            | 类别id | int | 从1开始 |
-| categories | name          | 类别标签名 | str |  |
-| categories | supercategory | 类别父类的标签名 | str |  |
-
-
-[点击这里](https://bj.bcebos.com/paddlex/datasets/garbage_ins_det.tar.gz),下载垃圾实例分割数据集
-在PaddleX中,使用`paddlex.cv.datasets.COCODetection`([API说明](./apis/datasets.html#cocodetection))加载COCO格式数据集
-
-## 语义分割数据
-语义分割数据集包含原图、标注图及相应的文件列表文件。
-参考数据文件结构如下:
-```
-./dataset/  # 数据集根目录
-|--images  # 原图目录
-|  |--xxx1.png
-|  |--...
-|  └--...
-|
-|--annotations  # 标注图目录
-|  |--xxx1.png
-|  |--...
-|  └--...
-|
-|--train_list.txt  # 训练文件列表文件
-|
-|--val_list.txt  # 验证文件列表文件
-|
-└--labels.txt  # 标签列表
-
-```
-其中,相应的文件名可根据需要自行定义。
-
-`train_list.txt`和`val_list.txt`文本以空格为分割符分为两列,第一列为图像文件相对于dataset的相对路径,第二列为标注图像文件相对于dataset的相对路径。如下所示:
-```
-images/xxx1.png annotations/xxx1.png
-images/xxx2.png annotations/xxx2.png
-...
-```
-
-`labels.txt`: 每一行为一个单独的类别,相应的行号即为类别对应的id(行号从0开始),如下所示:
-```
-labelA
-labelB
-...
-```
-
-标注图像为单通道图像,像素值即为对应的类别,像素标注类别需要从0开始递增,
-例如0,1,2,3表示有4种类别,标注类别最多为256类。其中可以指定特定的像素值用于表示该值的像素不参与训练和评估(默认为255)。
-
-[点击这里](https://bj.bcebos.com/paddlex/datasets/optic_disc_seg.tar.gz),下载视盘语义分割数据集
-在PaddleX中,使用`paddlex.cv.datasets.SegReader`([API说明](./apis/datasets.html#segreader))加载语义分割数据集
-
+该部分内容已迁移至[附录](./appendix/datasets.md)

+ 3 - 1
paddlex/__init__.py

@@ -29,7 +29,6 @@ from . import cls
 from . import slim
 from . import convertor
 from . import tools
-from . import interpret
 from . import deploy
 
 try:
@@ -51,4 +50,7 @@ load_model = cv.models.load_model
 datasets = cv.datasets
 
 log_level = 2
+
+from . import interpret
+
 __version__ = '0.2.0.github'

+ 1 - 1
paddlex/cv/datasets/easydata_cls.py

@@ -28,7 +28,7 @@ class EasyDataCls(ImageNet):
 
     Args:
         data_dir (str): 数据集所在的目录路径。
-        file_list (str): 描述数据集图片文件和类别id的文件路径(文本内每行路径为相对data_dir的相对路)。
+        file_list (str): 描述数据集图片文件和对应标注文件的文件路径(文本内每行路径为相对data_dir的相对路)。
         label_list (str): 描述数据集包含的类别信息文件路径。
         transforms (paddlex.cls.transforms): 数据集中每个样本的预处理/增强算子。
         num_workers (int|str): 数据集中样本在预处理过程中的线程或进程数。默认为'auto'。当设为'auto'时,根据

+ 3 - 2
paddlex/cv/models/base.py

@@ -139,9 +139,10 @@ class BaseAPI:
         dataset.num_samples = batch_size * batch_num
         try:
             from .slim.post_quantization import PaddleXPostTrainingQuantization
+            PaddleXPostTrainingQuantization._collect_target_varnames
         except:
             raise Exception(
-                "Model Quantization is not available, try to upgrade your paddlepaddle>=1.7.0"
+                "Model Quantization is not available, try to upgrade your paddlepaddle>=1.8.0"
             )
         is_use_cache_file = True
         if cache_dir is None:
@@ -544,4 +545,4 @@ class BaseAPI:
                                 best_accuracy))
                 if eval_dataset is not None and early_stop:
                     if earlystop(current_accuracy):
-                        break
+                        break

+ 68 - 90
paddlex/cv/models/slim/post_quantization.py

@@ -14,7 +14,7 @@
 
 from paddle.fluid.contrib.slim.quantization.quantization_pass import QuantizationTransformPass
 from paddle.fluid.contrib.slim.quantization.quantization_pass import AddQuantDequantPass
-from paddle.fluid.contrib.slim.quantization.quantization_pass import _op_real_in_out_name
+from paddle.fluid.contrib.slim.quantization.quantization_pass import _out_scale_op_list
 from paddle.fluid.contrib.slim.quantization import PostTrainingQuantization
 import paddlex.utils.logging as logging
 import paddle.fluid as fluid
@@ -44,7 +44,6 @@ class PaddleXPostTrainingQuantization(PostTrainingQuantization):
         fp32 model. It uses calibrate data to calculate the scale factor of
         quantized variables, and inserts fake quant/dequant op to obtain the
         quantized model.
-
         Args:
             executor(fluid.Executor): The executor to load, run and save the
                 quantized model.
@@ -78,6 +77,21 @@ class PaddleXPostTrainingQuantization(PostTrainingQuantization):
         Returns:
             None
         '''
+        self._support_activation_quantize_type = [
+            'range_abs_max', 'moving_average_abs_max', 'abs_max'
+        ]
+        self._support_weight_quantize_type = ['abs_max', 'channel_wise_abs_max']
+        self._support_algo_type = ['KL', 'abs_max', 'min_max']
+        self._support_quantize_op_type = \
+            list(set(QuantizationTransformPass._supported_quantizable_op_type +
+                AddQuantDequantPass._supported_quantizable_op_type))
+        
+        # Check inputs
+        assert executor is not None, "The executor cannot be None."
+        assert batch_size > 0, "The batch_size should be greater than 0."
+        assert algo in self._support_algo_type, \
+            "The algo should be KL, abs_max or min_max."
+        
         self._executor = executor
         self._dataset = dataset
         self._batch_size = batch_size
@@ -86,18 +100,19 @@ class PaddleXPostTrainingQuantization(PostTrainingQuantization):
         self._algo = algo
         self._is_use_cache_file = is_use_cache_file
         self._cache_dir = cache_dir
+        self._activation_bits = 8
+        self._weight_bits = 8
+        self._activation_quantize_type = 'range_abs_max'
+        self._weight_quantize_type = 'channel_wise_abs_max'
         if self._is_use_cache_file and not os.path.exists(self._cache_dir):
             os.mkdir(self._cache_dir)
 
-        supported_quantizable_op_type = \
-            QuantizationTransformPass._supported_quantizable_op_type + \
-            AddQuantDequantPass._supported_quantizable_op_type
         if is_full_quantize:
-            self._quantizable_op_type = supported_quantizable_op_type
+            self._quantizable_op_type = self._support_quantize_op_type
         else:
             self._quantizable_op_type = quantizable_op_type
             for op_type in self._quantizable_op_type:
-                assert op_type in supported_quantizable_op_type + \
+                assert op_type in self._support_quantize_op_type + \
                     AddQuantDequantPass._activation_type, \
                     op_type + " is not supported for quantization."
 
@@ -107,25 +122,29 @@ class PaddleXPostTrainingQuantization(PostTrainingQuantization):
         self._fetch_list = list(outputs.values())
         self._data_loader = None
 
-        self._op_real_in_out_name = _op_real_in_out_name
+        self._out_scale_op_list = _out_scale_op_list
         self._bit_length = 8
         self._quantized_weight_var_name = set()
         self._quantized_act_var_name = set()
         self._sampling_data = {}
-        self._quantized_var_scale_factor = {}
+        self._quantized_var_kl_threshold = {}
+        self._quantized_var_min = {}
+        self._quantized_var_max = {}
+        self._quantized_var_abs_max = {}
 
     def quantize(self):
         '''
         Quantize the fp32 model. Use calibrate data to calculate the scale factor of
         quantized variables, and inserts fake quant/dequant op to obtain the
         quantized model.
-
         Args:
             None
         Returns:
             the program of quantized model.
         '''
-        self._preprocess()
+        self._load_model_data()
+        self._collect_target_varnames()
+        self._set_activation_persistable()
         batch_ct = 0
         for data in self._data_loader():
             batch_ct += 1
@@ -140,7 +159,10 @@ class PaddleXPostTrainingQuantization(PostTrainingQuantization):
                 feed=data,
                 fetch_list=self._fetch_list,
                 return_numpy=False)
-            self._sample_data(batch_id)
+            if self._algo == "KL":
+                self._sample_data(batch_id)
+            else:
+                self._sample_threshold()
             end = time.time()
             logging.debug('[Run batch data] Batch={}/{}, time_each_batch={} s.'.format(
                 str(batch_id + 1),
@@ -150,19 +172,23 @@ class PaddleXPostTrainingQuantization(PostTrainingQuantization):
             if self._batch_nums and batch_id >= self._batch_nums:
                 break
         logging.info("All run batch: ".format(batch_id))
+        self._reset_activation_persistable()
         logging.info("Calculate scale factor ...")
-        self._calculate_scale_factor()
+        if self._algo == "KL":
+            self._calculate_kl_threshold()
         logging.info("Update the program ...")
-        self._update_program()
+        if self._algo in ["KL", "abs_max"]:
+            self._update_program()
+        else:
+            self._save_input_threhold()
         logging.info("Save ...")
-        self._save_output_scale()
+        self._save_output_threshold()
         logging.info("Finish quant!")
         return self._program
 
     def save_quantized_model(self, save_model_path):
         '''
         Save the quantized model to the disk.
-
         Args:
             save_model_path(str): The path to save the quantized model
         Returns:
@@ -176,88 +202,47 @@ class PaddleXPostTrainingQuantization(PostTrainingQuantization):
             executor=self._executor,
             params_filename='__params__',
             main_program=self._program)
-
-    def _preprocess(self):
+        
+    def _load_model_data(self):
         '''
-        Load model and set data loader, collect the variable names for sampling,
-        and set activation variables to be persistable.
+        Set data loader.
         '''
         feed_vars = [fluid.framework._get_var(var.name, self._program) \
             for var in self._feed_list]
-
         self._data_loader = fluid.io.DataLoader.from_generator(
             feed_list=feed_vars, capacity=3 * self._batch_size, iterable=True)
         self._data_loader.set_sample_list_generator(
             self._dataset.generator(self._batch_size, drop_last=True),
             places=self._place)
 
-        # collect the variable names for sampling
-        persistable_var_names = []
-        for var in self._program.list_vars():
-            if var.persistable:
-                persistable_var_names.append(var.name)
-
-        for op in self._program.global_block().ops:
-            op_type = op.type
-            if op_type in self._quantizable_op_type:
-                if op_type in ("conv2d", "depthwise_conv2d"):
-                    self._quantized_act_var_name.add(op.input("Input")[0])
-                    self._quantized_weight_var_name.add(op.input("Filter")[0])
-                    self._quantized_act_var_name.add(op.output("Output")[0])
-                elif op_type == "mul":
-                    if self._is_input_all_not_persistable(
-                            op, persistable_var_names):
-                        op._set_attr("skip_quant", True)
-                        logging.warning(
-                            "Skip quant a mul op for two input variables are not persistable"
-                        )
-                    else:
-                        self._quantized_act_var_name.add(op.input("X")[0])
-                        self._quantized_weight_var_name.add(op.input("Y")[0])
-                        self._quantized_act_var_name.add(op.output("Out")[0])
-                else:
-                    # process other quantizable op type, the input must all not persistable
-                    if self._is_input_all_not_persistable(
-                            op, persistable_var_names):
-                        input_output_name_list = self._op_real_in_out_name[
-                            op_type]
-                        for input_name in input_output_name_list[0]:
-                            for var_name in op.input(input_name):
-                                self._quantized_act_var_name.add(var_name)
-                        for output_name in input_output_name_list[1]:
-                            for var_name in op.output(output_name):
-                                self._quantized_act_var_name.add(var_name)
-
-        # set activation variables to be persistable, so can obtain
-        # the tensor data in sample_data
-        for var in self._program.list_vars():
-            if var.name in self._quantized_act_var_name:
-                var.persistable = True
-                
-    def _calculate_scale_factor(self):
+    def _calculate_kl_threshold(self):
         '''
-        Calculate the scale factor of quantized variables.
+        Calculate the KL threshold of quantized variables.
         '''
-        # apply channel_wise_abs_max quantization for weights
+        assert self._algo == "KL", "The algo should be KL to calculate kl threshold."
         ct = 1
+        # Abs_max threshold for weights
         for var_name in self._quantized_weight_var_name:
             start = time.time()
-            data = self._sampling_data[var_name]
-            scale_factor_per_channel = []
-            for i in range(data.shape[0]):
-                abs_max_value = np.max(np.abs(data[i]))
-                scale_factor_per_channel.append(abs_max_value)
-            self._quantized_var_scale_factor[
-                var_name] = scale_factor_per_channel
+            weight_data = self._sampling_data[var_name]
+            weight_threshold = None
+            if self._weight_quantize_type == "abs_max":
+                weight_threshold = np.max(np.abs(weight_data))
+            elif self._weight_quantize_type == "channel_wise_abs_max":
+                weight_threshold = []
+                for i in range(weight_data.shape[0]):
+                    abs_max_value = np.max(np.abs(weight_data[i]))
+                    weight_threshold.append(abs_max_value)
+            self._quantized_var_kl_threshold[var_name] = weight_threshold
             end = time.time()
             logging.debug('[Calculate weight] Weight_id={}/{}, time_each_weight={} s.'.format(
                 str(ct),
                 str(len(self._quantized_weight_var_name)),
                 str(end-start)))
             ct += 1
-            
+
         ct = 1
-        # apply kl quantization for activation
+        # KL threshold for activations
         if self._is_use_cache_file:
             for var_name in self._quantized_act_var_name:
                 start = time.time()
@@ -269,13 +254,8 @@ class PaddleXPostTrainingQuantization(PostTrainingQuantization):
                     sampling_data.append(np.load(file_path))
                     os.remove(file_path)
                 sampling_data = np.concatenate(sampling_data)
-
-                if self._algo == "KL":
-                    self._quantized_var_scale_factor[var_name] = \
-                        self._get_kl_scaling_factor(np.abs(sampling_data))
-                else:
-                    self._quantized_var_scale_factor[var_name] = \
-                        np.max(np.abs(sampling_data))
+                self._quantized_var_kl_threshold[var_name] = \
+                    self._get_kl_scaling_factor(np.abs(sampling_data))
                 end = time.time()
                 logging.debug('[Calculate activation] Activation_id={}/{}, time_each_activation={} s.'.format(
                     str(ct),
@@ -287,15 +267,13 @@ class PaddleXPostTrainingQuantization(PostTrainingQuantization):
                 start = time.time()
                 self._sampling_data[var_name] = np.concatenate(
                     self._sampling_data[var_name])
-                if self._algo == "KL":
-                    self._quantized_var_scale_factor[var_name] = \
-                        self._get_kl_scaling_factor(np.abs(self._sampling_data[var_name]))
-                else:
-                    self._quantized_var_scale_factor[var_name] = \
-                        np.max(np.abs(self._sampling_data[var_name]))
+                self._quantized_var_kl_threshold[var_name] = \
+                    self._get_kl_scaling_factor(np.abs(self._sampling_data[var_name]))
                 end = time.time()
                 logging.debug('[Calculate activation] Activation_id={}/{}, time_each_activation={} s.'.format(
                     str(ct),
                     str(len(self._quantized_act_var_name)),
                     str(end-start)))
-                ct += 1
+                ct += 1
+
+                

+ 21 - 6
paddlex/interpret/core/_session_preparation.py

@@ -13,15 +13,30 @@
 #limitations under the License.
 
 import os
+import os.path as osp
 import paddle.fluid as fluid
+import paddlex as pdx
 import numpy as np
 from paddle.fluid.param_attr import ParamAttr
-from ..as_data_reader.readers import preprocess_image
+from paddlex.interpret.as_data_reader.readers import preprocess_image
 
-root_path = os.environ['HOME']
-root_path = os.path.join(root_path, '.paddlex')
-h_pre_models = os.path.join(root_path, "pre_models")
-h_pre_models_kmeans = os.path.join(h_pre_models, "kmeans_model.pkl")
+def gen_user_home():
+    if "HOME" in os.environ:
+        home_path = os.environ["HOME"]
+        if os.path.exists(home_path) and os.path.isdir(home_path):
+            return home_path
+    return os.path.expanduser('~')
+
+
+root_path = gen_user_home()
+root_path = osp.join(root_path, '.paddlex')
+h_pre_models = osp.join(root_path, "pre_models")
+if not osp.exists(h_pre_models):
+    if not osp.exists(root_path):
+        os.makedirs(root_path)
+    url = "https://bj.bcebos.com/paddlex/interpret/pre_models.tar.gz"
+    pdx.utils.download_and_decompress(url, path=root_path)
+h_pre_models_kmeans = osp.join(h_pre_models, "kmeans_model.pkl")
 
 
 def paddle_get_fc_weights(var_name="fc_0.w_0"):
@@ -110,4 +125,4 @@ def compute_features_for_kmeans(data_content):
     images = preprocess_image(data_content)  # transpose to [N, 3, H, W], scaled to [0.0, 1.0]
     result = exe.run(prog, fetch_list=[resized_features], feed={'image': images})
 
-    return result[0][0]
+    return result[0][0]

+ 2 - 1
paddlex/interpret/core/interpretation_algorithms.py

@@ -17,9 +17,10 @@ import numpy as np
 import time
 
 from . import lime_base
-from ..as_data_reader.readers import read_image
 from ._session_preparation import paddle_get_fc_weights, compute_features_for_kmeans, h_pre_models_kmeans
 from .normlime_base import combine_normlime_and_lime, get_feature_for_kmeans, load_kmeans_model
+from paddlex.interpret.as_data_reader.readers import read_image
+
 
 import cv2
 

+ 1 - 1
paddlex/interpret/core/normlime_base.py

@@ -16,7 +16,7 @@ import os
 import numpy as np
 import glob
 
-from ..as_data_reader.readers import read_image
+from paddlex.interpret.as_data_reader.readers import read_image
 from . import lime_base
 from ._session_preparation import compute_features_for_kmeans, h_pre_models_kmeans
 

+ 2 - 2
paddlex/interpret/visualize.py

@@ -21,7 +21,7 @@ import paddlex as pdx
 from .interpretation_predict import interpretation_predict
 from .core.interpretation import Interpretation
 from .core.normlime_base import precompute_normlime_weights
-
+from .core._session_preparation import gen_user_home
 
 def visualize(img_file, 
               model, 
@@ -109,7 +109,7 @@ def get_normlime_interpreter(img, model, dataset, num_samples=3000, batch_size=5
     labels_name = None
     if dataset is not None:
         labels_name = dataset.labels
-    root_path = os.environ['HOME']
+    root_path = gen_user_home()
     root_path = osp.join(root_path, '.paddlex')
     pre_models_path = osp.join(root_path, "pre_models")
     if not osp.exists(pre_models_path):

+ 2 - 3
requirements.txt

@@ -1,9 +1,8 @@
-pyyaml
 tqdm
 colorama
 sklearn
 cython
 pycocotools
-visualdl=1.3.0
-paddleslim=1.0.1
+visualdl >= 2.0.0b
+paddleslim == 1.0.1
 shapely