AlexNet——CNN經典網路模型詳解(pytorch實現)

建議大家可以實踐下,程式碼都很詳細,有不清楚的地方評論區見~

二、AlexNet

AlexNet——CNN經典網路模型詳解(pytorch實現)

在imagenet上的影象分類challenge上大神Alex提出的alexnet網路結構模型贏得了2012屆的冠軍,振奮人心,利用CNN實現了圖片分類,別人用傳統的機器學習演算法調參跳到半死也就那樣,Alex利用CNN精度遠超傳統的網路。

AlexNet——CNN經典網路模型詳解(pytorch實現)

1. conv1階段DFD(data flow diagram):

AlexNet——CNN經典網路模型詳解(pytorch實現)

AlexNet——CNN經典網路模型詳解(pytorch實現)

第一層輸入資料為原始的227

227

3的影象,這個影象被11

11

3的卷積核進行卷積運算,卷積核對原始影象的每次卷積都生成一個新的畫素。卷積核沿原始影象的x軸方向和y軸方向兩個方向移動,移動的步長是4個畫素。因此,卷積核在移動的過程中會生成(227-11)/4+1=55個畫素(227個畫素減去11,正好是54,即生成54個畫素,再加上被減去的11也對應生成一個畫素),行和列的55

55個畫素形成對原始影象卷積之後的畫素層。共有96個卷積核,會生成55

55

96個卷積後的畫素層。96個卷積核分成2組,每組48個卷積核。對應生成2組55

55

48的卷積後的畫素層資料。這些畫素層經過relu1單元的處理,生成啟用畫素層,尺寸仍為2組55

55*48的畫素層資料。

這些畫素層經過pool運算(池化運算)的處理,池化運算的尺度為3

3,運算的步長為2,則池化後圖像的尺寸為(55-3)/2+1=27。 即池化後像素的規模為27

27

96;然後經過歸一化處理,歸一化運算的尺度為5

5;第一卷積層運算結束後形成的畫素層的規模為27

27

96。分別對應96個卷積核所運算形成。這96層畫素層分為2組,每組48個畫素層,每組在一個獨立的GPU上進行運算。

反向傳播時,每個卷積核對應一個偏差值。即第一層的96個卷積核對應上層輸入的96個偏差值。

2. conv2階段DFD(data flow diagram):

AlexNet——CNN經典網路模型詳解(pytorch實現)

AlexNet——CNN經典網路模型詳解(pytorch實現)

第二層輸入資料為第一層輸出的27

27

96的畫素層,為便於後續處理,每幅畫素層的左右兩邊和上下兩邊都要填充2個畫素;27x27x96的畫素資料分成27x27x48的兩組畫素資料,兩組資料分別再兩個不同的GPU中進行運算。每組畫素資料被5x5x48的卷積核進行卷積運算,卷積核對每組資料的每次卷積都生成一個新的畫素。卷積核沿原始影象的x軸方向和y軸方向兩個方向移動,移動的步長是1個畫素。因此,卷積核在移動的過程中會生成(27-5+2x2)/1+1=27個畫素。(27個畫素減去5,正好是22,在加上上下、左右各填充的2個畫素,即生成26個畫素,再加上被減去的5也對應生成一個畫素),行和列的27x27個畫素形成對原始影象卷積之後的畫素層。共有256個5x5x48卷積核;這256個卷積核分成兩組,每組針對一個GPU中的27x27x48的畫素進行卷積運算。會生成兩組27x27x128個卷積後的畫素層。這些畫素層經過relu2單元的處理,生成啟用畫素層,尺寸仍為兩組27x27x128的畫素層。

這些畫素層經過pool運算(池化運算)的處理,池化運算的尺度為3x3,運算的步長為2,則池化後圖像的尺寸為(57-3)/2+1=13。 即池化後像素的規模為2組13x13x128的畫素層;然後經過歸一化處理,歸一化運算的尺度為5*5;第二卷積層運算結束後形成的畫素層的規模為2組13x13x128的畫素層。分別對應2組128個卷積核所運算形成。每組在一個GPU上進行運算。即共256個卷積核,共2個GPU進行運算。

