MachineLearning-Is-Fun-For-Anyone-Curious-About-ML
What is Machine Learning:Machine Learning 的概念与算法介绍
估计你已经厌烦了听身边人高谈阔论什么机器学习、深度学习但是自己摸不着头脑,这篇文章就由浅入深高屋建瓴地给你介绍一下机器学习的方方面面。本文的主旨即是让每个对机器学习的人都有所得,因此你也不能指望在这篇文章中学到太多高深的东西。言归正传,我们先来看看到底什么是机器学习:
Machine learning is the idea that there are generic algorithms that can tell you something interesting about a set of data without you having to write any custom code specific to the problem. Instead of writing code, you feed data to the generic algorithm and it builds its own logic based on the data.
笔者在这里放了原作者的英文描述,以帮助更好地理解。

“Machine learning” is an umbrella term covering lots of these kinds of generic algorithms.
Two kinds of Machine Learning Algorithms: 两类机器学习算法
粗浅的划分,可以认为机器学习攘括的算法主要分为有监督学习与无监督学习,概念不难,但是很重要。
Supervised Learning: 有监督学习
假设你是一位成功的房地产中介,你的事业正在蒸蒸日上,现在打算雇佣更多的中介来帮你一起工作。不过问题来了,你可以一眼看出某个房子到底估值集合,而你的实习生们可没你这个本事。为了帮你的实习生尽快适应这份工作,你打算写个小的

利用这些数据,我们希望最后的程序能够帮我们自动预测一个新的屋子大概能卖到多少钱

解决这个问题的算法呢就是叫做监督学习,你已知一些历史数据,可以在这些历史数据的基础上构造出大概的处理逻辑。在将这些训练数据用于算法训练之后,通用的算法可以根据这个具体的场景得出最优的参数,有点像下面这张图里描述的一个简单的智力题

这个例子里,你能够知道根据左边的数字的不同推导出不同的右边的数字,那么你脑子里就自然而然生成了一个处理该问题的具体的逻辑。在监督学习里,你则是让机器帮你推导出这种关系,一旦知道了处理特定系列问题的数学方法,其他类似的问题也就都能迎刃而解。
Unsupervised Learning: 无监督学习
我们再回到最初的那个问题,如果你现在不知道每个房间的售价,而只知道房间大小、尺寸以及临近的地方,那咋办呢?这个时候,就是无监督学习派上用场的时候了。

