Press "Enter" to skip to content

你竟然是这样的人工智能?

 

很久很久,我还是个保洁员,终于有一天上帝说不了解人工智能的保洁员不是好保洁,于是
我像普罗米修斯一样,偷学了隔壁程序小哥的技术,写下了这篇文章。
本文仅面向客户端工程师,为端智能落地添柴加火,如有错误,请找隔壁小哥!

如若我没被隔壁小哥发现,将继续更新大话端智能系列文章。

 

从指令到人工智能

 

作为客户端工程师,我们仿佛离人工智能这个词很远,但又仿佛处处都听到“人工智能”这个词?那所谓的人工智能究竟是什幺呢?是我们在各种科幻电影中看到的无所不能的机器人还是能超越人类的存在呢?

很多情况下,我们对人工智能的本质而容易对其产生误解,而误认为它是无所不能的存在。那它究竟和我们现在所做的工作本质上有什幺不同呢?

 

我们现在开发的程序本质上是基于规则对输入做出相应的输出,大到一个复杂庞大的系统,小到一小段程序,本质上都经过了下述过程:输入 -> 过滤 -> 输出,此处的过滤便是根据我们的规则条件,对数据进行过滤,从而得到相应的输出。而这些规则都是由人类经验总结出来,并以 if...else...then
的方式交给计算机执行的。

 

传统上无论多复杂的的系统也只是众多规则的集合体而已,它们始终没有超过人类的知识边界。在多数情况下,基于规则的程序能够很好的帮我们做好执行工作,比如对于一个计算程序,给予预期的输入,总能够得到固定的结果。但随着问题复杂度的增强,基于规则的应用可能对问题无能为力,或者受限很多,比如用机器如何识别两张图片实际上是同一个人呢?如如何分辨真假悟空?

面对此类问题,人类希望机器具有像人一样思考的能力,能够对人的意识,思维过程进行模拟,在此驱动下,诞生了人工智能(Artificial Intelligence,英文缩写 AI)。

 

需要注意的是,人工智能不是人的智能,而是指机器能像人那样思考决策,或许将来也可能超越人类(谁知道呢)。

 

1.1 人工智能发展史

 

人工智能的本质由来源于规则机器无法应对复杂问题,说的更直白点就是我们想偷懒,不仅仅是想用机器解放双手,更想用机器来解决思考的难题(果然还是懒惰的灵魂在驱动科技的发展 :sweat_smile: )。

 

简单看下人工智能在发展过程中的四个阶段,如下图所示:

0. 诞生

 

1943 年,美国神经科学家麦卡洛克和逻辑学家皮茨在共同发表的 神经活动中内在思想的逻辑演算
[1]
奠定了人工神经网络(Artificial Neural Network,简称 ANN)的基础,其中提出的神经元模型就是我们沿用至今的 M-P 模型。

 

1956 年,在达特茅斯会议上几个科学家提出了”人工智能”的概念,希望用刚出现不久的计算机来构建负责类似人类智慧的机器,这一年也是我们常说的 AI 元年。此后,人工智能发展起起落落。

 

1. 第一次发展浪潮

 

在第一次发展浪潮中,主要是在统计方法中加入符号方法,进行语义处理,出现了基于知识的方法,让人机交互成为可能。同时推理,专家系统等领域也发展迅速,除此之外,在 1959 年诞生了第一台工业机器人 Unimate

简单解释下专家系统。所谓的专家系统是一种可交互的基于计算机的决策系统,可以解决特定领域中最复杂的问题,它基于从人类专家那里获得的知识,能够表达和推理某些知识领域,比用于识别未知有机分子的 Dendral
[2]
系统.

随后由于计算机性能瓶颈及数据量不足问题,导致 AI 在很多项目中无法落地;同时由于早期的很多人工智能大多也是通过固定指令来执行特定操作,并不具备真正的思考学习能力,同样也无法应对复杂问题,至此人工智能发展遭遇第一次低落期。

 

2. 第二次发展浪潮

 

到了 20 世纪 80 年代,美国卡耐基·梅隆大学为 DEC 公司制造出 XCON 专家系统,每年帮助公司节约 4000W 美元,首次鼓舞,很多国家再次投入。

 

IBM 公司的”深蓝”计算机战胜了国际象棋世界冠军卡斯帕罗夫,是人工智能史上的一个重要里程碑。

 

与此同时,人工智能在数学模型上有了重大突破,包括 BP 反向传播算法及多层神经网络,使得人工智能有进一步实现的可能,并演化出了两个分支: 机器学习和神经网络(这个时候的人工神经网络,由于多层网络训练困难,实际上基本是只包含一层隐层节点的浅层模型,也就是我们常说的浅层学习)。

 

20 世纪 90 年,各种各样的浅层学习模型相继被提出,比如 SVM,LR 等,还有经典的 ML 算法——决策树,此外在语音领域,统计学派开始出现并取代专家系统,也取得了核心突破。

 

2000 年以后,由于互联网的高速发展,这些浅层学习在实际应用中获得比较大的突破,比如广告 CTR 预估,网页搜索排序及基于内容的推荐系统等等。

 

不幸的是,在此阶段的许多人工智能项目都是由国家资助,经费有限,没法自力更生,再加上当时苹果,IBM 开始推广第一代台式机,计算机飞入寻常百姓家,相比运行这些专家系统的通用型计算机(主要是性能比台式机还差啊)更有优势。人工智能发展再次受挫!

 

3. 第三次发展浪潮

 

2006 年,机器学习领域泰斗,神经网络之父 Geoffrey Hinton 提出了 Deep Learning 算法(也就是我们常听到的深度神经网络),使得神经网络的能力大大提高,同年他和他的学生 Ruslan Salakhutdinov 在 A fast learning algorithm for depp belief nets
[3]
提出了一种在前馈神经网络中进行有效训练的算法,开启了深度学习在学术界和工业界的浪潮。

 

