新闻关键字提取和新闻推荐

新闻关键字提取和新闻推荐

jieba——超好用的分词器

在介绍我自己的关键字提取之前先介绍一下这个中文处理的包,现在在中文处理里使用非常广泛,也真的效率很高使用便捷。有非常多的功能,也有关键字提取的功能,但是效率一般,不如它的分词功能使用的人多,那么我介绍一下在我的代码会用到的它的2个功能。
结巴分词分为三种模式:精确模式(默认)、全模式和搜索引擎模式,下面对这三种模式分别举例介绍:

精确模式

import jieba
s = u'我想和女朋友一起去北京故宫博物院参观和闲逛。'
cut = jieba.cut(s)

print '【Output】'
print cut
print ','.join(cut)
【Output】
<generator object cut at 0x7f8dbc0efc30>
我,想,和,女朋友,一起,去,北京故宫博物院,参观,和,闲逛,。

可见分词结果返回的是一个生成器(这对大数据量数据的分词尤为重要)。

全模式

print '【Output】'
print ','.join(jieba.cut(s,cut_all = True))
【Output】
我,想,和,女朋友,朋友,一起,去,北京,北京故宫,北京故宫博物院,故宫,故宫博物院,博物,博物院,参观,和,闲逛,,

可见全模式就是把文本分成尽可能多的词。

搜索引擎模式

print '【Output】'
print ','.join(jieba.cut_for_search(s))
【Output】
我,想,和,朋友,女朋友,一起,去,北京,故宫,博物,博物院,北京故宫博物院,参观,和,闲逛,。

获取词性

每个词都有其词性,比如名词、动词、代词等,结巴分词的结果也可以带上每个词的词性,要用到jieba.posseg,举例如下:

import jieba.posseg as psg
print '【Output】'
print [(x.word,x.flag) for x in psg.cut(s)]

# 输出:
'''
[(u'我', u'r'), (u'想', u'v'), (u'和', u'c'), (u'女朋友', u'n'), (u'一起', u'm'), 
(u'去', u'v'), (u'北京故宫博物院', u'ns'), (u'参观', u'n'), (u'和', u'c'), (u'闲逛', u'v'), (u'。', u'x')]
'''

可以看到成功获取到每个词的词性,这对于我们对分词结果做进一步处理很有帮助,比如只想获取分词结果列表中的名词,那么就可以这样过滤:

print [(x.word,x.flag) for x in psg.cut(s) if x.flag.startswith('n')]

# 输出:
'''
[(u'女朋友', u'n'), (u'北京故宫博物院', u'ns'), (u'参观', u'n')]
'''

至于词性的每个字母分别表示什么词性,jieba分词的结果可能有哪些词性,就要去查阅词性对照表了,本文结尾附了一份从网上搜到的词性对照表,想了解更详细的词性分类信息,可以到网上搜索”结巴分词词性对照”。

关键词提取

只要关键词提取准确,新闻推荐就不是什么难事了,在经过各方调查和各种资料的阅读,我发现要想得到非常准确的新闻关键字,即得到某词在文章中的关键程度,有两方面因素都需要考虑:

  • 词本身(词长0.1,词性0.8,词频0.3)0.4
  • 居间度0.6
    tw = 0.4  # 词权重
    vdw = 0.6  # 居间度权重
    lenw = 0.1  # 词长权重
    posw = 0.8  # 词性权重
    tfw = 0.3  # tf词频权重

    以上的小数为每一项因素在我的程序中所占的比例,仅为我个人的想法,可以依据实际情况改动。词长非常容易理解,词的长度。词性是根据jieba分词判断的词性,给出一个该词性在一篇文章中一般来讲的重要程度,比如名词等词性相对来讲会更重要一些,而连词介词则是重要度为0的需要尽量剔除的词。这里给出一份参考的词性重要度表。词频也是字面意思,在一篇文章中词的频率。
