長(zhǎng)文預(yù)警【深度學(xué)習(xí)】基于 Pytorch 的網(wǎng)絡(luò)訓(xùn)練

我是 雪天魚(yú)袍榆,一名FPGA愛(ài)好者印蓖,研究方向是FPGA架構(gòu)探索和數(shù)字IC設(shè)計(jì)碘箍。

關(guān)注公眾號(hào)【集成電路設(shè)計(jì)教程】,獲取更多學(xué)習(xí)資料鲸郊,并拉你進(jìn)“IC設(shè)計(jì)交流群”丰榴。
QQIC設(shè)計(jì)&FPGA&DL交流群 群號(hào):866169462

一秆撮、數(shù)學(xué)基礎(chǔ):標(biāo)量四濒,向量,矩陣與張量

enter description here

一個(gè)標(biāo)量(Scalar)就是一個(gè)單獨(dú)的數(shù)职辨;
一個(gè)向量就是一列數(shù)盗蟆,這些數(shù)是有序排列的。通過(guò)索引舒裤,喳资、可以確定對(duì)應(yīng)的每個(gè)單獨(dú)的數(shù);
矩陣是二維數(shù)組腾供,其中的每一個(gè)元素被兩個(gè)索引而非一個(gè)所確定仆邓。
幾何代數(shù)中定義的張量是基于向量和矩陣的推廣,通俗一點(diǎn)理解的話伴鳖,可以將標(biāo)量視為零階張量节值,向量視為一階張量,那么矩陣就是二階張量榜聂,任意一張彩色圖片表示成一個(gè)三階張量搞疗,三個(gè)維度分別是圖片的高度、寬度和色彩數(shù)據(jù)须肆。所以Tensor一般指三階及更高階的張量匿乃。

二、自動(dòng)求導(dǎo)

x = torch.tensor([2.0], requires_grad = True)
a = torch.tensor([4.0], requires_grad = True)
y = x * a

# 求計(jì)算圖各節(jié)點(diǎn)導(dǎo)數(shù)
y.backward()    

print(x.grad)   # y 對(duì) x 的偏導(dǎo) -> a
print(a.grad)   # y 對(duì) a 的偏導(dǎo) -> x

結(jié)果:

enter description here

pytorch 自動(dòng)求導(dǎo)方式很簡(jiǎn)單豌汇,定義一個(gè)表達(dá)式 y幢炸,然后 y 由變量(一般為標(biāo)量)x1,x2瘤礁,x3...計(jì)算得到阳懂,那么此時(shí)調(diào)用 y.backward() 進(jìn)行反向傳播,對(duì)各變量計(jì)算梯度(即偏導(dǎo)數(shù))柜思,然后這些梯度會(huì)保存在變量的 grad 屬性中岩调。
再舉個(gè)例子,如下所示赡盘,對(duì) y=x**4 求導(dǎo)

enter description here

三号枕、線性回歸與擬合

enter description here

在上圖中有很多藍(lán)點(diǎn),也就是數(shù)據(jù)陨享,我們所要做的是找出一條直線葱淳,能盡可能多的經(jīng)過(guò)這些數(shù)據(jù)點(diǎn)钝腺,也就是達(dá)到盡可能好的擬合效果,求出斜率W截距b赞厕。
可采用迭代法艳狐,即先隨機(jī)初始化一個(gè) W 和 b,然后設(shè)置一個(gè) loss函數(shù)(即能衡量擬合效果優(yōu)劣的指標(biāo))皿桑,然后根據(jù) loss 去修改 W 和 b毫目,目標(biāo)是使 loss 盡可能的小,即擬合效果盡可能的好诲侮。示意圖如下所示:

enter description here

對(duì)于線性回歸而言镀虐,這個(gè)衡量指標(biāo)為:

enter description here

即預(yù)測(cè)值與真實(shí)值之間差值的均方差,每次調(diào)整 w 和 b 沟绪,都是為了使此 loss 更小刮便,所以就可以轉(zhuǎn)化為優(yōu)化問(wèn)題:

enter description here

那么具體怎么調(diào)整 w,b呢绽慈?即在當(dāng)前位置恨旱,w是變大還是變小,b是變大還是變小久信。
解決方法就是通過(guò)梯度(偏導(dǎo)數(shù))來(lái)判斷增大還是減小所要調(diào)整的參數(shù)窖杀,以 w 為例漓摩,當(dāng) loss 對(duì) w 的偏導(dǎo)數(shù)裙士,即梯度為正表示該處 loss 隨 w 增大而增大,故減小 w管毙,f反之梯度為負(fù)表示該處 loss 隨 w 增大而增大減小腿椎,故增大 w。
而具體增大多少夭咬,減小多少則由學(xué)習(xí)率(learning rate)梯度共同決定啃炸,學(xué)習(xí)率越大,收斂越快卓舵,這很容易理解南用,因?yàn)橐苿?dòng)的步伐增大了嘛,但同時(shí)也可以錯(cuò)過(guò)最佳擬合點(diǎn)掏湾。
代碼示例:

x_train = torch.rand(100)  
y_train = x_train * 2 + 3  # 目標(biāo)曲線:w = 2, b = 3, y = 2 * x + 3
# (x_train,y_train)即為要擬合的數(shù)據(jù)

# 初始化 w 和 b
w = torch.tensor([0.0],requires_grad = True)
b = torch.tensor([0.0],requires_grad = True)

# 擬合
lr = 0.015 # 學(xué)習(xí)率
loss_func = torch.nn.MSELoss() # loss 函數(shù)裹虫,衡量擬合效果,越小越好
for i in range(200):
    y_pre = x_train * w + b # 預(yù)測(cè)值

    loss = loss_func(y_train, y_pre) # 輸入預(yù)測(cè)值與實(shí)際值融击,計(jì)算 loss
    if i % 10 == 0: # 每 10 輪輸出一次 w, b, loss
        print("Iter: %d, w: %.4f, b: %.4f, training loss: %.4f" % (i, w.item(), b.item(), loss.item()))
    loss.backward() # 反相傳播筑公,計(jì)算 loss 對(duì) w 和 b 的偏導(dǎo)數(shù)
    
    # 調(diào)整 w 和 b
    w.data -= w.grad * lr
    b.data -= b.grad * lr
    
    # 梯度清零
    w.grad.data.zero_()
    b.grad.data.zero_()

結(jié)果:

enter description here

從結(jié)果上來(lái)看,可以發(fā)現(xiàn) loss 越來(lái)越小尊浪,說(shuō)明擬合結(jié)果越來(lái)越好匣屡, w 和 b 也是分別越來(lái)越靠近 2 和 3封救,這就是迭代法。

四捣作、Pytorch 寫(xiě)法

寫(xiě)法總結(jié)為:

enter description here

示例代碼:

import torch 

# 1 定義 model誉结,給訓(xùn)練輸入,輸出對(duì)應(yīng)預(yù)測(cè)值
class SimpleLinear:
    def __init__(self):
        self.w = torch.tensor([0.0], requires_grad=True)
        self.b = torch.tensor([0.0], requires_grad=True)
    # 前向傳播獲得預(yù)測(cè)值
    def forward(self, x):
        y = self.w * x + self.b
        return y

    def parameters(self):
        return [self.w, self.b]

    def __call__(self, x):
        return self.forward(x)
    
# 2 定義優(yōu)化器
class Optimizer:
    def __init__(self, parameters, lr):
        self.parameters = parameters
        self.lr = lr
    
    # 更新參數(shù)
    def step(self):
        for para in self.parameters:
            para.data -= para.grad * self.lr
    # 清零參數(shù)的梯度
    def zero_grad(self):
        for para in self.parameters:
            para.grad.data.zero_()

# 3 訓(xùn)練
def train():
    model = SimpleLinear()
    opt = Optimizer(model.parameters(), lr=0.3)
    
    for epoch in range(10):
        output = model(x_train)
        loss = loss_func(y_train, output)
        loss.backward()
        opt.step()
        opt.zero_grad()
        print('Epoch {},w:{:.4f} b:{:.4f},loss is {:.4f}'.format(epoch,model.parameters()[0].item(),
                                                             model.parameters()[1].item(), loss.item()))

train()

結(jié)果:

enter description here

五券躁、實(shí)戰(zhàn)

MNIST 數(shù)據(jù)集為例搓彻,做手寫(xiě)數(shù)字識(shí)別網(wǎng)絡(luò)的訓(xùn)練與評(píng)估。
示例代碼:

import math
import torch
import torch.nn as nn
import torch.nn.functional as F
from torchvision import datasets, transforms
import torch.utils.data

# 定義 model 
class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        # torch.nn.Conv2d(in_channels, out_channels, kernel_size,
        # stride=1, padding=0, dilation=1, groups=1, bias=True, padding_mode='zeros')
        self.conv1 = nn.Conv2d(1, 32, 3, 1)
        self.conv2 = nn.Conv2d(32, 64, 3, 1)
        self.dropout1 = nn.Dropout2d(0.25)
        self.dropout2 = nn.Dropout2d(0.5)
        self.fc1 = nn.Linear(9216, 128)
        self.fc2 = nn.Linear(128, 10)

    def forward(self, x):
        x = self.conv1(x)
        x = F.relu(x)
        x = self.conv2(x)
        x = F.max_pool2d(x, 2)
        x = self.dropout1(x)
        x = torch.flatten(x, 1)
        x = self.fc1(x)
        x = F.relu(x)
        x = self.dropout2(x)
        x = self.fc2(x)
        output = F.log_softmax(x, dim=1)
        return output

def train(model, device, train_loader, optimizer, epoch):
    model.train()
    total = 0
    for batch_idx, (data, target) in enumerate(train_loader):
        # 獲取實(shí)際值嘱朽,可以簡(jiǎn)單理解: data -> x  target -> y
        data, target = data.to(device), target.to(device)
        optimizer.zero_grad() # 梯度清零
        output = model(data) # 獲得預(yù)測(cè)值
        loss = F.nll_loss(output, target) # 計(jì)算 loss
        loss.backward() # loss 反向傳播
        optimizer.step() # 更新參數(shù)
        
        # 訓(xùn)練進(jìn)度統(tǒng)計(jì)
        total += len(data) # 目前訓(xùn)練圖片數(shù)
        progress = math.ceil(batch_idx / len(train_loader) * 50)
        print("\rTrain epoch %d: %d/%d, [%-51s] %d%%" %
              (epoch, total, len(train_loader.dataset),
               '-' * progress + '>', progress * 2), end='')
        
def test(model, device, test_loader):
    model.eval()
    test_loss = 0
    correct = 0
    with torch.no_grad():
        for data, target in test_loader:
            # 獲取實(shí)際值
            data, target = data.to(device), target.to(device)
            # 獲取預(yù)測(cè)值
            output = model(data)
            # 計(jì)算 loss
            test_loss += F.nll_loss(output, target, reduction='sum').item()  # sum up batch loss
            pred = output.argmax(dim=1, keepdim=True)  # get the index of the max log-probability
            correct += pred.eq(target.view_as(pred)).sum().item()

    test_loss /= len(test_loader.dataset)
    
    print('\nTest: average loss: {:.4f}, accuracy: {}/{} ({:.0f}%)'.format(
        test_loss, correct, len(test_loader.dataset),
        100. * correct / len(test_loader.dataset)))
        
