4 PyTorch 张量基础

4 PyTorch 张量基础

在深度学习中,张量是最基础的数据结构。PyTorch 的张量类似于 NumPy 的数组,但它可以在 GPU 上进行计算,从而加速运算。以下我们将从基础开始,逐步深入了解 PyTorch 张量。

1. 什么是张量?

张量是一个多维数组,可以看作是标量、向量和矩阵的推广。以下是一些张量的基本概念:

  • 标量:0维张量,例如一个单独的数值。
  • 向量:1维张量,例如一组数值。
  • 矩阵:2维张量,例如二维数组。
  • 高维张量:3维及以上张量,例如图片、视频等。

2. 创建张量

2.1 从数据创建张量

使用 torch.tensor 可以从数据(列表、元组等)创建张量:

1
2
3
4
5
6
7
8
9
10
11
12
13
import torch

# 创建一个标量张量
scalar_tensor = torch.tensor(3.14)
print(scalar_tensor)

# 创建一个向量张量
vector_tensor = torch.tensor([1.0, 2.0, 3.0])
print(vector_tensor)

# 创建一个二维矩阵张量
matrix_tensor = torch.tensor([[1.0, 2.0], [3.0, 4.0]])
print(matrix_tensor)

2.2 创建特定形状的张量

PyTorch 提供了几种函数来创建特定形状的张量,如全零、全一和随机数等:

1
2
3
4
5
6
7
8
9
10
11
# 创建一个全零的张量
zero_tensor = torch.zeros(2, 3)
print(zero_tensor)

# 创建一个全一的张量
one_tensor = torch.ones(2, 3)
print(one_tensor)

# 创建一个随机数的张量
random_tensor = torch.rand(2, 3)
print(random_tensor)

2.3 从 NumPy 数组创建张量

如果你已经有了 NumPy 数组,可以使用 torch.from_numpy 将其转换为张量:

1
2
3
4
5
6
7
8
import numpy as np

# 创建一个 NumPy 数组
numpy_array = np.array([[1, 2, 3], [4, 5, 6]])

# 将 NumPy 数组转换为 PyTorch 张量
tensor_from_numpy = torch.from_numpy(numpy_array)
print(tensor_from_numpy)

3. 张量的属性

张量具有一些重要的属性,如形状、数据类型等:

1
2
3
4
5
6
7
8
9
10
11
# 创建一个张量
tensor = torch.tensor([[1, 2, 3], [4, 5, 6]], dtype=torch.float32)

# 获取张量的形状
print(tensor.shape) # 输出: torch.Size([2, 3])

# 获取张量的数据类型
print(tensor.dtype) # 输出: torch.float32

# 获取张量的维度数量
print(tensor.dim()) # 输出: 2

4. 张量的基本操作

4.1 张量的加法和减法

可以使用 +- 操作符进行张量的加法和减法:

1
2
3
4
5
6
7
8
9
10
tensor_a = torch.tensor([[1, 2], [3, 4]])
tensor_b = torch.tensor([[5, 6], [7, 8]])

# 张量相加
tensor_sum = tensor_a + tensor_b
print(tensor_sum)

# 张量相减
tensor_diff = tensor_a - tensor_b
print(tensor_diff)

4.2 张量的乘法和除法

乘法和除法可以使用 */ 操作符。注意,张量之间的乘法为逐元素乘法,如果要进行矩阵乘法可以使用 torch.mm@ 符号:

1
2
3
4
5
6
7
# 逐元素乘法
tensor_product = tensor_a * tensor_b
print(tensor_product)

# 矩阵乘法
matrix_product = torch.mm(tensor_a, tensor_b.T) # 转置 tensor_b
print(matrix_product)

4.3 张量的索引和切片

张量支持索引和切片,类似于 Python 的列表和 NumPy 数组:

1
2
3
4
5
6
7
8
9
# 创建一个张量
tensor = torch.tensor([[1, 2, 3], [4, 5, 6]])

# 索引
print(tensor[0, 1]) # 输出: 2

# 切片
print(tensor[:, 1]) # 输出: tensor([2, 5]),第二列
print(tensor[1, :]) # 输出: tensor([4, 5, 6]),第二行

5. 张量的转换

5.1 转换数据类型

可以使用 .to() 方法将张量转换为其他数据类型(如 floatint 等):

1
2
3
4
5
tensor_float = tensor_a.float()
print(tensor_float)

tensor_int = tensor_a.int()
print(tensor_int)

5.2 移动到 GPU

如果你的计算机有 GPU,可以使用 .to(device) 方法将张量移动到 GPU:

1
2
3
4
5
# 移动张量到 GPU
if torch.cuda.is_available():
device = torch.device('cuda')
tensor_gpu = tensor.to(device)
print(tensor_gpu)

6. 总结

本文介绍了 PyTorch 张量的基础知识,包括创建、操作、属性以及转换等。掌握张量的基本使用是学习深度学习和使用 PyTorch 的基础。通过实践和示例,将能更好地理解和应用这些概念。接下来的学习中,我们将继续深入 PyTorch 的其他功能和应用。

