代码pytorch

model.train() 和 model.eval()

一般在模型训练和评价的时候会加上这两句,主要是针对由于model 在训练时和评价时 Batch Normalization 和 Dropout 方法模式不同:

  1. model.eval(),不启用 BatchNormalization 和 Dropout。此时pytorch会自动把BN和DropOut固定住,不会取平均,而是用训练好的值。不然的话,一旦test的batch_size过小,很容易就会因BN层导致模型performance损失较大;
  2. model.train() :启用 BatchNormalization 和 Dropout。 在模型测试阶段使用model.train() 让model变成训练模式,此时 dropout和batch normalization的操作在训练q起到防止网络过拟合的问题。
    因此,在使用PyTorch进行训练和测试时一定要记得把实例化的model指定train/eval。

super().init()

简单的说super().init(),就是继承父类的init方法,同样可以使用super()去继承其他方法

DataFrame

DataFrame是Python中Pandas库中的一种数据结构,它类似excel,是一种二维表。或许说它可能有点像matlab的矩阵,但是matlab的矩阵只能放数值型值(当然matlab也可以用cell存放多类型数据)

DataFrame的单元格可以存放数值、字符串等,这和excel表很像。

同时DataFrame可以设置列名columns与行名index,可以通过像matlab一样通过位置获取数据也可以通过列名和行名定位

unit8

unit8是无符号8位整型,范围[0,255]

2**16(2^16)

thermal

thermal = cv2.imread(thermal_path, cv2.IMREAD_ANYDEPTH)
#cv2.IMREAD_ANYDEPTH:当输入具有相应深度的时候,返回16/32位的图像,否则将其转换为8位
# 在0,2**16(2^16)范围内-->裁剪到相关范围(20800,27000)
thermal[thermal < self.ir_minval] = self.ir_minval
thermal[thermal > self.ir_maxval] = self.ir_maxval

rgb

rgb = cv2.imread(rgb_path)  # 读取图片,读进来的是BGR格式,默认为unit8格式
rgb = cv2.cvtColor(rgb, cv2.COLOR_BGR2RGB)  # 将BGR格式转换成RGB格式
rgb = rgb[:, self.crop_left:self.crop_right, :]  # 截取图片self.crop_left:self.crop_right的部分

depth

# 由于磁盘限制,只启用jpg,而不是实际的深度文件
depth = cv2.imread(depth_path)
# 3通道
depth = depth[:, self.crop_left:self.crop_right, :]

load_state_dict

我们在构造好了一个模型后,可能要加载一些训练好的模型参数。举例子如下:

假设 trained.pth 是一个训练好的网络的模型参数存储model = Net()是我们刚刚生成的一个新模型,我们希望model将trained.pth中的参数加载加载进来,但是model中多了一些trained.pth中不存在的参数,如果使用下面的命令:

state_dict = torch.load('trained.pth')
model.load_state_dict(state_dict)

会报错,说key对应不上,因为model你强人所难,我堂堂trained.pth没有你的那些个零碎玩意,你非要向我索取,我上哪给你弄去。

但是model不干,说既然你不能完全满足我的需要,那么你有什么我就拿什么吧,怎么办呢?下面的指令代码就行了。

model.load_state_dict(state_dict, strict=False)

src.utils.utils----602 line

state_dict = filter_model_dict(model, torch.load(path, map_location=map_location))

#torch.load加载字典, filter_model_dict函数用来过滤model中不必要的密钥,清除字典

model.load_state_dict(state_dict)  # 加载参数,即.pth文件中保存的模型参数

如果说我不清除字典会咋样,看看那个热的教师模型会不会能正常加载

Scheduler 调度程序

torch的ReduceLROnPlateau类:当参考的评价指标停止改进时,降低学习率,factor为每次下降的比例,训练过程中,当指标连续patience次数还没有改进时,降低学习率

torch的StepLR类:动态调整学习率,每隔step_size步,学习率乘以gamma倍

class torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer, mode='min', factor=0.1, patience=10, verbose=False, threshold=0.0001, threshold_mode='rel', cooldown=0, min_lr=0, eps=1e-08)

其中

  • optimer指的是网络的优化器
  • mode (str) ,可选择‘min’或者‘max’,min表示当监控量停止下降的时候,学习率将减小,max表示当监控量停止上升的时候,学习率将减小。默认值为‘min’
  • factor 学习率每次降低多少,new_lr = old_lr * factor
  • patience=10,容忍网路的性能不提升的次数,高于这个次数就降低学习率
  • verbose(bool) - 如果为True,则为每次更新向stdout输出一条消息。 默认值:False
  • threshold(float) - 测量新最佳值的阈值,仅关注重大变化。 默认值:1e-4
  • cooldown: 减少lr后恢复正常操作之前要等待的时期数。 默认值:0。
  • min_lr,学习率的下限
  • eps ,适用于lr的最小衰减。 如果新旧lr之间的差异小于eps,则忽略更新。 默认值:1e-8。

