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加上括號
測試結果: