关于结构化感知器、自定义领域命名实体识别

何博士,我这里有几个基于HanLP 1.7.6 的问题,还请帮忙解答一下
1、如果想调整感知器特征向量生成的方式是不是要继承 com.hankcs.hanlp.model.perceptron.instance.NERInstance 针对要识别的命名实体不同调整生成不同的特征?
2、想要使用自己定制的NERInstance,是不是就要继承com.hankcs.hanlp.model.perceptron.PerceptronNERecognizer,实现其中的三个方法,因为我发现只有这三个方法会用到 Instance

public class CustomsPerceptronNERecognizer extends PerceptronNERecognizer {

	private String nerLabel;

	public CustomsPerceptronNERecognizer(String nerLabel, LinearModel nerModel) {
		super(nerModel);
		this.nerLabel = nerLabel;
	}

	public CustomsPerceptronNERecognizer(String nerLabel, String nerModelPath) throws IOException {
		super(nerModelPath);
		this.nerLabel = nerLabel;
	}

	@Override
	public boolean learn(String segmentedTaggedNERSentence) {
        // 调用定制的Instance
		return super.learn(createInstance(Sentence.create(segmentedTaggedNERSentence), model.featureMap));
	}

	@Override
	protected Instance createInstance(Sentence sentence, FeatureMap featureMap) {
		NERTagSet tagSet = (NERTagSet) featureMap.tagSet;
		List<String[]> collector = Utility.convertSentenceToNER(sentence, tagSet);
		String[] wordArray = new String[collector.size()];
		String[] posArray = new String[collector.size()];
		String[] nerArray = new String[collector.size()];
		Utility.reshapeNER(collector, wordArray, posArray, nerArray);
         // 调用定制的Instance
		return CustomsNERInstanceFactory.getNERInstance(nerLabel, wordArray, posArray, nerArray, tagSet, featureMap);
	}

	@Override
	public String[] recognize(String[] wordArray, String[] posArray) {
        // 调用定制的Instance
		return super.recognize(CustomsNERInstanceFactory.getNERInstance(nerLabel, wordArray, posArray, model.featureMap));
	}

}

3、自定义的语料库,比如说 有一个需要识别的 命名实体代号为 GeographicalLocation,但是语料库可能只有几千条,是否是将这些语料库跟PKU语料库合并起来一起作为 命名实体识别的语料库会 比 只有这几千条语料 效果好?

是的。

是的,但你还需要继承NERTrainer做训练。为了方便继承,我作了一下修改:

由于Java不支持C++的模板,而反射又会影响效率,所以只有继承然后替换所有的new了。

条件是如果PKU里面也有地址你得把它们改成你的标注,不然就引入了噪音。

好的,谢谢。何博士

@hankcs 何博士您好,首先感谢一下您之前的 耐心、细心的 解答。
目前还有一个问题,就是使用结构化感知器对命名实体的识别时,命名实体有可能是简单词 也有可能是 复合词,
看过您在论坛中其他帖子里说过,简单词用CWS+POS,复合词用NER,三者用LexicalAnalyzer组合起来。

所以我对这样的命名实体的识别,采用以下步骤,请您帮忙看一下有没有问题

  1. 首先收集原料数据,基于HanLP中使用默认模型的结构化感知器对原料数据进行第一步的分词、词性标注、实体识别
        PerceptronLexicalAnalyzer perceptronLexicalAnalyzer = new PerceptronLexicalAnalyzer();
        perceptronLexicalAnalyzer.analyze("据悉这批走私品是从非洲尼日利亚运至越南的");

初步结果:

据悉/v 这/r 批/q 走私品/n 是/v 从/p [非洲/ns 尼日利亚/ns]/ns 运/v 至/p 越南/ns 的/u 

2、维护自己的 国家命名实体 country

据悉/v 这/r 批/q 走私品/n 是/v 从/p [非洲/ns 尼日利亚/ns]/country 运/v 至/p 越南/country 的/u 

上面的 非洲尼日利亚 属于复合词可以通过NER标注,上面的 越南 属于简单词 需要通过锻炼POS来标注
3、重复上面的 1、2 步骤
4、因为语料库中的分词没做任何调整,所以结构化感知器中的中文分词 PerceptronSegmenter 仍然使用HanLP同的默认模型,而 词性标注、命名实体识别 由于添加了自定义的词性 和 命名实体,所以需要自己重新按照自己的预料库进行训练之后使用。

		String corpusFile = "/Users/wangjie/Development/ELK/hanlp/自定义预料/customCorpus.txt";

		// 1、训练一个分词器(因为语料库就是根据默认感知器分词器生成的,所以此处直接默认的分词语料)
		String cwsModel = "/Users/wangjie/Development/ELK/hanlp/data/model/perceptron/large/cws.bin";
		PerceptronSegmenter perceptronSegmenter = new PerceptronSegmenter(cwsModel);

		// 2、训练一个词性标注
		POSTrainer posTrainer = new POSTrainer();
		String posModelFile = corpusFile.replace(".txt", ".pos.bin");
		LinearModel posModel = posTrainer.train(corpusFile, null, posModelFile).getModel();
		PerceptronPOSTagger perceptronPOSTagger = new PerceptronPOSTagger(posModel);

		// 3、训练命名实体的
		NERTrainer nerTrainer = new NERTrainer();
		nerTrainer.tagSet.nerLabels.clear();
		nerTrainer.tagSet.nerLabels.add("country");
		String nerModelFile = corpusFile.replace(".txt", ".ner.bin");
		LinearModel nerModel = nerTrainer.train(corpusFile, null, nerModelFile).getModel();
		PerceptronNERecognizer recognizer = new PerceptronNERecognizer(nerModel);

		// 4、运用训练好之后结构化感知器进行 命名实体识别
		PerceptronLexicalAnalyzer perceptronLexicalAnalyzer = new PerceptronLexicalAnalyzer(perceptronSegmenter, perceptronPOSTagger, recognizer);
	

何博士,请您帮我看一下我上面的处理逻辑有没有什么问题,谢谢

没有问题。