PaddleLite使用

PaddleLite模型使用
PaddleLite使用流程基本上和TensorflowLite大体类似,只不过输入Tensor的shape信息需要用户自行分配,否则输入和输出Tensor的shape信息全都是默认值0

1、配置 Config 信息:创建 MobileConfig ,用于配置模型路径、运行设备环境等相关信息
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 接口创建 Predictor 对像,完成模型解析和环境初始化。

p->predictor = paddle::lite_api::CreatePaddlePredictor<paddle::lite_api::MobileConfig>(config);

4、推理之前需要向输入 Tensor 中填充数据。即通过 predictor->GetInput(num) 接口获取第 num 个输入 tensor,也可以通过name来获取,获取输入Tensor后先做 Resize 处理,给 tensor 分配相应的空间;然后通过获取 input_tensor->mutable_data<Dtype>() 输入数据地址进行赋值处理

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 对像的成员函数 Run 进行模型推理

predictor->Run();

6、推理执行结束后,通过 predictor->GetOutput(num) 接口获取第 num 个输出 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;//纹理内存
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; 
    }
} 

使用过程遇到问题
如何确定模型预处理和后处理所做的操作
PaddleHub项目提供的待优化的模型压缩包python脚本包含了图像预处理和后处理过程,只需要将python代码中必须的代码转化为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脚本中推断逻辑

加载数据处理器

processor = Processor(
    images, 
    paths,  
    output_dir, 
    min_size, 
    max_size
)

# 模型预测
outputs = self.model.predict(processor.input_datas)

# 结果后处理
results = processor.postprocess(outputs, visualization)

# 返回结果
return results

processor.py中预处理和后处理逻辑

    # 数据预处理函数
    def preprocess(self):
        input_datas = []

        # 数据预处理
        for i, img in enumerate(self.datas):
            # 格式转换
            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')

            # 加入输入数据列表
            input_datas.append(img)

        # 数据按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)   

        # 返回预处理完成的数据
        return input_datas
    
    # 后处理函数
    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)

            # 格式转换
            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)

        # 返回结果
        return results

CUDA和OpenCL支持问题

PaddleLite CUDA和OpenCL支持的op不是很完善,PaddleLite不像TensorflowLite完善,目前没找到什么方式可以在不支持的op情况下自动切换到CPU方式,在加载模型这一步就会出错,提示有不支持的op,并且在模型转化为nb模型这一步如果出现不支持的op就会报错,必须添加对应架构下的op实现,并重新编译PaddleLite和opt导出工具

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容