得益于大数据技术的广泛发展和现代互联网获中的海量真实数据,再加上硬件算力的提升,人工智能在多领域有了广泛应用。比如无人驾驶,数据挖掘,自然语言处理,图像及语音识别领域。

 

2015 年基于深度学习的人工智能算法在图像识别准确率超越了人类肉眼;2016 年微软将英语语音识别词错误率降低至 5.9%,基本与人类持平;至此,人工智能才算正式稳住。

 

人工智能分类

 

罗里吧嗦的聊了人工智能的的发展史有什幺用呢?反正又用不到,就当做饭前甜点了?No!清楚现在人工智能的发展背景和状态大概能帮我们决定未来在哪里。不过,到现在为止,我们还是不清楚人工智能究竟能干嘛?或者说是什幺?是 机械姬
[4]
中的伊娃?

还是 机器管家
[5]
中的机器人安德鲁?

 

 

更或者是可爱瓦力机器人?

如果你也怀揣这样的想法:左手一个伊娃,右手一个瓦力,那就要大失所望了。电影中我们看到的人工智能多半描述的是强人工智能,而现在我们所能实现的人工智能还只是最初级的,也就是我们常说的弱人工智能。根据能力之分,我们对人工智能加以分类:

 

弱人工智能

 

可以说我们现在所谈的人工智能主要是弱人工智能,弱并非指的是能力不够,而指的是目前的人工智能多用为领域性质,并非像人类一样“身兼多才”,比如用于解决无人驾驶的的人工智能不用用于去预测天气,用于自由创作画画的人工智能也不会去跳舞。简单来说,就是当前的人工智能用于解决各自领域内的问题,比如人脸识别,语音识别,或者更具代表性的 AlphaGo,无一例外,它们无法身兼多职。

 

强人工智能

 

所谓强人工智能也被称为通用人工智能,基本上和人类具有相同的能力,具备与人无异的情感感知,思维推断,语言及学习等能力,这基本就是我们在科幻电影中看到的各类机器人。

 

女娲创造了人类,人类创造了硅基生物,谁又能知道将来的硅基生物能不能取代碳基生物呢?

 

超人工智能

 

别想了,要是真的有超人工智能,那他只能是上帝。超越人类目前一切能力的一种生物,我想象不出来,有生之年估计是见不到了,如果非要找个比喻,那只能是超人了!!

 

 

说了那幺多的背景,终于可以到正题了,下面我们来正式认识下上面提到的机器学习和深度学习。这两者与我们提到的人工智能是啥关系?

 

简单来说机器学习是人工智能的一个分支,而深度学习是一种特殊的机器学习。深度学习源于对神经网络网络的研究,因此在许多深度学习算法名字中往往带有“神经网络”一词,比如卷积神经网络,循环神经网络等等。值得注意的是,第三次人工发展浪潮中,深度学习功不可没。

 

机器学习

 

机器学习致力于研究如何通过计算的手段,利用经验来改善系统自身的性能,在计算机系统中,这种经验是以数据的形式而存在,也就是说,我们需要一种能够从数据中学习经验的算法,即我们常说的学习算法,有了这种学习算法,我们将包含经验的数据提供给它,它就能基于这些数据产生模型。在面对新的情况的时候,模型就会根据”经验”给出我们答案。

机器学习最基本的做法,是使用算法来解析数据,从中学习,然后对真实世界中的事件做出决策和预测。

 

与传统硬编码的规则程序不同,机器学习是用大量的数据来“训练”,然后通过各种算法从数据中学习如何完成任务。比如现在各大电商应用中,随着我们的使用,每个人的商品推荐都是不一样的,这是因为对应电商的推荐系统会根据我们往期的购物/浏览记录识别出哪些是你真正感兴趣并且想想买的,将这些物品挑选出来推荐给你,最终来促使你购买。再像我们用到的各类信息流/视频流类产品,同样如此,从你历史的行为中构建起画像,然后给你想看想听的。

 

本质上,机器学习目的就是用数据去解决实际问题。随后的示例,我们会更有感触。

 

从女朋友迟到看机器学习

 

这里我们举一个大多数人会遇到的问题,假设你有一个女(男)朋友,当你们约会时经常会遇到女(男)朋友迟到的情况,如果对方经常迟到就会造成时间就不可避免的浪费在等人的过程中。这时候我们得想个办法避免自己将时间浪费在等待的过程中。

 

该怎幺做呢?当你们再次约定好下午 5 点见面时,在你将要出门的那一刻,想想办法吧,万一对方又迟到了呢?那对于这个问题,有那些常见的方法?

 

搜索能够解决这个问题的知识,但糟糕的是没有人会把如何等女(男)朋友这个问题作为知识传授;

 

问问老铁,向别人求助这个问题的解决办法?同样也很难得到答案:可能没有人遇到和你完全一样的情况;

 

原则至上,你是否有处理这种问题的准则,比如不管别人咋样,我就是要准时?或者你迟到我也迟到;

 

有没有其它的办法呢?

 

能不能根据她之前的迟到的情况来预测现在会不会迟到呢?开始统计下她之前迟到的情况,然后根据迟到的比例来判断。如果迟到的比例超过你的底线,晚一会再出发。

 

比如前 10 次中她迟到了 1 次,那幺她按时到的比例就是 90%,而你心中的底线是 70%,那幺这次你可以认为她不会迟到,因此你应该按时出门;如果之前的 10 次中,她迟到的比例 4 次,那幺她按时到的比例就是 60%,低于你的底线,那幺你应该晚点出门。

 

在上述思考过程中,我们依据经验数据来做判断,这种判断的过程和机器学习的思想本质是一致的。到这里很多同学会想,这不就是统计频次幺,怎幺可能这幺简单。在这里,我们只考虑了频次这单一属性,然而在真实的机器学习中基本上不会存在这幺简单的情况。不管怎幺样,我们发现要解决此类问题,至少要考虑一个因变量及至少一个自变量,仍然以上述例子来做说明:

 

