深度學習發展到如今的地位, 離不開下面這 6 段代碼。 小編從網上搜集到這些代碼的創作者及其完成這些突破性成就的故事背景。 每個故事都有簡單的代碼示例, 大家可以在 FloydHub 和 GitHub 找到相關代碼。
最小二乘法
所有的深度學習演算法都始於下面這個數學公式(已將其轉成 Python 代碼)
# y = mx + b
# m is slope, b is y-intercept
defcompute_error_for_line_given_points(b,m,coordinates):
totalError = 0
foriinrange(0,len(coordinates)):
x = coordinates[i][0]
y = coordinates[i][1]
totalError += (y - (m * x + b)) ** 2
returntotalError / 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 是預測的常數項,
這是深度學習的核心思想:給定輸入值和期望的輸出值, 然後尋找兩者之間的相關性。
梯度下降
Legendre 這種通過手動嘗試來降低錯誤率的方法非常耗時。 荷蘭的諾貝爾獎得主 Peter Debye 在一個世紀後(1909 年)正式提出了一種簡化這個過程的方法。
假設 Legendre 的演算法需要考慮一個參數 —— 我們稱之為 X 。 Y 軸表示每個 X 的誤差值。 Legendre 的演算法是找到使得誤差最小的 X。 在下圖中, 我們可以看到當 X = 1.1 時, 誤差 Y 取到最小值。
Peter Debye 注意到最低點左邊的斜率是負的, 而另一邊則是正的。
這便是梯度下降演算法的基本思想。 幾乎所有的深度學習模型都會用到梯度下降演算法。
要實現這個演算法, 我們假設誤差函數是 Error = x ^ 5 -2x ^ 3-2。 要得到任意給定 X 的斜率, 我們需要對其求導, 即 5x^4 – 6x^2:
如果您需要複習導數的相關知識, 請觀看 Khan Academy 的視頻。
下面我們用 Python 實現 Debye 的演算法:
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)
defslope_at_given_x_value(x):
return5 * x**4 - 6 * x**2
# Move X to the right or left depending on the slope of the error function
foriinrange(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 是你預計到達最小值之前所需的反覆運算次數。 可以通過調試該參數訓練自己關於梯度下降演算法的直覺。
線性回歸
最小二乘法配合梯度下降演算法,
下面是用 Python 實現線性回歸的一個示例(我們不需要在打卡機上完成這個操作):
#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]]
defstep_gradient(b_current,m_current,points,learningRate):
b_gradient = 0
m_gradient = 0
N = float(len(points))
foriinrange(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]
defgradient_descent_runner(points,starting_b,starting_m,learning_rate,num_iterations):
b = starting_b
m = starting_m
foriinrange(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)
線性回歸本身並沒有引入什麼新的內容。 但是, 如何將梯度下降演算法運用到誤差函數上就需要動動腦子了。 運行代碼並使用這個線性回歸模擬器來加深你的理解吧。
感知機
接下來讓我們來認識一下 Frank Rosenblatt。 這是一個白天解剖老鼠大腦, 晚上尋找外星生命跡象的傢伙。 1958年, 他發明了一個模仿神經元的機器(1958, Rosenblatt), 並因此登上《紐約時報》的頭條:“New Navy Device Learns By Doing”。
如果向 Rosenblatt 的機器展示 50 組分別在左右兩側有標記的圖像,它可以在沒有預先程式設計的情況下分辨出兩張圖像(標記的位置)。大眾被這個可能真正擁有學習能力的機器震驚了。
如上圖所示,每個訓練週期都是從左側輸入資料開始。給所有輸入資料添加一個初始的隨機權重。然後將它們相加。如果總和為負,將其輸出為 0,否則輸出為 1。
如果預測結果是正確的,就不改變迴圈中的權重。如果預測結果是錯誤的,可以用誤差乘以學習率來相應地調整權重。
我們用經典的“或”邏輯來運行感知機。
下面是用 Python 實現的感知機模型:
fromrandomimportchoice
fromnumpy importarray,dot,random
1_or_0 = lambdax: 0ifx < 0else1
training_data = [(array([0,0,1]),0),
(array([0,1,1]),1),
(array([1,0,1]),1),
(array([1,1,1]),1),]
weights = random.rand(3)
errors = []
learning_rate = 0.2
num_iterations = 100
foriinrange(num_iterations):
input,truth = choice(training_data)
result = dot(weights,input)
error = truth - 1_or_0(result)
errors.append(error)
weights += learning_rate * error * input
forx,_intraining_data:
result = dot(x,w)
print("{}: {} -> {}".format(input[:2],result,1_or_0(result)))
經過最初的炒作一年之後,Marvin Minsky 和 Seymour Papert 擊碎了這個想法(1969, Minsky & Papert)。當時,Minsky 和 Papert 都在麻省理工學院的 AI 實驗室工作。他們寫了一本書,證明感知機只能解決線性問題。他們還批判了關於多層感知機的想法。可悲的是,Frank Rosenblatt 兩年後因船難去世。
在 Minsky 和 Papert 的書籍出版一年之後,一位芬蘭碩士研究生提出了用多層感知機解決非線性問題的理論(Linnainmaa, 1970)。由於業內主流對感知機普遍不看好,十多年來 AI 的研究資金也非常短缺。這是 AI 首次遇冷。
Minsky 和 Papert 對感知機的批判主要針對“異或”問題。這個邏輯與“或”邏輯相同,但有一個例外 —— 對兩個 true 語句取和(1&1)時,結果返回 False(0)。
如上圖所示,在“或”邏輯中,我們可以將 true 和 false 分開。但是可以看出,我們無法使用一個線性函數將“異或”邏輯的結果進行區分。
人工神經網路
到 1986 年,幾項實驗證明,神經網路可以解決複雜的非線性問題(Rumelhart et al., 1986)。 當時電腦的運算速度比該理論提出的時候快了一萬倍。Rumelhart 等人是這樣介紹他們赫赫有名的論文的:
我們描述了一種新的類神經元網路學習過程——反向傳播。該過程通過反復調整網路中的連接權重,最小化網路的實際輸出向量與期望輸出向量之間的差異。調整權重的結果就是,不屬於輸入或輸出的內部“隱藏”單元成為了描述任務域的重要特徵,並且這些單元的交互項還捕獲了任務中的正則條件。 相較於早期更簡單的方法,如“感知機收斂過程” Nature 323, 533 – 536 (09 October 1986),反向傳播可以創造出有用的新特徵。
為了理解這篇文章的核心內容,我會在下面重現 DeepMind 團隊 Andrew Trask 的代碼。這不是一段普通的代碼。它曾被用於斯坦福大學 Andrew Karpathy 的深度學習課程,以及 Siraj Raval 的 Udacity 課程。最重要的是,它解決了“異或”問題,也結束了 AI 遇冷的時代。
學習這段代碼之前,我們首先通過這個模擬器交互學習一到兩個小時來掌握神經網路的核心邏輯。然後閱讀 Trask 的博客,然後再閱讀四次。需要注意到,X_XOR 資料中添加的參數 [1] 是偏置神經元,它們等價於線性函數中的常數項。
importnumpy asnp
X_XOR = np.array([[0,0,1],[0,1,1],[1,0,1],[1,1,1]])
y_truth = np.array([[0],[1],[1],[0]])
np.random.seed(1)
syn_0 = 2*np.random.random((3,4)) - 1
syn_1 = 2*np.random.random((4,1)) - 1
defsigmoid(x):
output = 1/(1+np.exp(-x))
returnoutput
defsigmoid_output_to_derivative(output):
returnoutput*(1-output)
forjinrange(60000):
layer_1 = sigmoid(np.dot(X_XOR,syn_0))
layer_2 = sigmoid(np.dot(layer_1,syn_1))
error = layer_2 - y_truth
layer_2_delta = error * sigmoid_output_to_derivative(layer_2)
layer_1_error = layer_2_delta.dot(syn_1.T)
layer_1_delta = layer_1_error * sigmoid_output_to_derivative(layer_1)
syn_1 -= layer_1.T.dot(layer_2_delta)
syn_0 -= X_XOR.T.dot(layer_1_delta)
print("Output After Training: n",layer_2)
反向傳播,矩陣乘法和梯度下降放在一起會讓人很難理解。這個過程的視覺化通常是對其背後原理的簡化。專注於理解其背後的邏輯,但不要過多地考慮直覺上的理解。
另外,讀者們也可以看看 Andrew Karpathy 關於反向傳播的課程,在這個視覺化網站交互學習,以及閱讀 Michael Nielsen 關於反向傳播的章節。
深度神經網路
深度神經網路就是在輸入層和輸出層之間具有多個中間層的神經網路。這個概念最早是由 Rina Dechter (Dechter, 1986) 引入的,但在2012年,也就是在 IBM 的人工智慧程式 Watson 贏得美國電視智力競賽節目 Jeopardy 和 Google 推出貓咪識別器之後才受到廣泛關注。
深度神經網路與之前神經網路的核心結構相同,但是應用於一些不同的問題。在正則化方面也有很大改進。最初,這只是一組用來簡化冗雜的地球資料的數學函數(Tikhonov,A.N.,1963)。而現在被用於神經網路中,以加強其泛化能力。
這種技術創新很大程度上依賴於電腦的運算能力。而運算能力的提升大大縮短了研究者的創新週期 —— 如今的 GPU 技術只需半秒鐘就可以完成一個八十年代中期的超級電腦一年的運算量。
計算成本的降低和各種深度學習庫的發展將深度學習帶入了大眾視野。我們來看一個常見的深度學習堆疊示例,從底層開始:
GPU > Nvidia Tesla K80。該硬體常用於圖形處理。它們深度學習的速度平均要比 CPU 快50-200倍。
CUDA > GPU 的底層程式設計語言
CuDNN > Nvidia 的庫,用來優化 CUDA
Tensorflow > 由 Google 開發,基於 CuDNN 的深度學習框架
TFlearn > Tensorflow 的前端框架
下面我們來看看 MNIST 數位分類圖像,它被稱作深度學習的 “Hello World”。
我們用 TFlearn 來實現:
from__future__importdivision,print_function,absolute_import
importtflearn
fromtflearn.layers.core importdropout,fully_connected
fromtensorflow.examples.tutorials.mnist importinput_data
fromtflearn.layers.conv importconv_2d,max_pool_2d
fromtflearn.layers.normalization importlocal_response_normalization
fromtflearn.layers.estimator importregression
# Data loading and preprocessing
mnist = input_data.read_data_sets("/data/",one_hot=True)
X,Y,testX,testY = mnist.train.images,mnist.train.labels,mnist.test.images,mnist.test.labels
X = X.reshape([-1,28,28,1])
testX = testX.reshape([-1,28,28,1])
# Building convolutional network
network = tflearn.input_data(shape=[None,28,28,1],name='input')
network = conv_2d(network,32,3,activation='relu',regularizer="L2")
network = max_pool_2d(network,2)
network = local_response_normalization(network)
network = conv_2d(network,64,3,activation='relu',regularizer="L2")
network = max_pool_2d(network,2)
network = local_response_normalization(network)
network = fully_connected(network,128,activation='tanh')
network = dropout(network,0.8)
network = fully_connected(network,256,activation='tanh')
network = dropout(network,0.8)
network = fully_connected(network,10,activation='softmax')
network = regression(network,optimizer='adam',learning_rate=0.01,
loss='categorical_crossentropy',name='target')
# Training
model = tflearn.DNN(network,tensorboard_verbose=0)
model.fit({'input': X},{'target': Y},n_epoch=20,
validation_set=({'input': testX},{'target': testY}),
snapshot_step=100,show_metric=True,run_id='convnet_mnist')
並因此登上《紐約時報》的頭條:“New Navy Device Learns By Doing”。如果向 Rosenblatt 的機器展示 50 組分別在左右兩側有標記的圖像,它可以在沒有預先程式設計的情況下分辨出兩張圖像(標記的位置)。大眾被這個可能真正擁有學習能力的機器震驚了。
如上圖所示,每個訓練週期都是從左側輸入資料開始。給所有輸入資料添加一個初始的隨機權重。然後將它們相加。如果總和為負,將其輸出為 0,否則輸出為 1。
如果預測結果是正確的,就不改變迴圈中的權重。如果預測結果是錯誤的,可以用誤差乘以學習率來相應地調整權重。
我們用經典的“或”邏輯來運行感知機。
下面是用 Python 實現的感知機模型:
fromrandomimportchoice
fromnumpy importarray,dot,random
1_or_0 = lambdax: 0ifx < 0else1
training_data = [(array([0,0,1]),0),
(array([0,1,1]),1),
(array([1,0,1]),1),
(array([1,1,1]),1),]
weights = random.rand(3)
errors = []
learning_rate = 0.2
num_iterations = 100
foriinrange(num_iterations):
input,truth = choice(training_data)
result = dot(weights,input)
error = truth - 1_or_0(result)
errors.append(error)
weights += learning_rate * error * input
forx,_intraining_data:
result = dot(x,w)
print("{}: {} -> {}".format(input[:2],result,1_or_0(result)))
經過最初的炒作一年之後,Marvin Minsky 和 Seymour Papert 擊碎了這個想法(1969, Minsky & Papert)。當時,Minsky 和 Papert 都在麻省理工學院的 AI 實驗室工作。他們寫了一本書,證明感知機只能解決線性問題。他們還批判了關於多層感知機的想法。可悲的是,Frank Rosenblatt 兩年後因船難去世。
在 Minsky 和 Papert 的書籍出版一年之後,一位芬蘭碩士研究生提出了用多層感知機解決非線性問題的理論(Linnainmaa, 1970)。由於業內主流對感知機普遍不看好,十多年來 AI 的研究資金也非常短缺。這是 AI 首次遇冷。
Minsky 和 Papert 對感知機的批判主要針對“異或”問題。這個邏輯與“或”邏輯相同,但有一個例外 —— 對兩個 true 語句取和(1&1)時,結果返回 False(0)。
如上圖所示,在“或”邏輯中,我們可以將 true 和 false 分開。但是可以看出,我們無法使用一個線性函數將“異或”邏輯的結果進行區分。
人工神經網路
到 1986 年,幾項實驗證明,神經網路可以解決複雜的非線性問題(Rumelhart et al., 1986)。 當時電腦的運算速度比該理論提出的時候快了一萬倍。Rumelhart 等人是這樣介紹他們赫赫有名的論文的:
我們描述了一種新的類神經元網路學習過程——反向傳播。該過程通過反復調整網路中的連接權重,最小化網路的實際輸出向量與期望輸出向量之間的差異。調整權重的結果就是,不屬於輸入或輸出的內部“隱藏”單元成為了描述任務域的重要特徵,並且這些單元的交互項還捕獲了任務中的正則條件。 相較於早期更簡單的方法,如“感知機收斂過程” Nature 323, 533 – 536 (09 October 1986),反向傳播可以創造出有用的新特徵。
為了理解這篇文章的核心內容,我會在下面重現 DeepMind 團隊 Andrew Trask 的代碼。這不是一段普通的代碼。它曾被用於斯坦福大學 Andrew Karpathy 的深度學習課程,以及 Siraj Raval 的 Udacity 課程。最重要的是,它解決了“異或”問題,也結束了 AI 遇冷的時代。
學習這段代碼之前,我們首先通過這個模擬器交互學習一到兩個小時來掌握神經網路的核心邏輯。然後閱讀 Trask 的博客,然後再閱讀四次。需要注意到,X_XOR 資料中添加的參數 [1] 是偏置神經元,它們等價於線性函數中的常數項。
importnumpy asnp
X_XOR = np.array([[0,0,1],[0,1,1],[1,0,1],[1,1,1]])
y_truth = np.array([[0],[1],[1],[0]])
np.random.seed(1)
syn_0 = 2*np.random.random((3,4)) - 1
syn_1 = 2*np.random.random((4,1)) - 1
defsigmoid(x):
output = 1/(1+np.exp(-x))
returnoutput
defsigmoid_output_to_derivative(output):
returnoutput*(1-output)
forjinrange(60000):
layer_1 = sigmoid(np.dot(X_XOR,syn_0))
layer_2 = sigmoid(np.dot(layer_1,syn_1))
error = layer_2 - y_truth
layer_2_delta = error * sigmoid_output_to_derivative(layer_2)
layer_1_error = layer_2_delta.dot(syn_1.T)
layer_1_delta = layer_1_error * sigmoid_output_to_derivative(layer_1)
syn_1 -= layer_1.T.dot(layer_2_delta)
syn_0 -= X_XOR.T.dot(layer_1_delta)
print("Output After Training: n",layer_2)
反向傳播,矩陣乘法和梯度下降放在一起會讓人很難理解。這個過程的視覺化通常是對其背後原理的簡化。專注於理解其背後的邏輯,但不要過多地考慮直覺上的理解。
另外,讀者們也可以看看 Andrew Karpathy 關於反向傳播的課程,在這個視覺化網站交互學習,以及閱讀 Michael Nielsen 關於反向傳播的章節。
深度神經網路
深度神經網路就是在輸入層和輸出層之間具有多個中間層的神經網路。這個概念最早是由 Rina Dechter (Dechter, 1986) 引入的,但在2012年,也就是在 IBM 的人工智慧程式 Watson 贏得美國電視智力競賽節目 Jeopardy 和 Google 推出貓咪識別器之後才受到廣泛關注。
深度神經網路與之前神經網路的核心結構相同,但是應用於一些不同的問題。在正則化方面也有很大改進。最初,這只是一組用來簡化冗雜的地球資料的數學函數(Tikhonov,A.N.,1963)。而現在被用於神經網路中,以加強其泛化能力。
這種技術創新很大程度上依賴於電腦的運算能力。而運算能力的提升大大縮短了研究者的創新週期 —— 如今的 GPU 技術只需半秒鐘就可以完成一個八十年代中期的超級電腦一年的運算量。
計算成本的降低和各種深度學習庫的發展將深度學習帶入了大眾視野。我們來看一個常見的深度學習堆疊示例,從底層開始:
GPU > Nvidia Tesla K80。該硬體常用於圖形處理。它們深度學習的速度平均要比 CPU 快50-200倍。
CUDA > GPU 的底層程式設計語言
CuDNN > Nvidia 的庫,用來優化 CUDA
Tensorflow > 由 Google 開發,基於 CuDNN 的深度學習框架
TFlearn > Tensorflow 的前端框架
下面我們來看看 MNIST 數位分類圖像,它被稱作深度學習的 “Hello World”。
我們用 TFlearn 來實現:
from__future__importdivision,print_function,absolute_import
importtflearn
fromtflearn.layers.core importdropout,fully_connected
fromtensorflow.examples.tutorials.mnist importinput_data
fromtflearn.layers.conv importconv_2d,max_pool_2d
fromtflearn.layers.normalization importlocal_response_normalization
fromtflearn.layers.estimator importregression
# Data loading and preprocessing
mnist = input_data.read_data_sets("/data/",one_hot=True)
X,Y,testX,testY = mnist.train.images,mnist.train.labels,mnist.test.images,mnist.test.labels
X = X.reshape([-1,28,28,1])
testX = testX.reshape([-1,28,28,1])
# Building convolutional network
network = tflearn.input_data(shape=[None,28,28,1],name='input')
network = conv_2d(network,32,3,activation='relu',regularizer="L2")
network = max_pool_2d(network,2)
network = local_response_normalization(network)
network = conv_2d(network,64,3,activation='relu',regularizer="L2")
network = max_pool_2d(network,2)
network = local_response_normalization(network)
network = fully_connected(network,128,activation='tanh')
network = dropout(network,0.8)
network = fully_connected(network,256,activation='tanh')
network = dropout(network,0.8)
network = fully_connected(network,10,activation='softmax')
network = regression(network,optimizer='adam',learning_rate=0.01,
loss='categorical_crossentropy',name='target')
# Training
model = tflearn.DNN(network,tensorboard_verbose=0)
model.fit({'input': X},{'target': Y},n_epoch=20,
validation_set=({'input': testX},{'target': testY}),
snapshot_step=100,show_metric=True,run_id='convnet_mnist')