TensorFlow实战:构建情感分析模型处理IMDB影评
项目概览
本文演示如何使用TensorFlow和Keras构建一个文本分类模型,对IMDB电影评论进行正面/负面情感分类。完整代码可在GitHub仓库获取。
数据准备与探索
加载数据集
import tensorflow as tf
from tensorflow import keras
import numpy as np
import matplotlib.pyplot as plt
import pathlib
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
print(f"TensorFlow: {tf.__version__}, Keras: {tf.keras.__version__}")
data_dir = str(pathlib.Path.cwd()) + "/datasets/imdb/"
# 检查预下载的numpy文件
np_data = np.load(data_dir + "imdb.npz")
print("Keys in npz:", list(np_data.keys()))
# 加载IMDB数据集,仅保留前10000个高频词
imdb = keras.datasets.imdb
(train_texts, train_labels), (test_texts, test_labels) = imdb.load_data(
path=data_dir + "imdb.npz",
num_words=10000
)
数据结构分析
每条评论已预处理为整数序列,每个整数对应词典中的一个单词。标签0表示负面,1表示正面。
print(f"训练样本数: {len(train_texts)}, 标签数: {len(train_labels)}")
print(f"第一条评论长度: {len(train_texts[0])}, 第二条: {len(train_texts[1])}")
print(f"第一条评论整数序列:\n{train_texts[0]}")
整数到文本的逆向转换
word_index = imdb.get_word_index(data_dir + "imdb_word_index.json")
word_index = {k: (v + 3) for k, v in word_index.items()}
word_index["<PAD>"] = 0
word_index["<START>"] = 1
word_index["<UNK>"] = 2
word_index["<UNUSED>"] = 3
reverse_lookup = dict([(value, key) for (key, value) in word_index.items()])
def decode_review(encoded_text):
"""将整数序列还原为可读文本"""
return ' '.join([reverse_lookup.get(i, '?') for i in encoded_text])
print("原始文本:", decode_review(train_texts[0]))
数据预处理
所有评论序列需要统一长度,通过填充操作实现。
max_len = 256
train_data = keras.preprocessing.sequence.pad_sequences(
train_texts,
value=word_index["<PAD>"],
padding='post',
maxlen=max_len
)
test_data = keras.preprocessing.sequence.pad_sequences(
test_texts,
value=word_index["<PAD>"],
padding='post',
maxlen=max_len
)
print(f"处理后第一条评论长度: {len(train_data[0])}")
print(f"填充后样本:\n{train_data[0]}")
模型构建
定义网络结构
vocab_size = 10000
model = keras.Sequential([
keras.layers.Embedding(vocab_size, 16), # 词嵌入层
keras.layers.GlobalAveragePooling1D(), # 全局平均池化
keras.layers.Dense(16, activation='relu'), # 全连接隐含层
keras.layers.Dense(1, activation='sigmoid') # 输出层,二分类
])
model.summary()
编译模型
model.compile(
optimizer='adam',
loss='binary_crossentropy',
metrics=['accuracy']
)
训练验证分离
# 从训练集中切出10000条作为验证集
val_features = train_data[:10000]
partial_train_features = train_data[10000:]
val_labels = train_labels[:10000]
partial_train_labels = train_labels[10000:]
模型训练
history = model.fit(
partial_train_features,
partial_train_labels,
epochs=40,
batch_size=512,
validation_data=(val_features, val_labels),
verbose=2
)
模型评估
test_loss, test_acc = model.evaluate(test_data, test_labels)
print(f"测试集损失: {test_loss:.4f}, 准确率: {test_acc:.4f}")
训练过程可视化
通过图形展示训练过程中的损失和准确率变化,便于诊断过拟合。
history_dict = history.history
train_loss = history_dict['loss']
val_loss = history_dict['val_loss']
train_acc = history_dict['accuracy']
val_acc = history_dict['val_accuracy']
epochs_range = range(1, len(train_acc) + 1)
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
plt.plot(epochs_range, train_loss, 'bo', label='训练损失')
plt.plot(epochs_range, val_loss, 'b-', label='验证损失')
plt.title('损失变化曲线')
plt.xlabel('训练轮数')
plt.ylabel('损失值')
plt.legend()
plt.subplot(1, 2, 2)
plt.plot(epochs_range, train_acc, 'ro', label='训练准确率')
plt.plot(epochs_range, val_acc, 'r-', label='验证准确率')
plt.title('准确率变化曲线')
plt.xlabel('训练轮数')
plt.ylabel('准确率')
plt.legend()
plt.tight_layout()
plt.savefig("./imdb_training_curves.png", dpi=200)
plt.show()
常见问题处理
网络下载失败
问题:执行load_data()或get_word_index()时出现下载超时。
解决方案:
- 手动下载所需文件(imdb.npz和imdb_word_index.json)
- 通过
path参数指定本地文件路径 - 或将文件放置于
~/.keras/datasets/目录下
