`

hanlp分词工具应用案例:商品图自动推荐功能的应用

 
阅读更多

 

 

本篇分享一个hanlp分词工具应用的案例,简单来说就是做一图库,让商家轻松方便的配置商品的图片,最好是可以一键完成配置的。

先看一下效果图吧:

 

 

 

商品单个推荐效果:匹配度高的放在最前面

 



 

 

这个想法很好,那怎么实现了。分析了一下解决方案步骤:

 

1、图库建设:至少要有图片吧,图片肯定要有关联的商品名称、商品类别、商品规格、关键字等信息。

 

2、商品分词算法:由于商品名称是商家自己设置的,不是规范的,所以不可能完全匹配,要有好的分词库来找出关键字。还有一点,分词库要能够自定义词库,最好能动态添加。如果读者不知道什么是分词,请自行百度,本文不普及这个。

 

3、推荐匹配度算法:肯定要最匹配的放在前面,而且要有匹配度分数。商家肯定有图库没有的商品,自动匹配的时候,不能随便配置不相关的图片。

 

 先说明一下,本文企业没有搜索引擎之类的工具,所以本质就靠的是数据库检索。

首页让我们先分析一下图库,下面是图库的设置界面。

 



 

 

让我们先贴一下图库的表结构

 

CREATE TABLE `wj_tbl_gallery` (

  `gallery_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',

  `fileid` int(11) NOT NULL COMMENT '文件服务器上的文件ID',

  `ptype` tinyint(4) NOT NULL DEFAULT '0' COMMENT '图片类型,0 点歌屏点餐图片',

  `materialsort` varchar(50) DEFAULT NULL COMMENT '商品分类',

  `materialbrand` varchar(50) DEFAULT NULL COMMENT '商品品牌',

  `materialname` varchar(100) NOT NULL COMMENT '商品名称',

  `material_spec` varchar(50) DEFAULT NULL COMMENT '商品规格',

  `material_allname` varchar(200) DEFAULT NULL COMMENT '商品完整名称',

  `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '状态,0正常,1停用,2删除',

  `updatedatetime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',

  `keyword` varchar(200) DEFAULT NULL COMMENT '商品关键字,用逗号隔开',

  `bstorage` tinyint(4) NOT NULL DEFAULT '0' COMMENT '关键字是否入库 0没有,1',

  PRIMARY KEY (`gallery_id`),

  KEY `idx_fileid` (`fileid`)

) ENGINE=InnoDB AUTO_INCREMENT=435 DEFAULT CHARSET=utf8 COMMENT='图库信息表';

 

 

数据示例:

 

 

 

 

简单说一下material_allname是干什么用的呢,主要就是拼接商品名称、规则 、关键字字段。用来写sql的时候比较方便。关键字字段是干什么用的呢,作用有两个。1是商品可能有多个名字,补充名称的。二是给分词库动态添加词库。图库简单说到这。

 

再说一下分词库,笔者选择的是开源的汉语言分词库-hanlp分词工具

优点是词库大,有词性分析,可以自定义词库。缺点当然也有,就是不支持数据库方法动态读取词库。后面说一下我自己的解决办法。

 

上代码:

分词代码,这时差会去掉一些没用字符。



 

5

 

我们分词,就是调用SegmentUtils.segmentTerm(materialname);

 

