Windows Tensorflow C++ API 對mnist模型的測試

1.API自身沒編譯成,至少Debug版本沒編譯成,Release版本沒試過。

很多教程都是linux或者mac系統編譯的,windows上編譯的教程找了一些,但很多提醒(包括評論)坑很多,我自然期待就很低,再加上我編譯時跟找到的教程總有一些版本上的出入,最後實在沒信心編譯出來,就放棄了。

2.使用的是別人事先源碼編譯出來的庫,感謝。參考鏈接:windows下編譯tensorflow源碼 用其c++接口調用訓練好的模型

github倉庫地址:fo40225 / tensorflow-windows-wheel

由於電腦拉跨,下載的是tensorflow1.10.0CPU版本的C++ API

以下測試流程參考鏈接:用keras訓練模型並用Tensorflow的C++API調用模型

2.1 訓練keras模型

使用google colaboratory進行訓練,由於採用tensorflow1.10.0 C++API,所以使用tensorflow1.10.0和keras2.2.0進行訓練(參考鏈接:TensorFlow C++ on Windows: Executor failed to create kernel

在google colaboratory上卸載原裝的tensorflow和keras,換上指定版本的庫:

!pip uninstall tensorflow
!pip uninstall keras
!pip install tensorflow==1.10.0
!pip install keras==2.2.0

聲明使用版本和檢查版本

%tensorflow_version 1.10
import tensorflow as tf
import keras
print(tf.__version__, keras.__version__)

連接google硬盤

from google.colab import drive
drive.mount('/content/drive')

轉至工作目錄

import os
os.chdir('drive/My Drive/test')
!ls

訓練模型

from tensorflow.examples.tutorials.mnist import *
from keras.models import *
from keras.layers import *
import numpy as np

mnist = input_data.read_data_sets('MNIST_data/', one_hot=True)
train_X = mnist.train.images
train_Y = mnist.train.labels
test_X = mnist.test.images
test_Y = mnist.test.labels
train_X = train_X.reshape((55000, 28, 28, 1))
test_X = test_X.reshape((test_X.shape[0], 28, 28, 1))

print('type of train_X: ', type(train_X))
print('size of train_X: ', np.shape(train_X))
print('train_X: ', train_X)

print('type of train_Y: ', type(train_Y))
print('size of train_Y: ', np.shape(train_Y))
print('train_Y: ', train_Y)

print('num of test: ', test_X.shape[0])

model = Sequential()
model.add(Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1), padding='same'))
model.add(MaxPool2D(pool_size=(2, 2)))
model.add(Dropout(0.5))
model.add(Conv2D(64, (3, 3), activation='relu', padding='same'))
model.add(MaxPool2D(pool_size=(2, 2)))
model.add(Dropout(0.5))
model.add(Conv2D(128, (3, 3), activation='relu', padding='same'))
model.add(MaxPool2D(pool_size=(2, 2)))
model.add(Dropout(0.5))
model.add(Flatten())
model.add(Dense(625, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(10, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='adadelta', metrics=['accuracy'])
epoches = 20
model.fit(train_X, train_Y, batch_size=32, epochs=epoches)
accuracy = model.evaluate(test_X, test_Y, batch_size=20)
print('\nTest accuracy: ', accuracy[1])
save_model(model,'my_model_ep{}_10_22.h5'.format(epoches))

2.2 將h5文件轉換爲tensorflow api能識別的pb文件

from keras.models import load_model
import tensorflow as tf
from keras import backend as K
from tensorflow.python.framework import graph_io

def freeze_session(session, keep_var_names=None, output_names=None, clear_devices=True):
  from tensorflow.python.framework.graph_util import convert_variables_to_constants
  graph = session.graph
  with graph.as_default():
    freeze_var_names = list(set(v.op.name for v in tf.global_variables()).difference(keep_var_names or []))
    output_names = output_names or []
    output_names += [v.op.name for v in tf.global_variables()]
    input_graph_def = graph.as_graph_def()
    if clear_devices:
      for node in input_graph_def.node:
        node.device=''
    frozen_graph = convert_variables_to_constants(session, input_graph_def, output_names, freeze_var_names)
    return frozen_graph
epochs = 20
h5_model_path = './my_model_ep{}_10_22.h5'.format(epochs)
output_path = '.'
pb_model_name = 'my_model_ep{}_10_22.pb'.format(epochs)
K.set_learning_phase(0)
net_model = load_model(h5_model_path)
print('input is: ', net_model.input.name)
print('output is: ', net_model.output.name)
sess = K.get_session()
frozen_graph = freeze_session(K.get_session(), output_names=[net_model.output.op.name])
graph_io.write_graph(frozen_graph, output_path, pb_model_name, as_text=False)

在這一階段記下輸入節點和輸出節點的名字,之後在圖計算時會用到

2.3 檢查h5模型的識別結果

import os
import cv2
import numpy as np
from keras.models import load_model
from google.colab.patches import cv2_imshow
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
new_model = load_model('my_model_ep20_10_22.h5')
src = cv2.imread('6.png')
cv2_imshow(src)
src = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
dst = cv2.resize(src, (28, 28))
dst = dst.astype(np.float32)
picture = 1-dst/255
picture = np.reshape(picture, (1, 28, 28, 1))
y = new_model.predict(picture)
print('softmax: ')
for i, prob in enumerate(y[0]):
  print('class{}, Prob: {}'.format(i, prob))
result = np.argmax(y)
print('num is: ', result)
print('prob is: ', np.max(y[0]))
cv2.waitKey()

最後結果:一張數字6的測試圖片

softmax: class0, Prob: 0.03252997621893883 class1, Prob: 0.004192057531327009 class2, Prob: 0.007209540344774723 class3, Prob: 0.010383786633610725 class4, Prob: 0.04903695359826088 class5, Prob: 0.07620254904031754 class6, Prob: 0.7160110473632812 class7, Prob: 0.0008604172617197037 class8, Prob: 0.07261374592781067 class9, Prob: 0.030959874391555786 num is: 6 prob is: 0.71601105

2.4 pb文件的識別結果

import tensorflow as tf
import numpy as np
def recognize(jpg_path, pb_file_path):
  with tf.Graph().as_default():
    output_graph_def = tf.GraphDef()
    with open(pb_file_path, 'rb') as f:
      output_graph_def.ParseFromString(f.read())
      tensors = tf.import_graph_def(output_graph_def, name='')
      print('tensors: ', tensors)
    with tf.Session() as sess:
      init = tf.global_variables_initializer()
      sess.run(init)
      op = sess.graph.get_operations()
      for i, m in enumerate(op):
        print('op{}: '.format(i), m.values())
      input_x = sess.graph.get_tensor_by_name('conv2d_1_input_1:0')
      print('input_X: ', input_x)
      out_softmax = sess.graph.get_tensor_by_name('dense_2_1/Softmax:0')
      print('Output:', out_softmax)
      img = cv2.imread(jpg_path, 0)
      img = cv2.resize(img, (28, 28))
      img = img.astype(np.float32)
      img = 1-img/255
      print('img data type: ', img.dtype)
      for row in range(28):
        for col in range(28):
          if col!=27:
            print(img[row][col], ' ', end='')
          else:
            print(img[row][col])
      img_out_softmax = sess.run(out_softmax, feed_dict={input_x: np.reshape(img, (1, 28, 28, 1))})
      print('img_out_softmax: ', img_out_softmax)
      for i, prob in enumerate(img_out_softmax[0]):
        print('class{} prob: {}'.format(i, prob))
      prediction_labels = np.argmax(img_out_softmax, axis=1)
      print('Final class is: ', prediction_labels)
      print('prob of label: ', img_out_softmax[0, prediction_labels])
pb_path = './my_model_ep20_10_22.pb'
img = '6.png'
recognize(img, pb_path)

最後結果:img_out_softmax: [[0.03252998 0.00419206 0.00720954 0.01038379 0.04903695 0.07620255 0.71601105 0.00086042 0.07261375 0.03095987]] class0 prob: 0.03252997621893883 class1 prob: 0.004192057531327009 class2 prob: 0.007209540344774723 class3 prob: 0.010383786633610725 class4 prob: 0.04903695359826088 class5 prob: 0.07620254904031754 class6 prob: 0.7160110473632812 class7 prob: 0.0008604172617197037 class8 prob: 0.07261374592781067 class9 prob: 0.030959874391555786 Final class is: [6] prob of label: [0.71601105]

綜合2.3和2.4結果可知,模型在keras和tensorflow環境下表達結果一致,是預期的。可以使用,於是打開VS進行第2.5步

2.5 C++ API測試

dll是Release版本,所以設定項目在x64,release環境下運行。

環境設定和一般情況一樣,所以我這裏就不說了,下面說一下代碼以及庫代碼的一處錯誤。

代碼:

#include <opencv2/core.hpp>
#include <tensorflow/core/framework/tensor.h>
#include <opencv2/imgproc.hpp>
#include <tensorflow/core/public/session.h>
#include <tensorflow/stream_executor/lib/status.h>
#include <opencv2/imgcodecs.hpp>

void CVMat_to_Tensor(cv::Mat img, tensorflow::Tensor* output_tensor, int input_rows, int input_cols) {
	cv::resize(img, img, cv::Size(input_cols, input_rows));
	img.convertTo(img, CV_32FC1);
	img = 1 - img / 255;
	float *p = output_tensor->flat<float>().data();
	cv::Mat tempMat(input_rows, input_cols, CV_32FC1, p);
	img.convertTo(tempMat, CV_32FC1);
}

int main(int argc, char** argv) {
	tensorflow::GraphDef graphdef;
	std::string model_path = "my_model_ep20_10_22.pb";
	std::string image_path = "6.png";
	int input_height = 28;
	int input_width = 28;
	std::string input_tensor_name = "conv2d_1_input_1:0";
	std::string output_tensor_name = "dense_2_1/Softmax:0";
	tensorflow::Session* session;
	tensorflow::Status status = tensorflow::NewSession(tensorflow::SessionOptions(), &session);
	tensorflow::Status status_load = tensorflow::ReadBinaryProto(tensorflow::Env::Default(), model_path, &graphdef);
	if (!status_load.ok()) {
		std::cout << "ERROR: Loading model failed..." << model_path << std::endl;
		std::cout << status_load.ToString() << "\n";
		system("pause");
		return -1;
	}
	tensorflow::Status status_create = session->Create(graphdef);
	if (!status_create.ok()) {
		std::cout << "ERROR: Creating graph in session failed..." << status_create.ToString() << std::endl;
		system("pause");
		return -1;
	}
	std::cout << "<----Successfully created session and load graph.---->" << std::endl;
	std::cout << std::endl << "<----loading test_img---->" << std::endl;
	cv::Mat img = cv::imread(image_path, 0);
	if (img.empty()) {
		std::cout << "can't open the image!!!" << std::endl;
		system("pause");
		return -1;
	}
	tensorflow::Tensor resized_tensor(tensorflow::DT_FLOAT, tensorflow::TensorShape({ 1, input_height, input_width, 1 }));
	CVMat_to_Tensor(img, &resized_tensor, input_height, input_width);
	std::cout << resized_tensor.DebugString() << std::endl;
	std::cout << std::endl << "<----Running the model with test_image---->" << std::endl;
	std::vector<tensorflow::Tensor> outputs;
	std::string output_node = output_tensor_name;
	tensorflow::Status status_run = session->Run({ {input_tensor_name, resized_tensor} }, { output_node }, {}, &outputs);
	if (!status_run.ok()) {
		std::cout << "ERROR: RUN failed..." << std::endl;
		std::cout << status_run.ToString() << "\n";
		system("pause");
		return -1;
	}
	std::cout << "Output tensor size: " << outputs.size() << std::endl;
	for (std::size_t i = 0; i < outputs.size(); i++) {
		std::cout << outputs[i].DebugString() << std::endl;
	}
	tensorflow::Tensor t = outputs[0];
	auto tmap = t.tensor<float, 2>();
	int output_dim = t.shape().dim_size(1);
	int output_class_id = -1;
	double output_prob = 0.0;
	for (int j = 0; j < output_dim; j++) {
		std::cout << "Class " << j << " prob: " << tmap(0, j) << "," << std::endl;
		if (tmap(0, j) >= output_prob) {
			output_class_id = j;
			output_prob = tmap(0, j);
		}
	}
	std::cout << "Final class id: " << output_class_id << std::endl;
	std::cout << "Final class prob: " << output_prob << std::endl;
	system("pause");
	return 0;
}

庫代碼的一處錯誤:參考鏈接:C2589 “(”:“::”右邊的非法標記

原因:Visual C++ min/max的宏定義和源代碼中std::max/std::min衝突

解決方法:將std::max/std::min加上括號

測試結果:

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