基於pytorch實現Resnet對本地資料集的訓練

眼前有座山發表於2022-03-19

       本文是使用pycharm下的pytorch框架編寫一個訓練本地資料集的Resnet深度學習模型,其一共有兩百行程式碼左右,分成mian.py、network.py、dataset.py以及train.py檔案,功能是對本地的資料集進行分類。本文介紹邏輯是總分形式,即首先對總流程進行一個概括,然後分別介紹每個流程中的實現過程(程式碼+流程圖+文字的介紹)。

       對於整個專案的流程首先是載入本地資料集,然後匯入Resnet網路,最後進行網路訓練。整體來說一個完整的小專案,難度並不高,需要有一定的pytorch語句以及深度學習的基礎。

       mian.py檔案是該專案的總檔案,也是訓練網路模型的執行檔案,文字的介紹流程是隨著該檔案一 一對程式碼進行介紹。

  main.py程式碼如下所示:

from dataset import data_dataloader    #電腦本地寫的讀取資料的函式
from torch import nn                   #匯入pytorch的nn模組
from torch import optim                #匯入pytorch的optim模組
from network import Res_net            #電腦本地寫的網路框架的函式
from train import train                #電腦本地寫的訓練函式

def main():
    # 以下是通過Data_dataloader函式輸入為:資料的路徑,資料模式,資料大小,batch的大小,有幾線並用 (把dataset和Dataloader功能合在了一起)
    train_loader = data_dataloader(data_path='./data', mode='train', size=64, batch_size=24, num_workers=4)
    val_loader = data_dataloader(data_path='./data', mode='val', size=64, batch_size=24, num_workers=2)
    test_loader = data_dataloader(data_path='./data', mode='test', size=64, batch_size=24, num_workers=2)

    # 以下是超引數的定義
    lr = 1e-4           #學習率
    epochs = 10         #訓練輪次

    model = Res_net(2)  # resnet網路
    optimizer = optim.Adam(model.parameters(), lr=lr)  # 優化器
    loss_function = nn.CrossEntropyLoss()  # 損失函式

    # 訓練以及驗證測試函式
    train(model=model, optimizer=optimizer, loss_function=loss_function, train_data=train_loader, val_data=val_loader,test_data= test_loader, epochs=epochs)

if __name__ == '__main__':
    main()

  main.py流程圖如圖1所示:

圖 1 main.py 程式碼流程圖

1.dataset.py(先看程式碼的總體流程再看介紹)

       main.py()前五行分別是匯入相應的模組,其中dataset,network以及train是本地編寫的檔案。在mian()函式中的前幾行程式碼中,我們使用dataset.py檔案中的Data_dataloader函式匯入訓練集、驗證集和測試集。Dataset檔案是匯入我們自己的本地資料庫,其功能是得到所有的資料,將其變成pytorch能夠識別的tensor資料,然後得到圖片。

       dataset.py檔案程式碼如下所示:

import torch
import os,glob
import random
import csv
from torch.utils.data import Dataset
from PIL import Image
from torchvision import transforms
from torch.utils.data import DataLoader

