本案例主要介绍自然语言处理方面的核心技术,其中主要是文本处理相关技术,例如分词、词性标记、情感分析、语言模型、语义角色标记等。
实现目的:实现机器自动生成商品的推荐标题和推荐语。
具体要求:是根据一个商品的标题及商品的详细描述信息,自动生成推荐语的标题和推荐内容,推语的内容字数要求在50〜60字之间,要求生成的文字语法通顺,语义准确。
// pip install jieba (其他库的安装也是这样)
pip install gensim -i https://pypi.tuna.tsinghua.edu.cn/simple/
(如果速度太慢,可加镜像源)
①在安装gensim时,出现安装成功但pycharm依旧显示没有gensim库的状况:
发现是安装路径出现错误:cmd命令将gensim安装在python下的site-package中,与pycharm的解释器不是统一路径。
②gensim的版本最好选择4.0以下版本。
对物品标题进行分词以提取其中的关键特征,采用jieba分词组件
①zh-CN:表示用在中国大陆区域的中文。包括各种大方言、小方言、繁体、简体等等都可以被匹配到。
②zh-Hans:表示简体中文。适用区域范围是全宇宙用中文简体的地方,内容包括各种用简体的方言等
import jieba
import jieba.analyse
import csv
import re
in_debug=False
#用正则表达式移除特殊符号
def remove_punc(line_sentence):
multi_version = re.compile("-\{.*?(zh-hans|zh-cn):([^;]*?)(;.*?)?\}-")
punctuation = re.compile("[-~!@#$%^&*()_+`=\[\]\\\{\}\"|;':,./<>?·!@#¥%……&*()——+【】、;‘:“”,。、《》?「『」』]")
line = multi_version.sub(r"\2", line_sentence)
line = punctuation.sub(' ', line_sentence)
return line
停用词是指在文本分析的时候不重要的词,一般情况下,有通用的停用词表,但在处理这个项目时,为了提高后续分词的效果,我们加载自定义的停用词表,并将其放在TXT文件中。
每一行是一个词语,每一行分三部分:词语、词频(可省略)、词性(可省略),用空格隔开,顺序不可颠倒。
def get_stop_words_set(file_name):
with open(file_name,'r',encoding='utf-8') as file: #加载停用词
return set([line.strip() for line in file])
def load_words_list(stop_word_file):
global stop_words_set
stop_words_set = get_stop_words_set(stop_word_file)
if in_debug:
print("共计导入 %d 个停用词"%len(stop_words_set))
jieba.load_userdict("dict.txt.big.txt") #自定义停用词表
stop_word_file ="stopwords.txt" #通用停用词
jieba.analyse.set_stop_words(stop_word_file) #将自定义词表用来关键词提取
load_words_list(stop_word_file)
def cut_words(sentence):
word_list =[]
words = jieba.cut(remove_punc(sentence)) #默认精准模式
for word in words:
if word in stop_words_set:
if in_debug:
print("ignore words:"+word)
continue
if len(word.strip())>1:
word_list.append(word)
return word_list
调用函数,使用自定义停用词表进行关键词提取,并输出词表,共746个停用词。
with open('item_titles.csv', 'r',encoding='utf-8') as f:
reader = csv.reader(f)
item_titles = list(reader) #将文本转化成列表格式
result_titles = []
for item in item_titles:
result_titles.append(" ".join(cut_words(item[1])))
print(result_titles)
print(item_titles[:10])
导入“item_titles.csv”文件,将内容转化成列表格式,对列表中的数据进行遍历,达到词性过滤的目的,最终输出到result_titles文件中。
###对于主题词的提取,这里介绍三种方法。
TextRank 算法是一种用于文本的基于图的排序算法。其基本思想来源于谷歌的 PageRank算法, 通过把文本分割成若干组成单元(单词、句子)并建立图模型, 利用投票机制对文本中的重要成分进行排序, 仅利用单篇文档本身的信息即可实现关键词提取、文摘。
其主要步骤如下:
(1)把给定的文本T按照完整句子进行分割,即
(2)对于每个句子,进行分词和词性标注处理,并过滤掉停用词,只保留指定词性的单词,如名词、动词、形容词,其中是保留后的候选关键词。
(3)构建候选关键词图G = (V,E),其中V为节点集,由2生成的候选关键词组成,然后采用共现关系(co-occurrence)构造任两点之间的边,两个节点之间存在边仅当它们对应的词汇在长度为K的窗口中共现,K表示窗口大小,最多共现K个单词。
(4)根据上面公式,迭代传播各节点的权重,直至收敛。
(5)对节点权重进行倒序排序,从而得到最重要的T个单词,作为候选关键词。
(6)由(5)得到最重要的T个单词,在原始文本中进行标记,若形成相邻词组,则组合成多词关键词。
def textrank_words(line):
line = remove_punc(line)
line = line.strip()
if len(line) < 1: return ""
line_words =""
for word,x in jieba.analyse.textrank(line.strip(), withWeight=True,allowPOS=('n', 'vn')):#(数据,权重,词性过滤)
if word.strip()=="": continue
line_words = line_words + (word + " ")
return line_words
result_titles = [] #建立存储分词的列表
for item in item_titles:
rank_result = textrank_words(item[1])
if len(rank_result)>0: result_titles.append(rank_result)
print(result_titles[:10])
可以看出Textank算法得出的结果是:
['移动 电信 玫瑰 手机 ',
'移动 电信 魅海 手机 ',
'网通 内存 手机 ',
'电信 耀金 移动 手机 ',
'移动 电信 星空 手机 ',
'金色 手机 网通 小米 ',
'电信 黑色 移动 手机 ',
'电信 移动 光金 手机 ',
'网通 空间 香槟金 智能手机 ',
'金色 玫瑰 ']
虽然将关键词提取出来了,但是数据之间的差异太小,显然Textrank算法并不适用于此项目。因此尝试我们采用LDA模型。
1.LDA主题模型主要用于推测文档的主题分布,可以将文档集中每篇文档的主题以概率分布的形式给出根据主题进行主题聚类或文本分类
如果用通俗的语言来讲,假设我们有一个文档集,里面有M个文档,对于第d个文档中会出现一堆单词,其中有一个单词是“周杰伦”,那么通过这个单词我们就可以理解为该文档的主题可能是“娱乐”,但是这个文档中还出现“姚明”,“孙杨”,“张继科”这些单词,此时该文档为“体育”主题的概率将大大上升,LDA模型就是要根据给定一篇文档,推断这个文档的主题是什么,并给出各个主题的概率大小是多少。
如果我们要生成一篇文档,它里面的每个词语出现的概率为:
import gensim
from gensim.models import LdaModel #导入gensim包
from gensim.corpora import Dictionary
from gensim import corpora, models #导入gensim中的corpor(语料)
titles = [item[1] for item in item_titles]
counter = 10
for title in titles:
title_words_list = [cut_words(title)]
dictionary = corpora.Dictionary(title_words_list) #构建词典
corpus = [ dictionary.doc2bow(title) for title in title_words_list ] #语料向量化---遍历词的列表得出一个新的语料
lda = LdaModel(corpus=corpus, id2word=dictionary, num_topics=2) #分为2个主题
print(lda.print_topics(num_topics=2, num_words=2)) #每个主题输出2个单词
counter -=1
if counter <0: break
输出结果:
通过lda.print_topics方法将每个商品的2个主题,每个主题中限制主题词的数量为2个进行输出,前10个标题的主题词
###出现频率最多的单词,绘制成柱状图。
import pandas
import os
import matplotlib
import matplotlib.pyplot as plt
import seaborn as sns #绘图的库(统计图表)
#出现最频繁的单词(如果一个词的出现与他周围的词是独立的,我们就称之为unigram,也就是一元语言模型)
def get_top_n_words(corpus, n=None):
vec = CountVectorizer().fit(corpus)
bag_of_words = vec.transform(corpus)
sum_words = bag_of_words.sum(axis=0)
words_freq = [(word, sum_words[0, idx]) for word, idx in
vec.vocabulary_.items()]
words_freq =sorted(words_freq, key = lambda x: x[1],
reverse=True)
return words_freq[:n]
#Convert most freq words to dataframe for plotting bar plot
top_words = get_top_n_words(result_titles, n=20)
top_df = pandas.DataFrame(top_words) #出现最频繁单词矩阵
top_df.columns=["Word", "Freq"]
#Barplot of most freq words(直方图)
sns.set(rc={'figure.figsize':(13,8)})
plt.rcParams['font.sans-serif'] = ['SimHei'] #中文显示代码
g = sns.barplot(x="Word", y="Freq", data=top_df)
g.set_xticklabels(g.get_xticklabels(), rotation=0,) #刻度
简单来说就是:一个词语在一篇文章中出现次数越多, 同时在所有文档中出现次数越少, 越能够代表该文章。这也就是TF-IDF的含义。
它的公式:
具体操作:将所有的商品标题进行分词后,构造成一个标题列表,作为语料集合。初始化CountVectorizer对象,并将语料集进行训练转换。
from sklearn.feature_extraction.text import CountVectorizer #向量转换
from sklearn.feature_extraction.text import TfidfTransformer #词频-逆文件频率
import re
#文本特征抽取
counter = 10
result_titles = []
for item in item_titles:
result_titles.append(" ".join(cut_words(item[1])))
print(result_titles[:10])
len(result_titles) #100个title
cv=CountVectorizer(max_df=0.8,stop_words=stop_words_set, max_features=100, ngram_range=(1,3))
#(max_df=0.8,阈值,当超过0.8时,则不会被选择为关键词)
#(max_features=100,对关键词进行降序排序,取前100个)
#(ngram_range=(1,3),词组切分的长度范围)
len(stop_words_set)
X=cv.fit_transform(result_titles) #通过fit_transform函数将文本中的词语转换为词频矩阵
print(X) #第0个列表元素,**词典中索引的元素**, 词频
运行结果:
(0, 87) 1
(0, 33) 1
(0, 60) 1
(0, 64) 1
(0, 79) 1
(0, 61) 1
(0, 66) 1
…
#TF-IDF模型提取关键字
tfidf_transformer = TfidfTransformer(smooth_idf=True,use_idf=True) #TfidfTransformer用于统计vectorizer中每个词语的TF-IDF值
tfidf_transformer.fit(X)
feature_names=cv.get_feature_names() #特征名
通过tfidf_transformer进行转换,获得其tf_idf_vector对象,接下来,经过sort_coo排序之后提取其中前n个主题词(目前为3个),并将主题词与对应的权重值输出。
代码如下:
#coo_matrix一种基于坐标格式的稀疏矩阵,每一个矩阵项是一个三元组(行,列,值)。
def sort_coo(coo_matrix): #将矩阵映射到坐标,TF-IDF函数将频率分数映射到矩阵,然后进行排序,方便找到关键字
tuples = zip(coo_matrix.col, coo_matrix.data)
return sorted(tuples, key=lambda x: (x[1], x[0]), reverse=True)
帮助排序和选择关键字的函数:extract_topn_from_vector,从排序结果中取出前N =10个关键词:
def extract_topn_from_vector(feature_names, sorted_items, topn=10):
sorted_items = sorted_items[:topn]
score_vals = []
feature_vals = []
for idx, score in sorted_items:
score_vals.append(round(score, 3))
feature_vals.append(feature_names[idx])
results= {}
for idx in range(len(feature_vals)):
results[feature_vals[idx]]=score_vals[idx]
return results
####输出10个关键词和它所对应的权重:
for title in result_titles[:10]:
tf_idf_vector=tfidf_transformer.transform(cv.transform([title])) #给定文档生成TF-IDF
sorted_items=sort_coo(tf_idf_vector.tocoo()) #排序向量
keywords = extract_topn_from_vector(feature_names,sorted_items,3) #抽取前10个
str_keywords = [k +" " + str(keywords[k]) for k in keywords]
print(str_keywords)
代码结果:
通过TF-IDF方法获得到标题主题词与前面两种方式相比,已经具有较强的差异化和区分度,以此进行推荐标题和推荐语的生成。
至此,推荐标题和推荐语已经生成,该项目效果已经完成。