You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何NRC情感分析对负面评论误判为正面情绪?求解决方案

问题

我正在对某Yelp餐厅的评论做情感分析,该餐厅90%以上的评论都是负面评价,但用NRC词典(通过syuzhet包)分析后,结果却持续显示正面情绪占优。我想知道:

  • 是否遗漏了关键的数据预处理步骤?
  • 代码是否存在逻辑问题?
  • 如何才能得到更准确的情感分析结果?

另外我曾寻找可绘制多种情感图表的包,目前只找到syuzhet具备该功能。

我的代码如下:

library(syuzhet)
library(plotly)

df <- read.csv('reviews.csv')

sentReviews <- iconv(df$Text)

# 获取NRC词典情感标注
nrc_emotions <- get_nrc_sentiment(sentReviews)
head(nrc_emotions)

# 基于列求和构建情感统计数据框
emo_bar = colSums(nrc_emotions)
emo_sum = data.frame(count=emo_bar, emotion=names(emo_bar))
emo_sum

# 按计数从高到低排序情感类别
emo_sum$emotion = factor(emo_sum$emotion, levels=emo_sum$emotion[order(emo_sum$count, decreasing = TRUE)])

# 绘制情感分布柱状图
plot_ly(emo_sum, x=~emotion, y=~count, type="bar", color=~emotion) %>%
  layout(xaxis=list(title=""), showlegend=FALSE,
         title="Distribution of emotion categories")

运行后得到的情感分布图表显示正面、信任等积极情感的计数远高于负面、愤怒等消极情感,与实际评论倾向完全相反。

解决方案

问题根源

  1. NRC词典的局限性:NRC基于单个词汇的情感标注,完全忽略上下文逻辑。例如not good中的good会被标注为正面,但实际表达负面情绪;同时部分词汇会被标注为多种情感(如disappointed同时对应悲伤和负面),直接对所有评论的情感列求和会放大这类偏差。
  2. 缺失关键预处理步骤:原始文本未做清洗,包含大量无情感价值的停用词、标点、特殊字符,干扰情感统计;也未处理not这类能反转情感的否定结构。
  3. 统计逻辑错误:直接对所有评论的情感列求和,统计的是所有词汇的情感出现次数,而非单条评论的整体情感倾向,这会导致被否定的正面词仍被计入正面情感,最终结果失真。

改进步骤

1. 完善数据预处理

添加文本清洗和否定词处理,示例代码:

library(tidytext)
library(dplyr)
library(stringr)

# 读取本地评论数据
df <- read.csv('reviews.csv')

# 文本预处理流程
df$clean_text <- df$Text %>%
  str_to_lower() %>% # 统一转为小写
  str_remove_all("[^a-zA-Z\\s]") %>% # 移除标点、数字、特殊字符
  str_replace_all("\\b(not|no|never|hardly|scarcely)\\s+(\\w+)", "\\1_\\2") %>% # 处理否定词,如not good转为not_good
  str_squish() # 移除多余空格

# 移除停用词(如the、and这类无情感词汇)
data(stop_words)
tidy_reviews <- df %>%
  unnest_tokens(word, clean_text) %>%
  anti_join(stop_words)

2. 改进情感分析逻辑

方式一:优化NRC词典的使用逻辑

先计算单条评论的净情感,再汇总整体分布:

# 获取NRC情感词典
nrc_dict <- get_sentiments("nrc")

# 匹配情感并计算每条评论的情感得分
review_sentiments <- tidy_reviews %>%
  inner_join(nrc_dict) %>%
  count(Text, sentiment) %>%
  pivot_wider(names_from = sentiment, values_from = n, values_fill = 0) %>%
  mutate(
    net_positive = positive - negative, # 计算净正面得分
    overall_sentiment = case_when(
      net_positive > 0 ~ "positive",
      net_positive < 0 ~ "negative",
      TRUE ~ "neutral"
    )
  )

# 统计整体情感分布
sentiment_dist <- review_sentiments %>%
  count(overall_sentiment) %>%
  mutate(prop = n / sum(n))

# 绘制整体情感分布柱状图
plot_ly(sentiment_dist, x=~overall_sentiment, y=~n, type="bar", color=~overall_sentiment) %>%
  layout(title="评论整体情感分布", xaxis=list(title=""), showlegend=FALSE)
方式二:使用更适合评论场景的VADER词典

VADER专门针对短文本/评论设计,能识别否定词、程度词、表情符号,情感判断更精准:

# 安装并加载VADER包
# install.packages("vader")
library(vader)

# 计算每条评论的VADER情感得分
df$vader_scores <- lapply(df$Text, get_vader)
df$compound_score <- sapply(df$vader_scores, function(x) x$compound)

# 根据复合得分划分情感类别
df$overall_sentiment <- case_when(
  df$compound_score >= 0.05 ~ "positive",
  df$compound_score <= -0.05 ~ "negative",
  TRUE ~ "neutral"
)

# 统计并绘制情感分布
sentiment_dist <- df %>% count(overall_sentiment)
plot_ly(sentiment_dist, x=~overall_sentiment, y=~n, type="bar", color=~overall_sentiment) %>%
  layout(title="VADER情感分析结果", xaxis=list(title=""), showlegend=FALSE)

3. 替代绘图方案

如果需要绘制多维度情感图表,tidytext结合ggplot2也能实现,例如NRC的8种情感分布:

library(ggplot2)

# 统计所有评论的NRC情感词出现次数
nrc_emotion_counts <- tidy_reviews %>%
  inner_join(nrc_dict) %>%
  count(sentiment) %>%
  arrange(desc(n))

# 绘制情感分布条形图
ggplot(nrc_emotion_counts, aes(x=reorder(sentiment, n), y=n, fill=sentiment)) +
  geom_col() +
  coord_flip() +
  labs(title="NRC情感词分布", x="情感类别", y="出现次数") +
  theme_minimal() +
  theme(legend.position="none")

关键总结

  • 必须处理文本中的否定词并清洗无关内容,否则词典会误判情感倾向
  • 避免直接对所有评论的情感列求和,应先计算单条评论的整体情感再汇总
  • 针对短评论场景,VADER的准确性优于NRC;若坚持使用NRC,需结合上下文处理逻辑

内容的提问来源于stack exchange,提问作者bandcar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 06:15:46