最近看到一個巨牛的人工智能教程,分享一下給大家。教程不僅是零基礎,通俗易懂,而且非常風趣幽默,像看小說一樣!覺得太牛了,所以分享給大家。平時碎片時間可以當小說看,【點這裏可以去膜拜一下大神的“小說”】。
請先閱讀我的上一篇文章《Visual Studio 2017 配置OpenVINO開發環境》,在VS2017中配置好OpenVINO環境。
1 模型轉換
1.1安裝模型轉換工具
打開conda
控制檯,創建虛擬環境vino
:
conda create -n vino python=3.6
創建完成後,執行activate vino
。然後安裝OpenVINO
模型轉換工具,具體命令如下:
> activate vino
> cd E:\OpenVINO\openvino_2019.3.334\deployment_tools\model_optimizer
> pip install -r requirements_tf.txt
1.2 模型轉換
以MobileNet
爲例,前往https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet_v1.md
下載MobileNet_v1_1.0_224
模型,解壓到目錄E:\model
後,對mobilenet_v1_1.0_224_frozen.pb
執行如下命令完成模型轉換:
python E:\OpenVINO\openvino_2019.3.334\deployment_tools\model_optimizer\mo_tf.py --input_model mobilenet_v1_1.0_224_frozen.pb --input_shape [1,224,224,3] --output MobilenetV1/Logits/Conv2d_1c_1x1/Conv2D --mean_values [127.5,127.5,127.5] --scale_values [127.5,127.5,127.5]
參數介紹:
--input_model :指定輸入模型路徑
--input_shape :指定模型的輸入Tensor的shape,如果不指定,則會自動從pb中讀取
--output :指定輸出節點名稱,如果不指定,會自動從圖中提取。注意,這裏由於openVINO不支持squeeze層,所以我們主動指定squeeze的上一層即:MobilenetV1/Logits/Conv2d_1c_1x1/Conv2D,獲取每一層名稱的方法:可以先不指定output,會自動導出xml,從xml中即可看到每一層名稱。
--scale_values :指定數據預處理的scale係數
--mean_values: 指定數據預處理的mean係數
除了上面參數外,還有一些其他常用的參數:
--data_type: 指定計算類型,可以選擇全浮點和半浮點,可選參數爲:{FP16,FP32,half,float}
注意,scale_values參數和mean_values參數一般用於輸入Tensor預處理,更常見的就是歸一化。假設輸入Tensor名稱爲in_tensor,經過預處理後,輸出Tensor爲out_tensor,其計算公式如下:
out_tensor = (in_tensor-mean_values)/scale_values
例如,需要將輸入歸一化爲[-1,1],則mean_values取值爲[127.5,127.5,127.5]且scale_values取值爲[127.5,127.5,127.5]
完成後,在E:\model
目錄中生成如下三個文件:
其中bin
文件是模型參數,xml
文件是網絡結構,mapping
文件是模型轉換前後計算節點映射關係。我們主要用bin
和xml
文件。
注意,如果轉換過程中出錯了,可以嘗試卸載Tenorflow,可能是因爲Tensorflow版本問題,改爲Tensorflow1.14-cpu版本,筆者這邊使用1.14-cpu版本沒有問題。
2 VS2017運行
2.1 環境配置
主要用到OpenVINO和OpenCV環境,OpenCV用於讀取圖片,OpenVINO用於運行模型。
- 參考我的上一篇文章【Visual Studio 2017 配置OpenVINO開發環境】配置好openVINO環境。
- 參考我的另一篇文章【OpenCV 3.2.0 opencv_contrib VS2017】配置好OpenCV環境。
注意:如果懶得配置,可以從附件中下載筆者已經搭建好的環境,可直接用VS2017打開運行
2.2 代碼實現
將E:\model
拷貝到項目根目錄,輸入以下代碼。
#include <inference_engine.hpp>
#include <iostream>
#include <string>
#include <vector>
#include <opencv2/opencv.hpp>
using namespace InferenceEngine;
using namespace std;
string inputName;
string outputName;
InferRequest inferReq;
vector<wstring> labels;
//初試化模型相關參數
void initModel(string xml,string bin,string plugin="plugins.xml") {
try {
Core ie(plugin);
CNNNetReader network_reader;
network_reader.ReadNetwork(xml);
network_reader.ReadWeights(bin);
network_reader.getNetwork().setBatchSize(1);
CNNNetwork network = network_reader.getNetwork();
InputInfo::Ptr input_info = network.getInputsInfo().begin()->second;
inputName = network.getInputsInfo().begin()->first;
input_info->getPreProcess().setResizeAlgorithm(RESIZE_BILINEAR);
input_info->setLayout(Layout::NCHW);
input_info->setPrecision(Precision::U8);
DataPtr output_info = network.getOutputsInfo().begin()->second;
outputName = network.getOutputsInfo().begin()->first;
output_info->setPrecision(Precision::FP32);
ExecutableNetwork executable_network = ie.LoadNetwork(network, "CPU");
inferReq = executable_network.CreateInferRequest();
}catch (const std::exception & ex) {
std::cerr << ex.what() << std::endl;
}
}
//Mat 轉Blob
void matU8ToBlob(const cv::Mat& orig_image, InferenceEngine::Blob::Ptr& blob, int batchIndex=0) {
InferenceEngine::SizeVector blobSize = blob->getTensorDesc().getDims();
const size_t width = blobSize[3];
const size_t height = blobSize[2];
const size_t channels = blobSize[1];
uint8_t* blob_data = blob->buffer().as<uint8_t*>();
cv::Mat resized_image(orig_image);
if (static_cast<int>(width) != orig_image.size().width ||
static_cast<int>(height) != orig_image.size().height) {
cv::resize(orig_image, resized_image, cv::Size(width, height));
}
int batchOffset = batchIndex * width * height * channels;
for (size_t c = 0; c < channels; c ) {
for (size_t h = 0; h < height; h ) {
for (size_t w = 0; w < width; w ) {
blob_data[batchOffset c * width * height h * width w] =
resized_image.at<cv::Vec3b>(h, w)[c];
}
}
}
}
//讀取label
void readLabel(string labelPath)
{
std::wstring_convert<std::codecvt_utf8<wchar_t>> conv;
ifstream in(labelPath.c_str());
string line;
if (in) { // 有該文件
while (getline(in, line)) { // line中不包括每行的換行符
wstring wb = conv.from_bytes(line);
labels.push_back(wb);
}
} else { // 沒有該文件
cout << "no such file:" << labelPath << endl;
}
}
//前向計算
wstring infer(cv::Mat rgb,float& rtP) {
Blob::Ptr imgBlob = inferReq.GetBlob(inputName);
matU8ToBlob(rgb, imgBlob);
inferReq.Infer();
Blob::Ptr output = inferReq.GetBlob(outputName);
float* logits = output->buffer().as<InferenceEngine::PrecisionTrait<InferenceEngine::Precision::FP32>::value_type*>();
int maxIdx = 0;
float maxP = 0;
int nclasses = labels.size();//1001類
float sum = 1;
//softmax
for (int i = 0; i < nclasses; i ) {
logits[i] = exp(logits[i]);
sum = sum logits[i];
if (logits[i] > maxP) {
maxP = logits[i];
maxIdx = i;
}
}
rtP = maxP / sum;
return labels[maxIdx];
}
//測試
int main()
{
string xml = "../model/mobilenet_v1_1.0_224_frozen.xml";
string bin = "../model/mobilenet_v1_1.0_224_frozen.bin";
string plugin = "../model/plugins.xml";
string label = "../model/labels.txt";
string testImg = "../model/test.png";
initModel(xml, bin, plugin);
readLabel(label);
cv::Mat test = cv::imread(testImg);
cv::Mat rgb;
cv::cvtColor(test,rgb, cv::COLOR_BGR2RGB);
float p;
wstring cls = infer(rgb, p);
std::wcout.imbue(std::locale("chs"));
wcout << "類別:" << cls << ",概率:" << p << endl;
}
readLabel函數讀取label信息,用於將模型識別出的最大概率類別對應的中文文字,測試圖片如下:
運行後,結果如下:
軍用飛機,0.927341
3 附件下載
可以從【附件】中下載所有相關文件,直接用VS2017打開即可,注意只能用x64模式運行,openVNO目前不支持x86。另外,如果CSDN下載沒有積分,或者是下載鏈接出錯,可直接加羣:824420877,聯繫羣主免費獲取代碼。