深度学习之 5 基本数据处理与计算操作

本文是接着上一篇深度学习之 4 深度学习框架2_水w的博客-CSDN博客

目录

基本数据处理与计算操作

1、创建Tensor

◼ 创建Tensor

◼ 通过shape或者size()来获取Tensor的形状

◼ 其他创建Tensor的函数(可查阅官方API)

2、Tensor的相关操作 

◼ 算术操作

◼ 索引

◼ 改变形状  :用view()来改变Tensor的形状

3、广播机制 

4、Tensor和NumPy相互转换

5、Tensor on GPU

6、自动求梯度 

 梯度


基本数据处理与计算操作

1、创建Tensor

在深度学习中,我们通常会频繁地对数据进行操作。而在PyTorch中, torch.Tensor 是存储和变换数据的主要工具。 Tensor NumPy 的多维数组非常类似。 然而, Tensor 提供了GPU计算和自动求梯度等更多功能,这些使 Tensor 更加适合深度学习。
"tensor"这个单词一般可译作“张量”,张量可以看作是一个多维数组。 标量 可以看作是0维张量, 向量 可以看作1维张量, 矩阵 可以看作是2维张量 导入PyTorch

创建Tensor

import torch # 导入PyTorch


# 1、创建一个2x3的未初始化的Tensor
x = torch.empty(2, 3)
print(x)
# 输出结果:
Out [1]: tensor([[1.1710e+32, 4.5782e-41, 1.1710e+32], [4.5782e-41, 2.1459e+20, 9.2489e-04]])

# 2、创建一个2x3的随机初始化的Tensor
x = torch.rand(2, 3) # 随机分布,产生[0,1)区间里的数值
print(x)
# 输出结果:
Out [2]: tensor([[0.8891, 0.7304, 0.1292], [0.8943, 0.6942, 0.1651]])

# 3、创建一个2x3的long型全0的Tensor
x = torch.zeros(2, 3, dtype=torch.long) # dype指的是data数据的类型
print(x)
# 输出结果:
Out [3]: tensor([[0, 0, 0],[0, 0, 0]])


# 4、直接根据数据创建
x = torch.tensor([[5.5, 3],[2.2,5]])
print(x)
# 输出结果:
Out [4]: tensor([[5.5000, 3.0000], [2.2000, 5.0000]])
我们还可以通过现有的 Tensor 来创建
此类方法会默认重用输入 Tensor 的一些属性,例如数据类型,除非自定义数据类型。
# 返回的tensor默认具有相同的torch.dtype和torch.device
x = x.new_ones(2, 3) 
print(x)
# 指定新的数据类型
x = torch.randn_like(x, dtype=torch.float) # 生成的新张量与原来的张量的大小是相同的(2x3),只手将数据类型改为了浮点型
print(x) 

Out [5]: tensor([[1., 1., 1.],[1., 1., 1.]], dtype=torch.float64)
         tensor([[ 0.5344, 0.5095, 0.3691],[ 0.0160, 1.4369, 1.3419]])

通过shape或者size()来获取Tensor的形状

# 这两个函数调用之后都可以返回张量的形状
In [6]: # 返回的torch.Size其实就是一个tuple, 支持所有tuple的操作。
print(x.size())
print(x.shape)

Out [6]: torch.Size([2, 3])
         torch.Size([2, 3])

其他创建Tensor的函数(可查阅官方API

2、Tensor的相关操作 

算术操作

在PyTorch中,同一种操作可能有很多种形式,下面用加法作为例子。

索引

我们还可以使用类似NumPy的索引操作来访问 Tensor 的一部分,
需要注意的是: 索引出来的结果与原数据共享内存,也即修改一个,另一个会跟着修改。

其他更高级的选择函数(可查阅官方API):

改变形状  :用view()来改变Tensor的形状

y = x.view(6)
z = x.view(-1, 2) # 只给了一个维度是2,可以推算出前面-1指定的维度应该是3(-1所指的维度可以根据其他维度的值推出来)
print(x.size(), y.size(), z.size())


Out [3]: torch.Size([2, 3]) torch.Size([6]) torch.Size([3, 2])
注意: view() 返回的新 Tensor 与源 Tensor 虽然可能有不同的 size ,但是是共享 data 的,也
即更改其中的一个,另外一个也会跟着改变。(顾名思义,view仅仅是改变了对这个张量的
观察角度,内部数据并未改变)
x += 1
print(x)
print(y) # 发现y也加了1


Out [4]: tensor([[2.0910, 2.5265, 2.3833],
                [1.4564, 1.3117, 1.5181]])
         tensor([2.0910, 2.5265, 2.3833, 1.4564, 1.3117, 1.5181])
如果我们想返回一个真正新的副本(即不共享data内存)该怎么办呢?
Pytorch还提供了一个 reshape() 方法可以改变形状,但是此函数并不能保证返回的是其拷贝,所以不推荐使用。我们推荐先用 clone() 创造一个副本然后再使用 view()
x_cp = x.clone().view(6)
x -= 1
print(x)
print(x_cp)

# 我们发现x没有变化,x_cp相对于x改变了
Out [5]: tensor([[1.0910, 1.5265, 1.3833],
                [0.4564, 0.3117, 0.5181]]) 
         tensor([2.0910, 2.5265, 2.3833, 1.4564, 1.3117, 1.5181])

另外,PyTorch还支持一些线性函数,具体用法可参考官方文档。

3、广播机制 

x = torch.arange(1, 3).view(1, 2)
print(x)
y = torch.arange(1, 4).view(3, 1)
print(y)
print(x + y) 
# 由于 x 和 y 分别是1行2列和3行1列的矩阵,要计算x+y,那么x中第一行的2个元素被广播(复制) 到了第二行和第三行,而 y 中第一列的3个元素被广播(复制)到了第二列。如此,就可以对2个3行2列的矩阵按元素相加。


Out [1]: tensor([[1, 2]]) 
         tensor([[1], 
                [2], 
                [3]]) 
         tensor([[2, 3], 
                [3, 4], 
                [4, 5]])

 由于 x y 分别是1行2列和3行1列的矩阵,如果要计算 x + y,那么 x 中第一行的2个元素被广播(复制) 到了第二行和第三行,而 y 中第一列的3个元素被广播(复制)到了第二列。如此,就可以对2个3行2列的矩阵按元素相加。

当我们对两个形状不同的Tensor按元素运算时,可能会触发广播(broadcasting)机制:先适当复制元素使这两个Tensor形状相同后再按元素运算。

4、Tensor和NumPy相互转换

  • numpy()from_numpy()
  • torch.tensor()
我们可以使用 numpy()from_numpy() 将Tensor和NumPy中的数组相互转换。但是需要注意的一点是: 这两个函数所产生的 Tensor NumPy 中的数组共享相同的内存(所以他们之间的转换很快),改变其 中一个时另一个也会改变!

 还有一个常用的将NumPy中的array转换成Tensor的方法就是torch.tensor(), 需要注意的是,此方法总是会进行数据拷贝(就会消耗更多的时间和空间),所以返回的Tensor和原来的数据不再共享内存。

 5、Tensor on GPU

用方法 to() 可以将Tensor在CPU和GPU(需要硬件支持)之间相互移动。

6、自动求梯度 

在深度学习中,我们经常需要对函数求梯度(gradient)。PyTorch提供的 autograd 包能够根据输入和前向传播过程自动构建计算图,并执行反向传播。
Tensor autograd 包的核心类, 如果将其属性 .requires_grad 设置为 True ,它将开始追踪(track)在其上的所有操作(这样就可以利用链式法则进行梯度传播了)。完成计算后,可以调用 .backward() 来完成所有梯度计算。此 Tensor 的梯度将累积到 .grad 属性中。
如果不想要被继续追踪,可以调用 .detach() 将其从追踪记录中分离出来,这样就可以防止将来的计算被追踪,这样梯度就传不过去了。此外,还可以用 with torch.no_grad() 将不想被追踪的操作代码块包裹起来,这种方法在评估模型的时候很常用 , 因为在评估模型时 , 我们并不需要计算可训练参数( requires_grad=True )的梯度。
Function 是另外一个很重要的类。 Tensor Function 互相结合就可以构建一个记录有整个计算过程的有向无环图(DAG)。每个 Tensor 都有一个 .grad_fn 属性,该属性即创建该 Tensor Function , 就是说该 Tensor 是不是通过某些运算得到的,若是,则 grad_fn 返回一个与这些运算相关的对象,否则是None。

例如:

 梯度

x = torch.ones(2, 2, requires_grad=True)
y = x + 2
z = y * y * 3
out = z.mean()
out.backward() # 等价于 out.backward(torch.tensor(1.))
print(x.grad)


Out [1]: tensor([[4.5000, 4.5000], 
                [4.5000, 4.5000]])

 注意:grad在反向传播过程中是累加的(accumulated),这意味着每一次运行反向传播,梯度都会累加之前的梯度,所以一般在反向传播之前都需要把梯度清零

 

 注意:y.backward()时,

  • 如果y是标量,则不需要为backward()传入任何参数;
  • 否则,需要传入一个与y同形的Tensor。这样的原因简单来说就是为了避免向量(甚至更高维张量)对张量求导,因此转换成标量对张量进行求导。
举例:
       假设形状为 𝒎 × 𝒏 的矩阵 𝐗 经过运算得到了 𝒑 × 𝒒 的矩阵 𝐘 𝐘 又经过运算得到了 𝒔 × 𝒕 的矩阵 𝐙 。那么按照梯度计算的规则, 𝒅𝒁/𝒅𝒀 应该是一个 𝒔 × 𝒕 × 𝒑 × 𝒒 四维张量, 𝒅𝒀/𝒅𝑿 是一个 𝒑 × 𝒒 × 𝒎 × 𝒏 的四维张量,而如何对4维张量进行计算是一个比较复杂的问题。 为了避免这个问题,我们不允许张量对张量求导,只允许标量对张量求导,求导结果是和自变量同形的张量
       所以必要时我们要 把张量通过将所有张量的元素加权求和的方式转换为标量 ,举个例子,假设 𝒚 由自变量 𝒙 计算而来, 𝒘 是和 𝒚 同形的张量,则 𝐲. 𝐛𝐚𝐜𝐤𝐰𝐚𝐫𝐝(𝒘) 的含义是:先计算 𝒍 = 𝐭𝐨𝐫𝐜𝐡. 𝐬𝐮𝐦(𝒚 ∗ 𝒘) ,因此 𝒍 是一个标量,然后我们再求 𝒍 对自变量 𝒙 的导数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

水w

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值