RNN循环神经网络
实现文本预测
RNN原理:
A -> Model -> B -> Model -> C -> …
所形成的句子[A, B, C, …]
先看结果:
预测 -> "不分开"
不分开 还知道多难 你心烦叫你也才有些天 别 爱着还是 说 外场米渐我知道 自己会太多走不走 一颗三废都不要我手 这样也和义 自己的受伤 让我刮 小用往 只要女生活能记不多 那果有在想要你们过去 我不能怕做 你否定不你 脑诉为要承透 融必我遇见你还是微笑 自然却没要让你看着我 想知到你去一遍想办得模样 我不开 小情掉在我情球 断于的弦 再怎么接 又上不见 你很快化没是 我不能听多久 如果说你的一样伴
训练情况:
结果是挺不满意的, 毕竟理解不是很深
基本思路框架
和以前线性模型比较相似
- 加载数据 – 处理数据
- 设置独热编码
- 分批数据
- 建立模型
- 训练模型
- 预测
正常导包
import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
加载数据, 自己找篇文章啥的都可以
提前把"\n"和"\t"这两个字符转为空格
设置可以由文字转数字, 数字转文字的字典
encoded 就是我们的训练数据, 也就是一列表的的数字, 方便onehot矩阵的实现
with open("../input/jaychou_lyrics4703/jaychou_lyrics.txt", "r", encoding="utf-8") as f:
text = f.readlines()
text = "".join(text).replace("\n", " ").replace("\t", " ")[:30000]
char = list(set(text))
char_to_int = {c: i for i, c in enumerate(char)}
int_to_char = {i: c for i, c in enumerate(char)}
vocab_size = len(char_to_int)
encoded = [char_to_int[i] for i in text]
onehot 将输入的文本(刚刚已经转成数字了)实现成矩阵, 以便训练
onehot会输出一个二维矩阵
例: {a: 0, b: 1, c: 2}
input = “bac”
return [[0, 1, 0],
[1, 0, 0],
[0, 0, 1]]
def one_hot(text, n_class):
"""
text: 序列串, tensor
n_class: chars(dict) 长度
"""
res = torch.zeros(text.shape[0], n_class, dtype=torch.float)
res.scatter_(1, text.long().view(-1, 1), 1.)
return res
接下来分批数据
相似 torch.utils.data.DataLoader
返回一个生成器, 包含feature, label
def get_batches(data, batch_size, steps):
"""
data: 需要分割的数据 tensor
batch: 样本批次大小
steps: 每个样本的长度
"""
time_step = len(data) // (batch_size * steps)
data = data[: time_step * batch_size * steps]
data = data.view(batch_size, -1)
for i in range(time_step):
i *= steps
x = data[:, i: i + steps]
y = data[:, i + 1: i + steps + 1]
if x.shape == y.shape:
yield x, y
建立LSTM模型
建议参考官网nn.LSTM
这里使用的是lstm多层循环神经网络
dropout 就是为了防止过拟合使用的丢弃法
init_h_state函数是用来在训练模型时初始化h
class Model(nn.Module):
def __init__(self, inputs, hidden, outputs, layers):
super(Model, self).__init__()
self.inputs = inputs
self.hidden = hidden
self.outputs = outputs
self.layers = layers
self.rnn = nn.LSTM(self.inputs, self.hidden, self.layers, batch_first=True)
self.fc = nn.Linear(self.hidden, self.outputs)
self.dropout = nn.Dropout(.3)
def forward(self, x, h):
r_out, h = self.rnn(x, h)
out = self.dropout(r_out)
out = out.view(-1, self.hidden)
out = self.fc(out)
return out, h
def init_h_state(self, batch_size):
weight = next(self.parameters()).data
h = (weight.new(self.layers, batch_size, self.hidden).zero_(),
weight.new(self.layers, batch_size, self.hidden).zero_())
return h
预测函数
根据rnn原理去提取向量, 最后转成文字输出
def predict_to_word(net, text, pre_size, n_class):
h = None
out = [char_to_int[text[0]]]
for t in range(pre_size + len(text) - 1):
x = one_hot(torch.tensor([out[-1]]), n_class).view(1, 1, -1)
res, h = net(x, h)
if t < len(text) - 1:
out.append(char_to_int[text[t + 1]])
else:
out.append(res.argmax().item())
return "".join([int_to_char[i] for i in out])
训练模型
和线性回归相似, 就是这个h
h是以前的时间步留下的信息
def train(net, data, vocab_size,batch_size=8, steps=64,
epochs=30, lr=.001):
loss = nn.CrossEntropyLoss()
optimizer = optim.Adam(net.parameters(), lr=lr)
for epoch in range(epochs):
h = net.init_h_state(batch_size)
losses = 0.0
n = 0
for x, y in get_batches(data, batch_size, steps):
h = tuple([each.data for each in h])
x = x.reshape(-1, 1)
x = one_hot(x, vocab_size)
x = x.view(batch_size, steps, -1)
out, h = net(x, h)
l = loss(out, y.reshape(batch_size * steps).long())
losses += l.mean().item()
net.zero_grad()
l.backward()
nn.utils.clip_grad_norm_(net.parameters(), 5)
optimizer.step()
n += y.shape[0]
if (epoch + 1) % 20 == 0:
print(f"Epoch: {epoch + 1}\n"
f"Loss: {losses / n}")
print("Pre: ", predict_to_word(net, "分开", 20, vocab_size), "\n")
print("Over")
预处理参数:
将列表转换成tensor
初始化超参数
这里使用两层循环神经网络
encoded = torch.tensor(encoded, dtype=torch.float)
inputs = vocab_size
hidden = 256
outputs = vocab_size
layers = 2
model = Model(inputs, hidden, outputs, layers)
train(model, encoded, vocab_size, epochs=200)
最后就是那令人不太满意的结果了
希望能更进一步
来源:CSDN
作者:Crazy - ?
链接:https://blog.csdn.net/weixin_46310125/article/details/104342689