前言

有不少開發者在學習深度學習框架的時候會開源一些訓練好的模型,我們可以使用這些模型來運用到我們自己的項目中。如果使用的是同一個深度學習框架,那就很方便,可以直接使用,但是如果時不同深度學習框架,我們就要對模型轉換一下。下面我們就介紹如何把Caffe的模型轉換成PaddlePaddle的Fluid模型。

環境準備

  • 在線安裝最新發布的PaddlePaddle,可以使用pip命令直接在線安裝PaddlePaddle。
pip install paddlepaddle
  • 下在安裝最新的PaddlePaddle,可以在下面選擇適合自己PaddlePaddle的版本,這裏下載的是最新編譯得到的,然後使用pip命令安裝。
http://www.paddlepaddle.org/documentation/docs/zh/0.14.0/new_docs/beginners_guide/install/install_doc.html#id26
  • 克隆PaddlePaddle下的models源碼。
git clone https://github.com/PaddlePaddle/models.git

轉換模型

  • 進入到上一步克隆的modelscaffe2fluid目錄。
cd models/fluid/image_classification/caffe2fluid/
  • 下載轉換時所需要的依賴的Python文件。
cd proto/ && wget https://raw.githubusercontent.com/ethereon/caffe-tensorflow/master/kaffe/caffe/caffepb.py

修改剛下載的文件名:

mv caffepb.py caffe_pb2.py
  • 獲取需要轉換的Caffe模型,筆者是參考以下這個開源獲取的:
https://gist.github.com/ksimonyan/211839e770f7b538e2d8

首先是獲取VGG_ILSVRC_16_layers_deploy.prototxt網絡文件。

cd ../ && wget https://gist.githubusercontent.com/ksimonyan/211839e770f7b538e2d8/raw/ded9363bd93ec0c770134f4e387d8aaaaa2407ce/VGG_ILSVRC_16_layers_deploy.prototxt

其次是獲取VGG_ILSVRC_16_layers.caffemodel權重文件。

wget http://www.robots.ox.ac.uk/~vgg/software/very_deep/caffe/VGG_ILSVRC_16_layers.caffemodel
  • 把Caffe模型轉換成Fluid版本的網絡結構文件和權重文件,其中VGG16.py是PaddlePaddle定義網絡結構的Python文件,VGG16.npy是網絡的權重文件。
python convert.py VGG_ILSVRC_16_layers_deploy.prototxt \
        --caffemodel VGG_ILSVRC_16_layers.caffemodel \
        --data-output-path VGG16.npy \
        --code-output-path VGG16.py

在執行過程中會生產以下信息:

register layer[Axpy]
register layer[Flatten]
register layer[ArgMax]
register layer[Reshape]
register layer[ROIPooling]
register layer[PriorBox]
register layer[Permute]
register layer[DetectionOutput]
register layer[Normalize]
register layer[Select]
register layer[Crop]
register layer[Reduction]

------------------------------------------------------------
    WARNING: PyCaffe not found!
    Falling back to a pure protocol buffer implementation.
    * Conversions will be drastically slower.
------------------------------------------------------------

