美文网首页iOS 杂谈AIiOS入的那些坑
面向普通开发者的机器学习入门

面向普通开发者的机器学习入门

作者: 狸小华 | 来源:发表于2017-07-20 15:24 被阅读17054次

    前言

    1. 最近在摸索这方面相关的知识,本着整理巩固,分享促进的精神。所以有了这篇博文。
    2. 需要注意的是,本文受众:对机器学习感兴趣,且愿意花点时间学习的应用(业务)程序员
    3. 我本意是尽量简单,易于理解,快速上手,短时间能跑出来东西,这样子才能正向激励我们的学习欲望。
    4. 基于上述条件,需要你已经有一定的开发经验,微不足道的数学能力,以及善用搜索引擎的觉悟。

    开发环境搭建

    首先,我希望你是Linux系用户,如果你是巨硬党,装一个VirtualBox吧,然后再装个ubuntu,由于我们只是入个门,对性能要求不高的。

    机器学习相关的框架也很多,我这里选择了==keras== keras中文文档,后端采用的==tensorflow== 官方网站 。那么理所当然的,会用到python来开发,没有python经验也莫慌,影响并不大。

    1. ubuntu自带python 我就不介绍怎么安装了吧?
      先安装pip(我用的python2.7,后文统一)打开你的终端,输入这个:(我建议更换下apt-get为国内镜像,安装完pip后也更换为国内镜像吧)
    sudo apt-get install python-pip python-dev
    
    1. 安装tensorflow和keras,matplotlib
      还是打开终端,输入输入
    pip install tensorflow
    
    pip install matplotlib
    
    pip install keras
    

    安装完输入 python 然后import测试下

    图1
    你也可以测试下tensorflow,下面是个标准hello world
    import tensorflow as tf
    hello = tf.constant('Hello, TensorFlow!')
    sess = tf.Session()
    print(sess.run(hello))
    

    你看,ubuntu下安装环境这么简单,我不知道你为什么不尝试下

    卷积神经网络CNN浅析

    我建议你先把CNN当作一个黑盒子,不要关心为什么,只关心结果。

    这里借用了一个分辨Xo的例子来这里看原文,就是每次给你一张图,你需要判断它是否含有"X"或者"O"。并且假设必须两者选其一,不是"X"就是"O"。


    下面看一下CNN是怎么分辨输入的图像是x还是o,如果需要你来编程分辨图像是x还是o,你会怎么做?可能你第一时间就想到了逐个像素点对比。但是,如果图片稍微有点变化呢?像是下面这个x,它不是标准的x,我们可以分辨它是x,但是对于计算机而言,它就只是一个二维矩阵,逐个像素点对比肯定是不行的。

    CNN就是用于解决这类问题的,它不在意具体每个点的像素,而是通过一种叫卷积的手段,去提取图片的特征。

    什么是特征? 特征就是我们用于区分两种输入是不是同一类的分辨点,像是这个XXOO的例子,如果要你描述X和O的区别,你会怎么思考?X是两条线交叉,O是封闭的中空的。。。

    我们来看个小小的例子,如果下面两张图,需要分类出喜欢和不喜欢两类,那么你会提取什么作为区分的特征?(手动滑稽)

    卷积层

    所以对于CNN而言,第一步就是提取特征,卷积就是提取猜测特征的神奇手段。而我们不需要指定特征,任凭它自己去猜测,就像上图,我们只需要告诉它,我们喜欢左边的,不喜欢右边的,然后它就去猜测,区分喜不喜欢的特征是黑丝,还是奶子呢?


    假设,我们上面这个例子,CNN对于X的猜测特征如上,现在要通过这些特征来分类。
    计算机对于图像的认知是在矩阵上的,每一张图片有rgb二维矩阵(不考虑透明度)所以,一张图片,应该是3x高度x宽度的矩阵。而我们这个例子就只有黑白,所以可以简单标记1为白,-1为黑。是个9x9的二维矩阵。

    我们把上面的三个特征作为卷积核(我们这里是假设已经训练好了CNN,训练提出的特征就是上面三个,我们可以通过这三个特征去分类 X ),去和输入的图像做卷积(特征的匹配)。




    看完上面的,估计你也能看出特征是如何去匹配输入的,这就是一个卷积的过程,具体的卷积计算过程如下(只展示部分):





    把计算出的结果填入新的矩阵

    其他部分也是相同的计算


    最后,我们整张图用卷积核计算完成后:

    三个特征都计算完成后:


    不断地重复着上述过程,将卷积核(特征)和图中每一块进行卷积操作。最后我们会得到一个新的二维数组。其中的值,越接近为1表示对应位置的匹配度高,越是接近-1,表示对应位置与特征的反面更匹配,而值接近0的表示对应位置没有什么关联。

    以上就是我们的卷积层,通过特征卷积,输出一个新的矩阵给下一层。

    池化层

    在图像经过以上的卷积层后,得到了一个新的矩阵,而矩阵的大小,则取决于卷积核的大小,和边缘的填充方式,总之,在这个XXOO的例子中,我们得到了7x7的矩阵。池化就是缩减图像尺寸和像素关联性的操作,只保留我们感兴趣(对于分类有意义)的信息。

    常用的就是2x2的最大池。




    看完上面的图,你应该知道池化是什么操作了。通常情况下,我们使用的都是2x2的最大池,就是在2x2的范围内,取最大值。因为最大池化(max-pooling)保留了每一个小块内的最大值,所以它相当于保留了这一块最佳的匹配结果(因为值越接近1表示匹配越好)。这也就意味着它不会具体关注窗口内到底是哪一个地方匹配了,而只关注是不是有某个地方匹配上了。这也就能够看出,CNN能够发现图像中是否具有某种特征,而不用在意到底在哪里具有这种特征。这也就能够帮助解决之前提到的计算机逐一像素匹配的死板做法。

    同样的操作以后,我们就输出了3个4x4的矩阵。

    全连接层

    全连接层一般是为了展平数据,输出最终分类结果前的归一化。 我们把上面得到的4x4矩阵再卷积+池化,得到2x2的矩阵


    全连接就是这样子,展开数据,形成1xn的'条'型矩阵。

    然后再把全连接层连接到输出层。之前我们就说过,这里的数值,越接近1表示关联度越大,然后我们根据这些关联度,分辨到底是O还是X.


    看上图(圈圈里面的几个关键信息点),这里有个新的图像丢进我们的CNN了,根据卷积>池化>卷积>池化>全连接的步骤,我们得到了新的全连接数据,然后去跟我们的标准比对,得出相似度,可以看到,相似度是X的为0.92 所以,我们认为这个输入是X。

    一个基本的卷积神经网络就是这样子的。回顾一下,它的结构:

    Relu是常用的激活函数,所做的工作就是max(0,x),就是输入大于零,原样输出,小于零输出零,这里就不展开了。

    CNN实现手写数字识别

    感觉,这个mnist的手写数字,跟其他语言的helloworld一样了。我们这里来简单实现下。首先,我建议你先下载好数据集,keras的下载太慢了下载地址 下载好以后,按下面的位置放,你可能要先运行下程序,让他自己创建文件夹,不然,你就手动创建吧。

    新建个python文件,test.py然后输入下面的内容

    #coding: utf-8
    from keras.datasets import mnist
    import matplotlib.pyplot as plt
    # 加载数据
    (X_train, y_train), (X_test, y_test) = mnist.load_data()
    # 展示下第一张图
    plt.imshow(X_train[0], cmap=plt.get_cmap('PuBuGn_r'))
    plt.show()
    

    运行后出来张图片,然后关掉就行,这里只是看看我们加载数据有没有问题。
    x_train,x_test是我们的图像矩阵数据,是28x28大小,然后有12500条吧好像。然后y_train,y_test都是标签数据,标明这张图代表是数字几。

    #coding: utf-8
    #Simple CNN
    import numpy
    from keras.datasets import mnist
    from keras.models import Sequential
    from keras.layers import Dense
    from keras.layers import Dropout
    from keras.layers import Flatten
    from keras.layers.convolutional import Conv2D
    from keras.layers.convolutional import MaxPooling2D
    from keras.utils import np_utils
    
    seed = 7
    numpy.random.seed(seed)
    
    #加载数据
    (X_train, y_train), (X_test, y_test) = mnist.load_data()
    # reshape to be [samples][channels][width][height]
    X_train = X_train.reshape(X_train.shape[0],28, 28,1).astype('float32')
    X_test = X_test.reshape(X_test.shape[0],28, 28,1).astype('float32')
    
    # normalize inputs from 0-255 to 0-1
    X_train = X_train / 255
    X_test = X_test / 255
    
    # one hot encode outputs
    y_train = np_utils.to_categorical(y_train)
    y_test = np_utils.to_categorical(y_test)
    num_classes = y_test.shape[1]
    
    # 简单的CNN模型
    def baseline_model():
        # create model
        model = Sequential()
        #卷积层
        model.add(Conv2D(32, (3, 3), padding='valid', input_shape=(28, 28,1), activation='relu'))
        #池化层
        model.add(MaxPooling2D(pool_size=(2, 2)))
        #卷积
        model.add(Conv2D(15, (3, 3), padding='valid' ,activation='relu'))
        #池化
        model.add(MaxPooling2D(pool_size=(2, 2)))
        #全连接,然后输出
        model.add(Flatten())
        model.add(Dense(128, activation='relu'))
        model.add(Dense(num_classes, activation='softmax'))
        # Compile model
        model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
        return model
    
    # build the model
    model = baseline_model()
    
    # Fit the model
    model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=10, batch_size=128, verbose=2)
    
    

    代码也挺简单,因为keras也是封装的挺好的了。基本你看懂了前面的就没问题。

    Epoch 1/10
    3s - loss: 0.2791 - acc: 0.9203 - val_loss: 0.1420 - val_acc: 0.9579
    Epoch 2/10
    3s - loss: 0.1122 - acc: 0.9679 - val_loss: 0.0992 - val_acc: 0.9699
    Epoch 3/10
    3s - loss: 0.0724 - acc: 0.9790 - val_loss: 0.0784 - val_acc: 0.9745
    Epoch 4/10
    3s - loss: 0.0509 - acc: 0.9853 - val_loss: 0.0774 - val_acc: 0.9773
    Epoch 5/10
    3s - loss: 0.0366 - acc: 0.9898 - val_loss: 0.0626 - val_acc: 0.9794
    Epoch 6/10
    3s - loss: 0.0265 - acc: 0.9930 - val_loss: 0.0639 - val_acc: 0.9797
    Epoch 7/10
    3s - loss: 0.0185 - acc: 0.9956 - val_loss: 0.0611 - val_acc: 0.9811
    Epoch 8/10
    3s - loss: 0.0150 - acc: 0.9967 - val_loss: 0.0616 - val_acc: 0.9816
    Epoch 9/10
    4s - loss: 0.0107 - acc: 0.9980 - val_loss: 0.0604 - val_acc: 0.9821
    Epoch 10/10
    4s - loss: 0.0073 - acc: 0.9988 - val_loss: 0.0611 - val_acc: 0.9819
    

    然后你就能看到这些输出,acc就是准确率了,看后面的val_acc就行。
    其他的参数那些,我建议你看看keras的文档。然后,入门就结束了。如果你感兴趣的话,就自己去摸索吧,后续我也可能会继续分享相关的内容。

    相关文章

      网友评论

      • 32a102b08060:本人小白一个,有没有什么学习路径啊?
      • 835ca9d36c43:【全连接层一般是为了展平数据,输出最终分类结果前的归一化。 我们把上面得到的4x4矩阵再卷积+池化,得到2x2的矩阵】
        请问一下,池化后已经是一个小数矩阵,如何再进行卷积?以什么为特征?
      • skn_小鱼:当然是奶子
      • 3fcbae90dc48:Conv2D(32, (3, 3)
        filters是32 为什么?
      • 培根炒蛋:牛逼裆朗地。
      • 奇董:units 参数解释为输出维度 正数。。这个参数 有什么标准的选择方法么
      • 萌之迟迟:“然后再把全连接层连接到输出层。之前我们就说过,这里的数值,越接近1表示关联度越大,然后我们根据这些关联度,分辨到底是O还是X.”
        为什么1.00 0.55 0.55 1.00 1.00 0.55 0.55 0.55 0.55 1.00 1.00 0.55变成了
        0.9 0.65 0.45 0.87 0.96 0.73 0.23 0.63 0.44 0.89 0.94 0.53了?请赐教~
      • d7f0679dd7e3:很明显,先是根据要识别的东西取样,这里的例子是取三个3X3的1/0矩阵,然后根据所识别的图像所占据的矩阵像素的特征(这里深色也就是黑色用1表示,未着色的地方也就是白色用0表示)用“卷积”乘法计算出最后的1xn的'条'型矩阵----我们姑且称这是标准的X的“特征矩阵”,而其它所有的要识别的图像则可以根据其取样的3X3的矩阵,与"样本"的“条“型矩阵的1的各位置取出进行平均值对比,值越大则表示就越是符合样本(认为和样本是同样的)。
        个人见解,如有不足,望指正。~
        vinnyxiong::+1: 看了你的回复终于理解这个全连接层了。
      • 0e5b35181f1a:全连接层的4x4矩阵再卷积怎么操作,还是用3*3的去算 ?
        d7f0679dd7e3:如果是多色的图像,如何处理1和0的值的问题呢?是不是此时会根据色深加入小数0~1和0~-1间的取值取参呢?
        d7f0679dd7e3:由每组的2*2得到一个maxmium,就得到了2X2的4x4矩阵的卷积结果
      • VincentTown:卷积时对边缘如何运算的
        d7f0679dd7e3:感觉应该是由像素坐标就可以取到边缘了,不过如果是多色的图像,如何处理1和0的值的问题呢?是不是此时会根据色深加入小数0~1和0~-1间的取值取参呢?
      • 浩林Leon:看了2遍, 感觉看懂了,我说说我的理解,楼主看看我理解的对不对,谢谢了。

        1、对标准图片(标准的 X )进行 卷积>ReLU>池化>全连接 ==>得出 一维数组);
        2、对需要 验证的图片(需要识别的手写 x) 进行 卷积>Relu>池化>全连接 ===》得出 一维数组。
        3、拿需要识别的全连接数据和 标准图片 全连接数组进行比对==》得出相似值。

        这里有个疑问,两个一维数组 相似值 具体怎么计算的?
        LinuxNerd:也有同样的问题,最后的0.92和0.51是如何得出的?
      • 一个老程序员儿:那么现在人工智能这么火,楼主为啥要做android开发?因为兴趣?
      • JoeJiang:不错,收藏了,可以互相关注,我也是技术出身背景的
      • 闫_锋:所以入门之后的重点是在理论指导下调参数还是重新造轮子?谢谢
        闫_锋:@狸小华 兴趣有了就是能力的培养了,我觉得早期规划好坚持下来还是可以做成事情的。谢谢你的分享。
        狸小华:@eyan422 怎么说呢,研究肯定造轮子,应用只需学学调参数。一看兴趣,二看能力。例如我,本身只是个移动端的码农,这些都是出于玩耍的心态,所以我既不感兴趣造轮子也不感兴趣调参数。先认清自己的天花板,然后做自己开心的事。
      • 3ebbfba9f55f:我在慕课网看过一点机器学习的视频,还是很云里雾里的,看了你的图形加代码的讲解,我对机器学习有了基本的概念。
        狸小华:本文只适合入个门,兴趣才是最好的老师,加油吧。
      • 夢的Dead_Body:不错不错,作为入门材写的很好
        狸小华:感谢支持,共同成长:wink:

      本文标题:面向普通开发者的机器学习入门

      本文链接:https://www.haomeiwen.com/subject/trtjkxtx.html