这种问题有点类似于某人给了你一长串的数字然后跟你说,我不知道每个数字到底啥意思,不过你看看能不能通过某种模式或者分类或者啥玩意找出它们之间是不是有啥关系。那么对于你的实习生来说,这种类型的数据有啥意义呢?你虽然不能知道每个屋子的价格,但是你可以把这些屋子划分到不同的市场区间里,然后你大概能发现购买靠近大学城旁边的屋子的人们更喜欢更多的小卧室户型,而靠近城郊的更喜欢三个左右的卧室。知道不同地方的购买者的喜好可以帮助你进行更精确的市场定位。另外你也可以利用无监督学习发现些特殊的房产,譬如一栋大厦,和其他你售卖的屋子差别很大,销售策略也不同,不过呢却能让你收获更多的佣金。本文下面会更多的关注于有监督学习,不过千万不能觉得无监督学习就无关紧要了。实际上,在大数据时代,无监督学习反而越来越重要,因为它不需要标注很多的测试数据。
这里的算法分类还是很粗浅的,如果要了解更多的细致的分类可以参考:
House Price Estimation With Supervised Learning: 利用监督学习进行房屋价格估计
作为高等智慧生物,人类可以自动地从环境与经历中进行学习,所谓熟读唐诗三百首,不会做诗也会吟,你房子卖多了那自然而然看到了某个屋子也就能知道价格以及该卖给啥样的人了。这个 Strong_AI 项目也就是希望能够将人类的这种能力复制到计算机上。不过目前的机器学习算法还没这么智能,它们只能面向一些特定的有一定限制的问题。因此,Learning
这个概念,在这里更应该描述为
Let’s Write the Program
基本的思想很好理解,下面就开始简单的实战咯。这里假设你还没写过任何机器学习的算法,那么直观的来说,我们可以编写一些简单的条件判断语句来进行房屋价格预测,譬如:
def estimate_house_sales_price(num_of_bedrooms, sqft, neighborhood):
price = 0
# 俺们这嘎达,房子基本上每平方200
price_per_sqft = 200
if neighborhood == "hipsterton":
# 市中心会贵一点
price_per_sqft = 400
elif neighborhood == "skid row":
# 郊区便宜点
price_per_sqft = 100
# 可以根据单价*房子大小得出一个基本价格
price = price_per_sqft * sqft
# 基于房间数做点调整
if num_of_bedrooms == 0:
# 没房间的便宜点
price = price — 20000
else:
# 房间越多一般越值钱
price = price + (num_of_bedrooms * 1000)
return price
这就是典型的简答的基于经验的条件式判断,你也能通过这种方法得出一个较好地模型。不过如果数据多了或者价格发生较大波动的时候,你就有心无力了。而应用机器学习算法则是让计算机去帮你总结出这个规律,大概如下所示:
def estimate_house_sales_price(num_of_bedrooms, sqft, neighborhood):
price = <computer, plz do some math for me>
return price
通俗的理解,价格好比一锅炖汤,而卧室的数量、客厅面积以及邻近的街区就是食材,计算机帮你自动地根据不同的食材炖出不同的汤来。如果你是喜欢数学的,那就好比有三个自变量的方程,代码表述的话大概是下面这个样子:
def estimate_house_sales_price(num_of_bedrooms, sqft, neighborhood):
price = 0
# a little pinch of this
price += num_of_bedrooms * .841231951398213
# and a big pinch of that
price += sqft * 1231.1231231
# maybe a handful of this
price += neighborhood * 2.3242341421
# and finally, just a little extra salt for good measure
price += 201.23432095
return price
注意,上面那些譬如 .841...
这样奇怪的数据,它们就是被称为 权重
,只要我们能根据数据寻找出最合适的权重,那我们的函数就能较好地预测出房屋的价格。
Weights
首先,我们用一个比较机械式的方法来寻找最佳的权重。
Step 1
首先将所有的权重设置为
def estimate_house_sales_price(num_of_bedrooms, sqft, neighborhood):
price = 0
# a little pinch of this
price += num_of_bedrooms * 1.0
# and a big pinch of that
price += sqft * 1.0
# maybe a handful of this
price += neighborhood * 1.0
# and finally, just a little extra salt for good measure
price += 1.0
return price
Step 2
拿已知的数据来跑一波,看看预测出来的值和真实值之间有多少差距,大概效果如下所示

咳咳,可以看出这个差距还是很大的啊,不过不要担心,获取正确的权重参数的过程漫漫,我们慢慢来。我们将每一行的真实价格与预测价格的差价相加再除以总的屋子的数量得到一个差价的平均值,即将这个平均值称为 cost
,即所谓的代价函数。最理想的状态就是将这个代价值归零,不过基本上不太可能。因此,我们的目标就是通过不断的迭代使得代价值不断地逼近零。
Step 3
不断测试不同的权重的组合,从而找出其中最靠近零的一组。
- 过去
40 年来,包括语言学、翻译等等在内的很多领域都证明了通用的学习算法也能表现出色,尽管这些算法本身看上去毫无意义。 - 刚才咱写的那个函数也是所谓的无声的,即函数中,并不知道卧室数目
bedrooms 、客厅大小square_feet 这些变量到底是啥意思,它只知道输入某些数字然后得出一个值。这一点就很明显地和那些面向特定的业务逻辑的处理程序有很大区别。 - 估计你是猜不到哪些权重才是最合适的,或许你连自己为啥要这么写函数都不能理解,虽然你能证明这么写就是有用的。
- 如果我们把参数
sqft
改成了图片中的像素的敏感度,那么原来输出的值是所谓的价格,而现在的值就是所谓的图片的类型,输入的不同,输出值的意义也就可以不一样。
Try every number?
言归正传,我们还是回到寻找最优的权重组合上来。你可以选择去带入所有的可能的权重组合,很明显是无穷大的一个组合,这条路肯定是行不通的。是时候展示一波数学的魅力了,这里我们介绍一个数学中常见的优化求值的方法:首先,我们将

