最常见应用场景:
1.文本分类/垃圾文本过滤/情感判别:在文本分类场景中,朴素贝叶斯依旧坚挺地占据着一席之地。因为多分类很简单,同时在文本数据中,分布独立这个假设基本是成立的。而垃圾文本过滤(比如垃圾邮件识别)和情感分析(微博上的褒贬情绪)用朴素贝叶斯也通常能取得很好的效果。
2.多分类实时预测:对于文本相关的多分类实时预测,它因为上面提到的优点,被广泛应用,简单又高效。
3.推荐系统:朴素贝叶斯和协同过滤(Collaborative Filtering)是一对好搭档,协同过滤是强相关性,但是泛化能力略弱,朴素贝叶斯和协同过滤一起,能增强推荐的覆盖度和效果。
需要注意点:
1.如果连续性特征不具有正态分布,则应采用变换或不同的方法将其转换为正态分布。
2.如果测试数据集具有零频率问题,则应用平滑技术“拉普拉斯修正”来预测测试数据集的类别。
3.删除相关特征,因为高度相关的特征在模型中被投票两次,并且可能导致过度膨胀的重要性。
将训练数据集中的每条短信都拆分为独立的单词,存在一个变量中,对于要分类的短信同样也拆分为单词。这些单词来表示短信的特征。
第一列表示短信是否为垃圾短信,spam表示垃圾短信,ham表示正常短信;第二列是具体的短信内容。
R语言中的klaR包就提供了朴素贝叶斯算法实现的函数NaiveBayes,我们来看一下该函数的用法及参数含义:
NaiveBayes(formula, data, ..., subset, na.action= na.pass)
NaiveBayes(x, grouping, prior, usekernel= FALSE, fL = 0, ...)
formula指定参与模型计算的变量,以公式形式给出,类似于y=x1+x2+x3;
data用于指定需要分析的数据对象;
na.action指定缺失值的处理方法,默认情况下不将缺失值纳入模型计算,也不会发生报错信息,当设为“na.omit”时则会删除含有缺失值的样本;
x指定需要处理的数据,可以是数据框形式,也可以是矩阵形式;
grouping为每个观测样本指定所属类别;
prior可为各个类别指定先验概率,默认下用各个类别的样本比例作为先验概率;
usekernel指定密度估计的方法(在无法判断数据的分布时,采用密度密度估计方法),默认下使用正态分布密度估计,设为TRUE,则使用核密度估计方法;
fL指定是否进行拉普拉斯修正,默认情况下不对数据进行修正,当数据量较小时,可以设置该参数为1,即进行拉普拉斯修正。
在本例中使用e1701包进行分析
补充:(未实现)
聚类分析
```{r}
#词频过滤,筛选至多出现10次的词
findFreqTerms(sms_dtm,lowfreq=0,highfreq=10);
#词语之间的相关性计算
findAssocs(sms_dtm,"winter",0.5)
#因为生成的矩阵是一个稀疏矩阵,再进行降维处理,之后转为标准数据框格式
#sparse为最大允许稀疏性的词,高于的将被删除
dtm2<-removeSparseTerms(sms_dtm,sparse=0.95)
data1<-as.data.frame(inspect(dtm2))
#之后就可以利用R语言中任何工具加以研究了,层次聚类
#先进行标准化处理,再生成距离矩阵,再用层次聚类
data.scale<-scale(data1)
d<-dist(data.scale,method="euclidean")
fit<-hclust(d,method="ward")
#绘制聚类图
plot(fit)
正文:
一般来说一个完整的文本挖掘解决流程是:
网页爬取数据——数据格式转化(分隔)——建立语料库——词频去噪——提取词干——创建文档——聚类分析——词频矩阵——词云分析——数据再处理——训练模型——评估模型性能——提升模型性能
读取数据及格式转换
data<-read.csv("F:\\简书\\SMSSpamCollection.csv",header=F,stringsAsFactors=FALSE)
#重新设置列名时要在前面加上data<-,否则最后列名仍没有被修改
data<-setNames(data,c("type","text"))
str(data)
head(data,3)
#将分类变量因子化
data$type<-as.factor(data$type)
attach(data)
a<-table(type);prop.table(a)
可利用管道函数方便书写
#用于加载管道函数
library(magrittr)
table(type) %>% prop.table()
其中有86.59%左右的短信是正常短信,有13.40%的短信为垃圾短信。下面分别看一下垃圾短信与正常短信的云图,看看二者在文本内容上是否有显著区别
建立语料库
#加载文本挖掘包
library(tm)
#创建语料库,VectorSource():输入文本构成的向量,目的是创建矢量源
data_corpus<-VCorpus(VectorSource(data$text))
data_corpus
可以使用print()和summary()查看语料库的部分信息。而完整信息的提取则需要使用inspect()函数
print(data_corpus)
inspect(data_corpus[1:3])
为了进行分词分析,用tm_map对语料库文件进行预处理,我们需要将这些短信划分成单个单词,但是我们要先清理文本,去除标点符号,将其转为纯文本并去除多余空格,转换小写,去除常用词汇,合并异形同义词汇.通过使用map的方式将转化函数应用到每一个语料上
词频去噪
library(SnowballC)
#PlainTextDocument对象,处理为纯文本
corpus_clean<-tm_map(data_corpus, PlainTextDocument)
#大小写转换
corpus_clean<-tm_map(corpus_clean, tolower)
#再除去数字,由于数字基本上不会提供有用的信息
corpus_clean<-tm_map(corpus_clean, removeNumbers)
#去除标点符号
corpus_clean<-tm_map(corpus_clean, removePunctuation)
#消除空格
corpus_clean<-tm_map(corpus_clean, stripWhitespace)
#再去除像to、and、but等出现次数较多,但是又没什么实际价值的词,这些词我们可以作为停用词去除
corpus_clean<-tm_map(corpus_clean, removeWords, stopwords())
#新增停用词
stopwordVector<-c("supplier","order")
#去掉新增停用词
corpus_clean<-tm_map(corpus_clean, removeWords, stopwordVector)
提取词干
#去词干化.词干化,即词干提取.指的是去除词缀得到词根的过程:得到单词最一般的写法.如以单复数等多种形式存在的词,或多种时态形式存在的同一个词,它们代表的其实是同一个意思.因此需要通过词干化将它们的形式进行统一
corpus_clean<-tm_map(corpus_clean,stemDocument)
创建文档
#将处理后的语料库进行断字处理,生成词频权重矩阵(稀疏矩阵)也叫词汇文档矩阵 行表示文档,列表示单词,矩阵单元格表示由列标识的单词出现在由行标识的文档中的次数
#先转化为纯文本文件,去除标签
sms_corpus_clean<-tm_map(corpus_clean,PlainTextDocument)
sms_dtm<-DocumentTermMatrix(sms_corpus_clean)
#查看词汇文档矩阵内容
inspect(sms_dtm[1:5, 100:105])
Non-/sparse entries: 非0/是0
Sparsity : 稀疏性 稀疏元素占全部元素的比例
Maximal term length: 切词结果的字符最长那个的长度
Weighting : term frequency (tf)---词频率 `
DocumentTermMatrix生成的矩阵是文档-词频的稀疏矩阵,横向是文档文件,纵向是分出来的词,矩阵里面代表词频
为评估naive bayes的性能,将数据集分成训练集和测试集,75%的数据用于训练,25%的数据用于评估算法的性能
createDataPartition则来自于caret包,该函数通过对数据进行抽样,保证训练集与测试集中,垃圾短信的比例一致,避免训练集中出现大量的正常短信,而几乎没有垃圾短信这样的情况。createDataPartition 的第一个参数是vector,函数根据这个参数内容进行抽样,p=0.75表示75%的数据进入训练集,则有25%的数据进入测试集,list=FASLE表示返回结果的格式为常规的数组,否则将返回一个列表
词频矩阵
library(caret)
set.seed(1071)
train_index <- createDataPartition(data$type, p = 0.75, list = FALSE)
# 创建测试数据集和训练数据集
sms_raw_train <- data[train_index, ]
sms_raw_test <- data[-train_index, ]
下面看看训练集与测试集中spam/ham邮件的分布情况,通过上述的抽样,训练集与测试集中邮件分布一致
library(magrittr)
table(sms_raw_train$type) %>% prop.table()
table(sms_raw_test$type) %>% prop.table()
然后是文本-单词矩阵
sms_dtm_train <- sms_dtm[train_index, ]
sms_dtm_test <- sms_dtm[-train_index,]
最后得到语料库
corpus_train <- sms_corpus_clean[train_index]
corpus_test <- sms_corpus_clean[-train_index]
词云分析
#如果一个包不存在,执行到library将会停止执行,require则会继续执行。
#require将会根据包的存在与否返回true或者false
require(dplyr)
require(wordcloud)
require(RColorBrewer)
##调用brewer.pal包里面的dark2调色板,从中取出8个颜色
pal<-brewer.pal(8, "Dark2")
函数:
wordcloud(words,freq,scale=c(4,.5),min.freq=3,max.words=Inf,random.order=TRUE, random.color=FALSE, rot.per=.1,colors="black",ordered.colors=FALSE,use.r.layout=FALSE,...)
常用参数:
(1)words——关键词列表
(2)freq——关键词对应的词频列表
(3)scale——字号列表。c(最大字号,最小字号)
(4)min.freq——最小限制频数。低于此频数的关键词将不会被显示。
(5)max.words——限制词云图上关键词的数量。最后出现在词云图上的关键词数量不超过此限制。
(6)random.order——控制关键词在图上的排列顺序。T:关键词随机排列;F:关键词按频数从图中心位置往外降序排列,即频数大的词出现在中心位置。
(7)random.color——控制关键词的字体颜色。T:字体颜色随机分配;F:根据频数分配字体颜色。
(8)rot.per——控制关键词摆放角度。T:水平摆放;F:旋转90度。
(9)colors——字体颜色列表
(10)ordered.colors——控制字体颜色使用顺序。T:按照指定的顺序给出每个关键词字体颜色,(似乎是要求颜色列表中每个颜色一一对应关键词列表);F:任意给出字体颜色。
(11)use.r.layout=T;F
wordcloud(sms_corpus_clean, scale=c(3, 0.5),min.freq=10, min.words = 10, random.order=FALSE, rot.per=.15, colors=pal)
wordcloud(corpus_train , min.freq = 40, random.order = FALSE, rot.per=.15, colors=pal)
训练数据区分垃圾邮件和非垃圾邮件
spam <- subset(sms_raw_train, type == "spam")
ham <- subset(sms_raw_train, type == "ham")
分别查看垃圾邮件和非垃圾邮件词云图,如果需要保存图片采用png方法
#--png(file = "/Users/chenyangang/01.png", bg = "transparent")
#--dev.off()
wordcloud(spam$text, max.words = 40, scale = c(3, 0.5), random.order = FALSE, rot.per=.15, colors=pal)
wordcloud(ham$text, max.words = 40, scale = c(3, 0.5), random.order = FALSE, rot.per=.15, colors=pal)
数据再处理
可以删除出现次数过少的单词,这些单词出现较少,删除这些单词对预测的结果没有(估计)影响
删除词频少于5的单词,剩下的单词做为后续构建dtm的单词表,其实是字符串列表,是那些出现频次超过5次的单词
dtm <- findFreqTerms(sms_dtm, 5)
根据前文构建的单词表,重新构建训练集和测试集的dtm矩阵
dtm_train <- DocumentTermMatrix(
corpus_train, control = list(dictionary = dtm)
)
dtm_test <- DocumentTermMatrix(
corpus_test, control = list(dictionary = dtm)
)
在naive bayes的算法中,计算的是单词表中,每个单词出现与否的概率,上述产生的dtm矩阵记录的是每条短信中,每个单词的出现次数,因此需要做进一步的转换。若出现次数大于0次,表明该单词出现在短信中
转换为因子变量
convert_counts <- function(x) {
x <- ifelse(x > 0, "Yes", "No")
}
#将训练数据和测试数据按列转换为因子变量
sms_train <- apply(dtm_train, MARGIN = 2, convert_counts)
sms_test <- apply(dtm_test, MARGIN = 2, convert_counts)
通过上述的方法,得到一个跟dtm矩阵同样大小的矩阵(因为使用了apply,sms_train是普通的矩阵),且各元素是字符“Yes”或者“No”
训练模型
下面调用e1071中的naive bayes函数,对训练数据进行模型训练,得到模型nb_model_0,并在测试集上使用该模型预测
函数:
m <- naiveBayes(train, class, laplace = 0):函数返回一个朴素贝叶斯对象,该对象能够用于预测
train: 数据框或包含训练数据的矩阵
class: 包含训练数据的每一行的分类的一个因子向量
laplace: 控制拉普拉斯估计的一个数值(默认为0)
预测:
p <- predict(m, test, type = "class"):该函数返回一个向量,根据参数type的值,该向量含有预测的类别值或者原始的预测概率
m:由naiveBayes(train, class, laplace = 0) 训练的模型对象
test:数据框或包含测试数据的矩阵,包含用来建立分类器的训练数据相同的特征
type:值为“class”或“raw”,标示预测是最可能的类别值或者原始的预测概率
library(e1071)
sms_classifier <- naiveBayes(sms_train, sms_raw_train$type)
sms_classifier
......
评估模型性能
sms_test_pred <- predict(sms_classifier, sms_test)
library(gmodels)
CrossTable(sms_test_pred, sms_raw_test$type,
prop.chisq = TRUE, prop.t = TRUE, prop.r = TRUE,
dnn = c('predicted', 'actual'))
提升模型性能(应用拉普拉斯估计:本质是给频率数的每个计数加上一个较小的数)
sms_classifier2 <- naiveBayes(sms_train, sms_raw_train$type, laplace = 1)
sms_test_pred2 <- predict(sms_classifier2, sms_test)
CrossTable(sms_test_pred2, sms_raw_test$type,
prop.chisq = FALSE, prop.t = FALSE, prop.r = FALSE,
dnn = c('predicted', 'actual'))
使用naive bayes预测垃圾短信,有(1201+164)/1392 = 98.06%的短信被正确分类