前言
这篇博客跟随上一篇博客之后,有兴趣的大佬们可以点击链接直接查看,如果发现文章中出现了错误或者想要交流,欢迎在留言区留下评论。
一、广播机制
在上一篇博客中我们主要讲述了如何对两个形状相同的张量进行“按元素”操作。但在一些情况下即使形状不同,我们也可以通过调用广播机制来执行“按元素”操作。
这种机制的工作原理:
(1)通过适当复制元素来扩展一个或两个数组,以便在转换之后,两个张量具有相同的形状;
(2)对生成的数组执行按元素操作。
在大多数情况下,我们将沿着数组中长度为1的轴进行广播。代码如下:
a = torch.arange(3).reshape((3, 1))
b = torch.arange(2).reshape((1, 2))
print(a)
print(b)
如果我们想要将a,b两个张量相加,他们两个形状不匹配。这是我们将他们广播为一个3*2的矩阵,矩阵a将复制列, 矩阵b将复制行,然后再按元素相加。
print(a+b)
二、索引和切片
就像在任何其他Python数组中一样,张量中的元素可以通过索引访问。第一个元素的索引是0,最后一个元素索引是-1; 同时可以指定范围以包含第一个元素和最后一个之前的元素。
X = torch.arange(12, dtype=torch.float32).reshape((3,4))
print(X[-1])
print(X[1:3])
注:X[1:3]指的是第二到第三行之中的所有元素,X[1,2]指的是第二行第三个元素
X的形状:
索引出的元素:
记忆点:
除了读取外,我们还可以通过索引来将元素写入张量中。
X[1, 2] = 9
print(X)
如果我们想为多个元素赋值相同的值,我们只需要索引所有元素,然后为它们赋值。
X[0:2, 1:3] = 12
print(X)
假如我们在上一个例子中想要选取所有目标行中的元素,我们可以将控制列的位置空出,反之亦然。
X[0:2, :] = 12
X[:, 1:3] = 12
三、节省内存
运行一些操作可能会导致为新结果分配内存。 例如,如果我们用Y = X + Y,我们将取消引用Y指向的张量,而是指向新分配的内存处的张量。
接下来我们使用python的id函数来验证这一部分内容,它给我们提供了内存中引用对象的确切地址。
X = torch.arange(12, dtype=torch.float32).reshape((3,4))
Y = torch.tensor([[2.0, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])
before = id(Y)
Y = Y + X
print(id(Y) == before)
运行X = X + Y后我们发现id(Y)指向了一个新的位置,这是因为Python首先计算X + Y,为结果分配新的内存,然后使Y指向内存中的这个新位置。
我们是不希望发生这种事的:
1、首先,我们不想总是不必要地分配内存。在机器学习中,我们可能有数百兆的参数,并且在一秒内多次更新所有参数。通常情况下,我们希望原地执行这些更新;
2、如果我们不原地更新,其他引用仍然会指向旧的内存位置,这样我们的某些代码可能会无意中引用旧的参数。
要解决这一点也很简单,我们可以使用切片表示法将操作的结果分配给先前分配的数组,代码如下
Z = torch.zeros_like(Y)
print('id(Z):', id(Z))
Z[:] = X + Y
print('id(Z):', id(Z))
如果后续操作中我们没有重复使用X,那么我们可以使用X[ : ] = X + Y或者X += Y来减少内存的开支。
before = id(X)
X += Y
print(id(X) == before)
四、 转化为其他python对象
NumPy张量(ndarray)和torch张量可以很轻松的相互转化。torch张量和numpy数组将共享它们的底层内存,就地操作更改一个张量也会同时更改另一个张量。
A = X.numpy()
B = torch.tensor(A)
print(type(A))
print(type(B))
要将大小为1的张量转换为Python标量,我们可以调用item函数或python的内置函数。
a = torch.tensor([3.5])
print(a)
print(a.item())
print(float(a))
print(int(a))
结语
到此,深度学习中数据操作部分的知识点和代码就记述完毕了,后续的学习也将以此为基础一步步深入。
如果各位大佬在阅读过程中发现了出错的地方请在留言区指正,万分感谢。同时希望各位同学们在留言区交流、学习。