HTKbook翻译之第十三章HVite解码(维特比)

第十三章 HVite解码

 

前一章描述了如何构建识别网络,指定什么是合法的语音输入,以及每个词是怎么发音的。有了这样一个网络和它对应的HMM集合,输入一段语音,就可以通过网络计算该语音片段经过的所有路径的概率。解码的任务就是从中找出概率最大的路径。

 

前面提到过,在HTK中HRec模块实现了解码功能。它是利用令牌传递(token passing)的范式实现寻找最优路径,可以选择提供多个替代路径。后一种情况下,它会通过格栅的形式给出多个可能的假设,然后转换为N-best最优识别结果。HTK通过HVite工具调用HRec模块的功能。除了提供识别功能外,HVite还能执行强制对齐,格栅打分和对输入语音直接识别等功能。

 

HTK还提供了一个工具HResults通过测试集数据和标注文本来测试评估识别的效果,包括词准确率和相应的统计。本章接下来就是关于识别原理和识别功能的介绍。

 

13.1解码操作

 

在第十二章和图12.1都描述了,HTK利用识别网络来控制解码过程,该识别网络是由词级网络、字典和HMM模型集合编译而成的。识别网络有一系列的节点和边组成,每个节点代表模型实例或者“词尾”。每个模型自身也是一个网络,由HMM模型的状态和连接的边组成。这样,最终编译完成后的识别网络由HMM状态和连接它们之间的转移边共同组成。可以从三个不同的视角来看待这个网络:词级、模型级和状态级。如图13.1说明了这样的层级结构。

 

对于一个有T帧的未知输入语音,从网络的开始节点到结束节点,经过T个发射HMM状态的任意路径,都是可能的隐含状态。每个路径都有一个概率值,是通过计算它所经过的路径上的节点转移概率和状态似然概率(发射概率)的log和相加而得。HMM模型的状态转移概率可以通过模型训练得到,而模型间的转移概率是固定的,词间转移概率由语言模型决定,附在词网络上。

 

解码器的作用就是从这些路径中找到最优的(概率值最大)。这个解码过程是通过令牌传递算法完成的。一个令牌代表了从时间片0到当前t为止的部分概率值。在一开始时间0时,所有可能的开始节点都放置了一个令牌。

 

时间片每前进一步,令牌就沿着当前节点往与它连接的下一节点传递,直到一个发射状态。如果某个节点有多个出口相连接,那么每个出口都复制一份令牌,这样在多个路径并行探索。令牌在传递过程中,它的概率对数值随着经过的节点间转移概率和经过的发射状态的发射概率的增加而增加。一个网络节点最多可持有N个令牌。那么,在最后T时刻,除了最优的N个令牌外,其他节点上的令牌都会被抛弃。

 

令牌在网络传递过程中必须保存它的历史路径。历史路径的细节包括哪些信息是根据识别结果不同而有所区别。通常识别结果一般是词序列,那么“词尾”节点的转移输出需要被记录下来。但是有些情况下,获得语音的模型序列(这里应该是指phone sequence)和时间片t时刻的模型转移信息也是非常有用的。有时候也需要知道每个路径经过的模型状态(model state)信息。所有这些信息,无论什么级别的,都可以通过格栅结构(lattice structure)来表示。

 

每个节点允许持有的最多令牌数,以及每个令牌保存的历史信息的多少,都会影响在解码过程中都会影响内存资源和计算的时间复杂度。效率最高的配置就是设置N=1,和只需要词级的历史信息,这对多数应用来说是够的。

 

一个巨大的网络一般包含很多个节点,在令牌传递过程中,为减少计算量的一个可行的方法就是将令牌传递限制在有潜力最终能获胜的节点上,这个过程叫“剪枝”(pruning),实现方式是在每个时间片上,只保留全局最优的前N个得分最高的令牌而去除其他令牌,这个N的值就是束宽(“beam-width”)。为了效率起见,这个剪枝过程一般发生在模型级别(sub-word),而不是状态级上。当模型的所有状态都不在束宽范围内,那么这个模型就是被“灭活”,而当令牌又重新传递进入它们的时候,将再次被激活。在状态级别的剪枝也是通过的方式,用空令牌替代原来的,如果它们不在束宽范围内了。如果用于剪枝的束宽设置的太小,那么较容易在到达语音的最后之前,把最优路径剪枝了,导致错误搜索。束宽的设置是在识别速度和准确度之间做平衡。

 

