Libtorch的基本操作

Libtorch中的Tensor与Pytorch中的Tensor相对应,是库中最核心的数据结构,是可以在不同设备上运行且支持梯度的张量。使用方式上与Pytorch中的Tensor也很类似,只是在一些Python支持但C++不支持的语法上会有些不同,例如Slice操作。

使用Libtorch前需要包含Libtorch的头文件torch/torch.h:

#include <torch/torch.h>

用到的很多函数都在此头文件中有声明,而且这些函数的namespace都是torch,因此都可以以torch::xxx的形式来调用。

创建Tensor

Tensor的创建方式有多种,比如通过字面量创建,通过C++数组创建,通过vector创建,或者通过Libtorch中的函数创建。

通过字面量创建Tensor:

#include <torch/torch.h>
#include <iostream>

int main(){
	torch::Tensor x = torch::tensor({1, 2, 3, 4});
	
	std::cout << x << std::endl;
	
	system("pause");
	
	return 0;
}
---------
1
2
3
4
[ CPULongType{4} ]

通过数组创建Tensor,torch::from_blob()函数在将数组转成Tensor时会进行reshape:

#include <torch/torch.h>
#include <iostream>

int main(){
	float arr[5] = {1, 2, 3, 4, 5};
	torch::Tensor x = torch::from_blob(arr, {1, 5});  // 第一个参数为数组,第二个参数为创建的Tensor shape
	
	std::cout << x << std::endl;
	
	system("pause");
	
	return 0;
}
---------
 1  2  3  4  5
[ CPUFloatType{1,5} ]

通过vector创建Tensor:

#include <torch/torch.h>
#include <iostream>

int main(){
	std::vector<float> v = {1.0, 2, 3, 4.0};
	torch::Tensor x = torch::from_blob(v.data(), {2, 2});
	
	std::cout << x << std::endl;
	
	system("pause");
	
	return 0;
}
---------
1  2
3  4
[ CPUFloatType{2,2} ]

通过Libtorch中的函数创建Tensor:

#include <torch/torch.h>
#include <iostream>

int main(){
	torch::Tensor x1 = torch::arange(4);
	torch::Tensor x2 = torch::arange(12);
	x2 = torch::reshape(x2, {3, 4});  // C++中没有tuple类型,因此参数不能用小括号,否则会出错
	torch::Tensor x3 = torch::ones(3);
	torch::Tensor x4 = torch::ones({3, 3});
	torch::Tensor x5 = torch::zeros(4);
	torch::Tensor x6 = torch::eye(3);
	torch::Tensor x7 = torch::rand({2, 3});
	torch::Tensor x8 = torch::randn({1, 5});
	
	std::cout << x1 << std::endl;
	std::cout << x2 << std::endl;
	std::cout << x3 << std::endl;
	std::cout << x4 << std::endl;
	std::cout << x5 << std::endl;
	std::cout << x6 << std::endl;
	std::cout << x7 << std::endl;
	std::cout << x8 << std::endl;
	
	system("pause");
	
	return 0;
}
---------
 0
 1
 2
 3
[ CPULongType{4} ]
 0   1   2   3
 4   5   6   7
 8   9  10  11
[ CPULongType{3,4} ]
 1
 1
 1
[ CPUFloatType{3} ]
 1  1  1
 1  1  1
 1  1  1
[ CPUFloatType{3,3} ]
 0
 0
 0
 0
[ CPUFloatType{4} ]
 1  0  0
 0  1  0
 0  0  1
[ CPUFloatType{3,3} ]
 0.1380  0.1055  0.2361
 0.4628  0.0153  0.0506
[ CPUFloatType{2,3} ]
 0.2509  0.4332 -1.6624 -0.5655  1.4356
[ CPUFloatType{1,5} ]

Tensor对象的属性函数

创建Tensor后,我们经常需要查看它们的一些属性,以判断是否跟预期相符。Libtorch中的Tensor是没有公开可访问的属性的,Tensor的属性信息需要通过属性函数来获取。常见的属性函数如下所示:

  • dim():Tensor的维度
  • sizes():与Pytorch中的shape属性一样
  • size(n):第n个维度的shape
  • numel():总的元素数目,sizes()中的各元素相乘
  • dtype():数据类型
  • device():Tensor所在的设备类型,例如CPU、CUDA、MPS等
#include <torch/torch.h>
#include <iostream>

int main(){
	torch::Tensor x = torch::tensor({{1, 2, 3}, {4, 5, 6}});
	
	auto dim = x.dim();  // 2
	auto sizes = x.sizes();  // [2, 3]
	auto size_0 = x.size(0);  // 2
	auto numel = x.numel();  // 6
	auto dtype = x.dtype();  // int64
	auto device = x.device();  // cpu
	
	std::cout << "dim: \n" << dim << std::endl;
	std::cout << "sizes: \n" << sizes << std::endl;
	std::cout << "size_0: \n" << size_0 << std::endl;
	std::cout << "numel: \n" << numel << std::endl;
	std::cout << "dtype: \n" << dtype << std::endl;
	std::cout << "device: \n" << device << std::endl;
	
	system("pause");
	
	return 0;
}
---------
dim:
2
sizes:
[2, 3]
size_0:
2
numel:
6
dtype:
__int64
device:
cpu

