libtorch使用踩坑指南

一、使用背景

使用pytorch在python环境下,利用cuda训练前馈神经网络,生成神经网络文件(*.pt),在C++环境下,读取神经网络文件,通过不同输入,计算不同输出。

二、环境安装

环境主要分三种,电脑cuda版本,进行神经网络训练的pytorch和cuda版本,在C++环境中使用的libtorch版本。

2.1 本机cuda和cudnn安装

利用显卡训练速度更快,网络上关于显卡驱动环境安装的教程也比较多,主要参考以下文章:

Cuda和cuDNN安装教程(超级详细)-CSDN博客

Win10系统下不同版本cuda切换_windows cuda低版本替换高版本-CSDN博客

在win10中显卡驱动只能安装一个,但是可以同时安装多个版本的cuda,我安装了如下3个版本(好像对后续使用影响不大,最终使用的还是我最先安装的v12.1版本)

注意:安装时选择【自定义安装】,不要勾选【精简安装】,【精简安装】会覆盖原有显卡驱动,并且只勾选【cuda】,等待安装完毕,记得安装cudnn,即将cudnn内的文件复制到cuda下对应文件夹内。

查看cuda版本:

nvcc -V

2.2 使用conda配置pytorch环境

主要参考以下文章:

PyTorch环境配置及安装_pytorch配置-CSDN博客

背景

在机器学习,深度学习中,要用到大量的 package(就是各种工具包)。如果说,函数是一个工具,那么 package 就是一个工具包。一个个安装 package 很麻烦,而且容易出现疏漏。于是,就有了 Anaconda,这是一个集成了常用于科学分析(机器学习,深度学习)的大量package。
也就是说,你只要安装了 Anaconda,就安装了很多我们之后要用的许多packages。

当你遇到不同的项目,需要使用到不同版本的环境。比如这个项目要用到 pytorch 0.4,另一个项目要用到 pytorch 1.0,如果你卸载了0.4版本,安装了1.0版本。那么下一次,你再碰到0.4版本,你就需要卸载1.0版本,安装0.4版本。很折腾。

Anaconda 集成的 conda 包就能够解决这个问题。它可以创造出两个屋子,相互隔离。一个屋子放 0.4 版本,一个屋子放 1.0 版本。你需要哪个版本,就进哪个屋子工作。

# 查看已安装的环境
conda info --envs

# 创建环境
# -n后面的名字是自己命名的环境名称,本例名称为pytorch
# python=3.6 是指创建的环境,是python 3.6版本
conda create -n pytorch python=3.6

# 激活环境
activate pytorch

# 退出环境
deactivate pytorch

我安装了如下几个环境:

pytorch环境安装

首先激活,你想要安装的环境名称,例如:activate pytorch

下载网址:Start Locally | PyTorch

可以按照如下选择

之前版本也可点击 install previous versions of PyTorch

cuda按照自己的cuda版本选就可以,选成别的版本也行(只要低于自己机器的最高支持版本),亲测没什么影响,然后在你自定义的环境下安装就行。

安装好后,如果你忘记了自己安装的pytorch版本和cuda版本,可以用以下python语句打印查看:

# 判断GPU是否可用
use_gpu = torch.cuda.is_available()
print(torch.__version__)
print(torchvision.__version__)
print(torch.version.cuda)
print("isGPU =", use_gpu)

进行到这步,在python环境下训练神经网络的所有环境已经搭建完毕,下面的libtorch环境是在VS2019下,用C++调用神经网络所需要的库。

2.3 libtorch下载

踩坑无数,libtorch是对能否在C++环境下调用影响最大的。安装包在官网也很难找,且有的版本仅支持C++17,有的版本加载神经网络时报错从c10::error,有的版本报错c10::NotImplementedError


libtorch的下载路径:

1.直接在官网上下载(但好像只能下载最新的版本)

Start Locally | PyTorch

2.自己在论坛找

我怎样才能得到旧版本的libtorch,谢谢 ·期刊 #40961 ·pytorch/pytorch ·GitHub的


在我的电脑上我下载了如下libtorch版本,经实测,可用的有:

