Предварительные показания:
Портал «Машинное обучение, вы поймете, прочитав»
получить источник данных
Данные для обучения напрямую используют рукописный набор данных с открытым исходным кодом MNIST.
Набор данных MNIST — это база данных рукописного ввода с открытым исходным кодом. Он предоставляет большое количество выборок данных в качестве обучающих и проверочных наборов. Этот набор данных содержит 60 000 обучающих и 10 000 тестовых выборок.
Официальный сайт MNIST (очень низкий сайт) портал: http://yann.lecun.com/exdb/mnist/
Это красные и подчеркнутые выше! ! !
К счастью, если ваш загруженный тензорный поток включает пример mnist, вы можете напрямую ссылаться на инструмент загрузки данных:
from tensorflow.contrib.learn.python.learn.datasets.mnist import read_data_sets
В противном случае вам нужно написать свой собственный инструмент для загрузки и чтения данных обучения. Но вы можете найти этот код по этому адресу.
Мы на самом деле снаружи, все, что нам нужно, это позвонитьread_data_sets
эта функция.
Что касается загрузки данных, вы можете напрямую загрузить данные на официальном веб-сайте MNIST, предоставленном CoorChice, а затем использоватьread_data_sets
Функция читает из пути хранения.
Начните строить сеть
Определите несколько вспомогательных функций
Во-первых, несколько функций абстрагируются для создания весов, смещений, ядер свертки и слоев объединения. Они такие.
# 定义一个用于创建 权重 变量的函数
def weight_variable(shape):
initial = tf.truncated_normal(shape, stddev=0.1)
var = tf.Variable(initial)
# 记录每一个权重,因为后面要使用正则化
# 至于原因,后面具体再说
tf.add_to_collection(tf.GraphKeys.WEIGHTS, var)
return var
# 定义一个用于创建 偏置量 变量的函数
def bias_variable(shape):
# 初始化值为0.1
initial = tf.constant(0.1, shape=shape)
return tf.Variable(initial)
# 构建卷积函数
# 该卷积核每次在长、宽上移动一个步长,padding采用"SAME"策略
def conv2d(x, W):
return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
# 该池化核大小为2x2,长、宽上步长为2,padding采用"SAME"策略
def max_pool_2x2(x):
return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
В приведенном выше коде CoorChice необходимо объяснить генерацию весов.
tf.truncated_normal(shape, mean, stddev)
Эта функция будет генерировать случайные числа из нормального распределения (среднее значение нормального распределения равно среднему, по умолчанию равно 0). Что это значит?
Глядя на рисунок выше, стандартное отклонение представляет собой стандартное отклонение, которое представляет собой значение по оси абсцисс на рисунке, то есть эта функция будет начинаться с[mean ± 2stddev]
Если случайное число не находится в этом диапазоне, оно будет генерироваться случайным образом снова, пока случайное число не попадет в этот диапазон.
Что касается того, зачем использовать этот метод для инициализации веса? Это основано на опыте больших коров.Сгенерированный таким образом вес не подвержен проблеме исчезновения или взрыва градиента.
В любом случае, это загадка.
Создайте сетевую структуру
Давайте сначала посмотрим на полный код, и мы объясним его один за другим.
import tensorflow as tf
class CnnModel_MNIST:
def __init__(self):
# 创建占位tensor,用于装载数据
self.x_data = tf.placeholder(tf.float32, [None, 784])
self.y_data = tf.placeholder(tf.float32, [None, 10])
# -----------------------构建第一层卷积-----------------------
with tf.name_scope('hidden1'):
W_conv1 = weight_variable([5, 5, 1, 32])
b_conv1 = bias_variable([32])
x_image = tf.reshape(self.x_data, [-1, 28, 28, 1])
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
h_pool1 = max_pool_2x2(h_conv1)
# -----------------------构建第二层卷积------------------------
with tf.name_scope('hidden2'):
W_conv2 = weight_variable([5, 5, 32, 64])
b_conv2 = bias_variable([64])
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
h_pool2 = max_pool_2x2(h_conv2)
# -----------------------构建密集(全)链接层------------------------
with tf.name_scope('FC1'):
W_fc1 = weight_variable([7 * 7 * 64, 1024])
b_fc1 = bias_variable([1024])
h_pool2_flat = tf.reshape(h_pool2, [-1, 7 * 7 * 64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
# -----------------------加入Dropout------------------------
with tf.name_scope('dropout'):
self.keep_prob = tf.placeholder(tf.float32)
h_fc1_drop = tf.nn.dropout(h_fc1, self.keep_prob)
# -----------------------构建输出层------------------------
with tf.name_scope('output'):
W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])
self.y_conv = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
Создание заполнителей для ввода данных
x_data = tf.placeholder(tf.float32, [None, 784])
y_data = tf.placeholder(tf.float32, [None, 10])
Нам нужно создать две переменные-заполнители для хранения данных, которые будут введены позже, а затем перенести их в сеть для работы.
-
x_dataиспользуется для хранения обучающих данных, и его форма необъяснимым образом определена здесь как [None, 784]. На самом деле здесь есть знания, давайте послушаем CoorChice не спеша.
Первое измерение определено как «Нет», чтобы указать неопределенность, и позже оно будет заменено фактическим значением. Это сделано потому, что мы сначала не знаем, сколько данных изображения будет введено. Или, когда мы принимаем стратегию мини-пакетного градиентного спуска, мы можем свободно устанавливать размер пакета.
Второе измерение определяется как 784, что полностью связано с тем, что размер изображения в нашем наборе данных унифицирован до 28 * 28.
y_dataиспользуется для хранения меток обучающих данных, а его форма определяется как [Нет, 10], поскольку его первое измерение равно "Нет" иx_dataПо той же причине второе измерение равно 10, потому что у нас всего 10 категорий чисел от 0 до 9.
Построение сети первого уровня
W_conv1 = weight_variable([5, 5, 1, 32])
b_conv1 = bias_variable([32])
x_image = tf.reshape(self.x_data, [-1, 28, 28, 1])
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
h_pool1 = max_pool_2x2(h_conv1)
Конструкция веса w,
W_conv1 = weight_variable([5, 5, 1, 32])
Входящий массив представляет собой форму w, которая фактически определяет размер и количество ядер свертки в этом слое:
Размер каждого ядра свертки 5x5
Количество входных каналов равно 1, потому что используемое нами изображение имеет оттенки серого. Если вы используете карту цветов rgb без прозрачного канала, значение устанавливается равным 3, а если вы используете карту цветов rgba с прозрачным каналом, значение устанавливается равным 4.
Количество выходных каналов этого слоя равно 32, то есть этот слой имеет 32 ядра свертки. Что касается определения размера ядра свертки в каждом сверточном слое в скрытом слое, то еще раз подчеркивается, что это метафизика и задается на ощупь. Самый надежный способ — использовать какие-то публичные сетевые модели, по настройкам гигантов, ведь проверено многократными попытками.
Строительство уклона б
b_conv1 = bias_variable([32])
Размер b соответствует количеству ядер свертки. Что это обозначает? То есть после свертки каждого ядра свертки с входными данными добавляется смещение. Просмотрите структуру ядра свертки.
wx + b
Изменить форму входных данных
x_image = tf.reshape(self.x_data, [-1, 28, 28, 1])
Что делает эта строка кода, так это изменяет форму наших входных данных, чтобы их можно было свернуть с нашим ядром свертки. Поскольку входные данные x_data представляют собой тензор Nx784, его необходимо изменить на форму M x 28 x 28 x 1, прежде чем можно будет выполнить операцию.
-1 первого измерения означает, что размер должен быть определен, и следующие три измерения сначала удовлетворяются, и, наконец, вычисляется размер первого измерения. Вот и все:N x 784 / (28 x 28 x 1)
.
Второе и третье измерения на самом деле означают, что размер каждого входного изображения необходимо изменить на 28 x 28.
Четвертое измерение представляет количество каналов, шкала серого равна 1, rgb — 3, rgba — 4.
Построить свертку и добавить функцию активации
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
Эта строка кода содержит 3 операции:
-
conv2d(x_image, W_conv1)
, который сворачивает вход со всеми ядрами свертки этого слоя
Это динамическая диаграмма операции свертки.
Каждый раз тензор того же размера, что и ядро свертки, берется из входных данных для операции свертки.
Смысл в том, чтобы повернуть ядро свертки на 180 градусов и затем ввести его для расчета.
Из-за задействованной операции вращения размер ядра свертки обычно является нечетным числом, так что ядро свертки имеет очевидный центр вращения.
conv2d(x_image, W_conv1) + b_conv1
, добавляя смещение после каждой сверткиtf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
, и, наконец, добавьте функцию активации, чтобы увеличить нелинейное преобразование
Используемая здесь функция активации — это популярная функция ReLu, и ее функциональная форма очень проста:
y = max(x, 0)
Изображение также более интуитивно понятно:
Преимущество функции активации ReLu заключается в том, что, поскольку в первом квадранте она равна x, она может значительно сократить расчет, тем самым ускоряя сходимость. В то же время это по своей сути снижает вероятность исчезновения градиента, но взрыв градиента все же может произойти.
объединение
h_pool1 = max_pool_2x2(h_conv1)
Наконец, добавляется объединяющий слой, который может сжимать количество весов, уменьшать размер конечной модели и улучшать обобщение модели.
Здесь используется max_pooling 2x2, а размер шага равен 1. можно вернуться к определению вышеmax_pool_2x2(x)
Обзор функций.
max_pooling на самом деле принимает максимальное значение в тензоре 2x2, что может отфильтровать некоторый шум, который не очень важен.
Построение сети уровня 2
W_conv2 = weight_variable([5, 5, 32, 64])
b_conv2 = bias_variable([64])
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
h_pool2 = max_pool_2x2(h_conv2)
Построение второго слоя и первого слоя в основном одно и то же, фактически мы будем следовать этому шаблону, когда добавим еще несколько слоев.
Следует отметить, что во втором слое количество входных каналов w равно последнему количеству выходных каналов предыдущего слоя, то есть выходу hpool1, который напрямую вычисляется как 32, поскольку он был определен выше. .
Если вы не уверены, вы можете определить количество входных каналов таким образом:
in = h_pool1.get_shape()[-1].value
[-1] означает, что независимо от формы h_pool1 берется размер его последнего измерения.
Создайте полностью связанный слой
W_fc1 = weight_variable([7 * 7 * 64, 1024])
b_fc1 = bias_variable([1024])
h_pool2_flat = tf.reshape(h_pool2, [-1, 7 * 7 * 64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
Полносвязный слой играет роль сопоставления изученного «распределенного представления объектов» с образцом пространства меток.
Это предложение кажется очень конкретным, но я не знаю, о чем оно говорит.
Итак, попробуем разобраться, сначала посмотрим на структуру полного ссылочного слоя.
определить w и b
W_fc1 = weight_variable([7 * 7 * 64, 1024])
Приведенная выше строка кода устанавливает w-форму fc1 в[7 * 7 * 64, 1024]
. Размер первого измерения7 * 7 * 64
, потому что после слоя пула предыдущего сверточного слоя выход представляет собой7 * 7 * 64
тензор, поэтому вход здесь является выходом предыдущего слоя. Ну, это верно для всей нейронной сети.
Размер второго измерения равен 1024. Это как-то странно! Почему это значение?
На самом деле это то, что мы можем произвольно установить сами.Оно представляет собой количество нейронов в полносвязном слое.Чем больше число, тем дольше время вычисления.Однако, если число слишком мало, эффект классификации ранее извлеченных функций недостаточно.хорошо.
Таким образом, количество признаков, которыми обладает наш полносвязный слой, равно7 x 7 x 64 x 1024. Цифры по-прежнему ошеломляют.
b_fc1 = bias_variable([1024])
Точно так же число b должно соответствовать последнему измерению w, то есть один нейрон соответствует одному смещению b.
ввод деформации
h_pool2_flat = tf.reshape(h_pool2, [-1, 7 * 7 * 64])
Чтобы иметь возможность умножать на тензор признаков полносвязного слоя, определенный выше, входной тензор необходимо деформировать. В самом деле, для каждого7 x 7 x64
Для входных тензоров это сведение их в одномерный вектор.
Первое измерение принимает -1 То же значение, что и упомянутое выше, определяется окончательное измерение. На самом деле это количество выходов последнего слоя пула.
Создайте линейную функцию плюс функцию ReLu
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
Приведенный выше код по-прежнему следуетwx + b
Линейная формула строит функцию, а затем добавляется функция ReLu для увеличения нелинейного изменения.
Эта волна вычислений ясно показывает, что когда выход из последнего слоя пула[7 x 7 x 64]
После вывода полного слоя ссылок он выравнивается в[1 x 1024]
вектор. Это эквивалентно связыванию всех разрозненных признаков на переднем плане, поэтому предыдущее ядро свертки наблюдается с локальной точки зрения, в то время как полный слой связи наблюдается с глобальной точки зрения, потому что он объединяет все предыдущие слои. После интеграции всех признаков мы можем перейти к последующим операциям классификации.
На данный момент я считаю, что у вас есть общее представление о полном ссылочном слое. Некоторые трюки можно увидеть из него.
Полностью связанные слои увеличивают сложность модели, поскольку для расширения набора функций добавляется множество нейронов. Следовательно, это помогает повысить точность модели.
Но со взрывным увеличением количества функций скорость обучения обязательно замедлится. Более того, если количество нейронов, установленных в полном слое ссылок, слишком велико, произойдет переобучение. Поэтому требуются соответствующие настройки, а большего слепо жадничать нельзя.
Присоединяйтесь к отсева
self.keep_prob = tf.placeholder(tf.float32)
h_fc1_drop = tf.nn.dropout(h_fc1, self.keep_prob)
После полного слоя ссылок часто следует операция отсева. Поскольку количество нейронов в нейронной сети очень взрывоопасно, часто возникает проблема переобучения, особенно после введения полносвязного слоя. Поэтому нам нужно что-то сделать, чтобы уменьшить вероятность переобучения.
DropoutЭто очень популярное решение.
h_fc1_drop = tf.nn.dropout(h_fc1, self.keep_prob)
Для второго параметра этой строки кода мы можем динамически передавать значение, чтобы указать, насколько вероятно, что каждый нейрон выйдет из строя.На самом деле, он не участвует в расчетах.
Описание точки изображения является таким процессом. Перед тем, как каждый нейрон выполнит операцию, он решает, должен ли он участвовать в расчете в соответствии с заданной вероятностью keep_prob. Напримерkeep_prob=0.5
, что означает, что вероятность отказа каждого нейрона составляет 50%.
Нетрудно заметить, что операция Dropout может в определенной степени увеличить скорость обучения, уменьшая при этом возможность переобучения.
Создайте выходной слой
W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])
self.y_conv = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
Последний слой выходного слоя, мы можем получить результат.
Функциональная структура выходного слоя по-прежнемуwx+b
линейная структура.
Здесь необходимо пояснить форму выхода w. Излишне говорить, что первое измерение является результатом вывода предыдущего слоя, где предыдущий слой — это вывод 1024 нейронов из полносвязного слоя. Второе измерение — это общее количество категорий, которые мы классифицировали, поскольку мы распознаем рукописные цифры от 0 до 9, всего имеется 10 категорий.
После создания линейной функции добавьте нелинейную функцию активации. В сценарии классификацииSoftmax— широко используемая функция активации в выходном слое.
Это формула для Softmax, и легко увидеть, что она имеет диапазон [0, 1]. Это более мощная функция, и она напрямую преобразуется в вероятность один за другим. То есть какова вероятность того, что каждый выход соответствует классу.
Давайте взглянем на схематическое изображение изображения, чтобы понять его.
Это сеть!
Наконец сеть обретает форму. На самом деле это очень простая сеть с 4 слоями, включая два сверточных слоя, полносвязный слой и выходной слой.
Поток данных хорошо виден на рисунке.
собираюсь начать обучение
# coding=utf-8
import time
from input_data import *
from cnn_utils import *
from cnn_model import CnnMnistNetwork
train_times = 35000
base_path = "../mnist/"
save_path = base_path + str(train_times) + "/"
# 读取数据
mnist = read_data_sets("MNIST_data/", one_hot=True)
# 创建网络
network = CnnMnistNetwork()
x_data = network.x_data
y_data = network.y_data
y_conv = network.y_conv
keep_prob = network.keep_prob
# ------------------------构建损失函数---------------------
with tf.name_scope("cross_entropy"):
# 创建正则化对象,此处使用的是 L2 范数
regularization = tf.contrib.layers.l2_regularizer(scale=(5.0 / 50000))
# 应用正则化到参数集上
reg_term = tf.contrib.layers.apply_regularization(regularization)
# 在损失函数中加入正则化项
cross_entropy = (-tf.reduce_sum(y_data * tf.log(y_conv)) + reg_term)
tf.scalar_summary('loss', cross_entropy)
with tf.name_scope("train_step"):
# 使用 Adam 进行损失函数的梯度下降求解
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
# ------------------------构建模型评估函数---------------------
with tf.name_scope("accuracy"):
with tf.name_scope("correct_prediction"):
# 对比预测结果和标签
correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_data, 1))
with tf.name_scope("accuracy"):
# 计算准确率
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
tf.scalar_summary('accuracy', accuracy)
# 创建会话
sess = tf.InteractiveSession()
# 合并 summary
summary_merged = tf.merge_all_summaries()
train_writer = tf.train.SummaryWriter(save_path + "graph/train", sess.graph)
test_writer = tf.train.SummaryWriter(save_path + "graph/test")
start_time = int(round(time.time() * 1000))
# 初始化参数
sess.run(tf.initialize_all_variables())
for i in range(train_times):
# 从训练集中取出 50 个样本进行一波训练
batch = mnist.train.next_batch(50)
if i % 100 == 0:
summary, train_accuracy = sess.run([summary_merged, accuracy],
feed_dict={x_data: batch[0], y_data: batch[1], keep_prob: 1.0})
test_writer.add_summary(summary, i)
consume_time = int(round(time.time() * 1000)) - start_time
print("当前共训练 " + str(i) + "次, 累计耗时:" + str(consume_time) + "ms,实时准确率为:%g" % (train_accuracy))
# 记录训练时数据,每训练1000次保存一次训练信息
if i % 1000 == 0:
run_options = tf.RunOptions(trace_level=tf.RunOptions.FULL_TRACE)
run_metadata = tf.RunMetadata()
# 训练一次,dropout 的参数设置为 0.5
summary, _ = sess.run([summary_merged, train_step],
feed_dict={x_data: batch[0], y_data: batch[1], keep_prob: 0.5}, options=run_options,
run_metadata=run_metadata)
train_writer.add_run_metadata(run_metadata, str(i))
train_writer.add_summary(summary, i)
else:
summary, _ = sess.run([summary_merged, train_step],
feed_dict={x_data: batch[0], y_data: batch[1], keep_prob: 0.5})
train_writer.add_summary(summary, i)
# 每训练 2000 次保存一次模型
if i != 0 and i % 2000 == 0:
test_accuracy = int(
accuracy.eval(feed_dict={x_data: mnist.test.images, y_data: mnist.test.labels, keep_prob: 1.0}) * 100)
save_model(base_path + str(i) + "_" + str(test_accuracy) + "%/", sess, i)
# 在测试集计算准确率
summary, test_accuracy = sess.run([summary_merged, accuracy],
feed_dict={x_data: mnist.test.images, y_data: mnist.test.labels, keep_prob: 1.0})
train_writer.add_summary(summary)
print("测试集准确率:%g" % (test_accuracy))
print("训练完成!")
train_writer.close()
test_writer.close()
# 保存模型
save_model(save_path, sess, train_times)
Сначала поместите волну полного кода, а затем выберите ключевые моменты для обсуждения.
Он включает в себя некоторые основные концепции глубокого обучения.Эта статья достаточно длинная, и CoorChice не будет объяснять ее здесь слишком подробно. Если вам все еще не ясно, вы можете перейти к следующей статье и потратить несколько минут, чтобы понять основные понятия, прежде чем продолжить.
Портал «Машинное обучение, вы поймете, прочитав»
Построить функцию потерь
# 创建正则化对象,此处使用的是 L2 范数
regularization = tf.contrib.layers.l2_regularizer(scale=(5.0 / 50000))
# 应用正则化到参数集上
reg_term = tf.contrib.layers.apply_regularization(regularization)
# 在损失函数中加入正则化项
cross_entropy = (-tf.reduce_sum(y_data * tf.log(y_conv)) + reg_term)
tf.scalar_summary('loss', cross_entropy)
with tf.name_scope("train_step"):
# 使用 Adam 进行损失函数的梯度下降求解
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
На самом деле ключевой код для построения функции потерь состоит всего из двух строк:
cross_entropy = (-tf.reduce_sum(y_data * tf.log(y_conv)) + reg_term)
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
Первая строка строитперекрестная энтропияФункция потерь, вторая строка выполняет градиентный спуск по функции потерь для получения тензора.
Алгоритм оптимизации Адама используется здесь для динамического расчета различных скоростей адаптивного обучения для разных параметров, что отличается от постоянной скорости обучения SGD. Этот алгоритм оптимизации делает изменение каждого параметра относительно стабильным, потребление памяти вычислений меньше, а сходимость будет быстрее.
Однако я видел в Интернете, что его тренировочный эффект не так хорош, как у SGD? ? ?
Первоначально построение функции потерь закончено, и можно приступать к обучению удара слева. Однако CoorChice обнаружил в процессе обучения, что каждый раз, когда обучение проводится 2w раз, происходит явление градиентного взрыва. Внезапно потеря становится NaN, точность хорошая 0,99, но внезапное падение близко к 0!
Так что CoorChice не говорит ни слова, открытие Google — это поиск, и в Интернете упоминаются разные причины.
Проблема такого рода не очень уверена, в чем конкретная причина, поэтому я добавляю регуляризацию, чтобы попробовать. Получилось просто отлично!
Давайте посмотрим, как добавляется регуляризация.
Ранее в функцию build w была добавлена строка кода:
tf.add_to_collection(tf.GraphKeys.WEIGHTS, var)
Цель состоит в том, чтобы поместить каждый w в набор для использования в регуляризации в настоящее время.
Здесь CoorChice использует высококлассную парадигму L2, а формула потерь после добавления регуляризации выглядит следующим образом:
c0 - исходная часть функции потерь, здесьперекрестная энтропия, эта часть также называетсяэмпирический риск. Последняя часть — это наша формула регуляризации L2, которая фактически возводит каждый вес в квадрат, суммирует его, делит на число w и умножает на коэффициент важности. Часть регуляризации также называетсяструктурный риск, потому что это значение рассчитывается на основе w, добавленного кперекрестная энтропияПоэтому значение перекрестной энтропии каждый раз увеличивается, то есть градиент увеличивается для достижения эффекта штрафа за проигрыш. Это в определенной степени ослабляет роль собственных значений в сети, тем самым улучшая обобщение модели и дополнительно избегая возможности переобучения.
Вернитесь и посмотрите на приведенный выше код, чтобы понять, как регуляризация добавляется в сеть.
Построить модель оценки
# 对比预测结果和标签
correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_data, 1))
# 计算准确率
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
Эти две строки кода создают тензор точности для оценки точности модели. Первая строка фактически сравнивает предсказанное значение с фактическим значением.Результатом является логический вектор, который преобразуется в число с плавающей запятой во второй строке, а среднее значение является точностью.
Начать обучение!
for i in range(train_times):
# 从训练集中取出 50 个样本进行一波训练
batch = mnist.train.next_batch(50)
summary, _ = sess.run([summary_merged, train_step],
feed_dict={x_data: batch[0], y_data: batch[1], keep_prob: 0.5})
# 每训练 2000 次保存一次模型
if i != 0 and i % 2000 == 0:
test_accuracy = int(
accuracy.eval(feed_dict={x_data: mnist.test.images, y_data: mnist.test.labels, keep_prob: 1.0}) * 100)
save_model(base_path + str(i) + "_" + str(test_accuracy) + "%/", sess, i)
Во-первых, 50 выборок случайным образом берутся из обучающей выборки в качестве входных данных для обучения. Зачем это делать? Потому что в тренировочном наборе всего 60 000 сэмплов! Обучение один раз занимает слишком много времени, особенно на ноутбуке, который обычно используется для разработки.
Теоретически показатель точности не так высок, как при полноценных тренировках, но он может достигать 99% точности, но при этом может сэкономить много времени.
тогда позвониsess.run(train_step)
Запустите тренировку, обратите внимание, что поскольку сюда добавляется отсев, его нужно каждый раз устанавливать в feed_dict.
if i != 0 and i % 2000 == 0:
test_accuracy = int(
accuracy.eval(feed_dict={x_data: mnist.test.images, y_data: mnist.test.labels, keep_prob: 1.0}) * 100)
save_model(base_path + str(i) + "_" + str(test_accuracy) + "%/", sess, i)
Каждые 2000 раз обучайте CoorChice, проверяйте его на тестовом наборе, а затем сохраняйте модель. Это хорошая привычка. Потому что когда-то обученный, есть много неконтролируемых факторов, сохраните больше моделей, и вы сможете выбрать наиболее подходящие позже.
Вышеупомянутый процесс CoorChice повторяется полных 35 000 раз! Компьютер работал на полной скорости всю ночь, прежде чем обучение было завершено.
Вышеприведенное изображение представляет собой набор архивных папок с моделями.Вы можете видеть, что уровень точности в начале на самом деле не низкий, 97%. По мере увеличения количества тренировок точность стабилизируется на уровне 99%.
Определите с помощью модели
Теперь, когда модель обучена, мы можем использовать ее для распознавания собственных рукописных цифр.
# coding=utf-8
import numpy as np
from PIL import Image
import os
from cnn_model import CnnMnistNetwork
import tensorflow.python as tf
train_times = 20000
num = 5
image_path = "num_images_test/num"
CKPT_DIR = "../mnist/" + str(train_times) + "_99%"
# 将数字图片缩放为标准的 28*28,接着进行灰度处理
img = Image.open(image_path + str(num) +".png").resize((28, 28), Image.ANTIALIAS).convert("L")
# os.system("open " + image_path + str(num) + ".png")
flatten_img = np.reshape(img, 784)
arr = np.array([1 - flatten_img])
print(arr)
# 创建模型对应的网络
network = CnnMnistNetwork()
x_data = network.x_data
y = network.y_conv
keep_prob = network.keep_prob
# 创建会话
sess = tf.InteractiveSession()
# 初始化参数
sess.run(tf.initialize_all_variables())
saver = tf.train.Saver()
ckpt = tf.train.get_checkpoint_state(CKPT_DIR)
if ckpt and ckpt.model_checkpoint_path:
# 读取恢复模型
saver.restore(sess, ckpt.model_checkpoint_path)
# 载入数据,进行识别
y = sess.run(y, feed_dict={x_data: arr, keep_prob:1.0})
# 取最大可能
result = str(np.argmax(y, 1))
print("\n期望结果" + str(num) + ", 预测结果:" + result)
os.system("open num_images_test/num" + result[1] + ".png")
else:
print("没有模型")
Использование модели относительно простое, то есть считывание изображения, затем создание сетевой структуры, соответствующей модели, затем считывание модели, ввод изображения и получение результата распознавания.
сплетни
Обучение на данных MNIST эквивалентно машинному обучению.HelloWorldПрограмма, мы построили 4-х слойную простую сеть для обучения и распознавания, и итоговая точность модели тоже хорошая.
Полный опыт того, как построить нейронную сеть с нуля, затем сохранить модель, а затем прочитать модель для распознавания. В целом идея этого процесса относительно проста, ключ заключается в настройке некоторых параметров и способах решения проблем. Например, CoorChice столкнулся с проблемами NaN во время обучения. Машинное обучение по-прежнему является технологией, которая опирается на опыт, и ему необходимо обобщать набор собственных процедур анализа и решения проблем в постоянном реальном бою.
- Выделение времени для написания статей и обмена ими требует веры, пожалуйста, поддержите чиновников и зарядите свою веру CoorChice ?
- CoorChice время от времени будет делиться галантереей. Если вы хотите сесть на автобус, просто введите[Персональная домашняя страница] CoorChiceПросто следуй этому.