高效的柬式分词解决方案?
我正在寻找一个方法,把柬埔寨语(高棉语)中的长句子拆分成单独的词(使用UTF-8编码)。高棉语的特点是单词之间没有空格。虽然网上有一些解决方案,但效果都不太理想(可以参考这里和这里),而且这些项目也没有继续更新。
下面是一个需要拆分的高棉语示例句子(实际上可能会更长):
ចូរសរសើរដល់ទ្រង់ដែលទ្រង់បានប្រទានការទាំងអស់នោះមកដល់រូបអ្នកដោយព្រោះអង្គព្រះយេស៊ូវ ហើយដែលអ្នកមិនអាចរកការទាំងអស់នោះដោយសារការប្រព្រឹត្តរបស់អ្នកឡើយ។
我想要找到一个有效的解决方案来拆分高棉语单词,主要有两个目的:首先,它可以鼓励那些使用高棉旧字体(非Unicode字体)的人转向Unicode字体(Unicode有很多好处);其次,它可以让旧的高棉字体快速导入到Unicode中,以便能用拼写检查器进行检查,而不需要手动逐个拆分单词,因为对于大文档来说,这样做会非常耗时。
我不需要100%的准确性,但速度很重要(尤其是需要拆分的高棉语句子可能会很长)。我对建议持开放态度,目前我有一个正确拆分的高棉语单词的大词库(使用不换行的空格),并且我创建了一个单词概率字典文件(frequency.csv),打算用它作为拆分器的字典。
我发现了这段Python代码这里,它使用了维特比算法,据说运行速度很快。
import re
from itertools import groupby
def viterbi_segment(text):
probs, lasts = [1.0], [0]
for i in range(1, len(text) + 1):
prob_k, k = max((probs[j] * word_prob(text[j:i]), j)
for j in range(max(0, i - max_word_length), i))
probs.append(prob_k)
lasts.append(k)
words = []
i = len(text)
while 0 < i:
words.append(text[lasts[i]:i])
i = lasts[i]
words.reverse()
return words, probs[-1]
def word_prob(word): return dictionary.get(word, 0) / total
def words(text): return re.findall('[a-z]+', text.lower())
dictionary = dict((w, len(list(ws)))
for w, ws in groupby(sorted(words(open('big.txt').read()))))
max_word_length = max(map(len, dictionary))
total = float(sum(dictionary.values()))
我还尝试使用了这篇页面作者的Java源代码:基于字典的文本分割,但运行速度太慢,无法使用(因为我的单词概率字典有超过10万个词条……)。
这里还有一个Python的选项,来自从没有空格的文本中检测最可能的单词/组合词:
WORD_FREQUENCIES = {
'file': 0.00123,
'files': 0.00124,
'save': 0.002,
'ave': 0.00001,
'as': 0.00555
}
def split_text(text, word_frequencies, cache):
if text in cache:
return cache[text]
if not text:
return 1, []
best_freq, best_split = 0, []
for i in xrange(1, len(text) + 1):
word, remainder = text[:i], text[i:]
freq = word_frequencies.get(word, None)
if freq:
remainder_freq, remainder = split_text(
remainder, word_frequencies, cache)
freq *= remainder_freq
if freq > best_freq:
best_freq = freq
best_split = [word] + remainder
cache[text] = (best_freq, best_split)
return cache[text]
print split_text('filesaveas', WORD_FREQUENCIES, {})
--> (1.3653e-08, ['file', 'save', 'as'])
我对Python还是个新手,实际上对编程(除了网站开发)也很陌生,所以请多多包涵。有没有人有觉得比较有效的方案呢?
3 个回答
我觉得这个主意挺不错的。
我建议你在有了一些经验之后,可以添加一些规则,这些规则可以非常具体。比如,可以根据前面的词、后面的词、周围的词,或者是当前词前面的一系列词来制定规则。这里列举了一些最常见的情况。你可以在 gposttl.sf.net 项目中找到一套规则,这个项目是关于词性标注的,相关的规则在 data/contextualrulefile 文件里。
这些规则应该在统计评估完成后使用,它们可以进行一些微调,显著提高准确性。
这个 Python 示例中的 filesaveas
看起来是在整个输入字符串中进行递归处理(for i in xrange(1, len(text) + 1)
),一路上把最好的结果放进 cache
中;在每个可能的单词处,它会 接着 查看 下一个 单词(这个单词又会去看下一个,以此类推),如果第二个单词看起来不太好,就不会保存这个特定的单词。感觉这个过程的运行时间是 O(N!),其中 N 是输入字符串的长度。
这个方法非常聪明,但可能只适合简单的任务。你知道最长的高棉语单词有多长吗?我希望它少于 20 个字符。
也许如果你每次输入 20 个字符,可以把运行时间控制在一个合理的范围内。先输入前 20 个字符,提取出第一个单词,然后再输入剩下的内容。如果你重复使用缓存,可能会出现一些奇怪的情况,比如在过程中存储部分单词。
换个话题,有多少高棉语单词是由两个或多个合法的高棉语单词组合而成的?(类似于“penknife”或“basketball”)如果不太多,可能可以创建一组字典,按单词长度分类,从单词到使用概率进行映射。
比如,最长的高棉语单词是 14 个字符;那么就把 14 个字符的输入放入 len14
字典中,存储它的概率。再把 13 个字符放入 len13
,存储概率。依此类推,直到 1 个字符放入 len1
。然后选择概率最高的解释,保存这个单词,去掉相应的字符,再试一次。
这样对于像“I”和“Image”这样的输入就不会出错,或许更长的输入应该自动增加概率?
谢谢你提出这个有趣的问题 ;) 我之前不知道有这样的语言,真不错。
ICU库(它有适用于Python和Java的接口)里面有一个叫做DictionaryBasedBreakIterator的类,可以用来处理这个问题。