使用PyTorch進行遷移學習

介紹

我去年在一個計算機視覺項目中工作,我們必須建立一個健壯的人臉檢測模型。

考慮到我們擁有的數據集的大小,從頭構建一個模型是一個挑戰。從頭構建將是一個耗時又消耗計算資源的方案。由於時間緊迫,我們必須儘快找出解決辦法。

使用PyTorch進行遷移學習

這就是遷移學習拯救我們的時候。這是一個非常有用的工具,可以放在你的數據科學家庫中,特別是當你使用有限的時間和計算能力時。

因此,在本文中,我們將學習有關遷移學習的所有內容,以及如何在使用Python的實際項目中利用它。我們還將討論預訓練模型在這個領域的作用,以及它們將如何改變構建機器學習pipeline(管道)的方式。

本文是面向初學者的PyTorch系列文章的一部分。我堅信PyTorch是目前最好的深度學習框架之一,在不久的將來會越來越強大。這是一個很好的時間來學習它是如何工作的,並參與其中。

目錄

  1. 遷移學習概論
  2. 什麼是預訓練模型?如何選擇正確的預訓練模型?
  3. 案例研究:緊急與非緊急車輛分類
  4. 使用卷積神經網絡(CNNs)解決挑戰
  5. 使用PyTorch的遷移學習解決挑戰
  6. CNN的性能比較和遷移學習

1.遷移學習概論

讓我用一個例子來說明遷移學習的概念。想象一下,你想從一個你完全陌生的領域學習一個主題。

你會採取什麼不同的方法來理解這個主題?你可能會:

  • 網上搜尋資源
  • 閱讀文章和博客
  • 參考書籍
  • 尋找視頻教程,等等

所有這些都會幫助你熟悉這個主題。在這種情況下,你是唯一一個付出所有時間來熟悉主題的人。

但還有另一種方法,它可能在短時間內產生更好的結果。

你可以諮詢對你想要學習的主題具有能力的領域/主題專家。這個人會把他/她的知識傳授給你。從而加快你的學習過程。

使用PyTorch進行遷移學習

第一種方法,即你獨自投入所有的努力,是從頭開始學習的一個例子。第二種方法被稱為遷移學習。知識轉移發生在一個領域的專家到一個面對新領域的新手。

是的,遷移學習背後的思想就是這麼簡單!

神經網絡和卷積神經網絡就是從零開始學習的例子。這兩個網絡都從給定的一組圖像中提取特徵(對於與圖像相關的任務),然後根據這些提取的特徵將圖像分類到各自的類中。

這就是遷移學習和預訓練的模型非常有用的地方。讓我們在下一節中瞭解一下後一個概念。

2.什麼是預訓練模型?如何選擇正確的預訓練模型?

在你將要從事的任何深度學習項目中,預訓練的模型都是非常有用的。並非所有人都擁有頂級科技巨頭的無限計算能力,相反我們需要使用我們本地有限的機器,所以預訓練模型是一個強大的工具。

預訓練的模型通過將它們的權重和偏置傳遞給一個新模型來共享它們的學習。因此,當我們進行遷移學習時,我們首先選擇恰當的預訓練模型,然後將其已學習的權值和偏置傳遞給新模型。

有n種預訓練過的模型。我們需要決定哪種模式最適合我們的問題。現在,讓我們考慮一下我們有三個預訓練好的網絡——BERT、ULMFiT和VGG16。

使用PyTorch進行遷移學習

我們的任務是對圖像進行分類(正如我們在本系列的前幾篇文章中所做的那樣)。那麼,你會選擇哪些預訓練好的模型呢?讓我先給你一個快速的概述這些預訓練的網絡,這將幫助我們決定正確的預訓練的模型。

語言建模使用BERT和ULMFiT,圖像分類任務使用VGG16。如果你看一下手頭的問題,這是一個圖像分類問題。所以我們選擇VGG16是理所當然的。

現在,VGG16可以有不同的重量,即VGG16訓練在ImageNet或VGG16訓練在MNIST:

