导包:
| import torch |
| import numpy as np |
#张量数据类型
# 一切都与张量有关
pytorch 的数据类型基本上和 python 的数据类型差不多,不过变成了对应的具有维度的张量类型,但 pytorch 只是个面向科学计算的 GPU 加速库,而不是完备的语言库,没有类件对字符串提供支持。
# 如何表示字符串(string)
- One - hot
- [0, 1, 0, 0, ...]
One-hot编码
表示,比如用一维向量 [1 0]
表示 dog
类别, [0 1]
表示 cat
类别,用数字代替了字符串,这和计算机利用 ASCII 码表示字符是一样的方式,比如 0x41
表示 A
字符,但是不能用来表示语言,因为语言相同的词具有语义相关性和语义相反性,使用 One-hot编码
的形式就很难具有区分度。
- Embedding
- Word2vec
- glove
用数字的方式表示语言,自然语言处理里有专门的方法来表示,就是 embedding
# 数据类型
Data type | dtype | CPU Tensor | GPU Tensor |
---|
32-bit floating point | torch.float32 or torch.float | torch.FloattTensor | torch.cuda.FloatTensor |
64-bit floating point | torch.float64 or torch.double | torch.DoubleTensor | torch.cuda.DoubleTensor |
16-bit floating point | torch.float16 or torch.half | torch.HalfTensor | torch.cuda.HalfTensor |
8-bit integer(unsigned) | torch.uint8 | torch.ByteTensor | torch.cuda.ByteTensor |
8-bit integer(signed) | torch.int8 | torch.CharTensor | torch.cuda.CharTensor |
16-bit integer(signed) | torch.int16 or torch.short | torch.ShortTensor | torch.cuda.ShortTensor |
32-bit integer(signed) | torch.int32 or torch.int | torch.IntTensor | torch.cuda.IntTensor |
64-bit integer(signed) | torch.int64 or torch.long | torch.LongTensor | torch.cuda.LongTensor |
主要使用的是 torch.FloatTensor
, torch.IntTensor
, torch.ByteTensor
,当设备不同时,即使是同一个数据,数据类型也是不一样的。
# 类型判断
| a = torch.randn(2, 3) |
| print(a.type()) |
| print(type(a)) |
| print(isinstance(a, torch.FloatTensor)) |
# 同一数据在不同设备内类型不同
| print(isinstance(data, torch.cuda.DoubleTensor)) |
| data = data.cuda() |
| print(isinstance(data, torch.cuda.DoubleTensor)) |
#标量(维度和秩都为 0)
torch 里面最简单的数据类型
| print(torch.tensor(1.)) |
| print(torch.tensor(1.3)) |
| print(torch.tensor([1.3])) |
# 标量通常用于计算误差(loss)
| a = torch.tensor(2.2) |
| print(a.shape) |
| print(len(a.shape)) |
| print(a.size()) |
#向量(维度和秩都为 1)
torch 里不管多少维统一都叫张量
| print(torch.tensor([1.1])) |
| print(torch.tensor([1.1, 2.2])) |
| |
| print(torch.FloatTensor(1)) |
| print(torch.FloatTensor(2)) |
# 用 numpy 数组生成 Tensor
| data = np.ones(2) |
| print(data) |
| print(torch.from_numpy(data)) |
向量通常用于神经元的偏置和神经网络线性层输入
# 得到 1 维的张量(shape 或 size 为 1)
| a = torch.ones(2) |
| print(a.shape) |
# 得到 2 维的张量(shape 或 size 为 2)
| a = torch.randn(2, 3) |
| print(a) |
| print(a.shape) |
| print(a.size(0)) |
| print(a.size(1)) |
Dim 2 的张量通常用于批量的线性层输入
# 得到 3 维的张量(shape 或 size 为 3)
| a = torch.rand(1, 2, 3) |
| print(a) |
| print(a.shape) |
| print(a[0]) |
| print(a[0][0]) |
| print(list(a.shape)) |
Dim 3 的张量通常用于批量循环神经网络输入
# 得到 4 维的张量(shape 或 size 为 4)
| a = torch.rand(2, 3, 28, 28) |
| |
| |
| print(a) |
| print(a.shape) |
特别适用于卷积神经网络
# 补充
| print(a.numel()) |
| print(a.dim()) |
| print(torch.tensor(1).dim()) |
# 创建 Tensor
# 从 numpy 导入数据
| a = np.array([2, 3.3]) |
| print(torch.from_numpy(a)) |
| a = np.ones([2, 3]) |
| print(torch.from_numpy(a)) |
# 从列表里导入
| print(torch.tensor([2., 3.2])) |
| print(torch.FloatTensor([2., 3.2])) |
| print(torch.tensor([[2., 3.2], [1., 22.3]])) |
# 生成未初始化的数据,(申请未初始化的内存空间)
- torch.empty () # 输入 shape
- torch.FloatTensor (dim1, dim2, dim3) # 输入 shape
- torch.IntTensor (dim1, dim2, dim3) # 输入 shape
| print(torch.empty(1)) |
| print(torch.Tensor(2, 3)) |
| print(torch.IntTensor(2, 3)) |
| print(torch.FloatTensor(2, 3)) |
未初始化的数据存在隐患,需要用其它的类型将其覆盖掉,否则喂给神经网络会出现 torch.nan
或 torch.inf
# 设置默认类型
Tensor () 是一个泛化概念,若不指定,默认是 FloatTensor ()
| print(torch.tensor([1.2, 3]).type()) |
| torch.set_default_tensor_type(torch.DoubleTensor) |
| print(torch.tensor([1.2, 3]).type()) |
增强学习一般使用 double (64 位有更高的精度),其他一般使用 float
# 随机初始化
# 随机均匀分布初始化 (rand/rand_like,randint)
rand () 随机的使用 [0,1) 的均值分布初始化
| print(torch.rand(3, 3)) |
| a = torch.rand(3, 3) |
| print(torch.rand_like(a)) |
| print(torch.randint(1, 10, [3, 3])) |
均匀采样 0 ~ 10 的 Tensor,要用 x=10*torch.rand(dim1,dim2)
, randint
只能采样整数
# 随机标准正态分布的初始化
N (0,1),其中 N (u,std),即 N (均值,方差 (或标准方差))
# 随机离散正态分布
| print(torch.normal(mean=torch.full([10], 0), std=torch.arange(1, 0, -0.1))) |
torch.normal()
先将 3×3 矩阵打平成 [9]
的矩阵,使用 torch.full()
生成长度为 10 但都为 0 的均值,方差是从 1 到 0 步长为 0.1 逐次减小
# torch.full()
| print(torch.full([2, 3], 7)) |
| print(torch.full([], 7)) |
| print(torch.full([1], 7)) |
# arange/range
生成等差的张量
| print(torch.arange(0, 10)) |
| print(torch.arange(0, 10, 2)) |
torch.range()
在 pytorch 0.5 中已经移除
# linspace/logspace
生成等分的张量 (线性间距向量)
| print(torch.linspace(0, 10, steps=4)) |
| print(torch.linspace(0, 10, steps=10)) |
| print(torch.linspace(0, 10, steps=11)) |
| print(torch.logspace(0, -1, steps=11)) |
| print(torch.logspace(0, 1, steps=11)) |
| print(torch.logspace(0, 2, steps=11, base=2)) |
| print(torch.logspace(0, 1, steps=11, base=10)) |
base
参数可以设置为 2,10,e 等底数
# ones/zeros/eye
| print(torch.ones(3, 3)) |
| print(torch.zeros(3, 3)) |
| print(torch.eye(3, 4)) |
| a = torch.zeros(3, 3) |
| print(torch.ones_like(a)) |
# randperm (随机打散)
| a = torch.randn(2, 3) |
| b = torch.randn(2, 2) |
| idx = torch.randperm(2) |
| print(idx) |
| print(a[idx]) |
| print(b[idx]) |
随机种子用来 shuffle (洗牌)
# 索引与切片
| a = torch.rand(4, 3, 28, 28) |
# 直接索引
| print(a[0].shape) |
| print(a[0, 0].shape) |
| print(a[0, 0, 0, 0]) |
# 取连续片段
| print(a[:2].shape) |
| print(a[:2, :1, :, :].shape) |
| print(a[:2, 1:, :, :].shape) |
| print(a[:2, -1:, :, :].shape) |
# 隔行取样
| print(a[:, :, 0:28:2, 0:28:2].shape) |
| print(a[:, :, ::2, ::2].shape) |
# 特定索引取样
| print(a.index_select(0, torch.tensor([0, 2])).shape) |
| print(a.index_select(1, torch.tensor([0, 2])).shape) |
| print(a.index_select(2, torch.arange(28)).shape) |
| print(a.index_select(2, torch.arange(8)).shape) |
# ...
当 ...
出现时,右边索引理解为最右边
| print(a[...].shape) |
| print(a[0, ...].shape) |
| print(a[0, ..., ::2].shape) |
| print(a[:, 1, ...].shape) |
| print(a[..., :2].shape) |
# 用掩码索引
弊端:将数据打平
| x = torch.randn(3, 4) |
| mask = x.ge(0.5) |
| print(torch.masked_select(x, mask)) |
| print(torch.masked_select(x, mask).shape) |
# 使用打平索引
| src = torch.tensor([[4, 3, 5], [6, 7, 8]]) |
| print(torch.take(src, torch.tensor([0, 2, 5]))) |
# 维度变换
# View/reshape
# view
| a = torch.rand(4, 1, 28, 28) |
| print(a.view(4, 28*28)) |
| print(a.view(4, 28*28).shape) |
| print(a.view(4*28, 28)) |
| print(a.view(4*28, 28).shape) |
| print(a.view(4*1, 28, 28).shape) |
| |
| b = a.view(4, 784) |
| print(b.view(4, 28, 28, 1)) |
| print(a.view(4, 783)) |
数据维度丢失。数据的存储 / 维度的顺序很重要
# reshape
| a = torch.arange(4.) |
| print(a) |
| print(torch.reshape(a, (2, 2))) |
| print(a.reshape(2, 2)) |
一般用 reshape
#Squeeze与unsqueeze
# unsqueeze
| print(a.unsqueeze(0).shape) |
| print(a.unsqueeze(-1).shape) |
| print(a.unsqueeze(-4).shape) |
a.unsqueeze(5).shape
添加超出原维度的索引会报错
| b = torch.rand(32) |
| |
| f = torch.rand(4, 32, 14, 14) |
| b = b.unsqueeze(1).unsqueeze(2).unsqueeze(0) |
| print(b.shape) |
# squeeze
| b = torch.rand(1, 32, 1, 1) |
| print(b.squeeze().shape) |
| print(b.squeeze(0).shape) |
| print(b.squeeze(-1).shape) |
| print(b.squeeze(1).shape) |
# Expand/repeat (维度扩展)
| b = torch.rand(1, 32, 1, 1) |
| print(b.expand(4, 32, 14, 14).shape) |
| print(b.expand(-1, 32, -1, -1).shape) |
| print(b.expand(-1, 32, -1, -4).shape) |
不建议 repeat
| print(b.repeat(4, 32, 1, 1).shape) |
| print(b.repeat(4, 1, 1, 1).shape) |
| print(b.repeat(4, 1, 32, 32).shape) |
# Transpose/t/permute (矩阵转置)
# t()
| b = torch.rand(1, 32, 1, 1) |
| print(b.t()) |
| a = torch.rand(3, 4) |
| print(a.t()) |
# transpose()
| a = torch.rand(4, 3, 32, 32) |
| print(a.transpose(1, 3).shape) |
| |
| a1 = a.transpose(1, 3).contiguous().view(4, 3*32*32).view(4, 3, 32, 32) |
| a2 = a.transpose(1, 3).contiguous().view(4, 3*32*32).view(4, 32, 32, 3).transpose(1, 3) |
| print(a1.shape, a2.shape) |
| print(torch.all(torch.eq(a, a1))) |
| print(torch.all(torch.eq(a, a2))) |
.view()
会导致维度顺序关系模糊,需要人为追踪
# permute()
| a = torch.rand(4, 3, 28, 28) |
| print(a.transpose(1, 3).shape) |
| |
| b = torch.rand(4, 3, 28, 32) |
| print(b.transpose(1, 3).shape) |
| print(b.transpose(1, 3).transpose(1, 2).shape) |
| print(b.permute(0, 2, 3, 1).shape) |