当前位置:   article > 正文

【Pytorch神经网络实战案例】11 循环神经网络结构训练语言模型并进行简单预测_循环神经网络预测模型

循环神经网络预测模型

1 语言模型步骤

简单概述:根据输入内容,继续输出后面的句子。

1.1 根据需求拆分任务

  • (1)先对模型输入一段文字,令模型输出之后的一个文字。
  • (2)将模型预测出来的文字当成输入,再放到模型里,使模型预测出下一个文字,这样循环下去,以使RNN完成一句话的输出。

1.2 根据任务设计功能模块

  • (1)模型能够记住前面文字的语义;
  • (2)能够根据前面的语义和一个输入文字,输出下一个文字。

1.3 根据功能模块设计实现方案

RNN模型的接口可以输出两个结果:预测值和当前状态

  1. 在实现时,将输入的序列样本拆开,使用循环的方式,将字符逐个输入模型。模型会对每次的输入预测出两个结果,一个是预测字符,另一个是当前的序列状态。
  2. 在训练场景下,将硕测字骑用于计算损失,列状动用于传入下一次循环计算。
  3. 在测试场景下,用循环的方式将输入序列中的文字一个个地传入到模型中,得到最后一个时刻的当前状态,将该状态和输入序列中的最后一个文字转入模型,生成下一个文字的预测结果。同时,按照要求生成的文字条件,重复地将新生成的文字和当前状态输入模型,来预测下一个文字。

2 语言模型的代码实现

2.1 准备样本数据

样本内容:
在尘世的纷扰中,只要心头悬挂着远方的灯光,我们就会坚持不懈地走,理想为我们灌注了精神的蕴藉。所以,生活再平凡、再普通、再琐碎,我们都要坚持一种信念,默守一种精神,为自己积淀站立的信心,前行的气力。

2.1.1定义基本工具函数---make_Language_model.py(第1部分)

首先引入头文件,然后定义相关函数:get_ch_lable()从文件中获取文本,get_ch._able_v0将文本数组转方向量,具体代码如下:

  1. import numpy as np
  2. import torch
  3. import torch.nn.functional as F
  4. import time
  5. import random
  6. from collections import Counter
  7. # 1.1 定义基本的工具函数
  8. RANDOM_SEED = 123
  9. torch.manual_seed(RANDOM_SEED)
  10. DEVICE = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
  11. def elapsed(sec): # 计算时间函数
  12. if sec < 60:
  13. return str(sec) + "sec"
  14. elif sec<(60*60):
  15. return str(sec/60) + "min"
  16. else:
  17. return str(sec/(60*60)) + "hour"
  18. training_file = 'csv_list/wordstest.txt' # 定义样本文件
  19. #中文字
  20. def get_ch_label(txt_file): # 提取样本中的汉字
  21. labels = ""
  22. with open(txt_file,'rb') as f :
  23. for label in f:
  24. labels = labels + label.decode("gb2312",errors = 'ignore')
  25. return labels
  26. #中文多文件
  27. def readalltxt(txt_files): # 处理中文
  28. labels = []
  29. for txt_file in txt_files:
  30. target = get_ch_label(txt_file)
  31. labels.append(target)
  32. return labels
  33. # 将汉子转化成向量,支持文件和内存对象里的汉字转换
  34. def get_ch_label_v(txt_file,word_num_map,txt_label = None):
  35. words_size = len(word_num_map)
  36. to_num = lambda word:word_num_map.get(word,words_size)
  37. if txt_file != None:
  38. txt_label = get_ch_label(txt_file)
  39. # 使用to_num()实现单个汉子转成向量的功能,如果没有该汉字,则将返回words_size(值为69)
  40. labels_vector = list(map(to_num,txt_label)) # 将汉字列表中的每个元素传入到to_num()进行转换
  41. return labels_vector

2.1.2 样本预处理---make_Language_model.py(第2部分)