# 第一部分:通過三個步驟得到輸出的tensor型別的資料
class Dataset_self(Dataset):                    #如果是nn.moduel 則是編寫網路模型框架,這裡需要繼承的是dataset的資料,所以括號中的是Dataset
    #第一步:初始化
    def __init__(self,root,mode,resize,):       #root是檔案根目錄,mode是選擇什麼樣的資料集,resize是影像重新調整大小
        super(Dataset_self, self).__init__()
        self.resize = resize
        self.root = root
        self.name_label = {}       #建立一個字典來儲存每個檔案的標籤
        #首先得到標籤相對於的字典(標籤和名稱一一對應)
        for name in sorted(os.listdir(os.path.join(root))):     #排序並且用列表的形式開啟資料夾
            if not os.path.isdir(os.path.join(root,name)):      #不是資料夾就不需要讀取
                continue
            self.name_label[name] = len(self.name_label.keys())  #每個檔案的名字為name_Label字典中有多少對鍵值對的個數
        #print(self.name_label)
        self.image,self.label = self.make_csv('images.csv')       #編寫一共函式來讀取圖片和標籤的路徑
        #在得到image和label的基礎上對圖片資料進行一共劃分  (注意:如果需要交叉驗證就不需要驗證集,只劃分為訓練集和測試集)
        if mode == 'train':
            self.image ,self.label= self.image[:int(0.6*len(self.image))],self.label[:int(0.6*len(self.label))]
        if mode == 'val':
            self.image ,self.label= self.image[int(0.6*len(self.image)):int(0.8*len(self.image))],self.label[int(0.6*len(self.label)):int(0.8*len(self.label))]
        if mode == 'test':
            self.image ,self.label= self.image[int(0.8*len(self.image)):],self.label[int(0.8*len(self.label)):]
    # 獲得圖片和標籤的函式
    def make_csv(self,filename):
        if not os.path.exists(os.path.join(self.root,filename)):  #如果不存在彙總的目錄就新建一個
            images = []
            for image in self.name_label.keys():                            # 讓image到name_label中的每個檔案中去讀取圖片
                images += glob.glob(os.path.join(self.root,image,'*jpg'))   #加* 貪婪搜尋關於jpg的所有檔案
            #print('長度為:{},第二張圖片為:{}'.format(len(images),images[1]))
            random.shuffle(images)                                         #把images列表中的資料洗牌
            # images[0]: ./data\ants\382971067_0bfd33afe0.jpg
            with open(os.path.join(self.root,filename),mode='w',newline='') as f :  #建立檔案
                writer = csv.writer(f)
                for image in images:
                    name = image.split(os.sep)[-2]  #得到與圖片相對應的標籤
                    label = self.name_label[name]
                    writer.writerow([image,label])  #寫入檔案  第一行:./data\ants\382971067_0bfd33afe0.jpg,0
        images,labels = [],[]
        with open(os.path.join(self.root,filename)) as f:   #讀取檔案
            reader = csv.reader(f)
            for row in reader:
                image, label = row
                label = int(label)
                images.append(image)
                labels.append(label)
        assert len(images) == len(labels)   #類似if語句,只有兩者長度一致才繼續執行,否則報錯
        return images,labels                #返回所有!!是所有的圖片和標籤(此處的圖片不是圖片資料本身,而是它的檔案目錄)

    #第二步:得到圖片資料的長度(標籤資料長度與圖片一致)
    def __len__(self):
        return len(self.image)

    #第三步:讀取圖片和標籤,並輸出
    def __getitem__(self, item):   # 單張返回張量的影像與標籤
        image,label = self.image[item],self.label[item]      #得到單張圖片和相應的標籤(此處都是image都是檔案目錄)
        image = Image.open(image).convert('RGB')             #得到圖片資料
        #使用transform對圖片進行處理以及變成tensor型別資料
        transf = transforms.Compose([transforms.Resize((int(self.resize),int(self.resize))),
                                     transforms.RandomRotation(15),
                                     transforms.CenterCrop(self.resize),
                                     transforms.ToTensor(),  #先變成tensor型別資料,然後在進行下面的標準化
                                     ])
        image = transf(image)
        label = torch.tensor(label)   #把圖片標籤也變成tensor型別
        return image,label

#第二部分:使用pytorch自帶的DataLoader函式批量得到圖片資料
def data_dataloader(data_path,mode,size,batch_size,num_workers):   #用一個函式載入上訴的資料,data_path、mode和size分別是以上定義的Dataset_self()中的引數,batch_size是一次性輸出多少張影像,num_worker是同時處理幾張影像
    dataset = Dataset_self(data_path,mode,size)
    dataloader = DataLoader(dataset,batch_size,num_workers)  #使用pytorch中的dataloader函式得到資料

    return dataloader

#測試
def main():
    test = Dataset_self('./data','train',64)

if __name__ == '__main__':
    main()

       dataset.py流程圖2所示:

