Tensorflow2.x版本的模型保存(pd格式),opencv c++模型導入

Tensorflow2.x版本的模型保存,和opencv c++模型導入

由於一個小項目的需求,需要使用C++的接口調用python環境下使用Tensorflow 2.x版本訓練好的模型。我想了兩種方式:

  1. 使用 Tensorflow 2. x 的 C++ API 。(我覺得可行,在源碼編譯的最後一部,由於誤操作系統崩了…,後續會繼續嘗試)
  2. 使用opencv dnn 模塊提供的 API接口。(我使用的這種方式)

使用Tensorflow 訓練模型

我使用的是2.1版本,2.0應該也可以
這裏就用 fashion_mnist 數據集爲例進行訓練

導入的一些包:
import cv2
import os
import numpy as np
import tensorflow as tf
from tensorflow import keras
from tensorflow.python.framework.convert_to_constants import convert_variables_to_constants_v2
import matplotlib.pyplot as plt
os.environ["TF_CPP_MIN_LOG_LEVEL"] = '2'
print('tf版本:',tf.__version__)

輸出:tf版本: 2.1.0

導入數據集:

類別標註
每個訓練和測試樣本都按照以下類別進行了標註:
標註編號描述0T-shirt/top(T恤)1Trouser(褲子)2Pullover(套衫)3Dress(裙子)4Coat(外套)5Sandal(涼鞋)6Shirt(汗衫)7Sneaker(運動鞋)8Bag(包)9Ankle boot(踝靴)

(train_image, train_label), (test_image, test_label) = keras.datasets.fashion_mnist.load_data()
print("數據維度(訓練集):", train_image.shape, train_label.shape)
print("數據維度(測試集):", test_image.shape, test_label.shape)

train_data = tf.data.Dataset.from_tensor_slices((train_image, train_label))
train_data = train_data.map(prepprocess).batch(128)
test_data = tf.data.Dataset.from_tensor_slices((test_image, test_label))
test_data = test_data.map(prepprocess).batch(128)
class_name = ['T-shirt', 'Trouser', 'Pullover', 'Dress', 'Coat', 'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle Boot']
# 數據可視化
plt.figure(figsize=(10, 10))
for i in range(25):
	plt.subplot(5, 5, i + 1)
	plt.xticks([])
	plt.yticks([])
	plt.grid(False)
	plt.imshow(train_image[i], cmap=plt.cm.binary)
	plt.xlabel(class_name[train_label[i]])
plt.show()

輸出:

數據維度(訓練集): (60000, 28, 28) (60000,)
數據維度(測試集): (10000, 28, 28) (10000,)

在這裏插入圖片描述

訓練

這裏直接使用官方文檔上的那個網絡,比較簡單。

def prepprocess(x,y):
    # 歸一化
    x = tf.cast(x, dtype=tf.float32) / 255.
    x = tf.reshape(x, [28 * 28])
    y = tf.cast(y, dtype=tf.int32)
    y = tf.one_hot(y, depth=10)
    return x, y
#訓練
def train():
    # 構建網絡
    network = keras.Sequential([
        keras.layers.Dense(128,'relu'),
        keras.layers.Dense(10)
    ])
    network.build(input_shape=(None,28*28))
    network.summary()

    network.compile(optimizer=keras.optimizers.Adam(lr=0.01),
                  loss = tf.losses.CategoricalCrossentropy(from_logits=True),
                  # loss = keras.losses.SparseCategoricalCrossentropy(from_logits=True),      # 用這個不用tf.one_hot()
                  metrics=['accuracy']
    )
    # 訓練
    history = network.fit(train_data,epochs=15,validation_data=test_data,validation_freq=1)
    plt.plot(history.history['accuracy'],label='accuracy')
    plt.plot(history.history['val_accuracy'],label='val_accuracy')
    plt.xlabel('Epoch')
    plt.ylabel('accuracy')
    plt.ylim([0.5,1])
    plt.legend(loc='lower right')
    plt.show()
    tf.saved_model.save(network,'/home/tjk/project/tf_doc/model_save/fashion_10/') 
    print("保存模型成功")     

模型最後保存爲pd格式。

模型的opencv導入

使用下面的API:
在這裏插入圖片描述
開始直接就用這個函數加載模型,然後出錯了
然後我就在網上找找看有沒有解決方法,開始看到的都是一些關於TF 1.x 版本的模型導入,甚至還有標題2.0,結果內容卻是1.x的坑人版本。後來在下面的博客上找到了解決方法:

參考博客:

https://blog.csdn.net/ouening/article/details/104335552,博客中提到了解決方法的出處,
https://leimao.github.io/blog/Save-Load-Inference-From-TF2-Frozen-Graph/

按照給出的方法,使用dnn模塊的相關接口去調用是無法直接讀入模型的,需要對導出的模型進行轉換,需要保存爲frozen graph格式,
直接copy博客的中一段轉換代碼,主要是使用了tf.function,在TF的官方文檔看到下面的一段話:

In TensorFlow 2.0, eager execution is turned on by default. The user interface is intuitive and flexible (running one-off operations is much easier and faster), but this can come at the expense of performance and deployability.
To get peak performance and to make your model deployable anywhere, use tf.function to make graphs out of your programs. Thanks to AutoGraph, a surprising amount of Python code just works with tf.function, but there are still pitfalls to be wary of.

我的理解就是,opencv 的dnn 接口支持的是以前的圖模型,但是在tf2中,用的是eager execution,所以導出的模型,需要使用tf.function轉化。具體的東西,還在看文檔學習。

但是這裏有個問題就是在,TF2 的官方文檔中寫了, 下面有幾個API 會在後續的版本中遺棄

# Convert Keras model to ConcreteFunction
full_model = tf.function(lambda x: network(x))
full_model = full_model.get_concrete_function(
tf.TensorSpec(network.inputs[0].shape, network.inputs[0].dtype))

# Get frozen ConcreteFunction
frozen_func = 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_models",
		name="frozen_graph.pb",
		as_text=False)

經過上面的轉換,可以開始測試了。
一開始還是使用的python 版本的opencv進行的測試:

import cv2
from cv2 import dnn
import numpy as np

print(cv2.__version__)


class_name = ['T-shirt', 'Trouser', 'Pullover', 'Dress', 'Coat', 'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle Boot']
img = cv2.imread('/home/tjk/project/picture/aj1.png', cv2.IMREAD_GRAYSCALE)
cv2.imshow("2",img)
print(img.shape)
# print(1-img_cv21/255.)
blob = cv2.dnn.blobFromImage(1-img,     #  這裏對顏色進行反轉,這是訓練圖片存儲格式的問題,可以把數值打印出來看下一
                             scalefactor=1.0/225.,
                             size=(28, 28),
                             mean=(0, 0, 0),
                             swapRB=False,
                             crop=False)

print("[INFO]img shape: ", blob.shape)

net = dnn.readNetFromTensorflow('/home/tjk/project/tf_doc/frozen_models/frozen_graph.pb')
print("success!!")
net.setInput(blob)
out = net.forward()
out = out.flatten()

classId = np.argmax(out)
print("classId",classId)
print("預測結果爲:",class_name[classId])
cv2.waitKey(0)

使用的圖片是這個:AJ1,
在這裏插入圖片描述

給出了預測結果,爲Sneaker.
在這裏插入圖片描述
python 這邊運行成功了,下面就是c++了

#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/dnn.hpp>
using namespace cv;
using namespace std;
using namespace cv::dnn;
static string class_name[] = {"T-shirt", "Trouser", "Pullover",
                          "Dress", "Coat","Sandal", "Shirt", "Sneaker", "Bag", "Ankle Boot"};
int main()
{
     Mat frame = imread("/home/tjk/project/picture/aj1.png",0);
     imshow("1",frame);
//     cout<<frame.channels()<<endl;
     string path = "/home/tjk/project/tf_doc/frozen_models/frozen_graph.pb";
     Net net = readNetFromTensorflow(path);
     printf("模型加載成功");
     Mat frame_32F;
     frame.convertTo(frame_32F,CV_32FC1);
//   cout<<1-frame_32F/255.0<<endl;
     Mat blob = blobFromImage(1-frame_32F/255.0,
                              1.0,
                              Size(28,28),
                              Scalar(0,0,0));
//   cout<<(blob.channels());
     net.setInput(blob);
     Mat out = net.forward();
//     cout<<out.cols<<endl;
     Point maxclass;
     minMaxLoc(out, NULL, NULL, NULL, &maxclass);
     cout <<"預測結果爲:"<<class_name[maxclass.x] << endl;
     waitKey(0);
}

結果:與之前一樣。
在這裏插入圖片描述

這樣就完成了一次簡單的部署。之後嘗試tensorflow 的C++ API。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章