印度,我正在接近上瘾的边缘 之三

老德里

摆脱了各种阻挠,最终我充满向往的前往老德里。

我对老德里是做好了心理准备的。我准备去看那里的嘈杂、脏乱、绚烂的色彩以及香料的味道。我知道老德里是一座古城,几个世纪以来原汁原味的保留了下来。它在我心中的形象至少是一个低配版的耶路撒冷,应该有类似哭墙附近的那些人流如织的小巷、卖各种稀奇古怪东西的商店、和各种宗教各种肤色的人群。

但我看到的和我想象的完全不一样。旧德里非常拥挤,杂乱,却并不有趣。“并不有趣”是重点。我一直告诉自己,要有一双发现美的眼睛,但恕我眼拙,我的确很难从中发现美感。

我寻找色彩,却发现绝大多数房屋是水泥的灰色;即便偶尔出现有些色彩的墙面,或者巨大的棚子,却因为灰尘和杂物斑驳不堪而失去了原本的颜色,和灰色渐渐混在一起,并不容易分辨。

我也努力扎进那些香料市场或者纱丽市场,但最终无法打起任何兴趣。商品过于廉价和劣质,以至于我没有一次对价格产生好奇心。

这个部分的印度就如同还没有发展的中国县城的大街,嘈杂,没有秩序。我心里面有一种强烈的逃离的冲动,又被自己再多看看的理性压制下来。

如果说好的部分,就是活力,无尽的活力。到处都是人,他们用手推,有肩拉,用大白牛车驼,将超乎想象的巨大货物从这里运输到印度各地。

太阳当空,空气里面弥漫着香料,垃圾,粪便,人畜混杂的味道,人潮涌动无法通行。这就是老德里在我的记忆中定格的画面。

对于古都的想像

这种落差,来自于我对于古城的想象与现实对比。如果说这就是一个普通的印度乡村,我或许还会更加心悦诚服的接受它,而作为一个古都,尤其是印度这么悠久历史国家的古都,我倍感失望。

我猜,或许我们是过于美化了过去的时代吧。是不是这个古都在沙贾汗兴建的时候就一直是这样的,只不过印度人民没有让古城仅仅变成一个观光景点,而是在这里一直延续着生活,这样生活,交易到现在呢?是不是这才是原汁原味的古都本来的面目呢?我其实不得而知。

泰姬陵

如果把我对印度所有的失望加在一起再放大十倍,一个泰姬陵就足矣扳回比分,让我重新爱上印度。3个小时的颠簸后,我来到了泰姬陵的面前。

举世闻名的奇观,当自己置身其中的时候,真正让人惊叹不已的其实不多,更多的是对于摄影家的仰慕:“他们是如何把平凡无奇的景色在合适的时间、合适的光线、合适的角度下变得如此美丽的?” 泰姬陵的美,摄影家无法捕获。它需要你站在面前,然后就会从心里“哇”的叫出来。

泰姬陵的美最主要来自于它的洁白。如果说它是装修完毕的宫殿,而不是一座树立在这里几百年的陵墓,我会觉得更加可信。

泰姬陵是纯白的,光滑的大理石覆盖了从基座到陵墓还有四周的高塔的全部表面。手摸上去是滑滑的,冰冰的。在北部印度的炙热阳光下,我很喜欢把双手和脸长时间的贴在泰姬陵的白色大理石表面上,就如同夏天的大狗把自己贴在水泥地上一样,这样能够感觉舒适和安宁。

泰姬陵的地板也是一尘不染,经过三百多年依然如五星级饭店的光洁的地板一样。这在印度的整体并不干净的大环境下显得如同来自外太空的一片飞地。

陵墓上只要不是白色的部分,都是用各种宝石镶嵌打磨而成,并不是画上去的,这工艺就如同佛罗伦萨大教堂的那种石头混合而成的图案,过多少年都不会褪色的可能。2万名工匠就这样用手,历时22年打造了一件泰姬本人都没有见过的礼物。只有亲眼见到,才知道世界上真的有如此美丽的礼物。

印度就是如此,有美如泰姬陵一样的珍珠,散落泥土和尘埃里面。而这泥土和尘埃的名字,应该叫做“贫穷”。眼前的印度就好似一副经过多重曝光的照片,不同时代的影像以建筑的形式固化在了同一块画布上,空间相同,时间却相距百年。

印度的一个碎片

这是我第一次踏足印度,用走马观花的方式,在最短的时间,获得了一个或许是世界上最多样、最复杂的国家的笼统的印象。

除了上面讲到的,我还大胆的跑到新德里火车站,登上他们的火车;见识了各种奇葩的自拍方式;访问了各式宗教的场所,比如巴哈伊教;被警察拦截;被保安勒索;目击轻微的车祸以及迅速的解决。。。各式各样新奇古怪的事情在24小时里面密集的发生,但我知道这些都是这个伟大的国度的很小的一部分。

套用在这个国度上发生的寓言故事,我仅仅摸到了印度这只大象的一个脚趾头,无法如瞎子们一样高兴的宣布,我已经知道大象的长相。

但可以肯定的是,印度的多样性远远超乎我的想象,里面的形形色色的人,不同的阶层,不同的宗教,他们之间的矛盾统一,从这短短的几十个小时里面已经让我倍感冲击。用简单的一个词来描述印度完全不可能。

对于同样来自悠久历史的中国的我,印度依然太不一样了。它的历史完全不一样,宗教不一样,发展阶段不一样,人也不一样。这种不一样构成了冲击,也构成了吸引力。

类似就意味着无趣,不同才让人着迷。我既忌惮印度的脏乱差,又忍不住想去体验完全不同的生活。

印度就像咖啡,香烟,或者辣椒等让人上瘾的东西一样,让你欲罢不能的,恰恰是它带来的不舒适、甚至痛苦。我隐约觉得,对于印度,我正在接近上瘾的边缘。。。

上篇:印度,我正在接近上瘾的边缘 之一 

   印度,我正在接近上瘾的边缘 之二

相关:西贡初体验

   对于三十几个城市的偏见

   10分钟日语教程

   10分钟俄语教程

   10分钟韩语教程

   画点画

   又一批纯手工制作的画新鲜出炉

   有人写文章就是为了促进思考

10分钟西班牙语教程

以前写10分钟日语韩语俄语教程的时候,从来没有想过西班牙语也可以有10分钟教程。其实所谓的10分钟教程对于一种语言肯定远不及入门,而仅仅是作为一种语言观察,发现一些这些语言中,我希望早一些有人告诉我的一些常识,好让自己消除一种语言的陌生感,从而开始升起一些学习的兴趣和信心。所以十分钟教程与其说是教程,不如说是语言结构简介。

我本来以为,西班牙语至少是英语,法语一个级别的难度,并不存在什么捷径。但当我真的开始研究这门语言的时候,却惊讶的发现,西班牙语实在是我知道的所有的语言中间最容易学的,远比英文容易。它的容易在于他的规律非常明确。

  1. 元音发音固定,没有变音,所见即所读。

  2. 头上带类似汉语二声符号的 á é ú í ó 是重音;没有重音符号的,如果以元音,字母s,字母n结尾的单词重音在倒数第二音节,其余的重音一律在最后一个音节。

  3. 除了 j , h,c 以外,辅音和汉语拼音几乎相同。(因为现在的汉语拼音采用的就是拉丁化方案,是汉语拼音和拉丁语保持了一致)

这些规则一个一个来说。

元音发音固定

西班牙语最大的特点就是五个元音发音永远不变。a, e, u, i, o 的发音就和国际音标里面这几个符号的发音,或者和汉语里“啊 哎 唔 衣 嗷”差不多。不像英语一样同一个字母会在不同的地方发不同的音,比如 hat,fast,wake,woman,water 中的 a 发音都不一样。要是使用西班牙语发音规则,所有这些单词都按照 fast 里的 a 发。换句话说,基本上按照汉语拼音的发音发就好了。比如女厕所上经常看到 Dama(女士)这个词,就直接发作d-a 大,m-a 妈,连在一起,dama:大妈。简单吗?

所以,西班牙语居然是不需要音标的,就如同汉语拼音本身就是一套发音标注法,不需要另外一套音标去标注汉语拼音的发音一样。

我听不懂一个词的时候,出租车司机会像中国小学学拼音一样的按照声母韵母的方式拼给我,比如edificio(建筑),他就会慢慢的拼出来,e,di,fi,cio = efidicio,而并不会用英文里面把字母 e-f-i-d-i-c-i-o 这样一个一个拼出来。这个过程就如同汉语拼音里面 “摸+啊 = 马”,“了+乌=路” 这样的教法,而不会拼出来 m-a-l-u 这四个字母。

