Deep learning CTC语音识别中常用的前缀波束搜索能否以这样简单的方式实现?

Deep learning CTC语音识别中常用的前缀波束搜索能否以这样简单的方式实现?,deep-learning,speech-recognition,ctc,beam-search,Deep Learning,Speech Recognition,Ctc,Beam Search,我最近在学习语音识别,我了解到,前缀波束搜索的思想是合并具有相同前缀的路径,例如[1,1,和[\u1,\u](如您所见,\u表示空白标记) 基于这种理解,我实现了我的一个版本,可以使用如下伪代码简化: 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):

我最近在学习语音识别,我了解到,前缀波束搜索的思想是合并具有相同前缀的路径,例如
[1,1,
[\u1,\u]
(如您所见,
\u
表示空白标记)

基于这种理解,我实现了我的一个版本,可以使用如下伪代码简化:

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
def prefix_beam_搜索(y,beam_大小,空白):
seq_len,n_class=y形
logY=np.log(y)
梁=[([],0)]
对于范围内的t(如下所示):
buff=[]
对于梁中的前缀p:
对于范围内的i(n_类):
新前缀=列表(前缀)+[i]
新p=p+logY[t][i]
buff.append((新的前缀,新的前缀))
#合并具有相同前缀的路径'
新光束=defaultdict(λ:ninf)
对于前缀,buff中的p:
#“norm_prefix”可以简化路径[1,1,2]===>[1,2]
#但是,结尾的“空白”被保留,[1,1,]==>[1,]
前缀=标准前缀(前缀,空白)
新波束[前缀]=logsumexp(新波束[前缀],p)
#选择最佳路径
new_beam=已排序(new_beam.items(),key=lambda x:x[1],reverse=True)
梁=新梁[:梁尺寸]
回程光束
但我在网上找到的大多数版本(根据报纸)都是这样的:

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
两者的结果是不同的,我的版本倾向于返回更长的字符串。我不太了解主要的两个方面:

  • 我的版本是否有任何细节不够周全?
  • 通过
    new\u prefix=prefix+(i,)
    生成新前缀时的通用版本,无论前一个前缀的结尾是否与给定的“s”相同。例如,旧的前缀是
    [a,a,b]
    ,当添加新字符s时,
    [a,a,b]
    [a,a,b,b]
    都被保存这样做的目的是什么?这会导致重复计算吗?

  • 期待您的回答,提前谢谢

    当您在代码中选择最佳路径时,您不想区分[1、[1]和[1],因为它们都对应于相同的前缀[1]

    例如,如果您有:

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

    那么你想让[1]和[1,u]的概率都是这两者的和

    概率([1])=概率([1])+概率([1,u])

    概率([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

    例如,如果您有尺寸为2的梁,那么您可能希望保留

    [1] ,[1,u]和[3],因此波束中有两个前缀,因为[1]和[1,u]算作相同的前缀(只要下一个字符不是1-这就是我们分别跟踪[1]和[1,]的原因)