在Solr中该如何使用IK分词器呢,这是小伙伴们问的频率比较高的一个问题,今晚特此更新此篇博客。其实之前我在其他博客里已经使用了IK分词器,只是我没做详细说明。
在schema.xml配置中其实有很多关于分词器的配置示例,我从中摘录一段配置示例,比如:
<fieldType name="text_general" class="solr.TextField" positionIncrementGap="100"> <analyzer type="index"> <tokenizer class="solr.StandardTokenizerFactory"/> <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" /> <!-- in this example, we will only use synonyms at query time <filter class="solr.SynonymFilterFactory" synonyms="index_synonyms.txt" ignoreCase="true" expand="false"/> --> <filter class="solr.LowerCaseFilterFactory"/> </analyzer> <analyzer type="query"> <tokenizer class="solr.StandardTokenizerFactory"/> <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" /> <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/> <filter class="solr.LowerCaseFilterFactory"/> </analyzer> </fieldType>
fileType是用来定义域类型的,name即表示域名称,class即表示域类型对应的class类,如果是solr内置的域类型则可以直接使用solr.前缀+域类型的类名即可,如果是你自定义的域类型,则class表示自定义域类型的完整类名(包含完整的包路径),在fileType元素下有analyzer元素,用来配置当前域类型使用什么分词器,你肯定很奇怪,为什么要配置两个analyzer,其实主要是为了区分两个阶段:索引建立阶段和Query查询阶段,索引建立阶段需要分词毋庸置疑,查询阶段是否需要分词,则取决于你的业务需求,用过Google的知道,用户在查询输入框里输入查询关键字,这时候我们需要对用户输入的查询关键字进行分词器,这时候我们就需要配置查询阶段使用什么分词器,为什么把分开配置?两者可以使用统一配置不行吗,配置两遍不是显得很冗余且繁琐吗?analyzer的type元素就表示这两个阶段,之所以要分阶段配置分词器,是为了满足用户潜在的需求,因为查询阶段的分词需求和索引阶段的分词需求不一定是相同的。我们都知道分词器Analyzer是由一个Tokenizer + N个tokenFilter组成,这就是为什么analyzer元素下会有tokenizer元素和filter元素,但tokenizer元素只允许有一个,filter元素可以有N个。之所以这样设计是为了为用户提供更细粒度的方式来配置分词器的行为,即你可以任意组合tokenizer和filter来实现你的特定需求,当然你也可以把这种组合写在Analyzer类里,然后直接在analyzer元素的class属性里配置自定义分词器的完整类名,这样就不需要这么繁琐的配置tokenizer和filter,即把实现细节屏蔽在analyzer类内部,但这样做的话,如果你需要更改实现细节,则需要修改Analyzer源码,然后重新打包成jar,相对来说,比较麻烦点,而使用analyzer,tokenizer,filter这样来配置,虽然繁琐点,但更灵活。而且采用<analyzer class="xxxxxxxx.IKAnalyzer"这样配置方式,看起来是比较简洁,我想你可能会比较喜欢这种方式,遗憾的是,solr在实现这种方式的时候,考虑不够周全,比如IKAnalyzer分词器,我们都知道IK分词器的构造器还有个useSmart参数,表示是否开启智能分词,而<analyzer class="xxxxxxxx.IKAnalyzer"这种方式,本质还是通过SAX方式解析XML,然后得到class类型字符串,然后通过反射去创建Analyzer实例对象,你可能会问我,我为什么知道是这样实现的?我看了Solr的源码所以我知道,无码无真相,来看截图:(在FieldTypePluginLoader类中)
关键点部分我已经使用红色方框标注出来了,class.newInstance()本质就是通过反射的方式去调用类的无参构造函数,这个大家都知道吧,而IKAnalyzer分词器的构造函数代码如图:
这意味着useSmart参数永远得不到设置,它永远为false,这就是采用<analyzer class="xxxxxxxx.IKAnalyzer"这种方式进行配置的弊端。它看似非常简洁,但暗藏陷阱,坑爹的Solr。那有没办法解决呢?我能想到的办法就是修改源码重新打包,你可能会问怎么修改?听我慢慢说,不要急。
在FieldTypePluginLoader类中有个readAnalyzer(Node node)方法,其中有一句代码非常关键:
NamedNodeMap attrs = node.getAttributes(); String analyzerName = DOMUtil.getAttr(attrs,"class");
其中node对象即表示当前<analyzer元素节点,而DOMUtil.getAttr(attrs,"class");表示通过DOMUtil工具类来获取<analyzer元素的class属性,这个好理解吧,我们在schema.xml中可能是这样配置的
<analyzer class="xxxxx.IKAnalyzer",那一句目的就是获取分词器的class类名,知道类名了就可以反射去创建分词器实例对象啊,就这么简单,所以我们可以自己在<analyzer元素中加一个参数,比如这样:
<analyzer class="org.wltea.analyzer.lucene.IKAnalyzer" useSmart="true"/>
然后我们在代码里String useSmart = DOMUtil.getAttr(attrs,"useSmart");就可以获取到属性值了,然后就是通过反射把属性值设置到IKAnalyzer类的useSmart属性中了,这是基本的Java反射操作,下面我提供几个反射工具方法:
/** * 循环向上转型, 获取对象的DeclaredField. 若向上转型到Object仍无法找到, 返回null. */ protected static Field getDeclaredField(final Object object, final String fieldName) { if (null == object || null == fieldName || fieldName.equals("")) { return null; } for (Class<?> superClass = object.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) { try { return superClass.getDeclaredField(fieldName); } catch (NoSuchFieldException e) { // Field不在当前类定义,继续向上转型 continue; } } return null; } /** * 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数. */ public static void setFieldValue(final Object object, final String fieldName, final Object value) { Field field = getDeclaredField(object, fieldName); if (field == null) { throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + object + "]"); } makeAccessible(field); try { field.set(object, value); } catch (IllegalAccessException e) { throw new RuntimeException("直接设置对象属性值出现异常", e); } } /** * 强行设置Field可访问 */ protected static void makeAccessible(final Field field) { if (!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers())) { field.setAccessible(true); } }
直接调用setFieldValue方法即可,比如在Analyzer analyzer = clazz.newInstance();这句下面添加一句
setFieldValue(analyzer,"useSmart",Boolean.valueOf(useSmart ));
这样我们在xml中配置的useSmart参数就设置到Analyzer类中了,这样才能起作用。solr源码如何导入Eclipse上篇博客里我已经介绍过了,至于如果把修改过后的代码打包成jar,直接使用eclipse自带的export功能即可,如图:
然后一路Next即可。我只是说说思路,剩下留给你们自己去实践。
但是采用改源码方式不是很优雅,因为你本地虽然是修改好了,哪天你由Solr5.1.0升级到5.2.0,还要再改一遍,没升级一次就要改一次,你的代码copy给别人用,别人运行代码后看不到效果,增加沟通成本,你还得把你改过源码的jar包共享给别人,这也就是为什么有那么多人找我要什么IK jar包。
在Solr中可以使用TokenizerFactory方式来解决我刚才提出的问题:IKAnalyzer分词器的useSmart参数无法通过schema.xml配置文件进行设置。我花了点时间扩展了IKTokenizerFactory类,代码如下:
package org.apache.lucene.analysis.ik; import java.util.Map; import org.apache.lucene.analysis.Tokenizer; import org.apache.lucene.analysis.util.TokenizerFactory; import org.apache.lucene.util.AttributeFactory; import org.wltea.analyzer.lucene.IKTokenizer; public class IKTokenizerFactory extends TokenizerFactory { public IKTokenizerFactory(Map<String, String> args) { super(args); useSmart = getBoolean(args, "useSmart", false); } private boolean useSmart; @Override public Tokenizer create(AttributeFactory attributeFactory) { Tokenizer tokenizer = new IKTokenizer(attributeFactory,useSmart); return tokenizer; } }
同时我对IKTokenizer类也稍作了修改,修改后源码如下:
/** * IK分词器 Lucene Tokenizer适配器类 * 兼容Lucene 4.0版本 */ public final class IKTokenizer extends Tokenizer { //IK分词器实现 private IKSegmenter _IKImplement; //词元文本属性 private final CharTermAttribute termAtt; //词元位移属性 private final OffsetAttribute offsetAtt; //词元分类属性(该属性分类参考org.wltea.analyzer.core.Lexeme中的分类常量) private final TypeAttribute typeAtt; //记录最后一个词元的结束位置 private int endPosition; private Version version = Version.LATEST; /** * Lucene 4.0 Tokenizer适配器类构造函数 * @param in * @param useSmart */ public IKTokenizer(Reader in , boolean useSmart){ //super(in); offsetAtt = addAttribute(OffsetAttribute.class); termAtt = addAttribute(CharTermAttribute.class); typeAtt = addAttribute(TypeAttribute.class); _IKImplement = new IKSegmenter(input , useSmart); } public IKTokenizer(AttributeFactory factory, boolean useSmart) { super(factory); offsetAtt = addAttribute(OffsetAttribute.class); termAtt = addAttribute(CharTermAttribute.class); typeAtt = addAttribute(TypeAttribute.class); _IKImplement = new IKSegmenter(input , useSmart); } /* (non-Javadoc) * @see org.apache.lucene.analysis.TokenStream#incrementToken() */ @Override public boolean incrementToken() throws IOException { //清除所有的词元属性 clearAttributes(); Lexeme nextLexeme = _IKImplement.next(); if(nextLexeme != null){ //将Lexeme转成Attributes //设置词元文本 termAtt.append(nextLexeme.getLexemeText()); //设置词元长度 termAtt.setLength(nextLexeme.getLength()); //设置词元位移 offsetAtt.setOffset(nextLexeme.getBeginPosition(), nextLexeme.getEndPosition()); //记录分词的最后位置 endPosition = nextLexeme.getEndPosition(); //记录词元分类 typeAtt.setType(nextLexeme.getLexemeTypeString()); //返会true告知还有下个词元 return true; } //返会false告知词元输出完毕 return false; } /* * (non-Javadoc) * @see org.apache.lucene.analysis.Tokenizer#reset(java.io.Reader) */ @Override public void reset() throws IOException { super.reset(); _IKImplement.reset(input); } @Override public final void end() { // set final offset int finalOffset = correctOffset(this.endPosition); offsetAtt.setOffset(finalOffset, finalOffset); }
修改后重新打包的IKAnalyzer jar请见底下的附件。
然后我把它打包成了solr-analyzer-ik-5.1.0.jar,只需要把这个jar包复制到你的core\lib目录下即可,然后你就可以像配置StandardTokenizerFactory一样的使用我们自定义的IKTokenizerFactory类了,并且能配置useSmart参数,这正是我想要的,能灵活的控制分词器参数,so cool。配置示例如下:
然后field域里应用我们配置的这个text_ik域类型,如图:
然后你还需要把IKAnalyzer jar包以及我们自定义的IKTokenizerFactory的jar包copy到你当前core\lib目录下,如图:
IKAnalyzer jar建议使用底下附件里我新上传的,因为源码我稍作了修改,上面已经提到过了。然后你需要把IKAnalyzer.cfg.xml配置文件copy到E:\apache-tomcat-7.0.55\webapps\solr\WEB-INF\classes目录下,其中E:\apache-tomcat-7.0.55为我的Tomcat安装根目录,请类比成你自己的tomcat安装根目录,你懂的。如图:
IKAnalyzer.cfg.xml配置如图:
ext.dic为IK分词器的自定义扩展词典,内容如图:
我就在里面加了两个自定义词语。
然后你就可以启动你的tomcat,然后如图进行分词测试了,
上图是用来测试useSmart参数设置是否有生效,如果你看到如图的效果,说明配置成功了。
上图是用来测试自定义词典是否有生效,因为我在ext.dic自定义词典里添加了 劲爆 和 屌丝 这两个词,所以IK能分出来,逆袭和白富美没有在自定义扩展词典里添加,所以IK分不出来。如果你能看到如图效果,说明IK的自定义扩展词典也配置成功了。到此,关于在Solr中使用IK分词器就介绍到这儿了,如果你还有任何疑问,请通过以下方式联系到我,谢谢!!!博客里提到的相关jar包配置文件等等资源文件,我待会儿都会上传到底下的附件里,特此提醒!!!!!
益达Q-Q: 7-3-6-0-3-1-3-0-5
益达的Q-Q群: 1-0-5-0-9-8-8-0-6
相关推荐
《跟益达学Solr5之使用Ansj分词器》 在中文信息检索和文本分析领域,分词是至关重要的第一步。Solr,作为一款强大的开源搜索平台,提供了多种分词器供用户选择,其中之一就是Ansj分词器。这篇文章将深入探讨如何在...
基于 OpenCV 的魔兽世界钓鱼机器人
供应链管理中信息共享问题的研究
青春文学中的爱情观呈现
1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
XLSReadWriteII6.02.01.7z
图解系统-小林coding-v1.0
1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
漫画作品与乌托邦理想追求
江苏建筑消防设施维护保养规程.rar
内容概要:论文介绍了一款名为DODRIO的交互式可视化工具,帮助自然语言处理(NLP)研究人员和从业者解析基于转换器架构的语言模型内部工作机理。DODRIO整合了概述图与详尽视图,支持用户比较注意力权重与其输入文本的句法结构和语义特征。具体而言,它包含了依赖关系视图(Dependency View)、语义关注图(Semantic Attention Graph)以及注意力头概览(Attention Head Overview),并利用不同的图形展示方法使复杂的多层多头转换器模型中的注意力模式更容易理解和研究。 适用人群:适用于从事深度学习、自然语言处理的研究人员和技术从业者;尤其适合对基于变换器架构的大规模预训练语言模型感兴趣的开发者们。 使用场景及目标:DODRIO用于探索转换器模型各层级之间的联系、验证已有研究成果,同时激发新假设形成。具体使用时可以选择特定数据集中的句子作为样本输入,观察不同注意力机制如何响应文本内容的变化。此外,还可以用来对比精简版本DistilBERT的表现,评估其相对全量模型BERT的优势与不足。 其他说明:DODRIO为开源项目,提供web端实施方式,使得
该代码使用scikit-learn的乳腺癌数据集,完成分类模型训练与评估全流程。主要功能包括:数据标准化、三类模型(逻辑回归、随机森林、SVM)的训练、模型性能评估(分类报告、混淆矩阵、ROC曲线)、随机森林特征重要性分析及学习曲线可视化。通过`train_test_split`划分数据集,`StandardScaler`标准化特征,循环遍历模型进行统一训练和评估。关键实现细节包含:利用`classification_report`输出精确度/召回率等指标,绘制混淆矩阵和ROC曲线量化模型效果,随机森林的特征重要性通过柱状图展示,学习曲线分析模型随训练样本变化的拟合趋势。最终将原始数据和预测结果保存为CSV文件,便于后续分析,并通过matplotlib进行多维度可视化比较。代码结构清晰,实现了数据处理、模型训练、评估与可视化的整合,适用于乳腺癌分类任务的多模型对比分析。
在智慧城市建设的大潮中,智慧园区作为其中的璀璨明珠,正以其独特的魅力引领着产业园区的新一轮变革。想象一下,一个集绿色、高端、智能、创新于一体的未来园区,它不仅融合了科技研发、商业居住、办公文创等多种功能,更通过深度应用信息技术,实现了从传统到智慧的华丽转身。 智慧园区通过“四化”建设——即园区运营精细化、园区体验智能化、园区服务专业化和园区设施信息化,彻底颠覆了传统园区的管理模式。在这里,基础设施的数据收集与分析让管理变得更加主动和高效,从温湿度监控到烟雾报警,从消防水箱液位监测到消防栓防盗水装置,每一处细节都彰显着智能的力量。而远程抄表、空调和变配电的智能化管控,更是在节能降耗的同时,极大地提升了园区的运维效率。更令人兴奋的是,通过智慧监控、人流统计和自动访客系统等高科技手段,园区的安全防范能力得到了质的飞跃,让每一位入驻企业和个人都能享受到“拎包入住”般的便捷与安心。 更令人瞩目的是,智慧园区还构建了集信息服务、企业服务、物业服务于一体的综合服务体系。无论是通过园区门户进行信息查询、投诉反馈,还是享受便捷的电商服务、法律咨询和融资支持,亦或是利用云ERP和云OA系统提升企业的管理水平和运营效率,智慧园区都以其全面、专业、高效的服务,为企业的发展插上了腾飞的翅膀。而这一切的背后,是大数据、云计算、人工智能等前沿技术的深度融合与应用,它们如同智慧的大脑,让园区的管理和服务变得更加聪明、更加贴心。走进智慧园区,就像踏入了一个充满无限可能的未来世界,这里不仅有科技的魅力,更有生活的温度,让人不禁对未来充满了无限的憧憬与期待。
内容概要:本文档介绍了基于MATLAB实现的贝叶斯优化(BO)、Transformer和GRU相结合的多特征分类预测项目实例,涵盖了详细的程序设计思路和具体代码实现。项目旨在应对数据的多样性与复杂性,提供一种更高效的多特征数据分类解决方案。文档主要内容包括:项目背景与意义,技术难点与解决方案,具体的实施流程如数据处理、模型构建与优化、超参数调优、性能评估以及精美的GUI设计;详细说明了Transformer和GRU在多特征数据分类中的应用及其与贝叶斯优化的有效结合,强调了其理论与实际应用中的价值。 适合人群:具备一定机器学习和MATLAB编程基础的研发人员,特别是从事多维数据处理与预测工作的专业人士和技术爱好者。 使用场景及目标:① 适用于金融、医疗、交通等行业,进行复杂的多维数据处理和预测任务;② 提升现有分类任务中复杂数据处理的准确度和效率,为各行业提供智能预测工具,如金融市场预测、患者病情发展跟踪、交通流量管理等。 其他说明:本文档包含了丰富的实战案例和技术细节,不仅限于模型设计本身,还涉及到数据清洗、模型优化等方面的知识,帮助使用者深入理解每一步骤背后的原理与实现方法。通过完整的代码样例和GUI界面设计指导,读者可以从头到尾跟随文档搭建起一套成熟的分类预测系统。
大数据的sql练习题,初级中级高级
内容概要:论文介绍了名为Transformer的新网络架构,它完全基于自注意力机制,在不使用递归或卷积神经网络的情况下建模输入与输出之间的全局依赖关系,尤其适用于长文本处理。通过多头自注意力层和平行化的全连接前馈网络,使得在机器翻译任务上的表现优于当时最佳模型。具体地,作者用此方法实现了对英语-德语和英语-法语翻译、句法解析等任务的高度并行化计算,并取得显著效果。在实验方面,Transformer在较短训练时间内获得了高质量的翻译结果以及新的单一模型基准。除此之外,研究人员还探索了模型变体的效果及其对于不同参数变化时性能的变化。 适用人群:从事自然语言处理领域的研究者、工程师、学生,熟悉深度学习概念尤其是编码器-解码器模型以及关注模型创新的人士。 使用场景及目标:主要适用于序列到序列(seq2seq)转换任务如机器翻译、语法分析、阅读理解和总结等任务的研究和技术开发;目标在于提高计算效率、缩短训练时间的同时确保模型性能达到或超过现有技术。 其他说明:本文不仅提出了一个新的模型思路,更重要的是展示了自注意力机制相较于传统LSTM或其他方式所拥有的优势,例如更好地捕捉远距离上下文关系的能力
1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。