batch_size、epoch、iteration

batch_size、epoch、iteration是深度学习中常见的几个超参数:

(1)batchsize:每批数据量的大小。DL通常用SGD的优化算法进行训练,也就是一次(1 个iteration)一起训练batchsize个样本,计算它们的平均损失函数值,来更新参数。

(2)iteration:1个iteration即迭代一次,也就是用batchsize个样本训练一次。

(3)epoch:1个epoch指用训练集中的全部样本训练一次,此时相当于batchsize 等于训练集的样本数。

最初训练DNN采用一次对全体训练集中的样本进行训练(即使用1个epoch),并计算一次损失函数值,来更新一次权值。当时数据集较小,该方法尚可。后来随着数据集迅速增大,导致这种方法一次开销大进而占用内存过大,速度过慢。

后来产生了一次只训练一个样本的方法(batchsize=1),称作在线学习。该方法根据每一个样本的情况更新一次权值,开销小速度快,但收到单个样本的巨大随机性,全局来看优化性能较差,收敛速度很慢,产生局部震荡,有限迭代次数内很可能无法收敛。

目前常用随机梯度下降SGD来训练,相当于上述两个“极端”方法的折中:将训练集分成多个mini_batch(即常说的batch),一次迭代训练一个minibatch(即batchsize个样本),根据该batch数据的loss更新权值。这相比于全数据集训练,相当于是在寻找最优时人为增加了一些随机噪声,来修正由局部数据得到的梯度,尽量避免因batchsize过大陷入局部最优。

这种方法存在两对矛盾。由于一次只分析的一小部分数据,因此整体优化效果与batchsize有关:

batchsize越小,一个batch中的随机性越大,越不易收敛。然而batchsize越小,速度越快,权值更新越频繁;且具有随机性,对于非凸损失函数来讲,更便于寻找全局最优。从这个角度看,收敛更快,更容易达到全局最优。

batchsize越大,越能够表征全体数据的特征,其确定的梯度下降方向越准确,(因此收敛越快),且迭代次数少,总体速度更快。然而大的batchsize相对来讲缺乏随机性,容易使梯度始终向单一方向下降,陷入局部最优;而且当batchsize增大到一定程度,再增大batchsize,一次batch产生的权值更新(即梯度下降方向)基本不变。因此理论上存在一个最合适的batchsize值,使得训练能够收敛最快或者收敛效果最好(全局最优点)。

根据现有的调参经验,加入正则化项BN后,在内存容量允许的情况下,一般来说设置一个较大的batchsize值更好,通常从128开始调整。

当然,包括超参数对网络优化的影响在内的许多关于的DL理论尚不成熟,大多处在依靠实验尝试、观察、归纳总结的阶段。

batch_size可以理解为批处理参数,它的极限值为训练集样本总数,当数据量比较少时,可以将batch_size值设置为全数据集(Full batch cearning)。
实际上,在深度学习中所涉及到的数据都是比较多的,一般都采用小批量数据处理原则。

小批量训练网络的优点:

  • 相对海量的的数据集和内存容量,小批量处理需要更少的内存就可以训练网络。
  • 通常小批量训练网络速度更快,例如我们将一个大样本分成11小样本(每个样本100个数据),采用小批量训练网络时,每次传播后更新权重,就传播了11批,在每批次后我们均更新了网络的(权重)参数;如果在传播过程中使用了一个大样本,我们只会对训练网络的权重参数进行1次更新。
  • 全数据集确定的方向能够更好地代表样本总体,从而能够更准确地朝着极值所在的方向;但是不同权值的梯度值差别较大,因此选取一个全局的学习率很困难。

小批量训练网络的缺点:

  • 批次越小,梯度的估值就越不准确,在下图中,我们可以看到,与完整批次渐变(蓝色)方向相比,小批量渐变(绿色)的方向波动更大。

极端特例batch_size = 1,也成为在线学习(online learning);线性神经元在均方误差代价函数的错误面是一个抛物面,横截面是椭圆,对于多层神经元、非线性网络,在局部依然近似是抛物面,使用online learning,每次修正方向以各自样本的梯度方向修正,这就造成了波动较大,难以达到收敛效果

设置batch大小是一种艺术,太小时可能会使学习过于随机,虽然训练速率很快,但会收敛到不可靠的模型;batch过小时,网络训练需要很长时间,更重要的是它不适合记忆。

如何选择合适的batch_size值:

采用批梯度下降法mini batch learning时,如果数据集足够充分,用一半(甚至少的多)的数据训练算出来的梯度与全数据集训练full batch learning出来的梯度几乎一样。

