Вопрос или проблема
Я пытаюсь разработать решатель капчи, используя простую полностью связанную нейронную сеть в TensorFlow. Все капчи содержат 5 цифр/букв, каждый символ может быть числом от 0 до 9 или буквой от A до Z. Они выглядят примерно так:
Каждая капча имеет метку, например: “AB4D1”. Я беру эти метки, разбиваю их на [‘A’, ‘B’, ‘4’, ‘D’, ‘1’], конвертирую каждый из символов в число от 0 до 35 (26 букв и 10 цифр) и конвертирую каждое из них в одноразовые представления [0, 0, 0, 1, 0,..]. Затем я конкатенирую одноразовые массивы для всех букв в капче, и в итоге у меня получается единый массив длиной 5*36 для каждого изображения. (Этот массив имеет 5 единиц и остальные нули)
Теперь я задумался, какую функцию потерь использовать. Я пробовал среднеквадратичную ошибку, но она совершенно не сработала по какой-то причине. Теперь я пытаюсь использовать кросс-энтропию (с softmax сначала), но, насколько я понимаю, мне нужно применить ее к каждой букве отдельно, а затем сложить потери для каждой буквы, так как softmax требует эксклюзивных категорий.
Я попробовал разделить вывод модели и метки:
split_logits = tf.split(logits, num_or_size_splits=5, axis=1)
split_train_labels = tf.split(tf_train_labels, num_or_size_splits=5, axis=1)
Это работает, насколько я знаю. Теперь я просто складываю потери кросс-энтропии для каждой буквы вместе.
loss =
tf.reduce_sum(tf.nn.softmax_cross_entropy_with_logits_v2(labels=split_train_labels[0], logits=split_logits[0])) +
tf.reduce_sum(tf.nn.softmax_cross_entropy_with_logits_v2(labels=split_train_labels[1], logits=split_logits[1])) +
tf.reduce_sum(tf.nn.softmax_cross_entropy_with_logits_v2(labels=split_train_labels[2], logits=split_logits[2])) +
tf.reduce_sum(tf.nn.softmax_cross_entropy_with_logits_v2(labels=split_train_labels[3], logits=split_logits[3])) +
tf.reduce_sum(tf.nn.softmax_cross_entropy_with_logits_v2(labels=split_train_labels[4], logits=split_logits[4]))
Но здесь у меня проблема. Пока у меня есть строка только для первого (0-го) символа, все работает очень хорошо (но, очевидно, только учится распознавать первую букву). Как только я добавляю термин для другой буквы или просто имею одну другую букву вместо первой, сеть сразу же застаивается после начала обучения и никогда не улучшает производительность выше случайной.
Я не уверен, как это исправить. Я думаю, что проблема либо в моей функции потерь, либо в структуре сети.
Проблема в том, что все буквы, кроме первой, сильно варьируются по их положению из-за различных размеров/ширин шрифтов? Я понимаю, что сверточные сети были бы гораздо лучше для этого, но я действительно хочу сначала попробовать с полностью связанными сетями.
Другой способ сформулировать проблему — это предсказание одиночного символа. Если вы можете разделить изображение на отдельные символы, предсказывайте каждый символ отдельно.
Если вы сформулируете проблему таким образом, наиболее подходящей функцией потерь будет softmax. Сеть будет учиться предсказывать относительную вероятность каждого из 36 символов.
Ответ или решение
Использование функции потерь кросс-энтропии для распознавания символов капчи
Для решения задачи распознавания символов капчи с помощью нейронной сети полностью связного типа (fully-connected neural network) важно правильно выбрать архитектуру и функцию потерь. Я подробно объясню, как использовать кросс-энтропию для оценки работоспособности вашего решателя капчи.
1. Подготовка данных
Вы правильно подходите к подготовке данных, разделяя метки на отдельные символы и кодируя их в формате one-hot. Учитывая, что каждый символ может принимать одно из 36 значений (26 букв и 10 цифр), необходимо построить выходной слой модели с 5 * 36 нейронами (по 36 нейронов для каждого из 5 символов).
Примерно это будет выглядеть так:
- Для каждой капчи "AB4D1" создайте один горячий вектор длиной 180 (5 * 36), который соответствует 5 символам, где единица указывает на конкретный класс.
2. Выбор функции потерь
Хотя функция потерь среднего квадратичного отклонения (mean squared error, MSE) используется в регрессионных задачах, в задаче классификации более подходящей является кросс-энтропия, поскольку она приспособлена для работы с вероятностными распределениями и двухклассовыми или многоклассовыми задачами.
Кросс-энтропия в вашем случае
Каждый символ должен обрабатываться отдельно. Вы правы, что нужно использовать функцию softmax, чтобы получить вероятностное распределение по классам:
- Необходимо разделить выход модели на 5 частей, каждая из которых будет соответствовать одному символу:
split_logits = tf.split(logits, num_or_size_splits=5, axis=1)
split_train_labels = tf.split(tf_train_labels, num_or_size_splits=5, axis=1)
- Используйте кросс-энтропию для каждого символа.
3. Проблема с обучением
Вы упомянули, что когда вы добавляете потери для каждого символа, сеть перестает улучшаться. Это может быть связано с несколькими факторами:
-
Масштабирование весов: Возможно, вам стоит попробовать нормализовать свои входные данные или изменить скорость обучения. Если обучение слишком быстрое, возможно, нейронная сеть начинает "платье" из-за больших градиентов.
-
Необходимиая мощность модели: Убедитесь, что ваша архитектура соответствует сложности задачи. Попробуйте увеличить количество нейронов или слоев. Нейронная сеть могла "застрять" на локальном минимуме. Регуляризация (например, путем добавления дропаутов) также может помочь.
-
Разделение на слои: Важно внимательно следить за тем, чтобы идентифицировать, как ваша сеть обрабатывает каждый отдельный символ. Смещённость в одном из символов (например, если все примеры для него неравномерны) может сказаться на всей сети.
4. Альтернативные подходы
Несмотря на то, что вы хотите использовать лишь полностью связанные сети, стоит учитывать возможность применения свёрточных нейронных сетей (CNN), которые лучше подходят для работы с изображениями и могут значительно улучшить результаты в подобной задаче. Это связано с их способностью улавливать пространственные временные зависимости.
Заключение
Ваша задача – найти оптимальный баланс между архитектурой нейронной сети, функцией потерь и параметрами обучения. Следуя изложенным шагам, вы сможете правильно использовать функцию потерь кросс-энтропии для построения эффективного решателя капчи. Удачи в вашем проекте!