张量的元素类型
与列表不同的是,张量只能用来处理数值类型,不像Python列表,什么类型都可以往里面放,下面就是tensor中可以处理的数值类型
- torch.float32 或 torch.float 32位浮点数
- torch.float64 或 torch.double 64位双精度浮点数
- torch.float16 或 torch.half 16位半精度浮点数
- torch.int8 8位有符号整数
- torch.uint8 8位无符号整数
- torch.int16 或 torch.short 16位有符号整数
- torch.int32 或 torch.int 32位有符号整数
- torch.int64 或 torch.long 64位有符号整数
- torch.bool 布尔类型
不同的元素类型直接影响tensor在内存中所占用的内存块大小,比如32位浮点数就占用32个bite,而64位则占用64个bite,所以在需要优化模型性能的时候可以考虑降低精度。 对于PyTorch来说,如果不做特殊处理,在构建浮点数tensor时默认使用float32,也就是32位浮点数,在构建整数tensor的时候使用的是int64,64位有符号整数
关于元素类型的操作: 除了默认情况以外,我们当然可以手动的来修改元素类型。比如在构建tensor的时候在后面加上dtype参数,并指定具体的类型
代码语言:javascript复制double_points = torch.ones(10, 2, dtype=torch.double)
short_points = torch.tensor([[1, 2], [3, 4]], dtype=torch.short)
对于tensor也有一个dtype方法,可以用来查看当前tensor的元素类型
代码语言:javascript复制short_points.dtype
outs:torch.int16 #这里输出了类型为int16
除了上述的指定类型,我们还可以用转换函数,我理解这些是强制转换函数?
代码语言:javascript复制double_points = torch.zeros(10, 2).double()
short_points = torch.ones(10, 2).short()
double_points = torch.zeros(10, 2).to(torch.double)
short_points = torch.ones(10, 2).to(dtype=torch.short)
这里有用到to()方法,这是一个比较神奇的方法,除了转换元素类型,还可以接收其他参数,我们后面会看到。 在进行运算的时候,两个精度不同的tensor如果需要进行操作,那么低精度类型的tensor会自动转成较高精度的tensor。 下面的例子中,一个double类型的与一个short类型的进行运算,结果变成了double类型。
代码语言:javascript复制points_64 = torch.rand(5, dtype=torch.double) # <1>
points_short = points_64.to(torch.short)
points_64 * points_short
outs:tensor([0., 0., 0., 0., 0.], dtype=torch.float64)
把张量存储到GPU
PyTorch提供了设备的概念,方便在不同的运算单元上转移我们的tensor数据以及运算。前面我们都没有指定设备,默认就是在CPU上进行运算,如果我们像下面这样指定它的设备device,就可以使用GPU来进行加速计算了:
代码语言:javascript复制points_gpu = torch.tensor([[4.0,1.0], [5.0, 3.0], [2.0,1.0]], device='cuda')
或者这里也可以用我们前面提到的to()方法,当存在多个GPU的时候也可以根据GPU的序号来指定使用哪个GPU
代码语言:javascript复制points_gpu = points.to(device='cuda')
points_gpu = points.to(device='cuda:0')
数据建立在GPU上之后,运算也都是在GPU上进行的,在需要的时候我们可以指定把数据传回CPU,比如
代码语言:javascript复制points_cpu = points_gpu.to(device='cpu')
#还有更简略的写法如下,但是我觉得用to()更规范一点,而且to()还可以支持其他的操作
points_gpu = points.cuda()
points_gpu = points.cuda(0)
points_cpu = points_gpu.cpu()
与NumPy的交互
前面多次提到了NumPy,可能有人不熟悉NumPy,比如像我,但是它确实是Python数据科学中极其重要的一个库,所以PyTorch的很多操作跟NumPy也都是互通的。下面的代码展示了如何把一个PyTorch的tensor转换成NumPy的array,这里需要注意的是,经过这步操作,PyTorch的tensor与NumPy的array是共享底层存储的,也就是这里的tensor和array其实都是底层数据的一个虚拟镜像,当你修改NumPy数组的时候tensor的数据也会发生变化。
代码语言:javascript复制points = torch.ones(3, 4)
points_np = points.numpy()
points_np
outs:array([[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]], dtype=float32)
既然能把tensor转换成NumPy array,那么也可以把NumPy array转换成tensor
代码语言:javascript复制points = torch.from_numpy(points_np)
序列化
作为深度学习的框架,对数据的存储和加载当然是十分重要的,对于大模型一次训练都需要很长的时间,没有人喜欢每次都重新训练模型,所以要把这些数据好好的保存下来。PyTorch中提供了序列化方法
代码语言:javascript复制# 存储tensor 方法1
torch.save(points, '../data/p1ch3/ourpoints.t')
# 存储tensor 方法2
with open('../data/p1ch3/ourpoints.t','wb') as f:
torch.save(points, f)
#读取tensor 方法1
points = torch.load('../data/p1ch3/ourpoints.t')
#读取tensor 方法2
with open('../data/p1ch3/ourpoints.t','rb') as f:
points = torch.load(f)
上面的存储方法把tensor保存到了outpoints文件,但是这个文件只能由PyTorch进行读取,这里作者又介绍了一种额外的方法,可以把tensor保存成其他项目也可以读取的格式。这里使用的库是h5py。
代码语言:javascript复制#注意这个库需要单独安装
import h5py
#存储数据,这里输入了一个key‘coords’,我理解这个存储是key-value格式的,这个key应该是可以自定义的?
f = h5py.File('../data/p1ch3/ourpoints.hdf5', 'w')
dset = f.create_dataset('coords', data=points.numpy())
f.close()
#读取数据
f = h5py.File('../data/p1ch3/ourpoints.hdf5', 'r')
dset = f['coords']
last_points = dset[-2:]
#把数据恢复到tensor中
last_points = torch.from_numpy(dset[-2:])
f.close()
关于张量操作的其他API
前面我们介绍了一些tensor操作,不过关于tensor操作还有各种各样的API,作者也没办法都讲一遍,所以作者让大家自己去看PyTorch官方文档,关于API的种类大概有如下几种:
- 构造张量,像前面提到的zeros,ones,还有从numpy中读取from_numpy()等
- 索引、切片、连接、转换
- 张量运算,这里面涉及的API比较多,比如tensor的加减乘除,归约,比较,频谱变换等等
- 随机采样
- 存储和加载,比如load()和save(),这个可能会比较频繁的用到
- 并行计算
最后再吐槽一下,这本书的中文翻译真的很烂啊,英文明明写的很好。