- 2021.12.8的记录
- 2021.12.9的记录
代码文件路径:E:sotsugyosextsukeiVGG16featuresvision.py
书本 P167-176 2021.12.8的记录
先看一下效果,我的可莉。
:
def hook(model, input, output):
activation[name] = output.detach()
return hook
vgg16.eval()
vgg16.features[4].register_forward_hook(get_activation("maxpool1"))
_ = vgg16(input_im)
maxpool1 = activation["maxpool1"]
print("获得特征的尺寸为:", maxpool1.shape)
plt.figure(figsize=(11, 6))
for ii in range(maxpool1.shape[1]):
plt.subplot(6, 11, ii+1)
plt.imshow(maxpool1.data.numpy()[0, ii, :, :], cmap="gray")
plt.axis("off")
plt.subplots_adjust(wspace=0.1, hspace=0.1)
plt.show()
首先定义了一个字典。关于字典:字典是一种可变容器模型,且可存储任意类型对象。
然后get_activation(name)函数中的hook函数了。
首先了解一下什么是回调函数:添加链接描述
钩子技术代码:修改自添加链接描述
import time
class LazyPerson(object):
def __init__(self, name):
self.name = name
self.watch_tv_func = None
self.have_dinner_func = None
def get_up(self):
print("%s get up at:%s" % (self.name, time.time()))
def go_to_sleep(self):
print("%s go to sleep at:%s" % (self.name, time.time()))
def register_tv_hook(self, watch_tv_func):
self.watch_tv_func = watch_tv_func
def register_dinner_hook(self, have_dinner_func):
self.have_dinner_func = have_dinner_func
def enjoy_a_lazy_day(self):
# get up
self.get_up()
time.sleep(3)
# watch tv
# check the watch_tv_func(hooked or unhooked)
# hooked
if self.watch_tv_func is not None:
self.watch_tv_func(self.name)
# unhooked
else:
print("no tv to watch")
time.sleep(3)
# have dinner
# check the have_dinner_func(hooked or unhooked)
# hooked
if self.have_dinner_func is not None:
self.have_dinner_func(self.name)
# unhooked
else:
print("nothing to eat at dinner")
time.sleep(3)
self.go_to_sleep()
def watch_daydayup(name):
print("%s : The program ---day day up--- is funny!!!" % name)
def watch_happyfamily(name):
print("%s : The program ---happy family--- is boring!!!" % name)
def eat_meat(name):
print("%s : The meat is nice!!!" % name)
def eat_hamburger(name):
print("%s : The hamburger is not so bad!!!" % name)
if __name__ == "__main__":
lazy_tom = LazyPerson("Tom")
lazy_tom.register_tv_hook(watch_daydayup)
lazy_tom.register_dinner_hook(eat_meat)
lazy_tom.enjoy_a_lazy_day()
个人理解:钩子技术貌似就是函数调用函数。为什么叫钩子呢。我的理解是他的父级的输入并没有什么限制,可以挂载任何函数,你想用啥函数的时候就钩上去。但我总感觉还是不能用这个来说服自己,什么是钩子技术。希望有高人指点。
好了再看一下这个函数,参考链接:添加链接描述
.register_forward_hook(hook)
这里要输入的参数是一个固定格式的hook函数
hook(module, input, output) -> None or modified output
hook可以修改input和output,但是不会影响forward的结果。最常用的场景是需要提取模型的某一层(不是最后一层)的输出特征,但又不希望修改其原有的模型定义文件,这时就可以利用forward_hook函数。
其中hook的内容是可以自己写的,像上文程序中利用字典获取也行,像链接文章中用列表也行。
activation[name] = output.detach()
先看一下百度百科的解释吧:
神经网络的训练有时候可能希望保持一部分的网络参数不变,只对其中一部分的参数进行调整;或者值训练部分分支网络,并不让其梯度对主网络的梯度造成影响,torch.tensor.detach()和torch.tensor.detach_()函数来切断一些分支的反向传播。本文会具体介绍这两种方法的用法和区别。
也就是说这个.detach()去掉也是没有任何问题的,并不会对output有什么改变。但是在反向传播到这一层的时候会停止更新权重。由于这里也没有进行反向传播,去掉也是没有问题的。测试一下也的确如此。
下午去办身份证遇到一个老奶奶,说卡没了没吃午饭,问我要几块钱。我没理就走了。后来想想,估计是地铁卡丢了,要几块钱坐地铁回家吧。不像是骗子,唉,想想老奶奶当时的表情心里挺不是滋味。
解释一下.register_hook()函数,是对梯度进行各种操作,需要注意的是传入的参数必须是一个函数,然后传入的函数测试下来,只能有一个参数,不然会报错。如下代码
import torch
v = torch.randn((1, 3), dtype=torch.float32, requires_grad=True)
z = v.sum()
zi = {}
def a(m):
zi["grad"] = m
def b(m):
zi["g"] = m * 2
v.register_hook(a)
v.register_hook(b)
z.backward()
print(v.grad)
print(zi["grad"])
print(zi)
输出为
tensor([[1., 1., 1.]])
tensor([[1., 1., 1.]])
{'grad': tensor([[1., 1., 1.]]), 'g': tensor([[2., 2., 2.]])}
函数a就是将梯度直接传给字典中的“grad”,而b是将梯度乘2传给“g”。至于register_hook()是怎么传的,猜测也是和上文的name一样,把梯度作为类中的一个变量传给了a或b函数中的m。
最后的类激活热力图输出
真好看。但好不明显,换个大象的图吧。
网络更关注大象的牙齿和脸呢。
代码部分缺少理论知识的辅助,暂时无法知道所有在干嘛,只能理解部分。明天再看看吧。