看到就可以准确发音,这是一种语言对于学习者的莫大帮助。街上看到什么就会念而不需要字典。这样的方式汉语拼音是这样的,日文的平假名片假名也是这样的。但是中文日文还有一个汉字的问题,看到汉字依然不知道读音。而西班牙文的读音和拼写完全一致,并且神奇的对于我们这种学过汉语拼音的直接对接,我不知道还有什么语言如此简单。

重音

曾经在西拔牙旅行的时候,感觉西班牙人说话有种特别的韵律,有种前脚赶后脚的紧凑。我总觉得他们说话的时候手里在甩一个溜溜球,一圈一圈的转,而语言也跟这溜溜球一样重一下,再重一下,形成”吧吧吧铛吧,吧吧吧吧铛吧“这样的节奏。

当时不得要领。而看了重音规则才知道,西拔牙语大多数的单词都是倒数第二个音节重音(英语里基本上是第一个音节重音比较多),而这就是我觉得他们发音像甩溜溜球一样的原因,一堆轻音,一个重音再紧接一个轻音构成溜溜球的一圈。 再加上西班牙语的音节基本上是以元音结尾,字正腔圆,发音饱满,听起来那种奔放的拉丁美洲气息扑面而来。

声母(辅音)特殊变化

大多数的字母发音和英文没有差别,只有几个特殊,从而清晰的标记出了西班牙语的风味。

J – 发H的音,比如 San José 发作圣

H – 不发音,比如 Hola 就发作 ola

C – 发 G 的音,Mexico发作墨西哥(而不是墨西科)

LL – 这居然曾经是一个字母(就如同两个V直到现在还是英文的一个单独字母W一样),发作Y

Ñ – 这是唯一一个西班牙语独有的字母(现在西班牙语是27个字母),近似读作 “ni”。

从英语学习西班牙语的含义

对于有一些英语的基础的人来说,猜到西班牙语的含义就容易很多,就如同中国人猜日文的意思一样,虽然不准确但能猜个八九不离十。

西班牙语/法语/意大利语/葡萄牙语是拉丁语系,而英语是日耳曼语系里面混杂了拉丁语系的词(主要从法语混过去的)。所以英语里面有两套完全不相关的语系混在在一起。

一般来说,简单的词多是日耳曼语系的,复杂的合成词多是拉丁语系的。而西班牙语就是那个被混进英语的纯正的拉丁语系语言。

比如这些词:

太阳,英语是 Sun,但是太阳系的太阳就变成 Solar。为什么太阳是 Sun太阳的就是 Solar。没有什么关系嘛,原因是 Sun 来自于日耳曼语系,而 Solar来自于拉丁语系。西班牙语的太阳就是这个拉丁语词根:Sol。

同样道理,月亮:Moon,月亮的:Lunar,而西班牙语的月亮就是 Luna。

Hand(手)- Mannual (手册) – Manos(西班牙语的手)

Hill(山) – Mountain (山)- Monte (西班牙语的山)

Sea (海) – Marine (海的) – Mar (西班牙语的海)

Wash (洗) – Lavatory (盥洗间)- Lava (西班牙语的洗)

这样的例子很多,就是很多的英文的复杂一点的词的词根就是拉丁语(或者说是西班牙语)。当然学会了西班牙语,其它拉丁语也好学很多,因为基本上都是这些词的变种。很大一部分西班牙语单词第一次看到就能猜出来含义,比如museo(museum),información(information),aire(air)。

西班牙语就是一门 形同英语,音同汉语的语言,还有比这更好学的语言吗?

例子

我在墨西哥入境表上看到的如下的内容。大家可以尝试按照汉语拼音的方法朗读出来,而含义可以对照英文理解。

西班牙语:Tipo de documento de identificación y número

英语:Type of document identification and number

西语:Pais de destino final

英语:City of final desination

如上列出的两句话第一次看或许还没法找到对应的关系,但对照读一遍,第二次的时候,就很容易通过同词根的英文联想起来西班牙文的意思。

最常用的几个词

再记住几个最常见的重要的单词的对应,就大体可以读很多东西了。

El  / La / Los / Las = the (El是阳性单数,La阴性单数,Los阳性复数,Las阴性复数)

es = is

en = in

con = with 

y = and 

o = or

toda = all

aqui = here

antes = before

有了如上的知识,至少可以开始学习西班牙语的旅程了。至于语法的阴性阳性时态的变化多端,或许即使不会,作为一个旅行者来并没有到阻碍沟通的地步。

最后还是需要声明,我不懂西班牙语,仅仅来源于对西班牙语的观察。

卷积网络 CNN 学习笔记之一:我们是怎么认识0的

题图:Airbnb总部 绘于:2020年3月

最近在做人工智能方面的探索的时候,深感能找到的材料中,按照一定步骤编程就能得出结果的信息很多,但大家都把这个过程视为一个黑盒子,常说,这个过程我们就不知道为什么了,反正结果就是计算机有智能了。

我当然不满足于这个答案,于是把YouTube上面关于卷积网络( Convolutional Neural Network )的大量视频看完了以后,依然觉得缺少一些关于“人工智能到底是怎么工作的”这个问题讲清楚的资料。

至于完全不讨论代码,仅仅在很高的层面上大讲人工智能对于人类的影响的书,实在对于我这种希望动手搭建一些东西的人来说,帮助更加有限。

这也就是我想写一些文章,把我的关于人工智能的 “人文方面的思考” 和 “代码实践” 结合在一起来的原因。

最简单的问题

为了回答“人工智能到底是怎么认识事物的”,我们可以从人工智能届的 Hello World 的例子(注①)来开始尝试回答。这个例子,就是 MNIST 的数据集训练识别 0 – 9 的手写数字。

MNIST 数据集

人工智能领域和传统的编程领域很大的一个区别在于数据和代码的占比。想象一下,传统的编程中,估计 90% 的程序内容是代码,而代码中各种常量从存储角度占的比例或许不到 10% 。而人工智能领域,代码比较少,大头是数据。以 Glove 为例,代码只有 100K ,但是训练数据集是来自互联网的 60 亿个单词,训练结果是 1 G 的数据。代码和数据的比例就反过来了,99.999%是数据,0.001% 不到是代码。

人工智能领域很多的数据集都是以数据的来源的机构命名的。MNIST (Modified National Institute of Stands and Technology)就是这么个原始数据集。它包含用于训练的六万个手写的数字的图像,以及图像对应的数字(这个算是正确答案),还包含一万对用来测试准确度的同样结构的图像和正确答案。

这些图像得来的过程很巧妙,是从美国普查局收集的机读卡里面采样的,就是那种高考时用的卡片,上面手写自己的考号,然后把对应的数字旁边的黑色椭圆用2B铅笔涂黑。这样子不就有了一个天然的手写体图像和正确答案的对应的数据吗?有人就把60000个这样的手写体和正确答案打包,供机器学习的研究者使用,并且挑战谁能达到更高的识别率。

上代码,来看一看别人为我们准备好的数据

如下四行 python 代码就能用 tensorflow 从互联网上把这个 11.49M 的数据集下载到本地来。

# 导入著名的 tensorflow。 在命令行下用 pip3 install tensorflow 安装import tensorflow as tf# 从互联网上下载 mnist 数据集到本地 ~/.keras/datasets/mnist.npz# x_train, y_train 分别是是60000个训练图像和答案# x_test, y_test 分别是10000个测试图像和答案# 训练的算是日常习题,测试的才是高考题。为了计算机防止作弊,计算机读书的时候是不能看到高考试卷的(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()# 把第1001个数字的图像打印出来print(x_train[1000])# 把“正确答案”打印出来。结果应该是 0print(y_train[1000])

如果我们好奇这个里面的数据长成什么样子,可以用 print 直接打印出来,得到如下所示(可以左右滑动可以看到全貌)

