腾讯优图开源项目TNN总结与实践

  • Post author:
  • Post category:其他




一、Github的license

1、开源软件的License,一般是授权用户使用、拷贝、修改和再发布的合法权利及应当遵守的约定,同时包含作者的免责声明和使用自担责声明。

2、使用合适的开源许可证可以给开发者/使用者避免一些不必要的麻烦事情。


3、我还是不明白我再这里写TNN的总结,会不会侵权,知道的谢谢留言。



开源软件为什么要有授权



为什么开源项目要选择License



二、如何进行模型转换

1、目前TNN仅支持CNN,支持主流的模型文件格式,包括ONNX、PyTorch、Tensorflow以及Caffe等。

2、模型转换有三种:

(1)方法一分两步:比如Caffe先通过脚本caffe2onnx,转成ONNX模型,再通过脚本onnx2tnn,转换成TNN模型。脚本位置分别在

TNN/tools/caffe2onnx/caffe2onnx.py和TNN/tools/convert2tnn/caffe_converter/caffe2tnn.py

注意的是:执行caffe2onnx.py只需补充待转换模型位置等参数即可;执行caffe2tnn.py需要先编译,具体环境搭建以及编译步骤见TNN/doc/cn/usr/onnx2tnn.md;同时wIndows系统不建议尝试。

(2)方法二一步走:手动安装依赖工具和编译工具,使用conver2tnn转换工具,其实和方法一差不多。

(3)方法三也是一步走:通过docker image的方式使用convert2tnn转换工具,重点说一下这种方式



基于Docker的模型转换方式

TNN/doc/cn/user/convert.md之Convert2tnn Docker(推荐)这里步骤和参数说的很清楚

具体终端命令如下:

docker pull turandotkay/tnn-convert
docker images
docker tag turandotkay/tnn-convert:latest tnn-convert:latest
docker rmi turandotkay/tnn-convert:latest
docker images

#convert TF
docker run --volume=$(pwd):/workspace -it tnn-convert:latest  python3 ./converter.py tf2tnn \
    -tp /workspace/test.pb \
    -in "input0[1,32,32,3];input1[1,32,32,3]" \
    -on output0 \
    -v v2.0 \
    -optimize \
    -align \
    -input_file /workspace/in.txt \
    -ref_file /workspace/ref.txt
    
# convert onnx
docker run --volume=$(pwd):/workspace -it tnn-convert:latest python3 ./converter.py onnx2tnn \
    /workspace/mobilenetv3-small-c7eb32fe.onnx \
    -optimize \
    -v v3.0 \
    -align  \
    -input_file /workspace/in.txt \
    -ref_file /workspace/ref.txt

# convert caffe
docker run --volume=$(pwd):/workspace -it tnn-convert:latest python3 ./converter.py caffe2tnn \
    /workspace/squeezenet.prototxt \
    /workspace/squeezenet.caffemodel \
    -optimize \
    -v v1.0 \
    -align  \
    -input_file /workspace/in.txt \
    -ref_file /workspace/ref.txt

对docker不熟悉的小伙伴可以查一下docker的常用命令,这里备注一下相关的:

#列出本地镜像
docker images
#列出本地所有镜像(含中间映像层,默认情况下,过滤掉中间映像层)
docker images -a 
#列出本地镜像中REPOSITORY为ubuntu的镜像列表
docker images ubuntu

#列出所有在运行的容器信息
docker ps
#列出最近创建的5个容器信息
docker ps -n 5
#列出所有创建的容器
#docker ps -a

#使用镜像nginx:latest以交互模式启动一个容器,并在容器内执行/bin/bash命令
docker run -it nginx:latest /bin/bash
#-i:以交互模型运行容器。-t:为容器重新分配一个伪输入终端。两个通常同时使用
#--name="nginx-lb":为容器指定一个名称
# -p:指定端口映射,格式为:主机端口:容器端口
#-v/--volume:绑定一个卷,冒号前为宿主机目录,必须为绝对路径,冒号后为镜像内挂载的路径
#使用镜像nginx:lates,以后台模式启动一个容器,将容器的80端口映射到主机的80端口,主机的目录/data映射到容器的/data
#被挂载之后的容器地址将会存在挂载的挂载内容
docker run -p 80:80 -v /data:/data -d nginx:latest
#删除指定ID的容器
docker rm 6655086fbaff 67b710cbe462

下面是我执行的命令

docker run --name="model2tnn3" --volume=/data02/lhh/models/:/workspace -p 50009:22 -it tnn-convert:latest  

python3 ./converter.py tf2tnn \
    -tp /workspace/tfmodel/mobilenet_v1_1.0_224_frozen.pb \
    -in "input0[1,224,224,3];input1[1,224,224,3]" \
    -on output0 \
    -v v2.0 \
    -optimize \
    -align \
    -input_file /workspace/in.txt \
    -ref_file /workspace/ref.txt

 python3 ./converter.py caffe2tnn \
    /workspace/caffemodel/slim-320.prototxt \
    /workspace/caffemodel/slim-320.caffemodel \
    -optimize \
    -v v1.0 \
    -align  \
    -input_file /workspace/in.txt \
    -ref_file /workspace/ref.txt

