閱讀314 返回首頁    go 阿裏雲 go 技術社區[雲棲]


“尋根問祖”深度學習隻需六段代碼

更多深度文章,請關注:https://yq.aliyun.com/cloud 


本文涵蓋了每段代碼的發明人及其突破背景。每個故事都有GitHub上的簡單代碼示例。

2f146af5cb039eda4fda60bbf2a7f42db86c704f

最小二乘法(又稱最小平方法)是一種數學優化技術。它通過最小化誤差的平方和尋找數據的最佳函數匹配。利用最小二乘法可以簡便地求得未知的數據,並使得這些求得的數據與實際數據之間誤差的平方和為最小。最小二乘法還可用於曲線擬合。

深度學習最先開始於這個數學片段,可能你覺得很奇怪,但是事實就是如此。(我用Python實現它):

# 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年,勒讓德)首次提出的,他也是以測量儀器而聞名的數學家。他對預測彗星的未來位置特別癡迷,鑒於彗星以前的幾個位置,他準備創建一種計算其軌跡的方法。

他嚐試了幾種方法後,然後一個方法終於難住了他。Legendre的過程是通過猜測彗星的未來位置而開始的,通過記錄數據,分析,最後通過數據驗證了他的猜測,以減少平方誤差的總和。這是線性回歸的種子。

在我提供的代碼你可以更好理解最小二乘法。m是係數,b是你的預測的常數,所求坐標是彗星的位置。目標是找到mb的組合,其中錯誤盡可能小。

39a37b8e9bfb1b6a8ea7030009fcb3e76ebbe167

其實深度學習的核心就是:采取輸入和期望的輸出,然後搜索兩者之間的相關性。

Legendre的手動嚐試降低錯誤率的方法是耗時的。荷蘭諾貝爾獎得主彼得·德比(Peter Debye)在一個世紀後(1909年,德拜)正式發布了這一缺陷的解決方案。

讓我們想象一下Legendre正在苦惱一個參數,我們把它叫做XY軸表示X每個值的誤差值。Legendre正在搜索X以尋找到錯誤最低的位置。在這種圖形化表示中,我們可以看到X最小化的X值是X = 1.1時。

c4ab037fcbc5481f66d032e52945c916f308e2ab

彼得·德比(Peter Debye)注意到最低點左邊的斜率是負的,而另一邊則是正的。因此,如果你知道任何給定X值的斜率值,你可以將Y指向最小值。

這導致了梯度下降的方法。這個原則幾乎在每一個深度學習模式中使用。要玩這個,我們假設錯誤函數是 Error = x ^ 5-2x ^ 3-2要知道任何給定X值的斜率,我們取其導數,即5x ^ 4 - 6x ^ 2

3d37a13575bc9dba84836c81e32f0144335084ee

這個數學片段用Python實現:

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語言:

#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)

這沒有引入任何新的知識。然而,將誤差函數與漸變下降合並可能對一些人有一點困擾。你可以運行代碼,慢慢思考這裏麵的邏輯。

弗蘭克·羅森布拉特(Frank Rosenblatt)這個怪才字白天解剖大鼠的大腦,在夜間尋找外星生命的跡象。在1958年,他發布了一個模仿神經元的機器(1958年,羅森布拉特)的紐約時報的頭版“ 新海軍裝備學習 ” 

如果你展示了Rosenblatt機器的50套兩張圖像,其中一張標有左圖,另一張在右側,你可以在不預編程的情況下進行區分。

fb69b31cd301f66ddb6661c4bc7e16dec2e30509

對於每個訓練周期,你從左側輸入數據。初始隨機權重添加到所有輸入數據。然後總結出來。如果總和為負數,則將其轉換為0否則映射到1

如果預測是正確的,那麼該循環中的權重就沒有任何反應。如果這是錯誤的,你可以將錯誤乘以學習率。這相應地調整權重。

讓我們用經典OR邏輯運行感知器。

輸入

產量

0

0 =

0

0

1 =

1

1

0 =

1

1

1 =

1

感知器Python代碼實現:

from random import choice 
from numpy import array, dot, random 
1_or_0 = lambda x: 0 if x < 0 else 1 
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 
for i in range(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 
   
for x, _ in training_data: 
    result = dot(x, w) 
    print("{}: {} -> {}".format(input[:2], result, 1_or_0(result)))

感知器最初炒作一年之後,馬文·明斯基和西摩·帕普特(Seymour Papert)摧毀了這個想法(1969年,明斯基和帕普特)。當時,明斯基和帕普特都在麻省理工學院的AI實驗室工作。他們寫了一本書,證明感知器隻能解決線性問題。他們還揭露了關於多層感知器的缺陷。

在明斯基和帕普特書籍出版一年之後,芬蘭的一名大學生發現了解多層感知器的非線性問題的理論(Linnainmaa1970)。由於馬文·明斯基和西摩·帕普特的影響力,關於AI的研究大部分都停止了。這被稱為第一個AI冬天。

明斯基和帕普特的批評是異端問題。邏輯與OR邏輯相同,但有一個例外:當有兩個true語句(11)時,返回False0)。

