35、深度学习-自学之路-深入理解-NLP自然语言处理-RNN一个简单的程序,解析RNN的运行原理。同时配有对应的输出解析。还有对应的资料下载。同时和CBOW和Skip-Gram也进行了比较。
·
词义的处理上有不同的方式
我们现在使用的RNN, CBOW,Skip-Gram
RNN 是一类专门用于处理序列数据的神经网络。与传统的前馈神经网络不同,RNN 引入了循环结构,允许网络在处理当前输入时,能够记住之前的信息。具体来说,RNN 在每个时间步接收输入并生成输出,同时将当前时间步的隐藏状态传递到下一个时间步,使得网络可以对序列中的信息进行建模
应用场景
- 语言模型:预测下一个单词的概率。
- 机器翻译:将一种语言的句子翻译成另一种语言。
- 语音识别:将语音信号转换为文本。
CBOW
原理
CBOW 是一种用于学习词向量的无监督学习模型。它的基本思想是根据一个词的上下文(即周围的词)来预测这个词。具体来说,CBOW 模型将上下文词的词向量进行求和或平均,然后通过一个线性变换和 softmax 函数来预测中心词的概率。模型的训练目标是最大化预测中心词的概率。
应用场景
- 词向量学习:生成词的分布式表示,用于后续的 NLP 任务,如文本分类、情感分析等。
- 语义相似度计算:通过比较词向量的相似度来判断词语之间的语义关系。
优点
- 训练速度快,因为它只需要根据上下文预测一个中心词。
- 对于低频词的处理效果较好,因为它可以从上下文中学习到低频词的信息。
3. Skip - Gram 模型
原理
Skip - Gram 也是一种用于学习词向量的无监督学习模型,与 CBOW 相反,Skip - Gram 模型根据一个中心词来预测它的上下文。具体来说,模型接收一个中心词的词向量作为输入,然后通过一个线性变换和 softmax 函数来预测上下文词的概率。模型的训练目标是最大化预测上下文词的概率。
应用场景
- 词向量学习:与 CBOW 类似,Skip - Gram 也可以用于生成词的分布式表示。
- 推荐系统:通过学习物品之间的语义关系,为用户推荐相关的物品。
优点
- 能够更好地捕捉词与词之间的语义关系,尤其是对于语义相近的词。
- 对于生僻词的学习效果较好,因为它可以通过中心词来学习生僻词的上下文信息。
三者比较
- RNN 与 CBOW、Skip - Gram:RNN 主要用于处理序列数据,关注序列中信息的顺序和上下文关系,可用于多种序列建模任务;而 CBOW 和 Skip - Gram 主要用于学习词向量,侧重于捕捉词语之间的语义关系。
- CBOW 与 Skip - Gram:CBOW 根据上下文预测中心词,训练速度快,对低频词处理较好;Skip - Gram 根据中心词预测上下文,能更好地捕捉语义关系,对生僻词学习效果较好。
程序如下:
import sys,random,math
from collections import Counter
import numpy as np
#读取文件
f = open('tasks_1-20_v1/en/qa1_single-supporting-fact_train.txt','r')
raw = f.readlines()
f.close()
#把文件中的前1000条,出去空格,出去换行,单独的单词列出来
tokens = list()
for line in raw[0:1000]:
tokens.append(line.lower().replace("\n","").split(" ")[1:])
#打印前四条 [['mary', 'moved', 'to', 'the', 'bathroom.'], ['john', 'went', 'to', 'the', 'hallway.'], ['where', 'is', 'mary?', '\tbathroom\t1']]
print(tokens[0:3])
vocab = list(set(word for sent in tokens for word in sent))
vocab_size = len(vocab)
#把tokens 中的数据都放到vocab链表中
vocab = set()
for sent in tokens:
for word in sent:
vocab.add(word)
#整理成一个整个链表
word2indexvocab = list(vocab)
print("vocab")
print(vocab)
'''
{'hallway.', 'back', '\tbedroom\t2', 'bathroom.', '\toffice\t5', '\thallway\t11', '\thallway\t13', '\tkitchen\t11', '\tbathroom\t2', '\tgarden\t10', 'garden.', 'sandra', '\tbedroom\t1', '\tgarden\t4', '\tbedroom\t5', '\tkitchen\t5', '\thallway\t1', 'daniel', '\tbathroom\t8', 'john?', '\tkitchen\t1', '\tbathroom\t4', '\thallway\t10', '\tgarden\t13', 'where', '\thallway\t5', 'went', '\tgarden\t8', '\tbathroom\t13', '\tkitchen\t13', '\tbedroom\t11', 'bedroom.', 'is', '\tkitchen\t7', 'office.', '\thallway\t7', 'moved', '\toffice\t2', '\tkitchen\t10', '\tbedroom\t13', '\toffice\t14', '\tbathroom\t11', '\toffice\t1', 'mary?', '\tbathroom\t7', '\tgarden\t5', '\tbedroom\t10', 'john', '\tgarden\t2', '\toffice\t10', '\tbedroom\t8', '\tkitchen\t8', 'sandra?', '\tbathroom\t1', 'daniel?', 'journeyed', '\tgarden\t1', 'the', '\tbathroom\t5', '\tgarden\t14', '\tbathroom\t10', '\thallway\t4', 'travelled', '\tgarden\t11', '\tbedroom\t14', '\thallway\t8', '\thallway\t14', '\tkitchen\t4', '\tkitchen\t2', '\thallway\t2', 'mary', '\tbedroom\t4', '\toffice\t11', 'kitchen.', '\toffice\t8', '\toffice\t13', '\tbedroom\t7', 'to', '\tbathroom\t14', '\tgarden\t7', '\tkitchen\t14', '\toffice\t4'}
'''
#给vocab链表中的每个单词一个序号
word2index = {}
for i, word in enumerate(vocab):
word2index[word] = i
print("word2index")
print(word2index)
'''
word2index
{'hallway.': 0, 'back': 1, '\tbedroom\t2': 2, 'bathroom.': 3, '\toffice\t5': 4, '\thallway\t11': 5,
'\thallway\t13': 6, '\tkitchen\t11': 7, '\tbathroom\t2': 8, '\tgarden\t10': 9, 'garden.': 10,
'sandra': 11, '\tbedroom\t1': 12, '\tgarden\t4': 13, '\tbedroom\t5': 14, '\tkitchen\t5': 15,
'\thallway\t1': 16, 'daniel': 17, '\tbathroom\t8': 18, 'john?': 19, '\tkitchen\t1': 20, '\tbathroom\t4': 21,
'\thallway\t10': 22, '\tgarden\t13': 23, 'where': 24, '\thallway\t5': 25, 'went': 26, '\tgarden\t8': 27,
'\tbathroom\t13': 28, '\tkitchen\t13': 29, '\tbedroom\t11': 30, 'bedroom.': 31, 'is': 32,
'\tkitchen\t7': 33, 'office.': 34, '\thallway\t7': 35, 'moved': 36, '\toffice\t2': 37, '\tkitchen\t10': 38,
'\tbedroom\t13': 39, '\toffice\t14': 40, '\tbathroom\t11': 41, '\toffice\t1': 42, 'mary?': 43,
'\tbathroom\t7': 44, '\tgarden\t5': 45, '\tbedroom\t10': 46, 'john': 47, '\tgarden\t2': 48,
'\toffice\t10': 49, '\tbedroom\t8': 50, '\tkitchen\t8': 51, 'sandra?': 52, '\tbathroom\t1': 53,
'daniel?': 54, 'journeyed': 55, '\tgarden\t1': 56, 'the': 57, '\tbathroom\t5': 58, '\tgarden\t14': 59,
'\tbathroom\t10': 60, '\thallway\t4': 61, 'travelled': 62, '\tgarden\t11': 63, '\tbedroom\t14': 64,
'\thallway\t8': 65, '\thallway\t14': 66, '\tkitchen\t4': 67, '\tkitchen\t2': 68, '\thallway\t2': 69,
'mary': 70, '\tbedroom\t4': 71, '\toffice\t11': 72, 'kitchen.': 73, '\toffice\t8': 74,
'\toffice\t13': 75, '\tbedroom\t7': 76, 'to': 77, '\tbathroom\t14': 78, '\tgarden\t7': 79,
'\tkitchen\t14': 80, '\toffice\t4': 81}
'''
#返回链表索引
'''
sentence = ['apple', 'banana', 'cherry'],
并且word2index字典中'apple': 1,'banana': 2,'cherry': 3,
那么调用words2indices(sentence)将返回[1, 2, 3]。
'''
def words2indices(sentence):
idx = list()
for word in sentence:
idx.append(word2index[word])
return idx
#定义一个softmanx的处理函数
def softmax(x):
e_x = np.exp(x - np.max(x))
return e_x / e_x.sum(axis=0)
#固定初始化的随机权重值
np.random.seed(1)
#词向量维度
embed_size = 10
# word embeddings 初始化词向量维度的值,
embed = (np.random.rand(len(vocab),embed_size) - 0.5) * 0.1
#定义一个embed_size*embed_size的单位矩阵 循环权重矩阵 recurrent
# embedding -> embedding (initially the identity matrix)
recurrent = np.eye(embed_size)
#start 初始化初始的向量 起始向量 start
# sentence embedding for empty sentence
start = np.zeros(embed_size)
# embedding -> output weights 输出权重矩阵 decoder
decoder = (np.random.rand(embed_size, len(vocab)) - 0.5) * 0.1
#单位矩阵 one_hot。
# one hot lookups (for loss function)
one_hot = np.eye(len(vocab))
def predict(sent):
layers = list() #一个空列表,用于存储每个时间步的状态信息。
layer = {} #一个字典,用于存储当前时间步的状态。初始时,将 start(应该是一个预先定义好的初始隐藏状态向量)赋值给 layer['hidden'],并将该状态添加到 layers 列表中。
layer['hidden'] = start
layers.append(layer)
loss = 0 #loss:初始化为 0,用于累计预测损失。
# forward propagate
preds = list()
for target_i in range(len(sent)):
layer = {}
# try to predict the next term
layer['pred'] = softmax(layers[-1]['hidden'].dot(decoder))
#对下一个词进行预测,每次都进行预测的原因是你不知道那个词是最后一个,也就是最后你需要预测的那个词,所以每次都进行预测。
# 直到最后的那个预测。
#计算误差值
loss += -np.log(layer['pred'][sent[target_i]])
#更新新的隐藏层
# generate the next hidden state
layer['hidden'] = layers[-1]['hidden'].dot(recurrent) + embed[sent[target_i]]
layers.append(layer)
return layers, loss
# forward
for iter in range(30000):
alpha = 0.001
sent = words2indices(tokens[iter%len(tokens)][1:]) #选择一句文本中的句子进行预测
layers,loss = predict(sent) #对这个句子进行预处理,把要预测的值提取出来,把每一层分层分时相加。
# back propagate
for layer_idx in reversed(range(len(layers))):
layer = layers[layer_idx]
target = sent[layer_idx-1]
if(layer_idx > 0):
layer['output_delta'] = layer['pred'] - one_hot[target]
'''
这里的真实值使用的还是one-hot的数据模型,不过数据少的时候,这样做是没有问题的。
'''
new_hidden_delta = layer['output_delta'].dot(decoder.transpose())
'''
decoder 是一个矩阵,它的形状通常是 (隐藏层维度, 输出层维度),表示从隐藏层到输出层的权重矩阵。
decoder.transpose() 是 decoder 的转置矩阵,形状变为 (输出层维度, 隐藏层维度)。
'''
# if the last layer - don't pull from a
# later one becasue it doesn't exist
if(layer_idx == len(layers)-1): #如果是最后一层,不要从后面拉取层,因为不存在
layer['hidden_delta'] = new_hidden_delta
else:
layer['hidden_delta'] = new_hidden_delta + layers[layer_idx+1]['hidden_delta'].dot(recurrent.transpose())
else:#如果是第一层
layer['hidden_delta'] = layers[layer_idx+1]['hidden_delta'].dot(recurrent.transpose())
# update weights
start -= layers[0]['hidden_delta'] * alpha / float(len(sent))
for layer_idx, layer in enumerate(layers[1:]):
#对隐藏层进行更新,输出层的权重进行跟新,常规操作,可以理解。
decoder -= np.outer(layers[layer_idx]['hidden'], layer['output_delta']) * alpha / float(len(sent))
#
embed_idx = sent[layer_idx]
#对对应的中间层进行跟新。
embed[embed_idx] -= layers[layer_idx]['hidden_delta'] * alpha / float(len(sent))
#对单位举证向量也进行更新,这个很重要,这个已经对单词是怎么走的做出来了自己更新。
#但是有一个问题,单位矩阵会使用在很多层上的,如果改变了,不是每一次层单独改变的,是一起改变的。会不会有其他的影响。
recurrent -= np.outer(layers[layer_idx]['hidden'], layer['hidden_delta']) * alpha / float(len(sent))
if (iter % 1000 == 0):
print("Perplexity:" + str(np.exp(loss / len(sent))))
sent_index = 4
l,_ = predict(words2indices(tokens[sent_index]))
print(tokens[sent_index])
for i,each_layer in enumerate(l[1:-1]):
input = tokens[sent_index][i]
true = tokens[sent_index][i+1]
pred = vocab[each_layer['pred'].argmax()]
print("Prev Input:" + input + (' ' * (12 - len(input))) +\
"True:" + true + (" " * (15 - len(true))) + "Pred:" + pred)
'''
核心思想是RNN 通过将一个句子中的每个词的输入顺序进行加入影响因素中,这样更像在表达一个正常的话,而不是仅仅是概率上的分析,也可以对 韩国队打败日本队和日本队打败韩国队,这样的输入进行混淆。
可以在一定程度上真正的理解训练的这段话是什么意思了。
'''
更多推荐


所有评论(0)