【LSTM文本生成器】动手写一个自动生成文章的AI,附完整代码

【LSTM文本生成器】写一个自动生成文章的AI,代码完整

我是@老K玩码,不是著名的IT企业家。专注分享实战项目和最新行业资讯,已分享1000多个实战项目!

0. 前言

长短期记忆网络(LSTM)是一种优化的循环神经网络(RNN)。通过对神经元设置更新门、遗忘门和输出门,可以有效避免长序列参数传递过程中由于梯度消失而丢失有效历史信息的问题。

LSTM 的工作原理如下:

当写成公式时,可以写成这样的形式:

$ \widetilde{c}^{} = tanh(W_c[a^{}, x^{}] + b_c) $

$ \Gamma_u = \sigma(W_u[a^{}, x^{}] + b_u) $

$ \Gamma_f = \sigma(W_f[a^{}, x^{}] + b_f) $

$ \Gamma_o​​ ​​= \sigma(W_o[a^{}, x^{}] + b_o) $

$ c^{} = \Gamma_u * \widetilde{c}^{} + \Gamma_f * c^{} $

$ a^{} = \Gamma_o​​ ​​* tanh(c^{}) $

LSTM的详细介绍,建议大家参考大神,或者我之前的文章。

理论知识晦涩难懂,结合实际项目学习会事半功倍。在这里,老K分享一个具体应用场景的项目——文本生成器中文关键词生成器,供大家学习和实践。

1. 准备

在开始代码之前,首先将需要的第三方库一一导入到项目中:

import torch
import torch.nn as nn
from torch.nn.utils import clip_grad_norm_
import jieba
from tqdm import tqdm

2.设计类和函数2.1 字典映射表

我们设计了一个名为 Dictionary 的类来建议单词和索引的映射表。

class Dictionary(object):
    def __init__(self):
        self.word2idx = {}
        self.idx2word = {}
        self.idx = 0
    def __len__(self):
        return len(self.word2idx)
    def add_word(self, word):
        if not word in self.word2idx:
            self.word2idx[word] = self.idx
            self.idx2word[self.idx] = word
            self.idx += 1

2.2 语料库

我们得到的语料是一个字符串,需要将其编码成计算机可以操作的数值,才能学习神经网络模型。

所以我们设计了一个语料库类,专门用来对文本数据进行数字化和矢量化。

class Corpus(object):
    def __init__(self):
        self.dictionary = Dictionary()
    def get_data(self, path, batch_size=20):
        # step 1
        with open(path, 'r', encoding="utf-8") as f:
            tokens = 0
            for line in f.readlines():
                words = jieba.lcut(line) + ['']
                tokens += len(words)
                for word in words:
                    self.dictionary.add_word(word)
        # step 2
        ids = torch.LongTensor(tokens)
        token = 0
        with open(path, 'r', encoding="utf-8") as f:
            for line in f.readlines():
                words = jieba.lcut(line) + ['']
                for word in words:
                    ids[token] = self.dictionary.word2idx[word]
                    token += 1
        # step 3
        num_batches = ids.size(0) // batch_size
        ids = ids[:num_batches * batch_size]
        ids = ids.view(batch_size, -1)
        return ids

2.3 架构 LSTM 模型

我们将从 torch.nn 继承 Module 类并将其设置为训练整个循环神经网络

class LSTMmodel(nn.Module):
    def __init__(self, vocab_size, embed_size, hidden_size, num_layers):
        super(LSTMmodel, self).__init__()
        self.embed = nn.Embedding(vocab_size, embed_size)
        self.lstm = nn.LSTM(embed_size, hidden_size, num_layers, batch_first=True)
        self.linear = nn.Linear(hidden_size, vocab_size)
    def forward(self, x, h):
        x = self.embed(x)
        out, (h, c) = self.lstm(x, h)
        out = out.reshape(out.size(0) * out.size(1), out.size(2))
        out = self.linear(out)
        return out, (h, c)

进行培训

有了以上基础,我们就可以训练我们的模型了

embed_size = 128
hidden_size = 1024
num_layers = 1
num_epochs = 5
batch_size = 50
seq_length = 30
learning_rate = 0.001
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

我们首先设置将用于训练的参数变量:

corpus = Corpus()
ids = corpus.get_data('sgyy.txt', batch_size)
vocab_size = len(corpus.dictionary)

接下来中文关键词生成器,我们通过语料库的get_data方法读取语料库,并对数据进行必要的预处理

model = LSTMmodel(vocab_size, embed_size, hidden_size, num_layers).to(device)
cost = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)

在这里,我们实例化训练所需的完整结构:

