博主曾經在試過用C++調用tensorflow模型失敗后棄坑,選擇了C++調用Pytorch模型,雖然也是一路踩坑,但是最終結果還是成功了,固在此記錄一下。
step1:
下載pytorch:可以根據官網自行選擇符合自己電腦和環境的pytorch版本
下載libtorch(一個讓pytorch模型能被C++調用的庫):最好選擇和pytorch版本一樣的libtorch,否則好像也會也版本兼容問題(但是博主torch版本1.0.0,libtorch1.7.1也能用= = )
step2:
訓練pytorch模型,博主這兒訓練了一個簡單的二分類模型,輸入是一個float類型的一維數據,輸出是一個二維的tensor張量,分別代表該數據為類型0/1的概率
導出對應的torch script:
traced_script_module = torch.jit.trace(net, text) # 保存模型 traced_script_module.save("D:/***/torch_script_eval.pt")
step3:
在clion中,調用torch_script_eval.pt,這兒來了第一個坑
在使用如下語句調用模型時報錯找不到路徑:
string path ="D:/***/torch_script_eval.pt"; torch::jit::Module module = torch::jit::load(path);
博主的代碼和模型都是放在D盤中,但是我通過Clion的wsl功能,鏈接到了windows的Ubuntu子系統下,用了子系統的環境,包括libtorch都是放在子系統中的,
所以這里的訪問路徑並不能直接從D盤訪問,而是應該站在子系統的角度去訪問,改為如下就能成功找到模型了:
string path ="/mnt/d/***/torch_script_eval.pt"; torch::jit::Module module = torch::jit::load(path);
step4:
博主想使用我訓練時候的數據,進行測試,看看在c++中調用和python調用結果是否一樣,於是我從csv文件中訪問我的數據,並且進行測試
// 加載數據 string pos_path="/mnt/d/***/data/pos_test1.csv"; string neg_path="/mnt/d/***/data/neg_test1.csv"; ifstream fin(neg_path); //打開文件流操作 string line = ""; vector<torch::jit::IValue> inputs; while (getline(fin, line)) //整行讀取,換行符“\n”區分,遇到文件尾標志eof終止讀取 { // line 中格式為string,且末尾帶一個'\r' istringstream sin(line); //將整行字符串line讀入到字符串流istringstream中 getline(sin,line,'\r'); //第一行是id,跳過 if( strcmp(line.c_str(),"id") == 0) continue; float tmp; tmp = atof(line.c_str()); torch::Tensor test = torch::ones({1, 1}); at::Tensor t = test.variable_data(); t[0][0] = tmp; //將一個數字改為一個1*1的tensor矩陣 inputs.clear(); inputs.push_back(test); at::Tensor output = module.forward(inputs).toTensor();//輸出兩個數字,分別對應類別0/1的分數 cout << output<<endl; }
這是最終成功的代碼,其中的重點在於,我需要向module.forward()方法中輸入一個vector,且vector中的數據結構應該是一個1*1的tensor張量(沒錯,坑在這兒,要如何將自己的一個一維float數據轉換為1*1的tensor張量,博主在坑里躺了很久)
最終采用的方法如下:
float tmp; tmp = atof(line.c_str()); torch::Tensor test = torch::ones({1, 1}); at::Tensor t = test.variable_data(); t[0][0] = tmp; //將一個數字改為一個1*1的tensor矩陣
這個方法可能不是最優的,但是確實是有效地
先用torch::ones({1,1})方法,生成一個1*1的tensor張量,當然,里面的數據是1
然后將test.variable_data()賦值給一個at::Tensor類,據說at::Tensor類才可以修改里面的數據,torch::Tensor不可以
然后at::Tensor支持隨機訪問,此時用t[0][0]=tmp,就成功構造了一個1*1的一維張量,且里面包含的數據是自己定義的數據啦