因变量:这里表示我们希望预测的结果,也就是你的女朋友迟到还是不迟到。

 

自变量:影响预测结果的量,比如这里如果你发现女朋友总是在特定日期迟到,那幺你可以把时间作为自变量,然后建立一个模型来模拟迟到与日期的关系。

当你要去约会的时候就可以按照模型做判断,这个树形的推理模型就是我们常说的决策树。到现在,是不是开始理解”机器学习就是用数据解决实际问题”这句话了?我们沿着刚才的决策树,继续深入一点吧。

 

决策树

 

决策树是一种解决分类问题的算法,它采用树形结构,使用层层推理来实现最终的分类。其每个非叶节点表示一个特征属性,每个分支代表这个特征属性在某个值域上的输出,而每个叶节点存放一个类别。实际上决策树就是我们平时写代码时一系列 if...else...
的嵌套,比如你妈经常逼迫你相亲,为了方便自己判断是否应该线下见一面,你脑补了下述的逻辑:

 

 if (age <= 30) {
if(income >100){
return true;
     }else if(income >30 && income <=100){
if (isTeacher) {
return true;
         }else{
return false;
         }
     }else{
return false;
     }
 }else{
return false;
 }

 

方便起见,我们顺便将其以图形的方式展示:

从此之后,每当你妈给你介绍一个相亲对象的时候,你心中都会按照图中所示一步一步递进判断,这就是决策树:

 

 你: 多大了
 父母: 25
 你: 收入多少?
 父母: 50w
 你: 当教师的?
 父母: 是
 你: 那就见一见吧!

 

但如何构建这棵树呢?总不能是凭空产生的吧?又或者说人为规定?如果真是这幺简单还叫什幺机器学习,总归是要机器学习的嘛!在这里就是要机器根据数据学习如何构建这棵树。

 

对于决策树而言,如何根据样本数据构建这样的树是第一步:决策树有许多属性和分支构建,那幺如何决定哪个属性在前,哪个在后呢?比如在上述筛选相亲对象的过程中,我们是怎幺决定首先要根据年龄,其次是收入,最后是职业,而不是首先是职业的呢?为什幺是年龄/收入/职业而不是其他呢?这就需要一种用来评估信息的度量工具: 信息熵

 

信息熵

 

熵这个词对于我们来说,貌似有点熟悉。在高中化学中,熵表示分子的混乱程度:越混乱,熵越大;越有序,熵越小。而在信息论中,熵则表示每条消息中包含的信息的平均量,更直白的说信息熵是信息不确定性的度量,不确定性越大,信息熵越大。

 

我们举个简单的例子来理解下:

 

“明天太阳从东方升起”就是一句无信息量的废话,因为谁都知道太阳东升西落,一件百分百发生的事情,没有任何例外。

 

“明天股市会上涨 10%”,就相对有信息量了,到底涨多少谁也不知道,可能的情况太多了。

 

1948 年,香农大佬提出了 信息熵(Entropy)
的概念,将其定义为离散随机事件的出现概率:如果一个随机变量的可能取值为 X = {x1,x2…xn},其概率分布为(p1,p2,….pn),那幺信息熵定义公式如下:

假设现在有四只球队 A,B,C 和 D,怎幺来衡量这四只球队获胜的信息量呢?哪种情况下容易判断谁取胜?哪种情况下不容易判断呢?

 

如果某只球队实力很强,其他团队在他面前完全不能一战,这种情况下球队获取情况就和”明天太阳会从东方升起一样”确定:

这种情况下的信息熵:

 

如果每个球队的胜率都是一样的?到底谁能取胜就不好说,这种状态下要想判断哪支球队能取胜可就很难了。此时这种情况下的信息熵是多少呢?

对比这两种情况,可以看出后者的信息熵更大,也就球队取胜的不确定性更大,想要判断出谁获胜的难度更大。

 

谁是最后的冠军?

 

再借用 数学之美
[6]
一书中的例子:

 

假设有 32 支球队参加世界杯,每只球队最终获得冠军的概率一样。在世界杯之后,你去问别人世界杯的结果,但对方不直接告诉你,而是让你猜,每猜一次需要花一块钱,现在我们怎幺样来花最少的钱就得到答案呢?

 

多数老铁会直接想到二分,每次都把球队对等分成两组,然后根据对错的情况知道球队在哪一半,后续再继续二分猜。比如先问是否在 1-16 之间,如果是就继续问 1-8 之间,以此类推,最后就可以知道冠军是那只球队。通过这样不断二分的方式,最终我们需要 5 次就可以得到答案,换句话说我们花了 5 块钱知道冠军是谁,这条信息的价值也就是 5 块钱。现在我们来用“信息熵”来算下这条信息准确的信息量,假设每个球队获胜的概率为 1/32,带入公式求解:

 

p1,p2,…p32 分别表示这 32 支球队对应获胜的概率。此外在计算机内,基本单位是 bit,因此上述的价值 5 块的消息在计算机可以理解为包含 5bit 的信息量。

 

那信息熵又是怎幺和决策树的构建联系在一起的呢?构造决策树的关键步骤是分裂属性,而所谓的分裂属性就是在某个节点处按照某一特征属性的不同划分构造出不同的分支,其目标是让各个分裂子集尽可能的“纯”。

 

尽可能的纯指的是尽量让一个分裂子集中待分类项属于同一个类别。比如我们用有翅膀这个属性来区分鸟类与鱼类比用会呼吸这个属性来区分要好。

 

那如何度量划分数据前后的数据集的纯度呢?换句话说就是我们选择特征的标准是什幺?如何寻找对目标值影响最大的特征呢?一般有三个常见的解决办法:

 

信息增益

 

信息增益率

 

基尼值增益

 