然后,我们将这个代价方程变得更加通用一点


图中的蓝色低点即意味着代价最小,也就是权重组合最接近完美值的时候。

有了图之后是不是感觉形象多了?我们寻找最优权重的过程就是一步一步走到谷底的过程,如果我们每次小小地修改权重而使得其不断向谷底靠近,我们也就在向结果靠近。如果你还记得微积分的一些知识,应该知道函数的导数代表着函数的切线方向,换言之,在图中的任何一点我们通过计算函数的导数就知道变化的方向,即梯度下降的方向。我们可以计算出代价函数中每个变量的偏导数然后将每个当前变量值减去该偏导数,即按照梯度相反的方向前进,那就可以逐步解决谷底咯。如果你感兴趣的话,可以深入看看批量梯度下降相关的知识。如果你是打算找个机器学习的工具库来辅助工具,那么到这里你的知识储备已经差不多咯,下面我们再扯扯其他的东西。
Something Skip Over: 刚才没提到的一些东西
上文提到的所谓三步的算法,用专业的名词表述应该是多元线性回归。即通过输入含有多个自变量的训练数据获得一个有效的计算表达式用于预测未来的部分房屋的价格。但是上面所讲的还是一个非常简单的例子,可能并不能在真实的环境中完美地工作,这时候就会需要下文即将介绍的包括神经网络、
Further Reading | 深入阅读
可能看完了这些,觉着

所以,总结而言,如果是能够手动解决的问题,那计算机可能解决的更快,但是它也无法解决压根解决不了的问题。在原作者看来,目前机器学习存在的一个很大的问题就是依然如阳春白雪般,只是部分科研人员或者商业分析人员的关注对象,其他人并不能简单地理解或者使用,在本节的最后也推荐一些公开的课程给对机器学习有兴趣的朋友:
Neural Network: 神经网络
上文中,我们通过一个简单的房价预测的例子了解了机器学习的基本含义,在本节,我们将会继续用一些泛化的算法搭配上一些特定的数据做些有趣的事情。本节的例子大概如下图所示,一个很多人的童年必备的游戏:马里奥,让我们用神经网络帮你设计一些新奇的关卡吧。
在正文之前,还是要强调下,本文是面向所有对机器学习有兴趣的朋友,所以大牛们看到了勿笑。
Introduction To Neural Networks: 神经网络模型初探
上文中我们是使用了多元线性回归来进行房屋价格预测,数据格式大概这个样子

最后得到的函数是:
def estimate_house_sales_price(num_of_bedrooms, sqft, neighborhood):
price = 0
# a little pinch of this
price += num_of_bedrooms * 0.123
# and a big pinch of that
price += sqft * 0.41
# maybe a handful of this
price += neighborhood * 0.57
return price
如果用图来表示的话,大概是这个样子

不过正如上文中提到的,这个算法只能处理一些较为简单的问题,即结果与输入的变量之间存在着某些线性关系。

现在等于每次预测我们有了四个独立的预测值,下一步就是需要将四个值合并为一个最终的输出值

What is Neural Network?: 神经网络初识
我们将上文提到的两个步骤合并起来,大概如下图所示

咳咳,没错,这就是一个典型的神经网络,每个节点接收一系列的输入,为每个输入分配权重,然后计算输出值。通过连接这一系列的节点,我们就能够为复杂的函数建模。同样为了简单起见,我在这里也跳过了很多概念,譬如 feature scaling 以及 activation function,不过核心的概念是:
- 每个能够接收一系列的输入并且能够按权重求和的估值函数被称为
Neuron( 神经元) - 多个简单的神经元的连接可以用来构造处理复杂问题的模型
有点像乐高方块,单个的乐高方块非常简单,而大量的乐高方块却可以构建出任何形状的物体

