Beam Search解码

Beam Search(束搜索)是一种在搜索空间中寻找最优解的算法。它常用于自然语言处理任务中,如机器翻译和语言生成。Beam Search通过在每个时间步选择概率最高的一组候选解来进行搜索,以此来寻找最有可能的解。

Beam Search的原理如下:

(1)首先,根据模型的输出概率分布,选择概率最高的K个候选解作为初始解集。

(2)在每个时间步,对于每个候选解,根据模型的输出概率分布,生成K个新的候选解。

(3)对于生成的新候选解,根据其得分(通常是概率的对数)进行排序,并选择得分最高的K个候选解作为下一步的候选解集。

(4)重复步骤2和步骤3,直到达到指定的搜索深度或满足终止条件为止。

(5)最后,从最终的候选解集中选择得分最高的解作为最终的输出。

1、数据准备

# 生成一个数组,在这个数组中寻求最优解。其中每一行代表着一个时刻。
#  t1   data00  data01  data02  data03  data04  data05
#  t2   data10  data11  data12  data13  data14  data15
#       ......  ......  ......  ......  ......  ......
#  tn   datan0  datan1  datan2  datan3  datan4  datan5
np.random.seed(1111)
data_origin = np.random.random([20, 6])
print("生成原始数据 data_origin : \n", data_origin, "\n")

# 模拟计算每个时刻的概率值
def softmax(logits):
    # 假设logits维度为:m*n
    # max_value是求得每一行的最大值,是一个m*1的数组
    max_value = np.max(logits, axis=1, keepdims=True)
    # exp的维度为:m*n
    exp = np.exp(logits - max_value)
    # exp_sum的维度为:m*1
    exp_sum = np.sum(exp, axis=1, keepdims=True)
    # dist的维度为:m*n
    dist = exp / exp_sum
    return dist

data_log = softmax(data_origin)
print("原始数据的概率 data_log : \n", data_log, "\n")

结果为:

生成原始数据 data_origin : 
 [[0.0955492  0.9250037  0.34357342 0.31047694 0.00200984 0.23559472]
 [0.23779172 0.73591587 0.49546808 0.78442535 0.12650631 0.60664932]
 [0.46612097 0.23713212 0.43515918 0.24367151 0.38383991 0.83839369]
 [0.65518473 0.14844667 0.63914517 0.63737456 0.61087429 0.93001855]
 [0.81649992 0.76942493 0.08540093 0.66500273 0.71169585 0.88733419]
 [0.26800864 0.24307673 0.7893055  0.37798441 0.79447948 0.86733103]
 [0.79581463 0.91718092 0.27112277 0.76085495 0.63041107 0.07933233]
 [0.13085797 0.30031675 0.32228152 0.16112149 0.31519324 0.52552848]
 [0.64584938 0.75064547 0.05602729 0.87420587 0.35373503 0.80443069]
 [0.06895161 0.17132584 0.12733474 0.45520635 0.62634312 0.00899385]
 [0.27226354 0.23227754 0.96010383 0.92113492 0.99903243 0.5457968 ]
 [0.24133854 0.14059083 0.63565862 0.90564256 0.61679546 0.8036375 ]
 [0.99099401 0.38455772 0.48742394 0.00766609 0.56447557 0.47900091]
 [0.39117119 0.01299563 0.04977387 0.48996132 0.50718393 0.37742878]
 [0.21948261 0.76436243 0.60285625 0.69879507 0.16387843 0.95983512]
 [0.94478792 0.18832797 0.42354165 0.06001482 0.29266343 0.37617702]
 [0.19016842 0.26495532 0.61076973 0.78200893 0.01128725 0.60054146]
 [0.79762971 0.88663537 0.43738937 0.73174125 0.77715349 0.29868446]
 [0.39374269 0.37934963 0.49425629 0.83092538 0.11742423 0.15918496]
 [0.57850174 0.27645561 0.6318197  0.99729503 0.79025817 0.17842424]] 

