一、簡介

Pytorch是目前非常流行的大規模矩陣計算框架,上手簡易,文件詳盡,最新發表的深度學習領域的論文中有多半是以pytorch框架來實現的,足以看出其易用性和流行度。 這篇文章將以yolov3為例,介紹pytorch中如何實現一個網路的訓練和推斷。

二、Pytorch構建深度學習網路

這一部分主要講解一下,在pytorch中構建一個深度學習網路,需要包含哪些部分,各部分都起了什麼作用。不同的框架的實現方式會有許多不同,但基本都包含這些部分。在以下的講解中我隱去了一些具體的實現細節,如果想詳細瞭解,可以前往Pytorch-YOLOv3這個github瞭解,我的講解程式碼也是以它為基礎改編的,兩個版本配合著看能更好地瞭解和上手。

1。datasets

資料集在網路的訓練過程是必須的。通常在訓練指令碼中,會看到類似下面的這樣一行程式碼。

dataloader

=

torch

utils

data

DataLoader

Dataset

train_path

))

其中的Dataset是自定義的一個類,train_path是訓練資料集的路徑。Dataset通常定義在命名為datasets的檔案內,當然也有以VOCDataset、COCODataset來命名的,其作用都是相同的,定義一個數據集類,以便pytorch呼叫。下面給出一個Dataset的類定義模板,該模板為yolov3的框架所使用。

class

Dataset

Dataset

):

def

__init__

self

img_dir

label_dir

):

self

img_files

=

glob

glob

os

path

join

img_dir

‘*。*’

))

self

label_files

=

glob

glob

os

path

join

label_dir

‘*。*’

))

def

__getitem__

self

index

):

# === 圖片 ===

# 讀取圖片

img_path

=

self

img_files

index

%

len

self

img_files

)]

rstrip

()

img

=

np

array

Image

open

img_path

))

img

=

torch

from_numpy

img

transpose

2

0

1

))

float

()

div

255

# 將numpy。array的格式轉為torch。Tensor格式,並轉換通道

# 影象預處理(可選)

# 做一些諸如pad、resize之類的操作

# === 標籤 ===

# 獲取標籤檔案路徑

label_path

=

self

label_files

index

%

len

self

img_files

)]

rstrip

()

# 解析標籤檔案(可選)

# 讀取label_path的檔案然後解析,也可直接返回label_path

return

img

label_path

def

__len__

self

):

return

len

self

img_files

基本上所有的Dataset類都會包含init、getitem、len這三個函式,在getitem函式中,一般會包含影象預處理和標籤預處理,也有些是把這兩部分放在外部處理,getitem只獲取影象和標籤檔案路徑,值得注意的是,有不少的框架對getitem進行了過載,所以你可能沒有找到getitem函式,但是有其他函式能代替getitem的作用。

2。models

在DL框架中models是一個最為重要的部分,它實現了整個網路的整體結構和具體細節,在一些通用型的大型專案框架內,通常會把這部分拆分成多個modules進行實現,而在一些小專案裡,models也可能僅僅用一個檔案來實現。這裡我還是以yolov3的models來舉例介紹。

# === 讀取cfg配置檔案 ===

def

create_modules

cfg

):

# 根據配置檔案進行解析

return

module_list

# === yolo層定義 ===

class

YOLOLayer

nn

Module

):

def

__init__

self

cfg

):

super

YOLOLayer

self

__init__

()

def

forward

self

x

targets

=

None

):

if

targets

is

not

None

# === 訓練階段 ===

# 計算loss,根據輸入的x的結果與targets進行計算,最後得到loss

return

x

loss

else

# === 推斷階段 ===

# 根據輸入的x計算出預測結果

return

x

# === darknet網路結構定義 ===

class

Darknet

nn

Module

):

def

__init__

self

cfg

):

super

Darknet

self

__init__

()

self

module_list

=

create_modules

cfg

def

forward

self

x

targets

=

None

):

losses

=

[]

for

module

in

self

module_list

