訂閱
糾錯
加入自媒體

用Pytorch訓(xùn)練神經(jīng)網(wǎng)絡(luò)

本文目標(biāo)是如何使用Pytorch以盡可能短的方式從圖像中預(yù)測顏色、填充級別等連續(xù)屬性。我們將學(xué)習(xí)加載現(xiàn)有網(wǎng)絡(luò),修改它以預(yù)測特定屬性,并用不到40行代碼(不包括空格)對其進行訓(xùn)練。

Standart神經(jīng)網(wǎng)絡(luò)通常專注于分類問題,比如識別貓和狗。然而,這些網(wǎng)絡(luò)可以很容易地進行修改,從圖像中預(yù)測連續(xù)屬性,如年齡、大小或價格。

首先,讓我們導(dǎo)入軟件包并定義主要的訓(xùn)練參數(shù):

import numpy as np

import torchvision.models.segmentation

import torch

import torchvision.transforms as tf

Learning_Rate=1e-5

width=height=900

batchSize=1

學(xué)習(xí)率:是訓(xùn)練過程中梯度下降的步長。

寬度和高度是用于訓(xùn)練的圖像的尺寸。訓(xùn)練過程中的所有圖像都將調(diào)整為該大小。

batchSize:是將用于每次訓(xùn)練迭代的圖像數(shù)。

batchSize,width,height將與訓(xùn)練的內(nèi)存需求成比例。根據(jù)硬件的不同,可能需要使用較小的批處理大小來避免內(nèi)存不足問題。

請注意,由于我們只使用單一大小的圖像進行訓(xùn)練,因此訓(xùn)練后的網(wǎng)絡(luò)可能僅限于使用這種大小的圖像。

接下來,讓我們創(chuàng)建訓(xùn)練數(shù)據(jù)。我們想做一個簡單的演示,所以我們將創(chuàng)建一個用白色填充到一定高度的圖像。該網(wǎng)絡(luò)的目標(biāo)是預(yù)測被白色覆蓋的圖像的比例。這可以很容易地用于從真實圖像預(yù)測更復(fù)雜的屬性,如其他教程所示。

例如:

在上圖中,我們希望網(wǎng)絡(luò)預(yù)測為0.47,因為47%的圖像填充為白色。在底圖中,我們希望網(wǎng)絡(luò)預(yù)測0.76,因為76%的圖像填充為白色。

在實際環(huán)境中,你可能會從文件中加載數(shù)據(jù)。在這里,我們將動態(tài)創(chuàng)建它:

def ReadRandomImage(): 

  FillLevel=np.random.random() # Set random fill level

  Img=np.zeros([900,900,3],np.uint8) # Create black image 

  Img[0:int(FillLevel*900),:]=255 # Fill the image  

  transformImg=tf.Compose([tf.ToPILImage(),  

tf.Resize((height,width)),tf.ToTensor(),tf.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225))]) # Set image transformation 

Img=transformImg(Img) # Transform to pytorch

return Img,F(xiàn)illLevel

在第一部分中,我們創(chuàng)建圖像:

FillLevel=np.random.random() # Set random fill level

Img=np.zeros([900,900,3],np.uint8) # Create black image

Img[0:int(FillLevel*900),:]=255 # Fill the image

第一行選擇0–1之間的隨機數(shù)作為填充級別。

Img=np.zeros([900,900,3])創(chuàng)建一個大小為900X900的矩陣,填充零作為圖像。這相當(dāng)于一個高度和寬度為900的黑色圖像。圖像有3個對應(yīng)于RGB的通道。

接下來,我們用白色填充圖像的頂部,直到填充水平線。

Img[0:int(FillLevel*900),:]=255

現(xiàn)在我們創(chuàng)建了圖像,我們對其進行處理并將其轉(zhuǎn)換為Pytorch格式:

transformImg=tf.Compose([tf.ToPILImage(),  

tf.Resize((height,width)),tf.ToTensor(),tf.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225))]) # Set image transformation

這定義了一組將應(yīng)用于圖像的變換。這包括轉(zhuǎn)換為PIL格式(轉(zhuǎn)換的標(biāo)準(zhǔn)格式),以及調(diào)整大小和轉(zhuǎn)換為PyTorch格式。

對于圖像,我們還通過減去平均值并除以像素強度來標(biāo)準(zhǔn)化圖像中像素的強度。

對于我們的簡單圖像,標(biāo)準(zhǔn)化和大小調(diào)整并不是真正需要的,但這些轉(zhuǎn)換對于真實圖像很重要。

接下來,我們將變換應(yīng)用于圖像:

Img=transformImg(Img)