在合理的范围内,增大batch_size可以提高内存利用率,大矩阵乘法的并行化效率提高;跑完一次epoch(全数据集)所需的迭代次数减少,对于相同数据量的处理速度进一步加快;在适当的范围内,batch_size越大,其确定的下降方向越准,引起训练波动越小。注意,当batch_size增大到一定程度,其确定的下降方向基本不会变化。

batch_size值增大到超过合理范围时,和全数据训练full batch learning就会表现出相近的症候;内存容量占有率增加,跑完一次epoch所需的迭代次数减少(全数据集),达到相同的精度所耗损的时间增加,从而对参数的修正也就显得更加缓慢。

调节 Batch_Size 对训练效果影响到底如何?
以 LeNet 在 MNIST 数据集的效果为例:

batch_size 太小,算法在 200 epoches 内不收敛。
随着 batch_size 增大,处理相同数据量的速度越快。
随着 batch_size 增大,达到相同精度所需要的 epoch 数量越来越多。
由于上述两种因素的矛盾,batch_size 增大到某个时候,达到时间上的最优
由于最终收敛精度会陷入不同的局部极值,因此batch_size 增大到某些时候,达到最终收敛精度上的最优。

stack()

官方解释:沿着一个新维度对输入张量序列进行连接。 序列中所有的张量都应该为相同形状。
浅显说法:把多个2维的张量凑成一个3维的张量;多个3维的凑成一个4维的张量…以此类推,也就是在增加新的维度进行堆叠。
outputs = torch.stack(inputs, dim=?) → Tensor

  1. 参数
    inputs : 待连接的张量序列。
    注:python的序列数据只有list和tuple。
    dim : 新的维度, 必须在0到len(outputs)之间。
    注:len(outputs)是生成数据的维度大小,也就是outputs的维度值。

  2. 重点
    函数中的输入inputs只允许是序列;且序列内部的张量元素,必须shape相等
    ----举例:[tensor_1, tensor_2,…]或者(tensor_1, tensor_2,…),且必须tensor_1.shape == tensor_2.shape
    dim是选择生成的维度,必须满足0<=dim

torch.mul和torch.mm

torch.mul(a, b)是矩阵a和b对应位相乘,a和b的维度必须相等,比如a的维度是(1, 2),b的维度是(1, 2),返回的仍是(1, 2)的矩阵
torch.mm(a, b)是矩阵a和b矩阵相乘,比如a的维度是(1, 2),b的维度是(2, 3),返回的就是(1, 3)的矩阵

forward()函数

nn.Module中包含了__call__函数,在函数中调用了forward,由于继承关系,MTALoss等函数同样具备__call__函数的功能,因此只要传入参数,自动调用forward函数

tensorboard

write将loss信息写入tensorboard,查看函数:

tensorboard --logdir=MM-DistillNet/0

optimizer.step()

当调用optimizer.step()的时候应当是loss.backward()的时候(loss.backward()的具体运算过程可以参看Pytorch 入门),这也就是经常会碰到,如下情况:

total_loss.backward()
optimizer_G.step()

loss.backward()在前,然后跟一个step。

那么为什么optimizer.step()需要放在每一个batch训练中,而不是epoch训练中,这是因为现在的mini-batch训练模式是假定每一个训练集就只有mini-batch这样大,因此实际上可以将每一次mini-batch看做是一次训练,一次训练更新一次参数空间,因而optimizer.step()放在这里。

scheduler.step()

scheduler.step():按照Pytorch的定义是用来更新优化器的学习率的,一般是按照epoch为单位进行更换,即多少个epoch后更换一次学习率,因而scheduler.step()放在epoch这个大循环下。

Varibale

torch.autograd.Variable是Autograd的核心类,它封装了Tensor,并整合了反向传播的相关实现

from .variable import Variable

Varibale包含三个属性:

data:存储了Tensor,是本体的数据
grad:保存了data的梯度,本事是个Variable而非Tensor,与data形状一致
grad_fn:指向Function对象,用于反向传播的梯度计算之用

Variable和tensor的区别和联系

Variable是篮子,而tensor是鸡蛋,鸡蛋应该放在篮子里才能方便拿走(定义variable时一个参数就是tensor)

Variable这个篮子里除了装了tensor外还有requires_grad参数,表示是否需要对其求导,默认为False

Variable这个篮子呢,自身有一些属性

比如grad,梯度variable.grad是d(y)/d(variable)保存的是变量y对variable变量的梯度值,如果requires_grad参数为False,所以variable.grad返回值为None,如果为True,返回值就为对variable的梯度值

比如grad_fn,对于用户自己创建的变量(Variable())grad_fn是为none的,也就是不能调用backward函数,但对于由计算生成的变量,如果存在一个生成中间变量的requires_grad为true,那其的grad_fn不为none,反则为none