反向傳播時,每個卷積核對應一個偏差值。即第一層的96個卷積核對應上層輸入的256個偏差值。

3. conv3階段DFD(data flow diagram):

AlexNet——CNN經典網路模型詳解(pytorch實現)

AlexNet——CNN經典網路模型詳解(pytorch實現)

第三層輸入資料為第二層輸出的2組13

13

128的畫素層;為便於後續處理,每幅畫素層的左右兩邊和上下兩邊都要填充1個畫素;2組畫素層資料都被送至2個不同的GPU中進行運算。每個GPU中都有192個卷積核,每個卷積核的尺寸是3

3

256。因此,每個GPU中的卷積核都能對2組13

13

128的畫素層的所有資料進行卷積運算。卷積核對每組資料的每次卷積都生成一個新的畫素。卷積核沿畫素層資料的x軸方向和y軸方向兩個方向移動,移動的步長是1個畫素。因此,運算後的卷積核的尺寸為(13-3+1

2)/1+1=13(13個畫素減去3,正好是10,在加上上下、左右各填充的1個畫素,即生成12個畫素,再加上被減去的3也對應生成一個畫素),每個GPU中共13

13

192個卷積核。2個GPU中共13

13

384個卷積後的畫素層。這些畫素層經過relu3單元的處理,生成啟用畫素層,尺寸仍為2組13

13

192畫素層,共13

13*384個畫素層。

4. conv4階段DFD(data flow diagram):

AlexNet——CNN經典網路模型詳解(pytorch實現)

AlexNet——CNN經典網路模型詳解(pytorch實現)

第四層輸入資料為第三層輸出的2組13

13

192的畫素層;為便於後續處理,每幅畫素層的左右兩邊和上下兩邊都要填充1個畫素;2組畫素層資料都被送至2個不同的GPU中進行運算。每個GPU中都有192個卷積核,每個卷積核的尺寸是3

3

192。因此,每個GPU中的卷積核能對1組13

13

192的畫素層的資料進行卷積運算。卷積核對每組資料的每次卷積都生成一個新的畫素。卷積核沿畫素層資料的x軸方向和y軸方向兩個方向移動,移動的步長是1個畫素。因此,運算後的卷積核的尺寸為(13-3+1

2)/1+1=13(13個畫素減去3,正好是10,在加上上下、左右各填充的1個畫素,即生成12個畫素,再加上被減去的3也對應生成一個畫素),每個GPU中共13

13

192個卷積核。2個GPU中共13

13

384個卷積後的畫素層。這些畫素層經過relu4單元的處理,生成啟用畫素層,尺寸仍為2組13

13

192畫素層,共13

13*384個畫素層。

5. conv5階段DFD(data flow diagram):

AlexNet——CNN經典網路模型詳解(pytorch實現)

AlexNet——CNN經典網路模型詳解(pytorch實現)

第五層輸入資料為第四層輸出的2組13

13

192的畫素層;為便於後續處理,每幅畫素層的左右兩邊和上下兩邊都要填充1個畫素;2組畫素層資料都被送至2個不同的GPU中進行運算。每個GPU中都有128個卷積核,每個卷積核的尺寸是3

3

192。因此,每個GPU中的卷積核能對1組13

13

192的畫素層的資料進行卷積運算。卷積核對每組資料的每次卷積都生成一個新的畫素。卷積核沿畫素層資料的x軸方向和y軸方向兩個方向移動,移動的步長是1個畫素。因此,運算後的卷積核的尺寸為(13-3+1

2)/1+1=13(13個畫素減去3,正好是10,在加上上下、左右各填充的1個畫素,即生成12個畫素,再加上被減去的3也對應生成一個畫素),每個GPU中共13

13

128個卷積核。2個GPU中共13

13

256個卷積後的畫素層。這些畫素層經過relu5單元的處理,生成啟用畫素層,尺寸仍為2組13