5 PyTorch 张量操作教程

5 PyTorch 张量操作教程

在本小节中,我们将深入探索 PyTorch 中的张量操作。张量是 PyTorch 中的核心数据结构,类似于 NumPy 的数组,但具有更强大的功能,能够利用 GPU 加速计算。我们将从基本的张量创建开始,逐步介绍常用的张量操作。

1. 创建张量

1.1 从列表创建张量

可以使用 torch.tensor() 从 Python 列表创建张量。

1
2
3
4
5
6
7
8
9
import torch

# 从列表创建一维张量
a = torch.tensor([1, 2, 3])
print(a) # 输出: tensor([1, 2, 3])

# 从嵌套列表创建二维张量
b = torch.tensor([[1, 2], [3, 4]])
print(b) # 输出: tensor([[1, 2], [3, 4]])

1.2 使用内置函数创建张量

PyTorch 提供了多种内置函数来创建张量:

  • torch.zeros(): 创建全零张量
  • torch.ones(): 创建全一张量
  • torch.arange(): 创建等间隔的张量
  • torch.rand(): 创建随机张量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 创建全零张量
zero_tensor = torch.zeros((2, 3))
print(zero_tensor) # 输出: tensor([[0., 0., 0.], [0., 0., 0.]])

# 创建全一张量
one_tensor = torch.ones((2, 3))
print(one_tensor) # 输出: tensor([[1., 1., 1.], [1., 1., 1.]])

# 创建等间隔张量
arange_tensor = torch.arange(0, 10, step=2) # 从0到8,步伐为2
print(arange_tensor) # 输出: tensor([0, 2, 4, 6, 8])

# 创建随机张量
random_tensor = torch.rand((2, 3)) # 创建2x3的随机张量
print(random_tensor) # 输出类似: tensor([[0.3424, 0.1342, 0.4393], [0.2938, 0.0294, 0.3847]])

2. 张量的属性

2.1 张量的维度、形状和数据类型

张量有多个属性,可以获取它们的维度、形状和数据类型。

1
2
3
4
tensor_example = torch.tensor([[1, 2, 3], [4, 5, 6]])
print(tensor_example.dim()) # 输出: 2 (维度)
print(tensor_example.shape) # 输出: torch.Size([2, 3]) (形状)
print(tensor_example.dtype) # 输出: torch.int64 (数据类型)

3. 张量的索引和切片

可以使用类似 NumPy 的方法进行张量的索引和切片。

3.1 索引

1
2
3
4
5
6
7
8
9
10
# 创建示例张量
example_tensor = torch.tensor([[1, 2, 3], [4, 5, 6]])

# 获取单个元素
element = example_tensor[0, 1] # 第0行第1列的元素
print(element) # 输出: tensor(2)

# 获取某一行
row = example_tensor[1]
print(row) # 输出: tensor([4, 5, 6])

3.2 切片

1
2
3
# 获取前两行,前两列
slice_tensor = example_tensor[:2, :2]
print(slice_tensor) # 输出: tensor([[1, 2], [4, 5]])

4. 张量的运算

PyTorch 支持多种类型的数学运算,包括加法、减法、乘法和除法等。

4.1 基本运算

1
2
3
4
5
6
7
8
9
10
a = torch.tensor([1, 2, 3])
b = torch.tensor([4, 5, 6])

# 加法
add_result = a + b
print(add_result) # 输出: tensor([5, 7, 9])

# 乘法
mul_result = a * b
print(mul_result) # 输出: tensor([4, 10, 18])

4.2 矩阵运算

1
2
3
4
5
6
7
# 创建两个矩阵
matrix_a = torch.tensor([[1, 2], [3, 4]])
matrix_b = torch.tensor([[5, 6], [7, 8]])

# 矩阵乘法
matrix_mul_result = torch.matmul(matrix_a, matrix_b)
print(matrix_mul_result) # 输出: tensor([[19, 22], [43, 50]])

5. 张量的广播

张量广播是指在进行运算时,自动扩展形状不相同的张量以匹配进行运算的张量。

1
2
3
4
5
6
7
# 示例
a = torch.tensor([1, 2, 3]) # 形状 (3,)
b = torch.tensor([[1], [2]]) # 形状 (2,1)

# 进行广播相加
broadcast_result = a + b
print(broadcast_result) # 输出: tensor([[2, 3, 4], [3, 4, 5]])

6. 张量的转换

张量可以在 CPU 和 GPU 之间移动,也可以转换数据类型。

6.1 移动到 GPU

1
2
3
4
5
6
7
if torch.cuda.is_available():
device = 'cuda'
else:
device = 'cpu'

tensor_on_gpu = tensor_example.to(device) # 将张量移动到 GPU
print(tensor_on_gpu.device) # 输出: 'cuda:0'

6.2 改变数据类型

