[Перевод] Потоковая передача RNN в TensorFlow

искусственный интеллект TensorFlow Программа перевода самородков GitHub

Исследовательский институт MozillaКоманда машинного обучения разрабатывает механизм автоматического распознавания речи, который будет служитьПроект DeepSpeechВ рамках своих усилий по открытию технологии распознавания речи и предварительно обученных моделей для разработчиков. Мы прилагаем все усилия, чтобы улучшить производительность и упростить использование нашего механизма преобразования речи в текст с открытым исходным кодом. Предстоящий выпуск 0.2 будет включать в себя долгожданную функцию: возможность распознавания речи в режиме реального времени во время записи звука. В этом сообщении блога описывается, как мы модифицировали архитектуру механизма STT (то есть преобразования речи в текст) для достижения требований к производительности транскрипции в реальном времени. Вскоре, когда будет выпущена официальная версия, вы сможете воспользоваться этой функцией преобразования звука.

При применении нейронных сетей к последовательным данным, таким как аудио или текст, важно фиксировать закономерности в данных с течением времени. Рекуррентные нейронные сети (RNN) — это нейронные сети с «памятью» — они принимают на вход не только очередной элемент данных, но и состояние, которое развивается во времени, и используют это состояние для захвата зависимых от времени паттернов. Иногда вы можете захотеть зафиксировать закономерности, которые зависят от будущих данных. Один из способов решить эту проблему — использовать два RNN, один из которых движется вперед во времени, а другой — назад во времени (т. е. начиная с последнего элемента данных и переходя к первому). ты сможешьЭта статья Криса ОлаУзнайте больше о RNN (и о конкретных типах RNN, используемых в DeepSpeech) в .

Использование двунаправленных RNN

Текущая версия DeepSpeech (Обсуждалось ранее на Hacks) используется сTensorFlowРеализована двунаправленная RNN, что означает, что перед началом работы ему необходимо иметь доступ ко всем входным данным. Один из способов улучшить это — реализовать поточную модель: работайте порциями по мере поступления данных, чтобы, когда ввод заканчивается, модель уже обрабатывала его и могла быстрее выдавать результаты. Вы также можете попытаться увидеть частичные результаты в процессе набора текста.

This animation shows how the data flows through the network. Data flows from the audio input to feature computation, through three fully connected layers. Then it goes through a bidirectional RNN layer, and finally through a final fully connected layer, where a prediction is made for a single time step.

Эта анимация показывает, как данные передаются по сети. Данные преобразуются из аудиовхода в вычисление признаков через три полностью связанных слоя. Затем он проходит через двунаправленный уровень RNN и, наконец, через полностью связанный уровень, который делает прогнозы для одного временного шага.

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

Видно, что в двунаправленной RNN для расчета предпоследнего шага требуются данные последнего шага, а для расчета предпоследнего шага требуются данные предпоследнего шага. ... и так далее. Это красные стрелки справа налево на рисунке.

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

Потоковая передача с однонаправленными RNN

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

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

An alternative architecture that uses a unidirectional RNN in which each time step only depends on the input at that time and the state from the previous step.

Альтернативная архитектура, использующая однонаправленную RNN, где каждый временной шаг зависит только от непосредственного ввода и состояния предыдущего шага.

Вот код для создания графа вывода, который отслеживает состояние между каждым входным окном:

import tensorflow as tf

def create_inference_graph(batch_size=1, n_steps=16, n_features=26, width=64):
    input_ph = tf.placeholder(dtype=tf.float32,
                              shape=[batch_size, n_steps, n_features],
                              name='input')
    sequence_lengths = tf.placeholder(dtype=tf.int32,
                                      shape=[batch_size],
                                      name='input_lengths')
    previous_state_c = tf.get_variable(dtype=tf.float32,
                                       shape=[batch_size, width],
                                       name='previous_state_c')
    previous_state_h = tf.get_variable(dtype=tf.float32,
                                       shape=[batch_size, width],
                                       name='previous_state_h')
    previous_state = tf.contrib.rnn.LSTMStateTuple(previous_state_c, previous_state_h)

    # 从以批次为主转置成以时间为主
    input_ = tf.transpose(input_ph, [1, 0, 2])

    # 展开以契合前馈层的维度
    input_ = tf.reshape(input_, [batch_size*n_steps, n_features])

    # 三个隐含的 ReLU 层
    layer1 = tf.contrib.layers.fully_connected(input_, width)
    layer2 = tf.contrib.layers.fully_connected(layer1, width)
    layer3 = tf.contrib.layers.fully_connected(layer2, width)

    # 单向 LSTM
    rnn_cell = tf.contrib.rnn.LSTMBlockFusedCell(width)
    rnn, new_state = rnn_cell(layer3, initial_state=previous_state)
    new_state_c, new_state_h = new_state

    # 最终的隐含层
    layer5 = tf.contrib.layers.fully_connected(rnn, width)

    # 输出层
    output = tf.contrib.layers.fully_connected(layer5, ALPHABET_SIZE+1, activation_fn=None)

    # 用新的状态自动更新原先的状态
    state_update_ops = [
        tf.assign(previous_state_c, new_state_c),
        tf.assign(previous_state_h, new_state_h)
    ]
    with tf.control_dependencies(state_update_ops):
        logits = tf.identity(logits, name='logits')

    # 创建初始化状态
    zero_state = tf.zeros([batch_size, n_cell_dim], tf.float32)
    initialize_c = tf.assign(previous_state_c, zero_state)
    initialize_h = tf.assign(previous_state_h, zero_state)
    initialize_state = tf.group(initialize_c, initialize_h, name='initialize_state')

    return {
        'inputs': {
            'input': input_ph,
            'input_lengths': sequence_lengths,
        },
        'outputs': {
            'output': logits,
            'initialize_state': initialize_state,
        }
    }

