最小二乘法
所有的深度學習算法都始於下面這個數學公式(我已將其轉成 Python 代碼)
1 2 3 4 5 6 7 8 9 10 11 12 | # y = mx + b # m is slope, b is y-intercept def compute_error_for_line_given_points(b, m, coordinates): totalError = 0 for i in range(0, len(coordinates)): x = coordinates[i][0] y = coordinates[i][1] totalError += (y - (m * x + b)) ** 2 return totalError / float(len(coordinates)) # example compute_error_for_line_given_points(1, 2, [[3,6],[6,9],[12,18]]) |
最小二乘法在 1805 年由 Adrien-Marie Legendre 首次提出(1805, Legendre),這位巴黎數學家也以測量儀器聞名。他極其癡迷於預測彗星的方位,堅持不懈地尋找一種可以基於彗星方位歷史數據計算其軌跡的算法。
他嘗試了許多種算法,一遍遍試錯,終於找到了一個算法與結果相符。Legendre 的算法是首先預測彗星未來的方位,然後計算誤差的平方,最終目的是通過修改預測值以減少誤差平方和。而這也正是線性迴歸的基本思想。
讀者可以在 Jupyter notebook 中運行上述代碼來加深對這個算法的理解。m 是係數,b 是預測的常數項,coordinates 是彗星的位置。目標是找到合適的 m 和 b 使其誤差儘可能小。
這是深度學習的核心思想:給定輸入值和期望的輸出值,然後尋找兩者之間的相關性。
梯度下降
Legendre 這種通過手動嘗試來降低錯誤率的方法非常耗時。荷蘭的諾貝爾獎得主 Peter Debye 在一個世紀後(1909 年)正式提出了一種簡化這個過程的方法。
假設 Legendre 的算法需要考慮一個參數 —— 我們稱之爲 X 。Y 軸表示每個 X 的誤差值。Legendre 的算法是找到使得誤差最小的 X。在下圖中,我們可以看到當 X = 1.1 時,誤差 Y 取到最小值。
Peter Debye 注意到最低點左邊的斜率是負的,而另一邊則是正的。因此,如果知道了任意給定 X 的斜率值,就可以找到 Y 的最小值點。
這便是梯度下降算法的基本思想。幾乎所有的深度學習模型都會用到梯度下降算法。
要實現這個算法,我們假設誤差函數是 Error = x ^ 5 -2x ^ 3-2。要得到任意給定 X 的斜率,我們需要對其求導,即 5x^4 – 6x^2:
如果您需要複習導數的相關知識,請觀看 Khan Academy 的視頻。
下面我們用 Python 實現 Debye 的算法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
current_x
=
0.5
#
the algorithm starts at x=0.5
learning_rate
=
0.01
#
step size multiplier
num_iterations
=
60
#
the number of times to train the function
#the
derivative of the error function (x**4 = the power of 4 or x^4)
def
slope_at_given_x_value(x):
return
5
*
x**4
-
6
*
x**2
#
Move X to the right or left depending on the slope of the error function
for
i
in
range(num_iterations):
previous_x
=
current_x
current_x
+=
-learning_rate
*
slope_at_given_x_value(previous_x)
print(previous_x)
print("The
local minimum occurs at %f"
%
current_x)
|
這裏的竅門在於 learning_rate。我們通過沿斜率的相反方向行進來逼近最低點。此外,越接近最低點,斜率越小。因此當斜率接近零時,每一步下降的幅度會越來越小。
num_iterations 是你預計到達最小值之前所需的迭代次數。可以通過調試該參數訓練自己關於梯度下降算法的直覺。
線性迴歸
最小二乘法配合梯度下降算法,就是一個完整的線性迴歸過程。在 20 世紀 50 年代和 60 年代,一批實驗經濟學家在早期的計算機上實現了這些想法。這個過程是通過實體打卡 —— 真正的手工軟件程序實現的。準備這些打孔卡就需要幾天的時間,而通過計算機進行一次迴歸分析最多需要 24 小時。
下面是用 Python 實現線性迴歸的一個示例(我們不需要在打卡機上完成這個操作):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
#Price
of wheat/kg and the average price of bread
wheat_and_bread
=
[[0.5,5],[0.6,5.5],[0.8,6],[1.1,6.8],[1.4,7]]
def
step_gradient(b_current,
m_current,
points,
learningRate):
b_gradient
=
0
m_gradient
=
0
N
=
float(len(points))
for
i
in
range(0,
len(points)):
x
=
points[i][0]
y
=
points[i][1]
b_gradient
+=
-(2/N)
*
(y
-
((m_current
*
x)
+
b_current))
m_gradient
+=
-(2/N)
*
x
*
(y
-
((m_current
*
x)
+
b_current))
new_b
=
b_current
-
(learningRate
*
b_gradient)
new_m
=
m_current
-
(learningRate
*
m_gradient)
return
[new_b,
new_m]
def
gradient_descent_runner(points,
starting_b,
starting_m,
learning_rate,
num_iterations):
b
=
starting_b
m
=
starting_m
for
i
in
range(num_iterations):
b,
m
=
step_gradient(b,
m,
points,
learning_rate)
return
[b,
m]
gradient_descent_runner(wheat_and_bread,
1,
1,
0.01,
100)
|
線性迴歸本身並沒有引入什麼新的內容。但是,如何將梯度下降算法運用到誤差函數上就需要動動腦子了。運行代碼並使用這個線性迴歸模擬器來加深你的理解吧。