在本節中將通過一個預測房屋價格的實例來講解利用線性回歸預測房屋價格,以及在tensorflow中如何實現 ...
在本節中將通過一個預測房屋價格的實例來講解利用線性回歸預測房屋價格,以及在tensorflow中如何實現
1.1. 準備工作
從網上得到的數據可以看到房屋價格與房屋尺寸的一個對比關係,如下圖:
我們假設x軸(房屋尺寸)而Y軸(房屋價格)依據上表數據繪製折線圖
現在我們使用簡單的線性模型來預測,
- 紅線表述我們的預測曲線 : \[y_p=ax+b\]
- 藍線表述房屋價格與尺寸的實際關係
- 預測與實際的不同用黃線表示
接下來需要通過數據來找到a,b的最佳值從而使預測與實際的誤差最小。此次我們採用SSE(和方差)來判別誤差。該統計參數計算的是擬合數據和原始數據對應點的誤差的平方和,計算公式如下
\[\frac{1}{2}\sum_{k=1}^{n} \ {({y} -{y_p})^2}\]
在拿到原始的數據後,為方便運算,我們將數據進行歸一化處理,歸一化計算公式如下
\[\frac{x-x_{min}}{x_{max}-x_{min}}\]
1.2. 歸一化數據
我們將原始的數據進行歸一化處理,歸一化處理後的結果如圖:
def normalize(arr):
arr_min = np.min(arr)
arr_max = np.max(arr)
arr_out = []
for item in arr:
out = np.divide(np.subtract(item, arr_min), np.subtract(arr_max, arr_min))
arr_out = np.append(arr_out, np.array(out))
return arr_out
1.3. 用隨機的值填充a,b並計算誤差,誤差採用上文所使用SSE(和方差)
def model(x, b, a):
# linear regression is just b*x + a, so this model line is pretty simple
return tf.multiply(x, b) + a
loss = tf.multiply(tf.square(Y - y_model), 0.5)
1.4. 計算誤差梯度
對sse分別求a,b的偏微分
\[\frac{\partial sse}{\partial a}\]
\[\frac{\partial sse}{\partial b}\]
1.5. 調整參數直到SSE參數最小
新 a = a – r * ∂SSE/∂a = 0.45-0.01*3.300 = 0.42
新 b = b – r * ∂SSE/∂b= 0.75-0.01*1.545 = 0.73
(r是學習率,表示調整的步長)
# construct an optimizer to minimize cost and fit line to mydata
train_op = tf.train.GradientDescentOptimizer(0.01).minimize(loss)
然後再重覆上一步驟計算,直到所設定的次數完成
for i in range(500):
for (x, y) in zip(trX, trY):
output = sess.run(train_op, feed_dict={X: x, Y: y})
通過剛纔幾步的組合,程式便能計算出最合適的a,b的值,完成代碼清單如下:
import tensorflow as tf
import numpy as np
sess = tf.Session()
# 線性模型 y=bx+a
def model(x, b, a):
return tf.multiply(x, b) + a
# 歸一化函數
def normalize(arr):
arr_min = np.min(arr)
arr_max = np.max(arr)
arr_out = []
for item in arr:
out = np.divide(np.subtract(item, arr_min), np.subtract(arr_max, arr_min))
arr_out = np.append(arr_out, np.array(out))
return arr_out
# 原始數據
trX_i = [1100., 1400., 1425., 1550., 1600., 1700., 1700., 1875., 2350., 2450.]
trY_i = [199000., 245000., 319000., 240000., 312000., 279000., 310000., 308000., 405000., 324000.]
# 數據歸一化
trX = normalize(trX_i)
trY = normalize(trY_i)
X = tf.placeholder(tf.float32)
Y = tf.placeholder(tf.float32)
# 設一個權重變數b,和一個偏差變數a
b = tf.Variable(0.0, name="weights")
# create a variable for biases
a = tf.Variable(0.0, name="biases")
y_model = model(X, b, a)
# 損失函數
loss = tf.multiply(tf.square(Y - y_model), 0.5)
# 梯度下降
train_op = tf.train.GradientDescentOptimizer(0.01).minimize(loss)
init = tf.global_variables_initializer()
sess.run(init)
# 訓練數據
for i in range(500):
for (x, y) in zip(trX, trY):
output = sess.run(train_op, feed_dict={X: x, Y: y})
print('b:' + str(sess.run(b)) + ' || a:' + str(sess.run(a)))
---result
b:0.682465 || a:0.1512
1.6. 概念
1.6.1. 簡單線性回歸
在房價預測例子中,我們發現房價數據呈一種比較明顯的線性關係,那麼自然我們可能會選擇簡單線性回歸對數據進行擬合,首先從線性模型著手:
\[y_p=ax+b\]
從上面的二元一次方程看出,我們的輸入x是已知向量,只要我們求出a,b的值,就能通過上述公式進行房價預測了,這就是簡單線性回歸的思想。
1.6.2. 梯度下降
梯度
如上一節中講的我們需要找出SSE最小化時的a,b的值,採用的這種方法就叫做梯度下降。梯度下降不僅僅局限於最小化這個函數,也可能根據實際情況需要最大化某個函數,這種情況叫做梯度上升。單純從數學上講,對一個函數來說,梯度表示某個向量的偏導數,同時還代表了該向量的方向,在這個方向上,函數增加得最快,在相反的方向上,函數減小得最快。
利用梯度這一性質,我們採用梯度下降演算法去最小化我們的損失函數,我們在梯度的反方向跨域一小步,再從一個新起點開始重覆這個過程,直到我們找到損失函數的最小值,最後確定我們的a, b值。
我們需要最小化的函數為(又稱為損失函數):
\[sse=\frac{1}{2}\sum_{k=1}^{n} \ {({y} -{y_p})^2}=\frac{1}{2}\sum_{k=1}^{n} \ {(y_k-ax_k-b)^2}\]
對a,b分別求偏導,並令偏導等於0:
\[\frac{\partial sse}{\partial a}=- \sum_{k=1}^n \ x_k(y_k-ax_k-b) \ =0\]
\[\frac{\partial sse}{\partial b}=- \sum_{k=1}^n \ (y_k-ax_k-b) =0\]
最後,輸入已知的x和y值(均為向量),解兩個一次方程就計算出a,b的確切值。
步長
為了求SSE的最小值,我們需要向梯度相反的方法移動,每移動一步,梯度逐漸降低,但是移動多少才合適呢,這需要我們謹慎的選擇步長。目前,主流的選擇方法有:
• 使用固定步長
• 隨時間增長逐步減小步長
• 在每一步中通過最小化目標函數的值來選擇合適的步長
在上一例子中,我們選擇固定步長r=0.01,其實,最後一種方法很好,但它的計算代價很大。我們還可以嘗試一系列步長,並選出使目標函數值最小的那個步長來求其近似值。
stepSizes=[10, 1, 0.1, 0.01, 0.001]
1.6.3 損失函數
損失函數是用來評價模型的預測值與真實值的不一致程度,它是一個非負實值函數。通常使用L(Y,f(x))來表示,損失函數越小,模型的性能就越好。
在預測房價的例子中,我們使用了和方差來計算誤差,並把該函數稱為損失函數,即計算實際值和預測值的誤差平方和。為什麼要選擇這一函數來計算誤差,而不採用絕對值誤差,或誤差的三次方,四次方來定義誤差函數是因為:
- 相對於絕對值誤差,誤差平方和計算更加方便。
- 這裡的損失函數使用的是“最小二乘法”的思想,假定我們的誤差滿足均值為0的高斯分佈,這樣符合一般的統計規律,然後根據最大似然函數估計進行推導,就得出了求導結果,平方和最小公式:
\[sse=\frac{1}{2}\sum_{k=1}^{n} \ {({y} -{y_p})^2}\]
除上面提到的損失函數外,還有其他的一些常見的損失函數:
0-1 Loss
如果預測值與標值不等,則記為1;如果相等,則標記為0
\[L(Y, f(x)) = \left\{ \begin{array}{ll}
1 & \textrm{$Y\neq f(x)$}\\
0 & \textrm{$Y= f(x)$}
\end{array} \right.
\]
Log對數損失函數
在邏輯回歸中損失函數的推導是假設樣本服從伯努利分佈(0-1分佈),然後求滿足該分佈的似然函數,最後推導出順勢函數的公式為:\[L(Y,P(Y|X)) = -logP(Y|X)\]
指數損失函數
出現在Adaboost演算法中
\[L(y,f(x))=\frac{1}{n}\sum_{i=1}^{n}\ {exp[-y_if(x_i)]}\]
Hinge損失函數
線上性支持向量機中,Hinge的損失函數標準形式為:
\[L(y)=\frac{1}{n}\sum_{i=1}^{n}\ {l(wx_i+by_i)}\]
絕對值損失函數
\[L(y,f(x))=|Y-f(x)|\]
1.6.4 特征歸一化
對於多屬性的樣本,我們在做分類預測的時候,應該把每個屬性看作同等重要,不能讓某個屬性的計算結果嚴重影響模型的預測結果。例如,以下有一個樣本數據:
玩游戲所耗時間百分比 | 描述每年獲得的飛行常客里程數 | 每周消費的冰淇淋公升數 |
---|---|---|
0.8 | 400 | 0.5 |
12 | 134000 | 0.9 |
0 | 20000 | 1.1 |
67 | 32000 | 0.1 |
如果我們採用KNN演算法做分類預測,在計算歐式距離的時候,比如計算樣本3和樣本4之間的距離,很明顯我們發現每年獲得的飛行常客里程數由於本身數值很大,其計算結果的影響將遠遠大於其他兩個特征值的影響,對於三個等權重的特征之一,我們不能讓它嚴重的影響計算結果,所以,我們通常會採用特征歸一化的方法把值處理為0到1或者-1到1之間。
\[\sqrt{(0-67)^2+(20000-32000)^2+(1.1-0.1)^2}\]
即上面提到的公式:
\[\frac{x-x_{min}}{x_{max}-x_{min}}\]
其中\(x_{min}\)和\(x_{max}\)是特征向量x的最小值和最大值,這樣通過對每個特征向量進行歸一化處理,所有特征值的計算都統一了,而計算得到的結果就更加準確。
在之前預測房價的例子中,我們對已有的特征向量,即房屋大小和實際價格做了歸一化處理,即便是只有一個特征向量,我們仍然需要這樣做,其目的與上面的樣本數據一樣,比如假設我們需要在該房屋預測中增加房間數量或房屋年齡等特征進行房屋價格預測,我們都可以採用同一類方法進行處理,以減少各特征值對計算結果的影響。
參考鏈接
【1】:http://www.kdnuggets.com/2017/04/simple-understand-gradient-descent-algorithm.html