Может ли поиск луча с префиксом, обычно используемый при распознавании речи с CT C, быть реализован таким простым способом? - PullRequest
0 голосов
/ 06 февраля 2020

Недавно я узнал о распознавании речи и узнал, что идея поиска луча по префиксу состоит в объединении путей с одинаковым префиксом, таким как [1,1,_] и [_,1,_] (как вы можете см. _ обозначает пустую отметку).

Исходя из этого понимания, я реализовал свою версию, которую можно упростить с помощью псевдокода, например:

def prefix_beam_search(y, beam_size, blank):
    seq_len, n_class = y.shape
    logY = np.log(y)
    beam = [([], 0)]

    for t in range(seq_len):
        buff = []
        for prefix, p in beam:
            for i in range(n_class):
                new_prefix = list(prefix) + [i]
                new_p = p + logY[t][i]
                buff.append((new_prefix, new_p))

        # merge the paths with same prefix'
        new_beam = defaultdict(lambda: ninf)
        for prefix, p in buff:
            # 'norm_prefix' can simplify the path, [1,1,_,2] ==> [1,2]
            # However, the ending 'blank' is retained, [1,1,_] ==> [1,_]
            prefix = norm_prefix(prefix, blank)
            new_beam[prefix] = logsumexp(new_beam[prefix], p)

        # choose the best paths
        new_beam = sorted(new_beam.items(), key=lambda x: x[1], reverse=True)
        beam = new_beam[: beam_size]

    return beam

Но большинство версии, которые я нашел в Интернете (согласно статье), выглядят так:

def _prefix_beam_decode(y, beam_size, blank):
    T, V = y.shape
    log_y = np.log(y)
    beam = [(tuple(), (0, ninf))]

    for t in range(T):
        new_beam = defaultdict(lambda: (ninf, ninf))
        for prefix, (p_b, p_nb) in beam:
            for i in range(V):
                p = log_y[t, i]
                if i == blank:
                    new_p_b, new_p_nb = new_beam[prefix]
                    new_p_b = logsumexp(new_p_b, p_b + p, p_nb + p)
                    new_beam[prefix] = (new_p_b, new_p_nb)
                    continue
                end_t = prefix[-1] if prefix else None
                new_prefix = prefix + (i,)
                new_p_b, new_p_nb = new_beam[new_prefix]
                if i != end_t:
                    new_p_nb = logsumexp(new_p_nb, p_b + p, p_nb + p)
                else:
                    new_p_nb = logsumexp(new_p_nb, p_b + p)
                new_beam[new_prefix] = (new_p_b, new_p_nb)
                if i == end_t:
                    new_p_b, new_p_nb = new_beam[prefix]
                    new_p_nb = logsumexp(new_p_nb, p_nb + p)
                    new_beam[prefix] = (new_p_b, new_p_nb)

        beam = sorted(new_beam.items(), key=lambda x: logsumexp(*x[1]), reverse=True)
        beam = beam[:beam_size]
    return beam

Результаты двух разных, и моя версия имеет тенденцию возвращать более длинные строки. И я не совсем понимаю основные два аспекта:

  1. Есть ли какие-либо детали моей версии, которые не вдумчивы?
  2. Общая версия при генерации новый префикс new_prefix = prefix + (i,) независимо от того, совпадают ли конец предыдущего с заданным 's'. Например, старый префикс [a,a,b] и при добавлении нового символа s сохраняются как [a,a,b], так и [a,a,b,b]. Какова цель, если это? И вызывает ли это двойной счет?

Ждем Вашего ответа, заранее спасибо!

1 Ответ

0 голосов
/ 30 марта 2020

Когда вы выбираете лучшие пути в своем коде, вы не хотите проводить различие между [1, _] и [1], так как оба соответствуют одному и тому же префиксу [1].

Если у вас есть например:

[1], [1, _], [1,2]

, тогда вы хотите, чтобы вероятность [1] и [1, _] оба имели сумму из двух.

вероятность ([1]) = вероятность ([1]) + вероятность ([1, _])

вероятность ([1, _]) = вероятность ([ 1]) + вероятность ([1, _])

И после сортировки с этими вероятностями вы можете захотеть сохранить так много, что число истинных префиксов будет beam_size.

Например, у вас есть [1], [1, _], [2], [3].

Из которых вероятности: 0,1, 0,08, 0,11, 0,15

Тогда вероятности, с которыми вы хочу отсортировать их:

0,18, 0,18, 0,11, 0,15 соответственно (0,18 = 0,1 + 0,08)

отсортировано: [1]: 0,18, [1, _]: 0,18, [3]: 0,15, [2]: 0,11

А если у вас есть, например, beam_size 2, вы можете оставить

[1], [1, _] и [3], чтобы в вашем луче было 2 префикса, поскольку [1] и [1, _] считаются одним и тем же префиксом.

...