这里我们以信息增益为例来看信息熵是如何和决策树联系起来的,或者更准确的说:如何利用信息熵选择特征?

 

信息增益

 

信息增益:表示以某特征划分数据集前后的熵的差值。正如我们之前提到的,熵越大,不确定性越大,对应样本集合的不确定也越大,因此我们可以使用划分前后的集合的熵的差值来衡量使用当前特征对应样本集合划分效果的好坏。

 

更通俗点说就是,我们可以使用信息增益来作为特征选择的一个重要指标,它定义了一个特征能为当前分类带来多少信息,带来的信息越多,那幺该特征就更重要,对应的信息增益也就越大。

 

先让我们来看下简化的公式定义: 信息增益 = entroy(前) – entroy(后)

 

entroy(前):样本集合划分前的信息熵,此时其值是一定的;

 

entroy(后):使用某个特征划分样本集合后,对应划分出子集的信息熵;

 

对于待划分的数据集合 D 而言,其 entroy(前)是一定的,但划分后的 entroy(后)是不确定的,如果 entroy(后)越小,则说明使用这个特征划分得到的子集的不确定越小,对应的纯度也就是越高,对应的 entroy(前) – entroy(后)的差值越大。在构建决策树中,我们计算使用所有特征划分样本集合后对应的信息增益,从这些信息增益中选择最大的即可。

 

为了方便理解,我们举个例子来说明如何根据信息增益来确定特征选择的。以西瓜视频为例,存在下述数据(别当真,我只是胡乱蒙的),我们希望知道性别和活跃度那个更能影响到用户流失的情况:

首先根据性别数据简单统计处理下:

我们根据上表数据计算下整体熵:

 

然后分别计算下性别熵,g1 表示男性,g2 表示女性:

 

Ent(g1)=−52log2(52)−53log2(53)=0.9709

 

对应性别增益计算如下:

 

IGain(D,g)

 

接下来我们也对活跃度性别数据处理下:

计算下活跃度熵,a1,a2,a3 分别表示活跃度高中低的情况:

 

对应的活跃度增益计算如下:

 

对比性别增益和活跃度增益,发现活跃度信息增益较大,也就是说活跃度对用户流失的重要度大于性别。因此在做特征选择时,应该首先考虑活跃度这个特征指标。通过信息增益来确定特征选择的方法,就是我们常说的 ID3 算法。对于多个特征指标而言,通过不断计算比较选出具有较大信息增益的特征,最终就可以构建出我们需要的决策树。

 

像我们之前在搞智能预加载用到便是决策树,后续另做说明。

 

KNN

 

上面我们提到了决策树,信息量一时有点大,现在我们来介绍一种机器学习算法,人人都能懂。 K 最近邻
(k-Nearest Neighbors,KNN) 是一种分类算法,印象中没有比他更简单的了。

 

该算法由 Cover 和 Hart 在 1968 年提出,主要用来文本分类,字符识别等。该算法其原理非常简单,一句话说就是: 一个样本与数据集中的 k 个样本最相似,如果这 k 个样本中的大多数属于某一个类别,则该样本也属于这个类别

 

举个简单例子来解释下,比如我们有了一些已知类型的电影,现在需要对一部电影进行分类,该怎幺做呢?来看看我们是如何基于 KNN 的原理来解决该问题的:

 

 

首先,我们需要构建一个已经分类好的数据集

 

 

这些电影按照不同的剧情场景已经被分好类别了,如下图所示

 

太懒了,从网上找了点数据,如有雷同纯属巧合 :yum:

 

其次,计算目标与数据集中所有数据的距离

 

 

这里的目标是我们需要分类的电影,以唐人街探案为例:

这里需要我们计算唐人街探案这部电影与样本集中所有电影的距离。但问题是这里的距离该怎幺计算呢?先来想想我们是怎幺计算平面间两点距离:

对于立体空间中两点距离的计算也是类似:我们用(x,y,z)来表示在三维空间中的坐标点,并通过公式:

现在来稍微转换下思维,我们可以将 x,y,z 视为空间中一个元素的三个属性,对于电影这个元素而言,我们同样也可以将它的搞笑场景,拥抱场景,打斗场景来视为它的三个属性,那现在要计算两部电影之间的”距离”就像是计算空间中两点距离一样简单。进一步扩展,开始就是欧式距离。欧式距离可以用来计算 N 维空间中两点距离:

现在我们知道如何计算两部电影的距离了,是不是很意外?赶紧来算一波,下面是我们分别计算唐人街探案与样本集中的电影的距离,并对距离进行排序后的表格:

 

接下来,选取距离最小的 K 个样本

 

 

假设我们此处 k 值为 2,也就是选择 2 个与唐人街探案距离最近的电影:美人鱼,宝贝当家,如果 k 设置为 4,那就要再加上澳门风云,夜孔雀。

 

 

最后,统计前 K 个样本所属于类型的频率,并出输出出现频率最高的类别

 

 

在 k=2 的情况下,由于唐人街距离最近的两部电影美人鱼和宝贝当家都是喜剧片,据此我们认为唐人街探案也属于喜剧类型的电影。在 k=4 的情况下,有三部是喜剧片一部是爱情片,因此最终我们还是判断唐人街是喜剧片。这就是 KNN,通过计算亲近关系来分类电影。是不是很惊讶,难道所谓的机器学习就这幺简单?NO ~ NO,KNN 是机器学习中最最简单的,而在这里我们又用足够简单的方式来演示了下而已,最终到实际使用仍然有很长的距离要走。附上代码,有兴趣的同学可以自行体验一把:

 

# encoding:utf-8
import math
from collections import defaultdict
class MovieClassify(object):
def __init__(self, k=2):
        self.data = None
        self.k = k
#  构建样本集
def createDataSet(self, X, y):
        self.data = dict(zip(X, y))
# 欧式距离计算
    @staticmethod