def main():
    epochs = 2 # 訓(xùn)練總輪數(shù)
    batch_size = 64
    torch.manual_seed(0)

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

    train_loader = torch.utils.data.DataLoader(
        datasets.MNIST('../data/MNIST', train=True, download=False,
                       transform=transforms.Compose([
                           transforms.ToTensor(),
                           transforms.Normalize((0.1307,), (0.3081,))
                       ])),
        batch_size=batch_size, shuffle=True)
    test_loader = torch.utils.data.DataLoader(
        datasets.MNIST('../data/MNIST', train=False, download=False, transform=transforms.Compose([
            transforms.ToTensor(),
            transforms.Normalize((0.1307,), (0.3081,))
        ])),
        batch_size=1000, shuffle=True)

    model = Net().to(device)
    optimizer = torch.optim.SGD(model.parameters(), lr=0.025, momentum=0.9)

    for epoch in range(1, epochs + 1):
        train(model, device, train_loader, optimizer, epoch)
        test(model, device, test_loader)

#     torch.save(model.state_dict(), "mnist_cnn.pt")
main()

結(jié)果:

enter description here

至此基于 Pytorch 的網(wǎng)絡(luò)訓(xùn)練與評(píng)估就講解完畢了旭贬,不知道你有沒(méi)有理解呢?

  • 更多技術(shù)文章和學(xué)習(xí)資料搪泳,請(qǐng)關(guān)注我的公眾號(hào):【集成電路設(shè)計(jì)教程】
  • 全平臺(tái)統(tǒng)一:【雪天魚(yú)】
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末稀轨,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子岸军,更是在濱河造成了極大的恐慌奋刽,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,729評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件艰赞,死亡現(xiàn)場(chǎng)離奇詭異佣谐,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)方妖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)狭魂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人党觅,你說(shuō)我怎么就攤上這事雌澄。” “怎么了杯瞻?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,461評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵镐牺,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我魁莉,道長(zhǎng)睬涧,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,135評(píng)論 1 300
  • 正文 為了忘掉前任旗唁,我火速辦了婚禮畦浓,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘逆皮。我一直安慰自己宅粥,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,130評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布电谣。 她就那樣靜靜地躺著秽梅,像睡著了一般抹蚀。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上企垦,一...
    開(kāi)封第一講書(shū)人閱讀 52,736評(píng)論 1 312
  • 那天环壤,我揣著相機(jī)與錄音,去河邊找鬼钞诡。 笑死郑现,一個(gè)胖子當(dāng)著我的面吹牛梦碗,可吹牛的內(nèi)容都是我干的龄减。 我是一名探鬼主播完疫,決...
    沈念sama閱讀 41,179評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼伏伯,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了哼审?” 一聲冷哼從身側(cè)響起毅哗,我...
    開(kāi)封第一講書(shū)人閱讀 40,124評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤率寡,失蹤者是張志新(化名)和其女友劉穎剪返,沒(méi)想到半個(gè)月后废累,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,657評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡脱盲,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,723評(píng)論 3 342
  • 正文 我和宋清朗相戀三年邑滨,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片钱反。...
    茶點(diǎn)故事閱讀 40,872評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡掖看,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出诈铛,到底是詐尸還是另有隱情乙各,我是刑警寧澤墨礁,帶...
    沈念sama閱讀 36,533評(píng)論 5 351
  • 正文 年R本政府宣布幢竹,位于F島的核電站,受9級(jí)特大地震影響恩静,放射性物質(zhì)發(fā)生泄漏焕毫。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,213評(píng)論 3 336
  • 文/蒙蒙 一驶乾、第九天 我趴在偏房一處隱蔽的房頂上張望邑飒。 院中可真熱鬧,春花似錦级乐、人聲如沸疙咸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,700評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)撒轮。三九已至乞旦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間题山,已是汗流浹背兰粉。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,819評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留顶瞳,地道東北人玖姑。 一個(gè)月前我還...
    沈念sama閱讀 49,304評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像慨菱,于是被迫代替她去往敵國(guó)和親焰络。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,876評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容