1. 程式人生 > 其它 >從零開始寫模型LeNet

從零開始寫模型LeNet

技術標籤:深度學習

個人部落格同步更新,介面更美觀呀

Introduction

近來安裝了MindSpore框架,便想測試一下它和Pytorch等其他深度學習框架有什麼區別。首先便從模型的訓練耗時和測試耗時來測試,MindSpore官方提供了使用LeNet解決MNIST手寫數字識別的Demo,此時還需要一個用Pytorch編寫的作為對比,為了使引數、模型等各方面儘可能一致,便有了照貓畫虎自己編寫的需求。

Coding

為了能總覽模型編寫訓練流程,我決定從__main__函式入手,用到什麼寫什麼。

parser

首先要用的就是一個命令列程式碼的解析工具,使用方法概括如下:

# 導包
import
argparse # 定義一個解析器 parser = argparse.ArgumentParser(description="") # 給解析器新增需要從命令列讀入的引數 parser.add_argument('--device_target', type=str, default="CPU", choices=['GPU', 'CPU']) # 呼叫解析函式生成引數物件 args = parser.parse_args()

dataset

準備資料集,最好單獨封裝為一個處理函式,提供路徑載入臨時下載兩種方式。

super parameter

超引數的設定,其實也可以放到第一步命令列引數解析裡面。

模型的定義

__main__函式中例項化一個模型:

model = LeNet()

在前面預先定義模型類:

class LeNet(nn.Module):
    """lenet network structure."""
    def __init__(self, num_channel=1, num_class=10):
        self.conv1 = nn.Conv2d(in_channels=num_channel, out_channels=6, 
            kernel_size=
5, stride=1, padding=2) self.conv2 = nn.Conv2d(in_channels=6, out_channels=16, kernel_size=5) self.fc1 = nn.Linear(16*5*5, 120) self.fc2 = nn.Linear(120, 84) self.fc3 = nn.Linear(84, num_class) self.relu = nn.ReLU() self.max_pool2d = nn.MaxPool2d(2, 2) def forward(self, x): x = self.max_pool2d(self.relu(self.conv1(x))) x = self.max_pool2d(self.relu(self.conv2(x))) # 展開為一維 x = x.view(x.size()[0], -1) x = self.relu(self.fc1(x)) x = self.relu(self.fc2(x)) x = self.fc3(x) return x

模型的訓練

訓練的過程就是送入資料執行計算過程,得到輸出結果,與標準值進行誤差計算,將誤差進行梯度反向傳播更新模型引數。然後重複此過程,訓練結束條件可以為設定的epoch次數,或者給定一個收斂條件(誤差的縮小程度已經不值得繼續訓練了)。

**損失函式:**此例採用交叉熵損失函式,多用於分類問題。

loss_function = nn.CrossEntropyLoss()

**優化器:**即計算得到的損失以怎樣的方式作用到模型引數上,此例採用隨機梯度下降(SGD)。

optimizer = optim.SGD(model.parameters(), lr=lr, momentum=momentum)

訓練迴圈:

第一層迴圈一般為epoch維度的迴圈,簡單說就是在這個資料集上訓練幾次,epoch=2即利用這個資料集進行兩次訓練過程。第二層迴圈就是在batch上的訓練了,對所有資料分批進行前向計算和反向傳播。

訓練體一般有如下流程:

  1. 對得到的資料x標籤label進行device上的轉換,比如使用了GPU。
  2. 前向計算:pred=model(x)
  3. 計算損失(看看算的對不對):loss=loss_function(pred, label)
  4. 反向傳播(不對的話如何改正):先要對梯度進行一個清零操作optimizer.zero_grad(),然後進行反向傳播loss.backward(),執行一個優化步optimizer.step()。

至此一個訓練過程就結束了,有需要的話可以在不同邏輯層進行損失準確率等引數的列印輸出,方便監測訓練狀況。

for epoch in range(1, epoch_size+1):
        # data loop
        for batch_idx, (img, label) in enumerate(train_loader):
            img = img.to(device)
            label = label.to(device)
            img, label = Variable(img), Variable(label)
            # 前向傳播
            pred = model(img)
            # 計算損失
            loss = loss_function(pred, label)
            # 梯度清零
            optimizer.zero_grad()
            # 誤差反向傳播
            loss.backward()
            # 優化步
            optimizer.step()
            # 為了和MindSpore版本保持一致 每次都列印一遍
            print("epoch: {} step: {}, loss id {}".format(epoch, batch_idx, loss.item()))

模型的測試

測試過程即對一些樣本逐個進行預測並記錄是否正確,最終輸出準確率、召回率等引數指標。

eval_acc = 0
    for img, label in test_loader:
        img = img.to(device)
        label = label.to(device)
        img, label = Variable(img), Variable(label)
        # 預測
        output = model(img)
        _, pred = torch.max(output, 1)
        num_cortect = (pred == label).sum()
        eval_acc += num_cortect.item()
acc = eval_acc / (len(test_loader.dataset))

Experiment

對於添加了argparse的訓練指令碼來說,執行命令一般為:

python lenet.py --device_target=GPU --other=otherValue

需要通過命令列傳入的引數以如上方式進行匹配賦值。

執行無誤的話就能得到預期的結果反饋,有錯誤就根據提示Debug啦。

執行命令一般為:

python lenet.py --device_target=GPU --other=otherValue

需要通過命令列傳入的引數以如上方式進行匹配賦值。

執行無誤的話就能得到預期的結果反饋,有錯誤就根據提示Debug啦。

image-20210102181126480