PyTorch提取中間層特徵?
在PyTorch裡面提取中間層特徵不是很容易,有人在PyTorch論壇上提了這個問題。官方給出了解答(見圖):
但是我感覺這種做法有問題,舉個例子,如果我這麼定義網路:
那麼nn.Sequential()後的結果豈不是不含Max Pooling還有ReLU?
所以是不是慎用nn.functional?
謝邀,之前剛好看過這個,所以這次就來回答了。
之前跟體主有一樣的疑惑,為什麼同樣的操作同時出現在了nn.modules和nn.functional兩個模塊中,然後,我翻了下源碼。。。發現,對於運算來說,pytorch不在計算時區分操作(不論你是在init還是在forward中定義,使用modules還是functional)
目錄:
- 太長不看版本
- 詳細版本
- 回答題主的問題
太長不看版本:
- modules中會保存該操作具體的參數(所有相關參數),因為是使用類實現的,所以所有的狀態會被保留。
- 而functional中僅僅是一個函數,不會保存相關的參數和狀態,用完所有的狀態會被丟棄。
- modules本質是對functional的封裝,會在forward方法中直接調用functional的相關函數。
詳細版本:
以max_pool2d為例。
提前聲明,我的pytorch版本是0.2.0,目前github上master分支的的代碼已經變更過,不過大致意思相同,最新的版本是functional直接使用torch._C運行庫進行計算。
PyTorch Modules.MaxPool2d
# 我忽略了注釋和文檔
from .module import Module
from .. import functional as F
class MaxPool2d(Module):
def __init__(self, kernel_size, stride=None, padding=0, dilation=1,
return_indices=False, ceil_mode=False):
super(MaxPool2d, self).__init__()
self.kernel_size = kernel_size
self.stride = stride or kernel_size
self.padding = padding
self.dilation = dilation
self.return_indices = return_indices
self.ceil_mode = ceil_mode
def forward(self, input):
return F.max_pool2d(input, self.kernel_size, self.stride,
self.padding, self.dilation, self.ceil_mode,
self.return_indices)
def __repr__(self):
kh, kw = _pair(self.kernel_size)
dh, dw = _pair(self.stride)
padh, padw = _pair(self.padding)
dilh, dilw = _pair(self.dilation)
padding_str = ", padding=(" + str(padh) + ", " + str(padw) + ")"
if padh != 0 and padw != 0 else ""
dilation_str = (", dilation=(" + str(dilh) + ", " + str(dilw) + ")"
if dilh != 0 and dilw != 0 else "")
return self.__class__.__name__ + " ("
+ "size=(" + str(kh) + ", " + str(kw) + ")"
+ ", stride=(" + str(dh) + ", " + str(dw) + ")"
+ padding_str + dilation_str + ")"
而在functional中 PyTorch functional.maxpool2d
def max_pool2d(input, kernel_size, stride=None, padding=0, dilation=1,
ceil_mode=False, return_indices=False):
ret = _functions.thnn.MaxPool2d.apply(input, kernel_size, stride, padding, dilation,
ceil_mode)
return ret if return_indices else ret[0]
conv的代碼跟這個大同小異,結構上差不多。
恩,可以看到在modules中,maxpool2d被註冊為了一個類,意味著相關的參數被保留了下來了,雖然這在maxpool中並沒有什麼x用,但是如果是conv就不一樣了,我們可以看到,MaxPool2d這個類在forward方法中也是在調用functional的max_pool2d函數,而max_pool2d函數則是接調用了類torch.nn._functions.thnn.MaxPool2d的apply方法,但是MaxPool2d這個類是沒有重寫apply方法的,說明是這個方法來自父類torch.autograd.Function
class Function(with_metaclass(FunctionMeta, _C._FunctionBase, _ContextMethodMixin, _HookMixin)):
,然而這個類中同樣的沒有定義,然後看到它的父類中類_C._FunctionBase有apply方法,那麼就可以確定完整的運行流程了,這個文件在我的機器上是"_C.cpython-35m-x86_64-linux-gnu.so"該文件是release時編譯的,所以github上是沒有的,在根目錄下進入python交互模式,可以直接通過
import _C
的方式進行引用,這本質上是一個Cython擴展,如下圖所示,apply確實是在_C擴展中
恩,到這裡我們解決了modules和functional的關係,也明確了這些操作是怎麼被計算的,對了,對於目前github上的master分支而言,這個計算模塊實在_C._nn中的。
PyTorch如何提取中間層
這個問題很簡單,你在forward裡面新建一個變數,把你想要的中間層賦值給這個變數然後保存下來就可以了,唯一的問題就是如果你用了cuda的話貌似需要先.cpu()然後再在之後的操作.cuda(),我之前有時候出現過這個問題,但有時候沒有問題,待考證。
以上
該回答將再修改後發表至我的專欄(上一個回答還沒修改完23333)
提取中間特徵是指把中間的weights給提出來嗎?這樣不是直接訪問那個矩陣不就好了嗎? pytorch在存參數的時候, 其實就是給所有的weights bias之類的起個名字然後存在了一個字典裡面. 不然你看看state_dict.keys(), 找到相對應的key拿出來就好了.
然後你說的慎用也是一個很奇怪的問題啊..
就算用modules下面的class, 你存模型的時候因為你的activation function上面本身沒有參數, 所以也不會被存進去. 不然你可以試試在Sequential裡面把relu換成sigmoid, 你還是可以把之前存的state_dict給load回去.
不能說是慎用functional吧, 我覺得其他的設置是應該分開也存一份的(假設你把這些當做超參的話)
利益相關: 給pytorch提過PR
中間層特徵的話就在forward上保存變數比較方便。要提梯度的話就得hook了。仔細看看nn.sequential類就會發現他的init函數里是add_module().你用nn.functional肯定就加不進去了啊。net.children()不會有pooling和relu。
貌似確實是個問題。兩點:
(1)一般我都採用註冊好class。load預訓練好的模型(state_dict方式保存),首先自己定義好自己的forward,並且保證自己的forward和訓練用的模型一致。
(2)提取中間特徵採用register hook, 舉個例子:
step1 : define hooks
def hook_net(net):
feature_maps = []
hooks = []
def extractF_hook(module, inputs, outputs):
if outputs.__class__.__name__ == "list": # a list of outputs
fmaps = [output.data.cpu() for output in outputs]
feature_maps.append(fmaps)
else: # output is not a list
f_map = outputs.data.cpu()
feature_maps.append(f_map)
keys_list_0 = net._modules.keys()
for k0 in keys_list_0:
m = net._modules.get(k0)
if type(m) == torch.nn.modules.container.ModuleList:
keys_list_1 = m._modules.keys()
for k1 in keys_list_1:
mm = m._modules.get(k1)
if k0 == "modules_list" and k1 == "3": # take specific layer output
hook = mm.register_forward_hook(extractF_hook)
hooks.append(extractF_hook)
# return layers, feature_maps, hooks
return feature_maps, hooks
forward data and get features stored in a list
dataset_features, hooks = hook_net(model)
for i, (inputs, labels) in enumerate(data_loader):
# forward data
model(inputs)
推薦閱讀:
※如何評價 2017 年 12 月 5 日發布的 PyTorch 0.3.0?
※如何評價MXNet發布的1.0版本?
※PyTorch到底好用在哪裡?
※2017年1月18日Facebook發行的PyTorch相比TensorFlow、MXNet有何優勢?
※如何理解一維卷積神經網路的輸入?
TAG:計算機視覺 | 深度學習DeepLearning | PyTorch |