Giving Our Neural Network a Memory: 给神经网络加点上下文
目前,整个神经网络是无状态的,即对于任何相同的输入都返回相同的输出。这个特性在很多情况下,譬如房屋价格估计中是不错的,不过这种模式并不能处理时间序列的数据。举个栗子,我们常用的输入法中有个智能联想的功能,可以根据用户输入的前几个字符预测下一个可能的输入字符。最简单的,可以根据常见的语法来推测下一个出现的字符,而我们也可以根据用户历史输入的记录来推测下一个出现的字符。基于这些考虑,我们的神经网络模型即如下所示

譬如用户已经输入了如下的语句:
Robert Cohn was once middleweight boxi
你可能会猜想是n
,这样整个词汇就是boxing
,这是基于你看过了前面的语句以及基本的英文语法得出的推论,另外,middleweight
这个单词也给了我们额外的提示,跟在它后面的是boxing
。换言之,在文本预测中,如果你能将句子的上下文也考虑进来,再加上基本的语法知识就能较为准确地预测出下一个可能的字符。因此,我们需要给上面描述的神经网络模型添加一些状态信息,也就是所谓的上下文的信息

在神经网络模型中也保持了对于上下文的追踪,从而使得该模型不仅仅能预测第一个词是啥,也能预测最有可能出现的下一个词汇。该模型就是所谓的
Generating a story: 生成一个完整的故事
正如上文所说,文本预测在实际应用中一个典型的例子就是输入法,譬如

不过我们做的更疯狂一点,既然模型可以根据上一个字符自动预测下一个字符,那我们何不让模型来自动构建一个完整的故事?我们在这里使用Andrej Karpathy创建的Recurrent Neural Network implementation框架来进行实验,他也发表了一系列关于如何使用The Sun Also Rises
这篇文章,该文章包括大小写、标点符号等在内一共有
hjCTCnhoofeoxelif edElobe negnk e iohehasenoldndAmdaI ayio pe e h’e btentmuhgehi bcgdltt. gey heho grpiahe.
Ddelnss.eelaishaner” cot AAfhB ht ltny
ehbih a”on bhnte ectrsnae abeahngy
amo k ns aeo?cdse nh a taei.rairrhelardr er deffijha
惨不忍睹啊,继续进行训练,大概
hing soor ither. And the caraos, and the crowebel for figttier and ale the room of me? Streat was not to him Bill-stook of the momansbed mig out ust on the bull, out here. I been soms
inick stalling that aid.
“Hon’t me and acrained on .Hw’s don’t you for the roed,” In’s pair.”
“Alough marith him.”
已经能够识别基本的句型结构与语法规则咯,甚至能够较好地为上下文添加标点符号了,不过还是存在着大量的无意义词汇,我们继续增加训练的次数:
He went over to the gate of the café. It was like a country bed.
“Do you know it’s been me.”
“Damned us,” Bill said.
“I was dangerous,” I said. “You were she did it and think I would a fine cape you,” I said.
“I can’t look strange in the cab.”
“You know I was this is though,” Brett said.
“It’s a fights no matter?”
“It makes to do it.”
“You make it?”
“Sit down,” I said. “I wish I wasn’t do a little with the man.”
“You found it.”
“I don’t know.”
“You see, I’m sorry of chatches,” Bill said. “You think it’s a friend off back and make you really drunk.”
现在差不多能看了,有些句式还是很模仿
There were a few people inside at the bar, and outside, alone, sat Harvey Stone. He had a pile of saucers in front of him, and he needed a shave.
“Sit down,” said Harvey, “I’ve been looking for you.”
“What’s the matter?”
“Nothing. Just looking for you.”
“Been out to the races?”
“No. Not since Sunday.”
“What do you hear from the States?”
“Nothing. Absolutely nothing.”
“What’s the matter?”
Super Mario: 利用神经网络进行Mario 过关训练

