摘要: 通用版學習機器學習算法的方法,你值得擁有!html
從0開始編寫機器學習算法是一種很是好的體驗python
當你點擊以後你會感到壓力,由於其中有些算法比其餘算法更復雜,因此我建議你從一些簡單甚至更簡單的算法開始,好比單層感知器。github
以感知器爲例從頭開始編寫算法,主要爲如下6個步驟:web
1.對算法有一個基本的瞭解;算法
2.找到一些不一樣的學習來源;api
3.將算法分解成塊;數組
4.從一個簡單的例子開始;網絡
5.使用可行的實現進行驗證;app
6.寫下你的過程。
一、對算法進行基本的瞭解
若是你不瞭解基礎知識,不要從零開始編寫算法。
至少,你應該可以回答如下問題:
1.它是什麼?
2.它的典型用途是什麼?
3.使用條件是什麼?
對於感知器,至少可以回答如下問題:
1.單層感知器是最基本的神經網絡。它一般用於二分類問題(1或0,「是」或「否」)。
2.一些簡單的用法多是情緒分析(積極或消極反應)或貸款違約預測(「將違約」或「將不違約」)。對於這兩種狀況,決策邊界都必須是線性的。
3.若是決策邊界是非線性的,你就不能用感知器。對於這些問題,須要使用其餘不一樣的方法。
二、使用不一樣的學習資源
在你對模型有了基本的瞭解以後,這個時候能夠開始你的研究了。
有些人用教材學得更好,有些人用視頻學得更好。我我的喜歡處處轉轉,使用各類各樣的資源。
對於數學細節,教材講的比較詳細,但對於更實際的例子,我更喜歡博客帖子和視頻。
對於感知器,這裏有一些很好的學習資源:
教材
博客
如何在Python中從Scratch開始實現感知器算法,Jason Brownlee
視頻
三、將算法分解成塊
如今咱們已經收集了各類學習資源,是時候開始學習了。
與其從頭至尾讀一篇博客文章,不如先瀏覽一下章節標題和其餘重要信息,寫下要點,並試着概述算法。
在瀏覽了這些資料以後,我將感知器分爲如下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代碼的完整副本,單擊下面的綠色按鈕。
這是在更深層次上學習算法的一個很好的方法,這樣就能夠本身實現它了。
大多數狀況下,你將使用可行的實現,但若是你真的想深刻了解底層的狀況,從頭實現它是一個很好的練習。