libtorch-2.1.0+cu121(仅支持C++17!!!

libtorch-1.10.0+cu113(C++14可用,C++17没试过)

其他版本,load神经网络的时候,会报c10::Error的错误。发现一个规律,可使用的版本,解压后文件夹的大小>8G,不可用的一般2-3G,不知道是不是巧合。

引入到项目中时,具体步骤参考以下文章:

C++ windows调用ubuntu训练的PyTorch模型(.pt/.pth) - 简书 (jianshu.com)

有时会报c10::NotImplementedError的错误,解决方式可参考以下文章:

c10::NotImplementedError - 知乎 (zhihu.com)

项目 -> 属性 -> C/C++ -> 命令行:

/INCLUDE:"?searchsorted_cuda@native@at@@YA?AVTensor@2@AEBV32@0_N1@Z" 

项目 -> 属性 -> 链接器 -> 命令行:

/INCLUDE:"?ignore_this_library_placeholder@@YAHXZ" 

三、环境版本选择

亲测可用的搭配有以下两种:

C++17下:

【电脑cuda v12.1】 + 【Pytorch-1.7.0+cu101】训练 + 【libtorch-2.1.0+cu121】调用

C++14下:

【电脑cuda v12.1】 + 【Pytorch-1.7.0+cu101】训练 + 【libtorch-1.10.0+cu113】调用

四、具体步骤及代码

4.1 本机cuda和cudnn安装

参照2.1内容,我安装的版本为v12.1

4.2 使用conda配置pytorch并训练

参照2.2内容,我安装的版本为Pytorch-1.7.0+cu101,环境名称为torch

# 显示conda下所有环境
conda info --envs

# 进入torch环境
activate torch

# 训练用python代码
python C:\Users\dell\Desktop\111\pytorchtrain.py

# 退出torch环境
deactivate

pytorchtrain.py文件:

import torch
import torchvision
from torch import nn
from torchvision import datasets, transforms
from datetime import datetime

# 定义超参数
batch_size = 512
learning_rate = 0.01
num_epochs = 10000 # 训练次数

# 定义输入/输出元素个数
inputnums = 6
outputnums = 9

# 判断GPU是否可用
use_gpu = torch.cuda.is_available()
print(torch.__version__)
print(torchvision.__version__)
print(torch.version.cuda)
print("isGPU =", use_gpu)

# 读取csv文件
import pandas as pd
# dataset = pd.read_csv('dataset.csv')
dataset = pd.read_csv('singlecomponent.csv')

# 将数据转化成一个去掉表头的标准numpy二维数组
dataset = dataset.values

# 设置训练集和测试集的长度
train_size = int(len(dataset) * 0.8)  # 这里按照8:2进行训练和测试
test_size = len(dataset) - train_size

# 打乱数据后按比例分割
import torch.utils.data as Data
train_dataset, test_dataset = Data.random_split(dataset, [train_size, test_size])

# 使用DataLoader方法进行批量读取数据
from torch.utils.data import DataLoader
train_loader = DataLoader(train_dataset, batch_size, shuffle=False, pin_memory=True)
test_loader = DataLoader(test_dataset, batch_size, shuffle=False, pin_memory=True)
	
# 定义简单的前馈神经网络
class neuralNetwork(nn.Module):
    def __init__(self, in_dim, n_hidden_1, n_hidden_2, out_dim):
        super(neuralNetwork, self).__init__() # super() 函数是用于调用父类(超类)的一个方法
# Sequential()表示将一个有序的模块写在一起,也就相当于将神经网络的层按顺序放在一起,这样可以方便结构显示
        self.layer1 = nn.Sequential(
            nn.Linear(in_dim, n_hidden_1),
            nn.Sigmoid()) # 表示使用Sigmoid激活函数
        self.layer2 = nn.Sequential(
            nn.Linear(n_hidden_1, n_hidden_2),
            nn.Sigmoid())
        self.outputlayer = nn.Sequential(
            nn.Linear(n_hidden_2, out_dim))

# 定义向前传播
    def forward(self, x):
        x = x.to(torch.float32)
        x = self.layer1(x)
        x = self.layer2(x)
        x = self.outputlayer(x)
        return x

# 定义简单的前馈神经网络
# 为适应cpp做了更改
# class neuralNetwork5Layer(nn.Module):
class neuralNetwork5Layer(torch.jit.ScriptModule):
    def __init__(self, in_dim, n_hidden_1, n_hidden_2, n_hidden_3, n_hidden_4, n_hidden_5, out_dim):
        super(neuralNetwork5Layer, self).__init__() # super() 函数是用于调用父类(超类)的一个方法
# Sequential()表示将一个有序的模块写在一起,也就相当于将神经网络的层按顺序放在一起,这样可以方便结构显示
        self.layer1 = nn.Sequential(
            nn.Linear(in_dim, n_hidden_1),
            nn.Sigmoid()) # 表示使用Sigmoid激活函数
        self.layer2 = nn.Sequential(
            nn.Linear(n_hidden_1, n_hidden_2),
            nn.Sigmoid())
        self.layer3 = nn.Sequential(
            nn.Linear(n_hidden_2, n_hidden_3),
            nn.Sigmoid()) # 表示使用Sigmoid激活函数
        self.layer4 = nn.Sequential(
            nn.Linear(n_hidden_3, n_hidden_4),
            nn.Sigmoid())
        self.layer5 = nn.Sequential(
            nn.Linear(n_hidden_4, n_hidden_5),
            nn.Sigmoid())
        self.outputlayer = nn.Sequential(
            nn.Linear(n_hidden_5, out_dim))

# 定义向前传播
# 为适应cpp加了注解
    @torch.jit.script_method
    def forward(self, x):
        x = x.to(torch.float32)
        x = self.layer1(x)
        x = self.layer2(x)
        x = self.layer3(x)
        x = self.layer4(x)
        x = self.layer5(x)
        x = self.outputlayer(x)
        return x

# 层节点数目(包括输入层、输出层)
model = neuralNetwork5Layer(inputnums, 80, 80, 80, 80, 80, outputnums)
if use_gpu:
    model = model.cuda() # 现在可以在GPU上跑代码了

class My_loss(nn.Module):
    def __init__(self):
        super().__init__()   #没有需要保存的参数和状态信息
        
    def forward(self, x, y):  # 定义前向的函数运算即可
        return torch.mean(torch.pow((x - y), 2))
    
criterion = nn.MSELoss() # 定义损失函数类型
# criterion = My_loss()
# optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate, momentum=0.9) # 定义优化器,使用随机梯度下降
# optimizer = torch.optim.Adadelta(model.parameters(), lr=1.0, rho=0.9, eps=1e-06, weight_decay=0) #不收敛
# optimizer = torch.optim.Adagrad(model.parameters(), lr=0.01, lr_decay=0, weight_decay=0, initial_accumulator_value=0) #收敛 但是下降速度很慢
# optimizer = torch.optim.Adam(model.parameters(), lr=0.001, betas=(0.9, 0.999), eps=1e-08, weight_decay=0, amsgrad=False) #收敛 速度快 效果不错的
optimizer = torch.optim.Adamax(model.parameters(), lr=0.002, betas=(0.9, 0.999), eps=1e-08, weight_decay=0) #收敛 速度快 效果不错的
# optimizer = torch.optim.ASGD(model.parameters(), lr=0.01, lambd=0.0001, alpha=0.75, t0=1000000.0, weight_decay=0) #不收敛
# optimizer = torch.optim.RMSprop(model.parameters(), lr=0.01, alpha=0.99, eps=1e-08, weight_decay=0, momentum=0, centered=False) #收敛 效果也还可以
# optimizer = torch.optim.Rprop(model.parameters(), lr=0.01, etas=(0.5, 1.2), step_sizes=(1e-06, 50)) #收敛的很慢 效果不太好

starttime = datetime.now() # 获得当前时间
# 开始模型训练
for epoch in range(num_epochs):
    # print('*' * 10)
    # print(f'epoch {epoch+1}')
    running_loss = 0.0 # 初始值
    running_acc = 0.0 # 准确率,暂时不算
    for i, data in enumerate(train_loader, 1): # 枚举函数enumerate返回下标和值
        features = data[:,0:inputnums]
        tags = data[:,inputnums:]
        features = features.float()
        tags = tags.float()
        # 使用GPU?
        if use_gpu:
            features = features.cuda()
            tags = tags.cuda()
        # 向前传播
        out = model(features) # 前向传播
        # print(out.size())
        # print(tags.size())
        # print("out = ", out)
        # print("tags = ", tags)
        loss = criterion(out, tags) # 计算loss
        running_loss += loss.item() # loss求和
        # 向后传播
        optimizer.zero_grad() # 梯度归零
        loss.backward() # 后向传播
        optimizer.step() # 更新参数

    if epoch % 20 == 0: # 每20轮报告一轮
        nowtime = datetime.now();
        spendtime = (nowtime - starttime).seconds
        print(f'Finish {epoch+1} epoch, Loss: {running_loss/i:.6f}, Acc: {running_acc/i:.6f}, Time: {spendtime}')
    
# 模型测试
model.eval() # 让模型变成测试模式
eval_loss = 0.
eval_acc = 0.
for data in test_loader:
    features = data[:,0:inputnums]
    tags = data[:,inputnums:]
    features = features.float()
    tags = tags.float()
    if use_gpu:
        features = features.cuda()
        tags = tags.cuda()
    with torch.no_grad():
        out = model(features)
        # print("out = ", out)
        # print("tags = ", tags)
        loss = criterion(out, tags)
    eval_loss += loss.item()
print(f'Test Loss: {eval_loss/len(test_loader):.6f}, Acc: {eval_acc/len(test_loader):.6f}\n')

# 保存模型
model.save('./neural_network_singlecom_test_torch170_cu101.pt')

训练完成后,得到了如下模型文件:

4.3 C++下模型文件的使用

1.调试 -> 环境

2.VC++目录 -> 包含目录+库目录

3.链接器 -> 输入 -> 附加依赖项

dir *.lib
# libtorch2.1.0+cu121
asmjit.lib
c10.lib
c10_cuda.lib
caffe2_nvrtc.lib
clog.lib
cpuinfo.lib
dnnl.lib
fbgemm.lib
fbjni.lib
fmtd.lib
kineto.lib
libprotobuf-lited.lib
libprotobufd.lib
libprotocd.lib
nvfuser_codegen.lib
pthreadpool.lib
pytorch_jni.lib
torch.lib
torch_cpu.lib
torch_cuda.lib
XNNPACK.lib

# libtorch1.10.0+cu113
asmjit.lib
c10.lib
c10_cuda.lib
caffe2_detectron_ops_gpu.lib
caffe2_module_test_dynamic.lib
caffe2_nvrtc.lib
Caffe2_perfkernels_avx.lib
Caffe2_perfkernels_avx2.lib
Caffe2_perfkernels_avx512.lib
clog.lib
cpuinfo.lib
dnnl.lib
fbgemm.lib
fbjni.lib
kineto.lib
libprotobuf-lited.lib
libprotobufd.lib
libprotocd.lib
mkldnn.lib
pthreadpool.lib
pytorch_jni.lib
torch.lib
torch_cpu.lib
torch_cuda.lib
torch_cuda_cpp.lib
torch_cuda_cu.lib
XNNPACK.lib

4.命令行(参照2.3中配置)

main.cpp

#include <iostream>
#include <torch/torch.h>
#include <torch/script.h>
#include <bits/stdc++.h>

using namespace std;

int main()
{
	torch::DeviceType device_type;
	if (torch::cuda::is_available()) {
		std::cout << "CUDA available! Predicting on GPU." << std::endl;
		device_type = torch::kCUDA;
	}
	else {
		std::cout << "Predicting on CPU." << std::endl;
		device_type = torch::kCPU;
	}
	torch::Device device(device_type);

	//Init model
	std::string model_pb = "C:/Users/dell/Desktop/111/neural_network_singlecom_test.pt";
	auto module = torch::jit::load(model_pb, device);
	module.to(at::kCUDA);

	// Create a vector of inputs.
	std::vector<torch::jit::IValue> inputs;
	vector<float> inputvec = { -1.04719, 0.34907, 1.57080, 0, -1.91986, 0 };
	inputs.push_back(torch::from_blob(inputvec.data(), 6, torch::kFloat).to(at::kCUDA));


	// Execute the model and turn its output into a tensor.
	auto o = module.forward(inputs);
	at::Tensor output = o.toTensor();
	cout << output << endl;

	// Tensor张量转vector数组
	// 要转到CPU上,不转就报错
	at::Tensor t = output.toType(torch::kFloat).clone().to(at::kCPU);
	cout << t << endl;
	cout << "vector数组元素如下:" << endl;
	std::vector<float> outputvec(t.data_ptr<float>(), t.data_ptr<float>() + t.numel());
	for (int i = 0; i < outputvec.size(); i++) {
		cout << outputvec[i] << endl;
	}


	return 0;
}

 执行结果:

五、其他问题

5.1 C2059 C2334

VS2019 + Qt 下 libtorch配置报错:C2059 C2334-CSDN博客

Libtorch+Qt 踩坑记录_include <torch/torch.h> 运行时报错-CSDN博客

头文件顺序问题

#include <torch/torch.h> // 必须要在QObject头文件的前面
#include <QObject>

class XXX {

}

同时在其他类中引入XXX头文件时 也必须在所有头文件的上面

#include <XXX.h> //必须在所有头文件的最上方
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtQml>

5.2 链接问题

/INCLUDE:"?searchsorted_cuda@native@at@@YA?AVTensor@2@AEBV32@0_N1@Z" 

  • 30
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值