《Arduino家居安全系统构建实战》——1.6 改进我们的模型

1.6 改进我们的模型

我们实现了可能有效的最愚蠢的模型,它也确实干得不错——93.4%的正确率。我们还能让它变得更好吗?

遗憾的是,对此没有通用的答案。除非你的模型预测已经100%正确,否则始终有可能改进,知道答案的方法只有一个:尝试各种方法,看看它们是否有效。构建好的预测模型涉及许多反复尝试,正确建立模型以快速迭代、试验和验证思路至关重要。

我们可以探索哪些方向?我可以不假思索地提出一些。

调整距离函数。我们在此使用的曼哈顿距离只是许多可能性中的一个,选择“正确”的距离函数通常是获得好模型的关键因素之一。距离函数(或者代价函数)本质上是向机器传达如何在其世界中考虑类似或者不同事物的手段,因此认真思考这一点是非常重要的。

搜索多个最靠近的点,而不是只考虑一个最近点,并进行“多数表决”。这可使模型变得更加健壮;观察更多的候选可以降低无意中选择错误对象的概率。这种方法有一个名称——“K最近邻”算法,是机器学习的经典方法之一。

对图像进行一些巧妙的处理。例如,想象拍摄一张照片,但是向右移动一个像素。如果将这个图像与原始版本比较,尽管它们是同一个图像,但距离可能很大。用于补偿这一问题的方法之一是使用某种模糊。用邻近像素颜色的平均值代替每个像素能够缓解“图像不重合”问题。

我敢肯定,你也能想到其他的主意,下面我们一起来探索第一种思路。

1.6.1 试验距离的另一种定义

我们从距离开始。为何不尝试一下高中已经学过的距离(学术上叫作“欧几里得距离”)?下面是这种距离的公式:

Dist(X,Y)=sqrt{(x_{1}-y_{1})^{2}+(x_{2}-y_{2})^{2}+ cdots + (x_{n}-y_{n})^{2}}

上述公式说明,两点X和Y之间的距离是对应坐标差值平方和的平方根。你可能已经见过这一公式的简化版本,如果取某个平面上的两个点:X=(x 1,x 2)和Y=(y 1,y 2),其欧几里得距离为:

Dist(X,Y)=\sqrt{(x_1-y_1)^2+(x_2-y_2)^2}

相比数学,代码可能让你更舒服一些,下面是上式在F#中的表现形式:

let euclideanDistance (X,Y) =
    Array.zip X Y
    |> Array.map (fun (x,y) -> pown (x-y) 2)
    |> Array.sum
    |> sqrt```
我们取两个浮点数组作为输入(每个数组代表一个点的坐标),计算每个元素差值的平方,加总,再求平方根。这不是很难,而且相当清晰!

在此应该提到几个技术细节。首先,F#内建了许多很好的数学函数,通常可以在System.Math类中搜索。Sqrt就是这样一个函数——用let x = sqrt 16.0代替var x = Math.Sqrt(16)不是好多了吗?pown是另一个此类函数,它是指数为整数时“求n次方”的专用版本,通用版本则是**运算符,如let x = 2.0 ** 4.0。如果已知指数为整数,pown将明显提升性能。

另一个细节,我们在此使用的距离函数是正确的,但是从技术上说,对于我们的目的,实际上可以去掉sqrt。我们需要的是最接近的点,如果0≤A<B,则sqrt A<sqrt B。因此不必承担这一运算的代价,可以将其去掉。这使我们仅在整数上运算,比双精度数或者浮点数要快得多。

####1.6.2 重构距离函数
如果我们的目标是试验不同的模型,现在就是进行一些重构的好时机。我们希望更换代码中的不同部分,观察对预测质量的影响。具体地说,我们打算切换距离。典型的面向对象方法是提取一个接口(如IDistance),注入训练中(这就是C#示例中所做的)。然而,如果你真正地思考了这个问题,就会发现接口有些大材小用了——我们需要的只是一个函数,以两个点作为输入并返回一个整数——两点之间的距离。可以这样做。

程序清单1-11 重构距离函数

type Distance = int[] * int[] -> int

let manhattanDistance (pixels1,pixels2) =

Array.zip pixels1 pixels2
|> Array.map (fun (x,y) -> abs (x-y))
|> Array.sum

let euclideanDistance (pixels1,pixels2) =

Array.zip pixels1 pixels2
|> Array.map (fun (x,y) -> pown (x-y) 2)
|> Array.sum

let train (trainingset:Observation[]) (dist:Distance) =

let classify (pixels:int[]) =
    trainingset
    |> Array.minBy (fun x -> dist (x.Pixels, pixels))
    |> fun x -> x.Label
classify```

我们创建一个Distance类型以代替接口,这是一个函数签名,以一对像素为参数,返回一个整数。现在可以传递任意多个Distance作为train函数的参数,这将返回使用特定距离的分类器。例如,我们可以用manhattanDistance函数或者euclideanDistance函数(或者想要进一步试验的任意距离函数)训练分类器,比较它们的表现。注意,这在C#中也完全可能做到。我们可以简单地使用Func,而不创建IDistance接口,下面是代码。