1
2
float_tensor = torch.tensor([1, 2, 3], dtype=torch.float32)  # 改变数据类型为 float
print(float_tensor.dtype) # 输出: torch.float32

7. 小结

在本小节中,我们介绍了 PyTorch 中的张量操作,包括创建张量、索引与切片、基本运算、矩阵运算、广播以及张量转换。这些操作是 PyTorch 编程的基础,对于后续构建深度学习模型至关重要。请多加练习,熟悉这些操作,以便在实际项目中能够熟练应用。

自动微分

自动微分

1. 什么是自动微分?

自动微分是深度学习中一个核心的概念,它允许我们高效地计算函数的导数。相较于数值微分和符号微分,自动微分能够在计算图中精确地得到导数,并且计算效率高。

在 PyTorch 中,自动微分是通过 torch.autograd 模块实现的。

2. PyTorch 中的张量和梯度

在 PyTorch 中,张量是基本的数据结构,它与 NumPy 数组非常相似,但有额外的功能,比如支持 GPU 加速和自动微分。

当你创建一个张量时,可以通过设置 requires_grad=True 来指示 PyTorch 需要计算该张量的梯度。

1
2
3
4
import torch

# 创建一个张量并要求计算梯度
x = torch.tensor(2.0, requires_grad=True)

3. 前向传播与计算图

在进行前向传播时,PyTorch 会根据张量的操作创建一个 计算图。每个操作(如加法、乘法)都会记录在计算图中,以便后续的反向传播计算梯度。

1
2
# 定义一个简单的计算
y = x ** 2 + 3 * x + 1 # y = x^2 + 3x + 1

4. 反向传播

一旦得到了结果,就可以通过调用 .backward() 方法来执行反向传播,从而计算要求张量的梯度。

1
2
3
4
5
# 执行反向传播
y.backward()

# 查看梯度
print(x.grad) # 输出 dy/dx,即 y 关于 x 的导数

在上面的情况下,y = x^2 + 3x + 1 的导数是 2*x + 3。当 x=2 时,导数 dy/dx 的值为 2*2 + 3 = 7

5. 多个参数的梯度计算

当模型有多个参数时,自动微分依然可以方便地计算梯度。例如,考虑两个张量:

1
2
3
4
5
6
# 创建两个张量
a = torch.tensor(2.0, requires_grad=True)
b = torch.tensor(3.0, requires_grad=True)

# 定义一个依赖于这两个张量的函数
z = a**2 + 3 * b + a * b # z = a^2 + 3b + ab

进行反向传播后,可以分别获取 ab 的梯度:

1
2
3
4
5
6
# 执行反向传播
z.backward()

# 输出各自的梯度
print(a.grad) # 输出 dz/da
print(b.grad) # 输出 dz/db

6. 清空梯度

在训练神经网络时,每次反向传播都会累加梯度,因此在每次优化步骤之前,需清空梯度。使用 optimizer.zero_grad() 来清空模型参数的梯度。

1
2
3
4
5
6
7
8
9
10
11
12
# 假设我们在使用一个优化器
optimizer = torch.optim.SGD([a, b], lr=0.01)

# 清空梯度
optimizer.zero_grad()

# 计算新一轮的损失和梯度
z = a**2 + 3 * b + a * b
z.backward()

# 更新参数
optimizer.step()

7. 取消梯度跟踪

在某些情况下,我们不希望 PyTorch 跟踪梯度,这可以通过 torch.no_grad() 或者使用 with torch.no_grad(): 上下文管理器来实现,常用于推断阶段。

1
2
3
with torch.no_grad():
# 在这个代码块内不会计算梯度
result = a * b

8. 示例:简单的线性回归

下面是一个使用 PyTorch 自动微分进行线性回归的简化示例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import torch

# 生成一些样本数据
x_train = torch.tensor([[1.0], [2.0], [3.0]])
y_train = torch.tensor([[2.0], [3.0], [4.0]])

# 初始化参数
w = torch.tensor([[0.0]], requires_grad=True)
b = torch.tensor([[0.0]], requires_grad=True)

# 学习率
lr = 0.01

# 训练过程
for epoch in range(100):
# 前向传播
y_pred = x_train.mm(w) + b

# 计算损失 (均方误差)
loss = ((y_pred - y_train) ** 2).mean()

# 反向传播
loss.backward()

# 更新参数
with torch.no_grad():
w -= lr * w.grad
b -= lr * b.grad

# 清空梯度
w.grad.zero_()
b.grad.zero_()

# 输出损失
if epoch % 10 == 0:
print(f'Epoch {epoch}, Loss: {loss.item()}')

print(f'学习后的权重: {w.item()}, 偏置: {b.item()}')

在这个示例中,我们进行了线性回归的训练,通过自动微分计算梯度并更新参数。

9. 总结

自动微分是使用 PyTorch 进行深度学习的重要工具,它简化了复杂的梯度计算过程,使得研究者和开发者能够集中精力构建和训练模型。掌握自动微分是深入理解和使用 PyTorch 的基础。