动态添加词库方法:

 

   private void addCustomerDictory(){

        Integer max = galleryRepository.getMaxGallery();

        if(CommonUtils.isNotEmpty(max) && max > 0 && max > SegmentUtils.CACHE_GALLERY_ID){

            int oldid = SegmentUtils.CACHE_GALLERY_ID;

            SegmentUtils.CACHE_GALLERY_ID = max;

            List<String> gallery = galleryRepository.getGallery(oldid,max);

            if(CommonUtils.isNotEmpty(gallery)){

                Map<String,Boolean> dicMap = new HashMap<>();

                for(String w : gallery){

                    if(CommonUtils.isNotEmpty(w)){

                        String[] array = w.split(",");

                        if(CommonUtils.isNotEmpty(array)){

                            for(String item : array){

                                String value = item.trim();

                                if(CommonUtils.isNotEmpty(value)){

                                    dicMap.put(value, true);

                                }

                            }

                        }

                    }

                }

                Set<String> keys = dicMap.keySet();

                if(CommonUtils.isNotEmpty(keys)){

                    SegmentUtils.insertCustomDictory(keys);

                }

            }

        }

    }

 

    /**

     * 获取关键字

     *

     * @author deng

     * @date 2019313

     * @param galleryId

     * @return

     */

    @Query("select keyword from Gallery a where galleryId > ?1 and galleryId<=?2  and a.keyword !=''  and bstorage=0")

    public List<String> getGallery(int bgalleryId, int egalleryId);

 

    @Cacheable(value = CacheConstants.CACHE_GALLERY, keyGenerator = CacheConstants.KEY_GENERATOR_METHOD)

    @Query(value = "select gallery_id from wj_tbl_gallery a where  a.keyword !=''  and  bstorage=0 order by gallery_id desc limit 1", nativeQuery = true)

    public Integer getMaxGallery();

 

 说一下解决思路,由于hanlp文档上没有看到从mysql上动态添加词库方法,只有CustomDictionary.insert能动态添加单个实例词库,系统如果重启,就要重新添加。我就想出一个办法,就是分词的时候,查一下类的保存的最大图库表的主键是什么,如果跟数据库一样,就不动态添加。如果小于图库的主键,就把没有的那一段用CustomDictionary.insert添加进去。系统一般不重启,如果重启就在分词的时候重新添加一下。查询数据库当然都有缓存,编辑图库的时候,把对应缓存清除一下。这种方式也能支持分布式环境,多个实例都是一样处理的。每过一段时间,就把图库表的关键字词库搞成文件的词库,避免动态添加太多,占用太多内存。自定义词库其实是很重要的,任何分词库都不可能包含所有的词库,而分词算法是根据词库来展开的,可以说词库决定了分词结果的准确性。

 

让我们看一下分词的效果

 

商品名称为”雪碧(大)“的分词结果 雪碧/nz, /a ,其中nz表示专有词汇,a表示形容词。

 

再看一下不理想的分词结果:

 

商品品名称:”蕾芙曼金棕色啤酒“,类别名称:啤酒,

 

分词结果:/ng,/n,/ag,/ng,棕色/n,啤酒/nz

 

很明显,分词结果不理想,蕾芙曼金棕色其实是一个商品名,不能分开。怎么办呢,这时候动态添加词汇功能就派上用场了。

 

再图库关键字时差添加蕾芙曼金棕色啤酒,保存一下,再看一下分词效果:



 

 

 

物品名称:蕾芙曼金棕色啤酒,类别名称:啤酒,分词结果:蕾芙曼金棕色/nz,啤酒/nz

 

蕾芙曼金棕色被分到了一起,达到预期效果,这其实就是 CustomDictionary.insert(data, "nz 1024");再起作用。hanlp具体API功能,请参考官方文档,本文就不介绍了。

 

最后重头戏来了,商品图片匹配度分析。作者就是采用了mysqlsql词句的方法搞定了,其实就用到了LOCATE函数,很简单。SQL示例如下

 

SELECT gallery_id, fileid, materialname, material_allname, score

, ROUND(score / 4 * 100, 0) AS rate

FROM (

SELECT a.gallery_id, a.fileid, materialname, material_allname

, IF(LOCATE('雪碧', a.material_allname), 2, 0) + IF(LOCATE('', a.material_allname), 1, 0) + IF(LOCATE('饮料', a.material_allname), 1, 0) AS score

FROM wj_tbl_gallery a

WHERE a.STATUS = 0

AND (a.material_allname LIKE '%雪碧%'

OR a.material_allname LIKE '%%'

OR a.material_allname LIKE '%饮料%')

) b

ORDER BY score DESC, materialname

LIMIT 0, 8

 

执行结果:



 

 

 

可以看出gallery_id是第一条,它的rate的是75,满分是100,匹配度蛮高的。

 

