吾本轻狂|PyTorch文本:02.使用字符级RNN生成名字( 三 )

3.2 训练神经网络和只使用最后一个时刻输出的分类任务相比 , 这次我们每一个时间序列都会进行一次预测 , 所以每一个时间序列我们都会计算损失 。
autograd 的神奇之处在于您可以在每一步中简单地累加这些损失 , 并在最后反向传播 。
criterion = nn.NLLLoss()learning_rate = 0.0005def train(category_tensor, input_line_tensor, target_line_tensor):target_line_tensor.unsqueeze_(-1)hidden = rnn.initHidden()rnn.zero_grad()loss = 0for i in range(input_line_tensor.size(0)):output, hidden = rnn(category_tensor, input_line_tensor[i], hidden)l = criterion(output, target_line_tensor[i])loss += lloss.backward()for p in rnn.parameters():p.data.add_(-learning_rate, p.grad.data)return output, loss.item() / input_line_tensor.size(0)为了跟踪训练耗费的时间 , 我添加一个timeSince(timestamp)函数 , 它返回一个人类可读的字符串:
import timeimport mathdef timeSince(since):now = time.time()s = now - sincem = math.floor(s / 60)s -= m * 60return '%dm %ds' % (m, s)训练过程和平时一样 。 多次运行训练 , 等待几分钟 , 每print_every次打印当前时间和损失 。 在all_losses中保留每plot_every次的平 均损失 , 以便稍后进行绘图 。
rnn = RNN(n_letters, 128, n_letters)n_iters = 100000print_every = 5000plot_every = 500all_losses = []total_loss = 0 # Reset every plot_every itersstart = time.time()for iter in range(1, n_iters + 1):output, loss = train(*randomTrainingExample())total_loss += lossif iter % print_every == 0:print('%s (%d %d%%) %.4f' % (timeSince(start), iter, iter / n_iters * 100, loss))if iter % plot_every == 0:all_losses.append(total_loss / plot_every)total_loss = 0输出结果:
0m 23s (5000 5%) 3.15690m 43s (10000 10%) 2.31321m 3s (15000 15%) 2.50691m 24s (20000 20%) 1.31001m 44s (25000 25%) 3.60832m 4s (30000 30%) 3.53982m 24s (35000 35%) 2.43872m 44s (40000 40%) 2.22623m 4s (45000 45%) 2.65003m 24s (50000 50%) 2.45593m 44s (55000 55%) 2.50304m 4s (60000 60%) 2.94174m 24s (65000 65%) 2.15714m 44s (70000 70%) 1.74155m 4s (75000 75%) 2.36495m 24s (80000 80%) 3.00965m 44s (85000 85%) 1.91966m 4s (90000 90%) 1.94686m 25s (95000 95%) 2.15226m 45s (100000 100%) 2.03443.3 损失数据作图从all_losses得到历史损失记录 , 反映了神经网络的学习情况:
import matplotlib.pyplot as pltimport matplotlib.ticker as tickerplt.figure()plt.plot(all_losses)4.网络采样我们每次给网络提供一个字母并预测下一个字母是什么 , 将预测到的字母继续输入 , 直到得到EOS字符结束循环 。
用输入类别、起始字母和空隐藏状态创建输入张量 。
用起始字母构建一个字符串变量 output_name
得到最大输出长度 ,
? * 将当前字母传入神经网络
? * 从前一层得到下一个字母和下一个隐藏状态
? * 如果字母是EOS , 在这里停止
? * 如果是一个普通的字母 , 添加到output_name变量并继续循环
返回最终得到的名字单词
另一种策略是 , 不必给网络一个起始字母 , 而是在训练中提供一个“字符串开始”的标记 , 并让网络自己选择起始的字母 。
max_length = 20# 来自类别和首字母的样本def sample(category, start_letter='A'):with torch.no_grad():# no need to track history in samplingcategory_tensor = categoryTensor(category)input = inputTensor(start_letter)hidden = rnn.initHidden()output_name = start_letterfor i in range(max_length):output, hidden = rnn(category_tensor, input[0], hidden)topv, topi = output.topk(1)topi = topi[0][0]if topi == n_letters - 1:breakelse:letter = all_letters[topi]output_name += letterinput = inputTensor(letter)return output_name# 从一个类别和多个起始字母中获取多个样本def samples(category, start_letters='ABC'):for start_letter in start_letters:print(sample(category, start_letter))samples('Russian', 'RUS')samples('German', 'GER')samples('Spanish', 'SPA')samples('Chinese', 'CHI')


推荐阅读