Граф, созданный приведенным выше кодом, имеет два входа и два выхода. На вход подается последовательность и ее длина. выводlogitи специальный узел, который нужно запустить в новой последовательностиinitialize_state. При закреплении изображения убедитесь, что переменная состояния не затвердевает.previous_state_hиprevious_state_c.

Вот код затвердевшего изображения:

from tensorflow.python.tools import freeze_graph

freeze_graph.freeze_graph_with_def_protos(
        input_graph_def=session.graph_def,
        input_saver_def=saver.as_saver_def(),
        input_checkpoint=checkpoint_path,
        output_node_names='logits,initialize_state',
        restore_op_name=None,
        filename_tensor_name=None,
        output_graph=output_graph_path,
        initializer_nodes='',
        variable_names_blacklist='previous_state_c,previous_state_h')

С указанными выше изменениями в модели мы можем предпринять следующие шаги на стороне клиента:

  1. бегатьinitialize_stateузел.
  2. Накапливайте аудиосэмплы до тех пор, пока не будет достаточно данных для подачи модели (мы используем 16 временных шагов или 320 мс).
  3. Подавайте данные в модель, где-то накапливайте выходные данные.
  4. Повторяйте шаги 2 и 3 до конца данных.

Нет смысла бросать читателю сотни строк клиентского кода, но если вам интересно, посмотрите его.Код в GitHub, эти коды соответствуют соглашению MPL 2.0. На самом деле у нас есть реализации на двух разных языках, одна сPython, используемый для создания отчетов об испытаниях; другой используетсяC++, который является нашим официальным клиентским API.

повышение производительности

Как эти архитектурные изменения повлияют на наш движок STT? Вот некоторые цифры по сравнению с текущей стабильной версией:

  • Размер модели уменьшен с 468 МБ до 180 МБ.
  • Время транскрипции: 3-секундный файл, запущенный на процессоре ноутбука, занял время с 9 до 1,5 с.
  • Пиковое использование кучи уменьшено с 4 ГБ до 20 МБ (модель теперь отображается в памяти)
  • Общее выделение памяти кучи уменьшено с 12 ГБ до 264 МБ.

Я думаю, что наиболее важным моментом является то, что теперь мы можем получать ставки в реальном времени без использования графического процессора, что вместе с потоковым выводом открывает множество новых возможностей использования, таких как живые субтитры для радиошоу, потоки Twitch и основные презентации, домашняя автоматизация, голос. основанный на пользовательском интерфейсе; бла-бла-бла. Если вы хотите включить распознавание речи в свой следующий проект, рассмотрите возможность использования нашего движка!

Ниже приведена небольшая программа на Python, которая демонстрирует, как использовать библиотеку libSoX для вызова микрофона для записи и подачи его в движок во время записи звука.

import argparse
import deepspeech as ds
import numpy as np
import shlex
import subprocess
import sys

parser = argparse.ArgumentParser(description='DeepSpeech speech-to-text from microphone')
parser.add_argument('--model', required=True,
                    help='Path to the model (protocol buffer binary file)')
parser.add_argument('--alphabet', required=True,
                    help='Path to the configuration file specifying the alphabet used by the network')
parser.add_argument('--lm', nargs='?',
                    help='Path to the language model binary file')
parser.add_argument('--trie', nargs='?',
                    help='Path to the language model trie file created with native_client/generate_trie')
args = parser.parse_args()

LM_WEIGHT = 1.50
VALID_WORD_COUNT_WEIGHT = 2.25
N_FEATURES = 26
N_CONTEXT = 9
BEAM_WIDTH = 512

print('Initializing model...')

model = ds.Model(args.model, N_FEATURES, N_CONTEXT, args.alphabet, BEAM_WIDTH)
if args.lm and args.trie:
    model.enableDecoderWithLM(args.alphabet,
                              args.lm,
                              args.trie,
                              LM_WEIGHT,
                              VALID_WORD_COUNT_WEIGHT)
sctx = model.setupStream()

subproc = subprocess.Popen(shlex.split('rec -q -V0 -e signed -L -c 1 -b 16 -r 16k -t raw - gain -2'),
                           stdout=subprocess.PIPE,
                           bufsize=0)
print('You can start speaking now. Press Control-C to stop recording.')

try:
    while True:
        data = subproc.stdout.read(512)
        model.feedAudioContent(sctx, np.frombuffer(data, np.int16))
except KeyboardInterrupt:
    print('Transcription:', model.finishStream(sctx))
    subproc.terminate()
    subproc.wait()

Наконец, если вы хотите внести свой вклад в проект Deep Speech, у нас есть много возможностей. Кодовая база написана на Python и C++, и мы добавим поддержку для iOS и Windows. через нашIRC-каналили нашДискуссионный форумПриходите и свяжитесь с нами.

О Рубене Мораисе

Рубен — инженер группы машинного обучения в Исследовательском институте Мужи.

Другие статьи Рубена Мораиса…

Если вы обнаружите ошибки в переводе или в других областях, требующих доработки, добро пожаловать наПрограмма перевода самородковВы также можете получить соответствующие бонусные баллы за доработку перевода и PR. начало статьиПостоянная ссылка на эту статьюЭто ссылка MarkDown этой статьи на GitHub.


Программа перевода самородковэто сообщество, которое переводит высококачественные технические статьи из Интернета сНаггетсДелитесь статьями на английском языке на . Охват контентаAndroid,iOS,внешний интерфейс,задняя часть,блокчейн,продукт,дизайн,искусственный интеллектЕсли вы хотите видеть более качественные переводы, пожалуйста, продолжайте обращать вниманиеПрограмма перевода самородков,официальный Вейбо,Знай колонку.