可以使⽤类似NumPy的索引操作来访问 Tensor 的⼀部分,需要注意的是:索引出来的结果与
原数据共享内存,也即修改⼀个,另⼀个会跟着修改。
ex1:
下面展示一些 内联代码片。
// A code block var foo = 'bar';
// An highlighted block y = x[0, :] y += 1 print(y) print(x[0, :]) # 源tensor也被改了;
即print(y)和print(x[0, :])输出相同,在使用中注意torch中有很多这种共享内存的操作
ex2:
⽤ view() 来改变 Tensor 的形状:
下面展示一些 内联代码片。
// A code block var foo = 'bar';
// An highlighted block y = x.view(15) z = x.view(-1, 5) # -1所指的维度可以根据其他维度的值推出来 print(x.size(), y.size(), z.size());
注意 view() 返回的新tensor与源tensor共享内存(其实是同⼀个tensor),也即更改其中的⼀个,另
外⼀个也会跟着改变。(顾名思义,view仅仅是改变了对这个张量的观察⻆度)
所以如果我们想返回⼀个真正新的副本(即不共享内存)该怎么办呢?Pytorch还提供了⼀
个 reshape() 可以改变形状,但是此函数并不能保证返回的是其拷⻉,所以不推荐使⽤。推荐先
⽤ clone 创造⼀个副本然后再使⽤ view 。
ex3
下面展示一些 内联代码片。
// A code block var foo = 'bar';
// An highlighted block x_cp = x.clone().view(15) x -= 1 print(x) print(x_cp);
使⽤ clone 还有⼀个好处是会被记录在计算图中,即梯度回传到副本时也会传到源 Tensor 。
另外⼀个常⽤的函数就是 item() , 它可以将⼀个标量 Tensor 转换成⼀个Python number:
当对两个形状不同的 Tensor 按元素运算时,可能会触发⼴播(broadcasting)机制:先适当复制元素使这两个 Tensor 形状相同后再按元素运算。例如:
下面展示一些 内联代码片。
// A code block var foo = 'bar';
// An highlighted block x = torch.arange(1, 3).view(1, 2) print(x) y = torch.arange(1, 4).view(3, 1) print(y) print(x + y); 输出 tensor([[1, 2]]) tensor([[1], [2], [3]]) tensor([[2, 3], [3, 4], [4, 5]])
前⾯说了,索引、 view 是不会开辟新内存的,⽽像 y = x + y 这样的运算是会新开内存的,然后
将 y 指向新内存。为了演示这⼀点,我们可以使⽤Python⾃带的 id 函数:如果两个实例的ID⼀致,那
么它们所对应的内存地址相同;反之则不同。
下面展示一些 内联代码片。
// A code block var foo = 'bar';
// An highlighted block x = torch.tensor([1, 2]) y = torch.tensor([3, 4]) id_before = id(y) y = y + x print(id(y) == id_before) # False;
如果想指定结果到原来的 y 的内存,我们可以使⽤前⾯介绍的索引来进⾏替换操作。在下⾯的例⼦中,
我们把 x + y 的结果通过 [:] 写进 y 对应的内存中。
下面展示一些 内联代码片。
// A code block var foo = 'bar';
// An highlighted block x = torch.tensor([1, 2]) y = torch.tensor([3, 4]) id_before = id(y) y[:] = y + x print(id(y) == id_before) # True;
我们还可以使⽤运算符全名函数中的 out 参数或者⾃加运算符 += (也即 add_() )达到上述效果,例如
torch.add(x, y, out=y) 和 y += x ( y.add_(x) )。
下面展示一些 内联代码片。
// A code block var foo = 'bar';
// An highlighted block x = torch.tensor([1, 2]) y = torch.tensor([3, 4]) id_before = id(y) torch.add(x, y, out=y) # y += x, y.add_(x) print(id(y) == id_before) # True;
tensor和numpy之间相互转换
我们很容易⽤ numpy() 和 from_numpy() 将 Tensor 和NumPy中的数组相互转换。但是需要注意的⼀
点是: 这两个函数所产⽣的的 Tensor 和NumPy中的数组共享相同的内存(所以他们之间的转换很
快),改变其中⼀个时另⼀个也会改变!!!
还有⼀个常⽤的将NumPy中的array转换成 Tensor 的⽅法就是 torch.tensor() , 需要注意的
是,此⽅法总是会进⾏数据拷⻉(就会消耗更多的时间和空间),所以返回的 Tensor 和原来的数
据不再共享内存。
下面展示一些 内联代码片。
// A code block var foo = 'bar';
// An highlighted block a = torch.ones(5) b = a.numpy() print(a, b) a += 1 print(a, b) b += 1 print(a, b); 输出 tensor([1., 1., 1., 1., 1.]) [1. 1. 1. 1. 1.] tensor([2., 2., 2., 2., 2.]) [2. 2. 2. 2. 2.] tensor([3., 3., 3., 3., 3.]) [3. 3. 3. 3. 3.]
使⽤ from_numpy() 将NumPy数组转换成 Tensor :
下面展示一些 内联代码片。
// A code block var foo = 'bar';
// An highlighted block import numpy as np a = np.ones(5) b = torch.from_numpy(a) print(a, b) a += 1 print(a, b) b += 1 print(a, b); 输出 [1. 1. 1. 1. 1.] tensor([1., 1., 1., 1., 1.], dtype=torch.float64) [2. 2. 2. 2. 2.] tensor([2., 2., 2., 2., 2.], dtype=torch.float64) [3. 3. 3. 3. 3.] tensor([3., 3., 3., 3., 3.], dtype=torch.float64)
所有在CPU上的 Tensor (除了 CharTensor )都⽀持与NumPy数组相互转换。
此外上⾯提到还有⼀个常⽤的⽅法就是直接⽤ torch.tensor() 将NumPy数组转换成 Tensor ,需要
注意的是该⽅法总是会进⾏数据拷⻉,返回的 Tensor 和原来的数据不再共享内存。
下面展示一些 内联代码片。
// A code block var foo = 'bar';
// An highlighted block c = torch.tensor(a) a += 1 print(a, c); 输出 [4. 4. 4. 4. 4.] tensor([3., 3., 3., 3., 3.], dtype=torch.float64)
⾃动求梯度
Tensor 是这个包的核⼼类,如果将其属性 .requires_grad 设置为 True ,它将开始追
踪(track)在其上的所有操作(这样就可以利⽤链式法则进⾏梯度传播了)。完成计算后,可以调
⽤ .backward() 来完成所有梯度计算。此 Tensor 的梯度将累积到 .grad 属性中。
注意在 y.backward() 时,如果 y 是标量,则不需要为 backward() 传⼊任何参数;否则,需要
传⼊⼀个与 y 同形的 Tensor 。
下面展示一些 内联代码片。
// A code block var foo = 'bar';
// An highlighted block x = torch.ones(2, 2, requires_grad=True) print(x) print(x.grad_fn); 输出 tensor([[1., 1.], [1., 1.]], requires_grad=True) None 123
这里我们首先创建一个全1的tensor,并且设置一个属性
requires_grad=True
每个 Tensor 都有⼀个 .grad_fn 属性,该属性即创建该 Tensor 的
Function , 就是说该 Tensor 是不是通过某些运算得到的,若是,则 grad_fn 返回⼀个与这些运算相
关的对象,否则是None。Function 是另外⼀个很重要的类。
接下来我们做一个运算操作
下面展示一些 内联代码片。
// A code block var foo = 'bar';
// An highlighted block y = x + 2 print(y) print(y.grad_fn); 输出 tensor([[3., 3.], [3., 3.]], grad_fn=)
注意x是直接创建的,所以它没有 grad_fn , ⽽y是通过⼀个加法操作创建的,所以它有⼀个为
的 grad_fn 。 像x这种直接创建的称为叶⼦节点,叶⼦节点对应的 grad_fn 是 None 。
print(x.is_leaf, y.is_leaf) # True False,is_leaf是判断当前该tensor是否是一个叶子节点
下面展示一些 内联代码片。
// A code block var foo = 'bar';
// An highlighted block z = y * y * 3 out = z.mean() print(z, out); 输出 tensor([[27., 27.], [27., 27.]], grad_fn=) tensor(27., grad_fn= )
通过 .requires_grad_() 来⽤in-place的⽅式改变 requires_grad 属性:
下面展示一些 内联代码片。
// A code block var foo = 'bar';
// An highlighted block a = torch.randn(2, 2) # 缺失情况下默认 requires_grad = False a = ((a * 3) / (a - 1)) print(a.requires_grad) # False a.requires_grad_(True) print(a.requires_grad) # True b = (a * a).sum() print(b.grad_fn); 输出 False True



