深度之眼 PyTorch 训练营第 4 期(2)- 张量的性质

15 篇文章 3 订阅
15 篇文章 1 订阅

1. 计算图

一个深度学习模型是由“计算图”构成的。所谓计算图是一个有向无环图(directed acyclic graph)。数据是这个图的节点(node),运算是这个图的边(edge)。如下图所示:
在这里插入图片描述

这张计算图的数学表达式为 y = ( x + w ) ∗ ( w + 1 ) y=(x+w)*(w+1) y=(x+w)(w+1)。其中, x x x w w w b b b 是由用户定义的,称为“叶子节点”(leaf node),可在 PyTorch 中加以验证:

a = torch.tensor([1.])
b = torch.tensor([2.])
c = a.add(b)

a.is_leaf() # True
c.is_leaf() # False

计算图可以分为动态图与静态图两种。

1.1 动态图

动态图的搭建过程与执行过程可以同时进行。PyTorch 默认采用动态图机制。我们看一个例子:

import torch
first_counter = torch.Tensor([0])
second_counter = torch.Tensor([10])
 
while (first_counter < second_counter): #[0] 加不加没有影响
    first_counter += 2
    second_counter += 1
 
print(first_counter)
print(second_counter)

1.2 静态图

静态图先创建计算图,然后执行计算图。计算图一经定义,无法改变。TensorFlow 2.0 以前以静态图为主。我们看同样的例子在 TensorFlow 2.0 以前是怎么搭建的:

import tensorflow as tf
first_counter = tf.constant(0) # 定义变量
second_counter = tf.constant(10) # 定义变量

def cond(first_counter, second_counter, *args): # 定义条件
    return first_counter < second_counter
def body(first_counter, second_counter): # 定义条件
    first_counter = tf.add(first_counter, 2)
    second_counter = tf.add(second_counter, 1)
    return first_counter, second_counter
    
c1, c2 = tf.while_loop(cond, body, [first_counter, second_counter]) # 定义循环

with tf.Session() as sess: # 建立会话执行计算图
    counter_1_res, counter_2_res = sess.run([c1, c2])

print(first_counter)
print(second_counter)

因为静态图在设计好以后不能改变,调试的过程中 debug 实在太痛苦了。所以 TensorFlow 2.0 开始默认使用动态图。

1.3 计算图示例

假如我们想计算上面计算图中 y = ( x + w ) ∗ ( w + 1 ) y=(x+w)*(w+1) y=(x+w)(w+1) x = 2 x=2 x=2 w = 1 w=1 w=1 时的导数:

  1. 首先,我们将上式进行分解:
    a = x + w a=x+w a=x+w
    b = w + 1 b=w+1 b=w+1
    于是我们得
    y = a ∗ b y=a*b y=ab
    对上式求导有:
    ∂ y ∂ w = ∂ y ∂ a ∂ a ∂ w + ∂ y ∂ b ∂ b ∂ w \frac{\partial y}{\partial w}=\frac{\partial y}{\partial a}\frac{\partial a}{\partial w}+\frac{\partial y}{\partial b}\frac{\partial b}{\partial w} wy=aywa+bywb
    根据 y = a ∗ b y=a*b y=ab a = x + w a=x+w a=x+w b = w + 1 b=w+1 b=w+1 可知:
    ∂ y ∂ a = b = w + 1 \frac{\partial y}{\partial a}=b=w+1 ay=b=w+1
    ∂ a ∂ w = 1 \frac{\partial a}{\partial w}=1 wa=1
    ∂ y ∂ b = a = x + w \frac{\partial y}{\partial b}=a=x+w by=a=x+w
    ∂ b ∂ w = 1 \frac{\partial b}{\partial w}=1 wb=1
    所以
    ∂ y ∂ w = ( w + 1 ) + ( x + w ) = 2 ∗ 1 + 2 + 1 = 5 \frac{\partial y}{\partial w}=(w+1)+(x+w)=2*1+2+1=5 wy=(w+1)+(x+w)=21+2+1=5
    在 PyTorch 中求导数非常简单,使用 tensor.backward()即可:
import torch

