提交 bb3030a8 编写于 作者: F FlyingQianMM

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

...@@ -35,6 +35,6 @@ ...@@ -35,6 +35,6 @@
- id: cpplint-cpp-source - id: cpplint-cpp-source
name: cpplint name: cpplint
description: Check C++ code style using cpplint.py. description: Check C++ code style using cpplint.py.
entry: bash cpplint_pre_commit.hook entry: bash ./tools/codestyle/cpplint_pre_commit.hook
language: system language: system
files: \.(c|cc|cxx|cpp|cu|h|hpp|hxx)$ files: \.(c|cc|cxx|cpp|cu|h|hpp|hxx)$
...@@ -66,7 +66,7 @@ int main(int argc, char** argv) { ...@@ -66,7 +66,7 @@ int main(int argc, char** argv) {
std::cout << "image file: " << image_path std::cout << "image file: " << image_path
<< ", predict label: " << result.boxes[i].category << ", predict label: " << result.boxes[i].category
<< ", label_id:" << result.boxes[i].category_id << ", label_id:" << result.boxes[i].category_id
<< ", score: " << result.boxes[i].score << ", box:(" << ", score: " << result.boxes[i].score << ", box(xmin, ymin, w, h):("
<< result.boxes[i].coordinate[0] << ", " << result.boxes[i].coordinate[0] << ", "
<< result.boxes[i].coordinate[1] << ", " << result.boxes[i].coordinate[1] << ", "
<< result.boxes[i].coordinate[2] << ", " << result.boxes[i].coordinate[2] << ", "
...@@ -89,7 +89,7 @@ int main(int argc, char** argv) { ...@@ -89,7 +89,7 @@ int main(int argc, char** argv) {
for (int i = 0; i < result.boxes.size(); ++i) { for (int i = 0; i < result.boxes.size(); ++i) {
std::cout << ", predict label: " << result.boxes[i].category std::cout << ", predict label: " << result.boxes[i].category
<< ", label_id:" << result.boxes[i].category_id << ", label_id:" << result.boxes[i].category_id
<< ", score: " << result.boxes[i].score << ", box:(" << ", score: " << result.boxes[i].score << ", box(xmin, ymin, w, h):("
<< result.boxes[i].coordinate[0] << ", " << result.boxes[i].coordinate[0] << ", "
<< result.boxes[i].coordinate[1] << ", " << result.boxes[i].coordinate[1] << ", "
<< result.boxes[i].coordinate[2] << ", " << result.boxes[i].coordinate[2] << ", "
......
...@@ -63,9 +63,10 @@ class SegResult : public BaseResult { ...@@ -63,9 +63,10 @@ class SegResult : public BaseResult {
public: public:
Mask<int64_t> label_map; Mask<int64_t> label_map;
Mask<float> score_map; Mask<float> score_map;
std::string type = "seg";
void clear() { void clear() {
label_map.clear(); label_map.clear();
score_map.clear(); score_map.clear();
} }
}; };
} // namespce of PaddleX } // namespace PaddleX
...@@ -19,30 +19,30 @@ import argparse ...@@ -19,30 +19,30 @@ import argparse
def export_lite(): def export_lite():
opt = lite.Opt() opt = lite.Opt()
model_file = os.path.join(FLAGS.model_path, '__model__') model_file = os.path.join(FLAGS.model_dir, '__model__')
params_file = os.path.join(FLAGS.model_path, '__params__') params_file = os.path.join(FLAGS.model_dir, '__params__')
opt.run_optimize("", model_file, params_file, FLAGS.place, FLAGS.save_dir) opt.run_optimize("", model_file, params_file, FLAGS.place, FLAGS.save_file)
if __name__ == '__main__': if __name__ == '__main__':
parser = argparse.ArgumentParser(description=__doc__) parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument( parser.add_argument(
"--model_path", "--model_dir",
type=str, type=str,
default="", default="",
help="model path.", help="path of '__model__' and '__params__'.",
required=True) required=True)
parser.add_argument( parser.add_argument(
"--place", "--place",
type=str, type=str,
default="arm", default="arm",
help="preprocess config path.", help="run place: 'arm|opencl|x86|npu|xpu|rknpu|apu'.",
required=True) required=True)
parser.add_argument( parser.add_argument(
"--save_dir", "--save_file",
type=str, type=str,
default="paddlex.onnx", default="paddlex.onnx",
help="Directory for storing the output visualization files.", help="file name for storing the output files.",
required=True) required=True)
FLAGS = parser.parse_args() FLAGS = parser.parse_args()
export_lite() export_lite()
...@@ -82,4 +82,4 @@ predict(self, img_file, transforms=None) ...@@ -82,4 +82,4 @@ predict(self, img_file, transforms=None)
> >
> **返回值** > **返回值**
> >
> > - **list**: 预测结果列表,列表中每个元素均为一个dict,key'bbox', 'mask', 'category', 'category_id', 'score',分别表示每个预测目标的框坐标信息、Mask信息,类别、类别id、置信度,其中框坐标信息为[xmin, ymin, w, h],即左上角x, y坐标和框的宽和高 > > - **list**: 预测结果列表,列表中每个元素均为一个dict,key'bbox', 'mask', 'category', 'category_id', 'score',分别表示每个预测目标的框坐标信息、Mask信息,类别、类别id、置信度。其中框坐标信息为[xmin, ymin, w, h],即左上角x, y坐标和框的宽和高。Mask信息为原图大小的二值图,1表示像素点属于预测类别,0表示像素点是背景
...@@ -9,7 +9,7 @@ PaddleX对于图像分类、目标检测、实例分割和语义分割内置了 ...@@ -9,7 +9,7 @@ PaddleX对于图像分类、目标检测、实例分割和语义分割内置了
| 任务类型 | 增强方法 | | 任务类型 | 增强方法 |
| :------- | :------------| | :------- | :------------|
| 图像分类 | [RandomCrop](cls_transforms.html#randomcrop)[RandomHorizontalFlip](cls_transforms.html#randomhorizontalflip)[RandomVerticalFlip](cls_transforms.html#randomverticalflip)<br> [RandomRotate](cls_transforms.html#randomratate)[RandomDistort](cls_transforms.html#randomdistort) | | 图像分类 | [RandomCrop](cls_transforms.html#randomcrop)[RandomHorizontalFlip](cls_transforms.html#randomhorizontalflip)[RandomVerticalFlip](cls_transforms.html#randomverticalflip)<br> [RandomRotate](cls_transforms.html#randomratate)[RandomDistort](cls_transforms.html#randomdistort) |
|目标检测<br>实例分割| [RandomHorizontalFlip](det_transforms.html#randomhorizontalflip)[RandomDistort](det_transforms.html#randomdistort)[RandomCrop](det_transforms.html#randomcrop)<br> [[MixupImage](det_transforms.html#mixupimage)(仅支持YOLOv3模型)RandomExpand](det_transforms.html#randomexpand) | |目标检测<br>实例分割| [RandomHorizontalFlip](det_transforms.html#randomhorizontalflip)[RandomDistort](det_transforms.html#randomdistort)[RandomCrop](det_transforms.html#randomcrop)<br> [MixupImage](det_transforms.html#mixupimage)(仅支持YOLOv3模型)[RandomExpand](det_transforms.html#randomexpand) |
|语义分割 | [RandomHorizontalFlip](seg_transforms.html#randomhorizontalflip)[RandomVerticalFlip](seg_transforms.html#randomverticalflip)[RandomRangeScaling](seg_transforms.html#randomrangescaling)<br> [RandomStepScaling](seg_transforms.html#randomstepscaling)[RandomPaddingCrop](seg_transforms.html#randompaddingcrop)[RandomBlur](seg_transforms.html#randomblur)<br> [RandomRotation](seg_transforms.html#randomrotation)[RandomScaleAspect](seg_transforms.html#randomscaleaspect)[RandomDistort](seg_transforms.html#randomdistort) | |语义分割 | [RandomHorizontalFlip](seg_transforms.html#randomhorizontalflip)[RandomVerticalFlip](seg_transforms.html#randomverticalflip)[RandomRangeScaling](seg_transforms.html#randomrangescaling)<br> [RandomStepScaling](seg_transforms.html#randomstepscaling)[RandomPaddingCrop](seg_transforms.html#randompaddingcrop)[RandomBlur](seg_transforms.html#randomblur)<br> [RandomRotation](seg_transforms.html#randomrotation)[RandomScaleAspect](seg_transforms.html#randomscaleaspect)[RandomDistort](seg_transforms.html#randomdistort) |
## imgaug增强库的支持 ## imgaug增强库的支持
......
# 移动端部署 # 移动端部署
PaddleX的移动端部署由PaddleLite实现,部署的流程如下,首先将训练好的模型导出为inference model,然后使用PaddleLite的python接口对模型进行优化,最后使用PaddleLite的预测库进行部署,
PaddleLite的详细介绍和使用可参考:[PaddleLite文档](https://paddle-lite.readthedocs.io/zh/latest/)
> PaddleX --> Inference Model --> PaddleLite Opt --> PaddleLite Inference
以下介绍如何将PaddleX导出为inference model,然后使用PaddleLite的OPT模块对模型进行优化:
step 1: 安装PaddleLite step 1: 安装PaddleLite
``` ```
...@@ -9,15 +16,21 @@ pip install paddlelite ...@@ -9,15 +16,21 @@ pip install paddlelite
step 2: 将PaddleX模型导出为inference模型 step 2: 将PaddleX模型导出为inference模型
参考[导出inference模型](deploy_server/deploy_python.html#inference)将模型导出为inference格式模型。 参考[导出inference模型](deploy_server/deploy_python.html#inference)将模型导出为inference格式模型。
**注意:由于PaddleX代码的持续更新,版本低于1.0.0的模型暂时无法直接用于预测部署,参考[模型版本升级](../upgrade_version.md)对模型版本进行升级。** **注意:由于PaddleX代码的持续更新,版本低于1.0.0的模型暂时无法直接用于预测部署,参考[模型版本升级](./upgrade_version.md)对模型版本进行升级。**
step 3: 将inference模型转换成PaddleLite模型 step 3: 将inference模型转换成PaddleLite模型
``` ```
python /path/to/PaddleX/deploy/lite/export_lite.py --model_path /path/to/inference_model --save_dir /path/to/onnx_model python /path/to/PaddleX/deploy/lite/export_lite.py --model_dir /path/to/inference_model --save_file /path/to/onnx_model --place place/to/run
``` ```
`--model_path`用于指定inference模型的路径,`--save_dir`用于指定Lite模型的保存路径。 | 参数 | 说明 |
| ---- | ---- |
| model_dir | 预测模型所在路径,包含"__model__", "__params__"文件 |
| save_file | 模型输出的名称,默认为"paddlex.nb" |
| place | 运行的平台,可选:arm|opencl|x86|npu|xpu|rknpu|apu |
step 4: 预测 step 4: 预测
......
...@@ -113,7 +113,7 @@ class VOCDetection(Dataset): ...@@ -113,7 +113,7 @@ class VOCDetection(Dataset):
is_crowd = np.zeros((len(objs), 1), dtype=np.int32) is_crowd = np.zeros((len(objs), 1), dtype=np.int32)
difficult = np.zeros((len(objs), 1), dtype=np.int32) difficult = np.zeros((len(objs), 1), dtype=np.int32)
for i, obj in enumerate(objs): for i, obj in enumerate(objs):
cname = obj.find('name').text cname = obj.find('name').text.strip()
gt_class[i][0] = cname2cid[cname] gt_class[i][0] = cname2cid[cname]
_difficult = int(obj.find('difficult').text) _difficult = int(obj.find('difficult').text)
x1 = float(obj.find('bndbox').find('xmin').text) x1 = float(obj.find('bndbox').find('xmin').text)
......
...@@ -367,7 +367,8 @@ class FasterRCNN(BaseAPI): ...@@ -367,7 +367,8 @@ class FasterRCNN(BaseAPI):
Returns: Returns:
list: 预测结果列表,每个预测结果由预测框类别标签、 list: 预测结果列表,每个预测结果由预测框类别标签、
预测框类别名称、预测框坐标、预测框得分组成。 预测框类别名称、预测框坐标(坐标格式为[xmin, ymin, w, h])、
预测框得分组成。
""" """
if transforms is None and not hasattr(self, 'test_transforms'): if transforms is None and not hasattr(self, 'test_transforms'):
raise Exception("transforms need to be defined, now is None.") raise Exception("transforms need to be defined, now is None.")
......
...@@ -333,8 +333,10 @@ class MaskRCNN(FasterRCNN): ...@@ -333,8 +333,10 @@ class MaskRCNN(FasterRCNN):
transforms (paddlex.det.transforms): 数据预处理操作。 transforms (paddlex.det.transforms): 数据预处理操作。
Returns: Returns:
dict: 预测结果列表,每个预测结果由预测框类别标签、预测框类别名称、预测框坐标、预测框内的二值图、 dict: 预测结果列表,每个预测结果由预测框类别标签、预测框类别名称、
预测框得分组成。 预测框坐标(坐标格式为[xmin, ymin, w, h])、
原图大小的预测二值图(1表示预测框类别,0表示背景类)、
预测框得分组成。
""" """
if transforms is None and not hasattr(self, 'test_transforms'): if transforms is None and not hasattr(self, 'test_transforms'):
raise Exception("transforms need to be defined, now is None.") raise Exception("transforms need to be defined, now is None.")
......
...@@ -306,11 +306,10 @@ class YOLOv3(BaseAPI): ...@@ -306,11 +306,10 @@ class YOLOv3(BaseAPI):
images = np.array([d[0] for d in data]) images = np.array([d[0] for d in data])
im_sizes = np.array([d[1] for d in data]) im_sizes = np.array([d[1] for d in data])
feed_data = {'image': images, 'im_size': im_sizes} feed_data = {'image': images, 'im_size': im_sizes}
outputs = self.exe.run( outputs = self.exe.run(self.test_prog,
self.test_prog, feed=[feed_data],
feed=[feed_data], fetch_list=list(self.test_outputs.values()),
fetch_list=list(self.test_outputs.values()), return_numpy=False)
return_numpy=False)
res = { res = {
'bbox': (np.array(outputs[0]), 'bbox': (np.array(outputs[0]),
outputs[0].recursive_sequence_lengths()) outputs[0].recursive_sequence_lengths())
...@@ -326,13 +325,13 @@ class YOLOv3(BaseAPI): ...@@ -326,13 +325,13 @@ class YOLOv3(BaseAPI):
res['gt_label'] = (res_gt_label, []) res['gt_label'] = (res_gt_label, [])
res['is_difficult'] = (res_is_difficult, []) res['is_difficult'] = (res_is_difficult, [])
results.append(res) results.append(res)
logging.debug("[EVAL] Epoch={}, Step={}/{}".format( logging.debug("[EVAL] Epoch={}, Step={}/{}".format(epoch_id, step +
epoch_id, step + 1, total_steps)) 1, total_steps))
box_ap_stats, eval_details = eval_results( box_ap_stats, eval_details = eval_results(
results, metric, eval_dataset.coco_gt, with_background=False) results, metric, eval_dataset.coco_gt, with_background=False)
evaluate_metrics = OrderedDict( evaluate_metrics = OrderedDict(
zip(['bbox_mmap' if metric == 'COCO' else 'bbox_map'], zip(['bbox_mmap'
box_ap_stats)) if metric == 'COCO' else 'bbox_map'], box_ap_stats))
if return_details: if return_details:
return evaluate_metrics, eval_details return evaluate_metrics, eval_details
return evaluate_metrics return evaluate_metrics
...@@ -346,7 +345,8 @@ class YOLOv3(BaseAPI): ...@@ -346,7 +345,8 @@ class YOLOv3(BaseAPI):
Returns: Returns:
list: 预测结果列表,每个预测结果由预测框类别标签、 list: 预测结果列表,每个预测结果由预测框类别标签、
预测框类别名称、预测框坐标、预测框得分组成。 预测框类别名称、预测框坐标(坐标格式为[xmin, ymin, w, h])、
预测框得分组成。
""" """
if transforms is None and not hasattr(self, 'test_transforms'): if transforms is None and not hasattr(self, 'test_transforms'):
raise Exception("transforms need to be defined, now is None.") raise Exception("transforms need to be defined, now is None.")
...@@ -359,14 +359,11 @@ class YOLOv3(BaseAPI): ...@@ -359,14 +359,11 @@ class YOLOv3(BaseAPI):
im, im_size = self.test_transforms(img_file) im, im_size = self.test_transforms(img_file)
im = np.expand_dims(im, axis=0) im = np.expand_dims(im, axis=0)
im_size = np.expand_dims(im_size, axis=0) im_size = np.expand_dims(im_size, axis=0)
outputs = self.exe.run( outputs = self.exe.run(self.test_prog,
self.test_prog, feed={'image': im,
feed={ 'im_size': im_size},
'image': im, fetch_list=list(self.test_outputs.values()),
'im_size': im_size return_numpy=False)
},
fetch_list=list(self.test_outputs.values()),
return_numpy=False)
res = { res = {
k: (np.array(v), v.recursive_sequence_lengths()) k: (np.array(v), v.recursive_sequence_lengths())
for k, v in zip(list(self.test_outputs.keys()), outputs) for k, v in zip(list(self.test_outputs.keys()), outputs)
......
...@@ -20,6 +20,7 @@ import six ...@@ -20,6 +20,7 @@ import six
import glob import glob
from .data_path_utils import _find_classes from .data_path_utils import _find_classes
from PIL import Image from PIL import Image
import paddlex.utils.logging as logging
def resize_short(img, target_size, interpolation=None): def resize_short(img, target_size, interpolation=None):
...@@ -117,7 +118,7 @@ def read_image(img_path, target_size=256, crop_size=224): ...@@ -117,7 +118,7 @@ def read_image(img_path, target_size=256, crop_size=224):
assert len(img_path.shape) == 4 assert len(img_path.shape) == 4
return img_path return img_path
else: else:
ValueError(f"Not recognized data type {type(img_path)}.") ValueError("Not recognized data type {}.".format(type(img_path)))
class ReaderConfig(object): class ReaderConfig(object):
...@@ -156,7 +157,7 @@ class ReaderConfig(object): ...@@ -156,7 +157,7 @@ class ReaderConfig(object):
img = cv2.imread(img_path) img = cv2.imread(img_path)
if img is None: if img is None:
print(img_path) logging.info(img_path)
continue continue
img = resize_short(img, target_size, interpolation=None) img = resize_short(img, target_size, interpolation=None)
img = crop_image(img, crop_size, center=self.is_test) img = crop_image(img, crop_size, center=self.is_test)
...@@ -208,7 +209,7 @@ def create_reader(list_image_path, list_label=None, is_test=False): ...@@ -208,7 +209,7 @@ def create_reader(list_image_path, list_label=None, is_test=False):
img = cv2.imread(img_path) img = cv2.imread(img_path)
if img is None: if img is None:
print(img_path) logging.info(img_path)
continue continue
img = resize_short(img, target_size, interpolation=None) img = resize_short(img, target_size, interpolation=None)
......
...@@ -21,6 +21,7 @@ from . import lime_base ...@@ -21,6 +21,7 @@ from . import lime_base
from ._session_preparation import paddle_get_fc_weights, compute_features_for_kmeans, gen_user_home from ._session_preparation import paddle_get_fc_weights, compute_features_for_kmeans, gen_user_home
from .normlime_base import combine_normlime_and_lime, get_feature_for_kmeans, load_kmeans_model 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 from paddlex.interpret.as_data_reader.readers import read_image
import paddlex.utils.logging as logging
import cv2 import cv2
...@@ -71,7 +72,8 @@ class CAM(object): ...@@ -71,7 +72,8 @@ class CAM(object):
if self.label_names is not None: if self.label_names is not None:
ln = self.label_names[l] ln = self.label_names[l]
print(f'predicted result: {ln} with probability {probability[pred_label[0]]:.3f}') prob_str = "%.3f" % (probability[pred_label[0]])
logging.info("predicted result: {} with probability {}.".format(ln, prob_str))
return feature_maps, fc_weights return feature_maps, fc_weights
def interpret(self, data_, visualization=True, save_to_disk=True, save_outdir=None): def interpret(self, data_, visualization=True, save_to_disk=True, save_outdir=None):
...@@ -96,7 +98,8 @@ class CAM(object): ...@@ -96,7 +98,8 @@ class CAM(object):
ax.axis("off") ax.axis("off")
axes = axes.ravel() axes = axes.ravel()
axes[0].imshow(self.image) axes[0].imshow(self.image)
axes[0].set_title(f"label {ln}, proba: {self.predicted_probability: .3f}") prob_str = "{%.3f}" % (self.predicted_probability)
axes[0].set_title("label {}, proba: {}".format(ln, prob_str))
axes[1].imshow(cam) axes[1].imshow(cam)
axes[1].set_title("CAM") axes[1].set_title("CAM")
...@@ -157,14 +160,15 @@ class LIME(object): ...@@ -157,14 +160,15 @@ class LIME(object):
if self.label_names is not None: if self.label_names is not None:
ln = self.label_names[l] ln = self.label_names[l]
print(f'predicted result: {ln} with probability {probability[pred_label[0]]:.3f}') prob_str = "%.3f" % (probability[pred_label[0]])
logging.info("predicted result: {} with probability {}.".format(ln, prob_str))
end = time.time() end = time.time()
algo = lime_base.LimeImageInterpreter() algo = lime_base.LimeImageInterpreter()
interpreter = algo.interpret_instance(self.image, self.predict_fn, self.labels, 0, interpreter = algo.interpret_instance(self.image, self.predict_fn, self.labels, 0,
num_samples=self.num_samples, batch_size=self.batch_size) num_samples=self.num_samples, batch_size=self.batch_size)
self.lime_interpreter = interpreter self.lime_interpreter = interpreter
print('lime time: ', time.time() - end, 's.') logging.info('lime time: ' + str(time.time() - end) + 's.')
def interpret(self, data_, visualization=True, save_to_disk=True, save_outdir=None): def interpret(self, data_, visualization=True, save_to_disk=True, save_outdir=None):
if self.lime_interpreter is None: if self.lime_interpreter is None:
...@@ -189,7 +193,8 @@ class LIME(object): ...@@ -189,7 +193,8 @@ class LIME(object):
ax.axis("off") ax.axis("off")
axes = axes.ravel() axes = axes.ravel()
axes[0].imshow(self.image) axes[0].imshow(self.image)
axes[0].set_title(f"label {ln}, proba: {self.predicted_probability: .3f}") prob_str = "{%.3f}" % (self.predicted_probability)
axes[0].set_title("label {}, proba: {}".format(ln, prob_str))
axes[1].imshow(mark_boundaries(self.image, self.lime_interpreter.segments)) axes[1].imshow(mark_boundaries(self.image, self.lime_interpreter.segments))
axes[1].set_title("superpixel segmentation") axes[1].set_title("superpixel segmentation")
...@@ -201,7 +206,7 @@ class LIME(object): ...@@ -201,7 +206,7 @@ class LIME(object):
l, positive_only=False, hide_rest=False, num_features=num_to_show l, positive_only=False, hide_rest=False, num_features=num_to_show
) )
axes[ncols + i].imshow(mark_boundaries(temp, mask)) axes[ncols + i].imshow(mark_boundaries(temp, mask))
axes[ncols + i].set_title(f"label {ln}, first {num_to_show} superpixels") axes[ncols + i].set_title("label {}, first {} superpixels".format(ln, num_to_show))
if save_to_disk and save_outdir is not None: if save_to_disk and save_outdir is not None:
os.makedirs(save_outdir, exist_ok=True) os.makedirs(save_outdir, exist_ok=True)
...@@ -232,8 +237,9 @@ class NormLIME(object): ...@@ -232,8 +237,9 @@ class NormLIME(object):
raise ValueError("NormLIME needs the KMeans model, where we provided a default one in " raise ValueError("NormLIME needs the KMeans model, where we provided a default one in "
"pre_models/kmeans_model.pkl.") "pre_models/kmeans_model.pkl.")
else: else:
print("Warning: It is *strongly* suggested to use the default KMeans model in pre_models/kmeans_model.pkl. " logging.debug("Warning: It is *strongly* suggested to use the \
"Use another one will change the final result.") default KMeans model in pre_models/kmeans_model.pkl. \
Use another one will change the final result.")
self.kmeans_model = load_kmeans_model(kmeans_model_for_normlime) self.kmeans_model = load_kmeans_model(kmeans_model_for_normlime)
self.num_samples = num_samples self.num_samples = num_samples
...@@ -243,7 +249,7 @@ class NormLIME(object): ...@@ -243,7 +249,7 @@ class NormLIME(object):
self.normlime_weights = np.load(normlime_weights, allow_pickle=True).item() self.normlime_weights = np.load(normlime_weights, allow_pickle=True).item()
except: except:
self.normlime_weights = None self.normlime_weights = None
print("Warning: not find the correct precomputed Normlime result.") logging.debug("Warning: not find the correct precomputed Normlime result.")
self.predict_fn = predict_fn self.predict_fn = predict_fn
...@@ -289,8 +295,7 @@ class NormLIME(object): ...@@ -289,8 +295,7 @@ class NormLIME(object):
self.predicted_probability = self._lime.predicted_probability self.predicted_probability = self._lime.predicted_probability
self.image = image_show[0] self.image = image_show[0]
self.labels = self._lime.labels self.labels = self._lime.labels
# print(f'predicted result: {self.predicted_label} with probability {self.predicted_probability: .3f}') logging.info('performing NormLIME operations ...')
print('performing NormLIME operations ...')
cluster_labels = self.predict_cluster_labels( cluster_labels = self.predict_cluster_labels(
compute_features_for_kmeans(image_show).transpose((1, 2, 0)), self._lime.lime_interpreter.segments compute_features_for_kmeans(image_show).transpose((1, 2, 0)), self._lime.lime_interpreter.segments
...@@ -329,7 +334,8 @@ class NormLIME(object): ...@@ -329,7 +334,8 @@ class NormLIME(object):
axes = axes.ravel() axes = axes.ravel()
axes[0].imshow(self.image) axes[0].imshow(self.image)
axes[0].set_title(f"label {ln}, proba: {self.predicted_probability: .3f}") prob_str = "{%.3f}" % (self.predicted_probability)
axes[0].set_title("label {}, proba: {}".format(ln, prob_str))
axes[1].imshow(mark_boundaries(self.image, self._lime.lime_interpreter.segments)) axes[1].imshow(mark_boundaries(self.image, self._lime.lime_interpreter.segments))
axes[1].set_title("superpixel segmentation") axes[1].set_title("superpixel segmentation")
...@@ -342,7 +348,7 @@ class NormLIME(object): ...@@ -342,7 +348,7 @@ class NormLIME(object):
l, positive_only=False, hide_rest=False, num_features=num_to_show l, positive_only=False, hide_rest=False, num_features=num_to_show
) )
axes[ncols + i].imshow(mark_boundaries(temp, mask)) axes[ncols + i].imshow(mark_boundaries(temp, mask))
axes[ncols + i].set_title(f"LIME: first {num_to_show} superpixels") axes[ncols + i].set_title("LIME: first {} superpixels".format(num_to_show))
# NormLIME visualization # NormLIME visualization
self._lime.lime_interpreter.local_weights = g_weights self._lime.lime_interpreter.local_weights = g_weights
...@@ -351,7 +357,7 @@ class NormLIME(object): ...@@ -351,7 +357,7 @@ class NormLIME(object):
l, positive_only=False, hide_rest=False, num_features=num_to_show l, positive_only=False, hide_rest=False, num_features=num_to_show
) )
axes[ncols * 2 + i].imshow(mark_boundaries(temp, mask)) axes[ncols * 2 + i].imshow(mark_boundaries(temp, mask))
axes[ncols * 2 + i].set_title(f"NormLIME: first {num_to_show} superpixels") axes[ncols * 2 + i].set_title("NormLIME: first {} superpixels".format(num_to_show))
# NormLIME*LIME visualization # NormLIME*LIME visualization
combined_weights = combine_normlime_and_lime(lime_weights, g_weights) combined_weights = combine_normlime_and_lime(lime_weights, g_weights)
...@@ -361,7 +367,7 @@ class NormLIME(object): ...@@ -361,7 +367,7 @@ class NormLIME(object):
l, positive_only=False, hide_rest=False, num_features=num_to_show l, positive_only=False, hide_rest=False, num_features=num_to_show
) )
axes[ncols * 3 + i].imshow(mark_boundaries(temp, mask)) axes[ncols * 3 + i].imshow(mark_boundaries(temp, mask))
axes[ncols * 3 + i].set_title(f"Combined: first {num_to_show} superpixels") axes[ncols * 3 + i].set_title("Combined: first {} superpixels".format(num_to_show))
self._lime.lime_interpreter.local_weights = lime_weights self._lime.lime_interpreter.local_weights = lime_weights
...@@ -433,32 +439,32 @@ def save_fig(data_, save_outdir, algorithm_name, num_samples=3000): ...@@ -433,32 +439,32 @@ def save_fig(data_, save_outdir, algorithm_name, num_samples=3000):
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
if isinstance(data_, str): if isinstance(data_, str):
if algorithm_name == 'cam': if algorithm_name == 'cam':
f_out = f"{algorithm_name}_{data_.split('/')[-1]}.png" f_out = "{}_{}.png".format(algorithm_name, data_.split('/')[-1])
else: else:
f_out = f"{algorithm_name}_{data_.split('/')[-1]}_s{num_samples}.png" f_out = "{}_{}_s{}.png".format(algorithm_name, data_.split('/')[-1], num_samples)
plt.savefig( plt.savefig(
os.path.join(save_outdir, f_out) os.path.join(save_outdir, f_out)
) )
else: else:
n = 0 n = 0
if algorithm_name == 'cam': if algorithm_name == 'cam':
f_out = f'cam-{n}.png' f_out = 'cam-{}.png'.format(n)
else: else:
f_out = f'{algorithm_name}_s{num_samples}-{n}.png' f_out = '{}_s{}-{}.png'.format(algorithm_name, num_samples, n)
while os.path.exists( while os.path.exists(
os.path.join(save_outdir, f_out) os.path.join(save_outdir, f_out)
): ):
n += 1 n += 1
if algorithm_name == 'cam': if algorithm_name == 'cam':
f_out = f'cam-{n}.png' f_out = 'cam-{}.png'.format(n)
else: else:
f_out = f'{algorithm_name}_s{num_samples}-{n}.png' f_out = '{}_s{}-{}.png'.format(algorithm_name, num_samples, n)
continue continue
plt.savefig( plt.savefig(
os.path.join( os.path.join(
save_outdir, f_out save_outdir, f_out
) )
) )
print('The image of intrepretation result save in {}'.format(os.path.join( logging.info('The image of intrepretation result save in {}'.format(os.path.join(
save_outdir, f_out save_outdir, f_out
))) )))
...@@ -34,6 +34,7 @@ import scipy as sp ...@@ -34,6 +34,7 @@ import scipy as sp
import tqdm import tqdm
import copy import copy
from functools import partial from functools import partial
import paddlex.utils.logging as logging
class LimeBase(object): class LimeBase(object):
...@@ -230,9 +231,9 @@ class LimeBase(object): ...@@ -230,9 +231,9 @@ class LimeBase(object):
local_pred = easy_model.predict(neighborhood_data[0, used_features].reshape(1, -1)) local_pred = easy_model.predict(neighborhood_data[0, used_features].reshape(1, -1))
if self.verbose: if self.verbose:
print('Intercept', easy_model.intercept_) logging.info('Intercept' + str(easy_model.intercept_))
print('Prediction_local', local_pred,) logging.info('Prediction_local' + str(local_pred))
print('Right:', neighborhood_labels[0, label]) logging.info('Right:' + str(neighborhood_labels[0, label]))
return (easy_model.intercept_, return (easy_model.intercept_,
sorted(zip(used_features, easy_model.coef_), sorted(zip(used_features, easy_model.coef_),
key=lambda x: np.abs(x[1]), reverse=True), key=lambda x: np.abs(x[1]), reverse=True),
...@@ -451,7 +452,6 @@ class LimeImageInterpreter(object): ...@@ -451,7 +452,6 @@ class LimeImageInterpreter(object):
d = cdist(centroids, centroids, 'sqeuclidean') d = cdist(centroids, centroids, 'sqeuclidean')
for x in np.unique(segments): for x in np.unique(segments):
# print(np.argmin(d[x]))
a = [image[segments == i] for i in np.argsort(d[x])[1:6]] a = [image[segments == i] for i in np.argsort(d[x])[1:6]]
mx = np.mean(np.concatenate(a), axis=0) mx = np.mean(np.concatenate(a), axis=0)
fudged_image[segments == x] = mx fudged_image[segments == x] = mx
......
...@@ -21,6 +21,7 @@ from paddlex.interpret.as_data_reader.readers import read_image ...@@ -21,6 +21,7 @@ from paddlex.interpret.as_data_reader.readers import read_image
import paddlex.utils.logging as logging import paddlex.utils.logging as logging
from . import lime_base from . import lime_base
from ._session_preparation import compute_features_for_kmeans, gen_user_home from ._session_preparation import compute_features_for_kmeans, gen_user_home
import paddlex.utils.logging as logging
def load_kmeans_model(fname): def load_kmeans_model(fname):
...@@ -67,7 +68,6 @@ def centroid_using_superpixels(features, segments): ...@@ -67,7 +68,6 @@ def centroid_using_superpixels(features, segments):
one_list = np.zeros((len(np.unique(segments)), features.shape[2])) one_list = np.zeros((len(np.unique(segments)), features.shape[2]))
for i, r in enumerate(regions): for i, r in enumerate(regions):
one_list[i] = features[int(r.centroid[0] + 0.5), int(r.centroid[1] + 0.5), :] one_list[i] = features[int(r.centroid[0] + 0.5), int(r.centroid[1] + 0.5), :]
# print(one_list.shape)
return one_list return one_list
...@@ -85,7 +85,7 @@ def precompute_normlime_weights(list_data_, predict_fn, num_samples=3000, batch_ ...@@ -85,7 +85,7 @@ def precompute_normlime_weights(list_data_, predict_fn, num_samples=3000, batch_
precompute_lime_weights(list_data_, predict_fn, num_samples, batch_size, save_dir) precompute_lime_weights(list_data_, predict_fn, num_samples, batch_size, save_dir)
# load precomputed results, compute normlime weights and save. # load precomputed results, compute normlime weights and save.
fname_list = glob.glob(os.path.join(save_dir, f'lime_weights_s{num_samples}*.npy')) fname_list = glob.glob(os.path.join(save_dir, 'lime_weights_s{}*.npy'.format(num_samples)))
return compute_normlime_weights(fname_list, save_dir, num_samples) return compute_normlime_weights(fname_list, save_dir, num_samples)
...@@ -117,10 +117,10 @@ def precompute_lime_weights(list_data_, predict_fn, num_samples, batch_size, sav ...@@ -117,10 +117,10 @@ def precompute_lime_weights(list_data_, predict_fn, num_samples, batch_size, sav
for data_index, each_data_ in enumerate(list_data_): for data_index, each_data_ in enumerate(list_data_):
if isinstance(each_data_, str): if isinstance(each_data_, str):
save_path = f"lime_weights_s{num_samples}_{each_data_.split('/')[-1].split('.')[0]}.npy" save_path = "lime_weights_s{}_{}.npy".format(num_samples, each_data_.split('/')[-1].split('.')[0])
save_path = os.path.join(save_dir, save_path) save_path = os.path.join(save_dir, save_path)
else: else:
save_path = f"lime_weights_s{num_samples}_{data_index}.npy" save_path = "lime_weights_s{}_{}.npy".format(num_samples, data_index)
save_path = os.path.join(save_dir, save_path) save_path = os.path.join(save_dir, save_path)
if os.path.exists(save_path): if os.path.exists(save_path):
...@@ -174,16 +174,16 @@ def precompute_lime_weights(list_data_, predict_fn, num_samples, batch_size, sav ...@@ -174,16 +174,16 @@ def precompute_lime_weights(list_data_, predict_fn, num_samples, batch_size, sav
def compute_normlime_weights(a_list_lime_fnames, save_dir, lime_num_samples): def compute_normlime_weights(a_list_lime_fnames, save_dir, lime_num_samples):
normlime_weights_all_labels = {} normlime_weights_all_labels = {}
for f in a_list_lime_fnames: for f in a_list_lime_fnames:
try: try:
lime_weights_and_cluster = np.load(f, allow_pickle=True).item() lime_weights_and_cluster = np.load(f, allow_pickle=True).item()
lime_weights = lime_weights_and_cluster['lime_weights'] lime_weights = lime_weights_and_cluster['lime_weights']
cluster = lime_weights_and_cluster['cluster'] cluster = lime_weights_and_cluster['cluster']
except: except:
print('When loading precomputed LIME result, skipping', f) logging.info('When loading precomputed LIME result, skipping' + str(f))
continue continue
print('Loading precomputed LIME result,', f) logging.info('Loading precomputed LIME result,' + str(f))
pred_labels = lime_weights.keys() pred_labels = lime_weights.keys()
for y in pred_labels: for y in pred_labels:
normlime_weights = normlime_weights_all_labels.get(y, {}) normlime_weights = normlime_weights_all_labels.get(y, {})
...@@ -207,23 +207,23 @@ def compute_normlime_weights(a_list_lime_fnames, save_dir, lime_num_samples): ...@@ -207,23 +207,23 @@ def compute_normlime_weights(a_list_lime_fnames, save_dir, lime_num_samples):
# check normlime # check normlime
if len(normlime_weights_all_labels.keys()) < max(normlime_weights_all_labels.keys()) + 1: if len(normlime_weights_all_labels.keys()) < max(normlime_weights_all_labels.keys()) + 1:
print( logging.info(
"\n" "\n" + \
"Warning: !!! \n" "Warning: !!! \n" + \
f"There are at least {max(normlime_weights_all_labels.keys()) + 1} classes, " "There are at least {} classes, ".format(max(normlime_weights_all_labels.keys()) + 1) + \
f"but the NormLIME has results of only {len(normlime_weights_all_labels.keys())} classes. \n" "but the NormLIME has results of only {} classes. \n".format(len(normlime_weights_all_labels.keys())) + \
"It may have cause unstable results in the later computation" "It may have cause unstable results in the later computation" + \
" but can be improved by computing more test samples." " but can be improved by computing more test samples." + \
"\n" "\n"
) )
n = 0 n = 0
f_out = f'normlime_weights_s{lime_num_samples}_samples_{len(a_list_lime_fnames)}-{n}.npy' f_out = 'normlime_weights_s{}_samples_{}-{}.npy'.format(lime_num_samples, len(a_list_lime_fnames), n)
while os.path.exists( while os.path.exists(
os.path.join(save_dir, f_out) os.path.join(save_dir, f_out)
): ):
n += 1 n += 1
f_out = f'normlime_weights_s{lime_num_samples}_samples_{len(a_list_lime_fnames)}-{n}.npy' f_out = 'normlime_weights_s{}_samples_{}-{}.npy'.format(lime_num_samples, len(a_list_lime_fnames), n)
continue continue
np.save( np.save(
......
#!/bin/bash
set -e
readonly VERSION="3.8"
version=$(clang-format -version)
if ! [[ $version == *"$VERSION"* ]]; then
echo "clang-format version check failed."
echo "a version contains '$VERSION' is needed, but get '$version'"
echo "you can install the right version, and make an soft-link to '\$PATH' env"
exit -1
fi
clang-format $@
#!/bin/bash
TOTAL_ERRORS=0
if [[ ! $TRAVIS_BRANCH ]]; then
# install cpplint on local machine.
if [[ ! $(which cpplint) ]]; then
pip install cpplint
fi
# diff files on local machine.
files=$(git diff --cached --name-status | awk '$1 != "D" {print $2}')
else
# diff files between PR and latest commit on Travis CI.
branch_ref=$(git rev-parse "$TRAVIS_BRANCH")
head_ref=$(git rev-parse HEAD)
files=$(git diff --name-status $branch_ref $head_ref | awk '$1 != "D" {print $2}')
fi
# The trick to remove deleted files: https://stackoverflow.com/a/2413151
for file in $files; do
if [[ $file =~ ^(patches/.*) ]]; then
continue;
else
cpplint --filter=-readability/fn_size $file;
TOTAL_ERRORS=$(expr $TOTAL_ERRORS + $?);
fi
done
exit $TOTAL_ERRORS
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册