原始数据的概率 data_log : 
 [[0.12699445 0.29107993 0.16274224 0.15744421 0.11565412 0.14608504]
 [0.12487911 0.20550499 0.16158357 0.21571968 0.11172726 0.18058539]
 [0.16842255 0.13395275 0.16328779 0.13483159 0.15511937 0.24438595]
 [0.17117829 0.10312766 0.16845457 0.16815657 0.1637589  0.22532402]
 [0.18979942 0.18107165 0.09136558 0.16311747 0.17091455 0.20373132]
 [0.12063207 0.11766167 0.20316979 0.13465568 0.20422371 0.21965708]
 [0.19910643 0.2247988  0.11781878 0.192266   0.16875297 0.09725702]
 [0.14059406 0.16655666 0.17025551 0.14491397 0.16905296 0.20862685]
 [0.17123365 0.19015226 0.09493637 0.21516076 0.12785728 0.20065967]
 [0.13647602 0.15118786 0.1446811  0.20081893 0.23830239 0.1285337 ]
 [0.10814803 0.10390894 0.21515125 0.2069283  0.22369195 0.14217152]
 [0.11708187 0.10586087 0.17367634 0.22750618 0.17043097 0.20544377]
 [0.26484441 0.14441719 0.1600638  0.09906881 0.17288456 0.15872124]
 [0.17828226 0.122143   0.12671883 0.19679413 0.20021279 0.17584899]
 [0.11300484 0.19486567 0.16580365 0.1824987  0.1068928  0.23693435]
 [0.28064958 0.13171584 0.16664413 0.11585435 0.146201   0.1589351 ]
 [0.12900848 0.13902657 0.19646405 0.23315854 0.10787757 0.1944648 ]
 [0.1881916  0.20570976 0.13126527 0.17619161 0.18437733 0.11426443]
 [0.16156969 0.15926087 0.17865386 0.25016465 0.12256214 0.1277888 ]
 [0.16076369 0.11885323 0.16956791 0.24438108 0.1986793  0.10775478]] 

2、举例说明

假设现在有一个简化版的中文翻译英文任务,输入和输出如下,为了方便描述搜索算法,限制输出词典只有{"W", "A", "Z", "G"} 这4个候选词,限制1个时间步长翻译1个汉字,1个汉字对应1个英文单词,这里总共4个汉字,所以只有4个时间步长。

中文输入:"我" "爱" "中" "国"

英文输出:"W" "A" "Z" "G"

目标:得到最优的翻译序列 W-A-Z-G

3、exhaustive search(穷举搜索)

最直观的方法就是穷举所有可能的输出序列,4个时间步长,每个步长4种选择,共计4*4=36 种排列组合。从所有的排列组合中找到输出条件概率最大的序列。穷举搜索能保证全局最优,但计算复杂度太高,当输出词典稍微大一点根本无法使用。

4、Greedy Search(贪心搜索)

贪心算法在翻译每个字的时候,直接选择条件概率最大的候选值作为当前最优。如下图所以:

第1个时间步长:首先翻译"我",发现候选"W"的条件概率最大为0.6,所以第一个步长直接翻译成了"W"。

第2个时间步长:翻译"我爱",发现WW概率0.2,WA概率0.6,WZ概率0.1,WG概率0.1,所以选择WA作为当前步长最优翻译结果。

第3个时间步长:翻译"我爱中",发现WAW概率0.05,WAA概率0.05,WAZ概率0.8,WAG概率0.1,所以选择WAZ作为最终的翻译结果。

第4个时间步长:翻译"我爱中国",发现WAZW概率0.05,WAZA概率0.05,WAZZ概率0.1,WAZG概率0.8,所以选择WAZG作为最终的翻译结果。

贪心算法每一步选择中都采取在当前状态下最好或最优的选择,通过这种局部最优策略期望产生全局最优解。但是期望是好的,能不能实现是另外一回事了。贪心算法本质上没有从整体最优上加以考虑,并不能保证最终的结果一定是全局最优的。但是相对穷举搜索,搜索效率大大提升。

5、Beam Search(束搜索)

Beam Search是对Greedy Search的一个改进算法。相对Greedy Search扩大了搜索空间,但远远不及穷举搜索指数级的搜索空间,是二者的一个折中方案。

Beam Search有一个超参数beam size(束宽),设为 K。第一个时间步长,选取当前条件概率最大的 K个词,当做候选输出序列的第一个词。之后的每个时间步长,基于上个步长的输出序列,挑选出所有组合中条件概率最大的 K 个,作为该时间步长下的候选输出序列。始终保持 K 个候选。最后从 K个候选中挑出最优的。

还是以上面的任务为例,假设 K=2 ,我们走一遍这个搜索流程。

