::iterator it;
+ for (it = this->label_list_.begin(); it != this->label_list_.end();) {
+ if (*it == "") {
+ it = this->label_list_.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+ // add_special_char
this->label_list_.insert(this->label_list_.begin(), this->beg);
this->label_list_.push_back(this->end);
}
@@ -363,12 +376,12 @@ void TablePostProcessor::Run(
std::vector &rec_scores, std::vector &loc_preds_shape,
std::vector &structure_probs_shape,
std::vector> &rec_html_tag_batch,
- std::vector>>> &rec_boxes_batch,
+ std::vector>> &rec_boxes_batch,
std::vector &width_list, std::vector &height_list) {
for (int batch_idx = 0; batch_idx < structure_probs_shape[0]; batch_idx++) {
// image tags and boxs
std::vector rec_html_tags;
- std::vector>> rec_boxes;
+ std::vector> rec_boxes;
float score = 0.f;
int count = 0;
@@ -378,7 +391,7 @@ void TablePostProcessor::Run(
// step
for (int step_idx = 0; step_idx < structure_probs_shape[1]; step_idx++) {
std::string html_tag;
- std::vector> rec_box;
+ std::vector rec_box;
// html tag
int step_start_idx = (batch_idx * structure_probs_shape[1] + step_idx) *
structure_probs_shape[2];
@@ -399,17 +412,19 @@ void TablePostProcessor::Run(
count += 1;
score += char_score;
rec_html_tags.push_back(html_tag);
+
// box
if (html_tag == "| " || html_tag == " | | ") {
- for (int point_idx = 0; point_idx < loc_preds_shape[2];
- point_idx += 2) {
- std::vector point(2, 0);
+ for (int point_idx = 0; point_idx < loc_preds_shape[2]; point_idx++) {
step_start_idx = (batch_idx * structure_probs_shape[1] + step_idx) *
loc_preds_shape[2] +
point_idx;
- point[0] = int(loc_preds[step_start_idx] * width_list[batch_idx]);
- point[1] =
- int(loc_preds[step_start_idx + 1] * height_list[batch_idx]);
+ float point = loc_preds[step_start_idx];
+ if (point_idx % 2 == 0) {
+ point = int(point * width_list[batch_idx]);
+ } else {
+ point = int(point * height_list[batch_idx]);
+ }
rec_box.push_back(point);
}
rec_boxes.push_back(rec_box);
diff --git a/deploy/cpp_infer/src/structure_table.cpp b/deploy/cpp_infer/src/structure_table.cpp
index bbc32580e4..7df0ab94b5 100644
--- a/deploy/cpp_infer/src/structure_table.cpp
+++ b/deploy/cpp_infer/src/structure_table.cpp
@@ -20,7 +20,7 @@ void StructureTableRecognizer::Run(
std::vector img_list,
std::vector> &structure_html_tags,
std::vector &structure_scores,
- std::vector>>> &structure_boxes,
+ std::vector>> &structure_boxes,
std::vector ×) {
std::chrono::duration preprocess_diff =
std::chrono::steady_clock::now() - std::chrono::steady_clock::now();
@@ -89,8 +89,7 @@ void StructureTableRecognizer::Run(
auto postprocess_start = std::chrono::steady_clock::now();
std::vector> structure_html_tag_batch;
std::vector structure_score_batch;
- std::vector>>>
- structure_boxes_batch;
+ std::vector>> structure_boxes_batch;
this->post_processor_.Run(loc_preds, structure_probs, structure_score_batch,
predict_shape0, predict_shape1,
structure_html_tag_batch, structure_boxes_batch,
diff --git a/deploy/cpp_infer/src/utility.cpp b/deploy/cpp_infer/src/utility.cpp
index 251184b91b..0e6ba17fc3 100644
--- a/deploy/cpp_infer/src/utility.cpp
+++ b/deploy/cpp_infer/src/utility.cpp
@@ -65,6 +65,37 @@ void Utility::VisualizeBboxes(const cv::Mat &srcimg,
<< std::endl;
}
+void Utility::VisualizeBboxes(const cv::Mat &srcimg,
+ const StructurePredictResult &structure_result,
+ const std::string &save_path) {
+ cv::Mat img_vis;
+ srcimg.copyTo(img_vis);
+ for (int n = 0; n < structure_result.cell_box.size(); n++) {
+ if (structure_result.cell_box[n].size() == 8) {
+ cv::Point rook_points[4];
+ for (int m = 0; m < structure_result.cell_box[n].size(); m += 2) {
+ rook_points[m / 2] =
+ cv::Point(int(structure_result.cell_box[n][m]),
+ int(structure_result.cell_box[n][m + 1]));
+ }
+ const cv::Point *ppt[1] = {rook_points};
+ int npt[] = {4};
+ cv::polylines(img_vis, ppt, npt, 1, 1, CV_RGB(0, 255, 0), 2, 8, 0);
+ } else if (structure_result.cell_box[n].size() == 4) {
+ cv::Point rook_points[2];
+ rook_points[0] = cv::Point(int(structure_result.cell_box[n][0]),
+ int(structure_result.cell_box[n][1]));
+ rook_points[1] = cv::Point(int(structure_result.cell_box[n][2]),
+ int(structure_result.cell_box[n][3]));
+ cv::rectangle(img_vis, rook_points[0], rook_points[1], CV_RGB(0, 255, 0),
+ 2, 8, 0);
+ }
+ }
+
+ cv::imwrite(save_path, img_vis);
+ std::cout << "The table visualized image saved in " + save_path << std::endl;
+}
+
// list all files under a directory
void Utility::GetAllFiles(const char *dir_name,
std::vector &all_inputs) {
@@ -270,12 +301,44 @@ void Utility::sorted_boxes(std::vector &ocr_result) {
std::sort(ocr_result.begin(), ocr_result.end(), Utility::comparison_box);
if (ocr_result.size() > 0) {
for (int i = 0; i < ocr_result.size() - 1; i++) {
- if (abs(ocr_result[i + 1].box[0][1] - ocr_result[i].box[0][1]) < 10 &&
- (ocr_result[i + 1].box[0][0] < ocr_result[i].box[0][0])) {
- std::swap(ocr_result[i], ocr_result[i + 1]);
+ for (int j = i; j > 0; j--) {
+ if (abs(ocr_result[j + 1].box[0][1] - ocr_result[j].box[0][1]) < 10 &&
+ (ocr_result[j + 1].box[0][0] < ocr_result[j].box[0][0])) {
+ std::swap(ocr_result[i], ocr_result[i + 1]);
+ }
}
}
}
}
+std::vector Utility::xyxyxyxy2xyxy(std::vector> &box) {
+ int x_collect[4] = {box[0][0], box[1][0], box[2][0], box[3][0]};
+ int y_collect[4] = {box[0][1], box[1][1], box[2][1], box[3][1]};
+ int left = int(*std::min_element(x_collect, x_collect + 4));
+ int right = int(*std::max_element(x_collect, x_collect + 4));
+ int top = int(*std::min_element(y_collect, y_collect + 4));
+ int bottom = int(*std::max_element(y_collect, y_collect + 4));
+ std::vector box1(4, 0);
+ box1[0] = left;
+ box1[1] = top;
+ box1[2] = right;
+ box1[3] = bottom;
+ return box1;
+}
+
+std::vector Utility::xyxyxyxy2xyxy(std::vector &box) {
+ int x_collect[4] = {box[0], box[2], box[4], box[6]};
+ int y_collect[4] = {box[1], box[3], box[5], box[7]};
+ int left = int(*std::min_element(x_collect, x_collect + 4));
+ int right = int(*std::max_element(x_collect, x_collect + 4));
+ int top = int(*std::min_element(y_collect, y_collect + 4));
+ int bottom = int(*std::max_element(y_collect, y_collect + 4));
+ std::vector box1(4, 0);
+ box1[0] = left;
+ box1[1] = top;
+ box1[2] = right;
+ box1[3] = bottom;
+ return box1;
+}
+
} // namespace PaddleOCR
\ No newline at end of file
diff --git a/ppstructure/utility.py b/ppstructure/utility.py
index bdea0af69e..97b6d6fec0 100644
--- a/ppstructure/utility.py
+++ b/ppstructure/utility.py
@@ -32,7 +32,7 @@ def init_args():
parser.add_argument(
"--table_char_dict_path",
type=str,
- default="../ppocr/utils/dict/table_structure_dict.txt")
+ default="../ppocr/utils/dict/table_structure_dict_ch.txt")
# params for layout
parser.add_argument("--layout_model_dir", type=str)
parser.add_argument(
From 149e118474a217e22c3a923436f21086220bdab5 Mon Sep 17 00:00:00 2001
From: qili93
Date: Tue, 13 Sep 2022 14:11:21 +0800
Subject: [PATCH 42/53] [TIPC] add scripts for NPU and XPU, test=develop
---
test_tipc/test_train_inference_python_npu.sh | 52 ++++++++++++++++++++
test_tipc/test_train_inference_python_xpu.sh | 52 ++++++++++++++++++++
tools/infer/utility.py | 26 +++++++---
tools/program.py | 45 ++++++-----------
4 files changed, 137 insertions(+), 38 deletions(-)
create mode 100644 test_tipc/test_train_inference_python_npu.sh
create mode 100644 test_tipc/test_train_inference_python_xpu.sh
diff --git a/test_tipc/test_train_inference_python_npu.sh b/test_tipc/test_train_inference_python_npu.sh
new file mode 100644
index 0000000000..bab70fc78e
--- /dev/null
+++ b/test_tipc/test_train_inference_python_npu.sh
@@ -0,0 +1,52 @@
+#!/bin/bash
+source test_tipc/common_func.sh
+
+function readlinkf() {
+ perl -MCwd -e 'print Cwd::abs_path shift' "$1";
+}
+
+function func_parser_config() {
+ strs=$1
+ IFS=" "
+ array=(${strs})
+ tmp=${array[2]}
+ echo ${tmp}
+}
+
+BASEDIR=$(dirname "$0")
+REPO_ROOT_PATH=$(readlinkf ${BASEDIR}/../)
+
+FILENAME=$1
+
+# disable mkldnn on non x86_64 env
+arch=$(uname -i)
+if [ $arch != 'x86_64' ]; then
+ sed -i 's/--enable_mkldnn:True|False/--enable_mkldnn:False/g' $FILENAME
+ sed -i 's/--enable_mkldnn:True/--enable_mkldnn:False/g' $FILENAME
+fi
+
+# change gpu to npu in tipc txt configs
+sed -i 's/use_gpu/use_npu/g' $FILENAME
+# disable benchmark as AutoLog required nvidia-smi command
+sed -i 's/--benchmark:True/--benchmark:False/g' $FILENAME
+dataline=`cat $FILENAME`
+
+# parser params
+IFS=$'\n'
+lines=(${dataline})
+
+# replace training config file
+grep -n 'tools/.*yml' $FILENAME | cut -d ":" -f 1 \
+| while read line_num ; do
+ train_cmd=$(func_parser_value "${lines[line_num-1]}")
+ trainer_config=$(func_parser_config ${train_cmd})
+ sed -i 's/use_gpu/use_npu/g' "$REPO_ROOT_PATH/$trainer_config"
+done
+
+# change gpu to npu in execution script
+sed -i 's/\"gpu\"/\"npu\"/g' test_tipc/test_train_inference_python.sh
+
+# pass parameters to test_train_inference_python.sh
+cmd='bash test_tipc/test_train_inference_python.sh ${FILENAME} $2'
+echo -e '\033[1;32m Started to run command: ${cmd}! \033[0m'
+eval $cmd
diff --git a/test_tipc/test_train_inference_python_xpu.sh b/test_tipc/test_train_inference_python_xpu.sh
new file mode 100644
index 0000000000..7c6dc1e52a
--- /dev/null
+++ b/test_tipc/test_train_inference_python_xpu.sh
@@ -0,0 +1,52 @@
+#!/bin/bash
+source test_tipc/common_func.sh
+
+function readlinkf() {
+ perl -MCwd -e 'print Cwd::abs_path shift' "$1";
+}
+
+function func_parser_config() {
+ strs=$1
+ IFS=" "
+ array=(${strs})
+ tmp=${array[2]}
+ echo ${tmp}
+}
+
+BASEDIR=$(dirname "$0")
+REPO_ROOT_PATH=$(readlinkf ${BASEDIR}/../)
+
+FILENAME=$1
+
+# disable mkldnn on non x86_64 env
+arch=$(uname -i)
+if [ $arch != 'x86_64' ]; then
+ sed -i 's/--enable_mkldnn:True|False/--enable_mkldnn:False/g' $FILENAME
+ sed -i 's/--enable_mkldnn:True/--enable_mkldnn:False/g' $FILENAME
+fi
+
+# change gpu to xpu in tipc txt configs
+sed -i 's/use_gpu/use_xpu/g' $FILENAME
+# disable benchmark as AutoLog required nvidia-smi command
+sed -i 's/--benchmark:True/--benchmark:False/g' $FILENAME
+dataline=`cat $FILENAME`
+
+# parser params
+IFS=$'\n'
+lines=(${dataline})
+
+# replace training config file
+grep -n 'tools/.*yml' $FILENAME | cut -d ":" -f 1 \
+| while read line_num ; do
+ train_cmd=$(func_parser_value "${lines[line_num-1]}")
+ trainer_config=$(func_parser_config ${train_cmd})
+ sed -i 's/use_gpu/use_xpu/g' "$REPO_ROOT_PATH/$trainer_config"
+done
+
+# change gpu to xpu in execution script
+sed -i 's/\"gpu\"/\"xpu\"/g' test_tipc/test_train_inference_python.sh
+
+# pass parameters to test_train_inference_python.sh
+cmd='bash test_tipc/test_train_inference_python.sh ${FILENAME} $2'
+echo -e '\033[1;32m Started to run command: ${cmd}! \033[0m'
+eval $cmd
diff --git a/tools/infer/utility.py b/tools/infer/utility.py
index 04de23260f..463360091c 100644
--- a/tools/infer/utility.py
+++ b/tools/infer/utility.py
@@ -36,6 +36,7 @@ def init_args():
# params for prediction engine
parser.add_argument("--use_gpu", type=str2bool, default=True)
parser.add_argument("--use_xpu", type=str2bool, default=False)
+ parser.add_argument("--use_npu", type=str2bool, default=False)
parser.add_argument("--ir_optim", type=str2bool, default=True)
parser.add_argument("--use_tensorrt", type=str2bool, default=False)
parser.add_argument("--min_subgraph_size", type=int, default=15)
@@ -245,6 +246,8 @@ def create_predictor(args, mode, logger):
f"when using tensorrt, dynamic shape is a suggested option, you can use '--shape_info_filename=shape.txt' for offline dygnamic shape tuning"
)
+ elif args.use_npu:
+ config.enable_npu()
elif args.use_xpu:
config.enable_xpu(10 * 1024 * 1024)
else:
@@ -413,7 +416,8 @@ def draw_ocr_box_txt(image,
for idx, (box, txt) in enumerate(zip(boxes, txts)):
if scores is not None and scores[idx] < drop_score:
continue
- color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
+ color = (random.randint(0, 255), random.randint(0, 255),
+ random.randint(0, 255))
draw_left.polygon(box, fill=color)
img_right_text = draw_box_txt_fine((w, h), box, txt, font_path)
pts = np.array(box, np.int32).reshape((-1, 1, 2))
@@ -427,8 +431,10 @@ def draw_ocr_box_txt(image,
def draw_box_txt_fine(img_size, box, txt, font_path="./doc/fonts/simfang.ttf"):
- box_height = int(math.sqrt((box[0][0] - box[3][0])**2 + (box[0][1] - box[3][1])**2))
- box_width = int(math.sqrt((box[0][0] - box[1][0])**2 + (box[0][1] - box[1][1])**2))
+ box_height = int(
+ math.sqrt((box[0][0] - box[3][0])**2 + (box[0][1] - box[3][1])**2))
+ box_width = int(
+ math.sqrt((box[0][0] - box[1][0])**2 + (box[0][1] - box[1][1])**2))
if box_height > 2 * box_width and box_height > 30:
img_text = Image.new('RGB', (box_height, box_width), (255, 255, 255))
@@ -444,15 +450,19 @@ def draw_box_txt_fine(img_size, box, txt, font_path="./doc/fonts/simfang.ttf"):
font = create_font(txt, (box_width, box_height), font_path)
draw_text.text([0, 0], txt, fill=(0, 0, 0), font=font)
- pts1 = np.float32([[0, 0], [box_width, 0], [box_width, box_height], [0, box_height]])
+ pts1 = np.float32(
+ [[0, 0], [box_width, 0], [box_width, box_height], [0, box_height]])
pts2 = np.array(box, dtype=np.float32)
M = cv2.getPerspectiveTransform(pts1, pts2)
img_text = np.array(img_text, dtype=np.uint8)
- img_right_text = cv2.warpPerspective(img_text, M, img_size,
- flags=cv2.INTER_NEAREST,
- borderMode=cv2.BORDER_CONSTANT,
- borderValue=(255, 255, 255))
+ img_right_text = cv2.warpPerspective(
+ img_text,
+ M,
+ img_size,
+ flags=cv2.INTER_NEAREST,
+ borderMode=cv2.BORDER_CONSTANT,
+ borderValue=(255, 255, 255))
return img_right_text
diff --git a/tools/program.py b/tools/program.py
index 16d3d4035a..c91e66fd7f 100755
--- a/tools/program.py
+++ b/tools/program.py
@@ -114,7 +114,7 @@ def merge_config(config, opts):
return config
-def check_device(use_gpu, use_xpu=False):
+def check_device(use_gpu, use_xpu=False, use_npu=False):
"""
Log error and exit when set use_gpu=true in paddlepaddle
cpu version.
@@ -134,24 +134,8 @@ def check_device(use_gpu, use_xpu=False):
if use_xpu and not paddle.device.is_compiled_with_xpu():
print(err.format("use_xpu", "xpu", "xpu", "use_xpu"))
sys.exit(1)
- except Exception as e:
- pass
-
-
-def check_xpu(use_xpu):
- """
- Log error and exit when set use_xpu=true in paddlepaddle
- cpu/gpu version.
- """
- err = "Config use_xpu cannot be set as true while you are " \
- "using paddlepaddle cpu/gpu version ! \nPlease try: \n" \
- "\t1. Install paddlepaddle-xpu to run model on XPU \n" \
- "\t2. Set use_xpu as false in config file to run " \
- "model on CPU/GPU"
-
- try:
- if use_xpu and not paddle.is_compiled_with_xpu():
- print(err)
+ if use_npu and not paddle.device.is_compiled_with_npu():
+ print(err.format("use_npu", "npu", "npu", "use_npu"))
sys.exit(1)
except Exception as e:
pass
@@ -279,7 +263,9 @@ def train(config,
model_average = True
# use amp
if scaler:
- with paddle.amp.auto_cast(level=amp_level, custom_black_list=amp_custom_black_list):
+ with paddle.amp.auto_cast(
+ level=amp_level,
+ custom_black_list=amp_custom_black_list):
if model_type == 'table' or extra_input:
preds = model(images, data=batch[1:])
elif model_type in ["kie"]:
@@ -479,7 +465,7 @@ def eval(model,
extra_input=False,
scaler=None,
amp_level='O2',
- amp_custom_black_list = []):
+ amp_custom_black_list=[]):
model.eval()
with paddle.no_grad():
total_frame = 0.0
@@ -500,7 +486,9 @@ def eval(model,
# use amp
if scaler:
- with paddle.amp.auto_cast(level=amp_level, custom_black_list=amp_custom_black_list):
+ with paddle.amp.auto_cast(
+ level=amp_level,
+ custom_black_list=amp_custom_black_list):
if model_type == 'table' or extra_input:
preds = model(images, data=batch[1:])
elif model_type in ["kie"]:
@@ -627,14 +615,9 @@ def preprocess(is_train=False):
logger = get_logger(log_file=log_file)
# check if set use_gpu=True in paddlepaddle cpu version
- use_gpu = config['Global']['use_gpu']
+ use_gpu = config['Global'].get('use_gpu', False)
use_xpu = config['Global'].get('use_xpu', False)
-
- # check if set use_xpu=True in paddlepaddle cpu/gpu version
- use_xpu = False
- if 'use_xpu' in config['Global']:
- use_xpu = config['Global']['use_xpu']
- check_xpu(use_xpu)
+ use_npu = config['Global'].get('use_npu', False)
alg = config['Architecture']['algorithm']
assert alg in [
@@ -647,10 +630,12 @@ def preprocess(is_train=False):
if use_xpu:
device = 'xpu:{0}'.format(os.getenv('FLAGS_selected_xpus', 0))
+ elif use_npu:
+ device = 'npu:{0}'.format(os.getenv('FLAGS_selected_npus', 0))
else:
device = 'gpu:{}'.format(dist.ParallelEnv()
.dev_id) if use_gpu else 'cpu'
- check_device(use_gpu, use_xpu)
+ check_device(use_gpu, use_xpu, use_npu)
device = paddle.set_device(device)
From 4589f51b5ca5a96eabab1ac60964450b479f2fe3 Mon Sep 17 00:00:00 2001
From: littletomatodonkey
Date: Wed, 14 Sep 2022 10:26:27 +0800
Subject: [PATCH 43/53] fix (#7587)
---
deploy/slim/quantization/export_model.py | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/deploy/slim/quantization/export_model.py b/deploy/slim/quantization/export_model.py
index fd1c3e5e10..bd132b6251 100755
--- a/deploy/slim/quantization/export_model.py
+++ b/deploy/slim/quantization/export_model.py
@@ -151,17 +151,24 @@ def main():
arch_config = config["Architecture"]
- arch_config = config["Architecture"]
+ if arch_config["algorithm"] == "SVTR" and arch_config["Head"][
+ "name"] != 'MultiHead':
+ input_shape = config["Eval"]["dataset"]["transforms"][-2][
+ 'SVTRRecResizeImg']['image_shape']
+ else:
+ input_shape = None
if arch_config["algorithm"] in ["Distillation", ]: # distillation model
archs = list(arch_config["Models"].values())
for idx, name in enumerate(model.model_name_list):
sub_model_save_path = os.path.join(save_path, name, "inference")
export_single_model(model.model_list[idx], archs[idx],
- sub_model_save_path, logger, quanter)
+ sub_model_save_path, logger, input_shape,
+ quanter)
else:
save_path = os.path.join(save_path, "inference")
- export_single_model(model, arch_config, save_path, logger, quanter)
+ export_single_model(model, arch_config, save_path, logger, input_shape,
+ quanter)
if __name__ == "__main__":
From 9e9e6d37c1ef4249eca97da0a8016ce80fe31bef Mon Sep 17 00:00:00 2001
From: andyjpaddle
Date: Wed, 14 Sep 2022 06:50:43 +0000
Subject: [PATCH 44/53] update tipc doc
---
.../docs/mac_test_train_inference_python.md | 41 ++++------
test_tipc/docs/test_inference_cpp.md | 22 +++--
test_tipc/docs/test_paddle2onnx.md | 15 ++--
test_tipc/docs/test_ptq_inference_python.md | 51 ++++++++++++
test_tipc/docs/test_serving.md | 51 +++---------
test_tipc/docs/test_train_inference_python.md | 80 +++++++++++--------
.../docs/win_test_train_inference_python.md | 42 ++++------
7 files changed, 162 insertions(+), 140 deletions(-)
create mode 100644 test_tipc/docs/test_ptq_inference_python.md
diff --git a/test_tipc/docs/mac_test_train_inference_python.md b/test_tipc/docs/mac_test_train_inference_python.md
index c37291a8fc..2c25bcddac 100644
--- a/test_tipc/docs/mac_test_train_inference_python.md
+++ b/test_tipc/docs/mac_test_train_inference_python.md
@@ -1,6 +1,6 @@
# Mac端基础训练预测功能测试
-Mac端基础训练预测功能测试的主程序为`test_train_inference_python.sh`,可以测试基于Python的模型CPU训练,包括裁剪、量化、蒸馏训练,以及评估、CPU推理等基本功能。
+Mac端基础训练预测功能测试的主程序为`test_train_inference_python.sh`,可以测试基于Python的模型CPU训练,包括裁剪、PACT在线量化、蒸馏训练,以及评估、CPU推理等基本功能。
注:Mac端测试用法同linux端测试方法类似,但是无需测试需要在GPU上运行的测试。
@@ -10,7 +10,7 @@ Mac端基础训练预测功能测试的主程序为`test_train_inference_python.
| 算法名称 | 模型名称 | 单机单卡(CPU) | 单机多卡 | 多机多卡 | 模型压缩(CPU) |
| :---- | :---- | :---- | :---- | :---- | :---- |
-| DB | ch_ppocr_mobile_v2.0_det| 正常训练 | - | - | 正常训练:FPGM裁剪、PACT量化 离线量化(无需训练) |
+| DB | ch_ppocr_mobile_v2.0_det| 正常训练 | - | - | 正常训练:FPGM裁剪、PACT量化 |
- 预测相关:基于训练是否使用量化,可以将训练产出的模型可以分为`正常模型`和`量化模型`,这两类模型对应的预测功能汇总如下,
@@ -26,7 +26,7 @@ Mac端基础训练预测功能测试的主程序为`test_train_inference_python.
Mac端无GPU,环境准备只需要Python环境即可,安装PaddlePaddle等依赖参考下述文档。
### 2.1 安装依赖
-- 安装PaddlePaddle >= 2.0
+- 安装PaddlePaddle >= 2.3
- 安装PaddleOCR依赖
```
pip install -r ../requirements.txt
@@ -37,7 +37,7 @@ Mac端无GPU,环境准备只需要Python环境即可,安装PaddlePaddle等
cd AutoLog
pip install -r requirements.txt
python setup.py bdist_wheel
- pip install ./dist/auto_log-1.0.0-py3-none-any.whl
+ pip install ./dist/auto_log-1.2.0-py3-none-any.whl
cd ../
```
- 安装PaddleSlim (可选)
@@ -49,53 +49,46 @@ Mac端无GPU,环境准备只需要Python环境即可,安装PaddlePaddle等
### 2.2 功能测试
-先运行`prepare.sh`准备数据和模型,然后运行`test_train_inference_python.sh`进行测试,最终在```test_tipc/output```目录下生成`python_infer_*.log`格式的日志文件。
+先运行`prepare.sh`准备数据和模型,然后运行`test_train_inference_python.sh`进行测试,最终在```test_tipc/output```目录下生成`,model_name/lite_train_lite_infer/*.log`格式的日志文件。
-`test_train_inference_python.sh`包含5种运行模式,每种模式的运行数据不同,分别用于测试速度和精度,分别是:
+`test_train_inference_python.sh`包含基础链条的4种运行模式,每种模式的运行数据不同,分别用于测试速度和精度,分别是:
- 模式1:lite_train_lite_infer,使用少量数据训练,用于快速验证训练到预测的走通流程,不验证精度和速度;
```shell
# 同linux端运行不同的是,Mac端测试使用新的配置文件mac_ppocr_det_mobile_params.txt,
# 配置文件中默认去掉了GPU和mkldnn相关的测试链条
-bash test_tipc/prepare.sh ./test_tipc/configs/ch_ppocr_mobile_v2.0_det/train_mac_cpu_normal_normal_infer_python_mac_cpu.txt 'lite_train_lite_infer'
-bash test_tipc/test_train_inference_python.sh ./test_tipc/configs/ch_ppocr_mobile_v2.0_det/train_mac_cpu_normal_normal_infer_python_mac_cpu.txt 'lite_train_lite_infer'
+bash test_tipc/prepare.sh ./test_tipc/configs/ch_ppocr_mobile_v2_0_det/train_mac_cpu_normal_normal_infer_python_mac_cpu.txt 'lite_train_lite_infer'
+bash test_tipc/test_train_inference_python.sh ./test_tipc/configs/ch_ppocr_mobile_v2_0_det/train_mac_cpu_normal_normal_infer_python_mac_cpu.txt 'lite_train_lite_infer'
```
- 模式2:lite_train_whole_infer,使用少量数据训练,一定量数据预测,用于验证训练后的模型执行预测,预测速度是否合理;
```shell
-bash test_tipc/prepare.sh ./test_tipc/configs/ch_ppocr_mobile_v2.0_det/train_mac_cpu_normal_normal_infer_python_mac_cpu.txt 'lite_train_whole_infer'
-bash test_tipc/test_train_inference_python.sh ./test_tipc/configs/ch_ppocr_mobile_v2.0_det/train_mac_cpu_normal_normal_infer_python_mac_cpu.txt 'lite_train_whole_infer'
+bash test_tipc/prepare.sh ./test_tipc/configs/ch_ppocr_mobile_v2_0_det/train_mac_cpu_normal_normal_infer_python_mac_cpu.txt 'lite_train_whole_infer'
+bash test_tipc/test_train_inference_python.sh ./test_tipc/configs/ch_ppocr_mobile_v2_0_det/train_mac_cpu_normal_normal_infer_python_mac_cpu.txt 'lite_train_whole_infer'
```
- 模式3:whole_infer,不训练,全量数据预测,走通开源模型评估、动转静,检查inference model预测时间和精度;
```shell
-bash test_tipc/prepare.sh ./test_tipc/configs/ch_ppocr_mobile_v2.0_det/train_mac_cpu_normal_normal_infer_python_mac_cpu.txt 'whole_infer'
+bash test_tipc/prepare.sh ./test_tipc/configs/ch_ppocr_mobile_v2_0_det/train_mac_cpu_normal_normal_infer_python_mac_cpu.txt 'whole_infer'
# 用法1:
-bash test_tipc/test_train_inference_python.sh ./test_tipc/configs/ch_ppocr_mobile_v2.0_det/train_mac_cpu_normal_normal_infer_python_mac_cpu.txt 'whole_infer'
+bash test_tipc/test_train_inference_python.sh ./test_tipc/configs/ch_ppocr_mobile_v2_0_det/train_mac_cpu_normal_normal_infer_python_mac_cpu.txt 'whole_infer'
# 用法2: 指定GPU卡预测,第三个传入参数为GPU卡号
-bash test_tipc/test_train_inference_python.sh ./test_tipc/configs/ch_ppocr_mobile_v2.0_det/train_mac_cpu_normal_normal_infer_python_mac_cpu.txt 'whole_infer' '1'
+bash test_tipc/test_train_inference_python.sh ./test_tipc/configs/ch_ppocr_mobile_v2_0_det/train_mac_cpu_normal_normal_infer_python_mac_cpu.txt 'whole_infer' '1'
```
- 模式4:whole_train_whole_infer,CE: 全量数据训练,全量数据预测,验证模型训练精度,预测精度,预测速度;(Mac端不建议运行此模式)
```shell
-bash test_tipc/prepare.sh ./test_tipc/configs/ch_ppocr_mobile_v2.0_det/train_mac_cpu_normal_normal_infer_python_mac_cpu.txt 'whole_train_whole_infer'
-bash test_tipc/test_train_inference_python.sh ./test_tipc/configs/ch_ppocr_mobile_v2.0_det/train_mac_cpu_normal_normal_infer_python_mac_cpu.txt 'whole_train_whole_infer'
+bash test_tipc/prepare.sh ./test_tipc/configs/ch_ppocr_mobile_v2_0_det/train_mac_cpu_normal_normal_infer_python_mac_cpu.txt 'whole_train_whole_infer'
+bash test_tipc/test_train_inference_python.sh ./test_tipc/configs/ch_ppocr_mobile_v2_0_det/train_mac_cpu_normal_normal_infer_python_mac_cpu.txt 'whole_train_whole_infer'
```
-- 模式5:klquant_whole_infer,测试离线量化;
-```shell
-bash test_tipc/prepare.sh ./test_tipc/configs/ch_ppocr_mobile_v2.0_det_KL/model_linux_gpu_normal_normal_infer_python_mac_cpu.txt 'klquant_whole_infer'
-bash test_tipc/test_train_inference_python.sh ./test_tipc/configs/ch_ppocr_mobile_v2.0_det_KL/model_linux_gpu_normal_normal_infer_python_mac_cpu.txt 'klquant_whole_infer'
-```
-
运行相应指令后,在`test_tipc/output`文件夹下自动会保存运行日志。如`lite_train_lite_infer`模式下,会运行训练+inference的链条,因此,在`test_tipc/output`文件夹有以下文件:
```
-test_tipc/output/
+test_tipc/output/model_name/lite_train_lite_infer/
|- results_python.log # 运行指令状态的日志
|- norm_train_gpus_-1_autocast_null/ # CPU上正常训练的训练日志和模型保存文件夹
-|- pact_train_gpus_-1_autocast_null/ # CPU上量化训练的训练日志和模型保存文件夹
......
-|- python_infer_cpu_usemkldnn_False_threads_1_batchsize_1.log # CPU上关闭Mkldnn线程数设置为1,测试batch_size=1条件下的预测运行日志
+|- python_infer_cpu_usemkldnn_False_threads_1_precision_fp32_batchsize_1.log # CPU上关闭Mkldnn线程数设置为1,测试batch_size=1条件下的fp32精度预测运行日志
......
```
diff --git a/test_tipc/docs/test_inference_cpp.md b/test_tipc/docs/test_inference_cpp.md
index e662f4bacc..5d8aeda6c4 100644
--- a/test_tipc/docs/test_inference_cpp.md
+++ b/test_tipc/docs/test_inference_cpp.md
@@ -17,15 +17,15 @@ C++预测功能测试的主程序为`test_inference_cpp.sh`,可以测试基于
运行环境配置请参考[文档](./install.md)的内容配置TIPC的运行环境。
### 2.1 功能测试
-先运行`prepare.sh`准备数据和模型,然后运行`test_inference_cpp.sh`进行测试,最终在```test_tipc/output```目录下生成`cpp_infer_*.log`后缀的日志文件。
+先运行`prepare.sh`准备数据和模型,然后运行`test_inference_cpp.sh`进行测试,最终在```test_tipc/output/{model_name}/cpp_infer```目录下生成`cpp_infer_*.log`后缀的日志文件。
```shell
-bash test_tipc/prepare.sh ./test_tipc/configs/ch_ppocr_mobile_v2.0_det/model_linux_gpu_normal_normal_infer_cpp_linux_gpu_cpu.txt "cpp_infer"
+bash test_tipc/prepare.sh ./test_tipc/configs/ch_PP-OCRv2_rec/model_linux_gpu_normal_normal_infer_cpp_linux_gpu_cpu.txt "cpp_infer"
# 用法1:
-bash test_tipc/test_inference_cpp.sh test_tipc/configs/ch_ppocr_mobile_v2.0_det/model_linux_gpu_normal_normal_infer_cpp_linux_gpu_cpu.txt
+bash test_tipc/test_inference_cpp.sh test_tipc/configs/ch_PP-OCRv2_rec/model_linux_gpu_normal_normal_infer_cpp_linux_gpu_cpu.txt
# 用法2: 指定GPU卡预测,第三个传入参数为GPU卡号
-bash test_tipc/test_inference_cpp.sh test_tipc/configs/ch_ppocr_mobile_v2.0_det/model_linux_gpu_normal_normal_infer_cpp_linux_gpu_cpu.txt '1'
+bash test_tipc/test_inference_cpp.sh test_tipc/configs/ch_PP-OCRv2_rec/model_linux_gpu_normal_normal_infer_cpp_linux_gpu_cpu.txt '1'
```
运行预测指令后,在`test_tipc/output`文件夹下自动会保存运行日志,包括以下文件:
@@ -33,23 +33,21 @@ bash test_tipc/test_inference_cpp.sh test_tipc/configs/ch_ppocr_mobile_v2.0_det/
```shell
test_tipc/output/
|- results_cpp.log # 运行指令状态的日志
-|- cpp_infer_cpu_usemkldnn_False_threads_1_precision_fp32_batchsize_1.log # CPU上不开启Mkldnn,线程数设置为1,测试batch_size=1条件下的预测运行日志
-|- cpp_infer_cpu_usemkldnn_False_threads_6_precision_fp32_batchsize_1.log # CPU上不开启Mkldnn,线程数设置为6,测试batch_size=1条件下的预测运行日志
-|- cpp_infer_gpu_usetrt_False_precision_fp32_batchsize_1.log # GPU上不开启TensorRT,测试batch_size=1的fp32精度预测日志
-|- cpp_infer_gpu_usetrt_True_precision_fp16_batchsize_1.log # GPU上开启TensorRT,测试batch_size=1的fp16精度预测日志
+|- cpp_infer_cpu_usemkldnn_False_threads_6_precision_fp32_batchsize_6.log # CPU上不开启Mkldnn,线程数设置为6,测试batch_size=6条件下的预测运行日志
+|- cpp_infer_gpu_usetrt_False_precision_fp32_batchsize_6.log # GPU上不开启TensorRT,测试batch_size=6的fp32精度预测日志
......
```
其中results_cpp.log中包含了每条指令的运行状态,如果运行成功会输出:
```
-Run successfully with command - ./deploy/cpp_infer/build/ppocr det --use_gpu=False --enable_mkldnn=False --cpu_threads=6 --det_model_dir=./inference/ch_ppocr_mobile_v2.0_det_infer/ --rec_batch_num=1 --image_dir=./inference/ch_det_data_50/all-sum-510/ --benchmar k=True > ./test_tipc/output/cpp_infer_cpu_usemkldnn_False_threads_6_precision_fp32_batchsize_1.log 2>&1 !
-Run successfully with command - ./deploy/cpp_infer/build/ppocr det --use_gpu=True --use_tensorrt=False --precision=fp32 --det_model_dir=./inference/ch_ppocr_mobile_v2.0_det_infer/ --rec_batch_num=1 --image_dir=./inference/ch_det_data_50/all-sum-510/ --benchmark =True > ./test_tipc/output/cpp_infer_gpu_usetrt_False_precision_fp32_batchsize_1.log 2>&1 !
+[33m Run successfully with command - ch_PP-OCRv2_rec - ./deploy/cpp_infer/build/ppocr --rec_char_dict_path=./ppocr/utils/ppocr_keys_v1.txt --rec_img_h=32 --use_gpu=True --use_tensorrt=False --precision=fp32 --rec_model_dir=./inference/ch_PP-OCRv2_rec_infer/ --rec_batch_num=6 --image_dir=./inference/rec_inference/ --benchmark=True --det=False --rec=True --cls=False --use_angle_cls=False > ./test_tipc/output/ch_PP-OCRv2_rec/cpp_infer/cpp_infer_gpu_usetrt_False_precision_fp32_batchsize_6.log 2>&1 ! [0m
+[33m Run successfully with command - ch_PP-OCRv2_rec - ./deploy/cpp_infer/build/ppocr --rec_char_dict_path=./ppocr/utils/ppocr_keys_v1.txt --rec_img_h=32 --use_gpu=False --enable_mkldnn=False --cpu_threads=6 --rec_model_dir=./inference/ch_PP-OCRv2_rec_infer/ --rec_batch_num=6 --image_dir=./inference/rec_inference/ --benchmark=True --det=False --rec=True --cls=False --use_angle_cls=False > ./test_tipc/output/ch_PP-OCRv2_rec/cpp_infer/cpp_infer_cpu_usemkldnn_False_threads_6_precision_fp32_batchsize_6.log 2>&1 ! [0m
......
```
如果运行失败,会输出:
```
-Run failed with command - ./deploy/cpp_infer/build/ppocr det --use_gpu=True --use_tensorrt=True --precision=fp32 --det_model_dir=./inference/ch_ppocr_mobile_v2.0_det_infer/ --rec_batch_num=1 --image_dir=./inference/ch_det_data_50/all-sum-510/ --benchmark=True > ./test_tipc/output/cpp_infer_gpu_usetrt_True_precision_fp32_batchsize_1.log 2>&1 !
-Run failed with command - ./deploy/cpp_infer/build/ppocr det --use_gpu=True --use_tensorrt=True --precision=fp16 --det_model_dir=./inference/ch_ppocr_mobile_v2.0_det_infer/ --rec_batch_num=1 --image_dir=./inference/ch_det_data_50/all-sum-510/ --benchmark=True > ./test_tipc/output/cpp_infer_gpu_usetrt_True_precision_fp16_batchsize_1.log 2>&1 !
+Run failed with command - ch_PP-OCRv2_rec - ./deploy/cpp_infer/build/ppocr --rec_char_dict_path=./ppocr/utils/ppocr_keys_v1.txt --rec_img_h=32 --use_gpu=True --use_tensorrt=False --precision=fp32 --rec_model_dir=./inference/ch_PP-OCRv2_rec_infer/ --rec_batch_num=6 --image_dir=./inference/rec_inference/ --benchmark=True --det=False --rec=True --cls=False --use_angle_cls=False > ./test_tipc/output/ch_PP-OCRv2_rec/cpp_infer/cpp_infer_gpu_usetrt_False_precision_fp32_batchsize_6.log 2>&1 !
+Run failed with command - ch_PP-OCRv2_rec - ./deploy/cpp_infer/build/ppocr --rec_char_dict_path=./ppocr/utils/ppocr_keys_v1.txt --rec_img_h=32 --use_gpu=False --enable_mkldnn=False --cpu_threads=6 --rec_model_dir=./inference/ch_PP-OCRv2_rec_infer/ --rec_batch_num=6 --image_dir=./inference/rec_inference/ --benchmark=True --det=False --rec=True --cls=False --use_angle_cls=False > ./test_tipc/output/ch_PP-OCRv2_rec/cpp_infer/cpp_infer_cpu_usemkldnn_False_threads_6_precision_fp32_batchsize_6.log 2>&1 !
......
```
可以很方便的根据results_cpp.log中的内容判定哪一个指令运行错误。
diff --git a/test_tipc/docs/test_paddle2onnx.md b/test_tipc/docs/test_paddle2onnx.md
index df2734771e..299621d011 100644
--- a/test_tipc/docs/test_paddle2onnx.md
+++ b/test_tipc/docs/test_paddle2onnx.md
@@ -15,29 +15,30 @@ PaddleServing预测功能测试的主程序为`test_paddle2onnx.sh`,可以测
## 2. 测试流程
### 2.1 功能测试
-先运行`prepare.sh`准备数据和模型,然后运行`test_paddle2onnx.sh`进行测试,最终在```test_tipc/output```目录下生成`paddle2onnx_infer_*.log`后缀的日志文件。
+先运行`prepare.sh`准备数据和模型,然后运行`test_paddle2onnx.sh`进行测试,最终在```test_tipc/output/{model_name}/paddle2onnx```目录下生成`paddle2onnx_infer_*.log`后缀的日志文件。
```shell
-bash test_tipc/prepare.sh ./test_tipc/configs/ppocr_det_mobile/model_linux_gpu_normal_normal_paddle2onnx_python_linux_cpu.txt "paddle2onnx_infer"
+bash test_tipc/prepare.sh ./test_tipc/configs/ch_PP-OCRv2_det/model_linux_gpu_normal_normal_paddle2onnx_python_linux_cpu.txt "paddle2onnx_infer"
# 用法:
-bash test_tipc/test_paddle2onnx.sh ./test_tipc/configs/ppocr_det_mobile/model_linux_gpu_normal_normal_paddle2onnx_python_linux_cpu.txt
+bash test_tipc/test_paddle2onnx.sh ./test_tipc/configs/ch_PP-OCRv2_det/model_linux_gpu_normal_normal_paddle2onnx_python_linux_cpu.txt
```
#### 运行结果
-各测试的运行情况会打印在 `test_tipc/output/results_paddle2onnx.log` 中:
+各测试的运行情况会打印在 `test_tipc/output/{model_name}/paddle2onnx/results_paddle2onnx.log` 中:
运行成功时会输出:
```
-Run successfully with command - paddle2onnx --model_dir=./inference/ch_ppocr_mobile_v2.0_det_infer/ --model_filename=inference.pdmodel --params_filename=inference.pdiparams --save_file=./inference/det_mobile_onnx/model.onnx --opset_version=10 --enable_onnx_checker=True!
-Run successfully with command - python test_tipc/onnx_inference/predict_det.py --use_gpu=False --image_dir=./inference/ch_det_data_50/all-sum-510/ --det_model_dir=./inference/det_mobile_onnx/model.onnx 2>&1 !
+Run successfully with command - ch_PP-OCRv2_det - paddle2onnx --model_dir=./inference/ch_PP-OCRv2_det_infer/ --model_filename=inference.pdmodel --params_filename=inference.pdiparams --save_file=./inference/det_v2_onnx/model.onnx --opset_version=10 --enable_onnx_checker=True!
+Run successfully with command - ch_PP-OCRv2_det - python3.7 tools/infer/predict_det.py --use_gpu=True --image_dir=./inference/ch_det_data_50/all-sum-510/ --det_model_dir=./inference/det_v2_onnx/model.onnx --use_onnx=True > ./test_tipc/output/ch_PP-OCRv2_det/paddle2onnx/paddle2onnx_infer_gpu.log 2>&1 !
+Run successfully with command - ch_PP-OCRv2_det - python3.7 tools/infer/predict_det.py --use_gpu=False --image_dir=./inference/ch_det_data_50/all-sum-510/ --det_model_dir=./inference/det_v2_onnx/model.onnx --use_onnx=True > ./test_tipc/output/ch_PP-OCRv2_det/paddle2onnx/paddle2onnx_infer_cpu.log 2>&1 !
```
运行失败时会输出:
```
-Run failed with command - paddle2onnx --model_dir=./inference/ch_ppocr_mobile_v2.0_det_infer/ --model_filename=inference.pdmodel --params_filename=inference.pdiparams --save_file=./inference/det_mobile_onnx/model.onnx --opset_version=10 --enable_onnx_checker=True!
+Run failed with command - ch_PP-OCRv2_det - paddle2onnx --model_dir=./inference/ch_PP-OCRv2_det_infer/ --model_filename=inference.pdmodel --params_filename=inference.pdiparams --save_file=./inference/det_v2_onnx/model.onnx --opset_version=10 --enable_onnx_checker=True!
...
```
diff --git a/test_tipc/docs/test_ptq_inference_python.md b/test_tipc/docs/test_ptq_inference_python.md
new file mode 100644
index 0000000000..7887c0b5c9
--- /dev/null
+++ b/test_tipc/docs/test_ptq_inference_python.md
@@ -0,0 +1,51 @@
+# Linux GPU/CPU KL离线量化训练推理测试
+
+Linux GPU/CPU KL离线量化训练推理测试的主程序为`test_ptq_inference_python.sh`,可以测试基于Python的模型训练、评估、推理等基本功能。
+
+## 1. 测试结论汇总
+- 训练相关:
+
+| 算法名称 | 模型名称 | 单机单卡 |
+| :----: | :----: | :----: |
+| | model_name | KL离线量化训练 |
+
+- 推理相关:
+
+| 算法名称 | 模型名称 | device_CPU | device_GPU | batchsize |
+| :----: | :----: | :----: | :----: | :----: |
+| | model_name | 支持 | 支持 | 1 |
+
+## 2. 测试流程
+
+### 2.1 准备数据和模型
+
+先运行`prepare.sh`准备数据和模型,然后运行`test_ptq_inference_python.sh`进行测试,最终在```test_tipc/output/{model_name}/whole_infer```目录下生成`python_infer_*.log`后缀的日志文件。
+
+```shell
+bash test_tipc/prepare.sh ./test_tipc/configs/ch_PP-OCRv2_det/train_ptq_infer_python.txt "whole_infer"
+
+# 用法:
+bash test_tipc/test_ptq_inference_python.sh ./test_tipc/configs/ch_PP-OCRv2_det/train_ptq_infer_python.txt "whole_infer"
+```
+
+#### 运行结果
+
+各测试的运行情况会打印在 `test_tipc/output/{model_name}/paddle2onnx/results_paddle2onnx.log` 中:
+运行成功时会输出:
+
+```
+Run successfully with command - ch_PP-OCRv2_det_KL - python3.7 deploy/slim/quantization/quant_kl.py -c configs/det/ch_PP-OCRv2/ch_PP-OCRv2_det_cml.yml -o Global.pretrained_model=./inference/ch_PP-OCRv2_det_infer/ Global.save_inference_dir=./inference/ch_PP-OCRv2_det_infer/_klquant > ./test_tipc/output/ch_PP-OCRv2_det_KL/whole_infer/whole_infer_export_0.log 2>&1 !
+Run successfully with command - ch_PP-OCRv2_det_KL - python3.7 tools/infer/predict_det.py --use_gpu=False --enable_mkldnn=False --cpu_threads=6 --det_model_dir=./inference/ch_PP-OCRv2_det_infer/_klquant --rec_batch_num=1 --image_dir=./inference/ch_det_data_50/all-sum-510/ --precision=int8 > ./test_tipc/output/ch_PP-OCRv2_det_KL/whole_infer/python_infer_cpu_usemkldnn_False_threads_6_precision_int8_batchsize_1.log 2>&1 !
+Run successfully with command - ch_PP-OCRv2_det_KL - python3.7 tools/infer/predict_det.py --use_gpu=True --use_tensorrt=False --precision=int8 --det_model_dir=./inference/ch_PP-OCRv2_det_infer/_klquant --rec_batch_num=1 --image_dir=./inference/ch_det_data_50/all-sum-510/ > ./test_tipc/output/ch_PP-OCRv2_det_KL/whole_infer/python_infer_gpu_usetrt_False_precision_int8_batchsize_1.log 2>&1 !
+```
+
+运行失败时会输出:
+
+```
+Run failed with command - ch_PP-OCRv2_det_KL - python3.7 deploy/slim/quantization/quant_kl.py -c configs/det/ch_PP-OCRv2/ch_PP-OCRv2_det_cml.yml -o Global.pretrained_model=./inference/ch_PP-OCRv2_det_infer/ Global.save_inference_dir=./inference/ch_PP-OCRv2_det_infer/_klquant > ./test_tipc/output/ch_PP-OCRv2_det_KL/whole_infer/whole_infer_export_0.log 2>&1 !
+...
+```
+
+## 3. 更多教程
+
+本文档为功能测试用,更详细的量化使用教程请参考:[量化](../../deploy/slim/quantization/README.md)
diff --git a/test_tipc/docs/test_serving.md b/test_tipc/docs/test_serving.md
index 71f01c0d5f..ef38888784 100644
--- a/test_tipc/docs/test_serving.md
+++ b/test_tipc/docs/test_serving.md
@@ -18,71 +18,44 @@ PaddleServing预测功能测试的主程序为`test_serving_infer_python.sh`和`
### 2.1 功能测试
**python serving**
-先运行`prepare.sh`准备数据和模型,然后运行`test_serving_infer_python.sh`进行测试,最终在```test_tipc/output```目录下生成`serving_infer_python*.log`后缀的日志文件。
+先运行`prepare.sh`准备数据和模型,然后运行`test_serving_infer_python.sh`进行测试,最终在```test_tipc/output/{model_name}/serving_infer/python```目录下生成`python_*.log`后缀的日志文件。
```shell
-bash test_tipc/prepare.sh ./test_tipc/configs/ch_ppocr_mobile_v2.0_det/model_linux_gpu_normal_normal_serving_python_linux_gpu_cpu.txt "serving_infer"
+bash test_tipc/prepare.sh ./test_tipc/configs/ch_PP-OCRv2/model_linux_gpu_normal_normal_serving_python_linux_gpu_cpu.txt "serving_infer"
# 用法:
-bash test_tipc/test_serving_infer_python.sh ./test_tipc/configs/ch_ppocr_mobile_v2.0_det/model_linux_gpu_normal_normal_serving_python_linux_gpu_cpu.txt "serving_infer"
+bash test_tipc/test_serving_infer_python.sh ./test_tipc/configs/ch_PP-OCRv2/model_linux_gpu_normal_normal_serving_python_linux_gpu_cpu.txt "serving_infer"
```
**cpp serving**
-先运行`prepare.sh`准备数据和模型,然后运行`test_serving_infer_cpp.sh`进行测试,最终在```test_tipc/output```目录下生成`serving_infer_cpp*.log`后缀的日志文件。
+先运行`prepare.sh`准备数据和模型,然后运行`test_serving_infer_cpp.sh`进行测试,最终在```test_tipc/output/{model_name}/serving_infer/cpp```目录下生成`cpp_*.log`后缀的日志文件。
```shell
-bash test_tipc/prepare.sh ./test_tipc/configs/ch_ppocr_mobile_v2.0/model_linux_gpu_normal_normal_serving_python_linux_gpu_cpu.txt "serving_infer"
+bash test_tipc/prepare.sh ./test_tipc/configs/ch_PP-OCRv2/model_linux_gpu_normal_normal_serving_cpp_linux_gpu_cpu.txt "serving_infer"
# 用法:
-bash test_tipc/test_serving_infer_cpp.sh ./test_tipc/configs/ch_ppocr_mobile_v2.0/model_linux_gpu_normal_normal_serving_cpp_linux_gpu_cpu.txt "serving_infer"
+bash test_tipc/test_serving_infer_cpp.sh ./test_tipc/configs/ch_PP-OCRv2/model_linux_gpu_normal_normal_serving_cpp_linux_gpu_cpu.txt "serving_infer"
```
#### 运行结果
-各测试的运行情况会打印在 `test_tipc/output/results_serving.log` 中:
+各测试的运行情况会打印在 `test_tipc/output/{model_name}/serving_infer/python(cpp)/results_python(cpp)_serving.log` 中:
运行成功时会输出:
```
-Run successfully with command - python3.7 pipeline_http_client.py --image_dir=../../doc/imgs > ../../tests/output/server_infer_cpu_usemkldnn_True_threads_1_batchsize_1.log 2>&1 !
-Run successfully with command - xxxxx
+Run successfully with command - ch_PP-OCRv2_rec - nohup python3.7 web_service_rec.py --config=config.yml --opt op.rec.concurrency="1" op.det.local_service_conf.devices= op.det.local_service_conf.use_mkldnn=False op.det.local_service_conf.thread_num=6 op.rec.local_service_conf.model_config=ppocr_rec_v2_serving > ./test_tipc/output/ch_PP-OCRv2_rec/serving_infer/python/python_server_cpu_usemkldnn_False_threads_6.log 2>&1 &!
+Run successfully with command - ch_PP-OCRv2_rec - python3.7 pipeline_http_client.py --det=False --image_dir=../../inference/rec_inference > ./test_tipc/output/ch_PP-OCRv2_rec/serving_infer/python/python_client_cpu_pipeline_http_usemkldnn_False_threads_6_batchsize_1.log 2>&1 !
...
```
运行失败时会输出:
```
-Run failed with command - python3.7 pipeline_http_client.py --image_dir=../../doc/imgs > ../../tests/output/server_infer_cpu_usemkldnn_True_threads_1_batchsize_1.log 2>&1 !
-Run failed with command - python3.7 pipeline_http_client.py --image_dir=../../doc/imgs > ../../tests/output/server_infer_cpu_usemkldnn_True_threads_6_batchsize_1.log 2>&1 !
-Run failed with command - xxxxx
+Run failed with command - ch_PP-OCRv2_rec - nohup python3.7 web_service_rec.py --config=config.yml --opt op.rec.concurrency="1" op.det.local_service_conf.devices= op.det.local_service_conf.use_mkldnn=False op.det.local_service_conf.thread_num=6 op.rec.local_service_conf.model_config=ppocr_rec_v2_serving > ./test_tipc/output/ch_PP-OCRv2_rec/serving_infer/python/python_server_cpu_usemkldnn_False_threads_6.log 2>&1 &!
+Run failed with command - ch_PP-OCRv2_rec - python3.7 pipeline_http_client.py --det=False --image_dir=../../inference/rec_inference > ./test_tipc/output/ch_PP-OCRv2_rec/serving_infer/python/python_client_cpu_pipeline_http_usemkldnn_False_threads_6_batchsize_1.log 2>&1 !
...
```
-详细的预测结果会存在 test_tipc/output/ 文件夹下,例如`server_infer_gpu_usetrt_True_precision_fp16_batchsize_1.log`中会返回检测框的坐标:
-
-```
-{'err_no': 0, 'err_msg': '', 'key': ['dt_boxes'], 'value': ['[[[ 78. 642.]\n [409. 640.]\n [409. 657.]\n
-[ 78. 659.]]\n\n [[ 75. 614.]\n [211. 614.]\n [211. 635.]\n [ 75. 635.]]\n\n
-[[103. 554.]\n [135. 554.]\n [135. 575.]\n [103. 575.]]\n\n [[ 75. 531.]\n
-[347. 531.]\n [347. 549.]\n [ 75. 549.] ]\n\n [[ 76. 503.]\n [309. 498.]\n
-[309. 521.]\n [ 76. 526.]]\n\n [[163. 462.]\n [317. 462.]\n [317. 493.]\n
-[163. 493.]]\n\n [[324. 431.]\n [414. 431.]\n [414. 452.]\n [324. 452.]]\n\n
-[[ 76. 412.]\n [208. 408.]\n [209. 424.]\n [ 76. 428.]]\n\n [[307. 409.]\n
-[428. 409.]\n [428. 426.]\n [307 . 426.]]\n\n [[ 74. 385.]\n [217. 382.]\n
-[217. 400.]\n [ 74. 403.]]\n\n [[308. 381.]\n [427. 380.]\n [427. 400.]\n
-[308. 401.]]\n\n [[ 74. 363.]\n [195. 362.]\n [195. 378.]\n [ 74. 379.]]\n\n
-[[303. 359.]\n [423. 357.]\n [423. 375.]\n [303. 377.]]\n\n [[ 70. 336.]\n
-[239. 334.]\n [239. 354.]\ n [ 70. 356.]]\n\n [[ 70. 312.]\n [204. 310.]\n
-[204. 327.]\n [ 70. 330.]]\n\n [[303. 308.]\n [419. 306.]\n [419. 326.]\n
-[303. 328.]]\n\n [[113. 2 72.]\n [246. 270.]\n [247. 299.]\n [113. 301.]]\n\n
- [[361. 269.]\n [384. 269.]\n [384. 296.]\n [361. 296.]]\n\n [[ 70. 250.]\n
- [243. 246.]\n [243. 265.]\n [ 70. 269.]]\n\n [[ 65. 221.]\n [187. 220.]\n
-[187. 240.]\n [ 65. 241.]]\n\n [[337. 216.]\n [382. 216.]\n [382. 240.]\n
-[337. 240.]]\n\n [ [ 65. 196.]\n [247. 193.]\n [247. 213.]\n [ 65. 216.]]\n\n
-[[296. 197.]\n [423. 191.]\n [424. 209.]\n [296. 215.]]\n\n [[ 65. 167.]\n [244. 167.]\n
-[244. 186.]\n [ 65. 186.]]\n\n [[ 67. 139.]\n [290. 139.]\n [290. 159.]\n [ 67. 159.]]\n\n
-[[ 68. 113.]\n [410. 113.]\n [410. 128.]\n [ 68. 129.] ]\n\n [[277. 87.]\n [416. 87.]\n
-[416. 108.]\n [277. 108.]]\n\n [[ 79. 28.]\n [132. 28.]\n [132. 62.]\n [ 79. 62.]]\n\n
-[[163. 17.]\n [410. 14.]\n [410. 50.]\n [163. 53.]]]']}
-```
+详细的预测结果会存在 test_tipc/output/{model_name}/serving_infer/python(cpp)/ 文件夹下
## 3. 更多教程
diff --git a/test_tipc/docs/test_train_inference_python.md b/test_tipc/docs/test_train_inference_python.md
index 99de940079..2636192511 100644
--- a/test_tipc/docs/test_train_inference_python.md
+++ b/test_tipc/docs/test_train_inference_python.md
@@ -1,6 +1,6 @@
# Linux端基础训练预测功能测试
-Linux端基础训练预测功能测试的主程序为`test_train_inference_python.sh`,可以测试基于Python的模型训练、评估、推理等基本功能,包括裁剪、量化、蒸馏。
+Linux端基础训练预测功能测试的主程序为`test_train_inference_python.sh`,可以测试基于Python的模型训练、评估、推理等基本功能,包括PACT在线量化。
- Mac端基础训练预测功能测试参考[链接](./mac_test_train_inference_python.md)
- Windows端基础训练预测功能测试参考[链接](./win_test_train_inference_python.md)
@@ -11,13 +11,14 @@ Linux端基础训练预测功能测试的主程序为`test_train_inference_pytho
| 算法名称 | 模型名称 | 单机单卡 | 单机多卡 | 多机多卡 | 模型压缩(单机多卡) |
| :---- | :---- | :---- | :---- | :---- | :---- |
-| DB | ch_ppocr_mobile_v2.0_det| 正常训练 混合精度 | 正常训练 混合精度 | 正常训练 混合精度 | 正常训练:FPGM裁剪、PACT量化 离线量化(无需训练) |
-| DB | ch_ppocr_server_v2.0_det| 正常训练 混合精度 | 正常训练 混合精度 | 正常训练 混合精度 | 正常训练:FPGM裁剪、PACT量化 离线量化(无需训练) |
-| CRNN | ch_ppocr_mobile_v2.0_rec| 正常训练 混合精度 | 正常训练 混合精度 | 正常训练 混合精度 | 正常训练:PACT量化 离线量化(无需训练) |
-| CRNN | ch_ppocr_server_v2.0_rec| 正常训练 混合精度 | 正常训练 混合精度 | 正常训练 混合精度 | 正常训练:PACT量化 离线量化(无需训练) |
-|PP-OCR| ch_ppocr_mobile_v2.0| 正常训练 混合精度 | 正常训练 混合精度 | 正常训练 混合精度 | - |
-|PP-OCR| ch_ppocr_server_v2.0| 正常训练 混合精度 | 正常训练 混合精度 | 正常训练 混合精度 | - |
+| DB | ch_ppocr_mobile_v2_0_det| 正常训练 混合精度 | 正常训练 混合精度 | 正常训练 混合精度 | 正常训练:FPGM裁剪、PACT量化 |
+| DB | ch_ppocr_server_v2_0_det| 正常训练 混合精度 | 正常训练 混合精度 | 正常训练 混合精度 | 正常训练:FPGM裁剪、PACT量化 |
+| CRNN | ch_ppocr_mobile_v2_0_rec| 正常训练 混合精度 | 正常训练 混合精度 | 正常训练 混合精度 | 正常训练:PACT量化 |
+| CRNN | ch_ppocr_server_v2_0_rec| 正常训练 混合精度 | 正常训练 混合精度 | 正常训练 混合精度 | 正常训练:PACT量化 |
+|PP-OCR| ch_ppocr_mobile_v2_0| 正常训练 混合精度 | 正常训练 混合精度 | 正常训练 混合精度 | - |
+|PP-OCR| ch_ppocr_server_v2_0| 正常训练 混合精度 | 正常训练 混合精度 | 正常训练 混合精度 | - |
|PP-OCRv2| ch_PP-OCRv2 | 正常训练 混合精度 | 正常训练 混合精度 | 正常训练 混合精度 | - |
+|PP-OCRv3| ch_PP-OCRv3 | 正常训练 混合精度 | 正常训练 混合精度 | 正常训练 混合精度 | - |
- 预测相关:基于训练是否使用量化,可以将训练产出的模型可以分为`正常模型`和`量化模型`,这两类模型对应的预测功能汇总如下,
@@ -35,7 +36,7 @@ Linux端基础训练预测功能测试的主程序为`test_train_inference_pytho
运行环境配置请参考[文档](./install.md)的内容配置TIPC的运行环境。
### 2.1 安装依赖
-- 安装PaddlePaddle >= 2.0
+- 安装PaddlePaddle >= 2.3
- 安装PaddleOCR依赖
```
pip3 install -r ../requirements.txt
@@ -46,7 +47,7 @@ Linux端基础训练预测功能测试的主程序为`test_train_inference_pytho
cd AutoLog
pip3 install -r requirements.txt
python3 setup.py bdist_wheel
- pip3 install ./dist/auto_log-1.0.0-py3-none-any.whl
+ pip3 install ./dist/auto_log-1.2.0-py3-none-any.whl
cd ../
```
- 安装PaddleSlim (可选)
@@ -57,60 +58,57 @@ Linux端基础训练预测功能测试的主程序为`test_train_inference_pytho
### 2.2 功能测试
-先运行`prepare.sh`准备数据和模型,然后运行`test_train_inference_python.sh`进行测试,最终在```test_tipc/output```目录下生成`python_infer_*.log`格式的日志文件。
+#### 2.2.1 基础训练推理链条
+先运行`prepare.sh`准备数据和模型,然后运行`test_train_inference_python.sh`进行测试,最终在```test_tipc/output```目录下生成`,model_name/lite_train_lite_infer/*.log`格式的日志文件。
-`test_train_inference_python.sh`包含5种运行模式,每种模式的运行数据不同,分别用于测试速度和精度,分别是:
+`test_train_inference_python.sh`包含基础链条的4种运行模式,每种模式的运行数据不同,分别用于测试速度和精度,分别是:
- 模式1:lite_train_lite_infer,使用少量数据训练,用于快速验证训练到预测的走通流程,不验证精度和速度;
```shell
-bash test_tipc/prepare.sh ./test_tipc/configs/ch_ppocr_mobile_v2.0_det/train_infer_python.txt 'lite_train_lite_infer'
-bash test_tipc/test_train_inference_python.sh ./test_tipc/configs/ch_ppocr_mobile_v2.0_det/train_infer_python.txt 'lite_train_lite_infer'
+bash test_tipc/prepare.sh ./test_tipc/configs/ch_ppocr_mobile_v2_0_det/train_infer_python.txt 'lite_train_lite_infer'
+bash test_tipc/test_train_inference_python.sh ./test_tipc/configs/ch_ppocr_mobile_v2_0_det/train_infer_python.txt 'lite_train_lite_infer'
```
- 模式2:lite_train_whole_infer,使用少量数据训练,一定量数据预测,用于验证训练后的模型执行预测,预测速度是否合理;
```shell
-bash test_tipc/prepare.sh ./test_tipc/configs/ch_ppocr_mobile_v2.0_det/train_infer_python.txt 'lite_train_whole_infer'
-bash test_tipc/test_train_inference_python.sh ../test_tipc/configs/ch_ppocr_mobile_v2.0_det/train_infer_python.txt 'lite_train_whole_infer'
+bash test_tipc/prepare.sh ./test_tipc/configs/ch_ppocr_mobile_v2_0_det/train_infer_python.txt 'lite_train_whole_infer'
+bash test_tipc/test_train_inference_python.sh ../test_tipc/configs/ch_ppocr_mobile_v2_0_det/train_infer_python.txt 'lite_train_whole_infer'
```
- 模式3:whole_infer,不训练,全量数据预测,走通开源模型评估、动转静,检查inference model预测时间和精度;
```shell
-bash test_tipc/prepare.sh ./test_tipc/configs/ch_ppocr_mobile_v2.0_det/train_infer_python.txt 'whole_infer'
+bash test_tipc/prepare.sh ./test_tipc/configs/ch_ppocr_mobile_v2_0_det/train_infer_python.txt 'whole_infer'
# 用法1:
-bash test_tipc/test_train_inference_python.sh ../test_tipc/configs/ch_ppocr_mobile_v2.0_det/train_infer_python.txt 'whole_infer'
+bash test_tipc/test_train_inference_python.sh ../test_tipc/configs/ch_ppocr_mobile_v2_0_det/train_infer_python.txt 'whole_infer'
# 用法2: 指定GPU卡预测,第三个传入参数为GPU卡号
-bash test_tipc/test_train_inference_python.sh ./test_tipc/configs/ch_ppocr_mobile_v2.0_det/train_infer_python.txt 'whole_infer' '1'
+bash test_tipc/test_train_inference_python.sh ./test_tipc/configs/ch_ppocr_mobile_v2_0_det/train_infer_python.txt 'whole_infer' '1'
```
- 模式4:whole_train_whole_infer,CE: 全量数据训练,全量数据预测,验证模型训练精度,预测精度,预测速度;
```shell
-bash test_tipc/prepare.sh ./test_tipc/configs/ch_ppocr_mobile_v2.0_det/train_infer_python.txt 'whole_train_whole_infer'
-bash test_tipc/test_train_inference_python.sh ./test_tipc/configs/ch_ppocr_mobile_v2.0_det/train_infer_python.txt 'whole_train_whole_infer'
+bash test_tipc/prepare.sh ./test_tipc/configs/ch_ppocr_mobile_v2_0_det/train_infer_python.txt 'whole_train_whole_infer'
+bash test_tipc/test_train_inference_python.sh ./test_tipc/configs/ch_ppocr_mobile_v2_0_det/train_infer_python.txt 'whole_train_whole_infer'
```
-- 模式5:klquant_whole_infer,测试离线量化;
-```shell
-bash test_tipc/prepare.sh ./test_tipc/configs/ch_ppocr_mobile_v2.0_det_KL/model_linux_gpu_normal_normal_infer_python_linux_gpu_cpu.txt 'klquant_whole_infer'
-bash test_tipc/test_train_inference_python.sh ./test_tipc/configs/ch_ppocr_mobile_v2.0_det_KL/model_linux_gpu_normal_normal_infer_python_linux_gpu_cpu.txt 'klquant_whole_infer'
-```
-
运行相应指令后,在`test_tipc/output`文件夹下自动会保存运行日志。如'lite_train_lite_infer'模式下,会运行训练+inference的链条,因此,在`test_tipc/output`文件夹有以下文件:
```
-test_tipc/output/
+test_tipc/output/model_name/lite_train_lite_infer/
|- results_python.log # 运行指令状态的日志
-|- norm_train_gpus_0_autocast_null/ # GPU 0号卡上正常训练的训练日志和模型保存文件夹
-|- pact_train_gpus_0_autocast_null/ # GPU 0号卡上量化训练的训练日志和模型保存文件夹
+|- norm_train_gpus_0_autocast_null/ # GPU 0号卡上正常单机单卡训练的训练日志和模型保存文件夹
+|- norm_train_gpus_0,1_autocast_null/ # GPU 0,1号卡上正常单机多卡训练的训练日志和模型保存文件夹
......
-|- python_infer_cpu_usemkldnn_True_threads_1_batchsize_1.log # CPU上开启Mkldnn线程数设置为1,测试batch_size=1条件下的预测运行日志
-|- python_infer_gpu_usetrt_True_precision_fp16_batchsize_1.log # GPU上开启TensorRT,测试batch_size=1的半精度预测日志
+|- python_infer_cpu_usemkldnn_False_threads_6_precision_fp32_batchsize_1.log # CPU上关闭Mkldnn线程数设置为6,测试batch_size=1条件下的fp32精度预测运行日志
+|- python_infer_gpu_usetrt_False_precision_fp32_batchsize_1.log # GPU上关闭TensorRT,测试batch_size=1的fp32精度预测日志
......
```
其中`results_python.log`中包含了每条指令的运行状态,如果运行成功会输出:
```
-Run successfully with command - python3.7 tools/train.py -c tests/configs/det_mv3_db.yml -o Global.pretrained_model=./pretrain_models/MobileNetV3_large_x0_5_pretrained Global.use_gpu=True Global.save_model_dir=./tests/output/norm_train_gpus_0_autocast_null Global.epoch_num=1 Train.loader.batch_size_per_card=2 !
-Run successfully with command - python3.7 tools/export_model.py -c tests/configs/det_mv3_db.yml -o Global.pretrained_model=./tests/output/norm_train_gpus_0_autocast_null/latest Global.save_inference_dir=./tests/output/norm_train_gpus_0_autocast_null!
+[33m Run successfully with command - ch_ppocr_mobile_v2_0_det - python3.7 tools/train.py -c configs/det/ch_ppocr_v2_0/ch_det_mv3_db_v2_0.yml -o Global.pretrained_model=./pretrain_models/MobileNetV3_large_x0_5_pretrained Global.use_gpu=True Global.save_model_dir=./test_tipc/output/ch_ppocr_mobile_v2_0_det/lite_train_lite_infer/norm_train_gpus_0_autocast_null Global.epoch_num=100 Train.loader.batch_size_per_card=2 ! [0m
+[33m Run successfully with command - ch_ppocr_mobile_v2_0_det - python3.7 tools/export_model.py -c configs/det/ch_ppocr_v2_0/ch_det_mv3_db_v2_0.yml -o Global.checkpoints=./test_tipc/output/ch_ppocr_mobile_v2_0_det/lite_train_lite_infer/norm_train_gpus_0_autocast_null/latest Global.save_inference_dir=./test_tipc/output/ch_ppocr_mobile_v2_0_det/lite_train_lite_infer/norm_train_gpus_0_autocast_null > ./test_tipc/output/ch_ppocr_mobile_v2_0_det/lite_train_lite_infer/norm_train_gpus_0_autocast_null_nodes_1_export.log 2>&1 ! [0m
+[33m Run successfully with command - ch_ppocr_mobile_v2_0_det - python3.7 tools/infer/predict_det.py --use_gpu=True --use_tensorrt=False --precision=fp32 --det_model_dir=./test_tipc/output/ch_ppocr_mobile_v2_0_det/lite_train_lite_infer/norm_train_gpus_0_autocast_null --rec_batch_num=1 --image_dir=./train_data/icdar2015/text_localization/ch4_test_images/ --benchmark=True > ./test_tipc/output/ch_ppocr_mobile_v2_0_det/lite_train_lite_infer/python_infer_gpu_usetrt_False_precision_fp32_batchsize_1.log 2>&1 ! [0m
+[33m Run successfully with command - ch_ppocr_mobile_v2_0_det - python3.7 tools/infer/predict_det.py --use_gpu=False --enable_mkldnn=False --cpu_threads=6 --det_model_dir=./test_tipc/output/ch_ppocr_mobile_v2_0_det/lite_train_lite_infer/norm_train_gpus_0_autocast_null --rec_batch_num=1 --image_dir=./train_data/icdar2015/text_localization/ch4_test_images/ --benchmark=True --precision=fp32 > ./test_tipc/output/ch_ppocr_mobile_v2_0_det/lite_train_lite_infer/python_infer_cpu_usemkldnn_False_threads_6_precision_fp32_batchsize_1.log 2>&1 ! [0m
......
```
如果运行失败,会输出:
@@ -121,6 +119,22 @@ Run failed with command - python3.7 tools/export_model.py -c tests/configs/det_m
```
可以很方便的根据`results_python.log`中的内容判定哪一个指令运行错误。
+#### 2.2.2 PACT在线量化链条
+此外,`test_train_inference_python.sh`还包含PACT在线量化模式,命令如下:
+以ch_PP-OCRv2_det为例,如需测试其他模型更换配置即可。
+
+```shell
+bash test_tipc/prepare.sh ./test_tipc/configs/ch_PP-OCRv2_det/train_pact_infer_python.txt 'lite_train_lite_infer'
+bash test_tipc/test_train_inference_python.sh ./test_tipc/configs/ch_PP-OCRv2_det/train_pact_infer_python.txt 'lite_train_lite_infer'
+```
+#### 2.2.3 混合精度训练链条
+此外,`test_train_inference_python.sh`还包含混合精度训练模式,命令如下:
+以ch_PP-OCRv2_det为例,如需测试其他模型更换配置即可。
+
+```shell
+bash test_tipc/prepare.sh ./test_tipc/configs/ch_PP-OCRv2_det/train_linux_gpu_normal_amp_infer_python_linux_gpu_cpu.txt 'lite_train_lite_infer'
+bash test_tipc/test_train_inference_python.sh ./test_tipc/configs/ch_PP-OCRv2_det/train_linux_gpu_normal_amp_infer_python_linux_gpu_cpu.txt 'lite_train_lite_infer'
+```
### 2.3 精度测试
diff --git a/test_tipc/docs/win_test_train_inference_python.md b/test_tipc/docs/win_test_train_inference_python.md
index 6e3ce93bb3..ba9aa21300 100644
--- a/test_tipc/docs/win_test_train_inference_python.md
+++ b/test_tipc/docs/win_test_train_inference_python.md
@@ -8,7 +8,7 @@ Windows端基础训练预测功能测试的主程序为`test_train_inference_pyt
| 算法名称 | 模型名称 | 单机单卡 | 单机多卡 | 多机多卡 | 模型压缩(单机多卡) |
| :---- | :---- | :---- | :---- | :---- | :---- |
-| DB | ch_ppocr_mobile_v2.0_det| 正常训练 混合精度 | - | - | 正常训练:FPGM裁剪、PACT量化 离线量化(无需训练) |
+| DB | ch_ppocr_mobile_v2_0_det| 正常训练 混合精度 | - | - | 正常训练:FPGM裁剪、PACT量化 |
- 预测相关:基于训练是否使用量化,可以将训练产出的模型可以分为`正常模型`和`量化模型`,这两类模型对应的预测功能汇总如下:
@@ -29,7 +29,7 @@ Windows端基础训练预测功能测试的主程序为`test_train_inference_pyt
### 2.1 安装依赖
-- 安装PaddlePaddle >= 2.0
+- 安装PaddlePaddle >= 2.3
- 安装PaddleOCR依赖
```
pip install -r ../requirements.txt
@@ -40,7 +40,7 @@ Windows端基础训练预测功能测试的主程序为`test_train_inference_pyt
cd AutoLog
pip install -r requirements.txt
python setup.py bdist_wheel
- pip install ./dist/auto_log-1.0.0-py3-none-any.whl
+ pip install ./dist/auto_log-1.2.0-py3-none-any.whl
cd ../
```
- 安装PaddleSlim (可选)
@@ -51,54 +51,46 @@ Windows端基础训练预测功能测试的主程序为`test_train_inference_pyt
### 2.2 功能测试
-先运行`prepare.sh`准备数据和模型,然后运行`test_train_inference_python.sh`进行测试,最终在```test_tipc/output```目录下生成`python_infer_*.log`格式的日志文件。
+先运行`prepare.sh`准备数据和模型,然后运行`test_train_inference_python.sh`进行测试,最终在```test_tipc/output```目录下生成`,model_name/lite_train_lite_infer/*.log`格式的日志文件。
-`test_train_inference_python.sh`包含5种运行模式,每种模式的运行数据不同,分别用于测试速度和精度,分别是:
+`test_train_inference_python.sh`包含基础链条的4种运行模式,每种模式的运行数据不同,分别用于测试速度和精度,分别是:
- 模式1:lite_train_lite_infer,使用少量数据训练,用于快速验证训练到预测的走通流程,不验证精度和速度;
```shell
-bash test_tipc/prepare.sh ./test_tipc/configs/ch_ppocr_mobile_v2.0_det/train_windows_gpu_normal_normal_infer_python_windows_cpu_gpu.txt 'lite_train_lite_infer'
-bash test_tipc/test_train_inference_python.sh ./test_tipc/configs/ch_ppocr_mobile_v2.0_det/train_windows_gpu_normal_normal_infer_python_windows_cpu_gpu.txt 'lite_train_lite_infer'
+bash test_tipc/prepare.sh ./test_tipc/configs/ch_ppocr_mobile_v2_0_det/train_windows_gpu_normal_normal_infer_python_windows_cpu_gpu.txt 'lite_train_lite_infer'
+bash test_tipc/test_train_inference_python.sh ./test_tipc/configs/ch_ppocr_mobile_v2_0_det/train_windows_gpu_normal_normal_infer_python_windows_cpu_gpu.txt 'lite_train_lite_infer'
```
- 模式2:lite_train_whole_infer,使用少量数据训练,一定量数据预测,用于验证训练后的模型执行预测,预测速度是否合理;
```shell
-bash test_tipc/prepare.sh ./test_tipc/configs/ch_ppocr_mobile_v2.0_det/train_windows_gpu_normal_normal_infer_python_windows_cpu_gpu.txt 'lite_train_whole_infer'
-bash test_tipc/test_train_inference_python.sh ./test_tipc/configs/ch_ppocr_mobile_v2.0_det/train_windows_gpu_normal_normal_infer_python_windows_cpu_gpu.txt 'lite_train_whole_infer'
+bash test_tipc/prepare.sh ./test_tipc/configs/ch_ppocr_mobile_v2_0_det/train_windows_gpu_normal_normal_infer_python_windows_cpu_gpu.txt 'lite_train_whole_infer'
+bash test_tipc/test_train_inference_python.sh ./test_tipc/configs/ch_ppocr_mobile_v2_0_det/train_windows_gpu_normal_normal_infer_python_windows_cpu_gpu.txt 'lite_train_whole_infer'
```
- 模式3:whole_infer,不训练,全量数据预测,走通开源模型评估、动转静,检查inference model预测时间和精度;
```shell
-bash test_tipc/prepare.sh ./test_tipc/configs/ch_ppocr_mobile_v2.0_det/train_windows_gpu_normal_normal_infer_python_windows_cpu_gpu.txt 'whole_infer'
+bash test_tipc/prepare.sh ./test_tipc/configs/ch_ppocr_mobile_v2_0_det/train_windows_gpu_normal_normal_infer_python_windows_cpu_gpu.txt 'whole_infer'
# 用法1:
-bash test_tipc/test_train_inference_python.sh ./test_tipc/configs/ch_ppocr_mobile_v2.0_det/train_windows_gpu_normal_normal_infer_python_windows_cpu_gpu.txt 'whole_infer'
+bash test_tipc/test_train_inference_python.sh ./test_tipc/configs/ch_ppocr_mobile_v2_0_det/train_windows_gpu_normal_normal_infer_python_windows_cpu_gpu.txt 'whole_infer'
# 用法2: 指定GPU卡预测,第三个传入参数为GPU卡号
-bash test_tipc/test_train_inference_python.sh ./test_tipc/configs/ch_ppocr_mobile_v2.0_det/train_windows_gpu_normal_normal_infer_python_windows_cpu_gpu.txt 'whole_infer' '1'
+bash test_tipc/test_train_inference_python.sh ./test_tipc/configs/ch_ppocr_mobile_v2_0_det/train_windows_gpu_normal_normal_infer_python_windows_cpu_gpu.txt 'whole_infer' '1'
```
- 模式4:whole_train_whole_infer,CE: 全量数据训练,全量数据预测,验证模型训练精度,预测精度,预测速度;
```shell
-bash test_tipc/prepare.sh ./test_tipc/configs/ch_ppocr_mobile_v2.0_det/train_windows_gpu_normal_normal_infer_python_windows_cpu_gpu.txt 'whole_train_whole_infer'
-bash test_tipc/test_train_inference_python.sh ./test_tipc/configs/ch_ppocr_mobile_v2.0_det/train_windows_gpu_normal_normal_infer_python_windows_cpu_gpu.txt 'whole_train_whole_infer'
+bash test_tipc/prepare.sh ./test_tipc/configs/ch_ppocr_mobile_v2_0_det/train_windows_gpu_normal_normal_infer_python_windows_cpu_gpu.txt 'whole_train_whole_infer'
+bash test_tipc/test_train_inference_python.sh ./test_tipc/configs/ch_ppocr_mobile_v2_0_det/train_windows_gpu_normal_normal_infer_python_windows_cpu_gpu.txt 'whole_train_whole_infer'
```
-- 模式5:klquant_whole_infer,测试离线量化;
-```shell
-bash test_tipc/prepare.sh ./test_tipc/configs/ch_ppocr_mobile_v2.0_det_KL/model_linux_gpu_normal_normal_infer_python_windows_gpu_cpu.txt 'klquant_whole_infer'
-bash test_tipc/test_train_inference_python.sh ./test_tipc/configs/ch_ppocr_mobile_v2.0_det_KL/model_linux_gpu_normal_normal_infer_python_windows_gpu_cpu.txt 'klquant_whole_infer'
-```
-
-
运行相应指令后,在`test_tipc/output`文件夹下自动会保存运行日志。如'lite_train_lite_infer'模式下,会运行训练+inference的链条,因此,在`test_tipc/output`文件夹有以下文件:
```
-test_tipc/output/
+test_tipc/output/model_name/lite_train_lite_infer/
|- results_python.log # 运行指令状态的日志
|- norm_train_gpus_0_autocast_null/ # GPU 0号卡上正常训练的训练日志和模型保存文件夹
-|- pact_train_gpus_0_autocast_null/ # GPU 0号卡上量化训练的训练日志和模型保存文件夹
......
-|- python_infer_cpu_usemkldnn_True_threads_1_batchsize_1.log # CPU上开启Mkldnn线程数设置为1,测试batch_size=1条件下的预测运行日志
-|- python_infer_gpu_usetrt_True_precision_fp16_batchsize_1.log # GPU上开启TensorRT,测试batch_size=1的半精度预测日志
+|- python_infer_cpu_usemkldnn_False_threads_6_precision_fp32_batchsize_1.log # CPU上关闭Mkldnn线程数设置为6,测试batch_size=1条件下的fp32精度预测运行日志
+|- python_infer_gpu_usetrt_False_precision_fp32_batchsize_1.log # GPU上关闭TensorRT,测试batch_size=1的fp32精度预测日志
......
```
From a8e854b8814b6280f681e548912af7a8869209b3 Mon Sep 17 00:00:00 2001
From: andyjpaddle
Date: Wed, 14 Sep 2022 07:01:47 +0000
Subject: [PATCH 45/53] update aotolog install
---
test_tipc/docs/jeston_test_train_inference_python.md | 7 +------
test_tipc/docs/mac_test_train_inference_python.md | 7 +------
test_tipc/docs/test_train_inference_python.md | 7 +------
test_tipc/docs/win_test_train_inference_python.md | 7 +------
4 files changed, 4 insertions(+), 24 deletions(-)
diff --git a/test_tipc/docs/jeston_test_train_inference_python.md b/test_tipc/docs/jeston_test_train_inference_python.md
index b25175ed00..22fc21c1cb 100644
--- a/test_tipc/docs/jeston_test_train_inference_python.md
+++ b/test_tipc/docs/jeston_test_train_inference_python.md
@@ -24,12 +24,7 @@ Jetson端基础训练预测功能测试的主程序为`test_inference_inference.
```
- 安装autolog(规范化日志输出工具)
```
- git clone https://github.com/LDOUBLEV/AutoLog
- cd AutoLog
- pip install -r requirements.txt
- python setup.py bdist_wheel
- pip install ./dist/auto_log-1.0.0-py3-none-any.whl
- cd ../
+ pip install https://paddleocr.bj.bcebos.com/libs/auto_log-1.2.0-py3-none-any.whl
```
- 安装PaddleSlim (可选)
```
diff --git a/test_tipc/docs/mac_test_train_inference_python.md b/test_tipc/docs/mac_test_train_inference_python.md
index 2c25bcddac..759ea51643 100644
--- a/test_tipc/docs/mac_test_train_inference_python.md
+++ b/test_tipc/docs/mac_test_train_inference_python.md
@@ -33,12 +33,7 @@ Mac端无GPU,环境准备只需要Python环境即可,安装PaddlePaddle等
```
- 安装autolog(规范化日志输出工具)
```
- git clone https://github.com/LDOUBLEV/AutoLog
- cd AutoLog
- pip install -r requirements.txt
- python setup.py bdist_wheel
- pip install ./dist/auto_log-1.2.0-py3-none-any.whl
- cd ../
+ pip install https://paddleocr.bj.bcebos.com/libs/auto_log-1.2.0-py3-none-any.whl
```
- 安装PaddleSlim (可选)
```
diff --git a/test_tipc/docs/test_train_inference_python.md b/test_tipc/docs/test_train_inference_python.md
index 2636192511..d1dbd8ee47 100644
--- a/test_tipc/docs/test_train_inference_python.md
+++ b/test_tipc/docs/test_train_inference_python.md
@@ -43,12 +43,7 @@ Linux端基础训练预测功能测试的主程序为`test_train_inference_pytho
```
- 安装autolog(规范化日志输出工具)
```
- git clone https://github.com/LDOUBLEV/AutoLog
- cd AutoLog
- pip3 install -r requirements.txt
- python3 setup.py bdist_wheel
- pip3 install ./dist/auto_log-1.2.0-py3-none-any.whl
- cd ../
+ pip3 install https://paddleocr.bj.bcebos.com/libs/auto_log-1.2.0-py3-none-any.whl
```
- 安装PaddleSlim (可选)
```
diff --git a/test_tipc/docs/win_test_train_inference_python.md b/test_tipc/docs/win_test_train_inference_python.md
index ba9aa21300..d631c38873 100644
--- a/test_tipc/docs/win_test_train_inference_python.md
+++ b/test_tipc/docs/win_test_train_inference_python.md
@@ -36,12 +36,7 @@ Windows端基础训练预测功能测试的主程序为`test_train_inference_pyt
```
- 安装autolog(规范化日志输出工具)
```
- git clone https://github.com/LDOUBLEV/AutoLog
- cd AutoLog
- pip install -r requirements.txt
- python setup.py bdist_wheel
- pip install ./dist/auto_log-1.2.0-py3-none-any.whl
- cd ../
+ pip install https://paddleocr.bj.bcebos.com/libs/auto_log-1.2.0-py3-none-any.whl
```
- 安装PaddleSlim (可选)
```
From 541954da526d575b448babf0859c0c7e0d831c12 Mon Sep 17 00:00:00 2001
From: Wenmuzhou <572459439@qq.com>
Date: Wed, 14 Sep 2022 15:30:30 +0800
Subject: [PATCH 46/53] add layoutxml kl he pact
---
deploy/slim/quantization/quant.py | 6 +--
deploy/slim/quantization/quant_kl.py | 24 +++++++--
.../layoutxlm_ser/train_pact_infer_python.txt | 53 +++++++++++++++++++
.../layoutxlm_ser/train_ptq_infer_python.txt | 21 ++++++++
test_tipc/prepare.sh | 31 +++++++++--
5 files changed, 125 insertions(+), 10 deletions(-)
create mode 100644 test_tipc/configs/layoutxlm_ser/train_pact_infer_python.txt
create mode 100644 test_tipc/configs/layoutxlm_ser/train_ptq_infer_python.txt
diff --git a/deploy/slim/quantization/quant.py b/deploy/slim/quantization/quant.py
index 64521b5e06..ef2c3e28f9 100755
--- a/deploy/slim/quantization/quant.py
+++ b/deploy/slim/quantization/quant.py
@@ -158,8 +158,7 @@ def main(config, device, logger, vdl_writer):
pre_best_model_dict = dict()
# load fp32 model to begin quantization
- if config["Global"]["pretrained_model"] is not None:
- pre_best_model_dict = load_model(config, model)
+ pre_best_model_dict = load_model(config, model, None, config['Architecture']["model_type"])
freeze_params = False
if config['Architecture']["algorithm"] in ["Distillation"]:
@@ -184,8 +183,7 @@ def main(config, device, logger, vdl_writer):
model=model)
# resume PACT training process
- if config["Global"]["checkpoints"] is not None:
- pre_best_model_dict = load_model(config, model, optimizer)
+ pre_best_model_dict = load_model(config, model, optimizer, config['Architecture']["model_type"])
# build metric
eval_class = build_metric(config['Metric'])
diff --git a/deploy/slim/quantization/quant_kl.py b/deploy/slim/quantization/quant_kl.py
index cc3a455b97..73e1a957e8 100755
--- a/deploy/slim/quantization/quant_kl.py
+++ b/deploy/slim/quantization/quant_kl.py
@@ -97,6 +97,17 @@ def sample_generator(loader):
return __reader__
+def sample_generator_layoutxlm_ser(loader):
+ def __reader__():
+ for indx, data in enumerate(loader):
+ input_ids = np.array(data[0])
+ bbox = np.array(data[1])
+ attention_mask = np.array(data[2])
+ token_type_ids = np.array(data[3])
+ images = np.array(data[4])
+ yield [input_ids, bbox, attention_mask, token_type_ids, images]
+
+ return __reader__
def main(config, device, logger, vdl_writer):
# init dist environment
@@ -107,16 +118,18 @@ def main(config, device, logger, vdl_writer):
# build dataloader
config['Train']['loader']['num_workers'] = 0
+ is_layoutxlm_ser = config['Architecture']['model_type'] =='kie' and config['Architecture']['Backbone']['name'] == 'LayoutXLMForSer'
train_dataloader = build_dataloader(config, 'Train', device, logger)
if config['Eval']:
config['Eval']['loader']['num_workers'] = 0
valid_dataloader = build_dataloader(config, 'Eval', device, logger)
+ if is_layoutxlm_ser:
+ train_dataloader = valid_dataloader
else:
valid_dataloader = None
paddle.enable_static()
- place = paddle.CPUPlace()
- exe = paddle.static.Executor(place)
+ exe = paddle.static.Executor(device)
if 'inference_model' in global_config.keys(): # , 'inference_model'):
inference_model_dir = global_config['inference_model']
@@ -127,6 +140,11 @@ def main(config, device, logger, vdl_writer):
raise ValueError(
"Please set inference model dir in Global.inference_model or Global.pretrained_model for post-quantazition"
)
+
+ if is_layoutxlm_ser:
+ generator = sample_generator_layoutxlm_ser(train_dataloader)
+ else:
+ generator = sample_generator(train_dataloader)
paddleslim.quant.quant_post_static(
executor=exe,
@@ -134,7 +152,7 @@ def main(config, device, logger, vdl_writer):
model_filename='inference.pdmodel',
params_filename='inference.pdiparams',
quantize_model_path=global_config['save_inference_dir'],
- sample_generator=sample_generator(train_dataloader),
+ sample_generator=generator,
save_model_filename='inference.pdmodel',
save_params_filename='inference.pdiparams',
batch_size=1,
diff --git a/test_tipc/configs/layoutxlm_ser/train_pact_infer_python.txt b/test_tipc/configs/layoutxlm_ser/train_pact_infer_python.txt
new file mode 100644
index 0000000000..fbf2a88026
--- /dev/null
+++ b/test_tipc/configs/layoutxlm_ser/train_pact_infer_python.txt
@@ -0,0 +1,53 @@
+===========================train_params===========================
+model_name:layoutxlm_ser_PACT
+python:python3.7
+gpu_list:0|0,1
+Global.use_gpu:True|True
+Global.auto_cast:fp32
+Global.epoch_num:lite_train_lite_infer=1|whole_train_whole_infer=17
+Global.save_model_dir:./output/
+Train.loader.batch_size_per_card:lite_train_lite_infer=4|whole_train_whole_infer=8
+Architecture.Backbone.checkpoints:pretrain_models/ser_LayoutXLM_xfun_zh
+train_model_name:latest
+train_infer_img_dir:ppstructure/docs/kie/input/zh_val_42.jpg
+null:null
+##
+trainer:pact_train
+norm_train:null
+pact_train:deploy/slim/quantization/quant.py -c test_tipc/configs/layoutxlm_ser/ser_layoutxlm_xfund_zh.yml -o
+fpgm_train:null
+distill_train:null
+null:null
+null:null
+##
+===========================eval_params===========================
+eval:null
+null:null
+##
+===========================infer_params===========================
+Global.save_inference_dir:./output/
+Architecture.Backbone.checkpoints:
+norm_export:null
+quant_export:deploy/slim/quantization/export_model.py -c test_tipc/configs/layoutxlm_ser/ser_layoutxlm_xfund_zh.yml -o
+fpgm_export: null
+distill_export:null
+export1:null
+export2:null
+##
+infer_model:null
+infer_export:null
+infer_quant:False
+inference:ppstructure/kie/predict_kie_token_ser.py --kie_algorithm=LayoutXLM --ser_dict_path=train_data/XFUND/class_list_xfun.txt --output=output
+--use_gpu:True|False
+--enable_mkldnn:False
+--cpu_threads:6
+--rec_batch_num:1
+--use_tensorrt:False
+--precision:fp32
+--ser_model_dir:
+--image_dir:./ppstructure/docs/kie/input/zh_val_42.jpg
+null:null
+--benchmark:False
+null:null
+===========================infer_benchmark_params==========================
+random_infer_input:[{float32,[3,224,224]}]
diff --git a/test_tipc/configs/layoutxlm_ser/train_ptq_infer_python.txt b/test_tipc/configs/layoutxlm_ser/train_ptq_infer_python.txt
new file mode 100644
index 0000000000..47e1e7026b
--- /dev/null
+++ b/test_tipc/configs/layoutxlm_ser/train_ptq_infer_python.txt
@@ -0,0 +1,21 @@
+===========================train_params===========================
+model_name:layoutxlm_ser_KL
+python:python3.7
+Global.pretrained_model:
+Global.save_inference_dir:null
+infer_model:./inference/ser_LayoutXLM_xfun_zh_infer/
+infer_export:deploy/slim/quantization/quant_kl.py -c test_tipc/configs/layoutxlm_ser/ser_layoutxlm_xfund_zh.yml -o Train.loader.batch_size_per_card=1 Eval.loader.batch_size_per_card=1
+infer_quant:True
+inference:ppstructure/kie/predict_kie_token_ser.py --kie_algorithm=LayoutXLM --ser_dict_path=./train_data/XFUND/class_list_xfun.txt
+--use_gpu:True|False
+--enable_mkldnn:False
+--cpu_threads:6
+--rec_batch_num:1
+--use_tensorrt:False
+--precision:int8
+--ser_model_dir:
+--image_dir:./ppstructure/docs/kie/input/zh_val_42.jpg
+null:null
+--benchmark:False
+null:null
+null:null
diff --git a/test_tipc/prepare.sh b/test_tipc/prepare.sh
index 5d50a5ade9..fcdd2f05b4 100644
--- a/test_tipc/prepare.sh
+++ b/test_tipc/prepare.sh
@@ -145,7 +145,7 @@ if [ ${MODE} = "lite_train_lite_infer" ];then
array=(${python_name_list})
python_name=${array[0]}
${python_name} -m pip install -r requirements.txt
- ${python_name} -m pip install git+https://github.com/LDOUBLEV/AutoLog
+ ${python_name} -m pip install https://paddleocr.bj.bcebos.com/libs/auto_log-1.2.0-py3-none-any.whl
# pretrain lite train data
wget -nc -P ./pretrain_models/ https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/MobileNetV3_large_x0_5_pretrained.pdparams --no-check-certificate
wget -nc -P ./pretrain_models/ https://paddleocr.bj.bcebos.com/dygraph_v2.0/en/det_mv3_db_v2.0_train.tar --no-check-certificate
@@ -257,7 +257,17 @@ if [ ${MODE} = "lite_train_lite_infer" ];then
wget -nc -P ./pretrain_models/ https://paddleocr.bj.bcebos.com/rec_r32_gaspin_bilstm_att_train.tar --no-check-certificate
cd ./pretrain_models/ && tar xf rec_r32_gaspin_bilstm_att_train.tar && cd ../
fi
- if [ ${model_name} == "layoutxlm_ser" ] || [ ${model_name} == "vi_layoutxlm_ser" ]; then
+ if [ ${model_name} == "layoutxlm_ser" ]; then
+ ${python_name} -m pip install -r ppstructure/kie/requirements.txt
+ ${python_name} -m pip install opencv-python -U
+ wget -nc -P ./train_data/ https://paddleocr.bj.bcebos.com/ppstructure/dataset/XFUND.tar --no-check-certificate
+ cd ./train_data/ && tar xf XFUND.tar
+ cd ../
+
+ wget -nc -P ./pretrain_models/ https://paddleocr.bj.bcebos.com/pplayout/ser_LayoutXLM_xfun_zh.tar --no-check-certificate
+ cd ./pretrain_models/ && tar xf ser_LayoutXLM_xfun_zh.tar && cd ../
+ fi
+ if [ ${model_name} == "vi_layoutxlm_ser" ]; then
${python_name} -m pip install -r ppstructure/kie/requirements.txt
${python_name} -m pip install opencv-python -U
wget -nc -P ./train_data/ https://paddleocr.bj.bcebos.com/ppstructure/dataset/XFUND.tar --no-check-certificate
@@ -332,9 +342,18 @@ elif [ ${MODE} = "lite_train_whole_infer" ];then
cd ./inference/ && tar xf en_ppocr_mobile_v2.0_table_det_infer.tar && tar xf en_ppocr_mobile_v2.0_table_rec_infer.tar && cd ../
fi
elif [ ${MODE} = "whole_infer" ];then
+ python_name_list=$(func_parser_value "${lines[2]}")
+ array=(${python_name_list})
+ python_name=${array[0]}
+ ${python_name} -m pip install paddleslim
+ ${python_name} -m pip install -r requirements.txt
wget -nc -P ./inference https://paddleocr.bj.bcebos.com/dygraph_v2.0/test/ch_det_data_50.tar --no-check-certificate
wget -nc -P ./inference/ https://paddleocr.bj.bcebos.com/dygraph_v2.0/test/rec_inference.tar --no-check-certificate
cd ./inference && tar xf rec_inference.tar && tar xf ch_det_data_50.tar && cd ../
+ wget -nc -P ./train_data/ https://paddleocr.bj.bcebos.com/ppstructure/dataset/XFUND.tar --no-check-certificate
+ cd ./train_data/ && tar xf XFUND.tar && cd ../
+ head -n 2 train_data/XFUND/zh_val/val.json > train_data/XFUND/zh_val/val_lite.json
+ mv train_data/XFUND/zh_val/val_lite.json train_data/XFUND/zh_val/val.json
if [ ${model_name} = "ch_ppocr_mobile_v2_0_det" ]; then
eval_model_name="ch_ppocr_mobile_v2.0_det_train"
rm -rf ./train_data/icdar2015
@@ -500,6 +519,12 @@ elif [ ${MODE} = "whole_infer" ];then
wget -nc -P ./inference/ https://paddleocr.bj.bcebos.com/dygraph_v2.0/table/en_ppocr_mobile_v2.0_table_rec_infer.tar --no-check-certificate
cd ./inference/ && tar xf en_ppocr_mobile_v2.0_table_structure_infer.tar && tar xf en_ppocr_mobile_v2.0_table_det_infer.tar && tar xf en_ppocr_mobile_v2.0_table_rec_infer.tar && cd ../
fi
+ if [[ ${model_name} =~ "layoutxlm_ser" ]]; then
+ ${python_name} -m pip install -r ppstructure/kie/requirements.txt
+ ${python_name} -m pip install opencv-python -U
+ wget -nc -P ./inference/ https://paddleocr.bj.bcebos.com/pplayout/ser_LayoutXLM_xfun_zh_infer.tar --no-check-certificate
+ cd ./inference/ && tar xf ser_LayoutXLM_xfun_zh_infer.tar & cd ../
+ fi
fi
if [[ ${model_name} =~ "KL" ]]; then
@@ -667,7 +692,7 @@ if [ ${MODE} = "serving_infer" ];then
${python_name} -m pip install paddle-serving-server-gpu
${python_name} -m pip install paddle_serving_client
${python_name} -m pip install paddle-serving-app
- ${python_name} -m pip install git+https://github.com/LDOUBLEV/AutoLog
+ ${python_name} -m pip install https://paddleocr.bj.bcebos.com/libs/auto_log-1.2.0-py3-none-any.whl
# wget model
if [ ${model_name} == "ch_ppocr_mobile_v2_0_det_KL" ] || [ ${model_name} == "ch_ppocr_mobile_v2.0_rec_KL" ] ; then
wget -nc -P ./inference https://paddleocr.bj.bcebos.com/tipc_fake_model/ch_ppocr_mobile_v2.0_det_klquant_infer.tar --no-check-certificate
From 4b59fa2996b79f7ce312a26cc8da6228e69ce8a9 Mon Sep 17 00:00:00 2001
From: Wenmuzhou <572459439@qq.com>
Date: Wed, 14 Sep 2022 17:06:55 +0800
Subject: [PATCH 47/53] add table cpp infer to tipc
---
..._normal_normal_infer_cpp_linux_gpu_cpu.txt | 20 +++++++++++++++++++
test_tipc/prepare.sh | 12 +++++++++++
2 files changed, 32 insertions(+)
create mode 100644 test_tipc/configs/en_table_structure/model_linux_gpu_normal_normal_infer_cpp_linux_gpu_cpu.txt
diff --git a/test_tipc/configs/en_table_structure/model_linux_gpu_normal_normal_infer_cpp_linux_gpu_cpu.txt b/test_tipc/configs/en_table_structure/model_linux_gpu_normal_normal_infer_cpp_linux_gpu_cpu.txt
new file mode 100644
index 0000000000..ad002a334e
--- /dev/null
+++ b/test_tipc/configs/en_table_structure/model_linux_gpu_normal_normal_infer_cpp_linux_gpu_cpu.txt
@@ -0,0 +1,20 @@
+===========================cpp_infer_params===========================
+model_name:en_table_structure
+use_opencv:True
+infer_model:./inference/en_ppocr_mobile_v2.0_table_structure_infer/
+infer_quant:False
+inference:./deploy/cpp_infer/build/ppocr --rec_img_h=32 --det_model_dir=./inference/en_ppocr_mobile_v2.0_table_det_infer --rec_model_dir=./inference/en_ppocr_mobile_v2.0_table_rec_infer --rec_char_dict_path=./ppocr/utils/dict/table_dict.txt --table_char_dict_path=./ppocr/utils/dict/table_structure_dict.txt --limit_side_len=736 --limit_type=min --output=./output/table --merge_no_span_structure=False --type=structure --table=True
+--use_gpu:True|False
+--enable_mkldnn:False
+--cpu_threads:6
+--rec_batch_num:6
+--use_tensorrt:False
+--precision:fp32
+--table_model_dir:
+--image_dir:./ppstructure/docs/table/table.jpg
+null:null
+--benchmark:True
+--det:True
+--rec:True
+--cls:False
+--use_angle_cls:False
\ No newline at end of file
diff --git a/test_tipc/prepare.sh b/test_tipc/prepare.sh
index fcdd2f05b4..1185dec59e 100644
--- a/test_tipc/prepare.sh
+++ b/test_tipc/prepare.sh
@@ -577,6 +577,12 @@ if [[ ${model_name} =~ "KL" ]]; then
cd ./inference/ && tar xf en_ppocr_mobile_v2.0_table_structure_infer.tar && tar xf en_ppocr_mobile_v2.0_table_det_infer.tar && tar xf en_ppocr_mobile_v2.0_table_rec_infer.tar && cd ../
cd ./train_data/ && tar xf pubtabnet.tar && cd ../
fi
+ if [[ ${model_name} =~ "layoutxlm_ser_KL" ]]; then
+ wget -nc -P ./train_data/ https://paddleocr.bj.bcebos.com/ppstructure/dataset/XFUND.tar --no-check-certificate
+ cd ./train_data/ && tar xf XFUND.tar && cd ../
+ wget -nc -P ./inference/ https://paddleocr.bj.bcebos.com/pplayout/ser_LayoutXLM_xfun_zh_infer.tar --no-check-certificate
+ cd ./inference/ && tar xf ser_LayoutXLM_xfun_zh_infer.tar & cd ../
+ fi
fi
if [ ${MODE} = "cpp_infer" ];then
@@ -681,6 +687,12 @@ if [ ${MODE} = "cpp_infer" ];then
wget -nc -P ./inference https://paddleocr.bj.bcebos.com/PP-OCRv3/chinese/ch_PP-OCRv3_rec_infer.tar --no-check-certificate
cd ./inference && tar xf ch_PP-OCRv3_det_infer.tar && tar xf ch_PP-OCRv3_rec_infer.tar && tar xf ch_det_data_50.tar && cd ../
fi
+ elif [ ${model_name} = "en_table_structure_KL" ];then
+ wget -nc -P ./inference/ https://paddleocr.bj.bcebos.com/dygraph_v2.0/table/en_ppocr_mobile_v2.0_table_structure_infer.tar --no-check-certificate
+ wget -nc -P ./inference/ https://paddleocr.bj.bcebos.com/dygraph_v2.0/table/en_ppocr_mobile_v2.0_table_det_infer.tar --no-check-certificate
+ wget -nc -P ./inference/ https://paddleocr.bj.bcebos.com/dygraph_v2.0/table/en_ppocr_mobile_v2.0_table_rec_infer.tar --no-check-certificate
+ cd ./inference/ && tar xf en_ppocr_mobile_v2.0_table_structure_infer.tar && tar xf en_ppocr_mobile_v2.0_table_det_infer.tar && tar xf en_ppocr_mobile_v2.0_table_rec_infer.tar && cd ../
+ fi
fi
if [ ${MODE} = "serving_infer" ];then
From b5c7400161c3e0fe778a8a8ac771b55b7a471b27 Mon Sep 17 00:00:00 2001
From: Wenmuzhou <572459439@qq.com>
Date: Wed, 14 Sep 2022 17:24:49 +0800
Subject: [PATCH 48/53] add slanet cpp infer to tipc
---
..._normal_normal_infer_cpp_linux_gpu_cpu.txt | 20 +++++++++++++++++++
1 file changed, 20 insertions(+)
create mode 100644 test_tipc/configs/slanet/model_linux_gpu_normal_normal_infer_cpp_linux_gpu_cpu.txt
diff --git a/test_tipc/configs/slanet/model_linux_gpu_normal_normal_infer_cpp_linux_gpu_cpu.txt b/test_tipc/configs/slanet/model_linux_gpu_normal_normal_infer_cpp_linux_gpu_cpu.txt
new file mode 100644
index 0000000000..1b4226706b
--- /dev/null
+++ b/test_tipc/configs/slanet/model_linux_gpu_normal_normal_infer_cpp_linux_gpu_cpu.txt
@@ -0,0 +1,20 @@
+===========================cpp_infer_params===========================
+model_name:slanet
+use_opencv:True
+infer_model:./inference/ch_ppstructure_mobile_v2.0_SLANet_infer/
+infer_quant:False
+inference:./deploy/cpp_infer/build/ppocr --det_model_dir=./inference/ch_PP-OCRv3_det_infer --rec_model_dir=./inference/ch_PP-OCRv3_rec_infer --output=./output/table --type=structure --table=True --rec_char_dict_path=./ppocr/utils/ppocr_keys_v1.txt --table_char_dict_path=./ppocr/utils/dict/table_structure_dict_ch.txt
+--use_gpu:True|False
+--enable_mkldnn:False
+--cpu_threads:6
+--rec_batch_num:6
+--use_tensorrt:False
+--precision:fp32
+--table_model_dir:
+--image_dir:./ppstructure/docs/table/table.jpg
+null:null
+--benchmark:True
+--det:True
+--rec:True
+--cls:False
+--use_angle_cls:False
\ No newline at end of file
From b5268dc3a0847dce2668265e07ff50d54265b2d8 Mon Sep 17 00:00:00 2001
From: huangjun12 <2399845970@qq.com>
Date: Thu, 15 Sep 2022 11:08:16 +0000
Subject: [PATCH 49/53] add centripetal text model
---
configs/det/det_r18_vd_ct.yml | 107 ++++++
doc/doc_ch/algorithm_det_ct.md | 95 +++++
doc/doc_en/algorithm_det_ct_en.md | 96 +++++
doc/imgs_results/det_res_img623_ct.jpg | Bin 0 -> 140971 bytes
ppocr/data/imaug/__init__.py | 1 +
ppocr/data/imaug/ct_process.py | 355 ++++++++++++++++++
ppocr/data/imaug/label_ops.py | 26 ++
ppocr/losses/__init__.py | 3 +-
ppocr/losses/det_ct_loss.py | 276 ++++++++++++++
ppocr/metrics/__init__.py | 4 +-
ppocr/metrics/ct_metric.py | 52 +++
ppocr/modeling/heads/__init__.py | 3 +-
ppocr/modeling/heads/det_ct_head.py | 69 ++++
ppocr/modeling/necks/__init__.py | 4 +-
ppocr/modeling/necks/ct_fpn.py | 185 +++++++++
ppocr/postprocess/__init__.py | 3 +-
ppocr/postprocess/ct_postprocess.py | 154 ++++++++
ppocr/utils/e2e_metric/Deteval.py | 225 ++++++++---
requirements.txt | 1 +
.../configs/det_r18_ct/train_infer_python.txt | 53 +++
test_tipc/prepare.sh | 5 +
tools/infer/predict_det.py | 8 +-
tools/program.py | 2 +-
tools/train.py | 15 +-
train.sh | 2 +-
25 files changed, 1682 insertions(+), 62 deletions(-)
create mode 100644 configs/det/det_r18_vd_ct.yml
create mode 100644 doc/doc_ch/algorithm_det_ct.md
create mode 100644 doc/doc_en/algorithm_det_ct_en.md
create mode 100644 doc/imgs_results/det_res_img623_ct.jpg
create mode 100644 ppocr/data/imaug/ct_process.py
create mode 100755 ppocr/losses/det_ct_loss.py
create mode 100644 ppocr/metrics/ct_metric.py
create mode 100644 ppocr/modeling/heads/det_ct_head.py
create mode 100644 ppocr/modeling/necks/ct_fpn.py
create mode 100755 ppocr/postprocess/ct_postprocess.py
create mode 100644 test_tipc/configs/det_r18_ct/train_infer_python.txt
diff --git a/configs/det/det_r18_vd_ct.yml b/configs/det/det_r18_vd_ct.yml
new file mode 100644
index 0000000000..42922dfd22
--- /dev/null
+++ b/configs/det/det_r18_vd_ct.yml
@@ -0,0 +1,107 @@
+Global:
+ use_gpu: true
+ epoch_num: 600
+ log_smooth_window: 20
+ print_batch_step: 10
+ save_model_dir: ./output/det_ct/
+ save_epoch_step: 10
+ # evaluation is run every 2000 iterations
+ eval_batch_step: [0,1000]
+ cal_metric_during_train: False
+ pretrained_model: ./pretrain_models/ResNet18_vd_pretrained.pdparams
+ checkpoints:
+ save_inference_dir:
+ use_visualdl: False
+ infer_img: doc/imgs_en/img623.jpg
+ save_res_path: ./output/det_ct/predicts_ct.txt
+
+Architecture:
+ model_type: det
+ algorithm: CT
+ Transform:
+ Backbone:
+ name: ResNet_vd
+ layers: 18
+ Neck:
+ name: CTFPN
+ Head:
+ name: CT_Head
+ in_channels: 512
+ hidden_dim: 128
+ num_classes: 3
+
+Loss:
+ name: CTLoss
+
+Optimizer:
+ name: Adam
+ lr: #PolynomialDecay
+ name: Linear
+ learning_rate: 0.001
+ end_lr: 0.
+ epochs: 600
+ step_each_epoch: 1254
+ power: 0.9
+
+PostProcess:
+ name: CTPostProcess
+ box_type: poly
+
+Metric:
+ name: CTMetric
+ main_indicator: f_score
+
+Train:
+ dataset:
+ name: SimpleDataSet
+ data_dir: ./train_data/total_text/train
+ label_file_list:
+ - ./train_data/total_text/train/train.txt
+ ratio_list: [1.0]
+ transforms:
+ - DecodeImage:
+ img_mode: RGB
+ channel_first: False
+ - CTLabelEncode: # Class handling label
+ - RandomScale:
+ - MakeShrink:
+ - GroupRandomHorizontalFlip:
+ - GroupRandomRotate:
+ - GroupRandomCropPadding:
+ - MakeCentripetalShift:
+ - ColorJitter:
+ brightness: 0.125
+ saturation: 0.5
+ - ToCHWImage:
+ - NormalizeImage:
+ - KeepKeys:
+ keep_keys: ['image', 'gt_kernel', 'training_mask', 'gt_instance', 'gt_kernel_instance', 'training_mask_distance', 'gt_distance'] # the order of the dataloader list
+ loader:
+ shuffle: True
+ drop_last: True
+ batch_size_per_card: 4
+ num_workers: 8
+
+Eval:
+ dataset:
+ name: SimpleDataSet
+ data_dir: ./train_data/total_text/test
+ label_file_list:
+ - ./train_data/total_text/test/test.txt
+ ratio_list: [1.0]
+ transforms:
+ - DecodeImage:
+ img_mode: RGB
+ channel_first: False
+ - CTLabelEncode: # Class handling label
+ - ScaleAlignedShort:
+ - NormalizeImage:
+ order: 'hwc'
+ - ToCHWImage:
+ - KeepKeys:
+ keep_keys: ['image', 'shape', 'polys', 'texts'] # the order of the dataloader list
+ loader:
+ shuffle: False
+ drop_last: False
+ batch_size_per_card: 1
+ num_workers: 2
diff --git a/doc/doc_ch/algorithm_det_ct.md b/doc/doc_ch/algorithm_det_ct.md
new file mode 100644
index 0000000000..ea3522b7bf
--- /dev/null
+++ b/doc/doc_ch/algorithm_det_ct.md
@@ -0,0 +1,95 @@
+# CT
+
+- [1. 算法简介](#1)
+- [2. 环境配置](#2)
+- [3. 模型训练、评估、预测](#3)
+ - [3.1 训练](#3-1)
+ - [3.2 评估](#3-2)
+ - [3.3 预测](#3-3)
+- [4. 推理部署](#4)
+ - [4.1 Python推理](#4-1)
+ - [4.2 C++推理](#4-2)
+ - [4.3 Serving服务化部署](#4-3)
+ - [4.4 更多推理部署](#4-4)
+- [5. FAQ](#5)
+
+
+## 1. 算法简介
+
+论文信息:
+> [CentripetalText: An Efficient Text Instance Representation for Scene Text Detection](https://arxiv.org/abs/2107.05945)
+> Tao Sheng, Jie Chen, Zhouhui Lian
+> NeurIPS, 2021
+
+
+在Total-Text文本检测公开数据集上,算法复现效果如下:
+
+|模型|骨干网络|配置文件|precision|recall|Hmean|下载链接|
+| --- | --- | --- | --- | --- | --- | --- |
+|CT|ResNet18_vd|[configs/det/det_r18_vd_ct.yml](../../configs/det/det_r18_vd_ct.yml)|88.68%|81.70%|85.05%|[训练模型](https://paddleocr.bj.bcebos.com/dygraph_v2.0/en/det_r18_ct_train.tar)|
+
+
+
+## 2. 环境配置
+请先参考[《运行环境准备》](./environment.md)配置PaddleOCR运行环境,参考[《项目克隆》](./clone.md)克隆项目代码。
+
+
+
+## 3. 模型训练、评估、预测
+
+CT模型使用Total-Text文本检测公开数据集训练得到,数据集下载可参考 [Total-Text-Dataset](https://github.com/cs-chan/Total-Text-Dataset/tree/master/Dataset), 我们将标签文件转成了paddleocr格式,转换好的标签文件下载参考[train.txt](https://paddleocr.bj.bcebos.com/dataset/ct_tipc/train.txt), [text.txt](https://paddleocr.bj.bcebos.com/dataset/ct_tipc/test.txt)。
+
+请参考[文本检测训练教程](./detection.md)。PaddleOCR对代码进行了模块化,训练不同的检测模型只需要**更换配置文件**即可。
+
+
+
+## 4. 推理部署
+
+
+### 4.1 Python推理
+首先将CT文本检测训练过程中保存的模型,转换成inference model。以基于Resnet18_vd骨干网络,在Total-Text英文数据集训练的模型为例( [模型下载地址](https://paddleocr.bj.bcebos.com/dygraph_v2.0/en/det_r18_ct_train.tar) ),可以使用如下命令进行转换:
+
+```shell
+python3 tools/export_model.py -c configs/det/det_r18_vd_ct.yml -o Global.pretrained_model=./det_r18_ct_train/best_accuracy Global.save_inference_dir=./inference/det_ct
+```
+
+CT文本检测模型推理,可以执行如下命令:
+
+```shell
+python3 tools/infer/predict_det.py --image_dir="./doc/imgs_en/img623.jpg" --det_model_dir="./inference/det_ct/" --det_algorithm="CT"
+```
+
+可视化文本检测结果默认保存到`./inference_results`文件夹里面,结果文件的名称前缀为'det_res'。结果示例如下:
+
+
+
+
+
+### 4.2 C++推理
+
+暂不支持
+
+
+### 4.3 Serving服务化部署
+
+暂不支持
+
+
+### 4.4 更多推理部署
+
+暂不支持
+
+
+## 5. FAQ
+
+
+## 引用
+
+```bibtex
+@inproceedings{sheng2021centripetaltext,
+ title={CentripetalText: An Efficient Text Instance Representation for Scene Text Detection},
+ author={Tao Sheng and Jie Chen and Zhouhui Lian},
+ booktitle={Thirty-Fifth Conference on Neural Information Processing Systems},
+ year={2021}
+}
+```
diff --git a/doc/doc_en/algorithm_det_ct_en.md b/doc/doc_en/algorithm_det_ct_en.md
new file mode 100644
index 0000000000..d56b3fc6b3
--- /dev/null
+++ b/doc/doc_en/algorithm_det_ct_en.md
@@ -0,0 +1,96 @@
+# CT
+
+- [1. Introduction](#1)
+- [2. Environment](#2)
+- [3. Model Training / Evaluation / Prediction](#3)
+ - [3.1 Training](#3-1)
+ - [3.2 Evaluation](#3-2)
+ - [3.3 Prediction](#3-3)
+- [4. Inference and Deployment](#4)
+ - [4.1 Python Inference](#4-1)
+ - [4.2 C++ Inference](#4-2)
+ - [4.3 Serving](#4-3)
+ - [4.4 More](#4-4)
+- [5. FAQ](#5)
+
+
+## 1. Introduction
+
+Paper:
+> [CentripetalText: An Efficient Text Instance Representation for Scene Text Detection](https://arxiv.org/abs/2107.05945)
+> Tao Sheng, Jie Chen, Zhouhui Lian
+> NeurIPS, 2021
+
+
+On the Total-Text dataset, the text detection result is as follows:
+
+|Model|Backbone|Configuration|Precision|Recall|Hmean|Download|
+| --- | --- | --- | --- | --- | --- | --- |
+|CT|ResNet18_vd|[configs/det/det_r18_vd_ct.yml](../../configs/det/det_r18_vd_ct.yml)|88.68%|81.70%|85.05%|[trained model](https://paddleocr.bj.bcebos.com/dygraph_v2.0/en/det_r18_ct_train.tar)|
+
+
+
+## 2. Environment
+Please prepare your environment referring to [prepare the environment](./environment_en.md) and [clone the repo](./clone_en.md).
+
+
+
+## 3. Model Training / Evaluation / Prediction
+
+
+The above CT model is trained using the Total-Text text detection public dataset. For the download of the dataset, please refer to [Total-Text-Dataset](https://github.com/cs-chan/Total-Text-Dataset/tree/master/Dataset). PaddleOCR format annotation download link [train.txt](https://paddleocr.bj.bcebos.com/dataset/ct_tipc/train.txt), [test.txt](https://paddleocr.bj.bcebos.com/dataset/ct_tipc/test.txt).
+
+
+Please refer to [text detection training tutorial](./detection_en.md). PaddleOCR has modularized the code structure, so that you only need to **replace the configuration file** to train different detection models.
+
+
+## 4. Inference and Deployment
+
+
+### 4.1 Python Inference
+First, convert the model saved in the CT text detection training process into an inference model. Taking the model based on the Resnet18_vd backbone network and trained on the Total Text English dataset as example ([model download link](https://paddleocr.bj.bcebos.com/dygraph_v2.0/en/det_r18_ct_train.tar)), you can use the following command to convert:
+
+```shell
+python3 tools/export_model.py -c configs/det/det_r18_vd_ct.yml -o Global.pretrained_model=./det_r18_ct_train/best_accuracy Global.save_inference_dir=./inference/det_ct
+```
+
+CT text detection model inference, you can execute the following command:
+
+```shell
+python3 tools/infer/predict_det.py --image_dir="./doc/imgs_en/img623.jpg" --det_model_dir="./inference/det_ct/" --det_algorithm="CT"
+```
+
+The visualized text detection results are saved to the `./inference_results` folder by default, and the name of the result file is prefixed with 'det_res'. Examples of results are as follows:
+
+
+
+
+
+### 4.2 C++ Inference
+
+Not supported
+
+
+### 4.3 Serving
+
+Not supported
+
+
+### 4.4 More
+
+Not supported
+
+
+## 5. FAQ
+
+
+## Citation
+
+```bibtex
+@inproceedings{sheng2021centripetaltext,
+ title={CentripetalText: An Efficient Text Instance Representation for Scene Text Detection},
+ author={Tao Sheng and Jie Chen and Zhouhui Lian},
+ booktitle={Thirty-Fifth Conference on Neural Information Processing Systems},
+ year={2021}
+}
+```
diff --git a/doc/imgs_results/det_res_img623_ct.jpg b/doc/imgs_results/det_res_img623_ct.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..2c5f57d96cca896c70d9e0d33ba80a0177a8aeb9
GIT binary patch
literal 140971
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 |