利用Hownet进行语义相似度计算的类

http://blog.sina.com.cn/s/blog_4a8c215101000c1i.html

#if !defined HownetPrimitive_H
#define  HownetPrimitive_H
#include <string.h>
/// <summary>
/// Hownet的义原
/// </summary>
class HownetPrimitive
{
public:
 HownetPrimitive(string strLine):name(""),parent(0),id(0)
 {
  char *token;
  token=strtok((char*)strLine.c_str()," \t\n");
  //while()
  if(token!=NULL)
  {
   id=atoi(token);
   token=strtok(NULL," \t\n");
  }
  if(token!=NULL)
  {
   name = string(token);
   //name=name.substr(0,name.length()-1);
   token=strtok(NULL," \t\n");
  }
  if(token!=NULL)
  {
   parent=atoi(token);
  }
 }
 
 // 义原名称
 string name ;
 
 // 义原的上一级义原的ID
 int parent;
 
 // 义原的ID
 int id ;
};
#endif
 

#if !defined HownetWord_H
#define HownetWord_H
/// <summary>
/// Hownet的词的表示(只考虑四大部分而忽略概念,即具体词),与刘群算法不同,对于一个词的多次出现进行合并,因此所谓第一义原也不止一个
/// </summary>
class HownetWord
{
public:
 HownetWord(string strLine):word("")
 {
  char* token;
  token=strtok((char*)strLine.c_str()," \t");
  if(token!=NULL)
  {
   word=string(token);
   token=strtok(NULL," \t");
  }
  if(token!=NULL)
  {
   token=strtok(NULL," \t");
  }
  if(token!=NULL)
  {
   char *ttok=strtok(token,",");
   while(ttok!=NULL)
   {
    char firstC=ttok[0];
    string strP="";
    string ps(ttok);
    string::size_type pos=ps.find(" ");
    // 关系义原
    if(pos!=string::npos)
    {
     strP = ps.substr(pos+1,ps.length()-pos-1);
    }
    // 基本义原
    else if(firstC>='a'&&firstC<='z'||firstC>='A'&&firstC<='Z')
    {
     strP=ps;
    }
    // 关系符号描述
    else if(firstC != '(')
    {
     strP=ps.substr(1,ps.length()-1);
    }

    if(strP.length()>0&&relationPrimitives.find(strP)==relationPrimitives.end())
    {
     relationPrimitives.insert(map<string,int>::value_type(strP,NULL));
    }
    // 忽略具体词
    ttok=strtok(NULL,",\n");
   }
  }
 
 
 /// <summary>
 /// 关系义原描述
 /// </summary>
 map<string,int> relationPrimitives ;
 /// <summary>
 /// 词
 /// </summary>
 string word ;
};
#endif

 

// WordRelvanceCalculator.cpp: implementation of the CWordRelvanceCalculator class.
//
//

#include "WordRelvanceCalculator.h"

//
// Construction/Destruction
//

CWordRelvanceCalculator::CWordRelvanceCalculator():Alpha(1.60),Gama(0.20)
{
 
 TCHAR szPhraseFileName[200]={'\0'};
 LPTSTR lpPhraseFileName=szPhraseFileName;
 lpPhraseFileName = szPhraseFileName;
 lpPhraseFileName += GetSystemDirectory(szPhraseFileName,200);
 if (*(lpPhraseFileName-1) != _T('\\'))
  *lpPhraseFileName++ = _T('\\'); 
 string strDir(szPhraseFileName);

 Initialize(strDir);
}

CWordRelvanceCalculator::~CWordRelvanceCalculator()
{

}
void CWordRelvanceCalculator::Initialize(string &strDictDir)
{

  LoadPrimitiveDict(strDictDir + "WHOLE.DAT"); 
 
  LoadWordDict(strDictDir + "glossary.dat");
}
//读取义原词典
void CWordRelvanceCalculator::LoadPrimitiveDict(string &strDictPath)
{
  FILE* fhandle=fopen(strDictPath.c_str(),"r");
  if(!fhandle)
  {
  }
  else
  {
   char in_line[1024];
   fgets(in_line,1024,fhandle);
   while(!feof(fhandle))
   {
    string line(in_line);
    HownetPrimitive hp(line);
    // 如果义原在义原词典中重复出现,则只保留最前面一个(暂时先这么做)
    if(m_oNamePrimitiveMap.find(hp.name)==m_oNamePrimitiveMap.end())
    {
     m_oNamePrimitiveMap.insert(map<string,HownetPrimitive>::value_type(hp.name,hp));
    }
    m_oIdParentMap.insert(map<int,int>::value_type(hp.id,hp.parent));
    fgets(in_line,1024,fhandle);
   }
   fclose(fhandle);
  }
}
//读取词汇字典
void CWordRelvanceCalculator::LoadWordDict(string &strDictPath)
{
 FILE* fhandle=fopen(strDictPath.c_str(),"r");
 if(!fhandle)
 {
 }
 else
 {
  char in_line[1024];
  fgets(in_line,1024,fhandle);
  while(!feof(fhandle))
  {
   string line(in_line);
   HownetWord hw(line);
   // 如果该词在前面已经出现过,则将所有这些词的义原进行合并(暂时先这么做)
   map<string,HownetWord>::iterator hwIte;
   if((hwIte=m_oWordMap.find(hw.word))!=m_oWordMap.end())
   {
    HownetWord prevHW=hwIte->second;
    map<string,int>::iterator ite2;
    for(ite2=hw.relationPrimitives.begin();ite2!=hw.relationPrimitives.end();ite2++)
    {
     if(prevHW.relationPrimitives.find(ite2->first)!=prevHW.relationPrimitives.end())
     {
      prevHW.relationPrimitives.insert(map<string,int>::value_type(ite2->first,ite2->second));
     }
    }
   }
   else
   {
    m_oWordMap.insert(map<string,HownetWord>::value_type(hw.word, hw));
   }
   fgets(in_line,1024,fhandle);
  }
  fclose(fhandle);
 }
}
/*
 利用hownet计算两个义原之间的关联度
 */
double CWordRelvanceCalculator::ComputePrimitiveSimilarity(string &strPrimitive1, string &strPrimitive2)
{
 if(strPrimitive1==strPrimitive2)
  return 1.0;
    // 两个义原之间的距离
 int distance = 0;
 
 // 如果两个义原有任何一个不是合法的义原,则返回一个默认值
 map<string,HownetPrimitive>::iterator ite=m_oNamePrimitiveMap.find(strPrimitive1);
 if(ite==m_oNamePrimitiveMap.end()) return Gama;
 HownetPrimitive firstP = ite->second;

 ite=m_oNamePrimitiveMap.find(strPrimitive2);
 if(ite==m_oNamePrimitiveMap.end()) return Gama;
 HownetPrimitive secondP = ite->second;

 // 计算两个义原之间的距离
 int firstID = firstP.id;
 int secondID = secondP.id;
 while(firstID != secondID)
 {
  // 将id较大的义原沿着树上升一级,直至两个id相等
  if(firstID > secondID)
  {
   int tmpID = firstID;
   firstID = secondID;
   secondID = tmpID;
  
  int parentID = m_oIdParentMap[secondID];
  // 如果有某个ID没有父义原,则返回一个较小值
  if(secondID == parentID)
  {
   distance = 15;
   break;
  }
  secondID = parentID;
  distance++;
 }
 
 return Alpha / (distance + Alpha);
}
/*
 计算两个词语的相关度
 */
double CWordRelvanceCalculator::ComputeRelevance(const string &strCnWord1, const string &strCnWord2)
{
  if(strCnWord1==strCnWord2)
  {
   return 1.0;
  }
  
  map<string,HownetWord>::iterator myite= m_oWordMap.find(strCnWord1);
  if(myite==m_oWordMap.end()) return Gama;
  HownetWord &hw1=myite->second;

  myite= m_oWordMap.find(strCnWord2);
  if(myite==m_oWordMap.end()) return Gama;
  HownetWord &hw2 = myite->second;
  
  
  // 相似度
  double fSim = 0;
  
  // 矩阵的长边和短边
  int length = hw1.relationPrimitives.size();
  int width = hw2.relationPrimitives.size();
  if(length < width)
  {
   length = hw2.relationPrimitives.size();
   width = hw1.relationPrimitives.size();
  }
  if(length == 0 || width == 0)
  {
   return Gama;
  }
  
  // 矩阵用以存两两之间的相似度
  vector<vector<double> > simMatrix;
  for(int i=0; i < hw1.relationPrimitives.size()+1; i++)
  {
   vector<double> tv;
   simMatrix.push_back(tv);
   vector<double> & tv2 = simMatrix[i];
   tv2.resize(hw2.relationPrimitives.size()+1);
   //vector<double> & tv3 = simMatrix[i];
   //cout << tv3.size() << "\t";
   //simMatrix[i] = new double[hw2.relationPrimitives.size()];
  }
  
  // 计算两两义原之间的相似度
  map<string,int>::iterator ite=hw1.relationPrimitives.begin();
  int row = 0;
  for(;ite!=hw1.relationPrimitives.end();ite++)
  {
   // 第一个词的第i个义原
   string strP1=ite->first;
   int col=0;
   map<string,int>::iterator ite2=hw1.relationPrimitives.begin();
   for(;ite2!=hw1.relationPrimitives.end();ite2++)
   {
    string strP2=ite2->first;
    simMatrix[row][col] = ComputePrimitiveSimilarity(strP1, strP2);
    col++;
   }
   row++;
  }
 
  // 从矩阵中找出最大值,然后把该值所在行列清零
  double fSimSum = 0;
  int MaxRow = -1;
  int MaxCol = -1;
  double MaxSim = 0;
  for(i=0; i < width; i++)
  {
   // 找出当前对大的值
   for(int m=0; m < hw1.relationPrimitives.size(); m++)
   {
    for(int n=0; n < hw2.relationPrimitives.size(); n++)
    {
     if(simMatrix[m][n] > MaxSim)
     {
      MaxRow = m;
      MaxCol = n;
      MaxSim = simMatrix[m][n];
     }
    }
   }
   fSimSum += MaxSim;
   MaxSim = 0;
   
   // 将最大值所在行列全部清零
   for(int j=0; j < hw2.relationPrimitives.size(); j++)
   {
    simMatrix[MaxRow][j] = 0;
   }
   for(j=0; j < hw1.relationPrimitives.size(); j++)
   {
    simMatrix[j][MaxCol] = 0;
   }
  }

  // 计算最终相似度,公式推导过程:
  // 如果length和width越接近,fSimSum越接近width,则最终的相似度应该越大;
  // 所以我们假设那些没有对齐的部分的默认相似度不应该是固定的,我们用(width / length) * (fSimSum / width) = fSimSum / length表示
  // 所以总共需要补充到fSimSum的数值为: (length - width) * SimSum / length
  // 最后的相似度还要除以 length
  // 最后得到下面的计算公式
  fSim = fSimSum * (2.0 / length - width * 1.0 / (length * length));
  
  return fSim;
}

 

// WordRelvanceCalculator.h: interface for the CWordRelvanceCalculator class.
//
//

#if !defined(AFX_WORDRELVANCECALCULATOR_H__C5D16340_1E8E_450C_A65F_7101DA8D638C__INCLUDED_)
#define AFX_WORDRELVANCECALCULATOR_H__C5D16340_1E8E_450C_A65F_7101DA8D638C__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#pragma warning(disable:4786)

#include "MyIME.h"
#include "HownetPrimitive.h"
#include "HownetWord.h"
/*
 * 利用hownet进行语意相似度计算
 * 包括相关度,相似度两个方面 
 */
class CWordRelvanceCalculator 
{
public:
 CWordRelvanceCalculator();
 virtual ~CWordRelvanceCalculator();
private:
 void Initialize(string &strDir);
 /// <summary>
 /// 载入义原词典
 /// </summary>
 /// <param name="strDictPath"></param>
 void LoadPrimitiveDict(string &strDictPath);
 /// <summary>
 /// 载入词典
 /// </summary>
 /// <param name="strDictPath"></param>
 void LoadWordDict(string &strDictPath);
 /// <summary>
 /// 计算两个义原的相似度
 /// </summary>
 /// <param name="strPrimitive1"></param>
 /// <param name="strPrimitive2"></param>
 /// <returns></returns>
 double ComputePrimitiveSimilarity(string &strPrimitive1, string &strPrimitive2);
public:
 /// <summary>
 /// 计算两个词的相关度
 /// </summary>
 /// <param name="strCnWord1"></param>
 /// <param name="strCnWord2"></param>
 /// <returns></returns>
 double ComputeRelevance(const string &strCnWord1, const string &strCnWord2);
private:
 /// <summary>
 /// 存储每个词及其相关的义原描述
 /// </summary>
 map<string,HownetWord> m_oWordMap ;
 
 /// <summary>
 /// 存储每个义原的名称与该义原的对应关系,用于按义原名称检索义原
 /// </summary>
 map<string,HownetPrimitive> m_oNamePrimitiveMap ;
 
 /// <summary>
 /// 存储每个义原的ID与其父义原的ID的对应关系,用于从低一级义原往上层回溯
 /// </summary>
 map<int,int> m_oIdParentMap ;
 
 /// <summary>
 /// 计算义原相似度的参数
 /// </summary>
 const double Alpha ;
 
 /// <summary>
 /// 当一个义原没有对应义原的时候,赋予一个默认值
 /// </summary>
 const double Gama ;
 
};

#endif // !defined(AFX_WORDRELVANCECALCULATOR_H__C5D16340_1E8E_450C_A65F_7101DA8D638C__INCLUDED_)

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值