首先,加载数据集,获取标签并随机打乱样本,然后对文档分词并使用Word2vec模型使其向量化,接下来,获取标签,最后按照80/20的比例将原始数据分成训练集和测试集。
首先,导入数据处理和循环神经网络训练所需的所有模块:
python
import glob
import os
from random import shuffle
from nltk.tokenize import TreebankWordTokenizer
from nlpia.loaders import get_data
from gensim.models.keyedvectors import KeyedVectors
word_vectors=KeyedVectors.load_word2vec_format('GoogleNews-vectors-negative300.bin',binary=True,limit=2000)
然后,我们可以构建数据预处理模块,它能对数据进行训练前的处理:
python
def pre_process_data(filepath):
positive_path=os.path.join(filepath,'pos')
negative_path=os.path.join(filepath,'neg')
pos_label=1
neg_label=0
dataset=[]
for filename in glob.glob(os.path.join(positive_path,'*.txt')):
with open(filename,'r') as f:
dataset.append((pos_label,f.read()))
for filename in glob.glob(os.path.join(negative_path,'*.txt')):
with open(filename,'r') as f:
dataset.append((neg_label,f.read()))
shuffle(dataset)
return dataset
将数据分词和向量化的方法写在一个函数中:
python
def tokenize_and_vectorize(dataset):
tokenizer=TreebankWordTokenizer()
vectorized_data=[]
for sample in dataset:
tokens=tokenizer.tokenize(sample[1])
sample_vecs=[]
for token in tokens:
try:
sample_vecs.append(word_vectors[token])
except:
pass
vectorized_data.append(sample_vecs)
return vectorized_data
将目标变量提取(解压)到单独的(但对应的)样本中:
python
def collect_excepted(dataset):
excepted=[]
for sample in dataset:
excepted.append(sample[0])
return excepted
在数据上运行:
python
dataset=pre_process_data('C:\\Users\\86185\\PycharmProjects\\pythonProject\\NLP\\aclImdb\\train')
vectorized_data=tokenize_and_vectorize(dataset)
excepted=collect_excepted(dataset)
split_point=int(len(vectorized_data)*0.8)
X_train=vectorized_data[:split_point]
y_train=excepted[:split_point]
X_test=vectorized_data[split_point:]
y_test=excepted[split_point:]
下面将为这个模型使用相同的超参数:每个样本使用400个词条,批大小为32。词向量是300维,我们将让它运行2个周期:
python
maxlen=400
batch_size=32
embedding_dims=300
epochs=2
接下来,再次填充和截断样本。通常我们不需要对循环神经网络使用填充或截断,因为它们可以处理任意长度的输入序列。但是,在接下来的几个步骤中,我们将看到所使用的模型要求输入指定长度的序列:
python
def pad_turnc(data,maxlen):
new_data=[]
zero_vector=[]
for _ in range(len(data[0][0])):
zero_vector.append(0.0)
for sample in data:
if len(sample)>maxlen:
temp=sample[:maxlen]
elif len(sample)<maxlen:
temp=sample
additional_elems=maxlen-len(sample)
for _ in range(additional_elems):
temp.append(zero_vector)
else:
temp=sample
new_data.append(temp)
return new_data
import numpy as np
X_train=pad_turnc(X_train,maxlen)
X_test=pad_turnc(X_test,maxlen)
X_train=np.reshape(X_train,(len(X_train),maxlen,embedding_dims))
y_train=np.array(y_train)
X_test=np.reshape(X_test,(len(X_test),maxlen,embedding_dims))
y_test=np.array(y_test)
现在已经有了数据,是时候构建模型了。我们将再次从Keras的一个标准的分层模型Sequential()(分层的)模型开始:
python
from keras.api.models import Sequential
from keras.api.layers import Dense,Dropout,Flatten,SimpleRNN
num_neurons=50
model=Sequential()
然后,Keras处理了组装神经网络的各个复杂环节:我们只需要将想要的循环层添加到网络中:
python
model.add(SimpleRNN(
num_neurons,return_sequences=True,
input_shape=(maxlen,embedding_dims)
))
现在,基础模块已经搭建完毕,可以接收各个输入并将其传递到一个简单的循环神经网络中,对于每个词条,将它们的输出集合到一个向量中。因为序列有400个词条长,并且使用了50个隐藏层神经元,所以这一层的输出将是一个400个元素的向量,其中每个元素都是一个50个元素的向量,每个神经元对应着一个输出。
关键词采纳数return_sequences会告诉网络每个时刻都要返回网络输出,因此有400个向量,每个向量为50维。如果return_sequences被设置为False,那么只会返回最后一个时刻的50维向量。
在本例中,50个神经元的选择是任意的,主要是为了减少计算时间。
使用循环神经网络时,通常不需要使用截断和填充。我们可以提供不同长度的训练数据,并展开网络直到输入结束,Keras对此会自动处理。问题是循环层的输出长度会随着输入时刻的变化而变化。4个词条的输入将输出4个元素长的序列。100个词条的输入将输出100个元素长的序列。如果我们需要把它传递到另一个层,即一个期望输入的维度统一的层,那么上述不等长的结果就会出现问题。但在某些情况下,这种不等长的序列输入也是可以接受的,甚至本来就期望如此。
python
model.add(Dropout(0.2))
model.add(Flatten())
model.add(Dense(1,activation='sigmoid'))
我们要求上述简单的RNN返回完整的序列,但是为了防止过拟合,我们添加了一个Dropout层,在每个输入样本上随机选择输入,使这些输入有20%的概率为零,最后再添加一个分类器。在这种情况下,我们只有一个类:"Yes-Positive Sentiemnt-1"或"No-Negative Sentiment-0",所以我们选择只有单个神经元(Dense(1))的层并使用Sigmoid激活函数。但是该稠密层需要输入一个由n个元素组成的扁平的训练(每个元素都是一个浮点数)。SimpleRNN输出的是一个400个元素长的张量,张量中的每个元素都是50个元素长。但是前馈网络并不关心元素的顺序而只关心输入是否符合网络的需要,所以我们使用Keras提供 的一个非常方便的网络层Flatten()将输入从400*50的张量扁平化为一个长度为20000个元素的向量。这就是我们要传递到最后一次用来做分类的向量。实际上,Flatten是一个映射,这意味着误差将从最后一层反向传播回RNN层的输出,这些反向传播的误差之后将在输出的合适点随时间反向传播。
将循环神经网络层生成的"思想向量"传递到前馈网络中,将不再保留我们努力试图想要包含的输入顺序的关系。但重要的是,与词条的序列相关的"学习"发生在RNN层本身;通过随时间反向传播过程中误差的聚合将这种关系编码进了网络中,并将其表示为"思想向量"本身。我们基于思想向量做出的决策,通过分类器,就特定的分类问题向顺序向量的"质量"提供反馈。