《动手学深度学习》 第二天 (线性回归)
3.2 線性回歸的從零開(kāi)始實(shí)現(xiàn)
只利用NDArray和autograd來(lái)實(shí)現(xiàn)一個(gè)線性回歸的訓(xùn)練。
首先,導(dǎo)入本節(jié)中實(shí)驗(yàn)所需的包或模塊,其中的matplotlib包可用于作圖,且設(shè)置成嵌入顯示。
%matplotlib inline from IPython import display from matplotlib import pyplot as plt from mxnet import autograd, nd import random3.2.1 生成數(shù)據(jù)集
我們構(gòu)造一個(gè)簡(jiǎn)單的人工訓(xùn)練數(shù)據(jù)集,它可以使我們能夠直觀比較學(xué)到的參數(shù)和真實(shí)的模型參數(shù)的區(qū)別。
設(shè)訓(xùn)練數(shù)據(jù)集樣本數(shù)為1000,輸入個(gè)數(shù)(特征數(shù))為2。
給定隨機(jī)生成的批量樣本特征𝑋∈?1000×2,
我們使用線性回歸模型真實(shí)權(quán)重𝑤=[2,?3.4]?和偏差𝑏=4.2,以及一個(gè)隨機(jī)噪聲項(xiàng)𝜖來(lái)生成標(biāo)簽 𝑦=𝑋𝑤+𝑏+𝜖,
其中噪聲項(xiàng)𝜖服從均值為0、標(biāo)準(zhǔn)差為0.01的正態(tài)分布。噪聲代表了數(shù)據(jù)集中無(wú)意義的干擾。下面,讓我們生成數(shù)據(jù)集。
注意,features的每一行是一個(gè)長(zhǎng)度為2的向量,而labels的每一行是一個(gè)長(zhǎng)度為1的向量(標(biāo)量)。
features[0], labels[0]輸出:
([2.2122064 0.7740038]<NDArray 2 @cpu(0)>, [6.000587]<NDArray 1 @cpu(0)>)通過(guò)生成第二個(gè)特征features[:, 1]和標(biāo)簽 labels 的散點(diǎn)圖,可以更直觀地觀察兩者間的線性關(guān)系。
def use_svg_display():# 用矢量圖顯示display.set_matplotlib_formats('svg') ? def set_figsize(figsize=(3.5, 2.5)):use_svg_display()# 設(shè)置圖的尺寸plt.rcParams['figure.figsize'] = figsize ? set_figsize() plt.scatter(features[:, 1].asnumpy(), labels.asnumpy(), 1); # 加分號(hào)只顯示圖我們將上面的plt作圖函數(shù)以及use_svg_display函數(shù)和set_figsize函數(shù)定義在d2lzh包里。以后在作圖時(shí),我們將直接調(diào)用d2lzh.plt。由于plt在d2lzh包中是一個(gè)全局變量,我們?cè)谧鲌D前只需要調(diào)用d2lzh.set_figsize()即可打印矢量圖并設(shè)置圖的尺寸。
3.2.2 讀取數(shù)據(jù)集
在訓(xùn)練模型的時(shí)候,我們需要遍歷數(shù)據(jù)集并不斷讀取小批量數(shù)據(jù)樣本。這里我們定義一個(gè)函數(shù):它每次返回batch_size(批量大小)個(gè)隨機(jī)樣本的特征和標(biāo)簽。
def data_iter(batch_size, features, labels):num_examples = len(features)indices = list(range(num_examples))random.shuffle(indices) # 樣本的讀取順序是隨機(jī)的for i in range(0, num_examples, batch_size):j = nd.array(indices[i: min(i + batch_size, num_examples)])yield features.take(j), labels.take(j) # take函數(shù)根據(jù)索引返回對(duì)應(yīng)元素讓我們讀取第一個(gè)小批量數(shù)據(jù)樣本并打印。每個(gè)批量的特征形狀為(10, 2),分別對(duì)應(yīng)批量大小和輸入個(gè)數(shù);標(biāo)簽形狀為批量大小。
batch_size = 10 ? for X, y in data_iter(batch_size, features, labels):print(X, y)break輸出:
[[-1.0929538 -0.1200345 ][-1.2860336 -1.6586353 ][ 0.00389364 1.1413413 ][-0.51129895 0.46543437][ 0.8011116 -0.5865901 ][ 0.52092004 0.18693134][ 0.5604595 0.96975976][-0.6614866 0.09907386][-0.4813231 0.5334126 ][-0.21595766 2.066646 ]]<NDArray 10x2 @cpu(0)> [ 2.409014 7.265286 0.31805784 1.6139998 7.7808976 4.6176642.0270698 2.5347762 1.4169512 -3.246182 ] <NDArray 10 @cpu(0)>3.2.3 初始化模型參數(shù)
我們將權(quán)重初始化成均值為0、標(biāo)準(zhǔn)差為0.01的正態(tài)隨機(jī)數(shù),偏差則初始化成0。
w = nd.random.normal(scale=0.01, shape=(num_inputs, 1)) b = nd.zeros(shape=(1,))之后的模型訓(xùn)練中,需要對(duì)這些參數(shù)求梯度來(lái)迭代參數(shù)的值,因此我們需要?jiǎng)?chuàng)建它們的梯度。
w.attach_grad() b.attach_grad()3.2.4 定義模型
下面是線性回歸的矢量計(jì)算表達(dá)式的實(shí)現(xiàn)。我們使用dot函數(shù)做矩陣乘法。
def linreg(X, w, b): # 本函數(shù)已保存在d2lzh包中方便以后使用return nd.dot(X, w) + b3.2.5 定義損失函數(shù)
我們使用上一節(jié)描述的平方損失來(lái)定義線性回歸的損失函數(shù)。在實(shí)現(xiàn)中,我們需要把真實(shí)值y變形成預(yù)測(cè)值y_hat的形狀。以下函數(shù)返回的結(jié)果也將和y_hat的形狀相同。
def squared_loss(y_hat, y): return (y_hat - y.reshape(y_hat.shape)) ** 2 / 23.2.6 定義優(yōu)化算法
以下的sgd函數(shù)實(shí)現(xiàn)了小批量隨機(jī)梯度下降算法。它通過(guò)不斷迭代模型參數(shù)來(lái)優(yōu)化損失函數(shù)。這里自動(dòng)求梯度模塊計(jì)算得來(lái)的梯度是一個(gè)批量樣本的梯度和。我們將它除以批量大小來(lái)得到平均值。
def sgd(params, lr, batch_size): for param in params:param[:] = param - lr * param.grad / batch_size3.2.7 訓(xùn)練模型
在訓(xùn)練中,我們將多次迭代模型參數(shù)。在每次迭代中,我們根據(jù)當(dāng)前讀取的小批量數(shù)據(jù)樣本(特征X和標(biāo)簽y),通過(guò)調(diào)用反向函數(shù)backward計(jì)算小批量隨機(jī)梯度,并調(diào)用優(yōu)化算法sgd迭代模型參數(shù)。由于我們之前設(shè)批量大小batch_size為10,每個(gè)小批量的損失l的形狀為(10, 1)。回憶一下“自動(dòng)求梯度”一節(jié)。由于變量l并不是一個(gè)標(biāo)量,運(yùn)行l(wèi).backward()將對(duì)l中元素求和得到新的變量,再求該變量有關(guān)模型參數(shù)的梯度。
在一個(gè)迭代周期(epoch)中,我們將完整遍歷一遍data_iter函數(shù),并對(duì)訓(xùn)練數(shù)據(jù)集中所有樣本都使用一次(假設(shè)樣本數(shù)能夠被批量大小整除)。這里的迭代周期個(gè)數(shù)num_epochs和學(xué)習(xí)率lr都是超參數(shù),分別設(shè)3和0.03。在實(shí)踐中,大多超參數(shù)都需要通過(guò)反復(fù)試錯(cuò)來(lái)不斷調(diào)節(jié)。雖然迭代周期數(shù)設(shè)得越大模型可能越有效,但是訓(xùn)練時(shí)間可能過(guò)長(zhǎng)。
lr = 0.03 num_epochs = 3 net = linreg loss = squared_loss ? for epoch in range(num_epochs): # 訓(xùn)練模型一共需要num_epochs個(gè)迭代周期# 在每一個(gè)迭代周期中,會(huì)使用訓(xùn)練數(shù)據(jù)集中所有樣本一次(假設(shè)樣本數(shù)能夠被批量大小整除)。X# 和y分別是小批量樣本的特征和標(biāo)簽for X, y in data_iter(batch_size, features, labels):with autograd.record():l = loss(net(X, w, b), y) # l是有關(guān)小批量X和y的損失l.backward() # 小批量的損失對(duì)模型參數(shù)求梯度sgd([w, b], lr, batch_size) # 使用小批量隨機(jī)梯度下降迭代模型參數(shù)train_l = loss(net(features, w, b), labels)print('epoch %d, loss %f' % (epoch + 1, train_l.mean().asnumpy()))輸出:
epoch 1, loss 0.040552 epoch 2, loss 0.000158 epoch 3, loss 0.000051訓(xùn)練完成后,我們可以比較學(xué)到的參數(shù)和用來(lái)生成訓(xùn)練集的真實(shí)參數(shù)。它們應(yīng)該很接近。
true_w, w輸出
([2, -3.4], [[ 1.9997406][-3.4000957]]<NDArray 2x1 @cpu(0)>) true_b, b輸出:
(4.2, [4.199303]<NDArray 1 @cpu(0)>)總結(jié)
以上是生活随笔為你收集整理的《动手学深度学习》 第二天 (线性回归)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 【计算机网络复习 物理层】2.1.2 数
- 下一篇: 计算机网络(十八)-以太网