pytorch获取中间层参数、输出与可视化

  2019-7-10 


获取模型中间层的权重和其他参数

#先设定如下网络
#定义网络结构
class net(nn.Module):
    def __init__(self):
        super().__init__()
	    self.c1 = nn.Sequential(
	    nn.Conv2d(3,16,5,1,2), 
	    nn.ReLU()
	        ) 
	    self.c2 = nn.Sequential(
	        nn.Conv2d(16,32,5,1,2), 
	        nn.ReLU(),
	    )
	    self.fc = nn.Linear(2097152,2)
	def forward(self,x):
	    x = self.c1(x)
	    x = self.c2(x)
	    x = x.view(x.size(0), -1) 
	    x = self.fc(x) 
	    return x    
model = net()
model = model.cuda()
#载入先前训练好并保存的模型权重
model.load_state_dict(torch.load('model_wts.pkl'))
#此时网络的结构为
model
>>
net(
  (c1): Sequential(
    (0): Conv2d(3, 16, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
    (1): ReLU()
  )
  (c2): Sequential(
    (0): Conv2d(16, 32, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
    (1): ReLU()
  )
  (fc): Linear(in_features=2097152, out_features=2, bias=True)
)
	
#获取某一层
model.c1
>>
Sequential(
  (0): Conv2d(3, 16, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
  (1): ReLU()
)
model.fc
>>
Linear(in_features=1048576, out_features=2, bias=True)
#获取Sequential里的子层
model.c1[0]
>>
Conv2d(3, 16, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
#获得某层的权重
model.c1[0].weight
>>
Parameter containing:
tensor([[[[ 2.7182e-03, -8.7767e-03,  3.2988e-02, -1.0006e-01, -1.1177e-01],
          [-2.9155e-02, -6.2152e-02,  4.1465e-02, -4.5812e-02,  6.7885e-02],
          [-1.0680e-01, -1.0023e-01, -1.7158e-02, -1.3828e-02,  5.7319e-02],
          [ 5.1668e-02, -4.2982e-02,  2.7770e-02, -1.1801e-01,  7.9863e-02],
          [ 1.1050e-01,  2.4979e-02,  5.1047e-03, -4.6120e-02, -9.9121e-02]],
··························#省略

#参数
model.c1[0].parameters()为该层的参数,包含梯度等等
parameters()输出的参数在训练的时候需要传入优化器,比如在训练网络的时候,model的所有参数
都要传入优化器,则是 optimizer = torch.optim.Adam(model.parameters(),lr=LR)
又如下面一条迁移学习,只要训练最后一层,就只将最后一层的参数传入了优化器

#层的迭代器
for layer in model.SequentialName.children():
	pass
#应用方法可以见下面迁移学习部分

所以如果要进行迁移学习

#以VGG举例
VGG的结构为
VGG{
(features):Sequential(....略....)
(classifier):Sequntial(....略.........(6)Linear(4096->1000))
}

#这里采用预训练的,[创建vgg网络实例]
vgg = models.vgg16(pretrained=True)
#[设置冻结层](只改变classifier里的最后一个线性层,特征提取层不变)
#冻结住特征提取层(vgg取名叫features)的参数
for param in vgg.features.parameters():
	param.requires_grad=False
#[微调模型](只改变最后一层fc层,将1000类分类变为我想要的2类分类)
#注意如果用vgg.classifier[6].out_features = 2的话以后会遇到问题,输出还是1000种类
   vgg.classifier[6] = nn.Linear(4096,2)
#优化器设置 (由于我们直接用预训练的权重,所以只需要训练分类器的参数,所以只将最后一个分类器层的classifier.parameters传入优化器)
optimizer = optim.SGD(vgg.classifier.parameters(),lr=0.0001,momentum=0.5)
#还可以继续对模型进行一些修改,比如修改最后一层中的dropout
#这里使用了迭代器
for layer in vgg.classifier.children():
	if (type(layer) == nn.Dropout):
		layer.p=0.2

使用pytorch hook进行中间层输出与可视化

(这里hook上面第一节中创建的网络)

#定义用于hook的类
class LayerActivations():
	#定义这个变量用于储存结果
	features = None
	#类初始化。当前向传播的时候(即图像数据通过层传输的时候),调用register_forward_hook方法。
	#register_forward_hook方法即为[钩子],此方法返回一个句柄保存到self.hook
	def __init__(self,model,layer_num):
		self.hook = model[layer_num].register_forward_hook(self.hook_fn)
	#hook函数具体执行的方法,即hook方法
	#register_forward_hook将三个参数传入hook_fn方法内
	#module:允许访问层本身 input:流进层的数据 output:层变换后的流出的数据或激活
	def hook_fn(self,module,input,output):
		#将输出保存到[自己设置的features变量中]
		self.features = output.cpu()
	#注销句柄self.hook
	def remove(self):
		self.hook.remove()

#定义hook类实例
conv_out = LayerActivations(model.c1,0)
#运行模型
output = model(img)
#注销函数
conv_out.remove()

# 在hook class中被保存到了features变量的即为输出,自己定义的
activations = conv_out.features

#activations 即为层输出

#对其进行可视化
fig = plt.figure(figsize=(20,50))
fig.subplots_adjust(left=0,right=1,bottom=0,top=0.8,hspace=0,wspace=0.2)
for i in range(30):
ax = fig.add_subplot(12,5,i+1,xticks=[],yticks=[])
ax.imshow(activations[0][i].detach().numpy())

中间层参数可视化

有两种方法

#方法一
for param in nn.parameters():
	xxx
#方法二
weight = model.state_dict()['features.0.weight']

且听风吟