说一下匹配度算法原则,如果完全匹配就是1百分,肯定就上了。然后去除某些关键字后,也匹配上了就是90分。最后采用分词算法,按照1百分打分,其中如果高于50分,可以算基本匹配,自动配置图片的时候,就可以当成匹配成功。总体原则就是匹配词汇越多,分数越多。但是两个字的词汇,和5个字的词汇,分数是不一样的。还有词性,专属词汇理论上应该比形容词分数高。详见下面的calculateWeight代码,自己体会了。

 

 public List<Map<String, Object>> queryList(String searchstr, int pagenumber, int pagesize, String materialsortname,

            List<Term> segmentList) {

        String name = "%" + searchstr + "%";

        // 先简单搜索 ,完全匹配100

        List<Map<String, Object>> list = queryList(name, pagenumber, pagesize, 100);

        if (CommonUtils.isEmpty(list)) {

            searchstr = searchstr.replaceAll("\\s", "");

            String regEx = "(特价)|(/)|(\\()|(\\))|()|()|(\\d+ml)|(..)|(/)|(\\*)";

            searchstr = searchstr.replaceAll(regEx, "");

            if (CommonUtils.isNotEmpty(searchstr)) {

                name = "%" + searchstr + "%";

                // 简单过滤 90

                list = queryList(name, pagenumber, pagesize, 90);

            }

            // 剩下分词 靠计算

            if (CommonUtils.isEmpty(list)) {

                if (CommonUtils.isNotEmpty(segmentList)) {

                    list = queryListTerm(pagenumber, pagesize, segmentList, materialsortname);

                }

                // 如果只有分类,先定10

                else if (CommonUtils.isNotEmpty(materialsortname))

                    list = queryList(materialsortname, pagenumber, pagesize, 10);

            }

        }

        return list;

    }

 

    private List<Map<String, Object>> queryList(String name, int pagenumber, int pagesize, int rate) {

        String sql = "SELECT\n" + "   a.gallery_id,\n" + "   a.fileid,a.material_allname,a.materialname \n, " + rate

                + " rate FROM\n" + "   wj_tbl_gallery a\n" + "WHERE\n"

                + "   a.material_allname LIKE :searchstr and a.status = 0  order by length(materialname)  LIMIT :pagenumber,:pagesize  ";

        Dto param = new BaseDto();

        param.put("searchstr", name).put("pagenumber", pagenumber * pagesize).put("pagesize", pagesize);

        return namedParameterJdbcTemplate.queryForList(sql, param);

 

 

 

    private List<Map<String, Object>> queryListTerm(int pagenumber, int pagesize, List<Term> segmentList,

            String materialsortname) {

 

        Dto param = new BaseDto();

        StringBuffer sb = new StringBuffer();

        StringBuffer wsb = new StringBuffer(" (");

        // 总权重

        int tw = 0;

        if (CommonUtils.isNotEmpty(segmentList)) {

            for (int i = 0; i < segmentList.size(); i++) {

                String str = segmentList.get(i).word;

                int w = SegmentUtils.calculateWeight(segmentList.get(i));

                str = StringUtils.escapeMysqlSpecialChar(str);

                tw += w;

                sb.append("if(LOCATE('").append(str).append("', a.material_allname),").append(w).append(",0) ");

                wsb.append(" a.material_allname like '%").append(str).append("%' ");

                if (i < segmentList.size() - 1) {

                    sb.append(" + ");

                    wsb.append(" or ");

                }

            }

            // 类别单独处理,目前权重较低

            // 表示字符串是否为空

            int emptylen = 3;

            if (CommonUtils.isNotEmpty(materialsortname)) {

                if (sb.length() > emptylen) {

                    sb.append(" + ");

                    wsb.append(" or ");

                }

                tw += SegmentUtils.DWEIGHT;

                materialsortname = StringUtils.escapeMysqlSpecialChar(materialsortname);

                sb.append(" if(LOCATE('").append(materialsortname).append("', a.material_allname),")

                        .append(SegmentUtils.DWEIGHT).append(",0) ");

                wsb.append(" a.material_allname like '%").append(materialsortname)

                        .append("%' ");

 

            }

            if (sb.length() > emptylen) {

                sb.append(" as score ");

                wsb.append(") ");

                String scoreSelect = sb.toString();

                String scorewhere = wsb.toString();

                String sql = "select gallery_id,fileid,materialname,material_allname,score,ROUND(score/" + tw

                        + "*100, 0) rate   from   (SELECT " + "   a.gallery_id, "

                        + "   a.fileid,materialname,material_allname, " + scoreSelect + " FROM "

                        + "   wj_tbl_gallery a " + "WHERE " + "  a.status = 0  and " + scorewhere

                        + " ) b order by  score desc ,materialname LIMIT " + pagenumber * pagesize + "," + pagesize;

                param.put("pagenumber", pagenumber * pagesize).put("pagesize", pagesize);

                logger.debug("商家搜索图库的SQL语句是{}", sql);

                List<Map<String, Object>> list = namedParameterJdbcTemplate.queryForList(sql, param);

                if (CommonUtils.isNotEmpty(list)) {

                    return list;

                }

            }

 

        }

 

 

    /**

     *  计算分词权重

     *  @author deng

     *  @date  2019621

     *  @param term

     *  @return

     */

    public static int calculateWeight(Term term) {

 

        // 汉字数

        int num = countChinese(term.word);

        // 大于3个汉字,权重增加

        int value = num >= 3 ? 2 + (num - 3) / 2 : DWEIGHT;

        // 专属词,如果有两个字至少要最小分是2

        if (term.nature == Nature.nz && value <= DWEIGHT) {

            value = DWEIGHT + 1;

        }

        return value;

 

    }

总结一下,本文介绍的商品图片推荐和自动匹配方法,可以看出来是相当简单的,本质就是mysqllike%% 优化来的,依赖sql语句和hanlp分词库,做法简单,但是能满足专门商品的匹配,适合小图库。自然比不上大公司搞的搜索引擎来的效率高,仅供参考。

  • 大小: 353.3 KB
  • 大小: 193.9 KB
  • 大小: 224.9 KB
  • 大小: 5.4 KB
  • 大小: 673.3 KB
  • 大小: 38.1 KB
  • 大小: 6.5 KB
分享到:
评论

相关推荐

    hanlp分词各类词性状态表,汉普分词规格表,hanlp各类词性表,hanlp分词词性表,nlp领域

    hanlp分词各类词性状态表: 比如: a 形容词 ad 副形词 b 区别词 n 名词 h 前缀 i 成语 j 简称略语 k 后缀 l 习用语 m 数词 mg 数语素 Mg 甲乙丙丁之类的数词 mq 数量词

    Elasticsearch hanlp 分词插件

    elasticsearch-6.4.2 hanlp分词插件 windows下安装命令 首先进入es bin目录 elasticsearch-6.4.2\bin&gt; 然后执行 elasticsearch-plugin.bat install file:///E:/elasticsearch-analysis-ik-6.4.2.zip Linux下安装...

    hanlp分词es插件字典和模型大全

    HanLP-portable-1.7.3:hanlp分词器源码 1. 修改了hanlp-portable-1.7.3源码中线程不安全问题 2. 修改了elasticsearch-analysis-hanlp-7.x.x插件源码对于es7版本适配性问题,更换新的hanlp-portable源码 3. 新增了...

    NLPIR、pyltp、jieba、hanlp、snownlp分词工具安装使用记录

    最近适用了这五款分词工具,光是下载安装就踩了很多坑,特别是pyltp和hanlp,装到我怀疑人生。 以下是整理的安装过程和注意事项。 希望能给大家提供些帮助。 目录一、Nlpir第一步:下载工具。第二步:下载后,解压,...

    hanlp分词解析字符串.zip

    而HanLP作为一款优秀的中文自然语言处理工具,其分词功能得到了广泛的应用和认可。 首先,让我们来了解一下什么是分词。简单来说,分词就是将连续的文本切分成一个个独立的词语,为后续的文本分析提供基础数据。分词...

    基于HanLP对地址字符串分词流程图.eddx

    基于HanLP对地址字符串分词流程图.eddx

    Lucene5+HanLP分词例子

    本文将详细探讨如何结合`Lucene5`与`HanLP`进行高效的中文分词,以实现更精确的全文索引和搜索功能。 首先,`Lucene5`是一个强大的全文搜索引擎库,由Apache软件基金会开发。它提供了核心的搜索功能,如索引、查询...

    Hanlp分词实现从网络片段中提取省份和城市

    HanLP是由科大讯飞开发的一款高性能的自然语言处理工具包,它提供了丰富的中文分词、词性标注、命名实体识别等功能。 首先,我们需要理解如何使用HanLP进行分词。HanLP的核心是基于统计的分词模型,它能够将输入的...

    基于Elasticsearch的HanLP分词插件.zip

    HanLP是一个开源的中文自然语言处理工具包,支持多种分词方式和自然语言处理任务。通过本插件,用户可以在Elasticsearch中使用HanLP的各种分词算法,从而提升中文文本的索引和搜索效果。 项目的主要特性和功能 ...

    ElasticSearch安装包整理,包含Hanlp分词,IK分词,x-pack,,Mysql动态加载停用词、基础词、同义词

    ElasticSearch安装包整理,包含Hanlp分词,IK分词,x-pack,,Mysql动态加载停用词、基础词、同义词,个人整理的ElasticSearch7.9.0安装压缩包,其中的同义词,基础词,停用词mysql动态加载属于个人完善并编译,多年...

    Java中通过HanLP实现文本分词、提取关键词、聚类(工具资源+实例)

    HanLP是由一系列模型与算法组成的工具包,目标是普及自然语言处理在生产环境中的应用。HanLP具备功能完善、性能高效、架构清晰、语料时新、可自定义的特点;提供词法分析(中文分词、词性标注、命名实体识别)、句法...

    HanLP实现文本分词、提取关键词、聚类(工具资源+实例)

    HanLP是由一系列模型与算法组成的工具包,目标是普及自然语言处理在生产环境中的应用。HanLP具备功能完善、性能高效、架构清晰、语料时新、可自定义的特点;提供词法分析(中文分词、词性标注、命名实体识别)、句法...

    HanLP中文分词所需内容_2.zip

    HanLP是由一系列模型与算法组成的Java工具包,目标是普及自然语言处理在生产环境中的应用。HanLP具备功能完善、性能高效、架构清晰、语料时新、可自定义的特点。 在提供丰富功能的同时,HanLP内部模块坚持低耦合、...

    Python自然语言处理NLP算法课程 第10课 文本分类,情感分析。应用案例:互联网自动门户,评论倾向性分析 共54页.pdf

    【大纲】第01课 自然语言处理与文本挖掘概述 共37页第02课 自动机及其应用,文稿自动校正,歧义消除 共41页第03课 语言模型,平滑方法 共38页第04课 概率图模型,生成式模型与判别式模型,贝叶斯网,马尔科夫链,隐...

    一种带词性标注的分词器使用方法–HanLP分词

    HanLP是一系列模型与算法组成的NLP工具包,目标是普及自然语言处理在生产环境中的应用。HanLP具备功能完善、性能高效、架构清晰、语料时新、可自定义的特点。内部算法经过工业界和学术界考验,配套书籍《自然语言...

    基于HanLP分词和Bayes分类器实现的问答机器人.zip

    《基于HanLP分词和Bayes分类器实现的问答机器人》是一个典型的自然语言处理(NLP)项目,主要应用于Python编程环境下。这个课程设计旨在帮助学生掌握如何利用现代的NLP工具和技术构建一个智能问答系统。下面将详细...

    中文分词工具包.zip

    综上所述,“中文分词工具包.zip”可能提供了从传统到现代的各种分词技术,适用于各种NLP应用场景。通过学习和使用这个工具包,开发者可以有效地处理中文文本,提升其在信息处理领域的效率和精度。

    读书笔记2之中文分词流程HanLP

    在选择分词工具时,如HanLP,我们需要考虑其在社区的活跃度、开源性、易用性和广泛应用程度等因素。例如,HanLP因其高效、准确和灵活的特点,在Java中被广泛使用,其源代码可以在GitHub上查看和学习。 总的来说,...

    百度竞价推广关键词自动分词工具 关键词分词工具(宏命令)改进版 过万关键词轻松

    百度竞价推广关键词自动分词工具,wps可用,需启用宏

    hanlp 1.7.7.zip

    1. **基础分词**:HanLP提供精准和快速两种模式的分词,适用于不同场景的需求。 2. **词性标注**:基于大规模语料训练的词性标注模型,可以准确地对分词结果进行词性的标注。 3. **命名实体识别**:识别文本中的专有...

Global site tag (gtag.js) - Google Analytics