最后,还有第三种剪枝方式。给允许同时处于激活状态的模型个数设置上限,一旦触碰到上限了,就减小束宽避免激活态模型个数超过这个限制。

 

13.2解码组织

解码过程由一系列的功能实现的,都是有模块HRec提供。图13.2展示这个识别过程。

 

第一步就是构建识别实体。它是一个数据结构,用以包含识别网络和存储令牌。将识别涉及到的所有信息都封装在一个对象中的目的是使得HRec可重入和支持多个识别器并行操作。但是这个功能在HVite中并没到得到支持,但是它确实有能力支持开发者让多个识别器含有不同的网络。

 

一旦识别器被构建,处理未知输入前,首先执行启动识别器,然后一个个的处理的观察向量。当所有的观察向量都处理完毕后,识别器会生成一个格栅(lattice),它可以标准网格格式(standard lattice format SLF)保存到硬盘或者转化为文本。

 

解码器的组织结构是很灵活的。HTK工具HVite就是一个简单的命令行工具,用它去调用HRec模块。

 

首先,通过识别网络的不同形式,可以控制输入的三种不同操作模式。

 

  1. 识别

这是通常的模式,就是从一个任务的词网络来编译、构建识别网络。

  1. 强制对齐

这个模式下,识别网络是通过词级标注文件和发音字典来构建的。这个网络可能包含静音模型和多音字。在训练时,强制对齐可以自动提取音子级别的标注信息。也可以在自动标注系统中使用。

 

  1. 以格栅为基础的重估分

在这个模式下,输入的网络是通过前期的识别器产生的识别格栅编译得来。这个模式的操作在识别器开发过程中特别有用,因为能极大的提高识别速度。它需要的识别格栅(lattices)通常由基础识别器产生的,一般有多个令牌。这个过程包含正确的标注以及多个混淆文本。重估分可以用来快速评估更高级的识别的性能和新识别技术的有效性。

 

第二个灵活性来源是,它提供多个令牌,以及识别输出是格栅形式的。除了提供重估分的机制,格栅输出包含多个可能的识别结果,可以作为进一步识别的源数据,或者直接作为自然语言处理过程的输入。还可以方便的转换为最优前N句列表输出。

 

最后,HRec在调用过程中是对一个一个的处理观察向量,因此可以对识别过程进行精确的控制,提供多种跟踪回溯和即时输出。

 

应用开发者可以直接调用HRec或者其他依赖的模块开发应用程序。也提供工业级的开发API接口。还有就是HTK提供命令工具HVite来调用HRec。本章剩下的内容就是关于HVite提供的各式各样调用方式来进行语音识别。

 

13.3识别测试数据

 

当构建一个识别器时或者探索语音识别算法时,系统性能是必须要测试的。这可以通过测试集和它对应的标注文本来实现。下面这条命令就能完成该功能:

 

HVite -w wdnet dict hmmlist testf1 testf2 ....

 

其中wdnet就是一个SLF文件,包含词级网络,dict为发音字典,hmmlist是模型集合。这条命令HVite调用HRec来编译词网络,然后识别每个测试文件。为了方便评估,测试文件存储都是音频的基本特征参数数据。例如参数的一阶差是在加载时临时计算的。在第五章解释过,HTK工具在加载数据时可以进行多种运行,这是通过配置参数来控制的。HVite命令的参数配置文件通过-C来指定,它里面指定目标参数的种类。13.6章节说明当指定解码音频信号输入时参数配置的细节。

 

默认调用时,HVite会在当前目录下搜索单独的文件来查找HMM模型定义以及每个标注文本文件。可以有很多测试文件。

 

实际上,把HMM定义在MMF文件中,把标注文件存储在MLF文件中,然后数据写在脚本文件中更加方便。更通用的调用方式为:

 

HVite -T 1 -S test.scp -H hmmset -i results -w wdnet dict hmmlist

 

Test.scp包含了测试文件名的列表,hmmset是一个MMF文件,包含HMM模型集,results是用来存储MFL格式的识别结果。

 

