java tf-idf提取关键字

来源:互联网 发布:宋代权知开封府事 编辑:程序博客网 时间:2024/06/06 12:47

最近在研究nlp,nlp第一步就是分词,目前开源的工具中,java的有中科院的分词工具nlpir、还有word分词器,ansj_seg等,python的比较火的jieba,ansj_seg5.x版本之后提供了提取关键字的方法,jieba也提供了提取关键字的方法。
提取关键字比较常用的算法有tf-idf、textrank。其中tf-idf是统计词频和逆文档词频,textrank是基于pagerank原理。这两个工具的提取关键字地方法各有利弊。
首先新闻的组成是人物时间地点发生了什么事儿,人名比,专业名词,地名等一般形容词或者动词没有名词重要或者更具有说服力。
而且出现在标题的词语要比出现在正文中的词重要,需要给予其权重。词语的长度也需要一定的权重。
词语的词性也需要赋予一定的权重,基于以上几点实现tfidf

public static String TFIDF (String title,String content, int topK){        FilterRecognition filterRecognition = new FilterRecognition();        filterRecognition.insertStopWords(stopWords);        filterRecognition.insertStopWord("事儿", "有没有", "前有", "后有", "更多");        filterRecognition.insertStopNatures("d", "p", "m", "r", "w", "a", "j", "l","null","num");        List<Term> terms = NlpAnalysis.parse(content).recognition(filterRecognition).getTerms();        //词的总数        int totalWords= terms.size();        Map<String, Integer> wordsCount = new HashMap<String, Integer>();        //根据词的长度加权        int maxWordLen = 0;        for(Term term:terms){            Integer count = wordsCount.get(term.getName());            count = count == null ? 0 : count;            wordsCount.put(term.getName(), count+1);            if(maxWordLen<term.getName().length()){                maxWordLen = term.getName().length();            }        }        //计算tf        Map<String, Double> tf = new HashMap<String, Double>();        for(String word:wordsCount.keySet()){            tf.put(word, (double)wordsCount.get(word)/(totalWords+1));        }        //保留词的长度        Set<Integer> perWordLen = new HashSet<Integer>();        //计算每个词的词长权重        Map<String, Double> lenWeight = new HashMap<String, Double>();        for( String key:tf.keySet()){            lenWeight.put(key, (double)key.length()/maxWordLen);            perWordLen.add(key.length());        }        //标题中出现的关键词        List<Term> titleTerms = NlpAnalysis.parse(title).recognition(filterRecognition).getTerms();        Map<String, String> titleWords = new HashMap<String, String>();        for(Term term:titleTerms){            titleWords.put(term.getName(), term.getNatureStr());        }        //计算idf        Map<Integer, Integer> map = new HashMap<Integer, Integer>();        for(int len:perWordLen){            int sum = 0;            for(String w:wordsCount.keySet()){                if(w.length()==len){                    sum += wordsCount.get(w);                }            }            map.put(len, sum);        }        Map<String, Double> idf = new HashMap<String, Double>();        for(String w:wordsCount.keySet()){            Integer integer = wordsCount.get(w);            int len = w.length();            Integer totalSim = map.get(len);            idf.put(w, Math.log(((double)totalSim/integer)+1));        }        //计算每个词的在文章中的权重        Map<String, Double> wordWeight = new HashMap<String, Double>();        for(Term term:terms){            String word = term.getName();            String nature = term.getNatureStr();            if(word.length()<2){                continue;            }            if(wordWeight.get(word)!=null){                continue;            }            Double aDouble = tf.get(word);            Double aDouble1 = idf.get(word);            double weight = 1.0;            if(titleWords.keySet().contains(word)){                weight += 3.0;            }            weight += (double)word.length()/maxWordLen;            switch (nature){                case "en":                    weight += 3.0;                case "nr":                    weight += 6.0;                case "nrf":                    weight += 6.0;                case "nw" :                    weight += 3.0;                case "nt":                    weight += 6.0;                case "nz":                    weight += 3.0;                case "kw":                    weight += 3.0;                case "ns":                    weight += 3.0;                default:                    weight += 1.0;            }            wordWeight.put(word,aDouble*aDouble1*weight);        }        Map<String, Double> stringDoubleMap = MapUtil.sortByValue(wordWeight);        List<String> topKSet = new ArrayList<String>();        int i = 0;        for(String word:stringDoubleMap.keySet()){            if(i >= topK){                break;            }            topKSet.add(word+" ``+stringDoubleMap.get(word));            i++;        }        return StringUtils.join(topKSet, "\t");    }

int topK = 10;

    String title = "余文乐本来习惯一个人王棠云2个优点抓住男神心";    String content = "余文乐夫妇据台湾媒体报道,36岁香港男星余文乐宣布和王棠云(Sarah)结婚,两人认爱1年感情修成正果,婚纱照曝光让大批粉丝涌入祝福。他在2016年的圣诞节被目击在纽约求婚率最高的法国餐厅用餐,恋情因此曝光,随后由低调逐渐转为高调,年初宣传电影时,曾松口提到女友的2个优点,成为两人愿意相守一生的关键。\n" +            "余文乐发文余文乐2月在香港参加电影活动时,被问到和女友王棠云相处的过程,坦言虽然工作很忙,但是仍会抽时间陪伴女友,“其实我自己也很不习惯,不习惯有另外一半,本来以前一个人习惯了尽快将工作完成,但是现在要分配时间给对方,所以要好好分配时间,但不敢保证可以分配好。”他工作忙碌,女友却没有半句抱怨,提到最欣赏对方什么地方,腼腆称赞“简单、单纯的女孩”,两人相处非常舒服。\n" +            "走红两岸三地的余文乐非常顾家,他高中被挖角出道,一肩扛起家中经济重任,拍戏17年来从没停下拍戏脚步,“家人的开心健康对我来说比什么都重要。”他认爱后更直言婚后不希望另一半工作,“未来我和太太(的生活),也希望是我负责(开销),不希望她来工作,除非她很想工作,我会尊重她。”小两口交往1年后结婚,再度公开示爱:“感恩妳把人生的余下日子交到我手上,我一定会把幸福带给妳,我一定会好好的照顾妳!I love you。";

余文乐 1.8134162231616129 认爱 1.5255924833176655 王棠云 1.1672971055953232 挖角 0.89414671294365 相守 0.5643384991529595 love 0.5615120226399731 求婚率 0.481328374683345

感觉还可以,还没有优化,后面实现textrank和tfidf的另一种实现提取关键字