您的位置:首页 > 其它

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

2015-03-25 11:22 344 查看
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]
> MaxSim)

{

MaxRow = m;

MaxCol = n;

MaxSim = simMatrix[m]
;

}

}

}

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_)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: