我的實(shí)踐:通過一個(gè)簡(jiǎn)單線性回歸入門pytorch

機(jī)器學(xué)習(xí)簡(jiǎn)單來講就是要在數(shù)據(jù)中訓(xùn)練出一個(gè)模型枕稀,能夠?qū)⑤斎胗成涑珊侠淼妮敵鲅病K裕谟?xùn)練模型之前萎坷,我們首先準(zhǔn)備好輸入凹联、輸出對(duì);然后再利用這些輸入哆档、輸出對(duì)來優(yōu)化模型蔽挠,使模型的LOSS(預(yù)測(cè)輸出和實(shí)際輸出的誤差)盡可能小。模型優(yōu)化的基本原理是梯度下降法瓜浸。pytorch為實(shí)現(xiàn)上述任務(wù)提供了一個(gè)很好的框架澳淑,或者說一個(gè)很好的模板,使得做深度學(xué)習(xí)變得非常簡(jiǎn)單插佛,簡(jiǎn)單到一兩個(gè)小時(shí)就能入門杠巡。本文借助一個(gè)簡(jiǎn)單線性回歸的例子,簡(jiǎn)要介紹了Pytorch框架中的數(shù)據(jù)加載及模型訓(xùn)練等雇寇。

生成輸入輸出對(duì)

在訓(xùn)練模型之前氢拥,我們首先生成一些輸入蚌铜、輸出對(duì),作為模型訓(xùn)練數(shù)據(jù)和測(cè)試數(shù)據(jù)兄一。本例通過一個(gè)線性函數(shù)疊加一些噪聲來生成厘线。比如下面這段代碼,取權(quán)重值為2出革,偏置為5的線性函數(shù)造壮,然后疊加一個(gè)標(biāo)準(zhǔn)正態(tài)分布的噪聲,生成100個(gè)數(shù)據(jù)點(diǎn)骂束。生成兩次耳璧,一次用于模型訓(xùn)練,一次用于模型測(cè)試展箱。通過該數(shù)據(jù)訓(xùn)練的線性模型旨枯,我們希望權(quán)重越接近2越好,偏置越接近5越好混驰。

import random
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
# 生成數(shù)據(jù)
x = random.sample(range(0, 100), 100)
x = 0.1*np.array(x)
y = x*2+5+np.random.randn(100)
plt.plot(x, y, 'o')
plt.show()
data = [list(x), list(y)]
# 矩陣轉(zhuǎn)置
data = list(zip(*data))
column = ['x', 'y']
# list轉(zhuǎn)換成dataFrame
dataset = pd.DataFrame(data=data, columns=column)
# 將數(shù)據(jù)保存到'.csv'文件中
dataset.to_csv('data/trainData.csv')
# dataset.to_csv('data/testData.csv')

上述代碼生成的數(shù)據(jù)分布如下圖所示攀隔,


訓(xùn)練數(shù)據(jù)![Figure_2.png](https://upload-images.jianshu.io/upload_images/2656792-fc5b5ea73db94e12.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

模型的設(shè)計(jì)

有了輸入、輸出對(duì)數(shù)據(jù)栖榨,接下來我們?cè)賮碓O(shè)計(jì)模型昆汹。pytorch為我們提供了一個(gè)框架,使得網(wǎng)絡(luò)模型的搭建非常簡(jiǎn)單婴栽,簡(jiǎn)單得像是在搭積木满粗。pytorch不僅提供了搭積木的框架,還提供了大量的積木塊愚争,例如Linear層映皆、卷積層、激活函數(shù)等等轰枝,我們只需要根據(jù)任務(wù)需求將這些積木堆在一起就行了捅彻。我們通過線性回歸這個(gè)最簡(jiǎn)單的例子來學(xué)習(xí)一下pytorch的模型搭建框架。
pytorch的積木搭建框架以及積木塊都在pytorch的nn模塊中狸膏,因此首先要導(dǎo)入nn模塊沟饥。pytorch的積木搭建框架是一個(gè)叫做Module的類,在搭建自己的網(wǎng)絡(luò)模型是需要繼承這個(gè)類湾戳。在這個(gè)類里面,有兩個(gè)函數(shù)為我們搭建積木提供了支撐广料,需要改寫砾脑,一個(gè)是init函數(shù),一個(gè)是forward函數(shù)艾杏。init函數(shù)列出我們需要用的積木塊韧衣,并根據(jù)需要設(shè)置好這些積木塊的相關(guān)參數(shù);在設(shè)置參數(shù)的時(shí)候一定要注意上一層積木的輸出維度和下一層積木的輸入維度匹配。forward函數(shù)將這些積木塊壘在一起畅铭,使輸入能順利地通過一層層的積木塊氏淑,最后輸出。我們這里是一個(gè)簡(jiǎn)單的線性回歸問題硕噩,所以只需要一塊積木假残,那就是nn.Linear,該積木塊提供了線性變換功能炉擅。nn.Linear有三個(gè)參數(shù)辉懒,分別是in_features, out_features和bias,分別代表了輸入的維度谍失、輸出的維度和是否需要偏置(默認(rèn)的情況下偏置保留)眶俩。在我們這個(gè)例子中,輸入和輸出的維度都是1快鱼,需要偏置颠印。模型搭建的代碼如下,代碼保存在model.py中抹竹。

from torch import nn
# 定義模型時(shí)繼承nn.Module
class LinearRegress(nn.Module):
    # __init__函數(shù)列出積木塊并設(shè)置積木的參數(shù)线罕,這里的參數(shù)由模型實(shí)例化時(shí)給出
    def __init__(self, inputsize, outputsize):
        super(LinearRegress, self).__init__()
        self.Linear1 = nn.Linear(in_features=inputsize, out_features=outputsize)
    # forward函數(shù)搭積木,將積木壘在一塊柒莉,讓輸入依次通過積木塊最后輸出
    def forward(self, x):
        return self.Linear1(x)

數(shù)據(jù)的加載

為了簡(jiǎn)化訓(xùn)練數(shù)據(jù)和測(cè)試數(shù)據(jù)的加載過程闻坚,pytorch為我們提供了數(shù)據(jù)集模板Dataset以及數(shù)據(jù)加載器DataLoader。我們?cè)谟?xùn)練模型時(shí)需要從數(shù)據(jù)集中摳出輸入兢孝、輸出對(duì)窿凤,Dataset恰好為我們給我們提供了一個(gè)摳輸入、輸出對(duì)的模板跨蟹。我們定義自己的數(shù)據(jù)集時(shí)雳殊,需要繼承Dataset,并改寫三個(gè)函數(shù)窗轩,分別是init, getitem, len夯秃。init一般告訴代碼要加載的數(shù)據(jù)集存在哪個(gè)位置。getitem從文件夾中讀入數(shù)據(jù)集并進(jìn)行一些處理痢艺,返回輸入輸出對(duì)仓洼。這里要注意返回輸入、輸出對(duì)的格式是Tensor堤舒,并且輸入Tensor的維度一定要和模型的輸入維度一致色建,輸出Tensor的維度一定要和模型的輸出維度一致,否則會(huì)出錯(cuò)舌缤。例如mn.Linear輸入箕戳、輸出都是二維Tensor某残,分別是[batchsize,in_features]、[bathchsize,outfeature]陵吸。所以玻墅,在加載了數(shù)據(jù)之后,首先要將輸入壮虫、輸出數(shù)據(jù)都轉(zhuǎn)換成Tensor澳厢,然后將1維Tensor轉(zhuǎn)換成二維Tensor。len函數(shù)返回?cái)?shù)據(jù)集的長(zhǎng)度旨指。
DataLoader提供一些列參數(shù)設(shè)置赏酥,方便我們可以根據(jù)需要靈活的加載數(shù)據(jù)。例如一次加載的數(shù)據(jù)大小batchsize谆构,是否打亂數(shù)據(jù)順序shuffer等裸扶,還有各種參數(shù)可以看和help中對(duì)DataLoader的解釋。下面是代碼搬素,保存在dataProcess.py中呵晨。

import os
from torch.utils.data import DataLoader
from torch.utils.data import Dataset
import pandas as pd
# 將數(shù)據(jù)導(dǎo)入到DataSet
class MyDataSet(Dataset):
    # 初始化時(shí)從文件中把數(shù)據(jù)讀進(jìn)來
    def __init__(self, dataDir, dataName):
        DataPath = os.path.join(dataDir, dataName)
        self.data = pd.read_csv(DataPath)
    def __getitem__(self, idx):
        # 將數(shù)據(jù)轉(zhuǎn)換成二維Tensor
        x_tensor = torch.Tensor(self.data['x'].to_list()).reshape(-1, 1)
        y_tensor = torch.Tensor(self.data['y'].to_list()).reshape(-1, 1)
        return x_tensor[idx], y_tensor[idx]
    def __len__(self):
        return len(self.data)
#加載訓(xùn)練數(shù)據(jù)
myTrainData = MyDataSet("data", "trainData.csv")
#將batch_size設(shè)置成50,表示每一次迭代取出50個(gè)數(shù)據(jù)熬尺。
myTrainDataLoader = DataLoader(dataset=myTrainData, batch_size=50, shuffle=True)
#加載測(cè)試數(shù)據(jù)
myTestData = MyDataSet("data", "testData.csv")
myTestDataLoader = DataLoader(dataset=myTestData, batch_size=50, shuffle=True)

模型的訓(xùn)練與測(cè)試

模型的訓(xùn)練過程大致如下:

  1. 從數(shù)據(jù)集中取出一個(gè)btachsize的輸入摸屠、輸出對(duì)。
  2. 把輸入扔給模型粱哼,得到預(yù)測(cè)輸出
  3. 計(jì)算預(yù)測(cè)輸出和真實(shí)輸出之間的LOSS
  4. 反向傳播計(jì)算梯度季二,并優(yōu)化一次模型參數(shù)
  5. 回到第1步,直到從數(shù)據(jù)集中取出所有數(shù)據(jù)揭措,完成一次完整的訓(xùn)練
  6. 重復(fù)1-5步epoch次

為了測(cè)試模型的泛化能力胯舷,我們往往在優(yōu)化模型的過程中還會(huì)使用一些測(cè)試數(shù)據(jù)來測(cè)試模型的預(yù)測(cè)效果。這里一定要注意測(cè)試數(shù)據(jù)和訓(xùn)練數(shù)據(jù)不是同一個(gè)數(shù)據(jù)集绊含,提前要把數(shù)據(jù)進(jìn)行分割桑嘶,分成訓(xùn)練數(shù)據(jù)和測(cè)試數(shù)據(jù)。一般每進(jìn)行一次完整的訓(xùn)練后躬充,對(duì)模型進(jìn)行一次測(cè)試逃顶,也就是每一個(gè)epoch,測(cè)試一次模型充甚。測(cè)試的時(shí)候也需要計(jì)算模型預(yù)測(cè)輸出和真實(shí)輸出之間的LOSS以政,只是測(cè)試不用再計(jì)算梯度和優(yōu)化模型了。如果在訓(xùn)練過程中發(fā)現(xiàn)訓(xùn)練的LOSS在不斷減小伴找,但是測(cè)試的LOSS卻在增加妙蔗,這時(shí)候模型發(fā)生了過擬合問題,要提前終止訓(xùn)練疆瑰。
當(dāng)然眉反,我們?yōu)榱丝吹接?xùn)練的效果,往往要畫LOSS隨著迭代次數(shù)的變化曲線穆役,這個(gè)我們可以借助Tensorboard寸五,也可以用一個(gè)list把訓(xùn)練過程的LOSS保存下來,最后用matplotlib.pyplot畫出來耿币。

from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
from model import *
from dataProcess import *
import matplotlib.pyplot as plt
#加載訓(xùn)練數(shù)據(jù)
myTrainData = MyDataSet("data", "trainData.csv")
#將batch_size設(shè)置成50梳杏,表示每一次迭代取出50個(gè)數(shù)據(jù)。
myTrainDataLoader = DataLoader(dataset=myTrainData, batch_size=50, shuffle=True)
#加載測(cè)試數(shù)據(jù)
myTestData = MyDataSet("data", "testData.csv")
myTestDataLoader = DataLoader(dataset=myTestData, batch_size=50, shuffle=True)
# 創(chuàng)建網(wǎng)絡(luò)模型
myModel = LinearRegress(inputsize=1, outputsize=1)
# 損失函數(shù)
loss_fn = nn.MSELoss()
# 學(xué)習(xí)率
learning_rate = 5e-3
# 優(yōu)化器
optimizer = torch.optim.SGD(myModel.parameters(), lr=learning_rate)
# 總共的訓(xùn)練步數(shù)
total_train_step = 0
#總共的測(cè)試步數(shù)
total_test_step = 0
step = 0
epoch =500
# Tensorboard的writer實(shí)例淹接,用于記錄訓(xùn)練過程中的LOSS變化
writer = SummaryWriter("logs")
train_loss_his = []
test_totalloss_his = []
for i in range(epoch):
    print(f"-------第{i}輪訓(xùn)練開始-------")
    # 這一部分是模型訓(xùn)練
    for data in myTrainDataLoader:
        # 注意這里是取了一個(gè)batchsize的數(shù)據(jù)十性,該例batchsize=50,因此取了50個(gè)數(shù)據(jù)
        x, y = data
        # 把輸入扔給模型塑悼,得到預(yù)測(cè)輸出output
        output = myModel(x)
        # 計(jì)算預(yù)測(cè)輸出output和真是輸出y之間的LOSS
        loss = loss_fn(output, y)
        # 將梯度清零劲适,好像這一步必須要
        optimizer.zero_grad()
        # 反向傳播,計(jì)算梯度
        loss.backward()
        # 優(yōu)化一次參數(shù)
        optimizer.step()
        # 總的迭代次數(shù)加1
        total_train_step = total_train_step+1
         # 將當(dāng)前的LOSS放到LOSS記錄的list中
        train_loss_his.append(loss)
        # 將當(dāng)前的LOSS記錄到tensorboard的中
        writer.add_scalar("train_loss", loss.item(), total_train_step)
        print(f"訓(xùn)練次數(shù):{total_train_step}厢蒜,loss:{loss}")
    # 下面這段代碼是模型測(cè)試
    total_test_loss = 0
    # 這里告訴代碼不用求梯度了
    with torch.no_grad():
        for data in myTestDataLoader:
            x, y = data
            output = myModel(x)
            loss = loss_fn(output, y)
            # 這里求一個(gè)epoch的總loss
            total_test_loss = total_test_loss + loss
        print(f"測(cè)試集上的loss:{total_test_loss}")
        test_totalloss_his.append(total_test_loss)
        writer.add_scalar("test_loss", total_test_loss.item(), i)