對于訓(xùn)練,我們需要使用一批圖像。這意味著在4D矩陣中,多個圖像相互疊加。我們使用以下函數(shù)創(chuàng)建batch:

def LoadBatch(): # Load batch of images
          images = torch.zeros([batchSize,3,height,width])
          FillLevel = torch.zeros([batchSize])
          for i in range(batchSize):
              images[i],F(xiàn)illLevel[i]=ReadRandomImage()
          return images,F(xiàn)illLevel

第一行創(chuàng)建一個空的4d矩陣,該矩陣將存儲尺寸為[batchSize,Channel,height,width]的圖像,其中Channel是圖像的層數(shù);這是RGB圖像的3。下一行創(chuàng)建一個數(shù)組,其中存儲填充級別。這將作為我們訓(xùn)練的標(biāo)簽。

下一部分使用前面定義的ReadRandomImage函數(shù)將圖像集和填充級別加載到空矩陣:

for i in range(batchSize):
           images[i],F(xiàn)illLevel[i]=ReadRandomImage()

現(xiàn)在我們可以加載數(shù)據(jù)了,是時候加載神經(jīng)網(wǎng)絡(luò)了:

device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')

Net = torchvision.models.resnet18(pretrained=True) # Load net

Net.fc = torch.nn.Linear(in_features=512, out_features=1, bias=True)

Net = Net.to(device)

optimizer = torch.optim.Adam(params=Net.parameters(),lr=Learning_Rate)

第一部分是確定計算機是否有GPU或CPU。如果有Cuda GPU,訓(xùn)練將在GPU上進行:

device = torch.device(‘cuda’) if torch.cuda.is_available() else torch.device(‘cpu’)

對于任何實際數(shù)據(jù)集,使用CPU進行訓(xùn)練都非常緩慢。

接下來,我們加載用于圖像分類的網(wǎng)絡(luò):

Net = torchvision.models.resnet18(pretrained=True)

torchvision.models包含許多有用的圖像分類模型。Reseet18是一個輕量級的分類模型,適用于低資源訓(xùn)練或簡單的數(shù)據(jù)集。對于更難的問題,最好使用resenet50(請注意,數(shù)字指的是網(wǎng)絡(luò)中的層數(shù))。

通過設(shè)置pretrained=True,我們在Imagenet數(shù)據(jù)集上加載帶有預(yù)訓(xùn)練權(quán)重的網(wǎng)絡(luò)。

在學(xué)習(xí)新問題時,最好從預(yù)訓(xùn)練的模型開始,因為它允許網(wǎng)絡(luò)使用以前的經(jīng)驗并更快地收斂。

我們可以看到我們剛剛通過print(Net)查看網(wǎng)絡(luò)的所有結(jié)構(gòu)和所有層:

print(Net)

(avgpool): AdaptiveAvgPool2d(output_size=(1, 1))

(fc): Linear(in_features=512, out_features=1000, bias=True)

這會按使用順序打印層。

網(wǎng)絡(luò)的最后一層是線性變換,輸入512層,輸出1000層。1000代表輸出類的數(shù)量(這個網(wǎng)絡(luò)是在圖像網(wǎng)絡(luò)上訓(xùn)練的,圖像網(wǎng)絡(luò)將圖像分為1000個類中的一個)。

因為我們只想預(yù)測一個值,所以我們想用一個輸出的新線性層來代替它:

Net.fc = torch.nn.Linear(in_features=512, out_features=1, bias=True)

公平地說,這部分是可選的,因為一個有1000個輸出通道的網(wǎng)絡(luò)只需忽略999個通道就可以預(yù)測一個值。但這樣更優(yōu)雅。

接下來,我們將網(wǎng)絡(luò)加載到GPU或CPU設(shè)備中:

Net=Net.to(device)

最后,我們加載一個優(yōu)化器:

optimizer=torch.optim.Adam(params=Net.parameters(),lr=Learning_Rate) # Create adam optimizer

優(yōu)化器將在反向傳播步驟中控制梯度速率。Adam是最快的優(yōu)化器之一。

最后,我們通過加載數(shù)據(jù)開始訓(xùn)練,使用網(wǎng)絡(luò)進行預(yù)測:

AverageLoss=np.zeros([50]) # Save average loss for display

for itr in range(2001): # Training loop 

   images,GTFillLevel=LoadBatch() # Load taining batch   

   images=torch.a(chǎn)utograd.Variable(images,requires_grad=False).
   to(device)    

   GTFillLevel = torch.a(chǎn)utograd.Variable(GTFillLevel,

   requires_grad=False).to(device)

   PredLevel=Net(images) # make prediction

首先,我們希望保存訓(xùn)練期間的平均損失;我們創(chuàng)建一個數(shù)組來存儲最后50步的損失。

