前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >PyTorch的Broadcasting 和 Element-Wise 操作 | PyTorch系列(八)

PyTorch的Broadcasting 和 Element-Wise 操作 | PyTorch系列(八)

作者头像
AI算法与图像处理
发布2020-04-26 16:51:55
6K0
发布2020-04-26 16:51:55
举报

欢迎回到这个关于神经网络编程的系列。在这篇文章中,我们将通过学习 element-wise 的操作来扩展我们的知识,而不仅仅是 reshape 操作。

  • Reshaping operations
  • Element-wise operations
  • Reduction operations
  • Access operations

深度学习中Element-Wise 操作

一、Element-Wise的含义

element-wise 是神经网络编程中非常常见的张量操作。让我们首先定义一下 element-wise 操作。

element-wise 是两个张量之间的操作,它在相应张量内的对应的元素进行操作。

An element-wise operation operates on corresponding elements between tensors.

如果两个元素在张量内占据相同位置,则称这两个元素是对应的。该位置由用于定位每个元素的索引确定。

假设我们有以下两个张量:

代码语言:javascript
复制
> t1 = torch.tensor([
    [1,2],
    [3,4]
], dtype=torch.float32)

> t2 = torch.tensor([
    [9,8],
    [7,6]
], dtype=torch.float32)

这两个张量均为2 x 2形状的2阶张量。

这意味着我们有两个轴,每个轴的长度均为两个元素。第一轴的元素是数组,第二轴的元素是数字。

代码语言:javascript
复制
# Example of the first axis
> print(t1[0])
tensor([1., 2.])

# Example of the second axis
> print(t1[0][0])
tensor(1.)

这是我们现在在本系列中经常看到的那种东西。好吧,让我们以此为基础。

我们知道,如果两个元素在张量内占据相同位置,则认为这两个元素是对应的,并且该位置由用于定位每个元素的索引确定。让我们看一个对应元素的例子。

代码语言:javascript
复制
> t1[0][0]
tensor(1.)

> t2[0][0]
tensor(9.)

这使我们看到 t1 中1的对应元素是 t2 中9的元素。

对应关系由索引定义。这很重要,因为它揭示了element-wise 操作的重要特征。我们可以推断出张量必须具有相同数量的元素才能执行 element-wise 的操作。

我们将继续进行此声明,使其更具限制性。两个张量必须具有相同的形状,以便对其执行 element-wise 操作。

二、加法也是一种Element-Wise操作

让我们看看第一个 element-wise 操作,加法。别担心。它会变得更有趣。

代码语言:javascript
复制
> t1 + t2
tensor([[10., 10.],
        [10., 10.]])

这让我们看到张量之间的加法是一个element-wise 操作。在相应位置的每一对元素被加在一起,产生一个新的相同形状的张量。

加法是一种element-wise 运算,事实上,所有的算术运算,加、减、乘、除都是element-wise 运算。


算术运算是基于 Element-Wise的运算

我们通常看到的张量运算是使用标量值的算术运算。我们有两种方法可以做到这一点:

(1) 使用这些符号运算:

代码语言:javascript
复制
> print(t + 2)
tensor([[3., 4.],
        [5., 6.]])

> print(t - 2)
tensor([[-1.,  0.],
        [ 1.,  2.]])

> print(t * 2)
tensor([[2., 4.],
        [6., 8.]])

> print(t / 2)
tensor([[0.5000, 1.0000],
        [1.5000, 2.0000]])

或者等价地,(2) 这些张量内置的方法:

代码语言:javascript
复制
> print(t1.add(2))
tensor([[3., 4.],
        [5., 6.]])

> print(t1.sub(2))
tensor([[-1.,  0.],
        [ 1.,  2.]])

> print(t1.mul(2))
tensor([[2., 4.],
        [6., 8.]])

> print(t1.div(2))
tensor([[0.5000, 1.0000],
        [1.5000, 2.0000]])

