前言
這一篇文章主要簡單的瞭解下在Pytorch框架下,卷積神經網絡的一些設置和應用,如有遺漏或錯誤,歡迎指出,不甚感激,互相學習!
簡單的兩層卷積網絡
<code>class
ConvNet(nn.Module): def __init__(self
, num_classes=10
):super
(ConvNet,self
).__init__() # 這個super
我一直沒能理解self
.layer1 = nn.Sequential( nn.Conv2d(1
,16
, kernel_size=5
, stride=1
, padding=2
), nn.BatchNorm2d(16
), nn.ReLU(), nn.MaxPool2d(kernel_size=2
, stride=2
))self
.layer2 = nn.Sequential( nn.Conv2d(16
,32
, kernel_size=5
, stride=1
, padding=2
), nn.BatchNorm2d(32
), nn.ReLU(), nn.MaxPool2d(kernel_size=2
, stride=2
))self
.fc = nn.Linear(7
*7
*32
, num_classes) def forward(self
, x):out
=self
.layer1(x)out
=self
.layer2(out
)out
=out
.reshape(out
.size(0
),-1
)out
=self
.fc(out
)return
out
model = ConvNet(num_classes).to(device)/<code>
關於卷積運算和對應的可視化,有很多網站做的都很好,可以便於理解和學習卷積操作,有想要分享的可以私信我,交換學習資源,共同進步。
多卡同步 BN(Batch normalization)
當使用 torch.nn.DataParallel 將代碼運行在多張 GPU 卡上時,PyTorch 的 BN 層默認操作是各卡上數據獨立地計算均值和標準差,同步 BN 使用所有卡上的數據一起計算 BN 層的均值和標準差,緩解了當批量大小(batch size)比較小時對均值和標準差估計不準的情況,是在目標檢測等任務中一個有效的提升性能的技巧。
<code>sync_bn = torch.nn.SyncBatchNorm(num_features, eps=1e-05
, momentum=0.1
, affine=True
, track_running_stats=True
)/<code>
但是網絡中可能不止一個BN層,一個一個的修改太繁瑣了。還好,pytorch框架考慮到了這個問題,可以通過下面的代碼,將已有網絡的所有BN層改為同步BN層。
<code>def
convertBNtoSyncBN
(
module
, process_group=None): '''Recursively replace all BN layers to SyncBN layer. Args:module
[torch.nn.Module]. Network '''if
isinstance
(
module
, torch.nn.modules.batchnorm._BatchNorm): sync_bn = torch.nn.SyncBatchNorm(module
.num_features,module
.eps,module
.momentum,module
.affine,module
.track_running_stats, process_group) sync_bn.running_mean =module
.running_mean sync_bn.running_var =module
.running_varif
module
.affine: sync_bn.weight =module
.weight.clone().detach() sync_bn.bias =module
.bias.clone().detach()return
sync_bnelse
:for
name, child_module inmodule
.named_children(): setattr(module
, name) = convert_syncbn_model(child_module, process_group=process_group))return
module
/<code>
雙線性匯合(bilinear pooling)
<code>X = torch.reshape(N, D, H * W) X = torch.bmm(X, torch.transpose(X, 1, 2)) / (H * W) assert X.size() == (N, D, D) X = torch.reshape(X, (N, D * D)) X = torch.sign(X) * torch.sqrt(torch.abs(X) + 1e-5) X = torch.nn.functional.normalize(X) /<code>
計算模型整體參數量
<code>num_parameters
= sum(torch.numel(parameter) for parameter in model.parameters())/<code>
查看網絡中的參數
如何查看神經網絡的參數也很重要,在Pytorch中可以通過model.state_dict()函數或者model.named_parameters()函數查看現在的全部可訓練參數(包括通過繼承得到的父類中的參數)。
<code>params =list
(model.named_parameters()) (name, param) = params[28
]'-------------------------------------------------'
) (name2, param2) = params[29
]'----------------------------------------------------'
) (name1, param1) = params[30
]
模型權重初始化
注意 model.modules() 和 model.children() 的區別:
model.modules() 會迭代地遍歷模型的所有子層,而 model.children() 只會遍歷模型下的一層。
<code># Common practisefor
initialization.for
layerin
model.modules():if
isinstance(layer, torch.nn.Conv2d): torch.nn.init
.kaiming_normal_(layer.weight, mode='fan_out'
, nonlinearity='relu'
)if
layer.biasis
not None: torch.nn.init
.constant_(layer.bias,val
=0.0
) elif isinstance(layer, torch.nn.BatchNorm2d): torch.nn.init
.constant_(layer.weight,val
=1.0
) torch.nn.init
.constant_(layer.bias,val
=0.0
) elif isinstance(layer, torch.nn.Linear): torch.nn.init
.xavier_normal_(layer.weight)if
layer.biasis
not None: torch.nn.init
.constant_(layer.bias,val
=0.0
) # Initialization with given tensor. layer.weight = torch.nn.Parameter(tensor)/<code>
如何提取模型中的某一層
modules()會返回模型中所有模塊的迭代器,它能夠訪問到最內層,比如self.layer1.conv1這個模塊,還有一個與它們相對應的是name_children()屬性以及named_modules(),這兩個不僅會返回模塊的迭代器,還會返回網絡層的名字。
<code>new_model
=nn.Sequential(*list(model.children())[:2]
for
layer in model.named_modules():
if
isinstance(layer[1],nn.Conv2d):
conv_model.add_module(layer[0],layer[1])
/<code>
部分層使用預訓練模型
<code>model.load_state_dict(torch.load('model.pth'
),strict
=False
)/<code>
將在 GPU 保存的模型加載到 CPU
有些時候,訓練神經網絡時沒有GPU,但是有的人並沒有GPU訓練網絡,而Github上下載的模型大多數都是GPU訓練的,因此將GPU 保存的模型加載到 CPU也很重要。
<code>model
.load_state_dict
(torch.load('model.pth'
, map_location='cpu'
))/<code>
導入另一個模型的相同部分到新的模型
模型導入參數時,如果兩個模型結構不一致,則直接導入參數會報錯。用下面方法可以把另一個模型的相同的部分導入到新的模型中。
<code> model_new_dict = model_new.state_dict() model_common_dict = {k:v for k, v in model_saved.items() if k in model_new_dict.keys()} model_new_dict.update(model_common_dict) model_new.load_state_dict(model_new_dict)/<code>
未完待續...