從頭開始編寫任何機器學習演算法的6個步驟:感知器案例研究
從頭開始編寫機器學習演算法是一種非常有益的學習體驗。 我們在此過程中強調了6個步驟。
有些演算法比其他演算法更復雜,所以從一些簡單的演算法開始,從一些非常簡單的演算法開始,比如單層感知器。
我將以感知器為例,帶您經歷以下6步過程,從頭開始編寫演算法:
- 對演算法有一個基本的瞭解
- 找到一些不同的學習來源
- 將演算法分解成塊
- 從一個簡單的例子開始
- 使用可信的實現進行驗證
- 寫下你的過程
獲得基本瞭解
這又回到了我最初所說的。如果你不瞭解基礎知識,不能從頭開始處理演算法。
至少,你應該能夠回答以下問題:
- 它是什麼?
- 它的典型用途是什麼?
- 我什麼時候不能用這個?
對於感知器,讓我們繼續回答這些問題:
- 單層感知器是最基本的神經網路。它通常用於二進位制分類問題(1或0,“是”或“否”)。
- 一些簡單的用法可能是情感分析(正面或負面反應)或貸款違約預測(“將違約”,“將不違約”)。對於這兩種情況,決策邊界都必須是線性的。
- 如果決策邊界是非線性的,你就不能用感知器。對於這些問題,您需要使用不同的方法。
使用不同的學習資源
在你對模型有了基本的瞭解之後,是時候開始你的研究了。 有些人用教科書學得更好,有些人用視訊學得更好。 就我個人而言,我喜歡到處轉轉,使用各種各樣的資源。
對於數學細節,教科書做得很好,但對於更實際的例子,我更喜歡部落格帖子和YouTube視訊。 對於感知器,這裡有一些很好的來源:
教科書
-
ofollow,noindex" target="_blank">The Elements of Statistical Learning 4.5.1節
-
Understanding Machine Learning: From Theory To Algorithms 21.4 節
部落格
-
How To Implement The Perceptron Algorithm From Scratch In Python , by Jason Brownlee
-
Single-Layer Neural Networks and Gradient Descent , by Sebastian Raschka
視訊
Perceptron Training How the Perceptron Algorithm Works
將演算法分解成塊
現在我們已經收集了資料,是時候開始學習了。 與其從頭到尾讀一章或一篇部落格文章,不如先瀏覽一下章節標題和其他重要資訊。 寫下要點,並試著概述演算法 。
在瀏覽了這些資料之後,我將感知器分為以下5個部分:
- 初始化權重
- 將權重乘以輸入,然後求和
- 將結果與閾值進行比較以計算輸出(1或0)
- 更新權重
- 重複
讓我們詳細討論每一個問題。
1。初始化權重首先,我們將初始化權向量。 權重的數量需要與特徵的數量匹配。假設我們有三個特徵,這是權重向量的樣子
權重向量通常是用零初始化的,所以我將在這個例子中繼續使用它。
2。將權重乘以輸入,然後求和
接下來,我們將權重乘以輸入,然後求和。 為了更容易理解,我在第一行中對權重及其對應的特徵進行了著色
在我們把權重乘以特徵之後,我們把它們加起來。這也被稱為點積。
最後的結果是0。我將把這個臨時結果稱為“f”。
3.比較閾值
在計算出點積之後,我們需要將它與閾值進行比較。 我選擇用0作為我的臨界值,但是你可以試著用一些不同的數字。
由於我們計算出來的點積f不大於我們的閾值(0)我們的估計值等於0。 我將估計值表示為帶帽的y(又名“y帽”),下標為0以對應第一行。你可以在第一行用1,這無所謂。我選擇從0開始。 如果我們將這個結果與實際值進行比較,我們可以看到我們當前的權重沒有正確地預測實際輸出。
由於我們的預測是錯誤的,我們需要更新權重,這將我們帶到下一步。
**4 更新權重 **
接下來,我們要更新權重。下面是我們要用到的方程:
基本思想是,我們在迭代“n”處調整當前權重,以便在下一個迭代中得到一個新的權重“n+1”。 為了調整權重,我們需要設定一個“學習率”。這是用希臘字母“eta”表示的。 我選擇用0.1表示學習速率,但是你可以用不同的數字,就像用臨界值一樣。 以下是我們到目前為止的總結:
現在讓我們繼續計算迭代n=2的新權重。
我們已經成功地完成了感知器演算法的第一次迭代。
**5 重複 **
由於我們的演算法沒有計算正確的輸出,我們需要繼續。 通常我們需要多次迭代。遍歷資料集中的每一行,我們將每次更新權重。 對資料集的一次完整掃描稱為“epoch”。 因為我們的資料集有3行,我們需要3次迭代才能完成1epoch。
我們可以設定總的迭代次數或epoch以繼續執行演算法。也許我們想指定30次迭代(或10次epochs)。 與閾值和學習率一樣,epoch的數量是一個可以隨意使用的引數。 在下一個迭代中,我們將繼續討論第二行特徵。
我不會重複每一步,但這是下一個點積的計算:
接下來,我們將比較點積和閾值,以計算新的估計值,更新權值,然後繼續。如果資料是線性可分的,感知器就會收斂。
從一個簡單的例子開始
現在我們已經手工將演算法分解成塊,現在是開始在程式碼中實現它的時候了。 為了簡單起見,我總是喜歡從一個非常小的“玩具資料集”開始。
對於這種型別的問題,一個漂亮的小的線性可分離資料集是NAND門。這是數位電子學中常用的邏輯閘。
由於這是一個相當小的資料集,我們可以手動將其輸入到Python中。 我要新增一個虛擬的特徵“x0”它是一列1。我這樣做是為了讓我們的模型計算偏差項。 您可以將偏差看作是截距項,它正確地允許我們的模型分離這兩個類。 以下是輸入資料的程式碼:
# Importing libraries # NAND Gate # Note: x0 is a dummy variable for the bias term #x0x1x2 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
正如預期的那樣,結果是0。 為了與上一節的筆記保持一致,我將點積賦給變數f。
3.與閾值比較
在計算了點積之後,我們準備將結果與閾值進行比較,從而對輸出進行預測。 同樣,我將保持與上一節的筆記一致。 我要讓臨界值z等於0。如果點積f大於0,我們的預測是1。否則,它就是零。 記住,這個預測通常是頂部一橫來表示的,也被稱為“帽子”。我將把預測賦給的變數是yhat。
# Activation Function z = 0.0 if f > z: yhat = 1. else: yhat = 0. print yhat
正如預期的那樣,預測為0。 您會注意到,在上面的註釋中,我將其稱為“啟用函式”。這是對我們正在做的更正式的描述。 檢視NAND輸出的第一行,我們可以看到實際值是1。由於我們的預測是錯誤的,我們需要繼續更新權重。
4 更新權重
現在我們已經做出了預測,我們準備更新權重。 我們需要設定一個學習速度才能做到這一點。為了與前面的示例一致,我將學習速率“eta”賦值為0.1。 我將對每個權重的更新進行硬編碼,使其更易於閱讀。
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
我們可以看到我們的權重現在已經更新了,所以我們準備繼續。
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
現在我們已經編寫了完整感知器的程式碼,讓我們繼續執行它:
#x0x1x2 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]
看一看錯誤,我們可以看到錯誤在第6次迭代時趨於0。對於迭代的其餘部分,它保持在0。 當誤差趨於0時,我們知道模型收斂了。這告訴我們,我們的模型已經正確地“學習”了適當的權重。 在下一節中,我們將使用對較大資料集的計算權重來進行預測。
使用可信的實現進行驗證
到目前為止,我們已經找到了不同的學習資源,手工完成了演算法,並通過一個簡單的例子在程式碼中測試了它。 現在是時候將我們的結果與可信的實現進行比較了。為了比較,我們將使用scikit-learn中的感知器。 我們將使用以下步驟進行比較:
- 匯入資料
- 將資料分成訓練集/測試集
- 訓練我們的感知器
- 測試感知器
- 和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)
看看這個圖,很容易看出我們可以用一條直線將這些資料分開。 在繼續之前,我將在上面解釋我的繪圖程式碼。 我使用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 productf = np.dot(x[i], w)# activation functionif 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
權值現在對我們來說意義不大,但我們將在下一節中使用這些數字來測試感知器。我們還將使用權重來比較我們的模型和scikit-learn模型。 看一下平方求和誤差,我們可以看到感知器已經收斂,這是我們期望的,因為資料是線性可分離的。
4測試我們的感知器
現在是測試感知器的時候了。為此,我們將構建一個小型的perceptron_test函式。 這和我們已經看到的很相似。這個函式取我們使用perceptron_train函式計算的權值的點積,以及特徵,以及啟用函式,來進行預測。 我們唯一沒有看到的是accuracy_score。這是一個來自scikitlearn的評價度量函式。你可以在這裡瞭解更多。 把所有這些放在一起,下面是程式碼的樣子:
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)
得分為1.0表明我們的模型正確地預測了所有的測試資料。這個資料集顯然是可分離的,所以我們期望這個結果。 5。和學過的感知器相比 最後一步是將我們的結果與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)
現在我們已經訓練了模型,讓我們將權重與模型計算的權重進行比較。
scikit-learn模型中的權重與我們的相同。這意味著我們的模型工作正常,這是個好訊息。 在我們結束之前,有幾個小問題需要複習一下。在scikit-learn模型中,我們必須將隨機狀態設定為“None”並關閉變換。我們已經設定了一個隨機種子並打亂了資料,所以我們不需要再這樣做了。 我們還必須將學習速率“eta0”設定為0.1,以與我們的模型相同。
最後一點是截距。因為我們已經包含了一個虛擬的特徵列1s,我們正在自動擬合截距,所以我們不需要在scikit-learn感知器中開啟它。 這些看起來都是次要的細節,但如果我們不設定這些,我們就無法複製與我們的模型相同的結果。 **這一點很重要。在使用模型之前,閱讀文件並理解所有不同設定的作用是非常重要的。 **
寫下你的過程 這個過程中的最後一步可能是最重要的。 您已經完成了所有的工作,包括學習、記筆記、從頭開始編寫演算法,並將其與可信的實現進行比較。不要讓所有的好工作白白浪費掉! 寫下這個過程很重要,原因有二:
- 你會得到更深的理解,因為你正在教導別人你剛剛學到的東西。
- 你可以向潛在僱主展示它。
證明你可以從機器學習庫中實現一個演算法是一回事,但如果你可以自己從頭實現它,那就更令人印象深刻了。 一個展示你作品的好方法是使用 GitHub頁面組合 。
總結
在這篇文章中,我們學習瞭如何從零開始實現感知器。 更重要的是,我們學習瞭如何找到有用的學習資源,以及如何將演算法分解成塊。 然後,我們學習瞭如何使用一個玩具資料集在程式碼中實現和測試演算法。 最後,我們通過比較我們的模型和可信實現的結果來結束本文。
這是在更深層次上學習演算法的一個很好的方法,這樣您就可以自己實現它了。 大多數情況下,您將使用可信的實現,但如果您真的想深入瞭解底層的情況,從頭實現它是一個很好的練習。 請務必在下面留下您的評論,如果您在學習過程中還有其他的幫助您的技巧,請告訴我!
作者:John Sullivan 原文連結:https://www.dataoptimal.com/machine-learning-from-scratch/
版權宣告:作者保留權利,嚴禁修改,轉載請註明原文連結。
資料人網是資料人學習、交流和分享的平臺http://shujuren.org 。專注於從資料中學習到有用知識。 平臺的理念:人人投稿,知識共享;人人分析,洞見驅動;智慧聚合,普惠人人。 您在資料人網平臺,可以1)學習資料知識;2)建立資料部落格;3)認識資料朋友;4)尋找資料工作;5)找到其它與資料相關的乾貨。 我們努力堅持做原創,聚合和分享優質的省時的資料知識! 我們都是資料人,資料是有價值的,堅定不移地實現從資料到商業價值的轉換!