深度学习的张量 Reduction 运算
欢迎回到这个关于神经网络编程的系列。在这篇文章中,我们将学习张量的Reduction 运算。
我们将重点介绍常用的argmax() 函数,并了解如何访问张量中的数据。言归正传,我们开始吧。
张量的Reduction 运算
首先给出一个reduction 运算的定义。
A reduction operation on a tensor is an operation that reduces the number of elements contained within the tensor. 张量上的reduction 运算是减少张量中包含的元素数量的运算。
到目前为止,在这个系列中,我们已经了解到张量是深度学习的数据结构。我们的第一个任务是把数据元素载入到一个张量。
因此,张量是非常重要的,但最终,我们在本系列中学习的操作是管理张量中包含的数据元素。
Tensors give us the ability to manage our data. 张量使我们能够管理数据。
reshape 操作使我们能够沿着特定的轴 定位 元素。element-wise 运算允许我们对两个张量之间的元素进行运算,而reduction 运算允许我们对单个张量内的元素进行运算。
让我们来看一个代码示例。
假设有一个3x3的2阶张量
> t = torch.tensor([ [0,1,0], [2,0,2], [0,3,0]], dtype=torch.float32)
看一下第一个reduction操作,求和
> t.sum()tensor(8.)
统计张量中的元素个数
> t.numel()9
> t.sum().numel()1
我们可以看到
> t.sum().numel() < t.numel()True
我们的张量的标量分量的和是用sum()方法法计算出来的。调用的结果是一个标量值的张量。
根据sum()调用的结果检查原始张量中元素的数量,我们可以看到,实际上,调用sum()返回的张量包含的元素比原始张量少。
由于该操作减少了元素的数量,因此我们可以得出结论,sum()方法是一个reduction操作。
以下是一些其他常见的reduction函数:
> t.sum()tensor(8.)
> t.prod()tensor(0.)
> t.mean()tensor(.8889)
> t.std()tensor(1.1667)
所有这些张量方法都通过对所有张量元素进行运算而将张量缩减为单个元素标量值的张量。
reduction 操作通常使我们能够计算跨数据结构的累加(总)值。在现在的情况下,我们的结构是张量。
不过,这是一个问题:
Do reduction operations always reduce to a tensor with a single element? reduction 运算是否总是将一个张量reduction 为张量?
答案是否定的!
事实上,我们经常一次减少特定的轴。这个过程很重要。这就像我们在reshape 中看到的一样,我们的目标是在一个批处理中使图像张量变平,同时仍然保持batch轴。
为了化简一个关于特定轴的张量,我们使用相同的方法,我们只是传递一个维度参数的值。让我们看看它是如何运作的。
假设我们有以下张量:
> t = torch.tensor([ [1,1,1,1], [2,2,2,2], [3,3,3,3]], dtype=torch.float32)
这是一个3x4的2阶张量。两个轴对应不同的长度,这将帮助我们理解约减操作。
现在重新思考sum() 方法。只是,这一次,我们将指定要约减的维度。我们有两个坐标轴,所以我们把它们都画出来。确认一下。
> t.sum(dim=0)tensor([6., 6., 6., 6.])
> t.sum(dim=1)tensor([ 4., 8., 12.])
当我第一次看到它的时候,我正在学习它是如何工作的,我很困惑。如果你和我一样感到困惑,我强烈建议你在继续之前试着理解这里发生了什么。
记住,我们在第一个轴上约减这个张量,沿着第一个轴的元素是数组,沿着第二个轴的元素是数字。
让我们回顾一下这里发生了什么。
我们先处理第一个轴。当求第一个轴的和时,我们是在求第一个轴的元素的和。
是这样的:
> t[0]tensor([1., 1., 1., 1.])
> t[1]tensor([2., 2., 2., 2.])
> t[2]tensor([3., 3., 3., 3.])
> t[0] + t[1] + t[2]tensor([6., 6., 6., 6.])
这里使用了Element-wise操作。
当我们对第一个轴求和时,我们是对第一个轴的所有元素求和。为此,我们必须使用Element-wise 的加法。这就是为什么我们在本系列的化简操作之前介绍了Element-wise 的操作。
这个张量的第二轴包含四组数字。因为我们有三组四个数,所以我们得到三个和。
> t[0].sum()tensor(4.)
> t[1].sum()tensor(8.)
> t[2].sum()tensor(12.)
> t.sum(dim=1)tensor([ 4., 8., 12.])
这可能需要一点时间来理解。如果是这样,别担心,你可以做到的。
现在让我们来看看在神经网络编程中使用的一种非常常见的化简操作Argmax。
Argmax是一个数学函数,它告诉我们将哪个参数作为输入提供给一个函数时,会得到该函数的最大输出值。
Argmax returns the index location of the maximum value inside a tensor. Argmax返回张量内最大值的索引位置。
当我们在一个张量上调用argmax() 方法时,这个张量就会被约减成一个新的张量,这个张量包含一个索引值,这个索引值表示这个张量里面的最大值。让我们看看代码。
假设我们有以下张量:
t = torch.tensor([ [1,0,0,2], [0,3,3,0], [4,0,0,5]], dtype=torch.float32)
在这个张量中,我们可以看到最大值是最后一个数组的最后一个位置的5。
为了到达该元素,我们沿着第一个轴走直到到达最后一个数组元素,然后我们走到该数组的末尾并经过4和两个0。
让我们看一些代码。
> t.max()tensor(5.)
> t.argmax()tensor(11)
> t.flatten()tensor([1., 0., 0., 2., 0., 3., 3., 0., 4., 0., 0., 5.])
第一段代码输出张量的最大值是5,但是对argmax()方法的调用告诉我们5位于索引11处。这里发生了什么?
我们来看看这个张量的 flatten 后的输出。如果我们不指定argmax() 方法的一个轴,它会从平坦张量返回最大值的索引位置,在这个例子中确实是11。
现在我们来看看如何处理特定的坐标轴。
> t.max(dim=0)(tensor([4., 3., 3., 5.]), tensor([2, 1, 1, 2]))
> t.argmax(dim=0)tensor([2, 1, 1, 2])
> t.max(dim=1)(tensor([2., 3., 5.]), tensor([3, 1, 3]))
> t.argmax(dim=1)tensor([3, 1, 3])
在这段代码中,我们处理的是这个张量的两个轴。注意对max() 方法的调用如何返回两个张量。第一个张量包含最大值,第二个张量包含最大值的索引位置。这就是argmax 给我们的。
对于第一个轴,最大值是4、3、3和5。这些值是通过在第一个轴上运行的每个数组中获取元素的最大值来确定的。
对于这些最大值中的每一个,argmax()方法都会告诉我们值所在的第一个轴上的哪个元素。
对于第二轴,最大值是2、3和5。这些值是通过取第一个轴的每个数组中的最大值来确定的。我们有3组,每组4个,得到3个最大值。
这里的argmax 值,告诉索引在每个数组里面的最大值所在的地方。
在实际应用中,我们经常在网络的输出预测张量上使用argmax()函数来确定哪一类具有最高的预测值。
访问张量中的元素
张量的最后一种常见运算就是从张量中获取数据的能力。让我们看看PyTorch。
假设我们有以下张量:
> t = torch.tensor([ [1,2,3], [4,5,6], [7,8,9]], dtype=torch.float32)
> t.mean()tensor(5.)
> t.mean().item()5.0
看看这个操作。我们称这个3×3 张量的平均值,化简的输出是一个标量值的张量。如果我们想要得到一个数字,我们使用item()张量方法。这适用于标量值张量。
看看我们对于多个值如何做到上面的操作:
> t.mean(dim=0).tolist()[4.0, 5.0, 6.0]
> t.mean(dim=0).numpy()array([4., 5., 6.], dtype=float32)
当我们计算第一个轴上的平均值时,会返回多个值,我们可以通过将输出张量转换成Python列表或NumPy数组来访问这些数值。
一、高级索引和切片
对于NumPy ndarray对象,我们有一组相当健壮的索引和切片操作,PyTorch张量对象也支持大多数这些操作。
使用此资源进行高级索引和切片。
https://docs.scipy.org/doc/numpy/reference/arrays.indexing.html
祝贺你在这个系列中走到这一步。所有这些张量主题都是相当原始和低级的,但是对它们有一个深刻的理解会使我们的生活变得更容易,因为我们是神经网络程序员。
现在,我们已经准备好开始本系列的第二部分,在那里我们将使用所有这些知识。我们将通过研究我们将要训练的数据集,即Fashion-MNIST数据集,来开始我们的课程。