<kbd id="5sdj3"></kbd>
<th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>

    從零實(shí)現(xiàn)深度學(xué)習(xí)框架(十一)從零實(shí)現(xiàn)線性回歸

    共 8109字,需瀏覽 17分鐘

     ·

    2022-01-11 12:34

    更多精彩推薦,請(qǐng)關(guān)注我們


    引言

    本著“凡我不能創(chuàng)造的,我就不能理解”的思想,本系列文章會(huì)基于純Python以及NumPy從零創(chuàng)建自己的深度學(xué)習(xí)框架,該框架類(lèi)似PyTorch能實(shí)現(xiàn)自動(dòng)求導(dǎo)。

    要深入理解深度學(xué)習(xí),從零開(kāi)始創(chuàng)建的經(jīng)驗(yàn)非常重要,從自己可以理解的角度出發(fā),盡量不適用外部完備的框架前提下,實(shí)現(xiàn)我們想要的模型。本系列文章的宗旨就是通過(guò)這樣的過(guò)程,讓大家切實(shí)掌握深度學(xué)習(xí)底層實(shí)現(xiàn),而不是僅做一個(gè)調(diào)包俠。

    上篇文章中,我們了解了線性回歸。本文就來(lái)通過(guò)metagrad實(shí)現(xiàn)線性回歸。

    實(shí)現(xiàn)模型基類(lèi)

    class?Module:
    ????'''
    ????所有模型的基類(lèi)
    ????'''


    ????def?parameters(self)?->?List[Parameter]:
    ????????parameters?=?[]
    ????????for?name,?value?in?inspect.getmembers(self):
    ????????????if?isinstance(value,?Parameter):
    ????????????????parameters.append(value)
    ????????????elif?isinstance(value,?Module):
    ????????????????parameters.extend(value.parameters())

    ????????return?parameters

    ????def?zero_grad(self):
    ????????for?p?in?self.parameters():
    ????????????p.zero_grad()

    ????def?__call__(self,?*args,?**kwargs):
    ????????return?self.forward(*args,?**kwargs)

    ????def?forward(self,?*args,?**kwargs)?->?Tensor:
    ????????raise?NotImplementedError

    類(lèi)似PyTorch,我們也實(shí)現(xiàn)一個(gè)模型的基類(lèi),其中存放一些通用方法。代碼如上。主要實(shí)現(xiàn)了梯度清零方法。

    其中Parameter的定義如下:

    class?Parameter(Tensor):
    ????def?__init__(self,?data:?Union[Arrayable,?Tensor])?->?None:
    ????????
    ????????if?isinstance(data,?Tensor):
    ????????????data?=?data.data
    ????????#?Parameter都是需要計(jì)算梯度的
    ????????super().__init__(data,?requires_grad=True)

    Parameter默認(rèn)需要計(jì)算梯度,在Moduleparameters()方法中利用Parameter類(lèi)獲取模型的所有參數(shù)。

    實(shí)現(xiàn)線性回歸

    class?Linear(Module):
    ????r"""
    ?????????對(duì)給定的輸入進(jìn)行線性變換:?:math:`y=xA^T?+?b`

    ????????Args:
    ????????????in_features:?每個(gè)輸入樣本的大小
    ????????????out_features:?每個(gè)輸出樣本的大小
    ????????????bias:?是否含有偏置,默認(rèn)?``True``
    ????????Shape:
    ????????????-?Input:?`(*,?H_in)`?其中?`*`?表示任意維度,包括none,這里?`H_{in}?=?in_features`
    ????????????-?Output:?:math:`(*,?H_out)`?除了最后一個(gè)維度外,所有維度的形狀都與輸入相同,這里H_out?=?out_features`
    ????????Attributes:
    ????????????weight:?可學(xué)習(xí)的權(quán)重,形狀為?`(out_features,?in_features)`.
    ????????????bias:???可學(xué)習(xí)的偏置,形狀?`(out_features)`.
    ????????"""


    ????def?__init__(self,?in_features:?int,?out_features:?int,?bias:?bool?=?True)?->?None:
    ????????self.in_features?=?in_features
    ????????self.out_features?=?out_features

    ????????self.weight?=?Parameter(Tensor.empty((out_features,?in_features)))
    ????????if?bias:
    ????????????self.bias?=?Parameter(Tensor.zeros(out_features))
    ????????else:
    ????????????self.bias?=?None
    ????????self.reset_parameters()

    ????def?reset_parameters(self)?->?None:
    ????????self.weight.assign(np.random.randn(self.out_features,?self.in_features))

    ????def?forward(self,?input:?Tensor)?->?Tensor:
    ????????x?=?input?@?self.weight.T
    ????????if?self.bias?is?not?None:
    ????????????x?=?x?+?self.bias

    ????????return?x

    讓我們的線性回歸模型繼承Module,同時(shí)定義權(quán)重和偏置大小。最后我們只需要實(shí)現(xiàn)前向傳播算法,反向傳播就交給我們的自動(dòng)求導(dǎo)機(jī)制去完成。

    這樣,我們的線性回歸模型就實(shí)現(xiàn)完成了。但為了我們的模型能夠?qū)W習(xí),我們需要定義損失函數(shù)。

    實(shí)現(xiàn)損失基類(lèi)

    class?_Loss(Module):
    ????'''
    ????損失的基類(lèi)
    ????'''

    ????reduction:?str??#?none?|?mean?|?sum

    ????def?__init__(self,?reduction:?str?=?"mean")?->?None:
    ????????self.reduction?=?reduction

    參考了PyTorch,聚合方法支持均值和求和。

    實(shí)現(xiàn)均方誤差

    class?MSELoss(_Loss):
    ????def?__init__(self,?reduction:?str?=?"mean")?->?None:
    ????????'''
    ????????均方誤差
    ????????'''

    ????????super().__init__(reduction)

    ????def?forward(self,?input:?Tensor,?target:?Tensor)?->?Tensor:
    ????????assert?input.size?==?target.size,?f"Using?a?target?size?({target.size})?that?is?different?to?the?input?size?"?\
    ???????????????????????????????????????????f"({input.size}).?This?will?likely?lead?to?incorrect?results?due?to?"?\
    ???????????????????????????????????????????f"broadcasting.?Please?ensure?they?have?the?same?size."

    ????????errors?=?(input?-?target)?**?2
    ????????if?self.reduction?==?"mean":
    ????????????loss?=?errors.sum(keepdims=False)?/?len(input)
    ????????elif?self.reduction?==?"sum":
    ????????????loss?=?errors.sum(keepdims=False)
    ????????else:
    ????????????loss?=?errors

    ????????return?loss

    這里的input其實(shí)是模型的輸出,target真實(shí)輸出。

    有了損失函數(shù)后,我們還需要優(yōu)化方法來(lái)進(jìn)行參數(shù)優(yōu)化。

    實(shí)現(xiàn)優(yōu)化方法

    class?Optimizer:
    ????def?__init__(self,?params:?List[Parameter])?->?None:
    ????????self.params?=?params

    ????def?zero_grad(self)?->?None:
    ????????for?p?in?self.params:
    ????????????p.zero_grad()

    ????def?step(self)?->?None:
    ????????raise?NotImplementedError

    我們?nèi)缟蠈?shí)現(xiàn)了優(yōu)化方法的基類(lèi)。下面就是實(shí)現(xiàn)隨機(jī)梯度下降法(SGD)。

    實(shí)現(xiàn)隨機(jī)梯度下降法

    class?SGD(Optimizer):
    ????'''
    ????隨機(jī)梯度下降
    ????'''


    ????def?__init__(self,?params:?List[Parameter],?lr:?float?=?1e-3)?->?None:
    ????????super().__init__(params)
    ????????self.lr?=?lr

    ????def?step(self)?->?None:
    ????????for?p?in?self.params:
    ????????????p?-=?p.grad?*?self.lr

    lr是學(xué)習(xí)率,每次調(diào)用step()都會(huì)進(jìn)行參數(shù)更新。

    線性回歸實(shí)例

    我們基于上篇文章中采集的深圳市南山區(qū)臨近地鐵口二手房?jī)r(jià)的數(shù)據(jù)為例:

    #?面積
    areas?=?[64.4,?68,?74.1,?74.,?76.9,?78.1,?78.6]
    #?掛牌售價(jià)
    prices?=?[6.1,?6.25,?7.8,?6.66,?7.82,?7.14,?8.02]

    我們先考慮面積和掛牌價(jià)(可能是指導(dǎo)價(jià) ?單位:萬(wàn)/㎡)之間的關(guān)系。

    面積和售價(jià)的關(guān)系

    看上去似乎有一定的線性關(guān)系。我們這里簡(jiǎn)單的考慮套內(nèi)面積,實(shí)際上我們買(mǎi)房時(shí)還會(huì)考慮房齡、離地鐵口距離、小區(qū)周邊環(huán)境、空氣質(zhì)量、小區(qū)綠化區(qū)面積等。

    這里我們嘗試通過(guò)畫(huà)一條直線,使得該直線盡可能和每個(gè)樣本的距離最短。

    #?!pip?install?git+https://github.com/nlp-greyfoss/metagrad.git?--upgrade
    from?metagrad.loss?import?MSELoss
    from?metagrad.module?import?Linear
    from?metagrad.optim?import?SGD
    from?metagrad.tensor?import?Tensor

    model?=?Linear(1,?1)

    optimizer?=?SGD(model.parameters(),?lr=1e-1)

    loss?=?MSELoss()

    #?面積
    areas?=?[64.4,?68,?74.1,?74.,?76.9,?78.1,?78.6]
    #?掛牌售價(jià)
    prices?=?[6.1,?6.25,?7.8,?6.66,?7.82,?7.14,?8.02]

    X?=?Tensor(areas).reshape((-1,?1))
    y?=?Tensor(prices).reshape((-1,?1))

    epochs?=?100
    losses?=?[]
    for?epoch?in?range(epochs):
    ??l?=?loss(model(X),?y)
    ??optimizer.zero_grad()
    ??l.backward()
    ??optimizer.step()
    ??epoch_loss?=?l.data
    ??
    ??losses.append(epoch_loss)

    ??print(f'epoch?{epoch?+?1},?loss?{float(epoch_loss):f}')

    上面就是通過(guò)我們自己的metagrad實(shí)現(xiàn)的線性回歸學(xué)習(xí)過(guò)程,是不是看上去有那味了。

    輸出:

    epoch?1,?loss?198.071304
    epoch?2,?loss?232059248.000000
    epoch?3,?loss?272126879727616.000000
    epoch?4,?loss?319112649512125988864.000000
    epoch?5,?loss?374211056107641585692311552.000000
    epoch?6,?loss?438822818730812850430760481456128.000000
    epoch?7,?loss?inf
    ...

    怎么損失不降反增了!?

    莫慌,我們只有一個(gè)變量,不存在兩個(gè)變量的量綱不同的問(wèn)題。此時(shí),該顯示一下我們AI調(diào)參師的技術(shù)了。

    損失太大,可能是梯度太大了,我們直接將學(xué)習(xí)率調(diào)小。

    optimizer?=?SGD(model.parameters(),?lr=1e-4)

    我們修改學(xué)習(xí)率為1e-4

    epoch?1,?loss?21798.214844
    epoch?2,?loss?153.602646
    epoch?3,?loss?1.260477
    epoch?4,?loss?0.188241
    epoch?5,?loss?0.180694
    epoch?6,?loss?0.180641
    epoch?7,?loss?0.180641
    epoch?8,?loss?0.180641
    epoch?9,?loss?0.180641
    epoch?10,?loss?0.180641
    epoch?11,?loss?0.180641
    epoch?12,?loss?0.180640
    ...
    epoch?99,?loss?0.180638
    epoch?100,?loss?0.180638

    從輸出可以看出,第4次迭代后,損失就一直不變了,我們看一下學(xué)習(xí)率曲線:

    學(xué)習(xí)率曲線

    我們可以打印出得到的參數(shù):

    >?w,?b?=?model.weight.data.item(),model.bias.data.item()
    >?print(f'w:?{w},?b:')
    w:?0.09660441144108287,?b:0.026999891111711846

    然后畫(huà)出線性回歸擬合的直線:

    預(yù)測(cè)直線

    看上去還可以,如果你要買(mǎi)房的話,建議你買(mǎi)直線下面的房子。

    基于我們這點(diǎn)訓(xùn)練樣本,得到最后的損失為,我們能否使它再次降低呢?

    一種方法是收集更多的數(shù)據(jù),另一種方法是利用所有的維度。我們還有一個(gè)房齡維度沒(méi)有利用。下面把它加進(jìn)來(lái)。

    #?面積
    >?areas?=?[64.4,?68,?74.1,?74.,?76.9,?78.1,?78.6]
    #?房齡
    >?ages?=?[31,?21,?19,?24,?17,?16,?17]
    >?X?=?np.stack([areas,?ages]).T
    >?print(X)
    array([[64.4,?31.?],
    ???????[68.?,?21.?],
    ???????[74.1,?19.?],
    ???????[74.?,?24.?],
    ???????[76.9,?17.?],
    ???????[78.1,?16.?],
    ???????[78.6,?17.?]])

    第1列是面積,第2列是房齡,每行數(shù)據(jù)代表一個(gè)樣本。

    下面我們改寫(xiě)上面的線性回歸代碼,再次訓(xùn)練一個(gè)線性回歸模型:

    model?=?Linear(2,?1)?#?in_features:?2??out_features:?1

    optimizer?=?SGD(model.parameters(),?lr=1e-4)

    loss?=?MSELoss()

    #?面積
    areas?=?[64.4,?68,?74.1,?74.,?76.9,?78.1,?78.6]
    #?房齡
    ages?=?[31,?21,?19,?24,?17,?16,?17]

    X?=?np.stack([areas,?ages]).T
    #?掛牌售價(jià)
    prices?=?[6.1,?6.25,?7.8,?6.66,?7.82,?7.14,?8.02]


    X?=?Tensor(X)
    y?=?Tensor(prices).reshape((-1,?1))

    epochs?=?1000
    losses?=?[]
    for?epoch?in?range(epochs):
    ??l?=?loss(model(X),?y)
    ??optimizer.zero_grad()
    ??l.backward()
    ??optimizer.step()
    ??epoch_loss?=?l.data

    ??losses.append(epoch_loss)
    ??if?(epoch+1)?%?20?==?0:
    ????print(f'epoch?{epoch?+?1},?loss?{float(epoch_loss):f}')

    輸出:

    epoch?20,?loss?10.742877
    epoch?40,?loss?8.136241
    epoch?60,?loss?6.171517
    epoch?80,?loss?4.690628
    epoch?100,?loss?3.574423
    epoch?120,?loss?2.733095
    epoch?140,?loss?2.098954
    epoch?160,?loss?1.620976
    epoch?180,?loss?1.260706
    epoch?200,?loss?0.989156
    epoch?220,?loss?0.784478
    epoch?240,?loss?0.630204
    epoch?260,?loss?0.513921
    epoch?280,?loss?0.426275
    ...
    epoch?1000,?loss?0.158022

    加上房齡信息,最終可以使損失下降到。我們來(lái)看一下此時(shí)的權(quán)重和偏置:

    >?w,?b?=?model.weight.data,model.bias.data.item()
    >?print(f'w:?{w},?b:')
    w:?[[?0.10354146?-0.02362296]],?b:-0.00232952055510911

    可以看到,房齡特征對(duì)應(yīng)的權(quán)重為,所以說(shuō)房齡越大,房子的價(jià)值就越小,這一關(guān)系還是學(xué)到了的。

    我們的訓(xùn)練集才7個(gè)樣本,這真的是太少了,如果你收集更多的數(shù)據(jù),一定可以獲得更好的效果。

    總結(jié)

    本文我們通過(guò)metagrad實(shí)現(xiàn)了線性回歸,以及一些基類(lèi)方法。下篇文章我們就來(lái)學(xué)習(xí)邏輯回歸。

    最后一句:BUG,走你!

    Markdown筆記神器Typora配置Gitee圖床
    不會(huì)真有人覺(jué)得聊天機(jī)器人難吧(一)
    Spring Cloud學(xué)習(xí)筆記(一)
    沒(méi)有人比我更懂Spring Boot(一)
    入門(mén)人工智能必備的線性代數(shù)基礎(chǔ)

    1.看到這里了就點(diǎn)個(gè)在看支持下吧,你的在看是我創(chuàng)作的動(dòng)力。
    2.關(guān)注公眾號(hào),每天為您分享原創(chuàng)或精選文章!
    3.特殊階段,帶好口罩,做好個(gè)人防護(hù)。

    瀏覽 24
    點(diǎn)贊
    評(píng)論
    收藏
    分享

    手機(jī)掃一掃分享

    分享
    舉報(bào)
    評(píng)論
    圖片
    表情
    推薦
    點(diǎn)贊
    評(píng)論
    收藏
    分享

    手機(jī)掃一掃分享

    分享
    舉報(bào)

    <kbd id="5sdj3"></kbd>
    <th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>
    在线天堂资源19 | 一区二区三区四区五区在线 | 日屄视频免费观看 | 国产操逼www | 狠狠狠狠狠狠干 |