简介: 如何让机器识别一只猫?本文从人认识猫的基本方法入手,讲解如何训练机器获得模型的主要步骤,并进行简单的实践,分享了机器学习的一个基本原理——梯度下降实现线性回归。(文末福利:阿里云 AI 视觉训练营,AI 资源、课程等你来) 作者:溪夏

image.png

一 是什么是机器学习,为什么我们要机器学习

什么是机器学习

先看两个例子:

我们是如何习得“猫”这个动物的?

想象一下一个从来没有见过猫的人(比如一个小婴儿),他的词汇里面甚至没有猫这个词。有一天他看到了一个毛茸茸的动物:

image.png

这时候他不知道这是什么东西,你告诉他这是 ”猫“。这时候可能小婴儿记住了,这个就是猫。

又过了段时间他又看见了这样一个动物:

image.png

你又告诉他这也是猫。他记住了这也是猫。

后来又过了段时间,他又看见了一个动物:

image.png

这时他直接告诉你他看见了一只“猫”。

以上就是我们认识世界的基本方法,模式识别:人们通过大量的经验,得到结论,从而判断它就是猫。

在这个过程中我们通过接触样本(各种猫)学习到了猫的特征(人们通过阅读进行学习,观察它会叫、两只耳朵、四条腿、一条尾巴、有胡须,得到结论),从而知道什么是猫。

我们如何知道 npm 包判断一个 npm 包是测试 npm 包呢?

我贴一段小伙伴的代码:

SELECT * FROM
  tianma.module_xx
WHERE
  pt = TO_CHAR(DATEADD(GETDATE(), - 1, ‘dd’), ‘yyyymmdd’)
  AND name NOT LIKE ‘%test%’
  AND name NOT LIKE ‘%demo%’
  AND name NOT LIKE ‘%测试%’
  AND keywords NOT LIKE ‘%test%’
  AND keywords NOT LIKE ‘%测试%’
  AND keywords NOT LIKE ‘%demo%’

很明显我们判断的方式是这个模块的名称和关键字中是否包含:test、demo、测试这三个字符。如果有那么我们就认为他是测试模块。我们把规则告诉了数据库,然后数据库就帮我们筛选了非测试模块。

识别是否是猫或者识别一个模块是否是测试模块本质上是一样的,都是在找特征:

  • 猫的特征:会叫、两只耳朵、四条腿、一条尾巴、有胡须
  • 测试模块的特征:test、demo、测试

再进一步将特征程序化表述:

  • 猫的特征 叫:true、耳朵:2、腿:4、尾巴:1、胡须:10
  • 测试模块的特征:test:count>0、demo:count >0、测试:count > 0

有了这些特征无论是人还是机器都能正确的识别猫或者测试模块了。

简单的理解机器学习就是通过特征和特征的权重来实现数据的分类。(此处为了便于理解,更准确说法请参考:AiLearning/1.机器学习基础.md at master · apachecn/AiLearning · GitHub)

为什么要用机器识别呢?

原因是当某种分类任务的特征数量巨大之后我们就很难用 if else 的方法去做简单的分类了。比如我们常见的商品推荐算法,要确定某个商品是否适合推荐给某人可能的特征数量会达到上百上千个。

二 如何训练机器,并获得模型?

准备数据

数据的准备在整个机器学习的任务中时间占比可能超过 75%,是最重要的一部分,也是最困难的一部分。主要是:

  1. 采集基本的数据
  2. 清理异常值
  3. 挑选可能的特征:特征工程
  4. 数据打标

准备算法

让你的数据进行拟合的一个函数:y=f(x)

比如线性函数也就是一元一次函数:y=ax+b

评估算法

如何确定找到的 a、b 值是否合适,那就需要一个评估函数。

评估函数描述训练得到的参数和实际的值之前的差距(损失值)。比如下图:

image.png

右边的蓝色线条更加贴近真实的数据点。

最常见的损失评估函数就是均方误差函数了。通过计算预测的值和真实值的差的平方和来判断预测值的优劣程度。

如上图:样本黄色的小圆圈坐标为:

[

[x1, y1],
[x2, y2],
[x3, y3],
[x4, y4],
[x5, y5],
[x6, y6]

],

蓝色的线预测的坐标为:

[

[x1, y’1],
[x2, y‘2],
[x3, y’3],
[x4, y‘4],
[x5, y’5],
[x6, y‘6]

],

那么损失值为:

const cost = ((y’1-y1)^2 + (y’2-y2)^2 + (y’3-y3)^2 + (y’4-y4)^2 + (y’5-y5)^2 + (y’6-y6)^2 ) / 6

训练算法

如何找到合适的 a、b 值:抛物线的最低端

以上述的线性函数为例,训练算法实际上就是在寻找合适的 a,b 值。如果我们在茫茫的数字海洋中随机寻找 a,b 的值那应该是永远找不到的了。这时候我们就需要用到梯度下降算法来寻找 a,b 值了。

再明确一下目标,将上述的损失值计算公式替换为:y=ax+b

// 函数 2
const cost = (((a*x1+b)-y1)^2 + ((a*x2+b)-y2)^2 + ((a*x3+b)-y3)^2 + ((a*x4+b)-y4)^2 + ((a*x5+b)-y5)^2 + ((a*x6+b)-y6)^2 )/ 6

目标是找到一组 a、b 的值使得 cost 最小。有了这个目标就好办多了。

不知道你还记不记得初中的抛物线函数,也就是一元二次方程:y = ax^2+bx+c

而我们上述的 cost 函数虽然看起来很长,但是正好也是一个二次函数。它的图大概是这样的:

image.png

只要我们找到最低点的 a,b 值就完成我们的目标了。