样本预处理这个一套指读取整个样本,将其放入training_data里,获取全部的字表words,并生成样本向量wordlabel与向量有对应关系的word_num_map,代码如下:

  1. # 1.2 样本预处理
  2. training_data = get_ch_label(training_file)
  3. print("加载训练模型中")
  4. print("该样本长度:",len(training_data))
  5. counter = Counter(training_data)
  6. words = sorted(counter)
  7. words_size = len(words)
  8. word_num_map = dict(zip(words,range(words_size)))
  9. print("字表大小:",words_size)
  10. wordlabel = get_ch_label_v(training_file,word_num_map)
  11. # 加载训练模型中
  12. # 该样本长度: 75
  13. # 字表大小: 41(去重)

    上述结果表示样本文件里一共有75个文字,其中掉重复的文字之后,还有41个。这41个文字将作为字表词典,建立文字与索引值的对应关系。
    在训练模型时,每个文字都会被转化成数字形式的索引值输入模型。模型的输出是这41个文字的概率,即把每个文字当成一类。

2.2 代码实现:构建循环神经网络模型---make_Language_model.py(第3部分)

使用GRU构建RNN模型,令RNN模型只接收一个序列的喻入字符,并预测出下一个序列的字符。
在该模型里,所需要完成的步骤如下:

  1. 将输入的字索引转为词嵌入;
  2. 将词嵌入结果输入GRU层;
  3. 对GRU结果做全连接处理,得到维度为69的预测结果,这个预测结果代表每个文字的概率。

2.2.1 代码实现

  1. # 1.3 构建循环神经网络(RNN)模型
  2. class GRURNN(torch.nn.Module):
  3. def __init__(self,word_size,embed_dim,hidden_dim,output_size,num_layers):
  4. super(GRURNN, self).__init__()
  5. self.num_layers = num_layers
  6. self.hidden_dim = hidden_dim
  7. self.embed = torch.nn.Embedding(word_size,embed_dim)
  8. # 定义一个多层的双向层:
  9. # 预测结果:形状为[序列,批次,维度hidden_dim×2],因为是双向RNN,故维度为hidden_dim
  10. # 序列状态:形状为[层数×2,批次,维度hidden_dim]
  11. self.gru = torch.nn.GRU(input_size=embed_dim,
  12. hidden_size=hidden_dim,
  13. num_layers=num_layers,bidirectional=True)
  14. self.fc = torch.nn.Linear(hidden_dim *2,output_size)# 全连接层,充当模型的输出层,用于对GRU输出的预测结果进行处理,得到最终的分类结果
  15. def forward(self,features,hidden):
  16. embeded = self.embed(features.view(1,-1))
  17. output,hidden = self.gru(embeded.view(1,1,-1),hidden)
  18. # output = self.attention(output)
  19. output = self.fc(output.view(1,-1))
  20. return output,hidden
  21. def init_zero_state(self): # 对于GRU层状态的初始化,每次迭代训练之前,需要对GRU的状态进行清空,因为输入的序列是1,故torch.zeros的第二个参数为1
  22. init_hidden = torch.zeros(self.num_layers*2,1,self.hidden_dim).to(DEVICE)
  23. return init_hidden