如下显示的那样,在识别过程中打印必要的步骤是非常有用的,可以设置跟踪参数选项实现。下面的例子就是打印了处理的文件名和识别结果以及一些统计信息,例如帧数、平均每帧识别概率对数值、总的声学模型似然率、中的语言模型似然读和平均激活模型个数等。

 

File: testf1.mfc

SIL ONE NINE FOUR SIL

[178 frames] -96.1404 [Ac=-16931.8 LM=-181.2] (Act=75.0)

 

写入results的MLF格式如下:

 

"testf1.rec"

0 6200000 SIL -6067.333008

6200000 9200000 ONE -3032.359131

9200000 12300000 NINE -3020.820312

12300000 17600000 FOUR -4690.033203

17600000 17800000 SIL -302.439148

.

 

存储了每个单词开始和结束时间以及总的对数概率值。HVite输出信息可以通过-o选项控制。例如-o ST就不会输出对数概率值和时间信息,仅仅是词串。

 

"testf1.rec"

SIL

ONE

NINE

FOUR

SIL

.

 

 

如果参数设置恰当,比如剪枝的阈值(pruning threshold)、语言模型的系数(scaling parameter),HVite的有效性和效率值都会提高。-t选项设置主要的剪枝束宽。为了调试到较好的水平要进行必要的实验,一般从-t 为250是靠谱的开始。如果需要,词尾剪枝(-v)和模型最大限制数量(-u)也可以设置,但它们不是强制的,而且有效性跟任务密切相关。

 

插入和删除错误可以通过设置语言模型的系数来控制,-s设置模型系数,-p增加固定的惩罚值。例如设置-s 10.0 -p -20,意思每个语言模型的对数概率x经过公式10x-20计算后,加到“词尾”节点的概率值上。极端情况下,设置-p 100将导致识别结果为:

 

SIL OH OH ONE OH OH OH NINE FOUR OH OH OH OH SIL

 

因为给每个“词尾”节点的对数概率值增加100将导致插入错误的值很大,引入“oh”的字符来替代当前符号,因为它在词汇表是最短的。识别过程中另一个常见错误就是无法到达识别网络的结尾节点。发生这样的错误系统会提示,“没有令牌能抵达网络的结尾节点”。引起这个错误是因为声学模型训练不足或者剪枝束宽太窄,将所有的合理路径提前删除了。这样的情况下,部分识别结果也可以获得是,如果设置了FORCEOUT为true的话。识别结果就是在该网络中已发现的最可能的假设。

 

 

 

13.4评估识别结果

 

一旦识别器处理了测试数据,下一步就是分析识别结果。HResults实现该功能,通过比较HVite的输出结果和标注的参考文本,给出多种统计结果。HResults采用动态规划的方法实现字符串比较。除了下面将要讲到的关键词检出(word-spotter),不需要考虑边界的时间信息。最佳匹配的字符串是通过分别给不同情况打分,得分最好的为最优。其中打分过程如下,相同字符打0分、插入一个字符打7分、删除一个字符得7分以及替换一个字符得100分。

 

最佳对齐方案一旦确定,那么替换错误个数(S)、删除错误的个数(D)和插入错误的个数(I)就能计算到了。那么正确率计算公式如下:

Percent Correct=\frac{N-D-S}{N}

N表示参考标注文本中的字符总数。这个计算方法忽略了插入错误。对很多应用来说,准确率定义如下:

Percent Accuracy=\frac{N-D-S-I}{N}

 

它是识别性能的更具代表性的表达。

 

HResults的输出对上述两个公式都进行了计算。HTK的所有工具都能够既处理单独的标注文件,又能处理MLF格式的文件。这里假设测试文本和参考文本都是以MLF格式存储的。

 

举个例子,results包含了识别结果,refs包含参考文本,wlist包含所有词(就是word对应的字符),HResults那么命令如下:

 

HResults -I refs wlist results

 

它的输出结果:

 

====================== HTK Results Analysis =======================

Date: Sat Sep 2 14:14:22 1995

Ref : refs

Rec : results

------------------------ Overall Results --------------------------

SENT: %Correct=98.50 [H=197, S=3, N=200]

WORD: %Corr=99.77, Acc=99.65 [H=853, D=1, S=1, I=1, N=855]

==============================================================

 