[[  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0] [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0] [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0] [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0] [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0] [  0   0   0   0   0   0   0   0   0   0   0   0   0   0  36 146 254 255 251  95   6   0   0   0   0   0   0   0] [  0   0   0   0   0   0   0   0   0   0   0   0   3  97 234 254 254 232 254 254  35   0   0   0   0   0   0   0] [  0   0   0   0   0   0   0   0   0   0   0  89 140 254 254 174  67  33 200 254 190   0   0   0   0   0   0   0] [  0   0   0   0   0   0   0   0   0   0 108 253 254 235  51   1   0   0  12 254 253  56   0   0   0   0   0   0] [  0   0   0   0   0   0   0   0   0  12 216 254 244  55   0   0   0   0   6 213 254  57   0   0   0   0   0   0] [  0   0   0   0   0   0   0   0   0  25 254 254 132   0   0   0   0   0   0 168 254  57   0   0   0   0   0   0] [  0   0   0   0   0   0   0   0   0  45 254 243  34   0   0   0   0   0   0 168 254  57   0   0   0   0   0   0] [  0   0   0   0   0   0   0   0   0 128 254 157   0   0   0   0   0   0   0 168 254  57   0   0   0   0   0   0] [  0   0   0   0   0   0   0   0  19 228 254 105   0   0   0   0   0   0   7 228 254  57   0   0   0   0   0   0] [  0   0   0   0   0   0   0   0  58 254 254  87   0   0   0   0   0   0  10 254 246  47   0   0   0   0   0   0] [  0   0   0   0   0   0   0   0  58 254 254   9   0   0   0   0   0   0  10 254 210   0   0   0   0   0   0   0] [  0   0   0   0   0   0   0   0  58 254 254   9   0   0   0   0   0   0 105 254  91   0   0   0   0   0   0   0] [  0   0   0   0   0   0   0   0   5 219 254   9   0   0   0   0   0  24 230 254  24   0   0   0   0   0   0   0] [  0   0   0   0   0   0   0   0   0 216 254   9   0   0   0   0   0  84 254 251  23   0   0   0   0   0   0   0] [  0   0   0   0   0   0   0   0   0 216 254  36   0   0   0   0  22 208 251  94   0   0   0   0   0   0   0   0] [  0   0   0   0   0   0   0   0   0 129 254 120   0   0   0   3 140 254 229   0   0   0   0   0   0   0   0   0] [  0   0   0   0   0   0   0   0   0  83 254 222  17   0   0  91 254 236  53   0   0   0   0   0   0   0   0   0] [  0   0   0   0   0   0   0   0   0  18 235 254 134  21 119 237 254 124   0   0   0   0   0   0   0   0   0   0] [  0   0   0   0   0   0   0   0   0   0  53 249 254 234 252 254 172   3   0   0   0   0   0   0   0   0   0   0] [  0   0   0   0   0   0   0   0   0   0   0 116 237 254 254 133  20   0   0   0   0   0   0   0   0   0   0   0] [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0] [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0] [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0]]

原来,数据是60000个元素的数组,每个元素,是一个 28 行的数据,每一行有 28 个数,每个数都是从 0 到 255 之间的一个数字,代表灰度。0 就是纯黑,255 就是纯白,中间的数字代表从 0 到 255 之间的 256 档的灰度。

大家猜猜如上的这 28 x 28 = 784 个数字代表的是哪一个数字?

用我们从小就训练得炉火纯青的人肉神经网络判断,这排列成28行28列的 784 个数字后面代表的那一个数应该是 0 。看一下训练集里给出的正确答案,也是 0 。 

接下来,我们用三行 Python 语句把这个图像显示出来:

# 导入绘图工具 matplotlib。 在命令行下用 pip3 install matplotlib 安装import matplotlib.pyplot as plt# 把这个28x28的矩阵直接给 Matplot 画灰度图plt.imshow(x_train[1000], cmap="gray")# 显示出来plt.show()

如果说上面的那个纯数字拼出来的 0 还有点像《黑客帝国》里面的机师 Tank 眼中的场景的话,如下的图像就还原到人眼可以看到的世界。这个似乎更明确了,就是 0 ,没错了。

在 MNIST 数据集里面有 5923 个不同的人写的各种各样的 0 。之后我们会通过几行代码就可以训练计算机认识这个数字,并且识别准确率高达 99% 。

接下来的旅程,我们就从这个 0 开始。我希望来回答,“计算机怎么知道这是个 0 ” 这一个问题。

思考题

我希望你和我一样,深情的望着这个小圈圈,然后用灵魂拷问的方式来问自己:我是怎么知道这是个 0 的?

仔细的探索自己的神经网络的每个角落,拷问自己的大脑,是如何在0.01秒不到的时间就知道,这是一个 0。而不是 1 ,而不是 4 呢?是因为我看到了一个圈吗?那我怎么知道这是个圈呢?在我知道这是个圈之前,大脑里面到底发生了些什么呢?

注①:

C语言的 Hello World 是 printf(“Hello World”); 

Hadoop 的 Hello World 是 Word Count ,

而人工智能的 Hello World 一定就是 MNIST 识别数字。

卷积网络 CNN 学习笔记之二:细节消失,抽象生成

题图:香港的蜗居

绘于:2020年3月

上一篇:卷积网络 CNN 学习笔记之一:我们是怎么认识0的

昨天留了个思考题,你的大脑是如何认识那个一圈的?

在你翻看 前一篇文章 以前,我再问你一个问题,你还记得上一次我们见到的 MNIST 数据集里面的那个 0 是如下哪一副吗?或者降低点难度,和如下哪一个 0 更接近呢?

训练集里的各种 0 (生成这个图的代码见 代码一)

或许你和我一样,记不太清了。没关系,这正是我要讲的,就是神经网络在处理信息的时候,当得到高一层的信息的时候就会忽略底层的信息,故意的遗忘。然后基于这一层的信息再往上推导一层更高层次的信息,再把下面的信息遗忘。如此很多层,直到得到最终的结论。这是一个不断丢失信息,形成一个新的信息的过程。

以上面的图像为例,原始的信息量其实非常大,那是一个 28 x 28 x 256 (也就是 28行,28列,每个点有256种可能性的图)的数据,也就是20万种可能性。而经过我们的人肉神经网络处理以后形成了 0 – 9 这10个数字中间的一个数字( 0 ),从 20 万种可能性到 10 种可能性这个过程就是神经网络的功劳。否则,人眼睛有六百万以上个视锥,眼睛看到的还是连续的视频的信息,要是我们的底层神经元不忽略掉这些信息而直接都传导到上一层神经元的话,我们脑子里面就会堆满了所有的细节,就无法思考更抽象的东西了。

小量维度的大量信息 –> 大量维度的小量信息

虽然图像的细节丢失了,但更高一层的认知在大脑里面诞生了。比如看了这个图像,我问你这几个问题:

这是一个黑白图像吗?【是】图像里面有一个圈吗?【是】图像里面线条连续吗?【是】这是个正方形图片吗?【是】

这样的问题我可以问几十个,对于这些问题我们的神经网络都可以回答,但这些问题的答案都没有直接写在原始的 28 x 28 的数据里面的,而是我们通过底层的信息,一层一层的抽象出来的。

请注意,你有没有发现最早的数据是在一个维度(黑或白的程度)上的大量数据,一步步的变成在多个维度的少量数据。“这是黑白图像吗”就是一个维度,这个维度的数据只有 一比特(是/不是);图像里面有一个圈吗?是另外一个维度,这样的维度很多很多。

人工智能就是从灰度或者彩色的图像里面,先认识边界,然后认识线,然后认识形状,然后再在这些圆和方的组合再认识人脸,然后再认识人,然后再从人认识场景等等。这样一层层的学习的。因为学些的层次很多,很深,我们常把这种模型叫做深度学习 – 深度就是很多层的意思。

人脑是怎么认识 0 的?

现在我们准备来解决这个问题。直接从原始的数据中看出来最终的数字不容易,但如果计算机把原始数据简化成如下这16个问题的答案,你是不是觉得容易一些?为了简化我用 0.0 代表没有,1.0 代表有。(实际情况可能不像 0 和 1 这么绝对,而是它们之间的一个数字)

这个图片的左上角区域:   有 \  反斜线吗?   【0.0   有 | 垂直的线条吗? 【1.0】    有 /  正斜线吗?   【1.0   有 –  横线吗?    【1.0这个图片的右上角区域:   有 \  反斜线吗?   【1.0   有 | 垂直的线条吗? 【1.0】    有 /  正斜线吗?   【0.0   有 –  横线吗?    【1.0这个图片的左下角区域:   有 \  反斜线吗?    【1.0   有 | 垂直的线条吗? 【1.0】    有 /  正斜线吗?   【0.0   有 –  横线吗?    【1.0这个图片的右下角区域:   有 \  反斜线吗?   【0.0   有 | 垂直的线条吗? 【1.0】    有 /  正斜线吗?   【1.0   有 –  横线吗?    【1.0

如果你知道如上 16 个问题的答案,即使不看到原始的数据,也能脑补出来一个 0 的样子 (就是左上角有横线,然后转到右上到左下的斜线,然后是竖线,然后接左下角的四种线段)。

再看这张图呢?

这个图片的左上角区域: 1   有 \  反斜线吗?   【0.0 2   有 | 垂直的线条吗?【1.0】  3   有 /  正斜线吗?   【0.0 4   有 –  横线吗?   【0.0这个图片的右上角区域: 5   有 \  反斜线吗?   【0.0 6   有 | 垂直的线条吗?【1.0】  7   有 /  正斜线吗?  【0.0 8   有 –  横线吗?   【0.0这个图片的左下角区域: 9   有 \  反斜线吗?   【0.010   有 | 垂直的线条吗?【1.0】 11   有 /  正斜线吗?  【0.012   有 –  横线吗?   【0.0这个图片的右下角区域:13   有 \  反斜线吗?   【0.014   有 | 垂直的线条吗?【1.0】 15   有 /  正斜线吗?   【0.016   有 –  横线吗?   【0.0

