[jieba + spark] 使用R语言进行自然语言处理与机器情感认知

前言

  自然语言处理是机器理解人类情感的第一步,今天就让我们运用R语言,通过两款强大的工具——做中文分词的jieba、做大数据运算的spark,来处理自然语言,并从中提取出语言想要表达的情感。
  该项目用到的资源及R语言代码已被我打包上传,感兴趣的朋友可以点击这里获取资源。

载入需要用到的工具包

library(jiebaR)
library(cidian)
library(Matrix)
library(dplyr)
library(DBI)
library(sparklyr)
library(gmodels)


数据预处理

  首先我们从网上搜集到了一份关于顾客购买手表之后对商家的评分与评论的数据集,这份数据集共有10万余条记录数,但只有两个字段——stars、contents,分别表示顾客对相应商家的评分(1-5星)与评论内容。我们最终的目标,就是要通过顾客的评论来预测顾客的满意度(评分等级)。
[jieba + spark] 使用R语言进行自然语言处理与机器情感认知_第1张图片

   在该数据集中,有些顾客是认真评价的,当然也有些顾客不知道该如何评价随便写了几个字的、或者未来得及评论被系统默认好评的,这些评论会给我们之后的建模预测带来很大的不确定性。所以我们需要对数据集进行预处理,删除掉评论中的非中文字符,删除默认好评或评论不足10个字的观测值,最后我们需要将顾客的评分stars转换为满意度satisfaction:将1星与2星评论视为不满意,4星与5星评论视为满意。

comments <- read.csv('comments_raw.csv', stringsAsFactors = F)

removePattern <- function(corpus, pattern)gsub(pattern, '', corpus)
comments$contents <- removePattern(comments$contents, '[0-9a-zA-Z]')
comments$contents <- removePattern(comments$contents, '[[:punct:]]')
comments$contents <- removePattern(comments$contents, '[[:space:]]')

comments <- comments %>% 
  filter(!grepl('此用户未\\w+填写评价内容', contents)) %>% 
  filter(nchar(contents) > 10) %>% 
  filter(stars != 3) %>% 
  mutate(satisfaction = if_else(stars > 3, 'yes', 'no')) %>% 
  select(-stars)


分词

  接下来我们需要用到jiebaR工具包,对数据集中的评论内容进行分词。jiebaR自带的分词字典已经十分强大了,但是在处理一些专有词汇方面还可以提升。如下所示,我们使用jiebaR默认的分词字典时,“阿迪板鞋”被分成了两个词。同时,类似“我”、“的”这样的词汇在评论内容中会经常出现,但却与用户的满意度关联不大。
no_dict

  为了解决这些问题,我们从搜狗输入法的字典库中下载了一份”淘宝专用词库”,要将该词库转化为jiebaR所能识别的字典文件,我们需要用到cidian工具包中的decode_scel命令。同时我们还从网上搜集到了一份停止词字典。我们可以通过R语言自带的scan命令来查看两个字典。

decode_scel(scel = "淘宝专用词库.scel", cpp = TRUE, output = 'user.txt')

[jieba + spark] 使用R语言进行自然语言处理与机器情感认知_第2张图片
[jieba + spark] 使用R语言进行自然语言处理与机器情感认知_第3张图片

  接下来,我们使用自定义的用户字典与停止词字典,可以看到,“阿迪板鞋”在用户字典中,所以分词后被分成了一个词语,“我”、“的”在停止词字典中 ,所以分词后被忽略。当然,这里我们的停止词字典还不够十分强大,一个理想的停止词字典需要帮助我们删除所有与预测目标(用户满意度)无关的单词,这样会在之后的建模运算中节省很大一部分算力。
use_dict

  接下来对每条评论逐一进行分词,记录评论所在的行号,从评论中提取到的单词,以及这些单词出现的频率。

rowID <- colID <- values <- vector()
for(i in 1:nrow(comments)){
  words <- wk[comments$contents[i]]
  words_freq <- freq(words)
  rowID <- append(rowID, rep(i, nrow(words_freq)))
  colID <- append(colID, words_freq$char)
  values <- append(values, words_freq$freq)
}


数据清洗

  接下来,我们需要对数据进行最后一步清洗,使数据能够符合建模所需求的格式。
  理论上我们可以通过命令data.frame(rowID, colID, values),将上一步提取到的三个向量合并为一个长形数据,再通过reshape转制为我们需要的宽型数据(参考这篇帖子中对长形数据与宽型数据的描述)。但实际上取决于电脑性能,对如此大型的数据集进行转制可能导致R语言崩溃。因此我们需要将先将数据转换为分散矩阵,再将分散矩阵转化为常规数据集。
  同时我们需要删除掉出现频率小于15次的单词与评论词汇不足5个的观测值,这些数据对于优化模型贡献不大,并且还会浪费大量计算资源。

# 将colID修改为数值
colID <- as.factor(colID)
colLables <- levels(colID)
colID <- as.numeric(colID)

