基礎的資料生成模組已經有了,那接下來今天會介紹基礎的模型定義方式。
在設計模型之前,要先體認到最根本的問題:我們要執行怎麼樣的任務?
根據我們前幾天資料的介紹,可以得知ChestMNIST是一項具有下列特徵的資料集:
根據以上,可以進行一個基礎的設計來達成這個目標:
一個image in,然後會output出14個對應症狀機率值的模型。
這邊採用最常見的摺積神經網路(Convolutional Neural Network, CNN/ConvNet)作為開發,骨幹(backbone)的部份則是選取近年來很紅很高效的Efficient Net,是一個Google爸爸花了很多錢燒出來的框架,要好好的站在巨人的肩膀上。
現在很多地方都有 Efficient Net的實作可以直接採用,也包括MONAI。
這邊我們修改最小的B0來做使用。不過首先會發現一件問題是B0的設計是設計給ImageNet使用的,因此輸入的維度是常見的224x224:
>>> import monai
>>> monai.networks.nets.efficientnet.efficientnet_params['efficientnet-b0']
(1.0, 1.0, 224, 0.2, 0.2)
於是我們這裡稍微做一個小修改後再建立骨幹,以確保整個骨幹能夠正常運行:
monai.networks.nets.efficientnet.efficientnet_params['efficientnet-b0'] = (1.0, 1.0, 28, 0.2, 0.2)
model = monai.networks.nets.EfficientNetBN('efficientnet-b0',
spatial_dims = 2, # 表示2D 或 3D影像
in_channels = 1, # 採用灰階 所以設定1
num_classes = 14) # 14個不同的症狀
這邊介紹一個好用的工具TorchInfo,可以用來觀看模型的各階層:
>>> import torchinfo
>>> torchinfo.summary(model, input_size=(16,1,28,28))
====================================================================================================
Layer (type:depth-idx) Output Shape Param #
====================================================================================================
EfficientNetBN [16, 14] --
├─ConstantPad2d: 1-1 [16, 1, 29, 29] --
├─Conv2d: 1-2 [16, 32, 14, 14] 288
├─BatchNorm2d: 1-3 [16, 32, 14, 14] 64
├─MemoryEfficientSwish: 1-4 [16, 32, 14, 14] --
├─Sequential: 1-5 [16, 320, 1, 1] --
│ └─Sequential: 2-1 [16, 16, 14, 14] --
│ │ └─MBConvBlock: 3-1 [16, 16, 14, 14] 1,448
│ └─Sequential: 2-2 [16, 24, 7, 7] --
│ │ └─MBConvBlock: 3-2 [16, 24, 7, 7] 6,004
│ │ └─MBConvBlock: 3-3 [16, 24, 7, 7] 10,710
│ └─Sequential: 2-3 [16, 40, 4, 4] --
│ │ └─MBConvBlock: 3-4 [16, 40, 4, 4] 15,350
│ │ └─MBConvBlock: 3-5 [16, 40, 4, 4] 31,290
│ └─Sequential: 2-4 [16, 80, 2, 2] --
│ │ └─MBConvBlock: 3-6 [16, 80, 2, 2] 37,130
│ │ └─MBConvBlock: 3-7 [16, 80, 2, 2] 102,900
│ │ └─MBConvBlock: 3-8 [16, 80, 2, 2] 102,900
│ └─Sequential: 2-5 [16, 112, 2, 2] --
│ │ └─MBConvBlock: 3-9 [16, 112, 2, 2] 126,004
│ │ └─MBConvBlock: 3-10 [16, 112, 2, 2] 208,572
│ │ └─MBConvBlock: 3-11 [16, 112, 2, 2] 208,572
│ └─Sequential: 2-6 [16, 192, 1, 1] --
│ │ └─MBConvBlock: 3-12 [16, 192, 1, 1] 262,492
│ │ └─MBConvBlock: 3-13 [16, 192, 1, 1] 587,952
│ │ └─MBConvBlock: 3-14 [16, 192, 1, 1] 587,952
│ │ └─MBConvBlock: 3-15 [16, 192, 1, 1] 587,952
│ └─Sequential: 2-7 [16, 320, 1, 1] --
│ │ └─MBConvBlock: 3-16 [16, 320, 1, 1] 717,232
├─Identity: 1-6 [16, 320, 1, 1] --
├─Conv2d: 1-7 [16, 1280, 1, 1] 409,600
├─BatchNorm2d: 1-8 [16, 1280, 1, 1] 2,560
├─MemoryEfficientSwish: 1-9 [16, 1280, 1, 1] --
├─AdaptiveAvgPool2d: 1-10 [16, 1280, 1, 1] --
├─Dropout: 1-11 [16, 1280] --
├─Linear: 1-12 [16, 14] 17,934
====================================================================================================
Total params: 4,024,906
Trainable params: 4,024,906
Non-trainable params: 0
Total mult-adds (M): 128.06
====================================================================================================
Input size (MB): 0.05
Forward/backward pass size (MB): 31.48
Params size (MB): 16.10
Estimated Total Size (MB): 47.63
====================================================================================================
最後以Batch Size = 16來實際測試 Forward Propogation:
>>> device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
>>> model = model.to(device)
>>> test_input = torch.rand([16,1,28,28]).to(device)
>>> test_output = model(test_input)
>>> print(test_output.shape)
torch.Size([16, 14])
一切大功告成!