我猜这个数是 1 ,和你猜的一样吗?因为从数据上看,只有竖线,没有斜线和横线。。。。

到了这一步当然怎么做都行,最土的也是最不可扩展的,就是硬写程序:

如果 1,9,14,17 这四个问题的答案是 0 其他问题答案都是 1 的话,结果是 0如果所有的竖线的问题都是 1, 但是其他的问题答案都是 0 的话, 结果是 1

而实际上,大家还是用数学上面的线性回归来解决这样的问题。我们假设所有的问题都是 Y = a * X + b 这样的结构,只要有足够的(X,Y)数据可以让我们训练,找到 a 和 b 这两个参数就可以。也就是说,输入是 16 个数字 (x1, x2,  x3, x4, x5, …, x16),分别代表对于如上 1 到 16 号问题的答案。输出是 10 个数字 (y1, y2, y3, …., y10) 分别代表这个数字是 0 – 9 的可能性。当我们有了60000多组这样的对应以后,就可以找到规律,再给我一组 x,我就可以输出一组 y 。这部分是最基础的线性代数,用 Excel 就可以轻松搞定的,还没有涉及最近出现的人工智能或者卷积神经网络。而接下来有趣的部分,就是如果从图像信息转换到这样的 16 个问题答案。这是后面的文章需要解决的问题。

多层的神经网络

当然到这里我们还远远没有回答 我们是怎么认识0的 。上面的部分其实希望在我全部讲完了以后再回过头来看才有可能看懂。

但我们先打住,看一段代码,然后用后面的几次文章通过从代码的角度,来细致的把刚才两段说的每一个细节了解清楚。

# 导入著名的 tensorflow。在命令行下用 pip3 install tensorflow 安装import tensorflow as tfimport numpy as npfrom tensorflow.keras.models import Sequentialfrom tensorflow.keras.layers import Conv2D, Flatten, MaxPooling2D, Dense, BatchNormalization# 从互联网上下载 mnist 数据集到本地 ~/.keras/datasets/mnist.npz# x_train, y_train 分别是是60000个训练图像和答案# x_test, y_test 分别是10000个测试图像和答案# 训练的算是日常习题,测试的才是高考题。为了计算机防止作弊,计算机读书的时候是不能看到高考试卷的(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()# 下面两句做一个非常简单的预处理,就是先把 60000 x 28 x 28 的三维数据扩展成 60000 x 28 x 28 x 1的四维数据# 这个对于数据没有任何变化,只是和Tensorflow要求的输入保持一致而已# 然后再都除以 255,使得数据都落在0-1之间,仅仅提高些计算效率,不怎么影响结果的x_train = x_train.astype(np.float32).reshape(*x_train.shape, 1)x_train /= 255# 翠花,上模型!如下就是传说中的卷积神经网络model = Sequential()model.add(Conv2D(32, kernel_size=(3, 3), activation='relu', input_shape=(28, 28, 1)))model.add(MaxPooling2D(pool_size=(2, 2)))model.add(Flatten())model.add(Dense(10, activation='softmax'))model.compile(optimizer='adam', metrics=['accuracy'], loss='sparse_categorical_crossentropy')model.fit(x_train, y_train, epochs=10)

大家可以先看看这个 Python 代码。这是一个很简单的训练卷积网络模型的代码,19行以前的都还是一些预处理的代码,真正有用的就只有19到25行这7行代码。而这个代码就是构建了一个仅仅四层的神经网络,分别是

  • 第一层:卷积层 Conv2D

  • 第二层:池化层 MaxPooling

  • 第三层:压平层 Flatten

  • 第四层:全连接层 Dense

也不要被这些名字吓倒。每一层,即便是不用任何类,仅仅是用原生的Python代码,也都是几行搞定的简单的运算。所以人工智能的代码是如此之简洁优美,却达到了以前复杂的系统几十年没有达到的高度。

如上代码执行一遍,一两分钟以后,就会得到一个训练好的模型,按照输出我们看到,它的识别准确率高达 99.73%

Epoch 10/101875/1875 [==============================] - 10s 5ms/step - loss: 0.0083 - accuracy: 0.9973

我不得不承认,这个识别率已经远高于我这个人肉神经网络了,毕竟很多的数字我看半天也拿不准是多少。 😀 

下一篇就要开始正儿八经的解释,人工智能网络的工作细节了。后台回复 “ AI ” 可以获得全系列文章和代码

后注:代码一:画出包括第 1001 个图像的 64 各种 0 的代码

import tensorflow as tf# 画图工具,人工智能学习必备import matplotlib.pyplot as plt# 从互联网上下载 mnist 数据集到本地 ~/.keras/datasets/mnist.npz# x_train, y_train 分别是是60000个训练图像和答案# x_test, y_test 分别是10000个测试图像和答案# 训练的算是日常习题,测试的才是高考题。为了计算机防止作弊,计算机读书的时候是不能看到高考试卷的(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()# 准备取出来64个图像画出来看看c = 0for index in range(850, 2000):    # 不是 0 就算了    if y_train[index] != 0:        continue    c += 1    if c > 64:        continue    # 导入绘图工具 matplotlib。在命令行下用 pip3 install matplotlib 安装    # 把这个28x28的矩阵直接给 Matplot 画灰度图    ax = plt.subplot(8, 8, c)    ax.set_xticks([])    ax.set_yticks([])    plt.imshow(x_train[index], cmap="gray")# 显示出来plt.show()

卷积网络 CNN 学习笔记之三:卷积像卷地毯

学习笔记之一:我们是怎么认识0的

学习笔记之二:细节消失,抽象生成

总算可以正式开始讲卷积网络的工作原理了。

过滤器像卷地毯一样卷过去

上面的白纸就像图像,而我们现在拿一个红颜色的格子,这个格子是 3 x 3 ,里面写着9个数字。这 9 个数字开始是随机数,经过大量的训练以后开始不断的变得有意义。

这个红色的小纸片,会先从图像的左上角开始,和最左上角的 3 x 3 的那一小片重合,就得到了九对数字,就是每一个位置的红纸片上 和 它压住的那个位置的白纸片上的数字 形成了一对。

我们把这九对数字两两相乘,然后把乘积相加,最后形成 1 个数。把这个数填写在另外一个空白的格子的最左上角。

然后这个小红方块向右移动一格,再做同样的操作,然后接着向右移一格。到头了再回到最左边,再向下移动一行,再如此移动。就这样用一个小红纸片把这个白纸上的所有的位置都扫描一遍,从而把这张大白纸的数字变成另外一张大白纸的数字。

这个操作,就跟卷地毯一样,数学上成为卷积。

这有什么用呢?

这是个好问题。大家留意一下这个红格子里面的数字,我用了左侧都是 1 , 中间一列是 0 ,右侧都是 -1 的矩阵。大家想象这个矩阵放在什么样的数字上面产生的值会最大呢?

如果它放在纯色上面卷积出来的数字一定是0,比如放在纯黑色的部分。因为左边的数字和右边的数字相等而且一个为正一个为负。所以这个小方块滑过纯色的地方就是0。

如果它放在一个和它很类似的模式上,得到的数字会变的异常的大。比如说它放到了一个也是和它一样左边都是 1 ,右面都是 -1 的地方,这个卷积的结果将会是 6  ( 1 x 1 + 1 x 1 + 1 x 1 + -1 x -1 + -1 x -1 + -1 x -1)。

其实,这个图像就是一个竖线的检测器。它本身代表着一个竖线(左侧白色,右侧黑色),它在滑过图像的格子的时候,只有发现和它类似的模式的时候,就会点亮(返回很大的数字),而遇到其他的模式的时候就会暗淡(接近零或者负数),这样以来,经过滑动,我们就可以检测边沿了。这个叫做边沿检测(Edge Detection)。

多个Filter,来检测同一个图像

我们例子里面的这个红方块仅仅检测竖线,甚至它仅仅检测左边是白色,右边是黑色的竖线,而反过来的左黑右白的竖线它都不感兴趣。那如何把其他的模式也检测出来呢?

没关系,我们可以使用大量的检测模块。比如可以有另外一个。在我昨天的例子里面,我用了 32 个这样的小红方块,分别独立负责检测 32 种不同的模式。比如下面我又找了一个检测左上白色,右下黑色的从右上到左下45度的斜线的检测模块。