13

128畫素層,共13

13*256個畫素層。

2組13

13

128畫素層分別在2個不同GPU中進行池化(pool)運算處理。池化運算的尺度為3

3,運算的步長為2,則池化後圖像的尺寸為(13-3)/2+1=6。 即池化後像素的規模為兩組6

6

128的畫素層資料,共6

6*256規模的畫素層資料。

6. fc6階段DFD(data flow diagram):

AlexNet——CNN經典網路模型詳解(pytorch實現)

AlexNet——CNN經典網路模型詳解(pytorch實現)

第六層輸入資料的尺寸是6

6

256,採用6

6

256尺寸的濾波器對第六層的輸入資料進行卷積運算;每個6

6

256尺寸的濾波器對第六層的輸入資料進行卷積運算生成一個運算結果,透過一個神經元輸出這個運算結果;共有4096個6

6

256尺寸的濾波器對輸入資料進行卷積運算,透過4096個神經元輸出運算結果;這4096個運算結果透過relu啟用函式生成4096個值;並透過drop運算後輸出4096個本層的輸出結果值。

由於第六層的運算過程中,採用的濾波器的尺寸(6

6

256)與待處理的feature map的尺寸(6

6

256)相同,即濾波器中的每個係數只與feature map中的一個畫素值相乘;而其它卷積層中,每個濾波器的係數都會與多個feature map中畫素值相乘;因此,將第六層稱為全連線層。

第五層輸出的6

6

256規模的畫素層資料與第六層的4096個神經元進行全連線,然後經由relu6進行處理後生成4096個數據,再經過dropout6處理後輸出4096個數據。

7. fc7階段DFD(data flow diagram):

AlexNet——CNN經典網路模型詳解(pytorch實現)

第六層輸出的4096個數據與第七層的4096個神經元進行全連線,然後經由relu7進行處理後生成4096個數據,再經過dropout7處理後輸出4096個數據。

8. fc8階段DFD(data flow diagram):

AlexNet——CNN經典網路模型詳解(pytorch實現)

第七層輸出的4096個數據與第八層的1000個神經元進行全連線,經過訓練後輸出被訓練的數值。

Alexnet網路中各個層發揮的作用如下表所述:

AlexNet——CNN經典網路模型詳解(pytorch實現)

實現程式碼

#model。py

import torch。nn as nn

import torch

classAlexNet(nn。Module):

def__init__(self, num_classes=1000, init_weights=False):

super(AlexNet, self)。__init__()