Tensor对象的索引

Libtorch中的Tensor也能像Pytorch中的Tensor一样,通过下标获取元素:

#include <torch/torch.h>
#include <iostream>

int main(){
	torch::Tensor x = torch::tensor({{1, 2, 3}, {4, 5, 6}});
	auto ele = x[0][2];
	
	std::cout << ele << std::endl;
	
	system("pause");
	
	return 0;
}
---------
3
[ CPULongType{} ]

除了上述方式,还可以使用Tensor对象的index函数来获取元素,它的一个优势是支持Slice。对于单个元素的获取,可以直接使用index({dim_0, dim_1, dim_2})的方式来索引:

#include <torch/torch.h>
#include <iostream>

int main(){
	torch::Tensor x = torch::rand({1, 3, 4, 4});  // 生成一个4维的Tensor
	auto ele1 = x.index({0, 1, 2, 2});  // 通过index方式获取单个元素
	auto ele2 = x[0][1][2][2];  // 通过下标方式获取单个元素
	
	std::cout << ele1 << std::endl;
	std::cout << ele2 << std::endl;
	
	system("pause");
	
	return 0;
}
---------
0.627059
[ CPUFloatType{} ]
0.627059
[ CPUFloatType{} ]

如果想像Python一样获取x[:, 0:1, 2:, :-1]这样的切片,该怎么在Libtorch中表示呢?这里就需要用到torch::indexing::Slice对象,来实现Python中的Slice,示例如下:

#include <torch/torch.h>
#include <iostream>
using namespace torch::indexing;  // 使用该命名空间后,在使用Slice时便可直接写成Slice,而无需写成torch::indexing::Slice

int main(){
	torch::Tensor x = torch::rand({1, 3, 4, 4});  // 生成一个4维的Tensor
	auto ele = x.index({Slice(), Slice(0, 1), Slice(2, None), Slice(None, -1)});  // 等效于Python中的x[:, 0:1, 2:, :-1]
	
	std::cout << ele << std::endl;
	
	system("pause");
	
	return 0;
}
---------
(1,1,.,.) =
  0.1014  0.1333  0.8851
  0.1299  0.1242  0.1836
[ CPUFloatType{1,1,2,3} ]

获取Tensor中的数据

Tensor是一个Libtorch的对象,那怎么把它里面的数据拿出来保存到文件中或传给别的函数呢?使用data_ptr函数就可以:

#include <torch/torch.h>
#include <iostream>

int main(){
	torch::Tensor x = torch::rand({2, 3});  
	float* data = x.data_prt<float>();
	
	std::cout << data << std::endl;
	
	system("pause");
	
	return 0;
}
---------
00000192CDA7A580

对于单个元素的Tensor,还可以使用item函数得到具体的数值:

#include <torch/torch.h>
#include <iostream>

int main(){
	torch::Tensor x = torch::tensor({{23.27}});  
	float value = x.item<float>();
	
	std::cout << value << std::endl;
	
	system("pause");
	
	return 0;
}
---------
23.27

数据类型

Libtorch中支持float16,float32,float64,int8,int16,int32,int64,uint8这几类的Tensor数据类型,可以用to函数来进行类型转换:

bar = foo.to(torch::kF16);
bar = foo.to(torch::kF32);
bar = foo.to(torch::kF64);
bar = foo.to(torch::kFloat16);
bar = foo.to(torch::kFloat32);
bar = foo.to(torch::kFloat64);
bar = foo.to(torch::kI8);
bar = foo.to(torch::kI16);
bar = foo.to(torch::kI32);
bar = foo.to(torch::kI64);
bar = foo.to(torch::kInt8);
bar = foo.to(torch::kInt16);
bar = foo.to(torch::kInt32);
bar = foo.to(torch::kInt64);
bar = foo.to(torch::kU8);
bar = foo.to(torch::kUInt8);

设备类型

设备类型是保存Tensor的设备的种类,由于Libtorch不仅仅支持CPU,还支持各种类型的GPU,因此有很多的设备类型。

Libtorch支持的所有设备类型如下所示:

Variable c10::kCPU
Variable c10::kCUDA
Variable c10::kFPGA
Variable c10::kHIP
Variable c10::kHPU
Variable c10::kIPU
Variable c10::kLazy
Variable c10::kMeta
Variable c10::kMetal
Variable c10::kMPS
Variable c10::kMTIA
Variable c10::kORT
Variable c10::kPrivateUse1
Variable c10::kVE
Variable c10::kVulkan
Variable c10::kXLA
Variable c10::kXPU

需要注意的是,设备与编译时的配置、机器是否支持等情况是强相关的,有些设备的支持也并不好。

Tensor的形变函数

