我去年在一個計算機視覺項目中工做,咱們必須創建一個健壯的人臉檢測模型。python
考慮到咱們擁有的數據集的大小,從頭構建一個模型是一個挑戰。從頭構建將是一個耗時又消耗計算資源的方案。因爲時間緊迫,咱們必須儘快找出解決辦法。數組
這就是遷移學習拯救咱們的時候。這是一個很是有用的工具,能夠放在你的數據科學家庫中,特別是當你使用有限的時間和計算能力時。網絡
所以,在本文中,咱們將學習有關遷移學習的全部內容,以及如何在使用Python的實際項目中利用它。咱們還將討論預訓練模型在這個領域的做用,以及它們將如何改變構建機器學習pipeline(管道)的方式。架構
本文是面向初學者的PyTorch系列文章的一部分。我堅信PyTorch是目前最好的深度學習框架之一,在不久的未來會愈來愈強大。這是一個很好的時間來學習它是如何工做的,並參與其中。app
讓我用一個例子來講明遷移學習的概念。想象一下,你想從一個你徹底陌生的領域學習一個主題。框架
你會採起什麼不一樣的方法來理解這個主題?你可能會:機器學習
全部這些都會幫助你熟悉這個主題。在這種狀況下,你是惟一一個付出全部時間來熟悉主題的人。函數
但還有另外一種方法,它可能在短期內產生更好的結果。工具
你能夠諮詢對你想要學習的主題具備能力的領域/主題專家。這我的會把他/她的知識傳授給你。從而加快你的學習過程。性能
> 第一種方法,即你獨自投入全部的努力,是從頭開始學習的一個例子。第二種方法被稱爲遷移學習。知識轉移發生在一個領域的專家到一個面對新領域的新手。
是的,遷移學習背後的思想就是這麼簡單!
神經網絡和卷積神經網絡就是從零開始學習的例子。這兩個網絡都從給定的一組圖像中提取特徵(對於與圖像相關的任務),而後根據這些提取的特徵將圖像分類到各自的類中。
這就是遷移學習和預訓練的模型很是有用的地方。讓咱們在下一節中瞭解一下後一個概念。
在你將要從事的任何深度學習項目中,預訓練的模型都是很是有用的。並不是全部人都擁有頂級科技巨頭的無限計算能力,相反咱們須要使用咱們本地有限的機器,因此預訓練模型是一個強大的工具。
> 正如你可能已經猜到的,預訓練模型是由特定人員或團隊爲解決特定問題而設計和訓練的模型。 > 回想一下,咱們在訓練神經網絡和CNNs等模型時學習了權重和偏置。當這些權重和偏置與圖像像素相乘時,有助於生成特徵。
預訓練的模型經過將它們的權重和偏置傳遞給一個新模型來共享它們的學習。所以,當咱們進行遷移學習時,咱們首先選擇恰當的預訓練模型,而後將其已學習的權值和偏置傳遞給新模型。
有n種預訓練過的模型。咱們須要決定哪一種模式最適合咱們的問題。如今,讓咱們考慮一下咱們有三個預訓練好的網絡——BERT、ULMFiT和VGG16。
咱們的任務是對圖像進行分類(正如咱們在本系列的前幾篇文章中所作的那樣)。那麼,你會選擇哪些預訓練好的模型呢?讓我先給你一個快速的概述這些預訓練的網絡,這將幫助咱們決定正確的預訓練的模型。
語言建模使用BERT和ULMFiT,圖像分類任務使用VGG16。若是你看一下手頭的問題,這是一個圖像分類問題。因此咱們選擇VGG16是理所固然的。
如今,VGG16能夠有不一樣的重量,即VGG16訓練在ImageNet或VGG16訓練在MNIST:
如今,爲咱們的問題肯定正確的預訓練模型,咱們應該研究這些ImageNet和MNIST數據集。ImageNet數據集由1000個類和總共120萬張圖像組成。這些數據中的一些類別是動物、汽車、商店、狗、食物、儀器等:
另外一方面,MNIST是訓練手寫數字的。它包括10類從0到9:
咱們將在一個項目中工做,咱們須要將圖像分爲緊急和非緊急車輛(咱們將在下一節詳細討論)。這個數據集包括車輛的圖像,所以在ImageNet數據集上訓練的VGG16模型將更有用,由於它有車輛的圖像。
簡而言之,這就是咱們應該如何根據咱們的問題來決定正確的預訓練模型。
咱們將進行一個新的目標!這裏,咱們的目標是將車輛分爲緊急和非緊急。
如今讓咱們開始理解這個問題並可視化一些示例。你能夠經過這個連接下載圖片:https://drive.google.com/file/d/1EbVifjP0FQkyB1axb7KQ26yPtWmneApJ/view
首先,導入所需的庫:
接下來,咱們將讀取包含圖像名稱和相應標籤的.csv文件:
csv文件有兩列:
接下來,咱們將加載全部的圖像,並將它們存儲爲數組格式:
加載這些圖像大約須要12秒。在咱們的數據集中有1646張圖像,因爲VGG16須要全部這種特殊形狀的圖像,因此咱們將它們的形狀所有重設爲(224,224,3)。如今讓咱們從數據集中可視化一些圖像:
這是一輛警車,所以有緊急車輛的標籤。如今咱們將目標存儲在一個單獨的變量:
讓咱們建立一個驗證集來評估咱們的模型:
咱們在訓練集中有1,481張圖像,在驗證集中有165張圖像。如今咱們必須將數據集轉換爲torch格式:
相似地,咱們將轉換驗證集:
咱們的數據準備好了!在下一節中,咱們將構建一個卷積神經網絡(CNN),而後使用預訓練模型來解決這個問題。
咱們終於到了模型製做部分!在使用遷移學習來解決這個問題以前,咱們先用一個CNN模型爲本身設定一個benchmark。
咱們將構建一個很是簡單的CNN架構,它有兩個卷積層來提取圖像的特徵,最後是一個全鏈接層來對這些特徵進行分類:
如今讓咱們定義優化器,學習率和損失函數爲咱們的模型,並使用GPU訓練模型:
這就是模型架構的樣子。最後,咱們將對模型進行15個epoch的訓練。我將模型的batch_size設置爲128(你能夠嘗試一下):
這也會打印一份訓練總結。訓練損失在每一個時期以後都在減小,這是一個好跡象。咱們來檢查一下訓練和驗證的準確性:
咱們的訓練正確率在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))
驗證的準確性爲76%。如今咱們已經有了一個基準,是時候使用遷移學習來解決緊急和非緊急車輛分類問題了!
我在上面已經提到了這一點,我在這裏重申一下——咱們將使用在ImageNet數據集上訓練的VGG16預訓練模型。讓咱們看看咱們將遵循的步驟,以訓練模型使用遷移學習:
那麼,讓咱們從加載模型的權重開始:
# 加載預訓練模型 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()
如今,咱們將使用該模型併爲訓練和驗證圖像提取特性。我將batch_size設置爲128(一樣,你能夠根據須要增長或減小batch_size):
# 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)
如下是該模型的摘要。你能夠看到損失減小了,所以咱們能夠說模型在改進。讓咱們經過觀察訓練和驗證的準確性來驗證這一點:
# 預測訓練集 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))
咱們在訓練集上得到了大約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))
模型的驗證精度也類似, 83%。訓練和驗證的準確性幾乎是同步的,所以咱們能夠說這個模型是廣義的。如下是咱們的研究結果摘要:
Model | Training Accuracy | Validation Accuracy |
---|---|---|
CNN | 81.57% | 76.26% |
VGG16 | 83.70% | 83.47% |
咱們能夠推斷,與CNN模型相比,VGG16預訓練模型的準確率有所提升!
在這篇文章中,咱們學習瞭如何使用預訓練的模型和遷移學習來解決一個圖像分類問題。咱們首先了解什麼是預訓練模型,以及如何根據手頭的問題選擇正確的預訓練模型。而後,咱們以汽車圖像爲例進行了緊急和非緊急圖像的分類研究。咱們首先使用CNN模型解決了這個案例研究,而後使用VGG16預訓練模型解決了一樣的問題。
咱們發現使用VGG16預訓練模型顯著提升了模型性能,而且與CNN模型相比,咱們獲得了更好的結果。我但願你如今已經清楚地瞭解瞭如何在使用PyTorch使用遷移學習和正確的預訓練模型來解決問題。
我鼓勵你試着用遷移學習來解決其餘的圖像分類問題。這將幫助你更清楚地理解這個概念。