1 安装
环境:ubuntu20 + conda + pip
命令:
pip install torch torchvision torchaudio -i https://pypi.tuna.tsinghua.edu.cn/simple
2 Tensor 的创建
-
由 numpy 转换
a = np.array([1,2,3]) b = torch.from_numpy(a)
-
torch 直接创建
a = torch.tensor([[1,2,3],[4,5,6]]) # 数据类型 int64 a = torch.IntTensor([[1,2,3],[4,5,6]]) # 数据类型 int32 a = torch.FloatTensor([[1,2,3],[4,5,6]]) # 数据类型 float32
-
tensor 数据类型的转换
tensor = torch.Tensor(2, 5) torch.char() # 将 tensor 投射为 int8 torch.byte() # 将 tensor 投射为 uint8 torch.short() # 将 tensor 投射为 int16 torch.int() # 将 tensor 投射为 int32 torch.long() # 将 tensor 投射为 int64 torch.half() # 将 tensor 投射为 float16 torch.float() # 将 tensor 投射为 float32 torch.double() # 将 tensor 投射为 float64
-
随机数
a = torch.rand(3,3) # 取 0 ~ 1 之间的随机数 a = torch.rand_like(a) # 创建一个 shape 与 a 相同的 tensor a = torch.randint(1, 10, (3, 3)) # [0, 10) 区间内的随机数
-
正太分布
a = torch.randn(3,3) # 正态分布(0,1)
-
全 0、全 1、全 n、对角矩阵
a = torch.ones(3, 3) # 全 1 a = torch.zeros(3, 3) # 全 0 a = torch.full([3, 3], 100) # 全 100 a = torch.eye(3, 3) # 对角矩阵
-
线性分布
a = torch.arange(0, 10, 2) # [0, 10),步长为 2 # 输出 tensor([0, 2, 4, 6, 8]) a = torch.arange(10) # [0, 10),步长为 1 a = torch.range(10) # [0, 10],步长为 1 a = torch.linspace(0, 10, steps=5) # 从 0 到 10,平分为 5 份 # 输出 tensor([ 0.0000, 2.5000, 5.0000, 7.5000, 10.0000])
-
tensor 索引
a = torch.randn(5, 3, 28, 28) b = a[:, :, 0:28, 0:20:2] # : 表示所有,0:28 表示 0~27,0:28:2 表示 0~27,步长为 2 c = a.index_select(2, torch.tensor([0,1])) # 根据第 2 维的index,筛选出 [0, 1] 的数据 # 等同于 a[:, :, [0, 1], :]
-
… 表示所有
a = torch.randn(5, 3, 28, 28) b = a[..., 0:28] # 等同于 a[:, :, :, 0:28]
-
reshape 更改维度
用于改变数据的维度,是否穿件新的数据区存储空间有两种情况:
a. 若要操作的 tensor 数据区为连续分布,则不创建,reshape 后指向原数据区;
b. 若要操作的 tensor 数据区不连续(被permut、transpose操作过),则创建新的数据区;a = torch.rand(1,2,3,3) b = a.reshape( 6, 3) # 将 shape 1*2*3*3 改成了 6*3 b = a.reshpae(-1, 3) # -1 表示该维度的大小自动计算,当然,不能同时出现两个 -1
-
view 更改维度
不创建新数据区存储空间,更改维度(的描述)。
注意:操作的 tensor 需要是数据连续分布,如果 tensor 使用 permut()、transpose() 操作过,数据区就不是连续分布的了,不能使用 view(),但可以使用 reshape()。a = torch.rand(1,2,3,3) b = a.view( 6, 3) # 将 shape 1*2*3*3 改成了 6*3 b = a.view(-1, 3) # -1 表示该维度的大小自动计算,当然,不能同时出现两个 -1
-
permute 重新排列维度(会改变数据分布)
a = torch.tensor(2,3,4) b = a.permute(2,1,0) # 维度排列从原来的 0,1,2 改成 2,1,0 # shape: [4,3,2]
-
transpose 交换两个维度
a = torch.tensor(2,3,4) b = a.transpose(0, 1) # 交换第 0 维和第 1 维 # shape: [3,2,4]
-
unsqueeze 增加1维度、squeeze 去掉1维度
不改变数据存储空间,更改维度(的描述)a = torch.rand(2,3) b = a.unsqueeze(1) # 在第 1 维处(0维后面)增加一维 # shape 变成:[2,1,3] c = b.squeeze(1) # 去掉第 1 维(前提是该处维数为1,否则不生效) # shape 变为:[2,3]
-
expand 扩展维度
注意:expand 不改变数据存储空间,虽然增加了维数,但新增的维数是指针,指向原来的数据区;并且,要扩展的维数只能是 1,大于 1 的维数不能扩展。a = torch.tensor([[0, 1, 2]]) # a: # tensor([[0, 1, 2]]) b = a.expand(2, 3) # 维数从原来的 1*3 扩展到 2*3 # tensor([[0, 1, 2], # [0, 1, 2]]) # 这两行 [0, 1, 2] 的内存指向同一个地址,更改其中一个数据,另一个也会改变 c = a.expand_as(b) # 扩展的 shape 和 b 保持一致
-
repeat 扩展数据(同时扩展维度)
存储空间会发生改变,有复制数据的操作。a = torch.tensor([0, 1, 2]) # a: # tensor([0, 1, 2]) b = a.repeat(3, 2) # 表示对应维度重复的次数 # tensor([[0, 1, 2, 0, 1, 2], # [0, 1, 2, 0, 1, 2], # [0, 1, 2, 0, 1, 2]])
-
cat 拼接维数
a = torch.rand(1,3,3) b = torch.rand(1,3,3) c = torch.cat([a,b], 0) # 在第 0 维上拼接 # shape: [2,3,3]
3 Tensor 的运算
- 乘法
torch.mm(a, b) # 矩阵乘法 a * b # 矩阵对应元素相乘 a * 3 # 矩阵所有元素乘以 3
- 加法、减法、除法
a + b # 对应元素相加 a + 2 # 所有元素加 2
-
n
x
n^x
nx 和
x
\sqrt{x}
x
a**2 # 幂 a.rsqrt() # 平方
-
e
x
e^x
ex 和
l
o
g
(
x
)
log(x)
log(x)
torch.exp(a) torch.log(a)
- 截断
a.clamp(4) # 元素小于 4 的都等于 4 a.clamp(4, 10) # 元素小于 4 的都等于 4,大于 10 的都等于 10
- 四舍五入
a.round() # float 的四舍五入
- 向上取整
a.ceil() # float 的
- 绝对值
a.abs(0)
- 最大、最小值
a.max() # 返回矩阵中的最大值 values, indices = a.max(0) # 返回指定维度最大值 a.argmax() # 返回最大值的索引 a.argmax(dim=0) # 返回指定维度最大值的索引 a.min() # 同上 a.argmin()
- 均值、求和
a.mean() a.mean(dim=0) # 指定维度的均值 a.sun() a.sun(dmi=0)
- 范数
∣ ∣ x ∣ ∣ = ( ∣ x 1 ∣ p + ∣ x 2 ∣ p + . . . + ∣ x n ∣ p ) 1 p ||x|| = (|x_1|^p + |x_2|^p +...+ |x_n|^p)^{\frac{1}{p}} ∣∣x∣∣=(∣x1∣p+∣x2∣p+...+∣xn∣p)p1a.norm() # 默认 p = 2 a.norm(3) # p = 3
- 统计 TOPn
a = torch.rand(10,3,3) values, indeces = a.topk(2, dim=0) # 指定维度中,统计 TOP2 的数据 # shape: [2, 3, 3]
- 判断相等、不相等
torch.eq(a, b) # 判断对应元素相等,返回 bool torch.eq(a, 0) # 判断与 1 相等,返回 bool torch.equal(a,b) # 判断不等
4 模型
4.1 nn.Sequential 与 nn.ModuleList
-
不同点1
nn.Sequential 内部实现了 forward 函数,因此可以不用写 forward 函数。而 nn.ModuleList 则没有实现内部 forward 函数。
对于nn.Sequential:class net1(nn.Module): def __init__(self): super(net1, self).__init__() self.seq = nn.Sequential( nn.Conv2d(1,20,5), nn.ReLU(), nn.Conv2d(20,64,5), nn.ReLU() ) def forward(self, x): return self.seq(x) input = torch.randn(16, 1, 20, 20) net1 = net1() print(net1(input).shape)
而对于nn.ModuleList:
class net2(nn.Module): def __init__(self): super(net2, self).__init__() self.modlist = nn.ModuleList([ nn.Conv2d(1, 20, 5), nn.ReLU(), nn.Conv2d(20, 64, 5), nn.ReLU() ]) #注意:只能按照下面利用for循环的方式 def forward(self, x): for m in self.modlist: x = m(x) return x input = torch.randn(16, 1, 20, 20) net2 = net2() print(net2(input).shape)
-
不同点2:
nn.Sequential可以使用OrderedDict对每层进行命名;from collections import OrderedDict class net_seq(nn.Module): def __init__(self): super(net_seq, self).__init__() self.seq = nn.Sequential(OrderedDict([ ('conv1', nn.Conv2d(1,20,5)), ('relu1', nn.ReLU()), ('conv2', nn.Conv2d(20,64,5)), ('relu2', nn.ReLU()) ])) def forward(self, x): return self.seq(x)
-
不同点3:
nn.Sequential 里面的模块按照顺序进行排列的,而nn.ModuleList 模块之间并没有什么先后顺序可言。见下面代码:class net3(nn.Module): def __init__(self): super(net3, self).__init__() self.linears = nn.ModuleList([nn.Linear(10,20), nn.Linear(20,30), nn.Linear(5,10)]) def forward(self, x): x = self.linears[2](x) x = self.linears[0](x) x = self.linears[1](x) return x
-
不同点4:
有的时候网络中有很多相似或者重复的层,我们一般会考虑用 for 循环来创建它们,而不是一行一行地写,比如:class net4(nn.Module): def __init__(self): super(net4, self).__init__() layers = [nn.Linear(10, 10) for i in range(5)] self.linears = nn.ModuleList(layers) def forward(self, x): for layer in self.linears: x = layer(x) return x
其他
- 打印设置
torch 打印浮点数时精度默认是 4 位,可以自行设定精度;torch.set_printoptions( precision=None, # 保留位数 threshold=None, # 行数超过该值就折叠显示,默认4 edgeitems=None, # 折叠后显示出的行数,默认3 linewidth=None, # 显示宽度 profile=None, # 数据显示的长度,有default、short、full,默认short sci_mode=None) # 科学计数显示,有True、False、None,如果为None则由框架选择方法