for epoch in range(num_epochs):
    states = (torch.zeros(num_layers, batch_size, hidden_size).to(device),
              torch.zeros(num_layers, batch_size, hidden_size).to(device))
    for i in tqdm(range(0, ids.size(1) - seq_length, seq_length)):
        inputs = ids[:, i:i+seq_length].to(device)
        targets = ids[:, (i+1):(i+1)+seq_length].to(device)
        states = [state.detach() for state in states]
        outputs, states = model(inputs, states)
        loss = cost(outputs, targets.reshape(-1))
        model.zero_grad()
        loss.backward()
        clip_grad_norm_(model.parameters(), 0.5)
        optimizer.step()

这是主循环,呈现了训练的主要逻辑:

生成文章

当模型经过上述过程并完成训练后,我们就可以使用训练好的模型自动生成文章。

num_samples = 300
article = str()
state = (torch.zeros(num_layers, 1, hidden_size).to(device),
        torch.zeros(num_layers, 1, hidden_size).to(device))
prob = torch.ones(vocab_size)
_input = torch.multinomial(prob, num_samples=1).unsqueeze(1).to(device)

让我们先做一些初始化:

for i in range(num_samples):
    output, state = model(_input, state)
    prob = output.exp()
    word_id = torch.multinomial(prob, num_samples=1).item()
    _input.fill_(word_id)
    word = corpus.dictionary.idx2word[word_id]
    word = '\n' if word == '' else word
    article += word
print(article)

通过主循环,实现自动生成文本的功能:

总结

通过上面的方法,我们可以让LSTM模型自动为我们生成一些文章文本。

以下是我以《三国志》为语料的epoch训练后得到的模型自动生成的文本:

夏侯渊引项城濬赵云南山。可引军将切齿韦愿往插可借张引兵哨探,—不酿得中,崩寄臣居民而立。奂,降旗转加司徒王允,便赏先主姜维所讫坐定细作,傍若无人兵迎践踏。关公陇来报为兵战为,因小疮大进张飞。
且说可怜何进,正见遂通晓孔明马超亦之孙拜而出波浪袁术。病故入献酒食这,至丙寅日。孔明曰:“之处是朱灵同心?”操曰:“张翼德等。时定军山也。吾而定乘他府,蜀兵实为也?死罪相助禳,楮并举良谋乎为即命?问时满宠精兵姜维兵,山坚守殃及,不十合坐者不满火归坐守,选长叹、曹军入从吞并,果是痛饮、护卫军、公当速、众韩关之所学门、质入彪,只得三万,跃起潘隐谓,肩同归中。鼓噪托病赵彦杀,三声已危数十字子翼,杀入破绽飞乃入,皆创立大半六年人口。左右军,皆不能使人往去
关公横截樊城。众军击班者黄门其肉都督隆冬事截杀。忽起凋残营寨。望此不到别船刺臂,今卓齐自于所舵,然后虎豹曰:“兄为何人,秋天追夺术之功,乃大魏听令经典,不忧姜维归之今蜀兵名将。今晚之精兵。”荆棘甚妙之。允曰:“吾与将军归家好以此?”遂夏侯拜谢扬妻女而。两阵徐州惰慢兵。操大惊,引曹洪领进酒具言前,不觉两军两军会小校,坛自守。操曰:“何不同在关某!”分付平:“此医与文长阴平探其防护以金帛同扶。”
黄忠孙先锋齐声见山谷,军吏小匣冬投百步成万。彧魏军曰:“贵人为红旗来!”武士膂力过人颈曰:“各引东方之心休道,献深感而相府石,使子分外将矣,难芳引路。”后人知事美髯与允并素闻密授而去。
建安荀彧改正引路。正是校尉造饭陆口守豫州动,貂蝉蒯越曰:“三处,良苦汉高祖;今不能成大功草芥,俱杀此人,安出城之辱不得、投;岸去李辅围为此如,怎敢以三人部麾抚慰矣。”孔明曰审钧意冒死慌救入引兵。布苏,壮士利斧从江众之,娱情以赐赞徐往吕旷去,班部艾。华阴羕。禳欲攻亮出受敌。偿命之,兵败将亡汉中披挂。
且说至,邓艾自大半不分昼夜至。

需要语料的可以私信我关键词/RNN/接收。

通过上面的例子,我们可以发现,只要经过一层神经网络和一轮epoch训练,就可以生成一篇似是而非的文章。

我们或许可以通过以下方式进一步优化输出文本的结果:

上述方法不一定会给模型带来更好的结果,仍然存在过拟合或其他问题的情况。您可以根据代码自行尝试优化。

希望大家可以基于这个项目做一个优秀的文本生成器。

关于作者

我是@老K玩码,不是著名的IT企业家。专注分享实战项目和最新行业资讯,已分享1000多个实战项目!

全网同名,欢迎大家通过各种渠道与我交流。

如果需要源码或者对RNN感兴趣,可以私信关键词/RNN/,获取更多相关信息。

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论