Алгоритм поиска луча для декодирования выходного сигнала RNN
Я пытался понять логику, используемую алгоритмом поиска луча в автоматическом распознавании речи для части декодирования. Документы, которыми я пытался следовать, - это непрерывное распознавание речи с большим словарным запасом в первом проходе с использованием двунаправленных рекуррентных DNN, распознавание речи без разговоров без лексики с нейронными сетями и сквозное распознавание речи с рекуррентными нейронными сетями. Проблема в том, что идея алгоритма не так проста, и в псевдокоде, представленном в статьях, много опечаток. Кроме того, эта реализация из второй статьи невероятно трудна для отслеживания, и эта, из последней упомянутой статьи, не включает в себя языковую модель.
Это моя реализация в Python, которая терпит неудачу из-за некоторых отсутствующих вероятностей:
class BeamSearch(object):
"""
Decoder for audio to text.
From: https://arxiv.org/pdf/1408.2873.pdf (hardcoded)
"""
def __init__(self, alphabet='" abcdefghijklmnopqrstuvwxyz'):
# blank symbol plus alphabet
self.alphabet = '-' + alphabet
# index of each char
self.char_to_index = {c: i for i, c in enumerate(self.alphabet)}
def decode(self, probs, k=100):
"""
Decoder.
:param probs: matrix of size Windows X AlphaLength
:param k: beam size
:returns: most probable prefix in A_prev
"""
# List of prefixs, initialized with empty char
A_prev = ['']
# Probability of a prefix at windows time t to ending in blank
p_b = {('', 0): 1.0}
# Probability of a prefix at windows time t to not ending in blank
p_nb = {('', 0): 0.0}
# for each time window t
for t in range(1, probs.shape[0] + 1):
A_new = []
# for each prefix
for s in Z:
for c in self.alphabet:
if c == '-':
p_b[(s, t)] = probs[t-1][self.char_to_index[self.blank]] *\
(p_b[(s, t-1)] +\
p_nb[(s, t-1)])
A_new.append(s)
else:
s_new = s + c
# repeated chars
if len(s) > 0 and c == s[-1]:
p_nb[(s_new, t)] = probs[t-1][self.char_to_index[c]] *\
p_b[(s, t-1)]
p_nb[(s, t)] = probs[t-1][self.char_to_index[c]] *\
p_b[(s, t-1)]
# spaces
elif c == ' ':
p_nb[(s_new, t)] = probs[t-1][self.char_to_index[c]] *\
(p_b[(s, t-1)] +\
p_nb[(s, t-1)])
else:
p_nb[(s_new, t)] = probs[t-1][self.char_to_index[c]] *\
(p_b[(s, t-1)] +\
p_nb[(s, t-1)])
p_nb[(s, t)] = probs[t-1][self.char_to_index[c]] *\
(p_b[(s, t-1)] +\
p_nb[(s, t-1)])
if s_new not in A_prev:
p_b[(s_new, t)] = probs[t-1][self.char_to_index[self.blank]] *\
(p_b[(s, t-1)] +\
p_nb[(s, t-1)])
p_nb[(s_new, t)] = probs[t-1][self.char_to_index[c]] *\
p_nb[(s, t-1)]
A_new.append(s_new)
A = A_new
s_probs = map(lambda x: (x, (p_b[(x, t)] + p_nb[(x, t)])*len(x)), A_new)
xs = sorted(s_probs, key=lambda x: x[1], reverse=True)[:k]
Z, best_probs = zip(*xs)
return Z[0], best_probs[0]
Любая помощь будет по достоинству оценена.
2 ответа
Я реализовал поиск луча, используя инициализацию -inf, а также следовал алгоритму ctc_beam_search из статьи http://proceedings.mlr.press/v32/graves14.pdf... это почти аналогично этому, за исключением обновления p_b для символов... алгоритм работает правильно... даже этот алгоритм будет работать, если инициализация присутствует..
A_prev = ['']
p_b[('',0)] = 1
p_nb[('',0)] = 0
for alphabet in alphabets:
p_b[(alphabet,0)] = -float("inf")
p_nb[(alphabet,0)] = -float("inf")
for t in range(1,probs.shape[0] +1):
A_new = []
for s in A_prev:
if s!='':
try:
p_nb[(s,t)] = p_nb[(s,t-1)]*probs[t-1][char_map[s[-1:]]]
except:
p_nb[(s,t)] = p_nb[(s,t-1)]*probs[t-1][char_map['<SPACE>']]*pW(s)
if s[:-1] in A_prev:
p_nb[(s,t)] = p_nb[(s,t)]+pr(probs[t-1],s[-1:],s[:-1],t)
p_b[(s,t)] = (p_nb[(s,t-1)]+p_b[(s,t-1)])*probs[t-1][0]
if s=='':
p_nb[(s,t)] = 0
if s not in A_new:
A_new.append(s)
for c in alphabets:
s_new = s+c
p_b[(s_new,t)] = 0
p_nb[(s_new,t)] = pr(probs[t-1],c,s,t)
#print s_new,' ',p_nb[(s_new,t)]
if s_new not in A_new:
A_new.append(s_new)
s_probs = map(lambda x: (x,(p_b[(x, t)]+ p_nb[(x, t)])), A_new)
Проблема в том, что блок с s_new, отсутствующим в A_prev, ссылается на вероятности, которые не существуют для новой сгенерированной строки. Инициализируйте с -float("inf") для предыдущего временного шага новых строк, т.е. s_new, t-1. Вы можете поместить блок try catch, где, если p[(s_new,t-1)] не существует, он будет использовать -infinity.