Pytorch_张量

PyTorch是一个开源的深度学习框架,由Facebook的AI研究团队于2016年发布。它提供了丰富的工具和库,用于构建、训练和部署深度神经网络模型。PyTorch采用动态计算图的设计,使得构建和调试模型变得直观而灵活。它的自动求导功能也使得实现反向传播算法变得简单,是训练深度学习模型的关键组件。接下来,让我们一起了解一下Pytorch的基础语法,为后续Pytorch实战打好基础,本节主要分为如下两个主要内容:

  • 创建张量:学会如何创建空张量、随机张量、全零张量以及使用已有数据创建张量。
  • 张量的操作:学会如何对张量进行操作,包括张量的加减乘除运算、shape变换、转置、索引与切片以及数组、列表与张量之间的互转。
  • 自动求导原理与实现:自动求导是一个关键的功能,它允许我们自动计算梯度,从而进行反向传播和优化。
  • 反向传播:反向传播是深度学习中的关键概念之一,它是训练神经网络模型的基础。

一、张量的概念和表示

在PyTorch中,张量(tensor)是最基本的数据结构,它类似于多维数组。张量在深度学习中扮演着核心的角色,用于表示和处理数据以及进行数值计算。

1. 张量的维度和形状

张量可以具有不同的维度和形状。在PyTorch中,我们可以使用torch.Tensor类来创建张量。以下是一些常见的张量及其对应的维度和形状的示例:

  • 标量(0维张量):一个单独的数值。例如,tensor = torch.tensor(5)表示一个标量,其维度为0,形状为空。
  • 向量(1维张量):具有一个轴的张量。例如,tensor = torch.tensor([1, 2, 3, 4, 5])表示一个向量,其维度为1,形状为(5,)。
  • 矩阵(2维张量):具有两个轴的张量。例如,tensor = torch.tensor([[1, 2, 3], [4, 5, 6]])表示一个矩阵,其维度为2,形状为(2, 3)。
  • 3维张量:具有三个轴的张量。例如,tensor = torch.tensor([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])表示一个3维张量,其维度为3,形状为(2, 2, 2)。

您可以使用torch.Tensor类的构造函数以及其他一些创建张量的函数来创建具有不同维度和形状的张量。下面使用代码进行介绍

# 导入PyTorch库
import torch 

# 查看pytorch版本
print(torch.__version__) 
Intel MKL WARNING: Support of Intel(R) Streaming SIMD Extensions 4.2 (Intel(R) SSE4.2) enabled only processors has been deprecated. Intel oneAPI Math Kernel Library 2025.0 will require Intel(R) Advanced Vector Extensions (Intel(R) AVX) instructions.
Intel MKL WARNING: Support of Intel(R) Streaming SIMD Extensions 4.2 (Intel(R) SSE4.2) enabled only processors has been deprecated. Intel oneAPI Math Kernel Library 2025.0 will require Intel(R) Advanced Vector Extensions (Intel(R) AVX) instructions.
2.1.0
tensor = torch.tensor(5)

print(tensor.shape)
torch.Size([])
# 创建一个PyTorch张量(tensor),包含数字1到5
tensor = torch.tensor([1, 2, 3, 4, 5])

# 打印张量的形状(shape),即张量中元素的维度信息
print(tensor.shape)
torch.Size([5])

2. 张量的操作

PyTorch提供了许多用于操作张量的函数和方法。以下是一些常用的张量操作:

  • 创建张量:可以使用torch.tensor函数从Python列表或NumPy数组创建张量,也可以使用其他创建函数,如torch.zerostorch.onestorch.rand等创建具有特定形状和初始值的张量。
# 创建一个形状为(2, 3)的零张量
zeros_tensor = torch.zeros((2, 3))

print(zeros_tensor)
tensor([[0., 0., 0.],
        [0., 0., 0.]])
# 创建一个形状为(3, 3)的随机张量
rand_tensor = torch.rand((3, 3))

print(rand_tensor)
tensor([[0.4945, 0.3580, 0.6254],
        [0.3287, 0.3057, 0.1669],
        [0.4043, 0.5127, 0.5219]])
  • 张量操作:可以对张量执行各种数学运算和操作,如加法、减法、乘法、除法、矩阵乘法等。这些操作可以使用算术运算符或对应的函数来执行。
