[分词]基于Lucene8版本的混合分词器(分词合并)

本文介绍了如何在Lucene中使用自定义分词器,结合IK分词和WhitespaceAnalyzer,解决古文分词问题,以获取更广泛的词项,提高检索覆盖率。作者提供了Java代码示例以展示实现过程。
摘要由CSDN通过智能技术生成

介绍

  近期在研究NLP相关技术,再次感受到中文领域分词算法的重要性,突然想到一年前在项目中使用到的Lucene技术中关于分词器的部分,对其再次进行对比分析,并混合多种现有分词方法,获得更多词项。

背景

  目前在Lucene中使用较多的 IK 分词,但遇到古文的话分词效果并不好,会造成检索结果达不到要求。

  在实际使用Lucene的过程中,很难对同一个字段同时使用多种分词算法。

  NGram算法虽然能够获得更多的词项,但是划分的词项太多,造成检索过程中的效率问题。

解决方案

  采用自定义分词器的思路,合并多种分词器为一个,从而获得更多词项,后续在整合Lucene的时候,使用此分词器从而得到更好地效果。

实现代码

  详见如下测试代码,此代码调用自定义分词器,对输入字符串分别进行 IKAnalyzer 和 WhitespaceAnalyzer 两种分词并合并,如果遇到其他需求,可以合并其他分词器,最终达到理想效果。

  核心代码详见 CustomTokenizer.java 文件。完整代码详见:pap-lucene-spring-boot-starter

public class CombinedAnalyzerTest {
    public void words() {
        Analyzer analyzer = new CombinedAnalyzer();
        TokenStream tokenStream = null;
        try {
            tokenStream = analyzer.tokenStream("", new StringReader("电视 牛旦 法 水电费 水电费韦尔奇adfmex alexgaoyh 王测试数据"));
            ListAttribute attr = tokenStream.addAttribute(ListAttribute.class);
            tokenStream.reset();
            while (tokenStream.incrementToken()) {
                System.out.println(attr.getValues().toString());
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (tokenStream != null) {
                try {
                    tokenStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

// 如上代码输出(包含了IK分词和空格分词两种结果,并去重):  [电视, 牛, 旦, 法, 水电费, 韦尔奇, adfmex, alexgaoyh, 王, 测试数据, 牛旦, 水电费韦尔奇adfmex, 王测试数据]
public class CustomTokenizer extends Tokenizer {

    private final ListAttribute listAttribute = addAttribute(ListAttribute.class);

    public CustomTokenizer() {
        super();
    }

    @Override
    public final boolean incrementToken() throws IOException {
        clearAttributes();

        if (input.read() != -1) {

            input.reset();
            List<String> ikResultList = ikResult(input);

            input.reset();
            List<String> whitespaceResultList = whitespaceResult(input);

            List<String> mergeList = Stream.of(ikResultList, whitespaceResultList).flatMap(Collection::stream).distinct().collect(Collectors.toList());
            listAttribute.setValues(mergeList);

            return true;
        } else {
            return false;
        }
    }

    private List<String> ikResult(Reader input) throws IOException {
        List<String> tokens = new ArrayList<>();
        Analyzer ikAnalyzer = new IKAnalyzer(true);
        TokenStream ikTokenStream = ikAnalyzer.tokenStream("", input);
        CharTermAttribute ikAttr = ikTokenStream.addAttribute(CharTermAttribute.class);
        ikTokenStream.reset();
        while (ikTokenStream.incrementToken()) {
            tokens.add(ikAttr.toString());
        }
        return tokens;
    }

    private List<String> whitespaceResult(Reader input) throws IOException {
        List<String> tokens = new ArrayList<>();
        Analyzer whitespaceAnalyzer = new WhitespaceAnalyzer();
        TokenStream whitespaceTokenStream = whitespaceAnalyzer.tokenStream("", input);
        CharTermAttribute whitespaceAttr = whitespaceTokenStream.addAttribute(CharTermAttribute.class);
        whitespaceTokenStream.reset();
        while (whitespaceTokenStream.incrementToken()) {
            tokens.add(whitespaceAttr.toString());
        }
        return tokens;
    }

}

总结

  此代码在Lucene8.6.2版本下通过测试,采用自定义分词器的方式,通过合同多种分词器的方式,达到更广泛的分词结果,从而变相提升检索过程中的覆盖率。

参考

  1. https://pap-docs.pap.net.cn/
  2. https://gitee.com/alexgaoyh/pap-lucene-spring-boot-starter
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值