经过 32 个不同的检测模块,我们叫做 filter ,我们会把一副图片拆解成成 32 张新的图片。

但这里注意,如果原图的每一个像素里面的数据是代表了在灰度这个维度的 黑白程度 的话,那么生成的32张新的图像里面的每个像素代表的是在对应的 filter 下的和 filter 的程度。

这种 32 层的图像我们其实应该很熟悉。所有的彩色图像都是三层表示的,分别是RGB,代表着这个像素在三个不同维度的值,如果把这三层分别叫做 Red-ness, Green-ness, Blue-ness,这 32 个可能应该分别叫做 垂线度 (vertical-ness),横线度( horizontal-ness), 斜线度(diagnal-ness),叉度( cross-ness) 等等等等。不用去查了,这些词,无论中文还是英文都是我瞎编的。

第一层抽象完成

下面就是真实的实验结果了。如下就是 MNIST 数据集里面随机的一个数字,我目测是个 2 。

如下的上面四行就是训练以后生成的 32 个 filter 的样子,其中黑色代表接近 0 ,白色代表接近 1 。

这些 filter 已经很复杂,不仅仅是检测纯粹的横线或者竖线,但我们知道经过训练以后的这些 filter 一定是最有利于下一步计算机做决策的。

下面的后四行是上面的这个 2 分别经过 32 个 filter 以后形成的图像。你看到这些图像有点像被肢解了的 2 ,因为他们都分别关心 2 这个数字的不同的方面。 

下一篇文章我们将继续了解接下来的步骤,一步步深入下去。

后台回复 “ AI ” 可以获得全系列文章和代码

卷积网络 CNN 学习笔记之四:最终形成判断

题图:思南路别墅

绘于:2020年4月17日

学习笔记之一:我们是怎么认识 0 的

学习笔记之二:细节消失,抽象生成

学习笔记之三:卷积像卷地毯

上一次走到了卷积网络的第一步,就是用一个 3×3 的小矩阵就像探照灯一样,把图像扫一遍,就得到了第二层的网络。

第一层对于黑白的图像只有一个深度(depth),就是灰度值,对于第二层网络已经增加到了 32 层深,分别对应于在 32 个不同的 filter 上的匹配程度。我们这次接着深入。

ReLU (灯泡亮了!)

不知道是谁用灯泡💡来表示脑子里面的一个新主意,真是一个非常好的比喻,就是当一个想法诞生的时候,就觉得是脑子里面什么地方的一个小灯泡忽然亮起来了。

前面的卷积,生成的结果就算再复杂,也是按照线性函数来推倒的,就是 y = a * x + b ,再怎么变化多端,都还是一个线性函数。有人就提出来了,大脑的神经网络应该不是这么工作的。应该引入一些非线性的东西。这就是 ReLu 激活。(ReLU = Rectified Linear Unit)

这个函数简单得很,就是:当输入小于 0 的时候,输出为零。但输入大于 0 的时候,输出是这个数。用代码表示就是:

y = max( 0 , x )

这个就有效的把无关的信息给简化和屏蔽了。对于竖线的检测函数,如果我没有检测到竖线,我就输出为 0 , 而不再输出负向的信息了。也就是说,如果我看到了有人做好事,我就叫出来;如果有人做坏事,我就当没看见,也不会去举报你。

这就很像脑子里面的那个灯泡 💡,当几个输入的结果合并起来使得输出高过一个阈值的时候,下一层相应的神经元就被点亮(激活),如果没有超过的话,就继续暗淡下去。这个很简单的函数给整个系统带来了难得的非线性,就可以有一些逻辑运算的感觉在里面了。

我们构建的四层神经网络

model = Sequential()model.add(Conv2D(32, kernel_size=(3, 3), activation='relu', input_shape=(28, 28, 1)))

上面的代码,就是建设了一个 Conv2D 的模型,这个模型的 filter 数量是 32, kernel_size,就是这个 filter 的大小,是 3 x 3 , 激活函数使用了 relu ,并且接受一个输入为 28 x 28 x 1 的矩阵(就是那个图像),输出一个 26  x 26 x 32 的矩阵。宽和高从 28×28 变成了 26×26 是因为用一个 3×3 的矩阵向右挪的时候自然会失去最左和最右的两列,就像一个 4×4 的矩阵用一个 3×3 的矩阵去滑动的话只能滑动一格,结果变成 2×2 的矩阵一样。

model.add(MaxPooling2D(pool_size=(22)))

我们继续解释代码。下一个就是 MaxPooling2D 的功能。这个更简单的了。就是拿一个(2,2)的区域,找到其中最大的那个数,输出出来,把原来四个像素的位置合并成一个像素,并且把这个数字填进去。这个就是简单粗暴的把一个 26×26 的矩阵缩成了一个 13×13 的矩阵。

从逻辑上讲,这就是忽略细节的过程。我们看一个数字的时候,隐隐约约的觉得左上角有辆车,至于它精确的在什么位置,长什么样子,不重要,重要的是, 左上角有一辆车。他就是把得出的 32 个图像的大小都缩小一半,但是信息都保留(就是最大的,已经找到的那个模式)。

Flattern

model.add(Flatten())

这个一个比一个简单,就是把所有的数字从矩阵排成一个一维数组。前面的输出是一个 13 x 13 x 32 的一个矩阵,通过这一层,变成了一个 5408 个数字数组。就像把麻将桌中间的一堆麻将在自己面前一字排开那样。

全联接层

model.add(Dense(10, activation='softmax'))

最后的这一个层叫做全联接层,就是把前面的5408个数字和最终结果的10个数字做一个每一个节点和另外一层的每一个都连接起来,找到一个系数,找到 y = a * x + b 这样的关系。

就是说,对于结果的第一个数字,是前面5408个数字和5408个系数(a1, a2, a3, …, a5048) 相乘并且把结果相加,再加上一个数字(b)得来。这一层的参数最多,需要(5408 + 1)* 10 = 54090 个参数才可以完成。相比之下,前面的 (3 x 3 + 1)x 32 =  320个参数算是精简得不得了。

这一层最终是10个数字,对应着答案的十个数字,作为这个模型的第四层,也是最后一层,经过一个叫做 softmax 的激活函数,它的输出是一个十个数的数组,这十个数字分别都是0-1之间的一个数字,他们的加和等于 1 , 每个数字都对应相应位置的可能性。比如上一篇的结果,就会是 2 对应的数字非常高(就是电脑认为很有可能是 2 ),而其他的都很小。

训练函数

model.compile(optimizer='adam', metrics=['accuracy'], loss='sparse_categorical_crossentropy')

现在我们就到了核心的7行代码的最后三行了。前四行分别构建了四层网络,最后就开始训练这个网络了。

compile 那一行给出了优化器,loss函数还有如何衡量好坏的函数。这些对于初学者可以暂时不考虑,就先照猫画虎就好了。

model.summary()

接下来,我们把现在构建的这个网络的摘要打印出来

Model: "sequential"_________________________________________________________________Layer (type)                 Output Shape              Param #   =================================================================conv2d (Conv2D)              (None, 26, 26, 32)        320       _________________________________________________________________max_pooling2d (MaxPooling2D) (None, 13, 13, 32)        0         _________________________________________________________________flatten (Flatten)            (None, 5408)              0         _________________________________________________________________dense (Dense)                (None, 10)                54090     =================================================================Total params: 54,410Trainable params: 54,410Non-trainable params: 

从这里我们可以清晰的看到,第一层,输入一个 28x28x1 的图像,输出一个26x26x32 的图像。而深度从1变到32就是从灰度这一个维度变成了32不同的问题的答案(比如有没有竖线这样的问题)。

接下来的 MaxPooling 层,如我们刚才所说,在深度 32 不变的情况下把每一层都从 26×26 简化成了 13×13 。

在下面的 Flattern 层,把13x13x32压扁成一个长度为 5408 的数组。

最后一层再把这个数组变成长度为 10 的数组,分别对应于结果的 0 – 9 。

开始训练

到了现在,网络已经搭好了,但唯一的问题是,里面所有的参数都是 0 – 1 之间的随机数,用一个随机数构成的网络肯定准确率差得一塌糊涂了。用一个随机数作为参数的网络,当你输入一个图像,它一定也可以输出一个数字,但这个数字 10 次有 9 次是错的,对的一次也是瞎猫碰到死耗子。

为了避免随机数,接下来这一句话,才是最重要的一句话,就是开始“训练”了。

model.fit(x_train, y_train, epochs=20)

