自然语言处理(21:(第六章1.)基于RNN生成文本)

系列文章目录

第六章 1:基于RNN生成文本

第六章 2:seq2seq模型的实现

第六章 3:seq2seq模型的改进

第六章 4:seq2seq模型的应用


文章目录

系列文章目录

文章目录

前言

使用语言模型生成文本

1.使用RNN生成文本的步骤 

2.文本生成的实现

3.更好的文本生成


前言

在第4章和第5章中,我们仔细研究了RNN和LSTM的结构及其实现。 现在我们已经在代码层面理解了它们。在本章,RNN和LSTM将大显身手, 我们将利用LSTM实现几个有趣的应用。


使用语言模型生成文本

首先,本章将使用语言模型进行文本生成。具体来说,就是使用在语料库上训练好的语言模型生成新的文本。然后,我们将了解如何使用改进过的 语言模型生成更加自然的文本。通过这项工作,我们可以(简单地)体验基于AI的文本创作

另外,本章还会介绍一种结构名为seq2seq的新神经网络。seq2seq是 "(from) sequence to sequence"(从时序到时序)的意思,即将一个时序数 据转换为另一个时序数据。通过组合两个RNN,可以轻松实现seq2seq。seq2seq可以应用于多个应用,比如机器翻译、聊天机器人 和邮件自动回复等。

如前所述,语言模型可用于各 种各样的应用,其中具有代表性的例子有机器翻译、语音识别和文本生成。 这里,我们将使用语言模型来生成文本。

1.使用RNN生成文本的步骤

在上一章中,我们使用LSTM层实现了语言模型,这个语言模型的网络结构如下图所示。顺便说一下,我们还实现了整体处理(T个)时序数据的Time LSTM层和Time Affine层。

现在我们来说明一下语言模型生成文本的顺序。这里仍以"you say goobye and i say hello."这一在语料库上学习好的语言模型为例,考虑将单词i赋给这个语言模型的情况。此时,这个语言模型输出下图中的概率分布。

语言模型根据已经出现的单词输出下一个出现的单词的概率分布。在上图的例子中,语言模型输出了当给定单词i时下一个出现的单词的概率分 布。那么,它如何生成下一个新单词呢? 一种可能的方法是选择概率最高的单词 。在这种情况下,因为选择的是概率最高的单词,所以结果能唯一确定 。也就是说,这是一种"确定性的" 方法。另一种方法是"概率性地"进行选择 。根据概率分布进行选择,这样概率高的单词容易被选到 ,概率低的单词难以被选到。在这种情况下,被选到的单词(被采样到的单词)每次都不一样。(这不就是大模型中的输出方式吗?)

这里我们想让每次生成的文本有所不同,这样一来,生成的文本富有变化,会更有趣。因此,我们通过后一种方法(概率性地选择的方法)来选择单词。回到我们的例子中,如下图所示,假设(概率性地)选择了单词say。

上图中显示了根据概率分布进行采样后结果为say的例子。在上图的概率分布中,say的概率最高,所以它被采样到的概率也最高。不过请注意,这里选到say并不是必然的(不是确定性的),而是概率性的。因此,say以外的其他单词根据出现的概率也可能被采样到。

接下来,采样第2个单词。这只需要重复一下刚才的操作。也就是说, 将生成的单词say输入语言模型,获得单词的概率分布,然后再根据这个概 率分布采样下一个出现的单词,如下图所示。

之后根据需要重复此过程即可(或者直到出现<EOS>这一结尾记号)。这样一来,我们就可以生成新的文本。 这里需要注意的是,像上面这样生成的新文本是训练数据中没有的新生成的文本。因为语言模型并不是背诵了训练数据,而是学习了训练数据中单词的排列模式。如果语言模型通过语料库正确学习了单词的出现模式,我们就可以期待该语言模型生成的文本对人类而言是自然的、有意义的。

2.文本生成的实现

下面我们进行文本生成的实现。这里基于上一章实现的Rnnlm类(链接在此自然语言处理(19:(第五章4.)使用LSTM的语言模型)-CSDN博客)来创建继承自它的RnnlmGen类,然后向这个类添加生成文本的方法

python 复制代码
import numpy as np
from common.functions import softmax
from ch06.rnnlm import Rnnlm
from ch06.better_rnnlm import BetterRnnlm

class RnnlmGen(Rnnlm):
    def generate(self, start_id, skip_ids=None, sample_size=100):
        word_ids = [start_id]

        x = start_id
        while len(word_ids) < sample_size:
            x = np.array(x).reshape(1, 1)
            score = self.predict(x)
            p = softmax(score.flatten())

            sampled = np.random.choice(len(p), size=1, p=p)
            if (skip_ids is None) or (sampled not in skip_ids):
                x = sampled
                word_ids.append(int(x))

        return word_ids