self。features = nn。Sequential( #打包

nn。Conv2d(3, 48, kernel_size=11, stride=4, padding=2), # input[3, 224, 224] output[48, 55, 55] 自動捨去小數點後

nn。ReLU(inplace=True), #inplace 可以載入更大模型

nn。MaxPool2d(kernel_size=3, stride=2), # output[48, 27, 27] kernel_num為原論文一半

nn。Conv2d(48, 128, kernel_size=5, padding=2), # output[128, 27, 27]

nn。ReLU(inplace=True),

nn。MaxPool2d(kernel_size=3, stride=2), # output[128, 13, 13]

nn。Conv2d(128, 192, kernel_size=3, padding=1), # output[192, 13, 13]

nn。ReLU(inplace=True),

nn。Conv2d(192, 192, kernel_size=3, padding=1), # output[192, 13, 13]

nn。ReLU(inplace=True),

nn。Conv2d(192, 128, kernel_size=3, padding=1), # output[128, 13, 13]

nn。ReLU(inplace=True),

nn。MaxPool2d(kernel_size=3, stride=2), # output[128, 6, 6]

self。classifier = nn。Sequential(

nn。Dropout(p=0。5),

#全連結

nn。Linear(128 * 6 * 6, 2048),

nn。ReLU(inplace=True),

nn。Dropout(p=0。5),

nn。Linear(2048, 2048),

nn。ReLU(inplace=True),

nn。Linear(2048, num_classes),

if init_weights:

self。_initialize_weights()

defforward(self, x):

x = self。features(x)

x = torch。flatten(x, start_dim=1) #展平 或者view()

x = self。classifier(x)

return x

def_initialize_weights(self):

for m in self。modules():

if isinstance(m, nn。Conv2d):

nn。init。kaiming_normal_(m。weight, mode=‘fan_out’, nonlinearity=‘relu’) #何教授方法

if m。bias isnotNone:

nn。init。constant_(m。bias, 0)

elif isinstance(m, nn。Linear):

nn。init。normal_(m。weight, 0, 0。01) #正態分佈賦值

nn。init。constant_(m。bias, 0)

下載資料集

DATA_URL = ‘http://download。tensorflow。org/example_images/flower_photos。tgz’

下載完後執行下面指令碼,將資料集進行分類

#spile_data。py

import os

from shutil import copy

import random

defmkfile(file):

ifnot os。path。exists(file):

os。makedirs(file)

file = ‘flower_data/flower_photos’

flower_class = [cla for cla in os。listdir(file) if“。txt”notin cla]

mkfile(‘flower_data/train’)

for cla in flower_class:

mkfile(‘flower_data/train/’+cla)

mkfile(‘flower_data/val’)

for cla in flower_class:

mkfile(‘flower_data/val/’+cla)

split_rate = 0。1

for cla in flower_class:

cla_path = file + ‘/’ + cla + ‘/’

images = os。listdir(cla_path)

num = len(images)

eval_index = random。sample(images, k=int(num*split_rate))

for index, image in enumerate(images):

if image in eval_index:

image_path = cla_path + image

new_path = ‘flower_data/val/’ + cla

copy(image_path, new_path)

else:

image_path = cla_path + image

new_path = ‘flower_data/train/’ + cla

copy(image_path, new_path)

print(“\r[{}] processing [{}/{}]”。format(cla, index+1, num), end=“”) # processing bar

print()

print(“processing done!”)

之後應該是這樣:

AlexNet——CNN經典網路模型詳解(pytorch實現)

train。py

import torch

import torch。nn as nn

from torchvision import transforms, datasets, utils

import matplotlib。pyplot as plt

import numpy as np

import torch。optim as optim

from model import AlexNet

import os

import json

import time

#device : GPU or CPU

device = torch。device(“cuda:0”if torch。cuda。is_available() else“cpu”)

print(device)

#資料轉換

data_transform = {

“train”: transforms。Compose([transforms。RandomResizedCrop(224),

transforms。RandomHorizontalFlip(),

transforms。ToTensor(),

transforms。Normalize((0。5, 0。5, 0。5), (0。5, 0。5, 0。5))]),

“val”: transforms。Compose([transforms。Resize((224, 224)), # cannot 224, must (224, 224)

transforms。ToTensor(),

transforms。Normalize((0。5, 0。5, 0。5), (0。5, 0。5, 0。5))])}

#data_root = os。path。abspath(os。path。join(os。getcwd(), “。。/。。”)) # get data root path

data_root = os。getcwd()

image_path = data_root + “/flower_data/”# flower data set path

train_dataset = datasets。ImageFolder(root=image_path + “/train”,

transform=data_transform[“train”])

train_num = len(train_dataset)

# {‘daisy’:0, ‘dandelion’:1, ‘roses’:2, ‘sunflower’:3, ‘tulips’:4}

flower_list = train_dataset。class_to_idx

cla_dict = dict((val, key) for key, val in flower_list。items())

# write dict into json file

json_str = json。dumps(cla_dict, indent=4)

with open(‘class_indices。json’, ‘w’) as json_file:

json_file。write(json_str)

batch_size = 32

train_loader = torch。utils。data。DataLoader(train_dataset,

batch_size=batch_size, shuffle=True,

num_workers=0)

validate_dataset = datasets。ImageFolder(root=image_path + “/val”,

transform=data_transform[“val”])

val_num = len(validate_dataset)

validate_loader = torch。utils。data。DataLoader(validate_dataset,

batch_size=batch_size, shuffle=True,

num_workers=0)

test_data_iter = iter(validate_loader)

test_image, test_label = test_data_iter。next()

#print(test_image[0]。size(),type(test_image[0]))

#print(test_label[0],test_label[0]。item(),type(test_label[0]))

#顯示影象,之前需把validate_loader中batch_size改為4

# def imshow(img):

# img = img / 2 + 0。5 # unnormalize

# npimg = img。numpy()

# plt。imshow(np。transpose(npimg, (1, 2, 0)))

# plt。show()

#

# print(‘ ’。join(‘%5s’ % cla_dict[test_label[j]。item()] for j in range(4)))

# imshow(utils。make_grid(test_image))

net = AlexNet(num_classes=5, init_weights=True)

net。to(device)

#損失函式:這裡用交叉熵

loss_function = nn。CrossEntropyLoss()

#最佳化器 這裡用Adam

optimizer = optim。Adam(net。parameters(), lr=0。0002)

#訓練引數儲存路徑

save_path = ‘。/AlexNet。pth’

#訓練過程中最高準確率

best_acc = 0。0

#開始進行訓練和測試,訓練一輪,測試一輪

for epoch in range(10):

# train

net。train() #訓練過程中,使用之前定義網路中的dropout

running_loss = 0。0

t1 = time。perf_counter()

for step, data in enumerate(train_loader, start=0):

images, labels = data

optimizer。zero_grad()

outputs = net(images。to(device))

loss = loss_function(outputs, labels。to(device))

loss。backward()

optimizer。step()

# print statistics

running_loss += loss。item()

# print train process

rate = (step + 1) / len(train_loader)

a = “*” * int(rate * 50)

b = “。” * int((1 - rate) * 50)

print(“\rtrain loss: {:^3。0f}%[{}->{}]{:。3f}”。format(int(rate * 100), a, b, loss), end=“”)

print()

print(time。perf_counter()-t1)

# validate

net。eval() #測試過程中不需要dropout,使用所有的神經元

acc = 0。0# accumulate accurate number / epoch

with torch。no_grad():

for val_data in validate_loader:

val_images, val_labels = val_data

outputs = net(val_images。to(device))

predict_y = torch。max(outputs, dim=1)[1]

acc += (predict_y == val_labels。to(device))。sum()。item()

val_accurate = acc / val_num

if val_accurate > best_acc:

best_acc = val_accurate

torch。save(net。state_dict(), save_path)

print(‘[epoch %d] train_loss: %。3f test_accuracy: %。3f’ %

(epoch + 1, running_loss / step, val_accurate))

print(‘Finished Training’)

AlexNet——CNN經典網路模型詳解(pytorch實現)

最後進行預測

predict。py

import torch

from model import AlexNet

from PIL import Image

from torchvision import transforms

import matplotlib。pyplot as plt

import json

data_transform = transforms。Compose(

[transforms。Resize((224, 224)),

transforms。ToTensor(),

transforms。Normalize((0。5, 0。5, 0。5), (0。5, 0。5, 0。5))])

# load image

img = Image。open(“。/sunflower。jpg”) #驗證太陽花

#img = Image。open(“。/roses。jpg”) #驗證玫瑰花

plt。imshow(img)

# [N, C, H, W]

img = data_transform(img)

# expand batch dimension

img = torch。unsqueeze(img, dim=0)

# read class_indict

try:

json_file = open(‘。/class_indices。json’, ‘r’)

class_indict = json。load(json_file)

except Exception as e:

print(e)

exit(-1)

# create model

model = AlexNet(num_classes=5)

# load model weights

model_weight_path = “。/AlexNet。pth”

model。load_state_dict(torch。load(model_weight_path))

model。eval()

with torch。no_grad():

# predict class

output = torch。squeeze(model(img))

predict = torch。softmax(output, dim=0)

predict_cla = torch。argmax(predict)。numpy()

print(class_indict[str(predict_cla)], predict[predict_cla]。item())

plt。show()

AlexNet——CNN經典網路模型詳解(pytorch實現)