2.3 代码实现:实例化,训练模型--make_Language_model.py(第3部分)

  1. # 1.4 实例化模型类,并训练模型
  2. EMBEDDING_DIM = 10 # 定义词嵌入维度
  3. HIDDEN_DIM = 20 # 定义隐藏层维度
  4. NUM_LAYERS = 1 # 定义层数
  5. # 实例化模型
  6. model = GRURNN(words_size,EMBEDDING_DIM,HIDDEN_DIM,words_size,NUM_LAYERS)
  7. model = model.to(DEVICE)
  8. optimizer = torch.optim.Adam(model.parameters(),lr=0.005)
  9. # 定义测试函数
  10. def evaluate(model,prime_str,predict_len,temperature=0.8):
  11. hidden = model.init_zero_state().to(DEVICE)
  12. predicted = ""
  13. # 处理输入语义
  14. for p in range(len(prime_str) -1):
  15. _,hidden = model(prime_str[p],hidden)
  16. predicted = predicted + words[predict_len]
  17. inp = prime_str[-1] # 获得输入字符
  18. predicted = predicted + words[inp]
  19. #按照指定长度输出预测字符
  20. for p in range(predict_len):
  21. output,hidden = model(inp,hidden) # 将输入字符和状态传入模型
  22. # 从多项式中分布采样
  23. # 在测试环境下,使用温度的参数和指数计算对模型的输出结果进行微调,保证其数值是大于0的数,小于0,torch.multinomial()会报错
  24. # 同时,使用多项式分布的方式进行采样,生成预测结果
  25. output_dist = output.data.view(-1).div(temperature).exp()
  26. inp = torch.multinomial(output_dist,1)[0] # 获取采样结果
  27. predicted = predicted + words[inp] # 将索引转化成汉字保存在字符串中
  28. return predicted
  29. # 定义参数并训练
  30. training_iters = 5000
  31. display_step = 1000
  32. n_input = 4
  33. step = 0
  34. offset = random.randint(0,n_input+1)
  35. end_offset = n_input + 1
  36. while step < training_iters: # 按照迭代次数训练模型
  37. start_time = time.time() # 计算起始时间
  38. #随机取一个位置偏移
  39. if offset > (len(training_data)-end_offset):
  40. offset = random.randint(0,n_input+1)
  41. # 制作输入样本
  42. inwords = wordlabel[offset:offset+n_input]
  43. inwords = np.reshape(np.array(inwords),[n_input,-1,1])
  44. # 制作标签样本
  45. out_onehot = wordlabel[offset+1:offset+n_input+1]
  46. hidden = model.init_zero_state() # RNN的状态清零
  47. optimizer.zero_grad()
  48. loss = 0.0
  49. inputs = torch.LongTensor(inwords).to(DEVICE)
  50. targets = torch.LongTensor(out_onehot).to(DEVICE)
  51. for c in range(n_input): # 按照输入长度将样本预测输入模型并进行预测
  52. outputs,hidden = model(inputs[c],hidden)
  53. loss = loss + F.cross_entropy(outputs,targets[c].view(1))
  54. loss = loss / n_input
  55. loss.backward()
  56. optimizer.step()
  57. # 输出日志
  58. with torch.set_grad_enabled(False):
  59. if (step+1)%display_step == 0 :
  60. print(f'Time elapesd:{(time.time() - start_time)/60:.4f}min')
  61. print(f'step {step + 1}|Loss {loss.item():.2f}\n\n')
  62. with torch.no_grad():
  63. print(evaluate(model,inputs,32),'\n')
  64. print(50*'=')
  65. step = step +1
  66. # 每次迭代结束,将偏移值相后移动n_input+1个距离单位,可以保证输入数据的样本相互均匀,否则会出现文本两边的样本训练次数较少的情况。
  67. offset = offset + (n_input+1)
  68. print("Finished!")

2.4 代码实现:运行模型生成句子--make_Language_model.py(第4部分)

  1. # 1.5 运行模型生成句子
  2. while True:
  3. prompt = "输入几个文字:"
  4. sentence = input(prompt)
  5. inputword = sentence.strip()
  6. try:
  7. inputword = get_ch_label_v(None,word_num_map,inputword)
  8. keys = np.reshape(np.array(inputword),[len(inputword),-1,1])
  9. # get_ch_label_v()中,如果在字典中找不到对应的索引,就会为其分配一个无效的索引值,
  10. # 进而在 evaluate()函数中调用模型的时,差不多对应对的有效词向量而终止报错
  11. model.eval()
  12. with torch.no_grad():
  13. sentence = evaluate(model,torch.LongTensor(keys).to(DEVICE),32)
  14. print(sentence)
  15. except: # 异常处理,当输入的文字不在模型字典中时,系统会报错,有意设置,防止输入超范围的字词
  16. print("还没学会")