n 0.8
nr 0.8
nr1 0.8
nr2 0.8
nrj 0.8
nrf 0.8
ns 0.8
nsf 0.8
nt 0.8
nz 0.8
nl 0.6
ng 0.4
t 0.6
tg 0.4
s 0.3
f 0.2
v 0.5
vd 0.4
vn 0.6
vshi 0.0
vyou 0.0
vf 0.2
vx 0.3
vi 0.3
vl 0.4
vg 0.2
a 0.5
ad 0.3
an 0.6
ag 0.3
al 0.3
b 0.3
bl 0.4
z 0.4
r 0.1
rr 0.1
rz 0.1
rzt 0.1
rzs 0.1
rzv 0.1
ry 0.1
ryt 0.1
rys 0.1
ryv 0.1
rg 0.1
m 0.2
mq 0.2
q 0.1
qv 0.1
qt 0.2
d 0.2
p 0.1
pba 0.0
pbei 0.0
c 0.0
cc 0.3
u 0.1
uzhe 0.0
ule 0.0
uguo 0.0
ude1 0.0
ude2 0.0
ude3 0.0
usuo 0.0
udeng 0.0
uyy 0.0
udh 0.0
uls 0.0
uzhi 0.0
ulian 0.0
e 0.0
y 0.0
o 0.0
h 0.0
k 0.0
x 0.0
xx 0.0
xu 0.0
w 0.0
wkz 0.0
wky 0.0
wyz 0.0
wyy 0.0
wj 0.0
ww 0.0
wt 0.0
wd 0.0
wf 0.0
wn 0.0
wm 0.0
ws 0.0
wp 0.0
wb 0.0
wh 0.0
居间度是一个需要重点介绍的概念,需要一定离散数学的知识,我自己也不算完全理解透彻了它的意思,但是计算方法还是很好介绍清楚的。

居间度

居间度是在将相似度矩阵抽象成图之下进行计算的,所以首先需要介绍一下相似度矩阵。如果将词汇进行两两对比,那么不是所有词的词义差距都是一样大的,比如做和干就是意思非常相近的两个词,而中国和吃饭则完全不是一个意思,那这么多词怎么得到它们之间的相似度呢,可以使用这份将词表示为编码的词表,在这份词表中代码越相近,他们的意思也就越相近。
完整的这份表见我们项目组的github:

https://github.com/dl-sdu/Sports-News-Writer

这里写图片描述
找到他们的代码计算出相似度之后,我们就需要这篇文章中每两个词汇之间相似度的二维表,然而这个表并不全是0,1表示的,为了大量简化计算,我把所有相似度>0.5的设为1,<0.5的设为0,从而将词汇相似度的二维表抽象成为了一个由顶点和边的图,其中每个顶点就是文章中的一个词,这里需要一点点离散数学的知识了。
    def similar_matrix(self, string_data):
        """
        function: 构建语义相关度网络
        :param string_data: 待分析的语句
        :return: similar_matrix 语义相关度网络
        """
        word_tag_dict = self.word_tag_dictionary()
        keys = word_tag_dict.keys()
        candidate_words_dict, nwword = CandidateWords().get_candidate_list(string_data)
        nwword_words = nwword.values()   #order words
        length = len(nwword_words)
        similar_matrix = numpy.zeros(shape=(length, length))
        word_list = list()
        for word in nwword_words:
            if word in keys:
                word_list.append(word)
        for i in range(length):
            for j in range(length):
                if (nwword_words[i] in word_list) and (nwword_words[j] in word_list):
                    similar_matrix[i][j] = self.similarity(i, j, nwword_words, word_tag_dict)
                else:
                    similar_matrix[i][j] = 0.2
        # self.draw_network(similar_matrix, nwword_words)
        return similar_matrix

    def similarity_network_edges(self, string_data):
        similar_matrix = self.similar_matrix(string_data)
        row_col = similar_matrix.shape
        for i in range(row_col[0]):
            for j in xrange(i+1, row_col[0]):
                if similar_matrix[i][j] > 0.5:
                    self.E.append((i, j))
        return self.E
对于顶点Vi,它的居间度bci的定义为:

这里写图片描述

n是顶点的个数,gmk是顶点m和k之间的最短路径的个数,gmk(Vi)是顶点m和k之间的最短路径中经过顶点Vi的条数。
对于无向图可以表示为:

这里写图片描述

