未验证 提交 64956826 编写于 作者: C cuicheng01 提交者: GitHub

Merge branch 'develop' into develop

......@@ -5,6 +5,7 @@
# pip install ...
# 2 拷贝该模型需要数据、预训练模型
# 3 批量运行(如不方便批量,1,2需放到单个模型中)
log_path=${LOG_PATH_INDEX_DIR:-$(pwd)} # LOG_PATH_INDEX_DIR 后续QA设置参数
model_mode_list=(MobileNetV1 MobileNetV2 MobileNetV3_large_x1_0 ShuffleNetV2_x1_0 HRNet_W48_C SwinTransformer_tiny_patch4_window7_224 alt_gvt_base) # benchmark 监控模型列表
#model_mode_list=(MobileNetV1 MobileNetV2 MobileNetV3_large_x1_0 EfficientNetB0 ShuffleNetV2_x1_0 DenseNet121 HRNet_W48_C SwinTransformer_tiny_patch4_window7_224 alt_gvt_base) # 该脚本支持列表
fp_item_list=(fp32)
......
......@@ -8,7 +8,7 @@ function _set_params(){
fp_item=${3:-"fp32"} # fp32|fp16
epochs=${4:-"2"} # 可选,如果需要修改代码提前中断
model_name=${5:-"model_name"}
run_log_path="${TRAIN_LOG_DIR:-$(pwd)}/benchmark" # TRAIN_LOG_DIR 后续QA设置该参数
run_log_path=${TRAIN_LOG_DIR:-$(pwd)} # TRAIN_LOG_DIR 后续QA设置该参数
index=1
mission_name="图像分类" # 模型所属任务名称,具体可参考scripts/config.ini (必填)
......
......@@ -14,6 +14,11 @@ SET(TENSORRT_DIR "" CACHE PATH "Compile demo with TensorRT")
set(DEMO_NAME "clas_system")
include(external-cmake/yaml-cpp.cmake)
include_directories("${CMAKE_SOURCE_DIR}/")
include_directories("${CMAKE_CURRENT_BINARY_DIR}/ext/yaml-cpp/src/ext-yaml-cpp/include")
link_directories("${CMAKE_CURRENT_BINARY_DIR}/ext/yaml-cpp/lib")
macro(safe_set_static_flag)
foreach(flag_var
CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
......@@ -61,7 +66,7 @@ if (WIN32)
add_definitions(-DSTATIC_LIB)
endif()
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -o3 -std=c++11")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O3 -std=c++11")
set(CMAKE_STATIC_LIBRARY_PREFIX "")
endif()
message("flags" ${CMAKE_CXX_FLAGS})
......@@ -153,7 +158,7 @@ endif(WITH_STATIC_LIB)
if (NOT WIN32)
set(DEPS ${DEPS}
${MATH_LIB} ${MKLDNN_LIB}
glog gflags protobuf z xxhash
glog gflags protobuf z xxhash yaml-cpp
)
if(EXISTS "${PADDLE_LIB}/third_party/install/snappystream/lib")
set(DEPS ${DEPS} snappystream)
......@@ -164,7 +169,7 @@ if (NOT WIN32)
else()
set(DEPS ${DEPS}
${MATH_LIB} ${MKLDNN_LIB}
glog gflags_static libprotobuf xxhash)
glog gflags_static libprotobuf xxhash libyaml-cppmt)
set(DEPS ${DEPS} libcmt shlwapi)
if (EXISTS "${PADDLE_LIB}/third_party/install/snappy/lib")
set(DEPS ${DEPS} snappy)
......@@ -204,6 +209,7 @@ include_directories(${FETCHCONTENT_BASE_DIR}/extern_autolog-src)
AUX_SOURCE_DIRECTORY(./src SRCS)
add_executable(${DEMO_NAME} ${SRCS})
ADD_DEPENDENCIES(${DEMO_NAME} ext-yaml-cpp)
target_link_libraries(${DEMO_NAME} ${DEPS})
......
find_package(Git REQUIRED)
include(ExternalProject)
message("${CMAKE_BUILD_TYPE}")
ExternalProject_Add(
ext-yaml-cpp
URL https://bj.bcebos.com/paddlex/deploy/deps/yaml-cpp.zip
URL_MD5 9542d6de397d1fbd649ed468cb5850e6
CMAKE_ARGS
-DYAML_CPP_BUILD_TESTS=OFF
-DYAML_CPP_BUILD_TOOLS=OFF
-DYAML_CPP_INSTALL=OFF
-DYAML_CPP_BUILD_CONTRIB=OFF
-DMSVC_SHARED_RT=OFF
-DBUILD_SHARED_LIBS=OFF
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}
-DCMAKE_CXX_FLAGS_DEBUG=${CMAKE_CXX_FLAGS_DEBUG}
-DCMAKE_CXX_FLAGS_RELEASE=${CMAKE_CXX_FLAGS_RELEASE}
-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=${CMAKE_BINARY_DIR}/ext/yaml-cpp/lib
-DCMAKE_ARCHIVE_OUTPUT_DIRECTORY=${CMAKE_BINARY_DIR}/ext/yaml-cpp/lib
PREFIX "${CMAKE_BINARY_DIR}/ext/yaml-cpp"
# Disable install step
INSTALL_COMMAND ""
LOG_DOWNLOAD ON
LOG_BUILD 1
)
......@@ -28,64 +28,62 @@
#include <fstream>
#include <numeric>
#include "include/cls_config.h"
#include <include/preprocess_op.h>
using namespace paddle_infer;
namespace PaddleClas {
class Classifier {
public:
explicit Classifier(const std::string &model_path,
const std::string &params_path, const bool &use_gpu,
const int &gpu_id, const int &gpu_mem,
const int &cpu_math_library_num_threads,
const bool &use_mkldnn, const bool &use_tensorrt,
const bool &use_fp16, const int &resize_short_size,
const int &crop_size) {
this->use_gpu_ = use_gpu;
this->gpu_id_ = gpu_id;
this->gpu_mem_ = gpu_mem;
this->cpu_math_library_num_threads_ = cpu_math_library_num_threads;
this->use_mkldnn_ = use_mkldnn;
this->use_tensorrt_ = use_tensorrt;
this->use_fp16_ = use_fp16;
this->resize_short_size_ = resize_short_size;
this->crop_size_ = crop_size;
LoadModel(model_path, params_path);
}
// Load Paddle inference model
void LoadModel(const std::string &model_path, const std::string &params_path);
// Run predictor
double Run(cv::Mat &img, std::vector<double> *times);
private:
std::shared_ptr<Predictor> predictor_;
bool use_gpu_ = false;
int gpu_id_ = 0;
int gpu_mem_ = 4000;
int cpu_math_library_num_threads_ = 4;
bool use_mkldnn_ = false;
bool use_tensorrt_ = false;
bool use_fp16_ = false;
std::vector<float> mean_ = {0.485f, 0.456f, 0.406f};
std::vector<float> scale_ = {1 / 0.229f, 1 / 0.224f, 1 / 0.225f};
bool is_scale_ = true;
int resize_short_size_ = 256;
int crop_size_ = 224;
// pre-process
ResizeImg resize_op_;
Normalize normalize_op_;
Permute permute_op_;
CenterCropImg crop_op_;
};
class Classifier {
public:
explicit Classifier(const ClsConfig &config) {
this->use_gpu_ = config.use_gpu;
this->gpu_id_ = config.gpu_id;
this->gpu_mem_ = config.gpu_mem;
this->cpu_math_library_num_threads_ = config.cpu_threads;
this->use_fp16_ = config.use_fp16;
this->use_mkldnn_ = config.use_mkldnn;
this->use_tensorrt_ = config.use_tensorrt;
this->mean_ = config.mean;
this->std_ = config.std;
this->resize_short_size_ = config.resize_short_size;
this->scale_ = config.scale;
this->crop_size_ = config.crop_size;
this->ir_optim_ = config.ir_optim;
LoadModel(config.cls_model_path, config.cls_params_path);
}
// Load Paddle inference model
void LoadModel(const std::string &model_path, const std::string &params_path);
// Run predictor
double Run(cv::Mat &img, std::vector<double> *times);
private:
std::shared_ptr <Predictor> predictor_;
bool use_gpu_ = false;
int gpu_id_ = 0;
int gpu_mem_ = 4000;
int cpu_math_library_num_threads_ = 4;
bool use_mkldnn_ = false;
bool use_tensorrt_ = false;
bool use_fp16_ = false;
bool ir_optim_ = true;
std::vector<float> mean_ = {0.485f, 0.456f, 0.406f};
std::vector<float> std_ = {0.229f, 0.224f, 0.225f};
float scale_ = 0.00392157;
int resize_short_size_ = 256;
int crop_size_ = 224;
// pre-process
ResizeImg resize_op_;
Normalize normalize_op_;
Permute permute_op_;
CenterCropImg crop_op_;
};
} // namespace PaddleClas
......@@ -14,6 +14,14 @@
#pragma once
#ifdef WIN32
#define OS_PATH_SEP "\\"
#else
#define OS_PATH_SEP "/"
#endif
#include "include/utility.h"
#include "yaml-cpp/yaml.h"
#include <iomanip>
#include <iostream>
#include <map>
......@@ -21,70 +29,85 @@
#include <string>
#include <vector>
#include "include/utility.h"
namespace PaddleClas {
class ClsConfig {
public:
explicit ClsConfig(const std::string &config_file) {
config_map_ = LoadConfig(config_file);
this->use_gpu = bool(stoi(config_map_["use_gpu"]));
this->gpu_id = stoi(config_map_["gpu_id"]);
this->gpu_mem = stoi(config_map_["gpu_mem"]);
this->cpu_threads = stoi(config_map_["cpu_threads"]);
this->use_mkldnn = bool(stoi(config_map_["use_mkldnn"]));
this->use_tensorrt = bool(stoi(config_map_["use_tensorrt"]));
this->use_fp16 = bool(stoi(config_map_["use_fp16"]));
this->cls_model_path.assign(config_map_["cls_model_path"]);
this->cls_params_path.assign(config_map_["cls_params_path"]);
this->resize_short_size = stoi(config_map_["resize_short_size"]);
this->crop_size = stoi(config_map_["crop_size"]);
this->benchmark = bool(stoi(config_map_["benchmark"]));
}
bool use_gpu = false;
int gpu_id = 0;
int gpu_mem = 4000;
int cpu_threads = 1;
bool use_mkldnn = false;
bool use_tensorrt = false;
bool use_fp16 = false;
bool benchmark = false;
std::string cls_model_path;
std::string cls_params_path;
int resize_short_size = 256;
int crop_size = 224;
void PrintConfigInfo();
private:
// Load configuration
std::map<std::string, std::string> LoadConfig(const std::string &config_file);
std::vector<std::string> split(const std::string &str,
const std::string &delim);
std::map<std::string, std::string> config_map_;
};
class ClsConfig {
public:
explicit ClsConfig(const std::string &path) {
ReadYamlConfig(path);
this->infer_imgs =
this->config_file["Global"]["infer_imgs"].as<std::string>();
this->batch_size = this->config_file["Global"]["batch_size"].as<int>();
this->use_gpu = this->config_file["Global"]["use_gpu"].as<bool>();
if (this->config_file["Global"]["gpu_id"].IsDefined())
this->gpu_id = this->config_file["Global"]["gpu_id"].as<int>();
else
this->gpu_id = 0;
this->gpu_mem = this->config_file["Global"]["gpu_mem"].as<int>();
this->cpu_threads =
this->config_file["Global"]["cpu_num_threads"].as<int>();
this->use_mkldnn = this->config_file["Global"]["enable_mkldnn"].as<bool>();
this->use_tensorrt = this->config_file["Global"]["use_tensorrt"].as<bool>();
this->use_fp16 = this->config_file["Global"]["use_fp16"].as<bool>();
this->enable_benchmark =
this->config_file["Global"]["enable_benchmark"].as<bool>();
this->ir_optim = this->config_file["Global"]["ir_optim"].as<bool>();
this->enable_profile =
this->config_file["Global"]["enable_profile"].as<bool>();
this->cls_model_path =
this->config_file["Global"]["inference_model_dir"].as<std::string>() +
OS_PATH_SEP + "inference.pdmodel";
this->cls_params_path =
this->config_file["Global"]["inference_model_dir"].as<std::string>() +
OS_PATH_SEP + "inference.pdiparams";
this->resize_short_size =
this->config_file["PreProcess"]["transform_ops"][0]["ResizeImage"]
["resize_short"]
.as<int>();
this->crop_size =
this->config_file["PreProcess"]["transform_ops"][1]["CropImage"]["size"]
.as<int>();
this->scale = this->config_file["PreProcess"]["transform_ops"][2]
["NormalizeImage"]["scale"]
.as<float>();
this->mean = this->config_file["PreProcess"]["transform_ops"][2]
["NormalizeImage"]["mean"]
.as < std::vector < float >> ();
this->std = this->config_file["PreProcess"]["transform_ops"][2]
["NormalizeImage"]["std"]
.as < std::vector < float >> ();
if (this->config_file["Global"]["benchmark"].IsDefined())
this->benchmark = this->config_file["Global"]["benchmark"].as<bool>();
else
this->benchmark = false;
}
YAML::Node config_file;
bool use_gpu = false;
int gpu_id = 0;
int gpu_mem = 4000;
int cpu_threads = 1;
bool use_mkldnn = false;
bool use_tensorrt = false;
bool use_fp16 = false;
bool benchmark = false;
int batch_size = 1;
bool enable_benchmark = false;
bool ir_optim = true;
bool enable_profile = false;
std::string cls_model_path;
std::string cls_params_path;
std::string infer_imgs;
int resize_short_size = 256;
int crop_size = 224;
float scale = 0.00392157;
std::vector<float> mean = {0.485, 0.456, 0.406};
std::vector<float> std = {0.229, 0.224, 0.225};
void PrintConfigInfo();
void ReadYamlConfig(const std::string &path);
};
} // namespace PaddleClas
// Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
......@@ -31,26 +31,26 @@ using namespace std;
namespace PaddleClas {
class Normalize {
public:
virtual void Run(cv::Mat *im, const std::vector<float> &mean,
const std::vector<float> &scale, const bool is_scale = true);
};
class Normalize {
public:
virtual void Run(cv::Mat *im, const std::vector<float> &mean,
const std::vector<float> &std, float &scale);
};
// RGB -> CHW
class Permute {
public:
virtual void Run(const cv::Mat *im, float *data);
};
class CenterCropImg {
public:
virtual void Run(cv::Mat &im, const int crop_size = 224);
};
class ResizeImg {
public:
virtual void Run(const cv::Mat &img, cv::Mat &resize_img, int max_size_len);
};
} // namespace PaddleClas
\ No newline at end of file
class Permute {
public:
virtual void Run(const cv::Mat *im, float *data);
};
class CenterCropImg {
public:
virtual void Run(cv::Mat &im, const int crop_size = 224);
};
class ResizeImg {
public:
virtual void Run(const cv::Mat &img, cv::Mat &resize_img, int max_size_len);
};
} // namespace PaddleClas
......@@ -32,15 +32,15 @@
namespace PaddleClas {
class Utility {
public:
static std::vector<std::string> ReadDict(const std::string &path);
// template <class ForwardIterator>
// inline static size_t argmax(ForwardIterator first, ForwardIterator last)
// {
// return std::distance(first, std::max_element(first, last));
// }
};
class Utility {
public:
static std::vector <std::string> ReadDict(const std::string &path);
// template <class ForwardIterator>
// inline static size_t argmax(ForwardIterator first, ForwardIterator last)
// {
// return std::distance(first, std::max_element(first, last));
// }
};
} // namespace PaddleClas
\ No newline at end of file
......@@ -143,10 +143,10 @@ tar -xvf paddle_inference.tgz
```
inference/
|--cls_infer.pdmodel
|--cls_infer.pdiparams
|--inference.pdmodel
|--inference.pdiparams
```
**注意**:上述文件中,`cls_infer.pdmodel`文件存储了模型结构信息,`cls_infer.pdiparams`文件存储了模型参数信息。注意两个文件的路径需要与配置文件`tools/config.txt`中的`cls_model_path``cls_params_path`参数对应一致
**注意**:上述文件中,`inference.pdmodel`文件存储了模型结构信息,`inference.pdiparams`文件存储了模型参数信息。模型目录可以随意设置,但是模型名字不能修改
### 2.2 编译PaddleClas C++预测demo
......@@ -183,6 +183,7 @@ cmake .. \
-DCUDA_LIB=${CUDA_LIB_DIR} \
make -j
cd ..
```
上述命令中,
......@@ -200,31 +201,26 @@ make -j
在执行上述命令,编译完成之后,会在当前路径下生成`build`文件夹,其中生成一个名为`clas_system`的可执行文件。
### 运行demo
* 首先修改`tools/config.txt`中对应字段:
* use_gpu:是否使用GPU;
* gpu_id:使用的GPU卡号;
* gpu_mem:显存;
* cpu_math_library_num_threads:底层科学计算库所用线程的数量;
* use_mkldnn:是否使用MKLDNN加速;
* use_tensorrt: 是否使用tensorRT进行加速;
* use_fp16:是否使用半精度浮点数进行计算,该选项仅在use_tensorrt为true时有效;
* cls_model_path:预测模型结构文件路径;
* cls_params_path:预测模型参数文件路径;
* resize_short_size:预处理时图像缩放大小;
* crop_size:预处理时图像裁剪后的大小。
### 2.3 运行demo
#### 2.3.1 设置配置文件
* 然后修改`tools/run.sh`
* `./build/clas_system ./tools/config.txt ./docs/imgs/ILSVRC2012_val_00000666.JPEG`
* 上述命令中分别为:编译得到的可执行文件`clas_system`;运行时的配置文件`config.txt`;待预测的图像。
```shell
cp ../configs/inference_cls.yaml tools/
```
根据[python预测推理](../../docs/zh_CN/inference_deployment/python_deploy.md)`图像分类推理`部分修改好`tools`目录下`inference_cls.yaml`文件。`yaml`文件的参数说明详见[python预测推理](../../docs/zh_CN/inference_deployment/python_deploy.md)
请根据实际存放文件,修改好`Global.infer_imgs``Global.inference_model_dir`等参数。
* 最后执行以下命令,完成对一幅图像的分类。
#### 2.3.2 执行
```shell
sh tools/run.sh
./build/clas_system -c inference_cls.yaml
# or
./build/clas_system -config inference_cls.yaml
```
* 最终屏幕上会输出结果,如下图所示。
最终屏幕上会输出结果,如下图所示。
<div align="center">
<img src="./docs/imgs/cpp_infer_result.png" width="600">
......
......@@ -16,98 +16,97 @@
namespace PaddleClas {
void Classifier::LoadModel(const std::string &model_path,
const std::string &params_path) {
paddle_infer::Config config;
config.SetModel(model_path, params_path);
if (this->use_gpu_) {
config.EnableUseGpu(this->gpu_mem_, this->gpu_id_);
if (this->use_tensorrt_) {
config.EnableTensorRtEngine(
1 << 20, 1, 3,
this->use_fp16_ ? paddle_infer::Config::Precision::kHalf
: paddle_infer::Config::Precision::kFloat32,
false, false);
void Classifier::LoadModel(const std::string &model_path,
const std::string &params_path) {
paddle_infer::Config config;
config.SetModel(model_path, params_path);
if (this->use_gpu_) {
config.EnableUseGpu(this->gpu_mem_, this->gpu_id_);
if (this->use_tensorrt_) {
config.EnableTensorRtEngine(
1 << 20, 1, 3,
this->use_fp16_ ? paddle_infer::Config::Precision::kHalf
: paddle_infer::Config::Precision::kFloat32,
false, false);
}
} else {
config.DisableGpu();
if (this->use_mkldnn_) {
config.EnableMKLDNN();
// cache 10 different shapes for mkldnn to avoid memory leak
config.SetMkldnnCacheCapacity(10);
}
config.SetCpuMathLibraryNumThreads(this->cpu_math_library_num_threads_);
}
config.SwitchUseFeedFetchOps(false);
// true for multiple input
config.SwitchSpecifyInputNames(true);
config.SwitchIrOptim(this->ir_optim_);
config.EnableMemoryOptim();
config.DisableGlogInfo();
this->predictor_ = CreatePredictor(config);
}
} else {
config.DisableGpu();
if (this->use_mkldnn_) {
config.EnableMKLDNN();
// cache 10 different shapes for mkldnn to avoid memory leak
config.SetMkldnnCacheCapacity(10);
double Classifier::Run(cv::Mat &img, std::vector<double> *times) {
cv::Mat srcimg;
cv::Mat resize_img;
img.copyTo(srcimg);
auto preprocess_start = std::chrono::system_clock::now();
this->resize_op_.Run(img, resize_img, this->resize_short_size_);
this->crop_op_.Run(resize_img, this->crop_size_);
this->normalize_op_.Run(&resize_img, this->mean_, this->std_, this->scale_);
std::vector<float> input(1 * 3 * resize_img.rows * resize_img.cols, 0.0f);
this->permute_op_.Run(&resize_img, input.data());
auto input_names = this->predictor_->GetInputNames();
auto input_t = this->predictor_->GetInputHandle(input_names[0]);
input_t->Reshape({1, 3, resize_img.rows, resize_img.cols});
auto preprocess_end = std::chrono::system_clock::now();
auto infer_start = std::chrono::system_clock::now();
input_t->CopyFromCpu(input.data());
this->predictor_->Run();
std::vector<float> out_data;
auto output_names = this->predictor_->GetOutputNames();
auto output_t = this->predictor_->GetOutputHandle(output_names[0]);
std::vector<int> output_shape = output_t->shape();
int out_num = std::accumulate(output_shape.begin(), output_shape.end(), 1,
std::multiplies<int>());
out_data.resize(out_num);
output_t->CopyToCpu(out_data.data());
auto infer_end = std::chrono::system_clock::now();
auto postprocess_start = std::chrono::system_clock::now();
int maxPosition =
max_element(out_data.begin(), out_data.end()) - out_data.begin();
auto postprocess_end = std::chrono::system_clock::now();
std::chrono::duration<float> preprocess_diff =
preprocess_end - preprocess_start;
times->push_back(double(preprocess_diff.count() * 1000));
std::chrono::duration<float> inference_diff = infer_end - infer_start;
double inference_cost_time = double(inference_diff.count() * 1000);
times->push_back(inference_cost_time);
std::chrono::duration<float> postprocess_diff =
postprocess_end - postprocess_start;
times->push_back(double(postprocess_diff.count() * 1000));
std::cout << "result: " << std::endl;
std::cout << "\tclass id: " << maxPosition << std::endl;
std::cout << std::fixed << std::setprecision(10)
<< "\tscore: " << double(out_data[maxPosition]) << std::endl;
return inference_cost_time;
}
config.SetCpuMathLibraryNumThreads(this->cpu_math_library_num_threads_);
}
config.SwitchUseFeedFetchOps(false);
// true for multiple input
config.SwitchSpecifyInputNames(true);
config.SwitchIrOptim(true);
config.EnableMemoryOptim();
config.DisableGlogInfo();
this->predictor_ = CreatePredictor(config);
}
double Classifier::Run(cv::Mat &img, std::vector<double> *times) {
cv::Mat srcimg;
cv::Mat resize_img;
img.copyTo(srcimg);
auto preprocess_start = std::chrono::system_clock::now();
this->resize_op_.Run(img, resize_img, this->resize_short_size_);
this->crop_op_.Run(resize_img, this->crop_size_);
this->normalize_op_.Run(&resize_img, this->mean_, this->scale_,
this->is_scale_);
std::vector<float> input(1 * 3 * resize_img.rows * resize_img.cols, 0.0f);
this->permute_op_.Run(&resize_img, input.data());
auto input_names = this->predictor_->GetInputNames();
auto input_t = this->predictor_->GetInputHandle(input_names[0]);
input_t->Reshape({1, 3, resize_img.rows, resize_img.cols});
auto preprocess_end = std::chrono::system_clock::now();
auto infer_start = std::chrono::system_clock::now();
input_t->CopyFromCpu(input.data());
this->predictor_->Run();
std::vector<float> out_data;
auto output_names = this->predictor_->GetOutputNames();
auto output_t = this->predictor_->GetOutputHandle(output_names[0]);
std::vector<int> output_shape = output_t->shape();
int out_num = std::accumulate(output_shape.begin(), output_shape.end(), 1,
std::multiplies<int>());
out_data.resize(out_num);
output_t->CopyToCpu(out_data.data());
auto infer_end = std::chrono::system_clock::now();
auto postprocess_start = std::chrono::system_clock::now();
int maxPosition =
max_element(out_data.begin(), out_data.end()) - out_data.begin();
auto postprocess_end = std::chrono::system_clock::now();
std::chrono::duration<float> preprocess_diff =
preprocess_end - preprocess_start;
times->push_back(double(preprocess_diff.count() * 1000));
std::chrono::duration<float> inference_diff = infer_end - infer_start;
double inference_cost_time = double(inference_diff.count() * 1000);
times->push_back(inference_cost_time);
std::chrono::duration<float> postprocess_diff =
postprocess_end - postprocess_start;
times->push_back(double(postprocess_diff.count() * 1000));
std::cout << "result: " << std::endl;
std::cout << "\tclass id: " << maxPosition << std::endl;
std::cout << std::fixed << std::setprecision(10)
<< "\tscore: " << double(out_data[maxPosition]) << std::endl;
return inference_cost_time;
}
} // namespace PaddleClas
......@@ -16,49 +16,20 @@
namespace PaddleClas {
std::vector<std::string> ClsConfig::split(const std::string &str,
const std::string &delim) {
std::vector<std::string> res;
if ("" == str)
return res;
char *strs = new char[str.length() + 1];
std::strcpy(strs, str.c_str());
char *d = new char[delim.length() + 1];
std::strcpy(d, delim.c_str());
char *p = std::strtok(strs, d);
while (p) {
std::string s = p;
res.push_back(s);
p = std::strtok(NULL, d);
}
return res;
}
std::map<std::string, std::string>
ClsConfig::LoadConfig(const std::string &config_path) {
auto config = Utility::ReadDict(config_path);
std::map<std::string, std::string> dict;
for (int i = 0; i < config.size(); i++) {
// pass for empty line or comment
if (config[i].size() <= 1 || config[i][0] == '#') {
continue;
void ClsConfig::PrintConfigInfo() {
std::cout << "=======Paddle Class inference config======" << std::endl;
std::cout << this->config_file << std::endl;
std::cout << "=======End of Paddle Class inference config======" << std::endl;
}
std::vector<std::string> res = split(config[i], " ");
dict[res[0]] = res[1];
}
return dict;
}
void ClsConfig::PrintConfigInfo() {
std::cout << "=======Paddle Class inference config======" << std::endl;
for (auto iter = config_map_.begin(); iter != config_map_.end(); iter++) {
std::cout << iter->first << " : " << iter->second << std::endl;
}
std::cout << "=======End of Paddle Class inference config======" << std::endl;
}
void ClsConfig::ReadYamlConfig(const std::string &path) {
} // namespace PaddleClas
\ No newline at end of file
try {
this->config_file = YAML::LoadFile(path);
} catch (YAML::BadFile &e) {
std::cout << "Something wrong in yaml file, please check yaml file"
<< std::endl;
exit(1);
}
}
}; // namespace PaddleClas
......@@ -27,6 +27,7 @@
#include <numeric>
#include <auto_log/autolog.h>
#include <gflags/gflags.h>
#include <include/cls.h>
#include <include/cls_config.h>
......@@ -34,74 +35,81 @@ using namespace std;
using namespace cv;
using namespace PaddleClas;
int main(int argc, char **argv) {
if (argc < 3) {
std::cerr << "[ERROR] usage: " << argv[0]
<< " configure_filepath image_path\n";
exit(1);
}
ClsConfig config(argv[1]);
config.PrintConfigInfo();
std::string path(argv[2]);
DEFINE_string(config,
"", "Path of yaml file");
DEFINE_string(c,
"", "Path of yaml file");
std::vector<std::string> img_files_list;
if (cv::utils::fs::isDirectory(path)) {
std::vector<cv::String> filenames;
cv::glob(path, filenames);
for (auto f : filenames) {
img_files_list.push_back(f);
int main(int argc, char **argv) {
google::ParseCommandLineFlags(&argc, &argv, true);
std::string yaml_path = "";
if (FLAGS_config == "" && FLAGS_c == "") {
std::cerr << "[ERROR] usage: " << std::endl
<< argv[0] << " -c $yaml_path" << std::endl
<< "or:" << std::endl
<< argv[0] << " -config $yaml_path" << std::endl;
exit(1);
} else if (FLAGS_config != "") {
yaml_path = FLAGS_config;
} else {
yaml_path = FLAGS_c;
}
} else {
img_files_list.push_back(path);
}
std::cout << "img_file_list length: " << img_files_list.size() << std::endl;
Classifier classifier(config.cls_model_path, config.cls_params_path,
config.use_gpu, config.gpu_id, config.gpu_mem,
config.cpu_threads, config.use_mkldnn,
config.use_tensorrt, config.use_fp16,
config.resize_short_size, config.crop_size);
double elapsed_time = 0.0;
std::vector<double> cls_times;
int warmup_iter = img_files_list.size() > 5 ? 5 : 0;
for (int idx = 0; idx < img_files_list.size(); ++idx) {
std::string img_path = img_files_list[idx];
cv::Mat srcimg = cv::imread(img_path, cv::IMREAD_COLOR);
if (!srcimg.data) {
std::cerr << "[ERROR] image read failed! image path: " << img_path
<< "\n";
exit(-1);
ClsConfig config(yaml_path);
config.PrintConfigInfo();
std::string path(config.infer_imgs);
std::vector <std::string> img_files_list;
if (cv::utils::fs::isDirectory(path)) {
std::vector <cv::String> filenames;
cv::glob(path, filenames);
for (auto f : filenames) {
img_files_list.push_back(f);
}
} else {
img_files_list.push_back(path);
}
cv::cvtColor(srcimg, srcimg, cv::COLOR_BGR2RGB);
double run_time = classifier.Run(srcimg, &cls_times);
if (idx >= warmup_iter) {
elapsed_time += run_time;
std::cout << "Current image path: " << img_path << std::endl;
std::cout << "Current time cost: " << run_time << " s, "
<< "average time cost in all: "
<< elapsed_time / (idx + 1 - warmup_iter) << " s." << std::endl;
} else {
std::cout << "Current time cost: " << run_time << " s." << std::endl;
std::cout << "img_file_list length: " << img_files_list.size() << std::endl;
Classifier classifier(config);
double elapsed_time = 0.0;
std::vector<double> cls_times;
int warmup_iter = img_files_list.size() > 5 ? 5 : 0;
for (int idx = 0; idx < img_files_list.size(); ++idx) {
std::string img_path = img_files_list[idx];
cv::Mat srcimg = cv::imread(img_path, cv::IMREAD_COLOR);
if (!srcimg.data) {
std::cerr << "[ERROR] image read failed! image path: " << img_path
<< "\n";
exit(-1);
}
cv::cvtColor(srcimg, srcimg, cv::COLOR_BGR2RGB);
double run_time = classifier.Run(srcimg, &cls_times);
if (idx >= warmup_iter) {
elapsed_time += run_time;
std::cout << "Current image path: " << img_path << std::endl;
std::cout << "Current time cost: " << run_time << " s, "
<< "average time cost in all: "
<< elapsed_time / (idx + 1 - warmup_iter) << " s." << std::endl;
} else {
std::cout << "Current time cost: " << run_time << " s." << std::endl;
}
}
}
std::string presion = "fp32";
std::string presion = "fp32";
if (config.use_fp16)
presion = "fp16";
if (config.benchmark) {
AutoLogger autolog("Classification", config.use_gpu, config.use_tensorrt,
config.use_mkldnn, config.cpu_threads, 1,
"1, 3, 224, 224", presion, cls_times,
img_files_list.size());
autolog.report();
}
return 0;
if (config.use_fp16)
presion = "fp16";
if (config.benchmark) {
AutoLogger autolog("Classification", config.use_gpu, config.use_tensorrt,
config.use_mkldnn, config.cpu_threads, 1,
"1, 3, 224, 224", presion, cls_times,
img_files_list.size());
autolog.report();
}
return 0;
}
......@@ -32,59 +32,57 @@
namespace PaddleClas {
void Permute::Run(const cv::Mat *im, float *data) {
int rh = im->rows;
int rw = im->cols;
int rc = im->channels();
for (int i = 0; i < rc; ++i) {
cv::extractChannel(*im, cv::Mat(rh, rw, CV_32FC1, data + i * rh * rw), i);
}
}
void Permute::Run(const cv::Mat *im, float *data) {
int rh = im->rows;
int rw = im->cols;
int rc = im->channels();
for (int i = 0; i < rc; ++i) {
cv::extractChannel(*im, cv::Mat(rh, rw, CV_32FC1, data + i * rh * rw), i);
}
}
void Normalize::Run(cv::Mat *im, const std::vector<float> &mean,
const std::vector<float> &scale, const bool is_scale) {
double e = 1.0;
if (is_scale) {
e /= 255.0;
}
(*im).convertTo(*im, CV_32FC3, e);
for (int h = 0; h < im->rows; h++) {
for (int w = 0; w < im->cols; w++) {
im->at<cv::Vec3f>(h, w)[0] =
(im->at<cv::Vec3f>(h, w)[0] - mean[0]) * scale[0];
im->at<cv::Vec3f>(h, w)[1] =
(im->at<cv::Vec3f>(h, w)[1] - mean[1]) * scale[1];
im->at<cv::Vec3f>(h, w)[2] =
(im->at<cv::Vec3f>(h, w)[2] - mean[2]) * scale[2];
void Normalize::Run(cv::Mat *im, const std::vector<float> &mean,
const std::vector<float> &std, float &scale) {
if (scale) {
(*im).convertTo(*im, CV_32FC3, scale);
}
for (int h = 0; h < im->rows; h++) {
for (int w = 0; w < im->cols; w++) {
im->at<cv::Vec3f>(h, w)[0] =
(im->at<cv::Vec3f>(h, w)[0] - mean[0]) / std[0];
im->at<cv::Vec3f>(h, w)[1] =
(im->at<cv::Vec3f>(h, w)[1] - mean[1]) / std[1];
im->at<cv::Vec3f>(h, w)[2] =
(im->at<cv::Vec3f>(h, w)[2] - mean[2]) / std[2];
}
}
}
}
}
void CenterCropImg::Run(cv::Mat &img, const int crop_size) {
int resize_w = img.cols;
int resize_h = img.rows;
int w_start = int((resize_w - crop_size) / 2);
int h_start = int((resize_h - crop_size) / 2);
cv::Rect rect(w_start, h_start, crop_size, crop_size);
img = img(rect);
}
void CenterCropImg::Run(cv::Mat &img, const int crop_size) {
int resize_w = img.cols;
int resize_h = img.rows;
int w_start = int((resize_w - crop_size) / 2);
int h_start = int((resize_h - crop_size) / 2);
cv::Rect rect(w_start, h_start, crop_size, crop_size);
img = img(rect);
}
void ResizeImg::Run(const cv::Mat &img, cv::Mat &resize_img,
int resize_short_size) {
int w = img.cols;
int h = img.rows;
void ResizeImg::Run(const cv::Mat &img, cv::Mat &resize_img,
int resize_short_size) {
int w = img.cols;
int h = img.rows;
float ratio = 1.f;
if (h < w) {
ratio = float(resize_short_size) / float(h);
} else {
ratio = float(resize_short_size) / float(w);
}
float ratio = 1.f;
if (h < w) {
ratio = float(resize_short_size) / float(h);
} else {
ratio = float(resize_short_size) / float(w);
}
int resize_h = round(float(h) * ratio);
int resize_w = round(float(w) * ratio);
int resize_h = round(float(h) * ratio);
int resize_w = round(float(w) * ratio);
cv::resize(img, resize_img, cv::Size(resize_w, resize_h));
}
cv::resize(img, resize_img, cv::Size(resize_w, resize_h));
}
} // namespace PaddleClas
\ No newline at end of file
} // namespace PaddleClas
......@@ -18,3 +18,4 @@ cmake .. \
-DCUDA_LIB=${CUDA_LIB_DIR} \
make -j
cd ..
# model load config
use_gpu 0
gpu_id 0
gpu_mem 4000
cpu_threads 10
use_mkldnn 1
use_tensorrt 0
use_fp16 0
# cls config
cls_model_path /PaddleClas/inference/cls_infer.pdmodel
cls_params_path /PaddleClas/inference/cls_infer.pdiparams
resize_short_size 256
crop_size 224
# for log env info
benchmark 0
./build/clas_system ./tools/config.txt ./docs/imgs/ILSVRC2012_val_00000666.JPEG
......@@ -239,7 +239,7 @@ make -j
* `CUDNN_LIB_DIR`为cudnn库文件地址,在docker中为`/usr/lib/x86_64-linux-gnu/`
* `TENSORRT_DIR`是tensorrt库文件地址,在dokcer中为`/usr/local/TensorRT6-cuda10.0-cudnn7/`,TensorRT需要结合GPU使用。
* `FAISS_DIR`是faiss的安装地址
* `FAISS_WITH_MKL`是指在编译faiss的过程中,是否使用了mkldnn,本文档中编译faiss,没有使用,而使用了openblas,故设置为`OFF`,若使用了mkldnn,则为`ON`.
* `FAISS_WITH_MKL`是指在编译faiss的过程中,是否使用了mkldnn,本文档中编译faiss,没有使用,而使用了openblas,故设置为`OFF`,若使用了mkldnn,则为`ON`.
在执行上述命令,编译完成之后,会在当前路径下生成`build`文件夹,其中生成一个名为`pp_shitu`的可执行文件。
......@@ -264,7 +264,7 @@ make -j
cd ..
```
- 将相应的yaml文件拷到`test`文件夹下
- 将相应的yaml文件拷到当前文件夹下
```shell
cp ../configs/inference_drink.yaml .
......
../../docs/zh_CN/inference_deployment/paddle_serving_deploy.md
\ No newline at end of file
# PaddleClas Pipeline WebService
(English|[简体中文](./README_CN.md))
PaddleClas provides two service deployment methods:
- Based on **PaddleHub Serving**: Code path is "`./deploy/hubserving`". Please refer to the [tutorial](../../deploy/hubserving/readme_en.md)
- Based on **PaddleServing**: Code path is "`./deploy/paddleserving`". if you prefer retrieval_based image reocognition service, please refer to [tutorial](./recognition/README.md),if you'd like image classification service, Please follow this tutorial.
# Image Classification Service deployment based on PaddleServing
This document will introduce how to use the [PaddleServing](https://github.com/PaddlePaddle/Serving/blob/develop/README.md) to deploy the ResNet50_vd model as a pipeline online service.
Some Key Features of Paddle Serving:
- Integrate with Paddle training pipeline seamlessly, most paddle models can be deployed with one line command.
- Industrial serving features supported, such as models management, online loading, online A/B testing etc.
- Highly concurrent and efficient communication between clients and servers supported.
The introduction and tutorial of Paddle Serving service deployment framework reference [document](https://github.com/PaddlePaddle/Serving/blob/develop/README.md).
## Contents
- [Environmental preparation](#environmental-preparation)
- [Model conversion](#model-conversion)
- [Paddle Serving pipeline deployment](#paddle-serving-pipeline-deployment)
- [FAQ](#faq)
<a name="environmental-preparation"></a>
## Environmental preparation
PaddleClas operating environment and PaddleServing operating environment are needed.
1. Please prepare PaddleClas operating environment reference [link](../../docs/zh_CN/tutorials/install.md).
Download the corresponding paddle whl package according to the environment, it is recommended to install version 2.1.0.
2. The steps of PaddleServing operating environment prepare are as follows:
Install serving which used to start the service
```
pip3 install paddle-serving-server==0.6.1 # for CPU
pip3 install paddle-serving-server-gpu==0.6.1 # for GPU
# Other GPU environments need to confirm the environment and then choose to execute the following commands
pip3 install paddle-serving-server-gpu==0.6.1.post101 # GPU with CUDA10.1 + TensorRT6
pip3 install paddle-serving-server-gpu==0.6.1.post11 # GPU with CUDA11 + TensorRT7
```
3. Install the client to send requests to the service
In [download link](https://github.com/PaddlePaddle/Serving/blob/develop/doc/LATEST_PACKAGES.md) find the client installation package corresponding to the python version.
The python3.7 version is recommended here:
```
wget https://paddle-serving.bj.bcebos.com/test-dev/whl/paddle_serving_client-0.0.0-cp37-none-any.whl
pip3 install paddle_serving_client-0.0.0-cp37-none-any.whl
```
4. Install serving-app
```
pip3 install paddle-serving-app==0.6.1
```
**note:** If you want to install the latest version of PaddleServing, refer to [link](https://github.com/PaddlePaddle/Serving/blob/develop/doc/LATEST_PACKAGES.md).
<a name="model-conversion"></a>
## Model conversion
When using PaddleServing for service deployment, you need to convert the saved inference model into a serving model that is easy to deploy.
Firstly, download the inference model of ResNet50_vd
```
# Download and unzip the ResNet50_vd model
wget https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/inference/ResNet50_vd_infer.tar && tar xf ResNet50_vd_infer.tar
```
Then, you can use installed paddle_serving_client tool to convert inference model to mobile model.
```
# ResNet50_vd model conversion
python3 -m paddle_serving_client.convert --dirname ./ResNet50_vd_infer/ \
--model_filename inference.pdmodel \
--params_filename inference.pdiparams \
--serving_server ./ResNet50_vd_serving/ \
--serving_client ./ResNet50_vd_client/
```
After the ResNet50_vd inference model is converted, there will be additional folders of `ResNet50_vd_serving` and `ResNet50_vd_client` in the current folder, with the following format:
```
|- ResNet50_vd_client/
|- __model__
|- __params__
|- serving_server_conf.prototxt
|- serving_server_conf.stream.prototxt
|- ResNet50_vd_client
|- serving_client_conf.prototxt
|- serving_client_conf.stream.prototxt
```
Once you have the model file for deployment, you need to change the alias name in `serving_server_conf.prototxt`: Change `alias_name` in `feed_var` to `image`, change `alias_name` in `fetch_var` to `prediction`,
The modified serving_server_conf.prototxt file is as follows:
```
feed_var {
name: "inputs"
alias_name: "image"
is_lod_tensor: false
feed_type: 1
shape: 3
shape: 224
shape: 224
}
fetch_var {
name: "save_infer_model/scale_0.tmp_1"
alias_name: "prediction"
is_lod_tensor: true
fetch_type: 1
shape: -1
}
```
<a name="paddle-serving-pipeline-deployment"></a>
## Paddle Serving pipeline deployment
1. Download the PaddleClas code, if you have already downloaded it, you can skip this step.
```
git clone https://github.com/PaddlePaddle/PaddleClas
# Enter the working directory
cd PaddleClas/deploy/paddleserving/
```
The paddleserving directory contains the code to start the pipeline service and send prediction requests, including:
```
__init__.py
config.yml # configuration file of starting the service
pipeline_http_client.py # script to send pipeline prediction request by http
pipeline_rpc_client.py # script to send pipeline prediction request by rpc
classification_web_service.py # start the script of the pipeline server
```
2. Run the following command to start the service.
```
# Start the service and save the running log in log.txt
python3 classification_web_service.py &>log.txt &
```
After the service is successfully started, a log similar to the following will be printed in log.txt
![](./imgs/start_server.png)
3. Send service request
```
python3 pipeline_http_client.py
```
After successfully running, the predicted result of the model will be printed in the cmd window. An example of the result is:
![](./imgs/results.png)
Adjust the number of concurrency in config.yml to get the largest QPS.
```
op:
concurrency: 8
...
```
Multiple service requests can be sent at the same time if necessary.
The predicted performance data will be automatically written into the `PipelineServingLogs/pipeline.tracer` file.
<a name="faq"></a>
## FAQ
**Q1**: No result return after sending the request.
**A1**: Do not set the proxy when starting the service and sending the request. You can close the proxy before starting the service and before sending the request. The command to close the proxy is:
```
unset https_proxy
unset http_proxy
```
# PaddleClas 服务化部署
([English](./README.md)|简体中文)
PaddleClas提供2种服务部署方式:
- 基于PaddleHub Serving的部署:代码路径为"`./deploy/hubserving`",使用方法参考[文档](../../deploy/hubserving/readme.md)
- 基于PaddleServing的部署:代码路径为"`./deploy/paddleserving`", 基于检索方式的图像识别服务参考[文档](./recognition/README_CN.md), 图像分类服务按照本教程使用。
# 基于PaddleServing的图像分类服务部署
本文档以经典的ResNet50_vd模型为例,介绍如何使用[PaddleServing](https://github.com/PaddlePaddle/Serving/blob/develop/README_CN.md)工具部署PaddleClas
动态图模型的pipeline在线服务。
相比较于hubserving部署,PaddleServing具备以下优点:
- 支持客户端和服务端之间高并发和高效通信
- 支持 工业级的服务能力 例如模型管理,在线加载,在线A/B测试等
- 支持 多种编程语言 开发客户端,例如C++, Python和Java
更多有关PaddleServing服务化部署框架介绍和使用教程参考[文档](https://github.com/PaddlePaddle/Serving/blob/develop/README_CN.md)
## 目录
- [环境准备](#环境准备)
- [模型转换](#模型转换)
- [Paddle Serving pipeline部署](#部署)
- [FAQ](#FAQ)
<a name="环境准备"></a>
## 环境准备
需要准备PaddleClas的运行环境和PaddleServing的运行环境。
- 准备PaddleClas的[运行环境](../../docs/zh_CN/tutorials/install.md), 根据环境下载对应的paddle whl包,推荐安装2.1.0版本
- 准备PaddleServing的运行环境,步骤如下
1. 安装serving,用于启动服务
```
pip3 install paddle-serving-server==0.6.1 # for CPU
pip3 install paddle-serving-server-gpu==0.6.1 # for GPU
# 其他GPU环境需要确认环境再选择执行如下命令
pip3 install paddle-serving-server-gpu==0.6.1.post101 # GPU with CUDA10.1 + TensorRT6
pip3 install paddle-serving-server-gpu==0.6.1.post11 # GPU with CUDA11 + TensorRT7
```
2. 安装client,用于向服务发送请求
[下载链接](https://github.com/PaddlePaddle/Serving/blob/develop/doc/LATEST_PACKAGES.md)中找到对应python版本的client安装包,这里推荐python3.7版本:
```
wget https://paddle-serving.bj.bcebos.com/test-dev/whl/paddle_serving_client-0.0.0-cp37-none-any.whl
pip3 install paddle_serving_client-0.0.0-cp37-none-any.whl
```
3. 安装serving-app
```
pip3 install paddle-serving-app==0.6.1
```
**Note:** 如果要安装最新版本的PaddleServing参考[链接](https://github.com/PaddlePaddle/Serving/blob/develop/doc/LATEST_PACKAGES.md)
<a name="模型转换"></a>
## 模型转换
使用PaddleServing做服务化部署时,需要将保存的inference模型转换为serving易于部署的模型。
首先,下载ResNet50_vd的inference模型
```
# 下载并解压ResNet50_vd模型
wget https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/inference/ResNet50_vd_infer.tar && tar xf ResNet50_vd_infer.tar
```
接下来,用安装的paddle_serving_client把下载的inference模型转换成易于server部署的模型格式。
```
# 转换ResNet50_vd模型
python3 -m paddle_serving_client.convert --dirname ./ResNet50_vd_infer/ \
--model_filename inference.pdmodel \
--params_filename inference.pdiparams \
--serving_server ./ResNet50_vd_serving/ \
--serving_client ./ResNet50_vd_client/
```
ResNet50_vd推理模型转换完成后,会在当前文件夹多出`ResNet50_vd_serving``ResNet50_vd_client`的文件夹,具备如下格式:
```
|- ResNet50_vd_client/
|- __model__
|- __params__
|- serving_server_conf.prototxt
|- serving_server_conf.stream.prototxt
|- ResNet50_vd_client
|- serving_client_conf.prototxt
|- serving_client_conf.stream.prototxt
```
得到模型文件之后,需要修改serving_server_conf.prototxt中的alias名字: 将`feed_var`中的`alias_name`改为`image`, 将`fetch_var`中的`alias_name`改为`prediction`,
修改后的serving_server_conf.prototxt内容如下:
```
feed_var {
name: "inputs"
alias_name: "image"
is_lod_tensor: false
feed_type: 1
shape: 3
shape: 224
shape: 224
}
fetch_var {
name: "save_infer_model/scale_0.tmp_1"
alias_name: "prediction"
is_lod_tensor: true
fetch_type: 1
shape: -1
}
```
<a name="部署"></a>
## Paddle Serving pipeline部署
1. 下载PaddleClas代码,若已下载可跳过此步骤
```
git clone https://github.com/PaddlePaddle/PaddleClas
# 进入到工作目录
cd PaddleClas/deploy/paddleserving/
```
paddleserving目录包含启动pipeline服务和发送预测请求的代码,包括:
```
__init__.py
config.yml # 启动服务的配置文件
pipeline_http_client.py # http方式发送pipeline预测请求的脚本
pipeline_rpc_client.py # rpc方式发送pipeline预测请求的脚本
classification_web_service.py # 启动pipeline服务端的脚本
```
2. 启动服务可运行如下命令:
```
# 启动服务,运行日志保存在log.txt
python3 classification_web_service.py &>log.txt &
```
成功启动服务后,log.txt中会打印类似如下日志
![](./imgs/start_server.png)
3. 发送服务请求:
```
python3 pipeline_http_client.py
```
成功运行后,模型预测的结果会打印在cmd窗口中,结果示例为:
![](./imgs/results.png)
调整 config.yml 中的并发个数可以获得最大的QPS
```
op:
#并发数,is_thread_op=True时,为线程并发;否则为进程并发
concurrency: 8
...
```
有需要的话可以同时发送多个服务请求
预测性能数据会被自动写入 `PipelineServingLogs/pipeline.tracer` 文件中。
<a name="FAQ"></a>
## FAQ
**Q1**: 发送请求后没有结果返回或者提示输出解码报错
**A1**: 启动服务和发送请求时不要设置代理,可以在启动服务前和发送请求前关闭代理,关闭代理的命令是:
```
unset https_proxy
unset http_proxy
```
......@@ -21,6 +21,7 @@ import logging
import numpy as np
import base64, cv2
class ImagenetOp(Op):
def init_op(self):
self.seq = Sequential([
......@@ -46,9 +47,9 @@ class ImagenetOp(Op):
img = self.seq(im)
imgs.append(img[np.newaxis, :].copy())
input_imgs = np.concatenate(imgs, axis=0)
return {"image": input_imgs}, False, None, ""
return {"inputs": input_imgs}, False, None, ""
def postprocess(self, input_dicts, fetch_dict, log_id):
def postprocess(self, input_dicts, fetch_dict, data_id, log_id):
score_list = fetch_dict["prediction"]
result = {"label": [], "prob": []}
for score in score_list:
......
......@@ -3,15 +3,18 @@ import json
import base64
import os
def cv2_to_base64(image):
return base64.b64encode(image).decode('utf8')
if __name__ == "__main__":
url = "http://127.0.0.1:18080/imagenet/prediction"
with open(os.path.join(".", "daisy.jpg"), 'rb') as file:
image_data1 = file.read()
image = cv2_to_base64(image_data1)
data = {"key": ["image"], "value": [image]}
for i in range(100):
for i in range(1):
r = requests.post(url=url, data=json.dumps(data))
print(r.json())
# Product Recognition Service deployment based on PaddleServing
(English|[简体中文](./README_CN.md))
This document will introduce how to use the [PaddleServing](https://github.com/PaddlePaddle/Serving/blob/develop/README.md) to deploy the product recognition model based on retrieval method as a pipeline online service.
Some Key Features of Paddle Serving:
- Integrate with Paddle training pipeline seamlessly, most paddle models can be deployed with one line command.
- Industrial serving features supported, such as models management, online loading, online A/B testing etc.
- Highly concurrent and efficient communication between clients and servers supported.
The introduction and tutorial of Paddle Serving service deployment framework reference [document](https://github.com/PaddlePaddle/Serving/blob/develop/README.md).
## Contents
- [Environmental preparation](#environmental-preparation)
- [Model conversion](#model-conversion)
- [Paddle Serving pipeline deployment](#paddle-serving-pipeline-deployment)
- [FAQ](#faq)
<a name="environmental-preparation"></a>
## Environmental preparation
PaddleClas operating environment and PaddleServing operating environment are needed.
1. Please prepare PaddleClas operating environment reference [link](../../docs/zh_CN/tutorials/install.md).
Download the corresponding paddle whl package according to the environment, it is recommended to install version 2.1.0.
2. The steps of PaddleServing operating environment prepare are as follows:
Install serving which used to start the service
```
pip3 install paddle-serving-server==0.6.1 # for CPU
pip3 install paddle-serving-server-gpu==0.6.1 # for GPU
# Other GPU environments need to confirm the environment and then choose to execute the following commands
pip3 install paddle-serving-server-gpu==0.6.1.post101 # GPU with CUDA10.1 + TensorRT6
pip3 install paddle-serving-server-gpu==0.6.1.post11 # GPU with CUDA11 + TensorRT7
```
3. Install the client to send requests to the service
In [download link](https://github.com/PaddlePaddle/Serving/blob/develop/doc/LATEST_PACKAGES.md) find the client installation package corresponding to the python version.
The python3.7 version is recommended here:
```
wget https://paddle-serving.bj.bcebos.com/test-dev/whl/paddle_serving_client-0.0.0-cp37-none-any.whl
pip3 install paddle_serving_client-0.0.0-cp37-none-any.whl
```
4. Install serving-app
```
pip3 install paddle-serving-app==0.6.1
```
**note:** If you want to install the latest version of PaddleServing, refer to [link](https://github.com/PaddlePaddle/Serving/blob/develop/doc/LATEST_PACKAGES.md).
<a name="model-conversion"></a>
## Model conversion
When using PaddleServing for service deployment, you need to convert the saved inference model into a serving model that is easy to deploy.
The following assumes that the current working directory is the PaddleClas root directory
Firstly, download the inference model of ResNet50_vd
```
cd deploy
# Download and unzip the ResNet50_vd model
wget -P models/ https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/rec/models/inference/product_ResNet50_vd_aliproduct_v1.0_infer.tar
cd models
tar -xf product_ResNet50_vd_aliproduct_v1.0_infer.tar
```
Then, you can use installed paddle_serving_client tool to convert inference model to mobile model.
```
# Product recognition model conversion
python3 -m paddle_serving_client.convert --dirname ./product_ResNet50_vd_aliproduct_v1.0_infer/ \
--model_filename inference.pdmodel \
--params_filename inference.pdiparams \
--serving_server ./product_ResNet50_vd_aliproduct_v1.0_serving/ \
--serving_client ./product_ResNet50_vd_aliproduct_v1.0_client/
```
After the ResNet50_vd inference model is converted, there will be additional folders of `product_ResNet50_vd_aliproduct_v1.0_serving` and `product_ResNet50_vd_aliproduct_v1.0_client` in the current folder, with the following format:
```
|- product_ResNet50_vd_aliproduct_v1.0_serving/
|- __model__
|- __params__
|- serving_server_conf.prototxt
|- serving_server_conf.stream.prototxt
|- product_ResNet50_vd_aliproduct_v1.0_client
|- serving_client_conf.prototxt
|- serving_client_conf.stream.prototxt
```
Once you have the model file for deployment, you need to change the alias name in `serving_server_conf.prototxt`: change `alias_name` in `fetch_var` to `features`,
The modified serving_server_conf.prototxt file is as follows:
```
feed_var {
name: "x"
alias_name: "x"
is_lod_tensor: false
feed_type: 1
shape: 3
shape: 224
shape: 224
}
fetch_var {
name: "save_infer_model/scale_0.tmp_1"
alias_name: "features"
is_lod_tensor: true
fetch_type: 1
shape: -1
}
```
Next,download and unpack the built index of product gallery
```
cd ../
wget https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/rec/data/recognition_demo_data_v1.1.tar && tar -xf recognition_demo_data_v1.1.tar
```
<a name="paddle-serving-pipeline-deployment"></a>
## Paddle Serving pipeline deployment
**Attention:** pipeline deployment mode does not support Windows platform
1. Download the PaddleClas code, if you have already downloaded it, you can skip this step.
```
git clone https://github.com/PaddlePaddle/PaddleClas
# Enter the working directory
cd PaddleClas/deploy/paddleserving/recognition
```
The paddleserving directory contains the code to start the pipeline service and send prediction requests, including:
```
__init__.py
config.yml # configuration file of starting the service
pipeline_http_client.py # script to send pipeline prediction request by http
pipeline_rpc_client.py # script to send pipeline prediction request by rpc
recognition_web_service.py # start the script of the pipeline server
```
2. Run the following command to start the service.
```
# Start the service and save the running log in log.txt
python3 recognition_web_service.py &>log.txt &
```
After the service is successfully started, a log similar to the following will be printed in log.txt
![](../imgs/start_server_recog.png)
3. Send service request
```
python3 pipeline_http_client.py
```
After successfully running, the predicted result of the model will be printed in the cmd window. An example of the result is:
![](../imgs/results_recog.png)
Adjust the number of concurrency in config.yml to get the largest QPS.
```
op:
concurrency: 8
...
```
Multiple service requests can be sent at the same time if necessary.
The predicted performance data will be automatically written into the `PipelineServingLogs/pipeline.tracer` file.
<a name="faq"></a>
## FAQ
**Q1**: No result return after sending the request.
**A1**: Do not set the proxy when starting the service and sending the request. You can close the proxy before starting the service and before sending the request. The command to close the proxy is:
```
unset https_proxy
unset http_proxy
```
# 基于PaddleServing的商品识别服务部署
([English](./README.md)|简体中文)
本文以商品识别为例,介绍如何使用[PaddleServing](https://github.com/PaddlePaddle/Serving/blob/develop/README_CN.md)工具部署PaddleClas动态图模型的pipeline在线服务。
相比较于hubserving部署,PaddleServing具备以下优点:
- 支持客户端和服务端之间高并发和高效通信
- 支持 工业级的服务能力 例如模型管理,在线加载,在线A/B测试等
- 支持 多种编程语言 开发客户端,例如C++, Python和Java
更多有关PaddleServing服务化部署框架介绍和使用教程参考[文档](https://github.com/PaddlePaddle/Serving/blob/develop/README_CN.md)
## 目录
- [环境准备](#环境准备)
- [模型转换](#模型转换)
- [Paddle Serving pipeline部署](#部署)
- [FAQ](#FAQ)
<a name="环境准备"></a>
## 环境准备
需要准备PaddleClas的运行环境和PaddleServing的运行环境。
- 准备PaddleClas的[运行环境](../../docs/zh_CN/tutorials/install.md), 根据环境下载对应的paddle whl包,推荐安装2.1.0版本
- 准备PaddleServing的运行环境,步骤如下
1. 安装serving,用于启动服务
```
pip3 install paddle-serving-server==0.6.1 # for CPU
pip3 install paddle-serving-server-gpu==0.6.1 # for GPU
# 其他GPU环境需要确认环境再选择执行如下命令
pip3 install paddle-serving-server-gpu==0.6.1.post101 # GPU with CUDA10.1 + TensorRT6
pip3 install paddle-serving-server-gpu==0.6.1.post11 # GPU with CUDA11 + TensorRT7
```
2. 安装client,用于向服务发送请求
[下载链接](https://github.com/PaddlePaddle/Serving/blob/develop/doc/LATEST_PACKAGES.md)中找到对应python版本的client安装包,这里推荐python3.7版本:
```
wget https://paddle-serving.bj.bcebos.com/test-dev/whl/paddle_serving_client-0.0.0-cp37-none-any.whl
pip3 install paddle_serving_client-0.0.0-cp37-none-any.whl
```
3. 安装serving-app
```
pip3 install paddle-serving-app==0.6.1
```
**Note:** 如果要安装最新版本的PaddleServing参考[链接](https://github.com/PaddlePaddle/Serving/blob/develop/doc/LATEST_PACKAGES.md)
<a name="模型转换"></a>
## 模型转换
使用PaddleServing做服务化部署时,需要将保存的inference模型转换为serving易于部署的模型。
以下内容假定当前工作目录为PaddleClas根目录。
首先,下载商品识别的inference模型
```
cd deploy
# 下载并解压商品识别模型
wget -P models/ https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/rec/models/inference/product_ResNet50_vd_aliproduct_v1.0_infer.tar
cd models
tar -xf product_ResNet50_vd_aliproduct_v1.0_infer.tar
```
接下来,用安装的paddle_serving_client把下载的inference模型转换成易于server部署的模型格式。
```
# 转换商品识别模型
python3 -m paddle_serving_client.convert --dirname ./product_ResNet50_vd_aliproduct_v1.0_infer/ \
--model_filename inference.pdmodel \
--params_filename inference.pdiparams \
--serving_server ./product_ResNet50_vd_aliproduct_v1.0_serving/ \
--serving_client ./product_ResNet50_vd_aliproduct_v1.0_client/
```
商品识别推理模型转换完成后,会在当前文件夹多出`product_ResNet50_vd_aliproduct_v1.0_serving``product_ResNet50_vd_aliproduct_v1.0_client`的文件夹,具备如下格式:
```
|- product_ResNet50_vd_aliproduct_v1.0_serving/
|- __model__
|- __params__
|- serving_server_conf.prototxt
|- serving_server_conf.stream.prototxt
|- product_ResNet50_vd_aliproduct_v1.0_client
|- serving_client_conf.prototxt
|- serving_client_conf.stream.prototxt
```
得到模型文件之后,需要修改serving_server_conf.prototxt中的alias名字: 将`fetch_var`中的`alias_name`改为`features`,
修改后的serving_server_conf.prototxt内容如下:
```
feed_var {
name: "x"
alias_name: "x"
is_lod_tensor: false
feed_type: 1
shape: 3
shape: 224
shape: 224
}
fetch_var {
name: "save_infer_model/scale_0.tmp_1"
alias_name: "features"
is_lod_tensor: true
fetch_type: 1
shape: -1
}
```
接下来,下载并解压已经构建后的商品库index
```
cd ../
wget https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/rec/data/recognition_demo_data_v1.1.tar && tar -xf recognition_demo_data_v1.1.tar
```
<a name="部署"></a>
## Paddle Serving pipeline部署
**注意:** pipeline部署方式不支持windows平台
1. 下载PaddleClas代码,若已下载可跳过此步骤
```
git clone https://github.com/PaddlePaddle/PaddleClas
# 进入到工作目录
cd PaddleClas/deploy/paddleserving/recognition
```
paddleserving目录包含启动pipeline服务和发送预测请求的代码,包括:
```
__init__.py
config.yml # 启动服务的配置文件
pipeline_http_client.py # http方式发送pipeline预测请求的脚本
pipeline_rpc_client.py # rpc方式发送pipeline预测请求的脚本
recognition_web_service.py # 启动pipeline服务端的脚本
```
2. 启动服务可运行如下命令:
```
# 启动服务,运行日志保存在log.txt
python3 recognition_web_service.py &>log.txt &
```
成功启动服务后,log.txt中会打印类似如下日志
![](../imgs/start_server_recog.png)
3. 发送服务请求:
```
python3 pipeline_http_client.py
```
成功运行后,模型预测的结果会打印在cmd窗口中,结果示例为:
![](../imgs/results_recog.png)
调整 config.yml 中的并发个数可以获得最大的QPS
```
op:
#并发数,is_thread_op=True时,为线程并发;否则为进程并发
concurrency: 8
...
```
有需要的话可以同时发送多个服务请求
预测性能数据会被自动写入 `PipelineServingLogs/pipeline.tracer` 文件中。
<a name="FAQ"></a>
## FAQ
**Q1**: 发送请求后没有结果返回或者提示输出解码报错
**A1**: 启动服务和发送请求时不要设置代理,可以在启动服务前和发送请求前关闭代理,关闭代理的命令是:
```
unset https_proxy
unset http_proxy
```
......@@ -83,7 +83,7 @@ class DetOp(Op):
}
return feed_dict, False, None, ""
def postprocess(self, input_dicts, fetch_dict, log_id):
def postprocess(self, input_dicts, fetch_dict, data_id, log_id):
boxes = self.img_postprocess(fetch_dict, visualize=False)
boxes.sort(key=lambda x: x["score"], reverse=True)
boxes = filter(lambda x: x["score"] >= self.threshold,
......@@ -173,7 +173,7 @@ class RecOp(Op):
filtered_results.append(results[i])
return filtered_results
def postprocess(self, input_dicts, fetch_dict, log_id):
def postprocess(self, input_dicts, fetch_dict, data_id, log_id):
batch_features = fetch_dict["features"]
if self.feature_normalize:
......
......@@ -47,12 +47,14 @@ class ClsPredictor(Predictor):
import auto_log
import os
pid = os.getpid()
size = config["PreProcess"]["transform_ops"][1]["CropImage"][
"size"]
self.auto_logger = auto_log.AutoLogger(
model_name=config["Global"].get("model_name", "cls"),
model_precision='fp16'
if config["Global"]["use_fp16"] else 'fp32',
batch_size=config["Global"].get("batch_size", 1),
data_shape=[3, 224, 224],
data_shape=[3, size, size],
save_path=config["Global"].get("save_log_path",
"./auto_log.log"),
inference_config=self.config,
......
......@@ -35,6 +35,27 @@ class RecPredictor(Predictor):
self.preprocess_ops = create_operators(config["RecPreProcess"][
"transform_ops"])
self.postprocess = build_postprocess(config["RecPostProcess"])
self.benchmark = config["Global"].get("benchmark", False)
if self.benchmark:
import auto_log
pid = os.getpid()
self.auto_logger = auto_log.AutoLogger(
model_name=config["Global"].get("model_name", "rec"),
model_precision='fp16'
if config["Global"]["use_fp16"] else 'fp32',
batch_size=config["Global"].get("batch_size", 1),
data_shape=[3, 224, 224],
save_path=config["Global"].get("save_log_path",
"./auto_log.log"),
inference_config=self.config,
pids=pid,
process_name=None,
gpu_ids=None,
time_keys=[
'preprocess_time', 'inference_time', 'postprocess_time'
],
warmup=2)
def predict(self, images, feature_normalize=True):
input_names = self.paddle_predictor.get_input_names()
......@@ -44,16 +65,22 @@ class RecPredictor(Predictor):
output_tensor = self.paddle_predictor.get_output_handle(output_names[
0])
if self.benchmark:
self.auto_logger.times.start()
if not isinstance(images, (list, )):
images = [images]
for idx in range(len(images)):
for ops in self.preprocess_ops:
images[idx] = ops(images[idx])
image = np.array(images)
if self.benchmark:
self.auto_logger.times.stamp()
input_tensor.copy_from_cpu(image)
self.paddle_predictor.run()
batch_output = output_tensor.copy_to_cpu()
if self.benchmark:
self.auto_logger.times.stamp()
if feature_normalize:
feas_norm = np.sqrt(
......@@ -62,6 +89,9 @@ class RecPredictor(Predictor):
if self.postprocess is not None:
batch_output = self.postprocess(batch_output)
if self.benchmark:
self.auto_logger.times.end(stamp=True)
return batch_output
......@@ -85,16 +115,19 @@ def main(config):
batch_names.append(img_name)
cnt += 1
if cnt % config["Global"]["batch_size"] == 0 or (idx + 1) == len(image_list):
if len(batch_imgs) == 0:
if cnt % config["Global"]["batch_size"] == 0 or (idx + 1
) == len(image_list):
if len(batch_imgs) == 0:
continue
batch_results = rec_predictor.predict(batch_imgs)
for number, result_dict in enumerate(batch_results):
filename = batch_names[number]
print("{}:\t{}".format(filename, result_dict))
batch_imgs = []
batch_names = []
if rec_predictor.benchmark:
rec_predictor.auto_logger.report()
return
......
docs/images/wx_group.png

204.9 KB | W: | H:

docs/images/wx_group.png

112.8 KB | W: | H:

docs/images/wx_group.png
docs/images/wx_group.png
docs/images/wx_group.png
docs/images/wx_group.png
  • 2-up
  • Swipe
  • Onion skin
# 数据增强分类实战
---
本节将基于ImageNet-1K的数据集详细介绍数据增强实验,如果想快速体验此方法,可以参考[**30分钟玩转PaddleClas(进阶版)**](../quick_start/quick_start_classification_professional.md)中基于CIFAR100的数据增强实验。如果想了解相关算法的内容,请参考[数据增强算法介绍](../algorithm_introduction/DataAugmentation.md)
本节将基于 ImageNet-1K 的数据集详细介绍数据增强实验,如果想快速体验此方法,可以参考 [**30 分钟玩转 PaddleClas(进阶版)**](../quick_start/quick_start_classification_professional.md)中基于 CIFAR100 的数据增强实验。如果想了解相关算法的内容,请参考[数据增强算法介绍](../algorithm_introduction/DataAugmentation.md)
## 目录
- [参数配置](#1)
- [1. 参数配置](#1)
- [1.1 AutoAugment](#1.1)
- [1.2 RandAugment](#1.2)
- [1.3 TimmAutoAugment](#1.3)
......@@ -16,20 +16,20 @@
- [1.7 GridMask](#1.7)
- [1.8 Mixup](#1.8)
- [1.9 Cutmix](#1.9)
- [1.10 Mixup与Cutmix同时使用](#1.10)
- [启动命令](#2)
- [注意事项](#3)
- [实验结果](#4)
- [1.10 Mixup 与 Cutmix 同时使用](#1.10)
- [2. 启动命令](#2)
- [3. 注意事项](#3)
- [4. 实验结果](#4)
<a name="1"></a>
## 一、参数配置
## 1. 参数配置
由于不同的数据增强方式含有不同的超参数,为了便于理解和使用,我们在`configs/DataAugment`里分别列举了8种训练ResNet50的数据增强方式的参数配置文件,用户可以在`tools/run.sh`里直接替换配置文件的路径即可使用。此处分别挑选了图像变换、图像裁剪、图像混叠中的一个示例展示,其他参数配置用户可以自查配置文件。
由于不同的数据增强方式含有不同的超参数,为了便于理解和使用,我们在 `configs/DataAugment` 里分别列举了 8 种训练 ResNet50 的数据增强方式的参数配置文件,用户可以在 `tools/run.sh` 里直接替换配置文件的路径即可使用。此处分别挑选了图像变换、图像裁剪、图像混叠中的一个示例展示,其他参数配置用户可以自查配置文件。
<a name="1.1"></a>
### 1.1 AutoAugment
`AotoAugment`的图像增广方式的配置如下。`AutoAugment`是在uint8的数据格式上转换的,所以其处理过程应该放在归一化操作(`NormalizeImage`之前。
`AotoAugment` 的图像增广方式的配置如下。`AutoAugment` 是在 uint8 的数据格式上转换的,所以其处理过程应该放在归一化操作(`NormalizeImage`)之前。
```yaml
transform_ops:
......@@ -51,7 +51,7 @@
<a name="1.2"></a>
### 1.2 RandAugment
`RandAugment`的图像增广方式的配置如下,其中用户需要指定其中的参数`num_layers``magnitude`,默认的数值分别是`2``5``RandAugment`是在uint8的数据格式上转换的,所以其处理过程应该放在归一化操作(`NormalizeImage`之前。
`RandAugment` 的图像增广方式的配置如下,其中用户需要指定其中的参数 `num_layers``magnitude`,默认的数值分别是 `2``5``RandAugment` 是在 uint8 的数据格式上转换的,所以其处理过程应该放在归一化操作(`NormalizeImage`)之前。
```yaml
transform_ops:
......@@ -75,7 +75,7 @@
<a name="1.3"></a>
### 1.3 TimmAutoAugment
`TimmAutoAugment`的图像增广方式的配置如下,其中用户需要指定其中的参数`config_str``interpolation``img_size`,默认的数值分别是`rand-m9-mstd0.5-inc1``bicubic``224``TimmAutoAugment`是在uint8的数据格式上转换的,所以其处理过程应该放在归一化操作(`NormalizeImage`之前。
`TimmAutoAugment` 的图像增广方式的配置如下,其中用户需要指定其中的参数 `config_str``interpolation``img_size`,默认的数值分别是 `rand-m9-mstd0.5-inc1``bicubic``224``TimmAutoAugment` 是在 uint8 的数据格式上转换的,所以其处理过程应该放在归一化操作(`NormalizeImage`)之前。
```yaml
transform_ops:
......@@ -100,7 +100,7 @@
<a name="1.4"></a>
### 1.4 Cutout
`Cutout`的图像增广方式的配置如下,其中用户需要指定其中的参数`n_holes``length`,默认的数值分别是`1``112`。类似其他图像裁剪类的数据增强方式,`Cutout`既可以在uint8格式的数据上操作,也可以在归一化(`NormalizeImage`后的数据上操作,此处给出的是在归一化后的操作。
`Cutout` 的图像增广方式的配置如下,其中用户需要指定其中的参数 `n_holes``length`,默认的数值分别是 `1``112`。类似其他图像裁剪类的数据增强方式,`Cutout` 既可以在 uint8 格式的数据上操作,也可以在归一化)(`NormalizeImage`)后的数据上操作,此处给出的是在归一化后的操作。
```yaml
transform_ops:
......@@ -124,7 +124,7 @@
<a name="1.5"></a>
### 1.5 RandomErasing
`RandomErasing`的图像增广方式的配置如下,其中用户需要指定其中的参数`EPSILON``sl``sh``r1``attempt``use_log_aspect``mode`,默认的数值分别是`0.25``0.02``1.0/3.0``0.3``10``True``pixel`。类似其他图像裁剪类的数据增强方式,`RandomErasing`既可以在uint8格式的数据上操作,也可以在归一化(`NormalizeImage`后的数据上操作,此处给出的是在归一化后的操作。
`RandomErasing` 的图像增广方式的配置如下,其中用户需要指定其中的参数 `EPSILON``sl``sh``r1``attempt``use_log_aspect``mode`,默认的数值分别是 `0.25``0.02``1.0/3.0``0.3``10``True``pixel`。类似其他图像裁剪类的数据增强方式,`RandomErasing` 既可以在 uint8 格式的数据上操作,也可以在归一化(`NormalizeImage`)后的数据上操作,此处给出的是在归一化后的操作。
```yaml
transform_ops:
......@@ -153,7 +153,7 @@
<a name="1.6"></a>
### 1.6 HideAndSeek
`HideAndSeek`的图像增广方式的配置如下。类似其他图像裁剪类的数据增强方式,`HideAndSeek`既可以在uint8格式的数据上操作,也可以在归一化(`NormalizeImage`后的数据上操作,此处给出的是在归一化后的操作。
`HideAndSeek` 的图像增广方式的配置如下。类似其他图像裁剪类的数据增强方式,`HideAndSeek` 既可以在 uint8 格式的数据上操作,也可以在归一化(`NormalizeImage`)后的数据上操作,此处给出的是在归一化后的操作。
```yaml
transform_ops:
......@@ -173,9 +173,10 @@
```
<a name="1.7"></a>
### 1.7 GridMask
`GridMask`的图像增广方式的配置如下,其中用户需要指定其中的参数`d1``d2``rotate``ratio``mode`, 默认的数值分别是`96``224``1``0.5``0`。类似其他图像裁剪类的数据增强方式,`GridMask`既可以在uint8格式的数据上操作,也可以在归一化(`NormalizeImage`后的数据上操作,此处给出的是在归一化后的操作。
`GridMask` 的图像增广方式的配置如下,其中用户需要指定其中的参数 `d1``d2``rotate``ratio``mode`, 默认的数值分别是 `96``224``1``0.5``0`。类似其他图像裁剪类的数据增强方式,`GridMask` 既可以在 uint8 格式的数据上操作,也可以在归一化(`NormalizeImage`)后的数据上操作,此处给出的是在归一化后的操作。
```yaml
transform_ops:
......@@ -202,7 +203,7 @@
<a name="1.8"></a>
### 1.8 Mixup
`Mixup`的图像增广方式的配置如下,其中用户需要指定其中的参数`alpha`,默认的数值是`0.2`。类似其他图像混合类的数据增强方式,`Mixup`是在图像做完数据处理后将每个batch内的数据做图像混叠,将混叠后的图像和标签输入网络中训练,所以其是在图像数据处理(图像变换、图像裁剪)后操作。
`Mixup` 的图像增广方式的配置如下,其中用户需要指定其中的参数 `alpha`,默认的数值是 `0.2`。类似其他图像混合类的数据增强方式,`Mixup` 是在图像做完数据处理后将每个 batch 内的数据做图像混叠,将混叠后的图像和标签输入网络中训练,所以其是在图像数据处理(图像变换、图像裁剪)后操作。
```yaml
transform_ops:
......@@ -226,7 +227,7 @@
<a name="1.9"></a>
### 1.9 Cutmix
`Cutmix`的图像增广方式的配置如下,其中用户需要指定其中的参数`alpha`,默认的数值是`0.2`。类似其他图像混合类的数据增强方式,`Cutmix`是在图像做完数据处理后将每个batch内的数据做图像混叠,将混叠后的图像和标签输入网络中训练,所以其是在图像数据处理(图像变换、图像裁剪)后操作。
`Cutmix` 的图像增广方式的配置如下,其中用户需要指定其中的参数 `alpha`,默认的数值是 `0.2`。类似其他图像混合类的数据增强方式,`Cutmix` 是在图像做完数据处理后将每个 batch 内的数据做图像混叠,将混叠后的图像和标签输入网络中训练,所以其是在图像数据处理(图像变换、图像裁剪)后操作。
```yaml
transform_ops:
......@@ -248,9 +249,10 @@
```
<a name="1.10"></a>
### 1.10 Mixup与Cutmix同时使用
### 1.10 Mixup 与 Cutmix 同时使用
`Mixup``Cutmix` 同时使用的配置如下,其中用户需要指定额外的参数 `prob`,该参数控制不同数据增强的概率,默认为 `0.5`
`Mixup``与Cutmix`同时使用的配置如下,其中用户需要指定额外的参数`prob`,该参数控制不同数据增强的概率,默认为`0.5`
```yaml
transform_ops:
- DecodeImage:
......@@ -275,11 +277,11 @@
```
<a name="2"></a>
## 二、启动命令
## 2. 启动命令
当用户配置完训练环境后,类似于训练其他分类任务,只需要将`tools/train.sh`中的配置文件替换成为相应的数据增强方式的配置文件即可。
当用户配置完训练环境后,类似于训练其他分类任务,只需要将 `tools/train.sh` 中的配置文件替换成为相应的数据增强方式的配置文件即可。
其中`train.sh`中的内容如下:
其中 `train.sh` 中的内容如下:
```bash
......@@ -290,27 +292,27 @@ python3 -m paddle.distributed.launch \
-c ./ppcls/configs/ImageNet/DataAugment/ResNet50_Cutout.yaml
```
运行`train.sh`
运行 `train.sh`
```bash
sh tools/train.sh
```
<a name="3"></a>
## 三、注意事项
## 3. 注意事项
* 由于图像混叠时需对label进行混叠,无法计算训练数据的准确率,所以在训练过程中没有打印训练准确率。
* 由于图像混叠时需对 label 进行混叠,无法计算训练数据的准确率,所以在训练过程中没有打印训练准确率。
* 在使用数据增强后,由于训练数据更难,所以训练损失函数可能较大,训练集的准确率相对较低,但其有拥更好的泛化能力,所以验证集的准确率相对较高。
* 在使用数据增强后,模型可能会趋于欠拟合状态,建议可以适当的调小`l2_decay`的值来获得更高的验证集准确率。
* 在使用数据增强后,模型可能会趋于欠拟合状态,建议可以适当的调小 `l2_decay` 的值来获得更高的验证集准确率。
* 几乎每一类图像增强均含有超参数,我们只提供了基于ImageNet-1k的超参数,其他数据集需要用户自己调试超参数,具体超参数的含义用户可以阅读相关的论文,调试方法也可以参考训练技巧的章节。
* 几乎每一类图像增强均含有超参数,我们只提供了基于 ImageNet-1k 的超参数,其他数据集需要用户自己调试超参数,具体超参数的含义用户可以阅读相关的论文,调试方法也可以参考训练技巧的章节。
<a name="4"></a>
## 四、实验结果
## 4. 实验结果
基于PaddleClas,在ImageNet1k数据集上的分类精度如下。
基于 PaddleClas,在 ImageNet1k 数据集上的分类精度如下。
| 模型 | 初始学习率策略 | l2 decay | batch size | epoch | 数据变化策略 | Top1 Acc | 论文中结论 |
|-------------|------------------|--------------|------------|-------|----------------|------------|----|
......@@ -325,5 +327,5 @@ sh tools/train.sh
| ResNet50 | 0.1/cosine_decay | 0.0001 | 256 | 300 | hide and seek | 0.7743 | 0.7720 |
**注意**
* 在这里的实验中,为了便于对比,我们将l2 decay固定设置为1e-4,在实际使用中,我们推荐尝试使用更小的l2 decay。结合数据增强,我们发现将l2 decay由1e-4减小为7e-5均能带来至少0.3~0.5%的精度提升。
* 在这里的实验中,为了便于对比,我们将 l2 decay 固定设置为 1e-4,在实际使用中,我们推荐尝试使用更小的 l2 decay。结合数据增强,我们发现将 l2 decay 由 1e-4 减小为 7e-5 均能带来至少 0.3~0.5% 的精度提升。
* 我们目前尚未对不同策略进行组合并验证效果,这一块后续我们会开展更多的对比实验,敬请期待。
# PaddleClas代码解析
# PaddleClas 代码解析
## 目录
- [整体代码和目录概览](#1)
- [训练模块定义](#2)
- [1. 整体代码和目录概览](#1)
- [2. 训练模块定义](#2)
- [2.1 数据](#2.1)
- [2.2 模型结构](#2.2)
- [2.3 损失函数](#2.3)
......@@ -11,39 +11,39 @@
- [2.5 训练时评估](#2.5)
- [2.6 模型存储](#2.6)
- [2.7 模型裁剪与量化](#2.7)
- [预测部署代码和方式](#3)
- [3. 预测部署代码和方式](#3)
<a name="1"></a>
## 一、整体代码和目录概览
## 1. 整体代码和目录概览
PaddleClas主要代码和目录结构如下
PaddleClas 主要代码和目录结构如下
* benchmark: 文件夹下存放了一些shell脚本,主要是为了测试PaddleClas中不同模型的速度指标,如单卡训练速度指标、多卡训练速度指标等。
* dataset:文件夹下存放数据集和用于处理数据集的脚本。脚本负责将数据集处理为适合Dataloader处理的格式。
* deploy:部署核心代码,文件夹存放的是部署工具,支持 python/cpp inference、Hub Serveing、Paddle Lite、Slim离线量化等多种部署方式。
* ppcls:训练核心代码,文件夹下存放PaddleClas框架主体。配置文件、模型训练、评估、预测、动转静导出等具体代码实现均在这里。
* benchmark: 文件夹下存放了一些 shell 脚本,主要是为了测试 PaddleClas 中不同模型的速度指标,如单卡训练速度指标、多卡训练速度指标等。
* dataset:文件夹下存放数据集和用于处理数据集的脚本。脚本负责将数据集处理为适合 Dataloader 处理的格式。
* deploy:部署核心代码,文件夹存放的是部署工具,支持 python/cpp inference、Hub Serveing、Paddle Lite、Slim 离线量化等多种部署方式。
* ppcls:训练核心代码,文件夹下存放 PaddleClas 框架主体。配置文件、模型训练、评估、预测、动转静导出等具体代码实现均在这里。
* tools:训练、评估、预测、模型动转静导出的入口函数和脚本均在该文件下。
* requirements.txt 文件用于安装 PaddleClas 的依赖项。使用pip进行升级安装使用。
* tests:PaddleClas模型从训练到预测的全链路测试,验证各功能是否能够正常使用。
* requirements.txt 文件用于安装 PaddleClas 的依赖项。使用 pip 进行升级安装使用。
* tests:PaddleClas 模型从训练到预测的全链路测试,验证各功能是否能够正常使用。
<a name="2"></a>
## 二、训练模块定义
## 2. 训练模块定义
深度学习模型训练过程中,主要包含数据、模型结构、损失函数、优化器和学习率衰减、权重衰减策略等,以下一一解读。
<a name="2.1"></a>
## 2.1 数据
### 2.1 数据
对于有监督任务来说,训练数据一般包含原始数据及其标注。在基于单标签的图像分类任务中,原始数据指的是图像数据,而标注则是该图像数据所属的类比。PaddleClas中,训练时需要提供标签文件,形式如下,每一行包含一条训练样本,分别表示图片路径和类别标签,用分隔符隔开(默认为空格)。
对于有监督任务来说,训练数据一般包含原始数据及其标注。在基于单标签的图像分类任务中,原始数据指的是图像数据,而标注则是该图像数据所属的类比。PaddleClas 中,训练时需要提供标签文件,形式如下,每一行包含一条训练样本,分别表示图片路径和类别标签,用分隔符隔开(默认为空格)。
```
train/n01440764/n01440764_10026.JPEG 0
train/n01440764/n01440764_10027.JPEG 0
```
在代码`ppcls/data/dataloader/common_dataset.py`中,包含`CommonDataset`类,继承自`paddle.io.Dataset`,该数据集类可以通过一个键值进行索引并获取指定样本。`ImageNetDataset`, `LogoDataset`, `CommonDataset` 等数据集类都对这个类别
在代码 `ppcls/data/dataloader/common_dataset.py` 中,包含 `CommonDataset` 类,继承自 `paddle.io.Dataset`,该数据集类可以通过一个键值进行索引并获取指定样本。`ImageNetDataset`, `LogoDataset`, `CommonDataset` 等数据集类都对这个类别
对于读入的数据,需要通过数据转换,将原始的图像数据进行转换。训练时,标准的数据预处理包含:`DecodeImage`, `RandCropImage`, `RandFlipImage`, `NormalizeImage`, `ToCHWImage`。在配置文件中体现如下,数据预处理主要包含在`transforms`字段中,以列表形式呈现,会按照顺序对数据依次做这些转换。
对于读入的数据,需要通过数据转换,将原始的图像数据进行转换。训练时,标准的数据预处理包含:`DecodeImage`, `RandCropImage`, `RandFlipImage`, `NormalizeImage`, `ToCHWImage`。在配置文件中体现如下,数据预处理主要包含在 `transforms` 字段中,以列表形式呈现,会按照顺序对数据依次做这些转换。
```yaml
DataLoader:
......@@ -74,7 +74,7 @@ PaddleClas 中也包含了 `AutoAugment`, `RandAugment` 等数据增广方法,
图像分类中,数据后处理主要为 `argmax` 操作,在此不再赘述。
<a name="2.2"></a>
## 2.2 模型结构
### 2.2 模型结构
在配置文件中,模型结构定义如下
......@@ -86,7 +86,7 @@ Arch:
use_ssld: False
```
`Arch.name` 表示模型名称, `Arch.pretrained` 表示是否添加预训练模型,`use_ssld` 表示是否使用基于 `SSLD` 知识蒸馏得到的预训练模型。所有的模型名称均在 `ppcls/arch/backbone/__init__.py` 中定义。
`Arch.name` 表示模型名称,`Arch.pretrained` 表示是否添加预训练模型,`use_ssld` 表示是否使用基于 `SSLD` 知识蒸馏得到的预训练模型。所有的模型名称均在 `ppcls/arch/backbone/__init__.py` 中定义。
对应的,在 `ppcls/arch/__init__.py` 中,通过 `build_model` 方法创建模型对象。
......@@ -100,9 +100,9 @@ def build_model(config):
```
<a name="2.3"></a>
## 2.3 损失函数
### 2.3 损失函数
PaddleClas中,包含了 `CELoss` , `JSDivLoss`, `TripletLoss`, `CenterLoss` 等损失函数,均定义在 `ppcls/loss` 中。
PaddleClas 中,包含了 `CELoss`, `JSDivLoss`, `TripletLoss`, `CenterLoss` 等损失函数,均定义在 `ppcls/loss` 中。
`ppcls/loss/__init__.py` 文件中,使用 `CombinedLoss` 来构建及合并损失函数,不同训练策略中所需要的损失函数与计算方法不同,PaddleClas 在构建损失函数过程中,主要考虑了以下几个因素。
......@@ -112,7 +112,7 @@ PaddleClas中,包含了 `CELoss` , `JSDivLoss`, `TripletLoss`, `CenterLoss`
4. 是否是训练 metric learning
用户可以在配置文件中指定损失函数的类型及权重,如在训练中添加 TripletLossV2 ,配置文件如下:
用户可以在配置文件中指定损失函数的类型及权重,如在训练中添加 TripletLossV2,配置文件如下:
```yaml
Loss:
......@@ -125,13 +125,13 @@ Loss:
```
<a name="2.4"></a>
## 2.4 优化器和学习率衰减、权重衰减策略
### 2.4 优化器和学习率衰减、权重衰减策略
图像分类任务中,`Momentum` 是一种比较常用的优化器, PaddleClas 中提供了 `Momentum``RMSProp``Adam``AdamW`等几种优化器策略。
图像分类任务中,`Momentum` 是一种比较常用的优化器,PaddleClas 中提供了 `Momentum``RMSProp``Adam``AdamW` 等几种优化器策略。
权重衰减策略是一种比较常用的正则化方法,主要用于防止模型过拟合。 PaddleClas 中提供了 `L1Decay``L2Decay` 两种权重衰减策略。
学习率衰减是图像分类任务中必不可少的精度提升训练方法, PaddleClas 目前支持 `Cosine` , `Piecewise`, `Linear` 等学习率衰减策略。
学习率衰减是图像分类任务中必不可少的精度提升训练方法,PaddleClas 目前支持 `Cosine`, `Piecewise`, `Linear` 等学习率衰减策略。
在配置文件中,优化器、权重衰减策略、学习率衰减策略可以通过以下的字段进行配置。
......@@ -180,22 +180,22 @@ def build_optimizer(config, epochs, step_each_epoch, parameters):
return optim, lr
```
不同优化器和权重衰减策略均以类的形式实现,具体实现可以参考文件 `ppcls/optimizer/optimizer.py` ;不同的学习率衰减策略可以参考文件 `ppcls/optimizer/learning_rate.py`
不同优化器和权重衰减策略均以类的形式实现,具体实现可以参考文件 `ppcls/optimizer/optimizer.py`;不同的学习率衰减策略可以参考文件 `ppcls/optimizer/learning_rate.py`
<a name="2.5"></a>
## 2.5 训练时评估
### 2.5 训练时评估
模型在训练的时候,可以设置模型保存的间隔,也可以选择每隔若干个epoch对验证集进行评估,从而可以保存在验证集上精度最佳的模型。配置文件中,可以通过下面的字段进行配置。
模型在训练的时候,可以设置模型保存的间隔,也可以选择每隔若干个 epoch 对验证集进行评估,从而可以保存在验证集上精度最佳的模型。配置文件中,可以通过下面的字段进行配置。
```yaml
Global:
save_interval: 1 # 模型保存的epoch间隔
save_interval: 1 # 模型保存的 epoch 间隔
eval_during_train: True # 是否进行训练时评估
eval_interval: 1 # 评估的epoch间隔
eval_interval: 1 # 评估的 epoch 间隔
```
<a name="2.6"></a>
## 2.6 模型存储
### 2.6 模型存储
模型存储是通过 Paddle 框架的 `paddle.save()` 函数实现的,存储的是模型的动态图版本,以字典的形式存储,便于继续训练。具体实现如下
```python
......@@ -217,7 +217,7 @@ def save_model(program, model_path, epoch_id, prefix='ppcls'):
如果想对模型进行压缩训练,则通过下面字段进行配置
<a name="2.7"></a>
## 2.7 模型裁剪与量化
### 2.7 模型裁剪与量化
1.模型裁剪:
......@@ -236,14 +236,14 @@ Slim:
name: pact
```
训练方法详见模型[裁剪量化使用介绍](../advanced_tutorials/model_prune_quantization.md) 算法介绍详见[裁剪量化算法介绍](../algorithm_introduction/model_prune_quantization.md)
训练方法详见模型[裁剪量化使用介绍](../advanced_tutorials/model_prune_quantization.md),算法介绍详见[裁剪量化算法介绍](../algorithm_introduction/model_prune_quantization.md)
<a name="3"></a>
## 三、预测部署代码和方式
## 3. 预测部署代码和方式
* 如果希望将对分类模型进行离线量化,可以参考 [模型量化裁剪教程](../advanced_tutorials/model_prune_quantization.md) 中离线量化部分。
* 如果希望在服务端使用 python 进行部署,可以参考 [python inference 预测教程](../inference_deployment/python_deploy.md)
* 如果希望在服务端使用 cpp 进行部署,可以参考 [cpp inference 预测教程](../inference_deployment/cpp_deploy.md)
* 如果希望将分类模型部署为服务,可以参考 [hub serving 预测部署教程](../inference_deployment/paddle_hub_serving_deploy.md)
* 如果希望在移动端使用分类模型进行预测,可以参考 [PaddleLite 预测部署教程](../inference_deployment/paddle_lite_deploy.md)
* 如果希望使用whl包对分类模型进行预测,可以参考 [whl包预测](../inference_deployment/whl_deploy.md)
* 如果希望在服务端使用 python 进行部署,可以参考 [python inference 预测教程](../inference_deployment/python_deploy.md)
* 如果希望在服务端使用 cpp 进行部署,可以参考 [cpp inference 预测教程](../inference_deployment/cpp_deploy.md)
* 如果希望将分类模型部署为服务,可以参考 [hub serving 预测部署教程](../inference_deployment/paddle_hub_serving_deploy.md)
* 如果希望在移动端使用分类模型进行预测,可以参考 [PaddleLite 预测部署教程](../inference_deployment/paddle_lite_deploy.md)
* 如果希望使用 whl 包对分类模型进行预测,可以参考 [whl 包预测](../inference_deployment/whl_deploy.md)
......@@ -3,7 +3,7 @@
## 目录
- [如何贡献代码](#1)
- [1. 如何贡献代码](#1)
- [1.1 PaddleClas 分支说明](#1.1)
- [1.2 PaddleClas 代码提交流程与规范](#1.2)
- [1.2.1 fork 和 clone 代码](#1.2.1)
......@@ -12,28 +12,28 @@
- [1.2.4 使用 pre-commit 勾子](#1.2.4)
- [1.2.5 修改与提交代码](#1.2.5)
- [1.2.6 保持本地仓库最新](#1.2.6)
- [1.2.7 push到远程仓库](#1.2.7)
- [1.2.8 提交Pull Request](#1.2.8)
- [1.2.7 push 到远程仓库](#1.2.7)
- [1.2.8 提交 Pull Request](#1.2.8)
- [1.2.9 签署 CLA 协议和通过单元测试](#1.2.9)
- [1.2.10 删除分支](#1.2.10)
- [1.2.11 提交代码的一些约定](#1.2.11)
- [总结](#2)
- [参考文献](#3)
- [2. 总结](#2)
- [3. 参考文献](#3)
<a name="1"></a>
## 一、如何贡献代码
## 1. 如何贡献代码
<a name="1.1"></a>
### 1.1 PaddleClas 分支说明
PaddleClas 未来将维护 2 种分支,分别为:
* release/x.x 系列分支:为稳定的发行版本分支,会适时打 tag 发布版本,适配 Paddle 的 release 版本。当前最新的分支为 release/2.3 分支,是当前默认分支,适配 Paddle v2.1.0 。随着版本迭代, release/x.x 系列分支会越来越多,默认维护最新版本的 release 分支,前 1 个版本分支会修复 bug ,其他的分支不再维护。
* release/x.x 系列分支:为稳定的发行版本分支,会适时打 tag 发布版本,适配 Paddle 的 release 版本。当前最新的分支为 release/2.3 分支,是当前默认分支,适配 Paddle v2.1.0 。随着版本迭代, release/x.x 系列分支会越来越多,默认维护最新版本的 release 分支,前 1 个版本分支会修复 bug,其他的分支不再维护。
* develop 分支:为开发分支,适配 Paddle 的 develop 版本,主要用于开发新功能。如果有同学需要进行二次开发,请选择 develop 分支。为了保证 develop 分支能在需要的时候拉出 release/x.x 分支, develop 分支的代码只能使用 Paddle 最新 release 分支中有效的 api 。也就是说,如果 Paddle develop 分支中开发了新的 api,但尚未出现在 release 分支代码中,那么请不要在 PaddleClas 中使用。除此之外,对于不涉及 api 的性能优化、参数调整、策略更新等,都可以正常进行开发。
PaddleClas 的历史分支,未来将不再维护。考虑到一些同学可能仍在使用,这些分支还会继续保留:
* release/static 分支:这个分支曾用于静态图的开发与测试,目前兼容 >=1.7 版本的 Paddle 。如果有特殊需求,要适配旧版本的 Paddle ,那还可以使用这个分支,但除了修复 bug 外不再更新代码。
* release/static 分支:这个分支曾用于静态图的开发与测试,目前兼容 >=1.7 版本的 Paddle 。如果有特殊需求,要适配旧版本的 Paddle,那还可以使用这个分支,但除了修复 bug 外不再更新代码。
* dygraph-dev 分支:这个分支将不再维护,也不再接受新的代码,请使用的同学尽快迁移到 develop 分支。
......@@ -45,7 +45,7 @@ PaddleClas 欢迎大家向 repo 中积极贡献代码,下面给出一些贡献
<a name="1.2.1"></a>
#### 1.2.1 fork 和 clone 代码
* 跳转到 [PaddleClas GitHub首页](https://github.com/PaddlePaddle/PaddleClas) ,然后单击 Fork 按钮,生成自己目录下的仓库,比如 `https://github.com/USERNAME/PaddleClas`
* 跳转到 [PaddleClas GitHub 首页](https://github.com/PaddlePaddle/PaddleClas),然后单击 Fork 按钮,生成自己目录下的仓库,比如 `https://github.com/USERNAME/PaddleClas`
<div align="center">
......@@ -56,7 +56,7 @@ PaddleClas 欢迎大家向 repo 中积极贡献代码,下面给出一些贡献
* 将远程仓库 clone 到本地
```shell
# 拉取develop分支的代码
# 拉取 develop 分支的代码
git clone https://github.com/USERNAME/PaddleClas.git -b develop
cd PaddleClas
```
......@@ -77,7 +77,7 @@ origin https://github.com/USERNAME/PaddleClas.git (fetch)
origin https://github.com/USERNAME/PaddleClas.git (push)
```
上面的信息只包含了 clone 的远程仓库的信息,也就是自己用户名下的 PaddleClas ,接下来我们创建一个原始 PaddleClas 仓库的远程主机,命名为 upstream 。
上面的信息只包含了 clone 的远程仓库的信息,也就是自己用户名下的 PaddleClas,接下来我们创建一个原始 PaddleClas 仓库的远程主机,命名为 upstream 。
```shell
git remote add upstream https://github.com/PaddlePaddle/PaddleClas.git
......@@ -92,7 +92,7 @@ upstream https://github.com/PaddlePaddle/PaddleClas.git (fetch)
upstream https://github.com/PaddlePaddle/PaddleClas.git (push)
```
这主要是为了后续在提交 pull request (PR) 时,始终保持本地仓库最新。
这主要是为了后续在提交 pull request(PR)时,始终保持本地仓库最新。
<a name="1.2.3"></a>
#### 1.2.3 创建本地分支
......@@ -106,10 +106,10 @@ git checkout -b new_branch
也可以基于远程或者上游的分支创建新的分支,命令如下。
```shell
# 基于用户远程仓库(origin)的develop创建new_branch分支
# 基于用户远程仓库(origin)的 develop 创建 new_branch 分支
git checkout -b new_branch origin/develop
# 基于上游远程仓库(upstream)的develop创建new_branch分支
# 如果需要从upstream创建新的分支,需要首先使用git fetch upstream获取上游代码
# 基于上游远程仓库(upstream)的 develop 创建 new_branch 分支
# 如果需要从 upstream 创建新的分支,需要首先使用 git fetch upstream 获取上游代码
git checkout -b new_branch upstream/develop
```
......@@ -123,9 +123,9 @@ Switched to a new branch 'new_branch'
<a name="1.2.4"></a>
#### 1.2.4 使用 pre-commit 勾子
Paddle 开发人员使用 pre-commit 工具来管理 Git 预提交钩子。 它可以帮助我们格式化源代码(C++,Python),在提交(commit)前自动检查一些基本事宜(如每个文件只有一个 EOL,Git 中不要添加大文件等)。
Paddle 开发人员使用 pre-commit 工具来管理 Git 预提交钩子。 它可以帮助我们格式化源代码(C++,Python),在提交(commit)前自动检查一些基本事宜(如每个文件只有一个 EOL,Git 中不要添加大文件等)。
pre-commit 测试是 Travis-CI 中单元测试的一部分,不满足钩子的 PR 不能被提交到 PaddleClas ,首先安装并在当前目录运行它:
pre-commit 测试是 Travis-CI 中单元测试的一部分,不满足钩子的 PR 不能被提交到 PaddleClas,首先安装并在当前目录运行它:
```shell
pip install pre-commit
......@@ -168,21 +168,21 @@ git commit -m "your commit info"
```shell
git fetch upstream
# 如果是希望提交到其他分支,则需要从upstream的其他分支pull代码,这里是develop
# 如果是希望提交到其他分支,则需要从 upstream 的其他分支 pull 代码,这里是 develop
git pull upstream develop
```
<a name="1.2.7"></a>
#### 1.2.7 push到远程仓库
#### 1.2.7 push 到远程仓库
```shell
git push origin new_branch
```
<a name="1.2.8"></a>
#### 1.2.8 提交Pull Request
#### 1.2.8 提交 Pull Request
点击 new pull request,选择本地分支和目标分支,如下图所示。在 PR 的描述说明中,填写该 PR 所完成的功能。接下来等待 review ,如果有需要修改的地方,参照上述步骤更新 origin 中的对应分支即可。
点击 new pull request,选择本地分支和目标分支,如下图所示。在 PR 的描述说明中,填写该 PR 所完成的功能。接下来等待 review,如果有需要修改的地方,参照上述步骤更新 origin 中的对应分支即可。
<div align="center">
<img src="../../images/quick_start/community/004_create_pr.png" width = "600" />
......@@ -192,10 +192,10 @@ git push origin new_branch
#### 1.2.9 签署 CLA 协议和通过单元测试
* 签署 CLA
在首次向 PaddlePaddle 提交 Pull Request 时,您需要您签署一次 CLA (Contributor License Agreement) 协议,以保证您的代码可以被合入,具体签署方式如下:
在首次向 PaddlePaddle 提交 Pull Request 时,您需要您签署一次 CLA(Contributor License Agreement)协议,以保证您的代码可以被合入,具体签署方式如下:
1. 请您查看 PR 中的 Check 部分,找到 license/cla ,并点击右侧 detail ,进入 CLA 网站
2. 点击 CLA 网站中的 `Sign in with GitHub to agree` , 点击完成后将会跳转回您的 Pull Request 页面
1. 请您查看 PR 中的 Check 部分,找到 license/cla,并点击右侧 detail,进入 CLA 网站
2. 点击 CLA 网站中的 `Sign in with GitHub to agree`, 点击完成后将会跳转回您的 Pull Request 页面
<a name="1.2.10"></a>
#### 1.2.10 删除分支
......@@ -214,10 +214,10 @@ git push origin :new_branch
* 删除本地分支
```shell
# 切换到develop分支,否则无法删除当前分支
# 切换到 develop 分支,否则无法删除当前分支
git checkout develop
# 删除new_branch分支
# 删除 new_branch 分支
git branch -D new_branch
```
......@@ -228,17 +228,17 @@ git branch -D new_branch
1)请保证 Travis-CI 中单元测试能顺利通过。如果没过,说明提交的代码存在问题,官方维护人员一般不做评审。
2)提交 Pull Request前:
2)提交 Pull Request 前:
请注意 commit 的数量。
原因:如果仅仅修改一个文件但提交了十几个 commit ,每个 commit 只做了少量的修改,这会给评审人带来很大困扰。评审人需要逐一查看每个 commit 才能知道做了哪些修改,且不排除 commit 之间的修改存在相互覆盖的情况。
原因:如果仅仅修改一个文件但提交了十几个 commit,每个 commit 只做了少量的修改,这会给评审人带来很大困扰。评审人需要逐一查看每个 commit 才能知道做了哪些修改,且不排除 commit 之间的修改存在相互覆盖的情况。
建议:每次提交时,保持尽量少的 commit ,可以通过 `git commit --amend` 补充上次的 commit 。对已经 Push 到远程仓库的多个 commit ,可以参考 [squash commits after push](https://stackoverflow.com/questions/5667884/how-to-squash-commits-in-git-after-they-have-been-pushed)
建议:每次提交时,保持尽量少的 commit,可以通过 `git commit --amend` 补充上次的 commit 。对已经 Push 到远程仓库的多个 commit,可以参考 [squash commits after push](https://stackoverflow.com/questions/5667884/how-to-squash-commits-in-git-after-they-have-been-pushed)
请注意每个 commit 的名称:应能反映当前 commit 的内容,不能太随意。
3)如果解决了某个 Issue 的问题,请在该 Pull Request 的第一个评论框中加上: `fix #issue_number` ,这样当该 Pull Request 被合并后,会自动关闭对应的 Issue 。关键词包括: close, closes, closed, fix, fixes, fixed, resolve, resolves, resolved ,请选择合适的词汇。详细可参考 [Closing issues via commit messages](https://help.github.com/articles/closing-issues-via-commit-messages)
3)如果解决了某个 Issue 的问题,请在该 Pull Request 的第一个评论框中加上: `fix #issue_number`,这样当该 Pull Request 被合并后,会自动关闭对应的 Issue 。关键词包括: close, closes, closed, fix, fixes, fixed, resolve, resolves, resolved,请选择合适的词汇。详细可参考 [Closing issues via commit messages](https://help.github.com/articles/closing-issues-via-commit-messages)
此外,在回复评审人意见时,请您遵守以下约定:
......@@ -253,11 +253,11 @@ git branch -D new_branch
- 请采用 `start a review` 进行回复,而非直接回复的方式。原因是每个回复都会发送一封邮件,会造成邮件灾难。
<a name="2"></a>
## 二、总结
## 2. 总结
* 开源社区依赖于众多开发者与用户的贡献和反馈,在这里感谢与期待大家向 PaddleClas 提出宝贵的意见与 Pull Request ,希望我们可以一起打造一个领先实用全面的图像识别代码仓库!
* 开源社区依赖于众多开发者与用户的贡献和反馈,在这里感谢与期待大家向 PaddleClas 提出宝贵的意见与 Pull Request,希望我们可以一起打造一个领先实用全面的图像识别代码仓库!
<a name="3"></a>
## 三、参考文献
1. [PaddlePaddle本地开发指南](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/guides/08_contribution/index_cn.html)
2. [向开源框架提交pr的过程](https://blog.csdn.net/vim_wj/article/details/78300239)
## 3. 参考文献
1. [PaddlePaddle 本地开发指南](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/guides/08_contribution/index_cn.html)
2. [向开源框架提交 pr 的过程](https://blog.csdn.net/vim_wj/article/details/78300239)
......@@ -4,32 +4,34 @@
复杂的模型有利于提高模型的性能,但也导致模型中存在一定冗余。此部分提供精简模型的功能,包括两部分:模型量化(量化训练、离线量化)、模型剪枝。
其中模型量化将全精度缩减到定点数减少这种冗余,达到减少模型计算复杂度,提高模型推理性能的目的。
模型量化可以在基本不损失模型的精度的情况下,将FP32精度的模型参数转换为Int8精度,减小模型参数大小并加速计算,使用量化后的模型在移动端等部署时更具备速度优势。
模型量化可以在基本不损失模型的精度的情况下,将 FP32 精度的模型参数转换为 Int8 精度,减小模型参数大小并加速计算,使用量化后的模型在移动端等部署时更具备速度优势。
模型剪枝将CNN中不重要的卷积核裁剪掉,减少模型参数量,从而降低模型计算复杂度。
模型剪枝将 CNN 中不重要的卷积核裁剪掉,减少模型参数量,从而降低模型计算复杂度。
本教程将介绍如何使用飞桨模型压缩库PaddleSlim做PaddleClas模型的压缩,即裁剪、量化功能。
[PaddleSlim](https://github.com/PaddlePaddle/PaddleSlim) 集成了模型剪枝、量化(包括量化训练和离线量化)、蒸馏和神经网络搜索等多种业界常用且领先的模型压缩功能,如果您感兴趣,可以关注并了解。
本教程将介绍如何使用飞桨模型压缩库 PaddleSlim 做 PaddleClas 模型的压缩,即裁剪、量化功能。
[PaddleSlim](https://github.com/PaddlePaddle/PaddleSlim)集成了模型剪枝、量化(包括量化训练和离线量化)、蒸馏和神经网络搜索等多种业界常用且领先的模型压缩功能,如果您感兴趣,可以关注并了解。
在开始本教程之前,建议先了解[PaddleClas模型的训练方法](../models_training/classification.md)以及[PaddleSlim](https://paddleslim.readthedocs.io/zh_CN/latest/index.html),相关裁剪、量化方法可以参考[模型裁剪量化算法介绍文档](../algorithm_introduction/model_prune_quantization.md)
在开始本教程之前,建议先了解 [PaddleClas 模型的训练方法](../models_training/classification.md)以及 [PaddleSlim](https://paddleslim.readthedocs.io/zh_CN/latest/index.html),相关裁剪、量化方法可以参考[模型裁剪量化算法介绍文档](../algorithm_introduction/model_prune_quantization.md)
-----------
## 目录
- [准备环境](#1)
- [1.1 安装PaddleSlim](#1.1)
- [1. 准备环境](#1)
- [1.1 安装 PaddleSlim](#1.1)
- [1.2 准备训练好的模型](#1.2)
- [快速开始](#2)
- [2. 快速开始](#2)
- [2.1 模型量化](#2.1)
- [2.1.1 在线量化训练](#2.1.1)
- [2.1.2 离线量化](#2.1.2)
- [2.1.1 在线量化训练](#2.1.1)
- [2.1.2 离线量化](#2.1.2)
- [2.2 模型剪枝](#2.2)
- [导出模型](#3)
- [模型部署](#4)
- [训练超参数建议](#5)
- [3. 导出模型](#3)
- [4. 模型部署](#4)
- [5. 训练超参数建议](#5)
<a name="1"></a>
## 一、准备环境
## 1. 准备环境
当训练出一个模型后,如果希望进一步的压缩模型大小并加速预测,可使用量化或者剪枝的方法压缩模型。
模型压缩主要包括五个步骤:
......@@ -40,15 +42,15 @@
5. 量化模型预测部署
<a name="1.1"></a>
### 1.1 安装PaddleSlim
### 1.1 安装 PaddleSlim
* 可以通过pip install的方式进行安装。
* 可以通过 pip install 的方式进行安装。
```bash
pip install paddleslim -i https://pypi.tuna.tsinghua.edu.cn/simple
```
* 如果获取PaddleSlim的最新特性,可以从源码安装。
* 如果获取 PaddleSlim 的最新特性,可以从源码安装。
```bash
git clone https://github.com/PaddlePaddle/PaddleSlim.git
......@@ -59,18 +61,18 @@ python3.7 setup.py install
<a name="1.2"></a>
### 1.2 准备训练好的模型
PaddleClas提供了一系列训练好的[模型](../models/models_intro.md),如果待量化的模型不在列表中,需要按照[常规训练](../models_training/classification.md)方法得到训练好的模型。
PaddleClas 提供了一系列训练好的[模型](../models/models_intro.md),如果待量化的模型不在列表中,需要按照[常规训练](../models_training/classification.md)方法得到训练好的模型。
<a name="2"></a>
## 二、 快速开始
## 2. 快速开始
进入PaddleClas根目录
进入 PaddleClas 根目录
```bash
cd PaddleClas
```
`slim`训练相关代码已经集成到`ppcls/engine/`下,离线量化代码位于`deploy/slim/quant_post_static.py`
`slim` 训练相关代码已经集成到 `ppcls/engine/` 下,离线量化代码位于 `deploy/slim/quant_post_static.py`
<a name="2.1"></a>
### 2.1 模型量化
......@@ -82,15 +84,15 @@ cd PaddleClas
训练指令如下:
* CPU/单卡GPU
* CPU/单卡 GPU
CPU为例,若使用GPU,则将命令中改成`cpu`改成`gpu`
CPU 为例,若使用 GPU,则将命令中改成 `cpu` 改成 `gpu`
```bash
python3.7 tools/train.py -c ppcls/configs/slim/ResNet50_vd_quantization.yaml -o Global.device=cpu
```
其中`yaml`文件解析详见[参考文档](../models_training/config_description.md)。为了保证精度,`yaml`文件中已经使用`pretrained model`.
其中 `yaml` 文件解析详见[参考文档](../models_training/config_description.md)。为了保证精度,`yaml` 文件中已经使用 `pretrained model`.
* 单机多卡/多机多卡启动
......@@ -106,28 +108,28 @@ python3.7 -m paddle.distributed.launch \
<a name="2.1.2"></a>
#### 2.1.2 离线量化
**注意**:目前离线量化,必须使用已经训练好的模型,导出的`inference model`进行量化。一般模型导出`inference model`可参考[教程](../inference_deployment/export_model.md).
**注意**:目前离线量化,必须使用已经训练好的模型,导出的 `inference model` 进行量化。一般模型导出 `inference model` 可参考[教程](../inference_deployment/export_model.md).
一般来说,离线量化损失模型精度较多。
生成`inference model`后,离线量化运行方式如下
生成 `inference model` 后,离线量化运行方式如下
```bash
python3.7 deploy/slim/quant_post_static.py -c ppcls/configs/ImageNet/ResNet/ResNet50_vd.yaml -o Global.save_inference_dir=./deploy/models/class_ResNet50_vd_ImageNet_infer
```
`Global.save_inference_dir``inference model`存放的目录。
`Global.save_inference_dir``inference model` 存放的目录。
执行成功后,在`Global.save_inference_dir`的目录下,生成`quant_post_static_model`文件夹,其中存储生成的离线量化模型,其可以直接进行预测部署,无需再重新导出模型。
执行成功后,在 `Global.save_inference_dir` 的目录下,生成 `quant_post_static_model` 文件夹,其中存储生成的离线量化模型,其可以直接进行预测部署,无需再重新导出模型。
<a name="2.2"></a>
### 2.2 模型剪枝
训练指令如下:
- CPU/单卡GPU
- CPU/单卡 GPU
CPU为例,若使用GPU,则将命令中改成`cpu`改成`gpu`
CPU 为例,若使用 GPU,则将命令中改成 `cpu` 改成 `gpu`
```bash
python3.7 tools/train.py -c ppcls/configs/slim/ResNet50_vd_prune.yaml -o Global.device=cpu
......@@ -144,9 +146,9 @@ python3.7 -m paddle.distributed.launch \
```
<a name="3"></a>
## 三、导出模型
## 3. 导出模型
在得到在线量化训练、模型剪枝保存的模型后,可以将其导出为inference model,用于预测部署,以模型剪枝为例:
在得到在线量化训练、模型剪枝保存的模型后,可以将其导出为 inference model,用于预测部署,以模型剪枝为例:
```bash
python3.7 tools/export.py \
......@@ -156,16 +158,16 @@ python3.7 tools/export.py \
```
<a name="4"></a>
## 四、模型部署
## 4. 模型部署
上述步骤导出的模型可以直接使用inferecne进行部署,参考[inference部署](../inference_deployment/)
上述步骤导出的模型可以直接使用 inferecne 进行部署,参考 [inference 部署](../inference_deployment/)
也通过PaddleLite的opt模型转换工具,完成inference模型到移动端模型转换,用于移动端的模型部署。
也通过 PaddleLite 的 opt 模型转换工具,完成 inference 模型到移动端模型转换,用于移动端的模型部署。
移动端模型部署的可参考 [移动端模型部署](../inference_deployment/paddle_lite_deploy.md)
<a name="5"></a>
## 五、训练超参数建议
## 5. 训练超参数建议
* 量化、裁剪训练时,建议加载常规训练得到的预训练模型,加速量化训练收敛。
* 量化训练时,建议初始学习率修改为常规训练的`1/20~1/10`,同时将训练epoch数修改为常规训练的`1/5~1/2`,学习率策略方面,加上Warmup,其他配置信息不建议修改。
* 量化训练时,建议初始学习率修改为常规训练的 `1/20~1/10`,同时将训练 epoch 数修改为常规训练的 `1/5~1/2`,学习率策略方面,加上 Warmup,其他配置信息不建议修改。
* 裁剪训练时,建议超参数配置与普通训练一致。
# 一、数据增强
在图像分类任务中,图像数据的增广是一种常用的正则化方法,常用于数据量不足或者模型参数较多的场景。在本章节中,我们将对除 ImageNet 分类任务标准数据增强外的8种数据增强方式进行简单的介绍和对比,用户也可以将这些增广方法应用到自己的任务中,以获得模型精度的提升。这8种数据增强方式在ImageNet上的精度指标如下所示。
## 目录
- [1. 数据增强](#1)
- [2. 常用数据增强方法](#2)
- [3. 图像变换类](#3)
- [3.1 AutoAugment](#3.1)
- [3.2 RandAugment](#3.2)
- [3.3 TimmAutoAugment](#3.3)
- [4. 图像裁剪类](#4)
- [4.1 Cutout](#4.1)
- [4.2 RandomErasing](#4.2)
- [4.3 HideAndSeek](#4.3)
- [4.4 GridMask](#4.4)
- [5. 图像混叠](#5)
- [5.1 Mixup](#5.1)
- [5.2 Cutmix](#5.2)
<a name="1"></a>
## 1. 数据增强
在图像分类任务中,图像数据的增广是一种常用的正则化方法,常用于数据量不足或者模型参数较多的场景。在本章节中,我们将对除 ImageNet 分类任务标准数据增强外的 8 种数据增强方式进行简单的介绍和对比,用户也可以将这些增广方法应用到自己的任务中,以获得模型精度的提升。这 8 种数据增强方式在 ImageNet 上的精度指标如下所示。
![](../../images/image_aug/main_image_aug.png)
# 二、常用数据增强方法
<a name="2"></a>
## 2. 常用数据增强方法
如果没有特殊说明,本章节中所有示例为 ImageNet 分类,并且假设最终输入网络的数据维度为:`[batch-size, 3, 224, 224]`
......@@ -20,7 +38,7 @@
相比于上述标准的图像增广方法,研究者也提出了很多改进的图像增广策略,这些策略均是在标准增广方法的不同阶段插入一定的操作,基于这些策略操作所处的不同阶段,我们将其分为了三类:
1.`RandCrop` 后的 224 的图像进行一些变换: AutoAugment,RandAugment
2.`Transpose` 后的 224 的图像进行一些裁剪: CutOut,RandErasing,HideAndSeek,GridMask
2. `Transpose` 后的 224 的图像进行一些裁剪: CutOut,RandErasing,HideAndSeek,GridMask
3.`Batch` 后的数据进行混合: Mixup,Cutmix
增广后的可视化效果如下所示。
......@@ -43,35 +61,35 @@
| **Process** | (N, 3, 224, 224)<br>float32 | (N, 3, 224, 224)<br>float32 | \- | \- | \- | \- | \- | \- | Y | Y |
PaddleClas中集成了上述所有的数据增强策略,每种数据增强策略的参考论文与参考开源代码均在下面的介绍中列出。下文将介绍这些策略的原理与使用方法,并以下图为例,对变换后的效果进行可视化。为了说明问题,本章节中将 `RandCrop` 替换为 `Resize`
PaddleClas 中集成了上述所有的数据增强策略,每种数据增强策略的参考论文与参考开源代码均在下面的介绍中列出。下文将介绍这些策略的原理与使用方法,并以下图为例,对变换后的效果进行可视化。为了说明问题,本章节中将 `RandCrop` 替换为 `Resize`
![][test_baseline]
# 三、图像变换类
<a name="3"></a>
## 3. 图像变换类
图像变换类指的是对 `RandCrop` 后的 224 的图像进行一些变换,主要包括
+ AutoAugment
+ RandAugment
+ TimmAutoAugment
## 3.1 AutoAugment
<a name="3.1"></a>
### 3.1 AutoAugment
论文地址:[https://arxiv.org/abs/1805.09501v1](https://arxiv.org/abs/1805.09501v1)
开源代码github地址:[https://github.com/DeepVoltaire/AutoAugment](https://github.com/DeepVoltaire/AutoAugment)
开源代码 github 地址:[https://github.com/DeepVoltaire/AutoAugment](https://github.com/DeepVoltaire/AutoAugment)
不同于常规的人工设计图像增广方式,AutoAugment 是在一系列图像增广子策略的搜索空间中通过搜索算法找到的适合特定数据集的图像增广方案。针对 ImageNet 数据集,最终搜索出来的数据增强方案包含 25 个子策略组合,每个子策略中都包含两种变换,针对每幅图像都随机的挑选一个子策略组合,然后以一定的概率来决定是否执行子策略中的每种变换。
经过AutoAugment数据增强后结果如下图所示。
经过 AutoAugment 数据增强后结果如下图所示。
![][test_autoaugment]
## 3.2 RandAugment
<a name="3.2"></a>
### 3.2 RandAugment
论文地址:[https://arxiv.org/pdf/1909.13719.pdf](https://arxiv.org/pdf/1909.13719.pdf)
开源代码github地址:[https://github.com/heartInsert/randaugment](https://github.com/heartInsert/randaugment)
开源代码 github 地址:[https://github.com/heartInsert/randaugment](https://github.com/heartInsert/randaugment)
`AutoAugment` 的搜索方法比较暴力,直接在数据集上搜索针对该数据集的最优策略,其计算量很大。在 `RandAugment` 文章中作者发现,一方面,针对越大的模型,越大的数据集,使用 `AutoAugment` 方式搜索到的增广方式产生的收益也就越小;另一方面,这种搜索出的最优策略是针对该数据集的,其迁移能力较差,并不太适合迁移到其他数据集上。
......@@ -79,84 +97,84 @@ PaddleClas中集成了上述所有的数据增强策略,每种数据增强策
`RandAugment` 中,作者提出了一种随机增广的方式,不再像 `AutoAugment` 中那样使用特定的概率确定是否使用某种子策略,而是所有的子策略都会以同样的概率被选择到,论文中的实验也表明这种数据增强方式即使在大模型的训练中也具有很好的效果。
经过RandAugment数据增强后结果如下图所示。
经过 RandAugment 数据增强后结果如下图所示。
![][test_randaugment]
<a name="3.3"></a>
### 3.3 TimmAutoAugment
## 3.3 TimmAutoAugment
开源代码github地址:[https://github.com/rwightman/pytorch-image-models/blob/master/timm/data/auto_augment.py](https://github.com/rwightman/pytorch-image-models/blob/master/timm/data/auto_augment.py)
`TimmAutoAugment`是开源作者对AutoAugment和RandAugment的改进,事实证明,其在很多视觉任务上有更好的表现,目前绝大多数VisionTransformer模型都是基于TimmAutoAugment去实现的。
开源代码 github 地址:[https://github.com/rwightman/pytorch-image-models/blob/master/timm/data/auto_augment.py](https://github.com/rwightman/pytorch-image-models/blob/master/timm/data/auto_augment.py)
`TimmAutoAugment` 是开源作者对 AutoAugment 和 RandAugment 的改进,事实证明,其在很多视觉任务上有更好的表现,目前绝大多数 VisionTransformer 模型都是基于 TimmAutoAugment 去实现的。
# 四、图像裁剪类
<a name="4"></a>
## 4. 图像裁剪类
图像裁剪类主要是对`Transpose` 后的 224 的图像进行一些裁剪,并将裁剪区域的像素值置为特定的常数(默认为0),主要包括:
图像裁剪类主要是对 `Transpose` 后的 224 的图像进行一些裁剪,并将裁剪区域的像素值置为特定的常数(默认为 0),主要包括:
+ CutOut
+ RandErasing
+ HideAndSeek
+ GridMask
图像裁剪的这些增广并非一定要放在归一化之后,也有不少实现是放在归一化之前的,也就是直接对 uint8 的图像进行操作,两种方式的差别是:如果直接对 uint8 的图像进行操作,那么再经过归一化之后被裁剪的区域将不再是纯黑或纯白(减均值除方差之后像素值不为0)。而对归一后之后的数据进行操作,裁剪的区域会是纯黑或纯白。
图像裁剪的这些增广并非一定要放在归一化之后,也有不少实现是放在归一化之前的,也就是直接对 uint8 的图像进行操作,两种方式的差别是:如果直接对 uint8 的图像进行操作,那么再经过归一化之后被裁剪的区域将不再是纯黑或纯白(减均值除方差之后像素值不为 0)。而对归一后之后的数据进行操作,裁剪的区域会是纯黑或纯白。
上述的裁剪变换思路是相同的,都是为了解决训练出的模型在有遮挡数据上泛化能力较差的问题,不同的是他们的裁剪方式、区域不太一样。
## 4.1 Cutout
<a name="4.1"></a>
### 4.1 Cutout
论文地址:[https://arxiv.org/abs/1708.04552](https://arxiv.org/abs/1708.04552)
开源代码github地址:[https://github.com/uoguelph-mlrg/Cutout](https://github.com/uoguelph-mlrg/Cutout)
开源代码 github 地址:[https://github.com/uoguelph-mlrg/Cutout](https://github.com/uoguelph-mlrg/Cutout)
Cutout 可以理解为 Dropout 的一种扩展操作,不同的是 Dropout 是对图像经过网络后生成的特征进行遮挡,而 Cutout 是直接对输入的图像进行遮挡,相对于Dropout对噪声的鲁棒性更好。作者在论文中也进行了说明,这样做法有以下两点优势:(1) 通过 Cutout 可以模拟真实场景中主体被部分遮挡时的分类场景;(2) 可以促进模型充分利用图像中更多的内容来进行分类,防止网络只关注显著性的图像区域,从而发生过拟合。
Cutout 可以理解为 Dropout 的一种扩展操作,不同的是 Dropout 是对图像经过网络后生成的特征进行遮挡,而 Cutout 是直接对输入的图像进行遮挡,相对于 Dropout 对噪声的鲁棒性更好。作者在论文中也进行了说明,这样做法有以下两点优势:(1)通过 Cutout 可以模拟真实场景中主体被部分遮挡时的分类场景;(2)可以促进模型充分利用图像中更多的内容来进行分类,防止网络只关注显著性的图像区域,从而发生过拟合。
经过RandAugment数据增强后结果如下图所示。
经过 RandAugment 数据增强后结果如下图所示。
![][test_cutout]
## 4.2 RandomErasing
<a name="4.2"></a>
### 4.2 RandomErasing
论文地址:[https://arxiv.org/pdf/1708.04896.pdf](https://arxiv.org/pdf/1708.04896.pdf)
开源代码github地址:[https://github.com/zhunzhong07/Random-Erasing](https://github.com/zhunzhong07/Random-Erasing)
开源代码 github 地址:[https://github.com/zhunzhong07/Random-Erasing](https://github.com/zhunzhong07/Random-Erasing)
`RandomErasing``Cutout` 方法类似,同样是为了解决训练出的模型在有遮挡数据上泛化能力较差的问题,作者在论文中也指出,随机裁剪的方式与随机水平翻转具有一定的互补性。作者也在行人再识别(REID)上验证了该方法的有效性。与`Cutout`不同的是,在`RandomErasing`中,图片以一定的概率接受该种预处理方法,生成掩码的尺寸大小与长宽比也是根据预设的超参数随机生成。
`RandomErasing``Cutout` 方法类似,同样是为了解决训练出的模型在有遮挡数据上泛化能力较差的问题,作者在论文中也指出,随机裁剪的方式与随机水平翻转具有一定的互补性。作者也在行人再识别(REID)上验证了该方法的有效性。与 `Cutout` 不同的是,在 `RandomErasing` 中,图片以一定的概率接受该种预处理方法,生成掩码的尺寸大小与长宽比也是根据预设的超参数随机生成。
PaddleClas`RandomErasing`的使用方法如下所示。
PaddleClas`RandomErasing` 的使用方法如下所示。
经过RandomErasing数据增强后结果如下图所示。
经过 RandomErasing 数据增强后结果如下图所示。
![][test_randomerassing]
## 4.3 HideAndSeek
<a name="4.3"></a>
### 4.3 HideAndSeek
论文地址:[https://arxiv.org/pdf/1811.02545.pdf](https://arxiv.org/pdf/1811.02545.pdf)
开源代码github地址:[https://github.com/kkanshul/Hide-and-Seek](https://github.com/kkanshul/Hide-and-Seek)
开源代码 github 地址:[https://github.com/kkanshul/Hide-and-Seek](https://github.com/kkanshul/Hide-and-Seek)
`HideAndSeek`论文将图像分为若干块区域(patch),对于每块区域,都以一定的概率生成掩码,不同区域的掩码含义如下图所示。
`HideAndSeek` 论文将图像分为若干块区域(patch),对于每块区域,都以一定的概率生成掩码,不同区域的掩码含义如下图所示。
![][hide_and_seek_mask_expanation]
PaddleClas`HideAndSeek`的使用方法如下所示。
PaddleClas`HideAndSeek` 的使用方法如下所示。
经过HideAndSeek数据增强后结果如下图所示。
经过 HideAndSeek 数据增强后结果如下图所示。
![][test_hideandseek]
## 4.4 GridMask
<a name="4.4"></a>
### 4.4 GridMask
论文地址:[https://arxiv.org/abs/2001.04086](https://arxiv.org/abs/2001.04086)
开源代码github地址:[https://github.com/akuxcw/GridMask](https://github.com/akuxcw/GridMask)
开源代码 github 地址:[https://github.com/akuxcw/GridMask](https://github.com/akuxcw/GridMask)
作者在论文中指出,此前存在的基于对图像 crop 的方法存在两个问题,如下图所示:
......@@ -169,21 +187,21 @@ PaddleClas中`HideAndSeek`的使用方法如下所示。
因此如果避免过度删除或过度保留成为需要解决的核心问题。
`GridMask`是通过生成一个与原图分辨率相同的掩码,并将掩码进行随机翻转,与原图相乘,从而得到增广后的图像,通过超参数控制生成的掩码网格的大小。
`GridMask` 是通过生成一个与原图分辨率相同的掩码,并将掩码进行随机翻转,与原图相乘,从而得到增广后的图像,通过超参数控制生成的掩码网格的大小。
在训练过程中,有两种以下使用方法:
1. 设置一个概率p,从训练开始就对图片以概率p使用`GridMask`进行增广。
2. 一开始设置增广概率为0,随着迭代轮数增加,对训练图片进行`GridMask`增广的概率逐渐增大,最后变为p。
1. 设置一个概率 p,从训练开始就对图片以概率 p 使用 `GridMask` 进行增广。
2. 一开始设置增广概率为 0,随着迭代轮数增加,对训练图片进行 `GridMask` 增广的概率逐渐增大,最后变为 p。
论文中验证上述第二种方法的训练效果更好一些。
经过GridMask数据增强后结果如下图所示。
经过 GridMask 数据增强后结果如下图所示。
![][test_gridmask]
# 五、图像混叠
<a name="5"></a>
## 5. 图像混叠
图像混叠主要对 `Batch` 后的数据进行混合,包括:
......@@ -191,38 +209,38 @@ PaddleClas中`HideAndSeek`的使用方法如下所示。
+ Cutmix
前文所述的图像变换与图像裁剪都是针对单幅图像进行的操作,而图像混叠是对两幅图像进行融合,生成一幅图像,两种方法的主要区别为混叠的方式不太一样。
## 5.1 Mixup
<a name="5.1"></a>
### 5.1 Mixup
论文地址:[https://arxiv.org/pdf/1710.09412.pdf](https://arxiv.org/pdf/1710.09412.pdf)
开源代码github地址:[https://github.com/facebookresearch/mixup-cifar10](https://github.com/facebookresearch/mixup-cifar10)
开源代码 github 地址:[https://github.com/facebookresearch/mixup-cifar10](https://github.com/facebookresearch/mixup-cifar10)
Mixup 是最先提出的图像混叠增广方案,其原理简单、方便实现,不仅在图像分类上,在目标检测上也取得了不错的效果。为了便于实现,通常只对一个 batch 内的数据进行混叠,在 `Cutmix` 中也是如此。
如下是 `imaug` 中的实现,需要指出的是,下述实现会出现对同一幅进行相加的情况,也就是最终得到的图和原图一样,随着 `batch-size` 的增加这种情况出现的概率也会逐渐减小。
经过Mixup数据增强结果如下图所示。
经过 Mixup 数据增强结果如下图所示。
![][test_mixup]
## 5.2 Cutmix
<a name="5.2"></a>
### 5.2 Cutmix
论文地址:[https://arxiv.org/pdf/1905.04899v2.pdf](https://arxiv.org/pdf/1905.04899v2.pdf)
开源代码github地址:[https://github.com/clovaai/CutMix-PyTorch](https://github.com/clovaai/CutMix-PyTorch)
开源代码 github 地址:[https://github.com/clovaai/CutMix-PyTorch](https://github.com/clovaai/CutMix-PyTorch)
`Mixup` 直接对两幅图进行相加不一样,`Cutmix` 是从一幅图中随机裁剪出一个 `ROI`,然后覆盖当前图像中对应的区域,代码实现如下所示:
`Mixup` 直接对两幅图进行相加不一样,`Cutmix` 是从一幅图中随机裁剪出一个 `ROI`,然后覆盖当前图像中对应的区域,代码实现如下所示:
经过Cutmix数据增强后结果如下图所示。
经过 Cutmix 数据增强后结果如下图所示。
![][test_cutmix]
关于数据增强相关的实战部分实参考[数据增强实战](../advanced_tutorials/DataAugmentation.md)
# 参考文献
## 参考文献
[1] Cubuk E D, Zoph B, Mane D, et al. Autoaugment: Learning augmentation strategies from data[C]//Proceedings of the IEEE conference on computer vision and pattern recognition. 2019: 113-123.
......
# 图像分类
---
------
## 目录
- [数据集介绍](#1)
- [1.1 ImageNet-1k](#1.1)
- [1.2 CIFAR-10/CIFAR-100](#1.2)
- [图像分类的流程](#2)
- [2.1 数据及其预处理](#2.1)
- [2.2 模型准备](#2.2)
- [2.3 模型训练](#2.3)
- [2.4 模型评估](#2.4)
- [主要算法简介](#3)
- [1. 数据集介绍](#1)
- [1.1 ImageNet-1k](#1.1)
- [1.2 CIFAR-10/CIFAR-100](#1.2)
- [2. 图像分类的流程](#2)
- [2.1 数据及其预处理](#2.1)
- [2.2 模型准备](#2.2)
- [2.3 模型训练](#2.3)
- [2.4 模型评估](#2.4)
- [3. 主要算法简介](#3)
图像分类是根据图像的语义信息将不同类别图像区分开来,是计算机视觉中重要的基本问题,也是图像检测、图像分割、物体跟踪、行为分析等其他高层视觉任务的基础。图像分类在很多领域有广泛应用,包括安防领域的人脸识别和智能视频分析等,交通领域的交通场景识别,互联网领域基于内容的图像检索和相册自动归类,医学领域的图像识别等。
一般来说,图像分类通过手工特征或特征学习方法对整个图像进行全部描述,然后使用分类器判别物体类别,因此如何提取图像的特征至关重要。在深度学习算法之前使用较多的是基于词袋(Bag of Words)模型的物体分类方法。而基于深度学习的图像分类方法,可以通过有监督或无监督的方式学习层次化的特征描述,从而取代了手工设计或选择图像特征的工作。深度学习模型中的卷积神经网络(Convolution Neural Network, CNN)近年来在图像领域取得了惊人的成绩,CNN直接利用图像像素信息作为输入,最大程度上保留了输入图像的所有信息,通过卷积操作进行特征的提取和高层抽象,模型输出直接是图像识别的结果。这种基于"输入-输出"直接端到端的学习方法取得了非常好的效果,得到了广泛的应用。
一般来说,图像分类通过手工特征或特征学习方法对整个图像进行全部描述,然后使用分类器判别物体类别,因此如何提取图像的特征至关重要。在深度学习算法之前使用较多的是基于词袋(Bag of Words)模型的物体分类方法。而基于深度学习的图像分类方法,可以通过有监督或无监督的方式学习层次化的特征描述,从而取代了手工设计或选择图像特征的工作。深度学习模型中的卷积神经网络(Convolution Neural Network, CNN)近年来在图像领域取得了惊人的成绩,CNN 直接利用图像像素信息作为输入,最大程度上保留了输入图像的所有信息,通过卷积操作进行特征的提取和高层抽象,模型输出直接是图像识别的结果。这种基于"输入-输出"直接端到端的学习方法取得了非常好的效果,得到了广泛的应用。
图像分类是计算机视觉里很基础但又重要的一个领域,其研究成果一直影响着计算机视觉甚至深度学习的发展,图像分类有很多子领域,如多标签分类、细粒度分类等,此处只对单标签图像分类做一个简述。
<a name="1"></a>
## 一、数据集介绍
## 1. 数据集介绍
<a name="1.1"></a>
### 1.1 ImageNet-1k
ImageNet项目是一个大型视觉数据库,用于视觉目标识别软件研究。该项目已手动注释了1400多万张图像,以指出图片中的对象,并在至少100万张图像中提供了边框。ImageNet-1k是ImageNet数据集的子集,其包含1000个类别。训练集包含1281167个图像数据,验证集包含50000个图像数据。2010年以来,ImageNet项目每年举办一次图像分类竞赛,即ImageNet大规模视觉识别挑战赛(ILSVRC)。挑战赛使用的数据集即为ImageNet-1k。到目前为止,ImageNet-1k已经成为计算机视觉领域发展的最重要的数据集之一,其促进了整个计算机视觉的发展,很多计算机视觉下游任务的初始化模型都是基于该数据集训练得到的权重。
ImageNet 项目是一个大型视觉数据库,用于视觉目标识别软件研究。该项目已手动注释了 1400 多万张图像,以指出图片中的对象,并在至少 100 万张图像中提供了边框。ImageNet-1k 是 ImageNet 数据集的子集,其包含 1000 个类别。训练集包含 1281167 个图像数据,验证集包含 50000 个图像数据。2010 年以来,ImageNet 项目每年举办一次图像分类竞赛,即 ImageNet 大规模视觉识别挑战赛(ILSVRC)。挑战赛使用的数据集即为 ImageNet-1k。到目前为止,ImageNet-1k 已经成为计算机视觉领域发展的最重要的数据集之一,其促进了整个计算机视觉的发展,很多计算机视觉下游任务的初始化模型都是基于该数据集训练得到的权重。
<a name="1.2"></a>
### 1.2 CIFAR-10/CIFAR-100
CIFAR-10数据集由10个类的60000个彩色图像组成,图像分辨率为32x32,每个类有6000个图像,其中训练集5000张,验证集1000张,10个不同的类代表飞机、汽车、鸟类、猫、鹿、狗、青蛙、马、轮船和卡车。CIFAR-100数据集是CIFAR-10的扩展,由100个类的60000个彩色图像组成,图像分辨率为32x32,每个类有600个图像,其中训练集500张,验证集100张。由于这两个数据集规模较小,因此可以让研究人员快速尝试不同的算法。这两个数据集也是图像分类领域测试模型好坏的常用数据集。
CIFAR-10 数据集由 10 个类的 60000 个彩色图像组成,图像分辨率为 32x32,每个类有 6000 个图像,其中训练集 5000 张,验证集 1000 张,10 个不同的类代表飞机、汽车、鸟类、猫、鹿、狗、青蛙、马、轮船和卡车。CIFAR-100 数据集是 CIFAR-10 的扩展,由 100 个类的 60000 个彩色图像组成,图像分辨率为 32x32,每个类有 600 个图像,其中训练集 500 张,验证集 100 张。由于这两个数据集规模较小,因此可以让研究人员快速尝试不同的算法。这两个数据集也是图像分类领域测试模型好坏的常用数据集。
<a name="2"></a>
## 二、图像分类的流程
## 2. 图像分类的流程
将准备好的训练数据做相应的数据预处理后经过图像分类模型,模型的输出与真实标签做交叉熵损失函数,该损失函数描述了模型的收敛方向,遍历所有的图片数据输入模型,对最终损失函数通过某些优化器做相应的梯度下降,将梯度信息回传到模型中,更新模型的权重,如此循环往复遍历多次数据,即可得到一个图像分类的模型。
<a name="2.1"></a>
### 2.1 数据及其预处理
数据的质量及数量往往可以决定一个模型的好坏。在图像分类领域,数据包括图像及标签。在大部分情形下,带有标签的数据比较匮乏,所以数量很难达到使模型饱和的程度,为了可以使模型学习更多的图像特征,图像数据在进入模型之前要经过很多图像变换或者数据增强,来保证输入图像数据的多样性,从而保证模型有更好的泛化能力。PaddleClas提供了训练ImageNet-1k的标准图像变换,也提供了8中数据增强的方法,相关代码可以[数据处理](../../../ppcls/data/preprocess),配置文件可以参考[数据增强配置文件](../../../ppcls/configs/ImageNet/DataAugment)
数据的质量及数量往往可以决定一个模型的好坏。在图像分类领域,数据包括图像及标签。在大部分情形下,带有标签的数据比较匮乏,所以数量很难达到使模型饱和的程度,为了可以使模型学习更多的图像特征,图像数据在进入模型之前要经过很多图像变换或者数据增强,来保证输入图像数据的多样性,从而保证模型有更好的泛化能力。PaddleClas 提供了训练 ImageNet-1k 的标准图像变换,也提供了 8 中数据增强的方法,相关代码可以[数据处理](../../../ppcls/data/preprocess),配置文件可以参考[数据增强配置文件](../../../ppcls/configs/ImageNet/DataAugment)
<a name="2.2"></a>
### 2.2 模型准备
在数据确定后,模型往往决定了最终算法精度的上限,在图像分类领域,经典的模型层出不穷,PaddleClas提供了36个系列共175个ImageNet预训练模型。具体的精度、速度等指标请参考[骨干网络和预训练模型库](./ImageNet_models.md)
在数据确定后,模型往往决定了最终算法精度的上限,在图像分类领域,经典的模型层出不穷,PaddleClas 提供了 36 个系列共 175 个 ImageNet 预训练模型。具体的精度、速度等指标请参考[骨干网络和预训练模型库](./ImageNet_models.md)
<a name="2.3"></a>
### 2.3 模型训练
在准备好数据、模型后,便可以开始迭代模型并更新模型的参数。经过多次迭代最终可以得到训练好的模型来做图像分类任务。图像分类的训练过程需要很多经验,涉及很多超参数的设置,PaddleClas提供了一些列的[训练调优方法](../models/Tricks.md),可以快速助你获得高精度的模型。
在准备好数据、模型后,便可以开始迭代模型并更新模型的参数。经过多次迭代最终可以得到训练好的模型来做图像分类任务。图像分类的训练过程需要很多经验,涉及很多超参数的设置,PaddleClas 提供了一些列的[训练调优方法](../models/Tricks.md),可以快速助你获得高精度的模型。
<a name="2.4"></a>
### 2.4 模型评估
当训练得到一个模型之后,如何确定模型的好坏,需要将模型在验证集上进行评估。评估指标一般是Top1-Acc或者Top5-Acc,该指标越高往往代表模型性能越好。
当训练得到一个模型之后,如何确定模型的好坏,需要将模型在验证集上进行评估。评估指标一般是 Top1-Acc 或者 Top5-Acc,该指标越高往往代表模型性能越好。
<a name="3"></a>
## 三、主要算法简介
## 3. 主要算法简介
- LeNet:Yan LeCun等人于上个世纪九十年代第一次将卷积神经网络应用到图像分类任务上,创造性的提出了LeNet,在手写数字识别任务上取得了巨大的成功。
- LeNet:Yan LeCun 等人于上个世纪九十年代第一次将卷积神经网络应用到图像分类任务上,创造性的提出了 LeNet,在手写数字识别任务上取得了巨大的成功。
- AlexNet:Alex Krizhevsky等人在2012年提出了AlexNet,并应用于ImageNet上,获得了2012年ImageNet分类比赛的冠军,从此,掀起了深度学习的热潮。
- AlexNet:Alex Krizhevsky 等人在 2012 年提出了 AlexNet,并应用于 ImageNet 上,获得了 2012 年 ImageNet 分类比赛的冠军,从此,掀起了深度学习的热潮。
- VGG:Simonyan和Zisserman于2014年提出了VGG网络结构,该网络结构使用了更小的卷积核堆叠整个网络,在ImageNet分类上取得了更好的性能,也为之后的网络结构设计提供了新的思路。
- VGG:Simonyan 和 Zisserman 于 2014 年提出了 VGG 网络结构,该网络结构使用了更小的卷积核堆叠整个网络,在 ImageNet 分类上取得了更好的性能,也为之后的网络结构设计提供了新的思路。
- GoogLeNet:Christian Szegedy等人在2014年提出了GoogLeNet,该网络使用了多分支结构和全局平均池化层(GAP),在保持模型精度的同时,模型存储和计算量大大缩减。该网络取得了2014年ImageNet分类比赛的冠军。
- GoogLeNet:Christian Szegedy 等人在 2014 年提出了 GoogLeNet,该网络使用了多分支结构和全局平均池化层(GAP),在保持模型精度的同时,模型存储和计算量大大缩减。该网络取得了 2014 年 ImageNet 分类比赛的冠军。
- ResNet:Kaiming He等人在2015年提出了ResNet,通过引入残差模块加深了网络的深度,最终该网络将ImageNet分类的识别错误率降低到了3.6\%,首次超出正常人眼的识别精度。
- ResNet:Kaiming He 等人在 2015 年提出了 ResNet,通过引入残差模块加深了网络的深度,最终该网络将 ImageNet 分类的识别错误率降低到了 3.6\%,首次超出正常人眼的识别精度。
- DenseNet:Huang Gao等人在2017年提出了DenseNet,该网络设计了一种更稠密连接的block,在更小的参数量上获得了更高的性能。
- DenseNet:Huang Gao 等人在 2017 年提出了 DenseNet,该网络设计了一种更稠密连接的 block,在更小的参数量上获得了更高的性能。
- EfficientNet:Mingxing Tan等人在2019年提出了EfficientNet,该网络平衡了网络的宽度、网络的深度、以及输入图片的分辨率,在同样FLOPS与参数量的情况下,精度达到了state-of-the-art的效果。
- EfficientNet:Mingxing Tan 等人在 2019 年提出了 EfficientNet,该网络平衡了网络的宽度、网络的深度、以及输入图片的分辨率,在同样 FLOPS 与参数量的情况下,精度达到了 state-of-the-art 的效果。
关于更多的算法介绍,请参考[算法介绍](../models)
# 知识蒸馏
---
## 目录
* [1. 模型压缩和知识蒸馏方法简介](#1)
* [2. 知识蒸馏应用](#2)
* [3. 知识蒸馏算法介绍](#3)
* [3.1 Response based distillation](#3.1)
* [3.2 Feature based distillation](#3.2)
* [3.3 Relation based distillation](#3.3)
* [4. 参考文献](#4)
<a name='1'></a>
## 1. 模型压缩和知识蒸馏方法简介
近年来,深度神经网络在计算机视觉、自然语言处理等领域被验证是一种极其有效的解决问题的方法。通过构建合适的神经网络,加以训练,最终网络模型的性能指标基本上都会超过传统算法。
......@@ -15,7 +24,7 @@
* Feature based distillation:教师模型对学生模型的中间层 feature map 进行监督。
* Relation based distillation:对于不同的样本,使用教师模型和学生模型同时计算样本之间 feature map 的相关性,使得学生模型和教师模型得到的相关性矩阵尽可能一致。
<a name='2'></a>
## 2. 知识蒸馏应用
......@@ -24,25 +33,25 @@
此外,对于相同的模型结构,使用知识蒸馏训练得到的预训练模型精度往往更高,这些预训练模型往往也可以提升下游任务的模型精度。比如在图像分类任务中,基于知识蒸馏算法得到的精度更高的预训练模型,也能够在目标检测、图像分割、OCR、视频分类等任务中获得明显的精度收益。
<a name='3'></a>
## 3. 知识蒸馏算法介绍
<a name='3.1'></a>
### 3.1 Response based distillation
最早的知识蒸馏算法KD,由Hinton提出,训练的损失函数中除了 gt loss 之外,还引入了学生模型与教师模型输出的 KL 散度,最终精度超过单纯使用 gt loss 训练的精度。这里需要注意的是,在训练的时候,需要首先训练得到一个更大的教师模型,来指导学生模型的训练过程。
最早的知识蒸馏算法 KD,由 Hinton 提出,训练的损失函数中除了 gt loss 之外,还引入了学生模型与教师模型输出的 KL 散度,最终精度超过单纯使用 gt loss 训练的精度。这里需要注意的是,在训练的时候,需要首先训练得到一个更大的教师模型,来指导学生模型的训练过程。
PaddleClas 中提出了一种简单使用的 SSLD 知识蒸馏算法 [6],在训练的时候去除了对 gt label 的依赖,结合大量无标注数据,最终蒸馏训练得到的预训练模型在 15 个模型上的精度提升平均高达 3%。
上述标准的蒸馏方法是通过一个大模型作为教师模型来指导学生模型提升效果,而后来又发展出 DML (Deep Mutual Learning) 互学习蒸馏方法 [7],即通过两个结构相同的模型互相学习。具体的。相比于 KD 等依赖于大的教师模型的知识蒸馏算法,DML 脱离了对大的教师模型的依赖,蒸馏训练的流程更加简单,模型产出效率也要更高一些。
上述标准的蒸馏方法是通过一个大模型作为教师模型来指导学生模型提升效果,而后来又发展出 DML(Deep Mutual Learning)互学习蒸馏方法 [7],即通过两个结构相同的模型互相学习。具体的。相比于 KD 等依赖于大的教师模型的知识蒸馏算法,DML 脱离了对大的教师模型的依赖,蒸馏训练的流程更加简单,模型产出效率也要更高一些。
<a name='3.2'></a>
### 3.2 Feature based distillation
Heo 等人提出了 OverHaul [8], 计算学生模型与教师模型的 feature map distance,作为蒸馏的 loss,在这里使用了学生模型、教师模型的转移,来保证二者的 feature map 可以正常地进行 distance 的计算。
基于 feature map distance 的知识蒸馏方法也能够和 `3.1章节` 中的基于 response 的知识蒸馏算法融合在一起,同时对学生模型的输出结果和中间层 feature map 进行监督。而对于 DML 方法来说,这种融合过程更为简单,因为不需要对学生和教师模型的 feature map 进行转换,便可以完成对齐 (alignment) 过程。PP-OCRv2 系统中便使用了这种方法,最终大幅提升了 OCR 文字识别模型的精度。
基于 feature map distance 的知识蒸馏方法也能够和 `3.1 章节` 中的基于 response 的知识蒸馏算法融合在一起,同时对学生模型的输出结果和中间层 feature map 进行监督。而对于 DML 方法来说,这种融合过程更为简单,因为不需要对学生和教师模型的 feature map 进行转换,便可以完成对齐(alignment)过程。PP-OCRv2 系统中便使用了这种方法,最终大幅提升了 OCR 文字识别模型的精度。
<a name='3.3'></a>
### 3.3 Relation based distillation

......@@ -51,9 +60,9 @@ Heo 等人提出了 OverHaul [8], 计算学生模型与教师模型的 feature m
Park 等人提出了 RKD [10],基于关系的知识蒸馏算法,RKD 中进一步考虑个体输出之间的关系,使用 2 种损失函数,二阶的距离损失(distance-wise)和三阶的角度损失(angle-wise)
本论文提出的算法关系知识蒸馏(RKD)迁移教师模型得到的输出结果间的结构化关系给学生模型,不同于之前的只关注个体输出结果,RKD 算法使用两种损失函数:二阶的距离损失 (distance-wise) 和三阶的角度损失 (angle-wise)。在最终计算蒸馏损失函数的时候,同时考虑KD loss 和 RKD loss。最终精度优于单独使用 KD loss 蒸馏得到的模型精度。
本论文提出的算法关系知识蒸馏(RKD)迁移教师模型得到的输出结果间的结构化关系给学生模型,不同于之前的只关注个体输出结果,RKD 算法使用两种损失函数:二阶的距离损失(distance-wise)和三阶的角度损失(angle-wise)。在最终计算蒸馏损失函数的时候,同时考虑 KD loss 和 RKD loss。最终精度优于单独使用 KD loss 蒸馏得到的模型精度。
<a name='4'></a>
## 4. 参考文献
[1] Hinton G, Vinyals O, Dean J. Distilling the knowledge in a neural network[J]. arXiv preprint arXiv:1503.02531, 2015.
......
# Metric Learning
----
## 目录
## 简介
在机器学习中,我们经常会遇到度量数据间距离的问题。一般来说,对于可度量的数据,我们可以直接通过欧式距离(Euclidean Distance),向量内积(Inner Product)或者是余弦相似度(Cosine Similarity)来进行计算。但对于非结构化数据来说,我们却很难进行这样的操作,如计算一段视频和一首音乐的匹配程度。由于数据格式的不同,我们难以直接进行上述的向量运算,但先验知识告诉我们ED(laugh_video, laugh_music) < ED(laugh_video, blue_music), 如何去有效得表征这种”距离”关系呢? 这就是Metric Learning所要研究的课题。
* [1. 简介](#1)
* [2. 应用](#2)
* [3. 算法](#3)
* [3.1 Classification based](#3.1)
* [3.2 Pairwise based](#3.2)
<a name='1'></a>
## 1. 简介
在机器学习中,我们经常会遇到度量数据间距离的问题。一般来说,对于可度量的数据,我们可以直接通过欧式距离(Euclidean Distance),向量内积(Inner Product)或者是余弦相似度(Cosine Similarity)来进行计算。但对于非结构化数据来说,我们却很难进行这样的操作,如计算一段视频和一首音乐的匹配程度。由于数据格式的不同,我们难以直接进行上述的向量运算,但先验知识告诉我们 ED(laugh_video, laugh_music) < ED(laugh_video, blue_music), 如何去有效得表征这种”距离”关系呢? 这就是 Metric Learning 所要研究的课题。
Metric learning全称是 Distance Metric Learning,它是通过机器学习的形式,根据训练数据,自动构造出一种基于特定任务的度量函数。Metric Learning的目标是学习一个变换函数(线性非线性均可)L,将数据点从原始的向量空间映射到一个新的向量空间,在新的向量空间里相似点的距离更近,非相似点的距离更远,使得度量更符合任务的要求,如下图所示。 Deep Metric Learning,就是用深度神经网络来拟合这个变换函数。
Metric learning 全称是 Distance Metric Learning,它是通过机器学习的形式,根据训练数据,自动构造出一种基于特定任务的度量函数。Metric Learning 的目标是学习一个变换函数(线性非线性均可)L,将数据点从原始的向量空间映射到一个新的向量空间,在新的向量空间里相似点的距离更近,非相似点的距离更远,使得度量更符合任务的要求,如下图所示。 Deep Metric Learning,就是用深度神经网络来拟合这个变换函数。
![example](../../images/ml_illustration.jpg)
## 应用
Metric Learning技术在生活实际中应用广泛,如我们耳熟能详的人脸识别(Face Recognition)、行人重识别(Person ReID)、图像检索(Image Retrieval)、细粒度分类(Fine-gained classification)等. 随着深度学习在工业实践中越来越广泛的应用,目前大家研究的方向基本都偏向于Deep Metric Learning(DML).
<a name='2'></a>
## 2. 应用
Metric Learning 技术在生活实际中应用广泛,如我们耳熟能详的人脸识别(Face Recognition)、行人重识别(Person ReID)、图像检索(Image Retrieval)、细粒度分类(Fine-gained classification)等。随着深度学习在工业实践中越来越广泛的应用,目前大家研究的方向基本都偏向于 Deep Metric Learning(DML).
一般来说, DML包含三个部分: 特征提取网络来map embedding, 一个采样策略来将一个mini-batch里的样本组合成很多个sub-set, 最后loss function在每个sub-set上计算loss. 如下图所示:
一般来说, DML 包含三个部分: 特征提取网络来 map embedding, 一个采样策略来将一个 mini-batch 里的样本组合成很多个 sub-set, 最后 loss function 在每个 sub-set 上计算 loss. 如下图所示:
![image](../../images/ml_pipeline.jpg)
## 算法
Metric Learning主要有如下两种学习范式:
### 1. Classification based:
这是一类基于分类标签的Metric Learning方法。这类方法通过将每个样本分类到正确的类别中,来学习有效的特征表示,学习过程中需要每个样本的显式标签参与Loss计算。常见的算法有[L2-Softmax](https://arxiv.org/abs/1703.09507), [Large-margin Softmax](https://arxiv.org/abs/1612.02295), [Angular Softmax](https://arxiv.org/pdf/1704.08063.pdf), [NormFace](https://arxiv.org/abs/1704.06369), [AM-Softmax](https://arxiv.org/abs/1801.05599), [CosFace](https://arxiv.org/abs/1801.09414), [ArcFace](https://arxiv.org/abs/1801.07698)等。
这类方法也被称作是proxy-based, 因为其本质上优化的是样本和一堆proxies之间的相似度。
### 2. Pairwise based:
<a name='3'></a>
## 3. 算法
Metric Learning 主要有如下两种学习范式:
<a name='3.1'></a>
### 3.1 Classification based:
这是一类基于分类标签的 Metric Learning 方法。这类方法通过将每个样本分类到正确的类别中,来学习有效的特征表示,学习过程中需要每个样本的显式标签参与 Loss 计算。常见的算法有 [L2-Softmax](https://arxiv.org/abs/1703.09507), [Large-margin Softmax](https://arxiv.org/abs/1612.02295), [Angular Softmax](https://arxiv.org/pdf/1704.08063.pdf), [NormFace](https://arxiv.org/abs/1704.06369), [AM-Softmax](https://arxiv.org/abs/1801.05599), [CosFace](https://arxiv.org/abs/1801.09414), [ArcFace](https://arxiv.org/abs/1801.07698)等。
这类方法也被称作是 proxy-based, 因为其本质上优化的是样本和一堆 proxies 之间的相似度。
<a name='3.2'></a>
### 3.2 Pairwise based:
这是一类基于样本对的学习范式。他以样本对作为输入,通过直接学习样本对之间的相似度来得到有效的特征表示,常见的算法包括:[Contrastive loss](http://yann.lecun.com/exdb/publis/pdf/hadsell-chopra-lecun-06.pdf), [Triplet loss](https://arxiv.org/abs/1503.03832), [Lifted-Structure loss](https://arxiv.org/abs/1511.06452), [N-pair loss](https://papers.nips.cc/paper/2016/file/6b180037abbebea991d8b1232f8a8ca9-Paper.pdf), [Multi-Similarity loss](https://arxiv.org/pdf/1904.06627.pdf)
2020年发表的[CircleLoss](https://arxiv.org/abs/2002.10857),从一个全新的视角统一了两种学习范式,让研究人员和从业者对Metric Learning问题有了更进一步的思考。
2020 年发表的[CircleLoss](https://arxiv.org/abs/2002.10857),从一个全新的视角统一了两种学习范式,让研究人员和从业者对 Metric Learning 问题有了更进一步的思考。
# 模型裁剪、量化算法介绍
深度学习因其计算复杂度或参数冗余,在一些场景和设备上限制了相应的模型部署,需要借助模型压缩、优化加速、异构计算等方法突破瓶颈。模型压缩算法能够有效降低参数冗余,从而减少存储占用、通信带宽和计算复杂度,有助于深度学习的应用部署。其中模型量化、裁剪应用比较广泛。在PaddleClas中,主要应该应用以下两种算法。
深度学习因其计算复杂度或参数冗余,在一些场景和设备上限制了相应的模型部署,需要借助模型压缩、优化加速、异构计算等方法突破瓶颈。模型压缩算法能够有效降低参数冗余,从而减少存储占用、通信带宽和计算复杂度,有助于深度学习的应用部署。其中模型量化、裁剪应用比较广泛。在 PaddleClas 中,主要应该应用以下两种算法。
- 量化方法:PACT量化
- 裁剪:FPGM裁剪
- 量化方法:PACT 量化
- 裁剪:FPGM 裁剪
其中具体算法参数请参考[PaddeSlim](https://github.com/PaddlePaddle/PaddleSlim/)
其中具体算法参数请参考 [PaddeSlim](https://github.com/PaddlePaddle/PaddleSlim/)
## PACT量化方法
## 目录
模型量化主要包括两个部分,一是对权重Weight量化,一是针对激活值Activation量化。同时对两部分进行量化,才能获得最大的计算效率收益。权重可以借助网络正则化等手段,让权重分布尽量紧凑,减少离群点、不均匀分布情况发生,而对于激活值还缺乏有效的手段。
* [1. PACT 量化方法](#1)
* [2. FPGM 裁剪](#2)
**PACT量化(PArameterized Clipping acTivation**是一种新的量化方法,该方法通过在量化激活值之前去掉一些离群点,将模型量化带来的精度损失降到最低,甚至比原模型准确率更高。提出方法的背景是作者发现:“在运用权重量化方案来量化activation时,激活值的量化结果和全精度结果相差较大”。作者发现,activation的量化可能引起的误差很大(相较于weight基本在 0到1范围内,activation的值的范围是无限大的,这是RELU的结果),所以提出**截断式RELU** 的激活函数。该截断的上界,即$α$ 是可学习的参数,这保证了每层能够通过训练学习到不一样的量化范围,最大程度降低量化带来的舍入误差。其中量化的示意图如下图所示,**PACT**解决问题的方法是,不断裁剪激活值范围,使得激活值分布收窄,从而降低量化映射损失。**PACT**通过对激活数值做裁剪,从而减少激活分布中的离群点,使量化模型能够得到一个更合理的量化scale,降低量化损失。
<a name='1'></a>
## 1. PACT 量化方法
模型量化主要包括两个部分,一是对权重 Weight 量化,一是针对激活值 Activation 量化。同时对两部分进行量化,才能获得最大的计算效率收益。权重可以借助网络正则化等手段,让权重分布尽量紧凑,减少离群点、不均匀分布情况发生,而对于激活值还缺乏有效的手段。
**PACT 量化(PArameterized Clipping acTivation**是一种新的量化方法,该方法通过在量化激活值之前去掉一些离群点,将模型量化带来的精度损失降到最低,甚至比原模型准确率更高。提出方法的背景是作者发现:“在运用权重量化方案来量化 activation 时,激活值的量化结果和全精度结果相差较大”。作者发现,activation 的量化可能引起的误差很大(相较于 weight 基本在 0 到 1 范围内,activation 的值的范围是无限大的,这是 RELU 的结果),所以提出**截断式 RELU** 的激活函数。该截断的上界,即$α$ 是可学习的参数,这保证了每层能够通过训练学习到不一样的量化范围,最大程度降低量化带来的舍入误差。其中量化的示意图如下图所示,**PACT** 解决问题的方法是,不断裁剪激活值范围,使得激活值分布收窄,从而降低量化映射损失。**PACT** 通过对激活数值做裁剪,从而减少激活分布中的离群点,使量化模型能够得到一个更合理的量化 scale,降低量化损失。
<div align="center">
<img src="../../images/algorithm_introduction/quantization.jpg" width = "600" />
</div>
**PACT**量化公式如下:
**PACT** 量化公式如下:
<div align="center">
<img src="../../images/algorithm_introduction/quantization_formula.png" width = "800" height="100"/>
......@@ -26,7 +32,7 @@
可以看出PACT思想是用上述量化代替*ReLU*函数,对大于零的部分进行一个截断操作,截断阈值为$a$。但是在*PaddleSlim*中对上述公式做了进一步的改进,其改进如下:
可以看出 PACT 思想是用上述量化代替 *ReLU* 函数,对大于零的部分进行一个截断操作,截断阈值为$a$。但是在*PaddleSlim*中对上述公式做了进一步的改进,其改进如下:
<div align="center">
<img src="../../images/algorithm_introduction/quantization_formula_slim.png" width = "550" height="120"/>
......@@ -34,18 +40,19 @@
经过如上改进后,在激活值和待量化的OP(卷积,全连接等)之间插入*PACT*预处理,不只对大于0的分布进行截断,同时也对小于0的部分做同样的限制,从而更好地得到待量化的范围,降低量化损失。同时,截断阈值是一个可训练的参数,在量化训练过程中,模型会自动的找到一个合理的截断阈值,从而进一步降低量化精度损失。
经过如上改进后,在激活值和待量化的 OP(卷积,全连接等)之间插入 *PACT* 预处理,不只对大于 0 的分布进行截断,同时也对小于 0 的部分做同样的限制,从而更好地得到待量化的范围,降低量化损失。同时,截断阈值是一个可训练的参数,在量化训练过程中,模型会自动的找到一个合理的截断阈值,从而进一步降低量化精度损失。
算法具体参数请参考PaddleSlim[参数介绍](https://github.com/PaddlePaddle/PaddleSlim/blob/release/2.0.0/docs/zh_cn/api_cn/dygraph/quanter/qat.rst#qat)
算法具体参数请参考 PaddleSlim [参数介绍](https://github.com/PaddlePaddle/PaddleSlim/blob/release/2.0.0/docs/zh_cn/api_cn/dygraph/quanter/qat.rst#qat)
## FPGM裁剪
<a name='2'></a>
## FPGM 裁剪
模型剪枝是减小模型大小,提升预测效率的一种非常重要的手段。在之前的网络剪枝文章中一般将网络filter的范数作为其重要性度量,**范数值较小的代表的filter越不重要**,将其从网络中裁剪掉,反之也就越重要。而**FPGM**认为之前的方法要依赖如下两点
模型剪枝是减小模型大小,提升预测效率的一种非常重要的手段。在之前的网络剪枝文章中一般将网络 filter 的范数作为其重要性度量,**范数值较小的代表的 filter 越不重要**,将其从网络中裁剪掉,反之也就越重要。而**FPGM**认为之前的方法要依赖如下两点
- filter的范数偏差应该比较大,这样重要和非重要的filter才可以很好区分开
- 不重要的filter的范数应该足够的小
- filter 的范数偏差应该比较大,这样重要和非重要的 filter 才可以很好区分开
- 不重要的 filter 的范数应该足够的小
基于此,**FPGM**利用层中filter的几何中心特性,由于那些靠近中心的filter可以被其它的表达,因而可以将其剔除,从而避免了上面提到的两点剪枝条件,从信息的冗余度出发,而不是选择范数少的进行剪枝。下图展示了**FPGM**方法与之前方法的不同,具体细节请详看[论文](https://openaccess.thecvf.com/content_CVPR_2019/papers/He_Filter_Pruning_via_Geometric_Median_for_Deep_Convolutional_Neural_Networks_CVPR_2019_paper.pdf)
基于此,**FPGM**利用层中 filter 的几何中心特性,由于那些靠近中心的 filter 可以被其它的表达,因而可以将其剔除,从而避免了上面提到的两点剪枝条件,从信息的冗余度出发,而不是选择范数少的进行剪枝。下图展示了 **FPGM** 方法与之前方法的不同,具体细节请详看[论文](https://openaccess.thecvf.com/content_CVPR_2019/papers/He_Filter_Pruning_via_Geometric_Median_for_Deep_Convolutional_Neural_Networks_CVPR_2019_paper.pdf)
<div align="center">
<img src="../../images/algorithm_introduction/fpgm.png" width = "600" />
......@@ -54,4 +61,4 @@
算法具体参数请参考PaddleSlim[参数介绍](https://github.com/PaddlePaddle/PaddleSlim/blob/release/2.0.0/docs/zh_cn/api_cn/dygraph/pruners/fpgm_filter_pruner.rst#fpgmfilterpruner)
算法具体参数请参考 PaddleSlim [参数介绍](https://github.com/PaddlePaddle/PaddleSlim/blob/release/2.0.0/docs/zh_cn/api_cn/dygraph/pruners/fpgm_filter_pruner.rst#fpgmfilterpruner)
......@@ -6,36 +6,36 @@
## 目录
- [数据集格式说明](#数据集格式说明)
- [图像分类任务常见数据集介绍](#图像分类任务常见数据集介绍)
- [2.1 ImageNet1k](#ImageNet1k)
- [2.2 Flowers102](#Flowers102)
- [2.3 CIFAR10 / CIFAR100](#CIFAR10/CIFAR100)
- [2.4 MNIST](#MNIST)
- [2.5 NUS-WIDE](#NUS-WIDE)
- [1. 数据集格式说明](#1)
- [2. 图像分类任务常见数据集介绍](#2)
- [2.1 ImageNet1k](#2.1)
- [2.2 Flowers102](#2.2)
- [2.3 CIFAR10 / CIFAR100](#2.3)
- [2.4 MNIST](#2.4)
- [2.5 NUS-WIDE](#2.5)
<a name="数据集格式说明"></a>
## 一、数据集格式说明
<a name="1"></a>
## 1. 数据集格式说明
PaddleClas 使用 `txt` 格式文件指定训练集和测试集,以 `ImageNet1k` 数据集为例,其中 `train_list.txt``val_list.txt` 的格式形如:
```shell
# 每一行采用"空格"分隔图像路径与标注
# 下面是train_list.txt中的格式样例
# 下面是 train_list.txt 中的格式样例
train/n01440764/n01440764_10026.JPEG 0
...
# 下面是val_list.txt中的格式样例
# 下面是 val_list.txt 中的格式样例
val/ILSVRC2012_val_00000001.JPEG 65
...
```
<a name="图像分类任务常见数据集介绍"></a>
## 二、 图像分类任务常见数据集介绍
<a name="2"></a>
## 2. 图像分类任务常见数据集介绍
这里整理了常用的图像分类任务数据集,持续更新中,欢迎各位小伙伴补充完善~
<a name="ImageNet1k"></a>
<a name="2.1"></a>
### 2.1 ImageNet1k
[ImageNet](https://image-net.org/)项目是一个大型视觉数据库,用于视觉目标识别研究任务,该项目已手动标注了 1400 多万张图像。ImageNet-1k 是 ImageNet 数据集的子集,其包含 1000 个类别。训练集包含 1281167 个图像数据,验证集包含 50000 个图像数据。2010 年以来,ImageNet 项目每年举办一次图像分类竞赛,即 ImageNet 大规模视觉识别挑战赛(ILSVRC)。挑战赛使用的数据集即为 ImageNet-1k。到目前为止,ImageNet-1k 已经成为计算机视觉领域发展的最重要的数据集之一,其促进了整个计算机视觉的发展,很多计算机视觉下游任务的初始化模型都是基于该数据集训练得到的。
......@@ -65,7 +65,7 @@ PaddleClas/dataset/ILSVRC2012/
|_ val_list.txt
```
<a name="Flowers102"></a>
<a name="2.2"></a>
### 2.2 Flowers102
数据集 | 训练集大小 | 测试集大小 | 类别数 | 备注|
......@@ -101,23 +101,23 @@ PaddleClas/dataset/flowers102/
|_ val_list.txt
```
<a name="CIFAR10/CIFAR100"></a>
<a name="2.3"></a>
### 2.3 CIFAR10 / CIFAR100
CIFAR-10 数据集由 10 个类的 60000 个彩色图像组成,图像分辨率为 32x32,每个类有 6000 个图像,其中训练集 5000 张,验证集 1000 张,10 个不同的类代表飞机、汽车、鸟类、猫、鹿、狗、青蛙、马、轮船和卡车。CIFAR-100 数据集是CIFAR-10的扩展,由 100 个类的 60000 个彩色图像组成,图像分辨率为 32x32,每个类有 600 个图像,其中训练集 500 张,验证集 100 张。
CIFAR-10 数据集由 10 个类的 60000 个彩色图像组成,图像分辨率为 32x32,每个类有 6000 个图像,其中训练集 5000 张,验证集 1000 张,10 个不同的类代表飞机、汽车、鸟类、猫、鹿、狗、青蛙、马、轮船和卡车。CIFAR-100 数据集是 CIFAR-10 的扩展,由 100 个类的 60000 个彩色图像组成,图像分辨率为 32x32,每个类有 600 个图像,其中训练集 500 张,验证集 100 张。
数据集地址:http://www.cs.toronto.edu/~kriz/cifar.html
<a name="MNIST"></a>
<a name="2.4"></a>
### 2.4 MNIST
MMNIST是一个非常有名的手写体数字识别数据集,在很多资料中,这个数据集都会被用作深度学习的入门样例。其包含 60000 张图片数据,50000 张作为训练集,10000 张作为验证集,每张图片的大小为 28 * 28。
MMNIST 是一个非常有名的手写体数字识别数据集,在很多资料中,这个数据集都会被用作深度学习的入门样例。其包含 60000 张图片数据,50000 张作为训练集,10000 张作为验证集,每张图片的大小为 28 * 28。
数据集地址:http://yann.lecun.com/exdb/mnist/
<a name="NUS-WIDE"></a>
<a name="2.5"></a>
### 2.5 NUS-WIDE
NUS-WIDE 是一个多分类数据集。该数据集包含 269648 张图片, 81 个类别, 每张图片被标记为该 81 个类别中的某一类或某几类。
NUS-WIDE 是一个多分类数据集。该数据集包含 269648 张图片, 81 个类别,每张图片被标记为该 81 个类别中的某一类或某几类。
数据集地址:https://lms.comp.nus.edu.sg/wp-content/uploads/2019/research/nuswide/NUS-WIDE.html
......@@ -7,18 +7,18 @@
## 目录
- [数据集格式说明](#数据集格式说明)
- [图像识别任务常见数据集介绍](#图像识别任务常见数据集介绍)
- [2.1 通用图像识别数据集](#通用图像识别数据集)
- [2.2 垂类图像识别数据集](#垂类图像识别数据集)
- [2.2.1 动漫人物识别](#动漫人物识别)
- [2.2.2 商品识别](#商品识别)
- [2.2.3 Logo识别](#Logo识别)
- [2.2.4 车辆识别](#车辆识别)
- [1. 数据集格式说明](#1)
- [2. 图像识别任务常见数据集介绍](#2)
- [2.1 通用图像识别数据集](#2.1)
- [2.2 垂类图像识别数据集](#2.2)
- [2.2.1 动漫人物识别](#2.2.1)
- [2.2.2 商品识别](#2.2.2)
- [2.2.3 Logo 识别](#2.2.3)
- [2.2.4 车辆识别](#2.2.4)
<a name="数据集格式说明"></a>
## 一、数据集格式说明
<a name="1"></a>
## 1. 数据集格式说明
与分类任务数据集不同,图像检索任务的数据集分为以下三部分:
......@@ -37,7 +37,7 @@ train/99/Ovenbird_0128_93366.jpg 99 6
...
```
验证数据集(`CUB_200_2011`中既是gallery dataset,也是query dataset) `test_list.txt` 文件内容格式如下所示:
验证数据集(`CUB_200_2011` 中既是 gallery dataset,也是 query dataset)`test_list.txt` 文件内容格式如下所示:
```shell
# 采用"空格"作为分隔符号
......@@ -48,85 +48,85 @@ test/200/Common_Yellowthroat_0114_190501.jpg 200 6
...
```
每行数据使用“空格”分割,三列数据的含义分别是训练数据的路径、训练数据的label信息、训练数据的unique id。
每行数据使用“空格”分割,三列数据的含义分别是训练数据的路径、训练数据的 label 信息、训练数据的 unique id。
**注意**
1.gallery dataset和query dataset相同时,为了去掉检索得到的第一个数据(检索图片本身无须评估),每个数据需要对应一个unique id(每张图片的id不同即可,可以用行号来表示unique id),用于后续评测mAP、recall@1等指标。yaml配置文件的数据集选用`VeriWild`
1. gallery dataset 和 query dataset 相同时,为了去掉检索得到的第一个数据(检索图片本身无须评估),每个数据需要对应一个 unique id(每张图片的 id 不同即可,可以用行号来表示 unique id),用于后续评测 mAP、recall@1 等指标。yaml 配置文件的数据集选用 `VeriWild`
2.gallery dataset和query dataset不同时,无需增加unique id,`query_list.txt``gallery_list.txt` 均为两列,分别是训练数据的路径、训练数据的label信息。yaml配置文件的数据集选用`ImageNetDataset`
2. gallery dataset 和 query dataset 不同时,无需增加 unique id,`query_list.txt``gallery_list.txt` 均为两列,分别是训练数据的路径、训练数据的 label 信息。yaml 配置文件的数据集选用 `ImageNetDataset`
<a name="图像识别任务常见数据集介绍"></a>
## 二、图像识别任务常见数据集介绍
<a name="2"></a>
## 2. 图像识别任务常见数据集介绍
这里整理了常用的图像识别任务数据集,持续更新中,欢迎各位小伙伴补充完善~
<a name="通用图像识别数据集"></a>
<a name="2.1"></a>
### 2.1 通用图像识别数据集
- SOP: SOP数据集是通用识别研究领域、MetricLearning技术研究方向常用的一个商品数据集, 其包含从eBay.com下载的22,634个产品的120,053张图片。其中, 训练集包含图片59551张, 类别数11318; 验证集包含图片60502张,类别数11316个。
- SOP: SOP 数据集是通用识别研究领域、MetricLearning 技术研究方向常用的一个商品数据集, 其包含从 eBay.com 下载的 22,634 个产品的 120,053 张图片。其中, 训练集包含图片 59551 张, 类别数 11318; 验证集包含图片 60502 张,类别数 11316 个。
地址: https://cvgl.stanford.edu/projects/lifted_struct/
- Cars196:
Cars数据集包含了196类汽车的16185张图像。数据被分成8144张训练图像和8041张测试图像,每个类大致以50-50的比例分割。级别通常是在制造,模型,年,例如2012特斯拉模型S或2012宝马M3双门跑车。
Cars 数据集包含了 196 类汽车的 16185 张图像。数据被分成 8144 张训练图像和 8041 张测试图像,每个类大致以 50-50 的比例分割。级别通常是在制造,模型,年,例如 2012 特斯拉模型 S 或 2012 宝马 M3 双门跑车。
地址: https://ai.stanford.edu/~jkrause/cars/car_dataset.html
- CUB_200_2011: CUB_200_2011数据集是由加州理工学院在2010年提出的细粒度数据集,也是目前细粒度分类识别研究的基准图像数据集。该数据集共有11788张鸟类图像,包含200类鸟类子类,其中训练数据集有5994张图像,测试集有5794张图像,每张图像均提供了图像类标记信息,图像中鸟的bounding box,鸟的关键part信息,以及鸟类的属性信息,数据集如下图所示。
- CUB_200_2011: CUB_200_2011 数据集是由加州理工学院在 2010 年提出的细粒度数据集,也是目前细粒度分类识别研究的基准图像数据集。该数据集共有 11788 张鸟类图像,包含 200 类鸟类子类,其中训练数据集有 5994 张图像,测试集有 5794 张图像,每张图像均提供了图像类标记信息,图像中鸟的 bounding box,鸟的关键 part 信息,以及鸟类的属性信息,数据集如下图所示。
地址: http://www.vision.caltech.edu/visipedia/CUB-200-2011.html
- In-shop Clothes: In-shop Clothes 是DeepFashion数据集的4个子集之一, 它是一个卖家秀图片集,每个商品id,有多张不同角度的卖家秀,放在同一个文件夹内。该数据集共包含7982件商品,共52712张图像,每张图片都有463中属性,Bbox,landmarks,以及店铺描述。
- In-shop Clothes: In-shop Clothes 是 DeepFashion 数据集的 4 个子集之一, 它是一个卖家秀图片集,每个商品 id,有多张不同角度的卖家秀,放在同一个文件夹内。该数据集共包含 7982 件商品,共 52712 张图像,每张图片都有 463 中属性,Bbox,landmarks,以及店铺描述。
地址: http://mmlab.ie.cuhk.edu.hk/projects/DeepFashion.html
<a name="垂类图像识别数据集"></a>
<a name="2.2"></a>
### 2.2 垂类图像识别数据集
<a name="动漫人物识别"></a>
<a name="2.2.1"></a>
#### 2.2.1 动漫人物识别
+ iCartoonFace: iCartoonFace是由爱奇艺开放的目前全球最大的手工标注卡通人物检测数据集与识别数据集,它包含超过5013个卡通人物、389678张高质量实景图片。相比于其他数据集,它具有大规模、高质量、多样性丰富、挑战难度大等特点,是目前研究动漫人物识别最常用的数据集之一。
+ iCartoonFace: iCartoonFace 是由爱奇艺开放的目前全球最大的手工标注卡通人物检测数据集与识别数据集,它包含超过 5013 个卡通人物、389678 张高质量实景图片。相比于其他数据集,它具有大规模、高质量、多样性丰富、挑战难度大等特点,是目前研究动漫人物识别最常用的数据集之一。
地址: http://challenge.ai.iqiyi.com/detail?raceId=5def69ace9fcf68aef76a75d
+ Manga109: Manga109是2020.5月发布的一个用于研究卡通人物检测和识别的数据集,其包含21142张图片,官方不允许用于商用。该数据集旗下的子集Manga109-s,可以供工业使用, 主要用于文本检测、基于线稿的任务检索、角色图像生成等任务。
+ Manga109: Manga109 是 2020.5 月发布的一个用于研究卡通人物检测和识别的数据集,其包含 21142 张图片,官方不允许用于商用。该数据集旗下的子集 Manga109-s,可以供工业使用,主要用于文本检测、基于线稿的任务检索、角色图像生成等任务。
地址:http://www.manga109.org/en/
+ IIT-CFW:IIF-CFW数据集共包含8928个带有标注的明星人物卡通头像,覆盖100个人物形象,每个人卡通头像数不等。 另外,其还提供了1000张真实人脸照(100个公众人物,每个人10张真实头像)。该数据集既可以用于研究动漫人物识别,也经常被用于研究跨模态的检索任务。
+ IIT-CFW:IIF-CFW 数据集共包含 8928 个带有标注的明星人物卡通头像,覆盖 100 个人物形象,每个人卡通头像数不等。 另外,其还提供了 1000 张真实人脸照(100 个公众人物,每个人 10 张真实头像)。该数据集既可以用于研究动漫人物识别,也经常被用于研究跨模态的检索任务。
地址: http://cvit.iiit.ac.in/research/projects/cvit-projects/cartoonfaces
<a name="商品识别"></a>
<a name="2.2.2"></a>
#### 2.2.2 商品识别
+ AliProduct: AliProduct数据集是目前开源最大的商品数据集,它是一个SKU级别的图像分类数据集, 包含5万类别、300万张商品图像,商品图像的类别和总量均为业界之最。此数据集中涵盖了大量的生活用品、食物等,数据集中没有人工标注,数据较脏,数据分布较不均衡,且有很多相似的商品图片。
+ AliProduct: AliProduct 数据集是目前开源最大的商品数据集,它是一个 SKU 级别的图像分类数据集,包含 5 万类别、300 万张商品图像,商品图像的类别和总量均为业界之最。此数据集中涵盖了大量的生活用品、食物等,数据集中没有人工标注,数据较脏,数据分布较不均衡,且有很多相似的商品图片。
地址: https://retailvisionworkshop.github.io/recognition_challenge_2020/
+ Product-10k: Products-10k数据集中的所有图片均来自京东商城。数据集中共包含1万个经常购买的SKU。所有SKU组织成一个层次结构。总共有近19万张图片。在实际应用场景中,图像量的分布是不均衡的。所有图像都由生产专家团队手工检查/标记。
+ Product-10k: Products-10k 数据集中的所有图片均来自京东商城。数据集中共包含 1 万个经常购买的 SKU。所有 SKU 组织成一个层次结构。总共有近 19 万张图片。在实际应用场景中,图像量的分布是不均衡的。所有图像都由生产专家团队手工检查/标记。
地址:https://www.kaggle.com/c/products-10k/data?select=train.csv
+ DeepFashion-Inshop: 同通用图像识别数据集中的In-shop Clothes
+ DeepFashion-Inshop: 同通用图像识别数据集中的 In-shop Clothes
<a name="Logo识别"></a>
### 2.2.3 Logo识别
+ Logo-2K+: Logo-2K+是一个仅用于logo图像识别的数据集,其包含10个大类,2341个小类和167140张图片。
<a name="2.2.3"></a>
### 2.2.3 Logo 识别
+ Logo-2K+: Logo-2K+是一个仅用于 logo 图像识别的数据集,其包含 10 个大类,2341 个小类和 167140 张图片。
地址: https://github.com/msn199959/Logo-2k-plus-Dataset
+ Tsinghua-Tencent 100K: 该数据集是从10万张腾讯街景全景图中创建的一个大型交通标志基准数据集。它提供包含30000个交通标志实例的100000张图像。这些图像涵盖了照度和天气条件的巨大变化。基准测试中的每个交通标志都标注了类别标签、边界框和像素掩码。 它总共包含222个类别 (0 background + 221 traffic signs)
+ Tsinghua-Tencent 100K: 该数据集是从 10 万张腾讯街景全景图中创建的一个大型交通标志基准数据集。它提供包含 30000 个交通标志实例的 100000 张图像。这些图像涵盖了照度和天气条件的巨大变化。基准测试中的每个交通标志都标注了类别标签、边界框和像素掩码。 它总共包含 222 个类别(0 background + 221 traffic signs)
地址: https://cg.cs.tsinghua.edu.cn/traffic-sign/
<a name="车辆识别"></a>
<a name="2.2.4"></a>
### 2.2.4 车辆识别
+ CompCars: 图像主要来自网络和监控数据,其中网络数据包含163个汽车制造商、1716个汽车型号的汽车。共136,726张全车图像,27,618张部分车图像。其中网络汽车数据包含bounding box、视角、5个属性(最大速度、排量、车门数、车座数、汽车类型)。监控数据包含50,000张前视角图像。
+ CompCars: 图像主要来自网络和监控数据,其中网络数据包含 163 个汽车制造商、1716 个汽车型号的汽车。共 136,726 张全车图像,27,618 张部分车图像。其中网络汽车数据包含 bounding box、视角、5 个属性(最大速度、排量、车门数、车座数、汽车类型)。监控数据包含 50,000 张前视角图像。
地址: http://mmlab.ie.cuhk.edu.hk/datasets/comp_cars/
+ BoxCars: 此数据集共包含21250辆车、63750张图像、27个汽车制造商、148个细类别,此数据集全部来自监控数据。
+ BoxCars: 此数据集共包含 21250 辆车、63750 张图像、27 个汽车制造商、148 个细类别,此数据集全部来自监控数据。
地址: https://github.com/JakubSochor/BoxCars
+ PKU-VD Dataset:该数据集包含了两个大型车辆数据集(VD1和VD2),它们分别从两个城市的真实世界不受限制的场景拍摄图像。其中VD1是从高分辨率交通摄像头获得的,VD2中的图像则是从监视视频中获取的。作者对原始数据执行车辆检测,以确保每个图像仅包含一辆车辆。由于隐私保护的限制,所有车牌号码都已被黑色覆盖遮挡。所有车辆图像均从前视图进行拍摄。 数据集中为每个图像提供了多样化的属性注释,包括身份编号,精确的车辆模型和车辆颜色。VD1原先包含1097649张图像,1232种车俩模型,11种车辆颜色,但删除图像里面有多辆车辆以及从车辆后方拍摄的图片,该数据集仅剩846358张图像,141756辆车辆。 VD2包含807260张图像,79763辆车辆,1112种车辆模型,11种车辆颜色。
+ PKU-VD Dataset:该数据集包含了两个大型车辆数据集(VD1 和 VD2),它们分别从两个城市的真实世界不受限制的场景拍摄图像。其中 VD1 是从高分辨率交通摄像头获得的,VD2 中的图像则是从监视视频中获取的。作者对原始数据执行车辆检测,以确保每个图像仅包含一辆车辆。由于隐私保护的限制,所有车牌号码都已被黑色覆盖遮挡。所有车辆图像均从前视图进行拍摄。 数据集中为每个图像提供了多样化的属性注释,包括身份编号,精确的车辆模型和车辆颜色。VD1 原先包含 1097649 张图像,1232 种车俩模型,11 种车辆颜色,但删除图像里面有多辆车辆以及从车辆后方拍摄的图片,该数据集仅剩 846358 张图像,141756 辆车辆。 VD2 包含 807260 张图像,79763 辆车辆,1112 种车辆模型,11 种车辆颜色。
地址: https://pkuml.org/resources/pku-vds.html
# 服务器端C++预测
# 服务器端 C++ 预测
本教程将介绍在服务器端部署PaddleClas分类模型的详细步骤,识别模型部署方式将在近期支持,敬请期待。
本教程将介绍在服务器端部署 PaddleClas 分类模型的详细步骤,识别模型部署方式将在近期支持,敬请期待。
---
## 目录
- [准备环境](#1)
- [1.1编译opencv](#1.1)
- [1.2准备环境](#1.2)
- [1. 准备环境](#1)
- [1.1 编译 opencv ](#1.1)
- [1.2 准备环境](#1.2)
- [1.2.1 预测库源码编译](#1.2.1)
- [1.2.2 直接下载安装](#1.2.2)
- [编译](#2)
- [2. 编译](#2)
- [2.1 编译 PaddleClas C++ 预测 demo](#2.1)
- [2.2 编译 config lib 预测库与 cls lib 预测库](#2.2)
- [运行](#3)
- [3. 运行](#3)
- [3.1 准备 inference model](#3.1)
- [3.2 运行 demo](#3.2)
......@@ -23,12 +23,12 @@
## 1. 准备环境
- Linux 环境,推荐使用 docker。
- Windows 环境,目前支持基于 `Visual Studio 2019 Community` 进行编译;此外,如果您希望通过生成 `sln解决方案` 的方式进行编译,可以参考该文档:[https://zhuanlan.zhihu.com/p/145446681](https://zhuanlan.zhihu.com/p/145446681)
- Windows 环境,目前支持基于 `Visual Studio 2019 Community` 进行编译;此外,如果您希望通过生成 `sln 解决方案` 的方式进行编译,可以参考该文档:[https://zhuanlan.zhihu.com/p/145446681](https://zhuanlan.zhihu.com/p/145446681)
* 该文档主要介绍基于 Linux 环境下的 PaddleClas C++ 预测流程,如果需要在 Windows 环境下使用预测库进行 C++ 预测,具体编译方法请参考 [Windows下编译教程](./cpp_deploy_on_windows.md)
* 该文档主要介绍基于 Linux 环境下的 PaddleClas C++ 预测流程,如果需要在 Windows 环境下使用预测库进行 C++ 预测,具体编译方法请参考 [Windows 下编译教程](./cpp_deploy_on_windows.md)
<a name="1.1"></a>
### 1.1 编译opencv
### 1.1 编译 opencv
* 首先需要从 opencv 官网上下载在 Linux 环境下源码编译的包,以 3.4.7 版本为例,下载及解压缩命令如下:
......@@ -95,9 +95,9 @@ opencv3/
<a name="1.2.1"></a>
#### 1.2.1 预测库源码编译
如果希望获取最新预测库特性,可以从 GitHub 上克隆 Paddle 最新代码,从源码编译预测库。对于不同平台的编译流程,请参考 [Paddle预测库官网](https://paddleinference.paddlepaddle.org.cn/v2.1/user_guides/source_compile.html) 的说明。编译示例如下:
如果希望获取最新预测库特性,可以从 GitHub 上克隆 Paddle 最新代码,从源码编译预测库。对于不同平台的编译流程,请参考 [Paddle 预测库官网](https://paddleinference.paddlepaddle.org.cn/v2.1/user_guides/source_compile.html) 的说明。编译示例如下:
1. 使用Git获取源代码:
1. 使用 Git 获取源代码:
```shell
git clone https://github.com/PaddlePaddle/Paddle.git
......@@ -140,7 +140,7 @@ build/paddle_inference_install_dir/
<a name="1.2.2"></a>
#### 1.2.2 直接下载安装
[Paddle预测库官网](https://paddleinference.paddlepaddle.org.cn/v2.1/user_guides/download_lib.html#c) 上提供了不同版本的 Paddle 预测库,包括多个操作系统平台和GPU、CPU等多个硬件平台的预编译库,可以在官网查找并选择合适的预测库版本进行下载,建议选择 `2.1.1` 版本。
[Paddle 预测库官网](https://paddleinference.paddlepaddle.org.cn/v2.1/user_guides/download_lib.html#c) 上提供了不同版本的 Paddle 预测库,包括多个操作系统平台和 GPU、CPU 等多个硬件平台的预编译库,可以在官网查找并选择合适的预测库版本进行下载,建议选择 `2.1.1` 版本。
**注意**:在选择预测库时,所选预测库版本需要与后续编译选项一致:
* CPU 预测库仅可用于 GPU 预测,具体又分为 `mkl``openblas`,分别对应其低层实现基于 `MKL` 数学运算库 和 `OpenBLAS` 数学运算库;
......@@ -167,7 +167,7 @@ tar -xf paddle_inference.tgz
sh tools/build_demo.sh
```
具体地,`tools/build_demo.sh`中内容如下:
具体地,`tools/build_demo.sh` 中内容如下:
```shell
OpenCV_DIR=path/to/opencv
......@@ -224,7 +224,7 @@ make -j
sh tools/build_lib.sh
```
具体地,`tools/build_lib.sh`中内容如下:
具体地,`tools/build_lib.sh` 中内容如下:
```shell
OpenCV_DIR=path/to/opencv
......@@ -269,19 +269,19 @@ inference/
### 3.2 运行 demo
首先修改 `tools/config.txt` 中对应字段:
* use_gpu:是否使用GPU;
* gpu_id:使用的GPU卡号;
* use_gpu:是否使用 GPU;
* gpu_id:使用的 GPU 卡号;
* gpu_mem:显存;
* cpu_math_library_num_threads:底层科学计算库所用线程的数量;
* use_mkldnn:是否使用MKLDNN加速;
* use_tensorrt: 是否使用tensorRT进行加速;
* use_fp16:是否使用半精度浮点数进行计算,该选项仅在use_tensorrt为true时有效;
* use_mkldnn:是否使用 MKLDNN 加速;
* use_tensorrt: 是否使用 tensorRT 进行加速;
* use_fp16:是否使用半精度浮点数进行计算,该选项仅在 use_tensorrt 为 true 时有效;
* cls_model_path:预测模型结构文件路径;
* cls_params_path:预测模型参数文件路径;
* resize_short_size:预处理时图像缩放大小;
* crop_size:预处理时图像裁剪后的大小。
然后修改`tools/run.sh`:
然后修改 `tools/run.sh`:
* `./build/clas_system ./tools/config.txt ./docs/imgs/ILSVRC2012_val_00000666.JPEG`
* 上述命令中分别为:编译得到的可执行文件 `clas_system`;运行时的配置文件 `config.txt`;待预测的图像。
......
# 安装 PaddleClas
---
## 目录
* [1. 克隆 PaddleClas](#1)
* [2. 安装 Python 依赖库](#2)
<a name='1'></a>
## 1. 克隆 PaddleClas
......@@ -15,6 +21,7 @@ git clone https://github.com/PaddlePaddle/PaddleClas.git -b release/2.3
```shell
git clone https://gitee.com/paddlepaddle/PaddleClas.git -b release/2.3
```
<a name='2'></a>
## 2. 安装 Python 依赖库
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册