hankcs / HanLP

Natural Language Processing for the next decade. Tokenization, Part-of-Speech Tagging, Named Entity Recognition, Syntactic & Semantic Dependency Parsing, Document Classification
https://hanlp.hankcs.com/en/
Apache License 2.0
33.83k stars 10.12k forks source link

关于CharacterBasedGenerativeModel 原始语料无法找到的问题 #566

Closed qujinqiang closed 4 years ago

qujinqiang commented 7 years ago

基于2阶HMM 分词器 HMMSegment中使用到的模型,但是无法找到与之相对应的原始语料。还望大神能够提供与之相对应的原始语料方便我们学习和进行debug

hankcs commented 7 years ago

目前对公众免费授权的语料有:http://sighan.cs.uchicago.edu/bakeoff2005/ 请转换为BMES字标注即可。

qujinqiang commented 7 years ago

谢谢大神,认真学习大神的神作。

TylunasLi commented 7 years ago

我最近对HMMSegment做了一定的修改,速度略有提升(这次改完之后性能还有一些改进空间),并且在Bakeoff2005PKU刷出了不错的成绩。下面是我记的一些笔记。 http://tylunas.lofter.com/post/1da3a3_10c453c5 唯一的问题是模型要修改。

hankcs commented 7 years ago

@TylunasLi 感谢指出,你的工作找到了问题的症结。如果可能的话,欢迎提交pr,谢谢。

hankcs commented 6 years ago

我最近整理文档,发现了问题。TnT的转移矩阵其实就是P(t3|t1,t2),这里的t指的是tag。转移矩阵在这里:

                        double p = pre[f][s] + log_prob(charArray[i - 2], f,
                                                        charArray[i - 1], s,
                                                        charArray[i],     t);

log_prob取出来的就是转移矩阵,并不需要额外的transMatrix。这里实现上犯了一个错误,那就是将观测char也考虑进去了,实际上HMM的状态转移只与隐状态有关,与显状态无关。NLTK的标准实现如下:

        if word in self._wd:
            self.known += 1

            for (history, curr_sent_logprob) in current_states:
                logprobs = []

                for t in self._wd[word].keys():
                    tC = (t,C)
                    p_uni = self._uni.freq(tC)
                    p_bi = self._bi[history[-1]].freq(tC)
                    p_tri = self._tri[tuple(history[-2:])].freq(tC)
                    p_wd = self._wd[word][t] / self._uni[tC]
                    p = self._l1 *p_uni + self._l2 *p_bi + self._l3 *p_tri
                    p2 = log(p, 2) + log(p_wd, 2)

                    # compute the result of appending each tag to this history
                    new_states.append((history + [tC],
                                       curr_sent_logprob + p2))

这里p是转移矩阵,p_wd是发射概率。 唯一与显状态有关的是发射概率,可正如你所言,严格来讲这份实现没考虑发射概率。实际上发射概率也在log_prob里面考虑了,而且考虑的是字符ngram到tag的发射概率。这也违反了发射概率的定义,所谓发射概率,观测的发射仅仅取决于当前状态,与ngram、之前的状态无关。 这份实现已经不是标准的TnT标注器了,它更像一个特征模板为unigram和bigram的感知机,所以你们的改进版可以拿到好看的分数,但应该比不上标准的感知机。 在计算发射概率时将字符ngram考虑进去其实是个好主意,因为字符ngram一直是非常有用的特征。TnT想解决的是trigram tag的稀疏问题,但对BMES这么小的标注集来讲则不存在,高阶ngram的转移特征没什么用处。真正稀疏的、有实际作用的是字符trigram特征,所以才出现这么一个“混血”的实现吧,其实论特征丰富程度,还是用线性模型吧。

TylunasLi commented 6 years ago

您的论述没有错,但是Which is More Suitable for Chinese Word Segmentation, the Generative Model or the Discriminative One?这篇论文中,观测char和tag是作为一对使用的(见公式6)。而且并没有明确实现细节。元论文作者的Urheen,SnowNLP和我的工作都是复现这篇论文的效果,(其中我的实验可能会更接近论文中的分数)不是简单的HMM模型。所以您后续的修改,都没有再参考这篇论文,我感到非常遗憾。

hankcs commented 6 years ago

感谢提供资料,当时废弃该模块的原因一是没找到正确的参考资料,二是因为准确率不理想。 你的试验是“训练数据来自1998年1-6月人民曰报,测试数据用了SiGHAN2005 PKU的语料”,然后分数是“96.14%”。这个分数其实并不高,特别是在训练集如此大的情况下。 由于我手头的pku语料合并了姓名,所以只能取2-6月份做训练集,1月份做测试集,感知机的分数是: P:96.74 R:96.25 F:96.50 在类似试验中,LTP宣称的分数是97.87(他们的特征模板更丰富,加了许多预处理规则)。也就是说,判别式模型能在更少的训练数据下得到更好的效果。在其他领域,普遍现象也是判别式效果更好,生成式模型更容易出论文。

目前HanLP的感知机实现还很朴素,如果使用一些高级的训练算法(RMSProp等),也加一些规则,可能还有一两个百分点的提升。总之事情还很多,人力有限,就没有往生成式模型的方向走了。现在代码库中的HMMSegmenter只是一份教学代码,不是HMMSegment的后续。

当然,如果你愿意继续改进该算法,我还是非常欢迎的。

hankcs commented 4 years ago

感谢您对HanLP1.x的支持,我一直为没有时间回复所有issue感到抱歉,希望您提的问题已经解决。或者,您可以从《自然语言处理入门》中找到答案。

时光飞逝,HanLP1.x感谢您的一路相伴。我于东部标准时间2019年12月31日发布了HanLP1.x在上一个十年最后一个版本,代号为最后的武士。此后1.x分支将提供稳定性维护,但不是未来开发的焦点。

值此2020新年之际,我很高兴地宣布,HanLP2.0发布了。HanLP2.0的愿景是下一个十年的前沿NLP技术。为此,HanLP2.0采用TensorFlow2.0实现了最前沿的深度学习模型,通过精心设计的框架支撑下游NLP任务,在海量语料库上取得了最前沿的准确率。作为第一个alpha版本,HanLP 2.0.0a0支持分词、词性标注、命名实体识别、依存句法分析、语义依存分析以及文本分类。而且,这些功能并不仅限中文,而是面向全人类语种设计。HanLP2.0提供许多预训练模型,而终端用户仅需两行代码即可部署,深度学习落地不再困难。更多详情,欢迎观看HanLP2.0的介绍视频,或参与论坛讨论

展望未来,HanLP2.0将集成1.x时代继承下来的高效率务实风范,同时冲刺前沿研究,做工业界和学术界的两栖战舰,请诸君继续多多指教,谢谢。