【Python机器学习】循环神经网络(RNN)——利用Keras实现循环神经网络

首先,加载数据集,获取标签并随机打乱样本,然后对文档分词并使用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层本身;通过随时间反向传播过程中误差的聚合将这种关系编码进了网络中,并将其表示为"思想向量"本身。我们基于思想向量做出的决策,通过分类器,就特定的分类问题向顺序向量的"质量"提供反馈。

相关推荐
佚明zj3 分钟前
全卷积和全连接
人工智能·深度学习
一点媛艺2 小时前
Kotlin函数由易到难
开发语言·python·kotlin
魔道不误砍柴功3 小时前
Java 中如何巧妙应用 Function 让方法复用性更强
java·开发语言·python
_.Switch4 小时前
高级Python自动化运维:容器安全与网络策略的深度解析
运维·网络·python·安全·自动化·devops
阿_旭4 小时前
一文读懂| 自注意力与交叉注意力机制在计算机视觉中作用与基本原理
人工智能·深度学习·计算机视觉·cross-attention·self-attention
王哈哈^_^4 小时前
【数据集】【YOLO】【目标检测】交通事故识别数据集 8939 张,YOLO道路事故目标检测实战训练教程!
前端·人工智能·深度学习·yolo·目标检测·计算机视觉·pyqt
测开小菜鸟5 小时前
使用python向钉钉群聊发送消息
java·python·钉钉
Power20246665 小时前
NLP论文速读|LongReward:基于AI反馈来提升长上下文大语言模型
人工智能·深度学习·机器学习·自然语言处理·nlp
数据猎手小k5 小时前
AndroidLab:一个系统化的Android代理框架,包含操作环境和可复现的基准测试,支持大型语言模型和多模态模型。
android·人工智能·机器学习·语言模型
YRr YRr6 小时前
深度学习:循环神经网络(RNN)详解
人工智能·rnn·深度学习