一、引言
随着社交媒体的蓬勃发展,微博等平台积累了海量的用户生成内容,其中蕴含着丰富的用户情感信息。对这些情感信息进行分析,能够帮助我们更好地理解公众舆论、品牌口碑以及用户需求等。本项目旨在构建一个基于长短期记忆网络(LSTM)的微博情感分析模型,以实现对微博文本情感倾向的准确判断。通过对数据的加载、预处理、可视化分析以及模型的构建、训练和评估,全面展示整个情感分析流程,并提出相应的优化建议,以期为相关研究和应用提供参考依据。
二、数据加载与预处理
(一)数据加载
项目初始阶段,利用pandas库读取存储微博数据的CSV文件,该文件包含用户评论文本(review列)及其对应的情感标签(label列,0代表负面情感,1代表正面情感)。通过检查数据加载情况,确认数据成功读取且格式正确,为后续处理奠定基础。例如,打印出前5条数据文本和标签,直观展示数据的基本形态,确保文本内容和标签能够准确对应,以便在模型训练过程中正确地关联输入特征与目标输出。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import jieba
from wordcloud import WordCloud
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report, accuracy_score, confusion_matrix
import tensorflow as tf
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Embedding, LSTM, Dense, Dropout
import os
import warnings
from collections import Counter
import seaborn as sns# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号# 设置随机种子,保证结果可复现
np.random.seed(42)
tf.random.set_seed(42)# 忽略警告
warnings.filterwarnings('ignore')# 数据加载与预处理
def load_and_preprocess_data(file_path, stopwords_path):"""加载数据并进行预处理"""# 读取数据data = pd.read_csv(file_path)texts = data['review'].tolist()labels = data['label'].tolist()# 检查数据加载情况print("数据加载完成,共有 {} 条数据".format(len(texts)))print("前5条数据文本:")for i in range(5):print(texts[i][:50], "...")print("\n前5条数据标签:")print(labels[:5])
(二)停用词表加载
停用词是指在文本中出现频率较高但对语义理解贡献较小的词汇,如“的”“是”“在”等。加载预先整理好的停用词表,将其存储为集合结构以便快速查询。检查停用词表是否存在以及其内容是否合理,对于后续文本预处理中的去噪操作至关重要。若停用词表缺失或不准确,可能会导致重要信息被误删或无意义词汇保留,影响模型对文本特征的提取和学习效果。
# 加载停用词表if not os.path.exists(stopwords_path):raise FileNotFoundError(f"停用词表文件 {stopwords_path} 不存在,请检查路径")with open(stopwords_path, 'r', encoding='utf-8') as f:stopwords = set(line.strip() for line in f)print("\n停用词表加载完成,共有 {} 个停用词".format(len(stopwords)))print("部分停用词:")print(list(stopwords)[:10])
(三)文本预处理
采用结巴分词库对微博文本进行分词处理,将连续的中文文本切分为一个个独立的词汇,这有助于模型更好地理解文本的语义结构和词汇特征。在分词的基础上,去除停用词,进一步精简文本内容,突出具有实际意义的词汇,降低模型的计算复杂度并提高其对关键信息的关注度。例如,对于一条原始微博评论“这个电影真的很好看,剧情非常吸引人”,经过分词和去停用词处理后,可能变为“电影 真 看 剧情 吸引”,保留了表达情感倾向的核心词汇。通过展示前5条处理后的文本,验证预处理流程的有效性,确保文本能够以合适的形式输入到后续的模型中进行训练和学习。
# 文本预处理:分词和去停用词def preprocess_text(text):words = jieba.lcut(text)return ' '.join([word for word in words if word not in stopwords])processed_texts = [preprocess_text(text) for text in texts]print("\n文本预处理完成,前5条处理后的文本:")for i in range(5):print(processed_texts[i][:50], "...") # 打印前50个字符return processed_texts, labels
三、数据可视化分析
(一)数据标签分布
利用matplotlib库绘制数据标签分布图,以柱状图的形式直观呈现正负面情感数据的数量差异。从图中可以清晰地看出数据集中正面和负面情感样本的比例关系,这对于了解数据的平衡性具有重要意义。若某一类标签的数据量远大于另一类,可能会导致模型在训练过程中出现偏差,倾向于多数类样本,从而影响对少数类样本的预测准确性。例如,若正面情感样本数量远多于负面情感样本,模型可能会过度拟合正面情感的特征,而对负面情感的识别能力不足。针对此类情况,可以考虑采用数据增强技术(如对少数类样本进行复制、扰动等操作)或调整分类器的阈值来平衡两类样本的影响力,优化模型性能。
# 绘制数据标签分布图
def plot_data_distribution(labels, save_path):"""绘制数据标签分布图"""label_counts = pd.Series(labels).value_counts()plt.figure(figsize=(8, 5))plt.bar(label_counts.index, label_counts.values, color=['green', 'red'])plt.title('数据标签分布', fontsize=14, fontweight='bold')plt.xlabel('标签', fontsize=12)plt.ylabel('数量', fontsize=12)plt.xticks([0, 1], ['负面情感', '正面情感'])plt.grid(axis='y', linestyle='--', alpha=0.7)plt.savefig(save_path, dpi=300, bbox_inches='tight', transparent=True)plt.show()
(二)文本长度分布
绘制文本长度分布图,以直方图展示微博文本在词数上的分布情况。这有助于了解文本的大致规模和复杂度,为后续确定文本向量化时的序列长度参数提供参考依据。例如,若大多数文本的长度集中在某个区间内,可以将序列长度设置为该区间的上限或一个稍大的值,既能保证包含大部分文本的信息,又能避免过长的序列导致计算资源的浪费和模型训练时间的增加。同时,观察文本长度分布是否存在异常情况,如极少数文本长度远超正常范围,可能需要对这些异常文本进行单独处理或分析其对模型的影响。
# 绘制文本长度分布图
def plot_text_length_distribution(texts, save_path):"""绘制文本长度分布图"""text_lengths = [len(text.split()) for text in texts]plt.figure(figsize=(10, 6))plt.hist(text_lengths, bins=30, color='skyblue', edgecolor='black')plt.title('文本长度分布', fontsize=14, fontweight='bold')plt.xlabel('文本长度(词数)', fontsize=12)plt.ylabel('频次', fontsize=12)plt.grid(axis='y', linestyle='--', alpha=0.7)plt.savefig(save_path, dpi=300, bbox_inches='tight', transparent=True)plt.show()
(三)词汇频率分布
通过统计所有文本中词汇的出现频率,并绘制词汇频率分布图,展示使用频率最高的前20个词汇及其对应的频次。这可以帮助我们了解文本中的高频词汇特征,初步判断哪些词汇可能与情感表达密切相关,以及是否存在某些词汇过度主导文本内容的情况。例如,若某些情感倾向明显的词汇(如“喜欢”“讨厌”)频繁出现,可能暗示这些词汇在情感分类任务中具有较高的区分度和预测能力。相反,若高频词汇多为一些通用词汇或无明确情感倾向的词汇,可能需要进一步优化文本预处理策略,如调整分词方法或停用词表,以挖掘更具代表性的词汇特征,提升模型对文本语义的理解和情感判断的准确性。
# 绘制词汇频率分布图
def plot_word_frequency_distribution(texts, save_path, top_n=20):"""绘制词汇频率分布图"""all_words = ' '.join(texts).split()word_freq = Counter(all_words).most_common(top_n)words, freqs = zip(*word_freq)plt.figure(figsize=(12, 7))plt.barh(words, freqs, color='purple')plt.title(f'词汇频率分布(前{top_n}个词)', fontsize=14, fontweight='bold')plt.xlabel('频次', fontsize=12)plt.ylabel('词汇', fontsize=12)plt.gca().invert_yaxis() # 使频次最高的词在顶部plt.grid(axis='x', linestyle='--', alpha=0.7)plt.savefig(save_path, dpi=300, bbox_inches='tight', transparent=True)plt.show()
(四)词云生成
利用词云技术将所有文本中的词汇以可视化的方式呈现,词汇的字体大小和颜色深浅根据其出现频率确定。词云图能够直观地展示文本中词汇的分布和重要程度,为用户快速把握文本主题和情感倾向提供直观印象。通过观察词云图中突出显示的词汇,可以进一步验证前期词汇频率分析的结果,并发现一些可能被忽略但具有一定代表性的词汇。同时,词云图作为一种美观且信息丰富的可视化形式,可用于报告展示或界面设计中,增强用户对数据分析结果的理解和接受度。
# 绘制混淆矩阵图
def plot_confusion_matrix(y_true, y_pred, save_path):"""绘制混淆矩阵图"""cm = confusion_matrix(y_true, y_pred)plt.figure(figsize=(8, 6))sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',xticklabels=['负面情感', '正面情感'],yticklabels=['负面情感', '正面情感'])plt.title('混淆矩阵', fontsize=14, fontweight='bold')plt.xlabel('预测标签', fontsize=12)plt.ylabel('真实标签', fontsize=12)plt.savefig(save_path, dpi=300, bbox_inches='tight', transparent=True)plt.show()
四、文本向量化
(一)序列化与填充
使用Keras的Tokenizer对预处理后的文本进行序列化处理,将每个词汇映射为一个唯一的整数索引,从而将文本转换为由整数组成的序列。这一过程实现了文本信息的数字化表示,便于模型进行计算和学习。由于不同文本的长度各异,采用pad_sequences函数对序列进行填充或截断,使其具有相同的长度(本项目中设置为100)。填充操作确保了输入数据的维度一致性,使得模型能够批量处理所有样本,提高训练效率。然而,填充也会引入一些额外的信息(如填充的0值),在模型训练过程中需要注意避免其对文本实际内容特征的干扰,同时合理设置填充位置(如在序列末尾填充)以保持文本的原始顺序和语义连贯性。
# 文本向量化
def text_vectorization(texts, max_len):"""对文本进行向量化处理"""tokenizer = Tokenizer()tokenizer.fit_on_texts(texts)sequences = tokenizer.texts_to_sequences(texts)word_index = tokenizer.word_index# 填充序列data = pad_sequences(sequences, maxlen=max_len)print("\n文本向量化完成")print("词汇表大小:{}".format(len(word_index) + 1))print("前5条向量化后的序列:")print(data[:5])return data, tokenizer, word_index
(二)词汇表构建
在序列化过程中,Tokenizer自动构建了词汇表,其中包含了所有文本中出现的唯一词汇及其对应的索引。了解词汇表的大小对于确定模型中嵌入层(Embedding Layer)的输入维度至关重要。嵌入层作为神经网络处理文本数据的第一层,将整数索引表示的词汇转换为密集的向量形式,每个向量维度的大小(即嵌入维度)需要根据词汇表的丰富程度和文本特征的复杂度进行合理设置。较大的词汇表可能需要更高的嵌入维度来充分捕捉词汇之间的语义关系和特征差异,但过高的维度也会增加模型的计算量和参数规模,可能导致过拟合等问题。因此,在实际应用中,需要通过实验和经验权衡嵌入维度的大小,以达到模型性能和计算资源的最佳平衡。
五、模型构建
(一)模型结构设计
构建的LSTM模型由三个主要层组成:嵌入层、LSTM层和全连接层。嵌入层负责将输入的整数索引序列转换为嵌入向量,为后续的LSTM层提供语义丰富的表示。LSTM层作为模型的核心部分,利用其独特的门控机制(输入门、遗忘门和输出门),能够有效地处理序列数据中的长期依赖关系,捕捉文本中词汇的先后顺序和语义关联,这对于情感分析任务中理解句子的整体情感倾向和语义逻辑至关重要。全连接层则将LSTM层输出的特征向量映射到最终的分类结果,通过sigmoid激活函数将输出值压缩到[0,1]区间,表示样本属于正面情感的概率。若概率大于0.5,则预测为正面情感,否则为负面情感。
# 模型构建
def build_lstm_model(vocab_size, embedding_dim, lstm_units):"""构建LSTM模型"""model = Sequential([Embedding(input_dim=vocab_size, output_dim=embedding_dim),LSTM(lstm_units, dropout=0.2, recurrent_dropout=0.2),Dense(1, activation='sigmoid')])model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])model.summary()return model
(二)模型参数设置
在模型构建过程中,合理设置各层的参数对于模型性能具有重要影响。嵌入层的输入维度根据词汇表大小确定(vocab_size),输出维度(embedding_dim)设置为50,这一维度大小能够在一定程度上平衡词汇特征表示能力和计算复杂度。LSTM层的单元数(lstm_units)设置为128,较多的单元数能够使模型学习到更复杂的序列特征和语义模式,但也可能导致模型训练时间增长和过拟合风险增加。为缓解过拟合问题,在LSTM层中添加了dropout和recurrent_dropout机制,分别在每个时间步和循环连接中随机失活一定比例的神经元,以增强模型的泛化能力。全连接层采用sigmoid激活函数,适合于二分类问题,能够将模型的输出转换为概率形式,便于后续的分类决策和性能评估。
# 模型构建print("\n构建模型...")vocab_size = len(tokenizer.word_index) + 1embedding_dim = 50lstm_units = 128model = build_lstm_model(vocab_size, embedding_dim, lstm_units)
(三)模型编译与总结
在模型编译阶段,选择合适的损失函数和优化器是关键。对于二分类问题,采用二元交叉熵损失函数(binary_crossentropy),它能够衡量模型预测概率分布与真实标签分布之间的差异,为模型训练提供优化方向。优化器选择Adam,它结合了AdaGrad和RMSProp的优点,能够自适应地调整学习率,在不同参数空间中实现快速且稳定的收敛。通过model.summary()函数输出模型的结构信息,包括各层的名称、输出形状和参数数量,这有助于直观了解模型的规模和复杂度,为后续的训练和调整提供参考依据。
六、模型训练
(一)数据集划分
将向量化后的数据集划分为训练集和测试集,测试集占比为20%。训练集用于模型的参数学习和优化,通过不断地输入样本和调整权重,使模型能够从数据中学习到文本特征与情感标签之间的映射关系。测试集则在模型训练完成后用于评估模型的泛化性能,即模型对未见过的新数据的预测能力。合理的数据划分比例能够确保模型在训练过程中有足够的数据进行学习,同时保留一部分数据用于客观评估模型的实际效果,避免过拟合或欠拟合等问题。
# 划分数据集print("\n划分数据集...")X_train, X_test, y_train, y_test = train_test_split(data, labels, test_size=0.2, random_state=42)print("训练集大小:{},测试集大小:{}".format(len(X_train), len(X_test)))
(二)训练过程监控
设置训练轮数(epochs)为20,批量大小(batch_size)为64。在训练过程中,通过可视化训练历史记录,实时监控模型在训练集和验证集上的损失值和准确率变化情况。训练损失和准确率曲线能够直观地反映模型的学习进度,若训练损失逐渐下降且准确率逐步提升,说明模型在不断学习和优化。同时,观察验证损失和准确率曲线,若其趋势与训练曲线一致且无明显波动或上升,表明模型具有较好的泛化能力,能够在验证集上稳定地发挥性能。若出现验证损失上升或波动较大等情况,可能意味着模型出现了过拟合现象,此时需要考虑调整模型结构、增加正则化项或提前停止训练等策略来改善模型性能。
# 模型训练print("\n开始模型训练...")epochs = 20batch_size = 64history = model.fit(X_train, np.array(y_train),validation_split=0.2,epochs=epochs,batch_size=batch_size,verbose=1)# 模型评估print("\n模型评估...")
# 可视化训练过程
def plot_training_history(history, save_path):"""绘制训练过程中的损失和准确率曲线"""plt.figure(figsize=(12, 6))# 绘制损失曲线plt.subplot(1, 2, 1)plt.plot(history.history['loss'], label='训练损失', color='blue')plt.plot(history.history['val_loss'], label='验证损失', color='orange')plt.title('训练与验证损失', fontsize=14, fontweight='bold')plt.xlabel('Epoch', fontsize=12)plt.ylabel('Loss', fontsize=12)plt.legend()plt.grid(True, linestyle='--', alpha=0.7)# 绘制准确率曲线plt.subplot(1, 2, 2)plt.plot(history.history['accuracy'], label='训练准确率', color='blue')plt.plot(history.history['val_accuracy'], label='验证准确率', color='orange')plt.title('训练与验证准确率', fontsize=14, fontweight='bold')plt.xlabel('Epoch', fontsize=12)plt.ylabel('Accuracy', fontsize=12)plt.legend()plt.grid(True, linestyle='--', alpha=0.7)# 保存图片plt.tight_layout()plt.savefig(save_path, dpi=300, bbox_inches='tight', transparent=True)plt.show()
七、模型评估与分析
(一)分类报告解读
在模型训练完成后,利用测试集对模型进行最终评估,生成分类报告。分类报告中包含了精确率(Precision)、召回率(Recall)和F1分数(F1-Score)等关键指标,这些指标从不同角度全面评估了模型对正负面情感分类的性能。精确率反映了模型预测为正面或负面情感的样本中有多少是真实正确的,关注的是预测结果的准确性。召回率则衡量了模型对实际正面或负面情感样本的识别能力,即从所有真实正面或负面样本中正确预测出来的比例,侧重于模型的全面性。F1分数是精确率和召回率的调和平均数,综合考虑了两者的表现,能够更全面地反映模型的分类效果。通过对分类报告的详细分析,可以明确模型在不同类别上的优势和不足,例如若正面情感的召回率较低,说明模型对正面情感样本的识别能力有待提高,可能需要进一步优化模型结构或调整训练策略,如增加正面情感样本的数据量、调整类别权重等。
print("分类报告:")print(classification_report(y_test, y_pred))print("准确率:", accuracy_score(y_test, y_pred))
(二)准确率分析
准确率作为整体评估指标,表示模型在测试集上正确预测的样本比例。较高的准确率意味着模型在整体上具有较好的分类性能,但需要注意的是,若数据集存在类别不平衡问题,仅依靠准确率可能会产生误导。例如,在一个正面情感样本远多于负面情感样本的数据集中,模型可能通过简单地将所有样本预测为正面情感来获得较高的准确率,但实际上对负面情感的预测能力很差。因此,在分析准确率时,应结合分类报告中的其他指标以及数据集的类别分布情况,全面客观地评价模型的实际效果。
(三)混淆矩阵解读
绘制混淆矩阵能够直观地展示模型在测试集上的预测结果与真实标签之间的对应关系。矩阵中的每个元素值表示在真实标签条件下,模型预测为某一标签的样本数量。通过分析混淆矩阵,可以清楚地看出模型在哪些类别上容易出现误判,例如,若矩阵中显示模型将较多的负面情感样本错误地预测为正面情感,则说明模型在识别负面情感特征方面存在不足,可能需要进一步挖掘负面情感的特有词汇和语义模式,或者调整模型的决策边界。此外,混淆矩阵还可以用于计算各种分类评价指标,如精确率、召回率等,为模型性能的深入分析提供数据支持。
# 绘制混淆矩阵图
def plot_confusion_matrix(y_true, y_pred, save_path):"""绘制混淆矩阵图"""cm = confusion_matrix(y_true, y_pred)plt.figure(figsize=(8, 6))sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',xticklabels=['负面情感', '正面情感'],yticklabels=['负面情感', '正面情感'])plt.title('混淆矩阵', fontsize=14, fontweight='bold')plt.xlabel('预测标签', fontsize=12)plt.ylabel('真实标签', fontsize=12)plt.savefig(save_path, dpi=300, bbox_inches='tight', transparent=True)plt.show()
八、总结与展望
(一)项目总结
本项目成功构建了一个基于LSTM的微博情感分析模型,通过对数据的加载、预处理、可视化分析以及模型的训练和评估,实现了对微博文本情感倾向的有效判断。在数据预处理阶段,采用分词和去停用词等方法对文本进行清洗和精简,为模型提取关键特征奠定了基础。数据可视化部分,从标签分布、文本长度、词汇频率等多个角度对数据进行了深入分析,增进了对数据特征和规律的理解。模型构建方面,合理设计了LSTM网络结构,选择了合适的参数设置,并通过训练过程监控确保模型能够稳定收敛和学习。最终的模型评估结果表明,所构建的LSTM模型在微博情感分析任务上具有较好的性能,能够为相关研究和实际应用提供有力支持。
(二)未来展望
尽管当前模型在微博情感分析方面取得了一定成果,但仍存在一些可以改进和拓展的方向。首先,在数据预处理环节,可以进一步优化分词算法和停用词表,结合领域知识和情感分析任务特点,挖掘更具代表性和区分度的词汇特征,例如引入情感词典对词汇进行情感极性标注,增强模型对情感倾向的理解能力。其次,在模型结构方面,可以尝试引入更先进的深度学习架构,如双向LSTM、Transformer等,这些模型能够更好地捕捉文本的上下文语义信息和长距离依赖关系,进一步提升模型的性能。此外,还可以考虑结合多源数据(如用户画像、评论时间等)进行融合分析,从更多维度挖掘影响情感倾向的因素,提高模型的预测准确性和鲁棒性。最后,在实际应用中,可以将情感分析模型与微博热点话题监测、品牌口碑分析等业务场景相结合,为决策者提供更深入、全面的舆情洞察和数据分析支持,推动社交媒体数据分析领域的不断发展和创新。
以上报告详细阐述了基于LSTM的微博情感分析模型的构建过程、数据分析方法、模型性能评估以及未来优化方向,希望能够为从事相关领域的研究人员和从业者提供有价值的参考和借鉴,促进情感分析技术在社交媒体领域的进一步发展和应用。
附上所有代码:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import jieba
from wordcloud import WordCloud
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report, accuracy_score, confusion_matrix
import tensorflow as tf
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Embedding, LSTM, Dense, Dropout
import os
import warnings
from collections import Counter
import seaborn as sns# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号# 设置随机种子,保证结果可复现
np.random.seed(42)
tf.random.set_seed(42)# 忽略警告
warnings.filterwarnings('ignore')# 数据加载与预处理
def load_and_preprocess_data(file_path, stopwords_path):"""加载数据并进行预处理"""# 读取数据data = pd.read_csv(file_path)texts = data['review'].tolist()labels = data['label'].tolist()# 检查数据加载情况print("数据加载完成,共有 {} 条数据".format(len(texts)))print("前5条数据文本:")for i in range(5):print(texts[i][:50], "...")print("\n前5条数据标签:")print(labels[:5])# 加载停用词表if not os.path.exists(stopwords_path):raise FileNotFoundError(f"停用词表文件 {stopwords_path} 不存在,请检查路径")with open(stopwords_path, 'r', encoding='utf-8') as f:stopwords = set(line.strip() for line in f)print("\n停用词表加载完成,共有 {} 个停用词".format(len(stopwords)))print("部分停用词:")print(list(stopwords)[:10])# 文本预处理:分词和去停用词def preprocess_text(text):words = jieba.lcut(text)return ' '.join([word for word in words if word not in stopwords])processed_texts = [preprocess_text(text) for text in texts]print("\n文本预处理完成,前5条处理后的文本:")for i in range(5):print(processed_texts[i][:50], "...") # 打印前50个字符return processed_texts, labels# 文本向量化
def text_vectorization(texts, max_len):"""对文本进行向量化处理"""tokenizer = Tokenizer()tokenizer.fit_on_texts(texts)sequences = tokenizer.texts_to_sequences(texts)word_index = tokenizer.word_index# 填充序列data = pad_sequences(sequences, maxlen=max_len)print("\n文本向量化完成")print("词汇表大小:{}".format(len(word_index) + 1))print("前5条向量化后的序列:")print(data[:5])return data, tokenizer, word_index# 模型构建
def build_lstm_model(vocab_size, embedding_dim, lstm_units):"""构建LSTM模型"""model = Sequential([Embedding(input_dim=vocab_size, output_dim=embedding_dim),LSTM(lstm_units, dropout=0.2, recurrent_dropout=0.2),Dense(1, activation='sigmoid')])model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])model.summary()return model# 可视化训练过程
def plot_training_history(history, save_path):"""绘制训练过程中的损失和准确率曲线"""plt.figure(figsize=(12, 6))# 绘制损失曲线plt.subplot(1, 2, 1)plt.plot(history.history['loss'], label='训练损失', color='blue')plt.plot(history.history['val_loss'], label='验证损失', color='orange')plt.title('训练与验证损失', fontsize=14, fontweight='bold')plt.xlabel('Epoch', fontsize=12)plt.ylabel('Loss', fontsize=12)plt.legend()plt.grid(True, linestyle='--', alpha=0.7)# 绘制准确率曲线plt.subplot(1, 2, 2)plt.plot(history.history['accuracy'], label='训练准确率', color='blue')plt.plot(history.history['val_accuracy'], label='验证准确率', color='orange')plt.title('训练与验证准确率', fontsize=14, fontweight='bold')plt.xlabel('Epoch', fontsize=12)plt.ylabel('Accuracy', fontsize=12)plt.legend()plt.grid(True, linestyle='--', alpha=0.7)# 保存图片plt.tight_layout()plt.savefig(save_path, dpi=300, bbox_inches='tight', transparent=True)plt.show()# 绘制数据标签分布图
def plot_data_distribution(labels, save_path):"""绘制数据标签分布图"""label_counts = pd.Series(labels).value_counts()plt.figure(figsize=(8, 5))plt.bar(label_counts.index, label_counts.values, color=['green', 'red'])plt.title('数据标签分布', fontsize=14, fontweight='bold')plt.xlabel('标签', fontsize=12)plt.ylabel('数量', fontsize=12)plt.xticks([0, 1], ['负面情感', '正面情感'])plt.grid(axis='y', linestyle='--', alpha=0.7)plt.savefig(save_path, dpi=300, bbox_inches='tight', transparent=True)plt.show()# 绘制文本长度分布图
def plot_text_length_distribution(texts, save_path):"""绘制文本长度分布图"""text_lengths = [len(text.split()) for text in texts]plt.figure(figsize=(10, 6))plt.hist(text_lengths, bins=30, color='skyblue', edgecolor='black')plt.title('文本长度分布', fontsize=14, fontweight='bold')plt.xlabel('文本长度(词数)', fontsize=12)plt.ylabel('频次', fontsize=12)plt.grid(axis='y', linestyle='--', alpha=0.7)plt.savefig(save_path, dpi=300, bbox_inches='tight', transparent=True)plt.show()# 绘制词汇频率分布图
def plot_word_frequency_distribution(texts, save_path, top_n=20):"""绘制词汇频率分布图"""all_words = ' '.join(texts).split()word_freq = Counter(all_words).most_common(top_n)words, freqs = zip(*word_freq)plt.figure(figsize=(12, 7))plt.barh(words, freqs, color='purple')plt.title(f'词汇频率分布(前{top_n}个词)', fontsize=14, fontweight='bold')plt.xlabel('频次', fontsize=12)plt.ylabel('词汇', fontsize=12)plt.gca().invert_yaxis() # 使频次最高的词在顶部plt.grid(axis='x', linestyle='--', alpha=0.7)plt.savefig(save_path, dpi=300, bbox_inches='tight', transparent=True)plt.show()# 绘制混淆矩阵图
def plot_confusion_matrix(y_true, y_pred, save_path):"""绘制混淆矩阵图"""cm = confusion_matrix(y_true, y_pred)plt.figure(figsize=(8, 6))sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',xticklabels=['负面情感', '正面情感'],yticklabels=['负面情感', '正面情感'])plt.title('混淆矩阵', fontsize=14, fontweight='bold')plt.xlabel('预测标签', fontsize=12)plt.ylabel('真实标签', fontsize=12)plt.savefig(save_path, dpi=300, bbox_inches='tight', transparent=True)plt.show()# 主函数
def main():# 配置路径data_path = 'weibo_senti_100k.csv'stopwords_path = 'D:/python项目/1/stopwords.txt'wordcloud_save_path = '词云.png'training_plot_save_path = '训练历史.png'data_distribution_save_path = '数据分布图.png'text_length_distribution_save_path = '文本长度分布图.png'word_frequency_distribution_save_path = '词汇频率分布图.png'confusion_matrix_save_path = '混淆矩阵图.png'# 数据加载与预处理print("开始数据加载与预处理...")texts, labels = load_and_preprocess_data(data_path, stopwords_path)# 绘制数据标签分布图print("\n绘制数据标签分布图...")plot_data_distribution(labels, data_distribution_save_path)print("数据标签分布图已保存到 {}".format(data_distribution_save_path))# 绘制文本长度分布图print("\n绘制文本长度分布图...")plot_text_length_distribution(texts, text_length_distribution_save_path)print("文本长度分布图已保存到 {}".format(text_length_distribution_save_path))# 绘制词汇频率分布图print("\n绘制词汇频率分布图...")plot_word_frequency_distribution(texts, word_frequency_distribution_save_path, top_n=20)print("词汇频率分布图已保存到 {}".format(word_frequency_distribution_save_path))# 生成词云print("\n生成词云...")all_words = ' '.join(texts)wordcloud = WordCloud(font_path='simhei.ttf', width=800, height=400, background_color='white').generate(all_words)plt.figure(figsize=(10, 5))plt.imshow(wordcloud, interpolation='bilinear')plt.axis('off')plt.xlabel('词云图')plt.ylabel('词汇')plt.savefig(wordcloud_save_path, dpi=300, bbox_inches='tight', transparent=True)plt.show()print("词云生成完成,已保存到 {}".format(wordcloud_save_path))# 文本向量化print("\n开始文本向量化...")max_len = 100data, tokenizer, word_index = text_vectorization(texts, max_len)# 划分数据集print("\n划分数据集...")X_train, X_test, y_train, y_test = train_test_split(data, labels, test_size=0.2, random_state=42)print("训练集大小:{},测试集大小:{}".format(len(X_train), len(X_test)))# 模型构建print("\n构建模型...")vocab_size = len(tokenizer.word_index) + 1embedding_dim = 50lstm_units = 128model = build_lstm_model(vocab_size, embedding_dim, lstm_units)# 模型训练print("\n开始模型训练...")epochs = 20batch_size = 64history = model.fit(X_train, np.array(y_train),validation_split=0.2,epochs=epochs,batch_size=batch_size,verbose=1)# 模型评估print("\n模型评估...")y_pred = model.predict(X_test)y_pred = (y_pred > 0.5).astype(int)print("分类报告:")print(classification_report(y_test, y_pred))print("准确率:", accuracy_score(y_test, y_pred))# 绘制训练过程曲线print("\n绘制训练过程曲线...")plot_training_history(history, training_plot_save_path)print("训练过程曲线已保存到 {}".format(training_plot_save_path))# 绘制混淆矩阵图print("\n绘制混淆矩阵图...")plot_confusion_matrix(y_test, y_pred, confusion_matrix_save_path)print("混淆矩阵图已保存到 {}".format(confusion_matrix_save_path))if __name__ == "__main__":main()