Pytorch 基础
— ModuleList 和Sequential比较
Sequential实现了forward函数,然而ModuleList里面没有forward函数。ModuleList本质是多个Moudle的list,与普通list的区别是它能被主module所识别,其参数会加入主module的参数中。
1.Sequential用法
实例化
(1)先实例化nn.Sequential(),再调用add_module方法添加子模块。
net = nn.Sequential()
net.add_module('conv', nn.Conv2d(3, 3, 3))
net.add_module('batchnorm', nn.BatchNorm2d(3))
(2)实例化nn.Sequential()同时,加入各子模块。
net = nn.Sequential(
nn.Conv2d(3, 3, 3),
nn.BatchNorm2d(3)
)
(3)实例化nn.Sequential()同时,使用OrderedDict加入各子模块并命名。
from collections import OrderedDict
net= nn.Sequential(OrderedDict([
('conv', nn.Conv2d(3, 3, 3)),
('batchnorm', nn.BatchNorm2d(3)),
('activation_layer', nn.ReLU())
]))
调用
(1)直接调用整个模块
input = torch.rand(1, 3, 4, 4)
output = net(input) #此时会一次性调用模块中各子模块,故在定义时,各子模块的维度和顺序要安排好,即前一个子模块的输出符合下一个子模块的输入要求。
(2)依次调用各子模块
input = torch.rand(1, 3, 4, 4)
'''名字调用子模块'''
output = net.conv(input)
output = net.batchnorm(output)
'''索引调用子模块'''
# output = net[0](input)
#output = net[1](output)
2.ModuleList用法
#1.实例化
linears = nn.ModuleList([nn.Linear(input_size,layers_size)])
#2.扩展
linears.extend([nn.Linear(layers_size, layers_size) for i in range(1, self.num_layers-1)])
#3.添加元素
linears.append(nn.Linear(layers_size, output_size)
#4.调用
#错误操作
#output = modelist(input) #modellist没有实现forward方法
#正确操作
for model in modellist:
input = model(input)
#####
—Parameter相关
- Module.parameters()
构建好神经网络后,网络的参数都保存在parameters属性当中。
一般先实例化一个预先定义好的网络net,再net.parameters()得到这个网络的参数,用作优化器的输入,则网络参数会随着训练进行更新。
当模型涉及多个网络时,可将这多个网络的parameters转化成list,再相加,作为优化器的输入。
param = list(encoder.parameters()) +list(decoder.parameters())
optimizer = optim.Adam(param, lr=1e-3)
-
torch.nn.Parameter()
self.v = torch.nn.Parameter(torch.FloatTensor(hidden_size))
可理解为一个类型转换函数,将一个不可训练的类型Tensor转换成可以训练的类型parameter类型,并将这个parameter绑定到这个module里面(即网络的Module.parameters()中)。
- torch.nn.ParameterList()
类似于moduleList(),都在定义网络时使用。只不过ParameterList()列表元素是Parameter()类型的变量,而moduleList()列表元素是各种module。
class MyModule(nn.Module):
def __init__(self):
super(MyModule, self).__init__()
self.params = nn.ParameterList([nn.Parameter(torch.randn(10, 10)) for i in range(10)])
def forward(self, x):
for i, p in enumerate(self.params):
x = self.params[i // 2].mm(x) + p.mm(x)
return x
— 模型保存和加载
1.两种模型保存和加载方式
-
保存整个网络的的结构信息和模型参数信息,对象是网络net。
# 保存整个模型 torch.save(model_object, 'resnet.pth') # 加载整个模型 model = torch.load('resnet.pth')
-
保存神经网络的模型参数,对象是网络的参数net.state_dict()。
# 保存模型参数 torch.save(my_resnet.state_dict(), "my_resnet.pth") # 加载模型参数 my_resnet = resnet() #加载模型参数前要先实例化模型 my_resnet.load_state_dict(torch.load("my_resnet.pth"))
2.GPU上训练好的模型,怎么在CPU上运行
# torch.load时添加 map_location 和loc参数
encoder = EncoderRNN(lang_size, hidden_size)
encoder.load_state_dict(torch.load('./encoder.pth', map_location=lambda storage, loc: storage))
—variable, tensor与numpy相互转化的方法
- list、numpy $ \rightarrow$ Tensor、Variable、cuda
s_list = [1, 2, 3]
s_numpy = np.array(s_list) #list ---- numpy
s_tensor = torch.Tensor(s_list) #list ---- tensor
s_tensor = torch.from_numpy(s_numpy) #numpy ---- tensor
s_variable = Variable(s_tensor) #tensor ---- variable
s_cuda = s_tensor.cuda() #tensor ---- cuda
s_cuda = s_variable.cuda() #variable ---- cuda
- Tensor、Variable、cuda $ \rightarrow$ list、numpy
s_list = s_cuda.cpu().data.numpy().tolist() #cuda - variable - tensor - numpy - list
s = s_tensor.item() #当tensor是0维时使用,返回其对应的数值。 一般用在累加loss中。
—Python、Numpy、Torch中的类型转换
-
Python
a = 1 b = str(a) print(a, type(a)) # 1 <class 'int'> print(b, type(b)) # 1 <class 'str'>
-
Numpy
a = np.array([1, 2, 3]) b = a.astype(np.int16) c = a.astype(float) # 同np.float、np.float64 print(a, a.dtype) # [1 2 3] int32 print(b, b.dtype) # [1 2 3] int16 print(c, c.dtype) # [1. 2. 3.] float64 print(type(a)) # <class 'numpy.ndarray'>
-
Torch
a = torch.Tensor([0, 1, 0]) b = a.bool() c = a.type_as(torch.BoolTensor()) d = a.type(torch.BoolTensor) print(a, a.type()) # tensor([0., 1., 0.]) torch.FloatTensor print(b, b.type()) # tensor([False, True, False]) torch.BoolTensor print(c, c.type()) # tensor([False, True, False]) torch.BoolTensor print(d, d.type()) # tensor([False, True, False]) torch.BoolTensor print(type(a)) # <class 'torch.Tensor'>
— Sigmoid、BCELoss、BCEWithLogitsLoss
1. sigmoid
sigmoid函数输出范围(0,1),常作为二分类问题的输出层。
-
输入输出
Input:(N, *),*表示后面可以添加任意数量个维度。
Output:(N, *),维度同Input。
-
计算公式 \(\text{Sigmoid}(x) = \frac{1}{1 + \exp(-x)}\)
$x$为Input中某一个元素的值。
2.BCELoss
用作二分类问题的损失函数。
- 参数
- weight:用于调整各个样本的权重,默认是一个全1的向量。
- reduction:默认为mean,表示对batch的数据的损失求平均。作用和size_average、reduce重合。
-
输入输出
-
输入
-
Input:(N,*) ,Input为经过sigmoid的网络输出。
-
Target:(N, *),维度和输入相同。Target各元素的值一般为0或1。
其中,N为batch_size,即一批有N个样本。
若(N,*)中的“*”为”d1,…,dk”,则表示一个样本中有d1*…*dk个标签要进行二分类。
-
-
输出
-
Output: scalar,所有样本,所有标签的平均损失。
若reduction参数为none,则维度和Target相同,为(N, *)。当样本中含有填充的标签时,考虑使用此种类型的Output与mask结合,进行特殊的平均化。
-
-
-
计算公式
-
若不对所有样本的损失取平均,即reduction=none时, \(\ell(x, y) = L = \{l_1,\dots,l_N\}^\top, \quad l_n = - w_n \left[ y_n \cdot \log x_n + (1 - y_n) \cdot \log (1 - x_n) \right]\) $N$是 batch size,表示有N个样本。
$l_n$为第n个样本的损失。
$x$为预测的二分类样本向量,$y$为实际的二分类样本向量(label);$x_n$,$y_n$分别和第n个样本对应。
$w_n$为第n个样本的权重, $w_n$默认为1。
-
若对所有样本的损失取平均 \(\ell(x, y) = \operatorname{mean}(L)\)
-
3. BCEWithLogitsLoss
BCEWithLogitsLoss相当于Sigmoid+BCELoss
-
参数和输入输出和BCELoss基本一致
仅多一个参数pos_weight
-
维度等于Input的最低维度或者和Input相同,若为Input的最低维度则系统会扩展至Input同维度。
pos_weight的元素$p_n$表示Input对应元素为正例时的权重。
p_n > 1
增加 recall,p_n < 1
增加 precision。
-
-
计算公式
-
不考虑调整正例权重时 \(\ell(x, y) = L = \{l_1,\dots,l_N\}^\top, \quad l_n = - w_n \left[ t_n \cdot \log \sigma(x_n) + (1 - t_n) \cdot \log (1 - \sigma(x_n)) \right]\)
-
考虑调整正例权重时 \(\ell(x, y) = L = \{l_1,\dots,l_N\}^\top, \quad l_n = - w_n \left[ p_n y_n \cdot \log \sigma(x_n) + (1 - y_n) \cdot \log (1 - \sigma(x_n)) \right]\)
-
对所有样本所有标签的损失求平均 \(\ell(x, y) = \operatorname{mean}(L)\)
-
— (log)Softmax 、NLLLoss、CrossEntropyLoss
CrossEntropyLoss 相当于nn.LogSoftmax()+nn.NLLLoss()
1. softmax
Softmax用于多分类过程中,它将每个类别的输出,映射到(0,1)区间内,看作是这个类别的概率,从而来进行多分类,各类别概率和为1。
- 计算公式
其中$x_i$为第i个类别对应神经元的输出,$Softmax(x_i)$为第i个类别的概率。
-
Softmax求导 $$
$$
-
nn.Softmax() 解析
-
输入输出
input维度无要求,output和input同维度。
-
参数
dim:对Input的哪个维度使用Softmax进行计算,Output相应维度的和将为1。
-
2.Logsoftmax
- LogSoftmax是在Softmax基础上做一个log运算:
由于$Softmax$输出都是$0-1$之间的,因此$LogSoftmax$输出的是小于$0$的数,输出值的范围在$[-inf ,0]$
-
LogSoftmax求导 $$
$$
-
实际中,一般都是用LogSoftmax(),因为它比Softmax()计算更快,有更好的数值特性。
3. NLLLoss()
negative log likelihood loss,负对数似然函数,用于多分类(0,1,… ,C-1)问题的损失函数,要求输入的每一个类别的概率为log-probabilities,故一般和LogSoftmax联合使用。
- 参数
- weight:(C),用于调整各个类别的权重,默认是一个全1的向量。
- reduction:默认为mean,表示对batch的数据的损失求平均。作用和size_average、reduce重合。
- ignore_index :忽略目标值为ignore_index的样本的损失,也就是在对所有样本的损失求平均时不考虑此类样本。一般用于含有填充项的问题中,如机器翻译,文本分类等。
-
输入,输出
-
输入
-
Input:(N, C) 或 (N, C, d_1, d_2, …, d_K) ,经过网络的输出。
-
Target:(N) 或 (N, d_1, d_2, …, d_K),真实的类别,即Label。
其中,N为batch_size,即一批有N个样本。
C表示有每一个标签有C个类别,后面扩展的d1…dk可理解成有d1*…*dk个标签要进行C分类。
-
-
输出
-
Output:scalar ,所有样本的所有标签的平均损失。
若reduction参数为none,则维度和Target相同,为(N) 或 (N, d_1, d_2, …, d_K)。
-
-
-
计算公式
- reduction参数为none,即不对batch样本loss取平均
x为Input,$x_n$为第n个样本对应的向量(维度为(C))。
y为Target,$y_n$为第n个样本的对应的真实类别(scalar),$x_{n,y_n}$为,向量$x_n$中与类别$y_n$对应的元素值。
$l_n$为第n个样本的损失,$w_{y_n}$为类别$y_n$的权重。
- 若reduction参数为mean,默认如此。
4. CrossEntropyLoss()
-
相当于nn.LogSoftmax
+
nn.NLLLoss -
参数和输入输出和NLLLoss()基本一致
-
计算公式
- 不考虑各类别的权重
其中,x为input,class为Target。
- 考虑各类别的权重 \(\text{loss}(x, class) = weight[class] \left(-x[class] + \log\left(\sum_j \exp(x[j])\right)\right)\)
— torch.nn与torch.nn.functional之间的区别和联系
torch.nn一般要先放在init()中实例化,而torch.nn.functional直接在forword()中使用,即torch.nn必须添加到nn.Module容器中才能使用,而torch.nn.functional则作为一个函数调用。
以Conv2d为例,nn.Conv2d是一个类,而F.conv2d()是一个函数,nn.Conv2d的forward()函数是用F.conv2d()实现的。F.conv2d()仅仅是做一个数学计算,并不会能像nn.Conv2d那样构建一个层,并随着训练更新层次中的参数。
在建图过程中,往往有两种层,一种如全连接层,卷积层等,当中有Variable
,另一种如Pooling层,Relu层等,当中没有Variable
。对于有Variable的层,为了能更新其中的参数,必须用torch.nn来定义,而对于没有Variable的层既可以用torch.nn定义,也可以直接使用torch.nn.functional。