跳转至

Tensors

https://docs.pytorch.org/tutorials/beginner/blitz/tensor_tutorial.html

学习思想,理解概念,记 API 没有意义(投入产出比太低)~

Learning theories, understanding concepts, and memorizing APIs make no sense (the return on investment is too low) ~

Tensor Initialization

Tensor 初始化可以通过多种方式实现

基于 data 初始化

直接基于给定的数据进行初始化,初始化的Tensor中的数据类型是基于原始数据进行自动推断

1
2
3
4
import torch

data = [[1, 2], [3, 4]]
x = torch.tensor(data)

基于 Numpy 初始化

Tensor 可以基于Numpy数组进行数据初始化,Tensor会保留原NumPy数组的数据类型

1
2
3
4
5
6
import torch
import numpy

data = [[1, 2], [3, 4]]
np_array = numpy.array(data)
x = torch.from_numpy(np_array)

基于 Tensor 初始化

新创建的 Tensor 会保留作为参数传递进入的 Tensor 的属性(shape,datatype),除非显示声明会覆盖默认值

1
2
3
4
5
x_ones = torch.ones_like(x_data) # retains the properties of x_data
print(f"Ones Tensor: \n {x_ones} \n")

x_rand = torch.rand_like(x_data, dtype=torch.float) # overrides the datatype of x_data
print(f"Random Tensor: \n {x_rand} \n")

输出:

1
2
3
4
5
6
7
Ones Tensor:
 tensor([[1, 1],
        [1, 1]])

Random Tensor:
 tensor([[0.9496, 0.7472],
        [0.2991, 0.5063]])

基于 随机/固定值 初始化

shape:是一个 tuple(元组)数据类型,声明 Tensor 的维度属性,决定了输出 Tensor 的数据维度。

1
2
3
4
5
6
7
8
shape = (2, 3,)
rand_tensor = torch.rand(shape)
ones_tensor = torch.ones(shape)
zeros_tensor = torch.zeros(shape)

print(f"Random Tensor: \n {rand_tensor} \n")
print(f"Ones Tensor: \n {ones_tensor} \n")
print(f"Zeros Tensor: \n {zeros_tensor}")

输出:

Random Tensor:
 tensor([[0.2294, 0.5491, 0.9405],
        [0.2509, 0.6368, 0.2384]])

Ones Tensor:
 tensor([[1., 1., 1.],
        [1., 1., 1.]])

Zeros Tensor:
 tensor([[0., 0., 0.],
        [0., 0., 0.]])

Tensor Attributes

Tensor Attributes 属性描述了这个 Tensor 的 shape(形状)、datatype(数据类型)、device(数据存储在什么设备上,CPU(内存)、GPU(显存))

1
2
3
4
5
tensor = torch.rand(3, 4)

print(f"Shape of tensor: {tensor.shape}")
print(f"Datatype of tensor: {tensor.dtype}")
print(f"Device tensor is stored on: {tensor.device}")

输出:

1
2
3
Shape of tensor: torch.Size([3, 4])
Datatype of tensor: torch.float32
Device tensor is stored on: cpu

Tensor Operations

https://docs.pytorch.org/docs/2.13/torch.html

尝试把 Tensor 移动到GPU上进行后续计算:

1
2
3
4
# We move our tensor to the GPU if available
device = torch.accelerator.current_accelerator().type if torch.accelerator.is_available() else 'cpu'
tensor = tensor.to(device)
print(f"Device tensor is stored on: {tensor.device}")

PS:.to(device) 这个操作本身不会改变后续所有代码的执行设备。它只移动了调用它的那个特定张量或模型。要确保计算在GPU上运行,需要对这个计算过程中用到的每一个张量都进行妥善的设备管理

输出:

Device tensor is stored on: cuda:0

Tensor 索引/切片

类似于Numpy的索引和切片

1
2
3
tensor = torch.ones(4, 4)
tensor[:,1] = 0
print(tensor)

输出:

1
2
3
4
tensor([[1., 0., 1., 1.],
        [1., 0., 1., 1.],
        [1., 0., 1., 1.],
        [1., 0., 1., 1.]])

Tensor 拼接

t1 = torch.cat([tensor, tensor, tensor], dim=1)
print(t1)

输出:

1
2
3
4
tensor([[1., 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1.],
        [1., 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1.],
        [1., 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1.],
        [1., 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1.]])

Tensor 乘法

PS:注意,这里是两个矩阵对应元素相乘

1
2
3
4
# This computes the element-wise product
print(f"tensor.mul(tensor) \n {tensor.mul(tensor)} \n")
# Alternative syntax:
print(f"tensor * tensor \n {tensor * tensor}")

输出:

tensor.mul(tensor)
 tensor([[1., 0., 1., 1.],
        [1., 0., 1., 1.],
        [1., 0., 1., 1.],
        [1., 0., 1., 1.]])

tensor * tensor
 tensor([[1., 0., 1., 1.],
        [1., 0., 1., 1.],
        [1., 0., 1., 1.],
        [1., 0., 1., 1.]])

计算两个矩阵相乘(使用矩阵乘法):

1
2
3
print(f"tensor.matmul(tensor.T) \n {tensor.matmul(tensor.T)} \n")
# Alternative syntax:
print(f"tensor @ tensor.T \n {tensor @ tensor.T}")

输出:

tensor.matmul(tensor.T)
 tensor([[3., 3., 3., 3.],
        [3., 3., 3., 3.],
        [3., 3., 3., 3.],
        [3., 3., 3., 3.]])

tensor @ tensor.T
 tensor([[3., 3., 3., 3.],
        [3., 3., 3., 3.],
        [3., 3., 3., 3.],
        [3., 3., 3., 3.]])

In-place 原地操作

带有 _ 后缀的操作是原地操作。例如:x.copy_(y),x.t_(),会修改 x

1
2
3
print(tensor, "\n")
tensor.add_(5)
print(tensor)

输出:

1
2
3
4
5
6
7
8
9
tensor([[1., 0., 1., 1.],
        [1., 0., 1., 1.],
        [1., 0., 1., 1.],
        [1., 0., 1., 1.]])

tensor([[6., 5., 6., 6.],
        [6., 5., 6., 6.],
        [6., 5., 6., 6.],
        [6., 5., 6., 6.]])

In-place 原地操作可以节省一些内存,但在计算导数时可能会因立即丢失历史信息而出现问题,因此不建议使用。


Tensor 关联 Numpy

CPU上的 Tensor 张量和 NumPy 数组 可以共享其底层的内存位置,修改其中一个会同时影响另一个。

Tensor 转换为 Numpy 数组

1
2
3
4
t = torch.ones(5)
print(f"t: {t}")
n = t.numpy()
print(f"n: {n}")

输出:

t: tensor([1., 1., 1., 1., 1.])
n: [1. 1. 1. 1. 1.]

在 Tensor 中的变更会反应到 Numpy数组中(操作了同一内存中的数据)

1
2
3
t.add_(1)
print(f"t: {t}")
print(f"n: {n}")

输出:

t: tensor([2., 2., 2., 2., 2.])
n: [2. 2. 2. 2. 2.]

Numpy 数组 转换为 Tensor

n = np.ones(5)
t = torch.from_numpy(n)

改变 Numpy 数组数据会影响 Tensor:

1
2
3
np.add(n, 1, out=n)
print(f"t: {t}")
print(f"n: {n}")

输出:

t: tensor([2., 2., 2., 2., 2.], dtype=torch.float64)
n: [2. 2. 2. 2. 2.]