6個步驟從頭開始編寫機器學習算法:感知器案例研究

摘要: 通用版學習機器學習算法的方法,你值得擁有!html

0開始編寫機器學習算法是一種很是好的體驗python

 點擊這裏你能夠查看本文所用到的Python代碼git

當你點擊以後你會感到壓力,由於其中有些算法比其餘算法更復雜,因此我建議你從一些簡單甚至更簡單的算法開始,好比單層感知器github

以感知器爲例從頭開始編寫算法,主要爲如下6個步驟:web

1.對算法有一個基本的瞭解;算法

2.找到一些不一樣的學習來源;api

3.將算法分解成塊;數組

4.從一個簡單的例子開始;網絡

5.使用可行的實現進行驗證;app

6.寫下你的過程。

一、對算法進行基本的瞭解

若是你不瞭解基礎知識,不要從零開始編寫算法。

至少,你應該可以回答如下問題:

1.它是什麼?

2.它的典型用途是什麼?

3.使用條件是什麼?

對於感知器,至少可以回答如下問題:

1.單層感知器是最基本的神經網絡。它一般用於二分類問題(1或0,「是」或「否」)。

2.一些簡單的用法多是情緒分析(積極或消極反應)或貸款違約預測(「將違約」或「將不違約」)。對於這兩種狀況,決策邊界都必須是線性的。

3.若是決策邊界是非線性的,你就不能用感知器。對於這些問題,須要使用其餘不一樣的方法。

二、使用不一樣的學習資源

在你對模型有了基本的瞭解以後,這個時候能夠開始你的研究了。

有些人用教材學得更好,有些人用視頻學得更好。我我的喜歡處處轉轉,使用各類各樣的資源。

對於數學細節,教材講的比較詳細,但對於更實際的例子,我更喜歡博客帖子和視頻。

對於感知器,這裏有一些很好的學習資源:

教材

統計學習基礎

機器學習理解:從理論到算法

博客

如何在Python中從Scratch開始實現感知器算法,Jason Brownlee

單層神經網絡與梯度降低,Sebastian Raschka

視頻

感知器訓練

感知器算法的原理

三、將算法分解成塊

如今咱們已經收集了各類學習資源,是時候開始學習了。

與其從頭至尾讀一篇博客文章,不如先瀏覽一下章節標題和其餘重要信息,寫下要點,並試着概述算法。

在瀏覽了這些資料以後,我將感知器分爲如下5個部分:

1.初始化權重;

2.將權重乘以輸入,而後求和;

3.將結果與閾值進行比較以計算輸出(1或0);

4.更新權重;

5.重複以上步驟;

讓咱們詳細討論每個部分。

1.初始化權重

首先初始化權重向量。權重的數量須要與特徵的數量匹配。假設咱們有三個特徵,則權重向量以下所示:

權重向量一般被初始化爲0,在本文中咱們將繼續使用它。

2.將權重乘以輸入,而後求和

接下來,咱們將權重乘以輸入,而後求和。爲了更容易理解,我在第一行中對權重及其對應的特徵進行了着色。

在權重乘以特徵以後,咱們把它們加起來,這也被稱爲點積。

最後的結果爲0,將這個零時的結果設爲f。

3.和閾值進行比較

在計算出點積以後,咱們須要將它與閾值進行比較。

這裏選擇用0做爲閾值,但也能夠用其餘值做爲閾值。

因爲咱們計算出來的點積f不大於咱們的閾值(0),因此估計值等於0。

我將估計值表示爲帶帽的y(又名「y hat」),下標0表示第一行,也能夠用1表示第一行,這無所謂。這裏選擇從0開始。

若是咱們將這個結果與實際值進行比較,能夠看到當前的權重沒有正確地預測實際輸出。

由於咱們的預測不正確,因此進行下一步來更新權重。

4.更新權重

接下來更新權重,如下是要使用的方程:

基本原理是在迭代「n」處調整當前權重,以便在下一個迭代中獲得一個新的權重「n+1」。

爲了調整權重,咱們須要設置一個「學習率」。這是用希臘字母「eta」表示。       

這裏選擇用0.1表示學習率,也能夠用其餘值表示學習率,就像閾值的設置同樣。

如下是到目前爲止的總結:

繼續計算在n=2時的權重。

咱們已經成功地完成了感知器算法的第一次迭代。

5.重複以上步驟

因爲算法沒有計算出正確的輸出,咱們須要繼續。一般須要屢次迭代,遍歷數據集中的每一行來更新權重。對數據集的一次完整遍歷稱爲「epoch」。

由於數據集有3行,咱們須要3次迭代才能完成1個epoch。

咱們能夠設置總的迭代次數或epoch來繼續執行算法,好比指定30次迭代(或10個epochs)。

