我想创建一个自定义的NER模型。我做了以下工作:
训练数据 (stanford-ner.tsv):
Hello O! OMy Oname Ois ODamiano PERSON. O
属性 (stanford-ner.prop):
trainFile = stanford-ner.tsvserializeTo = ner-model.ser.gzmap = word=0,answer=1maxLeft=1useClassFeature=trueuseWord=trueuseNGrams=truenoMidNGrams=truemaxNGramLeng=6usePrev=trueuseNext=trueuseDisjunctive=trueuseSequences=trueusePrevSequences=trueuseTypeSeqs=trueuseTypeSeqs2=trueuseTypeySequences=truewordShape=chris2useLCuseGazettes=truegazette=gazzetta.txtcleanGazette=true
公告 (gazzetta.txt):
PERSON JohnPERSON Andrea
我通过命令行构建模型:
java -classpath "stanford-ner.jar:lib/*" edu.stanford.nlp.ie.crf.CRFClassifier -prop stanford-ner.prop
并使用以下命令进行测试:
java -classpath "stanford-ner.jar:lib/*" edu.stanford.nlp.ie.crf.CRFClassifier -loadClassifier ner-model.ser.gz -textFile test.txt
我进行了两个测试,测试文本如下:
>>> 测试1 <<<
-
文本:你好!我的名字是Damiano,这是一段用于测试的虚构文本。
-
输出你好/O !/O我的/O 名字/O 是/O Damiano/PERSON 和/O 这/O 是/O 一/O 段/O 虚构/O 的/O 文本/O 用/O 于/O 测试/O ./O
>>> 测试2 <<<
-
文本:你好!我的名字是John,这是一段用于测试的虚构文本。
-
输出你好/O !/O我的/O 名字/O 是/O John/O 和/O 这/O 是/O 一/O 段/O 虚构/O 的/O 文本/O 用/O 于/O 测试/O ./O
如你所见,只有“Damiano”实体被识别。这个实体在我的训练数据中,但“John”(第二个测试)在公告中。所以问题是:
为什么“John”实体没有被识别?
非常感谢你的帮助。
回答:
正如斯坦福FAQ所说,
如果使用了公告,这并不能保证公告中的词总是被用作预期类别的成员,也不能保证公告之外的词不会被选中。它只是为CRF提供了一个额外的特征。如果CRF对其他特征的权重更高,公告特征可能会被压倒。
如果你希望文本在且仅当它在词列表中时才被识别为某一类别的成员,你可能更喜欢使用斯坦福CoreNLP中包含的regexner或tokensregex工具。CRF NER并不能保证接受公告中所有词作为预期类别的一部分,并且它也可能接受公告之外的词作为类别的一部分。
顺便说一下,用“单元测试”的方式测试机器学习流程,即仅用一两个例子,不是好的做法,因为它应该在更大数据量上工作,而且更重要的是,它本质上是概率性的。
如果你想检查你的公告文件是否实际被使用,可能最好使用现有的例子(见上述链接页面底部的austen.gaz.prop
和austen.gaz.txt
示例),然后用你自己的名字替换多个名字,再进行检查。如果失败,首先尝试更改你的测试,例如,添加更多名字,重新编写文本等。