通过从零开始实现一个感知机模型,我学到了这些

对许多刚入门机器学习的开发者而言,许多参数和定义都显得抽象、难以理解,可能许多人直到开始进入实际的项目研发,都还没能真正搞清楚这些参数和定义的确切含义。为此,我在这里故意避开 scikit-learn 等现成的算法工具,从零开始自己用 Python 实现了一个感知机二元分类器,一方面通过实际代码深入认识了感知机的内部原理和相关参数的具体含义,另一方面也总结了一些自己的研发心得,希望对各位初学者有所帮助。

  什么是二元分类器(Binary Classifier)?

分类器是基于一组特征来确定输入元素所在类别的机器学习算法。例如,分类器可以根据一些既定特征,预测一个啤酒的类别。这些特征可以是酒精含量、香气和外观等。更详细一点,例如一个基于机器学习的分类器,根据 8% 的酒精含量、100 IBU (International Bitterness Unit,国际苦味指数)和强烈橙子香味,就能判断一种啤酒是不是 Indian Pale Ale 。

一般来说,机器学习可以分为三个主要的类型:无监督学习,监督学习和强化学习。分类器属于监督学习的范畴。所谓监督学习就是我们提前知道待解问题的答案,即期望的输出是已知的那些场景。例如在上述关于啤酒分类的例子中,我们完全可以想办法得到一组描述啤酒各种特征和类别的数据集,然后基于这组数据对分类器展开训练。

这里我将实现的是一个二元分类器,是所有分类器中最简单的一种,其输出结果只有两种:0 或 1 ,对或错。

  怎么搭建机器学习模型?

概括地说,要搭建和使用一个机器学习模型,一般分为如下四个步骤:

1. 预处理

2. 训练

3. 评估

4. 预测

预处理

预处理是构建机器学习模型的第一步,该步骤的主要工作是获取数据,并对数据进行必要的预处理,以备后续使用。包括去掉数据中的冗余、格式整理以及选定与数据相关的特征等。预处理中的常见工作包括:

从原始数据中提取特征

清理并格式化数据

删除多余的特征(或高度相关的特征)

优化特征数

标准化特征数据的范围(也称为特征缩放 Feature Scaling )

随机拆分数据集:训练数据集和测试数据集

训练

准备好数据之后,下一步是为目标任务选择一个合适的算法。在下面的二元分类器中,我们选择的算法名为感知机(perceptron)。通常各种算法都有各自的优缺点,要根据目标任务灵活选择。

在这个步骤中,你可以先针对几个不同算法展开测试,然后根据测试结果选择性能最佳的算法。评估一个算法性能表现的方法有很多,在分类器场景中,一个最常用的方法是看分类精度(classification accuracy),即在所有输入样例中,正确分类的比例越高,算法就越优秀。在这个步骤中,开发者需要调整选定算法的参数,即所谓的超参数(Hyperparameters)过程。

本文将主要关注二元分类器的训练过程,深入探讨算法的内在工作原理。如果你对机器学习流程中的其他步骤感兴趣,可以通过文末链接阅读更多其他内容。

评估

当模型训练完成之后,就可以通过训练数据集之外的未知数据对模型展开评估。评估中一个非常重要的指标是泛化误差(Generalization Error),即一个算法面对未知数据集的预测精度究竟怎样。一旦你对评估结果满意,就可以通过模型进行真正的预测了。

  实现感知机

下面开始搭建我们的分类器。这里我们选用的算法是感知机(perceptron),它是神经网络与支持向量机的基础,是一种最简单的二元分类器模型。Perceptron算法的思路虽然简单,但功能强大,给定一个数据集,算法可以自动学习最佳权重系数,然后乘以输入特征,根据结果决定一个神经元是否启用。

下面我们根据具体代码简述感知机模型的基本实现流程。

首先,初始化一个权重等于零的数组,数组长度等于特征数加1。这里之所以加1,是为了存储“阈值”(threshold)。这里需要注意的是,Perceptron算法要求特征必须是数字值。具体代码如下:

self.w_ = np.zeros(1 + X.shape[1])

第二步,开始一个迭代次数为 n_iter 的循环。这是一个由数据科学家定义的超参数。具体代码如下:

for _ in range(self.n_iter):

第三步,针对每个训练数据和结果都开始一个循环,这里的结果是指算法的最终期望输出。由于我们搭建的是一个二元分类器,因此结果是 -1 或 1 两种。

基于数据点的特征,算法将计算出最终结果:-1 或 1 。这里的预测方法具体是指特征与适当权重的矩阵乘积。在乘积的基础上加上此前定义好的阈值,如果结果大于 0 ,则预测为 1 ,否则为 -1.

算法可以根据每次迭代得到的预测结果的准确性灵活调整权重。在迭代的初期,预测结果一般不太可能是准确的,因为权重没有被调整过,也就不会收敛。需要注意的是,调整操作与目标值、预测值之间的差成比例,这个差值需要乘以 eta。这里 eta 是数据科学家定义的另一个超参数,介于 0 和 1 之间,eta 的值越大,权重的校正就越多。最终当预测结果准确时,就会停止调整权重的过程。具体代码如下:

