線性迴歸是機器學習中最基本的一個演算法,大部分演算法都是由基本的演算法演變而來。本文著重用很簡單的語言說一下線性迴歸。
線性迴歸
包括一元線性迴歸和多元線性迴歸,一元指的是隻有一個x和一個y。通過一元對於線性迴歸有個基本的理解。
一元線性迴歸就是在資料中找到一條直線,以最小的誤差來(Loss)來擬和資料。
上面提到的誤差可以這樣表示,假設那條直線如下圖:
理想情況是所有點都落在直線上。退一步,希望所有點離直線的距離最近。簡單起見,將距離求平方,誤差可以表示為:
上面的i表示第i個資料。一般情況下對Loss求平均,來當作最終的損失。
最小化誤差
找到最能擬合資料的直線,也就是最小化誤差。
最小二乘法
上述公式只有m, b未知,因此可以看最一個m, b的二次方程,求Loss的問題就轉變成了求極值問題。
這裡不做詳細說明。
另每個變數的偏導數為0, 求方程組的解。
求出m,b即可得到所要的直線。
梯度下降法
沒有梯度下降就沒有現在的深度學習。
最小二乘法可以一步到位,直接求出m,b。在大部分公式中是無法簡單的直接計算的。而梯度下降通過一步一步的迭代,慢慢的去靠近那條最優的直線,因此需要不斷的優化。
Loss的函式影像可以類比成一個碗。
要求的最小值就在碗底,隨意給出一點往下走,即沿著下降最快的方向(梯度)往下走,定義每一步移動的步長,移動的次數來逼近最優值。
下面用演算法來實現:
初始化:
def init_data():
data = np.loadtxt(`data.csv`, delimiter=`,`)
return data
def linear_regression():
learning_rate = 0.01 #步長
initial_b = 0
initial_m = 0
num_iter = 1000 #迭代次數
data = init_data()
[b, m] = optimizer(data, initial_b, initial_m, learning_rate, num_iter)
plot_data(data,b,m)
print(b, m)
return b, m
複製程式碼
優化器去做梯度下降:
def optimizer(data, initial_b, initial_m, learning_rate, num_iter):
b = initial_b
m = initial_m
for i in range(num_iter):
b, m = compute_gradient(b, m, data, learning_rate)
# after = computer_error(b, m, data)
if i % 100 == 0:
print(i, computer_error(b, m, data)) # 損失函式,即誤差
return [b, m]
複製程式碼
每次迭代計算梯度做引數更新:
def compute_gradient(b_cur, m_cur, data, learning_rate):
b_gradient = 0
m_gradient = 0
N = float(len(data))
#
# 偏導數, 梯度
for i in range(0, len(data)):
x = data[i, 0]
y = data[i, 1]
b_gradient += -(2 / N) * (y - ((m_cur * x) + b_cur))
m_gradient += -(2 / N) * x * (y - ((m_cur * x) + b_cur)) #偏導數
new_b = b_cur - (learning_rate * b_gradient)
new_m = m_cur - (learning_rate * m_gradient)
return [new_b, new_m]
複製程式碼
Loss值的計算:
def computer_error(b, m, data):
totalError = 0
x = data[:, 0]
y = data[:, 1]
totalError = (y - m * x - b) ** 2
totalError = np.sum(totalError, axis=0)
return totalError / len(data)
複製程式碼
執行函式計算結果:
if __name__ == `__main__`:
linear_regression()
複製程式碼
運算結果如下:
0 3.26543633854
100 1.41872132865
200 1.36529867423
300 1.34376973304
400 1.33509372632
500 1.33159735872
600 1.330188348
700 1.32962052693
800 1.32939169917
900 1.32929948325
1.23930380135 1.86724196887
複製程式碼
可以看到,隨著迭代次數的增加,Loss函式越來越逼近最小值,而m,b也越來越逼近最優解。
注意:
在上面的方法中,還是通過計算Loss的偏導數來最小化誤差。上述方法在梯度已知的情況下,即肯定按照下降最快的方法到達碗底。那麼在公式非常難以計算的情況下怎麼去求最優解。此時求偏導數可以使用導數的定義,看另一個函式。
def optimizer_two(data, initial_b, initial_m, learning_rate, num_iter):
b = initial_b
m = initial_m
while True:
before = computer_error(b, m, data)
b, m = compute_gradient(b, m, data, learning_rate)
after = computer_error(b, m, data)
if abs(after - before) < 0.0000001: #不斷減小精度
break
return [b, m]
def compute_gradient_two(b_cur, m_cur, data, learning_rate):
b_gradient = 0
m_gradient = 0
N = float(len(data))
delta = 0.0000001
for i in range(len(data)):
x = data[i, 0]
y = data[i, 1]
# 利用導數的定義來計算梯度
b_gradient = (error(x, y, b_cur + delta, m_cur) - error(x, y, b_cur - delta, m_cur)) / (2*delta)
m_gradient = (error(x, y, b_cur, m_cur + delta) - error(x, y, b_cur, m_cur - delta)) / (2*delta)
b_gradient = b_gradient / N
m_gradient = m_gradient / N
#
new_b = b_cur - (learning_rate * b_gradient)
new_m = m_cur - (learning_rate * m_gradient)
return [new_b, new_m]
def error(x, y, b, m):
return (y - (m * x) - b) ** 2
複製程式碼
上述兩種中,迭代次數足夠多都可以逼近最優解。
分別求得的最優解為:
1: 1.23930380135 1.86724196887
2: 1.24291450769 1.86676417482
簡單比較
sklearn中有相應的方法求線性迴歸,其直接使用最小二乘法求最優解。簡單實現以做個比較。
def scikit_learn():
data = init_data()
y = data[:, 1]
x = data[:, 0]
x = (x.reshape(-1, 1))
linreg = LinearRegression()
linreg.fit(x, y)
print(linreg.coef_)
print(linreg.intercept_)
if __name__ == `__main__`:
# linear_regression()
scikit_learn()
複製程式碼
此時求的解為:
1.24977978176 1.86585571。
可以說明上述計算結果比較滿意,通過後期調整引數,可以達到比較好的效果。
原始碼和資料參考:
github.com/yunshuipiao…
感謝並參考博文:
線性迴歸理解(附純python實現)
Gradient Descent 梯度下降法
梯度下降(Gradient Descent)小結