與閾值和學習率同樣,epoch的數量是一個能夠隨意使用的參數。

在下一個迭代中,咱們將繼續討論第二行特徵。

這裏不一一重複每一步了,如下是下一個點積的計算。

接下來,將點積和閾值進行比較,以計算新的估計值,更新權重,而後繼續。若是數據是線性可分的,感知器就會收斂。

四、從一個簡單的例子開始

如今咱們已經手工將算法分解成塊,如今用代碼開始實現它。爲了簡單起見,從一個很是小的「玩具數據集」開始。對於這種類型的問題,一個好的小的線性可分離數據集是NAND門。這是數電中經常使用的邏輯門。

由於這是一個很是小的數據集,咱們能夠手動將其輸入到Python中。

爲了讓模型計算誤差項,添加一個虛擬的特徵「x0」表示第一列。

能夠將誤差看做是截距項,模型能夠正確地分離這兩個類。

如下是輸入數據的代碼:

 

# Importing libraries # NAND Gate # Note: x0 is a dummy variable for the bias term # x0 x1 x2 x = [[1., 0., 0.], [1., 0., 1.], [1., 1., 0.], [1., 1., 1.]] y =[1., 1., 1., 0.]

與前一節同樣,我將逐步詳細介紹算法,編寫代碼並測試它。

1.初始化權重

第一步是初始化權重

# Initialize the weights import numpy as np w = np.zeros(len(x[0])) Out:[ 0. 0. 0.]

請記住,權重向量的長度須要與特徵的數量相匹配。對於這個NAND門的例子,長度是3。

2.將權重乘以輸入,而後求和

接下來,咱們將權重乘以輸入,而後求和(即點積)。

一樣,咱們可使用Numpy的dot()函數輕鬆地執行此操做。

咱們從權重向量和第一行特徵的點積開始。

# Dot Product f = np.dot(w, x[0]) print f Out:0.0

正如預期的那樣,結果是0。

爲了與上一節保持一致,我將點積賦給變量f。

3.與閾值進行比較

在計算了點積以後,將結果與閾值進行比較,從而對輸出進行預測。

設定閾值z等於0。若是點積f大於0,咱們的預測是1。不然,它就是零。

記住,這個預測一般是用一克拉的頂部來表示的,也被稱爲「帽子」,把預測值賦給變量yhat。

# Activation Function z = 0.0 if f > z: yhat = 1. else: yhat = 0. print yhat Out:0.0

正如預期的那樣,預測爲0。

在上面的註釋中,將這些代碼稱爲「激活函數」,是更正式的名稱。

查看NAND輸出的第一行,能夠看到實際值是1,因爲咱們的預測是錯誤的,因此須要繼續更新權重。

4.更新權重

如今已經得出了預測值,準備更新權重。

咱們須要設定一個學習率才能作到這一點。爲了與前面的例子保持一致,將學習率「eta」賦值爲0.1。

我將對每一個權重的更新進行硬編碼,使其更容易閱讀。

# Update the weights eta = 0.1 w[0] = w[0] + eta*(y[0] - yhat)*x[0][0] w[1] = w[1] + eta*(y[0] - yhat)*x[0][1] w[2] = w[2] + eta*(y[0] - yhat)*x[0][2] print w Out:[ 0.1 0. 0. ]

能夠看到權重如今已經更新了,繼續下去。

5.重複以上步驟

如今咱們已經完成了每個步驟,如今是時候把全部的東西放在一塊兒了。

最後一個尚未討論的是損失函數,即實現最小化的函數。在例子中,這將是平方和(SSE)偏差。

這就是咱們用來計算偏差的方法,看看模型是如何運行的。

把全部這些都聯繫起來,完整的函數以下所示:

import numpy as np # Perceptron function def perceptron(x, y, z, eta, t): ''' Input Parameters: x: data set of input features y: actual outputs z: activation function threshold eta: learning rate t: number of iterations ''' # initializing the weights w = np.zeros(len(x[0])) n = 0 # initializing additional parameters to compute sum-of-squared errors yhat_vec = np.ones(len(y)) # vector for predictions errors = np.ones(len(y)) # vector for errors (actual - predictions) J = [] # vector for the SSE cost function while n < t: for i in xrange(0, len(x)): # dot product f = np.dot(x[i], w) # activation function if f >= z:  yhat = 1. else: yhat = 0. yhat_vec[i] = yhat # updating the weights for j in xrange(0, len(w)): w[j] = w[j] + eta*(y[i]-yhat)*x[i][j] n += 1 # computing the sum-of-squared errors for i in xrange(0,len(y)): errors[i] = (y[i]-yhat_vec[i])**2 J.append(0.5*np.sum(errors)) return w, J 

 

如今已經編寫了感知器的全部代碼,開始運行它:

 

