我教宝宝学AI (五)挖坑中成长

一、不断挖坑

学习的目标在哪里,如何看到技术未来的发展方向并选择?随着对新技术的深入理解,会根据心里的标准去评判每一个已经做出的选择,并警醒下一个新的选择。在教的过程中,女儿说我是在不断地“挖坑”,我要说形容的很贴切。暑假时间已经过去差不多一个月,在教学内容选择上我形成了一些思路:

1)开源是未来的方向,也是极好的资源,学会使用开源社区大众化的工具如git是必须的;

2)由于女儿的数学知识未达到一定程度,无法掌握深度学习的所有数学原理,但是要求她去理解深度学习的一些基本算法和整体的思路。通过keras做一个识别手写数字的简单卷积神经网络模型(LeNet)加深认识AI,backend采用tensorflow;

3)苹果操作系统是认证的UNIX,与开源技术自然结合,抛弃用Visual Studio学习的想法,而是改为学习在Xcode上用swift语言编程。学习苹果新推出的Core ML,以苹果app的模式在iPad Pro上开发一个识别手写数字的程序。另外ARKit也是苹果在iOS11上新推出的好东西,只可惜这次没有足够的时间去琢磨。但是我想只要教会了她学习方法,在有空的时候完全可以自己去琢磨。


编程语言从本质上说都差不多,Borland公司在1995年推出Delphi 1.0,曾经轰动全世界,因为是第一套真正面向对象的所见即所得快速开发工具。把Object Pascal和苹果的swift比较后,会发现两个语言很像:变量申明方式、类的引用等,但是由于苹果具有比Borland无法比拟的优势,就是iOS和MacOS操作系统是自家的产品,因此在swift上独特增加一些特性,使得完成的程序可以非常贴切地和苹果操作系统融合在一起,可以说swift就是为苹果操作系统专门设计的开发工具。在教女儿的同时,我也感觉自己在同步成长,在深入了解swift语言后,心里开始纠结以后是不是客户端也要完全脱离windows搬到MacOS上呢?


在这里要特别宣传下Stanford的CS193P课程:Developing Apps for iOS。白胡子教授Paul Hegarty的教学深入浅出,教会学生的每一个小技巧,课程内容非常棒。总共17课,和女儿一起学习到了第9课。Paul教授的经典手势:

我教宝宝学AI (五)挖坑中成长


二、卷积神经网络模型(CNN)

卷积神经网络模型的英文名称叫Convolutional Neural Network。


1)什么是卷积操作?

下面的动画图片形象的说明了卷积操作过程。如下图原始的绿色图像size为5*5,卷积的kernel是黄色的3*3矩阵[(1,0,1),(0,1,0),(1,0,1)],通过每次移动黄色kernel一格,也就是step为1,每次使用绿色相应像素值和矩阵相应位数相乘之和能获得一个数值,按顺序放在右边粉色图像中,则形成粉色图像就是经过卷积操作后的结果,也叫feature map。

我教宝宝学AI (五)挖坑中成长


通过filter取值的不同,经过卷积操作可以提取原图片的指定特征,形成新的feature map。


2)什么是pooling操作?

如果输入图片size为28*28,经过kernel size为3*3、step为1的卷积操作后的feature map size为26*26。为了进一步压缩图片向量数量,一个很自然的想法就是对不同位置的特征进行聚合统计,比如可以计算图像每个2*2区域上的最大值来代表这个区域的特征。这个操作就是pooling操作,如对26*26的图片取值pool size 2*2,则可以缩小图片为13*13。


3)LeNet卷积神经网络模型

LeCun教授在上世纪90年代发明LeNet卷积神经网络模型,该模型被成功应用于银行的支票识别系统。


下图为LeNet模型,由两个convolution层、两个pooling层和两个full connection层组成。


我教宝宝学AI (五)挖坑中成长


三、用Keras实现手写数字识别(LeNet实现)

源码可以用git获取:https://github.com/jiayulinfs/mnist-keras。


# 以下代码可以直接在python 3.6.1种执行

from __future__ import print_function

import keras

from keras.datasets import mnist

from keras.models import Sequential

from keras.layers import Dense, Dropout, Flatten

from keras.layers import Conv2D, MaxPooling2D

from keras import backend as K


batch_size = 128

num_classes = 10

epochs = 12


# 输入图片格式 28*28

img_rows, img_cols = 28, 28


# 加载mnist图片数据

(x_train, y_train), (x_test, y_test) = mnist.load_data()


if K.image_data_format() == 'channels_first':

    x_train = x_train.reshape(x_train.shape[0], 1, img_rows, img_cols)

    x_test = x_test.reshape(x_test.shape[0], 1, img_rows, img_cols)

    input_shape = (1, img_rows, img_cols)

else:

    x_train = x_train.reshape(x_train.shape[0], img_rows, img_cols, 1)

    x_test = x_test.reshape(x_test.shape[0], img_rows, img_cols, 1)

    input_shape = (img_rows, img_cols, 1)


x_train = x_train.astype('float32')

x_test = x_test.astype('float32')

x_train /= 255

x_test /= 255

print('x_train shape:', x_train.shape)

print(x_train.shape[0], 'train samples')

print(x_test.shape[0], 'test samples')


# 划分训练数据和测试数据

y_train = keras.utils.to_categorical(y_train, num_classes)

y_test = keras.utils.to_categorical(y_test, num_classes)


model = Sequential()

# 增加第一个convolution层,32个filter,kernel size 3*3

model.add(Conv2D(32, kernel_size=(3, 3),

                 activation='relu',

                 input_shape=input_shape))

# 增加pooling层

model.add(MaxPooling2D(pool_size=(2, 2)))


# 增加第一个convolution层,64个filter,kernel size 3*3

model.add(Conv2D(64, (3, 3), activation='relu'))


# 增加pooling层

model.add(MaxPooling2D(pool_size=(2, 2)))


# 增加full connection层

model.add(Flatten())

model.add(Dense(500, activation='relu'))


# 增加full connection层,结果是10个数字分类

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


model.compile(loss=keras.losses.categorical_crossentropy,

              optimizer=keras.optimizers.Adadelta(),

              metrics=['accuracy'])


model.fit(x_train, y_train,

          batch_size=batch_size,

          epochs=epochs,

          verbose=1,

          validation_data=(x_test, y_test))

score = model.evaluate(x_test, y_test, verbose=0)

print('Test loss:', score[0])

print('Test accuracy:', score[1])

model.save('mnist-keras.h5')


训练过程:

我教宝宝学AI (五)挖坑中成长


通过保存的模型文件mnist-keras.h5,测试图片:

import numpy as np

from keras.datasets import mnist

import pandas as pd

from keras.optimizers import Adam

from PIL import Image

from keras import backend as K

from keras.models import load_model


img_rows, img_cols = 28, 28

(x_train, y_train), (x_test, y_test) = mnist.load_data()

x_train = x_train.reshape(x_train.shape[0], -1) / 255.

x_test = x_test.reshape(x_test.shape[0], -1) / 255.

if K.image_data_format() == 'channels_first':

    x_train = x_train.reshape(x_train.shape[0], 1, 28, 28)

    x_test = x_test.reshape(x_test.shape[0], 1, img_rows, img_cols)

    input_shape = (1, img_rows, img_cols)

else:

    x_train = x_train.reshape(x_train.shape[0], img_rows, img_cols, 1)

    x_test = x_test.reshape(x_test.shape[0], img_rows, img_cols, 1)

    input_shape = (img_rows, img_cols, 1)


print(K.image_data_format())

model=load_model('mnist-keras.h5')


fname = '8.png'

image = Image.open(fname).convert("L")

image = image.resize((28,28),Image.ANTIALIAS)

arr = np.asarray(image)

print(arr.shape)

arr=1-arr/255

arr=np.reshape(arr,(1,28,28,1))

print(arr.shape)


out1 = model.predict_classes(arr)

print("print after load:", out1)


四、总述

2012年的ImageNet图像分类竞赛中采用改进LeNet后的AlexNet模型(以第一作者Alex命名),获得比赛第一名,并且最终成绩把第二名远远甩开。后续年份Google和Microsoft的连续改进提出的CNN模型也分别获得第一名,并且现在计算机的图片分类识别能力已经超过了人类的眼睛。


在这里讲一个AlexNet的微小改进,通过增加dropout层,就是人为的随机暂时丢弃部分神经元,这样可以通过增加整体的训练次数,又防止过拟合。仔细思考下这个小改动,是不是这个想法就是人类学习方式的一种仿生?我觉得从小学习就有这种经验,遇到一个不懂的不要着急,先放在一边,然后第二次看的时候,说不定就自动理解啦。


人类本身就是一个神奇,虽然还不知道大脑是怎么工作的,但是只要模仿它,就会有很多神奇的发现。不管它是什么,它的道理就存在那儿,人工智能也会这样去书写神奇的未来。