摘要: 通用版学习机器学习算法的方法,你值得拥有!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代码的完整副本,单击下面的绿色按钮。
这是在更深层次上学习算法的一个很好的方法,这样就能够本身实现它了。
大多数状况下,你将使用可行的实现,但若是你真的想深刻了解底层的状况,从头实现它是一个很好的练习。