import torch

# 创建两个张量
tensor1 = torch.tensor([1, 2, 3])
tensor2 = torch.tensor([4, 5, 6])

# 加法操作
result = tensor1 + tensor2
print(result)  # 输出: tensor([5, 7, 9])

# 乘法操作
result = tensor1 * tensor2
print(result)  # 输出: tensor([4, 10, 18])
tensor([5, 7, 9])
tensor([ 4, 10, 18])
  • 张量索引和切片:可以使用索引和切片操作来访问张量中的特定元素或子张量。
import torch

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

# 访问第一个元素
print(tensor[0, 0])  # 输出: tensor(1)

# 切片操作
print(tensor[:, 1])  # 输出: tensor([2, 5, 8])
tensor(1)
tensor([2, 5, 8])

3. 张量的GPU加速

在PyTorch中,可以将张量存储在CPU或GPU上。GPU加速可以显著提高深度学习模型的计算性能。要将张量移动到GPU上,可以使用.to方法。

import torch

# 创建一个形状为(2, 2)的张量并将其移动到GPU上
tensor = torch.tensor([[1, 2], [3, 4]])
tensor = tensor.to('cuda')

tensor
---------------------------------------------------------------------------

AssertionError                            Traceback (most recent call last)

Cell In[8], line 5
      3 # 创建一个形状为(2, 2)的张量并将其移动到GPU上
      4 tensor = torch.tensor([[1, 2], [3, 4]])
----> 5 tensor = tensor.to('cuda')
      7 tensor


File /opt/anaconda3/envs/pytorch_env/lib/python3.9/site-packages/torch/cuda/__init__.py:289, in _lazy_init()
    284     raise RuntimeError(
    285         "Cannot re-initialize CUDA in forked subprocess. To use CUDA with "
    286         "multiprocessing, you must use the 'spawn' start method"
    287     )
    288 if not hasattr(torch._C, "_cuda_getDeviceCount"):
--> 289     raise AssertionError("Torch not compiled with CUDA enabled")
    290 if _cudart is None:
    291     raise AssertionError(
    292         "libcudart functions unavailable. It looks like you have a broken build?"
    293     )


AssertionError: Torch not compiled with CUDA enabled

如果您的系统具有可用的GPU,并且已正确配置PyTorch以使用GPU加速,那么上述代码将使张量存储在GPU上。否则,它将继续在CPU上运行。

4. 将张量转移到CPU

tensor = tensor.to('cpu')

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

这是关于"张量的概念和表示"部分的一个简要介绍。在接下来的教程中,我们将深入探讨更多有关PyTorch的功能和操作。

二、创建张量

在PyTorch中,张量(Tensor)是一种多维数组,类似于NumPy的ndarray对象。张量可以用来表示数据和进行各种数学运算。本节将介绍如何创建不同类型的张量。

1. 创建空张量

要创建一个空张量,可以使用torch.empty()函数。空张量的元素值将不会被初始化,它们的内容是未知的。

import torch

# 创建一个未初始化的 5x3 张量
x = torch.empty(5, 3)
print(x)
tensor([[0., 0., 0.],
        [0., 0., 0.],
        [0., 0., 0.],
        [0., 0., 0.],
        [0., 0., 0.]])

2. 创建随机张量

要创建一个随机初始化的张量,可以使用torch.rand()函数。这将生成一个在[0, 1)范围内均匀分布的随机张量。

import torch

# 创建一个形状为 2x2 的随机张量
x = torch.rand(2, 2)
print(x)
tensor([[0.7503, 0.3125],
        [0.2789, 0.1899]])

3. 创建全零张量

要创建一个全零的张量,可以使用torch.zeros()函数。

import torch

# 创建一个形状为 3x3 的全零张量
x = torch.zeros(3, 3)
print(x)
tensor([[0., 0., 0.],
        [0., 0., 0.],
        [0., 0., 0.]])

4. 创建全1张量

import torch

# 创建一个形状为 3x3 的全零张量
x = torch.ones(3, 3)
print(x)
tensor([[1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.]])

5. 从数据创建张量

你还可以从现有的数据创建张量。可以使用torch.tensor()函数从Python列表或NumPy数组创建张量。

import torch
import numpy as np

# 从Python列表创建张量
data_list = [1, 2, 3, 4, 5]
x = torch.tensor(data_list)
print(x)

# 从NumPy数组创建张量
data_array = np.array([6, 7, 8, 9, 10])
x = torch.tensor(data_array)
print(x)
tensor([1, 2, 3, 4, 5])
tensor([ 6,  7,  8,  9, 10])

6. 创建具有特定数据类型的张量

在PyTorch中,张量可以具有不同的数据类型,如浮点型、整型等。可以使用dtype参数指定张量的数据类型。

import torch

# 创建一个具有特定数据类型的张量
x = torch.tensor([1, 2, 3], dtype=torch.float32)
print(x.dtype)

# 创建一个具有特定数据类型的全零张量
x = torch.zeros(3, 3, dtype=torch.int32)
print(x.dtype)
torch.float32
torch.int32

三、张量操作和运算

1. 张量运算

PyTorch提供了许多张量运算操作,例如加法、减法、乘法和除法。这些运算可以应用于张量之间,也可以应用于张量与标量之间。下面是一些常用的张量运算示例:

import torch

# 张量加法
a = torch.tensor([1, 2, 3])
b = torch.tensor([4, 5, 6])
c = a + b
print(c)
tensor([5, 7, 9])
# 张量减法
a = torch.tensor([4, 5, 6])
b = torch.tensor([1, 2, 3])
c = a - b
print(c)
tensor([3, 3, 3])
# 张量乘法
a = torch.tensor([1, 2, 3])
b = torch.tensor([4, 5, 6])
c = a * b
print(c)
tensor([ 4, 10, 18])
# 张量除法
a = torch.tensor([4, 6, 8])
b = torch.tensor([2, 3, 4])
c = a / b
print(c)
tensor([2., 2., 2.])
# 张量与标量相加
a = torch.tensor([1, 2, 3])
b = 2
c = a + b
print(c)
tensor([3, 4, 5])
# 张量与标量相乘
a = torch.tensor([1, 2, 3])
b = 2
c = a * b
print(c)
tensor([2, 4, 6])

2. 张量形状变换

在实际的深度学习任务中,经常需要对张量进行形状变换。PyTorch提供了一系列函数来改变张量的形状,例如reshape()、view()和transpose()等。下面是一些常用的张量形状变换的示例:

import torch

# 改变张量的形状
a = torch.tensor([[1, 2, 3], [4, 5, 6]])
b = a.reshape(3, 2)  # 改变为3x2的张量

b
tensor([[1, 2],
        [3, 4],
        [5, 6]])
# 改变张量的形状(与reshape()功能相同)
a = torch.tensor([[1, 2, 3], [4, 5, 6]])
b = a.view(3, 2)  # 改变为3x2的张量

b
tensor([[1, 2],
        [3, 4],
        [5, 6]])
# 转置张量
a = torch.tensor([[1, 2, 3], [4, 5, 6]])
b = a.transpose(0, 1)  # 转置为3x2的张量

b
tensor([[1, 4],
        [2, 5],
        [3, 6]])

3. 张量索引和切片

对于张量中的元素,可以使用索引和切片来访问和操作它们。下面是一些常用的张量索引和切片的示例:

import torch

# 张量索引
a = torch.tensor([[1, 2, 3], [4, 5, 6]])
element = a[0, 1]  # 获取第一行第二列的元素

element
tensor(2)
# 张量切片
a = torch.tensor([[1, 2, 3], [4, 5, 6]])
slice = a[:, 1:3]  # 获取所有行的第二列到第三列的切片

slice
tensor([[2, 3],
        [5, 6]])

4. 获取张量中的数值

  • 如果 tensor 仅有一个元素,可以采用 .item() 来获取数值:
a = torch.randn(1)

print(a)
print(a.item())
tensor([0.1826])
0.18262572586536407
  • 如果张量中含有多个元素
a = torch.tensor([[1, 2, 3], [4, 5, 6]])

print(a[1:2,1])
print(a[1:2,1].item())
tensor([5])
5

5. 张量转化为数组

import torch
import numpy as np

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

# 将张量转换为NumPy数组
array = tensor.numpy()

