在人工智能快速发展的今天,情绪分析(Emotion Analysis)已成为人机交互、心理健康监测、客户服务等领域的重要技术。通过AI分析人的情绪,我们可以从文本、语音、面部表情等多种模态中提取情感信息。本文将重点介绍基于文本的情绪分析,并提供完整的Python代码实现。

情绪分析的核心方法

目前主流的情绪分析方法包括:

  1. 基于词典的方法:使用情感词典匹配文本中的情感词

  2. 传统机器学习:使用SVM、朴素贝叶斯等算法

  3. 深度学习:使用LSTM、BERT等预训练模型

本文将实现后两种方法,让读者理解从基础到前沿的完整流程。

环境准备

首先安装必要的库:

bash

pip install pandas numpy scikit-learn torch transformers nltk matplotlib seaborn

方法一:基于传统机器学习的情绪分析

1. 数据准备

使用NLTK自带的电影评论数据集:

python

import nltk
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.svm import SVC
from sklearn.metrics import classification_report, confusion_matrix
import matplotlib.pyplot as plt
import seaborn as sns

# 下载必要的数据
nltk.download('movie_reviews')
nltk.download('stopwords')
nltk.download('punkt')

from nltk.corpus import movie_reviews, stopwords
from nltk.tokenize import word_tokenize

# 加载数据
documents = [(list(movie_reviews.words(fileid)), category)
             for category in movie_reviews.categories()
             for fileid in movie_reviews.fileids(category)]

# 转换为DataFrame
data = []
for words, sentiment in documents:
    data.append({
        'text': ' '.join(words),
        'sentiment': 1 if sentiment == 'pos' else 0  # 1:正面, 0:负面
    })

df = pd.DataFrame(data)
print(f"数据集大小: {len(df)}")
print(df['sentiment'].value_counts())

2. 特征工程与模型训练

python

# 文本预处理函数
def preprocess_text(text):
    # 转为小写
    text = text.lower()
    # 分词
    tokens = word_tokenize(text)
    # 去除停用词和标点
    stop_words = set(stopwords.words('english'))
    tokens = [token for token in tokens if token.isalpha() and token not in stop_words]
    return ' '.join(tokens)

# 应用预处理
df['processed_text'] = df['text'].apply(preprocess_text)

# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(
    df['processed_text'], df['sentiment'], 
    test_size=0.2, random_state=42, stratify=df['sentiment']
)

# TF-IDF特征提取
tfidf = TfidfVectorizer(max_features=5000, ngram_range=(1, 2))
X_train_tfidf = tfidf.fit_transform(X_train)
X_test_tfidf = tfidf.transform(X_test)

# 训练SVM分类器
svm_model = SVC(kernel='linear', C=1.0, random_state=42)
svm_model.fit(X_train_tfidf, y_train)

# 预测与评估
y_pred = svm_model.predict(X_test_tfidf)
print("\n=== SVM模型评估结果 ===")
print(classification_report(y_test, y_pred, target_names=['负面', '正面']))

# 混淆矩阵可视化
cm = confusion_matrix(y_test, y_pred)
plt.figure(figsize=(8, 6))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', 
            xticklabels=['负面', '正面'], yticklabels=['负面', '正面'])
plt.title('SVM情绪分析混淆矩阵')
plt.xlabel('预测标签')
plt.ylabel('真实标签')
plt.show()

方法二:基于深度学习(BERT)的情绪分析

BERT模型能够理解上下文语义,效果远超传统方法。

1. 使用预训练的BERT模型

python

import torch
from transformers import BertTokenizer, BertForSequenceClassification
from torch.utils.data import DataLoader, TensorDataset
from torch.optim import AdamW
from tqdm import tqdm

# 检查GPU
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print(f"使用设备: {device}")

# 加载BERT模型和分词器
model_name = 'bert-base-uncased'
tokenizer = BertTokenizer.from_pretrained(model_name)
bert_model = BertForSequenceClassification.from_pretrained(model_name, num_labels=2)
bert_model.to(device)

# 数据预处理
def encode_texts(texts, labels=None, max_length=128):
    encodings = tokenizer(
        texts.tolist(), 
        truncation=True, 
        padding=True, 
        max_length=max_length,
        return_tensors='pt'
    )
    if labels is not None:
        return encodings, torch.tensor(labels.tolist())
    return encodings