最短路径的算法就使用Dijkstra就可以。简而言之就是哪个点,各个点之间的最短路径都经过它的频率比较高,那么它的居间度就比较高。
 def codes_betweeness_centarlity(self, string_sentence):
        """
        function: 计算词语的居间度
        :param: string_sentence :  待分析的短句
        :return: self.bcdict : 词语居间度
        """
        candidate_words_dict, nwword = CandidateWords().get_candidate_list(string_sentence)
        nwword_words = nwword.values()
        length = len(nwword_words)
        for i in range(length):
            self.G.add_node(i)
        E = SemanticSimilarity().similarity_network_edges(string_sentence)
        self.G.add_edges_from(E)
        vd = nx.betweenness_centrality(self.G, k=None, normalized=True, weight=None, endpoints=False, seed=None)
        for i in range(length):
            self.bcdict[nwword_words[i]] = vd[i]
        for i in range(length):
            self.nword[i] = nwword_words[i]
        return self.bcdict

停用词

在一篇新闻中,有的词只是为了保持文章通顺而存在的,这种词的词性也不一定,但是他们在各种文章中都会大量出现但是既不是关键字也对新闻推荐毫无帮助,这样的词叫停用词,比如数字或者符号(+-*/)字母等,还有一些这,那等等与文章最重要表达的意思无关的词,我从网上搜到了一张停用词表并在程序中读入这张表去掉了文章中所有的停用词。

这里写图片描述

完整的表还是见GitHub,这里读入停用词表:
        def get_stopwd(self):
            """
            function: 获取停用词表(哈工大+百度停用词表)
            :return: 停用词表
            """
            base_dir = os.path.dirname(__file__)  # 获取当前文件夹的绝对路径
            file_path = os.path.join(base_dir, 'stopwords.txt')  # 获取当前文件夹内的文件
            files = open(file_path, "r")  # 读取文件
            stop_words = files.readlines()
            for line in stop_words:
                sw = line.strip('\n')
                sw = sw.decode('utf-8')  # type is str
                self.stopws.append(sw)
            files.close()
            return self.stopws
读入文章并用jieba进行分词,同时保留它们的词性:
        def get_candidate_list(self, string_sentence):
            """
            function:使用停用词过滤,
            :param: string_data: 带分析的短句
            :return: candidate_dict: 停用词过滤后的候选词及其词性, nword:候选词词典及其初始化的权重
            """
            stop_words = self.get_stopwd()
            words_tag = pseg.cut(string_sentence)
            for w in words_tag:
                if w.flag != u'x' and (w.word not in stop_words):
                    self.candidate_word.append(w.word.encode("utf-8"))  # 去除停用词后的候选词candidate_word
                    self.flag.append(w.flag.encode("utf-8"))  # 保留候选词的词性
            for i in range(len(self.flag)):
                self.candidate_dict[self.candidate_word[i]] = self.flag[i]   # disorder dict (word:flag)
            for i in range(len(self.candidate_word)):
                self.nword[i] = self.candidate_word[i]
            return self.candidate_dict, self.nword
接下来统计词长,词频,词性,这些不难,因为词性我们已经储存好了,只需要乘以我们表中的权重就可以了,所以这三样放在一个函数里就行:
    def feature(self, string_data):
        """
        function: 计算候选词的词性权重,词频,词长
        :param string_data: 待分析的语句
        :return:
        """
        base_dir = os.path.dirname(__file__)
        file_path = os.path.join(base_dir, 'tag.txt')
        files = open(file_path, "r")
        poss_file = files.readlines()
        for line in poss_file:
            s = line.strip().split(' ')
            self.poss[s[0]] = s[1]
            #print s[1]
            #print s[0]
        po = self.poss
        candidate_words_dict, nword = CandidateWords().get_candidate_list(string_data)
        nwword_words = nword.values()   #order words
        pos = {}
        for word in nwword_words:
            self.word_length[word] = len(word)/3
            if candidate_words_dict[word] in po.keys():
                pos[word] = float(po[candidate_words_dict[word]])
            else:
                pos[word] = 0.1
        words_tf_dict = dict(Counter(nwword_words))                #统计词出现的次数的列表
        files.close()
        return pos, words_tf_dict, self.word_length, nwword_words
