Libtorch的基本操作

Libtorch的基本操作

  • 创建Tensor
  • Tensor对象的属性函数
  • Tensor对象的索引
  • 获取Tensor中的数据
  • 数据类型
  • 设备类型
  • Tensor的形变函数
  • Tensor之间的操作函数
  • 神经网络相关函数

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

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

#include 

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

创建Tensor

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

通过字面量创建Tensor:

#include 
#include 

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 
#include 

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 
#include 

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 
#include 

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 
#include 

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 
#include 

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 
#include 

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 
#include 
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 
#include 

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 
#include 

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 
#include 

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 
#include 

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);

你可能感兴趣的:(Libtorch,C++,Libtorch)