AverageLoss=np.zeros([50])

這將使我們能夠跟蹤網(wǎng)絡(luò)的學(xué)習(xí)情況。

我們將訓(xùn)練2000個步驟:

for itr in range(2000):

LoadBatch在前面定義,幫助加載一批圖像及其填充級別。

torch.a(chǎn)utograd.Variable:將數(shù)據(jù)轉(zhuǎn)換成網(wǎng)絡(luò)可以使用的梯度變量。我們設(shè)置Requires_grad=False,因為我們只將梯度應(yīng)用于網(wǎng)絡(luò)的層。to(device) 將張量復(fù)制到對應(yīng)的設(shè)備(GPU/CPU)。

最后,我們將圖像輸入網(wǎng)絡(luò),得到預(yù)測結(jié)果。

PredLevel=Net(images)

一旦我們做出預(yù)測,我們可以將其與實際填充水平進行比較,并計算損失。損失是圖像的預(yù)測和真實填充水平之間的絕對差(L1):

Loss=torch.a(chǎn)bs(PredLevel-GTFillLevel).mean()

請注意,我們不是將損失應(yīng)用于一張圖像,而是應(yīng)用于批次中的多張圖像,因此我們需要將損失的平均值作為單個數(shù)字。

一旦我們計算了損失,我們就可以應(yīng)用反向傳播并改變權(quán)重。

Loss.backward() # Backpropogate loss

Optimizer.step() # Apply gradient descent change to wei

在訓(xùn)練期間,我們想看看我們的平均損失是否減少,看看網(wǎng)絡(luò)是否真的學(xué)到了什么。

因此,我們將最后50個損失值存儲在一個數(shù)組中,并顯示每個步驟的平均值:

AverageLoss[itr%50]=Loss.data.cpu().numpy() # Save loss average

print(itr,") Loss=",Loss.data.cpu().numpy(),
'AverageLoss',AverageLoss.mean())

這涵蓋了整個訓(xùn)練階段,但我們還需要保存經(jīng)過訓(xùn)練的模型。否則,一旦程序停止,它就會丟失。

保存很費時,所以我們希望大約每200步只做一次:

if itr % 200 == 0:
         print(“Saving Model” +str(itr) + “.torch”)

  torch.save(Net.state_dict(), str(itr) + “.torch”)

在運行這個腳本大約200步之后,網(wǎng)絡(luò)應(yīng)該會給出很好的結(jié)果。

總共40行代碼,不包括空格。

訓(xùn)練并保存網(wǎng)絡(luò)后,可以加載網(wǎng)絡(luò)進行預(yù)測:

https://github.com/sagieppel/Train-neural-net-to-predict-continuous-property-from-an-image-in-40-lines-of-code-with-PyTorch/blob/main/Infer.py

該腳本加載你之前訓(xùn)練和保存的網(wǎng)絡(luò),并使用它進行預(yù)測。

這里的大部分代碼與訓(xùn)練腳本相同,只有幾處不同:

Net.load_state_dict(torch.load(modelPath)) # Load trained model

從modelPath中的文件加載我們之前訓(xùn)練和保存的網(wǎng)絡(luò)

#Net.eval()

將網(wǎng)絡(luò)從訓(xùn)練模式轉(zhuǎn)換為評估模式。這主要意味著不會計算批次標(biāo)準(zhǔn)化統(tǒng)計數(shù)據(jù)。

雖然使用它通常是一個好主意,但在我們的例子中,它實際上會降低準(zhǔn)確性,因此我們將在沒有它的情況下使用網(wǎng)絡(luò)。

with torch.no_grad():

這意味著網(wǎng)絡(luò)運行時沒有收集梯度。梯度只與訓(xùn)練相關(guān),收集梯度需要大量資源。

感謝閱讀!


       原文標(biāo)題 : 用Pytorch訓(xùn)練神經(jīng)網(wǎng)絡(luò)

聲明: 本文由入駐維科號的作者撰寫,觀點僅代表作者本人,不代表OFweek立場。如有侵權(quán)或其他問題,請聯(lián)系舉報。

發(fā)表評論

0條評論,0人參與

請輸入評論內(nèi)容...

請輸入評論/評論長度6~500個字

您提交的評論過于頻繁,請輸入驗證碼繼續(xù)

暫無評論

暫無評論

    掃碼關(guān)注公眾號
    OFweek人工智能網(wǎng)
    獲取更多精彩內(nèi)容
    文章糾錯
    x
    *文字標(biāo)題:
    *糾錯內(nèi)容:
    聯(lián)系郵箱:
    *驗 證 碼:

    粵公網(wǎng)安備 44030502002758號