# 现在,`array`就是一个NumPy数组,你可以使用NumPy的功能来操作它
print(array)
[[1 2 3]
 [4 5 6]
 [7 8 9]]

6. 张量转化为列表

import torch

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

# 将张量转换为Python列表
tensor_list = tensor.tolist()

print(tensor_list)
[[1, 2, 3], [4, 5, 6]]

请注意,.tolist()方法只能用于包含标量元素的张量,例如整数或浮点数。如果张量包含其他张量或复杂数据类型,则不能直接使用.tolist()。在这种情况下,你需要先将内部的张量或复杂数据类型转换为Python列表。

四、自动求导原理

在PyTorch中,自动求导是一个关键的功能,它允许我们自动计算梯度,从而进行反向传播和优化。

1. 梯度

在深度学习中,梯度是一个非常重要的概念,它表示了函数在某一点的变化率。在PyTorch中,计算梯度是一项关键操作,它允许我们通过反向传播算法有效地更新模型参数。本节将详细介绍如何在PyTorch中计算梯度,并提供一些示例来帮助你更好地理解这个过程。

梯度是一个向量,其方向指向函数值增长最快的方向,而其大小表示函数值的变化率在深度学习中,我们通常希望最小化损失函数,因此我们要沿着梯度的反方向更新模型参数,以逐步降低损失值。

PyTorch中的torch.Tensor类是PyTorch的核心数据结构,同时也是计算梯度的关键。每个张量都有一个属性requires_grad,默认为False。如果我们希望计算某个张量的梯度,需要将requires_grad设置为True,那么就会开始追踪在该变量上的所有操作,而完成计算后,可以调用 .backward() 并自动计算所有的梯度,得到的梯度都保存在属性 .grad 中。

调用 .detach() 方法分离出计算的历史,可以停止一个 tensor 变量继续追踪其历史信息 ,同时也防止未来的计算会被追踪。而如果是希望防止跟踪历史(以及使用内存),可以将代码块放在 with torch.no_grad(): 内,这个做法在对模型进行评估的时候非常有用(节约算力、不会发生模型参数变化)。