# x0 x1 x2 x = [[1., 0., 0.], [1., 0., 1.], [1., 1., 0.], [1., 1., 1.]] y =[1., 1., 1., 0.] z = 0.0 eta = 0.1 t = 50 print "The weights are:" print perceptron(x, y, z, eta, t)[0] print "The errors are:" print perceptron(x, y, z, eta, t)[0] Out:The weights are: [ 0.2 -0.2 -0.1] The errors are: [0.5, 1.5, 1.5, 1.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 

 

看一看上方的偏差,能夠看到偏差在第6次迭代時趨於0,對於以後的迭代,始終爲0。當偏差趨於0時,表示模型收斂了。也就是說模型已經正確地「學習」了適當的權重。

在下一節中,咱們將在更大的數據集上使用計算過的權重來作預測。

五、使用可行的實現進行驗證

到目前爲止,咱們已經找到了不一樣的學習資源,手動完成了算法,並經過一個簡單的例子在代碼中測試了它。

如今是時候將結果與可行的實現進行比較了。爲了比較,咱們將使用scikit-learn中的感知器

步驟以下:

1.導入數據;

2.將數據分紅訓練/測試集;

3.訓練咱們的感知器;

4.測試感知器;

5.和scikit-learn的感知器相比;

1.導入數據

從導入數據開始,能夠在這裏得到數據集的副本。

爲了確保感知器可以正常工做,所建立的數據集是線性可分的。爲了驗證,繼續繪製數據。

 

import pandas as pd import numpy as np import matplotlib.pyplot as plt df = pd.read_csv("dataset.csv") plt.scatter(df.values[:,1], df.values[:,2], c = df['3'], alpha=0.8)

14

上圖很容易看出數據集輕易地被一條直線分開。

在繼續以前,先來解釋繪製數據的代碼。

使用panda導入csv,它自動將數據放入dataframe中。

爲了繪製數據,必須從dataframe中提取值,因此使用了.values方法。

特徵在第1和第2列中,因此在散點圖函數中使用了這些特徵。第0列是包含1的虛擬特徵,這樣就能計算出截距。這與咱們在前一節中對NAND gate所作的事情類似。

最後,在scatterplot函數中使用c = df['3']和alpha = 0.8爲兩個類着色。輸出是第3列(0或1)中的數據,所以告訴函數使用第3列爲兩個類着色。

你能夠在這裏找到關於Matplotlib的散點函數的更多信息。

2.將數據分紅訓練/測試集

既然咱們已經確認了數據能夠線性分離,那麼如今就該分離數據了。在單獨的數據集上訓練模型和測試數據集是很好的實踐,可以避免過擬合。分離數據有不一樣的方法,但爲了簡單起見,這裏使用一個訓練集和一個測試集。

我先整理一下個人數據。若是查看原始文件,你會看到數據是按輸出(第三列)中0的行進行分組的,而後是全部的1。我想要改變一下,增長一些隨機性,因此我要洗牌。

df = df.values np.random.seed(5) np.random.shuffle(df)

我首先將數據從dataframe改成numpy數組。這將更容易地使用許多numpy函數,例如.shuffle。

爲了讓結果重現,我設置了一個隨機種子(5)。完成後,嘗試改變隨機種子,看看結果如何變化。

接下來把70%的數據分紅訓練集,30%分紅測試集。

 train = df[0:int(0.7*len(df))] test = df[int(0.7*len(df)):int(len(df))]

最後一步是分離訓練和測試集的特徵和輸出。

x_train = train[:, 0:3] y_train = train[:, 3] x_test = test[:, 0:3] y_test = test[:, 3]

我選擇了70%/30%做爲訓練/測試集,只是爲了這個示例,但我但願你研究其餘方法 ,好比k-fold交叉驗證。

3.訓練感知器

接下來,咱們要訓練感知器。

這很是簡單,咱們將重用在前一節中構建的代碼。

def perceptron_train(x, y, z, eta, t): ''' Input Parameters: x: data set of input features y: actual outputs z: activation function threshold eta: learning rate t: number of iterations ''' # initializing the weights w = np.zeros(len(x[0])) n = 0 # initializing additional parameters to compute sum-of-squared errors yhat_vec = np.ones(len(y)) # vector for predictions errors = np.ones(len(y)) # vector for errors (actual - predictions) J = [] # vector for the SSE cost function while n < t: for i in xrange(0, len(x)): # dot product f = np.dot(x[i], w) # activation function if f >= z:  yhat = 1. else: yhat = 0. yhat_vec[i] = yhat # updating the weights for j in xrange(0, len(w)): w[j] = w[j] + eta*(y[i]-yhat)*x[i][j] n += 1 # computing the sum-of-squared errors for i in xrange(0,len(y)): errors[i] = (y[i]-yhat_vec[i])**2 J.append(0.5*np.sum(errors)) return w, J z = 0.0 eta = 0.1 t = 50 perceptron_train(x_train, y_train, z, eta, t)

