这些内容某些部分直接复制了其他博主的代码,一般我会留下转载的链接,初心只是为了自己能够方便的查找项目过程中的感悟。如果侵权了请直接联系我,会及时删除。
1.torch的register_hook()register_forward_hook()register_backawrd_hook()
参考链接:@Foneone
- 首先hook分为3种:
- register_hook(hook):为Tensor注册一个backward hook,用来获取变量的梯度;hook必须遵循如下的格式:
hook(grad) -> Tensor or None
- register_forward_hook(hook):对应于前向传播的梯度获取
- register_backward_hook(hook):对应与反向传播。
先看register_hook():
import torch from torch.autograd import Variable def print_grad(grad): print('grad is \n', grad) x = Variable(torch.randn(2, 1), requires_grad = True) ## x = torch.rand(2, 1, requires_grad = True)#等效 print('x value is \n', x) y = x + 3 print('y value is '\n'', y) z = torch.mean(torch.pow(y, 1 / 2)) lr = 1e-3 y.register_hook(print_grad) z.backward() x.data -= lr * x.grad.data print('new x is \n', x)
output: x value is tensor([[ 2.5474], [-1.1597]], requires_grad=True) y value is tensor([[5.5474], [1.8403]], grad_fn=<AddBackward0>) grad is tensor([[0.1061], [0.1843]]) new x is tensor([[ 2.5473], [-1.1599]], requires_grad=True)
解释:对于z来说,求梯度最终求解的是对x的梯度(导数,偏导),因此y是一个中间变量。因此可以用register_hook()来获取其作为中间值的导数,否则z对于y的偏导是获取不到的。
因此尝试不使用register_hook()获取梯度:
#y.register_hook(print_grad) z.backward() # 梯度求解 print('y\'s grad is ',y.grad) print('x\'s grad is \n',x.grad) x.data -= lr*x.grad.data print('new x is\n',x) output: y's grad is None x's grad is tensor([[0.1544], [0.1099]]) new x is tensor([[-0.3801], [ 2.1755]], requires_grad=True)
然后看register_forward_hook():
条件:执行过模型内置的forward.
格式:如下
hook(module, input, output) -> None or modified output
hook可以修改input和output,但是不会影响forward的结果。最常用的场景是需要提取模型的某一层(不是最后一层)的输出特征,但又不希望修改其原有的模型定义文件,这时就可以利用forward_hook函数。
import torch import torch.nn as nn import torch.nn.functional as F class LeNet(nn.Module): def __init__(self): super(LeNet, self).__init__() self.conv1 = nn.Conv2d(3, 6, 5) self.conv2 = nn.Conv2d(6, 16, 5) self.fc1 = nn.Linear(16*5*5, 120) self.fc2 = nn.Linear(120, 84) self.fc3 = nn.Linear(84, 10) def forward(self, x): out = F.relu(self.conv1(x)) #1 out = F.max_pool2d(out, 2) #2 out = F.relu(self.conv2(out)) #3 out = F.max_pool2d(out, 2) out = out.view(out.size(0), -1) out = F.relu(self.fc1(out)) out = F.relu(self.fc2(out)) out = self.fc3(out) return out features = [] def hook(module, input, output): # module: model.conv2 # input :in forward function [#2] # output:is [#3 self.conv2(out)] features.append(output.clone().detach()) # output is saved in a list net = LeNet() ## 模型实例化 x = torch.randn(2, 3, 32, 32) ## input handle = net.conv2.register_forward_hook(hook) ## 获取整个Lenet模型 conv2的中间结果 y = net(x) ## 获取的是 关于 input x 的 conv2 结果 print(features[0].size()) # 即 [#3 self.conv2(out)] handle.remove() ## hook删除
注意:使用完成后,最好用handle.remove()
再看register_backward_hook(hook)
使用情况是在反响传播之后。
2.torch.utils.data.Dataset
- 首先请看官网的解释:
- 细节讲解
链接:dataset
3.torch的tensor与numpy的转换
- tensor to numpy
a = torch.ones(1,2) print(a.numpy()) #输出 #[1 , 1]
- numpy to tensor
import numpy as np a = np.ones(1,2) p = torch.from_numpy(a) #输出 #tensor
4. 关于提取cnn冻结层的特征时tensor和array转换的理解
源代码如下(《oytorch与深度学习》p90)
def preconvfeat(dataset,model):#计算预卷积 conv_features = [] labels_list = [] for data in dataset:#一批一批的计算 inputs,labels = data if is_cuda: inputs , labels = inputs.cuda(),labels.cuda() inputs , labels = Variable(inputs),Variable(labels) output = model(inputs) conv_features.extend(output.detach().cpu().numpy())#将conv_features转换成numpy labels_list.extend(labels.data.cpu().numpy()) conv_features = np.concatenate([[feat] for feat in conv_features])#numpy的list整合命令 return (conv_features,labels_list)
- 首先介绍list的extend函数:
链接:link
在列表追加另一个序列的所有元素。(注意是逐个添加)
注意:extend只适用于一维数组,如果有一个二维的array它会把每一行当成一个元素extend进list。
>>> x1 array([[1, 2], [2, 2]]) >>> x2 array([[1, 2], [2, 2]]) >>> t = [] >>> t.extend(x1) >>> t [array([1, 2]), array([2, 2])]
- 然后来介绍numpy的concatenate()函数
链接:官网
在理解上面的代码中
conv_features = np.concatenate([[feat] for feat in conv_features])
这句话的时候,我完全不明白为什么要给每一个[feat]组成的序列之外再添加一个“[ ]”?
最后我看官方的书写格式: numpy.concatenate((a, b)),我发现这是将最外层的[]当作了(a,b)中的(),测试了后发现将()换成[]是不报错的。
>>> a array([[[1., 1., 1., 1.], [1., 1., 1., 1.], [1., 1., 1., 1.], [1., 1., 1., 1.]], [[1., 1., 1., 1.], [1., 1., 1., 1.], [1., 1., 1., 1.], [1., 1., 1., 1.]], [[1., 1., 1., 1.], [1., 1., 1., 1.], [1., 1., 1., 1.], [1., 1., 1., 1.]]]) >>> b array([[[1., 1., 1., 1.], [1., 1., 1., 1.], [1., 1., 1., 1.], [1., 1., 1., 1.]], [[1., 1., 1., 1.], [1., 1., 1., 1.], [1., 1., 1., 1.], [1., 1., 1., 1.]], [[1., 1., 1., 1.], [1., 1., 1., 1.], [1., 1., 1., 1.], [1., 1., 1., 1.]]]) >>> numpy.concatenate([a,b]) array([[[1., 1., 1., 1.], [1., 1., 1., 1.], [1., 1., 1., 1.], [1., 1., 1., 1.]], [[1., 1., 1., 1.], [1., 1., 1., 1.], [1., 1., 1., 1.], [1., 1., 1., 1.]], [[1., 1., 1., 1.], [1., 1., 1., 1.], [1., 1., 1., 1.], [1., 1., 1., 1.]], [[1., 1., 1., 1.], [1., 1., 1., 1.], [1., 1., 1., 1.], [1., 1., 1., 1.]], [[1., 1., 1., 1.], [1., 1., 1., 1.], [1., 1., 1., 1.], [1., 1., 1., 1.]], [[1., 1., 1., 1.], [1., 1., 1., 1.], [1., 1., 1., 1.], [1., 1., 1., 1.]]])
所以代码中的那句话是把所有的图像顺序存储在array中,将list转换array的numpy格式,这样就懂了
5.pytorch中tensor,list相互转换
6.pytorch中的momentum动量的讲解
链接:momentum的讲解
7.辨析:requre_grad,detach(),torch.nograd()
8.怎么更改vgg中的输出层的数目
#微调模型输出 num_input = vgg.classifier[6].in_features feature_model = list(vgg.classifier.children()) feature_model.pop() feature_model.append(nn.Linear(num_input, 10)) vgg.classifier = nn.Sequential(*feature_model)
要去除最后一层,然后append一个新的一层就好了。