x = torch.tensor([2.], requires_grad=True) # 开启导数追踪
w = torch.tensor([1.], requires_grad=True) # 开启导数追踪

a = w.add(x)
b = w.add(1)
y = a.mul(b)

y.backward() # 求导
print(w.grad)

2. 张量的运算

2.1 张量的四则运算

torch.add(input, other, *, alpha=1, out=None) # 相加
torch.sub(input, other, out=None) # 相减
torch.mul(input, other, out=None) # 相乘
torch.div(input, other, out=None) # 相除

torch.add()比较特殊,它遵循如下公式:
o u t = i n p u t + a l p h a × o t h e r out=input+alpha×other out=input+alpha×other
所以 torch.add(torch.tensor(1), torch.tensor(2), torch.tensor(3)) 的运算实际上是 1 + 2 ∗ 3 = 7 1+2*3=7 1+23=7

我们还有

torch.addcdiv(input, tensor1, tensor2, *, value=1, out=None)

对应的运算规则为
o u t = i n p u t + v a l u e ∗ t e n s o r 1 t e n s o r 2 out=input+value*\frac{tensor1}{tensor2} out=input+valuetensor2tensor1

torch.addcmul(input, tensor1, tensor2, *, value=1, out=None)

对应运算规则为
o u t = i n p u t + v a l u e ∗ t e n s o r 1 ∗ t e n s o r 2 out=input+value*tensor1*tensor2 out=input+valuetensor1tensor2
张量的乘法有点复杂,以后我们会另开一篇。

2.2 对数,指数,幂函数运算

torch.exp(input, out=None)

对应运算规则为
o u t = e i n p u t out=e^{input} out=einput

torch.pow(input, exponent, out=None)

对应运算规则为
o u t = x e x p o n e n t out=x^{exponent} out=xexponent
四个对数函数

torch.log(input, out=None)

对应运算规则为
o u t = l o g e i n p u t out=log_e{input} out=logeinput

torch.log1p(input, out=None)

对应运算规则为
o u t = l o g e ( i n p u t + 1 ) out=log_e{(input+1)} out=loge(input+1)

torch.log2(input, out=None)

对应运算规则为
o u t = l o g 2 i n p u t out=log_2{input} out=log2input

torch.log10(input, out=None)

对应运算规则为
o u t = l o g 10 i n p u t out=log_{10}{input} out=log10input

2.3 三角函数

torch.sin(input, out=None):正弦
torch.cos(input, out=None):余弦
torch.tan(input, out=None):正切

2.4 变换函数

  • torch.abs(input, out=None):返回张量的绝对值。
  • torch.ceil(input, out=None):对张量向上取整。
  • torch.floor(input, out=None):对张量向下取整。
  • torch.floor_divide(input, other, out=None):张量相除后向下取整。
  • torch.fmod(input, other, out=None):对张量取余。
  • torch.neg(input, out=None):取张量的相反数。
  • torch.round(input, out=None):对张量取整。
  • torch.sigmoid(input, out=None):对张量进行 sigmoid 计算。
  • torch.sqrt(input, out=None):对张量取平方根。
  • torch.square(input, out=None):对张量平方。
  • torch.sort(input, dim=-1, descending=False, out=None):返回张量的排序结果。

2.5 降维函数

  • torch.argmax(input, dim, keepdim=False):返回张量内最大元素的索引。
  • torch.argmin(input, dim, keepdim=False, out=None):返回张量内最小元素的索引。
  • torch.mean(input, dim, keepdim=False, out=None):返回张量内张量的平均数。
  • torch.median(input, dim=-1, keepdim=False, out=None):返回张量内张量的中位数。
  • torch.prod(input, dim, keepdim=False, dtype=None):返回张量内元素的乘积。
  • torch.std(input, dim, unbiased=True, keepdim=False, out=None):返回张量内的标准差。
  • torch.sum(input, dim, keepdim=False, dtype=None):返回张量内元素的和。
  • torch.var(input, dim, keepdim=False, unbiased=True, out=None):返回张量内元素的方差。
    如果上述函数中的 dim 变量没有显式赋值,则对整个张量进行计算,返回一个值;若 dim 被显性赋值,则对该 dim 内的每组数据分别进行运算。

