《动手学深度学习》 第二天 (线性回归)
3.2 線性回歸的從零開始實現
只利用NDArray和autograd來實現一個線性回歸的訓練。
首先,導入本節中實驗所需的包或模塊,其中的matplotlib包可用于作圖,且設置成嵌入顯示。
%matplotlib inline from IPython import display from matplotlib import pyplot as plt from mxnet import autograd, nd import random3.2.1 生成數據集
我們構造一個簡單的人工訓練數據集,它可以使我們能夠直觀比較學到的參數和真實的模型參數的區別。
設訓練數據集樣本數為1000,輸入個數(特征數)為2。
給定隨機生成的批量樣本特征𝑋∈?1000×2,
我們使用線性回歸模型真實權重𝑤=[2,?3.4]?和偏差𝑏=4.2,以及一個隨機噪聲項𝜖來生成標簽 𝑦=𝑋𝑤+𝑏+𝜖,
其中噪聲項𝜖服從均值為0、標準差為0.01的正態分布。噪聲代表了數據集中無意義的干擾。下面,讓我們生成數據集。
注意,features的每一行是一個長度為2的向量,而labels的每一行是一個長度為1的向量(標量)。
features[0], labels[0]輸出:
([2.2122064 0.7740038]<NDArray 2 @cpu(0)>, [6.000587]<NDArray 1 @cpu(0)>)通過生成第二個特征features[:, 1]和標簽 labels 的散點圖,可以更直觀地觀察兩者間的線性關系。
def use_svg_display():# 用矢量圖顯示display.set_matplotlib_formats('svg') ? def set_figsize(figsize=(3.5, 2.5)):use_svg_display()# 設置圖的尺寸plt.rcParams['figure.figsize'] = figsize ? set_figsize() plt.scatter(features[:, 1].asnumpy(), labels.asnumpy(), 1); # 加分號只顯示圖我們將上面的plt作圖函數以及use_svg_display函數和set_figsize函數定義在d2lzh包里。以后在作圖時,我們將直接調用d2lzh.plt。由于plt在d2lzh包中是一個全局變量,我們在作圖前只需要調用d2lzh.set_figsize()即可打印矢量圖并設置圖的尺寸。
3.2.2 讀取數據集
在訓練模型的時候,我們需要遍歷數據集并不斷讀取小批量數據樣本。這里我們定義一個函數:它每次返回batch_size(批量大小)個隨機樣本的特征和標簽。
def data_iter(batch_size, features, labels):num_examples = len(features)indices = list(range(num_examples))random.shuffle(indices) # 樣本的讀取順序是隨機的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函數根據索引返回對應元素讓我們讀取第一個小批量數據樣本并打印。每個批量的特征形狀為(10, 2),分別對應批量大小和輸入個數;標簽形狀為批量大小。
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 初始化模型參數
我們將權重初始化成均值為0、標準差為0.01的正態隨機數,偏差則初始化成0。
w = nd.random.normal(scale=0.01, shape=(num_inputs, 1)) b = nd.zeros(shape=(1,))之后的模型訓練中,需要對這些參數求梯度來迭代參數的值,因此我們需要創建它們的梯度。
w.attach_grad() b.attach_grad()3.2.4 定義模型
下面是線性回歸的矢量計算表達式的實現。我們使用dot函數做矩陣乘法。
def linreg(X, w, b): # 本函數已保存在d2lzh包中方便以后使用return nd.dot(X, w) + b3.2.5 定義損失函數
我們使用上一節描述的平方損失來定義線性回歸的損失函數。在實現中,我們需要把真實值y變形成預測值y_hat的形狀。以下函數返回的結果也將和y_hat的形狀相同。
def squared_loss(y_hat, y): return (y_hat - y.reshape(y_hat.shape)) ** 2 / 23.2.6 定義優化算法
以下的sgd函數實現了小批量隨機梯度下降算法。它通過不斷迭代模型參數來優化損失函數。這里自動求梯度模塊計算得來的梯度是一個批量樣本的梯度和。我們將它除以批量大小來得到平均值。
def sgd(params, lr, batch_size): for param in params:param[:] = param - lr * param.grad / batch_size3.2.7 訓練模型
在訓練中,我們將多次迭代模型參數。在每次迭代中,我們根據當前讀取的小批量數據樣本(特征X和標簽y),通過調用反向函數backward計算小批量隨機梯度,并調用優化算法sgd迭代模型參數。由于我們之前設批量大小batch_size為10,每個小批量的損失l的形狀為(10, 1)。回憶一下“自動求梯度”一節。由于變量l并不是一個標量,運行l.backward()將對l中元素求和得到新的變量,再求該變量有關模型參數的梯度。
在一個迭代周期(epoch)中,我們將完整遍歷一遍data_iter函數,并對訓練數據集中所有樣本都使用一次(假設樣本數能夠被批量大小整除)。這里的迭代周期個數num_epochs和學習率lr都是超參數,分別設3和0.03。在實踐中,大多超參數都需要通過反復試錯來不斷調節。雖然迭代周期數設得越大模型可能越有效,但是訓練時間可能過長。
lr = 0.03 num_epochs = 3 net = linreg loss = squared_loss ? for epoch in range(num_epochs): # 訓練模型一共需要num_epochs個迭代周期# 在每一個迭代周期中,會使用訓練數據集中所有樣本一次(假設樣本數能夠被批量大小整除)。X# 和y分別是小批量樣本的特征和標簽for X, y in data_iter(batch_size, features, labels):with autograd.record():l = loss(net(X, w, b), y) # l是有關小批量X和y的損失l.backward() # 小批量的損失對模型參數求梯度sgd([w, b], lr, batch_size) # 使用小批量隨機梯度下降迭代模型參數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訓練完成后,我們可以比較學到的參數和用來生成訓練集的真實參數。它們應該很接近。
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)>)總結
以上是生活随笔為你收集整理的《动手学深度学习》 第二天 (线性回归)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【计算机网络复习 物理层】2.1.2 数
- 下一篇: 《动手学深度学习 PyTorch版》学习