3 代码总览--make_Language_model.py

  1. import numpy as np
  2. import torch
  3. import torch.nn.functional as F
  4. import time
  5. import random
  6. from collections import Counter
  7. # 1.1 定义基本的工具函数
  8. RANDOM_SEED = 123
  9. torch.manual_seed(RANDOM_SEED)
  10. DEVICE = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
  11. def elapsed(sec): # 计算时间函数
  12. if sec < 60:
  13. return str(sec) + "sec"
  14. elif sec<(60*60):
  15. return str(sec/60) + "min"
  16. else:
  17. return str(sec/(60*60)) + "hour"
  18. training_file = 'csv_list/wordstest.txt' # 定义样本文件
  19. #中文字
  20. def get_ch_label(txt_file): # 提取样本中的汉字
  21. labels = ""
  22. with open(txt_file,'rb') as f :
  23. for label in f:
  24. labels = labels + label.decode("gb2312",errors = 'ignore')
  25. return labels
  26. #中文多文件
  27. def readalltxt(txt_files): # 处理中文
  28. labels = []
  29. for txt_file in txt_files:
  30. target = get_ch_label(txt_file)
  31. labels.append(target)
  32. return labels
  33. # 将汉子转化成向量,支持文件和内存对象里的汉字转换
  34. def get_ch_label_v(txt_file,word_num_map,txt_label = None):
  35. words_size = len(word_num_map)
  36. to_num = lambda word:word_num_map.get(word,words_size)
  37. if txt_file != None:
  38. txt_label = get_ch_label(txt_file)
  39. # 使用to_num()实现单个汉子转成向量的功能,如果没有该汉字,则将返回words_size(值为69)
  40. labels_vector = list(map(to_num,txt_label)) # 将汉字列表中的每个元素传入到to_num()进行转换
  41. return labels_vector
  42. # 1.2 样本预处理
  43. training_data = get_ch_label(training_file)
  44. print("加载训练模型中")
  45. print("该样本长度:",len(training_data))
  46. counter = Counter(training_data)
  47. words = sorted(counter)
  48. words_size = len(words)
  49. word_num_map = dict(zip(words,range(words_size)))
  50. print("字表大小:",words_size)
  51. wordlabel = get_ch_label_v(training_file,word_num_map)
  52. # 加载训练模型中
  53. # 该样本长度: 75
  54. # 字表大小: 41(去重)
  55. # 1.3 构建循环神经网络(RNN)模型
  56. class GRURNN(torch.nn.Module):
  57. def __init__(self,word_size,embed_dim,hidden_dim,output_size,num_layers):
  58. super(GRURNN, self).__init__()
  59. self.num_layers = num_layers
  60. self.hidden_dim = hidden_dim
  61. self.embed = torch.nn.Embedding(word_size,embed_dim)
  62. # 定义一个多层的双向层:
  63. # 预测结果:形状为[序列,批次,维度hidden_dim×2],因为是双向RNN,故维度为hidden_dim
  64. # 序列状态:形状为[层数×2,批次,维度hidden_dim]
  65. self.gru = torch.nn.GRU(input_size=embed_dim,
  66. hidden_size=hidden_dim,
  67. num_layers=num_layers,bidirectional=True)
  68. self.fc = torch.nn.Linear(hidden_dim *2,output_size)# 全连接层,充当模型的输出层,用于对GRU输出的预测结果进行处理,得到最终的分类结果
  69. def forward(self,features,hidden):
  70. embeded = self.embed(features.view(1,-1))
  71. output,hidden = self.gru(embeded.view(1,1,-1),hidden)
  72. # output = self.attention(output)
  73. output = self.fc(output.view(1,-1))
  74. return output,hidden
  75. def init_zero_state(self): # 对于GRU层状态的初始化,每次迭代训练之前,需要对GRU的状态进行清空,因为输入的序列是1,故torch.zeros的第二个参数为1
  76. init_hidden = torch.zeros(self.num_layers*2,1,self.hidden_dim).to(DEVICE)
  77. return init_hidden
  78. # 1.4 实例化模型类,并训练模型
  79. EMBEDDING_DIM = 10 # 定义词嵌入维度
  80. HIDDEN_DIM = 20 # 定义隐藏层维度
  81. NUM_LAYERS = 1 # 定义层数
  82. # 实例化模型
  83. model = GRURNN(words_size,EMBEDDING_DIM,HIDDEN_DIM,words_size,NUM_LAYERS)
  84. model = model.to(DEVICE)
  85. optimizer = torch.optim.Adam(model.parameters(),lr=0.005)
  86. # 定义测试函数
  87. def evaluate(model,prime_str,predict_len,temperature=0.8):
  88. hidden = model.init_zero_state().to(DEVICE)
  89. predicted = ""
  90. # 处理输入语义
  91. for p in range(len(prime_str) -1):
  92. _,hidden = model(prime_str[p],hidden)
  93. predicted = predicted + words[predict_len]
  94. inp = prime_str[-1] # 获得输入字符
  95. predicted = predicted + words[inp]
  96. #按照指定长度输出预测字符
  97. for p in range(predict_len):
  98. output,hidden = model(inp,hidden) # 将输入字符和状态传入模型
  99. # 从多项式中分布采样
  100. # 在测试环境下,使用温度的参数和指数计算对模型的输出结果进行微调,保证其数值是大于0的数,小于0,torch.multinomial()会报错
  101. # 同时,使用多项式分布的方式进行采样,生成预测结果
  102. output_dist = output.data.view(-1).div(temperature).exp()
  103. inp = torch.multinomial(output_dist,1)[0] # 获取采样结果
  104. predicted = predicted + words[inp] # 将索引转化成汉字保存在字符串中
  105. return predicted
  106. # 定义参数并训练
  107. training_iters = 5000
  108. display_step = 1000
  109. n_input = 4
  110. step = 0
  111. offset = random.randint(0,n_input+1)
  112. end_offset = n_input + 1
  113. while step < training_iters: # 按照迭代次数训练模型
  114. start_time = time.time() # 计算起始时间
  115. #随机取一个位置偏移
  116. if offset > (len(training_data)-end_offset):
  117. offset = random.randint(0,n_input+1)
  118. # 制作输入样本
  119. inwords = wordlabel[offset:offset+n_input]
  120. inwords = np.reshape(np.array(inwords),[n_input,-1,1])
  121. # 制作标签样本
  122. out_onehot = wordlabel[offset+1:offset+n_input+1]
  123. hidden = model.init_zero_state() # RNN的状态清零
  124. optimizer.zero_grad()
  125. loss = 0.0
  126. inputs = torch.LongTensor(inwords).to(DEVICE)
  127. targets = torch.LongTensor(out_onehot).to(DEVICE)
  128. for c in range(n_input): # 按照输入长度将样本预测输入模型并进行预测
  129. outputs,hidden = model(inputs[c],hidden)
  130. loss = loss + F.cross_entropy(outputs,targets[c].view(1))
  131. loss = loss / n_input
  132. loss.backward()
  133. optimizer.step()
  134. # 输出日志
  135. with torch.set_grad_enabled(False):
  136. if (step+1)%display_step == 0 :
  137. print(f'Time elapesd:{(time.time() - start_time)/60:.4f}min')
  138. print(f'step {step + 1}|Loss {loss.item():.2f}\n\n')
  139. with torch.no_grad():
  140. print(evaluate(model,inputs,32),'\n')
  141. print(50*'=')
  142. step = step +1
  143. # 每次迭代结束,将偏移值相后移动n_input+1个距离单位,可以保证输入数据的样本相互均匀,否则会出现文本两边的样本训练次数较少的情况。
  144. offset = offset + (n_input+1)
  145. print("Finished!")
  146. # 1.5 运行模型生成句子
  147. while True:
  148. prompt = "输入几个文字:"
  149. sentence = input(prompt)
  150. inputword = sentence.strip()
  151. try:
  152. inputword = get_ch_label_v(None,word_num_map,inputword)
  153. keys = np.reshape(np.array(inputword),[len(inputword),-1,1])
  154. # get_ch_label_v()中,如果在字典中找不到对应的索引,就会为其分配一个无效的索引值,
  155. # 进而在 evaluate()函数中调用模型的时,差不多对应对的有效词向量而终止报错
  156. model.eval()
  157. with torch.no_grad():
  158. sentence = evaluate(model,torch.LongTensor(keys).to(DEVICE),32)
  159. print(sentence)
  160. except: # 异常处理,当输入的文字不在模型字典中时,系统会报错,有意设置,防止输入超范围的字词
  161. print("还没学会")

模型结果:训练的并不好,但是还能用

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号