使用PyTorch進行遷移學習

ImageNet與MNIST

現在,為我們的問題確定正確的預訓練模型,我們應該研究這些ImageNet和MNIST數據集。ImageNet數據集由1000個類和總共120萬張圖像組成。這些數據中的一些類別是動物、汽車、商店、狗、食物、儀器等:

使用PyTorch進行遷移學習

另一方面,MNIST是訓練手寫數字的。它包括10類從0到9:

使用PyTorch進行遷移學習

我們將在一個項目中工作,我們需要將圖像分為緊急和非緊急車輛(我們將在下一節詳細討論)。這個數據集包括車輛的圖像,因此在ImageNet數據集上訓練的VGG16模型將更有用,因為它有車輛的圖像。

簡而言之,這就是我們應該如何根據我們的問題來決定正確的預訓練模型。

3.案例研究:緊急與非緊急車輛分類

我們將進行一個新的目標!這裡,我們的目標是將車輛分為緊急和非緊急。

現在讓我們開始理解這個問題並可視化一些示例。你可以通過這個鏈接下載圖片:https://drive.google.com/file/d/1EbVifjP0FQkyB1axb7KQ26yPtWmneApJ/view

首先,導入所需的庫:

接下來,我們將讀取包含圖像名稱和相應標籤的.csv文件:

使用PyTorch進行遷移學習

csv文件有兩列:

  1. image_names:它表示數據集中所有圖像的名稱
  2. emergencyorno:它指定特定的圖像屬於緊急類還是非緊急類。0表示圖像是非緊急車輛,1表示緊急車輛

接下來,我們將加載所有的圖像,並將它們存儲為數組格式:

使用PyTorch進行遷移學習

加載這些圖像大約需要12秒。在我們的數據集中有1646張圖像,由於VGG16需要所有這種特殊形狀的圖像,所以我們將它們的形狀全部重設為(224,224,3)。現在讓我們從數據集中可視化一些圖像:

使用PyTorch進行遷移學習

這是一輛警車,因此有緊急車輛的標籤。現在我們將目標存儲在一個單獨的變量:

讓我們創建一個驗證集來評估我們的模型:

使用PyTorch進行遷移學習

我們在訓練集中有1,481張圖像,在驗證集中有165張圖像。現在我們必須將數據集轉換為torch格式:

使用PyTorch進行遷移學習

類似地,我們將轉換驗證集:

使用PyTorch進行遷移學習

我們的數據準備好了!在下一節中,我們將構建一個卷積神經網絡(CNN),然後使用預訓練模型來解決這個問題。

4.使用卷積神經網絡(CNNs)解決挑戰

我們終於到了模型製作部分!在使用遷移學習來解決這個問題之前,我們先用一個CNN模型為自己設定一個benchmark。

我們將構建一個非常簡單的CNN架構,它有兩個卷積層來提取圖像的特徵,最後是一個全連接層來對這些特徵進行分類:

現在讓我們定義優化器,學習率和損失函數為我們的模型,並使用GPU訓練模型:

使用PyTorch進行遷移學習

這就是模型架構的樣子。最後,我們將對模型進行15個epoch的訓練。我將模型的batch_size設置為128(你可以嘗試一下):

使用PyTorch進行遷移學習

這也會打印一份訓練總結。訓練損失在每個時期之後都在減少,這是一個好跡象。我們來檢查一下訓練和驗證的準確性:

使用PyTorch進行遷移學習

我們的訓練正確率在82%左右,這是一個不錯的分數。下面檢查驗證的準確性:

# 驗證集預測
prediction_val = []
target_val = []
permutation = torch.randperm(val_x.size()[0])
for i in tqdm(range(0,val_x.size()[0], batch_size)):
indices = permutation[i:i+batch_size]
batch_x, batch_y = val_x[indices], val_y[indices]

if torch.cuda.is_available():
batch_x, batch_y = batch_x.cuda(), batch_y.cuda()