怎么知道到达抛物线的低端:抛物线的低端斜率为 0

假设我们随机初始化一个 a 值为 1, 这时我们的点就在抛物线的左上方位置,距离最低点(cost 最小)的位置还距离很远呢。

看图可知我们只要增加 a 的值就可以靠近最低点了。那看图机器可不会,这时候我们要祭出本篇文章中最复杂的数学知识了:导数。在这个点上的切线斜率值即为这个抛物线的导数,如上图最低点(斜率为 0 处)。

通过这个导数可以计算出这个位置的切线(红色的斜线)斜率。如果这个斜线的斜率为负数就意味着 a 太小了,需要增加才能更靠近底部。反之如果斜率为正意味着过了最低点了,需要减少才能更靠近底部。

如何求 cost 函数的导数呢?

不展开了,直接看代码吧。关键字:偏导数、复合求导

// 函数 3
// a参数的偏导数
const costDaoA = (((a*x1+b)-y1)*2*x1 + ((a*x2+b)-y2)*2*x1 + ((a*x3+b)-y3)*2*x1 + ((a*x4+b)-y4)*2*x1 + ((a*x5+b)-y5)*2*x1 + ((a*x6+b)-y6)*2*x1 )/ 6

// b参数的偏导数
const costDaoB = (((a*x1+b)-y1)*2 + ((a*x2+b)-y2)*2 + ((a*x3+b)-y3)*2 + ((a*x4+b)-y4)*2 + ((a*x5+b)-y5)*2 + ((a*x6+b)-y6)*2 )/ 6

也就是只要将 a,b 值带入 costDaoA 函数就可以得到一个斜率,这个斜率指导参数 a 该如何调整以便更靠近底部。

同理 costDaoB 指导参数 b 改如何靠近底部。

循环 500 次吧

就这样循环 500 次,基本上就能非常靠近底部了,从而获得合适的 a,b 值。

获得模型

当你获得了 a,b 值之后,那么我们就获得了 y=ax+b 这样一个模型,这个模型就可以帮助我们做预测了。

三 实践一下先从简单的开始:线性回归

什么是线性回归

人们早就知晓 ,相比凉爽的天气,蟋蟀在较为炎热的天气里鸣叫更为频繁。我们记录了气温和每分钟叫声的一个表格,并且在 Excel 中绘制了下图(案例来自 google tf 的官方教程):

image.png

是不是很清晰,这些小红点几乎排在了一条直线上:

image.png

那么我们就认为这些数据的分布是线性的,绘制的这条直线的过程就是线性回归。有了这条曲线我们就能准确的预测任何问题下鸣叫的次数了。

用浏览器做一个线性回归演示

地址:测试梯度下降
https://jshare.com.cn/feeqi/CtGy0a/share?spm=ata.13261165.0.0.6d8c3ebfIOhvAq

image.png

为了可视化采用了 highcharts 做数据可视化,同时为了省下 75% 的时间直接用了 highcharts 的默认数据点:
https://www.highcharts.com.cn/demo/highcharts/scatter

当训练完成后绘制了一条蓝色的线叠加在了图上,同时增加了每次训练的 a,b 值的损失率曲线。

代码介绍

/**

* 代价函数 均方差计算

*/

function cost(a, b) {

    let sum = data.reduce((pre, current) = >{

        return pre + ((a + current[0] * b) - current[1]) * ((a + current[0] * b) - current[1]);

    },
    0);

    return sum / 2 / data.length;

}

/**

* 计算梯度

* @param a

* @param b

*/

function gradientA(a, b) {

    let sum = data.reduce((pre, current) = >{

        return pre + ((a + current[0] * b) - current[1]) * (a + current[0] * b);

    },
    0);

    return sum / data.length;

}

function gradientB(a, b) {

    let sum = data.reduce((pre, current) = >{

        return pre + ((a + current[0] * b) - current[1]);

    },
    0);

    return sum / data.length;

}

// 训练次数
let batch = 200;

// 每次的靠近底部的速度,也就是学习速率。过高会导致在底部弹跳迟迟不能到到底部,过低会导致学习效率降低。
let alpha = 0.001;

let args = [0, 0]; // 初始化 a b 值
function step() {

    let costNumber = (cost(args[0], args[1]));

    console.log(‘cost’, costNumber);

    chartLoss.series[0].addPoint(costNumber, true, false, false);

    args[0] -= alpha * gradientA(args[0], args[1]);

    args[1] -= alpha * gradientB(args[0], args[1]);

    if ((—batch > 0)) {

        window.requestAnimationFrame(() = >{
            step()
        });

    } else {

        drawLine(args[0], args[1]);

    }

}

step();

四 接下来要做的

当特征更多的时候,我们需要更多的计算、更长时间的训练来获得训练模型。

上述描述都比较简单,但是相信机器学习对你已经不再神秘,那么可以参考更专业的入门文章。

参考
[1] GitHub - apachecn/AiLearning: AiLearning: 机器学习 - MachineLearning - ML、深度学习 - DeepLearning - DL、自然语言处理 NLP

[2] https://developers.google.com/machine-learning/crash-course/descending-into-ml/video-lecture?hl=zh-cn
[3] 从 0 开始机器学习 - 手把手用 Python 实现梯度下降法!- 掘金

福利来了 | 阿里云 AI 视觉训练营

加入阿里云高校计划 AI 视觉训练营,与达摩院视觉导师亲密接触。五天时间玩转身份证识别应用、电子相册应用、图像识别项目、车辆识别项目。

点击“阅读原文”马上参与!


阿里云开发者
3.2k 声望6.3k 粉丝

阿里巴巴官方技术号,关于阿里巴巴经济体的技术创新、实战经验、技术人的成长心得均呈现于此。