重点1——numpy和torch的相互转换
torch和numpy互相转换的时候,内存是共享的!因此改一个,必将修改另一个
import numpy as np
a = np.random.rand(4,3)
print(a)
array([[0.53097097, 0.39967826, 0.2925655 ],
[0.41214625, 0.85092555, 0.88813825],
[0.74869623, 0.20646449, 0.83864256],
[0.80688304, 0.89462968, 0.12394582]])
b = torch.from_numpy(a)
print(b)
0.5310 0.3997 0.2926
0.4121 0.8509 0.8881
0.7487 0.2065 0.8386
0.8069 0.8946 0.1239
[torch.DoubleTensor of size 4x3]
b.mul_(2)
print(b)
print(a)
1.0619 0.7994 0.5851
0.8243 1.7019 1.7763
1.4974 0.4129 1.6773
1.6138 1.7893 0.2479
[torch.DoubleTensor of size 4x3]
array([[1.06194194, 0.79935653, 0.58513101],
[0.8242925 , 1.7018511 , 1.7762765 ],
[1.49739246, 0.41292898, 1.67728512],
[1.61376609, 1.78925936, 0.24789164]])
重点2——等号两边是两个完全不一样的变量
当一个变量(numpy和torch都是)被重新赋值的时候,其内存会被重新分配!不过像+=,-=都不会重新分配内存,因此为了内存,尽量多的使用覆盖操作吧~
a=np.random.rand(3,2)
print(id(a))
139997338436912
a*=2
print(id(a))
139997338436912
a=a*2
print(id(a))
139997338438112
重点3——少了一个逗号,元组就变为了普通变量
当使用torch.transform中的Normalize变换的时候,注意给定的参数是数据分布的均值(mean)和标准差(std),且一定注意的一点是,当你的图像只是一个通道的时候,传入的一定是一个元祖,而不是单个元素,代码如下
transform = transforms.Compose([transforms.ToTensor(),
transforms.Normalize((0.5,), (0.5,)), # 一定不要少了这个点!!!
])
如上所述,如果少了0.5后面的",",则算法认为你传入了一个数值,那么,随后再从dataloader中取数据的时候就会出现如下错误too many indices for tensor of dimension 0,而错误的定位就在取batch数据的地方。
重点4——行列操作
在numpy和torch中,跟行列有关的函数,dim(axis)=0表示按列操作,dim(axis)=1表示按行操作
a = np.random.rand(4, 2)
array([[0.84739963, 0.77991831],
[0.10310179, 0.13808552],
[0.92275984, 0.13912286],
[0.6742185 , 0.32243499]])
''' 按行操作 '''
np.sum(a,axis=1)
array([1.62731793, 0.24118731, 1.0618827 , 0.99665349])
''' 按列操作 '''
np.sum(a,axis=0)
array([2.54747976, 1.37956167])
重点5——reshape和permute操作
在torch中,reshape操作是按照行优先进行拉伸或者折叠的,而permute就相当于transpose的高级版,可以对任意多的维度进行转置,而transpose只能对两个维度进行翻转;
In [4]: import torch
In [5]: a=torch.Tensor([1,2,3,4,5,6,7,8,9,10,11,12])
In [6]: a
Out[6]: tensor([ 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12.])
In [7]: a.shape
Out[7]: torch.Size([12])
In [8]: b=a.reshape((3,4))
In [9]: b
Out[9]:
tensor([[ 1., 2., 3., 4.],
[ 5., 6., 7., 8.],
[ 9., 10., 11., 12.]])
In [10]: c=b.permute(1,0)
In [11]: c
Out[11]:
tensor([[ 1., 5., 9.],
[ 2., 6., 10.],
[ 3., 7., 11.],
[ 4., 8., 12.]])
In [12]: d = c.reshape(12)
In [13]: d
Out[13]: tensor([ 1., 5., 9., 2., 6., 10., 3., 7., 11., 4., 8., 12.])
重点5——nn.ModuleList和nn.Sequential模块
在torch中,对一系列的操作而言,既可以用ModuleList,也可以用Sequential,两个模块很相近,但是也有不同的地方,此处做一个总结:
相同点
- 都可以使用迭代的方式对tensor进行操作,如下:
seq = nn.Sequential(
nn.Conv2d(3, 64, 3, 1, 1),
nn.ReLU(inplace=True),
nn.Conv2d(64, 64, 3, 1, 1),
nn.ReLU(inplace=True),
nn.Conv2d(64, 64, 3, 1, 1),
nn.ReLU(inplace=True)
)
mod = nn.ModuleList([
nn.Conv2d(3, 64, 3, 1, 1),
nn.ReLU(inplace=True),
nn.Conv2d(64, 64, 3, 1, 1),
nn.ReLU(inplace=True),
nn.Conv2d(64, 64, 3, 1, 1),
nn.ReLU(inplace=True)
])
input = torch.randn(1, 3, 10, 10)
x = input
for op in seq:
x = op(x)
print(x.shape)
x = input
for op in mod:
x = op(x)
print(x.shape)
输出为
torch.Size([1, 64, 10, 10])
torch.Size([1, 64, 10, 10])
- 都可以使用apply函数对内部子模块进行初始化
i = 0
def init_weight(m):
global i
if isinstance(m, nn.Conv2d):
print('>>> time: {}'.format(i))
init.normal_(m.weight.data)
i += 1
i = 0
seq.apply(init_weight)
i = 0
mod.apply(init_weight)
输出为
>>> time: 0
>>> time: 1
>>> time: 2
>>> time: 0
>>> time: 1
>>> time: 2
不同点
-
初始化的方式不同,例如上面的两种情况:
- nn.Sequential的初始化接收的是一个个操作,除此之外,还可以接受一个dict指定每个操作的名字,默认的话以序号命名;
- nn.ModuleList的初始化接收的是一个列表,列表中包含所有要进行的操作,不接受指定每个操作的名字的方式,只能以序号命名;
-
添加操作的方式不同:
- nn.ModuleList添加操作的方式有三种,append,extend,insert,基本和list的区别一样,不在赘述;
- nn.Sequential添加操作的方式只有一种,add_module,接收两个参数,第一个是名字或者序号,第二个是操作;
-
操作的整体性不一样:
- nn.Sequential虽然可以用迭代的方式进行,但是其整体是一个操作,亦即该操作与内置的操作一样,具有梯度等信息;
- nn.ModuleList不是一个整体,且只能用迭代的方式进行,本身不具有梯度等信息;