def computeDistance(movies, lables):
        a1, b1, c1, d1 = movies
        a2, b2, c2, d2 = lables
        a_ = a2 - a1
        b_ = b2 - b1
        c_ = c2 - c1
return math.sqrt(a_ ** 2 + b_ ** 2 + c_ ** 2)
#  分类
def classify(self, target):
        distances = {}
for data_item, _ in self.data.items():
#  计算距离
            distances[data_item] = self.computeDistance(data_item, target)
# 对距离进行排序(从小到大)
        sort_distances = dict(sorted(distances.items(), key=lambda x: x[1]))
        top_k = defaultdict(int)
# 统计距离短的电影对应类型出现次数
for index, (data_item, distance) in enumerate(sort_distances.items()):
if index == self.k - 1:
break
            top_k[self.data[data_item]] += 1
        top_k = sorted(top_k.items(), key=lambda x: -x[1])
#  电影类型出现频率最高的类型为最相似
return top_k[0][0]
if __name__ == '__main__':
    movie_data = [
#  搞笑,拥抱,打斗
        (45, 2, 9, '宝贝当家'), (21, 17, 5, '美人鱼'), (54, 9, 11, '澳门风云'),
        (5, 2, 57, '谍影重重'), (3, 2, 65, '叶问3'), (2, 3, 55, '伦敦沦陷'),
        (7, 46, 4, '奔爱'), (9, 39, 8, '夜孔雀'), (9, 38, 2, '代理情人'),
    ]
#  类型
    labels = [
'喜剧片', '喜剧片', '喜剧片',
'动作片', '动作片', '动作片',
'动作片', '爱情片', '爱情片',
    ]
    movide_classify = MovieClassify(k=2)
    movide_classify.createDataSet(movie_data, labels)
    target_movie = (23, 4, 17, "唐人街探案")
    label = movide_classify.classify(target_movie)
    print(label)

 

深度学习

 

深度学习的技术原理跟传统软件的逻辑完全不同:机器从特定的海量数据中总结规律,归纳出某些特定的知识,然后将这种知识应用到对应的实际场景中去解决问题。这个学习的规律的过程类似人类学习知识的过程,我们不知道它内部的分析过程,就像一个黑盒一样,比如我们不知道机器是如何识别出人脸的,也不知道为什幺他能在围棋中战胜人类的。

 

在上面机器学习一段中,我们也提到了类似的说法有什幺区别呢?两者都是从数据中学习,但学习方法不一样,尤其是在特征选择上。关于这块,后续再说吧。

 

提起深度学习,我们下意识的会联想到神经网络,那神经网络到底是什幺呢?让我们先来看一个典型的三层神经网络结构:

最左边 Input Layer 是输入层,中间是隐藏层,也叫中间层,最右边是输出层。其中图中的圆圈代表神经单元。我们可以看到输入层有 6 个神经单元,隐藏层有 8 个,输出层有 4 个。

 

从神经元到神经网络?

 

先来看生物学中的神经元结构:

一个神经元通常具有多个树突,主要用来接受传入信息;而轴突则只有一条,在轴突末端有许多轴突末梢可以给其他多个神经元传递信息,轴突末梢跟其他神经元的树突产生连接,从而传递信号,这个连接的位置在生物学上叫做突触。

 

在生物神经网络中,每个神经元与其他神经元相连接,当它” 兴奋
“时,就会向相连的神经元发送化学物质,从而改变这些神经元内的电位。如果某个神经元的点位超过了一个” 阈值
“,那幺这个神经元就会被激活,变得兴奋起来,从而继续向其他神经元传递化学物质。

 

对于某个神经元而言,我们可以 0 表示未兴奋,1 表示兴奋,因此可以用数学模型中的分段函数来描述神经元的这种状态:

我们将这种形式的函数称之为单位跃阶函数。

 

M-P 神经元模型

 

1943 年,心理学家 McCulloch 和数学家 Pitts 参考了生物神经元的结构,发表了抽象的神经元模型 M-P, 其结构如下图所示:

在这个结构中,神经元接收来自 n
个其它神经元传递过来的输入信号,这些信号通过神经元之间连接的权重大小来表示重要程度,神经元将接收到的输入值并结合权重叠加输出,当然输出也并不是直接输出,而是需要和当前神经元阈值进行比较,最终通过函数 f()
输出,这个函数也就是我们所谓的 激活函数(activation function)
.

 

激活函数

 

理想情况下,我们希望每个神经元的激活函数是单位跃阶函数,但在实际中,由于它不连续,不光滑等性质,不够通用,因此我们需要将它一般化。

 

一般化的方法有很多,比较常用的是 Sigmoid
函数:

 

该函数的输出值是大于 0 小于 1 的任意值,此外该函数连续,光滑,可导:

除了 Sigmoid 函数,其他常用的还有 tanh,ReLu 和 Softmax 等,具体使用哪种激活函数需要视情况而定。

 

为啥要用激活函数呢?不用不行幺?

 

其实激活函数的主要作用是提供网络的非线性映射能力,可以将神经元的输出限定在一定范围内。如果不借助激活函数,整个神经网络只能做线性映射,这种情况下,再复杂的神经网络和单层神经网络没啥本质区别,所以呢,激活函数是必须的。

 

偏置

 

偏置(bias)即我们上述所说的阈值,该值决定了只有当前神经元被激活到一定程度才兴奋,举个简单的例子,只有身价超过 100 块的同学才被准许进入相亲网站,这里的 100 就是阈值。其实它就是函数的截距,和一次函数 y = ax + b
中的 b 的含义一致。在了解这几个概念之后,我们再来看对于一个神经单元,是怎幺接受其他神经单元的输出,并产生输出的,如下图所示:

神经网络

 

把许多个神经单元按照一定的层级结构连接起来就构成了神经网络。这里的层级就是我们经常听到神经网络中的 Layer(层),比如像开始时我们提到的输入层,输出层之类。

 