caffemodel转换成功后得到:

在这里插入图片描述



三、如何在ARM Linux跨平台交叉编译TNN



1、环境要求依赖

  • 提前拉取三个镜像:qemu-user-static:register,arm64v8/ubuntu:18.04,tnn-dev:20.07
  • tnn-dev:20.07是开发推理程序的环境,可以在dev里面编译TNN及Demo
  • arm64v8/ubunbu:18.04里面不需要配置开发环境,它是在x86架构上模拟了一个arm64的环境。
  • 在tnn-dev里通过交叉编译工具生成arm64硬件架构下可以运行的程序。


(1)注册qemu解释器
docker run --rm --privileged multiarch/qemu-user-static:register --reset

这一步很简单



(2)启动arm64模拟环境
docker run -it -v /usr/bin/qemu-aarch64-static:/usr/bin/qemu-aarch64-static -v /data02/github/TNN:/workspace/TNN arm64v8/ubuntu:18.04

在这里如果报错:

"standard_init_linux.go:207:exec user process caused"permission denied"


这可能是账户对对qemu-aarch64-static文件的权限问题

解决方法:在启动模拟环境之前设置一下权限就好了,执行这条命令

chmod 777 /usr/bin/qemu-aarch64-static



(3)启动开发环境
docker run -it -p50013:22 -v /data02/github/TNN:/workspace/TNN tnn-dev:20.07
sudo apt-get install g++-aarch64-linux-gnu gcc-aarch64-linux-gnu

备注:

/data02/github/TNN为本地的TNN工程目录

同时将工程目录TNN挂载到两个容器中,就会将这两个容器连接起来。本地TNN,容器arm64v8/ubuntu:18.04挂载的TNN以及tnn-dev:20.07挂载的TNN,三处TNN其实为一处,其中一处改变,其它两处同步改变。

也就是说,在tnn-dev:20.07编译生成可执行文件后,在arm64v8/ubuntu:18.04里面执行



2、编译步骤



(1)切换到脚本目录
cd <path_to_tnn>/scripts


(2)修改配置选项

编辑build_aarch64_linux.sh

 SHARED_LIB="ON"                # ON表示编译动态库,OFF表示编译静态库
 ARM="ON"                       # ON表示编译带有Arm CPU版本的库
 OPENMP="ON"                    # ON表示打开OpenMP
 OPENCL="OFF"                   # ON表示编译带有Arm GPU版本的库
 #ARM64:
 CC=aarch64-linux-gnu-gcc       # 指定C编译器
 CXX=aarch64-linux-gnu-g++      # 指定C++编译器
 TARGET_ARCH=aarch64            # 指定指令架构
 #ARM32HF:
 CC=arm-linux-gnueabihf-gcc       
 CXX=arm-linux-gnueabihf-g++      
 TARGET_ARCH=arm


(3)执行编译脚本
./build_aarch64_linux.sh

在这里插入图片描述



四、跑通Armlinux Demo

cd /workspace/TNN/examples/armlinux
ls



(1)修改build_aarch64.sh,配置编译选项:

CC=aarch64-linux-gnu-gcc 
CXX=aarch64-linux-gnu-g++ 
TNN_LIB_PATH=../../scripts/build_aarch64_linux/



(2)执行build_aarch64.sh

./build_aarch64.sh



(3)执行demo

编译完成后会在armlinux下生成一个build文件夹,该文件夹下会生成一个可执行文件

demo_arm_linux

 ./demo_arm_linux ../../../model/SqueezeNet/squeezenet_v1.1.tnnproto ../../../model/SqueezeNet/squeezenet_v1.1.tnnmodel 224 224

如果在tnn-dev中执行会报错:

bash:./demo_arm_linux:No such file or directory

这个错误原因就是环境平台不符合,即转到容器arm64v8/ubuntu:18.04里面执行。

因为这两个容器挂载了本地的同一个目录,所以TNN目录是同步更改的。

这个demo,输入是一个随机的RGB数据,输出是一个nchw float blob,例如NCHW=1x3x256x25.

在caffe中Blob相当于tensorflow的tensor,即张量,是caffe框架中数据流的基本存储单位,caffe中关于数据的运算和存储都是基于Blob进行的。

在这里插入图片描述



五、ArmLinux平台耗时测试

并没有执行

TNN/benchmark

文件夹下的脚本,因为这个文件夹下的benchmark_model.sh大部分还是基于Android环境



1、修改benchmark_model.sh脚本

修改原本的

/workspace/TNN/benchmark/benchmark_android/benchmark_model.sh

,修改后为:

#!/bin/bash


ABI="arm64-v8a"
STL="c++_static"
SHARED_LIB="ON"
PROFILING="OFF"
CLEAN=""
PUSH_MODEL=""
DEVICE_TYPE=""
MODEL_TYPE=TNN
USE_NCNN_MODEL=0
ARM="ON"
OPENCL="OFF"


WORK_DIR=`pwd`
BENCHMARK_MODEL_DIR=$WORK_DIR/../benchmark-model
BUILD_DIR=build
ANDROID_DIR=/data/local/tmp/tnn-benchmark
ANDROID_DATA_DIR=$ANDROID_DIR/benchmark-model
OUTPUT_LOG_FILE=benchmark_models_result.txt
LOOP_COUNT=16
WARM_UP_COUNT=8

benchmark_model_list=(
#test.tnnproto \
resnet50.tnnproto        \
mobilenet_v1.tnnproto    \
mobilenet_v2.tnnproto    \
squeezenet_v1.0.tnnproto \
)

function usage() {
    echo "usage: ./benchmark_models.sh  [-32] [-c] [-b] [-f] [-d] <device-id> [-t] <CPU/GPU>"
    echo "options:"
    echo "        -32   Build 32 bit."
    echo "        -c    Clean up build folders."
    echo "        -b    build targets only"
    echo "        -f    build profiling targets "
    echo "        -d    run with specified device"
    echo "        -t    CPU/GPU specify the platform to run"
}

function exit_with_msg() {
    echo $1
    exit 1
}

function clean_build() {
    echo $1 | grep "$BUILD_DIR\b" > /dev/null
    if [[ "$?" != "0" ]]; then
        exit_with_msg "Warnning: $1 seems not to be a BUILD folder."
    fi
    rm -rf $1
    mkdir $1
}

function build_android_bench() {
    if [ "-c" == "$CLEAN" ]; then
        clean_build $BUILD_DIR
    fi
    mkdir -p build
    cd $BUILD_DIR
    cmake ../../.. \
          -DCMAKE_BUILD_TYPE=Release \
          -DTNN_ARM_ENABLE:BOOL=ON \
          -DTNN_OPENCL_ENABLE:BOOL=OFF \
          -DTNN_OPENMP_ENABLE:BOOL=ON \
          -DTNN_TEST_ENABLE:BOOL=ON \
          -DTNN_BENCHMARK_MODE:BOOL=ON \
          -DTNN_PROFILER_ENABLE:BOOL=${PROFILING} \
          -DTNN_BUILD_SHARED:BOOL=$SHARED_LIB \
          -DBUILD_FOR_ANDROID_COMMAND=true
    make -j4
}

function bench_android() {
    build_android_bench

    if [ $? != 0 ];then
        exit_with_msg "build failed"
    fi

    if [ "" != "$BUILD_ONLY" ]; then
        echo "build done!"
        exit 0
    fi

    mkdir -p $ANDROID_DIR

    if [ ${#benchmark_model_list[*]} == 0 ];then
        benchmark_model_list=`ls *.tnnproto`
    fi

    if [ "$DEVICE_TYPE" != "GPU" ] && [ "$DEVICE_TYPE" != "CPU" ];then
        DEVICE_TYPE=""
    fi

    if [ "ON" == $PROFILING ]; then
        WARM_UP_COUNT=5
        LOOP_COUNT=1
    fi


    echo '' >> $ANDROID_DIR/$OUTPUT_LOG_FILE
    date  >> $ANDROID_DIR/$OUTPUT_LOG_FILE

   
    cat ${WORK_DIR}/$OUTPUT_LOG_FILE
}

while [ "$1" != "" ]; do
    case $1 in
        -32)
            shift
            ABI="armeabi-v7a with NEON"
            ;;
        -c)
            shift
            CLEAN="-c"
            ;;
        -b)
            shift
            BUILD_ONLY="-b"
            ;;
        -f)
            shift
            PROFILING="ON"
            ;;
        -d)
            shift
            ADB="adb -s $1"
            shift
            ;;
        -t)
            shift
            DEVICE_TYPE="$1"
            shift
            ;;
        -n)
            shift
            MODEL_TYPE=NCNN
            ;;
        *)
            usage
            exit 1
    esac
done

bench_android



2、全网络性能分析

执行脚本:

./benchmark_models.sh -c

编译成功后,继续执行:

cd build/test
./TNNTest -mp ../../../benchmark-model/mobilenet_v2.tnnproto -dt ARM

在这里插入图片描述



3、逐层性能分析

执行脚本:

./benchmark_models.sh -c -f

编译成功后,继续执行:

cd build/test
./TNNTest -mp ../../../benchmark-model/mobilenet_v2.tnnproto -dt ARM

在这里插入图片描述



六、下一篇ArmLinux平台耗时测试用到的源文件test.cc



版权声明:本文为weixin_38952721原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。