上半部分是关于程序执行的时间和文件名的信息。SENT开头的行表明句子的识别正确率。下面的以WORD开头的行给出了识别出的词的统计信息,总共853个词,正确率99.77%,准确率99.85%。

 

将识别错误以可视化的方式呈现出来很有用。设置-t选项就会输出句子包含的错误信息。例如下面给出的可能输出:

 

Aligned transcription: testf9.lab vs testf9.rec

LAB:  FOUR       SEVEN   NINE   THREE

REC:  FOUR  OH  SEVEN   FIVE    THREE

 

识别的结果中多了一个“OH”,把“NINE”替换成“FIVE”。

 

可以将识别结果以NIST格式输出,通过设置-h选项。例如:

 

HTK Results Analysis at Sat Sep 2 14:42:06 1995 |

| Ref: refs |

| Rec: results |

|=============================================================|

| # Snt | Corr Sub Del Ins Err S. Err |

|-------------------------------------------------------------|

| Sum/Avg | 200 | 99.77 0.12 0.12 0.12 0.35 1.50

 

 

有时候在评估识别结果时,有些字符是不需要区分的。例如在电话拨号系统中,“OH”和“ZERO”可以认为是等同的。可以通过-e选项在执行HResults时设定:

 

HResults -e ZERO OH .....

 

 

如果把某个字符设置为与特殊符号“???”相同,那么就是忽略它们。例如,如果识别结果中包含“SIL”,设置-e ??? SIL那么结果中的“SIL”标记就会被忽略。

 

HResults还包括一系列设置参数。-f选项将让每个文件单独统计,-p选项将产生混淆矩阵。如果比较音子的识别结果,-s选择将使得HResults删除三音子的上下文。还可以处理最优前N项输出,通过参数-d N设置最优的个数。

 

在进行说话人无关的识别器性能分析时,获得对每一个说话者模型的表现是很有参考价值的。通过设置-l mask参数来提取某说话者对应的标注文件,其中mask是一个字符串,来表达某种模式,可以包含匹配字符*和?。它们俩分别匹配零个或多个字符,匹配某一个字符。该模式还应该包含一个或多个%字符,使得区分每个不同的说话者。

 

例如,假设测试文件名如下面表示的结构:

DIGITS_spkr_nnnn.rec

其中spkr是一个4字符的说话者id,nnnn是4个数字表示的音频文件id。那么执行命令:

 

HResults -h -k ’*_%%%%_????.*’ ....

 

将会给出下面的结果:

 

HTK Results Analysis at Sat Sep 2 15:05:37 1995 |

| Ref: refs |

| Rec: results |

|-------------------------------------------------------------|

| SPKR | # Snt | Corr Sub Del Ins Err S. Err |

|-------------------------------------------------------------|

| dgo1 | 20 | 100.00 0.00 0.00 0.00 0.00 0.00 |

|-------------------------------------------------------------|

| pcw1 | 20 | 97.22 1.39 1.39 0.00 2.78 10.00 |

|-------------------------------------------------------------|

......

|=============================================================|

| Sum/Avg | 200 | 99.77 0.12 0.12 0.12 0.35 1.50

 

除了字符串匹配,HResults还可以分析关键词检出模式下的识别结果。这是不是通过动态规划对齐算法。而是比较每个识别的符号与参考文本。如果字符w的开始和结束时间在参考文本中的相同字符的中间点两侧,那么就是“命中”,否则就是“虚警”。

 

这时候识别输出除了包括对数相似度数值外,还包括词的边界信息。相似度得分用来计算优良指数(Figure of Merit FOM),它是由NIST定义的平均每小时出现“虚警”次数的上限估计。FOM的计算公式如下所示,假设音频持续时间为T小时。

FOM=\frac{1}{10T}(p_{1}+p_{2}+...+p_{N}+ap_{N+1})

pi代表在发生第i次false alarm之前检测到的FA百分比,N为大于10T-0.5的第一个整数,a=10T-N用来保证每小时出现10次FA。

设置-w选项,进行关键词检出分析,输出结果如下:

 

------------------- Figures of Merit --------------------

KeyWord: #Hits #FAs #Actual FOM

BADGE: 92 83 102 73.56

CAMERA: 20 2 22 89.86

WINDOW: 84 8 92 86.98

VIDEO: 72 6 72 99.81

Overall: 268 99 188 87.55

