楼主: Nicolle
246 17

【独家发布】如何用RNN生成莎士比亚风格的句子? [推广有奖]

版主

巨擘

0%

还不是VIP/贵宾

-

TA的文库  其他...

Python Programming

SAS Programming

Must-Read Books

威望
16
论坛币
12297978 个
通用积分
117.6507
学术水平
2966 点
热心指数
2981 点
信用等级
2778 点
经验
450185 点
帖子
20590
精华
92
在线时间
7930 小时
注册时间
2005-4-23
最后登录
2019-8-25

Nicolle 学生认证  发表于 2019-8-11 04:55:51 |显示全部楼层
本帖最后由 Nicolle 于 2019-8-11 05:00 编辑

数据准备
在 data/names 目录下有 18 个文本文件,命名规范为 [国家].txt。每个文件的每一行都是一个姓名。此外,实现了一个 unicode_to_ascii 转换,把诸如 à 之类转换成 a。最终得到一个字典category_lines,language: [names ...]。key 是语言名,value 是姓名的列表。all_letters 里保存所有的字符。
  1. import glob        
  2. all_filenames = glob.glob('../data/names/*.txt')        
  3. print(all_filenames)        
  4. import unicodedata        
  5. import string        
  6. all_letters = string.ascii_letters + " .,;'"        
  7. n_letters = len(all_letters)        
  8. def unicode_to_ascii(s):        
  9. return ''.join(        
  10. c for c in unicodedata.normalize('NFD', s)        
  11. if unicodedata.category(c) != 'Mn'        
  12. and c in all_letters        
  13. )        
  14. print(unicode_to_ascii('฀lusàrski'))        
  15. category_lines = {}        
  16. all_categories = []        
  17. def readLines(filename):        
  18. lines = open(filename).read().strip().split('\n')        
  19. return [unicode_to_ascii(line) for line in lines]        
  20. for filename in all_filenames:        
  21. category = filename.split('/')[-1].split('.')[0]        
  22. all_categories.append(category)        
  23. lines = readLines(filename)        
  24. category_lines[category] = lines        
  25. n_categories = len(all_categories)        
  26. print('n_categories =', n_categories)
复制代码



本帖被以下文库推荐

stata SPSS
Nicolle 学生认证  发表于 2019-8-11 04:57:13 |显示全部楼层

把姓名从字符串变成 Tensor


现在我们已经把数据处理好了,接下来需要把姓名从字符串变成 Tensor,因为机器学习只能处理数字。我们使用“one-hot”的表示方法表示一个字母。这是一个 (1, n_letters) 的向量,对应字符的下标为 1,其余为 0。对于一个姓名,用大小为 (line_length, 1, n_letters) 的 Tensor 来表示。第二维表示样本(batch)大小,因为 PyTorch 的 RNN 要求输入格式是 (time, batch, input_features)。
  1. import torch        
  2. # 把一个字母变成<1 x n_letters> Tensor        
  3. def letter_to_tensor(letter):        
  4. tensor = torch.zeros(1, n_letters)        
  5. letter_index = all_letters.find(letter)        
  6. tensor[0][letter_index] = 1        
  7. return tensor        
  8. # 把一行(姓名)转换成<line_length x 1 x n_letters>的Tensor        
  9. def line_to_tensor(line):        
  10. tensor = torch.zeros(len(line), 1, n_letters)        
  11. for li, letter in enumerate(line):        
  12. letter_index = all_letters.find(letter)        
  13. tensor[li][0][letter_index] = 1        
复制代码
回复

使用道具 举报

Nicolle 学生认证  发表于 2019-8-11 04:59:42 |显示全部楼层
创建网络[img]https://ss.csdn.net/p?https://mmbiz.qpic.cn/mmbiz_png/ ... pSfg/640?wx_fmt=png[/img]如果想“手动”创建网络,那么在 PyTorch 里创建 RNN 和全连接网络的代码并没有太大差别。因为 PyTorch 的计算图是动态实时编译的,不同 time-step 的 for 循环不需要“内嵌”在 RNN里。每个训练数据即使长度不同也没有关系,因为计算图每次都是根据当前的数据长度“实时”编译出来的。这个网络结构使用了两个全连接层:一个用于计算新的 hidden;另一个用于计算当前的输出。定义网络的代码如下:
  1. import torch.nn as nn        
  2. from torch.autograd import Variable        
  3. class RNN(nn.Module):        
  4. def __init__(self, input_size, hidden_size, output_size):        
  5. super(RNN, self).__init__()        
  6. self.input_size = input_size        
  7. self.hidden_size = hidden_size        
  8. self.output_size = output_size        
  9. self.i2h = nn.Linear(input_size + hidden_size, hidden_size)        
  10. self.i2o = nn.Linear(input_size + hidden_size, output_size)        
  11. self.Softmax = nn.LogSoftmax(dim=1)        
  12. def forward(self, input, hidden):        
  13. combined = torch.cat((input, hidden), 1)        
  14. hidden = self.i2h(combined)        
  15. output = self.i2o(combined)        
  16. output = self.Softmax(output)        
  17. return output, hidden        
  18. def init_hidden(self):        
  19. return Variable(torch.zeros(1, self.hidden_size))
复制代码

回复

使用道具 举报

Nicolle 学生认证  发表于 2019-8-11 05:02:04 |显示全部楼层
测试网络

  1. n_hidden = 128rnn = RNN(n_letters, n_hidden, n_categories)input = Variable(line_to_tensor('Albert'))hidden = Variable(torch.zeros(1, n_hidden))# 实际是遍历所有inputoutput, next_hidden = rnn(input[0], hidden)print(output)hidden=net_hidden
复制代码

回复

使用道具 举报

Nicolle 学生认证  发表于 2019-8-11 05:03:05 |显示全部楼层
准备训练
测试通过之后就可以开始训练了。训练之前,需要工具函数根据网络的输出把它变成分类,这里使用 Tensor.topk 来选取概率最大的那个下标,然后得到分类名称。
  1. def category_from_output(output):       
  2. top_n, top_i = output.data.topk(1) # Tensor out of Variable with .data       
  3. category_i = top_i[0][0]       
  4. return all_categories[category_i], category_i       
  5. print(category_from_output(output))
复制代码
回复

使用道具 举报

Nicolle 学生认证  发表于 2019-8-11 05:04:13 |显示全部楼层
挑选一个训练数据
  1. import random       
  2. def random_training_pair():       
  3. category = random.choice(all_categories)       
  4. line = random.choice(category_lines[category])       
  5. category_tensor = Variable(torch.LongTensor([all_categories.index(category)]))       
  6. line_tensor = Variable(line_to_tensor(line))       
  7. return category, line, category_tensor, line_tensor       
  8. for i in range(10):       
  9. category, line, category_tensor, line_tensor = random_training_pair()       
  10. print('category =', category, '/ line =', line)
复制代码
回复

使用道具 举报

Nicolle 学生认证  发表于 2019-8-11 05:05:50 |显示全部楼层
训练
现在我们可以训练网络了,因为 RNN 的输出已经求过对数了,所以计算交叉熵只需要选择正确的分类对应的值就可以了,PyTorch 提供了 nn.NLLLoss() 函数来实现这个目的,它实现了loss(x, class) = -x[class]。
criterion = nn.NLLLoss()
用 optimizer 而不是自己手动来更新参数,这里使用最原始的 SGD 算法。
  1. learning_rate = 0.005       
  2. optimizer = torch.optim.SGD(rnn.parameters(), lr=learning_rate
  3. 训练的每个循环工作内容解释(伪代码)如下:
  4. 创建输入和输出Tensor       
  5. 创建初始化为零的隐藏状态Tensor       
  6. for each letter in 输入Tensor:       
  7. output, hidden=rnn(input,hidden)       
  8. 计算loss       
  9. backward计算梯度       
  10. optimizer.step       
  11. def train(category_tensor, line_tensor):       
  12. rnn.zero_grad()       
  13. hidden = rnn.init_hidden()       
  14. for i in range(line_tensor.size()[0]):       
  15. output, hidden = rnn(line_tensor[i], hidden       
  16. loss = criterion(output, category_tensor)       
  17. loss.backward()       
  18. optimizer.step()       
  19. return output, loss.data[0]
复制代码
回复

使用道具 举报

Nicolle 学生认证  发表于 2019-8-11 05:06:36 |显示全部楼层
用训练数据来训练
  1. import time       
  2. import math       
  3. n_epochs = 100000       
  4. print_every = 5000       
  5. plot_every = 1000       
  6. current_loss = 0       
  7. all_losses = []       
  8. def time_since(since):       
  9. now = time.time()       
  10. s = now - since       
  11. m = math.floor(s / 60)       
  12. s -= m * 60       
  13. return '%dm %ds' % (m, s)       
  14. start = time.time()       
  15. for epoch in range(1, n_epochs + 1):       
  16. # 随机选择一个样本       
  17. category, line, category_tensor, line_tensor = random_training_pair()       
  18. output, loss = train(category_tensor, line_tensor)       
  19. current_loss += loss       
  20. if epoch % print_every == 0:       
  21. guess, guess_i = category_from_output(output)       
  22. correct = '฀' if guess == category else '฀ (%s)' % category       
  23. print('%d %d%% (%s) %.4f %s / %s %s' % (epoch, epoch / n_epochs * 100,       
  24. time_since(start), loss, line, guess, correct))       
  25. if epoch % plot_every == 0:       
  26. all_losses.append(current_loss / plot_every)       
  27. current_loss = 0
复制代码
回复

使用道具 举报

Nicolle 学生认证  发表于 2019-8-11 05:07:06 |显示全部楼层
绘图
  1. 把所有的损失都绘制出来,以显示学习的过程,如图 4.5 所示。
  2. import matplotlib.pyplot as plt       
  3. import matplotlib.ticker as ticker       
  4. %matplotlib inline       
  5. plt.figure()       
  6. plt.plot(all_losses)
复制代码
回复

使用道具 举报

Nicolle 学生认证  发表于 2019-8-11 05:08:19 |显示全部楼层
查看模型的效果
# 混淆矩阵       
  1. confusion = torch.zeros(n_categories, n_categories)       
  2. n_confusion = 10000       
  3. def evaluate(line_tensor):       
  4. hidden = rnn.init_hidden()       
  5. for i in range(line_tensor.size()[0]):       
  6. output, hidden = rnn(line_tensor[i], hidden)       
  7. return output       
  8. # 从训练数据里随机采样       
  9. for i in range(n_confusion):       
  10. category, line, category_tensor, line_tensor = random_training_pair()       
  11. output = evaluate(line_tensor)       
  12. guess, guess_i = category_from_output(output)       
  13. category_i = all_categories.index(category)       
  14. confusion[category_i][guess_i] += 1       
  15. # 归一化       
  16. for i in range(n_categories):       
  17. confusion[i] = confusion[i] / confusion[i].sum()       
  18. fig = plt.figure()       
  19. ax = fig.add_subplot(111)       
  20. cax = ax.matshow(confusion.numpy())       
  21. fig.colorbar(cax)       
  22. # 设置x轴的文字往上走       
  23. ax.set_xticklabels([''] + all_categories, rotation=90)       
  24. ax.set_yticklabels([''] + all_categories)       
  25. ax.xaxis.set_major_locator(ticker.MultipleLocator(1))       
  26. ax.yaxis.set_major_locator(ticker.MultipleLocator(1))       
  27. plt.show()
复制代码
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 我要注册

京ICP备16021002-2号 京B2-20170662号 京公网安备 11010802022788号 论坛法律顾问:王进律师 知识产权保护声明   免责及隐私声明

GMT+8, 2019-8-25 08:55