self.w_ = np.zeros(1 + X.shape[1])
for _ in range(self.n_iter):
    for xi, target in zip(X, y):
        update = self.eta * (target - self.predict(xi))
        self.w_[1:] += update * xi
        self.w_[0] += update
def net_input(self, X):
    """Calculate net input"""
    return np.dot(X, self.w_[1:]) + self.w_[0]
def predict(self, X):
    """Return class label after unit step"""
    return np.where(self.net_input(X) >= 0.0, 1, -1)     

在代码中,只有当两个类别是线性可分时,感知机模型才会收敛。简单说就是:如果你能画一条直线来完全分离两个类,算法才会收敛。否则,算法将一直迭代下去,并将重新调整权重,直到循环达到最大次数 n_iter。

以上感知机的完整代码如下所示:

通过以上实践,我有如下几点收获:

  收获1:参数的理解

如果你直接调用 scikit-learn 等工具来实现感知机,那么像学习率和迭代次数这些参数就会显得很抽象,因为你只需要把它们填到 API 接口里,然后就得到了结果,完全不清楚这些参数的实际意义。但是如果你试着自己写代码来实现,例如自己实现一个感知机,那么这些参数的含义就一目了然。

学习率

例如学习率,就是指当预测不准确时权重被校正的比例,该值必须介于 0 和 1 之间。如下代码所示,fit 函数将对每个观察结果进行迭代,调用 predict 函数,然后根据目标和预测值之间的差异调整权重,然后乘以学习率。

更高的学习率意味着算法将更积极地调整权重。每次迭代都会根据预测值是否准确重新调整权重值。

# Partial portion of the "fit" function
for xi, target in zip(X, y):
    update = self.eta * (target - self.predict(xi))
    self.w_[1:] += update * xi
    self.w_[0] += update
    errors += int(update != 0.0)

迭代次数

迭代次数是指算法在训练集中运行的总次数。如果迭代次数设为 1,则算法就只在数据集上运行一次,针对每个数据点只更新一次权重。这样得到的模型相比较高迭代次数的模型,准确率可能更低。在数据集的体量较大时,高迭代次数可能引起非常高迭代成本。

for _ in range(self.n_iter):
    errors = 0
    for xi, target in zip(X, y):
        update = self.eta * (target - self.predict(xi))
        self.w_[1:] += update * xi
        self.w_[0] += update
        errors += int(update != 0.0)
    self.errors_.append(errors)

学习度和迭代次数通常是相互关联的,需要一起调整。例如,如果你的学习率很小,则意味着算法每次对权重的调整都很微小,那么可能就需要更多的迭代次数。

  收获2:线性代数的重要性

其次,特别重要的一点是:不单是Perceptron算法,在整个机器学习领域,线性代数课程中的相关内容都至关重要,因为整个算法都可以通过线性代数的相关公式来描述。而如果你从来没有学过线性代数的相关知识,那么这些公式对你来说就是不可见的,也就不利于算法的理解和实现。因此,学好线性代数对开发机器学习和理解各种算法至关重要,这里推荐一个线性代数的在线教程,并且附带练习。

教程地址:https://www.khanacademy.org/math/linear-algebra 

  收获3:一种通用的学习方法

最后,我想通过以上 Perceptron 算法推荐一个通用的学习方法,即手动敲入代码,拒绝简单的复制粘贴。

早在2012年,当我在学习编写一个 Web 应用时就体会到了手动敲入代码的好处。当时,我花了比别人多得多的时间跟着教程,一步一步把案例中的代码手动敲入编辑器,而没有选择复制粘贴。这看起来很蠢,但不可否认这种方法真的有用。因为不可避免的,在手动敲入这些代码时你一定会引入错误,因此你敲完的代码可能根本就运行不起来,也可能得到一些意想不到的错误,这时你就必须排查和修改代码中的错误。其实,这个排查和修改的过程就是思考和学习的过程,通过这样的过程,你会对整个代码和教程中的知识点理解的更透彻,当然也记得更清楚。

所以,如果你要学习 Perceptron 算法,请不要直接复制和粘贴。试着将这些代码手动敲入编辑器,然后编译运行。更不要被动地阅读,仅仅对着代码读来读去,永远也成不了数据科学家,你必须参与进去,主动修改和运行这些代码,才能收获的更多。

原文地址:http://www.jeannicholashould.com/what-i-learned-implementing-a-classifier-from-scratch.html 

深入阅读:http://www.jeannicholashould.com/learning-machine-learning.html 

本文作者:恒亮

本文转自雷锋网禁止二次转载,原文链接

时间: 2024-11-08 19:07:55

通过从零开始实现一个感知机模型,我学到了这些的相关文章

