本文把文章標題取名成PyTorch深度學習工具箱的用意是在於:

日常在進行PyTorch模型訓練的時候,如果想知道模型的好壞,我們會用不少方式去評估。例如:train/val loss、train/val accuracy、Confusion Matrix、per class accuracy、F1 Score、t-SNE視覺化、GradCAM、觀看模型分類時,判斷錯誤的圖片,及其原始圖片路徑。

然而,目前大多數research公開的程式碼當中,幾乎找不太到有一個將前述評估方法整合在一起的程式。也因此,如果我們想要得到這些資訊,就必須到Google搜尋,找別人是怎麼完成這些功能的,往往花上不少時間。

在本文當中,我將彙整過去寫過關於這些主題的文章及程式碼,製作成一個目錄,供讀者及自己查詢使用:

1.使用TensorBoard視覺化train/val loss、train/val accuracy
https://pytorch.org/docs/stable/tensorboard.html

2. Confusion Matrix和per class accuracy
如何根據PyTorch的Model預測的output繪製出混淆矩陣(Confusion Matrix)並取得每個class的accuracy?

3. F1 Score(imblanced dataset可用macro和weighted)
sklearn官方F1 Score文檔

4. t-SNE視覺化
如何使用PyTorch的Feature Extractor輸出進行t-SNE視覺化?

5. GradCAM
如何在PyTorch上使用GradCAM進行神經網路分類依據視覺化?

6. 觀看模型預測時,判斷錯誤的圖片
這個功能將預測錯誤的圖片保存到TensorBoard的log當中,再從中使用Tensorflow==1.13.1版本還原出來到資料夾中,有兩篇文章需要參考。

如何顯示PyTorch中預測錯誤的圖片?
如何將保存到TensorBoard的圖片擷取出來?

7.檢查模型的參數量及模型檔案大小
PyTorch如何檢查模型的參數量及模型檔案大小?

8.列出分類錯誤之原始圖片路徑?
PyTorch如何列出分類錯誤之原始圖片路徑?

9.讓訓練結果具有再現性(Reproducibility)
如何確保PyTorch訓練的結果具有再現性?

10.將模型的多個Label輸出改成只有2種輸出
如何修改PyTorch的Prediction結果?


以下內容來自iacob的回答,本文僅作筆記摘錄:

除了此方式之外,這個問題裡面的很多回答都相當的好,值得學習。

Random sample from DataLoader

Assuming DataLoader(shuffle=True) was used in its construction, a single random example can be drawn from the DataLoader with:

example = next(iter(dataloader))[0]

Random sample from Dataset

If that is not the case, you can draw a single random example from the Dataset with:

idx = torch.randint(len(dataset), (1,))
example = dataset[idx]


A PyTorch Extension: Tools for easy mixed precision and distributed training in Pytorch

使用官方提供的安裝指令在aiForge上會遇到PyTorch及CUDA版本無法對應的問題,致使安裝錯誤。以下提供能成功安裝apex的指令

# 官方的安裝指令
$ git clone https://github.com/NVIDIA/apex
$ cd apex
$ pip install -v --disable-pip-version-check --no-cache-dir --global-option="--cpp_ext" --global-option="--cuda_ext" ./
# 能成功安裝的指令
$ git clone https://github.com/NVIDIA/apex
$ cd apex
$ python setup.py install


論文

[1703.06868] Arbitrary Style Transfer in Real-time with Adaptive Instance Normalization (arxiv.org)

* Given a content input and a style input, AdaIN simply adjusts the mean and variance of the content input to match those of the style input.- This approach can transfer arbitrary new styles in real-time.
# 訓練
git clone https://github.com/irasin/Pytorch_AdaIN
cd Pytorch_AdaIN
# 將風格圖片(style)放入到style資料夾、想進行風格轉換的圖片(content)放入到content資料夾,並透過以下指令即可進行訓練。python train.py

如果圖片數量很多的話,可以重寫dataset.py的程式碼,使其可支援自定義的DataLoader(例如從json格式的檔案讀取圖片路徑資料,只不過train.py也要跟著修改部分的程式),修改過的dataset.py如下:

train.py主要修改的是將自己的json檔案透過pandas讀取後,與PreprocessDataset搭配在一起,傳給PyTorch的DataLoader進行讀取即可

預設是訓練20個epoch,我自己使用某個PCB瑕疵檢測的資料集(100多萬張圖片),大約13小時的時間可完成訓練。在Testing上確實能將目標的瑕疵風格(style)轉移到content上,從肉眼觀察很像是將Style image的對比度transfer到content image上。在視覺上,並不會影響到原有瑕疵的狀態。

# 測試(在content和style各放入1個圖片路徑)(也可使用for迴圈,將多張圖片一次轉換成特定的風格)
python test.py -c content_image_path -s style_image_path

這邊提供我自己修改過可以使用多張圖片一次完成風格遷移的程式碼


假設今天我們的模型在一個擁有6種不同Label的資料集上進行訓練,模型用來預測的時候,會輸出0, 1, 2, 3, 4, 5這6種不同的數字。

