NLP学习笔记06-维特比算法

article/2025/10/3 23:01:49

一序

  本文属于NLP学习笔记系列。

上一篇整理了前向最大匹配算法与所有组合算法缺点(时间复杂度太高了)。

二 维特比算法

log(x*y*z)= log(x)+log(y)+log(z)

概率上为了避免小数练乘出现的超范围溢出,改用log,改用-log,使得原来求概率最大的小数为-log结果最小。

画一条线,把各种结果对应路径标注上(定义f8 :从节点1到8 的最短路径的值),这里求总结果最小,演变成求最短路径。

如果不使用哪个DP算法,那么就是递归的过程:举例:f(8)=f(5)+3, f(8)=f(6)+1.6,f(8)= f(7)+20 . 要求f(8)需要知道前面的节点的数据。

所以,维护一个数组,从f(1)开始导f(8),分别求出每个节点的值,也就知道f(8)的最小值(记录下所选最小值节点),也知道了你选择的路径是哪条。

结果:经常、有意见、分歧。

词典的概率数据怎么有的,老师没有讲,先理解为统计出来的吧。

这个内容老师讲的很仔细,一步步讲解推导过程,有的文章:比如达观这篇(只说结果,推导过程需要自己理解)

https://www.jiqizhixin.com/articles/2019-09-29-5

0 (6)

对于到最终节点E的路径Route(E),有:

0 (7)              

这节课老师只是简单介绍下维特比算法。后面的HMM 会继续介绍。比这里复杂多了。

分词总结

  知识点总结:

  •   基于匹配规则:前向最大匹配算法
  •  基于概率统计方法: (我们学习了语言模型LM,还有HMM,CRF)
  • 分词可以认为已经解决的问题。

需要掌握:

前向最大匹配算法+ Unigram LM 方法。

  下面是从jieba分词 改了下代码,作为demo。

