Torch C++开发环境与第一个程序
头文件
编译环境
CMakeLists.txt
cmake_minimum_required
(VERSION 3.16
)
project
(main
)
set
(CMAKE_PREFIX_PATH
"C:/libtorch")
find_package
(Torch REQUIRED
)
set
(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FALAG} ${TORCH_CXX_FLAGS}")
add_executable
(main main.cpp
)
target_link_libraries
(main
"${TORCH_LIBRARIES}")
set_property
(TARGET main PROPERTY CXX_STANDARD 14
)
build.bat命令文件
@rem 创建工程构建工作目录
@mkdir build
@rem 进入构建目录
@cd build
@rem 生成本地工程
@cmake
..
@rem 直接编译visual studio工程
@cmake
--build
. --config Debug
@rem 拷贝执行文件到图像所在目录
@copy
.\Debug\main
.exe
..\
@rem 回到代码目录
@cd
..
CMake的详细使用参考博主的另一篇博客
https://blog.csdn.net/Luo_LA/article/details/107057414
Torch C++编程
Torch C++ 文档
https://pytorch.org/cppdocs/api/library_root.html
at命名空间Tensortorch命名:数据集/模型/函数/优化器
数据集
MNIST Dataset
auto ds_train
= torch
::data
::datasets
::MNIST(".\\data", torch
::data
::datasets
::MNIST
::Mode
::kTrain
);
auto ds_valid
= torch
::data
::datasets
::MNIST(".\\data", torch
::data
::datasets
::MNIST
::Mode
::kTest
);
torch
::data
::transforms
::Normalize
<> norm(0.1307, 0.3081);
torch
::data
::transforms
::Stack
<> stack
;
数据集批次处理
DataLoader
auto n_train
= ds_train
.map(norm
);
auto s_train
= ds_train
.map(stack
);
auto train_loader
= torch
::data
::make_data_loader
<torch
::data
::samplers
::SequentialSampler
>(std
::move(s_train
), 10);
auto n_valid
= ds_valid
.map(norm
);
auto s_valid
= ds_valid
.map(stack
);
auto valid_loader
= torch
::data
::make_data_loader
<torch
::data
::samplers
::SequentialSampler
>(std
::move(s_valid
), 10);
Lenet-5 模型
代码
class Lenet5 : public torch
::nn
::Module
{
private:
torch
::nn
::Conv2d conv1
;
torch
::nn
::Conv2d conv2
;
torch
::nn
::Conv2d conv3
;
torch
::nn
::Linear fc1
;
torch
::nn
::Linear fc2
;
public:
Lenet5():
conv1(torch
::nn
::Conv2dOptions(1, 6, 5).stride(1).padding(2)),
conv2(torch
::nn
::Conv2dOptions(6, 16, 5).stride(1).padding(0)),
conv3(torch
::nn
::Conv2dOptions(16, 120, 5).stride(1).padding(0)),
fc1(120, 84),
fc2(84, 10){
register_module("conv1", conv1
);
register_module("conv2", conv2
);
register_module("conv3", conv3
);
register_module("fc1", fc1
);
register_module("fc2", fc2
);
}
torch
::Tensor
forward(torch
::Tensor x
){
x
= conv1
->forward(x
);
x
= torch
::max_pool2d(x
, 2);
x
= torch
::relu(x
);
x
= conv2
->forward(x
);
x
= torch
::max_pool2d(x
, 2);
x
= torch
::relu(x
);
x
= conv3
->forward(x
);
x
= torch
::relu(x
);
x
= x
.view({-1, 120});
x
= fc1
->forward(x
);
x
= torch
::relu(x
);
x
= fc2
->forward(x
);
return torch
::log_softmax(x
, 1);
}
};
输出模型
std
::shared_ptr
<Lenet5
> model
= std
::make_shared
<Lenet5
>();
for(auto &batch
: *train_loader
){
auto data
= batch
.data
;
auto target
= batch
.target
;
data
= data
.view({-1, 1, 28, 28});
auto pred
= model3
->forward(data
);
auto digit
= pred
.argmax(1);
std
::cout
<< "预测:\n" << digit
<< std
::endl
;
std
::cout
<< "真实:\n" << target
<< std
::endl
;
break;
}
训练
在main函数中定义模型对象和优化器:
std
::shared_ptr
<Lenet5
> model
= std
::make_shared
<Lenet5
>();
torch
::optim
::Adam optimizer
= torch
::optim
::Adam(model
->parameters(), torch
::optim
::AdamOptions(0.001));
代码
template <typename DataLoader
>
void train(std
::shared_ptr
<Lenet5
> &model
, DataLoader
&loader
, torch
::optim
::Adam
&optimizer
){
model
->train();
int n
= 0;
for(torch
::data
::Example
<torch
::Tensor
, torch
::Tensor
> &batch
: loader
){
torch
::Tensor data
= batch
.data
;
auto target
= batch
.target
;
optimizer
.zero_grad();
torch
::Tensor y
= model
->forward(data
);
torch
::Tensor loss
= torch
::nll_loss(y
, target
);
loss
.backward();
optimizer
.step();
std
::cout
<< "\t|--批次:" << std
::setw(2) << std
::setfill(' ')<< ++n
<< ",\t损失值:" << std
::setw(8) << std
::setprecision(4) << loss
.item
<float>() << std
::endl
;
}
}
验证与测试
代码
template <typename DataLoader
>
void valid(std
::shared_ptr
<Lenet5
> &model
, DataLoader
&loader
) {
model
->eval();
torch
::NoGradGuard no_grad
;
double sum_loss
= 0.0;
int32_t num_correct
= 0;
int32_t num_samples
= 0;
for(const torch
::data
::Example
<> &batch
: loader
){
auto data
= batch
.data
;
auto target
= batch
.target
;
num_samples
+= data
.sizes()[0];
auto y
= model
->forward(data
);
auto pred
= y
.argmax(1);
sum_loss
+= torch
::nll_loss(y
, target
, {}, at
::Reduction
::Sum
).item
<double>();
num_correct
+= pred
.eq(target
).sum().item
<int32_t>();
}
std
::cout
<< std
::setw(8) << std
::setprecision(4)
<< "平均损失值:" << sum_loss
/ num_samples
<< ",\t准确率:" << 100.0 * num_correct
/ num_samples
<< " %" << std
::endl
;
}
在主函数中调用训练与测试函数
std
::cout
<< "开始训练" << std
::endl
;
int epoch
= 1;
int interval
= 1;
for(int e
= 0; e
< epoch
; e
++){
std
::printf("第d轮训练\n", e
+1);
train(model
, *train_loader
, optimizer
);
if (e
% interval
== 0){
valid(model
, *valid_loader
);
}
}
std
:: cout
<< "训练结束" << std
::endl
;
运行展示
训练一轮的部分结果展示
模型保存
torch
::save(model
, "lenet5.pt");
模型加载与识别
代码
int main(){
const char * data_filename
= ".\\data";
std
::shared_ptr
<Lenet5
> model
= std
::make_shared
<Lenet5
>();
torch
::load(model
, "lenet5.pt");
auto imgs
= torch
::data
::datasets
::MNIST(data_filename
, torch
::data
::datasets
::MNIST
::Mode
::kTest
);
for(int i
= 0; i
< 10; i
++){
torch
::data
::Example
<> example
= imgs
.get(i
);
torch
::Tensor a_img
= example
.data
;
a_img
= a_img
.view({-1, 1, 28, 28});
torch
::Tensor y
= model
->forward(a_img
);
int32_t result
= y
.argmax(1).item
<int32_t>();
std
::cout
<< "识别的结果是:" << result
<< "->" << example
.target
.item
<int32_t>() << std
::endl
;
}
std
::cout
<< "------------------------------" << std
::endl
;
cv
::Mat im
= cv
::imread("5.png");
std
::cout
<< im
.size() << std
::endl
;
im
.convertTo(im
, CV_32FC1
, 1.0f / 255.0f);
torch
::Tensor t_img
= torch
::from_blob(im
.data
, {28, 28});
t_img
= t_img
.view({-1, 1, 28, 28});
torch
::Tensor y_
= model
->forward(t_img
);
int32_t pred
= y_
.argmax(1).item
<int32_t>();
std
::cout
<< "识别的结果是:" << pred
<< std
::endl
;
return 0;
}
运行展示