with torch.no_grad():
output = model(batch_x.cuda())

softmax = torch.exp(output).cpu()
prob = list(softmax.numpy())
predictions = np.argmax(prob, axis=1)
prediction_val.append(predictions)
target_val.append(batch_y)

# 驗證集精確度
accuracy_val = []
for i in range(len(prediction_val)):
accuracy_val.append(accuracy_score(target_val[i],prediction_val[i]))

print('validation accuracy: \\t', np.average(accuracy_val))
使用PyTorch進行遷移學習

驗證的準確性為76%。現在我們已經有了一個基準,是時候使用遷移學習來解決緊急和非緊急車輛分類問題了!

5.使用PyTorch的遷移學習解決挑戰

我在上面已經提到了這一點,我在這裡重申一下——我們將使用在ImageNet數據集上訓練的VGG16預訓練模型。讓我們看看我們將遵循的步驟,以訓練模型使用遷移學習:

  1. 首先,我們將加載預訓練模型的權重——在我們的例子中是VGG16
  2. 然後我們將根據手頭的問題對模型進行微調
  3. 接下來,我們將使用這些預訓練的權重並提取圖像的特徵
  4. 最後,我們將使用提取的特徵訓練精細調整的模型

那麼,讓我們從加載模型的權重開始:

# 加載預訓練模型
model = models.vgg16_bn(pretrained=True)

現在我們將對模型進行微調。我們不訓練VGG16模型的層,因此讓我們固定這些層的權重:

# 固定模型權重
for param in model.parameters():
param.requires_grad = False

由於我們只需要預測2個類,而VGG16是在ImageNet上訓練的,ImageNet有1000個類,我們需要根據我們的問題更新最後一層:

# 最後加一個分類器
model.classifier[6] = Sequential(
Linear(4096, 2))
for param in model.classifier[6].parameters():
param.requires_grad = True

因為我們只訓練最後一層,所以我將最後一層的requires_grad設置為True。我們將訓練設置為GPU:

# 檢查GPU是否可用
if torch.cuda.is_available():
model = model.cuda()

現在,我們將使用該模型併為訓練和驗證圖像提取特性。我將batchsize設置為128(同樣,你可以根據需要增加或減少batchsize):

# batch大小
batch_size = 128

# 從訓練集提取特徵
data_x = []
label_x = []

inputs,labels = train_x, train_y

for i in tqdm(range(int(train_x.shape[0]/batch_size)+1)):
input_data = inputs[i*batch_size:(i+1)*batch_size]
label_data = labels[i*batch_size:(i+1)*batch_size]
input_data , label_data = Variable(input_data.cuda()),Variable(label_data.cuda())
x = model.features(input_data)
data_x.extend(x.data.cpu().numpy())

label_x.extend(label_data.data.cpu().numpy())

類似地,讓我們提取驗證圖像的特徵:

# 從驗證集提取特徵
data_y = []
label_y = []

inputs,labels = val_x, val_y

for i in tqdm(range(int(val_x.shape[0]/batch_size)+1)):
input_data = inputs[i*batch_size:(i+1)*batch_size]
label_data = labels[i*batch_size:(i+1)*batch_size]
input_data , label_data = Variable(input_data.cuda()),Variable(label_data.cuda())
x = model.features(input_data)
data_y.extend(x.data.cpu().numpy())
label_y.extend(label_data.data.cpu().numpy())

接下來,我們將這些數據轉換成torch格式:

# 轉換這些數據到torch格式
x_train = torch.from_numpy(np.array(data_x))
x_train = x_train.view(x_train.size(0), -1)
y_train = torch.from_numpy(np.array(label_x))
x_val = torch.from_numpy(np.array(data_y))
x_val = x_val.view(x_val.size(0), -1)
y_val = torch.from_numpy(np.array(label_y))

我們還必須為我們的模型定義優化器和損失函數:

# batch大小
batch_size = 128

# 30個epochs
n_epochs = 30

for epoch in tqdm(range(1, n_epochs+1)):