讓咱們來看看權重和平方偏差之和。

w = perceptron_train(x_train, y_train, z, eta, t)[0] J = perceptron_train(x_train, y_train, z, eta, t)[1] print w print J Out: [-0.5 -0.29850122 0.35054929] [4.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]

權值如今對咱們來講意義不大,但咱們將在下一節中使用這些數字來測試感知器,還將使用權重來比較咱們的模型和scikit-learn模型。

看一下平方偏差之和,咱們能夠看到感知器已經收斂,這是咱們指望的,由於數據是線性可分的。

4.測試感知器

如今是測試感知器的時候了。爲此,咱們將構建一個小型的perceptron_test函數。這和咱們已經看到的很類似。這個函數使用perceptron_train函數計算的權值的點積,以及特徵,以及激活函數來進行預測。

咱們惟一沒有看到的是accuracy_score,這是一個來自scikit-learn的評估度量函數,你能夠在這裏瞭解更多。

把全部這些放在一塊兒,如下是具體的代碼實現:

from sklearn.metrics import accuracy_score w = perceptron_train(x_train, y_train, z, eta, t)[0] def perceptron_test(x, w, z, eta, t): y_pred = [] for i in xrange(0, len(x-1)): f = np.dot(x[i], w) # activation function if f > z: yhat = 1 else: yhat = 0 y_pred.append(yhat) return y_pred y_pred = perceptron_test(x_test, w, z, eta, t) print "The accuracy score is:" print accuracy_score(y_test, y_pred) Out: The accuracy score is: 1.0

準確度爲1.0代表咱們的模型正確地預測了全部的測試數據。這個數據集顯然是可分離的,因此咱們指望這個結果。

    五、和scikit-learn的感知器相比

最後一步是將咱們的結果與scikit-learn的感知器進行比較。下面是這個模型的代碼:

from sklearn.linear_model import Perceptron # training the sklearn Perceptron clf = Perceptron(random_state=None, eta0=0.1, shuffle=False, fit_intercept=False) clf.fit(x_train, y_train) y_predict = clf. Predict(x_test)

如今咱們已經訓練了模型,讓咱們將權重與模型計算的權重進行比較。

Out: sklearn weights: [-0.5 -0.29850122 0.35054929] my perceptron weights: [-0.5 -0.298501220.35054929]Out: sklearn weights: [-0.5 -0.29850122 0.35054929] my perceptron weights: [-0.5 -0.298501220.35054929]

scikit-learn模型中的權重與咱們的相同,這意味着咱們的模型工做正常。

在咱們結束以前,有幾個小問題須要解決一下。在scikit-learn模型中,咱們必須將隨機狀態設置爲「None」並關閉變換,但咱們已經設置了一個隨機種子並打亂了數據,因此咱們不須要再這樣作了。

咱們還必須將學習率「eta0」設置爲0.1,以與咱們的模型相同。

最後一點是截距。由於咱們已經包含了一個虛擬的特徵列1s,咱們正在自動擬合截距,因此咱們不須要在scikit-learn感知器中打開它。

這些看起來都是次要的細節,但若是咱們不設置這些,就沒法達到與咱們的模型相同的結果。

這一點很重要。在使用模型以前,閱讀文檔並理解全部不一樣設置的做用是很是重要的。

6、寫下你的過程

這個過程當中的最後一步多是最重要的。你已經完成了全部的工做,包括學習、記筆記、從頭開始編寫算法,並將其與可行的實現進行比較,不要讓全部的好工做白白浪費掉!

寫下這個過程很重要,緣由有二:

一、你會獲得更深的理解,由於你正在教導別人你剛剛學到的東西。

二、你能夠向潛在僱主展現它。

證實你能夠從機器學習庫中實現一個算法是一回事,但若是你能夠本身從頭實現它,那就更使人印象深入了。一個展現你做品的好方法是使用GitHub頁面組合

結論

在這篇文章中,咱們學習瞭如何從零開始編寫實現感知器。更重要的是,咱們學習瞭如何找到有用的學習資源,以及如何將算法分解成塊。

而後,咱們學習瞭如何使用一個玩具數據集在代碼中實現和測試算法。

最後,咱們經過比較咱們的模型和可行實現的結果來結束本文。要得到使用的Python代碼的完整副本,單擊下面的綠色按鈕。

這是在更深層次上學習算法的一個很好的方法,這樣就能夠本身實現它了。

大多數狀況下,你將使用可行的實現,但若是你真的想深刻了解底層的狀況,從頭實現它是一個很好的練習。

原文連接 

相關文章
相關標籤/搜索