在第五章中,使用官方的原版歷程初步摸索了一下NPU相關的測試方法,本章就開始介紹如何使用自己的模型并遷移到YY3568上面。在這個過程中,我選擇的并不是自己訓練模型,而是從 tensorflow 官網上下載預訓練好的模型,進行遷移測試。在這個過程中,碰到了一些問題,主要集中在:
- 如何使用 rknn-toolkit2 對 tensorflow 官網上面訓練好的模型https://tfhub.dev/google/imagenet/mobilenet_v2_035_224/classification/5mageNetLabels.txt 進行轉換
- rknn-toolkit2轉換后為什么和在原始的模型測試的不一致?
1. 使用 rknn-toolkit2 對 tensorflow 官網上面訓練好的模型進行轉換
在這一部分中,主要解決的是第一個問題,在這個過程中主要是解決 python 軟件包的依賴關系。過程如下:
因為我下載的是壓縮包文件,解壓之后如下所示:
? tree imagenet_mobilenet_v2_035_224_classification_5
imagenet_mobilenet_v2_035_224_classification_5
├── saved_model.pb
└── variables
├── variables.data-00000-of-00001
└── variables.index
2 directories, 3 files
可以看到是 pb 類型的文件,如何引用這個文件我使用的 tensorflow-hub ,這個過程還算順利,但是后續使用 rknn-toolkit2 對這個模型進行轉換時就犯難了,因為在這個過程需要使用到 pb 文件是數據類型的,而從 tensorflow 官網下載的是PDP-11 pure executable not stripped - version 5
類型的,就需要對這個類型進行轉換,這里就是重點了,我嘗試使用 Python3.8 發現一致無法轉換,提示錯誤,主要原因是部分功能不支持,但是我可以用 Python3.10 對這個 pb 文件進行轉換為 rknn 需要的 pb 文件,接下去就是安裝 rknn 在 Python3.10 上的依賴了,因為我發現上游的 rknn-toolkit2 已經支持 Python3.10 了不過是在 Ubuntu 上,我就嘗試在 Solus 上進行安裝,通過將簡單的依賴問題解決后,卡在了這里報錯:
但是文件 requirements_cp310-1.5.0.txt
明確寫的是這個版本的依賴,我去 pip 上查看這個軟件包,是這樣子的:
可以看到,明確寫的只支持到 Python3.7,最后我嘗試到上游查看這個軟件包的倉庫,發現最新的提交已經支持到 Python3.10 了:
然后,我就手動修改這個包的 setup.py 文件,補丁如下:
diff --git a/tensorflow_estimator/tools/pip_package/setup.py b/tensorflow_estimator/tools/pip_package/setup.py
index 2568d6a..862cd6b 100644
--- a/tensorflow_estimator/tools/pip_package/setup.py
+++ b/tensorflow_estimator/tools/pip_package/setup.py
@@ -30,14 +30,14 @@ DOCLINES = __doc__.split('n')
# This version string is semver compatible, but incompatible with pip.
# For pip, we will remove all '-' characters from this string, and use the
# result for pip.
-_VERSION = '2.8.0'
+_VERSION = '2.8.0.dev2021122109'
REQUIRED_PACKAGES = [
# We depend on TensorFlow's declared pip dependencies.
# Add a new dep there if one is needed.
]
-project_name = 'tensorflow_estimator'
+project_name = 'tf-estimator-nightly'
if '--project_name' in sys.argv:
project_name_idx = sys.argv.index('--project_name')
project_name = sys.argv[project_name_idx + 1]
最重要的一個步驟是編譯,這里就不能使用setup.py sdist
這樣的,需要參看倉庫的 README,根據描述打包軟件包,具體指令是:
bazel build //tensorflow_estimator/tools/pip_package:build_pip_package
bazel-bin/tensorflow_estimator/tools/pip_package/build_pip_package /tmp/estimator_pip
會在 /tmp 目錄下打出 wheel 包,進行安裝就可以了。安裝好所有依賴的軟件包后,使用 Python3.10 安裝 rknn_toolkit2-1.5.0+1fa95b5c-cp310-cp310-linux_x86_64.whl
就可以了。
? python3.10 -m pip install dist/tensorflow_estimator-2.8.0-py3-none-any.whl
Defaulting to user installation because normal site-packages is not writeable
Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple
Processing ./dist/tensorflow_estimator-2.8.0-py3-none-any.whl
Installing collected packages: tensorflow-estimator
Successfully installed tensorflow-estimator-2.8.0
接著就可以愉快地進行 pb 格式轉換了,我使用的代碼如下:
#!/usr/bin/python3.10
LABEL_PATH='./ImageNetLabels.txt'
MODEL_PATH='./imagenet_mobilenet_v2_035_224_classification_5'
CHECK_SRC='./goldfish_299x299.jpg'
import tensorflow as tf
import tensorflow_hub as hub
import numpy as np
import sys
from tensorflow.python.framework import convert_to_constants as _convert_to_constants
def h5_to_pb(h5_save_path):
model = tf.keras.models.load_model(h5_save_path, compile=False)
model.summary()
full_model = tf.function(lambda Input: model(Input))
full_model = full_model.get_concrete_function(tf.TensorSpec(model.inputs[0].shape, model.inputs[0].dtype))
# Get frozen ConcreteFunction
frozen_func = _convert_to_constants.convert_variables_to_constants_v2(full_model)
frozen_func.graph.as_graph_def()
layers = [op.name for op in frozen_func.graph.get_operations()]
print("-" * 50)
print("Frozen model layers: ")
for layer in layers:
print(layer)
print("-" * 50)
print("Frozen model inputs: ")
print(frozen_func.inputs)
print("Frozen model outputs: ")
print(frozen_func.outputs)
# Save frozen graph from frozen ConcreteFunction to hard drive
tf.io.write_graph(graph_or_graph_def=frozen_func.graph,
logdir="./frozen_models2",
name="model.pb",
as_text=False)
print("save mode with write_graph success")
def load_label_file(path):
f = open(path, 'r')
txt = []
for line in f.readlines():
curline=line.strip('n')
txt.append(curline)
#print(txt)
return txt
label_list = load_label_file(LABEL_PATH)
def load_and_format_img(path):
image = tf.io.read_file(path)
image = tf.image.decode_jpeg(image, channels=3)
image = tf.image.resize(image, [224, 224])
image /= 255.0
image_list = np.array(list(image))
image_list = image_list.reshape(1, 224, 224, 3)
return image_list
model_obj = hub.load(MODEL_PATH)
model = tf.keras.Sequential([hub.KerasLayer(model_obj)])
model.build([None, 224, 224, 3])
if len(sys.argv) > 1:
CHECK_SRC = sys.argv[1]
image4check = load_and_format_img(CHECK_SRC)
#print(image4check)
tf.saved_model.save(model, "imgnet.tf")
model.save("imgnet3.tf")
ans=model.predict(image4check, verbose=2)
print("--------------------------------begin--")
print(np.argmax(ans))
print(label_list[np.argmax(ans)])
print("--------------------------------end--")
h5_to_pb("imgnet3.tf")
可以看到,我在這里將從網上下載的模型 imagenet_mobilenet_v2_035_224_classification_5
轉換為了 ./frozen_models2/model.pb
文件,測試結果如下所示:
省略 ...
--------------------------------begin--
2
goldfish
--------------------------------end--
省略 ...
至此, rknn-toolkit2 可以使用的 tensorflow 的 pb 文件已經有了,下一步是遷移到 YY3568 上進行測試了。我參考的是文件 rknn-toolkit2/examples/tensorflow/inception_v3_qat/test.py
。進行修改后,最終內容如下:
#!/usr/bin/python3.10
import numpy as np
import cv2
import os
import urllib
import tarfile
import shutil
import traceback
import time
import sys
from rknn.api import RKNN
#PB_FILE = './imgnet.tf/saved_model.pb'
PB_FILE = './frozen_models2/model.pb'
RKNN_MODEL_PATH = './red_imagenet_mobilenet_v2_035_224_classification_5.rknn'
INPUTS = ['Input']
OUTPUTS = ['Identity']
IMG_PATH = './goldfish_224x224.jpg'
#IMG_PATH = './German-Shepherd_224x224.jpg'
#IMG_PATH = '/var/lib/nfs/yy3568/red_own_model/cat_224x224.jpg'
INPUT_SIZE = 224
def show_outputs(outputs):
output = outputs[0][0]
output_sorted = sorted(output, reverse=True)
top5_str = 'red imagenetn-----TOP 5-----n'
for i in range(5):
value = output_sorted[i]
index = np.where(output == value)
for j in range(len(index)):
if (i + j) >= 5:
break
if value > 0:
topi = '{}: {}n'.format(index[j], value)
else:
topi = '-1: 0.0n'
top5_str += topi
print(top5_str)
def readable_speed(speed):
speed_bytes = float(speed)
speed_kbytes = speed_bytes / 1024
if speed_kbytes > 1024:
speed_mbytes = speed_kbytes / 1024
if speed_mbytes > 1024:
speed_gbytes = speed_mbytes / 1024
return "{:.2f} GB/s".format(speed_gbytes)
else:
return "{:.2f} MB/s".format(speed_mbytes)
else:
return "{:.2f} KB/s".format(speed_kbytes)
def show_progress(blocknum, blocksize, totalsize):
speed = (blocknum * blocksize) / (time.time() - start_time)
speed_str = " Speed: {}".format(readable_speed(speed))
recv_size = blocknum * blocksize
f = sys.stdout
progress = (recv_size / totalsize)
progress_str = "{:.2f}%".format(progress * 100)
n = round(progress * 50)
s = ('#' * n).ljust(50, '-')
f.write(progress_str.ljust(8, ' ') + '[' + s + ']' + speed_str)
f.flush()
f.write('rn')
if __name__ == '__main__':
# Create RKNN object
rknn = RKNN(verbose=True)
# If inception_v3_quant_frozen.pb does not exist, download it.
# Download address:
# https://storage.googleapis.com/download.tensorflow.org/models/tflite_11_05_08/inception_v3_quant.tgz
if not os.path.exists(PB_FILE):
print('Oh no exist file ', PB_FILE)
exit
# Pre-process config
print('-- > Config model')
# 這里很重要,如果照抄 inception_v3_quant 的均值和標準值會導致分類有很大誤差!!!
rknn.config(mean_values=[0, 0, 0], std_values=[255, 255, 255], target_platform='rk3568')
print('done')
# Load model
print('-- > Loading model')
ret = rknn.load_tensorflow(tf_pb=PB_FILE,
inputs=INPUTS,
outputs=OUTPUTS,
input_size_list=[[1, INPUT_SIZE, INPUT_SIZE, 3]])
if ret != 0:
print('Load model failed!')
exit(ret)
print('done')
# Build model
print('-- > Building model')
ret = rknn.build(do_quantization=True, dataset='./red_quantization.txt')
if ret != 0:
print('Build model failed!')
exit(ret)
print('done')
# Export rknn model
print('-- > Export rknn model')
ret = rknn.export_rknn(RKNN_MODEL_PATH)
if ret != 0:
print('Export rknn model failed!')
exit(ret)
print('done')
# Set inputs
img = cv2.imread(IMG_PATH)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# Init runtime environment
print('-- > Init runtime environment')
ret = rknn.init_runtime()
if ret != 0:
print('Init runtime environment failed!')
exit(ret)
print('done')
# Inference
print('-- > Running model')
outputs = rknn.inference(inputs=[img])
np.save('./tensorflow_inception_v3_qat_0.npy', outputs[0])
x = outputs[0]
output = np.exp(x)/np.sum(np.exp(x))
outputs = [output]
show_outputs(outputs)
print('done')
rknn.release()
使用上述腳本進行測試,同樣是 goldfish 的圖片,結果如下:
省略 ...
red imagenet
-----TOP 5-----
[2]: 0.9564507007598877
[122]: 0.007486562244594097
[121]: 0.005268004257231951
[964]: 0.0018354274798184633
[119]: 0.0017108423635363579
done
可以看到也準確地識別出了 goldfish 的編號 2。
從圖中可以看到仿真模擬結果顯示的 Top5 內容,后續可以和在YY3568上實際運行的結果進行對比。
2. rknn-toolkit2轉換后為什么和在原始的模型測試的不一致?
針對 rknn-toolkit2轉換后為什么和在原始的模型測試的不一致?的問題,在上述代碼中,我已經添加了注釋,關鍵的處理在:rknn.config(mean_values=[0, 0, 0], std_values=[255, 255, 255])
這里,其中 meas_value 表示對應的均值, std_values 表示對應的標準值。看下文檔的描述會更清晰:
開始我照抄例程的這兩個參數導致 PC 原始模型的結果和 rknn-toolkit2 轉換之后的結果有很大誤差。這里解決了之后就是簡單在 YY3568 上使用 NPU 實際驗證了。因為到目前位置,我們已經有了 xxxx.rknn 的模型文件,也有需要分類的圖片,缺少一個在 YY3568 上運行的可執行程序,我使用的可執行程序是工程 external/rknpu2/examples/rknn_common_test
。編譯出來后,使用 nfs 掛載,在 YY3568 上測試,測試結果如下:
[root@RK356X:/media/red_nfs/yy3568/red_own_model/rknn_common_test_Linux]# ./rknn
_common_test ../red_imagenet_mobilenet_v2_035_224_classification_5_right.rknn ..
/goldfish_224x224.jpg
rknn_api/rknnrt version: 1.3.0 (9b36d4d74@2022-05-04T20:17:01), driver version: 0.7.2
model input num: 1, output num: 1
input tensors:
index=0, name=Input:0, n_dims=4, dims=[1, 224, 224, 3], n_elems=150528, size=150528, fmt=NHWC, type=INT8, qnt_type=AFFINE, zp=-128, scale=0.003922
output tensors:
index=0, name=Identity:0, n_dims=2, dims=[1, 1001, 0, 0], n_elems=1001, size=1001, fmt=UNDEFINED, type=INT8, qnt_type=AFFINE, zp=-66, scale=0.070292
custom string:
Begin perf ...
0: Elapse Time = 3.36ms, FPS = 297.53
---- Top5 ----
11.738696 - 2
7.029160 - 122
6.958868 - 121
5.693619 - 119
5.553036 - 964
可以看到也準確識別出來了類型編號為2。支持完成了自定義模型的測試以及遷移。
-
Linux
+關注
關注
87文章
11304瀏覽量
209458 -
開發板
+關注
關注
25文章
5047瀏覽量
97441 -
NPU
+關注
關注
2文章
284瀏覽量
18602 -
tensorflow
+關注
關注
13文章
329瀏覽量
60535 -
RK3568
+關注
關注
4文章
514瀏覽量
5047
發布評論請先 登錄
相關推薦
評論