然而,如果我們改變Testing方式,打算將第1, 2, 3, 4 ,5這5個數字,都視為同一個Label,也就是只剩下兩種Label的情況(0和1)

我們可以怎麼做呢?
在PyTorch程式碼進行Forward的過程中,會有類似以下的寫法。我們的方法就是直接將輸出大於1的結果都轉換成1,這樣就能得到只有2種Label的輸出結果了。

output = model(images)             #模型輸出得到Output
_, preds = torch.max(output, 1) #轉換成實際的label(preds)
# 將preds搭配lambda function,只要preds中的預測結果有大於1的數值,則全轉換成1
preds = torch.tensor([(lambda i: 1 if i > 1 else i)(i) for i in preds]).cuda()
# 如此一來,我們的模型就能以2個label的方式進行輸出

有時在訓練模型的時候,經過多次訓練下來的結果都不太一樣,本文將提供參數設定,使訓練結果具有再現性(Reproducibility)

1. 訓練模型前預先設定seed

import torch
import numpy as np
seed = 3
random.seed(seed)
np.random.seed(seed)
torch.manual_seed(seed)
torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False

2. DataLoader

def seed_worker(worker_id):
worker_seed = torch.initial_seed() % 2**32
numpy.random.seed(worker_seed)
random.seed(worker_seed)

g = torch.Generator()
g.manual_seed(0)

DataLoader(
train_dataset,
batch_size=batch_size,
num_workers=num_workers,
worker_init_fn=seed_worker
generator=g,
)

參考資料

Reproducibility — PyTorch 1.9.0 documentation


說明

假設今天有個資料夾名稱叫作all_images,裡面包含了100萬張圖片,如果在Server上使用JupyterLab的過程中不小心點到,有很大的機率畫面會停住不動,這時候應該重啟Server就能解決這個問題。但是在aiForge上,卻需要release掉再create forge,一不小心點到卻要整個重新create forge,導致許多安裝好的套件及環境設定遺失。

我們有沒有什麼可能的解決方案呢?過去近一年的時間,這件事情不斷的困擾著我,今天下午又再次發生同樣的事情。我突然想到,其實只要把該資料夾隱藏後,JupyterLab內建的檔案總管就看不到這個資料了,因此能夠避免當機問題。

作法

mv all_images .all_images

相當簡單,只要把資料夾名稱前面加上一個「.」符號,就能讓資料夾隱藏,而這個問題也就被解決了。


參考資料

前提

本文假設batch size使用1的情況下,將分類錯誤的原始圖片路徑、實際標籤、預測標籤寫入到missclassified_file_path.txt檔案當中。

因為每張圖片都必須要有路徑,PyTorch的DataLoader才能順利載入圖片,也因此,如果希望獲得圖片的路徑,修改DataLoader讓它能夠return圖片原始路徑即可。如程式碼的第10至14行

關鍵在程式碼的第51~58行

if args.batch_size==1 and pred_index.cpu() != target.cpu():
with open("missclassified_file_path.txt", "a") as f:
true_label = str(target.cpu().tolist()[0])
pred_label = str(pred_index.cpu().tolist()[0])
write_content = ' '.join(path)
write_content = write_content + ' ' + true_label + ' ' + pred_label
f.write(write_content+"\n")

程式碼


1. torchinfo

安裝

pip install torchinfo

使用

from torchinfo import summary

model = ConvNet()
batch_size = 16
summary(model, input_size=(batch_size, 1, 28, 28))

輸出

====================================================================
Layer Input Shape Output Shape Param # Mult-Adds
========================================================= …


import torchvision.models as models
import torch.nn as nn
resnet18 = models.resnet18(pretrained=True)
#方法1和方法2都取出了ResNet18的前8層結構,只是slicing的方法不同而已,且前8層剛好位在classifier之前。要將第8層output的特徵拿來做t-SNE或是GradCAM輸出都可以features = nn.Sequential(*(list(resnet18.children())[0:8])) #方法1
features = nn.Sequential(*(list(resnet18.children())[:-2])) #方法2

可使用的 Pre-trained model

# alexnet = models.alexnet(pretrained=False)
# squeezenet = models.squeezenet1_0(pretrained=False)
# vgg16 = models.vgg16(pretrained=False)
# densenet = models.densenet161(pretrained=False)
# inception = models.inception_v3(pretrained=False)
# googlenet = models.googlenet(pretrained=False)
# shufflenet = models.shufflenet_v2_x1_0(pretrained=False)
# mobilenet_v2 = models.mobilenet_v2(pretrained=False)
# mobilenet_v3_large = models.mobilenet_v3_large(pretrained=False)
# mobilenet_v3_small = models.mobilenet_v3_small(pretrained=False)
# resnext50_32x4d = models.resnext50_32x4d(pretrained=False)
# wide_resnet50_2 = models.wide_resnet50_2(pretrained=False)
# mnasnet = models.mnasnet1_0(pretrained=False)

Yanwei Liu

Machine Learning | Deep Learning | https://linktr.ee/yanwei

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store