很多时候我们需要将Tensor进行形状的修改,Libtorch在这方面也有较多的支持,具体的支持如下:

  • reshape
  • flatten
  • squeeze
  • unsqueeze
  • transpose
  • cat/concat/concatenate

而且支持torch::reshape这种静态函数和tensor.reshape这种对象函数。

#include <torch/torch.h>
#include <iostream>

int main(){
	torch::Tensor x = torch::arange(12);
	
	auto x1 = x.reshape({3, 4});  // 也可以写成torch::reshape({3, 4});
	auto x2 = x1.flatten();  // 所有维度展平成一维向量
	auto x3 = x1.unsqueeze(0);  // 在第0维增加一个维度
	auto x4 = x3.squeeze();  // 压缩维度,将所有数值为1的维度删除
	auto x5 = x1.transpose(1, 0);  // 转置,将x1的第一维放到x5的第0维,将x1的第0维放到x5的第一维
	auto x6 = torch::cat({x1, x1}, 1);  // 沿着第一维进行拼接
	
	std::cout << x1 << std::endl;
	std::cout << x2 << std::endl;
	std::cout << x3 << std::endl;
	std::cout << x4 << std::endl;
	std::cout << x5 << std::endl;
	std::cout << x6 << std::endl;
	
	system("pause");
	
	return 0;
}
---------
 0   1   2   3
 4   5   6   7
 8   9  10  11
[ CPULongType{3,4} ]
  0
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
[ CPULongType{12} ]
(1,.,.) =
   0   1   2   3
   4   5   6   7
   8   9  10  11
[ CPULongType{1,3,4} ]
  0   1   2   3
  4   5   6   7
  8   9  10  11
[ CPULongType{3,4} ]
  0   4   8
  1   5   9
  2   6  10
  3   7  11
[ CPULongType{4,3} ]
  0   1   2   3   0   1   2   3
  4   5   6   7   4   5   6   7
  8   9  10  11   8   9  10  11
[ CPULongType{3,8} ]

一个比较特殊的地方是transpose只支持两个轴的交换,多个轴的交换需要调用多次来实现。

Tensor之间的操作函数

Tensor库中,Tensor和Tensor之间的操作是很常见的,比如矩阵的相乘、计算内积、计算外积等,有内置的函数支持能避免很多额外的开发工作。

#include <torch/torch.h>
#include <iostream>

int main(){
	torch::Tensor x = torch::tensor({{1, 2, 3}, {4, 5, 6}});
	torch::Tensor y = torch::tensor({{10, 20}, {30, 40}, {50, 60}});
	torch::Tensor z = torch::tensor({{10, 20, 30}, {40, 50, 60}});
	
	auto z1 = torch::matmul(x, y);  // 矩阵乘法
	auto z2 = torch::mul(x, z);  // 矩阵点乘,两个矩阵的维度需一致
	
	std::cout << z1 << std::endl;
	std::cout << z2 << std::endl;
	
	system("pause");
	
	return 0;
}
---------
 220  280
 490  640
[ CPULongType{2,2} ]
  10   40   90
 160  250  360
[ CPULongType{2,3} ]

神经网络相关函数

神经网络是torch的核心模块,常见的一些卷积层、全连接层都可以以函数的形式作用在Tensor上,这里写几个简单的例子:

bar = torch::softmax(foo, -1);
bar = torch::sigmoid(foo);
bar = torch::relu(foo);
bar = torch::gelu(foo);
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
LibTorch是一个在PyTorch生态系统中的C++库,它提供了一些功能来处理和操作张量、构建和训练神经网络模型。维特比算法是一种用于求解最优路径的动态规划算法,经常用于序列标注任务中。 在LibTorch中,可以使用维特比算法来解决一些序列标注问题。通常,这需要定义一个隐马尔可夫模型(HMM),其中包含了一组隐藏状态和一组观测状态。我们可以使用LibTorch中的张量来表示这些状态,并使用LibTorch提供的函数来实现维特比算法。 首先,我们需要定义HMM的转移矩阵和观测矩阵。转移矩阵描述了隐藏状态之间的转移概率,而观测矩阵描述了观测状态对应于隐藏状态的概率。 然后,我们可以使用维特比算法来计算最优路径。该算法利用动态规划的思想,通过递归地计算每个时间步中每个隐藏状态的最优路径分数和最优前驱状态,从而得到整个序列的最优路径。 在LibTorch中,可以使用torch::Tensor表示分数和路径,并使用torch::argmax函数来找到每个时间步中最优路径的索引。维特比算法的关键步骤是通过前一个时间步的最优路径分数和当前时间步的转移概率、观测概率来计算当前时间步的最优路径分数,并通过比较不同路径分数来选择最优路径。 维特比算法最终会给出整个序列的最优路径。通过在LibTorch中实现维特比算法,我们可以在序列标注任务中解决最优路径问题,并得到模型对输入序列的最佳标注结果。 总之,LibTorch提供了一些功能来处理和操作张量、构建和训练神经网络模型,包括维特比算法的实现,可以帮助我们解决序列标注任务中的最优路径问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值