# 删除掉出现频率小于15次的单词与评论词汇不足5个的观测值
rowID_freq <- table(rowID)
row_subset <- as.numeric(names(rowID_freq[rowID_freq > 5]))

colID_freq <- table(colID)
col_subset <- as.numeric(names(colID_freq[colID_freq > 15]))

# 将分词后的数据转换为分散矩阵
comments_words <- sparseMatrix(rowID, colID, x = values)
#                              dimnames = list(NULL, colLables))
# 将分散矩阵转换为常规矩阵
comments_words <- as.matrix(contents_words[row_subset, col_subset])
# 汇总成最终版本的数据集
comments <- data.frame(satisfaction = comments$satisfaction[row_subset], contents_words)


  我们用colLabels记录了分散矩阵的中文列名 (准确来说应该是colLabels[col_subset])。在最终的模型中,我们就是通过这些词汇来预测用户是否满意的,比如,假设用户评论中出现了”不尽人意”等词汇,可以推导出用户对本次购物不满意,相反假设用户评论中出现了”不可多得”等词汇,则可以推导出用户对本次购物是满意的。当然除了这些明确表示态度的词汇,colLabels中还含有大量中性词汇、无关词汇、意义不明的词汇等,这主要是由于之前自然语言处理的过程还不够精细化,但在大数据面前这些“噪声”影响并不是很大。
[jieba + spark] 使用R语言进行自然语言处理与机器情感认知_第4张图片

使用spark训练并测试数据

  最后我们需要调用spark训练数据集。这里我们使用了 Naive Bayes朴素贝叶斯模型,该模型具有如下假设:

P(A|B)=P(AB)P(B)=P(B|A)×P(A)P(B) P ( A | B ) = P ( A ∩ B ) P ( B ) = P ( B | A ) × P ( A ) P ( B )

比如在某次评论内容中出现了两个词语 X1 X 1 =”非常”, X2 X 2 =”不喜欢”, Y Y 表示事件satisfaction=”yes”, N N 表示事件satisfaction=”no”,则有:
P1=P(Y|X1X2)=P(X1|Y)×P(X2|Y)×P(Y)P(X1)×P(X2) P 1 = P ( Y | X 1 ∩ X 2 ) = P ( X 1 | Y ) × P ( X 2 | Y ) × P ( Y ) P ( X 1 ) × P ( X 2 )

P2=P(N|X1X2)=P(X1|N)×P(X2|N)×P(N)P(X1)×P(X2) P 2 = P ( N | X 1 ∩ X 2 ) = P ( X 1 | N ) × P ( X 2 | N ) × P ( N ) P ( X 1 ) × P ( X 2 )

其中等式右边的所有值都可以通过先验概率获得,而且 P1 P 1 即理论上 X1 X 1 X2 X 2 两个词汇同时存在时,satisfaction=”yes”的概率。但是,此时我们预测出来的概率 P1+P2 P 1 + P 2 不一定等于1,而实际场景中我们需要预测结果satisfaction=”yes”与satisfaction=”no”的概率是非此即彼的关系,因此预测结果satisfaction=”yes”的概率实际上等于 Y1Y1+Y2 Y 1 Y 1 + Y 2 ,同样的satisfaction=”yes”的概率实际上等于 Y2Y1+Y2 Y 2 Y 1 + Y 2

# 连接spark
sc <- spark_connect(master = 'local')
# 将数据拷贝进spark内存
comments_spark <- sdf_copy_to(sc, comments, 'comments')
# 将数据分割为训练集与测试集
comments_partition <- sdf_partition(comments_spark, training = 0.9, test = 0.1)
# 使用ml_naive_bayes建模
ml_model <- comments_partition$training %>% 
  ml_naive_bayes(satisfaction~.)
# 预测
comments_predict <- sdf_predict(comments_partition$test, ml_model)
# 为预测结果分配Hive表格
sdf_register(comments_predict, 'comments_predict')


  通过以下命令查看最终版数据集的行列数,可以看出数据集共有5万余行、近3千冽,其中只有satisfaction一列为预测目标,其余均为影响因素,与上文提到的colLabels[col_subset]对应。
comments_dims

  通过spark自带的模型评估方法,可以看出模型在测试集上的正确率达到了94%。
accuracy

  查看混淆矩阵,可以发现预测结果无论在satisfaction取值为“yes”或是“no”时,预测正确率都远远大于失误率,模型不存在显著偏差。
[jieba + spark] 使用R语言进行自然语言处理与机器情感认知_第5张图片

结语

  总而言之,我们证明了通过用户评论来预测用户满意度的方法是可行的。当然我们的预测目标也可以不仅限于满意度,比如通过获取用户的一段语言,预测用户是否开心、是否愤怒、是否心急等。当然,用户在表达不同的情感时会采用不同的词汇,相对应的,预测用户不同情感的模型需要挖掘出相应的词汇。



你可能感兴趣的:([R语言],数据分析)