zoukankan      html  css  js  c++  java
  • TensorFlow从1到2(三)数据预处理和卷积神经网络

    数据集及预处理

    从这个例子开始,相当比例的代码都来自于官方新版文档的示例。开始的几个还好,但随后的程序都将需要大量的算力支持。Google Colab是一个非常棒的云端实验室,提供含有TPU/GPU支持的Python执行环境(需要在Edit→Notebook Settings设置中打开)。速度比不上配置优良的本地电脑,但至少超过平均的开发环境。
    所以如果你的电脑运行速度不理想,建议你尝试去官方文档中,使用相应代码的对应链接进入Colab执行试一试。
    Colab还允许新建Python笔记,来尝试自己的实验代码。当然这一切的前提,是需要你科学上网。

    上一个例子已经完全使用了TensorFlow 2.0的库来实现。但数据集仍然沿用了TensorFlow 1.x讲解时所使用的样本。
    TensorFlow 2.0默认使用Keras的datasets类来管理数据集,包括Keras内置模型已经训练好的生产数据集,和类似MNIST这种学习项目所用到的练习数据集。
    使用Keras载入数据集同样只是一行代码:

    (train_images, train_labels), (test_images, test_labels) = keras.datasets.mnist.load_data()
    

    Keras.datasets默认是从谷歌网站下载数据集,以MNIST为例,数据下载地址是:https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz。文件下载之后,放置到~/.keras/datasets文件夹,以后执行程序的时候,会自动从本地读取数据。数据保存路径Linux/Mac都是如此,Windows同样是在用户主目录,比如:c:UsersAdministrator.kerasdatasets。

    接着是数据预处理的问题,主要是从原始的图片、标注,转换为机器学习所需要的规范化之后的数据。我们在TensorFlow 1.x中所使用的数据实际是已经规范化之后的。我们可以使用Python3的交互模式,载入数据之后,查看一下数据:

    $ python3
    Python 3.7.3 (default, Mar 27 2019, 09:23:39) 
    [Clang 10.0.0 (clang-1000.11.45.5)] on darwin
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import input_data
    >>> mnist = input_data.read_data_sets("data/", one_hot=True)
    Extracting data/train-images-idx3-ubyte.gz
    Extracting data/train-labels-idx1-ubyte.gz
    Extracting data/t10k-images-idx3-ubyte.gz
    Extracting data/t10k-labels-idx1-ubyte.gz
    >>> mnist.train.images[0]
    array([0.        , 0.        , 0.        , 0.        , 0.        ,
       ...省略部分...
           0.        , 0.        , 0.        , 0.        , 0.        ,
           0.9215687 , 0.9215687 , 0.9215687 , 0.9215687 , 0.9215687 ,
           0.9843138 , 0.9843138 , 0.9725491 , 0.9960785 , 0.9607844 ,
           0.9215687 , 0.74509805, 0.08235294, 0.        , 0.        ,
       ...省略部分...
           0.        , 0.        , 0.        , 0.        , 0.        ,
           0.        , 0.        , 0.        , 0.        ], dtype=float32)
    >>> mnist.train.labels[0]
    array([0., 0., 0., 0., 0., 0., 0., 1., 0., 0.])
    

    对于图,就是28x28的二维数组,其中每一个数据,代表一个点,数据的值越接近1,代表这个点的颜色越接近白色;反之,则颜色越接近黑色。借用原文第四篇中的一幅图来帮你回忆一下这个关系(上一篇中,图片显示部分的代码,功能也是还原这组数据):

    对于标签,因为我们是识别为0-9共10个数字,是10个输出的分类器。所以标签组某一个值为1,表示图像代表的手写数字属于该分类。同样借用一下原图:

    如果想将这样的分类数据转成我们习惯的0-9数字,可以使用TensorFlow中内置的函数argmax:

       ...接着上面的交互模式继续执行...
    >>> import tensorflow as tf
    >>> train_labels = tf.argmax(mnist.train.labels, 1)
    >>> train_labels
    <tf.Tensor: id=2, shape=(55000,), dtype=int64, numpy=array([7, 3, 4, ..., 5, 6, 8])>
    

    现在的数据看起来很习惯了吧?更幸福的是,使用Keras的的分类器模型训练,已经可以直接使用这样的标签数据了。
    keras.datasets.mnist.load_data()所载入的样本数据,跟TensorFlow 1.0所使用的数据有一些区别。其中的图像数据并未做规范化,仍然是通常BMP图像中的0-255的字节数据方式。标签数据,也直接是我们更熟悉的0-9数字标签。这两个微小的变化提现了TensorFlow理念的转变,TensorFlow更贴近真实的工作环境了。
    我们同样在交互模式来看一下这两组数据:

    $ python3
    Python 3.7.3 (default, Mar 27 2019, 09:23:39) 
    [Clang 10.0.0 (clang-1000.11.45.5)] on darwin
    Type "help", "copyright", "credits" or "license" for more information.
    >>> from tensorflow import keras
    >>> (train_images, train_labels), (test_images, test_labels) = keras.datasets.mnist.load_data()
    >>> train_images[0]
    array([[  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,  30,  36,  94, 154, 170,
            253, 253, 253, 253, 253, 225, 172, 253, 242, 195,  64,   0,   0,
              0,   0],
           [  0,   0,   0,   0,   0,   0,   0,  49, 238, 253, 253, 253, 253,
            253, 253, 253, 253, 251,  93,  82,  82,  56,  39,   0,   0,   0,
              0,   0],
           [  0,   0,   0,   0,   0,   0,   0,  18, 219, 253, 253, 253, 253,
            253, 198, 182, 247, 241,   0,   0,   0,   0,   0,   0,   0,   0,
              0,   0],
           [  0,   0,   0,   0,   0,   0,   0,   0,  80, 156, 107, 253, 253,
            205,  11,   0,  43, 154,   0,   0,   0,   0,   0,   0,   0,   0,
              0,   0],
           [  0,   0,   0,   0,   0,   0,   0,   0,   0,  14,   1, 154, 253,
             90,   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]], dtype=uint8)
    >>> train_labels
    array([5, 0, 4, ..., 5, 6, 8], dtype=uint8)
    

    就像从上一个系列中我就一直强调的,TensorFlow的使用越来越容易,成熟的模型越来越多。难度更多的会集中在样本的选取和预处理,所以一定要多关注对原始数据的理解。
    TensorFlow 2.0可以直接处理如上所示的标签数据。图像的数据则仍然需要规范化,图像数据的取值范围我们很清楚是0-255,规范化也很简单:

    # 数据规范化为0-1范围的浮点数
    train_images = train_images / 255.0
    test_images1 = test_images / 255.0
    

    下面看看完整的代码:

    #!/usr/bin/env python3
    
    # tensorflow库
    import tensorflow as tf
    # tensorflow 已经内置了keras
    from tensorflow import keras
    
    # 引入绘图库
    import matplotlib.pyplot as plt
    
    # 第一次使用会自动从网上下载mnist的训练样本
    (train_images, train_labels), (test_images, test_labels) = keras.datasets.mnist.load_data()
    
    
    def plot_image(i, imgs, labels, predictions):
    	# TensorFlow 2.x的数据已经是0-255范围,无需再次还原
        image = imgs[i]
        label = labels[i]
        prediction = tf.argmax(predictions[i])
        plt.grid(False)
        plt.xticks([])
        plt.yticks([])
        # 绘制样本图
        plt.imshow(image)
        # 显示标签值,对比显示预测值和实际标签值
        plt.xlabel("predict:{} label:{}".format(prediction, label))
    
    
    def show_samples(num_rows, num_cols, images, labels, predictions):
        num_images = num_rows*num_cols
        plt.figure('Predict Samples', figsize=(2*num_cols, 2*num_rows))
        # 循环显示前num_rows*num_cols副样本图片
        for i in range(num_images):
            plt.subplot(num_rows, num_cols, i+1)
            plot_image(i, images, labels, predictions)
        plt.show()
    
    # 数据规范化为0-1范围的浮点数
    train_images = train_images / 255.0
    test_images1 = test_images / 255.0
    
    # 定义神经网络模型
    model = keras.Sequential([
        # 输入层把28x28的2维矩阵转换成1维
        keras.layers.Flatten(input_shape=(28, 28)),
        keras.layers.Dense(128, activation='relu'),
        keras.layers.Dense(10, activation='softmax')
    ])
    # 编译模型
    model.compile(optimizer='adam', 
                  loss='sparse_categorical_crossentropy',
                  metrics=['accuracy'])
    # 使用训练集数据训练模型
    model.fit(train_images, train_labels, epochs=5)
    
    # 使用测试集样本验证识别准确率
    test_loss, test_acc = model.evaluate(test_images1, test_labels)
    print('
    Test accuracy:', test_acc)
    
    # 完整预测测试集样本
    predictions = model.predict(test_images1)
    
    # 显示测试样本预测结果
    show_samples(4, 6, test_images, test_labels, predictions)
    

    代码中,图片显示的部分也对应取消了把规范化的数据还原为0-255原始图像数据的过程。其它部分则并没有什么变化。
    现在,MNIST已经是完整的TensorFlow 2.0的原生代码了。绕了这么远,希望能帮你更深刻理解这些代码背后的工作。

    卷积和池化

    在前一个系列中,卷积和池化部分据很多反馈说是一个很严重的门槛。有读者说完全算不清每一层和相连接的层之间的数据关系。
    在TensorFlow 2.0中,就像前面说过的,这种层与层之间的数据维度模型完全是无需自己计算的,Keras会自动匹配这种数据关系。因此单纯从这一点上说,在TensorFlow 2.0中,无论多复杂的模型构建都不会再成为问题。只是会多一点其它的担心,那就是这样隐藏起来机器学习本质上的数学模型,究竟对程序员来说是好事还是坏事?
    TensorFlow 1.x中使用卷积神经网络解决MNIST问题的讲解在前系列第六篇。篇幅很长,这里就不重贴了。在TensorFlow 2.0中,则只是一个函数几行代码(请尽量跟TensorFlow 1.x版本的代码对应着看。对卷积、池化的概念已经忘记的也强烈建议去前系列复习一下):

    # 定义卷积池化神经网络模型
    model = keras.Sequential([
        keras.layers.Conv2D(32, (5, 5), strides=(1, 1),
                            padding='same', activation='relu'),
        keras.layers.MaxPooling2D(pool_size=(2, 2),
                                  strides=(2, 2), padding='same'),
        keras.layers.Conv2D(64, (5, 5), strides=(1, 1),
                            padding='same', activation='relu'),
        keras.layers.MaxPooling2D(pool_size=(2, 2),
                                  strides=(2, 2), padding='same'),
        keras.layers.Flatten(),  # 下面的神经网络需要1维的数据
        keras.layers.Dense(1024, activation='relu'),
        keras.layers.Dropout(0.5),
        keras.layers.Dense(10, activation='softmax')
    ])
    

    Keras让模型构建的过程变得极其容易。
    从原理上说,卷积是对图像的二维数据做扫描,还需要指定图像的色深。所以在样本预处理的阶段,我们还要对其做一个变形:

    # 卷积需要2维数据,还需要指定色深,因此是(样本数,长,宽,色深)
    train_images = train_images.reshape(train_labels.shape[0], 28, 28, 1)
    test_images1 = test_images1.reshape(test_labels.shape[0], 28, 28, 1)
    

    训练集的样本我们直接用变形后的数据替代了原始样本。测试集则另外使用了一个变量保留了原始的测试集,这是因为我们显示测试集图片的时候,使用原始数据集显然更方便。
    实际上整个代码只有这么两点区别,不过为了你练习的时候方便,还是把完整代码贴一遍:

    #!/usr/bin/env python3
    
    # tensorflow库
    import tensorflow as tf
    # tensorflow 已经内置了keras
    from tensorflow import keras
    
    # 引入绘图库
    import matplotlib.pyplot as plt
    
    # 第一次使用会自动从网上下载mnist的训练样本
    (train_images, train_labels), (test_images, test_labels) = keras.datasets.mnist.load_data()
    # 数据路径:~/.keras/datasets/mnist.npz
    
    
    def plot_image(i, imgs, labels, predictions):
        image = imgs[i]
        label = labels[i]
        prediction = tf.argmax(predictions[i])
        plt.grid(False)
        plt.xticks([])
        plt.yticks([])
        # 绘制样本图
        plt.imshow(image)
        # 显示标签值
        plt.xlabel("predict:{} label:{}".format(prediction, label))
    
    
    def show_samples(num_rows, num_cols, images, labels, predictions):
        num_images = num_rows*num_cols
        plt.figure('Predict Samples', figsize=(2*num_cols, 2*num_rows))
        # 循环显示前4*6副训练集样本图片
        for i in range(num_images):
            plt.subplot(num_rows, num_cols, i+1)
            plot_image(i, images, labels, predictions)
        plt.show()
    
    # 数据规范化为0-1范围的浮点数
    train_images = train_images / 255.0
    test_images1 = test_images / 255.0
    
    # 卷积需要2维数据,还需要指定色深,因此是(样本数,长,宽,色深)
    train_images = train_images.reshape(train_labels.shape[0], 28, 28, 1)
    test_images1 = test_images1.reshape(test_labels.shape[0], 28, 28, 1)
    # 定义卷积池化神经网络模型
    model = keras.Sequential([
        keras.layers.Conv2D(32, (5, 5), strides=(1, 1),
                            padding='same', activation='relu'),
        keras.layers.MaxPooling2D(pool_size=(2, 2),
                                  strides=(2, 2), padding='same'),
        keras.layers.Conv2D(64, (5, 5), strides=(1, 1),
                            padding='same', activation='relu'),
        keras.layers.MaxPooling2D(pool_size=(2, 2),
                                  strides=(2, 2), padding='same'),
        keras.layers.Flatten(),  # 下面的神经网络需要1维的数据
        keras.layers.Dense(1024, activation='relu'),
        keras.layers.Dropout(0.5),
        keras.layers.Dense(10, activation='softmax')
    ])
    # 编译模型
    model.compile(optimizer='adam', 
                  loss='sparse_categorical_crossentropy',
                  metrics=['accuracy'])
    # 使用训练集数据训练模型
    model.fit(train_images, train_labels, epochs=3)
    
    # 使用测试集样本验证识别准确率
    test_loss, test_acc = model.evaluate(test_images1, test_labels)
    print('
    Test accuracy:', test_acc)
    
    # 完整预测测试集样本
    predictions = model.predict(test_images1)
    
    # 显示测试样本预测结果
    show_samples(4, 6, test_images, test_labels, predictions)
    
    

    程序执行的时候,在控制台的输出信息类似下面:

    $ chmod +x mnist-conv-maxpool-v2.py
    $ ./mnist-conv-maxpool-v2.py
    Epoch 1/3
    60000/60000 [==============================] - 141s 2ms/sample - loss: 0.1139 - accuracy: 0.9643
    Epoch 2/3
    60000/60000 [==============================] - 143s 2ms/sample - loss: 0.0417 - accuracy: 0.9869
    Epoch 3/3
    60000/60000 [==============================] - 138s 2ms/sample - loss: 0.0312 - accuracy: 0.9904
    10000/10000 [==============================] - 7s 659us/sample - loss: 0.0289 - accuracy: 0.9903
    
    Test accuracy: 0.9903
    

    在样本集的测试上,卷积神经网络的版本可以达到超过99%的正确率。
    这个正确率,只进行了3次的训练迭代,当然因为卷积神经网络模型的复杂,这3次的训练就远远比上一例中的5次训练速度更慢。

    (待续...)

  • 相关阅读:
    Start Developing iOS Apps (Swift) 开始开发iOS应用(Swift)
    ansible copy file
    多个sshkey 指定key来clone仓库
    elastic search 日期为string类型导致视图无法展示时间的解决办法
    ubuntu 安装php 报错解决
    ruby hash 默认值的问题
    11.Mysql 之MHA(高可用)
    10. 主从复制-主从复制(基于GTID)--附加半同步复制
    9 .mysql的备份与恢复
    11.Mysql之回表、最左匹配、索引下推
  • 原文地址:https://www.cnblogs.com/andrewwang/p/10700288.html
Copyright © 2011-2022 走看看