2.6 比较函数

  • torch.eq(input, other, out=None):比较张量间的元素是否相等。
  • torch.equal(input, other):比较两个张量是否相同。
  • torch.ge(input, other, out=None):比较第一个张量内的元素是否小于等于第二个张量内的对应元素。
  • torch.gt(input, other, out=None):比较第一个张量内的元素是否小于第二个张量内的对应元素。
  • torch.isnan(input, out=None):张量内的元素是否为空。
  • torch.le(input, other, out=None):比较第一个张量内的元素是否大于等于第二个张量内的对应元素。
  • torch.lt(input, other, out=None):比较第一个张量内的元素是否大于第二个张量内的对应元素。
  • torch.max(input, dim, keepdim=False, out=None):返回张量指定轴上的最大元素。
  • torch.min(input, dim, keepdim=False, out=None):返回张量指定轴上的最小元素。
  • torch.ne(input, other, out=None):比较张量间的元素是否不相同。

3. 张量的索引,变换,拼接与拆分

3.1 张量的索引

  • 原生索引
    对 tensor 可以采用 Python 的原生索引方式:
>>> a = torch.rand((5, 3))
>>> a[0::2]
tensor([[0.6853, 0.1187, 0.0127],
        [0.1146, 0.0663, 0.8147],
        [0.9145, 0.5034, 0.3146]])
  • torch.index_select(input, dim, index, out=None):沿着指定维度对输入进行切片,取index中指定的相应项(index为一个LongTensor),然后返回到一个新的张量, 返回的张量与原始张量_Tensor_有相同的维度(在指定轴上)。
>>> x = torch.randn(3, 4)
>>> x

 1.2045  2.4084  0.4001  1.1372
 0.5596  1.5677  0.6219 -0.7954
 1.3635 -1.2313 -0.5414 -1.8478
[torch.FloatTensor of size 3x4]

>>> indices = torch.LongTensor([0, 2])
>>> torch.index_select(x, 0, indices)

 1.2045  2.4084  0.4001  1.1372
 1.3635 -1.2313 -0.5414 -1.8478
[torch.FloatTensor of size 2x4]

>>> torch.index_select(x, 1, indices)

 1.2045  0.4001
 0.5596  0.6219
 1.3635 -0.5414
[torch.FloatTensor of size 3x2]
  • torch.masked_select(input, mask, out=None):根据掩码张量 mask 中的二元值,取输入张量中的指定项( mask 为一个 ByteTensor),将取值返回到一个新的1D张量,张量 mask 须跟input 张量有相同数量的元素数目,但形状或维度不需要相同。
>>> x = torch.randn(3, 4)
>>> x

 1.2045  2.4084  0.4001  1.1372
 0.5596  1.5677  0.6219 -0.7954
 1.3635 -1.2313 -0.5414 -1.8478
[torch.FloatTensor of size 3x4]

>>> indices = torch.LongTensor([0, 2])
>>> torch.index_select(x, 0, indices)

 1.2045  2.4084  0.4001  1.1372
 1.3635 -1.2313 -0.5414 -1.8478
[torch.FloatTensor of size 2x4]

>>> torch.index_select(x, 1, indices)

 1.2045  0.4001
 0.5596  0.6219
 1.3635 -0.5414
[torch.FloatTensor of size 3x2]

3.2 张量的变换

  • torch.squeeze(input, dim=None, out=None):将输入张量形状中的 1 去除并返回。 若不指定维度,则去除所有形状为 1 的维度。
>>> x = torch.zeros(2,1,2,1,2)
>>> x.size()
torch.Size([2, 1, 2, 1, 2])
>>> y = torch.squeeze(x)
>>> y.size()
torch.Size([2, 2, 2])
>>> y = torch.squeeze(x, 0)
>>> y.size()
torch.Size([2, 1, 2, 1, 2])
>>> y = torch.squeeze(x, 1)
>>> y.size()
torch.Size([2, 2, 1, 2])
  • torch.unsqueeze(input, dim, out=None):返回一个新的张量,对输入的制定位置插入维度 1。