对于 autograd 的实现,还有一个类也是非常重要–FunctionTensorFunction 两个类是有关联并建立了一个非循环的图,可以编码一个完整的计算记录。每个 tensor 变量都带有属性 .grad_fn ,该属性引用了创建了这个变量的 Function (除了由用户创建的 Tensors,它们的 grad_fn二小节介绍梯度的内容。

3. 梯度计算过程

在PyTorch中,计算梯度的过程主要分为以下几个步骤:

  1. 创建张量并设置requires_grad=True:首先,我们需要创建一个张量,并将requires_grad属性设置为True,以便PyTorch跟踪其梯度。
import torch

x = torch.tensor([2.0, 3.0], requires_grad=True)

y = x**2 + 3*x + 1
  1. 计算梯度:使用backward()方法自动计算梯度。
y.sum().backward()
  1. 获取梯度值:通过访问张量的grad属性,我们可以获得计算得到的梯度值。
print(x.grad)
tensor([7., 9.])

完整的示例代码如下所示:

import torch

x = torch.tensor([2.0, 3.0], requires_grad=True)
y = x**2 + 3*x + 1
y.sum().backward()
print(x.grad)
tensor([7., 9.])

在上面的示例中,我们创建了一个包含两个元素的张量x,并将requires_grad设置为True。使用backward()方法计算梯度,并通过访问x.grad获取了梯度值。


📝 问题一:为什么要先执行sum再求导?

上面的代码中,先执行y.sum()再求导的目的是为了计算一个标量值(scalar)的梯度。PyTorch的自动微分机制是基于标量值的,因此我们需要确保我们要计算的梯度是一个标量。具体解释如下:

  1. y是一个张量,它可能包含多个元素,例如[y1, y2, ...],每个元素都与x相关。如果我们直接调用y.backward(),PyTorch会尝试计算y中每个元素对应的梯度,这将得到一个与x具有相同形状的梯度张量。这在某些情况下可能是有用的,但通常我们更关心的是一个标量目标函数的梯度。
  2. 通过执行y.sum(),我们将y中的所有元素相加,得到一个标量值(单个数字)。然后,我们对这个标量值执行反向传播,计算相对于x的梯度。这确保了我们计算的是整个目标函数相对于x的梯度,而不是每个元素的梯度。

如果去掉上面代码中的.sum(),将会提示你

RuntimeError: grad can be implicitly created only for scalar outputs

3. 梯度计算示例

下面是一些更复杂的示例,以帮助你更好地理解计算梯度的过程。

🚩示例 1:线性回归

考虑一个简单的线性回归模型,我们的目标是找到一条直线,以最小化预测值与真实值之间的平方误差。我们可以使用梯度下降算法来更新直线的参数。

import torch

# 创建训练数据
x_train = torch.tensor([[1.0], [2.0], [3.0]])
y_train = torch.tensor([[2.0], [4.0], [6.0]])

# 定义模型参数
w = torch.tensor([[0.0]], requires_grad=True)
b = torch.tensor([[0.0]], requires_grad=True)

# 定义模型
def linear_regression(x):
    return torch.matmul(x, w) + b

# 定义损失函数
def loss_fn(y_pred, y):
    return torch.mean((y_pred - y)**2)

# 定义优化器
optimizer = torch.optim.SGD([w, b], lr=0.01)

# 训练模型
for epoch in range(100):
    # 前向传播
    y_pred = linear_regression(x_train)
    
    # 计算损失
    loss = loss_fn(y_pred, y_train)
    
    # 反向传播
    loss.backward()
    
    # 更新参数
    optimizer.step()
    
    # 清零梯度
    optimizer.zero_grad()

五、反向传播

1. 反向传播原理

反向传播(Backpropagation,缩写为BP)是“误差反向传播”的简称,该方法对网络中所有权重计算损失函数的梯度。 这个梯度会反馈给梯度下降法,用来更新权重值以最小化损失函数,从而训练神经网络模型。

而反向传播是计算损失函数对模型参数梯度的一种有效方法。通过计算参数梯度,我们可以根据梯度的反方向更新参数,使得模型的预测结果逐渐接近真实标签。反向传播的过程可以概括为以下几个步骤:

  1. 前向传播:将输入样本通过神经网络的前向计算过程,计算出预测结果。
  2. 计算损失:将预测结果与真实标签进行比较,并计算损失函数的值。
  3. 反向传播梯度:根据损失函数的值,计算损失函数对模型参数的梯度。
  4. 参数更新:根据参数的梯度和优化算法的规则,更新模型的参数。

在PyTorch中,反向传播过程是自动完成的。通过调用backward()函数,PyTorch会自动计算损失函数对所有可学习参数的梯度,并将其保存在相应的参数张量的.grad属性中。接下来,我们将通过具体示例来演示这一过程。

2. 反向传播示例

假设我们有一个简单的线性回归模型,其中只有一个输入特征和一个输出标签。我们的目标是通过训练模型来找到最佳的线性关系。

首先,我们需要导入所需的库和模块、定义模型类

import torch
import torch.nn as nn
import torch.optim as optim

class LinearRegression(nn.Module):
    def __init__(self):
        super(LinearRegression, self).__init__()
        self.linear = nn.Linear(1, 1)  # 输入维度为1,输出维度为1

    def forward(self, x):
        return self.linear(x)

然后,我们创建模型的实例、定义损失函数和优化器:

model     = LinearRegression()
criterion = nn.MSELoss()  # 均方误差损失函数

# 随机梯度下降优化器
optimizer = optim.SGD(model.parameters(), lr=0.01) 

现在,我们生成一些样本数据,并进行训练:

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

# 训练模型
for epoch in range(100):
    optimizer.zero_grad()  # 梯度清零

    # 前向传播
    y_pred = model(x_train)

    # 计算损失
    loss = criterion(y_pred, y_train)

    # 反向传播
    loss.backward()

    # 参数更新
    optimizer.step()

在上述代码中,我们使用了一个简单的数据集,包含了输入特征x_train和对应的真实标签y_train。在每个训练迭代中,我们首先将梯度清零,然后进行前向传播计算预测值y_pred,接着计算损失loss,然后通过调用.backward()函数执行反向传播,最后使用优化器的.step()方法来更新模型的参数。

通过上述示例,我们可以看到在PyTorch中实现反向传播是非常简单的。PyTorch会自动计算参数的梯度,并通过优化器来更新参数,从而实现模型的训练过程。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值