程序清单1-12 函数式C#示例

public class FunctionalExample
{
    private IEnumerable<Observation> data; 

    private readonly Func<int[], int[], int> distance; 

    public FunctionalExample(Func<int[], int[], int> distance)
    {
        this.distance = distance;
    } 

    public Func<int[], int[], int> Distance
    {
        get { return this.distance; }
    } 

    public void Train(IEnumerable<Observation> trainingSet)
    {
        this.data = trainingSet;
    } 

    public string Predict(int[] pixels)
    {
        Observation currentBest = null;
        var shortest = Double.MaxValue; 

        foreach (Observation obs in this.data)
        {
            var dist = this.Distance(obs.Pixels, pixels);
            if (dist < shortest) 

            {
                shortest = dist;
                currentBest = obs;
            }
        } 

        return currentBest.Label;
    }
}```
这种方法的重点在于组成函数而不是对象,是典型的函数式编程。C#虽然来源于面向对象语言,但是也支持许多函数式方法。可以说,从3.5版本开始,C#中的每个特性和创新(LINQ、Async等)都是从函数式编程中借鉴的。现在,人们在描述C#时会说,它首先是面向对象的,但是支持函数式方法,而F#首先是一种函数式编程语言,但是也适应面向对象风格。在本书中,我们一般更强调函数式风格,而不强调面向对象风格,因为按照我们的看法,前者比后者更适合机器学习算法,还有一个原因是,以函数式风格编写的代码通常提供很大的优势。

在任何情况下,我们都要做好设置,观察自己的想法是否表现良好。现在,可以创建两个模型,比较它们的表现:

let manhattanClassifier = train trainingData manhattanDistance
let euclideanClassifier = train trainingData euclideanDistance

printfn "Manhattan"
evaluate validationData manhattanClassifier
printfn "Euclidean"
evaluate validationData euclideanClassifier`
在此,最好的一件事是我们不需要在内存中重新加载数据集,只需要简单地修改脚本文件,选择包含所要运行新代码的部分,运行该部分即可。如果我们那么做,就应该看到新模型euclideanModel的分类图像的正确率为94.4%,而不是manhattanModel的93.4%。好极了!我们取得了1%的改进。1%看起来似乎不多,但是如果准确率已经达到93.4%,这就意味着错误率从6.6%降低到5.6%,有大约15%的收获。

从距离函数中的一个小变化,我们得到了很明显的改进。这似乎是一个有前景的方向,可能值得试验更复杂的距离函数,或者尝试前面提到的其他方向。如果不多加小心,就有可能因为多次试验而留下一大堆乱七八糟的代码。版本控制和自动化对经典软件开发是必不可少的,同样,在开发预测模型时,它们也是你的朋友。过程的细节和节奏可能不同,但是总体思路相同:你希望在对代码进行更改时不需要担心造成任何破坏,“在我的机器上可以正常工作”还不够好。在任何时候,任何人都应该可以使用你的代码,在没有任何人工干预的情况下复制你的结果。这种方法称作“可复制研究”。我强烈鼓励你朝着这个方向发展,尽可能在任何地方使用脚本和源代码控制。我自己曾经经历过这样的恐怖场景:前一天你的模型工作得很好,但是因为没有花时间清晰地进行保存,忘记了自己究竟是怎么做到的,从而弄丢了模型。不要成为那样的人!特别是在用Git或者Mercurial等DVCS创建分支如此容易的时代,没有任何借口犯那样的错误。对某个模型有新想法时,可以创建一个分支,保存在脚本中执行的步骤。

时间: 2024-09-20 05:39:37

《Arduino家居安全系统构建实战》——1.6 改进我们的模型的相关文章

《Arduino家居安全系统构建实战》——第1章 家居安全系统的入门知识

第1章 家居安全系统的入门知识 Arduino家居安全系统构建实战在这一章中,我们将会介绍如下的内容: 什么是家居安全基础设施它是如何工作的部署这样一个系统都需要些什么在当前条件下为安全系统所做的准备有线安全系统和无线安全系统传统系统与现代化家居安全系统为了紧紧跟随硬件的发展趋势,本书介绍了随处可见且价格极为低廉的平台--Arduino.今时今日,我们可以在各种各样的公共场所如中小学校.高等院校.小型企业或者公共机构见到这个小电路板的身影. Arduino平台因其众多的优势而著名,例如低廉的成本

《Arduino家居安全系统构建实战》——导读

前言 机器学习项目开发实战 如果你手里拿着这本书,我就可以认定你是对机器学习感兴趣的.NET开发人员了.你可能对编写C#应用程序很熟悉,开发的很有可能是业务线应用程序.以前你可能遇到过F#,也可能没有.而且,你很有可能对机器学习感到好奇.这一主题每天都见诸报端,因为它和软件工程有着很紧密的联系,但是使用的是不熟悉.看似有些抽象的数学概念.简而言之,机器学习看上去是有趣的主题.值得学习的实用技能,但是从哪里入手难以说清. 本书的意图是作为开发人员的机器学习入门书.我的主要目标是使熟悉代码编写的读者