---------------------------------------------------------

 

如果设置标准时间单位为1小时,然后计算FOM值,可以通过-o option设置。

 

13.5生成强制对齐

 

 

HVite可以用来进行“强制对齐”,只需通过指定-a选项,而不用-w指定词网络。在这个模式下,HVite为每个输入的语音,通过文本和发音字典计算出一个新的网络。默认情况下,输出的文本包含词串和它们的边界。强制对齐的主要用途是确定输入的语音数据的发音模型,-m选项指定输出为模型级别(不是词)的文本。这样的强制对齐操作一般是在bootstrap处理阶段进行,初始模型参数是由为每个模型标注的数据训练得到。然后利用HVite的强制对齐操作来选择最匹配的发音音子识别,识别的音子序列又被用来重新训练模型。一般训练数据开头和结尾都有静音段,所以识别网络的开始和结尾都插入了静音模型,-b选项可以实现该操作。

 

 

HVite -a -b sil -m -o SWT -I words.mlf -H hmmset dict hmmlist file.mfc

 

这个命令就是指向强制对齐功能。file.mfc即为待执行的语音段的特征参数,该文件名的扩展名会被替换为lab,构成file.lab作为对应的标注文本,在words.mlf中搜索该文件名对应的内容。假设包含了file.lab文件,然后利用dict的发音信息来构建对应的识别网络,不同之处是对多音字进行平行处理。由于有-b选项,在网络的开始和结尾插入静音模型sil。解码器通过解码算法得出最佳的匹配模型序列,并构建一个格栅,包含模型对齐的信息。最后,格栅被转换为一个文本,输出到文件file.rec中。如果是测试数据,通过把待测试的文件名列在.scp文件里,输出的文件名通过选项-i来指定。

 

选项-m指定输出的文本将包含音子模型和词的文本信息。例如下面部分输出片段:

 

7500000 8700000 f -1081.604736 FOUR 30.000000

8700000 9800000 ao -903.821350

9800000 10400000 r -665.931641

10400000 10400000 sp -0.103585

10400000 11700000 s -1266.470093 SEVEN 22.860001

11700000 12500000 eh -765.568237

12500000 13000000 v -476.323334

13000000 14400000 n -1285.369629

14400000 14400000 sp -0.103585

 

音子模型名称旁边的分数就是该时间片段的声学得分,而词旁边的分数则是语言模型得分。

 

虽然上面的输出信息都有用处,如在bootstrap训练时,但是其他时候,只需要音子名称的序列。选项-o SWT指定只输出音子模型名称序列。

 

13.6直接识别音频输入

 

之前讨论中,都是对存储在磁盘上的语音文件进行识别。这些语音数据都是以特征参数的形式存储,没有涉及语音数据的转换。当HVite调用时,没有指定文件列表,默认是从语音输入设备上直接读取语音数据。这样的话,配置文件是必不可少的的。在该配置文件中,涉及如何获取语音波形,还有把这些语音数据转化为何种特征向量。

 

5.12章节已经描述了如何处理音频波形数据的获取,HTK提供两个形式:信号/按键和自动语音/静音检测器。如果仅仅使用语音/静音检测器,下面就是合适的配置文件内容。

 

# Waveform capture

SOURCERATE=625.0

SOURCEKIND=HAUDIO

SOURCEFORMAT=HTK

USESILDET=T

MEASURESIL=F

OUTSILWARN=T

ENORMALISE=F

 

 

声音的采样率设为16KHz,SOURCEKIND必须设为HAUDIO,SOURCEFORMAT必须设为HTK。设置布尔变量USESILDET为true,表明将使用语音/静音检测器,MEASURESIL为false OUTSILWARN为true表明背景噪音的静音作为sil数据,而不是拿语音最初始段的数据作为静音数据。为确保获取合适的输入语音数据,HVite设置选项-g,使得每次获取采样数据后,将被保存到磁盘文件中。注意,如果是对直播音频输入系统,无论是模型训练还是识别,ENORMALISE设为false。不能对直播语音输入进行能量归一化操作,默认这个变量为true。

 

语音/静音检测器之外的可替换方式是,用一个信号来确认记录的开始与结束。下面的配置所示:

 

# Waveform capture

SOURCERATE=625.0

SOURCEKIND=HAUDIO