# 輸出線性模型的兩個(gè)參數(shù)霞势,分別是權(quán)重和偏置
for parameters in myModel.parameters():
    print(parameters)
writer.close()
# 畫出訓(xùn)練損失變化曲線
plt.plot(train_loss_his)
plt.show()
# 畫出測(cè)試損失變化曲線
plt.plot(test_totalloss_his)
plt.show()

運(yùn)行上述代碼,訓(xùn)練LOSS的變化如下圖斑鸦,


Figure_2.png

測(cè)試LOSS的變化如下圖愕贡,


Figure_3.png

線性模型的兩個(gè)參數(shù):權(quán)重為2.1539,偏置為4.1611巷屿」桃裕可以看到訓(xùn)練后的參數(shù)和預(yù)期參數(shù)接近,但也存在一定的偏差嘱巾『┝眨可以嘗試設(shè)置不同學(xué)習(xí)率、采用不同的優(yōu)化器浓冒、使用不同的LOSS函數(shù)等栽渴,會(huì)對(duì)結(jié)果產(chǎn)生很大的影響,這就是無聊的調(diào)參了稳懒。
到此為止闲擦,基于pytorch的深度學(xué)習(xí)框架就入門了,確實(shí)很簡(jiǎn)單场梆!
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末墅冷,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子或油,更是在濱河造成了極大的恐慌寞忿,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件顶岸,死亡現(xiàn)場(chǎng)離奇詭異腔彰,居然都是意外死亡叫编,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門霹抛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來搓逾,“玉大人,你說我怎么就攤上這事杯拐∠即郏” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵端逼,是天一觀的道長(zhǎng)朗兵。 經(jīng)常有香客問我,道長(zhǎng)顶滩,這世上最難降的妖魔是什么余掖? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮诲祸,結(jié)果婚禮上浊吏,老公的妹妹穿的比我還像新娘。我一直安慰自己救氯,他們只是感情好找田,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著着憨,像睡著了一般墩衙。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上甲抖,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天漆改,我揣著相機(jī)與錄音,去河邊找鬼准谚。 笑死挫剑,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的柱衔。 我是一名探鬼主播樊破,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼唆铐!你這毒婦竟也來了哲戚?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤艾岂,失蹤者是張志新(化名)和其女友劉穎顺少,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡脆炎,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年梅猿,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片腕窥。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡粒没,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出簇爆,到底是詐尸還是另有隱情,我是刑警寧澤爽撒,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布入蛆,位于F島的核電站,受9級(jí)特大地震影響硕勿,放射性物質(zhì)發(fā)生泄漏哨毁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一源武、第九天 我趴在偏房一處隱蔽的房頂上張望扼褪。 院中可真熱鬧,春花似錦粱栖、人聲如沸话浇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽幔崖。三九已至,卻和暖如春渣淤,著一層夾襖步出監(jiān)牢的瞬間赏寇,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工价认, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留嗅定,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓用踩,卻偏偏與公主長(zhǎng)得像渠退,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子捶箱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345