人工智能原理自学(五)——序列依赖问题、循环神经网络与LSTM网络自然语言处理实践

序列依赖问题

我们之所以使用卷积运算,是因为图像数据在空间上具有关联性,当然数据不仅在空间上会具备一定的关联性,在时间上亦是如此。最典型的旧日人类的语言。

我们一般把“”作为自然语音处理当中的基本单位。我们可能会有一些简单的给编码的方式,比如依据词典中的序号,对整个词典做归一化处理后以数值表示一个词。但这样的缺点便是:往往会出现一些读音极为相近,但是意义差距甚远的词语,比如开心开除,但是归一化后两者的数值却可能十分相近。此外,或许可以尝试One-Hot编码,但这样的缺点便是:通过one-hot编码后每个词都完全不一样,又丧失了某种联系性。此外,one-hot编码会让输入数据变得十分庞大。参考之前我们用来描述一个“豆豆”的特征而使用的输入“特征向量”,我们是否也可以基于这种特征向量的思路来描述一个词。在这里插入图片描述
比如上图中所示的,同意或近义词相距很近,而一些词义相反或是完全不相干的词语则相距很远。基于这样的思想,如果特征提取地恰当,那么在这样的多维描述下,的词向量减去老鼠的词向量,结果与警察的词向量减去小偷的词向量的结果十分相近。这就可以说明在某种意义上两对的关系具有相似性。这样的技术,在NLP中称之为词嵌入。即把词嵌入到一个特征向量空间中。
例如,一个词典中有四个词,并拥有10个特征维度,就可以用一个4×10的词向量矩阵来描述这一“词典”。如果要组成一句话,仅将每个词的one-hot编码作为列向量,按顺序依次组成一个矩阵,与词向量矩阵点乘,就可以达到这句话的矩阵表达。在这里插入图片描述
如果我们将这两个矩阵相乘的结果矩阵铺开,并输入一个全连接神经网络,在上图中的嵌入层线性计算部分,就可以通过反向传播来更新和修改词向量矩阵,具体比如使用卷积等方法。经过不断地训练和学习,最终得到最合适的词向量描述。当然,越复杂的维度分类下,每一个维度对应的意义也会变得越来越抽象,也越来越难以用实际意义去具体描述。常见的词向量训练算法为:word2vec和Glove

编程实验:文本情感分类

import shopping_data
from keras_preprocessing import sequence
# 用来将传入神经网络的向量进行数据对齐
from keras.models import Sequential
from keras.layers import Dense,Embedding
from keras.layers import Flatten

x_train,y_train,x_test,y_test = shopping_data.load_data()
print('The X shape',x_train.shape)
print(x_train[10])

vocalen,word_index = shopping_data.createWordIndex(x_train,x_test)
#vocalen:词典的词汇量,word_index:训练集和测试集全部语料的词典
print(word_index)
x_train_index = shopping_data.word2Index(x_train,word_index)
x_test_index = shopping_data.word2Index(x_test,word_index)

maxlen = 25
x_train_index = sequence.pad_sequences(x_train_index,maxlen = maxlen)
x_test_index = sequence.pad_sequences(x_test_index,maxlen = maxlen)

model = Sequential()
model.add( Embedding(trainable = True,input_dim = vocalen,output_dim = 300,input_length=maxlen) )
# trainable:是否让这一层可以被训练,即决定词嵌入矩阵是否会被更新
# 输入维度,输出维度,序列长度
model.add(Flatten())
model.add(Dense(256,activation = 'relu'))
model.add(Dense(256,activation = 'relu'))
model.add(Dense(256,activation = 'relu'))

model.add(Dense(1,activation = 'sigmoid'))
model.compile(loss = 'binary_crossentropy',
                optimizer = 'adam',  #使用动量的自适应优化器
                metrics = ['accuracy'])

model.fit(x_train_index,y_train,
        batch_size=512,
        epochs=200)
score,acc = model.evaluate(x_test_index,y_test)
print(score)
print(acc)

LSTM网络

RNN循环神经网络

现在,我们来改造一下传统的全连接神经网络。如果将词向量矩阵中的四个记为x1,x2,x3,x4,将x1输入后经第一层神经元的输出记为a1。那么,在第二次输入x2时,将a1与x2一起作为新的输入。在这里插入图片描述
此后,又将这次的输出a2与x3一起作为第三次运行的输入。这样,每一个词对最后预测输出的影响就在每一次的保存并和下一步的数据的共同作用中,持续到了最后。把这样改造的神经网络就称为循环神经网络RNN(Rerrent Neural Network)

  • 循环神经网络中的激活函数多采用双曲正切函数tanh,而不是relu。Keras框架中,默认使用的激活函数就是tanh
  • 为了保证每一步输入的过程具有代码上的相似性,我们一般手动构建一全0的输入向量a0,与x1一起送入第一次运算
  • 习惯上,循环神经网络的示意图按照下图所示:在这里插入图片描述
  • 那么,对于单层的神经元,它在网络中的时间上的行为就可以按照下图方式“横向展开在这里插入图片描述需要注意的是,上图所示的三个“神经元层”,是同一个神经元不同输入时序时的行为特征,这里的作图只是将这种时间行为向空间进行了展开
  • 标准的循环神经网络可以在一定程度上应对这种在时间上有依赖的序列问题,但对于长依赖问题,效果就不太好,由此我们引出了LSTM——长短时记忆网络