SOURCEFORMAT=HTK

AUDIOSIG=2

 

表明可以用中断信号(control+c)来控制录音的开始与结束。如果是通过按键控制语音的输入则将变量AUDIOSIG设为负数。

 

上面两种方式可以同时使用,就是收到中断信号后,不在执行音频获取,然后就通过语音/静音检测器来控制。

 

获取的语音信号波形必须按照配置的目标参数种类进行转换。因此,配置文件必须给出转换为目标特征向量的详细参数。第五章描述这一过程。下面给出目标类型为梅尔倒谱系数以及一阶和二阶参数的配置信息。

 

# Waveform to MFCC parameters

TARGETKIND=MFCC_0_D_A

TARGETRATE=100000.0

WINDOWSIZE=250000.0

ZMEANSOURCE=T

USEHAMMING = T

PREEMCOEF = 0.97

USEPOWER = T

NUMCHANS = 26

CEPLIFTER = 22

NUMCEPS = 12

 

 

很多都是默认参数,可以使用缺省值,这里包含这些信息是为了提醒有哪些参数。

当HVite在处理直接音频输入模式时,它会在每次输入前显示提示符。一般会设置基本的追踪,识别结果实时显示。典型的终端输出如下:

 

READY[1]>

Please speak sentence - measuring levels

Level measurement completed

DIAL ONE FOUR SEVEN

== [258 frames] -97.8668 [Ac=-25031.3 LM=-218.4] (Act=22.3)

READY[2]>

CALL NINE TWO EIGHT

== [233 frames] -97.0850 [Ac=-22402.5 LM=-218.4] (Act=21.8)

etc

 

设置-e选项,每句话的文本输出到指定的文件中或者MLF格式的文件。但是这样,需要合成一个文件名,通过计数器加上变量RECOUTPREFIX作为前缀,RECOUTSUFFIX作为后缀。例如下面这样配合。

 

RECOUTPREFIX = sjy

RECOUTSUFFIX = .rec

 

则输出文件名为sjy0001.rec,sjy0002.rec等。

 

13.7 N最优列表和格栅

在13.1节提到过,HVite能产生格栅和最优前N输出。通过-n选项设置每个状态最多保存多少个令牌以及输出多少个最优的路径。结果就是每个输入音频,会产生多个文本。例如设置-n 4 20,数字识别器会输出下面的结果。

 

"testf1.rec"

FOUR

SEVEN

NINE

OH

///

FOUR

SEVEN

NINE

OH

OH

///

etc

 

由最优列表产生的格栅可以通过-z ext设置文件的扩展名。这样根据输入的测试文件testf.xxx,生成的格栅名就是testf.ext。默认情况下,这些格栅文件就保存在与测试文件相同目录下,也可以通过-l选项设置重定向到其他目录下。

HVite产生的格栅形式如下:

 

VERSION=1.0

UTTERANCE=testf1.mfc

lmname=wdnet

lmscale=20.00 wdpenalty=-30.00

vocab=dict

N=31 L=56

I=0 t=0.00

I=1 t=0.36

I=2 t=0.75

I=3 t=0.81

... etc

I=30 t=2.48

J=0 S=0 E=1 W=SILENCE v=0 a=-3239.01 l=0.00

J=1 S=1 E=2 W=FOUR v=0 a=-3820.77 l=0.00

... etc

J=55 S=29 E=30 W=SILENCE v=0 a=-246.99 l=-1.20

 

前5行包含一个文件头,它记录了涉及的文件名和语言模型的系数和惩罚因子。格栅中的每个节点代表了一个时间点,以秒为单位度量的;每个弧度代表一个词的持续,它从输入片段的开始节点到结束节点。每个这样的持续(词、弧度),v表示使用的发音的数量,a表示声学模型的得分,l表示语言模型的得分。

 

格栅中的语言模型得分并不包括语言模型缩放系数和惩罚系数。它们被移除了,这样格栅可以作为后续识别器的测试时的限制的网络。正常调用HVite,词网络文件通过-w选项指定。如果-w选项后面没有接文件名,HVite通过测试文件来构建格栅,并把它输入相应的文件中。这样,为每个文件新建一个识别网络。也是一个有效的确认语言模型的缩放系数和惩罚因素最优值的方法。

 

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值