这两个选项的作用是相同的。我们可以看到,在这两种情况下,标量值 2 通过相应的算术运算应用于每个元素。

这里好像有点不对劲。这些例子打破了我们建立的规则,即element-wise 作操作相同形状的张量。

标量的值是0阶张量,这意味着它们没有形状,而我们的张量t1是一个形状为2×2的2阶张量。

这是怎么回事呢?让我们分解一下。

可能想到的第一个解释是,这个运算只是使用单个标量值,对张量中的每个元素进行运算。

这种逻辑是可行的。然而,这有点误导人,而且在更一般的情况下,我们注意到它使用标量。

为了从不同的角度考虑这些操作,我们需要引入tensor broadcastingbroadcasting的概念。

一、Broadcasting Tensors

broadcasting 描述了在element-wise 操作期间如何处理不同形状的张量。

Broadcasting is the concept whose implementation allows us to add scalars to higher dimensional tensors.

我们考虑一下t1 + 2的运算。在这里,将标量值张量变换成t1的形状,然后进行element-wise 运算。

我们可以看到什么是broadcasted(广播后)标量值看起来像使用broadcast_to() Numpy函数:

代码语言:javascript
复制
> np.broadcast_to(2, t1.shape)
array([[2, 2],
        [2, 2]])

这意味着标量值被转换成一个2阶张量,就像t1一样,就像那样,形状匹配和element-wise 方面的规则有了相同的形状。当然,这一切都是秘密进行的。

可以这么说,这段代码美如画

代码语言:javascript
复制
> t1 + 2
tensor([[3., 4.],
        [5., 6.]])

实际上的操作

代码语言:javascript
复制
> t1 + torch.tensor(
    np.broadcast_to(2, t1.shape)
    ,dtype=torch.float32
)
tensor([[3., 4.],
        [5., 6.]])

在这一点上,您可能会认为这似乎有些令人费解,所以让我们看一个更棘手的示例,以解决这一问题。假设我们有以下两个张量。

二、广播的一个更棘手的例子

让我们看一个更复杂的例子来理解这一点。假设我们有下面这个张量。

代码语言:javascript
复制
t1 = torch.tensor([
    [1,1],
    [1,1]
], dtype=torch.float32)

t2 = torch.tensor([2,4], dtype=torch.float32)

这个element-wise 加法运算的结果是什么?对element-wise 运算的相同形状规则是否还可行?

代码语言:javascript
复制
# t1 + t2 ???????

> t1.shape
torch.Size([2, 2])

> t2.shape
torch.Size([2])

尽管这两个张量有不同的形状,但element-wise操作是可能的,而 broadcasting 使得运算成为可能。低阶张量t2将通过broadcasting 进行变换,以匹配高阶张量t1的形状,element-wise 操作将照常进行。

broadcasting 的概念是理解这个运算将如何进行的关键。与前面一样,我们可以使用broadcast_to() numpy函数检查broadcast 转换。

代码语言:javascript
复制
> np.broadcast_to(t2.numpy(), t1.shape)
array([[2., 4.],
        [2., 4.]], dtype=float32)

> t1 + t2
tensor([[3., 5.],
        [3., 5.]])

broadcasting之后,这两个张量之间的加法运算是同一形状张量之间的element-wise 运算。

广播是一个比基本element-wise 操作更先进的话题,所以如果需要更长的时间来熟悉这个概念,也不要担心。

理解element-wise 的操作和相同的形状要求为广播的概念和为什么使用广播提供了基础。

我们什么时候真正使用广播?在预处理数据时,特别是在归一化化过程中,我们经常需要使用广播。

在TensorFlow.js系列中有一篇文章更详细地介绍了广播。这里有一个实际的例子,并讨论了确定一个特定的张量如何广播的算法,所以检查一下,对广播进行更深入的讨论。

不要担心不知道TensorFlow.js。这不是必须的,我强烈推荐广播的内容。


比较操作也是Element-Wise的运算

比较操作也是element-wise 运算。

