深入學習深度學習——線性迴歸

線性迴歸

主要內容包括:

  1. 線性迴歸的基本要素
  2. 線性迴歸模型從零開始的實現

1 線性迴歸的基本要素

模型

爲了簡單起見,這裏我們假設價格只取決於房屋狀況的兩個因素,即面積(平方米)和房齡(年)。接下來我們希望探索價格與這兩個因素的具體關係。線性迴歸假設輸出與各個輸入之間是線性關係:

price=wareaarea+wageage+b \mathrm{price} = w_{\mathrm{area}} \cdot \mathrm{area} + w_{\mathrm{age}} \cdot \mathrm{age} + b

數據集

我們通常收集一系列的真實數據,例如多棟房屋的真實售出價格和它們對應的面積和房齡。我們希望在這個數據上面尋找模型參數來使模型的預測價格與真實價格的誤差最小。在機器學習術語裏,該數據集被稱爲訓練數據集(training data set)或訓練集(training set),一棟房屋被稱爲一個樣本(sample),其真實售出價格叫作標籤(label),用來預測標籤的兩個因素叫作特徵(feature)。特徵用來表徵樣本的特點。

損失函數

在模型訓練中,我們需要衡量價格預測值與真實值之間的誤差。通常我們會選取一個非負數作爲誤差,且數值越小表示誤差越小。一個常用的選擇是平方函數。 它在評估索引爲 ii 的樣本誤差的表達式爲

l(i)(w,b)=12(y^(i)y(i))2, l^{(i)}(\mathbf{w}, b) = \frac{1}{2} \left(\hat{y}^{(i)} - y^{(i)}\right)^2,

L(w,b)=1ni=1nl(i)(w,b)=1ni=1n12(wx(i)+by(i))2. L(\mathbf{w}, b) =\frac{1}{n}\sum_{i=1}^n l^{(i)}(\mathbf{w}, b) =\frac{1}{n} \sum_{i=1}^n \frac{1}{2}\left(\mathbf{w}^\top \mathbf{x}^{(i)} + b - y^{(i)}\right)^2.

優化函數 - 隨機梯度下降

當模型和損失函數形式較爲簡單時,上面的誤差最小化問題的解可以直接用公式表達出來。這類解叫作解析解(analytical solution)。本節使用的線性迴歸和平方誤差剛好屬於這個範疇。然而,大多數深度學習模型並沒有解析解,只能通過優化算法有限次迭代模型參數來儘可能降低損失函數的值。這類解叫作數值解(numerical solution)。

在求數值解的優化算法中,小批量隨機梯度下降(mini-batch stochastic gradient descent)在深度學習中被廣泛使用。它的算法很簡單:先選取一組模型參數的初始值,如隨機選取;接下來對參數進行多次迭代,使每次迭代都可能降低損失函數的值。在每次迭代中,先隨機均勻採樣一個由固定數目訓練數據樣本所組成的小批量(mini-batch)B\mathcal{B},然後求小批量中數據樣本的平均損失有關模型參數的導數(梯度),最後用此結果與預先設定的一個正數的乘積作爲模型參數在本次迭代的減小量。

(w,b)(w,b)ηBiB(w,b)l(i)(w,b) (\mathbf{w},b) \leftarrow (\mathbf{w},b) - \frac{\eta}{|\mathcal{B}|} \sum_{i \in \mathcal{B}} \partial_{(\mathbf{w},b)} l^{(i)}(\mathbf{w},b)

學習率: η\eta代表在每次優化中,能夠學習的步長的大小
批量大小: B\mathcal{B}是小批量計算中的批量大小batch size

總結一下,優化函數的有以下兩個步驟:

  • (i)初始化模型參數,一般來說使用隨機初始化;
  • (ii)我們在數據上迭代多次,通過在負梯度方向移動參數來更新每個參數。

矢量計算

在模型訓練或預測時,我們常常會同時處理多個數據樣本並用到矢量計算。在介紹線性迴歸的矢量計算表達式之前,讓我們先考慮對兩個向量相加的兩種方法。

  1. 向量相加的一種方法是,將這兩個向量按元素逐一做標量加法。
  2. 向量相加的另一種方法是,將這兩個向量直接做矢量加法。
import torch
import time

# 初始化變量 a, b (1000 dimension vector)
n = 1000
a = torch.ones(n)
b = torch.ones(n)
# 定義一個記錄時間的類 Timer
class Timer(object):
    """Record multiple running times."""
    def __init__(self):
        self.times = []
        self.start()

    def start(self):
        # start the timer
        self.start_time = time.time()

    def stop(self):
        # stop the timer and record time into a list
        self.times.append(time.time() - self.start_time)
        return self.times[-1]

    def avg(self):
        # calculate the average and return
        return sum(self.times)/len(self.times)

    def sum(self):
        # return the sum of recorded time
        return sum(self.times)

現在我們可以來測試了。首先將兩個向量使用for循環按元素逐一做標量加法。

timer = Timer()
c = torch.zeros(n)
for i in range(n):
    c[i] = a[i] + b[i]
'%.5f sec' % timer.stop()

結果是 ‘0.01118 sec’
另外是使用torch來將兩個向量直接做矢量加法:

timer.start()
d = a + b
'%.5f sec' % timer.stop()

結果是 ‘0.00036 sec’

結果很明顯,後者比前者運算速度更快。因此,我們應該儘可能採用矢量計算,以提升計算效率。

2 線性迴歸模型從零開始的實現

#導入所需的包和模塊
%matplotlib inline
import torch
from IPython import display
from matplotlib import pyplot as plt
import numpy as np
import random

print(torch.__version__)

生成數據集

使用線性模型來生成數據集,生成一個1000個樣本的數據集,下面是用來生成數據的線性關係:

price=wareaarea+wageage+b \mathrm{price} = w_{\mathrm{area}} \cdot \mathrm{area} + w_{\mathrm{age}} \cdot \mathrm{age} + b

# set input feature number 
num_inputs = 2
# set example number
num_examples = 1000

# set true weight and bias in order to generate corresponded label
true_w = [2, -3.4]
true_b = 4.2

features = torch.randn(num_examples, num_inputs,
                      dtype=torch.float32)
labels = true_w[0] * features[:, 0] + true_w[1] * features[:, 1] + true_b
labels += torch.tensor(np.random.normal(0, 0.01, size=labels.size()),
                       dtype=torch.float32)

使用圖像來展示生成的數據

plt.scatter(features[:, 1].numpy(), labels.numpy(), 1);

在這裏插入圖片描述

讀取數據集

def data_iter(batch_size, features, labels):
    num_examples = len(features)
    indices = list(range(num_examples))
    random.shuffle(indices)  # random read 10 samples
    for i in range(0, num_examples, batch_size):
        j = torch.LongTensor(indices[i: min(i + batch_size, num_examples)]) # the last time may be not enough for a whole batch
        yield  features.index_select(0, j), labels.index_select(0, j)
batch_size = 10

for X, y in data_iter(batch_size, features, labels):
    print(X, '\n', y)
    break

運行結果

tensor([[ 1.2052, -0.7449],
        [ 0.3211, -2.3908],
        [-0.5649, -0.9697],
        [ 0.4639, -0.4403],
        [ 0.3242, -2.2738],
        [-0.8496,  1.4918],
        [-0.7569, -0.0796],
        [ 0.1765, -0.4085],
        [ 0.5408, -2.4188],
        [ 1.3724,  1.8787]]) 
 tensor([ 9.1259, 12.9618,  6.3639,  6.6171, 12.5766, -2.5601,  2.9620,  5.9395,
        13.5043,  0.5678])

初始化模型參數

w = torch.tensor(np.random.normal(0, 0.01, (num_inputs, 1)), dtype=torch.float32)
b = torch.zeros(1, dtype=torch.float32)

w.requires_grad_(requires_grad=True)
b.requires_grad_(requires_grad=True)

定義模型

定義用來訓練參數的訓練模型:

price=wareaarea+wageage+b \mathrm{price} = w_{\mathrm{area}} \cdot \mathrm{area} + w_{\mathrm{age}} \cdot \mathrm{age} + b

def linreg(X, w, b):
    return torch.mm(X, w) + b

定義損失函數

我們使用的是均方誤差損失函數:
l(i)(w,b)=12(y^(i)y(i))2, l^{(i)}(\mathbf{w}, b) = \frac{1}{2} \left(\hat{y}^{(i)} - y^{(i)}\right)^2,

def squared_loss(y_hat, y): 
    return (y_hat - y.view(y_hat.size())) ** 2 / 2

定義優化函數

在這裏優化函數使用的是小批量隨機梯度下降:

(w,b)(w,b)ηBiB(w,b)l(i)(w,b) (\mathbf{w},b) \leftarrow (\mathbf{w},b) - \frac{\eta}{|\mathcal{B}|} \sum_{i \in \mathcal{B}} \partial_{(\mathbf{w},b)} l^{(i)}(\mathbf{w},b)

  def sgd(params, lr, batch_size): 
    for param in params:
        param.data -= lr * param.grad / batch_size # ues .data to operate param without gradient track

訓練

當數據集、模型、損失函數和優化函數定義完了之後就可來準備進行模型的訓練了。

# super parameters init
lr = 0.03
num_epochs = 5

net = linreg
loss = squared_loss

# training
for epoch in range(num_epochs):  # training repeats num_epochs times
    # in each epoch, all the samples in dataset will be used once
    
    # X is the feature and y is the label of a batch sample
    for X, y in data_iter(batch_size, features, labels):
        l = loss(net(X, w, b), y).sum()  
        # calculate the gradient of batch sample loss 
        l.backward()  
        # using small batch random gradient descent to iter model parameters
        sgd([w, b], lr, batch_size)  
        # reset parameter gradient
        w.grad.data.zero_()
        b.grad.data.zero_()
    train_l = loss(net(features, w, b), labels)
    print('epoch %d, loss %f' % (epoch + 1, train_l.mean().item()))
w, true_w, b, true_b
(tensor([[ 2.0005],
         [-3.4005]], requires_grad=True),
 [2, -3.4],
 tensor([4.1999], requires_grad=True),
 4.2)

softmax:
https://blog.csdn.net/boke14122621/article/details/104316090
多層感知機:
https://blog.csdn.net/boke14122621/article/details/104320813
語言模型及RNN:
https://blog.csdn.net/boke14122621/article/details/104321048

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章