Type                 Name                                          Param               Output
----------------------------------------------------------------------------------------------
Data                 data                                             --    (10, 3, 224, 224)
Convolution          conv1_1                               (64, 3, 3, 3)   (10, 64, 224, 224)
Convolution          conv1_1                                       (64,)   (10, 64, 224, 224)
Convolution          conv1_2                              (64, 64, 3, 3)   (10, 64, 224, 224)
Convolution          conv1_2                                       (64,)   (10, 64, 224, 224)
Pooling              pool1                                            --   (10, 64, 112, 112)
Convolution          conv2_1                             (128, 64, 3, 3)  (10, 128, 112, 112)
Convolution          conv2_1                                      (128,)  (10, 128, 112, 112)
Convolution          conv2_2                            (128, 128, 3, 3)  (10, 128, 112, 112)
Convolution          conv2_2                                      (128,)  (10, 128, 112, 112)
Pooling              pool2                                            --    (10, 128, 56, 56)
Convolution          conv3_1                            (256, 128, 3, 3)    (10, 256, 56, 56)
Convolution          conv3_1                                      (256,)    (10, 256, 56, 56)
Convolution          conv3_2                            (256, 256, 3, 3)    (10, 256, 56, 56)
Convolution          conv3_2                                      (256,)    (10, 256, 56, 56)
Convolution          conv3_3                            (256, 256, 3, 3)    (10, 256, 56, 56)
Convolution          conv3_3                                      (256,)    (10, 256, 56, 56)
Pooling              pool3                                            --    (10, 256, 28, 28)
Convolution          conv4_1                            (512, 256, 3, 3)    (10, 512, 28, 28)
Convolution          conv4_1                                      (512,)    (10, 512, 28, 28)
Convolution          conv4_2                            (512, 512, 3, 3)    (10, 512, 28, 28)
Convolution          conv4_2                                      (512,)    (10, 512, 28, 28)
Convolution          conv4_3                            (512, 512, 3, 3)    (10, 512, 28, 28)
Convolution          conv4_3                                      (512,)    (10, 512, 28, 28)
Pooling              pool4                                            --    (10, 512, 14, 14)
Convolution          conv5_1                            (512, 512, 3, 3)    (10, 512, 14, 14)
Convolution          conv5_1                                      (512,)    (10, 512, 14, 14)
Convolution          conv5_2                            (512, 512, 3, 3)    (10, 512, 14, 14)
Convolution          conv5_2                                      (512,)    (10, 512, 14, 14)
Convolution          conv5_3                            (512, 512, 3, 3)    (10, 512, 14, 14)
Convolution          conv5_3                                      (512,)    (10, 512, 14, 14)
Pooling              pool5                                            --      (10, 512, 7, 7)
InnerProduct         fc6                                   (4096, 25088)           (10, 4096)
InnerProduct         fc6                                         (4096,)           (10, 4096)
Dropout              drop6                                            --           (10, 4096)
InnerProduct         fc7                                    (4096, 4096)           (10, 4096)
InnerProduct         fc7                                         (4096,)           (10, 4096)
Dropout              drop7                                            --           (10, 4096)
InnerProduct         fc8                                    (1000, 4096)           (10, 1000)
InnerProduct         fc8                                         (1000,)           (10, 1000)
Softmax              prob                                             --           (10, 1000)
Converting data...
Saving data...
Saving source...
set env variable before using converted model if used custom_layers:
  • 使用PaddlePaddle的網絡結構文件和權重文件生成預測模型文件。
python VGG16.py VGG16.npy ./fluid_models
  • 執行上面的命令之後,就可以生成預測模型了,並存放在當前目錄的fluid_models目錄下,一共有兩文件,分別是modelparams,這個跟我們使用paddle.fluid.io.save_inference_model接口是一樣的,接口文檔在這裏。在下一步我們會使用這個模型文件來預測我們的圖片。

測試預測模型

獲得預測模型之後,我們可以使用它來在PaddlePaddle預測圖像,首先要編寫一個PaddlePaddle的預測程序:

# coding=utf-8
import os
import time
from PIL import Image
import numpy as np
import paddle.v2 as paddle
import paddle.fluid as fluid


def load_image(file):
    im = Image.open(file)
    im = im.resize((224, 224), Image.ANTIALIAS)
    im = np.array(im).astype(np.float32)
    # PIL打開圖片存儲順序爲H(高度),W(寬度),C(通道)。
    # PaddlePaddle要求數據順序爲CHW,所以需要轉換順序。
    im = im.transpose((2, 0, 1))
    # CIFAR訓練圖片通道順序爲B(藍),G(綠),R(紅),
    # 而PIL打開圖片默認通道順序爲RGB,因爲需要交換通道。
    im = im[(2, 1, 0), :, :]  # BGR
    # 減去均值
    mean = [123.68, 116.78, 103.94]
    mean = np.array(mean, dtype=np.float32)
    mean = mean[:, np.newaxis, np.newaxis]
    im -= mean

    return im


def infer_one(image_path, use_cuda, model_path, model_filename, params_filename):
    # 是否使用GPU
    place = fluid.CUDAPlace(0) if use_cuda else fluid.CPUPlace()
    # 生成調試器
    exe = fluid.Executor(place)

    inference_scope = fluid.core.Scope()
    with fluid.scope_guard(inference_scope):
        # 加載模型
        [inference_program, feed_target_names, fetch_targets] = fluid.io.load_inference_model(model_path, exe, model_filename=model_filename, params_filename=params_filename)

        # 獲取預測數據
        test_datas = [load_image(image_path)]
        test_data = np.array(test_datas)

        # 開始預測
        results = exe.run(inference_program,
                          feed={feed_target_names[0]: test_data},
                          fetch_list=fetch_targets)

        results = np.argsort(-results[0])

        result = results[0][0]

        print("infer label is: %d" % result)


if __name__ == '__main__':
    image_path = "0b77aba2-9557-11e8-a47a-c8ff285a4317.jpg"
    use_cuda = False
    model_path = "fluid_models/"
    model_filename = "model"
    params_filename = "params"
    infer_one(image_path, use_cuda, model_path, model_filename, params_filename)

使用上面的程序就是使用轉換的模型來預測圖片了。要注意訓練模型時對圖片的處理。

參考資料

  1. https://github.com/PaddlePaddle/models/tree/develop/fluid/image_classification/caffe2fluid
  2. https://gist.github.com/ksimonyan/211839e770f7b538e2d8
小夜