对于给定的两个张量之间的比较运算,返回一个形状相同的新张量,每个元素包含一个torch.bool值为True或Faslse。

一、PyTorch版本1.2.0中的更改

返回的比较操作从torch.uint8 变成 torch.bool.

https://github.com/pytorch/pytorch/pull/21113

1.1版本:

代码语言:javascript
复制
> torch.tensor([1, 2, 3]) < torch.tensor([3, 1, 2])
tensor([1, 0, 0], dtype=torch.uint8)

1.2版本:

代码语言:javascript
复制
> torch.tensor([1, 2, 3]) < torch.tensor([3, 1, 2])
tensor([True, False, False])

相关链接:

  • Release Notes: https://github.com/pytorch/pytorch/releases/tag/v1.2.0
  • Pull Request: https://github.com/pytorch/pytorch/pull/21113

下面的示例显示了PyTorch版本1.2.0及更高版本的输出。

二、元素比较运算的例子

假设有下面的张量

代码语言:javascript
复制
> t = torch.tensor([
    [0,5,0],
    [6,0,7],
    [0,8,0]
], dtype=torch.float32)

让我们来看看这些比较运算

代码语言:javascript
复制
> t.eq(0)
tensor([[True, False, True],
        [False, True, False],
        [True, False, True]])


> t.ge(0)
tensor([[True, True, True],
        [True, True, True],
        [True, True, True]])


> t.gt(0)
tensor([[False, True, False],
        [True, False, True],
        [False, True, False]])


> t.lt(0)
tensor([[False, False, False],
        [False, False, False],
        [False, False, False]])

> t.le(7)
tensor([[True, True, True],
        [True, True, True],
        [True, False, True]])

从广播的角度来思考这些操作,我们可以看到最后一个操作,t.le(7),实际上是这样的:

代码语言:javascript
复制
> t <= torch.tensor(
    np.broadcast_to(7, t.shape)
    ,dtype=torch.float32
)

tensor([[True, True, True],
        [True, True, True],
        [True, False, True]])

等同于:

代码语言:javascript
复制
> t <= torch.tensor([
    [7,7,7],
    [7,7,7],
    [7,7,7]
], dtype=torch.float32)

tensor([[True, True, True],
        [True, True, True],
        [True, False, True]])

三、使用函数的元素操作

对于函数的元素操作,我们可以假设这个函数适用于张量的每个元素。

以下是一些例子:

代码语言:javascript
复制
> t.abs() 
tensor([[0., 5., 0.],
        [6., 0., 7.],
        [0., 8., 0.]])


> t.sqrt()
tensor([[0.0000, 2.2361, 0.0000],
        [2.4495, 0.0000, 2.6458],
        [0.0000, 2.8284, 0.0000]])

> t.neg()
tensor([[-0., -5., -0.],
        [-6., -0., -7.],
        [-0., -8., -0.]])

> t.neg().abs()
tensor([[0., 5., 0.],
        [6., 0., 7.],
        [0., 8., 0.]])

一些术语

有一些其他的方法来引用element-wise的运算,所以我只想提一下,所有这些都意味着同一件事:

  • Element-wise
  • Component-wise
  • Point-wise

如果你在其他地方遇到这些术语,请记住这一点。

总结

现在,我们应该有一个很好的理解element-wise 的操作,以及如何将它们应用到神经网络和深度学习的张量操作。在下一篇文章中,我们将讨论最后两类张量运算:

  • Reshaping operations
  • Element-wise operations
  • Reduction operations
  • Access operations

文章中内容都是经过仔细研究的,本人水平有限,翻译无法做到完美,但是真的是费了很大功夫,希望小伙伴能动动你性感的小手,分享朋友圈或点个“在看”,支持一下我 ^_^

英文原文链接是:

https://deeplizard.com/learn/video/fCVuiW9AFzY

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-04-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 AI算法与图像处理 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 二、加法也是一种Element-Wise操作
  • 一些术语
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档