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次训练速度更慢。

    (待续...)

  • 相关阅读:
    HDU 5115 Dire Wolf (区间DP)
    HDU 4283 You Are the One(区间DP(最优出栈顺序))
    ZOJ 3469 Food Delivery(区间DP好题)
    LightOJ 1422 Halloween Costumes(区间DP)
    POJ 1651 Multiplication Puzzle(区间DP)
    NYOJ 石子合并(一)(区间DP)
    POJ 2955 Brackets(括号匹配一)
    POJ 1141 Brackets Sequence(括号匹配二)
    ZOJ 3537 Cake(凸包+区间DP)
    Graham求凸包模板
  • 原文地址:https://www.cnblogs.com/andrewwang/p/10700288.html
Copyright © 2011-2022 走看看