春水碧于天,画船听雨眠
实验基本要求
tensorflow2.0及以上版本
实验背景
在自然语言处理(NLP)领域,大多对话机器人的对话形成都会采用基于语料库和深度神经网络生成模型进行回答和交流。很多企业成功落地了许多产品,例如,微软小冰、Siri、谷歌翻译、淘宝智能客服系统等。文本生成的商业价值不断提升,用户的要求也不断提高,因此文本生成的研究意义重大。此外,在一些自动文本摘要和文本简化的地方,也会采用神经网络生成一段可读性强而且易于常人理解的文本。目前主流的文本生成的神经网络大多是基于循环神经网络的编码器-解码器框架,通过循环神经网络学习到序列的前后关系,能够生成一些较为通顺的文本。
本实验采用长短期记忆单元(LSTM)模型,对唐诗语句序列进行学习,得到一个简单的唐诗文本模型。整体的过程如下框图2.1所示,首先对原始数据的获取,可以提供本项目的爬虫或者自行下载的办法,获取得到想要的原始数据集。其次,想要对数据集的句子进行分词,得到词语字典。然后,数据总存在着不需要的其他字符,想要对其进行标准化,对于对英文的处理时,还需要将单词的格式统一,例如单复数形式的统一。由于计算机不能处理文字信息,在得到标准化数据之后,想要对数据进行特征提取,转换成向量的形式。比较经典的转换方式有独热(one_hot),词向量(word2vec)等方式。然后,搭建神经网络模型并且对其进行训练和测试。
实验数据下载
https://download.csdn.net/download/MARSHCW/13104886
LSTM模型分析
长短记忆单元模型 (LSTM模型)是一种改进型的RNN模型。由于RNN在计算前后变量想要累乘之前变量的梯度,所以在时序上梯度不断相乘,如果梯度值小于1,那么在累乘之后会导致梯度消失和梯度爆炸等问题。鉴于此,引入选择性记忆机制,它能够有选择性的记忆和忘却一些数据信息,学习到上下文的前后关系,二者的对比图如下2.2。
LSTM能够完成选择性记忆,是因为其引入了三个重要的门,分别是输入门、遗忘门、输出门。LSTM的一些重要的参数如下:
(1) 输入门it ,wi是待训练的参数矩阵,σ表示sigmoid函数,使门限的范围在0到1之间。
(2) 遗忘门ft ,记忆门是把上次的状态ht-1和这一次的输入xt进行比较,通过gate输出0到1的数值,其中1表示记住,0表示遗忘。
(3) 短期记忆ht ,其中ot表示需要记忆的比例,表示输出门。
(4) 长期记忆Ct,它是由两部分组成的,一部分是t时刻前的需要记忆信息和当前需要记忆的信息。
(5) 当前新知识 , 表示系统归纳出的新知识。
(6) 输出门ot,控制输出的比率。
LSTM在对序列进行记忆时,其机理比较复杂,整体的过程可以描述为以下几步:首先模型归纳出当前的新知识,其中一部分来自于之前神经网络中的短期记忆和现在输入的数据,通过二者则可以得到当前的新知识。第二步,计算遗忘门和输入门,是对数据进行有选择的记忆的关键。第三步,由遗忘门和输入门计算长期记忆Ct,并且根据输出门和长期记忆计算当前时刻的短期记忆,用于下一步的新知识计算。将这几步进行迭代,则可以得到整体的LSTM模型。
实验过程
文本预处理
在对词语编码之前,需要对原始数据集进行预处理,除去影响编码和低频的词汇。首先,需要把每一首诗歌提取出来,把其标题和不合标准的特殊字符去掉,比如书名号等。其次,分割每一首古诗,存放入列表中,方便在编码阶段进行编码。然后,将每一首古诗进行分词,将得到的词语存入列表。再对列表中的数据排序和统计每个词出现的次数,剔除出现频率较小的词语,按照出现次数由高到低排序。因为在古诗生成需要设置起始字符和停止字符,以及应对不能够识别的字符等情况,所以,需要在词表中加入特殊词。然后,因为所处理的古诗由有三万多首,所以需要将列表转换成能够高效索引的词典。最后,需要将每个词语进行编码,将文字转换成计算机可以处理的向量。这里采用了独热(one-hot)的编码方式,具体的流程如下图2.3所示。
图 2.3 数据预处理流程
编解码模型
编码器主要任务是对词语集合机型编码表示,将文字转换成向量的形式,先对文本数值化,用文本中词的字典序号来初步表示每一个词,然后经过嵌入层对文本进行编码表示。
解码器主要功能是将已经生成的数字序列转换成文本,模型起始时是起始字符[‘CLS’],在进行解码时需要去除。文本起始字符实在数据预处理时指定的,随着文本的不断生成,新生此不断连接到已生成的序列中,形成需要的文本格式的数字序列。这时,需要一个结束标志,本项目中设置的结束标志是[‘SEP’],在译码时,该标志位不会被显示。
LSTM模型设置
唐诗的格式大都比较统一,能够采用LSTM学习到比较好的参数,进而生成较为通顺的唐诗文本。
文本生成模型如下图所示
实验代码
#-*- coding:utf-8 -*-
#desc:唐诗生成
import collections
import numpy as np
import tensorflow as tf
from tensorflow.keras.models import load_model
from tensorflow.keras.utils import plot_model
import matplotlib.pyplot as plt
import matplotlib.image as mpimg # mpimg 用于读取图片
import os
#分词器,完成编码译码转换
class Tokenizer:
"""
分词器
"""
def __init__(self, token_dict):
# 词->编号的映射
self.token_dict = token_dict
# 编号->词的映射
self.token_dict_rev = {
value: key for key, value in self.token_dict.items()}
# 词汇表大小
self.vocab_size = len(self.token_dict)
def id_to_token(self, token_id):
"""
给定一个编号,查找词汇表中对应的词
:param token_id: 带查找词的编号
:return: 编号对应的词
"""
return self.token_dict_rev[token_id]
def token_to_id(self, token):
"""
给定一个词,查找它在词汇表中的编号
未找到则返回低频词[UNK]的编号
:param token: 带查找编号的词
:return: 词的编号
"""
return self.token_dict.get(token, self.token_dict['[UNK]'])
def encode(self, tokens):
"""
给定一个字符串s,在头尾分别加上标记开始和结束的特殊字符,并将它转成对应的编号序列
:param tokens: 待编码字符串
:return: 编号序列
"""
# 加上开始标记
token_ids = [self.token_to_id('[CLS]'), ]
# 加入字符串编号序列
for token in tokens:
token_ids.append(self.token_to_id(token))
# 加上结束标记
token_ids.append(self.token_to_id('[SEP]'))
return token_ids
def decode(self, token_ids):
"""
给定一个编号序列,将它解码成字符串
:param token_ids: 待解码的编号序列
:return: 解码出的字符串
"""
# 起止标记字符特殊处理
spec_tokens