Близкие!

Сегодня я собираюсь поговорить о теме, на понимание которой у меня ушло много времени (на самом деле очень много времени), как мотивации использования, так и его работы - Connectionist Temporal Classification (CTC).

Прежде чем говорить о CTC, давайте сначала разберемся, что такое модели Sequence-to-Sequence :-)

Модели последовательность-последовательность:. Это нейронные сети, которые принимают любую последовательность - символы, слова, пиксели изображения и выдают выходную последовательность, которая может находиться в том же домене или другой как вход.

Несколько примеров моделей от последовательности к последовательности:

  1. Проблема языкового перевода: ввод - это последовательность слов (предложение) на одном языке, а вывод - это еще одна последовательность слов на другом языке.
  2. Проблема распознавания слов: входные данные - это изображение, содержащее только слово, которое затем преобразуется в последовательность векторов признаков (столбцы карты признаков, выступающие в данном случае как измерение времени), а выходными данными являются последовательность символов, присутствующих в изображении слова.
  3. Проблема преобразования речи в текст - где вход - это аудиопоследовательность, а выход - последовательность фонем.

Рекуррентные нейронные сети обычно используются для таких задач последовательность в последовательность, поскольку сеть должна учитывать не только текущий ввод, но также вводы до и после, чтобы делать прогнозы. .

Какое же отношение модели «последовательность-последовательность» имеют к временной классификации коннекционистов (CTC)? Что ж, CTC - это алгоритм, используемый для решения ключевой проблемы, возникающей при обучении моделей seq-2-seq, а именно, когда длины входных и выходных последовательностей не совпадают.

Позвольте мне углубиться в проблему, над которой я работал раньше, чтобы раскрыть детали CTC :-)

Распознавание слов. Проблема состоит в том, чтобы предугадать слово, присутствующее в изображении. Один из способов решить эту проблему - рассматривать ее как проблему от последовательности к последовательности и использовать гибридную архитектуру сверточных и рекуррентных нейронных сетей. Архитектура CRNN следующая:

Здесь входная последовательность - это сверточная карта признаков, а выходная - последовательность символов.

Чтобы лучше понять, что именно происходит, позвольте мне объяснить поток:

  1. Conv Net принимает изображение входного слова, скажем, размером WxHx3, и создает сверточный объем объекта, скажем, размером W / 2 x H / 2 x D, где D - глубина объема объекта.
  2. Теперь каждый столбец размером 1 x H / 2 x D подается в качестве входных данных (обратите внимание, что таких столбцов W / 2) в RNN, который, в свою очередь, создает символ из словаря, установленного в качестве выходных данных.
  3. Таким образом, CRNN пытается предсказать символы, присутствующие в каждом столбце карты функций. Обратите внимание, что столбец может содержать или не содержать символ, и поэтому используется еще одна метка вывода - пустое поле «-» (проверьте вывод на изображении выше).

Можно спросить, какая здесь может быть временная зависимость в случае изображений. Что ж, поскольку карты функций разделены и прогнозы делаются для каждого столбца, маловероятно, что один полный символ попадет в один столбец. Следовательно, изучение паттернов персонажа зависит не только от текущего столбца, но и от столбцов до и после него.

Давайте посмотрим на вывод RNN, чтобы прояснить ситуацию.

Обратите внимание, что декодированный вывод (3) формируется путем свертывания повторяющихся символов и удаления пустого символа (если есть) из предсказанной последовательности (2), исходящей из RNN.

Для ясности позвольте мне заранее определить несколько вещей:

а. input_sequence: последовательность input длиной W / 2, которая передается в RNN. .

б. предсказанная_последовательность: предсказанная последовательность длиной Вт / 2, которая является результатом от РНН.

c. target_sequence: последовательность достоверных результатов длиной Вт / 2.

d. decoded_sequence: декодированный результат последовательность длины ≤ W / 2.

При обучении такой сети все, что у нас есть, это ‹1. входное изображение, 3. декодированный выход › пара в качестве учебного экземпляра. Но нейронная сеть выдает predicted_sequence как результат, длина которого не равна длине декодированного вывода.

predicted_sequence длины W / 2 не может сравниваться с decoded_sequence длины ≤ W / 2 при вычислении потерь.

  • Примечание. Мы предполагаем, что входная и прогнозируемая последовательности синхронны. т.е. порядок будет таким же.

Проблема: Итак, чтобы обучить эту сеть, нам нужно знать, что такое target_sequence. Но как мы собираемся получить эту target_sequence? Конечно, мы не можем аннотировать каждый временной шаг в последовательности длиной W / 2 (это невозможно!).

Решение. Угадайте target_sequence, используя разумную эвристику. Да, вы правильно прочитали. Сделайте первоначальное предположение для target_sequence. Рассмотрим входное изображение выше, каков декодированный выход для этого входа?

декодированный вывод → «СОСТОЯНИЕ»

Скажем, длина target_sequence / predicted_sequence (т.е. выход из RNN) равна 14. Теперь давайте предположим, что target_sequence будет «SSSTTAAAATTEEE» ( мы рассмотрим символ пустой позже).

Учитывая, что у нас есть target_sequence, мы можем обучить сеть этой последовательностью для первого шага, затем использовать предсказания сети в качестве новой target_sequence (для следующего шага) и повторять ее снова и снова во время обучения - Iterate и Estimate.