1、第一个时间步长:如下图所示,W和Z的概率是top2,所以第一个时间步长的输出的候选是W和Z,将W和Z加入到候选输出序列中。

2、第2个时间步长:如下图所示,以W开头有四种候选{WW, WA, WA, WG},以Z开头有四种候选{ZW, ZA, ZZ, ZG}。从这8个候选中挑出条件概率最大的2个,即WA和ZA,作为候选输出序列。

3、第3个时间步长:同理,以WA开头有四种候选{WAW, WAA, WAZ, WAG},以ZA开头有四种候选{ZAW, ZAA, ZAZ, ZAG}。从这8个候选中挑出条件概率最大的2个,即WAZ和WAG,作为候选输出序列。

4、第4个时间步长:同理,以WAZ开头有四种候选{WAZW, WAZA, WAZZ, WAZG},以ZAG开头有四种候选{ZAGW, ZAGA, ZAGZ, ZAGG}。从这8个候选中挑出条件概率最大的2个,即WAZZ和WAZG,作为候选输出序列。因为4个步长就结束了,直接从WAZG和WAZZ中挑选出最优值WAZG作为最终的输出序列。

5、beam search不保证全局最优,但是比greedy search搜索空间更大,一般结果比greedy search要好。

6、greedy search 可以看做是 beam size = 1时的 beam search

6、Prefix Beam Search(前缀束搜索)

参考论文:First-Pass Large Vocabulary Continuous Speech Recognition using Bi-Directional Recurrent DNNs.

有许多不同的路径在many-to-one map的过程中是相同的,但Beam Search却会将一部分舍去,这导致了很多有用的信息被舍弃了。比如许多单个概率低,但是合在一起概率就很高的情况。这种想法就催生了Prefix Beam Search。基本的思想是将记录prefix的时候不在记录raw sequence,而是记录去掉blankduplicatesequence(具体步骤较复杂,会同时保留duplicate的序列和没duplicate的序列)

前缀束搜索(Prefix Beam Search)方法,可以在搜索过程中不断的合并相同的前缀。具体较复杂,不过读者弄懂Beam Search后再想想Prefix Beam Search的流程不是很难,主要弄懂probability With Blank和probability No Blank分别代表最后一个字符是空格和最后一个字符不是空格的概率即可。

理解:

*表示任意字符串(经过many-to-one map后的),比如B(A-C)=*C,这里的-表示Blank。如果*表示的字符串的尾部是Blank*表示为*_{blank},即B(A-C)=*_{Blank}。同理B(AAC)=*_{NoBlank}C

那么任意字符串*与一个新的字符结合有多少种情况呢?一共有这样5种情况(为了简便,令*最后一个字符为A):

(1)*_{Blank}+Blank=*_{Blank}

(2)*_{Blank}+char=(*+char)_{NoBlank}

(3)*_{NoBlank}+Blank=*_{Blank}

(4)*_{NoBlank}+char_{NoA}=(*+char_{NoA})_{NoBlank}

(5)*_{NoBlank}+A=*_{NoBlank}

7、代码

import numpy as np
from math import log
from collections import defaultdict

# 生成一个数组,在这个数组中寻求最优解。其中每一行代表着一个时刻。
#  t1   data00  data01  data02  data03  data04  data05
#  t2   data10  data11  data12  data13  data14  data15
#       ......  ......  ......  ......  ......  ......
#  tn   datan0  datan1  datan2  datan3  datan4  datan5
np.random.seed(1111)
data_origin = np.random.random([20, 6])
print("生成原始数据 data_origin : \n", data_origin, "\n")

# 模拟计算每个时刻的概率值
def softmax(logits):
    # 假设logits维度为:m*n
    # max_value是求得每一行的最大值,是一个m*1的数组
    max_value = np.max(logits, axis=1, keepdims=True)
    # exp的维度为:m*n
    exp = np.exp(logits - max_value)
    # exp_sum的维度为:m*1
    exp_sum = np.sum(exp, axis=1, keepdims=True)
    # dist的维度为:m*n
    dist = exp / exp_sum
    return dist

data_log = softmax(data_origin)
print("原始数据的概率 data_log : \n", data_log, "\n")


# 去除空格【规定了blank字符是"0"】
def remove_blank(labels, blank=0):
    new_labels = []
    # 合并重复字符
    previous = None
    for l in labels:
        if l != previous:
            new_labels.append(l)
            previous = l
    # 去除重复字符
    new_labels = [l for l in new_labels if l != blank]
    return new_labels