import com.hankcs.hanlp.collection.AhoCorasick.AhoCorasickDoubleArrayTrie;import java.util.*;/*** bohu83* jieba分词*/
public class TestSpilit {static private AhoCorasickDoubleArrayTrie<Integer> acTrie;static TreeMap<String, Integer> freqMap = new TreeMap<>();static TreeMap<String, Integer> acTrieMap = new TreeMap<>();static private List<String> words = new ArrayList<>();static private List<Integer> wordFreqs = new ArrayList<>();static private Map<Pair<Integer, Integer>, Integer> subStringFreq = new HashMap<>();static {freqMap.put("经常", 10);freqMap.put("经",5);freqMap.put("常",1);freqMap.put("有",10);freqMap.put("有意见",10);freqMap.put("歧",1);freqMap.put("意见",20);freqMap.put("分歧",20);freqMap.put("见",5);freqMap.put("意",5);freqMap.put("见分歧",5);freqMap.put("分",10);freqMap.forEach((w, f) -> {acTrieMap.put(w, words.size());words.add(w);wordFreqs.add(f);});acTrie = new AhoCorasickDoubleArrayTrie<>();acTrie.build(acTrieMap);}/*** 基于trie查询前缀,生成DAG* @param sentence* @return*/static HashMap<Integer, List<Integer>> makeDAG(String sentence) {int len = sentence.length();HashMap<Integer, List<Integer>> dag = new HashMap<>();acTrie.parseText(sentence).forEach(hit -> dag.computeIfAbsent(hit.begin, k -> new ArrayList<>()).add(hit.end - hit.begin));for (int k = 0; k < len; k++) {if (!dag.containsKey(k)) {List<Integer> lis = new ArrayList<>();lis.add(1);dag.put(k, lis);}}return dag;}/*** 采用动态规划查找最大概率路径, 找出基于词频的最大切分组合*/private static Map<Integer, Pair<Integer, Double>> calcMaxProbPath(String sentence, Map<Integer, List<Integer>> dag) {int len = sentence.length();Map<Integer, Pair<Integer, Double>> route = new HashMap<>();route.put(len, Pair.of(0, 0d));double logTotal = 100;for (int k = len - 1; k >= 0; k--) {int offset = 0;double maxProb = -Double.MAX_VALUE;for (Integer x : dag.get(k)) {int end = k + x;//查位置int idx = acTrie.exactMatchSearch((sentence.substring(k, end)));//查词频int freq =  idx < 0 ? 0: wordFreqs.get(idx);subStringFreq.put(Pair.of(k, end), freq);double prob = Math.log(freq > 0 ? freq : 1) - logTotal + route.get(end).getValue();if (maxProb < prob) {maxProb = prob;offset = end - 1;}}route.put(k, Pair.of(offset, maxProb));}return route;}public static void main(String[] args){List<String> result = new ArrayList<>();String sentence ="我们经常有意见分歧";HashMap<Integer, List<Integer>>  dag =  TestSpilit.makeDAG(sentence);System.out.println(dag);Map<Integer, Pair<Integer, Double>> route = TestSpilit.calcMaxProbPath(sentence,dag);int st = 0;int ed;StringBuilder sb = new StringBuilder();while (st < sentence.length()) {ed = route.get(st).getKey() + 1;if (ed - st == 1 && Character.isLetterOrDigit(sentence.charAt(st))) {sb.append(sentence.charAt(st));} else {if (sb.length() > 0) {result.add(sb.toString());sb.setLength(0);}result.add(sentence.substring(st, ed));}st = ed;}if (sb.length() > 0) {result.add(sb.toString());}System.out.println(result);}
}

输出:

{0=[1], 1=[1], 2=[1, 2], 3=[1], 4=[1, 3], 5=[1, 2], 6=[1, 3], 7=[1, 2], 8=[1]}
[我们, 经常, 有意见, 分歧]

这里有些词典数据初始化是写死的,没有真正的加载txt文件。但是关键的步骤我都摘取出来。

1 创建DAG:生成句子中汉字所有可能成词情况所构成的有向无环图

jieba是基于 trie 树结构实现高效词图扫描

2 采用动态规划算法计算最佳切词组合

这个可以结合上面的手推图来理解。

 


http://chatgpt.dhexx.cn/article/WGXYpXpO.shtml

相关文章

HMM-维特比算法

HMM-维特比算法&#xff08;viterbi&#xff09; HMM回顾隐马科夫链解法&#xff1a;维特比算法&#xff08;Viterbi&#xff09; HMM回顾 最终的公式可以解释主要分为两个部分&#xff1a; P(xi|yi)&#xff0c;发射概率&#xff0c;字面意思是从一个词性中发射/生成出某一个…

Viterbi算法(维特比算法)

维特比算法背景&#xff1a; 安德鲁维特比&#xff08;Andrew J. Viterbi&#xff09;&#xff0c;CDMA之父&#xff0c;IEEE Fellow&#xff0c;高通公司创始人之一&#xff0c;高通首席科学家。他开发了卷积码编码的最大似然算法而享誉全球。1991年香农奖&#xff08;Claude …

HMM+维特比算法

一、简介 Viterbi 算法 考虑到穷举方法的缺点&#xff0c;可以采用&#xff1a;Viterbi 算法: 动态搜索最优状态序列&#xff0c;这样每个节点保存的是到当前节点的局部最优概率&#xff1b;依据最后一个时刻中概率最高的状态&#xff0c;逆向找其路径中的上一个最大部分最优路…

维特比算法的java实现_原创:维特比算法

看了宗成庆博士的《统计自然语言处理(中文信息处理)》的第六章&#xff0c;对维特比算法有着非常精辟的讲解。把其中的讲解上传上来&#xff0c;个人感觉比较正统。 今天用Java实现了这个算法&#xff0c;也可以转换为C代码&#xff1a; package com.nlp.hmm.algorithm.viterbi…

隐马尔可夫(HMM)、前/后向算法、Viterbi算法 再次总结

本总结是是个人为防止遗忘而作&#xff0c;不得转载和商用。 说明&#xff1a;此篇是作者对“隐马尔可夫模型”的第二次总结&#xff0c;因此可以算作对上次总结的查漏补缺以及更进一步的理解&#xff0c;所以很多在第一次总结中已经整理过的内容在本篇中将不再重复&#xff0c…

维特比(Viterbi)算法

维特比算法 (Viterbi algorithm) 是机器学习中应用非常广泛的动态规划算法&#xff0c;在求解隐马尔科夫、条件随机场的预测以及seq2seq模型概率计算等问题中均用到了该算法。实际上&#xff0c;维特比算法不仅是很多自然语言处理的解码算法&#xff0c;也是现代数字通信中使用…

小白给小白详解维特比算法(一)

小白给小白详解维特比算法&#xff08;一&#xff09; 小白给小白详解维特比算法一 篱笆网络Lattice的最短路径问题 这个问题长什么样子这个问题难在哪里简化成这个模样你总能回答了吧下一步我们该干什么 别倒立了我们再从头想一下这个问题 我们是怎么走过来的来我们从A开始走这…

viterbi算法

维特比算法&#xff08;Viterbi Algorithm&#xff09; 标签&#xff08;空格分隔&#xff09;&#xff1a; 未分类 维特比算法&#xff08;Viterbi Algorithm&#xff09; viterbi算法用于寻找最可能的隐藏状态序列(Finding most probable sequence of hidden states) 对于…

维特比算法

1. 概述 维特比算法是安德鲁.维特比(Andrew Viterbi)于1967年为解决通信领域中的解码问题而提出的&#xff0c;它同样广泛用于解决自然语言处理中的解码问题&#xff0c;隐马尔可夫模型的解码是其中典型的代表。无论是通信中的解码问题还是自然语言处理中的解码问题&#xff0c…

维特比算法(Viterbi algorithm)

一、维特比算法(Viterbi)原理以及简单实现 维特比算法&#xff08;Viterbi algorithm&#xff09;是一种动态规划算法。它用于寻找最有可能产生观测事件序列的维特比路径——隐含状态序列&#xff0c;特别是在马尔可夫信息源上下文和隐马尔可夫模型中。 维特比算法实际是用动…

机器学习:维特比算法(Viterbi Algorithm)

一、维特比算法&#xff08;Viterbi Algorithm&#xff09;讲解方式01&#xff1a;篱笆网络&#xff08;Lattice&#xff09;的最短路径问题 已知下图的篱笆网络&#xff0c;每个节点之间的数字表示相邻节点之间的距离&#xff0c;举个例子来说&#xff0c;如果我走&#xff0…

字符串匹配原理及实现(C++版)

字符串匹配原理及实现&#xff08;C版&#xff09; 1. 字符串匹配概念2. BF2.1 原理2.2 代码实现 3. KMP3.1 原理3.2 代码实现 4. BM4.1 坏字符4.2 好后缀4.3 代码实现 1. 字符串匹配概念 在查找操作中&#xff0c;我们用到很重要的概念就是字符串匹配&#xff0c;所谓字符串匹…

C++之单字符串匹配问题

有很多算法可以解决单模式匹配问题。而根据在文本中搜索模式串方式的不同&#xff0c;我们可以将单模式匹配算法分为以下几种&#xff1a; 基于前缀搜索方法&#xff1a;在搜索窗口内从前向后&#xff08;沿着文本的正向&#xff09;逐个读入文本字符&#xff0c;搜索窗口中文…

字符串——字符串匹配

文章目录 字符串匹配一、基本概念字符串匹配问题符号和术语后缀重叠引理 二、朴素字符串匹配算法三、Rabin-Karp算法(字符串Hash算法)进制Hash质数Hash 四、利用有限自动机进行字符串匹配有限自动机字符串匹配自动机计算状态转移函数代码 五、Knuth-Morris-Pratt算法模式的前缀…

朴素字符串匹配

描述 字符串匹配问题的形式定义&#xff1a; 文本(Text)是一个长度为 n 的字符串:T;模式(Pattern)是一个长度为 m 且 m≤n 的字符串:P; T 和 P 中的元素都属于有限的字母表 Σ 表; 有效位移 (Valid Shift): 如果 0≤ s ≤n-m&#xff0c;并且 T[s1…sm] P[1…m]&#xff0c…

算法之字符串匹配一

目录 前言&#xff1a; BF算法&#xff1a; RK算法 总结&#xff1a; 参考资料 前言&#xff1a; 字符串匹配指的是一个短点的字符串与一个长点的字符串进行匹配&#xff0c;并确认短的字符串是否在长的字符串中存在。匹配算法有很多&#xff0c;本文介绍两种简单、容易…

字符串匹配算法(C语言实现)

目录 文章目录 前言 一、BF算法 二、KMP算法 1.算法介绍 2.算法思路 3.整体代码实现 总结 前言 字符串匹配算法又称模式匹配算法&#xff0c;该算法的目的是为了子串从主串中寻找是否有与其匹配的部分&#xff0c; 其可分为BF暴力检索、RK哈希检索、KMP、BM等等&#xff0c;本…

shell字符串匹配

一、简介 Bash Shell提供了很多字符串和文件处理的命令。如awk、expr、grep、sed等命令&#xff0c;还有文件的排序、合并和分割等一系列的操作命令。grep、sed和awk内容比较多故单独列出&#xff0c;本文只涉及字符串的处理和部分文本处理命令。 二、字符串处理 1、expr命令…

golang字符串匹配算法

简介 字符串匹配算法主要用于在一个较长的文本串中查找一个较短的字符串&#xff08;称为模式串&#xff09;。在 Golang 中&#xff0c;可以使用最常见的字符串匹配算法之一&#xff1a;Knuth-Morris-Pratt&#xff08;KMP&#xff09;算法&#xff0c;它的时间复杂度为 O(nm…

【数据结构】字符串匹配(暴力匹配)

原理解析&#xff1a; 字符串匹配方法&#xff0c;创建两个字符串结构&#xff0c;主 串和子串比较。 主串字符 a 和 子串字符 c 不匹配&#xff0c;主串的指针向下移动&#xff0c;移动到上一次开始比较的下一个位置。 子串指向开始的位置。 主串字符 b 和 子串字符 c 不匹配…