Но здесь есть одна ключевая проблема. Как мы можем гарантировать, что предсказанная сетью target_sequence при декодировании дает нам желаемый результат, «СОСТОЯНИЕ» в нашем случае?

Что, если RNN выдаст «RRRRAAAEEELLLL»? Мы не можем использовать эту выходную последовательность в качестве новой target_sequence для следующего шага в процессе обучения. Следовательно, нам нужно наложить несколько ограничений на последовательность, предсказываемую RNN.

Ограничение 1: вместо того, чтобы принимать во внимание предсказания всего словаря (алфавиты от A до Z), учитывайте только те алфавиты, которые являются частью декодированного вывода.

В нашем случае алфавиты S, T, A, E являются частью декодированного вывода «СОСТОЯНИЕ».

Хорошо, теперь это гарантирует, что наша сеть создаст predicted_sequence, содержащий только алфавиты в декодированном выводе. Но нет никаких ограничений на последовательность, в которой должны выводиться алфавиты.

Например: RNN выводит «SSAAAATTTAAEEE», который по-прежнему содержит только целевые алфавиты, но при декодировании производит SATAE вместо STATE..

Ограничение 2. Чтобы гарантировать, что сеть выводит последовательность, которая при декодировании дает желаемый свернутый вывод, давайте наложим следующее ограничение:

Учитывайте не только алфавиты S, T, A, E, но и упорядочивайте отдельные алфавиты в той последовательности, в которой они встречаются в декодированном выводе (даже если алфавиты должны повторяться).

И теперь исправьте, что первым выводом всегда должен быть верхний левый символ, а последним выводом всегда должен быть нижний правый символ, а последовательность всегда должна быть строго сверху вниз, т.е. без движения вверх. Каждый путь от верхнего левого символа к нижнему правому символу считается допустимой последовательностью / выравниванием, поскольку он всегда будет декодироваться в допустимую «decoded_sequence».

Визуализируйте каждый выходной символ на каждом временном шаге как узел в графе и ребра, представленные стрелками. Оценка каждого узла - это его вероятность от сети на данном временном шаге, а оценки ребер равны 1. Оценка последовательности (или пути) является произведением вероятностей всех узлов, содержащихся в ней.

Подзадача: учитывая набор таких допустимых последовательностей, найти наиболее вероятную последовательность?

Решение: Эта проблема решается с помощью алгоритма Витерби (динамическое программирование).

Убедившись, что предсказанная последовательность является допустимой последовательностью (или выравниванием), мы можем использовать подход ITERATE и ESTIMATE для обучения сети. Можно также использовать предварительно обученную RNN, обученную для аналогичной задачи, чтобы сделать процесс обучения более надежным.

Но все это во многом зависит от того, как угадывается начальная целевая последовательность, а также от того, как вычисляется target_sequence во время обучения (максимально вероятная последовательность с использованием алгоритма Витерби). В результате этот подход неоптимален.

Вместо выбора наиболее вероятной последовательности мы используем математическое ожидание по всем возможным действительным последовательностям. Это снова можно вычислить с помощью динамического программирования, а используемый алгоритм известен как алгоритм CTC-вперед-назад. Я не буду вдаваться в подробности алгоритма Вперед-Назад / Витерби, поскольку для меня это все еще серая зона.

Наконец, рассмотрим случай, когда на выходе есть повторяющиеся символы. Пусть выходное слово будет «ПИСЬМО», а предсказанная_последовательность - «LLLLEETTTTEERR». Что происходит, когда эта предсказанная_последовательность свернута?

Мы получаем декодированный вывод как «LETER», что нежелательно.

Поэтому, чтобы учесть повторяющиеся символы в выходном домене, пробел (-) включен как часть словаря. Обычно нейронная сеть выводит пустой символ, когда ни один из символов в словаре отсутствует на этом временном шаге.

Таким образом, вместо создания «LLLLEETTTTEERR» RNN с пробелом как часть словаря теперь может выводить «- L-EE-TT-TER» , который при декодировании дает «ПИСЬМО».

Поскольку включен пробел и он может встретиться где угодно в target_sequence, мы располагаем целевые алфавиты + пробел следующим образом:

Теперь позвольте мне разбить термин «коннекционистская временная классификация», чтобы понять, что он на самом деле означает?

Наше решение теперь может маркировать (или прогнозировать) каждый временной шаг в последовательности. Эта задача маркировки несегментированных последовательностей данных называется временной классификацией, и, поскольку мы используем RNN для маркировки этих несегментированных данных, она известна как временная классификация Connectionist [1].

[1], как указано в разделе Временная классификация коннекционистов: маркировка несегментированных данных последовательностей с помощью рекуррентных нейронных сетей - Грейвс и др.

Чтобы было понятнее, здесь несегментированная последовательность данных - это наш декодированный вывод, сегментированная последовательность данных будет target_sequence, и мы используем RNN для ее маркировки.

Я надеюсь, что у всех вас есть интуиция относительно того, как нейронные сети обучаются для такой последовательности для задач последовательности, где длина входной и выходной последовательности не совпадает.

Мой пост в блоге вдохновлен этой блестящей лекцией CMU о CTC: https://www.youtube.com/watch?v=c86gfVGcvh4. Взгляните на это!

Ваше здоровье :-)