这个类用generate(start_id, skip_ids, sample_size) 生成本文。此处, 参数start_id是第1个单词ID,sample_size表示要采样的单词数量。另外, 参数skip_ids是单词ID列表(比如,[12, 20]),它指定的单词将不被采样。 这个参数用于排除PTB数据集中的、N等被预处理过的单词。

(PTB数据集对原始文本进行了预处理,稀有单词被替换, 数字被N替换。另外,我们用作为文本的分隔符。 )

generate() 方法首先通过model.predict(x) 输出各个单词的得分(得分是正规化之前的值),然后基于p = softmax(score),使用Softmax函数对得分进行正规化,这样就获得了我们想要的概率分布。接下来,使用 np.random.choice(),根据这个概率分布p采样下一个单词。关于np.random. choice(),搜一下就行了。

现在,使用这个RnnlmGen类进行文本生成。这里先在完全没有学习的状 态(即权重参数是随机初始值的状态)下生成文本,代码如下所示:

python 复制代码
from rnnlm_gen import RnnlmGen
from dataset import ptb


corpus, word_to_id, id_to_word = ptb.load_data('train')
vocab_size = len(word_to_id)
corpus_size = len(corpus)

model = RnnlmGen()
# model.load_params('../ch06/Rnnlm.pkl')

# 设定start单词和skip单词
start_word = 'you'
start_id = word_to_id[start_word]
skip_words = ['N', '<unk>', '$']
skip_ids = [word_to_id[w] for w in skip_words]

# 文本生成
word_ids = model.generate(start_id, skip_ids)
txt = ' '.join([id_to_word[i] for i in word_ids])
txt = txt.replace(' <eos>', '.\n')
print(txt)