这个制作器能够让你去手动制作马里奥的一些关卡,很不错的和朋友之间进行互动的小工具。你可以添加常见的障碍物或者敌人到你自己设计的关卡中,有点像可视化的乐高工作台。我们可以使用刚才创建的用于预测
这个游戏有大概

用放大镜观察的话,可以看出每一关都是由一系列的网格状对象组成的

这样的话,我们可以将每个网格中的对象用一个字符代替,而整个关卡的字符化表述就是:
--------------------------
--------------------------
--------------------------
#??#----------------------
--------------------------
--------------------------
--------------------------
-##------=--=----------==-
--------==--==--------===-
-------===--===------====-
------====--====----=====-
=========================-
其中:
-
代表空白=
代表坚固的方块#
代表那些可以被撞破的块?
代表钱币块

仔细瞅瞅这个文件,你会发现如果按照一行一行从左到右读的话,好像是毫无头绪

不过如果按照列的次序从上往下读的话,你会发现还是有点套路的

为了更好地训练数据,我们打算按列来分割数据,这里我们会使用特征选择的技术来将数据转化为最合适的表示。首先,我们将整个文本旋转
-------#---=
-------#---=
-------?---=
-------#---=
-----------=
-----------=
----------@=
----------@=
-----------=
-----------=
-----------=
---------PP=
---------PP=
----------==
---------===
--------====
-------=====
------======
-----=======
---=========
---=========
然后就可以使用上面创建好的模型进行训练咯,经过几轮训练之后大概可以得出这个样子:
LL+<&=------P-------------
--------
---------------------T--#--
-----
-=--=-=------------=-&--T--------------
--------------------
--=------$-=#-=-_
--------------=----=<----
-------b
-
最初的训练里模型认知到应该大量的出现-
与=
字符,不过还是很粗糙,再经过几千次的训练,得出的内容是:
-----------=
----------=
--------PP=
--------PP=
-----------=
-----------=
-----------=
-------?---=
-----------=
-----------=
此时模型已经能够认知到需要将每行保证相同的长度,甚至开始寻找出P
都放到了
--------PP=
--------PP=
----------=
----------=
----------=
---PPP=---=
---PPP=---=
----------=

看上去像模像样了,其中有几个需要特别注意的地方:
- Lakitu,就是那个小怪兽被放到了半空中,跟
Mario 关卡一样一样的。 - 它认知到了应该把管道插入大地
- 并没有让玩家无路可走
- 看起来风格非常像最传统的马里奥的版本
最后生成出来的游戏截图大概是这样的

你可以在这里观看完整的游戏视频。
Toys VS Real World Applications
这里用于训练模型的循环神经网络算法与真实环境下大公司用于解决语音识别以及文本翻译等常见问题的算法一本同源,而让我们的模型看上去好像个玩具一样的原因在于我们的训练数据。仅仅取自最早期的超级马里奥的一些关卡数据远远不足以让我们的模型出类拔萃。如果我们能够获取由其他玩家创建的成百上千的关卡信息,我们可以让模型变得更加完善。不过可惜的是我们压根获取不到这些数据。随着机器学习在不同的产业中变得日渐重要,好的程序与坏的程序之间的差异越发体现在输入数据的多少。这也就是为啥像
Further Reading
条条大道通罗马,在机器学习中解决问题的办法也永远不止一个。你可以有很多的选项来决定如何进行数据预处理以及应该用啥算法。增强学习正是可以帮你将多个单一的方法组合起来的好途径。如果你想更深入的了解,你可以参考下面几篇较为专业的论文:
-
Amy K. Hoover’s team used an approach that represents each type of level object (pipes, ground, platforms, etc) as if it were single voice in an overall symphony. Using a process called functional scaffolding, the system can augment levels with blocks of any given object type. For example, you could sketch out the basic shape of a level and it could add in pipes and question blocks to complete your design.
-
Steve Dahlskog’s team showed that modeling each column of level data as a series of n-gram “words” makes it possible to generate levels with a much simpler algorithm than a large RNN.
Object Recognition In Images With Deep Learning: 利用深度学习对于图片中对象进行识别
近年来关于深度学习的讨论非常火爆,特别是之前阿尔法狗大战李世乭之后,更是引发了人们广泛地兴趣。南大的周志华教授在《机器学习》这本书的引言里,提到了他对于深度学习的看法:深度学习掀起的热潮也许大过它本身真正的贡献,在理论和技术上并没有太大的创新,只不过是由于硬件技术的革命,从而得到比过去更精细的结果。相信读者看完了第三部分也会有所感。仁者见仁智者见智,这一章节就让我们一起揭开深度学习的神秘面纱。在本章中,我们还是基于一个实际的例子来介绍下深度学习的大概原理,这里我们会使用简单的卷积神经网络来进行图片中的对象识别。换言之,就类似于

