線性回歸
標簽(空格分隔): 深度學習
我們舉一個實際的例子:我們希望根據房屋的面積和房齡來預計房屋價格。為了開發一個能夠預測房價的模型,我們需要收集一個真實的數據集。
-
該數據集包括:房屋的銷售價格,房齡和面積。在機器學習的術語中,該數據集成為訓練數據集或訓練集,每一行數據被稱為樣本,也可成為數據點或者數據樣本。我們預測的目標(在這個例子中是房屋價格)成為標簽或目標。預測所根據的自變量成為特征或者協變量。
-
通常我們使用\(n\)來表示數據集中的樣本數量。對搜因為\(i\)的樣本,其輸入表示為\(X^{(i)}=[x_{1}^{(i)},x_2^{(2)}]^T\),其輸出對應的標簽是\(y^{(i)}\)
線性模型
線性假設是指目標可以表示為特征的加權和,如下面的式子:$$price = \omega_{area}\cdot area+\omega_{age}\cdot age +b \tag1$$
中的\(\omega_{area}\)和\(\omega_{age}\)稱為權重,\(b\)稱為偏置(bias),或稱為偏移量(offset)、截距(intercept)。權重決定了每個特征對我們預測值的影響,偏執是指當所有特征都取為0時,預測值應該為多少。即使現實中不會有任何房子的面積是0 或者房齡正好為0年,我們仍然需要偏置項。如果沒有偏置項,我們模型表的能力將受到限制。嚴格的講\(公式(1)\)*是輸入特征的一個仿射變換**。仿射變換的特點就是通過加u去哪和特征進行線性變換,並通過偏置項來進行平移。
給定一個數據集,我們的目標是尋找模型的權重\(W\)和偏置\(b\),使得根據模型做出的預測答題符合數據里的真實價格。輸出的預測值由輸入特征通過線性模型的仿射變換決定,仿射變換由所選權重和偏置決定。
- 有些學科往往只關注有少量特征的數據集,在這些學科中,建模時經常想這樣通過長形式顯示地表達。而在機器學習領域,我們通常使用的是高維數據集,建模時采用線性代數表示法會比較方便。當我們的輸入包含\(d\)個特征時、我們將預測結果\(\hat{y}\)(通常使用“尖號”符號表示估計值)表示為:$$\hat{y}=\omega_1x_1+\dots+\omega_dx_d+b \tag2$$
將特征向量放到\(x\in\mathbb{R}^d\)中,並將所有權重放到\(w\in\mathbb{R}^d\)中,我們可以用點積形式來簡潔的表達模型:$$\hat{y}=w^Tx+b\tag3$$
在公式3中,向量\(x\)對用於單個數據特征的樣本。用符號表示的矩陣\(X\in\mathbb{R}^{x\times{d}}\)可以很方便的引用我們整個數據集的n個樣本。其中,X的每一行是一個樣本,每一列是一種特征。
對於特征集合\(X\),預測值\(\hat{y}\in\mathbb{R}^n\)可以通過\(矩陣-向量\)乘法表示為:\(\hat{y}=Xw+b\tag4\)
損失函數
在我們開始考慮如何用模型擬合數據之前,我們需要確定一個擬合程度的度量。損失函數能夠量化目標的實際值和預測值之間的差距。通常我們回選擇非負數作為損失,且數值越小表示損失越小,完美與測試的損失為0,回歸問題中最常用的損失函數是平方誤差數。當樣本\(i\)的預測值為\(\hat{y}^i\)其對應的真是標簽為\(y^{(i)}\)時,平方誤差可以定義為以下公式:$$l{(i)}(\boldsymbol{w},b)=\frac{1}{2}(\hat{y}{(i)}-y{i})2\tag5$$
常數\(\frac{1}{2}\)不會帶來本質的差別,但這樣在形式上稍微簡單一些,表現為我們對損失函數求導后常數系數為1. 此處的平方時因為 較大的差異值可以帶來更大的損失函數。為了度量模型在整個數據集上的質量,我們需要計算在訓練集n個樣本上的損失均值。$$L(\boldsymbol{w},b)=\frac{1}{n}\sum_{i=1}nl{(i)}(\boldsymbol{w},b)=\frac{1}{2n}\sum_{i=1}n(wTx{(i)}+b-y{(i)})^2\tag6$$
在訓練模型的時候,我們希望能夠尋找一組參數,這組參數可以最小換在所有訓練樣本上的總損失$$(\boldsymbol{w}*,b*)=argmin L(w,b)\tag7$$
解析解
線性回歸剛好是一個很簡單的優化問題。線性回歸的解可以用一個公式簡單的表達出來,這類解叫做解析解(analytical solution)。首先,我們將偏置b合並到參數w中。合並方法是在包含所有參數的矩陣中附加一列。我們預測的問題是最小化\(\Vert y-Xw\Vert^2\tag8\)。在這個損失平面上有一個臨界點,這個臨界點對應於整個區域的損失最小值。將損失關於\(\boldsymbol{w}\)的倒數設為0,得到解析解:\(w^*=(X^TX)^{-1}X^Ty\tag9\)像線性回歸這樣的解析問題存在解析解,但並不是所有的問題都存在解析解。解析解可以很好的進行數學分析,但解析解的限制很嚴格,導致它無法應用在深度學習里面。
小批量隨機梯度下降
即使在我們無法得到解析解的情況下,我們仍然可以有效的訓練模型。在許多任務上,那些難以優化的模型效果要更好。因此,弄清楚如何訓練這些難以優化的模型是非常重要的。
本書中我們用到一種名梯度下降(gradient descent)的方法,這種方法幾乎可以優化所有深度學習模型。它通過不斷的在損失函數的遞減的方向更新參數來降低誤差。
梯度下降最簡單的用法是計算損失函數(數據集中所有樣本的損失均值)關於模型參數的導數(在這里也可以稱為梯度)。但是在實際的執行中可能會非常慢:因為在每一次更新參數之前,我們必須便利整個數據集。因此我們通常會在每次需要計算更新的時候隨機抽取一批小樣本,這種變體叫做小樣本隨機梯度下降(minibatch stochastic gradient descent)。
在每次迭代中,我們首先隨機抽取一個小批量\(\beta\),它是由固定數量的訓練樣本組成的。然后,我們計算小批量的平均損失關於模型參數的導數(也稱之為梯度)。最后,我們將梯度乘以一個預先確定正數\(\eta\),並從當前參數的值中減掉。
- 根據權重\(\omega\)和偏置\(\beta\)生成num_example個人造數據。其中x是訓練集的數據,y是標簽,在標簽上加上一點標准差為0.01的噪音,
true_w = torch.tensor([2, -3.4])
true_b = 4.2
features, labels = synthetic_data(true_w, true_b, 1000)
def synthetic_data(w, b, num_examples):
x = torch.normal(0, 1, (num_examples, len(w)))
y = torch.matmul(x, w) + b
y += torch.normal(0, 0.01, y.shape)
return x, y.reshape((-1, 1))
生成每批大小為batch_size的數據進行小樣本訓練,獲取總的樣本數量,然后將其打亂。返回batch_size大小的特征和標簽。
def data_iter(batch_size, features, labels):
num_example = len(features) # 特征數量
indices = list(range(num_example))
random.shuffle(indices) # 洗牌 特征順序打亂。
for i in range(0, num_example, batch_size):
batch_indices = torch.tensor(indices[i:min(i + batch_size, num_example)])
yield features[batch_indices], labels[batch_indices]
在開始下批量隨機梯度下賤優化我們的模型參數之前,我們需要一些參數,在下面的代碼當中,我們從均值為0,標准差為0.01的正態分布中隨機采樣隨機數來初始化權重,並將偏置初始化為0.
w = torch.normal(0, 0.01, size=(2, 1), requires_grad=True)
b = torch.zeros(1, requires_grad=True)
在初始參數之后,我們的任務是更新這些參數,直到這些參數足夠你和我們的數據,每次更新都需要計算損失函數關於模型參數的梯度,有了這個梯度,我們就可以向減小損失的方向更新每個參數。
- w 預設的均值為0, 標准差為0.01
- b 預設的初值為0
- x 有大量(n)的已知數據
- y 有大量(n)的已知數據
目標$$L(\boldsymbol{w},b)=\frac{1}{n}\sum_{i=1}nl{(i)}(\boldsymbol{w},b)=\frac{1}{2n}\sum_{i=1}n(wTx{(i)+b}-y{(i)})^2\tag6$$通過改變\(\boldsymbol{w},b\)盡量降低\(L\).
定義模型
def linreg(x,w,b):
return torch.matmul(x,w)+b
損失函數
def squared_loss(y_hat, y):
return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2
優化算法
介紹小批量隨機梯度下降的工作示例。
在每一步中,使用從數據集中隨機抽取的一個小批量,然后根據參數計算損失的梯度。接下來,朝着減少損失的方向更新我們的參數。下面的函數實現小批量隨機梯度下降更新。該函數接受模型參數集合,學習速率和批量大小作為輸入。每一步的更新大小由學習速率\(lr\)決定。因為我們計算的損失是一個批量樣本的綜合,所以我們用批量大小來歸一化步長,這樣步長大小就不會取決於我們對批量大小的選擇。
def sgd(params,lr,batch_size):
with torch.no_grad():
for param in params:
param -= lr* param.grad/batch_size
param.grad.zero_()
訓練
在每次迭代中,我們讀取一個小批量訓練樣本,並通過我們的模型來獲取一組預測。計算完損失之后,我們開始反向傳播,存儲每個參數的梯度。最后我們調用sgd來更新模型參數。
lr = 0.03
num_epochs = 3
net = linreg
loss = squared_loss
for epoch in range(num_epochs):
for x,y in data_iter(batch_size,features,labels):
l = loss(net(x,w,b),y)
l.sum().backward()
sgd([w,b],lr,batch_size)
with torch.no_grad():
train_l = loss(net(features,w,b),labels)
print(f'epoch {epoch+1},loss{float(train_l.mean()):f}')