if

module

is

not

‘YOLO’

x

=

module

x

else

# === 訓練階段 ===

if

is_training

x

loss

=

module

x

targets

losses

append

loss

# === 推斷階段 ===

else

x

=

module

x

return

x

losses

在網路結構和yolo層定義中,init和forward這兩個函式是必須的,事實上這兩個函式也是torch內建已經定義過了的,這裡這樣寫實際是過載了這兩個函式。有些專案中可能會把訓練和推斷的forward函式拆分成兩個函式,函式名字也改變了,實際運用時要注意。

3。train

訓練指令碼可以說是網路中最為關鍵的部分,它直接影響了模型的效能和魯棒性。基本上不同網路的訓練指令碼均有不同之處,但是均可以達到一定的效果。一個訓練指令碼一般包含dataloader、optimizer、model三個部分,運用這三個部分構成train迭代迴圈過程。

# 構建model,模型結構

model

=

Darknet

model_config_path

model

apply

weights_init_normal

model

train

()

if

cuda

model

=

model

cuda

()

# 設定dataloader ,資料集載入器

# batch_size根據視訊記憶體大小調整,shuffle是指是否打亂資料集的讀取順序,num_workers是指用多少個執行緒讀取資料集

dataloader

=

torch

utils

data

DataLoader

Dataset

img_dir

label_dir

),

batch_size

=

16

shuffle

=

True

num_workers

=

4

# 設定optimizer,最佳化器

# 最佳化器的種類有非常多,建議新手使用Adam,因為這是一個自適應調整學習率的最佳化器,不需要設定很多引數

# 如果需要精調模型,或者對這方面比較熟練,可以使用SGD+Momentum最佳化器

optimizer

=

torch

optim

Adam

filter

lambda

p

p

requires_grad

model

parameters

()))

# 主迴圈train過程

total_epoch

=

10

for

epoch

in

range

total_epoch

):

for

batch_i

imgs

targets

in

enumerate

dataloader

):

# 注意,輸入的影象必須進行通道轉換,我這裡忽略了這個步驟,因為我之前已經在Dataset部分實現了

# 這裡的imgs的shape應該為(B, C, H, W),B為batch_size,C為通道,H為高,W為寬

imgs

requires_grad

=

True

# imgs的requires_grad屬性必須為True,而targets的requires_grad屬性為False(預設為False)

if

cuda

imgs

=

imgs

cuda

()

targets

=

targets

cuda

()

optimizer

zero_grad

()

_

loss

=

model

imgs

targets

loss

backward

()

optimizer

step

()

print

‘epoch:’

epoch

‘batch:’

batch_i

‘loss:’

loss

detach

()

cpu

()

numpy

())

if

epoch

%

1

==

0

torch

save

model

state_dict

(),

‘backup。pth’

以上就是訓練指令碼中所包含的基本部分,關於loss的計算,有些project把它放在了forward函數里面,也是沒問題的,只要注意進行計算imgs的requires_grad必須為True就可以了。

4。inference

推斷指令碼相對於訓練來說比較簡單,基本上大同小異,只要模型結構沒錯基本上輸出結果都是相同的。

# 載入模型

model

=

Darknet

model_config_path

params

=

torch

load

‘backup。pth’

model

load_state_dict

params

model

eval

()

# 讀取圖片和推斷

img

=

cv2

imread

path

img

=

torch

from_numpy

img

transpose

2

0

1

))

float

()

div

255

unsqueeze

0

with

torch

no_grad

():

out

_

=

model

img

# 處理out,例如進行nms和結果顯示,該部分省略

三、總結

以上就是在pytorch中構建模型和訓練、推斷的主要過程,這個部落格主要目的是幫大家理解這個過程,所以對於一些具體實現細節我沒有給出,想詳細瞭解的可以去Pytorch-YOLOv3這個github上進行了解,後續我有時間也會公佈一個我個人的yolov3的pytorch版本。如有疑問也可以在下面評論,我有空會回覆,謝謝。