算法论文.docx
《算法论文.docx》由会员分享,可在线阅读,更多相关《算法论文.docx(26页珍藏版)》请在冰豆网上搜索。
算法论文
基于关键词提取的TFIDF和TextRank方法的对比研究
【摘要】随着大数据云计算的时代到来,关键词提取技术越发重要。
选用合适的关键词提取算法能大大加快工作效率。
本文选取了两个常见的关键词提取算法TFIDF和TextRank算法,对它们进行比较分析。
【Abstract】Astheeraofbigdataandcloudcomputingcomes,thetechniqueofextractionofkeywordsbecomesmoreandmoreimportant.Withappropriateextractionalgorithms,theefficiencycanbeimprovedsignificantly.Inthispaper,Ichoosetwopopularkeywordextractionalgorithm,TF-IDFandTextRank,toanalyzeandcomparetheirprosandcons.
【关键词】关键词抽取;TFIDF;TextRank
1.引言
关键词提取技术是自然语言处理和信息检索研究的重要基础之一。
随着互联网技术的快速发展,网络文本志愿信息呈几何级数不断增长,大数据、云计算等技术对文本分类的要求也越来越高。
关键词抽取技术被广泛应用在文章语义分析、文本的分类与聚类、情感分析等多种场合中。
面对日益更新和规模庞大的文本数据,能够高效准确得实现关键词提取成为加快计算速度性能的关键。
关键词抽取的主要任务是:
对未知类别的文档进行自动处理,通过一定算法提取出其中的关键词,从而方便后续操作。
因此,为了使之后的信息检索和过滤等操作的准确性加强,对文本关键词抽取算法的精确度要求也越来越高。
近年来,多种机器学习、统计理论等方法被用来进行文本的自动分类。
【1】
本文根据文本关键词词语之间的关联性与词频特性,选取了TFIDF和TextRank关键词提取算法,进行两者的效率和准确性的对比研究。
2.TFIDF算法
2.1.TF-IDF算法简介
TF-IDF是一种统计方法,用以评估一字词对于一个文件集或一个语料库中的其中一个词组或短语的重要程度。
字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率成反比下降。
在一组文档中,刻画某一文档特征的特征项可以根据其在这组文档中出现的频率赋予相应的权重,只有在少数文档中出现的较特殊的词,权重要比在多篇文档中出现的词的权重要高。
TF-IDF加权的各种形式常被搜索引擎应用,作为文件与用户查询之间相关程度的度量或评级。
2.2.TF-IDF算法原理
TF-IDF实际上是TF和IDF的组合。
TF即词频(TermFrequency),IDF即逆向文档频率(InverseDocumentFrequency)。
TF(词频)就是某个词在文章中出现的次数,此文章为需要分析的文本。
为了统一标准,有如下两种计算方法【2】:
和
IDF(逆向文档频率)为该词的常见程度,需要构建一个语料库来模拟语言的使用环境。
如果一个词越常见,那么其分母就越大,IDF值就越小。
之后,将每个单词的TF-IDF值按从大到小降序排列,排在最前面的几个词即为关键词。
2.3.TF-IDF算法实现
2.3.1.构建一一映射Map类
C++STL函数库中已经包含了map的库函数,但为了使用起来更加方便、更便于个性化定制操作,于是使用自己定制的Map类模板。
这个类函数主要是构建单词的string值与其TF、IDF值的一一对应关系,方便直接用string值下标访问其int值或double值,简化写代码的工作量。
同时模板类中主要采取树形结构,建立一棵查找树,利用vector的空间动态分配的灵活性,从string的第一个字母开始一个一个往下找。
每个Map类代表一个字母,从根部开始向下遍历,利用bool值判断该处是否为一个单词结尾。
代码如下:
/*********************************************************************
*
*一一映射函数Map类
*Type为存储的TF、IDF、TF-IDF值,如int、double等
*旨在通过string类型下标访问其Type值
*
*********************************************************************/
template
classMap
{
public:
Typeval=NULL;//Type值,类型为int、double
/*********************************************************************
*
*下标运算符[]重载
*[]中值为string类型
*如果该string值已存在,则返回对应val
*如果不存在则新建一个,返回初始值
*
*********************************************************************/
Type&operator[](stringitem)
{
Map&temp=find(item);//引用类函数find()查找到的值
if(temp!
=NULL)//找到,则返回对应val
{
returntemp.val;
}
else//未找到,则用create()函数创建一个并返回新的值
{
create(item);
returnfind(item).val;
}
}
/*********************************************************************
*
*算术运算符=重载
*左值为找到的Map.val引用
*右值为对应的val值
*返回当前Map的引用
*
*********************************************************************/
Map&operator=(Typeitem)
{
val=item;
returnthis;
}
/*********************************************************************
*
*类函数列表
*find为查找函数,找到则返回其值,没找到则新建一个返回初值
*create为创建新值得函数
*
*********************************************************************/
Mapfind(stringword);
voidcreate(stringword);
private:
boolis_end=false;//是否为单词结尾
charID;//代表当前类的字母
vector
};
/*********************************************************************
*
*find查找函数
*如果该string值已存在,则返回对应值
*如果不存在则新建一个,返回初始值
*
*********************************************************************/
template
MapMap:
:
find(stringword)
{
for(auto&r:
childs)//遍历类函数的所有子节点直到找到对应项
{
if(r->ID==word[0]&&r->is_end)//如果相等且为单词末尾
{
if(word.size()==1)//大小为一说明已找到
{
returnthis;//返回当前类
}
returnr->find(word.substr(1,word.size()-1));//递归调用find函数,一次减少一个字符
}
}
returnNULL;//返回空类
}
/*********************************************************************
*
*create创建函数
*递归创建字母树
*
*********************************************************************/
template
voidMap:
:
create(stringword)
{
if(word.size()==0)//如果大小为0,说明所有字母都已创建完毕
{
is_end=true;//标记单词结尾为true
return;
}
for(auto&r:
childs)//遍历所有子节点
{
if(r->ID==word[0])//如果找到,则减少第一个字符,继续递归向下遍历
{
r->creat(word.substr(1,word.size()-1));
return;
}
}
Map*temp=newMap;//没找到,则新建一个Map
temp->ID=word[0];//将其所代表的字符ID设为当前word的第一个字符
childs.push_back(temp);//子节点中增加这一Map
temp->creat(word.substr(1,word.size()-1));//继续递归创建后续字符
return;
}
2.3.2.构建语料库
为了使语料库更符合本程序的应用场景,语料库采用自己构建的语料库。
语料库素材来源于小说新闻等,总词数超过8亿词。
因为每篇文档有几十万字,为了使词语的ITF更具普遍性,假设每1000词为一篇文章。
然后每次将文章先存入一个临时语料库中,进行分析处理后,将处理后的词加入临时语料库。
之后再将临时语料库合并入总的语料库。
大致流程图如下:
//建立语料库
mapcorpus;//语料库,由字母string到出现频率double的映射
longlonginttimes=0;//文章总数,作为计算频率的基数
ofstreamoutput("corpus.csv");//输出到excel表格中存储
/*********************************************************************
*
*导入制作好的语料库
*存在一一映射corpus中
*
*********************************************************************/
voidbuildCorpus()
{
ifstreaminput("corpus.csv");//打开文件
stringitem;//定义临时存储变量string和double
doubleval;
while(!
input.eof())//读入文件,存储到语料库映射中
{
input>>item>>val;
corpus[item]=val;
}
}
/*********************************************************************
*
*分析处理读入的string短语
*先删去字符前的不必要符号如数字、标点"([{<等等
*将剩下的大写字符转化为小写
*保留字符中的标点,如Mr.Bill等
*再删除字符后的不必要标点,如.,"':
等等
*返回修改后的字符
*
*********************************************************************/
stringanalysis(stringitem)
{
stringtemp="";//定义空字符
boolflag=false;//flag标记,当碰到第一个字母时,flag变为true,否则为false,滤过不必要的前缀
for(auto&r:
item)//遍历string中每一个字符
{
if(r==','||r==';')//滤过,;等符号
{
continue;
}
if(r<='z'&&r>='a')//碰到字母标记true
{
flag=true;
}
if(r<='Z'&&r>='A')//转化大小写
{
flag=true;
r+=32;
}
if(flag)//如果已碰到字母,则在缓存string中加入该字符
{
temp+=r;
}
}
if(temp.size()==0)//如果是空字符,直接return
{
returntemp;
}
for(autoi=temp.rbegin();i<=temp.rend();i++)//从尾开始遍历string,删去string中不必要的后缀
{
if(*i<='z'&&*i>='a')//若碰到字母,则跳出循环
{
break;
}
temp.pop_back();//删去后缀
}
returntemp;//返回处理过的字符string
}
/*********************************************************************
*
*遍历临时语料库的数值
*将其合并到总的语料库
*
*********************************************************************/
voidprint(map&frequency)
{
for(auto&r:
frequency)//遍历临时语料库
{
if(corpus.find(r.first)==corpus.end())//第一次出现则将在总语料库中新建一个,并将其初始化
{
corpus[r.first]=0.0;
}
corpus[r.first]++;//合并语料库中对应数值
}
}
/*********************************************************************
*
*创建语料库
*通过文件读入文章
*由于部分文章为小说,有几十万字,于是统一按每1000字为一篇文章
*语料库素材中所有文章的总次数大概有8亿词
*将该文章中出现的单词存入临时语料库中
*每一千字更新总语料库corpus,清空临时语料库frequency
*最后计算每个单词的IDF值
*
*********************************************************************/
voidreadDictEn()
{
intwords=0;//记录当前已读入的word词数,每一千字更新一次
mapfrequency;/临时语料库,存放string单词到double频率的一一映射
longlongintcnt=0;//临时变量,记录临时语料库被清零多少次
for(inti=1;i<=633;i++)//遍历语料库素材TXT文档,共有633个TXT文档,每篇文档平均12万词
{
/*********************************************************************
*
*每篇文档的命名方式为"EN(i)",i为文档编号
*先利用stringstream流创建文件名的字符串
*再用ifstream流打开对应文件
*
*********************************************************************/
cout<
";//输出当前读入文件状态
ostringstreamout;//stringstream流,存储文件名字符串
out<<"EN("<
ifstreamfile(out.str());//打开文件
stringitem;//临时存储变量
while(!
file.eof())//读入文件
{
file>>item;
item=analysis(item);//将读入字符串处理成所需要求
if(item.empty())//空字符串则略过
{
continue;
}
if(frequency.find(item)==frequency.end())//如果是新的字符串,则在语料库里新建一个并初始化
{
frequency[item]=0.0;
}
frequency[item]++;//增加出现次数
words++;
if(words==1000)//读入一千词时,即读入一篇文章时,合并语料库并清空临时语料库
{
print(frequency);
frequency.clear();
cnt++;
words=0;
}
}
}
for(auto&r:
corpus)//打印每个单词的IDF值
{
r.second/=(cnt+1);
r.second=abs(log(r.second));
output<}
}
2.3.3.计算TF-IDF值
计算TF值时,只需要将所抽取的文章对象全部遍历一遍,对每个词计算其词频,再将词频乘以对应的IDF值,按照从大到小排序即可输出结果。
/*********************************************************************
*
*提取关键词
*对读入文章的每一个单词计算TF值
*再分别计算TF-IDF值
*将每个单词的结果输出到excel表格中
*
*********************************************************************/
voidextract(stringname)
{
ifstreamfile(name);//打开文件
stringitem;
longlongintwords=0;//单词总数,包括相同的单词
mapfrequency;//这篇文章的语料库
while(!
file.eof())//循环读入文章中所有单词
{//以下过程与构建语料库过程类似
file>>item;
item=analysis(item);
if(item.empty())
{
continue;
}
if(idle.find(item)!
=idle.end())
{
continue;
}
if(frequency.find(item)==frequency.end())
{
frequency[item]=0.0;
}
frequency[item]++;
words++;
}
output.open("data.csv");
for(auto&r:
frequency)//输出数据到Excel
{
r.second/=words;
r.second*=corpus[r.first];
output<}
}
3.TextRank算法
3.1.算法简介
TextRank也是一种用于文本排序的算法,它主要运用了矩阵迭代的代数知识来对每个文本词语进行分析,最后得出数值越高则越重要。
其思想来源于网页排序算法PageRank。
PageRank的基本思想主要是:
一个网页的重要程度取决于链接到它的网页的数量以及这些网页的重要程度。
比如有n个网页,对于其中一个网页
,所有链接到
的页面集合记为
,所有
链接到的页面集合记为
,则其重要程度为:
此处的求和号可以通过矩阵计算来模拟实现,并通过不断地迭代,重要性的值会不断收敛为一常值,最后可以求出最后的网页重要性。
TextRank就是仿造这算法设计。
【3】
3.2.算法思路
TextRank总体思路与PageRank类似。
因为文本中不存在窗口与链接,所以要模拟造出窗口。
在这里不妨设k个词为一个窗口,其中每个单词为一个链接,当一个窗口中的单词与另一个窗口中的单词一样时,即视为两个窗口有链接。
当然也可以脱离PageRank的思想来解释这一算法。
对于一个句子,其中的一个单词与这个句子的剩余单词都有关联性。
同时,这个句子中的每一个单词又与另外其他句子中相同的