一.RNN简介
1.)什么是RNN?
RNN是一种特殊的神经网络结构,考虑前一时刻的输入,且赋予了网络对前面的内容的一种‘记忆‘功能.
2.)RNN可以解决什么问题?
时间先后顺序的问题都可以使用RNN来解决,比如:音乐,翻译,造句,语音识别,视频图像预测,语言处理等等,后来经过变种甚至可以达到CNN的作用
具体例子1 Car which.............,() .........。使用RNN可以预测括号里面的内容应该为 is/was.
2 学习莎士比亚写的诗词,然后进行模仿
3 你想为朋友的生日创作一段爵士乐,然而,你不会任何乐器、乐理,幸运的是,你了解深度学习并将使用LSTM网络来解决这个问题
二.RNN原理
1.)RNN单元图(上)和完整结构图(下)以及前向传播
RNN单元理解:
上一时间t-1隐藏状态与本时刻t的输入xt 乘以权重矩阵Wa,加上一个偏置ba,再使用tanh激活函数得到本时间t得隐藏状态at。
使用本时间的隐藏状态at预测出yt
疑问1:它是如何进行“记忆”的呢?
答:我们称a^t为隐藏状态,由图可知,它是根据X^t计算得到的,也就是说它保留了上一个单元的输入信息,这就是它记忆的原理
疑问2:为什么使用tanh不使用sigmod激活函数呢?(RNN的激活函数除了双曲正切,RELU函数也用的非常多)
答:sigmod导数复杂,使用sigmod函数容易出现梯度消失,(tanh也会有此问题,lstm网络解决此问题)
,
,
2.)反向传播和损失函数 选用交叉熵(Cross Entropy)
反向传播
pass
损失函数
t时刻的损失值
总损失值
三 RNN的升级
RNN基础版,存在着一些问题, 其中较为严重的是容易出现梯度消失或者梯度爆炸的问题. 注意: 这里的梯度消失主要指由于时间过长而造成记忆值较小的现象,例如Car 和 后面的 距离太远了,网络无法记住
1.)长短时记忆(Long Short-Term Memory (LSTM))网络 参考 https://www.jianshu.com/p/95d5c461924c
我们称Ct为细胞状态
LSTM的理解
1)第一步决定细胞状态需要丢弃哪些信息
忘记门 Γf?t??=σ(Wf?[a?t−1?,x?t?]+bf?) 使用 sigmod激活函数,向量里面的0-1值表示细胞状态中的哪些信息保留或丢弃多少。0表示不保留,1表示都保留
2)第二步决定给细胞状态添加哪些新的信息
输入门 Γu?t?=it?=σ(Wu?[a?t−1?,x?t?]+bu?),使用sigmod激活函数,决定更新哪些信息
C~t为新的候选细胞信息(输入门决定选用细胞里面哪些信息)
3)更新旧的细胞信息,变为新的细胞信息
4)断输出细胞的哪些状态特征
拿语言模型来举例说明,在预测动词形式的时候,我们需要通过输入的主语是单数还是复数来推断输出门输出的预测动词是单数形式还是复数形式。
2)GRU
pass
四 实例
1)字符级语言模型 - 恐龙岛
欢迎来到恐龙岛,恐龙生活于在6500万年前,现在研究人员在试着复活恐龙,而你的任务就是给恐龙命名,如果一只恐龙不喜欢它的名字,它可能会狂躁不安,所以你要谨慎选择。
import numpy as np import random import time import utils data = open("dinos.txt", "r").read()#读取数据 data = data.lower() #转换为小写字符,类型为 str chars = list(set(data)) #转换为无序且不重复的元素列表[‘s‘, ‘y‘, ‘m‘. data_size, vocab_size = len(data), len(chars) #?, 27 #‘a‘:1,‘b‘:2的字典 char_to_ix = {ch: i for i, ch in enumerate(sorted(chars))} #‘1‘:a,‘2‘:b的字典 ix_to_char = {i: ch for i, ch in enumerate(sorted(chars))} #梯度修剪 def clip(gradients, maxValue): """ 使用maxValue修剪梯度 :param gradients: 字典类型,包含 dWaa, dWax, dWya,db ,dby :param maxValue: 阈值,把梯度限制在[-N,N] :return:修剪后的梯度 """ #获取参数 dWaa, dWax, dWya, db, dby = gradients[‘dWaa‘], gradients[‘dWax‘], gradients[‘dWya‘], gradients[‘db‘], gradients[‘dby‘] #梯度修剪 for gradient in [dWaa, dWax, dWya, db, dby]: np.clip(gradient, -maxValue, maxValue,out=gradient) gradients = {‘dWaa‘: dWaa, "dWax": dWax, "dWya": dWya, "db": db, "dby": dby} return gradients def text_clip(): np.random.seed(3) dWax = np.random.randn(5, 3) * 10 dWaa = np.random.randn(5, 5) * 10 dWya = np.random.randn(2, 5) * 10 db = np.random.randn(5, 1) * 10 dby = np.random.randn(2, 1) * 10 gradients = {"dWax": dWax, "dWaa": dWaa, "dWya": dWya, "db": db, "dby": dby} gradients = clip(gradients, 10) print("gradients[\"dWaa\"][1][2] =", gradients["dWaa"][1][2]) print("gradients[\"dWax\"][3][1] =", gradients["dWax"][3][1]) print("gradients[\"dWya\"][1][2] =", gradients["dWya"][1][2]) print("gradients[\"db\"][4] =", gradients["db"][4]) print("gradients[\"dby\"][1] =", gradients["dby"][1]) #采样 def sample(paramters, char_to_is, seed): """ 根据rnn输出的概率分布序列对字符序列进行采样 :param paramters: 包含Waa, Wax,Wya,by,b的字典 :param char_to_is: 字符映射到所以的字典 :param seed: :return: indices -- 包含采样字符索引的长度为n的列表 """ #从parameters中获取参数 Waa, Wax, Wya, by, b = paramters[‘Waa‘], paramters[‘Wax‘], paramters[‘Wya‘], paramters[‘by‘], paramters[‘b‘] vocab_size = by.shape[0] n_a = Waa.shape[1] #1.创建独热向量x x = np.zeros((vocab_size, 1)) a_prev = np.zeros((n_a, 1)) indices = [] idx = -1 #检测换行符的标记,初始化为-1 #循环遍历时间步骤t counter = 0 newline_character = char_to_ix["\n"] while (idx != newline_character and counter < 50): a = np.tanh(np.dot(Wax, x) + np.dot(Waa, a_prev) + b) z = np.dot(Wya, a) + by y = utils.softmax(z) #上面前向传播, np.random.seed(counter + seed) #从概率分布y中抽取词汇表中字符的索引 idx = np.random.choice(list(range(vocab_size)), p=y.ravel()) indices.append(idx) x = np.zeros((vocab_size, 1)) x[idx] = 1 a_prev = a seed += 1 counter += 1 if(counter == 50): indices.append((char_to_ix["\n"])) return indices def text_sample(): np.random.seed(2) _, n_a = 20, 100 Wax, Waa, Wya = np.random.randn(n_a, vocab_size), np.random.randn(n_a, n_a), np.random.randn(vocab_size, n_a) b, by = np.random.randn(n_a, 1), np.random.randn(vocab_size, 1) parameters = {"Wax": Wax, "Waa": Waa, "Wya": Wya, "b": b, "by": by} indices = sample(parameters, char_to_ix, 0) print("Sampling:") print("list of sampled indices:", indices) print("list of sampled characters:", [ix_to_char[i] for i in indices]) #优化函数 def optimize(X, Y, a_prev, parameters, learning_rate = 0.01): """ :param X: 整个列表,其中每个整数映射到词汇表中的字符 :param Y: 整数列表,与X相同,但左移动了一个索引 :param a_prev: 上一个隐藏状态 :param parameters: 字典,包含了一下参数: Wax -- 权重矩阵乘以输入,dim=(n_a, n_x) Waa -- 权重矩阵乘以隐藏状态,dim = (n_a, n_a) Wya -- 隐藏状态与输出相关的权重偏置 b -- 偏置,dim=(n_a,1) by -- 隐藏状态与输出相关的权重偏置,dim=(n_y,1) :param learning_rate: :return: loss, gradients -- 字典,包含了一下参数: dwax: dwaa: dwya: db: dby: a[len(X)-1]-- 最后的隐藏状态,dim=(n_a,1) """ #前向传播 loss, cache = utils.rnn_forward(X, Y, a_prev, parameters) #反向传播 gradients, a = utils.rnn_backward(X, Y, parameters, cache) #梯度修剪[-5,5] gradients = clip(gradients, 5) #更新参数 parameters = utils.update_parameters(parameters, gradients, learning_rate) return loss, gradients, a[len(X)-1] def text_optimize(): np.random.seed(1) vocab_size, n_a = 27, 100 a_prev = np.random.randn(n_a, 1) Wax, Waa, Wya = np.random.randn(n_a, vocab_size), np.random.randn(n_a, n_a), np.random.randn(vocab_size, n_a) b, by = np.random.randn(n_a, 1), np.random.randn(vocab_size, 1) parameters = {"Wax": Wax, "Waa": Waa, "Wya": Wya, "b": b, "by": by} X = [12, 3, 5, 11, 22, 3] Y = [4, 14, 11, 22, 25, 26] loss, gradients, a_last = optimize(X, Y, a_prev, parameters, learning_rate=0.01) print("Loss =", loss) print("gradients[\"dWaa\"][1][2] =", gradients["dWaa"][1][2]) print("np.argmax(gradients[\"dWax\"]) =", np.argmax(gradients["dWax"])) print("gradients[\"dWya\"][1][2] =", gradients["dWya"][1][2]) print("gradients[\"db\"][4] =", gradients["db"][4]) print("gradients[\"dby\"][1] =", gradients["dby"][1]) print("a_last[4] =", a_last[4]) def model(data, ix_to_char, char_to_ix, num_iterations=3500, n_a=50, dino_names=7, vocab_size=27): """ 下路模型并生成恐龙的名字 :param data: 语料库 :param ix_to_char:字符映射索引字典 :param char_to_ix: :param num_iterations:迭代 :param n_a: RNN单元数 :param dino_names: 每次迭代中采样的数量 :param vocab_size: 文本中的唯一字符的数量 :return:parameters -- 学习后的参数 """ n_x, n_y = vocab_size, vocab_size #27? 个字母 #初始化参数 parameters = utils.initialize_parameters(n_a, n_x, n_y) #初始化损失 loss = utils.get_initial_loss(vocab_size, dino_names) with open("dinos.txt") as f: examples = f.readlines() examples = [x.lower().strip() for x in examples] #转换成列表 np.random.seed(0) np.random.shuffle(examples) #打乱顺序 #初始化lstm隐藏状态 a_prev = np.zeros((n_a, 1)) #循环 for j in range(num_iterations): index = j % len(examples) #j%1536 共1536个样本 #print("index = ",index) X = [None] + [char_to_ix[ch] for ch in examples[index]] Y = X[1:] + [char_to_ix["\n"]] #Y=X[1:]+[0] #print("X={}, Y={}".format(X, Y)) curr_loss, gradients, a_prev = optimize(X, Y, a_prev, parameters) loss = utils.smooth(loss, curr_loss) if j % 500 == 0: print("第" + str(j+1) + "次迭代,损失值为:" + str(loss)) seed = 0 for name in range(dino_names): #采样 sampled_indices = sample(parameters, char_to_ix, seed) utils.print_sample(sampled_indices, ix_to_char) seed += 1 #print("\n") return parameters def text_model(): # 开始时间 start_time = time.perf_counter # 开始训练 parameters = model(data, ix_to_char, char_to_ix, num_iterations=3500) # 结束时间 end_time = time.perf_counter # 计算时差 #minium = end_time - start_time #print("执行了:" + str(int(minium / 60)) + "分" + str(int(minium % 60)) + "秒") text_model()
原文地址:https://www.cnblogs.com/ymzm204/p/12215240.html