# 插入空格
def insert_blank(labels, blank=0):
    new_labels = [blank]
    for l in labels:
        new_labels += [l, blank]
    return new_labels

# Greedy Search操作
def greedy_decode(data, blank=0):
    # 得到data中每一行的最大值的下标【每一行代表着一个时刻,有多少行就有多少时刻】
    raw_rs = np.argmax(data, axis=1)
    rs = remove_blank(raw_rs, blank)
    return raw_rs, rs

# 使用Greedy Search解码
rr, rs = greedy_decode(data_log)
print("使用Greedy Search未删除blank时的结果为:", rr)
print("使用Greedy Search已删除blank时的结果为:", rs)

# 使用Beam Search【束搜索】解码
def beam_decode(data, beam_size=10):
    # data是个二维数组,记录了所有时刻的所有元素的概率
    T, V = data.shape     # T是时刻,V是每一个时刻的维度
    # 将所有的data中值改为log是为了防止溢出,因为最后得到的p是data1...datan连乘,且datai都在0到1之间,可能会导致下溢出
    # 改成log(data)以后就变成连加了,这样就防止了下溢出
    log_y = np.log(data)
    # 初始的beam为一个tuple组成的list;tuple是由一个list【prefix】和一个概率【log】值组成。
    beam = [([], 0)]
    # 遍历所有时刻t
    for t in range(T):
        # 在每个时刻,都先初始化一个new_beam为一个空的list
        new_beam = []
        # 遍历上一次查询的结果beam, prefix是查找过的字符串, score是查找过的字符串的得分
        for prefix, score in beam:
            # 遍历这一个时刻中的每一个元素的概率值(一共V项)
            for i in range(V):
                # 记录添加的新项是这个时刻的第几项,对应的概率(log形式的)加上新的这项log形式的概率(本来是乘的,改成log就是加)
                new_prefix = prefix + [i]        # 往下遍历的过程中,向prefix中添加新元素的位置信息【即prefix+[]】
                new_score = score + log_y[t, i]  # 往下遍历的过程中,向log中添加新元素的概率值【score+log_y[t,i]】
                # new_beam记录了beam中现有的每一个prefix加上新时刻的每个元素和概率,然后组成新的候选项,再从其中选出beam_size个
                new_beam.append((new_prefix, new_score))
        # 给new_beam按score排序
        new_beam.sort(key=lambda x: x[1], reverse=True)
        # beam即为new_beam中概率最大的beam_size个路径
        beam = new_beam[:beam_size]
    return beam

beam_chosen = beam_decode(data_log, beam_size=100)
print("Beam Search(束搜索)的前20条结果: ")
for beam_string, beam_score in beam_chosen[:20]:
    print(remove_blank(beam_string), beam_score)


# Prefix Beam Search代码实现
ninf = float("-inf")     # python中的最小值

def _logsumexp(a, b):
    # np.log(np.exp(a) + np.exp(b))
    if a < b:
        a, b = b, a
    if b == ninf:
        return a
    else:
        return a + np.log(1 + np.exp(b - a))

def logsumexp(*args):
    # from scipy.special import logsumexp
    # logsumexp(args)
    res = args[0]
    for e in args[1:]:
        res = _logsumexp(res, e)
    return res

