@elsa907
2016-11-06T12:30:43.000000Z
字数 15091
阅读 4671
算法
朴素贝叶斯
机器学习
在网络数据爆炸的年代,用户面对的是海量的信息。这在一定程度上为用户提供全方位的信息,但另一方面,用户在海量的信息中要想找寻的感兴趣的内容可谓是难上加难,这就是所谓的“信息过载”。
搜索引擎和推荐系统是目前解决问题的主要手段。与搜索引擎相比,个性化的推荐系统能够主动地记录用户注册信息、用户浏览历史等,从而挖掘用户的兴趣偏好和项目特征,实现“私人定制”。目前,个性化推荐系统在网络购物,社交网站,电影,视频和音乐等领域都有不同的应用。其中,在网络购物中,个性化推荐系统可以根据用户的购买记录,浏览信息等主动地为用户推荐喜欢的商品,从而减少用户搜索商品的时间。但是,该系统在用户消费过程中仅仅起到了过滤信息,筛选商品的作用,在购买行为产生后用户对商品的真实满意度评价并未预测。
基于内容的推荐算法是个性化推荐系统的一种解决思路,它是根据已有用户行为历史对用户喜好进行建模,从大量物品中筛选出符合用户特征模型的物品对用户推荐。本文就是根据内容推荐算法的原理,对购买产品的满意度进行建模,希望能够得到用户购买决策的特征行为,从而实现有效购买。
基于内容的推荐也成为基于内容的信息过滤推荐,它不需要用户对推荐对象进行评价,而是把推荐对象的内容特征去学习用户的偏好兴趣,最后与用户偏好兴趣匹配度较高的对象被推荐给用户。
在基于内容的推荐算法中,主要利用对象内容特征和用户资料模型。其中对象内容特征采用基于权重的向量空间模型进行标识,用机器学习的方法来获取用户资料模型。基于内容的推荐算法中的效用函数可表示为:
真实应用中对象往往都会有一些可以描述它的属性。这些属性通常可以分为两种:结构化属性与非结构化的属性。所谓结构化的属性就是这个属性的意义比较明确,其取值限定在某个范围;而非结构化的属性往往其意义不太明确,取值也没什么限制,不好直接使用。比如在交友网站上,对象就是人,一个对象会有结构化属性如身高、学历、籍贯等,也会有非结构化属性(如对象自己写的交友宣言,博客内容等等)。对于结构化数据,我们自然可以拿来就用;但对于非结构化数据(如文章),我们往往要先把它转化为结构化数据后才能在模型里加以使用。因此可以利用向量空间模型(VSM)进行特征信息提取。
下面对文本的采用向量空间模型进行结构化,商品信息本质上就是一串具有多项特征的文本,其处理思想与对文本的处理类同:
记我们要表示的所有文章的集合为,而所有文章中出现的词的集合(也称为词典)为 。那么第j篇文章可被表示为。其中表示第n个词在文章中的权重,而权重越大可理解为该词对于文章特征体现越重要。在一篇文章中出现频率最多的词权重可能很大,但有些词在不同类型文章中出现频率都很高。因此,引入词频-逆文档频率进行权重计算。
TF-IDF(term frequency–inverse document frequency)是一種用於資訊檢索與文本挖掘的常用加權技術。TF-IDF是一種統計方法,用以評估一字詞對於一個文件集或一個語料庫中的其中一份文件的重要程度。字詞的重要性隨著它在文件中出現的次數成正比增加,但同時會隨著它在語料庫中出現的頻率成反比下降。TF-IDF加權的各種形式常被搜索引擎應用,作為文件與用戶查詢之間相關程度的度量或評級。除了TF-IDF以外,互聯網上的搜尋引擎還會使用基於連結分析的評級方法,以確定文件在搜尋結果中出現的順序。
TF-IDF的主要思想是:如果某个词或短语在一篇文章中出现的频率TF高,并且在其他文章中很少出现,则认为此词或者短语具有很好的类别区分能力,适合用来分类。TF-IDF实际上是:TF * IDF,TF词频(Term Frequency),IDF逆向文件頻率(Inverse Document Frequency)。TF表示词条在文档d中出现的频率。IDF的主要思想是:如果包含词条t的文档越少,也就是n越小,IDF越大,则说明词条t具有很好的类别区分能力。如果某一类文档C中包含词条t的文档数为m,而其它类包含t的文档总数为k,显然所有包含t的文档数n=m+k,当m大的时候,n也大,按照IDF公式得到的IDF的值会小,就说明该词条t类别区分能力不强。但是实际上,如果一个词条在一个类的文档中频繁出现,则说明该词条能够很好代表这个类的文本的特征,这样的词条应该给它们赋予较高的权重,并选来作为该类文本的特征词以区别于其它类文档。这就是IDF的不足之处。 在一份給定的文件裡,詞頻(term frequency,TF)指的是某一個給定的詞語在該文件中出現的频率。這個數字是对词数(term count)的归一化,以防止它偏向長的文件。(同一個詞語在長文件裡可能會比短文件有更高的詞数,而不管該詞語重要與否。)
(摘选自维基百科)
其权重可表示为:
对一篇文章所出现的所有单词进行权重计算,就可得到文章的特征矩阵,对于商品信息的处理也是如此。
假设用户已经对一些物品给出喜好判断,那么这一步就是要通过用户的喜好为他产生一个模型。有了这个模型,我们可以判断用户是否会喜欢一个新的物品。实际上,这是一个典型的有监督分类学习的问题,可以采用机器学习的算法。这里,我们采用朴素贝叶斯算法。
首先,介绍朴素贝叶斯算法的原理:
朴素贝叶斯算法是一个直观的方法,使用每个属性归属于某个类的概率来做预测。我们可以使用这种监督性学习方法,对一个预测性建模问题进行概率建模。因此,就可以将分类问题转化为概率预测问题,这也是朴素贝叶斯分类器的基本原理:
在维基百科中给出这样的解释:
在机器学习中,朴素贝叶斯分类器是一系列以假设特征之间强(朴素)独立下运用贝叶斯定理为基础的简单概率分类器。
朴素贝叶斯自20世纪50年代已广泛研究。在20世纪60年代初就以另外一个名称引入到文本信息检索界中,[1]:488 并仍然是文本分类的一种热门(基准)方法,文本分类是以词频为特征判断文件所属类别或其他(如垃圾邮件、合法性、体育或政治等等)的问题。通过适当的预处理,它可以与这个领域更先进的方法(包括支持向量机)相竞争。[2] 它在自动医疗诊断中也有应用。[3]
朴素贝叶斯分类器是高度可扩展的,因此需要数量与学习问题中的变量(特征/预测器)成线性关系的参数。最大似然训练可以通过评估一个封闭形式的表达式来完成,[1]:718 只需花费线性时间,而不需要其他很多类型的分类器所使用的费时的迭代逼近。
在统计学和计算机科学文献中,朴素贝叶斯模型有各种名称,包括简单贝叶斯和独立贝叶斯。[4] 所有这些名称都参考了贝叶斯定理在该分类器的决策规则中的使用,但朴素贝叶斯不(一定)用到贝叶斯方法;[4] Russell和Norvig提到“[朴素贝叶斯]有时被称为贝叶斯分类器,这个马虎的使用促使真正的贝叶斯论者称之为傻瓜贝叶斯模型。”[1]:482
通过训练贝叶斯分类器,我们可以完成用户特征建模这一步。
这一步主要是根据我们创建的用户特征模型,匹配符合特征的对象。在朴素贝叶斯分类器中,这一步可以通过预测直接给出。这里就不再阐述其过程。
算法实现过程主要由这样两部分组成:文本特征提取、训练贝叶斯分类器。其中,在文本特征提取中又涉及到英文文本特征提取和中文文本特征提取。下面我会结合不同实例对这几部分进行说明:
由于英文特征提取较为简单,这一部分我们的重点在于实现文本特征提取以及实现贝叶斯分类器。
下面我们对康奈尔大学2MB影评数据进行文本特征提取以及贝叶斯分类器构建。
文本特征提取
向量空间模型的原理已经在上文中有简单介绍,就不再赘述。而在代码实现过程中,需要读取数据并处理,然后计算TF-IDF。
康奈尔大学影评数据已经分类为积极评价(positive)和消极评价(negative)并分别存放在neg和pos的文件夹下,因此我们调用sklearn包中load_files命令读取。
movie_reviews = load_files('tokens')#读取名为“tokens”的文件夹
由于数据库并没有给出训练集和测试集因此我们需要对数据库进行切分,调用分割函数,将训练集和数据集的比例设置为80%:20%。
doc_train, doc_test, y_train, y_test = train_test_split(movie_reviews.data, movie_reviews.target, test_size = 0.2)#分割数据集
#a = type(doc_train)#查看参数类型
#b = type(y_train)
为了确保提取出的文本是字符串形式,我在这里查看了doc_train和y_train的参数类型。doc_train显示为list,可以理解为许多字符串的集合,而y_train则是矩阵,表示字符串所对应的标签。
词频计算我调用了sklearn包中的TfidVectorizer函数,这个函数就是计算TF-IDF,详细的调用方法参见
(http://scikit-learn.org/stable/modules/generated/sklearn.feature_extraction.text.TfidfVectorizer.html)
scikit-learn有很多函数可做机器学习的直接调用,其中关于计算词频有四种函数,分别为CountVectorizer、HashingVectorizer、TfidfTransformer、TfidfVectorizer。关于他们的不同计算原理和使用方法可在(http://scikit-learn.org/stable/modules/classes.html#module-sklearn.feature_extraction.text)页面查询。
count_vec = TfidfVectorizer(binary = False, decode_error = 'ignore')
x_train = count_vec.fit_transform(doc_train)#计算训练集词频
x_test = count_vec.transform(doc_test)#计算测试集词频
x = count_vec.transform(movie_reviews.data)
y = movie_reviews.target
然后我们把计算得到的文本特征矩阵送入到朴素贝叶斯分类器中,关于朴素贝叶斯分类器的实现,我再次调用了sklearn库中的MultinoNB函数,这时一个基于多项式频率进行概率预测的贝叶斯分类器,当然sklearn库中还包括基于高斯分布和正态分布的朴素贝叶斯分类器。可以根据不同需要选择使用。使用伯努利分类器和多项式频率的分类器效果是一样的。
(http://scikit-learn.org/stable/modules/classes.html#module-sklearn.naive_bayes)
clf = MultinomialNB().fit(x_train, y_train)#调用多项式型贝叶斯分类器
#clf = BernoulliNB().fit(x_train,y_train)#调用伯努利贝叶斯分类器
doc_class_predicted = clf.predict(x_test)#.predicted可以对测试集分类
#print(doc_train)
#print(count_vec.get_feature_names())
#print(x_train.toarray())
#print(movie_reviews.target)
#print(np.mean(doc_class_predicted == y_test))
presion, recall, thresholds = precision_recall_curve(y_test, clf.predict(x_test))
answer = clf.predict_proba(x_test)[:,1]
report = answer > 0.5
print(classification_report(y_test, report, target_names = ['neg', 'pso']))
最后输出结果显示该分类器精确度为0.8,说明贝叶斯分类器效果良好。那么我们就可以利用这个基本框架,继续进行下一步实现中文贝叶斯分类器。
输出结果:
precision recall f1-score support
neg 0.75 0.82 0.78 140
pso 0.80 0.73 0.76 140
total 0.78 0.78 0.77 280
调用伯努利贝叶斯分类器:
precision recall f1-score support
neg 0.77 0.85 0.81 139
pso 0.83 0.74 0.79 141
total 0.80 0.80 0.80 280
完整代码:
# -*- coding: utf-8 -*-
"""
Created on Mon Oct 10 15:01:06 2016
项目:朴素贝叶斯实现情感推荐
@author: Administrator
"""
import scipy as sp
import numpy as np
from sklearn.datasets import load_files
from sklearn.cross_validation import train_test_split
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.metrics import precision_recall_curve
from sklearn.metrics import classification_report
movie_reviews = load_files('tokens')
'''
读取和保存数据
sp.save('movie_data.npy', movie_data)
sp.save('movie_target.npy', movie_target)
movie_data = sp.load('movie_data.npy')
movie_target = sp.load('movie_target.npy')
'''
#movie_reviews = open("seg.txt", "rb")
doc_train, doc_test, y_train, y_test = train_test_split(movie_reviews.data, movie_reviews.target, test_size = 0.2)
#a = type(doc_train)
#b = type(y_train)
#特征提取
count_vec = TfidfVectorizer(binary = False, decode_error = 'ignore')
x_train = count_vec.fit_transform(doc_train)
x_test = count_vec.transform(doc_test)
x = count_vec.transform(movie_reviews.data)
y = movie_reviews.target
#调用分类器
clf = MultinomialNB().fit(x_train, y_train)
doc_class_predicted = clf.predict(x_test)
#print(doc_train)
#print(count_vec.get_feature_names())
#print(x_train.toarray())
#print(movie_reviews.target)
#print(np.mean(doc_class_predicted == y_test))
#计算准确率与召回率
presion, recall, thresholds = precision_recall_curve(y_test, clf.predict(x_test))
answer = clf.predict_proba(x_test)[:,1]
report = answer > 0.5
print(classification_report(y_test, report, target_names = ['neg', 'pso']))
文本提取和贝叶斯分类器已经在前面搭建好,因此这一部分我把重点放在中文数据处理方面。为了检测中文分类效果,我先选择了测试集和训练集总计包含4万条中文标题的数据库,训练贝叶斯分类器根据标题进行分类。
数据集为ANSI编码的文本文件,其格式为“game LOL战队实力排行榜第十周 OMG跃居第四”,前面的英文为标签,后一部分则是标题,标签需要提取,同时需要做中文分词处理。分词处理采用结巴中文分词组件。
关于结巴为此的详细用法参见(https://github.com/fxsjy/jieba)
f1 = open("train_file.txt")#读取文本文件
f2 = open("test_file.txt")
file_list = f1.readlines()
file_list_test = f2.readlines()
f1 = f1.close()
f2 = f2.close()
train_tags = []
train_words = []
train_seg = []
test_tags = []
test_words = []
test_seg = []
for line in file_list:
line = line.strip('\n')
t = line.split('\t',1)#切分标签和标题
train_tags.append(t[0])#将标签提取到Train_tags中
words = jieba.cut(t[1],cut_all=True)#调用结巴分词,这里设置成全模式
train_seg = ' '.join(words)
train_words.append(train_seg)
for line in file_list_test:
line = line.strip('\n')
t = line.split('\t',1)
test_tags.append(t[0])
words = jieba.cut(t[1],cut_all=True)
test_seg = ' '.join(words)
test_words.append(test_seg)
查看标签类型,会发现这里是一串字符,在而进行分类时贝叶斯分类器使用矩阵,因此还需要进行矩阵转化。
x_tags = numpy.asarray(train_tags)
y_tags = numpy.asarray(test_tags)
至此,我们完成了中文文本的预处理,可以计算词频并训练分类器了。
#tfi计算词频
count_vec = TfidfVectorizer(binary = False, decode_error = 'ignore')
x_train = count_vec.fit_transform(train_words)
#x_test = count_vec.transform(doc_test)#return
x_test = count_vec.transform(test_words)#tag
#bayes分类器
clf = MultinomialNB(alpha=0.01)
#clf = BernoulliNB(alpha = 0.01)
clf.fit(x_train, x_tags)
pred = clf.predict(x_test)
m_precision = metrics.precision_score(y_tags, pred)
m_recall = metrics.recall_score(y_tags, pred)
print('precision:{0:.3f}'.format(m_precision))
print('recall:{0:0.3f}'.format(m_recall))
输出结果:
precision:0.704
recall:0.705
伯努利分类器输出结果:
precision:0.714
recall:0.715
计算结果显示贝叶斯分类器精度为0.7,说明中文分类效果也还很好。有了中文文本的分类器我们就可以实现推荐系统了。
其实现可以套用中文处理部分代码,数据集为笔者自己的支付宝数据,然后根据购物体验将其分为good和bad两类。通过对商品标题的特征提取,构建个人购物偏好模型,对于新的商品通过提取其特征判断是否值得购买。具体代码如下:
import scipy
import numpy
from sklearn.cross_validation import train_test_split
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn import metrics
import jieba
from sklearn.metrics import f1_score
from sklearn.metrics import make_scorer
f1 = open("test.txt")
f2 = open("train.txt")
file_list = f1.readlines()
file_list_test = f2.readlines()
f1 = f1.close()
f2 = f2.close()
train_tags = []
train_words = []
train_seg = []
test_tags = []
test_words = []
test_seg = []
#seg_list = jieba.cut(file_list,cut_all=True)
for line in file_list:
line = line.strip('\n')
t = line.split('\t',1)
if(t[1]=='g'):
train_tags.append(1)
else:
train_tags.append(0)
words = jieba.cut(t[0],cut_all=True)
train_seg = ' '.join(words)
train_words.append(train_seg)
for line in file_list_test:
line = line.strip('\n')
t = line.split('\t',1)
if(t[1]=='g'):
test_tags.append(1)
else:
test_tags.append(0)
words = jieba.cut(t[0],cut_all=True)
test_seg = ' '.join(words)
test_words.append(test_seg)
count_vec = TfidfVectorizer(binary = False, decode_error = 'ignore')
#x_tags = numpy.asarray(train_tags)
#y_tags = numpy.asarray(test_tags)
#调用分词
count_vec = TfidfVectorizer(binary = False, decode_error = 'ignore')
x_train = count_vec.fit_transform(train_words)#返回文本向量
#x_test = count_vec.transform(doc_test)#从训练集返回
x_test = count_vec.transform(test_words)#标签为英文
#调用分类器
clf = MultinomialNB(alpha=0.01)
clf.fit(x_train, train_tags)
pred = clf.predict(x_test)
m_precision = metrics.precision_score(test_tags, pred)
m_recall = metrics.recall_score(test_tags, pred)
print('precision:{0:.3f}'.format(m_precision))
print('recall:{0:0.3f}'.format(m_recall))
结果输出:
precision:0.467
recall:0.467
结果显示贝叶斯分类器的精度为0.46,说明分类效果不好。排除了运行中可能出现的错误,我分析导致分类效果不好的原因可能是:
1. 数据集过小。许多商品购买次数仅为一次,而测试集同样可能出现之前不存在的关键字分类器可能根据不重要,权重低词对商品分类。
2. 分词效果不够好。淘宝商品标题语法不规范,无用信息过多。很可能对分词结果产生影响。
给出的可能解决问题的思路是在现有基础上增加聚类分析。扩大数据库范围,并对购买者进行聚类分析,筛选出购买行为相似的用户,将其合并为一个大的数据集。这样可能存在同一种商品多次购买的记录,也可以扩大购买种类,从而有效训练。
完整代码:
# -*- coding: utf-8 -*-
"""
Created on Mon Oct 17 19:29:48 2016
实现中文特征提取
@author: 王艾雪
"""
import scipy
import numpy
from sklearn.cross_validation import train_test_split
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn import metrics
import jieba
f1 = open("train_file.txt")
f2 = open("test_file.txt")
file_list = f1.readlines()
file_list_test = f2.readlines()
f1 = f1.close()
f2 = f2.close()
train_tags = []
train_words = []
train_seg = []
test_tags = []
test_words = []
test_seg = []
#seg_list = jieba.cut(file_list,cut_all=True)
for line in file_list:
line = line.strip('\n')
t = line.split('\t',1)
train_tags.append(t[0])
words = jieba.cut(t[1],cut_all=True)
train_seg = ' '.join(words)
train_words.append(train_seg)
for line in file_list_test:
line = line.strip('\n')
t = line.split('\t',1)
test_tags.append(t[0])
words = jieba.cut(t[1],cut_all=True)
test_seg = ' '.join(words)
test_words.append(test_seg)
x_tags = numpy.asarray(train_tags)
y_tags = numpy.asarray(test_tags)
#调用分词
count_vec = TfidfVectorizer(binary = False, decode_error = 'ignore')
x_train = count_vec.fit_transform(train_words)#返回文本向量
#x_test = count_vec.transform(doc_test)#从训练集返回
x_test = count_vec.transform(test_words)#标签为英文
#调用分类器
clf = MultinomialNB(alpha=0.01)
clf.fit(x_train, x_tags)
pred = clf.predict(x_test)
m_precision = metrics.precision_score(y_tags, pred)
m_recall = metrics.recall_score(y_tags, pred)
print('precision:{0:.3f}'.format(m_precision))
print('recall:{0:0.3f}'.format(m_recall))
基于内容的推荐算法本质上是文本特征提取并利用智能算法进行分类,朴素贝叶斯分类器不仅可以解决分类问题还可以解决个性化推荐问题。而利用内容推荐算法的基本原理还可以实现更多功能,例如通过浏览网页这一行为分析出用户的行为习惯,兴趣爱好。也可以通过一个人的购买记录,从准备的礼物清单中挑选出最优的选项。这里,我对自己喜爱的两位诗人李白、王维的作品进行了分类。在进行摘抄时,由于没有及时摘录作者,再回头翻看时我们并不知道作品作者是谁(假设我们不能通过风格判断)当然,最快的办法是利用搜索引擎,而我在这里运用本文解决问题的思路,实现了一个简单的分类器。随机挑选李白和王维的作品,共58首,将其划分为训练集和测试集,创建一个分类器并对其分类,具体代码如下:
import scipy as sp
import numpy as np
from sklearn.datasets import load_files
from sklearn.cross_validation import train_test_split
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.metrics import precision_recall_curve
from sklearn.metrics import classification_report
import jieba
movie_reviews = load_files('poem')
doc_train, doc_test, y_train, y_test = train_test_split(movie_reviews.data, movie_reviews.target, test_size = 0.2)
#a = type(doc_train)
#b = type(y_train)
train_words = []
train_seg = []
for line in doc_train:
words = jieba.cut(line,cut_all=True)
train_seg = ' '.join(words)
train_words.append(train_seg)
test_words = []
test_seg = []
for line in doc_test:
words = jieba.cut(line,cut_all=True)
test_seg = ' '.join(words)
test_words.append(test_seg)
count_vec = TfidfVectorizer(binary = False, decode_error = 'ignore')
#分词
x_train = count_vec.fit_transform(train_words)
x_test = count_vec.transform(test_words)
x = count_vec.transform(movie_reviews.data)
y = movie_reviews.target
#贝叶斯分类器
clf = MultinomialNB().fit(x_train, y_train)
doc_class_predicted = clf.predict(x_test)
#print(doc_train)
#print(count_vec.get_feature_names())
#print(x_train.toarray())
#print(movie_reviews.target)
#print(np.mean(doc_class_predicted == y_test))
presion, recall, thresholds = precision_recall_curve(y_test, clf.predict(x_test))
answer = clf.predict_proba(x_test)[:,1]
report = answer > 0.5
print(classification_report(y_test, report, target_names = ['li', 'wang']))
输出结果:
precision recall f1-score support
li 0.80 0.67 0.73 6
wang 0.71 0.83 0.77 6
total 0.76 0.75 0.75 12
伯努利分类器输出结果:
precision recall f1-score support
li 1.00 0.25 0.40 4
wang 0.73 1.00 0.84 8
avg / total 0.82 0.75 0.69 12
由于数据集较小,准确率在0.6到0.8不等。分类效果较为良好。而如果能够增加数据集(李白存世作品900余首,王维存世作品400余首)相信能有更为准确的分类。而我在实现该分类器的过程中发现“荒/城/临/ 古/渡/落日/满/秋/山”。这样的单字划分,这可能与结巴分词组件并被收录与唐诗相关的字典导致,但这并不影响分类结果。可能对于这两位诗人来说,他们也有习惯用字吧!