在單特徵線性迴歸模型中,我們通過一個特徵對目標變數進行預測,例如通過房子的大小來預測房價。但實際現實生活中,影響房價的因素往往不止面積一個,例如還有房間數、樓層、位置等等,所以我們需要用到多特徵的模型來對房價進行預測。
xj:第j個特徵
n:特徵的數量
x(i):第i個訓練樣本,是一個包含n個特徵的行向量
xj(i):表示第i個樣本的第j個特徵
使用向量化可以簡化模型,減少計算量和程式碼量,提升程式碼的執行速度。
線性迴歸中一種尋找引數w、b的方式,該方法不用進行多次迭代梯度下降,直接使用高階線性代數知識求解。但是正規方程無法推廣到其他機器學習演演算法,且當樣本特徵數量非常大時,執行速度非常慢。某些機器學習庫會在後臺使用這種方法解出w、b,但是大多數情況下都不會使用。
特徵縮放是機器學習中一項重要的技術,可大大提升梯度下降的速度。
在預測房價的例子中,假設有兩個影響房價的特徵,面積和房間數,面積的取值範圍是300-5000,房間數的取值範圍是0-5,我們會發現面積和房間數的取值範圍相差過大。當我們選擇引數時,如果面積的引數(也稱為權重)較大而房間數的引數較小,這樣會導致最終預測的價格與實際的價格偏差較大,因為面積因素佔的權重較大,對房價的影響佔主要部分。為預測更精準,面積的引數應該選擇較小的,房間數的引數應該選擇較大的。
特徵縮放對梯度下降會產生很大的影響,如圖
特徵的範圍差異過大時,在散點圖中,樣本點都集中特徵範圍較大的那一側,在在成本函數等高線圖中,圖形變得長且窄,梯度下降很可能越過全域性最小值左右橫跳。
1、最大值
2、均值歸一化
3、Z-score標準化(Z-score歸一化/規範化)
注:分母為該特徵的標準差
通過繪製成本函數學習曲線圖,可直觀地觀察到梯度下降是否收斂,同時也可觀察到趨於收斂時的迭代次數。
設定一個很小的值e,例如令它為0.001,判斷每一次梯度下降對成本函數的改變是否小於或等於這個極小值,如果是則說明梯度下降現在比較平緩,也就是趨於收斂。但這個極小值e是很難選擇的,所以一般更推薦繪製學習曲線。
當學習率過小時,下降的步子過小,導致到達最低點需要很多步,需要的時間也更多;
當學習率過大時,下降的步子較大,可能越過最低點,導致越來越遠,甚至永遠也到達不了最低點。
為什麼我們可以採用固定的學習率?因為越靠近最低點,斜率將會越來越小,等於邁的步子越來越小了,最終也會慢慢靠近最低點。
如何選擇合適的學習率呢?通常,可使用一些常見的值例如0.001、0.01、0.1、1.......,每次將學習率提高十倍,再結合學習曲線來判斷學習率是否得當。
特徵工程指的是為演演算法選擇或設計最合適的特徵。
例如在預測房價的例子中,房子有長和寬兩個特徵,但是我們知道房子的面積是等於長乘以寬,因此會預感到面積更能預測房價,因此我們可以構建一個新的特徵即面積,加入到模型中。
我們可以結合知識以及實際設計新的特徵,使其幫助演演算法更簡單、更準確的做出預測。
在此之前,我們都是選擇使用直線來擬合資料集,但是在有的情況下,使用曲線或其他函數可能更好的擬合資料集,所以就需要採用多項式迴歸。例如,模型中包含二次項、三次項、根號項等,根據實際情況構造合適的多項式,使其更好地擬合。
2104,3,399900
1600,3,329900
2400,3,369000
1416,2,232000
3000,4,539900
1985,4,299900
1534,3,314900
1427,3,198999
1380,3,212000
1494,3,242500
1940,4,239999
2000,3,347000
1890,3,329999
4478,5,699900
1268,3,259900
......
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
alpha = 0.01 # 學習速率α
iters = 1000 # 要執行的迭代次數。
path = './data/ex1data2.txt'
data = pd.read_csv(path, header=None, names=['Size', 'Bedrooms', 'Price'])
data.head() # 輸出前五行
# 特徵縮放
data = (data - data.mean()) / data.std() # Z-score標準化
# data = (data - data.max()) / (data.max() - data.min()) # max-min歸一化
# data = data / (np.abs(data.max())) # MaxAbs標準化
# 在訓練集中新增一列,以便我們可以使用向量化的解決方案來計算代價和梯度
data.insert(0, 'One', 1)
# 獲取列數
cols = data.shape[1]
X = data.iloc[:, 0:cols - 1] # 所有行,不包含最後一列
y = data.iloc[:, cols - 1:cols] # 所有行,只包含最後一列
# np.matrix()函數用於從類陣列物件或資料字串返回矩陣
X = np.matrix(X.values)
y = np.matrix(y.values)
# 初始化引數矩陣
theta = np.matrix(np.array([0, 0, 0])) # 1x3矩陣
def computeCost(X, y, theta):
"""代價函數"""
num = len(X)
inner = np.power(((X * theta.T) - y), 2)
cost = np.sum(inner) / (2 * num)
return cost
def gradientDescent(X, y, theta, alpha, iters):
"""梯度下降"""
temp = np.matrix(np.zeros(theta.shape))
parameters = int(theta.ravel().shape[1])
cost = np.zeros(iters)
for i in range(iters):
error = (X * theta.T) - y
for j in range(parameters):
term = np.multiply(error, X[:, j])
temp[0, j] = theta[0, j] - ((alpha / len(X)) * np.sum(term))
theta = temp
cost[i] = computeCost(X, y, theta)
return theta, cost
def normalEqn(X, y):
"""正規方程"""
# np.linalg.inv():矩陣求逆
theta = np.linalg.inv(X.T @ X) @ X.T @ y # @等價於 X.T.dot(X)
return theta.T
# 執行梯度下降
g, cost = gradientDescent(X, y, theta, alpha, iters)
# 使用正規方程求參
g1 = normalEqn(X, y)
cost1 = computeCost(X, y, g1)
# 未訓練前的代價
print("梯度下降代價值:", computeCost(X, y, g))
print("正規方程代價值:", computeCost(X, y, g1))
# 繪製訓練曲線
fig, ax = plt.subplots(figsize=(12, 8))
ax.plot(np.arange(iters), cost, 'r')
ax.set_xlabel('Iterations')
ax.set_ylabel('Cost')
ax.set_title('Error vs. Training Epoch')
plt.show()