🚀 作者 :“码上有前”
🚀 文章简介 :NLP
🚀 欢迎小伙伴们 点赞👍、收藏⭐、留言💬
中文分词技术:规则、统计与混合方法全解
摘要:本文围绕中文分词技术展开深入剖析,详细讲解基于规则(正向/逆向/双向最大匹配法)、基于统计(n - gram模型、隐马尔可夫模型)以及混合分词(以jieba库为例)的三类方法。通过原理阐释、实例演示与代码实践,对比不同分词技术的优缺点与适用场景,帮助读者全面掌握中文分词核心技能,为自然语言处理后续任务筑牢基础。
一、引言
中文分词作为自然语言处理(NLP)基石,需将连续文本切分为词汇单元,为文本分析、信息检索等上层应用奠基。因中文无天然分词标记,需借助规则、统计及混合技术实现,以下逐层解析。
二、基于规则(词典)的分词方法
(一)核心逻辑
依赖预构建词典,按匹配规则从文本识别词汇,以“南京市长江大桥”演示三种方法。
(二)具体方法
1. 正向最大匹配法(MM)
步骤:
① 设定最大词长(如5),从文本开头截取等长片段;
② 查词典,匹配则分割,失败则缩短片段长度(减1),重复匹配;
③ 遍历文本,直至全部分割。
Python示例代码:
def forward_max_match(text, word_dict, max_len=5):
result = []
while text:
start = 0
end = min(max_len, len(text))
while end > start:
segment = text[start:end]
if segment in word_dict:
result.append(segment)
text = text[end:]
break
end -= 1
else:
# 未匹配到,单字分割(可选策略)
result.append(text[0])
text = text[1:]
return result
word_dict = {"南京市", "长江", "大桥"}
text = "南京市长江大桥"
print(forward_max_match(text, word_dict))
# 输出: ['南京市', '长江', '大桥']
优缺点:
- 优点:逻辑直观,词典匹配成功时分词高效;
- 缺点:依赖词典完备性,遇未登录词易出错;最大词长难适配所有文本,长文本易因片段截取策略产生歧义。
2. 逆向最大匹配法(RMM)
步骤:
① 设定最大词长,从文本末尾截取等长片段;
② 查词典,匹配则分割,失败缩短片段长度(减1),重复匹配;
③ 遍历文本,完成分割。
Python示例代码:
def reverse_max_match(text, word_dict, max_len=5):
result = []
while text:
end = len(text)
start = max(0, end - max_len)
while start < end:
segment = text[start:end]
if segment in word_dict:
result.insert(0, segment)
text = text[:start]
break
start += 1
else:
result.insert(0, text[-1])
text = text[:-1]
return result
word_dict = {"南京市", "长江", "大桥"}
text = "南京市长江大桥"
print(reverse_max_match(text, word_dict))
# 输出: ['南京市', '长江', '大桥']
优缺点:
- 优点:对部分歧义文本(如“研究生命起源”),逆向匹配结果更优;
- 缺点:同样依赖词典,且分词效率随文本长度增加而降低,需频繁截取片段查词典。
3. 双向最大匹配法(BMM)
步骤:
① 分别用正向、逆向最大匹配法分词,得到两组结果;
② 按规则选优:优先选分词数少的;若数量相同,选单字少的;若仍相同,选逆向结果(或自定义规则 )。
Python示例代码:
def bidirectional_max_match(text, word_dict, max_len=5):
forward_result = forward_max_match(text, word_dict, max_len)
reverse_result = reverse_max_match(text, word_dict, max_len)
# 选优规则:分词数少优先,再看单字数量
def count_single_words(res):
return sum(1 for w in res if len(w) == 1)
if len(forward_result) != len(reverse_result):
return forward_result if len(forward_result) < len(reverse_result) else reverse_result
else:
forward_single = count_single_words(forward_result)
reverse_single = count_single_words(reverse_result)
return forward_result if forward_single < reverse_single else reverse_result
word_dict = {"南京市", "长江", "大桥", "研究", "生命", "起源"}
text = "研究生命起源"
print(bidirectional_max_match(text, word_dict))
# 正向可能 ['研究生', '命', '起源'],逆向 ['研究', '生命', '起源'],选逆向,输出: ['研究', '生命', '起源']
优缺点:
- 优点:通过双向匹配选优,降低歧义概率;
- 缺点:需执行两次最大匹配,效率低于单一方向;规则选优可能无法覆盖所有复杂歧义场景。
三、基于统计的分词方法
(一)语言模型(n - gram模型)
基本思想:计算不同分词路径概率,选概率最大的结果,将分词转化为概率求解问题。
步骤:
① 基于大规模语料,统计n - gram概率(如二元模型,统计相邻词共现概率 P ( w i ∣ w i − 1 ) P(w_i|w_{i - 1}) P(wi∣wi−1) );
② 对文本生成所有可能分词路径,计算每条路径概率(路径概率为各词概率乘积 );
③ 选概率最大的路径作为分词结果。
Python示例(简化版,基于频次模拟概率):
from collections import defaultdict
import math
# 模拟语料统计n - gram频次
corpus = ["机器学习 是 热门 领域", "机器 学习 很 重要", "我爱 机器学习"]
word_freq = defaultdict(int)
bigram_freq = defaultdict(int)
for sentence in corpus:
words = sentence.split()
for word in words:
word_freq[word] += 1
for i in range(len(words) - 1):
bigram_freq[(words[i], words[i + 1])] += 1
# 计算概率(简化为频次比)
def get_word_prob(word):
return word_freq[word] / sum(word_freq.values()) if word_freq[word] else 0
def get_bigram_prob(prev, curr):
return bigram_freq[(prev, curr)] / word_freq[prev] if word_freq[prev] and bigram_freq[(prev, curr)] else 0
# 分词路径概率计算(简化示例,仅演示逻辑)
def ngram_segment(text, possible_segments):
best_path = None
max_prob = -math.inf
for seg in possible_segments:
prob = 1
for i in range(len(seg) - 1):
prob *= get_bigram_prob(seg[i], seg[i + 1])
if prob > max_prob:
max_prob = prob
best_path = seg
return best_path
text = "机器学习"
possible_segments = [["机器", "学习"], ["机", "器", "学习"]]
print(ngram_segment(text, possible_segments))
# 输出: ['机器', '学习'](假设“机器 学习”概率更高)
优缺点:
- 优点:无需依赖固定词典,能适应新词;通过概率模型挖掘语义关联;
- 缺点:需大规模语料训练,计算成本高;分词路径爆炸(长文本可能路径极多 ),实际应用需剪枝优化。
(二)隐马尔可夫模型(HMM)
基本思想:将分词转化为字的序列标注任务,定义隐藏状态(B - 词首、M - 词中、E - 词尾、S - 单字成词 ),用维特比算法求解最优状态序列。
步骤:
① 标注训练语料,统计状态转移概率(如B→M、M→E等概率 )、发射概率(字→状态的概率,如“学”作为B状态的概率 );
② 对新文本,用维特比算法计算每个位置的状态概率,找到最优状态序列;
③ 依据状态序列分割文本(B开头、E结尾为一词,S单独成词 )。
Python示例(简化版,基于简单标注训练):
import numpy as np
from collections import defaultdict
# 模拟训练语料标注(状态:B, M, E, S)
corpus = [
("我", "S"), ("爱", "S"), ("自", "B"), ("然", "E"),
("语", "B"), ("言", "E"), ("处", "B"), ("理", "E")
]
state_trans = defaultdict(lambda: defaultdict(int)) # 状态转移计数
emission_prob = defaultdict(lambda: defaultdict(int)) # 发射概率计数
states = ["B", "M", "E", "S"]
prev_state = corpus[0][1]
for char, state in corpus[1:]:
state_trans[prev_state][state] += 1
emission_prob[state][char] += 1
prev_state = state
# 转化为概率(简化为频次比)
for state in states:
total_trans = sum(state_trans[state].values())
for next_state in states:
state_trans[state][next_state] /= total_trans if total_trans else 0
total_emission = sum(emission_prob[state].values())
for char in emission_prob[state]:
emission_prob[state][char] /= total_emission if total_emission else 0
# 维特比算法求解最优状态序列
def viterbi(text, states, state_trans, emission_prob):
dp = [{} for _ in range(len(text))]
# 初始化第一个字
for state in states:
dp[0][state] = emission_prob[state].get(text[0], 0)
# 递推计算
for i in range(1, len(text)):
for curr_state in states:
dp[i][curr_state] = max(
dp[i - 1][prev_state] * state_trans[prev_state].get(curr_state, 0)
for prev_state in states
) * emission_prob[curr_state].get(text[i], 0)
# 找最优路径
best_path = [max(dp[-1], key=dp[-1].get)]
for i in range(len(text) - 2, -1, -1):
best_path.insert(0, max(states, key=lambda s: dp[i][s] * state_trans[s].get(best_path[0], 0)))
return best_path
text = "我爱自然"
states_seq = viterbi(text, states, state_trans, emission_prob)
# 依据状态序列分词
seg_result = []
word = ""
for char, state in zip(text, states_seq):
if state in ["B", "S"]:
if word:
seg_result.append(word)
word = char
else:
word += char
seg_result.append(word)
print(seg_result)
# 输出: ['我', '爱', '自然'](假设状态序列匹配)
优缺点:
- 优点:能处理未登录词,通过序列标注挖掘字间关联;
- 缺点:依赖高质量标注语料,训练成本高;状态定义和概率计算对分词效果影响大,复杂文本易出错。
四、混合分词:以jieba库为例
(一)原理
融合规则分词(词典匹配)与统计方法(HMM优化未登录词、歧义),先词典匹配,再用HMM校准。
(二)实例与代码
Python示例(jieba库使用):
import jieba
# 加载自定义词典(可选,增强规则分词)
jieba.load_userdict("custom_dict.txt")
text = "深度学习助力自然语言处理发展"
# 分词(混合模式,默认启用)
seg_result = jieba.lcut(text)
print(seg_result)
# 输出: ['深度学习', '助力', '自然语言处理', '发展']
内部流程简化说明:
- 规则分词:用内置/自定义词典,匹配“深度学习”“助力”等词;
- 统计优化:对未匹配部分(如新词),用HMM模型标注字状态,识别“自然语言处理”等词;
- 融合结果:结合规则与统计结果,输出最终分词。
优缺点:
- 优点:兼顾词典匹配高效性与统计方法对新词、歧义的适应性,实际场景(如新闻、社交文本)表现好;
- 缺点:需维护词典,HMM模型依赖训练,极端领域(如专业医学文本 )需定制化优化。
五、总结
中文分词技术中,规则分词(MM/RMM/BMM )逻辑清晰但依赖词典;统计分词(n - gram/HMM )适应新词但成本高;混合分词(jieba为代表 )融合二者优势,成为实际应用主流。开发时,需根据场景选方法:词典覆盖度高的领域(如法律文本 )用规则分词;网络动态文本用混合分词;科研探索语义建模用统计分词。掌握各方法步骤、代码与优缺点,可精准应对NLP分词需求,为上层应用筑牢基础。
(注:文中代码为简化演示,实际工程需结合大规模语料、高效概率计算优化,如用jieba等成熟库处理复杂场景 。)。
五、总结
中文分词技术从基于规则的简单匹配,到基于统计的概率求解,再到混合策略的高效应用,各有优劣。基于规则的方法简单快速,但对新词和歧义处理不足;基于统计的方法能利用语料信息,但依赖数据且计算复杂;混合分词(如jieba库)融合两者优势,在实际NLP任务中应用广泛。在实践中,需根据文本特点(如领域、新词数量 )选择合适的分词方法,或结合多种方法优化分词效果,为后续的词性标注、文本理解等任务提供准确的词语单元,推动NLP应用的深入发展。
读者可尝试构建不同领域的词典,测试jieba库在专业文本(如医疗、金融文本 )中的分词效果,对比不同分词技术在实际任务(如文本分类 )中的影响,进一步深化对中文分词技术的理解与应用。