一、前言
文本情感分析是自然语言处理中非常根本的使命,咱们日子中有许多都是属于这一使命。比方购物网站的好评、差评,垃圾邮件过滤、垃圾短信过滤等。文本情感分析的完成办法也是多种多样的,能够运用传统的朴素贝叶斯、决策树,也能够运用基于深度学习的CNN、RNN等。本文运用IMDB电影谈论数据集,基于RNN网络来完成文本情感分析。
二、数据处理
2.1 数据预览
首先需求下载对应的数据:ai.stanford.edu/~amaas/data…。点击下图方位:
数据解压后得到下面的目录结构:
- aclImdb
- test
- neg
- pos
- labeledBow.feat
- urls_neg.txt
- urls_pos.txt
- train
- neg
- pos
这是一个电影影评数据集,neg中包含的谈论是评分较低的谈论,而pos中包含的是评分较高的谈论。咱们需求的数据分别是test里边的neg和pos,以及train里边的neg和pos(neg表示negative,pos表示positive)。下面咱们开端处理。
2.2 导入模块
在开端写代码之前需求先导入相关模块:
import os
import re
import string
import numpy as np
from tensorflow.keras import layers
from tensorflow.keras.models import Model
我的环境是tensorflow2.7,部分版别的tensorflow导入方法如下:
from keras import layers
from keras.models import Model
能够根据自己环境自行替换。
2.3 数据读取
这儿界说一个函数读取谈论文件:
def load_data(data_dir=r'/home/zack/Files/datasets/aclImdb/train'):
"""
data_dir:train的目录或test的目录
输出:
X:谈论的字符串列表
y:标签列表(0,1)
"""
classes = ['pos', 'neg']
X, y = [], []
for idx, cls in enumerate(classes):
# 拼接某个类别的目录
cls_path = os.path.join(data_dir, cls)
for file in os.listdir(cls_path):
# 拼接单个文件的目录
file_path = os.path.join(cls_path, file)
with open(file_path, encoding='utf-8') as f:
X.append(f.read().strip())
y.append(idx)
return X, np.array(y)
上述函数会得到两个列表,便于咱们后边处理。
2.4 构建词表
在咱们获取谈论文本后,咱们需求构建词表。即统计一切呈现的词,给每个词一个编号(也能够统计一部分,剩余的用unk表示)。这一步会得到一个词到id的映射
和id到词的映射
,具体代码如下:
def build_vocabulary(sentences):
"""
sentences:文本列表
输出:
word2idx:词到id的映射
idx2word:id到词的映射
"""
word2idx = {}
idx2word = {}
# 获取标点符号及空字符
punctuations = string.punctuation + "\t\n "
for sentence in sentences:
# 分词
words = re.split(f'[{punctuations}]', sentence.lower())
for word in words:
# 如果是新词
if word not in word2idx:
word2idx[word] = len(word2idx)
idx2word[len(word2idx) - 1] = word
return word2idx, idx2word
有了上面的两个映射后,咱们就能够将语句转换成id序列,也能够把id序列转换成语句,在本事例中只需求前者。
2.5 单词符号化(tokenize)
由于咱们模型需求固定长度的数据,因此在符号化时咱们对语句长度进行限制:
def tokenize(sentences, max_len=300):
"""
sentences:文本列表
tokens:符号化后的id矩阵,形状为(语句数量, 语句长度)
"""
# 生产一个形状为(语句数量, 语句长度)的矩阵,默认用空字符的id填充,类型必须为int
tokens = np.full((len(sentences), max_len),
fill_value=word2idx[''], dtype=np.int32)
punctuations = string.punctuation + "\t\n "
for row, sentence in enumerate(sentences):
# 分词
words = re.split(f'[{punctuations}]', sentence.lower())
for col, word in enumerate(words):
if col >= max_len:
break
# 把第row个语句的第col个词转成id
tokens[row, col] = word2idx.get(word, word2idx[''])
return tokens
运用该函数就能够将语句列表转换成ndarray了。
三、构建模型并练习
3.1 构建模型
这儿运用RNN来完成,模型结构如下图:
下面咱们用程序完成这个模型:
def build_model():
vocab_size = len(word2idx)
# 构建模型
inputs = layers.Input(shape=max_len)
x = layers.Embedding(vocab_size, embedding_dim)(inputs)
x = layers.LSTM(64)(x)
outputs = layers.Dense(1, activation='sigmoid')(x)
model = Model(inputs, outputs)
return model
这儿需求留意下面几个当地:
- Embedding层的输入是(batch_size,max_len),输出是(batch_size,max_len,embedding_dim)
- LSTM层的输入是(batch_size,max_len,embedding_dim),输出是(batch_size,units),units就是LSTM创立时传入的值。
3.2 练习模型
下面就能够运用前面完成好的几个办法开端练习模型了,代码如下:
# 超参数
max_len = 200
batch_size = 64
embedding_dim = 256
# 加载数据
X_train, y_train = load_data()
X_test, y_test = load_data('/home/zack/Files/datasets/aclImdb/test')
X = X_train + X_test
word2idx, idx2word = build_vocabulary(X)
X_train = tokenize(X_train, max_len=max_len)
X_test = tokenize(X_test, max_len=max_len)
# 构建模型
model = build_model()
model.summary()
model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['accuracy'])
model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=[X_test, y_test],
)
经过20个epoch的练习后,练习集准确率能够达到99%,而验证集准确率在80%左右,模型有一定程度的过拟合,能够经过修正模型结构或调节超参数来进行优化。
比方修正max_len的巨细、运用预练习的词嵌入、修正RNN中units的巨细、修正embedding_dim的巨细等。还能够添加BatchNormalization、Dropout层。
四、运用模型
模型练习好后,能够用predict来预测,predict的输入和embedding层的输入是相同的:
while True:
sentence = input("请输入语句:")
tokenized = tokenize([sentence], max_len)
output = model.predict(tokenized)
print('消沉' if output[0][0] >= 0.5 else '活跃')
下面是一些测试结果:
请输入语句:this is a bad movie
消沉
请输入语句:this is a good movie
活跃
请输入语句:i like this movie very much
活跃
请输入语句:i hate this movie very much
活跃
请输入语句:i will never see this movie again
活跃
效果不是特别理想,由于练习样本一般为长文本,而现在测试的是短文本。