这个函数把60000个 x_train 包含的图像,还有 60000 个 y_train 代表的正确答案交给网络。刚开始的时候,答案肯定是错的。但是神经网络通过错的方向(结果太大了还是太小了)去反过去调整上一层网络的参数,然后再进一步调整再上一层的参数,直到用6000组数据把这 54,410 个参数调整到精确度最高的程度。到了训练的末期,基本上上一次训练的这些参数已经可以让下一次输入的参数以极大的可能性(99%以上)和正确答案相同了,这个训练就完成了。

下一次,我们会再把上面说的这个过程用图像可视化出来,然后再继续用比喻的方法来宏观的理解,计算机到底是怎么认识数字的。

后台回复 AI 可以获得没有发表的AI相关的文章和代码。

卷积网络 CNN 学习笔记之五:就像医生看化验单

学习笔记之一:我们是怎么认识0的

学习笔记之二:细节消失,抽象生成

学习笔记之三:卷积像卷地毯

学习笔记之四:最终形成判断

今天,我们看上一次的代码跑起来是什么样子的。

输入图像

这个是输入图像,维度是 28x28x1 。 这是 MNIST 图像集里面的第1002号数字。我用我的人脑处理了一下,觉得似乎是一个  1 。我们看看电脑如何处理。

卷积层

model.add(Conv2D(32, kernel_size=(3, 3), activation='relu', input_shape=(28, 28, 1)))

经过训练,得到如下 32 个卷积核:

输入的图像经过32个过滤器(filter)之后,输出是如下的 32 个 26×26 的图像。如果你还记得的话,是拿左上角的 0 号过滤器,从输入图像的左上角开始,9 个点对应的 9 个点分别相乘,然后把这一个像素的数据输出到下面的图的最左上角的那个像素。如此 26 x 26 次以后形成左上角的图像。再如此用第二个过滤器来制作第二个图像,如此 32 次以后,下面的 32 个图像就完成了。

池化层

这些看不太清的图像继续经过一个池化,每4个像素合并成一个像素,直观感觉就是在一个不是很清楚的图像上面继续打了马赛克。

打马赛克的作用就是为了减小下一层的负担。实践证明,打了马赛克和不打马赛克,对于准确率几乎没有影响。

model.add(MaxPooling2D(pool_size=(22)))

大家可以试验一下,就算是把pool_size增加到(8,8),也就是八八六十四个点缩成一个点,下一层也基本上可以达到98%的准确率。这就好比给你 4K 高清大图,还是这种 28 x 28 的模模糊糊的图,你对数字的识别率都差不多。

压平层

model.add(Flatten())

经过这个简单的压平,如下就是最终准备给分类器看的数据了。这是一个 在5408 个维度的特征值的柱状图。这个像不像一个化学分析报告?

我觉得这个结果特别像我拿到的验血报告。这个报告指出了我在一些指标上异常,至于这些异常代表着什么,我一脸迷茫,但是对于经验丰富的医生来说,他们可能一眼就可以看出来,我得的是什么病。比如他看到如果白血球水平偏高,同时D-二聚体升高,在随便看几个相关的指标,立刻就知道这是血栓的特征。而这些知识,是医生看了几千个病例,计算机看了60000多个病例,以后慢慢学会的。

全连接层

model.add(Dense(10, activation='softmax'))

终于到了揭晓谜底的时候了。经过一个全连接层,如上的验血报告就得出了确定的答案。在 0 – 9 的 10 种可能里面,计算机毫不犹豫的给了 1 几乎100% 的投票,而其他的可能性它认为接近为 0 。

如下是打印出来的计算机认为的可能性。和它最接近的第二名 4 的可能性已经骤降到了 1.69 x 10 的-26次方,也就是

0.0000000000000000000000000169 

其他的选项比这更低。看起来计算机还挺自信的呢。

[0.0000000e+001.0000000e+00 0.0000000e+00 3.3420391e-30 1.6909421e-267.0893234e-33 0.0000000e+00 0.0000000e+00 5.1055432e-27 3.4242518e-33]

医生的比喻

或许用比喻是最好理解一样事物(虽然必然会有一些偏差)。我们就用一个医生来比喻人工智能的算法好了。

原来人工智能发展缓慢的原因,就好像抽了一管血,然后交给医生说:帮我看看,我得了什么病。医生只能通过血的颜色,黏稠度等表面的信息去判断,当然准确度非常低。

当有了卷积神经网络的想法以后,问题一下子就变得简单了。医生手里面出现了几十种不同的“filter”,可以对血液进行分析了。

比如一个 filter 叫做“红细胞数量”。医生拿出一个显微镜,再把一滴血滴在血细胞计数器(Haemocytometer)上,然后认真的数一下,就得到了一个指标:后细胞数量。在数红细胞的时候,他对白细胞以及其他各种细胞都直接忽略。

然后他开始数白细胞。在数白细胞数量的时候,他对红细胞彻底忽略,然后就有了检测报告的第二行。卷积网络每一个过滤器都有自己特定的关注点,比如检测竖线的过滤器,对纯色卷积结果为0,对横线卷机结果为0,哪怕是颜色反过来的竖线(比如检测黑到白的边缘的,对于白到黑的边缘),因为ReLU函数的原因,结果也是0。这样子使得每个过滤器就跟医学上分别数细胞和白细胞的操作一样,绝对不会混在一起。正因为分离了,对下一步才更有帮助。

当他把所有的实验都做完了,就形成了一个完整的检测报告。从一滴血看出来病人得了什么病不太可能,但是经过了拆解,得到的中间数据就容易多了。

计算机的学习过程

那么问题来了,医生是通过多年的理论才学会的,计算机开始什么都不懂,怎么最后好似有了智能一样呢?到现在为止,我们大约的知道了计算机是如何通过分了四层一层一层的把复杂问题变成简单无比的问题的。但学习过程依然是个黑盒。工程师无法忍受黑盒,所以接下来我们会去了解这个黑盒。

但现在有一点是肯定的,以前的程序都是一行一行的,现在大家看到了,未来的人工智能的程序都是一堆一堆的数。比如我们这个数字识别程序,就是54,410个数字。如果你拿笔把这些数都抄在在你的本子上,你就拥有了这个程序。

后台回复 AI 可以获得没有发表的 AI 相关的文章和代码。

边疆战事和烽火台的比喻

题图:金泽古镇 绘于:2020年4月12日

对于一个崭新的领域,比喻是一种容易的入门的方法,尤其对于牵涉大量的技术细节的领域。当我们既想尽量详细的了解它,有没有时间和勇气去深入进去的时候,比喻是最好的方式。不同的人对于不同的比喻的亲近和理解程度不一样,于是我试着用几种不同的比喻,描述同一个事物,总有一种能让某一个人心有戚戚焉。

北疆的战事

第一个我能想到的比喻,是这样一幅画面:

在辽阔的中国大地上面,长城修建好的时候,坐在北京的皇帝是如何知道边疆有战事的。

大家当然知道这个信号就是烽火台的浓烟。那就让我们仔细去思考一下,从成千上万的边疆战士的死亡和最终离北京最近的那一个烽火台点燃之间发生的一连串事件。

上帝视角还是平民视角

我们人脑识别一张图片里面有什么的时候,我们最终的意识其实是上帝视角,可以把所有的信息放在自己的大脑里面,但大脑里面的神经元并不是上帝视角。它们每一个都只有非常有限的信息,就是和它连接的几个神经元,然后通过它们的激活或者未激活来决定自己是否激活。对于每个神经元来说,它们都是被蒙在鼓里的。

历史学家也是站在上帝视角讲述边疆的战事。但我们以这个世界上的每一个人的个体来看,没有一个人可以如上帝一样,从塞北的荒漠一眼看到北京的紫禁城。每个人只能接受到和自己最近的几公里的信息,然后做一些决定。这样,我们就拿每个个体去模拟神经网络的神经元。

原始信息输入

最前方的,是伤亡层。被敌人忽然攻击的个体忽然流血了,疼痛,惨叫,死去。这些细节就像进入卷积网络的第一层的那些像素的灰度值。

这些信息对于个体很重要,但是对于上一层神经网络,就是校尉这一层来说,并不那么重要。他关心的,和被杀死的士兵,有相同也有不同。

相同的是,他们都关心这个士兵死了。但是这个死去的士兵到底是头部中箭,还是胸部受伤而死,这些细节对于士兵来说是无比残忍的全部世界,对于校尉来说,是需要忽略的细节。而且他关心的不仅仅是一个士兵,而是要用一个“过滤器”去获取死亡的士兵的总数,就知道了死亡率。

校尉的决定

校尉需要做一个决定,就是用 ReLU (注一)函数来判断,自己要不要向上一级(就是下一层神经网络)汇报。当死亡率低于一定阈值的时候,他最好什么都不做,输出为 0 ,以免干扰上级。如果无论什么地方死一两个士兵都把这个信息一层层上传,然后由北京来做决定的话,这个效率太低了。但一旦高于一个阈值,警报就要拉响,而且比阈值高得越多,警报就拉得越响。

校尉就是神经网络里面的第一层,卷积层。他们通过把士兵的疼痛,哀嚎,所有战场的混乱等等信息进行处理,丢失细节,形成在死伤率这个最重要的维度的值,然后往上一级传递。

将军的决定

在上一级可能是驻守边关的大将军。他和他手下的全部校尉都保持联系。坐在大帐里面的将军已经听不到厮杀声和惨叫声了,因为已经有校尉这层把个人的疼痛信息转化为死伤率这个维度的强烈程度。这就模拟了数字识别系统中,后面几层的神经网络得到的信息已经不是最初的图像的灰度信息,而是“竖线度”,“斜线度” 等等边缘检测信息。

细节的丢失虽然让大将军没有办法对战士的死亡从情感上感同身受,但是抽象一层的信息可以帮助他从理性上推断下一步要不要向北京报告。

将军这一层的处理也是一个技术活。肯定不是一个逻辑的“或”操作,不是简单的只要有任何一个校尉报告就点烽火台,也肯定不是一个一句话能说明白的算法。他可能需要根据手下的十几个的校尉报来的死伤信息,和多年战争经验得来的经验信息相结合。

结合的方法可能是一个复杂的线性函数。他对不同的重要度的关卡给予不同的权重。有些要塞就算死了10%的士兵也需要立刻采取行动,而有些不重要的,就算100%的死亡也不用担心。要塞之间或许还有些相互关联。如此这些都在将军的脑子里面,他不需要把脑子里面的这几万个参数写出来,就足以果断的做一个决定:现在的军情,有没有触发他的 ReLU 函数的那个临界值。

如果将军做出判断,事情危机,ReLU 函数输出为正,一个时辰以后,在北疆的大漠里,一柱浓烟腾空而起,十几里外都看得清清楚楚。

信息的传递

在此之后的神经网络比较简单,直接看到上一层激活,下一层就跟着激活,如此传递上千公里,直到北京附近的那些烽火台浓烟滚滚。作为这个国家的神经中枢,北京的皇帝就知道了一件事情:北疆起战事了。

整个形成概念的过程

从成千上万的士兵身体被长矛利剑刺穿,到校尉分析在伤亡率这个关键维度的输出值,到将军分析来自各个不同的军队的数据,用学习得来的参数对数据加权相加,最终到从北疆到首都的一系列烽火台被点亮,直到最终形成一一比特的信息:有还是没有战事(0 还是 1),这是一个组织的信息形成机制,也很类似于神经网络的学习方式。

我们看到一个数字 0 ,当大脑形成 0 的印象的时候,其实就是如北京的皇帝知道了边疆的战事一样,或许根本没有想到,在这 0 个结论前面,已经有视神经的刺激,电信号的形成,边缘识别,再从边缘形成形状,最终结合经验得出数字的过程。

所以所以如果你需要向别人简单的解释人工智能里的神经网络是怎么工作的,你可以用这个例子。在这个例子中需要注意的是,经过一层层的神经网络,信息的细节越来越少,而维度越来越多。大多数的维度是第一层不具备的。比如死亡率这个维度,就不是任何一个士兵通过个体可以感知得到的信息。但信息的密度越来越高。一个人的身上上亿的神经元感受的疼痛,浓缩到一个人的生死,上千人的生死,浓缩到校尉的报告还是不报告;十几个校尉的类似信息才最后形成烽火台的浓烟燃起还是保持安静。

最终,每一个士兵的每一点疼痛,最终集体的决定了,北京附近的那一柱浓烟。这一柱浓烟丢失了所有士兵感受到的细节,却浓缩了所有人共同的感受,从而让一个帝国像一个人一样,可以对入侵的敌人作出反应。

神经网络也是一摸一样的。

注一:ReLU (Rectified Linear Unit)就是输入小于零,输出为零,输入大于零,输出这个数的几位简单的函数。代码是 max(0, x) 。

我们是怎么认识0的

细节消失,抽象生成

卷积像卷地毯

最终形成判断

就像医生看化验单

血常规检测和医生看病的比喻

嘉定图书馆 绘于 2020年4月14日

这是为了让大家了解神经网络的另一个比喻,希望通过用几个不同的比喻,描述同一个原理,或许可以更容易让人出现 “a-ha” 时刻,就是不知道怎的,忽然一根筋搭上了,就懂了。

现代医生看病

现代医学,医生已经不仅仅通过望闻问切来看病了,他们有了更多的工具,每个工具都是像一个特定的神经元一样,把一个维度的输入转化为多个其他维度的输出。比如验血呀,心电图了,超声波啦,核磁共振啦。。。这些检测方法多了去了。我们以最简单的验血里面的血常规做例子,来看一看医生如何通过一系列工具把一个一眼无法确定的问题解决的。

显微镜就是卷机层

化验员拿到一管血液,把它制作成血涂片。这个时候,就算再有经验的医生,凭着肉眼凡胎,也没法从这一个大拇指大小的血涂片上看什么名堂。顶多看看红不红,是不是粘稠什么的。这就像如果给计算机输入一个图片,它看到这么多的信息在一起,也没法得出什么结论。

这个时候卷积层出现了。这个工具,就是显微镜!显微镜先把整个图像放大几百倍,然后忽略所有的其他的部分,仅仅关心它看到的很小很小的一个局部。这就像卷机层里面的那个小小的卷积核,先分析大的图像中间这一小块,得出结论以后再往右移一小格,然后再分析。我们来看一下这个小小窗口是怎么工作的。

红细胞计数

即便是显微镜下针尖大的那一小部分,也有各种不同的细胞在里面游动。化验员这个时候需要决定,要把注意力集中在哪一种细胞上。比如她可以选择,我现在要关注红血球!

之后她就认真的在一个计数池里面用自己脑子里面的那个过滤器,去寻找长得像红细胞的影像。

在她数红细胞的时候,白细胞是彻底被忽略的,血小板,还有各种其他的杂质都是直接忽略的。

这个过程就跟卷积网络里面每一个过滤器都有专门的检测对象很像,比如检测垂直边界的,如果放在纯色上面,输出就是 0 ;放在横线上面,输出也是 0 ;甚至对于检测左黑右白这样的垂直边界的过滤器,放在反过来的左白右黑的边界上面输出也是 0 。只有和它想要检测的那个模式一样的时候,才会忽然亮起,输出一个很大的值。

血检的例子中,输入是显微镜下彩色的画面,经过化验员一番认真的数数,输出为单位面积中出现的红细胞的数量。这里注意,输入和输出完全不是同一个维度。成千上万个红细胞的影像的明暗信息,就这样经过化验员这个处理,变成了一个红血球数量这个更抽象维度的上的数字。

白细胞和血小板计数

红细胞检查完了以后,我们的化验员换了一个过滤器,你可以想象她把头脑里面那个能看到红细胞的眼镜摘掉,换上一副看到白细胞的眼镜,然后再数数。和红细胞过滤器相反,这个过滤器只检测白细胞,反而把红细胞忽略了。

同样的方法,化验员还需要去数血小板的数量。这样子,化验员要做的事情,就是把眼睛看到的一个个像素的各种颜色的点,转换成了三个维度(红细胞,白细胞,血小板)的三个数字。

这个过程是一个原始信息大量丢失的过程。如果要储存显微镜下看到的图像信息的话,估计得用几百兆的硬盘空间才够,但经过卷机层输出的信息,只要三个数字,几个字节就够用了。但这几个数字里面其实是从成千上万的像素的信息中转化而来的。

其他的检测

除了计数以外,还有血糖呀,胰岛素呀,各种肿瘤标志物呀,这些指标或许有几千种,每一种都有专门的仪器,专门的测量方法,最终把一管血液这一个认知的对象,变成了几千个不同维度的数字。这里面的没有一个数字包含了全部血液的信息,但又没有一个数字不是被整管血液里的每一个物质集体的影响着。

医生层

刚才讲的所有的事情,其实仅仅是化验员层,她们从同一个输入(无论是医院里的一管血液,还是计算机里的一副图像)得出了几十个维度的输出。这些输出以检验报告的形式,递交到到了医生的桌前。

医生需要迅速的通过这些已经抽象过一层的信息进行下一层抽象,回答这些化验单上天书一样的数据,到底说明这个病人得了什么病?

这个过程和化验员那层很像,也是一个模型,就是拿一个代表已有知识的过滤器,去扫描所有数据。

医生脑子里面内置了大量的过滤器。比如,其中有一个叫做肝炎的过滤器。他知道,如果指标A, B,C 偏高,指标 D,E,F 偏低,大概率就是肝炎。

这个知识是怎么来的呢?就是医生经过多年的医学学习和临床经验,训练出来的一个模型。他拿这个模型作为一个模版,和化验单对照,发现,咦,对不上,这个过滤器输出结果是 0,结论:不是肝炎 。

那就再换一个叫做 “糖尿病” 的过滤器,再扫描一遍,发现对上了!医生脑子里面某处的一个神经元 “叮” 的一下点亮,结果出来了,这位病人患的是糖尿病!

整个过程

这个过程,就是从血液开始,一个个红血球白血球的信息经过化验员变成了红血球数量,白血球数量等抽象一层的信息,然后再和其他的类似信息在医生那一层继续转换为疾病的信息。这样的多层的网络就完成了以前医生用一层网络(通过看血液的颜色)无法完成的认知。

类似的比喻可以举出很多来,基本上都希望描述同一个本质的东西:一个巨大的网络,这些网络里面没有人有上帝视角,都是蒙在鼓里面的独立单元。他们仅仅能够通过他能看到的那一部分输入,加上自己多年训练出来的一些参数组成的过滤器,产生更加抽象的,更高层次的输出。

然后下一层的独立单元,根据已经剥离了原始信息的高抽象度的信息,再加上自己的特定的学习到的知识,再加工成更高抽象度的信息,如此一层一层传导上去,让整个系统有了每个单独的单元都没有的智能。

只要仔细观察,生活中这样的集体智慧高于单个智慧的例子比比皆是。希望这些例子可以帮助大家大概的了解,一个人工智能的神经网络是如何工作的。

人工智能是如何学习的?

上一次(就像医生看化验单)我们问了一个问题但是没有回答,那就是:人工智能到底是怎么学习的?

在前面的这一系列的文章里面,我们都假设了,所有的参数都是已经学好的,就好像上帝创造了人一样,被人类创造了出来。有了第一层这几百个数字,后面几层的几万个数字,机器就如同有了灵魂一样,能够先识别边缘,再从边缘学习线,从线学习圆圈,从圆圈学习数字0,或者继续学习圆圈的组合,从而认识猫,认识人脸。

但最重要的问题还是没有回答,这些数字是怎么确定的呢?作为这个系列的最后一篇文章,希望把这个最重要的问题来回答。

淋浴调节水温的例子

举一个例子:

假设你去洗淋浴。有一个水龙头,还有一个花洒。你想洗一个温度适中的热水澡。但你也不知道这个龙头的物理结构,甚至不知道哪边是冷哪边是热。你唯一知道的是,现在的水温你不满意,比如说太热了。好在这个系统你需要训练的参数只有一个,就是那个水龙头的位置。

你往右搬了一点点,感觉一下水龙头和自己希望的水温是更近了还是更远了。比如,你觉得水更烫了,这不对。你就知道了,接下来应该往左一点。然后发现水温真的更低了。好兆头,说明我们现在的前进方向是往最终误差减少的方向前进着。那下一步,再小一点点,然后再感觉,直到到了一个地方,你继续往右,发现水温已经低于你的舒适温度了,往左,水温就会高于你的舒适温度, 那我们就在这个位置停下来了。

到了这个时候,你依然对于水龙头的工作原理一窍不通,对于流体力学和热力学更是一窍不通,但通过尝试,你好似已经物理博士毕业一样,可以精确的控制水温了。

对于简单的学习,机器基本上就靠一个字 “蒙”,当然更好听一点叫做 “试”。但试也是试的有章法的,就是机器每试一下,就把它影响的后面每一层的输出的变化记下来,来帮助自己决定,下一次尝试朝哪个方向前进。

人工智能领域的段子

有一个人工智能领域的段子,是一个人去面试一个人工智能工程师的职位,面试官和他的对话是这样的:

面试官:7 + 2 等于几?

工程师:3?

面试官:太小了

工程师:8?

面试官:还是小了

工程师:10?

面试官:大了

工程师:9.5?

面试官:大了

工程师:9.2?

面试官:你被录取了

现在估计你能看懂这个段子了。

复杂的系统

但如果要调整的不止一个水龙头,你面前放着5万个水龙头,这怎么搞?

这就是典型的训练神经网络,决定那几万个参数的时候面对的问题(几万个参数算啥呢,最近的GPT-3训练需要调整1750亿个参数呢)

继续类比,用炒股的例子来说明

我们继续举例子。假设你和一群人组成一个公司炒股。再假设这个股市完全靠消息,消息灵通的就就能赚钱。而且你们只做短线,买完以后当天就卖。

然后我们搭出来了一个金字塔结构。你是最终做决定的那个人,是金字塔的最高的一级。

在你前面,你安排了10个眼线。分别是老赵,老钱,老孙。。。。。他们每个人也有几十个眼线,分别是大周,大吴,大郑。。。。,他们每个人下面还分别有了大量的眼线,叫做小冯,小陈,小楚。。。。。只有小字辈的,能够接触到第一线的情报。

就跟任何组织一样,每个人都对自己手下的几个小弟有不同的信任程度。这个信任程度我们不妨叫做权重 w (这就是神经网络里面的那个参数)。每个人都把自己的所有的小弟报告的数字,乘以对他的信任程度(权重),相加以后把这个值再报给上一级。

开始训练

于是,这个组织成立的第一天,所有人各就各位,但是信任度还没有建立,所以所有的人都随机的选择到底多信任自己的小弟(有的根据好看程度,有的根据高矮胖廋,有的根据年纪),总之,这个信任度没有任何事实依据。你们约定,你们就想知道一支股票要不要买。如果一个人认为是 0,就是不要买,1 就是买,在这中间的一个数字,就是你要买的建议程度。

这天上午,第一次消息传过来了,经过了小字辈,大字辈,老字辈三层的不靠谱的随机信任度,最终你收到了一个数字,是 0.2 。低于0.5,你决定不买。

当天过去了,结果出来了,股票涨了!

说明正确答案是 1 ,就是该买。好。这个时候你会怎么做?

你会秋后算账。你看一下,刚才让我买的是那几个人?让我不买的又是哪几个?哦。老张当时给我的情报是 0.8,他的贡献是朝向正确的方向的。你就在心里面悄悄地把老张的信任度调高了一点点:下回还得听他的。然后看其他的老钱,老孙,给的是 0.01, 0.1,不靠谱,你把对他们的信任度稍微调低了一点点。这样,你最后一层的连接老字辈的人和你之间的参数就都发生了变动。(就像上面图上的向上和向下箭头)

人间一秒,计算机中1年。我们训练了几分钟,计算机里面已经经过了几十年。这几十年里一起炒股票,最终老张等人在你心中的可信程度已经被训练得炉火纯青了:最终你最信任老张,老张怎么说,你就乘以0.99,直接就巨大的影响你的决策。老钱差了点,但还算靠谱,乘以0.2吧。至于老孙,那是相当的不靠谱,不过你也摸到规律了,只要他让买你就卖,他让卖你就买,一直反着听,倒也效果不错,信任值稳定到了 -0.8

同样的,对于老张,因为他的权重最高,而且结果和正确结果在一个方向上,我们希望,在同样的情况下,老张应该输出的数字再大一点,因为他说话管用。但老张自己是不会输出数字的,他唯一能够调整的,是他对他的小弟们的信任程度。于是,老张干了一件和你一模一样的事情,看看他的小弟们,是谁说要买的,往上调整信任值,说错的,往下调整。这样子他下一层也会做同样操作。一直到最前面的小冯,小陈,小楚。

也是经过了几十年的训练以后,每一层的权重(就是他的上级对他的信任程度)都调得差不多了。这也就是一个训练有素的炒股团队了。每一天,无数的小弟勤勤恳恳的把自己的第一手资料传给他们的上级,而他的直接上级心里面自然有一个算盘,根据过去几十年的经验,信任一些人,不信任一些人,甚至反向信任一些人(他们说涨那是肯定会跌的),如此再往上传,一层一层传递到你这里来,你一看,你得到了0.8,赶紧买!这个准确度是多少呢?一般来说一个搭建完好的神经网络对于认数字这样的问题,做到97%以上问题不大。

总结

现在可以总结一下这个过程。

  1. 开始的时候所有的参数都是随机数

  2. 从第一次训练开始,把数据输入,然后得到一个不靠谱的输出

  3. 把这个输出和正确答案比一比,从最后一层开始把这个误差向前传播,微调一下信任值。

  4. 再进行一次训练,再调整。直到训练完成,所有参数稳定下来。

到此,这一个系列的关于人工智能的八篇文章也暂时告一段落。如下是以前的文章:

我们是怎么认识0的

细节消失,抽象生成

卷积像卷地毯

最终形成判断

就像医生看化验单

边疆战事和烽火台的比喻

血常规检测和医生看病的比喻