这里,第1个单词是you,我们将它的单词ID设为start_id,来进行 文本生成。另外,指定不参与采样的单词为['N', "<unk>'', '$']。生成文本 的generate() 方法返回单词ID列表,因此需要将单词ID列表转化为句子。 这可以通过txt = ' '.join([id_to_word[i] for i in word_ids]) 这行代码进行。join() 方法通过"'分隔符'.join(列表)"这种形式连接单词。运行一下上面的代码,结果如下。

如你所见,输出的文本是一堆乱七八糟的单词。不过这可以理解,因为这里的模型权重使用的是随机初始值,所以输出了没有意义的文本。那么, 如果换成学习好的语言模型,结果会怎样呢?我们利用上一章学习好的权重来进行文本生成。使用model.load_params('../ch06/Rnnlm.pkl') 读入上一章学习好的权重参数,并生成文本,我们来看一下生成的文本(每次的结果都不一样)。

虽然上面的结果中可以看到多处语法错误和意思不通的地方,不过也有几处读起来已经比较像句子了。仔细看的话,这个模型正确生成了主语和动词的组合,比如"you'll include..."" there's no tires..."" knight transplants..."等。再者,它在一定程度上理解了形容词和名词的使用方法,比如"good problems"" japanese letter"等。另外,开头的"you'll include one of them a good problems."也是一个含义通顺的句子。 如上所述,上面的实验生成的文本在某种程度上可以说是正确的,不过 结果中仍有许多不自然的地方,改进空间很大。虽然不存在"完美的文章", 但是至少我们可以追求更自然的文章。为此,我们应该怎么做呢?当然是使用更好的语言模型!

3.更好的文本生成

如果有更好的语言模型,就可能有更好的文本。在上一章中,我们改进 了简单的RNNLM,实现了"更好的RNNLM",将模型的困惑度从136降至 75。现在,我们看一下这个"更好的RNNLM"生成文本的能力。

在上一章中,我们将更好的语言模型实现为了BetterRnnlm类。这里, 像刚才一样,继承这个类,并使之有生成文本的能力。代码如下:

python 复制代码
import sys
sys.path.append('..')
import numpy as np
from common.functions import softmax
from rnnlm import Rnnlm
from better_rnnlm import BetterRnnlm


class BetterRnnlmGen(BetterRnnlm):
    def generate(self, start_id, skip_ids=None, sample_size=100):
        word_ids = [start_id]

        x = start_id
        while len(word_ids) < sample_size:
            x = np.array(x).reshape(1, 1)
            score = self.predict(x).flatten()
            p = softmax(score).flatten()

            sampled = np.random.choice(len(p), size=1, p=p)
            if (skip_ids is None) or (sampled not in skip_ids):
                x = sampled
                word_ids.append(int(x))

        return word_ids

    def get_state(self):
        states = []
        for layer in self.lstm_layers:
            states.append((layer.h, layer.c))
        return states

    def set_state(self, states):
        for layer, state in zip(self.lstm_layers, states):
            layer.set_state(*state)

上面BetterRnnlmGen类的实现和刚刚的RnnlmGen类完全一样。现在,我们让这个更好的语言模型生成文本。和之前一样,第1个单词是you。这样一来,运行这段代码:

python 复制代码
# coding: utf-8
import sys
sys.path.append('..')
from common.np import *
from rnnlm_gen import BetterRnnlmGen
from dataset import ptb


corpus, word_to_id, id_to_word = ptb.load_data('train')
vocab_size = len(word_to_id)
corpus_size = len(corpus)


model = BetterRnnlmGen()
model.load_params('../ch06/BetterRnnlm.pkl') # 这个pkl权重文件得自己训练出来,不过这里我们看看就行,理解原理即可

# 设定start字符和skip字符
start_word = 'you'
start_id = word_to_id[start_word]
skip_words = ['N', '<unk>', '$']
skip_ids = [word_to_id[w] for w in skip_words]
# 文本生成
word_ids = model.generate(start_id, skip_ids)
txt = ' '.join([id_to_word[i] for i in word_ids])
txt = txt.replace(' <eos>', '.\n')

print(txt)


model.reset_state()

start_words = 'the meaning of life is'
start_ids = [word_to_id[w] for w in start_words.split(' ')]

for x in start_ids[:-1]:
    x = np.array(x).reshape(1, 1)
    model.predict(x)

word_ids = model.generate(start_ids[-1], skip_ids)
word_ids = start_ids[:-1] + word_ids
txt = ' '.join([id_to_word[i] for i in word_ids])
txt = txt.replace(' <eos>', '.\n')
print('-' * 50)
print(txt)

下述文本会被生成

可以看出,这个模型生成了比之前更自然的文本(可能有些主观)。最开始的句子"you've seen two families and the women..."正确使用了主语、动词和宾语,并且正确学习了and的使用方法(two families and the women)。其他部分读起来总体上也算说得过去。 虽然这里生成的文本仍然存在若干问题(特别是语义方面),但是从某种程度上来说,这个更好的语言模型生成了更加自然的文本。通过进一步改进这个模型,使用更大规模的语料库,应该能创造出更加自然的文本。 最后,我们尝试给这个更好的语言模型输入"the meaning of life is", 让它生成后续的单词(这是论文[35]中所做的实验)。为了做这个实验,我们按顺序向模型输入['the', 'meaning', 'of', 'life'],进行正向传播。此时不使用任何输出的结果,只是让LSTM层记住这些单词的信息。然后, 以单词is作为开始位置,生成"the meaning of life is"的后续内容。这个可以用ch07/generate_better_text.py 实现。每次实验生成的文本都不太一样,这里介绍一个有意思的结果。

如上所述,语言模型给出的回答是"人生的意义并不是一种状态良好的绘画"。虽然搞不懂什么才是"状态良好的绘画",不过说不定这其中有什么深刻的意义。

相关推荐
城电科技11 分钟前
城电科技|零碳美丽示范村建设方案 能源+景观+教育
人工智能·科技·生活·能源
opentrending5 小时前
Github 热点项目 awesome-mcp-servers MCP 服务器合集,3分钟实现AI模型自由操控万物!
服务器·人工智能·github
lisw055 小时前
DeepSeek原生稀疏注意力(Native Sparse Attention, NSA)算法介绍
人工智能·深度学习·算法
whaosoft-1436 小时前
51c深度学习~合集4
人工智能
逢生博客6 小时前
阿里 FunASR 开源中文语音识别大模型应用示例(准确率比faster-whisper高)
人工智能·python·语音识别·funasr
哲讯智能科技6 小时前
智慧能源新篇章:SAP如何赋能光伏行业数字化转型
大数据·人工智能
云卓SKYDROID6 小时前
无人机DSP处理器工作要点!
人工智能·无人机·科普·云卓科技
gang_unerry7 小时前
量子退火与机器学习(2):少量实验即可找到新材料,黑盒优化➕量子退火
人工智能·机器学习·量子计算·量子退火
訾博ZiBo7 小时前
AI日报 - 2025年4月2日
人工智能
说私域7 小时前
消费品行业创新创业中品类创新与数字化工具的融合:以开源 AI 智能客服、AI 智能名片及 S2B2C 商城小程序为例
人工智能·小程序·开源