# 编码数据
train_encodings, train_labels = encode_texts(X_train, y_train)
test_encodings, test_labels = encode_texts(X_test, y_test)

# 创建DataLoader
batch_size = 16
train_dataset = TensorDataset(train_encodings['input_ids'], 
                              train_encodings['attention_mask'], 
                              train_labels)
test_dataset = TensorDataset(test_encodings['input_ids'],
                             test_encodings['attention_mask'],
                             test_labels)

train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=batch_size)

# 优化器
optimizer = AdamW(bert_model.parameters(), lr=2e-5)

# 训练函数
def train_epoch(model, loader, optimizer):
    model.train()
    total_loss = 0
    for batch in tqdm(loader, desc="训练中"):
        input_ids, attention_mask, labels = [b.to(device) for b in batch]
        optimizer.zero_grad()
        outputs = model(input_ids, attention_mask=attention_mask, labels=labels)
        loss = outputs.loss
        total_loss += loss.item()
        loss.backward()
        optimizer.step()
    return total_loss / len(loader)

# 评估函数
def evaluate(model, loader):
    model.eval()
    predictions = []
    actuals = []
    with torch.no_grad():
        for batch in tqdm(loader, desc="评估中"):
            input_ids, attention_mask, labels = [b.to(device) for b in batch]
            outputs = model(input_ids, attention_mask=attention_mask)
            preds = torch.argmax(outputs.logits, dim=1)
            predictions.extend(preds.cpu().numpy())
            actuals.extend(labels.cpu().numpy())
    return predictions, actuals

# 训练模型
epochs = 3
for epoch in range(epochs):
    print(f"\n第 {epoch+1}/{epochs} 轮")
    loss = train_epoch(bert_model, train_loader, optimizer)
    print(f"训练损失: {loss:.4f}")
    
    # 每轮结束后评估
    y_pred_bert, y_true_bert = evaluate(bert_model, test_loader)
    from sklearn.metrics import accuracy_score
    acc = accuracy_score(y_true_bert, y_pred_bert)
    print(f"测试准确率: {acc:.4f}")

# 最终评估
print("\n=== BERT模型评估结果 ===")
print(classification_report(y_true_bert, y_pred_bert, target_names=['负面', '正面']))

方法三:实时情绪分析API

将训练好的模型封装成API服务:

python

from flask import Flask, request, jsonify
import torch
from transformers import BertTokenizer, BertForSequenceClassification

app = Flask(__name__)

# 加载模型(假设已保存)
# torch.save(bert_model.state_dict(), 'emotion_model.pth')
# bert_model.load_state_dict(torch.load('emotion_model.pth'))

# 简单版本:使用预训练模型进行推理
class EmotionAnalyzer:
    def __init__(self):
        self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
        self.tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
        self.model = BertForSequenceClassification.from_pretrained('bert-base-uncased', num_labels=2)
        self.model.to(self.device)
        self.model.eval()
    
    def predict(self, text):
        encoding = self.tokenizer(
            text, 
            truncation=True, 
            padding=True, 
            max_length=128,
            return_tensors='pt'
        )
        encoding = {k: v.to(self.device) for k, v in encoding.items()}
        
        with torch.no_grad():
            outputs = self.model(**encoding)
            pred = torch.argmax(outputs.logits, dim=1).item()
        
        sentiment = "正面" if pred == 1 else "负面"
        confidence = torch.softmax(outputs.logits, dim=1)[0][pred].item()
        
        return {
            'text': text,
            'sentiment': sentiment,
            'confidence': round(confidence, 4),
            'score': pred
        }

analyzer = EmotionAnalyzer()

@app.route('/analyze', methods=['POST'])
def analyze():
    data = request.json
    text = data.get('text', '')
    if not text:
        return jsonify({'error': '请提供文本'}), 400
    
    result = analyzer.predict(text)
    return jsonify(result)

@app.route('/batch_analyze', methods=['POST'])
def batch_analyze():
    data = request.json
    texts = data.get('texts', [])
    if not texts:
        return jsonify({'error': '请提供文本列表'}), 400
    
    results = [analyzer.predict(text) for text in texts]
    return jsonify({'results': results})

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)

情绪分析的可视化展示

python

