Jensen-Shannon散度
2015-07-10 16:18
363 查看
文本匹配有很多种算法,常见的如余弦相似度,杰卡德距离,TFIDF等,这些方法网上资料很多,这里就不在讲了,前段时间做工程的时候用到了JS散度,网上一查关于JS散度的资料太少,并且大多都是英文的,且没有找到算法源码,实在没办法,本人就花了一个小时左右写了一个JS散度的JAVA源码供大家参考使用。
先介绍JS散度吧,JS散度是KL散度的一种变种,其基于KL散度,但是区别于KL散度有两点: (1), JS散度的最终值域范围是[0,1],相同为0,相反为1.(例如,str1=毛主席 ,str2=毛主席, 那么因为str1与str2每个字都是相同的,故而JS(str1,str2)=0,而
str3=周总理,那么JS(str1,str3)=1)。(2),对称性(主要区别点),即JS(str1,str2)=JS(str2,str1),(因为JS相对与KL,它引入了一个变量,设为M,则M=1/2(P+Q))。
废话不多说,我们就直接上源码了,有什么问题或者不清楚的话在交流。
//注:本代码在于计算两段英文文章的JS散度,童鞋们可以根据自己的需要进行修改
package VirtualPaper;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
//以字符串中每个汉字所属拼音作为一个整体,js距离是相同的则为0,不相同的则为1
public class JensenShannonUnit {
//得到字符串的概率分布--以一个汉字拼音作为一个整体
public HashMap<String,Double> getDistribution(String str){
HashMap<String,Integer> map = new HashMap<String,Integer>(); //存储子字符串,及其出现的次数
HashMap<String,Double> mappro = new HashMap<String,Double>(); //存储子字符串的概率分布
String[] str11 = str.split(" ");
int total = str11.length; //字符串数组的长度(含有的子字符串的个数)
for(String word : str11){ //将字母存入HashMap中
if(map.containsKey(word)){
int value = map.get(word);
map.put(word, ++value);
}else
map.put(word, 1);
}
Set<String> set = map.keySet();
for(String word:set){
int value = map.get(word);
double probility = value*1.0/total;
mappro.put(word, probility);
}
//System.out.println(mappro);
return mappro;
}
//得到1/2(P+Q)的分布
public Map<String,Double> getM(Map<String,Double> map1,Map<String,Double> map2){
Map<String,Double> map = new HashMap<String,Double>(); //存储1/2(P+Q)的概率分布
Set<String> set1 = map1.keySet();
Set<String> set2 = map2.keySet();
//将所有的字符添加至一个 set里面
Set<String> set = new HashSet<String>();
set.addAll(set1);
set.addAll(set2);
for(String word:set){ //得到value1和value2的平均值
double i = 0; //P的单词概率
double j = 0;
//Q的单词概率
if(map1.containsKey(word))
i = map1.get(word);
if(map2.containsKey(word))
j = map2.get(word);
double min = (i+j)/2;
map.put(word, min);
}
return map;
}
//得到两个HashMap的KL距离 map1为P或Q,map2为M=1/2(P+Q)
public double getKLDistance(Map<String,Double> P,Map<String,Double> M){
double KLD = 0;
Set<String> set1 = P.keySet();
//Set<Character> set2 = map2.keySet();
for(String ch:set1){
double i = P.get(ch);
double j = M.get(ch);
double m = i*(Math.log(i/j)/Math.log(2)); //以底为2的对数函数
KLD+=m;
}
return KLD;
}
public double getJensenDistance(String str1,String str2){
Map<String,Double> P = getDistribution(str1);
Map<String,Double> Q = getDistribution(str2);
Map<String,Double> M = getM(P, Q);
double KLDP = getKLDistance(P, M);
double KLDQ = getKLDistance(Q, M);
double jsd = (KLDP+KLDQ)/2;
return jsd;
}
}
有问题的话 大家多讨论,也顺便贴出较简单的Cos相似度和杰卡德系数
package VirtualPaper;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeMap;
public class JaccardAndCosUnit {
//计算两个字符串的Jaccard系数--一个中文字体的拼音作为一个单元
public double getJaccard(String str1,String str2){
String[] str11=str1.split(" ");
String[] str22=str2.split(" ");
Set<String> intersection=new HashSet<String>();
Set<String> union=new HashSet<String>();
for(int i=0;i<str11.length;i++){
for(int j=0;j<str22.length;j++){
if(str11[i].equals(str22[j]))
intersection.add(str11[i]);
else{
union.add(str11[i]);
union.add(str22[j]);
}
}
}
/* System.out.println("交集大小:"+intersection.size());
System.out.println("并集大小:"+union.size());*/
return (double)intersection.size()/union.size();
}
//计算余弦相似度 向量值表示出现的次数
//得到字符串的向量表示
public List<Integer> getVectors(Set<String> set,String str){
List<Integer> list=new ArrayList<Integer>(); //存储向量值
TreeMap<String,Integer> treemap=new TreeMap<String,Integer>();
for(String word:set){
treemap.put(word, 0);
}
String[] str11 = str.split(" ");
for(int i=0;i<str11.length;i++){
int frequency=treemap.get(str11[i]);
treemap.put(str11[i],frequency+1);
}
Iterator<Integer> iter=treemap.values().iterator();
while(iter.hasNext()){
list.add(iter.next());
}
return list;
}
//计算余弦相似度
public double compcos(List<Integer> list1,List<Integer> list2){
double ab=0; //存储两个向量的积
for(int i=0;i<list1.size();i++){
ab+=list1.get(i)*list2.get(i);
}
double a=0;
for(Integer in:list1){
a+=Math.pow(in, 2);
}
double b=0;
for(Integer in:list2){
b+=Math.pow(in, 2);
}
return ab/Math.sqrt(a*b); //返回余弦相似度
}
//计算两个字符串的余弦相似度
public double getCosstr(String str1,String str2){
Set<String> hashset=new HashSet<String>(); //存储两个字符串的子字符串并集合
String[] str11 = str1.split(" ");
String[] str22 = str2.split(" ");
for(int i=0;i<str11.length;i++){
hashset.add(str11[i]);
}
for(int i=0;i<str22.length;i++){
hashset.add(str22[i]);
}
//将字符串用向量进行表示
List<Integer> list1=getVectors(hashset,str1);
List<Integer> list2=getVectors(hashset,str2);
//返回两个向量的余弦相似度
return compcos(list1,list2);
}
}
欢迎大家支出错误!
先介绍JS散度吧,JS散度是KL散度的一种变种,其基于KL散度,但是区别于KL散度有两点: (1), JS散度的最终值域范围是[0,1],相同为0,相反为1.(例如,str1=毛主席 ,str2=毛主席, 那么因为str1与str2每个字都是相同的,故而JS(str1,str2)=0,而
str3=周总理,那么JS(str1,str3)=1)。(2),对称性(主要区别点),即JS(str1,str2)=JS(str2,str1),(因为JS相对与KL,它引入了一个变量,设为M,则M=1/2(P+Q))。
废话不多说,我们就直接上源码了,有什么问题或者不清楚的话在交流。
//注:本代码在于计算两段英文文章的JS散度,童鞋们可以根据自己的需要进行修改
package VirtualPaper;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
//以字符串中每个汉字所属拼音作为一个整体,js距离是相同的则为0,不相同的则为1
public class JensenShannonUnit {
//得到字符串的概率分布--以一个汉字拼音作为一个整体
public HashMap<String,Double> getDistribution(String str){
HashMap<String,Integer> map = new HashMap<String,Integer>(); //存储子字符串,及其出现的次数
HashMap<String,Double> mappro = new HashMap<String,Double>(); //存储子字符串的概率分布
String[] str11 = str.split(" ");
int total = str11.length; //字符串数组的长度(含有的子字符串的个数)
for(String word : str11){ //将字母存入HashMap中
if(map.containsKey(word)){
int value = map.get(word);
map.put(word, ++value);
}else
map.put(word, 1);
}
Set<String> set = map.keySet();
for(String word:set){
int value = map.get(word);
double probility = value*1.0/total;
mappro.put(word, probility);
}
//System.out.println(mappro);
return mappro;
}
//得到1/2(P+Q)的分布
public Map<String,Double> getM(Map<String,Double> map1,Map<String,Double> map2){
Map<String,Double> map = new HashMap<String,Double>(); //存储1/2(P+Q)的概率分布
Set<String> set1 = map1.keySet();
Set<String> set2 = map2.keySet();
//将所有的字符添加至一个 set里面
Set<String> set = new HashSet<String>();
set.addAll(set1);
set.addAll(set2);
for(String word:set){ //得到value1和value2的平均值
double i = 0; //P的单词概率
double j = 0;
//Q的单词概率
if(map1.containsKey(word))
i = map1.get(word);
if(map2.containsKey(word))
j = map2.get(word);
double min = (i+j)/2;
map.put(word, min);
}
return map;
}
//得到两个HashMap的KL距离 map1为P或Q,map2为M=1/2(P+Q)
public double getKLDistance(Map<String,Double> P,Map<String,Double> M){
double KLD = 0;
Set<String> set1 = P.keySet();
//Set<Character> set2 = map2.keySet();
for(String ch:set1){
double i = P.get(ch);
double j = M.get(ch);
double m = i*(Math.log(i/j)/Math.log(2)); //以底为2的对数函数
KLD+=m;
}
return KLD;
}
public double getJensenDistance(String str1,String str2){
Map<String,Double> P = getDistribution(str1);
Map<String,Double> Q = getDistribution(str2);
Map<String,Double> M = getM(P, Q);
double KLDP = getKLDistance(P, M);
double KLDQ = getKLDistance(Q, M);
double jsd = (KLDP+KLDQ)/2;
return jsd;
}
}
有问题的话 大家多讨论,也顺便贴出较简单的Cos相似度和杰卡德系数
package VirtualPaper;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeMap;
public class JaccardAndCosUnit {
//计算两个字符串的Jaccard系数--一个中文字体的拼音作为一个单元
public double getJaccard(String str1,String str2){
String[] str11=str1.split(" ");
String[] str22=str2.split(" ");
Set<String> intersection=new HashSet<String>();
Set<String> union=new HashSet<String>();
for(int i=0;i<str11.length;i++){
for(int j=0;j<str22.length;j++){
if(str11[i].equals(str22[j]))
intersection.add(str11[i]);
else{
union.add(str11[i]);
union.add(str22[j]);
}
}
}
/* System.out.println("交集大小:"+intersection.size());
System.out.println("并集大小:"+union.size());*/
return (double)intersection.size()/union.size();
}
//计算余弦相似度 向量值表示出现的次数
//得到字符串的向量表示
public List<Integer> getVectors(Set<String> set,String str){
List<Integer> list=new ArrayList<Integer>(); //存储向量值
TreeMap<String,Integer> treemap=new TreeMap<String,Integer>();
for(String word:set){
treemap.put(word, 0);
}
String[] str11 = str.split(" ");
for(int i=0;i<str11.length;i++){
int frequency=treemap.get(str11[i]);
treemap.put(str11[i],frequency+1);
}
Iterator<Integer> iter=treemap.values().iterator();
while(iter.hasNext()){
list.add(iter.next());
}
return list;
}
//计算余弦相似度
public double compcos(List<Integer> list1,List<Integer> list2){
double ab=0; //存储两个向量的积
for(int i=0;i<list1.size();i++){
ab+=list1.get(i)*list2.get(i);
}
double a=0;
for(Integer in:list1){
a+=Math.pow(in, 2);
}
double b=0;
for(Integer in:list2){
b+=Math.pow(in, 2);
}
return ab/Math.sqrt(a*b); //返回余弦相似度
}
//计算两个字符串的余弦相似度
public double getCosstr(String str1,String str2){
Set<String> hashset=new HashSet<String>(); //存储两个字符串的子字符串并集合
String[] str11 = str1.split(" ");
String[] str22 = str2.split(" ");
for(int i=0;i<str11.length;i++){
hashset.add(str11[i]);
}
for(int i=0;i<str22.length;i++){
hashset.add(str22[i]);
}
//将字符串用向量进行表示
List<Integer> list1=getVectors(hashset,str1);
List<Integer> list2=getVectors(hashset,str2);
//返回两个向量的余弦相似度
return compcos(list1,list2);
}
}
欢迎大家支出错误!
相关文章推荐
- 数据挖掘-公司别名聚合(二)
- 数据挖掘笔记-寻找相似文章-Java
- 两篇文章的相似度比较
- Pearson相关系数、余弦相似度、修正余弦相似度
- sklearn计算余弦相似度
- 相似度(距离计算)汇总
- 余弦相似度
- 【通俗解释】余弦相似度
- 推荐算法之余弦相似度
- 【LintCode 简单】445. 余弦相似度
- R中如何利用余弦算法实现文章的自动摘要
- 数据挖掘之曼哈顿距离、欧几里距离、明氏距离、皮尔逊相关系数、余弦相似度Python实现代码
- 数据挖掘之推荐分析--python实现
- 【文本相似度】利用余弦相似性计算句子的相似度
- YDB近似匹配使用入门
- 字符串余弦相似度的java简单实现
- spark mllib 协同过滤算法,基于余弦相似度的用户相似度计算
- Python cookbook(字符串与文本)在字符串的开头或结尾处进行文本匹配操作
- Java实现的计算稀疏矩阵余弦相似度示例
- PHP数据分析引擎计算余弦相似度算法示例