就像前两章一样,本节的内容尽量做到即不云山雾罩,不知所云,也不阳春白雪,曲高和寡,希望每个队机器学习感兴趣的人都能有所收获。这里我们不会提到太多的数学原理与实现细节,所以也不能眼高手低,觉得深度学习不过尔尔呦。
Recognizing Objects: 对象识别
先来看一个有趣的漫画

这个漫画可能有点夸张了,不过它的灵感还是来自于一个现实的问题:一个三岁的小孩能够轻易的辨别出照片中的鸟儿,而最优秀的计算机科学家需要用
Starting Simple: 先来点简单的
在尝试怎么识别照片中的鸟儿之前,我们先从一些简单的识别开始:怎么识别手写的数字

再重述下机器学习的理念,即是一些通用的,可以根据不同的数据来处理不同的问题的算法。因此我们可以简单地修改一些神经网络就可以识别手写文字,在这里简单起见,我们只考虑一个字符:手写的数字18*18
的大小,其中部分关于

上章节中构造的神经网络有三个输入,在这里我们希望用神经网络来处理图片,第二步就是需要将一张图片转化为数字的组合,即是计算机可以处理的样子。表担心,这一步还是很简单的。对于电脑而言,一张图片就是一个多维的整型数组,每个元素代表了每个像素的模糊度,大概是这样子

18*18
像素的图片转化为

这次的共有

注意,我们的神经网络模型中有两个输出,第一个输出预测该图片是

Tunnel Vision
虽然我们上面一直说这个任务不难,不过也没那么简单。首先,我们的识别器能够对于标准的图片,就是那些数字端端正正坐在中间,不歪不扭的图片,可以非常高效准确地识别,譬如

不过实际情况总不会如我们所愿,当那些熊孩子一般的

Searching with a Sliding Window: 基于滑动窗口的搜索
虽然道路很曲折,但是问题还是要解决的,我们先来试试暴力搜索法。我们已经创建了一个可以识别端端正正的

这方法叫滑动窗口法,典型的暴力搜索解决方法。在部分特定的情况下能起到较好地作用,不过效率非常低下。譬如对于同一类型但是大小不同的图片,你可能就需要一遍遍地搜索。
More data and a Deep Neural Net
刚才那个识别器训练的时候,我们只是把部分规规矩矩的图片作为输入的训练数据。不过如果我们选择更多的训练数据时,自然也包含那些七歪八斜的

用这种方法,我们可以方便地创建无限的训练数据。数据有了,我们也需要来扩展下我们的神经网络,从而使它能够学习些更复杂的模式。具体而言,我们需要添加更多的中间层

这个呢,就是我们所谓的深度神经网络
,因为它比传统的神经网络有更多的中间层。这个概念从十九世纪六十年代以来就有了,不过训练大型的神经网络一直很缓慢而无法达到真实的应用预期。不过近年来随着我们认识到使用

不过尽管我们可以依靠
The Solution is Convolution: 卷积神经网络
人们在看图片的时候一般都会自带层次分割的眼光,譬如下面这张图