def prefix_beam_decode(y, beam_size=10, blank=0):
    """
    对给定输出概率进行预测
    Arguments:
        y: 输出概率 (e.g. post-softmax) for each time step. Should be an array of shape (time x output dim).
        beam_size (int): Size of the beam to use during inference.
        blank (int): Index of the CTC blank label.
    Returns the output label sequence and the corresponding negative log-likelihood estimated by the decoder.
    """
    T, V = y.shape
    log_y = np.log(y)

    # 在beam中的元素为(prefix, (probability_blank, probability_no_blank))
    # 初始beam为空序列,第一个是前缀,第二个是后接blank的log概率,第三个是后接非blank的log概率
    # 我们需要后接blank和后接非blank两种情况,来区分重复字符是否应该被合并,对于后接blank的情况,重复字符就不会被合并
    beam = [(tuple(), (0, ninf))]
    # 沿时间维度循环,对于每一个时刻t
    for t in range(T):
        # 使用普通的字典时,用法一般是dict={},添加元素的只需要dict[element] =value即可,调用的时候也是如此
        # dict[element] = xxx,但前提是element不在字典里,如果不在字典里就会报错
        # defaultdict的作用是在于,当字典里的key不存在但被查找时,返回的不是keyError而是一个默认值
        # dict = defaultdict(factory_function)
        # 这个factory_function可以是list、set、str等等,作用是当key不存在时,返回的是工厂函数的默认值
        # 这里就是(ninf, ninf)是默认值
        # new_beam为存储下一个候选集的预设置字典,每次新的时间节点都会重设
        new_beam = defaultdict(lambda: (ninf, ninf))
        # 对于beam中的每一项
        for prefix, (p_b, p_nb) in beam:
            for i in range(V):
                # beam的每一项都加上时刻t中的每一项
                p = log_y[t, i]
                # 如果新元素项i的属性是blank,那么前缀不会改变,因为后接的是blank,所以只需要更新前缀不变的情况下后接blank的log概率
                # p=log_y[t,i]是新元素项的概率,使用logsumexp(new_p_b, p_b + p, p_nb + p)进行更新
                if i == blank:
                    # 将新元素项直接加入路径中,路径prefix不变【因为新元素项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
                # 如果新元素项的属性不是blank,那么需要考虑前缀prefix会不会改变的情况,
                # 记录前缀prefix的最后一个字符,用于判断当前字符与前缀最后一个字符是否相同
                end_t = prefix[-1] if prefix else None
                # 让前缀prefix加上新元素项i后,得到新的前缀new_prefix,判断new_prefix是否在上一轮的beam结果中【可能存在】
                new_prefix = prefix + (i,)                 # new_prefix代表next prefix
                new_p_b, new_p_nb = new_beam[new_prefix]   # new_p_b代表 next probability of blank
                if i != end_t:
                    # 如果新元素项i和prefix的最后一个字符不一样,则将新元素项i加入路径前缀prefix中,并将整体加入到beam中
                    # 因为后接的是非blank,所以只需要更新后接非blank的log概率【即只更新new_p_nb】
                    # 上一轮的结果中,有两种情况:1、最后一个不是blank;2、最后一个是blank。所以计算的时候都要考虑。
                    new_p_nb = logsumexp(new_p_nb, p_b + p, p_nb + p)
                else:
                    # 如果新元素项i和prefix的最后一个字符一样,那么我们在更新后接非blank的log概率时不包括上一时刻后接非blank的概率。
                    # --------CTC算法会合并没有用blank分隔的重复字符--------
                    new_p_nb = logsumexp(new_p_nb, p_b + p)
                # 如果新元素项i和prefix的最后一个字符一样,分两种情况添加:
                #     1、new_prefix=prefix+(i,)  添加新元素项i,则最后两个字符会重复,计算概率,添加到new_beam中,优选
                #     2、new_prefix=prefix       不添加新元素项i,prefix中没有重复字符,计算概率,添加到new_beam中,优选
                new_beam[new_prefix] = (new_p_b, new_p_nb)   # 这一步:new_prefix最后两个字符是一样的
                # 如果一样,保留现有的路径前缀prefix,但是概率上要加上新的这个元素项i的概率
                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排序并取前beam_size个
        beam = sorted(new_beam.items(), key=lambda x: logsumexp(*x[1]), reverse=True)
        beam = beam[:beam_size]
    return beam

beam_test = prefix_beam_decode(data_log, beam_size=100)
print("Prefix Beam Search(束搜索)的前20条结果: ")
for beam_string, beam_score in beam_test[:20]:
    print(remove_blank(beam_string), beam_score)

结果:

生成原始数据 data_origin : 
 [[0.0955492  0.9250037  0.34357342 0.31047694 0.00200984 0.23559472]
 [0.23779172 0.73591587 0.49546808 0.78442535 0.12650631 0.60664932]
 [0.46612097 0.23713212 0.43515918 0.24367151 0.38383991 0.83839369]
 [0.65518473 0.14844667 0.63914517 0.63737456 0.61087429 0.93001855]
 [0.81649992 0.76942493 0.08540093 0.66500273 0.71169585 0.88733419]
 [0.26800864 0.24307673 0.7893055  0.37798441 0.79447948 0.86733103]
 [0.79581463 0.91718092 0.27112277 0.76085495 0.63041107 0.07933233]
 [0.13085797 0.30031675 0.32228152 0.16112149 0.31519324 0.52552848]
 [0.64584938 0.75064547 0.05602729 0.87420587 0.35373503 0.80443069]
 [0.06895161 0.17132584 0.12733474 0.45520635 0.62634312 0.00899385]
 [0.27226354 0.23227754 0.96010383 0.92113492 0.99903243 0.5457968 ]
 [0.24133854 0.14059083 0.63565862 0.90564256 0.61679546 0.8036375 ]
 [0.99099401 0.38455772 0.48742394 0.00766609 0.56447557 0.47900091]
 [0.39117119 0.01299563 0.04977387 0.48996132 0.50718393 0.37742878]
 [0.21948261 0.76436243 0.60285625 0.69879507 0.16387843 0.95983512]
 [0.94478792 0.18832797 0.42354165 0.06001482 0.29266343 0.37617702]
 [0.19016842 0.26495532 0.61076973 0.78200893 0.01128725 0.60054146]
 [0.79762971 0.88663537 0.43738937 0.73174125 0.77715349 0.29868446]
 [0.39374269 0.37934963 0.49425629 0.83092538 0.11742423 0.15918496]
 [0.57850174 0.27645561 0.6318197  0.99729503 0.79025817 0.17842424]] 

原始数据的概率 data_log : 
 [[0.12699445 0.29107993 0.16274224 0.15744421 0.11565412 0.14608504]
 [0.12487911 0.20550499 0.16158357 0.21571968 0.11172726 0.18058539]
 [0.16842255 0.13395275 0.16328779 0.13483159 0.15511937 0.24438595]
 [0.17117829 0.10312766 0.16845457 0.16815657 0.1637589  0.22532402]
 [0.18979942 0.18107165 0.09136558 0.16311747 0.17091455 0.20373132]
 [0.12063207 0.11766167 0.20316979 0.13465568 0.20422371 0.21965708]
 [0.19910643 0.2247988  0.11781878 0.192266   0.16875297 0.09725702]
 [0.14059406 0.16655666 0.17025551 0.14491397 0.16905296 0.20862685]
 [0.17123365 0.19015226 0.09493637 0.21516076 0.12785728 0.20065967]
 [0.13647602 0.15118786 0.1446811  0.20081893 0.23830239 0.1285337 ]
 [0.10814803 0.10390894 0.21515125 0.2069283  0.22369195 0.14217152]
 [0.11708187 0.10586087 0.17367634 0.22750618 0.17043097 0.20544377]
 [0.26484441 0.14441719 0.1600638  0.09906881 0.17288456 0.15872124]
 [0.17828226 0.122143   0.12671883 0.19679413 0.20021279 0.17584899]
 [0.11300484 0.19486567 0.16580365 0.1824987  0.1068928  0.23693435]
 [0.28064958 0.13171584 0.16664413 0.11585435 0.146201   0.1589351 ]
 [0.12900848 0.13902657 0.19646405 0.23315854 0.10787757 0.1944648 ]
 [0.1881916  0.20570976 0.13126527 0.17619161 0.18437733 0.11426443]
 [0.16156969 0.15926087 0.17865386 0.25016465 0.12256214 0.1277888 ]
 [0.16076369 0.11885323 0.16956791 0.24438108 0.1986793  0.10775478]] 