长短时记忆(网络)LSTM(Long Short-Trem Memory)

首先看标准的RNN结构的细节的数据流转和计算的结构图:在这里插入图片描述
再来看LSTM的主要数据流转与运算的过程:在这里插入图片描述
LSTM定义了一个细胞状态参数,即上图中的Ct与Ct-1,细胞状态将使得神经元具有记忆与遗忘功能在这里插入图片描述
如上图所示,将输入位置安置一个遗忘门,也即一个Sigmoid激活函数,这个函数的输入变量即为上一次的输出at-1与这一次的输入xt,并将结果与上一次的细胞状态相乘。显然,如果Sigmoid输出为0,则细胞状态就被更新为0,也即永久遗忘。反之,如果Sigmoid输出为1,则是选择继续记忆,为0~1之间,则可以理解为部分遗忘
同样的道理,我们再补充一个更新门,用来决定是否需要更新这次的细胞状态值,这样,网络就会选择重要的词汇更新细胞状态在这里插入图片描述
而除了记忆与遗忘之外,最后的输出还有一个输出门,这样就可以在遇到重要词汇时产生强输出,在不重要时产生弱输出,下图就是最终完整的LSTM网络层结构
在这里插入图片描述
当然,后人对LSTM又做了简化与改进,但仍然保留了相近的效果,比如常用的GRU网络结构。
具体可以查阅博客Understanding LSTM Networks

LSTM自然语言处理实践-编程实现

代码中用到的预训练词向量文件下载
核心代码:

import shopping_data
from keras_preprocessing import sequence
# 用来将传入神经网络的向量进行数据对齐
from keras.models import Sequential
from keras.layers import Dense,Embedding
from keras.layers import Flatten
from keras.layers import LSTM
import numpy as np
import chinese_vec

x_train,y_train,x_test,y_test = shopping_data.load_data()
print('The X shape',x_train.shape)
print(x_train[10])

vocalen,word_index = shopping_data.createWordIndex(x_train,x_test)
#vocalen:词典的词汇量,word_index:训练集和测试集全部语料的词典
print(word_index)
x_train_index = shopping_data.word2Index(x_train,word_index)
x_test_index = shopping_data.word2Index(x_test,word_index)

maxlen = 25
x_train_index = sequence.pad_sequences(x_train_index,maxlen = maxlen)
x_test_index = sequence.pad_sequences(x_test_index,maxlen = maxlen)

# 自行构造词嵌入矩阵
word_vecs = chinese_vec.load_word_vecs()
embedding_matrix = np.zeros((vocalen,300))
for word,i in word_index.items():
    embedding_vector = word_vecs.get(word)
    if embedding_vector is not None:
        embedding_matrix[i] = embedding_vector

model = Sequential()
model.add( Embedding(trainable = False,weights = [embedding_matrix],input_dim = vocalen,output_dim = 300,input_length=maxlen) )
# trainable:是否让这一层可以被训练,即决定词嵌入矩阵是否会被更新
# 输入维度,输出维度,序列长度
model.add(LSTM(128,return_sequences=True)) #输出数据维度
model.add(LSTM(128))

model.add(Dense(1,activation = 'sigmoid'))
model.compile(loss = 'binary_crossentropy',
                optimizer = 'adam',  #使用动量的自适应优化器
                metrics = ['accuracy'])

model.fit(x_train_index,y_train,
        batch_size=512,
        epochs=200)
score,acc = model.evaluate(x_test_index,y_test)
print(score)
print(acc)

代码中的辅助文件:chinese_vec.py

import os
import numpy as np
def load_word_vecs():
	embeddings_index = {}
	f = open(os.path.dirname(os.path.abspath(__file__))+'/sgns.target.word-word.dynwin5.thr10.neg5.dim300.iter5',encoding='utf8')
	f.readline()#escape first line
	for line in f:
	    values = line.split()
	    word = values[0]
	    coefs = np.asarray(values[1:], dtype='float32')
	    embeddings_index[word] = coefs
	f.close()

	print('Found %s word vectors.' % len(embeddings_index))
	return embeddings_index

进阶企划

  • 梯度下降优化器
    • 基于动量的梯度下降
    • AdaGradRMSProp等根据学习过程自我调节学习率的自适应算法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值