существует«[Получить] Распознавание рукописных цифр с помощью глубокого обучения», мы испытали, как его использовать на примере распознавания рукописных цифрглубокое обучение + тензорный потокрешить конкретную проблему.
По сути, это проблема классификации, то есть входные данные изображения разбиты на 10 категорий от 0 до 9, а наши данные — это все обработанные данные, загруженные напрямую из MNIST.
В реальном производстве наши источники данных обычно поступают из баз данных без предварительной обработки, так что же мы должны сделать, чтобы сделать данные в этих базах данных доступными для машинного обучения?
Этап предварительной обработки машинного обучения, предварительная обработка данных, призван решить эту проблему.
Этот CoorChice покажет, как это сделать с помощью задачи прогнозирования регрессии.
Необработанные данные
Объем данных, которые мы используем для машинного обучения, не может быть слишком мал, иначе эффект обучения будет крайне плохим. Но как лично нам получить большой объем данных для обучения?
Рекомендовано CoorChiceКаггле: https://www.kaggle.com/datasets, крупнейшее в мире сообщество по науке о данных и платформа для соревнований по данным. В этом замечательном месте мы можем легко найти различные, массивные и замечательные данные.
Открываем ссылку и видим огромное количество данных.Мы случайным образом выбираем один из источников данных для обучения.
Просто выберите первый!
Первая часть данных касается транзакций пекарни. После нажатия для входа найдите загрузку для загрузки этого набора данных.
Возможно, вы не сможете загрузить его сразу, потому что сначала вам будет предложено войти в систему. Тогда станьте участником Kaggle.
Мы используем таблицу, чтобы открыть данные наблюдений, которые мы только что скачали.
Боже мой!Это набор рудиментарных данных, всего 21293 фрагмента данных, каждый из которых имеет только 4 функции! 4 особенности! 4 особенности!
Все в порядке, мы только учимся обращаться с данными, так что давайте с этим справимся.
начать обработку данных
На самом деле, обычно наши данные остаются в базе данных в виде этой таблицы.
Прежде чем начать, вам необходимо установить библиотеку Pandas Python, которая используется для чтения данных нашей таблицы и выполнения таких операций, как добавление, удаление, изменение и запрос.
Ладно, приступим...
данные наблюдения
Наиболее важным шагом в обработке данных является наблюдение и анализ данных. Путем наблюдения и анализа нам необходимо удалить некоторые ненужные столбцы данных.Например, при прогнозировании прогноза погоды мы должны удалить столбец данных «что Ли Лэй ел утром», потому что это не помогает обучению модели.
В то же время нам нужно извлечь из данных некоторую скрытую информацию, чтобы расширить возможности. При прогнозировании информации о транзакции продукта мы можем узнать, является ли день выходным и какой это праздник, из столбца данных даты.
В куче данных о транзакциях в пекарне, которые мы загрузили, всего 4 ужасающие категории!
Часто, когда мы используем машинное обучение для решения проблемы, для этого есть цель. Например, в этой куче данных CoorChice надеется использовать машинное обучение для обучения модели, которую можно использовать для прогнозирования объема транзакций, которого может достичь определенный вид хлеба при определенных условиях в будущем.
Поэтому мы используем столбец объема транзакций в качестве данных метки набора данных.
Единственное, что осталось, это日期、时间、面包类目
эти три характеристики.
Добыча дополнительной информации из данных
Только с этими тремя функциями для обучения можно себе представить, насколько плохими будут результаты. Это хуже, чем вы думаете, поэтому нам нужно извлечь больше информации из существующих данных. Например, в данных изображения некоторые операции, такие как вращение и перемещение, обычно выполняются для дополнения объема данных.
Дата
Какую полезную информацию вы можете найти, взглянув сначала на дату?
Конечно, это неделя. Сегодня понедельник, вторник, ..., что определенно влияет на торговлю. Мы можем использовать Date для вычисления дня недели, который является текущей датой.
Есть что еще копать? Ну, если подумать... Эй, фестивали тоже должны быть важным фактором, влияющим на объем транзакций с хлебом С помощью Date мы также можем узнать, какой праздник текущая дата.
Затем путем наблюдения можно обнаружить, что по мере увеличения даты объем транзакций также накапливается, поэтому размер даты также можно напрямую использовать в качестве функции.
Поскольку CoorChice пытается предсказать объем транзакций, который может быть достигнут с определенным хлебом в определенное время в будущем, размер даты должен влиять на результат. Но мы не можем напрямую преобразовать дату в такую форму, как
20161030
Такое число, потому что в году всего 12 месяцев, а количество дней в каждом месяце разное, поэтому это значение имеет относительно большой скачок, что сильно увеличивает сложность и точность обучения. Нам нужно единое стандартное значение. Если в качестве точки отсчета определить определенный день, а затем перевести дату в расстояние от этого дня, то количественный стандарт, естественно, будет очень четким. потому что данные оттуда2016-10-30
Для начала правильнее будет использовать этот день как базовый.
время
Затем наблюдайте, доступно ли время или нет.
Само время представляет собой относительно плотные данные с небольшими интервалами. Мы можем разделить его на часы, так что получается 24 часа. Например, "10:13:03" пусть относится к часу 10 часов.
Через время мы также можем знать, что настоящее находится в
早晨、上午、中午、下午、傍晚、晚上、深夜
в тот период времени. Различные периоды времени также в большей или меньшей степени влияют на объем торгов.
Как только идея определена, следующим шагом будет поиск этой идеи для обработки данных.
Обработка необработанных данных
Первым шагом в обработке данных, конечно же, является чтение данных, для этого используются панды, которые мы скачали ранее.
читать данные
# 读取数据
datas = pd.read_csv('data/BreadBasket_DMS.csv')
Прочитав данные, выведите их, чтобы увидеть, как они выглядят.
print(datas.head())
->>
Date Time Transaction Item
0 2016-10-30 09:58:11 1 Bread
1 2016-10-30 10:05:34 2 Scandinavian
2 2016-10-30 10:05:34 2 Scandinavian
3 2016-10-30 10:07:57 3 Hot chocolate
4 2016-10-30 10:07:57 3 Jam
[5 rows x 4 columns]
Дополненные данные
Затем мы обрабатываем необработанные данные в соответствии с идеями приведенного выше анализа.
# 挖掘,周几对交易量的影响
datas['Weekday'] = datas['Date']
datas['Weekday'] = datas['Weekday'].map(lambda x: get_weekday(x))
# 挖掘,各个节日对交易量的影响
datas['Festival'] = datas['Date']
datas['Festival'] = datas['Festival'].map(lambda x: get_festival(x))
# 挖掘,当前所处的时间段对交易量的影响
datas['Time_Quantum'] = datas['Time']
datas['Time_Quantum'] = datas['Time_Quantum'].map(lambda x: get_time_quantum(x))
# 将 Date 一列变为与 2016-10-30 这天的距离
datas['Date'] = datas['Date'].map(lambda x: get_delta_days(x))
# 将时间转变为时段
datas['Time'] = datas['Time'].map(lambda x: get_time_range(x))
# 缩小数据大小
datas['Transaction'] = datas['Transaction'].map(lambda x: (float(x) / 1000.))
Теперь давайте снова выведем данные.
print(datas.head())
->>
Date Time Transaction ... Weekday Festival Time_Quantum
0 0.0 9 0.001 ... 6 null 上午
1 0.0 10 0.002 ... 6 null 上午
2 0.0 10 0.002 ... 6 null 上午
3 0.0 10 0.003 ... 6 null 上午
4 0.0 10 0.003 ... 6 null 上午
[5 rows x 7 columns]
Как видно из табличных данных, и Дата, и Транзакция стали числами с плавающей запятой, потому что их уменьшение способствует последующему градиентному спуску, иначе потери будут очень и очень большими.
Теперь мы расширили некоторые функции.
Горячее кодирование
Далее, для дискретных данных, таких как День недели и Время, их размер не повлияет на транзакцию, но значение категории этого дискретного признака повлияет на результат. Например, Праздник Весны повлияет на объем сделок с продуктами питания.
Нам нужно выполнитьоднократное кодирование. ооднократное кодирование, вы можете найти его на сайте CoorChice«Машинное обучение, вы поймете после прочтения»Прочтите эту статью для получения соответствующей информации.
ont_hot_data = pd.get_dummies(datas, prefix=['Time', 'Item', 'Weekday', 'Festival', 'Time_Quantum'])
Посмотрите, как теперь выглядит таблица данных.
print(ont_hot_data.head())
->>
Date Transaction ... Time_Quantum_晚上 Time_Quantum_深夜
0 0.0 0.001 ... 0 0
1 0.0 0.002 ... 0 0
2 0.0 0.002 ... 0 0
3 0.0 0.003 ... 0 0
4 0.0 0.003 ... 0 0
[5 rows x 136 columns]
После горячего кодирования столбцы выросли до ошеломляющих 136 столбцов!
Другая причина, по которой требуется горячее кодирование данных, заключается в том, что во время обучения горячее кодирование представляет собой отношение между 0 и 1 в классификации, и если используется метод меток классификации 1, 2, 3, .., это не способствует градиентному спуску, расчетный градиент часто бывает относительно большим.
Для даты и транзакции вы можете видеть, что в таблице CoorChice также уменьшает их, чтобы сделать числовой диапазон меньше, что выгодно для решения градиентного спуска.
Теперь посмотрим на распределение данных:
Перетасовать данные
Далее нам нужно перетасовать упорядоченные данные элементов, что помогает улучшить способность обучаемой модели к обобщению.
ont_hot_data = ont_hot_data.sample(frac=1, replace=False)
ont_hot_data = ont_hot_data.sample(frac=1, replace=False)
ont_hot_data = ont_hot_data.sample(frac=1, replace=False)
Одного раза мало, 3 раза карабкаться!
Глядя на столбец «Дата», это означает, что данные были достаточно перепутаны.
сохранять данные
Наконец, нам нужно разделить обработанные данные на обучающий набор и тестовый набор, CoorChice берет около 30% данных тестового набора и сохраняет их отдельно.
# 测试数据集大小
test_count = 6000
train_count = ont_hot_data.shape[0] - test_count
# 切割出训练数据集
train_data = ont_hot_data[:train_count]
# 切割出测试数据集
test_data = ont_hot_data[train_count:]
# 分别保存两个数据集
train_data.to_csv('data/train_data.csv', index=False, header=True)
test_data.to_csv('data/test_data.csv', index=False, header=True)
Сохранить обработанные данные легко, используяto_csv()
Вы можете сохранить данные в формате csv.
index и header указывают, следует ли сохранять номера строк и имена столбцов соответственно.
Теперь мы обработали необработанные данные и разделили их на обучающую и тестовую выборки. У нас есть 15 293 элемента обучающих данных и 6 000 элементов тестовых данных.
Начать обучение!
Перед началом рекомендуется ознакомиться с этой статьей«[Получить] Распознавание рукописных цифр с помощью глубокого обучения», потому что процедура в основном такая же, но некоторые места необходимо скорректировать в соответствии с конкретной ситуацией.
При распознавании рукописных цифр мы строим простую 4-слойную сеть, состоящую из двух сверточных слоев, полносвязного слоя и выходного слоя.
Просмотрите эту сетевую структуру.
Произошло недооснащение!
Сначала CoorChice напрямую использовал эту сеть для обучения.После 10-недельного обучения было обнаружено, что показатель точности составлял всего 0,1% на самом высоком уровне.
Сначала CoorChice уменьшил lr (скорость обучения), думая, что скорость обучения может быть слишком большой, что и вызвало колебания. Однако безрезультатно.
Когда происходит это явление, в основном можно судить о высокой вероятности недообучения.
Возникает недооснащение, вы можете устранить проблему и шаг за шагом улучшить сеть, выполнив следующие шаги.
Используйте подходящую схему инициализации веса
В этом обучении инициализация весов следует предыдущей схеме инициализации нормального распределения, которая должна быть отличной схемой, поэтому она не будет изменена.
Выберите подходящую функцию активации
В последней конструкции сети функция активации ReLu использовалась в сверточном слое и слое полной связи, а функция активации Softmax использовалась в выходном слое. И на этот раз CoorChice заменил все функции активации отличными функциями активации ReLu, которые должны подойти.
Выберите подходящий оптимизатор и скорость обучения
Это сравнительная таблица различных оптимизаторов Как видно из рисунка, есть оптимизатор под названием Adadelta, который работает очень хорошо!
Adadelta — это адаптивный оптимизатор, который автоматически вычисляет экспоненциально взвешенный скользящий средний член квадрата обновления независимой переменной в качестве скорости обучения. Поэтому установленная нами скорость обучения на самом деле мало влияет.
Как видно из рисунка, этот оптимизатор может быстро снижаться в начале и середине обучения, но при приближении к оптимальному решению будет слегка колебаться. Тем не менее, SGD поддерживает скорость обучения на протяжении всего процесса, поэтому, если настройка подходит, он будет проще сходиться вблизи оптимального решения.
# 使用 Adadelta 进行损失函数的梯度下降求解
train_step = tf.train.AdadeltaOptimizer(0.0001).minimize(cross_entropy)
Однако, поскольку фактический процесс обучения Adadelta имеет мало общего со скоростью обучения, о которой мы говорили вначале, он не контролируется, и его все еще трудно тренировать позже, поэтому CoorChice решил переключиться на SGD после того, как попробовал его.
lr = tf.Variable(1e-5)
# 使用 SGD 进行损失函数的梯度下降求解
train_step = tf.train.GradientDescentOptimizer(lr).minimize(cross_entropy)
Более того, CoorChice использует переменную lr для установки скорости обучения оптимизатора, так что в процессе обучения мы также можем динамически контролировать текущую скорость обучения.
Увеличение глубины и ширины сети
Когда происходит недообучение, также возможно, что сеть слишком проста, а веса слишком малы, что приводит к недостаточному количеству информации для изучения. В этом обучении CoorChice по-прежнему использует последнюю 4-слойную одноядерную сеть, которая действительно кажется слишком простой.
Затем укрепите его.
# coding=utf-8
from cnn_utils import *
class CnnBreadBasketNetwork:
def __init__(self):
with tf.name_scope("input"):
self.x_data = tf.placeholder(tf.float32, shape=[None, 135], name='x_data')
# 补位
input_data = tf.pad(self.x_data, [[0, 0], [0, 1]], 'CONSTANT')
with tf.name_scope("input_reshape"):
# 变形为可以和卷积核卷积的张量
input_data = tf.reshape(input_data, [-1, 17, 8, 1])
tf.summary.image("input", input_data, 1)
self.y_data = tf.placeholder(tf.float32, shape=[None], name='y_data')
# ------------------------构建第一层网络---------------------
with tf.name_scope("hidden1"):
# 第一个卷积
with tf.name_scope("weights1"):
W_conv11 = weight_variable([3, 3, 1, 64])
variable_summaries(W_conv11, "W_conv11")
with tf.name_scope("biases1"):
b_conv11 = bias_variable([64])
variable_summaries(b_conv11, "b_conv11")
h_conv11 = tf.nn.relu(conv2(input_data, W_conv11) + b_conv11)
tf.summary.histogram('activations_h_conv11', h_conv11)
# 第二个卷积
with tf.name_scope("weights2"):
W_conv12 = weight_variable([3, 3, 64, 64])
variable_summaries(W_conv12, "W_conv12")
with tf.name_scope("biases2"):
b_conv12 = bias_variable([64])
variable_summaries(b_conv12, "b_conv12")
h_conv12 = tf.nn.relu(conv2(h_conv11, W_conv12) + b_conv12)
tf.summary.histogram('activations_h_conv11', h_conv12)
# 池化
h_pool1 = max_pool_2x2(h_conv12)
tf.summary.histogram('pools_h_pool1', h_pool1)
# ------------------------构建第二层网络---------------------
with tf.name_scope("hidden2"):
# 第一个卷积核
with tf.name_scope("weights1"):
W_conv21 = weight_variable([3, 3, 64, 128])
variable_summaries(W_conv21, 'W_conv21')
with tf.name_scope("biases1"):
b_conv21 = bias_variable([128])
variable_summaries(b_conv21, 'b_conv21')
h_conv21 = tf.nn.relu(conv2(h_pool1, W_conv21) + b_conv21)
tf.summary.histogram('activations_h_conv21', h_conv21)
# 第二个卷积核
with tf.name_scope("weights2"):
W_conv22 = weight_variable([3, 3, 128, 128])
variable_summaries(W_conv22, 'W_conv22')
with tf.name_scope("biases2"):
b_conv22 = bias_variable([128])
variable_summaries(b_conv22, 'b_conv22')
h_conv22 = tf.nn.relu(conv2(h_conv21, W_conv22) + b_conv22)
tf.summary.histogram('activations_h_conv22', h_conv22)
# 池化
self.h_pool2 = max_pool_2x2(h_conv22)
tf.summary.histogram('pools_h_pool2', self.h_pool2)
shape_0 = self.h_pool2.get_shape()[1].value
shape_1 = self.h_pool2.get_shape()[2].value
h_pool2_flat = tf.reshape(self.h_pool2, [-1, shape_0 * shape_1 * 128])
# ------------------------ 构建第一层全链接层 ---------------------
with tf.name_scope("fc1"):
with tf.name_scope("weights"):
W_fc1 = weight_variable([shape_0 * shape_1 * 128, 4096])
variable_summaries(W_fc1, 'W_fc1')
with tf.name_scope("biases"):
b_fc1 = bias_variable([4096])
variable_summaries(b_fc1, 'b_fc1')
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
tf.summary.histogram('activations_h_fc1', h_fc1)
# ------------------------构建输出层---------------------
with tf.name_scope("output"):
with tf.name_scope("weights"):
W_out = weight_variable([4096, 1])
variable_summaries(W_out, 'W_out')
with tf.name_scope("biases"):
b_out = bias_variable([1])
variable_summaries(b_out, 'b_out')
# 注意⚠️,此处的激活函数已经替换成 ReLu 了
self.y_conv = tf.nn.relu(tf.matmul(h_fc1, W_out) + b_out)
tf.summary.histogram('activations_y_conv', self.y_conv)
Это весь код для улучшенной сети.
Как видите, это все еще привычная рутина. Ниже приведено содержание обновления версии:
Размер каждого ядра свертки изменился с 5x5 на 3x3.
Количество каналов ядра свертки в первом сверточном слое увеличено с 32 до 64. Удвойте количество ядер свертки.
Количество слоев ядра свертки по-прежнему равно двум слоям, но каждый слой добавляет еще одну группу ядра свертки.
Количество нейронов в полносвязном слое увеличено с 1024 до 4096.
Поговорим о некоторых изменениях в сети.
self.x_data = tf.placeholder(tf.float32, shape=[None, 135], name='x_data')
# 补位
input_data = tf.pad(self.x_data, [[0, 0], [0, 1]], 'CONSTANT')
# 变形为可以和卷积核卷积的张量
input_data = tf.reshape(input_data, [-1, 17, 8, 1])
self.y_data = tf.placeholder(tf.float32, shape=[None], name='y_data')
При определении заполнителя, поскольку в данных таблицы всего 135 столбцов, сначала определите заполнитель, форма которого [Нет, 135]. Но смущает то, что 135 является нечетным числом и не может быть преобразовано в тензор, который можно свернуть с помощью ядра свертки, поэтому CoorChice добавил к нему столбец, предоставленный tensorflow.pad()
функция.
input_data = tf.pad(self.x_data, [[0, 0], [0, 1]], 'CONSTANT')
Цифры во втором параметре соответственно указывают на необходимость быть в исходном тензоревверх вниз влево вправоСколько строк или столбцов добавить. Здесь CoorChice добавляет столбец с нулями справа от исходного тензора.
Теперь его можно преобразовать в тензор, который можно свернуть с ядром свертки.
input_data = tf.reshape(input_data, [-1, 17, 8, 1])
- Советы:Обычно после многоуровневой свертки и объединения при входе в первый полноканальный слой или непосредственно в выходной слой нам нужно знать форму предыдущего слоя, а затем мы можем спроектировать полноканальный слой или выходной слой. , Форма веса. Форма ввода может быть легко получена с помощью:
# 获得最后一个池化层的宽
shape_0 = self.h_pool2.get_shape()[1].value
# 获得最后一个池化层的高
shape_1 = self.h_pool2.get_shape()[2].value
# 变形为全链接层可乘的形状
h_pool2_flat = tf.reshape(self.h_pool2, [-1, shape_0 * shape_1 * 128])
На самом деле эта сеть может быть не очень хорошей, глубины может не хватить, а точности может не хватить. Однако, ограниченный оборудованием, дизайн слишком глубокий, из-за чего каждое обучение будет занимать много времени, поэтому CoorChice старается максимально упростить его, в основном в зависимости от того, как осуществляется процесс.
Вот новая структура сети:
Взгляните на локальные два набора сверточных скрытых слоев:
Начать обучение!
# coding=utf-8
import time
from cnn_model import *
from BBDATA import *
train_times = 20000
base_path = ".../BreadBasket/"
save_path = base_path + str(train_times) + "/"
# 读取数据
BBDATA = read_datas('data/')
# 创建网络
network = CnnBreadBasketNetwork()
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 / 500))
# 应用正则化到参数集上
reg_term = tf.contrib.layers.apply_regularization(regularization)
# 在损失函数中加入正则化项
cross_entropy = tf.reduce_mean(tf.square((y_conv - y_data))) + reg_term
tf.scalar_summary('loss', cross_entropy)
with tf.name_scope("train_step"):
lr = tf.Variable(1e-5)
# 使用 SGD 进行损失函数的梯度下降求解
train_step = tf.train.GradientDescentOptimizer(lr).minimize(cross_entropy)
# 记录平均差值
with tf.name_scope("difference_value"):
dv = tf.reduce_mean(tf.abs(y_conv - y_data))
tf.scalar_summary('difference_value', cross_entropy)
# ------------------------构建模型评估函数---------------------
with tf.name_scope("accuracy"):
with tf.name_scope("correct_prediction"):
correct_prediction = tf.less_equal(tf.abs(y_conv - y_data), 0.5)
with tf.name_scope("accuracy"):
# 计算准确率
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
tf.scalar_summary('accuracy', accuracy)
# 创建会话
sess = tf.InteractiveSession()
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())
global loss
loss = 0
global train_accuracy
for i in range(train_times):
# 从训练集中取出 200 个样本进行一波训练
batch = BBDATA.train_data.batch(200)
if i % 100 == 0:
summary, train_accuracy, test_loss, dv_value = sess.run([summary_merged, accuracy, cross_entropy, dv],
feed_dict={x_data: BBDATA.test_data.data, y_data: BBDATA.test_data.label, keep_prob: 1})
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 * 100.) + "%, "
+ "当前误差均值:" + str(dv_value) + ", train_loss = " + str(loss) + ", test_loss = " + str(test_loss))
if i % 1000 == 0:
run_options = tf.RunOptions(trace_level=tf.RunOptions.FULL_TRACE)
run_metadata = tf.RunMetadata()
summary, _, loss = sess.run([summary_merged, train_step, cross_entropy],
feed_dict={x_data: batch.data, y_data: batch.label, keep_prob: 0.6}, options=run_options,
run_metadata=run_metadata)
train_writer.add_run_metadata(run_metadata, str(i))
train_writer.add_summary(summary, i)
else:
summary, _, loss = sess.run([summary_merged, train_step, cross_entropy],
feed_dict={x_data: batch.data, y_data: batch.label, keep_prob: 0.6})
train_writer.add_summary(summary, i)
# 每训练 2000 次保存一次模型
if i != 0 and i % 1000 == 0:
test_accuracy, test_dv = sess.run([accuracy, dv], feed_dict={x_data: BBDATA.test_data.data, y_data: BBDATA.test_data.label, keep_prob: 1})
save_model(base_path + str(i) + "_" + str(test_dv) + "/", sess, i)
# 在测试集计算准确率
summary, final_test_accuracy, test_dv = sess.run([summary_merged, accuracy, dv],
feed_dict={x_data: BBDATA.test_data.data, y_data: BBDATA.test_data.label, keep_prob: 1})
train_writer.add_summary(summary)
print("测试集准确率:%g" % (final_test_accuracy * 100.) + "%, 误差均值:" + str(test_dv))
print("训练完成!")
train_writer.close()
test_writer.close()
# 保存模型
save_model(save_path, sess, train_times)
sess.close()
Вот полный код обучения, в основном и«[Получить] Распознавание рукописных цифр с помощью глубокого обучения»такое же в. Просто CoorChice добавляет MAE в качестве индикатора для оценки модели.
dv = tf.reduce_mean(tf.abs(y_conv - y_data))
Поскольку это прогноз регрессии, среднеквадратическая дисперсия MAE используется в качестве индикатора для оценки качества модели. Чем меньше средняя абсолютная разница, тем ближе результаты модельного прогноза к реальной ситуации.
Кроме того, поскольку это регрессионное предсказание, функция потерь перекрестной энтропии, использованная в предыдущей классификации, не особенно подходит, и мы заменяем ее функцией потерь среднеквадратичной ошибки.
cross_entropy = tf.reduce_mean(tf.square((y_conv - y_data))) + reg_term
нужно знать, это:
correct_prediction = tf.less_equal(tf.abs(y_conv - y_data), 0.5)
Оценка модели больше не должна сравнивать предсказанные значения и значения меток на равенство, В прогнозировании регрессии почти невозможно сделать значение, предсказанное моделью, равным истинному значению. Чтобы принять результат прогноза, мы преобразовали оценочную модель в абсолютную разницу
потери не уменьшаются
В приведенном выше коде переменная скорости обучения оптимизатора SGD фактически добавляется позже CoorChice. Раньше он был фиксированным, но когда его начали тренировать, обнаружилось, что потери всегда колебались в большом значении, но в небольшом диапазоне, и точность также колебалась в небольшом диапазоне, который нельзя было улучшить.
- кривая потерь
- Кривая изменения точности
В это время точно10.5%При зависании тренд убытков не сильно изменился.
Это показывает, что обучение в основном находится в застойном состоянии, но очевидно, что точность нашей модели недостаточна. Такая ситуация может возникнуть в следующих ситуациях.
застрял в локальном оптимуме
Как показано на рисунке, на многофункциональной многомерной поверхности потерь рядом с точкой А может быть много небольших ямок. При заходе на дно маленькой ямы первая производная от проигрыша тоже равна 0. В интернете часто говорят, что он попадает в локальное оптимальное решение, но не достигает настоящих Глобальных минимумов.
На самом деле выборка мини-патчей, которую мы взяли в приведенном выше коде для обучения, также может в определенной степени уменьшить ситуацию попадания в локальное оптимальное решение. Поскольку эта партия образцов может попасть в локальный оптимум, колебания следующей партии образцов снова вытащат вас из ямы. Конечно, иногда мы можем видеть, что потери вместо этого увеличиваются, что также вызвано этим методом.
ездить на седловине
Как показано на рисунке, в поверхности потерь седлообразных поверхностей будет больше, чем локальных оптимальных ямок.
Симпатичный ?...
Когда мы едем к седловой точке на седле, производная здесь тоже равна 0, значит, она действительно сходится? нет. Глядя на рисунок, у нас есть минимальное значение в направлении оси x, но максимальное значение в направлении оси y.
Если вы приземлитесь в такое неудобное положение, что вы входите в седловую точку, ваша тренировка будет казаться почти плоской, и вам потребуется много времени, чтобы выбраться из этой области.
К счастью, за счет надлежащего увеличения скорости обучения или надлежащего сокращения пакета полезно как можно скорее покинуть эту область.
бесконечное плоское лицо
Тогда в поверхности потерь также есть гладкие поверхности большой площади, подобные той, что показана выше.Для точек в этих областях, хотя производная не равна 0, значение чрезвычайно мало.Даже если обучение повторяется снова и снова , он до сих пор кажется почти неизменным.
Страшно то, что мы не можем сказать, находимся ли мы в локальном оптимуме, или в области седла, или просто в плоскости классов.
Как с этим бороться?
Все вышеперечисленное может привести к, казалось бы, неизученным ситуациям, особенно на персональных устройствах с ограниченной производительностью. Но у нас все еще есть некоторые подпрограммы, чтобы свести к минимуму это.
Проверьте данные. Убедитесь, что метки данных соответствуют друг другу.
Измените и настройте структуру сети. Попробуйте использовать сетевые модели, созданные некоторыми большими коровами с открытым исходным кодом, такими как серия VGG. Модифицировать на их основе скорее будет надежнее, чем возиться самому.
Отрегулируйте размер партии. Если партия слишком велика, время обучения слишком велико, и легко войти в локальный оптимум или область седла. Если он слишком мал, это вызовет сильную вибрацию, и найти оптимальное решение непросто.
Попробуйте разные инициализации параметров. Если повезет, инициализация может упасть в яму Global Minima.
Динамическая скорость обучения. Небольшая, неизменно скорость обучения падает в яму, когда попадает в локальный оптимум, или движется очень медленно по гладкой поверхности. Если он слишком велик, легко пропустить оптимальное решение. Таким образом, динамический оптимизатор скорости обучения, такой как Адам, может в некоторой степени уменьшить эти проблемы. Конечно, на основе SGD мы также можем динамически увеличивать скорость обучения в зависимости от ситуации с потерями.Когда потери большие, но неизменны, уменьшайте скорость обучения, когда потери малы, чтобы не пропустить оптимальное решение.
Грубо судить о проблеме через потери
Потери в поездах продолжают снижаться, а потери при тестировании продолжают снижаться, указывая на то, что сеть все еще обучается;
Потери в поезде продолжают уменьшаться, а потери при тестировании имеют тенденцию оставаться неизменными, что указывает на переоснащение сети;
Потери в поезде, как правило, остаются неизменными, а потери при тестировании продолжают снижаться, что указывает на то, что набор данных на 100% проблематичен;
Потери при поездке, как правило, остаются неизменными, а потери при тестировании, как правило, остаются неизменными, что указывает на то, что обучение столкнулось с узким местом, и скорость обучения или количество пакетов необходимо уменьшить;
Потери в поезде продолжают расти, а потери при тестировании продолжают расти, что указывает на то, что структура сети спроектирована неправильно, гиперпараметры обучения установлены неправильно, а набор данных очищен.
По проигрышу можно примерно судить о некоторых проблемах.Приведенные выше правила могут быть наиболее справочными, но их точность не гарантируется.
Суммировать
Напоследок картинка для подведения итогов:
- Нужна мотивация писать статьи и делиться в свободное время.Пожалуйста, ставьте пальцы вверх чиновникам.Веру еще нужно продолжать подзаряжать ?
- CoorChice время от времени делится новыми галантерейными товарами. Если вы хотите сесть в автобус, просто введите[Персональная домашняя страница] CoorChiceПросто следуй этому.