iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 17
0
自我挑戰組

深度學習所需入門知識--一位初學者的認知系列 第 17

損失函數,以Mxnet實做線性迴歸

  • 分享至 

  • xImage
  •  

在Fields一口氣講完 Linear Regression 以機器學習來計算最佳解,馬上有一些問題,首先佳麗不解的是:

”為何只有兩個特徵,面積與屋齡?而且還房價還只能與特徵有線性關係,難道不能開根號之類的關係嗎?”

Pete 立即搶答:”因為這是練習啦!幾個變數都可以,用兩個變數是因為可以少打幾個字,而且比較容易用視覺圖示呈現損失函數與特徵值的關係。我還有看過只用一個變量來說明的。”

Fields 補充:“線性迴歸模型的確不見得可以做好房價的預測,事實上因為他只有單層網路,無法解釋真實世界的問題,後面我們會再深入一點,做所謂的多層感知機 Multi-Layer Perceptron,MLP。多層的神經網路,或者說深度神經網路,較複雜的模型就更能貼近現實問題,特徵與目標值之間就不會只是線性關係。如 Pete 所言,因為是練習,目的讓大家知道機器學習有三大條件:數據,損失函數,還有演算法。”

Molly 聽到損失函數,提醒 Fields 解釋一下為何要採用MSE,Mean Squared Error

https://chart.googleapis.com/chart?cht=tx&chl=%5Cell%5E%7B(i)%7D(w_1%2C%20w_2%2C%20b)%20%3D%20%5Cfrac%7B1%7D%7B2%7D%20%5Cleft(%5Chat%7By%7D%5E%7B(i)%7D%20-%20y%5E%7B(i)%7D%5Cright)%5E2

Fields 說明:”損失函數也有人稱為目標函數,雖然學理上只要能將預測值與正確值的誤差表達出來,誤差越大損失值就必須跟著大,就可以。但是因為不同的函數對求導數,梯度計算的複雜度還有是否對演算法的收斂有加速等是有關係的,所以我們會因為不同的問題採用不同的損失函數。舉例來說,如果是分類問題,MSE 就派不上用場,一般會採用 Cross Entropy。這在後面的 Logistic Regression 的問題會說明。採用哪種損失函數這問題,不光是靠數學分析就可以得到答案,它跟資料數據也有關,例如:同樣是線性迴歸問題,一般的數據採 MSE有不錯的效果;但是如果資料有很多的異常值,採用Mean Absolute Error,MAE可能反而比較好。我建議我們先沿用別人的作法,再看有無改良空間。”

討論告一段落,又開始進入實作,Fields 因為採用 mxnet 作為框架,語法比Tensorflow簡單一點點,佳麗也可以猜出每段程式碼的用意,以下取材自 动手学深度学习


%matplotlib inline
import random #用隨機值 製造出虛擬的房價與(面積,屋齡)數據
from matplotlib import pyplot as plt #為了畫圖呈現數據
from IPython import display   
from mxnet import autograd, nd #利用 mxnet 計算梯度,ndarray 置放數據

正常情況訓練數據應該是收集來的,這邊我們隨機製造1000筆,這一千筆資料房價關係,我們假設如下:

成交價格為 https://chart.googleapis.com/chart?cht=tx&chl=%24y%24 = 25 萬元 x https://chart.googleapis.com/chart?cht=tx&chl=%24x_1%24(面積)— 6萬元 x https://chart.googleapis.com/chart?cht=tx&chl=%24x_2%24 (屋齡)+ 80萬元(屋價的起始值,上一篇文所提到Bias偏差 https://chart.googleapis.com/chart?cht=tx&chl=%24b%24)+ (個案因為屋況價差產生的浮動值)。

為了表達兩個特徵 https://chart.googleapis.com/chart?cht=tx&chl=%24x_1%24 以及 https://chart.googleapis.com/chart?cht=tx&chl=%24x_2%24 我們用 X 來代替;同樣的我們以 w 來代替兩個權重https://chart.googleapis.com/chart?cht=tx&chl=%24w_1%24 以及 https://chart.googleapis.com/chart?cht=tx&chl=%24w_2%24,前篇文的房價公式:
https://chart.googleapis.com/chart?cht=tx&chl=%5Chat%7By%7D%20%3D%20x_1%20w_1%20%2B%20x_2%20w_2%20%2B%20b

房價這個Label y變成就可以用更簡潔的線性代數符號來表達,這又可對應到 mxnet 的 ndarray:
https://chart.googleapis.com/chart?cht=tx&chl=%7By%7D%20%3D%20%7BX%7D%7Bw%7D%20%2B%20b

按照上面提到房價公式,這樣我們就可以 [25,-6] 來紀錄 w;80 來紀錄 b

num_features = 2 #有兩個特徵 x1(面積);x2(屋齡)
num_examples = 1000 #我們製造1000筆的虛擬房價樣本
true_w = [25, -6] #單位『萬元』,每坪25萬元,每年折價6萬
true_b = 80 #屋價的起始值 80 萬元
features = nd.random.normal(loc=30, scale=6, shape=(num_examples, num_features))
#面積與屋齡兩者我們都簡單的用平均面積30坪(年),標準差6坪(年)的標準分配來取值,反正是虛構的
labels = true_w[0] * features[:, 0] + true_w[1] * features[:, 1] + true_b #房價Label

但是真實房價總有屋況的差異,我們用 https://chart.googleapis.com/chart?cht=tx&chl=%5Cepsilon 來代表屋況價差,所以虛擬的房價數據Label產生公式變成:
https://chart.googleapis.com/chart?cht=tx&chl=%7By%7D%20%3D%20%7BX%7D%7Bw%7D%20%2B%20b + https://chart.googleapis.com/chart?cht=tx&chl=%5Cepsilon

屋況價差 https://chart.googleapis.com/chart?cht=tx&chl=%5Cepsilon 我們用平均為0,標準差2萬元的標準分配來取得。

labels += nd.random.normal(scale=2, shape=labels.shape) #房價Label再加上屋況價差
print(features[0], labels[0]) #印出我們虛構的第一筆房價與(面積,屋齡)數據

我們會看到
[33.177837 23.333397]
<NDArray 2 @cpu(0)>
[769.41327]
<NDArray 1 @cpu(0)>
類似的數值。33.178坪,23.333年的房子,賣了 769.413萬元,看來不像是台北的房子,也許在新竹吧。

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[:, 0].asnumpy(), labels.asnumpy(), 1); #只畫出面積與屋價的關係

https://ithelp.ithome.com.tw/upload/images/20181031/201052834dOd0LmkSr.png
這樣的圖,我們看到了面積與屋價的線性關係

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) 

這個函數是用來批量的抓上一篇文 mini-Batch 數據用的,我們另外一篇文章特別講他
我們要開始進行上一篇文演算法的幾個步驟

1.先隨機給予w,b參數

w = nd.random.normal(scale=.1, shape=(num_features, 1))
b = nd.zeros(shape=(1,))

#給予梯度計算,這是 mxnet 提供幫助計算梯度的工具
w.attach_grad()
b.attach_grad()

#定義線性迴歸
def linreg(X, w, b): 
    return nd.dot(X, w) + b 
    
#MSE損失函數    
def squared_loss(y_hat, y): 
    return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2

#定義 mini-Batch stochastic gradient descent    
def sgd(params, lr, batch_size):
    for param in params:
        param[:] = param - lr * param.grad / batch_size

2.開始訓練模型

batch_size = 10 #設 miniBatch 每次以10筆
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):
        with autograd.record():            
            l = loss(net(X, w, b), y)          
        l.backward()          
        sgd([w, b], lr, batch_size)  
        l = loss(net(features, w, b), labels)
    print('epoch %d, loss %f' % (epoch+1, l.mean().asnumpy()))

這樣我們就完成訓練

print(w,b)

Fields 留一手要大家自己實際跑一下,如果沒有意外,數字應該很接近 [25, -6], 80,亦即經過拿1000筆的數據訓練後,模型也找到了每坪25萬,每年折價6萬,起始價格是80萬起跳。


上一篇
由 Linear Regression 看機器學習演算法-mini-batch stochastic gradient descent
下一篇
由線性迴歸看機器學習的一些技術細節, 資料集數據 & 梯度計算
系列文
深度學習所需入門知識--一位初學者的認知31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言