import plotly.graph_objects as go
from plotly.subplots import make_subplots

def create_emotion_dashboard(texts, predictions, confidences):
    """创建交互式情绪分析仪表板"""
    
    # 统计情绪分布
    sentiment_counts = ['正面' if p==1 else '负面' for p in predictions]
    pos_count = sentiment_counts.count('正面')
    neg_count = sentiment_counts.count('负面')
    
    # 创建子图
    fig = make_subplots(
        rows=1, cols=2,
        subplot_titles=('情绪分布', '置信度分布'),
        specs=[[{'type': 'pie'}, {'type': 'bar'}]]
    )
    
    # 饼图
    fig.add_trace(
        go.Pie(labels=['正面', '负面'], values=[pos_count, neg_count], 
               marker=dict(colors=['#2ECC71', '#E74C3C'])),
        row=1, col=1
    )
    
    # 置信度柱状图
    colors = ['#2ECC71' if p==1 else '#E74C3C' for p in predictions]
    fig.add_trace(
        go.Bar(x=list(range(len(confidences))), y=confidences, 
               marker_color=colors, text=[f"{c:.2f}" for c in confidences],
               textposition='auto'),
        row=1, col=2
    )
    
    fig.update_layout(
        title_text="情绪分析结果仪表板",
        showlegend=True,
        height=500,
        width=1000
    )
    
    fig.show()
    
    # 输出详细结果
    print("\n详细分析结果:")
    for i, (text, sent, conf) in enumerate(zip(texts, sentiment_counts, confidences)):
        print(f"{i+1}. {text[:50]}... -> {sent} (置信度: {conf:.2f})")

# 示例使用
sample_texts = [
    "这部电影太精彩了,演员演技出色,剧情引人入胜!",
    "非常失望,剧情拖沓,浪费时间。",
    "平平无奇,没有特别出彩的地方。",
    "感动落泪,导演拍得太好了!"
]

# 假设已有模型预测结果
sample_preds = [1, 0, 0, 1]
sample_confs = [0.95, 0.89, 0.67, 0.92]

create_emotion_dashboard(sample_texts, sample_preds, sample_confs)

高级应用:多模态情绪分析

python

# 结合语音和文本的情绪分析示例
import speech_recognition as sr

class MultimodalEmotionAnalyzer:
    def __init__(self):
        self.text_analyzer = EmotionAnalyzer()
        self.recognizer = sr.Recognizer()
    
    def analyze_from_microphone(self, duration=5):
        """从麦克风实时分析情绪"""
        print(f"请说话 {duration} 秒...")
        with sr.Microphone() as source:
            audio = self.recognizer.listen(source, timeout=duration)
        
        try:
            # 语音转文字
            text = self.recognizer.recognize_google(audio, language='zh-CN')
            print(f"识别文本: {text}")
            
            # 情绪分析
            result = self.text_analyzer.predict(text)
            return result
        except sr.UnknownValueError:
            return {'error': '无法识别语音'}
        except sr.RequestError:
            return {'error': '语音识别服务出错'}

# 使用示例
# analyzer = MultimodalEmotionAnalyzer()
# result = analyzer.analyze_from_microphone()
# print(result)

实际应用场景

  1. 客户服务监控:实时分析客户聊天消息的情绪,当检测到负面情绪时自动升级处理

  2. 社交媒体分析:批量分析评论、帖子中的情绪倾向

  3. 心理健康监测:通过分析用户的日记或社交媒体内容,早期发现抑郁倾向

  4. 教育领域:分析学生反馈,改进教学质量

总结与建议

通过本文,我们实现了:

  1. 传统机器学习方法(SVM + TF-IDF):准确率约85-90%

  2. 深度学习方法(BERT):准确率可达92-95%

  3. API服务封装:便于集成到实际应用中

选择建议

  • 资源受限或快速原型:使用传统方法

  • 追求最佳效果:使用BERT等预训练模型

  • 中文场景:使用bert-base-chineseroberta-wwm-ext

改进方向

  • 使用更大的预训练模型(RoBERTa、ALBERT)

  • 加入数据增强技术

  • 进行模型蒸馏以加快推理速度

情绪分析是一个快速发展的领域,随着多模态大模型的出现,未来我们可以更准确地理解人类的复杂情感。希望本文能帮助你快速上手情绪分析项目!

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