你可以一眼看出图片中的不同的层次:
- 地上覆盖着草皮与水泥
- 有个宝宝
- 宝宝坐在个木马上
- 木马在草地上
更重要的是,不管宝宝坐在啥上面,我们都能一眼看到那嘎达有个宝宝。即使宝宝坐在汽车、飞机上,我们不经过重新的学习也可以一眼分辨出来。可惜现在我们的神经网络还做不到这一点,它会把不同图片里面的
How Convolution Works
上面我们提到一个暴力破解的办法是将图片分割到一个又一个的小网格中,我们需要稍微改进下这个办法。
将图片分割为重叠的砖块
譬如上面提到的滑动窗口搜索,我们将原图片分割为独立的小块,大概如下图所示
通过这一步操作,我们将原始图片分割为了
将每个图片瓷砖输入到小的神经网络中
之前我们就训练一个小的神经网络可以来判断单个图片是否属于

对于不同的图片的瓷砖块,我们都会使用具有相同权重的神经网络
来进行处理。换言之,我们将不同的图片小块都同等对待,如果在图片里发现了啥好玩的东西,我们会将该图片标识为待进一步观察的。
将每个小块的处理结果存入一个新的数组
对于每个小块输出的数组,我们希望依然保持图片块之间的相对位置关联,因此我们将每个输出的数组仍然按照之前的图片块的次序排布

到这里,我们输入一个大图片,输出一个相对而言紧密一点的数组,包含了我们可能刚兴趣的块的记录。

为了缩减该特征数组的大小,我们打算使用所谓的max pooling算法来缩减像素采样数组的大小,这算法听起来高大上,不过还是挺简单的

进行预测
截至目前,一个大图片已经转化为了一个相对较小地数组。该数组中只是一系列的数字,因此我们可以将该小数组作为输入传入另一个神经网络,该神经网络会判断该图片是否符合我们的预期判断。为了区别于上面的卷积步骤,我们将此称为fully connected
网络,整个步骤呢,如下所示

添加更多的步骤
上面的图片处理过程可以总结为以下步骤:
Convolution: 卷积Max-pooling: 特征各维最大汇总Full-connected: 全连接网络
在真实的应用中,这几个步骤可以组合排列使用多次,你可以选择使用两个、三个甚至十个卷积层,也可以在任何时候使用

在这个例子中,最早地是输入一个
Building our Bird Classifier: 构建一个真实的鸟儿分类器
概念了解了,下面我们就动手写一个真正的鸟类分类器。同样地,我们需要先收集一些数据。免费的 CIFAR10 data set包含了关于鸟儿的
非鸟类的图片大概这样
这边我们会使用TFLearn来构建我们的程序,
# -*- coding: utf-8 -*-
"""
Based on the tflearn example located here:
https://github.com/tflearn/tflearn/blob/master/examples/images/convnet_cifar10.py
"""
from __future__ import division, print_function, absolute_import
# Import tflearn and some helpers
import tflearn
from tflearn.data_utils import shuffle
from tflearn.layers.core import input_data, dropout, fully_connected
from tflearn.layers.conv import conv_2d, max_pool_2d
from tflearn.layers.estimator import regression
from tflearn.data_preprocessing import ImagePreprocessing
from tflearn.data_augmentation import ImageAugmentation
import pickle
# Load the data set
X, Y, X_test, Y_test = pickle.load(open("full_dataset.pkl", "rb"))
# Shuffle the data
X, Y = shuffle(X, Y)
# Make sure the data is normalized
img_prep = ImagePreprocessing()
img_prep.add_featurewise_zero_center()
img_prep.add_featurewise_stdnorm()
# Create extra synthetic training data by flipping, rotating and blurring the
# images on our data set.
img_aug = ImageAugmentation()
img_aug.add_random_flip_leftright()
img_aug.add_random_rotation(max_angle=25.)
img_aug.add_random_blur(sigma_max=3.)
# Define our network architecture:
# Input is a 32x32 image with 3 color channels (red, green and blue)
network = input_data(shape=[None, 32, 32, 3],
data_preprocessing=img_prep,
data_augmentation=img_aug)
# Step 1: Convolution
network = conv_2d(network, 32, 3, activation='relu')
# Step 2: Max pooling
network = max_pool_2d(network, 2)
# Step 3: Convolution again
network = conv_2d(network, 64, 3, activation='relu')
# Step 4: Convolution yet again
network = conv_2d(network, 64, 3, activation='relu')
# Step 5: Max pooling again
network = max_pool_2d(network, 2)
# Step 6: Fully-connected 512 node neural network
network = fully_connected(network, 512, activation='relu')
# Step 7: Dropout - throw away some data randomly during training to prevent over-fitting
network = dropout(network, 0.5)
# Step 8: Fully-connected neural network with two outputs (0=isn't a bird, 1=is a bird) to make the final prediction
network = fully_connected(network, 2, activation='softmax')
# Tell tflearn how we want to train the network
network = regression(network, optimizer='adam',
loss='categorical_crossentropy',
learning_rate=0.001)
# Wrap the network in a model object
model = tflearn.DNN(network, tensorboard_verbose=0, checkpoint_path='bird-classifier.tfl.ckpt')
# Train it! We'll do 100 training passes and monitor it as it goes.
model.fit(X, Y, n_epoch=100, shuffle=True, validation_set=(X_test, Y_test),
show_metric=True, batch_size=96,
snapshot_epoch=True,
run_id='bird-classifier')
# Save model when training is complete to a file
model.save("bird-classifier.tfl")
print("Network trained and saved as bird-classifier.tfl!")
如果你有足够的
Testing out Network
我们可以使用如下脚本进行图片的分类预测:
# -*- coding: utf-8 -*-
from __future__ import division, print_function, absolute_import
import tflearn
from tflearn.layers.core import input_data, dropout, fully_connected
from tflearn.layers.conv import conv_2d, max_pool_2d
from tflearn.layers.estimator import regression
from tflearn.data_preprocessing import ImagePreprocessing
from tflearn.data_augmentation import ImageAugmentation
import scipy
import numpy as np
import argparse
parser = argparse.ArgumentParser(description='Decide if an image is a picture of a bird')
parser.add_argument('image', type=str, help='The image image file to check')
args = parser.parse_args()
# Same network definition as before
img_prep = ImagePreprocessing()
img_prep.add_featurewise_zero_center()
img_prep.add_featurewise_stdnorm()
img_aug = ImageAugmentation()
img_aug.add_random_flip_leftright()
img_aug.add_random_rotation(max_angle=25.)
img_aug.add_random_blur(sigma_max=3.)
network = input_data(shape=[None, 32, 32, 3],
data_preprocessing=img_prep,
data_augmentation=img_aug)
network = conv_2d(network, 32, 3, activation='relu')
network = max_pool_2d(network, 2)
network = conv_2d(network, 64, 3, activation='relu')
network = conv_2d(network, 64, 3, activation='relu')
network = max_pool_2d(network, 2)
network = fully_connected(network, 512, activation='relu')
network = dropout(network, 0.5)
network = fully_connected(network, 2, activation='softmax')
network = regression(network, optimizer='adam',
loss='categorical_crossentropy',
learning_rate=0.001)
model = tflearn.DNN(network, tensorboard_verbose=0, checkpoint_path='bird-classifier.tfl.ckpt')
model.load("bird-classifier.tfl.ckpt-50912")
# Load the image file
img = scipy.ndimage.imread(args.image, mode="RGB")
# Scale it to 32x32
img = scipy.misc.imresize(img, (32, 32), interp="bicubic").astype(np.float32, casting='unsafe')
# Predict
prediction = model.predict([img])
# Check the result.
is_bird = np.argmax(prediction[0]) == 1
if is_bird:
print("That's a bird!")
else:
print("That's not a bird!")
How accurate is 95% accurate?: 怎么理解这95% 的准确率
刚才有提到,我们的程序有
- 首先,我们将正确被标识为鸟类的鸟类图片称为:True Positives

- 其次,对于标识为鸟类的非鸟类图片称为:True Negatives

- 对于划分为鸟类的非鸟类图片称为:False Positives

- 对于划分为非鸟类的鸟类图片称为:False Negatives

最后的值可以用如下矩阵表示:

这种分法的一个现实的意义譬如我们编写一个程序在