圖2 dataset.py流程圖

       如以上程式碼所示,使用pytorch載入自定義的資料集時,需要定義一個dataset的物件,然後定義一個dataloaber的物件,最後對dataloaber反覆得到訓練資料和標籤。所以本檔案主要分為兩個部分:自定義的dataset部分和使用pytorch中dataloaber來得到訓練資料的部分。

       程式碼首先是匯入必要的python庫,然後編寫第一部分。第一部分主要是通過三個步驟來得到單張輸出的tensor型別圖片和標籤。

       三個步驟分別是:初始化、獲得資料的長度以及讀取資料和標籤。其中初始化是為了得到一個檔案,檔案中儲存所有圖片相對應的目錄以及其標籤,再將得到的檔案讀出分為訓練集、驗證集和測試集。具體實現如上述程式碼所示,首先在初始化的函式中定義變數resize、root和name_label,方便與後面的函式呼叫:

圖3 Dataset_self中引數的初始化

  然後,我們編寫程式碼讀取根目錄,得到分類名字及其相對應的標籤:

圖4 標籤的獲得

  程式碼中,首先使用os庫來把根目錄內的檔案變成列表被讀取出來,然後把根目錄內所有檔名儲存在name_label字典中,在分別依照儲存進字典的個數來給標籤數值化。(第一個讀取進字典的標籤就是0,第二個是1,其餘檔案以此類推)

       得到標籤字典後,我們編寫一個函式來獲得所有圖片的目錄,便於下面步驟的圖片讀取:

圖5 圖片和標籤的讀取

  編寫make_csv函式,來得到image和label(image是每張圖片的目錄,label是相對應的標籤)。

       make_csv函式中,首先判斷是否以及存在我們需要的檔案,如果存在則直接讀取,如果不存在就先生成一個儲存所有圖片目錄和標籤的檔案。

圖6 make_csv函式

       當檔案不存在時(第一行語句的判斷),我們編寫檔案的思路是先編寫一個列表來儲存所有的圖片目錄,然後再建立檔案使用csv庫把列表資料寫入檔案中。所以在判斷語句下面,我們得到一個空的images列表,然後遍歷name_label中的keys,對於name_label來說,它是一個key是檔名,value是標籤(數值)的字典,因為是用os庫把檔案讀取成為字典的,所以遍歷字典內的key時,是讀取的是相對應的檔案。所以上圖第四行程式碼中是分別讀取檔案中的圖片,然後使用glob庫分別把所有jpg檔案儲存到images列表裡面。在列表中images[0]是:./data\ants\382971067_0bfd33afe0.jpg

       在得到圖片目錄列表後,首先將列表內的資料隨機排列,然後創造一個檔案,在列表images中的目錄得到標籤名稱,用name_label得到標籤名稱相對應的數值,最後寫入檔案中。檔案第一行是:./data\ants\382971067_0bfd33afe0.jpg,0 (圖片相對目錄和相對於的標籤)

       得到檔案後,因為我們需要的是每張圖片的目錄而不是檔案(主要是為了後面反覆除錯,所以得到一個檔案做中轉站),所以我們需要用兩個列表來得到圖片目錄和相對應的標籤值,最後分別把檔案中的資料寫入列表中,得到圖片和標籤列表。

       至此,我們就能通過函式make_csv來得到image和label。得到這兩個列表後,我們對其進行切割,因為列表裡面是儲存的所以資料,所以我們需要分割為訓練集、驗證集和測試集。程式碼很簡單,(如果需要交叉驗證則只需要劃分出訓練集和測試集即可)如下圖所示:

圖7 資料集的劃分

  以上是第一步初始化的過程,第二步讀取影像長度:

圖8 讀取影像長度

  很簡單,一個len()函式就搞定,其主要功能是知道一共有多少資料。

       第三步:讀取資料和標籤,讀取資料是一張一張來讀取的,所以首先從image和label列表中得到單個資料,因為image列表中儲存的是圖片的目錄,所以先讀取RGB格式的圖片,然後使用transform對圖片進行相應的處理(尺寸,圖片變化,變成tensor型別等),最後也將label變成tensor型別然後把圖片資料和標籤資料返回即可,程式碼如下圖所示:

圖8 讀取影像和標籤

       第一部分是讀取圖片和圖片相對應的標籤,流程是三步:初始化、得到資料長度和讀取單張資料,對於pytorch的dataset處理都是基於這三步。其中演算法邏輯並不複雜,主要是需要使用的語句有點多,需要仔細思考其中的邏輯。

       第二部分相對於第一部分要簡單很多,甚至可以把這部分放到main()函式中執行。其主要內容是通過第一部分得到的dataset_self來得到資料,然後使用pytorch自帶的dataloader得到放入模型中訓練的資料集,程式碼如下圖所示:

圖9 資料集的獲取

       Dataset部分其功能簡單概括就是將本地資料集中的圖片和標籤變成tensor型別資料讀取為需要使用的資料集。

2.network.py

       main.py()中,我們定義了一些超引數等,分別有學習率,訓練輪次,訓練模型,優化器以及損失函式。對於訓練模型,本文使用的是本地編寫的一個小型的Resnet模型。其程式碼如下所示:

import torch
from torch import nn

# 先寫好resnet的block塊
class Res_block(nn.Module):
    def __init__(self,in_num,out_num,stride):
        super(Res_block, self).__init__()
        self.cov1 = nn.Conv2d(in_num,out_num,(3,3),stride=stride,padding=1)    #(3,3)  padding=1 則影像大小不變,stride為幾影像就縮小几倍,能極大減少引數
        self.bn1 = nn.BatchNorm2d(out_num)
        self.cov2 = nn.Conv2d(out_num,out_num,(3,3),padding=1)
        self.bn2 = nn.BatchNorm2d(out_num)

        self.extra = nn.Sequential(
                nn.Conv2d(in_num,out_num,(1,1),stride=stride),
                nn.BatchNorm2d(out_num)
            )   #使得輸入前後的影像資料大小是一致的
        self.relu = nn.ReLU()
    def forward(self,x):
        out = self.relu(self.bn1(self.cov1(x)))
        out = self.relu(self.bn2(self.cov2(out)))

        out = self.extra(x) + out
        return out

class Res_net(nn.Module):
    def __init__(self,num_class):
        super(Res_net, self).__init__()
        self.init = nn.Sequential(
            nn.Conv2d(3,16,(3,3)),
            nn.BatchNorm2d(16)
        )   #預處理
        self.bn1 = Res_block(16,32,2)
        self.bn2 = Res_block(32,64,2)
        self.bn3 = Res_block(64,128,2)
        self.bn4 = Res_block(128,256,2)
        self.fl = nn.Flatten()
        self.linear1 = nn.Linear(8192,10)
        self.linear2 = nn.Linear(10,num_class)
        self.relu = nn.ReLU()
    def forward(self,x):
        out = self.relu(self.init(x))
        #print('inint:',out.shape)
        out = self.bn1(out)
        #print('bn1:', out.shape)
        out = self.bn2(out)
        #print('bn2:', out.shape)
        out = self.bn3(out)
        #print('bn3:', out.shape)
        out = self.fl(out)
        #print('flatten:', out.shape)
        out = self.relu(self.linear1(out))
        #print('linear1:', out.shape)
        out = self.relu(self.linear2(out))
        #print('linear2:', out.shape)
        return out


#測試
def main():
    x = torch.randn(2,3,64,64)
    net = Res_net(2)
    out = net(x)
    print(out.shape)


if __name__ == '__main__':
    main()

  network.py流程圖如圖10所示:

圖10 network.py流程圖

       Resnet模型網路主要是兩部分,首先編寫resnet中的每個殘差塊,然後編寫整個網路。在開始介紹程式碼之前,首先用我的理解來介紹一下Resnet,也就是殘差網路的思想與邏輯(具體可以搜尋其他資料檢視)。殘差網路其主要的目的是能夠訓練一個深層次的網路,希望是隨著網路的加深,效果越來越好。但是因為網路加深,很有可能一些引數會得不到訓練(一次次的迭代,使得梯度消失),所有Resnet網路巧妙的運用了一個殘差塊來解決因為網路模型太深而導致其梯度消失的問題,如圖11所示:

 

圖11  殘差塊

       簡單來說就是在x通過兩個層後,在和x本身相加,如此在反向傳播的過程中,f(x)+x求帶就變成如此就在回傳給x上面的隱藏層的時候就不會發生梯度消失(至少有個1)。如果在x輸入殘差塊前有n層,那麼就算殘差快內的隱藏層因為梯度消失的問題而沒有訓練好,但是至少x輸入之前的n層是訓練好了的,這樣只要殘差快中的隱藏層能訓練好一部分,神經網路的準確度就很有可能在原來基礎上增加。(還是得好好研究,這裡Resnet的解釋可能並沒有那麼準確)

       基於上述殘差塊的圖片,我們先定義好殘差塊,程式碼如下圖12所示:

圖12 殘差塊的定義

   其流程圖如圖13:

圖13 殘差塊定義流程圖

        當殘差塊寫好後,就可以編寫一個簡單的Resnet網路,程式碼如圖14所示:

 圖14 簡單Resnet網路模型

       上述程式碼中,首先通過一層正常的卷積層後,再通過3個殘差塊,最後通過兩層線性層,程式碼十分比較簡單。在定義好殘差塊之後,呼叫pytorch本身自帶的函式即可完成。唯一需要注意的地方是引數的設定,該網路一般來說都是維度在慢慢增加,影像的尺寸慢慢減少。

       3.train.py

       train.py是整個模型的訓練過程,本文將其打包成為一個函式,然後在mian.py中呼叫,因為基本上網路的訓練過程都大同小異,一般都是用訓練集訓練,在驗證集上得到最好的輪次,最後儲存網路引數並且在測試集上檢測,所以這裡直接將訓練過程和驗證過程打包成為函式,便於以後專案的直接呼叫。

       train.py程式碼如下所示:


import torch
from torch import optim
from torch.utils.data import DataLoader
from dataset import Dataset_self
from network import Res_net
from torch import nn
from matplotlib import pyplot as plt
import numpy as np

def evaluate(model,loader): #計算每次訓練後的準確率
correct = 0
total = len(loader.dataset)
for x,y in loader:
logits = model(x)
pred = logits.argmax(dim=1) #得到logits中分類值(要麼是[1,0]要麼是[0,1]表示分成兩個類別)
correct += torch.eq(pred,y).sum().float().item() #用logits和標籤label想比較得到分類正確的個數
return correct/total

#把訓練的過程定義為一個函式
def train(model,optimizer,loss_function,train_data,val_data,test_data,epochs): #輸入:網路架構,優化器,損失函式,訓練集,驗證集,測試集,輪次
best_acc,best_epoch =0,0 #輸出驗證集中準確率最高的輪次和準確率
train_list,val_List = [],[] # 建立列表儲存每一次的acc,用來最後的畫圖
for epoch in range(epochs):
print('============第{}輪============'.format(epoch + 1))
for steps,(x,y) in enumerate(train_data): # for x,y in train_data
logits = model(x) #資料放入網路中
loss = loss_function(logits,y) #得到損失值
optimizer.zero_grad() #優化器先清零,不然會疊加上次的數值
loss.backward() #後向傳播
optimizer.step()
train_acc =evaluate(model,train_data)
train_list.append(train_acc)
print('train_acc',train_acc)
#if epoch % 1 == 2: #這裡可以設定每兩次訓練驗證一次
val_acc = evaluate(model,val_data)
print('val_acc=',val_acc)
val_List.append((val_acc))
if val_acc > best_acc: #判斷每次在驗證集上的準確率是否為最大
best_epoch = epoch
best_acc = val_acc
torch.save(model.state_dict(),'best.mdl') #儲存驗證集上最大的準確率
print('===========================分割線===========================')
print('best acc:',best_acc,'best_epoch:',best_epoch)
#在測試集上檢測訓練好後模型的準確率
model.load_state_dict((torch.load('best.mdl')))
print('detect the test data!')
test_acc = evaluate(model,test_data)
print('test_acc:',test_acc)
train_list_file = np.array(train_list)
np.save('train_list.npy',train_list_file)
val_list_file = np.array(val_List)
np.save('val_list.npy',val_list_file)

#畫圖
x_label = range(1,len(val_List)+1)
plt.plot(x_label,train_list,'bo',label='train acc')
plt.plot(x_label,val_List,'b',label='validation acc')
plt.title('train and validation accuracy')
plt.xlabel('epochs')
plt.legend()
plt.show()

#測試
def main():
train_dataset = Dataset_self('./data', 'train', 64)
vali_dataset = Dataset_self('./data', 'val', 64)
test_dataset = Dataset_self('./data', 'test', 64)

train_loaber = DataLoader(train_dataset, 24, num_workers=4)
val_loaber = DataLoader(vali_dataset, 24, num_workers=2)
test_loaber = DataLoader(test_dataset, 24, num_workers=2)

lr = 1e-4
epochs = 5
model = Res_net(2)
optimizer = optim.Adam(model.parameters(), lr=lr)
criteon = nn.CrossEntropyLoss()

train(model,optimizer,criteon,train_loaber,val_loaber,test_loaber,epochs)

if __name__ == '__main__':
main()
 

  train.py流程圖如圖15所示:

圖15 train.py流程圖

       上述程式碼中,第一個函式的定義是為了得到一次訓練(或者驗證或者測試)後的準確率,也就是跑完一次所有訓練集後,模型的準確率是多少。其程式碼內容並不複雜,先得到經過模型logits中的分類標籤(是[1,0]還是[0,1],表示分成兩類)pred,然後用logits與標籤進行比較,從而得到一個batch_size中分類正確的個數,然後累加起來,得到一次訓練中網路對資料集分類正確的個數(correct),最後讓其除以資料集的個數從而得到準確率並且返回其數值。

       對於第二個函式,train的函式的定義,其主要內容是在訓練集上訓練,每一輪次訓練好之後放在驗證集上驗證(可以是每兩次或者三次),執行完所有輪次後,儲存在驗證集上最好的一次的網路引數與輪次,最後載入儲存的網路引數對測試集進行檢測。

       train函式內部首先定義驗證集中最好的準確率和最好的輪次,然後建立兩個列表來儲存每一次的訓練集和驗證集的準確率(用來畫圖檢視),然後就是進行epochs次訓練。

 圖16 trian函式內引數的定義

       訓練中,如果直接是用x,y來獲得資料的圖片和標籤則可以使用標註裡面的程式碼,而使用enumerate函式,其主要是為了給每次得到的資料(x,y)標上一個索引,這個索引是steps,從0開始(這裡沒有使用到steps引數)。在每次執行中,圖片資料x會被放入網路模型model中被處理,然後使用定義的loss_function函式得到預測和正確標籤之間的損失值。優化器先清零(不然會有數值疊加),然後讓損失值loss執行反向傳播操作(鏈式求導),最後優化器執行優化功能,如此便實現了模型的一次訓練與引數更新。

 圖17 模型的訓練步驟

       而後面的程式碼,每訓練一次網路模型,就把驗證集放入網路模型中,測試網路模型訓練得怎麼樣,然後儲存下epochs次數中最好準確率的網路模型引數與輪次。最後載入儲存下的網路模型引數,在測試集上檢測準確率如何。

 圖18 模型引數的儲存與測試

       最後幾句程式碼是將儲存下來的準確率做圖,有一點需要注意,因為這裡是每次訓練後都在驗證集上檢測過,所以座標軸的長度就用訓練集準確率的長度來表示兩個不同資料的長度。

 圖19 做圖

