[譯] TensorFlow 教程 #05 - 集成學習

題圖來自 Combining Classifiers
本篇主要介紹神經網絡的集成(ensemble)。
其中有大段以前教程的文字及代碼,若是看過的朋友能夠快速翻到下文 建立神經網絡的集成(ensemble) 部分。php

01 - 簡單線性模型 | 02 - 卷積神經網絡 | 03 - PrettyTensor | 04 - 保存 & 恢復python

by Magnus Erik Hvass Pedersen / GitHub / Videos on YouTube
中文翻譯 thrillerist / Github git

若有轉載,請附上本文連接。github


簡介

這篇教程介紹了卷積神經網絡的集成(ensemble)。咱們使用多個神經網絡,而後取它們輸出的平均,而不是隻用一個。數組

最終也是在MINIST數據集上識別手寫數字。ensemble稍微地提高了測試集上的分類準確率,但差別很小,也多是隨機出現的。此外,ensemble誤分類的一些圖像在單獨網絡上倒是正確分類的。網絡

本文基於上一篇教程,你須要瞭解基本的TensorFlow和附加包Pretty Tensor。其中大量代碼和文字與以前教程類似,若是你已經看過就能夠快速地瀏覽本文。session

流程圖

下面的圖表直接顯示了以後實現的卷積神經網絡中數據的傳遞。網絡有兩個卷積層和兩個全鏈接層,最後一層是用來給輸入圖像分類的。關於網絡和卷積的更多細節描述見教程 #02 。app

本教程實現了5個這樣的神經網絡的集成,每一個網絡的結構相同但權重以及其餘變量不一樣dom

from IPython.display import Image
Image('images/02_network_flowchart.png')複製代碼

導入

%matplotlib inline
import matplotlib.pyplot as plt
import tensorflow as tf
import numpy as np
from sklearn.metrics import confusion_matrix
import time
from datetime import timedelta
import math
import os

# Use PrettyTensor to simplify Neural Network construction.
import prettytensor as pt複製代碼

使用Python3.5.2(Anaconda)開發,TensorFlow版本是:ide

tf.__version__複製代碼

'0.12.0-rc0'

PrettyTensor 版本:

pt.__version__複製代碼

'0.7.1'

載入數據

MNIST數據集大約12MB,若是沒在給定路徑中找到就會自動下載。

from tensorflow.examples.tutorials.mnist import input_data
data = input_data.read_data_sets('data/MNIST/', one_hot=True)複製代碼

Extracting data/MNIST/train-images-idx3-ubyte.gz
Extracting data/MNIST/train-labels-idx1-ubyte.gz
Extracting data/MNIST/t10k-images-idx3-ubyte.gz
Extracting data/MNIST/t10k-labels-idx1-ubyte.gz

如今已經載入了MNIST數據集,它由70,000張圖像和對應的標籤(好比圖像的類別)組成。數據集分紅三份互相獨立的子集,但後面咱們會生成隨機的訓練集。

print("Size of:")
print("- Training-set:\t\t{}".format(len(data.train.labels)))
print("- Test-set:\t\t{}".format(len(data.test.labels)))
print("- Validation-set:\t{}".format(len(data.validation.labels)))複製代碼

Size of:

  • Training-set: 55000
  • Test-set: 10000
  • Validation-set: 5000

類別數字

類型標籤使用One-Hot編碼,這意外每一個標籤是長爲10的向量,除了一個元素以外,其餘的都爲零。這個元素的索引就是類別的數字,即相應圖片中畫的數字。咱們也須要測試集和驗證集的整形類別數字,在這裏計算。

data.test.cls = np.argmax(data.test.labels, axis=1)
data.validation.cls = np.argmax(data.validation.labels, axis=1)複製代碼

建立隨機訓練集的幫助函數

咱們將會在隨機選擇的訓練集上訓練5個不一樣的神經網絡。首先,將原始訓練集和驗證集合併到大的一個數組中。圖像和標籤都要進行此操做。

combined_images = np.concatenate([data.train.images, data.validation.images], axis=0)
combined_labels = np.concatenate([data.train.labels, data.validation.labels], axis=0)複製代碼

檢查合併後的數組大小是否正確。

print(combined_images.shape)
print(combined_labels.shape)複製代碼

(60000, 784)
(60000, 10)

合併數據集的大小。

combined_size = len(combined_images)
combined_size複製代碼

60000

定義每一個神經網絡使用的訓練集的大小。你能夠試着改變大小。

train_size = int(0.8 * combined_size)
train_size複製代碼

48000

在訓練時並無使用驗證集,但它的大小以下。

validation_size = combined_size - train_size
validation_size複製代碼

12000

幫助函數將合併數組集劃分紅隨機的訓練集和驗證集。

def random_training_set():
    # Create a randomized index into the full / combined training-set.
    idx = np.random.permutation(combined_size)

    # Split the random index into training- and validation-sets.
    idx_train = idx[0:train_size]
    idx_validation = idx[train_size:]

    # Select the images and labels for the new training-set.
    x_train = combined_images[idx_train, :]
    y_train = combined_labels[idx_train, :]

    # Select the images and labels for the new validation-set.
    x_validation = combined_images[idx_validation, :]
    y_validation = combined_labels[idx_validation, :]

    # Return the new training- and validation-sets.
    return x_train, y_train, x_validation, y_validation複製代碼

數據維度

在下面的源碼中,有不少地方用到了數據維度。它們只在一個地方定義,所以咱們能夠在代碼中使用這些變量而不是直接寫數字。

# We know that MNIST images are 28 pixels in each dimension.
img_size = 28

# Images are stored in one-dimensional arrays of this length.
img_size_flat = img_size * img_size

# Tuple with height and width of images used to reshape arrays.
img_shape = (img_size, img_size)

# Number of colour channels for the images: 1 channel for gray-scale.
num_channels = 1

# Number of classes, one class for each of 10 digits.
num_classes = 10複製代碼

用來繪製圖片的幫助函數

這個函數用來在3x3的柵格中畫9張圖像,而後在每張圖像下面寫出真實類別和預測類別。

def plot_images(images, # Images to plot, 2-d array. cls_true, # True class-no for images. ensemble_cls_pred=None, # Ensemble predicted class-no. best_cls_pred=None):     # Best-net predicted class-no.

    assert len(images) == len(cls_true)

    # Create figure with 3x3 sub-plots.
    fig, axes = plt.subplots(3, 3)

    # Adjust vertical spacing if we need to print ensemble and best-net.
    if ensemble_cls_pred is None:
        hspace = 0.3
    else:
        hspace = 1.0
    fig.subplots_adjust(hspace=hspace, wspace=0.3)

    # For each of the sub-plots.
    for i, ax in enumerate(axes.flat):

        # There may not be enough images for all sub-plots.
        if i < len(images):
            # Plot image.
            ax.imshow(images[i].reshape(img_shape), cmap='binary')

            # Show true and predicted classes.
            if ensemble_cls_pred is None:
                xlabel = "True: {0}".format(cls_true[i])
            else:
                msg = "True: {0}\nEnsemble: {1}\nBest Net: {2}"
                xlabel = msg.format(cls_true[i],
                                    ensemble_cls_pred[i],
                                    best_cls_pred[i])

            # Show the classes as the label on the x-axis.
            ax.set_xlabel(xlabel)

        # Remove ticks from the plot.
        ax.set_xticks([])
        ax.set_yticks([])

    # Ensure the plot is shown correctly with multiple plots
    # in a single Notebook cell.
    plt.show()複製代碼

繪製幾張圖像來看看數據是否正確

# Get the first images from the test-set.
images = data.test.images[0:9]

# Get the true classes for those images.
cls_true = data.test.cls[0:9]

# Plot the images and labels using our helper-function above.
plot_images(images=images, cls_true=cls_true)複製代碼

TensorFlow圖

TensorFlow的所有目的就是使用一個稱之爲計算圖(computational graph)的東西,它會比直接在Python中進行相同計算量要高效得多。TensorFlow比Numpy更高效,由於TensorFlow瞭解整個須要運行的計算圖,然而Numpy只知道某個時間點上惟一的數學運算。

TensorFlow也可以自動地計算須要優化的變量的梯度,使得模型有更好的表現。這是因爲圖是簡單數學表達式的結合,所以整個圖的梯度能夠用鏈式法則推導出來。

TensorFlow還能利用多核CPU和GPU,Google也爲TensorFlow製造了稱爲TPUs(Tensor Processing Units)的特殊芯片,它比GPU更快。

一個TensorFlow圖由下面幾個部分組成,後面會詳細描述:

  • 佔位符變量(Placeholder)用來改變圖的輸入。
  • 模型變量(Model)將會被優化,使得模型表現得更好。
  • 模型本質上就是一些數學函數,它根據Placeholder和模型的輸入變量來計算一些輸出。
  • 一個cost度量用來指導變量的優化。
  • 一個優化策略會更新模型的變量。

另外,TensorFlow圖也包含了一些調試狀態,好比用TensorBoard打印log數據,本教程不涉及這些。

佔位符 (Placeholder)變量

Placeholder是做爲圖的輸入,咱們每次運行圖的時候均可能改變它們。將這個過程稱爲feeding placeholder變量,後面將會描述這個。

首先咱們爲輸入圖像定義placeholder變量。這讓咱們能夠改變輸入到TensorFlow圖中的圖像。這也是一個張量(tensor),表明一個多維向量或矩陣。數據類型設置爲float32,形狀設爲[None, img_size_flat]None表明tensor可能保存着任意數量的圖像,每張圖象是一個長度爲img_size_flat的向量。

x = tf.placeholder(tf.float32, shape=[None, img_size_flat], name='x')複製代碼

卷積層但願x被編碼爲4維張量,所以咱們須要將它的形狀轉換至[num_images, img_height, img_width, num_channels]。注意img_height == img_width == img_size,若是第一維的大小設爲-1, num_images的大小也會被自動推導出來。轉換運算以下:

x_image = tf.reshape(x, [-1, img_size, img_size, num_channels])複製代碼

接下來咱們爲輸入變量x中的圖像所對應的真實標籤訂義placeholder變量。變量的形狀是[None, num_classes],這表明着它保存了任意數量的標籤,每一個標籤是長度爲num_classes的向量,本例中長度爲10。

y_true = tf.placeholder(tf.float32, shape=[None, 10], name='y_true')複製代碼

咱們也能夠爲class-number提供一個placeholder,但這裏用argmax來計算它。這裏只是TensorFlow中的一些操做,沒有執行什麼運算。

y_true_cls = tf.argmax(y_true, dimension=1)複製代碼

神經網絡

這一節用PrettyTensor實現卷積神經網絡,這要比直接在TensorFlow中實現來得簡單,詳見教程 #03。

基本思想就是用一個Pretty Tensor object封裝輸入張量x_image,它有一個添加新卷積層的幫助函數,以此來建立整個神經網絡。Pretty Tensor負責變量分配等等。

x_pretty = pt.wrap(x_image)複製代碼

如今咱們已經將輸入圖像裝到一個PrettyTensor的object中,再用幾行代碼就能夠添加捲積層和全鏈接層。

注意,在with代碼塊中,pt.defaults_scope(activation_fn=tf.nn.relu)activation_fn=tf.nn.relu看成每一個的層參數,所以這些層都用到了 Rectified Linear Units (ReLU) 。defaults_scope使咱們能更方便地修改全部層的參數。

with pt.defaults_scope(activation_fn=tf.nn.relu):
    y_pred, loss = x_pretty.\
        conv2d(kernel=5, depth=16, name='layer_conv1').\
        max_pool(kernel=2, stride=2).\
        conv2d(kernel=5, depth=36, name='layer_conv2').\
        max_pool(kernel=2, stride=2).\
        flatten().\
        fully_connected(size=128, name='layer_fc1').\
        softmax_classifier(num_classes=num_classes, labels=y_true)複製代碼

優化方法

PrettyTensor給咱們提供了預測類型標籤(y_pred)以及一個須要最小化的損失度量,用來提高神經網絡分類圖片的能力。

PrettyTensor的文檔並無說明它的損失度量是用cross-entropy仍是其餘的。但如今咱們用AdamOptimizer來最小化損失。

優化過程並非在這裏執行。實際上,還沒計算任何東西,咱們只是往TensorFlow圖中添加了優化器,以便後續操做。

optimizer = tf.train.AdamOptimizer(learning_rate=1e-4).minimize(loss)複製代碼

性能度量

咱們須要另一些性能度量,來向用戶展現這個過程。

首先咱們從神經網絡輸出的y_pred中計算出預測的類別,它是一個包含10個元素的向量。類別數字是最大元素的索引。

y_pred_cls = tf.argmax(y_pred, dimension=1)複製代碼

而後建立一個布爾向量,用來告訴咱們每張圖片的真實類別是否與預測類別相同。

correct_prediction = tf.equal(y_pred_cls, y_true_cls)複製代碼

上面的計算先將布爾值向量類型轉換成浮點型向量,這樣子False就變成0,True變成1,而後計算這些值的平均數,以此來計算分類的準確度。

accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))複製代碼

Saver

爲了保存神經網絡的變量,咱們建立一個稱爲Saver-object的對象,它用來保存及恢復TensorFlow圖的全部變量。在這裏並未保存什麼東西,(保存操做)在後面的optimize()函數中完成。

注意,若是在ensemble中有超過100個的神經網絡,你須要根據狀況來增長max_to_keep

saver = tf.train.Saver(max_to_keep=100)複製代碼

這是用來保存或恢復數據的文件夾。

save_dir = 'checkpoints/'複製代碼

若是文件夾不存在則建立。

if not os.path.exists(save_dir):
    os.makedirs(save_dir)複製代碼

這個函數根據輸入的網絡編號返回數據文件的保存路徑。

def get_save_path(net_number):
    return save_dir + 'network' + str(net_number)複製代碼

運行TensorFlow

建立TensorFlow會話(session)

一旦建立了TensorFlow圖,咱們須要建立一個TensorFlow會話,用來運行圖。

session = tf.Session()複製代碼

初始化變量

變量weightsbiases在優化以前須要先進行初始化。咱們寫一個簡單的封裝函數,後面會再次調用。

def init_variables():
    session.run(tf.initialize_all_variables())複製代碼

建立隨機訓練batch的幫助函數

在訓練集中有上千張圖。用這些圖像計算模型的梯度會花不少時間。所以,它在優化器的每次迭代裏只用到了一小部分的圖像。

若是內存耗盡致使電腦死機或變得很慢,你應該試着減小這些數量,但同時可能還須要更優化的迭代。

train_batch_size = 64複製代碼

函數根據給定的大小挑選一個隨機的training-batch。

def random_batch(x_train, y_train):
    # Total number of images in the training-set.
    num_images = len(x_train)

    # Create a random index into the training-set.
    idx = np.random.choice(num_images,
                           size=train_batch_size,
                           replace=False)

    # Use the random index to select random images and labels.
    x_batch = x_train[idx, :]  # Images.
    y_batch = y_train[idx, :]  # Labels.

    # Return the batch.
    return x_batch, y_batch複製代碼

執行優化迭代的幫助函數

函數用來執行必定數量的優化迭代,以此來逐漸改善網絡層的變量。在每次迭代中,會從訓練集中選擇新的一批數據,而後TensorFlow在這些訓練樣本上執行優化。每100次迭代會打印出(信息)。

def optimize(num_iterations, x_train, y_train):
    # Start-time used for printing time-usage below.
    start_time = time.time()

    for i in range(num_iterations):

        # Get a batch of training examples.
        # x_batch now holds a batch of images and
        # y_true_batch are the true labels for those images.
        x_batch, y_true_batch = random_batch(x_train, y_train)

        # Put the batch into a dict with the proper names
        # for placeholder variables in the TensorFlow graph.
        feed_dict_train = {x: x_batch,
                           y_true: y_true_batch}

        # Run the optimizer using this batch of training data.
        # TensorFlow assigns the variables in feed_dict_train
        # to the placeholder variables and then runs the optimizer.
        session.run(optimizer, feed_dict=feed_dict_train)

        # Print status every 100 iterations and after last iteration.
        if i % 100 == 0:

            # Calculate the accuracy on the training-batch.
            acc = session.run(accuracy, feed_dict=feed_dict_train)

            # Status-message for printing.
            msg = "Optimization Iteration: {0:>6}, Training Batch Accuracy: {1:>6.1%}"

            # Print it.
            print(msg.format(i + 1, acc))

    # Ending time.
    end_time = time.time()

    # Difference between start and end-times.
    time_dif = end_time - start_time

    # Print the time-usage.
    print("Time usage: " + str(timedelta(seconds=int(round(time_dif)))))複製代碼

建立神經網絡的集成(ensemble)

神經網絡ensemble的數量

num_networks = 5複製代碼

每一個神經網絡優化迭代的次數。

num_iterations = 10000複製代碼

建立神經網絡的ensemble。全部網絡都使用上面定義的那個TensorFlow圖。每一個網絡的TensorFlow權重和變量都用隨機值初始化,而後進行優化。接着將變量保存到磁盤中以便以後重載使用。

若是你只是想從新運行Notebook來對結果進行不一樣的分析,能夠跳過這一步。

if True:
    # For each of the neural networks.
    for i in range(num_networks):
        print("Neural network: {0}".format(i))

        # Create a random training-set. Ignore the validation-set.
        x_train, y_train, _, _ = random_training_set()

        # Initialize the variables of the TensorFlow graph.
        session.run(tf.global_variables_initializer())

        # Optimize the variables using this training-set.
        optimize(num_iterations=num_iterations,
                 x_train=x_train,
                 y_train=y_train)

        # Save the optimized variables to disk.
        saver.save(sess=session, save_path=get_save_path(i))

        # Print newline.
        print()複製代碼

Neural network: 0
Optimization Iteration: 1, Training Batch Accuracy: 6.2%
Optimization Iteration: 101, Training Batch Accuracy: 87.5%
...
Optimization Iteration: 9901, Training Batch Accuracy: 100.0%
Time usage: 0:00:40

Neural network: 1
Optimization Iteration: 1, Training Batch Accuracy: 7.8%
Optimization Iteration: 101, Training Batch Accuracy: 85.9%
...
Optimization Iteration: 9901, Training Batch Accuracy: 98.4%
Time usage: 0:00:40

Neural network: 2
Optimization Iteration: 1, Training Batch Accuracy: 3.1%
Optimization Iteration: 101, Training Batch Accuracy: 84.4%
...
Optimization Iteration: 9901, Training Batch Accuracy: 100.0%
Time usage: 0:00:39

Neural network: 3
Optimization Iteration: 1, Training Batch Accuracy: 9.4%
Optimization Iteration: 101, Training Batch Accuracy: 89.1%
...
Optimization Iteration: 9901, Training Batch Accuracy: 100.0%
Time usage: 0:00:39

Neural network: 4
Optimization Iteration: 1, Training Batch Accuracy: 9.4%
Optimization Iteration: 101, Training Batch Accuracy: 82.8%
...
Optimization Iteration: 9901, Training Batch Accuracy: 98.4%
Time usage: 0:00:39

計算而且預測分類的幫助函數

這個函數計算了圖像的預測標籤,對每張圖像來講,函數計算了一個長度爲10的向量,向量顯示了圖像的類別。

計算分批完成,不然將佔用太多內存。若是電腦死機了,你須要下降batch-size。

# Split the data-set in batches of this size to limit RAM usage.
batch_size = 256

def predict_labels(images):
    # Number of images.
    num_images = len(images)

    # Allocate an array for the predicted labels which
    # will be calculated in batches and filled into this array.
    pred_labels = np.zeros(shape=(num_images, num_classes),
                           dtype=np.float)

    # Now calculate the predicted labels for the batches.
    # We will just iterate through all the batches.
    # There might be a more clever and Pythonic way of doing this.

    # The starting index for the next batch is denoted i.
    i = 0

    while i < num_images:
        # The ending index for the next batch is denoted j.
        j = min(i + batch_size, num_images)

        # Create a feed-dict with the images between index i and j.
        feed_dict = {x: images[i:j, :]}

        # Calculate the predicted labels using TensorFlow.
        pred_labels[i:j] = session.run(y_pred, feed_dict=feed_dict)

        # Set the start-index for the next batch to the
        # end-index of the current batch.
        i = j

    return pred_labels複製代碼

計算一個布爾值向量,表明圖像的預測類型是否正確。

def correct_prediction(images, labels, cls_true):
    # Calculate the predicted labels.
    pred_labels = predict_labels(images=images)

    # Calculate the predicted class-number for each image.
    cls_pred = np.argmax(pred_labels, axis=1)

    # Create a boolean array whether each image is correctly classified.
    correct = (cls_true == cls_pred)

    return correct複製代碼

計算一個布爾數組,表明測試集中圖像是否分類正確。

def test_correct():
    return correct_prediction(images = data.test.images,
                              labels = data.test.labels,
                              cls_true = data.test.cls)複製代碼

計算一個布爾數組,表明驗證集中圖像是否分類正確。

def validation_correct():
    return correct_prediction(images = data.validation.images,
                              labels = data.validation.labels,
                              cls_true = data.validation.cls)複製代碼

計算分類準確率的幫助函數

這個函數計算了給定布爾數組的分類準確率,布爾數組表示每張圖像是否被正確分類。好比, cls_accuracy([True, True, False, False, False]) = 2/5 = 0.4

def classification_accuracy(correct):
    # When averaging a boolean array, False means 0 and True means 1.
    # So we are calculating: number of True / len(correct) which is
    # the same as the classification accuracy.
    return correct.mean()複製代碼

計算測試集的分類準確率。

def test_accuracy():
    # Get the array of booleans whether the classifications are correct
    # for the test-set.
    correct = test_correct()

    # Calculate the classification accuracy and return it.
    return classification_accuracy(correct)複製代碼

計算原始驗證集上的分類準確率。

def validation_accuracy():
    # Get the array of booleans whether the classifications are correct
    # for the validation-set.
    correct = validation_correct()

    # Calculate the classification accuracy and return it.
    return classification_accuracy(correct)複製代碼

結果與分析

函數用來爲ensemble中的全部神經網絡計算預測標籤。後面會將這些標籤合併起來。

def ensemble_predictions():
    # Empty list of predicted labels for each of the neural networks.
    pred_labels = []

    # Classification accuracy on the test-set for each network.
    test_accuracies = []

    # Classification accuracy on the validation-set for each network.
    val_accuracies = []

    # For each neural network in the ensemble.
    for i in range(num_networks):
        # Reload the variables into the TensorFlow graph.
        saver.restore(sess=session, save_path=get_save_path(i))

        # Calculate the classification accuracy on the test-set.
        test_acc = test_accuracy()

        # Append the classification accuracy to the list.
        test_accuracies.append(test_acc)

        # Calculate the classification accuracy on the validation-set.
        val_acc = validation_accuracy()

        # Append the classification accuracy to the list.
        val_accuracies.append(val_acc)

        # Print status message.
        msg = "Network: {0}, Accuracy on Validation-Set: {1:.4f}, Test-Set: {2:.4f}"
        print(msg.format(i, val_acc, test_acc))

        # Calculate the predicted labels for the images in the test-set.
        # This is already calculated in test_accuracy() above but
        # it is re-calculated here to keep the code a bit simpler.
        pred = predict_labels(images=data.test.images)

        # Append the predicted labels to the list.
        pred_labels.append(pred)

    return np.array(pred_labels), \
           np.array(test_accuracies), \
           np.array(val_accuracies)複製代碼
pred_labels, test_accuracies, val_accuracies = ensemble_predictions()複製代碼

Network: 0, Accuracy on Validation-Set: 0.9948, Test-Set: 0.9893
Network: 1, Accuracy on Validation-Set: 0.9936, Test-Set: 0.9880
Network: 2, Accuracy on Validation-Set: 0.9958, Test-Set: 0.9893
Network: 3, Accuracy on Validation-Set: 0.9938, Test-Set: 0.9889
Network: 4, Accuracy on Validation-Set: 0.9938, Test-Set: 0.9892

總結ensemble中的神經網絡在測試集上的分類準確率。

print("Mean test-set accuracy: {0:.4f}".format(np.mean(test_accuracies)))
print("Min test-set accuracy: {0:.4f}".format(np.min(test_accuracies)))
print("Max test-set accuracy: {0:.4f}".format(np.max(test_accuracies)))複製代碼

Mean test-set accuracy: 0.9889
Min test-set accuracy: 0.9880
Max test-set accuracy: 0.9893

ensemble的預測標籤是3維的數組,第一維是神經網絡數量,第二維是圖像數量,第三維是分類向量。

pred_labels.shape複製代碼

(5, 10000, 10)

ensemble預測

有幾種不一樣的方法來計算ensemble的預測標籤。一種是計算每一個神經網絡的預測類別數字,而後選擇得票最多的那個類別。但根據分類的類別數量,這種方法須要大量的神經網絡。

這裏用的方法是取ensemble中全部預測標籤的平均。這個計算很簡單,並且集成種不須要大量的神經網絡。

ensemble_pred_labels = np.mean(pred_labels, axis=0)
ensemble_pred_labels.shape複製代碼

(10000, 10)

取標籤中最大數字的索引做爲ensemble的預測類別數字,這一般用argmax來計算。

ensemble_cls_pred = np.argmax(ensemble_pred_labels, axis=1)
ensemble_cls_pred.shape複製代碼

(10000,)

布爾數組表示測試集中的圖像是否被神經網絡的ensemble正確分類。

ensemble_correct = (ensemble_cls_pred == data.test.cls)複製代碼

對布爾數組取反,所以咱們能夠用它來查找誤分類的圖像。

ensemble_incorrect = np.logical_not(ensemble_correct)複製代碼

最佳的神經網絡

如今咱們找出在測試集上表現最佳的單個神經網絡。

首先列出ensemble中全部神經網絡在測試集上的分類準確率。

test_accuracies複製代碼

array([ 0.9893, 0.988 , 0.9893, 0.9889, 0.9892])

準確率最高的神經網絡索引。

best_net = np.argmax(test_accuracies)
best_net複製代碼

0

最佳神經網絡在測試集上的分類準確率。

test_accuracies[best_net]複製代碼

0.98929999999999996

最佳神經網絡的預測標籤。

best_net_pred_labels = pred_labels[best_net, :, :]複製代碼

預測的類別數字。

best_net_cls_pred = np.argmax(best_net_pred_labels, axis=1)複製代碼

最佳神經網絡在測試集上是否正確分類圖像的布爾數組。

best_net_correct = (best_net_cls_pred == data.test.cls)複製代碼

圖像是否被誤分類的布爾數組。

best_net_incorrect = np.logical_not(best_net_correct)複製代碼

ensemble與最佳網絡的比較

測試集中被ensemble正確分類的圖像數量。

np.sum(ensemble_correct)複製代碼

9916

測試集中被最佳網絡正確分類的圖像數量。

np.sum(best_net_correct)複製代碼

9893

布爾數組表示測試集中每張圖像是否「被ensemble正確分類且被最佳網絡誤分類」。

ensemble_better = np.logical_and(best_net_incorrect,
                                 ensemble_correct)複製代碼

測試集上ensemble比最佳網絡表現更好的圖像數量:

ensemble_better.sum()複製代碼

39

布爾數組表示測試集中每張圖像是否「被最佳網絡正確分類且被ensemble誤分類」。

best_net_better = np.logical_and(best_net_correct,
                                 ensemble_incorrect)複製代碼

測試集上最佳網絡比ensemble表現更好的圖像數量:

best_net_better.sum()複製代碼

16

繪製以及打印對比的幫助函數

函數用來繪製測試集中的圖像,以及它們的真實類別與預測類別。

def plot_images_comparison(idx):
    plot_images(images=data.test.images[idx, :],
                cls_true=data.test.cls[idx],
                ensemble_cls_pred=ensemble_cls_pred[idx],
                best_cls_pred=best_net_cls_pred[idx])複製代碼

打印預測標籤的函數。

def print_labels(labels, idx, num=1):
    # Select the relevant labels based on idx.
    labels = labels[idx, :]

    # Select the first num labels.
    labels = labels[0:num, :]

    # Round numbers to 2 decimal points so they are easier to read.
    labels_rounded = np.round(labels, 2)

    # Print the rounded labels.
    print(labels_rounded)複製代碼

打印神經網絡ensemble預測標籤的函數。

def print_labels_ensemble(idx, **kwargs):
    print_labels(labels=ensemble_pred_labels, idx=idx, **kwargs)複製代碼

打印單個網絡預測標籤的函數。

def print_labels_best_net(idx, **kwargs):
    print_labels(labels=best_net_pred_labels, idx=idx, **kwargs)複製代碼

打印ensemble中全部神經網絡預測標籤的函數。只打印第一張圖像的標籤。

def print_labels_all_nets(idx):
    for i in range(num_networks):
        print_labels(labels=pred_labels[i, :, :], idx=idx, num=1)複製代碼

樣本:ensemble比最佳網絡好

繪製出那些被集成網絡正確分類,且被最佳網絡誤分類的樣本。

plot_images_comparison(idx=ensemble_better)複製代碼

ensemble對第一張圖像(左上)的預測標籤:

print_labels_ensemble(idx=ensemble_better, num=1)複製代碼

[[ 0. 0. 0. 0.76 0. 0. 0. 0. 0.23 0. ]]

最佳網絡對第一張圖像的預測標籤:

print_labels_best_net(idx=ensemble_better, num=1)複製代碼

[[ 0. 0. 0. 0.21 0. 0. 0. 0. 0.79 0. ]]

ensemble中全部網絡對第一張圖像的預測標籤:

print_labels_all_nets(idx=ensemble_better)複製代碼

[[ 0. 0. 0. 0.21 0. 0. 0. 0. 0.79 0. ]]
[[ 0. 0. 0. 0.96 0. 0.01 0. 0. 0.03 0. ]]
[[ 0. 0. 0. 0.99 0. 0. 0. 0. 0.01 0. ]]
[[ 0. 0. 0. 0.88 0. 0. 0. 0. 0.12 0. ]]
[[ 0. 0. 0. 0.76 0. 0.01 0. 0. 0.22 0. ]]

樣本:最佳網絡比ensemble好

如今繪製那些被ensemble誤分類,但被最佳網絡正確分類的樣本。

plot_images_comparison(idx=best_net_better)複製代碼

ensemble對第一張圖像(左上)的預測標籤:

print_labels_ensemble(idx=best_net_better, num=1)複製代碼

[[ 0.5 0. 0. 0. 0. 0.05 0.45 0. 0. 0. ]]

最佳網絡對第一張圖像的預測標籤:

print_labels_best_net(idx=best_net_better, num=1)複製代碼

[[ 0.3 0. 0. 0. 0. 0.15 0.56 0. 0. 0. ]]

ensemble中全部網絡對第一張圖像的預測標籤:

print_labels_all_nets(idx=best_net_better)複製代碼

[[ 0.3 0. 0. 0. 0. 0.15 0.56 0. 0. 0. ]]
[[ 1. 0. 0. 0. 0. 0. 0. 0. 0. 0.]]
[[ 0.19 0. 0. 0. 0. 0. 0.81 0. 0. 0. ]]
[[ 0.15 0. 0. 0. 0. 0.12 0.72 0. 0. 0. ]]
[[ 0.85 0. 0. 0. 0. 0. 0.14 0. 0. 0. ]]

關閉TensorFlow會話

如今咱們已經用TensorFlow完成了任務,關閉session,釋放資源。

# This has been commented out in case you want to modify and experiment
# with the Notebook without having to restart it.
# session.close()複製代碼

總結

這篇教程建立了5個神經網絡的集成(ensemble),用來識別MINIST數據集中的手寫數字。ensemble取5個單獨神經網絡的平均值。最終稍微提升了在測試集上的分類準確率,相比單個最佳網絡98.9%的準確率,ensemble是99.1%。

然而,ensemble的表現並非一直都比單個網絡好,有些單個網絡正確分類的圖像卻被ensemble誤分類。這代表神經網絡ensemble的做用有點隨機,可能沒法提供一個提高性能的可靠方式(和單獨神經網絡性能相比)。

這裏使用的集成學習的形式叫bagging (或 Bootstrap Aggregating),它經常使用來避免過擬合,但對(本文中的)這個特定的神經網絡和數據集來講不是必要的。在其餘狀況下集成學習可能仍然有效。

技術說明

本文在實現集成學習時用了TensorFlow中Saver()對象來保存和恢復神經網絡中的變量。但這個功能實際上是爲其餘目的設計的,使用在有多種類型神經網絡的集成學習中,或者想同時載入多個神經網絡時就有點笨拙了。有一個叫 sk-flow 的TensorFlow添加包有更簡單的方法,但到2016年八月爲止,它仍然處於開發的前期階段。

練習

下面是一些可能會讓你提高TensorFlow技能的一些建議練習。爲了學習如何更合適地使用TensorFlow,實踐經驗是很重要的。

在你對這個Notebook進行修改以前,可能須要先備份一下。

  • 改變程序的幾個不一樣地方,看看它如何影響性能:
    • 在集成中使用更多神經網絡。
    • 改變訓練集的大小。
    • 改變優化迭代的次數,試着增長或減小。
  • 向朋友解釋程序如何工做。
  • 你認爲集成學習值得更多的研究嗎,或者寧肯專一於提高單個神經網絡的性能?
相關文章
相關標籤/搜索