如何从零开始搭建一个技术平台?

郑昀 创建于2016/3/30 最后更新于2016/4/8 关键词:技术预研课题,平台设计,应用场景,故事,信息架构,业务流程,数据流程 本文档适用人员:全体研发 提纲: 如何从零开始搭建一个技术平台? 应用场景其实就是我们的愿景 从应用场景推导出故事 从故事推导出信息架构和业务流程 一,如何从零开始? 如果让你把下面这套技术体系串联起来,从零开始构建一个技术平台,你如何做需求分析呢,在没有产品经理帮助你梳理的情况下?   下面这些系统涵盖了我们研发测试运维日常工作的方方面面: idCenter

金庸说:创造一个功夫永远比学功夫的人厉害

  金庸说:创造一个功夫永远比学功夫的人厉害           大师一席话胜过十年书 by  luozhonghua.    现实生活中,很少有人会体会这句话的深刻含义,也很难做到这一点.    现实生活中,太多的人寓居现实,被现实的环境,气氛所陶醉,我记得最近出了一个电视剧,叫什么金牌律师,其中有一个典型现实式的人物叫全敏敏,看过的人都知道,最后被现实所抛弃,我想这就是以电视剧的方式折射了中国目前的悲剧式的环境.   今天,我看了有关魅族手机创始人,一个中国传奇式的人物,绝大部分人来说,他比

从零开始码一个皮卡丘检测器-CNN目标检测入门教程(上)

本文先为大家介绍目前流行的目标检测算法SSD (Single-Shot MultiBox Object Detection)和实验过程中的数据集.训练.测试过程及结果参见<从零开始码一个皮卡丘检测器-CNN目标检测入门教程(下)> 目标检测通俗的来说是为了找到图像或者视频里的所有目标物体.在下面这张图中,两狗一猫的位置,包括它们所属的类(狗/猫),需要被正确的检测到. 所以和图像分类不同的地方在于,目标检测需要找到尽量多的目标物体,而且要准确的定位物体的位置,一般用矩形框来表示. 在接下来的章

怎么把excel中的值导入到一个pojo模型中去

问题描述 怎么把excel中的值导入到一个pojo模型中去 如:有一个表格中有 姓名 性别 生日 有一个javabean的模型为user 其中有 id ,name ,sex ,birth 怎么将表中数据变成一个list对象 解决方案 参考一下POI,POI提供API给Java程序对Microsoft Office格式档案读和写的功能

程序人生-一个程序员对学弟学妹建议

  程序人生-一个程序员对学弟学妹建议 基础的课程,比方数据结构,操作系统原理等等虽然不能让你立马就实现一个linux(这是许多人嘲笑理论课程无用的原因),但它们能够显著的减少你在学习新技术时学习曲线的坡度.我把大二的所有时间花在了汇编,我始终认为,对一个初学者来说,IT界的技术风潮是不可追赶.我时常看见自己的DDMM们把课本扔了,去卖些价格不菲的诸如C#, VB.Net 这样的大部头,这让我感到非常痛心.而许多搞不清指针是咋回事的BBS站友眉飞色舞的讨论C#里面可以不用指针等等则让我觉得好笑.

net 4.0-C#实例化一个firefox模型问题

问题描述 C#实例化一个firefox模型问题 我想知道C#可以通过: SHDocVw.InternetExplorer IE = new InternetExplorer();实例化一个IE模型,然后可以抓取页面元素,模拟IE的各种行为,以下是模拟登陆代码(请不要说通过post,post返回的cookie是临时cookie) 但是C#可不可以实例化一个firefox或者chrome模型,然后像下面代码一样在firefox浏览器中模拟以下行为呢? SHDocVw.InternetExplorer

数据库-hibernate的一个问题,没学过,但需要看懂源码,看懂了,但不知道为什么错

问题描述 hibernate的一个问题,没学过,但需要看懂源码,看懂了,但不知道为什么错 报错是这样的:No row with the given identifier exists: [com.xo.waiter...... 在源码里是这样的: int j = 0; for (int size = waiters.size(); j < size; j++) { waiterService.deleteWaiter( (Waiter)waiters.get(j)); } 调用的hibernat

中间件-一个抽象模型的平台模块,用于高速查找。这个能卖的出去吗?

问题描述 一个抽象模型的平台模块,用于高速查找.这个能卖的出去吗? 比如底层按照二叉树的结构进行数据存储,对应用层屏蔽各类数据库差异,并能实现快速存取.我们想作为中间件销售,这个有人要吗?

自己动手从零开始写一个完整的android Service

自己动手从零开始写一个完整的android Service             Android service对于从事android开发的人,不管是底层开发人员还是应用开发人员都不是一个陌生的对象.笔者就是由于长期主要从事的都是底层开发,对framework下的service以前只是略知一二,知道上面有audio service.light service.power service等等service,这些service都是要通过层层调用call到驱动的,发挥着重要的作用.大家描写servi