4.結果與總結

       本文專案是使用Resnet模型來識別螞蟻和蜜蜂,其一共有三百九十六張的資料,訓練集只有兩百多張(資料集很小),執行十輪後,分別對訓練集和測試集在每一輪的準確率如圖所示:

 

 圖20 train and validation accuracy

        測試集的準確率如圖所示:

 

 圖21 測試集準確率

       最後得到的效果不理想,很大可能是資料集太少導致導致模型泛化能力變弱(模型把訓練集都記下來了),對於這樣的問題可以嘗試通過交叉驗證(效果可能有一定程度的提升)或者增加資料集的方法來增強模型的泛化能力。對精度的提升,會在後續的文章中進行討論。

  在得到模型引數後,我隨便在網上找了兩張螞蟻的圖片放進模型檢測看效果如何:

 圖22 第一次測試

 

 圖23 第二次測試

  第一次測試識別出來了是螞蟻,但是第二次就失敗了,有可能是模型沒有看到過黑色的蜜蜂所以把黑色的都當成了螞蟻吧,總之改模型還有很多需要改進的地方。

  附上單張檢測的程式碼:

from network import Res_net
import torch
from PIL import Image
import torchvision

#匯入圖片
img = '1.jpg'
img =Image.open(img)
tf = torchvision.transforms.Compose([torchvision.transforms.Resize((64,64)),torchvision.transforms.ToTensor()])
img = tf(img)
image = torch.reshape(img,(1,3,64,64))
#載入模型
net = Res_net(2)
net.load_state_dict(torch.load('best.mdl'))
with torch.no_grad():
    out = net(image)
#確定分類
class_cl =out.argmax(dim=1)
class_num = class_cl.numpy()

if class_num == 0:
    print('這張照片是螞蟻')
else:
    print('這張照片是蜜蜂')

       總的來說,整篇文章對於有pytorch以及深度學習基礎的人來說是偏向於簡單的,除了dataset.py中可能有一些小問題,而其中的問題也並非與深度學習有關,主要是演算法思維上的問題(即如何用程式碼來實現資料的匯入過程)而其他部分則是pytorch編寫深度學習演算法的常規操作。而其中的框架還是有很多可以改善的內容,比如模型的改善,做圖的改善等等。模型最後執行得到的結果並不理想,原因可能是資料集太少,用於訓練的圖片僅三百張左右,在這樣的情況下,要麼增加資料集,要麼可以使用交叉驗證的方法進行網路的精度提升(資料集太少了,網路把所有圖片都塊記住了,所以訓練時的準確率很高但是驗證集和測試集準確率卻不理想,改進的內容留在下次研究介紹),也有模型比較簡單執行輪次太少的緣故,總之其中還是有很多地方需要去研究考慮。

       縱橫整篇文章,其實主要思想還是如本人其他文章裡面的思想一樣,先是處理好資料集,然後搭建網路,最後訓練,編譯等。以我的薄見,以小見大,或許在深度學習中對於一些大的專案或者複雜的專案其本質也是逃不過這幾點,但是其分支,其問題,其模組會有很多複雜的考慮。這就關乎於問題中的演算法思維了,在後續中,本人可能會把這個小專案做得有深度一些,比如說對於正常物品和損壞物品之間的分類,當然,這樣的話對問題的考慮就會多了很多,對其資料集的處理以及模型的框架可能會複雜很多。

       對於深度學習也好,寫程式碼也好,如果只是簡單的寫,很難對自己的能力有所提升,關鍵還是在於如何把問題演算法化(即用程式碼高效的解決一個問題),所以演算法還是得好好學啊。(還有,一定要動手實踐)

       至此,一個Resnet網路訓練本地資料集的小專案就全部介紹完畢了,專案雖然簡單了一些,但是麻雀雖小五臟俱全啊!

       最後告誡自己以及所有小夥伴們:路漫漫其修遠兮,吾將上下而求索。

相關文章