最近在研究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的另一种实现提取关键字