9db25f5906f41c3c5333a4739f5baa09453e4910

OR邏輯中,可以將真實組合與虛假組合分開。但是從上圖你可以看到,你不能將XOR邏輯與一個線性函數進行分割。

1986年,幾項實驗證明,神經網絡可以解決複雜的非線性問題(Rumelhart等,1986)。而且當時的計算能力比預期的發展的要快,這就是Rumelhart等人 介紹了他們的傳奇論文:

我們描述了一種新的神經元網絡學習過程,反向傳播。該過程通過重複調整網絡中的連接的權重,以便最小化網絡的實際輸出向量與期望的輸出向量之間的差異的度量。由於權重調整的結果,不屬於輸入或輸出的而是內部隱藏單元來完成的,並且任務中的規則由這些單元的交互製定。例如感知器收斂過程Nature323,533-5361986109日)。

為了理解論文的核心,我們將用DeepMindAndrew Trask編寫來幫助我們理解。這不是一個隨機的代碼段。它曾被用於斯坦福大學Andrew Karpathy的深度學習課程,以及Siraj RavalUdacity課程。除此之外,它解決了XOR問題,也解凍了第一個AI冬天。

133c07dc7fe21aa3d43ca36c918602a83681b2ee

在我們編寫代碼之前,需要一到兩個小時來掌握核心邏輯,然後閱讀Trask的博客文章。請注意,X_XOR數據中添加的參數[1]偏置神經元,它們與線性函數中的常量具有相同的行為。

import numpy as np
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
def sigmoid(x):
    output = 1/(1+np.exp(-x))
    return output
def sigmoid_output_to_derivative(output):
    return output*(1-output) 
for j in range(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 DechterDechter1986)介紹,但在2012才開始獲得了主流關注。

深層神經網絡的核心結構基本保持不變,但現在應用於幾個不同的問題,專業化也有很多改進。最初,這是一組數學函數來簡化嘈雜的地球數據(TikhonovAN1963)。他們現在正在使用神經網絡,以提高其泛化能力。

計算能力的提升也是其大火的原因之一。一個超級計算機在八十年代中期計算一年的計算量而在今天的GPU技術的幫助下需要半秒鍾就OK了。

計算方麵的成本降低以及深度學習庫的發展,深層神經網絡結構正在被大多數人接受使用。我們來看一個普通的深層學習堆棧的例子,從底層硬件開始介紹:

  • GPU Nvidia特斯拉K80。硬件常用於圖形處理。與CPU相比,深度學習平均速度要快50-200倍。
  • CUDA GPU的低級編程語言
  • CuDNN  Nvidia的庫來優化CUDA
  • Tensorflow GoogleCuDNN之上的深層學習框架
  • TFlearn Tensorflow的前端框架

我們來看看MNIST圖像分類的數字,這是深度學習的“Hello World”

2e1bbde2f57ec041906720c76983b230290cae2d

TFlearn實施:

from __future__ import division, print_function, absolute_import
import tflearn
from tflearn.layers.core import dropout, fully_connected
from tensorflow.examples.tutorials.mnist import input_data
from tflearn.layers.conv import conv_2d, max_pool_2d
from tflearn.layers.normalization import local_response_normalization
from tflearn.layers.estimator import regression
# 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')

有很多不錯的文章介紹MNIST這裏 這裏這裏

總結:

我認為:深度學習的主要邏輯仍然類似於Rosenblatt的感知器。而不是使用Heaviside二進製步驟功能,今天神經網絡大多使用Relu激活。在卷積神經網絡的最後一層,損失等於categorical_crossentropy。這是勒布雷最小平方的演變,是多類別的邏輯回歸。優化器adam源於德拜梯度下降的工作。或許有了這些認識,我們才可以更好的進行深度學習的研究。

希望上述能夠幫助到你!

本文由北郵@愛可可-愛生活老師推薦,@阿裏雲雲棲社區組織翻譯。

文章原標題《Coding the History of Deep Learning

作者Emil Wallner 曾經執教於牛津大學商學院,現在正致力於技術教育。

譯者:袁虎 審閱:

文章為簡譯,更為詳細的內容,請查看原文




最後更新:2017-09-23 10:37:42

  上一篇:go  國慶長假到來,出遊期間信用卡防盜刷新招必知道
  下一篇:go  用gdb分析core文件及常見gdb命令操作示例