# 跟蹤訓練與驗證集損失
train_loss = 0.0

permutation = torch.randperm(x_train.size()[0])


training_loss = []
for i in range(0,x_train.size()[0], batch_size):

indices = permutation[i:i+batch_size]
batch_x, batch_y = x_train[indices], y_train[indices]

if torch.cuda.is_available():
batch_x, batch_y = batch_x.cuda(), batch_y.cuda()

optimizer.zero_grad()
outputs = model.classifier(batch_x)
loss = criterion(outputs,batch_y)

training_loss.append(loss.item())
loss.backward()
optimizer.step()

training_loss = np.average(training_loss)
print('epoch: \\t', epoch, '\\t training loss: \\t', training_loss)
使用PyTorch進行遷移學習

以下是該模型的摘要。你可以看到損失減少了,因此我們可以說模型在改進。讓我們通過觀察訓練和驗證的準確性來驗證這一點:

# 預測訓練集
prediction = []
target = []
permutation = torch.randperm(x_train.size()[0])
for i in tqdm(range(0,x_train.size()[0], batch_size)):
indices = permutation[i:i+batch_size]
batch_x, batch_y = x_train[indices], y_train[indices]

if torch.cuda.is_available():
batch_x, batch_y = batch_x.cuda(), batch_y.cuda()

with torch.no_grad():
output = model.classifier(batch_x.cuda())

softmax = torch.exp(output).cpu()
prob = list(softmax.numpy())
predictions = np.argmax(prob, axis=1)
prediction.append(predictions)
target.append(batch_y)

# 訓練精度
accuracy = []
for i in range(len(prediction)):
accuracy.append(accuracy_score(target[i],prediction[i]))

print('training accuracy: \\t', np.average(accuracy))
使用PyTorch進行遷移學習

我們在訓練集上獲得了大約84%的準確性。現在讓我們檢查驗證的準確性:

# 預測驗證集
prediction = []
target = []
permutation = torch.randperm(x_train.size()[0])
for i in tqdm(range(0,x_train.size()[0], batch_size)):
indices = permutation[i:i+batch_size]
batch_x, batch_y = x_train[indices], y_train[indices]

if torch.cuda.is_available():
batch_x, batch_y = batch_x.cuda(), batch_y.cuda()

with torch.no_grad():
output = model.classifier(batch_x.cuda())

softmax = torch.exp(output).cpu()
prob = list(softmax.numpy())
predictions = np.argmax(prob, axis=1)
prediction.append(predictions)
target.append(batch_y)

# 驗證精度
accuracy = []
for i in range(len(prediction)):
accuracy.append(accuracy_score(target[i],prediction[i]))

print('training accuracy: \\t', np.average(accuracy))
使用PyTorch進行遷移學習

模型的驗證精度也相似, 83%。訓練和驗證的準確性幾乎是同步的,因此我們可以說這個模型是廣義的。以下是我們的研究結果摘要:

Model Training Accuracy Validation Accuracy CNN 81.57% 76.26% VGG16
83.70% 83.47%

我們可以推斷,與CNN模型相比,VGG16預訓練模型的準確率有所提高!

6.結尾

在這篇文章中,我們學習瞭如何使用預訓練的模型和遷移學習來解決一個圖像分類問題。我們首先了解什麼是預訓練模型,以及如何根據手頭的問題選擇正確的預訓練模型。然後,我們以汽車圖像為例進行了緊急和非緊急圖像的分類研究。我們首先使用CNN模型解決了這個案例研究,然後使用VGG16預訓練模型解決了同樣的問題。

我們發現使用VGG16預訓練模型顯著提高了模型性能,並且與CNN模型相比,我們得到了更好的結果。我希望你現在已經清楚地瞭解瞭如何在使用PyTorch使用遷移學習和正確的預訓練模型來解決問題。

我鼓勵你試著用遷移學習來解決其他的圖像分類問題。這將幫助你更清楚地理解這個概念。


分享到:


相關文章: