Теплое напоминание, TensorFlow обновляется слишком быстро, некоторые методы реализации кода могли измениться, но мышление не изменилось, главное понятьисходный адрес
вводить
Предыдущий учебник показал, что простая линейная модель имеет точность классификации около 91% для распознавания рукописных цифр в наборе данных MNIST.
В этом руководстве мы реализуем простую сверточную нейронную сеть в TensorFlow с точностью классификации около 99% или лучше, если вы выполните некоторые из предложенных упражнений.
Сверточные сети работают, перемещая небольшие фильтры по входному изображению. Это означает, что фильтр повторно используется для выявления шаблонов во всем входном изображении. Это делает сверточные сети более мощными, чем полносвязные сети с тем же количеством переменных. Это, в свою очередь, позволяет Convolutional Networks обучаться быстрее.
Вы должны быть знакомы с основами линейной алгебры, Python и редактором Jupyter Notebook. Новички в Tensorflow также могут захотеть изучить первое руководство, прежде чем продолжить обучение.
FlowchartНа приведенной ниже диаграмме примерно показано, как данные передаются в сверточной нейронной сети, реализованной ниже.
Входное изображение обрабатывается в первом сверточном слое с использованием весов фильтров. В результате получается 16 новых изображений, по одному для каждого фильтра в сверточном слое. Изображение также подвергается даунсемплингу, поэтому разрешение изображения уменьшается с 28x28 до 14x14.Эти 16 меньших изображений затем обрабатываются на втором сверточном слое. Нам нужно предоставить веса фильтров для каждого из этих 16 каналов, и нам нужно использовать веса фильтров для каждого выходного канала этого слоя. Имеется 36 выходных каналов, поэтому всего 16 x 36 = 576 фильтров во втором сверточном слое. Результирующее изображение снова уменьшается до размера 7x7 пикселей.
На выходе второго сверточного слоя получается 36 изображений размером 7×7 пикселей каждое. Затем они объединяются в единый вектор длиной 7 × 7 × 36 = 1764, который используется в качестве входных данных для полностью связанного слоя со 128 нейронами (или элементами). Это переходит в другой полносвязный слой с 10 нейронами, по одному на каждый класс, который определяет класс изображения, то есть числа, изображенные на изображении.
Сверточные фильтры изначально выбираются случайным образом, поэтому классификация выполняется случайным образом. Ошибка между предсказанным классом и истинным классом входного изображения измеряется как так называемая кросс-энтропия. Затем оптимизатор автоматически передает эту ошибку обратно в сверточную сеть, используя правило дискриминационной цепочки, и обновляет веса фильтра, чтобы исправить ошибку классификации. Это повторяется тысячи раз, пока ошибка классификации не станет достаточно низкой.
Эти конкретные веса фильтров и промежуточные изображения являются результатом запуска оптимизации и могут отличаться, если вы повторно запустите эту записную книжку.
Обратите внимание, что вычисления в TensorFlow фактически выполняются для пакета изображений, а не для одного изображения, что делает вычисления более эффективными. Это означает, что при реализации в TensorFlow блок-схема фактически имеет измерение данных.
Convolutional Layer
На схеме ниже показана основная идея обработки изображений в первом сверточном слое. На входном изображении изображена цифра 7, здесь показаны четыре копии изображения, поэтому мы можем более четко видеть, как фильтр перемещается в разные позиции на изображении. Для каждой позиции фильтра вычисляется скалярное произведение между фильтром и пикселями изображения под фильтром, что приводит к одному пикселю в выходном изображении. Следовательно, перемещение фильтра по всему входному изображению приводит к созданию нового изображения.
Веса красных фильтров означают, что фильтр имеет положительный отклик на черные пиксели входного изображения, а синие пиксели означают, что фильтр имеет отрицательный отклик на черные пиксели.
В этом случае фильтр может идентифицировать 7-значную горизонтальную линию, о чем свидетельствует сильный отклик на эту строку в выходном изображении.
Размер шага, на который фильтр перемещается по входным данным, называется шагом. Существует шаг для перемещения фильтра по горизонтали (ось X) и еще один шаг для перемещения по вертикали (ось Y).
В приведенном ниже исходном коде для шага установлено значение 1 в обоих направлениях, что означает, что фильтр начинается в верхнем левом углу входного изображения и перемещается на 1 пиксель вправо на каждом шаге. Когда фильтр достигает конца с правой стороны изображения, фильтр смещается назад к левой стороне изображения на 1 пиксель. Это продолжается до тех пор, пока фильтр не достигнет правого нижнего угла входного изображения и не создаст полное выходное изображение.
Когда фильтр достигает правого и нижнего краев входного изображения, его можно дополнить нулями (белыми пикселями). Это сделает выходное изображение точно такими же размерами, как и входное изображение.
Кроме того, выходные данные свертки могут быть переданы через так называемый Rectified Linear Unit (ReLU), который гарантирует, что выходные данные будут положительными, поскольку отрицательные значения устанавливаются равными нулю. Выходные данные также могут быть уменьшены с помощью так называемого максимального объединения, которое рассматривает небольшое окно размером 2x2 пикселя и сохраняет только самые большие из этих пикселей. Это вдвое уменьшает разрешение входного изображения, например, с 28x28 до 14x14 пикселей.
Обратите внимание, что второй сверточный слой более сложен, так как требует 16 входных каналов. Мы хотим использовать отдельный фильтр для каждого входного канала, поэтому нам нужно 16 фильтров вместо одного. Кроме того, нам нужно 36 выходных каналов из второго сверточного слоя, поэтому всего для второго сверточного слоя необходимо 16 x 36 = 576 фильтров. Понять, как это работает, может быть немного сложно.
Imports
%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
Configuration of Neural Network
Для удобства здесь определена конфигурация сверточной нейронной сети, поэтому вы можете легко найти и изменить эти числа и перезапустить блокнот.
# Convolutional Layer 1.
filter_size1 = 5 # Convolution filters are 5 x 5 pixels.
num_filters1 = 16 # There are 16 of these filters.
# Convolutional Layer 2.
filter_size2 = 5 # Convolution filters are 5 x 5 pixels.
num_filters2 = 36 # There are 36 of these filters.
# Fully-connected layer.
fc_size = 128 # Number of neurons in fully-connected layer.
Load Data
Набор данных MNIST имеет размер около 12 МБ и будет загружен автоматически, если его нет по указанному пути.
from mnist import MNIST
data = MNIST(data_dir="data/MNIST/")
Копировать код. Теперь набор данных MNIST загружен и состоит из 70 000 изображений и номеров классов изображений. Набор данных был разделен на 3 взаимоисключающих подмножества. В этом руководстве мы будем использовать только наборы для обучения и тестирования.
print("Size of:")
print("- Training-set:\t\t{}".format(data.num_train))
print("- Validation-set:\t{}".format(data.num_val))
print("- Test-set:\t\t{}".format(data.num_test))
Size of:
- Training-set: 55000
- Validation-set: 5000
- Test-set: 10000
Копировать код Для удобства скопируйте некоторые измерения данных.
# The number of pixels in each dimension of an image.
img_size = data.img_size
# The images are stored in one-dimensional arrays of this length.
img_size_flat = data.img_size_flat
# Tuple with height and width of images used to reshape arrays.
img_shape = data.img_shape
# Number of classes, one class for each of 10 digits.
num_classes = data.num_classes
# Number of colour channels for the images: 1 channel for gray-scale.
num_channels = data.num_channels
Helper-function for plotting images
Функция для построения 9 изображений в сетке 3x3 и записи истинного и предсказанного классов под каждым изображением.
def plot_images(images, cls_true, cls_pred=None):
assert len(images) == len(cls_true) == 9
# Create figure with 3x3 sub-plots.
fig, axes = plt.subplots(3, 3)
fig.subplots_adjust(hspace=0.3, wspace=0.3)
for i, ax in enumerate(axes.flat):
# Plot image.
ax.imshow(images[i].reshape(img_shape), cmap='binary')
# Show true and predicted classes.
if cls_pred is None:
xlabel = "True: {0}".format(cls_true[i])
else:
xlabel = "True: {0}, Pred: {1}".format(cls_true[i], 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()
Plot a few images to see if data is correct
# Get the first images from the test-set.
images = data.x_test[0:9]
# Get the true classes for those images.
cls_true = data.y_test_cls[0:9]
# Plot the images and labels using our helper-function above.
plot_images(images=images, cls_true=cls_true)
TensorFlow Graph
#Helper-функции для создания новых переменных
Функция для создания новых переменных TensorFlow в заданной форме и инициализации их случайными значениями. Обратите внимание, что на этом этапе инициализация на самом деле не выполняется, она только определена в графе TensorFlow.
def new_weights(shape):
return tf.Variable(tf.truncated_normal(shape, stddev=0.05))
def new_biases(length):
return tf.Variable(tf.constant(0.05, shape=[length]))
#Helper-функция для создания нового Convolutional Layer
Эта функция создает новый сверточный слой в вычислительном графе TensorFlow. Здесь нет реальных вычислений, мы просто добавляем математические формулы в граф TensorFlow.
Предположим, что вход представляет собой 4-мерный тензор со следующими размерностями:
图像编号。
每个图像的Y轴。
每个图像的X轴。
每个图像的通道。
Обратите внимание, что входной канал может быть каналом цвета или может быть каналом фильтра, если входные данные были получены из предыдущего сверточного слоя.
На выходе получается еще один 4-мерный тензор со следующими размерностями:
图像编号,与输入相同。
每个图像的Y轴。 如果使用2x2池,则输入图像的高度和宽度除以2。
每个图像的X轴。 同上。
卷积滤波器产生的通道。
def new_conv_layer(input, # The previous layer.
num_input_channels, # Num. channels in prev. layer.
filter_size, # Width and height of each filter.
num_filters, # Number of filters.
use_pooling=True): # Use 2x2 max-pooling.
# Shape of the filter-weights for the convolution.
# This format is determined by the TensorFlow API.
shape = [filter_size, filter_size, num_input_channels, num_filters]
# Create new weights aka. filters with the given shape.
weights = new_weights(shape=shape)
# Create new biases, one for each filter.
biases = new_biases(length=num_filters)
# Create the TensorFlow operation for convolution.
# Note the strides are set to 1 in all dimensions.
# The first and last stride must always be 1,
# because the first is for the image-number and
# the last is for the input-channel.
# But e.g. strides=[1, 2, 2, 1] would mean that the filter
# is moved 2 pixels across the x- and y-axis of the image.
# The padding is set to 'SAME' which means the input image
# is padded with zeroes so the size of the output is the same.
layer = tf.nn.conv2d(input=input,
filter=weights,
strides=[1, 1, 1, 1],
padding='SAME')
# Add the biases to the results of the convolution.
# A bias-value is added to each filter-channel.
layer += biases
# Use pooling to down-sample the image resolution?
if use_pooling:
# This is 2x2 max-pooling, which means that we
# consider 2x2 windows and select the largest value
# in each window. Then we move 2 pixels to the next window.
layer = tf.nn.max_pool(value=layer,
ksize=[1, 2, 2, 1],
strides=[1, 2, 2, 1],
padding='SAME')
# Rectified Linear Unit (ReLU).
# It calculates max(x, 0) for each input pixel x.
# This adds some non-linearity to the formula and allows us
# to learn more complicated functions.
layer = tf.nn.relu(layer)
# Note that ReLU is normally executed before the pooling,
# but since relu(max_pool(x)) == max_pool(relu(x)) we can
# save 75% of the relu-operations by max-pooling first.
# We return both the resulting layer and the filter-weights
# because we will plot the weights later.
return layer, weights
#Вспомогательная функция для выравнивания слоя
Сверточные слои производят выходные тензоры с 4 измерениями. Мы добавим полносвязный слой после сверточного слоя, поэтому нам нужно уменьшить 4-мерный тензор до 2-мерного, который можно использовать в качестве входных данных полносвязного слоя.
def flatten_layer(layer):
# Get the shape of the input layer.
layer_shape = layer.get_shape()
# The shape of the input layer is assumed to be:
# layer_shape == [num_images, img_height, img_width, num_channels]
# The number of features is: img_height * img_width * num_channels
# We can use a function from TensorFlow to calculate this.
num_features = layer_shape[1:4].num_elements()
# Reshape the layer to [num_images, num_features].
# Note that we just set the size of the second dimension
# to num_features and the size of the first dimension to -1
# which means the size in that dimension is calculated
# so the total size of the tensor is unchanged from the reshaping.
layer_flat = tf.reshape(layer, [-1, num_features])
# The shape of the flattened layer is now:
# [num_images, img_height * img_width * num_channels]
# Return both the flattened layer and the number of features.
return layer_flat, num_features
#Helper-функция для создания нового полносвязного слоя
Эта функция создает новый полносвязный слой в вычислительном графе TensorFlow. Здесь нет реальных вычислений, мы просто добавляем математические формулы в граф TensorFlow.
Предположим, что ввод представляет собой двумерный тензор формы [di_images, num_inputs]. Выходные данные представляют собой двумерные тензоры формы [num_images, num_outputs].
def new_fc_layer(input, # The previous layer.
num_inputs, # Num. inputs from prev. layer.
num_outputs, # Num. outputs.
use_relu=True): # Use Rectified Linear Unit (ReLU)?
# Create new weights and biases.
weights = new_weights(shape=[num_inputs, num_outputs])
biases = new_biases(length=num_outputs)
# Calculate the layer as the matrix multiplication of
# the input and weights, and then add the bias-values.
layer = tf.matmul(input, weights) + biases
# Use ReLU?
if use_relu:
layer = tf.nn.relu(layer)
return layer
#Переменные-заполнители Переменные-заполнители используются в качестве входных данных для графа вычислений TensorFlow, которые мы можем изменять при каждом выполнении графа. Мы называем это переменной-заполнителем и описываем ее ниже.
Во-первых, мы определяем переменные-заполнители для входного изображения. Это позволяет нам изменить вход изображения в график TensorFlow. Это так называемый тензор, что означает, что это многомерный вектор или матрица. Для типа данных установлено значение float32, а для формы установлено значение [None, img_size_flat], где None означает, что тензор может содержать любое количество изображений, каждое изображение представляет собой вектор длины 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 и num_images можно автоматически вывести, используя -1 в качестве размера первого измерения. Таким образом, операция изменения формы:
x_image = tf.reshape(x, [-1, img_size, img_size, num_channels])
Затем у нас есть переменная-заполнитель для метки истинности, связанной с входным изображением в переменной-заполнителе x. Эта переменная-заполнитель имеет форму [None, num_classes], что означает, что она может содержать любое количество меток, каждая метка представляет собой вектор длины num_classes, в данном случае 10.
y_true = tf.placeholder(tf.float32, shape=[None, num_classes], name='y_true')
Мы также можем установить переменную-заполнитель для метки, но мы будем использовать argmax для ее вычисления. Обратите внимание, что это оператор TensorFlow, поэтому в этот момент ничего не вычисляется.
y_true_cls = tf.argmax(y_true, axis=1)
Convolutional Layer 1
Создайте первый сверточный слой. Он принимает x_image в качестве входных данных и создает различные фильтры num_filters1, каждый с шириной и высотой, равными filter_size1. Наконец, мы хотим вдвое уменьшить размер изображения, уменьшив его дискретизацию с помощью максимального пула 2x2.
layer_conv1, weights_conv1 = \
new_conv_layer(input=x_image,
num_input_channels=num_channels,
filter_size=filter_size1,
num_filters=num_filters1,
use_pooling=True)
Проверьте форму тензора, который будет выведен сверточным слоем. Это (?, 14, 14, 16), что означает наличие любого количества изображений (это?), каждое 14 пикселей в ширину и 14 пикселей в высоту, с 16 различными каналами, каждое из которых отфильтровано одним канальным устройством.
layer_conv1
<tf.Tensor 'Relu:0' shape=(?, 14, 14, 16) dtype=float32>
Convolutional Layer 2
Создайте второй сверточный слой, который принимает в качестве входных данных выходные данные первого сверточного слоя. Количество входных каналов соответствует количеству фильтров в первом сверточном слое.
layer_conv2, weights_conv2 = \
new_conv_layer(input=layer_conv1,
num_input_channels=num_filters1,
filter_size=filter_size2,
num_filters=num_filters2,
use_pooling=True)
Проверьте форму тензора, который будет выводиться из этого сверточного слоя. Форма (?, 7, 7, 36)? Опять же, это означает, что существует любое количество изображений, каждое шириной и высотой 7 пикселей, и 36 каналов, по одному для каждого фильтра.
layer_conv2
<tf.Tensor 'Relu_1:0' shape=(?, 7, 7, 36) dtype=float32>
Flatten Layer
Сверточный слой выдает четырехмерный тензор. Теперь мы хотим использовать их в качестве входных данных в полностью подключенной сети, что требует изменения формы или сглаживания тензоров в двумерные тензоры.
layer_flat, num_features = flatten_layer(layer_conv2)
Убедитесь, что тензор теперь имеет форму (?, 1764), что означает, что существует любое количество изображений, которые были сплющены в векторы длиной 1764 каждый. Обратите внимание, что 1764 = 7 х 7 х 36.
In [23]:layer_flat
Out[23]:<tf.Tensor 'Reshape_1:0' shape=(?, 1764) dtype=float32>
In [24]:num_features
Out[24]:1764
Fully-Connected Layer 1
Добавьте полностью связанные слои в сеть. Входными данными является сглаженный слой предыдущей свертки. Количество нейронов или узлов в полностью связанном слое равно fc_size. Используя ReLU, мы можем изучать нелинейные отношения.
layer_fc1 = new_fc_layer(input=layer_flat,
num_inputs=num_features,
num_outputs=fc_size,
use_relu=True)
Проверить, является ли выход полносвязного слоя тензором формы (?, 128)? Означает, что есть любое количество изображений, fc_size == 128.
In [26]:layer_fc1
Out[26]:<tf.Tensor 'Relu_2:0' shape=(?, 128) dtype=float32>
Fully-Connected Layer 2
Добавьте еще один полносвязный слой, который выводит вектор длины 10, определяющий, к каким 10 классам принадлежит входное изображение. Обратите внимание, что ReLU не используется на этом уровне.
In [27]:layer_fc2 = new_fc_layer(input=layer_fc1,
num_inputs=fc_size,
num_outputs=num_classes,
use_relu=False)
In [28]:layer_fc2
Out[28]:<tf.Tensor 'add_3:0' shape=(?, 10) dtype=float32>
Predicted Class
Второй полносвязный слой оценивает вероятность того, что входное изображение принадлежит каждому из 10 классов. Однако эти оценки немного приблизительны и их трудно интерпретировать, так как числа могут быть очень маленькими или большими, поэтому мы хотим нормализовать их, чтобы каждый элемент был ограничен между 0 и 1, а сумма 10 элементов равнялась 1. Это вычисляется с помощью так называемой функции softmax, а результат сохраняется в y_pred.
y_pred = tf.nn.softmax(layer_fc2)
class-number — это индекс самого большого элемента.
y_pred_cls = tf.argmax(y_pred, axis=1)
Cost-function to be optimized
Чтобы модель лучше классифицировала входные изображения, мы должны каким-то образом изменить переменные всех слоев сети. Для этого нам сначала нужно понять, как модель работает в настоящее время, сравнив прогнозируемый результат модели y_pred с ожидаемым результатом y_true.
Кросс-энтропия — это мера производительности, используемая для классификации. Кросс-энтропия — это непрерывная функция, которая всегда положительна и равна нулю, если прогнозируемый результат модели точно соответствует ожидаемому результату. Поэтому цель оптимизации состоит в минимизации кросс-энтропии как можно ближе к нулю путем изменения переменных слоев сети.
TensorFlow имеет встроенные функции для вычисления кросс-энтропии. Обратите внимание, что функция вычисляет softmax внутри, поэтому мы должны использовать выходные данные layer_fc2 напрямую вместо y_pred, где softmax уже был применен.
cross_entropy = tf.nn.softmax_cross_entropy_with_logits(logits=layer_fc2,
labels=y_true)
Теперь мы рассчитали кросс-энтропию для каждой классификации изображений, поэтому мы можем измерить, насколько хорошо модель работает с каждым изображением в отдельности. Но чтобы использовать кросс-энтропию для оптимизации переменных модели, нам нужно скалярное значение, поэтому нам просто нужно взять среднее значение кросс-энтропии для всех классификаций изображений.
cost = tf.reduce_mean(cross_entropy)
Optimization MethodТеперь, когда у нас есть показатель стоимости, который необходимо минимизировать, мы можем создать оптимизатор. В данном случае это AdamOptimizer, усовершенствованная форма Gradient Descent.
Обратите внимание, что в настоящее время оптимизация не выполняется. По сути, вообще ничего не вычисляется, мы просто добавляем объект-оптимизатор в граф TensorFlow для последующего исполнения.
optimizer = tf.train.AdamOptimizer(learning_rate=1e-4).minimize(cost)
Performance Measures
correct_prediction = tf.equal(y_pred_cls, y_true_cls)
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
TensorFlow Run
Следующим шагом является обучение, тестирование и отладка. Есть много способов, здесь я просто вставляю код
session = tf.Session()
session.run(tf.global_variables_initializer())
train_batch_size = 64
# Counter for total number of iterations performed so far.
total_iterations = 0
def optimize(num_iterations):
# Ensure we update the global variable rather than a local copy.
global total_iterations
# Start-time used for printing time-usage below.
start_time = time.time()
for i in range(total_iterations,
total_iterations + 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, _ = data.random_batch(batch_size=train_batch_size)
# 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.
if i % 100 == 0:
# Calculate the accuracy on the training-set.
acc = session.run(accuracy, feed_dict=feed_dict_train)
# Message for printing.
msg = "Optimization Iteration: {0:>6}, Training Accuracy: {1:>6.1%}"
# Print it.
print(msg.format(i + 1, acc))
# Update the total number of iterations performed.
total_iterations += num_iterations
# 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)))))
def plot_example_errors(cls_pred, correct):
# This function is called from print_test_accuracy() below.
# cls_pred is an array of the predicted class-number for
# all images in the test-set.
# correct is a boolean array whether the predicted class
# is equal to the true class for each image in the test-set.
# Negate the boolean array.
incorrect = (correct == False)
# Get the images from the test-set that have been
# incorrectly classified.
images = data.x_test[incorrect]
# Get the predicted classes for those images.
cls_pred = cls_pred[incorrect]
# Get the true classes for those images.
cls_true = data.y_test_cls[incorrect]
# Plot the first 9 images.
plot_images(images=images[0:9],
cls_true=cls_true[0:9],
cls_pred=cls_pred[0:9])
def plot_confusion_matrix(cls_pred):
# This is called from print_test_accuracy() below.
# cls_pred is an array of the predicted class-number for
# all images in the test-set.
# Get the true classifications for the test-set.
cls_true = data.y_test_cls
# Get the confusion matrix using sklearn.
cm = confusion_matrix(y_true=cls_true,
y_pred=cls_pred)
# Print the confusion matrix as text.
print(cm)
# Plot the confusion matrix as an image.
plt.matshow(cm)
# Make various adjustments to the plot.
plt.colorbar()
tick_marks = np.arange(num_classes)
plt.xticks(tick_marks, range(num_classes))
plt.yticks(tick_marks, range(num_classes))
plt.xlabel('Predicted')
plt.ylabel('True')
# Ensure the plot is shown correctly with multiple plots
# in a single Notebook cell.
plt.show()
# Split the test-set into smaller batches of this size.
test_batch_size = 256
def print_test_accuracy(show_example_errors=False,
show_confusion_matrix=False):
# Number of images in the test-set.
num_test = data.num_test
# Allocate an array for the predicted classes which
# will be calculated in batches and filled into this array.
cls_pred = np.zeros(shape=num_test, dtype=np.int)
# Now calculate the predicted classes 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_test:
# The ending index for the next batch is denoted j.
j = min(i + test_batch_size, num_test)
# Get the images from the test-set between index i and j.
images = data.x_test[i:j, :]
# Get the associated labels.
labels = data.y_test[i:j, :]
# Create a feed-dict with these images and labels.
feed_dict = {x: images,
y_true: labels}
# Calculate the predicted class using TensorFlow.
cls_pred[i:j] = session.run(y_pred_cls, feed_dict=feed_dict)
# Set the start-index for the next batch to the
# end-index of the current batch.
i = j
# Convenience variable for the true class-numbers of the test-set.
cls_true = data.y_test_cls
# Create a boolean array whether each image is correctly classified.
correct = (cls_true == cls_pred)
# Calculate the number of correctly classified images.
# When summing a boolean array, False means 0 and True means 1.
correct_sum = correct.sum()
# Classification accuracy is the number of correctly classified
# images divided by the total number of images in the test-set.
acc = float(correct_sum) / num_test
# Print the accuracy.
msg = "Accuracy on Test-Set: {0:.1%} ({1} / {2})"
print(msg.format(acc, correct_sum, num_test))
# Plot some examples of mis-classifications, if desired.
if show_example_errors:
print("Example errors:")
plot_example_errors(cls_pred=cls_pred, correct=correct)
# Plot the confusion matrix, if desired.
if show_confusion_matrix:
print("Confusion Matrix:")
plot_confusion_matrix(cls_pred=cls_pred)
print_test_accuracy()
optimize(num_iterations=900)
print_test_accuracy(show_example_errors=True)