接下里乘以我们预先制定好的各项因素的权重,将所有词从最关键向不关键的排下来,这里选取最重要的7个词进行示范,可以随意更改:
    def score(self, string_data):
        """
        function: 计算候选词的重要性权重(weight)
        :param string_data: 待分析的短句
        :return: 候选词的权重排位
        """

        tw = 0.4  # 词权重
        vdw = 0.6  # 居间度权重
        lenw = 0.1  # 词长权重
        posw = 0.8  # 词性权重
        tfw = 0.3  # tf词频权重
        pos, words_tf_dict, word_length, candidate_word = self.feature(string_data)
        vd = BetweenCentrality().codes_betweeness_centarlity(string_data)
        for word in candidate_word:
            s = (vd[word] * vdw ) + (tw * (word_length[word] * lenw + pos[word] * posw + words_tf_dict[word]*tfw))
            self.word_score[word] = s
        rank = sorted(self.word_score.iteritems(), key=lambda d: d[1], reverse=True)
        return rank

    def keyword(self, string_data):
        """
        function: 返回关键词及其评分
        :param string_data: 待分析的短句
        :return: keywords :关键词,关键词评分
        """
        key_score = self.score(string_data)
        keywords = []
        for key in key_score[0:7]:
            keywords.append(key[0])
        return keywords, key_score
大功告成以后对比一下这之下的分词效果和jieba的效果。
def main_keyword(news_list):
    string = ''
    for i in news_list:
        string += i
    keyword_list = Keyword().keyword(string)
    keywords = keyword_list[0]
    print '------------本程序的提取效果--------------'
    for key in keywords:
        print key
    print '\n'
    print '------------结巴分词的提取效果--------------'
    jieba_list = jieba.analyse.extract_tags(string)
    for key in jieba_list:
        print key
    print '\n'
    return keywords
现在随便挑选一篇新闻如下:

勇士几乎不可阻挡,骑士以联盟第一人詹姆斯为核心,总决赛面对勇士,2年竟然只获得一场比赛的胜利!美球迷凯尔特人球迷表示:凯尔特人才是勇士最好的对手!这真不是吹我的主队!凯尔特人不论是首发高度、替补深度都能够与勇士成分庭抗礼之势。我们有三名同位置上顶尖的球员霍福德、海沃德、欧文。防守端对勇士也能形成威胁。连勇士球迷都在感叹:等不及看到海沃德王者归来了!表示全联盟只有凯尔特人能与勇士抗衡了!海沃德和凯尔特人主教练史蒂文斯早有合作!在大学时期,少帅就是海沃德的主教练。他们在完全不被看好的情况下,打进了NCAA总决赛。两人在凯尔特人相遇,他们毫无疑问将会在北岸球馆,继续诠释着那些以往就已经契合的思维和理念。这对天作之合将是凯尔特人冲冠阵容最强大的力量!

运行结果如下,觉得我的效果更好一点:

这里写图片描述

获得关键词及他们的重要排名以后,我对他们进行给分,从最重要的100分,每个递减10分,第7重要的40分,然后用TFIDF做基于内容的推荐。

新闻推荐

新闻推荐是门大学问了,分为以下几种:

  • 基于内容(相似度)的推荐

  • 基于用户/物品相似度的协同过滤

  • 热点新闻推荐(你看到的那些头条新闻)

  • 基于模型的推荐(通过输入一些用户特征进入模型,产生推荐结果)

  • 混合推荐(以上十八般兵器一起耍!)

    这里只做基于内容相似度的推荐,我们依据刚才的关键词及他们的关键程度分,用公式
    Similarity(A,B)=Σi∈mTFIDFA∗TFIDFBSimilarity(A,B)=Σi∈mTFIDFA∗TFIDFB
    简而言之就是求和两篇文章的相似关键词的关键程度分的乘积
    计算两篇文章的相似度然后向编辑推荐相似度最高的文章。

    以上仅为关键代码,具体代码见GitHub:https://github.com/dl-sdu/Sports-News-Writer
    这里写图片描述

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页