>>> x = torch.zeros(2,1,2,1,2)
>>> x.size()
>>> torch.unsqueeze(x, 0).size()
torch.Size([1, 2, 1, 2, 1, 2])
  • torch.reshape(input, shape):对张量进行变形。
>>> a = torch.arange(4.)
>>> torch.reshape(a, (2, 2))
tensor([[ 0.,  1.],
        [ 2.,  3.]])
>>> b = torch.tensor([[0, 1], [2, 3]])
>>> torch.reshape(b, (-1,))
tensor([ 0,  1,  2,  3])

如果变形的维度中有 -1 则代表该维度的元素个数通过其他维度的元素个数计算。

  • torch.t(input):对张量进行转置。
>>> a = torch.zeros((3, 4))
>>> torch.t(a).size()
torch.Size([4, 3])
  • torch.transpose(input, dim0, dim1):对张量的某两个维度进行转置。
>>> a = torch.zeros((3, 4, 5))
>>> torch.transpose(a, 0, 1).size()
torch.Size([4, 3, 5])

3.3 张量的拼接

  • torch.cat(inputs, dimension=0):张量的连接操作不引入新的轴。
>>> x = torch.randn(2, 3)
>>> x
tensor([[-0.5741, -0.6404,  1.2253],
        [ 0.1389,  1.1403, -0.5611]])
>>> torch.cat((x, x, x), 0)
tensor([[-0.5741, -0.6404,  1.2253],
        [ 0.1389,  1.1403, -0.5611],
        [-0.5741, -0.6404,  1.2253],
        [ 0.1389,  1.1403, -0.5611],
        [-0.5741, -0.6404,  1.2253],
        [ 0.1389,  1.1403, -0.5611]])
>>> torch.cat((x, x, x), 1)
tensor([[-0.5741, -0.6404,  1.2253, -0.5741, -0.6404,  1.2253, -0.5741, -0.6404,
          1.2253],
        [ 0.1389,  1.1403, -0.5611,  0.1389,  1.1403, -0.5611,  0.1389,  1.1403,
         -0.5611]])
  • torch.stack(sequence, dim=0):沿着一个新的维度对张量进行连接。如果新维度不是最后一维,则该维度后面的维度往后顺延一维。
>>> x = torch.randn(2, 3)
>>> x
tensor([[1.1247, 0.2997, 0.5159],
        [0.6374, 0.3810, 0.6746]])
>>> torch.stack((x, x), dim=0)
tensor([[[1.1247, 0.2997, 0.5159],
         [0.6374, 0.3810, 0.6746]],

        [[1.1247, 0.2997, 0.5159],
         [0.6374, 0.3810, 0.6746]]])

3.4 张量的拆分

  • torch.chunk(tensor, chunks, dim=0):在给定维度(轴)上将输入张量进行分块儿。最后一块的元素数量可能少于 chunks
>>> a = torch.rand((5, 3))
>>> torch.chunk(a, 2, dim=0)
(tensor([[0.6853, 0.1187, 0.0127],
         [0.4053, 0.8772, 0.2952],
         [0.1146, 0.0663, 0.8147]]),
 tensor([[0.4756, 0.5134, 0.2130],
         [0.9145, 0.5034, 0.3146]]))

因为 5 不能被 2 整除,所以第一个新元素在 0 维上有 3 个元素,第二个新元素在 0 维上有 2 个元素。

  • torch.split(tensor, split_size, dim=0):将输入张量分割成相等形状的chunks(如果可分)。 如果沿指定维的张量形状大小不能被split_size 整分, 则最后一个分块会小于其它分块。
>>> torch.split(a, [1, 1, 3], dim=0)
(tensor([[0.6853, 0.1187, 0.0127]]),
 tensor([[0.4053, 0.8772, 0.2952]]),
 tensor([[0.1146, 0.0663, 0.8147],
         [0.4756, 0.5134, 0.2130],
         [0.9145, 0.5034, 0.3146]]))

欢迎关注我的微信公众号“花解语 NLP”:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值