《Arduino家居安全系统构建实战》——1.2 工作原理

1.2 工作原理 之前讨论了家居安全系统基本的组成部分,但是这些要素都是什么,它们是如何工作的呢?通过对它们进行分类,我们可以将一个安全系统分成两个部分. 1.2.1 硬件部分 一个基础设施的硬件要素必须能够胜任软件的所有技术需求.可以将它们进一步细分为3个不同的子类别.这种分类方式不应该被理解为具有排斥性,因为一个要素经常可以被分到多个子类别中,这种分类方式反而有助于你理解系统所执行的功能. 传感器:传感器将作为系统的感觉器官,其作用就如同人类的身体感官.它们的功能就是从环境中搜集信息,并将这

《Arduino家居安全系统构建实战》——1.6 传统系统与现代化的家居安全系统的比较

1.6 传统系统与现代化的家居安全系统的比较 最后,我们将给出一个传统系统与现代化系统之间的比较,值得指出的是,在面对不同需求的时候,它们各自都有着特定的用处,这些需求例如减少模型的复杂性,提高系统的可靠性,增加系统可用性以及向开发者提供丰富的信息. 以前,如果综合考虑到多种因素,例如价格.技术的成熟度以及现有的基础设施(连接)等,以一个较低的成本来实现一个可以实时管理的系统几乎是不可能的. 而在智能手机和平板电脑已经普及的今天,这些设备有足够的能力来将硬件收集的数据即时进行发送和接收,并实现这

《Arduino家居安全系统构建实战》——2.6 改进分类器

2.6 改进分类器 现在我们已经有了基准-- 低于84.4%是糟糕的结果,至少要超过87.7%.是时候观察使用可以自由支配的两大手段(标记化程序和分类器选用的标记)能达到什么效果了. 2.6.1 使用每个单词 利用一个单词,我们就能将分类器的正确率从84.8%提升到87.7%.如果使用每个可用的标记代替单个词汇,预测正确率当然应该大幅提高.我们来尝试一下这个思路,首先,必须从训练集中提取每个标记.我们编写一个vocabulary函数,对每个文档应用标记化程序,将标记合并为单一集合,实现上述功能.

《Arduino家居安全系统构建实战》——1.3 部署安全系统的先决条件

1.3 部署安全系统的先决条件 如果你正在考虑部署一个安全系统,那么最有可能的原因就是你有这方面的关键需求.因此,在这里最重要的事情就是要准确地确定这种需求.如果做不到这一点,我们最终会步入歧途并造成损失或者导致半途而废. 如果并没有这种需求,你只是拥有着一颗对现代科技勇于探索的心,那么你可能是出于学习和娱乐的目的来设计这个系统,或者试图将自己的家打造成一个现代化的场所.你可以浏览一下这个行业的先进企业的网站以获得参考. 我们可以看到,大多数企业的系统是类似的,只是在某些部分存在一些差异,而这些

《Arduino家居安全系统构建实战》——1.2 经典的机器学习问题:图像分类

1.2 经典的机器学习问题:图像分类 图像(特别是笔迹)识别是机器学习的一个经典问题.首先,这个问题有极其有益的应用.通过自动识别书信上的地址或者邮政编码,邮局就可以有效地分拣信件,免于人工进行这一乏味的工作:ATM机器如果能够识别金额,就可以在机器上存款,加快资金入账的速度,减少在银行排队的需求.想象一下,如果所有人类手写的文档都能够数字化,搜索和研究信息该有多么容易!其次,这个问题很难:人类的笔迹(即使是印刷体)有各种各样的变化(大小.形状.倾斜),人们可以毫无问题地识别不同人写的字母和数字

《Arduino家居安全系统构建实战》——第1章 256级灰度

第1章 256级灰度 机器学习项目开发实战 构建自动识别数字图像的程序 如果你打算建立一个当前技术热点的列表,机器学习当然会名列前茅.然而,虽然这个术语到处出现,但是它的真实含义往往含混不清.它是和"大数据"或者"数据科学"一样的东西吗?它和统计学有何不同之处?表面上,机器学习似乎是一种奇特.令人畏惧的专业,使用令人眼花缭乱的数学知识和算法,和软件工程师的日常活动没有多少共同之处. 在本章以及本书余下的部分中,我的目标是和大家一起完成实际项目,以此阐明机器学习的原理

《Arduino家居安全系统构建实战》——1.1 家居安全的基础设施

1.1 家居安全的基础设施 当然,如果你被问到一个关于安全系统组成部分的问题,你可能会毫不犹豫地对这个问题做出回答,例如,你可能会就监控摄像头.运动传感器和报警器等设备侃侃而谈.但是你确实了解这些不同的组成模块了吗?你能清楚地说出它们之间所有的关联吗?你能详细地讲出它们的工作方式吗?你能准确地说出传统的系统和当前的系统的区别吗?不要着急,这些答案都将为你揭晓. 好了,现在我们这里已经有这些问题的答案了.安全基础设施包括了所有的硬件设备.软件部分以及它们之间关联的设计,将所有这些组件组合在一起构成