PaddleLite模型使用
PaddleLite使用流程基本上和TensorflowLite大體類似缤弦,只不過輸入Tensor的shape信息需要用戶自行分配,否則輸入和輸出Tensor的shape信息全都是默認(rèn)值0
1狸捅、配置 Config 信息:創(chuàng)建 MobileConfig 尘喝,用于配置模型路徑斋陪、運(yùn)行設(shè)備環(huán)境等相關(guān)信息
2、通過 set_model_from_file 接口配置模型路徑
paddle::lite_api::MobileConfig config;
config.set_model_from_file(model_path);
config.set_threads(CPU_THREAD_NUM);
config.set_power_mode(CPU_POWER_MODE);
3鞍匾、通過 CreatePaddlePredictor 接口創(chuàng)建 Predictor 對像橡淑,完成模型解析和環(huán)境初始化咆爽。
p->predictor = paddle::lite_api::CreatePaddlePredictor<paddle::lite_api::MobileConfig>(config);
4置森、推理之前需要向輸入 Tensor 中填充數(shù)據(jù)凫海。即通過 predictor->GetInput(num) 接口獲取第 num 個(gè)輸入 tensor男娄,也可以通過name來獲取,獲取輸入Tensor后先做 Resize 處理建瘫,給 tensor 分配相應(yīng)的空間啰脚;然后通過獲取 input_tensor->mutable_data<Dtype>() 輸入數(shù)據(jù)地址進(jìn)行賦值處理
const std::vector<int64_t> INPUT_SHAPE = {1, 426, 640, 3};// NHWC格式
std::unique_ptr<paddle::lite_api::Tensor> input_tensor(
std::move(predictor->GetInput(0)));
input_tensor->Resize(INPUT_SHAPE);
auto *input_data = input_tensor->mutable_data<float>();//這里模型輸入類型fp32
uint8_t *image_data = reinterpret_cast<uint8_t *>(resize_image.data);
cv::Mat out_img = cv::Mat::ones(input_height, input_width, CV_8UC3);
for (int i = 0; i < input_height; ++i) {
for (int j = 0; j < input_width; ++j) {
float r = image_data[(i * input_width + j) * 3 + 0];
float g = image_data[(i * input_width + j) * 3 + 1];
float b = image_data[(i * input_width + j) * 3 + 2];
float rr = std::max(-1.0f, std::min(r / 127.5f - 1.0f, 1.0f));//具體模型處理不同
float gg = std::max(-1.0f, std::min(g / 127.5f - 1.0f, 1.0f));
float bb = std::max(-1.0f, std::min(b / 127.5f - 1.0f, 1.0f));
*input_data++ = rr;
*input_data++ = gg;
*input_data++ = bb;
out_img.at<cv::Vec3b>(i, j)[0] = (uint8_t) (r * 255);
out_img.at<cv::Vec3b>(i, j)[1] = (uint8_t) (g * 255);
out_img.at<cv::Vec3b>(i, j)[2] = (uint8_t) (b * 255);
}
}
cv::imwrite("../input.png", out_img);
5橄浓、使用 Predictor 對像的成員函數(shù) Run 進(jìn)行模型推理
predictor->Run();
6荸实、推理執(zhí)行結(jié)束后缴淋,通過 predictor->GetOutput(num) 接口獲取第 num 個(gè)輸出 tensor
std::unique_ptr<const paddle::lite_api::Tensor> output_tensor(
std::move(predictor->GetOutput(0)));
const float *output_data = output_tensor->mutable_data<float>();
// NHWC
int h = output_tensor->shape().at(1);
int w = output_tensor->shape().at(2);
int c = output_tensor->shape().at(3);
static uint8_t *s_texbuf = nullptr;//紋理內(nèi)存
if (s_texbuf == nullptr) {
s_texbuf = (uint8_t *) calloc(1, w * h * 4);
}
uint8_t *d = s_texbuf;
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
float r = *output_data++;
float g = *output_data++;
float b = *output_data++;
r = (uint8_t) ((r + 1.0f) / 2.0f * 255);//具體模型處理這里不同
g = (uint8_t) ((g + 1.0f) / 2.0f * 255);
b = (uint8_t) ((b + 1.0f) / 2.0f * 255);
r = std::min(255.0f, r);
g = std::min(255.0f, g);
b = std::min(255.0f, b);
*d++ = r;
*d++ = g;
*d++ = b;
*d++ = 0xFF;
}
}
使用過程遇到問題
如何確定模型預(yù)處理和后處理所做的操作
PaddleHub項(xiàng)目提供的待優(yōu)化的模型壓縮包python腳本包含了圖像預(yù)處理和后處理過程宴猾,只需要將python代碼中必須的代碼轉(zhuǎn)化為c++版本即可仇哆,以animegan_v2_shinkai_33模型為例夫植,解壓后的目錄格式
animegan_v2_shinkai_33
│ ├── animegan_v2_shinkai_33
│ │ ├── __model__
│ │ ├── generator_G_MODEL_A_Conv_1_weights
│ │ ├── generator_G_MODEL_A_Conv_2_weights
│ │ ├── generator_G_MODEL_A_Conv_weights
│ │ ├── generator_G_MODEL_A_LayerNorm_1_beta
│ │ ├── generator_G_MODEL_A_LayerNorm_1_gamma
│ │ └── ......
└── model.py
└── module.py
└── processor.py
module.py腳本中推斷邏輯
加載數(shù)據(jù)處理器
processor = Processor(
images,
paths,
output_dir,
min_size,
max_size
)
# 模型預(yù)測
outputs = self.model.predict(processor.input_datas)
# 結(jié)果后處理
results = processor.postprocess(outputs, visualization)
# 返回結(jié)果
return results
processor.py中預(yù)處理和后處理邏輯
# 數(shù)據(jù)預(yù)處理函數(shù)
def preprocess(self):
input_datas = []
# 數(shù)據(jù)預(yù)處理
for i, img in enumerate(self.datas):
# 格式轉(zhuǎn)換
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 縮放圖片
h, w = img.shape[:2]
if max(h,w)>self.max_size:
img = cv2.resize(img, (self.max_size, int(h/w*self.max_size))) if h<w else cv2.resize(img, (int(w/h*self.max_size), self.max_size))
elif min(h,w)<self.min_size:
img = cv2.resize(img, (self.min_size, int(h/w*self.min_size))) if h>w else cv2.resize(img, (int(w/h*self.min_size), self.min_size))
# 裁剪圖片
h, w = img.shape[:2]
img = img[:h-(h%32), :w-(w%32), :]
# 歸一化
img = img/127.5 - 1.0
# 新建維度
img = np.expand_dims(img, axis=0).astype('float32')
# 加入輸入數(shù)據(jù)列表
input_datas.append(img)
# 數(shù)據(jù)按batch_size切分
input_datas = np.concatenate(input_datas, 0)
split_num = len(self.datas)//self.batch_size+1 if len(self.datas)%self.batch_size!=0 else len(self.datas)//self.batch_size
input_datas = np.array_split(input_datas, split_num)
# 返回預(yù)處理完成的數(shù)據(jù)
return input_datas
# 后處理函數(shù)
def postprocess(self, outputs, visualization):
results = []
for im_id, output in enumerate(outputs):
# 反歸一化
image = (output.squeeze() + 1.) / 2 * 255
# 限幅
image = np.clip(image, 0, 255).astype(np.uint8)
# 格式轉(zhuǎn)換
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
# 可視化
if visualization:
# 檢查輸出目錄
check_dir(self.output_dir)
# 寫入輸出圖片
cv2.imwrite(os.path.join(self.output_dir, '%d_%d.jpg' % (im_id, time.time())), image)
results.append(image)
# 返回結(jié)果
return results
CUDA和OpenCL支持問題
PaddleLite CUDA和OpenCL支持的op不是很完善,PaddleLite不像TensorflowLite完善沈跨,目前沒找到什么方式可以在不支持的op情況下自動(dòng)切換到CPU方式,在加載模型這一步就會(huì)出錯(cuò)狞玛,提示有不支持的op,并且在模型轉(zhuǎn)化為nb模型這一步如果出現(xiàn)不支持的op就會(huì)報(bào)錯(cuò)心肪,必須添加對應(yīng)架構(gòu)下的op實(shí)現(xiàn),并重新編譯PaddleLite和opt導(dǎo)出工具