比如data,这个就很简单,这个属性就是装的鸡蛋(tensor)

rgb = Variable(
    rgb.to(device),
    requires_grad=True
).to(device)
# requires_grad=True 表示参与反向传播

Variable可以理解为盒子,盒子里包含了tensor及对它的操作,例如当tensor作为神经网络节点时计算它的梯度。Variable 存在于 torch.autograd库中

Variable作为盒子不仅可以封装数据,还可以封装对其的操作,在使用时直接由盒子中取相关的材料即可。

requires_grad=True,variable变量中封装了对tensor的操作,即自动求导操作

在每次backward后,grad值是会累加的,所以利用BP算法,每次迭代都需要将grad清零。

optimizer.zero_grad()
loss.backward()
# 进行反向传播

**Variable**的定义在文件 torch/autograd/variable.py 中

import torch
from torch._six import with_metaclass

class VariableMeta(type):
    def __instancecheck__(cls, other):
        return isinstance(other, torch.Tensor)

class Variable(with_metaclass(VariableMeta, torch._C._LegacyVariableBase)):
    pass

from torch._C import _ImperativeEngine as ImperativeEngine
Variable._execution_engine = ImperativeEngine()

Variable._execution_engine.run_backward其实就是torch._C中的函数_ImperativeEngine。torch._C这个是调用的被编译之后的C++代码,Windows系统下可以在Python目录\Lib\site-packages\torch下找到_C.cp35-win_amd64.pyd这个文件,当然不同的Python版本名称也会略有不同,但是这个_C和.pyd是一样的。具体的函数实现代码我们可以从GitHub上pytorch/torch/csrc 这里找到。

device = torch.device(“cuda” if torch.cuda.is_available() else “cpu”)

将模型加载到指定设备上

device=torch.device(“cpu”)代表的使用cpu,而device=torch.device(“cuda”)则代表的使用GPU

model=model.to(device),将模型加载到相应的设备中。

将由GPU保存的模型加载到GPU上。确保对输入的tensors调用input = input.to(device)方法

device = torch.device("cuda")
model = TheModelClass(*args, **kwargs)
model.load_state_dict(torch.load(PATH))
model.to(device)

map_location是将模型加载到GPU上,model.to(torch.device(‘cuda’))是将模型参数加载为CUDA的tensor。最后保证使用.to(torch.device(‘cuda’))方法将需要使用的参数放入CUDA。

torch.no_grad()

torch.no_grad() 是一个上下文管理器,被该语句 wrap 起来的部分将不会track 梯度。

例如:

a = torch.tensor([1.1], requires_grad=True)
b = a * 2
打印b可看到其 grad_fn 为 mulbackward 表示是做的乘法。
Out[63]: tensor([2.2000], grad_fn=)

b.add_(2)
Out[64]: tensor([4.2000], grad_fn=)

可以看到不被wrap的情况下,b.grad_fn 为 addbackward 表示这个add 操作被track了

with torch.no_grad():
b.mul_(2)

在被包裹的情况下可以看到 b.grad_fn 还是为 add,mul 操作没有被 track. 但是注意,乘法操作是被执行了的。(4.2 -> 8.4)
Out[66]: tensor([8.4000], grad_fn=)

所以如果有不想被track的计算部分可以通过这么一个上下文管理器包裹起来。这样可以执行计算,但该计算不会在反向传播中被记录

同样还可以用 torch.set_grad_enabled()来实现不计算梯度。

def eval():
	torch.set_grad_enabled(False)
	...	# your test code
	torch.set_grad_enabled(True)

np.expand_dims

np.expand_dims:用于扩展数组的形状

axis=x表示在x位置添加数据

axis = -1 也就是相当于在最后插入

this_batch_labels[i] = np.expand_dims(this_batch_labels[i], axis=0)  # 表示在0位置添加数据

numpy.array.shape

numpy创建的数组都有一个shape属性,它是一个元组,返回各个维度的维数

shape[0]表示最外围的数组的维数,shape[1]表示次外围的数组的维数,数字不断增大,维数由外到内

>>> x  = np.array([[[1,2,3],[4,5,6]],[[7,8,9],[0,1,2]],[[3,4,5],[6,7,8]]])
>>> > print(x)
>>> > [[[1 2 3]
>>> > [4 5 6]]
 [[7 8 9]
  [0 1 2]]
 [[3 4 5]
  [6 7 8]]]
>>> print(x.shape)
>>> (3, 2, 3)
>>> print(x.shape[0])
>>> 3
>>> print(x.shape[1])
>>> 2
>>> print(x.shape[2])
>>> 3

你可能感兴趣的:(pytorch,pytorch)