使用Greedy Search未删除blank时的结果为: [1 3 5 5 5 5 1 5 3 4 4 3 0 4 5 0 3 1 3 3]
使用Greedy Search已删除blank时的结果为: [1, 3, 5, 1, 5, 3, 4, 3, 4, 5, 3, 1, 3]
Beam Search(束搜索)的前20条结果: 
[1, 3, 5, 1, 5, 3, 4, 3, 4, 5, 3, 1, 3] -29.261797539205567
[1, 3, 5, 1, 5, 3, 4, 3, 3, 5, 3, 1, 3] -29.279020152518033
[1, 3, 5, 1, 5, 3, 4, 2, 3, 4, 5, 3, 1, 3] -29.300726142201842
[1, 5, 1, 5, 3, 4, 3, 4, 5, 3, 1, 3] -29.310307014773972
[1, 3, 5, 1, 5, 3, 4, 2, 3, 3, 5, 3, 1, 3] -29.31794875551431
[1, 5, 1, 5, 3, 4, 3, 3, 5, 3, 1, 3] -29.327529628086438
[1, 3, 5, 1, 5, 4, 3, 4, 5, 3, 1, 3] -29.331572723457334
[1, 3, 5, 5, 1, 5, 3, 4, 3, 4, 5, 3, 1, 3] -29.33263180992451
[1, 3, 5, 4, 1, 5, 3, 4, 3, 4, 5, 3, 1, 3] -29.334649090836038
[1, 3, 5, 1, 5, 3, 4, 3, 4, 5, 3, 1, 3] -29.33969505198154
[1, 3, 5, 2, 1, 5, 3, 4, 3, 4, 5, 3, 1, 3] -29.339823066915415
[1, 3, 5, 1, 5, 4, 3, 3, 5, 3, 1, 3] -29.3487953367698
[1, 5, 1, 5, 3, 4, 2, 3, 4, 5, 3, 1, 3] -29.349235617770248
[1, 3, 5, 5, 1, 5, 3, 4, 3, 3, 5, 3, 1, 3] -29.349854423236977
[1, 3, 5, 1, 5, 3, 4, 3, 4, 5, 3, 3] -29.350803198551016
[1, 3, 5, 4, 1, 5, 3, 4, 3, 3, 5, 3, 1, 3] -29.351871704148504
[1, 3, 5, 1, 5, 3, 4, 3, 3, 5, 3, 1, 3] -29.356917665294006
[1, 3, 5, 2, 1, 5, 3, 4, 3, 3, 5, 3, 1, 3] -29.35704568022788
[1, 3, 5, 1, 5, 3, 4, 5, 4, 5, 3, 1, 3] -29.363802591012263
[1, 5, 1, 5, 3, 4, 2, 3, 3, 5, 3, 1, 3] -29.366458231082714
Prefix Beam Search(束搜索)的前20条结果: 
[1, 5, 4, 1, 3, 4, 5, 2, 3] (-18.189863809114193, -17.613677981426175)
[1, 5, 4, 5, 3, 4, 5, 2, 3] (-18.19636512622969, -17.621013424585406)
[1, 5, 4, 1, 3, 4, 5, 1, 3] (-18.31701896033153, -17.666629973270073)
[1, 5, 4, 5, 3, 4, 5, 1, 3] (-18.323388267369936, -17.674125139073176)
[1, 5, 4, 1, 3, 4, 3, 2, 3] (-18.415808498759556, -17.862744326248826)
[1, 5, 4, 1, 3, 4, 3, 5, 3] (-18.36642276663863, -17.898463479112884)
[1, 5, 4, 5, 3, 4, 3, 2, 3] (-18.42224294936932, -17.870025672291458)
[1, 5, 4, 5, 3, 4, 3, 5, 3] (-18.37219911390019, -17.905130493229173)
[1, 5, 4, 1, 3, 4, 5, 4, 3] (-18.457066311773847, -17.880630315602037)
[1, 5, 4, 5, 3, 4, 5, 4, 3] (-18.462614293487096, -17.88759583852546)
[1, 5, 4, 1, 3, 4, 5, 3, 2] (-18.458941701567706, -17.951422824358747)
[1, 5, 4, 5, 3, 4, 5, 3, 2] (-18.464527031120184, -17.958629487208658)
[1, 5, 4, 1, 3, 4, 3, 1, 3] (-18.540857550725587, -17.92058991009369)
[1, 5, 4, 5, 3, 4, 3, 1, 3] (-18.547146092248852, -17.928030266681613)
[1, 5, 4, 1, 3, 4, 5, 3, 2, 3] (-19.325467801462263, -17.6892032244089)
[1, 5, 4, 5, 3, 4, 5, 3, 2, 3] (-19.328748799764973, -17.694105969982637)
[1, 5, 4, 1, 3, 4, 5, 3, 4] (-18.79699026165903, -17.945090229238392)
[1, 5, 4, 5, 3, 4, 5, 3, 4] (-18.80358553427324, -17.95258394264377)
[1, 5, 4, 3, 4, 3, 5, 2, 3] (-19.18153184608281, -17.859420073785095)
[1, 5, 4, 1, 3, 4, 5, 2, 3, 2] (-19.4393492963852, -17.884502168470895)

Process finished with exit code 0
  • 25
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值