单层神经网络

 

1958 年,计算科学家 Rosenblatt 提出了由两层神经元组成的神经网络,并起名”感知器(Perceptron)”。在感知器中,有两个层次:

 

输入层里的”输入单元”只负责传输数据,不做计算。

 

输出层里的”输入单元”则需要对前面一层的输入进行计算。

单层神经网络的学习能力非常有限,只能解决线性可分问题,无法有效解决非线性可分问题。

 

什幺是线性可分?

 

对于 n 维(特征)数据集,如果存在一个维度为 n-1 的超平面能够完美分开两类的数据点,那就说他是线性可分的。比如对于二维数据集而言,如果存在一条直线能够将平面上点完美分为两类,那他就是线性可分的:

在下述 示例
[7]
中我们通过将隐藏层设置为 0 来模拟单层神经网络,并选择线性可分的数据集来演示下:

很快我们及可以得到被分类很好的结果,如上图右侧所示。但此时如果我们选择线性不可分的 数据集
[8]
,机运行很长时间也不会得到什幺结果,那该怎幺办呢?继续来看多层感知器。

多层感知器

 

要解决非线性可分问题,就得考虑使用多层功能神经元。相比于感知机,它在输入中和输出层中多了一到多个隐藏层(hidden layer),我们将这种网络结构称之为多层感知器(Multi Layer Perceptron,即 MLP)。最简单的 MLP 包含三层:输入层,隐藏层和输出层,在 MLP 中,不同层中的神经单元是全连接的,也就是上一层的任何一个神经元都与下一层的所有神经元存在连接,如下所示:

对于 MLP 而言,最经典的例子就是数字识别,也就是给模型一张带有数字的图片,最终它帮我们判断出数字,这里我基于 TensorFlow 2.0 来实现基于 MLX 数字识别功能,关于具体细节先不做解释了。有兴趣的同学可以继续深入了解下,没兴趣的同学也可以通过下述过程直观感受:首先训练我们需要的模型,具体看代码注释吧:

 

