声明:本文是阅读《Deep Learning With Pytorch》所做的笔记,以方便学习pytorch,详细内容请阅读原书。
首先要将输入的数据转为浮点数 floating- point number,本章主要学习如何用张量来处理浮点数。
首先必须深刻理解PyTorch如何处理和储存数据,包括 输入 input、中间表示 intermediate representations、输出 output
tensor 也就是将数组扩展到多维,也叫多维数组 multidimensional array。一个tensor的维度就是指一个tensor内标量值的个数。
!!!tensor 的本质
Python列表或数字元组是在内存中单独分配的Python对象的集合,如上图左边所示。而 Pytorch 的 Tensors 或 Numpy 数组(通常)是连续的内存块。 正如上图右边所示,每个元素都是32位(4字节)的浮点数,这意味着存储1,000,000浮点数的一维张量将需要恰好4,000,000的连续字节,以及一些小开销(例如维度和数字类型)。
和python中对list的索引操作一样。
# input
> weights_named = torch.tensor([0.2126, 0.7152, 0.0722], names=['channels'])
> weights_named.sum('channels')
# output
tensor(1.)
# input
> img_t = torch.randn(3, 5, 5) # shape [channels, rows, columns]
> img_named = img_t.refine_names(..., 'channels', 'rows', 'columns')
> print("img named:", img_named.shape, img_named.names)
# output
img named: torch.Size([3, 5, 5]) ('channels', 'rows', 'columns')
# input
> weights_aligned = weights_named.align_as(img_named)
> weights_aligned.shape, weights_aligned.names
# output
(torch.Size([3, 1, 1]), ('channels', 'rows', 'columns'))
⚠️ 如果结合不同名字的维度,会报错!
# input
> weights = weights_named.rename(None)
> weights.names
# output
(None,)
python中的数字是对象,存储和处理相对于大量输血计算来说是低效的,因此应依靠 Numpy或者 PyTorch 中的Tensor 来处理,会更高效。为此,一个tensor中的对象必须都是相同类型的数字,并且PyTorch要跟踪此数字类型。
represent | meaning |
---|---|
torch.float32 or torch.float | 32-bit floating-point |
torch.float64 or torch.double | 64-bit, double-precision floating-point |
torch.float16 or torch.half | 16-bit, half-precision floating-point |
torch.int8 | signed 8-bit integers |
torch.uint8 | unsigned 8-bit integers |
torch.int16 or torch.short | signed 16-bit integers |
torch.int32 or torch.int | signed 32-bit integers |
torch.int64 or torch.long | signed 64-bit integers |
torch.bool | Boolean |
# input
> double_points = torch.ones(10, 2, dtype=torch.double)
> double_points.dtype
# output
torch.float64
> short_points = torch.ones(10, 2).short()
> double_points = torch.zeros(10, 2).to(torch.double)
tensor operation | example |
---|---|
creation | ones, from_numpy |
Indexing,slicing, joining, mutating | transpose |
pointwise | abs, cos |
reduction | mean, std, norm |
comparison | equal, max |
spectral | stft, hamming_window |
Other | trace |
BLAS and LAPACK | |
rabdom ampling | randn, normal |
serialiazation | load, save |
parallelism | set_num_threads |
# input
> points = torch.tensor([[4.0, 1.0], [5.0, 3.0], [2.0, 1.0]])
> points.storage()
# output
4.0
1.0
5.0
3.0
2.0
1.0
[torch.FloatStorage of size 6]
在内存里,实际是以一个大小为6 的连续数组。因此,我们不能通过两个指数去定位一个2维tensor的存储。存储结构永远是一维数组。
# input
points_storage = points.storage()
points_storage[0]
# output
4.0
通过改变存储位置的值,就会使得相应tensor也发生变化。
# input
> points_storage[0] = 2.0
> points
# Output
tensor([[2., 1.], [5., 3.], [2., 1.]])
size:每个维度元素的个数
offset:相对第一个元素的索引
stride:在每个维度下,索引+1所需要跳过的元素个数。
> second = points[0]
> second.storage_offset()
0
> second = points[1]
> second.storage_offset()
2
> second = points[2]
> second.storage_offset()
4
> points.stride()
(2,1)
⚠️注意,second是points的子张量,对second的改变会影响points的改变。
> second[0]=7.0
> points
tensor([[10., 1.],
[ 5., 3.],
[ 7., 1.]])
所以,最好克隆子张量成为一个新的张量。
> third = points[1].clone()
> third[0] = 20
> points
tensor([[10., 1.],
[ 5., 3.],
[ 7., 1.]])
> points_t = points.t()
> points_t
tensor([[10., 5., 7.],
[ 1., 3., 1.]])
此时,两个tensor是相同的存储位置,它们只是shape和stride不一样。
> id(points.storage()) == id(points_t.storage())
True
> points.stride(), points_t.stride()
((2, 1), (1, 2))
> some = torch.ones(3,4,5)
> some.stride()
(20, 5, 1)
> some_t = some.transpose(0,2)
> some_t.stride()
(1, 5, 20)
> points.is_contiguous
True
> points_t.is_contiguous()
False
我们可以通过 contiguous 方法从不连续的tensors获得连续的tensors。
> points.stride()
(2, 1)
> points_t.stride()
(1, 2)
> points_t_cont = points_t.contiguous()
> points_t_cont
tensor([[10., 5., 7.],
[ 1., 3., 1.]])
> points_t_cont.stride()
(3, 1)
> points_gpu = torch.tensor([[4.0, 1.0], [5.0, 3.0], [2.0, 1.0]], device='cuda')
> points_gpu = points.to(device='cuda')
> points_gpu = points.to(device='cuda:0')
> points_cpu = points_gpu.to(device='cpu')
> points_gpu = points.cuda()
> points_gpu = points.cuda(0)
> points_cpu = points_gpu.cpu()
> points_np = points.numpy()
> points = torch.from_numpy(points_np)
该数组与tensor共享存储空间,因此对该数组的修改也会影响对应tensor的结果