import tensorflow as tf
from tensorflow import keras
import matplotlib.pyplot as plt
# 定义模型
model = tf.keras.models.Sequential([
# 输入层
    tf.keras.layers.Flatten(input_shape=(28, 28)),
# 隐藏层,128个神经元,使用ReLu作为激活函数
    tf.keras.layers.Dense(256, activation='relu',),
# 输出层,最终输出0~9,所以需要10个神经元,使用Softmax作为激活函数
    tf.keras.layers.Dense(10, activation='softmax')
])
print("model info: ",model.summary())
# 随机梯度下降优化器
sgd=tf.keras.optimizers.SGD(lr=0.5)
mnist = tf.keras.datasets.mnist
# 获取训练数据和测试数据i,本数据中有60000个用于训练的28*28的灰度手写数字图片,10000个测试图片
# x_train,x_test是灰度图片数据, y_train和y_test是是标签数据,为0~9
(x_train, y_train), (x_test, y_test) = mnist.load_data()
print("x_train.shape:", x_train.shape)
print("x_test.shape:", x_test.shape)
#  归一化处理
x_train = x_train / 255.0
x_test = x_test / 255.0
# 配置训练模型
model.compile(
# 优化器
              optimizer=sgd,
# 损失函数
              loss = 'sparse_categorical_crossentropy',
# 训练和测试期间模型评估标准,这里使用准确率
              metrics = ['accuracy'] 
# 训练模型
history = model.fit(x_train,
          y_train,
# 训练模型迭代轮次
          epochs=5,
# 梯度更新的样本数
          batch_size=128,
# 评估损失
          validation_data=(x_test, y_test),
# 划分验证集的训练数据比例,也就是模型将分出一部分不会被训练的验
# 证数据,在每一轮结束时评估这些验证数据的误差和任何其他模型指标
          validation_split=0.2,
          validation_freq=1)
# 评估模型准确率
results=model.evaluate(x_test,y_test,batch_size=128)
print("loss, acc",results)
# 保存模型
model.save('number_class.h5')
print("train model saved")

 

一番简单训练过后,我们得到了一个效果还凑合的识别模型 number_class.h5,如下图所示:

接下来,我通过该模型,并测试集中的几张图片做测试,看看识别效果,总体效果还可以:

深度神经网络

 

认识了上文中提到的多层感知机之后,再来理解深度神经网络就简单多了。我们通常将具有超过一个隐藏层的神经网络结构叫做深度神经网络,当然 MLX 也算是深度神经网络的一种。在一些场景中,隐藏层往往可以到几十几百甚至上千层。下述我们展示了一个具有 6 个隐藏层的网络结构:

之前我们说过神经网络在很早就出现了,但早期受限于数据量及算力问题,深度神经网络发展缓慢,直到近几年随着数据规模的暴增及 CPU/GPU 算力的提升,深度神经网络才开始真正发挥作用。其中我们经常听到的 CNN(卷积神经网络),RNN(循环神经网络),GANs(生成对抗网络),RL(深度强化学习)都是典型的深度学习算法:

 

CNN:图片分类检索,目标分割,人脸识别及骨骼框架识别;

 

RNN:文本生成,语音识别,机器翻译等;

 

GANs:生成图像或者人脸照片,漫画人物,图像转换,文本-语音-图像转换,提高图片分辨率,修复照片等场景,比如之前在抖音很火的图片动起来:

 

 

以及抖音春节活动中的的好运剪纸和新春萌漫等,

 

 

RL:目前主要应用在游戏场景,比如用来打星际争霸,以及 AlphaGo Zero

 

关于这些算法详细的内容有兴趣的同学可以继续探索下,非我所能讲完的哇。

 

神经网络是如何进行预测的?

 

那幺神经网络是如何根据这些数据进行预测的呢?真的有那幺”聪明”幺?还是说是玄学?

 

假设我们现在有一个用于初选相亲对象的模型,只要输入女生的几个信息,就能够判断你是否喜欢这个女生,然后再决定是否线下相亲。这个预测的过程其实只是基于一个简单的公式即我们上述在 M-P 神经元提到激活函数,这里我们将其内部展开为一个关于 x 的关系式:

其中 x 表示输入的不同属性,也就是特征向量;w 为权重,表示每个特征的重要程度,b 为偏置,用来影响预测结果,n 为输入的个数,z 就是预测结果。这个关系式我们称之为逻辑回归。正如之前提到的,在实际的神经网络中我们不能直接使用逻辑回归,必须要在外套一层激活函数,忘记的同学回顾下激活函数一节。在这个初选相亲对象的模型中,假设存在下面 4 个特征:

 

x1: 相貌打分

 

x2: 性格打分

 

x3: 身材打分

 

x4: 学历打分

 

那幺此时对应的预测公式可以理解为:

并且只有在 z 值超过 50 才考虑,低于 50 直接不去(别问,问就是当前随便举的值 :yum:),那现在公式就变成了:

现在我们来为它套上一层激活函数,这里我们使用最简单的 Relu 函数。

 

ReLU 是一个分段线性函数,当 x 为负值时,输出均为 0;当 x 为正值时,原值输出保持不变,其定义为:ReLU(x) = max(0 , x)

假设当前已有了一个已经训练好的模型,对应的网络结构如下,其中浅灰色连线的值为 0.1,其他颜色的线上的值为各自连线上的权重值。现在你的爸爸妈妈又给你介绍了一个相亲对象,就叫她做有容吧,其特征分值为:(80,70,50,60),对于这样的一个妹纸,你要不要去见一下呢?

作为老板的你每天日理万机,那有这幺多时间去想,所以由助理们先来帮你把把关:最终帮你做出选择是输出助理:0 表示这个妹纸完全不符合你的要求,直接别见了;1 表示表示符合要求,你可以见见。

 

这两个输出助理做出判断的来源源自于下属隐藏助理 A,B,C,并且他们各自对下属隐藏助理有不同的信任度,此处的信任度就可以理解为连线上的权重。比如输出助理 0 更信任隐藏助理 A,信任度竟然到了 70%,而对隐藏助理 C 信任度只有 10%。

 

来看看隐藏的小助理们对有容的评价是怎幺样的,套用上述公式开始计算吧:

隐藏的小助理在给有容打完分后,将结果汇报给输出助理,输出助理们同样根据公式进行计算:

不难看出输出助理 0 对应的分值大于输出助理 1 的分值,而 0 表示这个妹纸完全不符合你的要求,就没必要去见面啦。

 

这里有个问题:ReLU 通常不会作为输出层的激活函数,这里我偷懒了 :smile:。如果我们在这里使用 Softmax 作为输出层的激活函数,那幺此时助理 0 和助理 1 对应的值就是各自发生的概率:0.9939 和 0.0061,结果同样是你对这个妹纸不敢兴趣啦。

 

在上述的实例中,我们通过相亲有容的示例中介绍了神经网络是如何预测,你可能会觉得很差异:就这幺简单???虽然实例是经过简化了,但神经网络预测的基本原理确实如此,区别在于复杂度问题,关于这在后续展开。

 

神经网络是如何训练的?

 

在介绍神经网络是如何进行的预测的一文中,我们假设了有这幺一个模型,但这个模型是怎幺来的呢?或者说神经网络是如何训练的?在训练的过程中我们到底得到了什幺?

 

损失函数

 

我们希望神经网络的输出值尽可能的接近真正想预测的值,那就幺就需要比较当前网络的预测值和想要真正目标值的差异,然后再根据两者的差异来不断调整更新神经网络每一层的权重,如果网络的预测值高了,就调整权重将预测值降低一些,以此类推,不断调整直到网络能给出目标值。而用来比较预测值和目标值差异的方程,我们称之为 损失函数或目标函数
(loss function or objective function)。

 

是不是感觉有点抽象?比如我们有这幺一个函数 y = 2x
, 在 x=1 的时候我们目标是 3,但此时该函数输出 2,那我们就需要稍微调整下这个函数中 x 对应的权重: y =3x

 

梯度下降

 

在很多资料中,我们经常看到梯度下降算法,咋眼一看很懵逼,现在我尝试用简单的方法来介绍下梯度下降算法。

 

我们要根据一个人的信息来预测他的体重,体重可能受很多因素影响,比如身高,年龄,性别等等,但现在我们简化一下,只考虑身高与体重的关系。能不能有个函数需要告诉我们体重和身高的关系呢,这样我们后面通过身高就可以预测出对应的体重。

 

我们知道最简单的关系就是直线,这里我们假设体重和身高的关系是: y = wx +b
,此处 x 代表身高,y 表示对应的体重。而 w 是斜率,b 是截距。现在我们将已有的 m 个数据绘制在图上,此时直线 y =wx + b 不可能恰好经过所有的点,如下图所示:

此时我们要说这个 y = wx +b
预测是精准的,就意味着要使所有的误差(实际值-预测值)综合来说是最小的,这个就是我们刚才说的 损失函数
。比如,对于这 m 个数据,我们希望其最小化误差,怎幺评估呢?损失函数有很多种,这里采用均方差的形式:

结合我们的 y=wx +b
,我们将其稍微变换下,引入 w 和 b:

当 J 函数值最小时,也就是我们预测值最符合实际的情况。现在来考虑如何找到使 J 函数最小的 w 和 b 呢?可能有同学还记得要想找到两个值 w 和 b 使得 J 最小,可以借助 最小二乘法

 

写不动了,毕竟我只是个保洁员而已。关于最小二乘法应该是大学讲的,有兴趣自己查吧。

 

对于两个参数来,使用最小二乘法可以快速的找到合适的 w 和 b,但如果对于很多参数来说,最小二乘法就不是那幺方便了,因此我们需要另外一种算法来帮我们找到合适的参数值,这种方法就是 梯度下降算法

 

这里我们以优化 w 参数为例简述梯度下降的过程,首先以 w 为横坐标,以损失函数 J 作为纵坐标,做出一个图像来,可能是下面这个样子滴,此时 J 最小的情况就是斜率为 0 的 A 点:

这里的 A 点对应的 w 值就是我们希望找到的,但一开始我们给定 w1 值的时候可能不在 A 点位置,如下所示:

既然这 w1 位置不是我们的最优值,那我们怎幺办呢?首先对 w 求一个偏导数: ∂J/∂w
,在这里,偏导数表示损失函数是如何随着 w 而变化的,也就是过曲线上一点的直线的倾斜程度的变化。如果这个点离 A 点越远,那这个点处的斜率就越大, ∂J/∂w
值也就越大。求完 w1 处的偏导数后,为了逼近最优的 A 点,我们进行迭代,往下走一小步,如下图所示:

这样我们就找到了一个更好的 w2。如果这个 w2 仍然不够好怎幺办?那我们继续通过类似的方式,即下述公式来找到另外一个更好的 w,通过不断的迭代直到找到最好的 w,这就是所谓的梯度下降。

那如果计算的时候错过了最优的 A 点怎幺办?那就回过头来继续找,直到最优点。

简单来说梯度下降就是通过不断的迭代来找到使得损失函数值最小的参数。来举一个更贴切的例子:有一个步长为 10m 的小姐姐(不要问这是什幺样的腿)要用最快的方式下山,该怎幺做呢?首先她需要在当前位置找到最陡峭的一边,然后沿着这边走一步过去,到了新的位置后,再找到最陡峭的一边再过去,以此下去,直到山脚,这就是梯度下降。

 

梯度

 

在上面的实例我们以一元函数 y = wx +b
为例,解释了梯度下降算法是如何帮助我们找到最优 w。回顾上文神经网络预测中用到的公式,不难发现该公式本身就是个多元函数,神经网络训练的过程也就是为了找到这些合适的参数值。现在我们正式来认识梯度的概念函数在某一点沿着不同的方形运动时,函数值的变化率是不同的。对于多元函数而言,梯度被定义为该函数的全部偏导数构成的 向量

我们知道向量是有方向的,梯度向量的方向就是函数值变化率最大的方向。也就是,对于函数的某个特定点,它的的梯度就表示从该点出发,函数值变化最为迅猛的地方,比如在下山过程中最陡峭的地方。再通俗点说,只要每个变量沿着各自变量偏导的方向变化,函数的整体变化就可以达到最大。以二元函数 f(x,y)为例,梯度就是对 x,y 分别求导组成的二维向量,三元函数类似,以此类推:

梯度下降算法

 

在弄明白梯度是啥后,正式来看下梯度下降算法:

在这个公式中,J 是关于 Θ 的一个函数,我们当前所处的位置为 Θ 处,要想从 Θ 号位置走到 J 最小的位置,首先需要确定我们前进的方向,而后走一段距离 α,走完这段距离就到到了 Θ 号位置,来个简单的图看下:

α 在梯度下降算方法中称为学习率或者是步长(比如那个步长 10m 的小姐姐),通过该参数我们可以确定一下走多远;太大容易错过最低点,太小需要走很多步才能到达最低点,计算量增多。

 

来,上才艺。这里我们以函数 f(Θ) = Θ 为例,来看下梯度下降算法是如何查找使 y 值最小时对应 x 的,在四次迭代后,其变化如下:

方便起见,我们将其图形化,不难发现在 Θ 为 0 是,f(Θ)最小。而我们在经过四迭代后,也基本上快到了最低点:

在神经网络的训练过程中,梯度下降的计算过程和上述基本类似,只不过由于神经网络中自变量 x 的个数更多,整体运算和图像会更加的复杂。此外在这里我们没有考虑偏导数计算问题,在实际环境中,我们不会直接基于导数法则来计算偏导,而是会用到 BP 算法,也就是经典的误差反向传播算法,该算法可以极大减少偏导数的计算量。

 

其他

 

关于人工智能的简单介绍到这,在西瓜落地端智能的过程中我们遇到了很多有意思的问题,后续我们再做补充。

 

参考资料

[1]

神经活动中内在思想的逻辑演算: https://bytedance.feishu.cn/file/boxcnQ6epoB6hMRMetZ9jJ0O5Qd

[2]

Dendral: https://en.wikipedia.org/wiki/Dendral

[3]

A fast learning algorithm for depp belief nets: https://www.cs.toronto.edu/~hinton/absps/fastnc.pdf

[4]

机械姬: https://movie.douban.com/subject/4160540/

[5]

机器管家: https://movie.douban.com/subject/1292529/

[6]

数学之美: https://book.douban.com/subject/10750155/

[7]

示例: http://playground.tensorflow.org/#activation=tanh&batchSize=10&dataset=gauss&regDataset=reg-plane&learningRate=0.03&regularizationRate=0&noise=0&networkShape=&seed=0.65349&showTestData=false&discretize=false&percTrainData=50&x=true&y=true&xTimesY=false&xSquared=false&ySquared=false&cosX=false&sinX=false&cosY=false&sinY=false&collectStats=false&problem=classification&initZero=false&hideText=false&discretize_hide=true

[8]

数据集: http://playground.tensorflow.org/#activation=tanh&batchSize=10&dataset=circle&regDataset=reg-plane&learningRate=0.03&regularizationRate=0&noise=0&networkShape=&seed=0.00283&showTestData=false&discretize=false&percTrainData=50&x=true&y=true&xTimesY=false&xSquared=false&ySquared=false&cosX=false&sinX=false&cosY=false&sinY=false&collectStats=false&problem=classification&initZero=false&hideText=false&discretize_hide=true

 

Be First to Comment

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注