代码详解:训练网络像你一样说话

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/duxinshuxiaobian/article/details/102635915
全文共5202字,预计学习时长10分钟

你有没有曾经想过构建一个像你一样说话的语言生成模型?

具体而言,就是像你一样写作。这是解释语言生成主要概念、使用Keras运行以及展示模型局限的完美方式。

Kaggle Kernel 是理解语言生成算法结构十分有用的资源。

语言生成

自然语言生成是一个旨在生成有意义的自然语言的领域。

大多数情况下,会生成一系列单个单词。工作原理如下:

• 训练模型,预测某一序列的下一个单词

• 为已训练的模型提供输入

• 迭代N次,以便生成后N个单词

序列预测过程

  1. 数据集创建

第一步是创建一个可以被我们稍后构建的网络所理解的数据集。首先导入以下文件夹:

from keras.preprocessing.sequence import pad_sequences
from keras.layers import Embedding, LSTM, Dense, Dropout
from keras.preprocessing.text import Tokenizer
from keras.callbacks import EarlyStopping
from keras.models import Sequential
import keras.utils as ku
import pandas as pd
import numpy as np
import string, os
a. 加载数据

假设你撰写的每篇文章的标题都遵循以下模板:

我们通常不希望在最终数据集中使用这种内容类型。接下来将专注于文本本身。

所有写好的文章都储存到一个单独的Markdown文件中。标头基本上包含有关标题、图片标头等信息。

首先,需要在名为“maelfabien.github.io”的目录中选择包含文章的文件夹。

import glob, os

os.chdir("/MYFOLDER/maelfabien.github.io/_posts/")

b. 句子标记

然后,打开文章,将每篇文章的内容附到列表中。但目的是生成句子而非整篇文章,因此将每篇文章分成一个句子列表,并将每个句子附到列表“all_sentences”中:

all_sentences= []

for file in glob.glob("*.md"):
f = open(file,‘r’)
txt = f.read().replace("\n", " “)
try:
sent_text = nltk.sent_tokenize(’’.join(txt.split(”—")
[2]).strip())
for k in sent_text :
all_sentences.append(k)
except :
pass
总的来说,我们有超过6’800个训练句。到目前为止的过程如下:

句子拆分

c. N-gram创建

然后,创建一起出现的N-gram单词。为此,需要:

• 在语料库上设置一个标记化程序,将索引与每个标记相关联

• 将语料库中的每个句子分解为一系列标记

• 存储一起发生的标记序列

可以通过以下方式说明:

N-gram的创建

首先需要设置标记器:

tokenizer = Tokenizer()
tokenizer.fit_on_texts(all_sentences)
total_words = len(tokenizer.word_index) + 1
变量“total_words”包含已使用的不同单词的总数。此处为8976。然后,每个句子获得相应的标记并生成N-gram:

input_sequences = []

For each sentence

for sent in all_sentences:

Get the corresponding token

token_list = tokenizer.texts_to_sequences([sent])[0]

Create the corresponding n-grams

for i in range(1, len(token_list)):
n_gram_sequence = token_list[:i+1]
input_sequences.append(n_gram_sequence)
Token_list变量包含作为标记序列的句子:

[656, 6, 3, 2284, 6, 3, 86, 1283, 640, 1193, 319]

[33, 6, 3345, 1007, 7, 388, 5, 2128, 1194, 62, 2731]

[7, 17, 152, 97, 1165, 1, 762, 1095, 1343, 4, 656]

然后,n_gram_sequences创建n-gram。从前两个单词开始,逐渐添加单词:

[656, 6]

[656, 6, 3]

[656, 6, 3, 2284]

[656, 6, 3, 2284, 6]

[656, 6, 3, 2284, 6, 3]

d. 填充

现在面临以下问题:并非所有序列都具有相同的长度!如何解决这个问题?

使用填充。在变量input_sequences每一行前,填充添加0的序列,以便每行与最长行具有相同长度。

图解填充

为将所有句子填充到最大长度,必须首先找到最长的句子:

max_sequence_len = max([len(x) for x in input_sequences])

在我们的例子中,最大数值为792。接下来绘制序列长度的直方图:

import matplotlib.pyplot as plt
plt.figure(figsize=(12,8))
plt.hist([len(x) for x in input_sequences], bins=50)
plt.axvline(max_sequence_len, c=“r”)
plt.title(“Sequence Length”)
plt.show()

序列长度

确实很少有单个序列超过200 +单词的例子。如何将最大序列长度设置为200?

max_sequence_len = 200
input_sequences = np.array(pad_sequences(input_sequences,
maxlen=max_sequence_len, padding=‘pre’))
输出结果如下:

array([[ 0, 0, 0, …, 0, 656, 6],

[ 0, 0, 0, …, 656, 6, 3],

[ 0, 0, 0, …, 6, 3, 2284],

…,

e. 拆分X和y

现在有了固定长度的数组,其中大多数数组用于填充在实际序列前的0。好了,之后如何将其变成训练集?我们需要拆分X和y!请记住,我们的目标是预测序列的下一个词。因此,必须将所有标记除最后一个作为X,并将最后一个标记为y。

拆分X和Y

在Python中,可简单表示为:

X, y = input_sequences[:,:-1],input_sequences[:,-1]

现在将此问题视为多类分类任务。像往常一样,必须首先对y进行一次热编码,得到一个稀疏矩阵,该矩阵的列中包含出现标记后产生的1,无标记则显示0:

在 Python中, 使用Keras Utils 进行to_categorical :

y = ku.to_categorical(y, num_classes=total_words)

X现在的形状为(164496,199),y的形状为(164496,8976)。

有大约165,000个训练样本。X是199列,对应于所允许的最长序列(200-1,即预测的标签)。Y有8976列,对应所有词汇单词的稀疏矩阵。数据集现已准备就绪!

  1. 构建模型

使用LSTM网络。LSTM具有能够理解整个序列依赖性的显著优点,因此,句子的开头可能会对预测的第15个单词产生影响。另一方面,递归神经网络(RNN)仅意味着依赖于网络的先前状态,并且只有前一个词有助于预测下一个词。如果选择RNN,就会很快地错过语境,因此,LSTM似乎是正确的选择。

a. 模型架构

由于训练可以非常(非常)(非常)(非常)(非常)(没开玩笑)长,构建一个简单的1嵌入+ 1 LSTM层+ 1密集网络:

def create_model(max_sequence_len, total_words):

input_len = max_sequence_len - 1

model = Sequential() 

   # Add Input Embedding Layer
model.add(Embedding(total_words, 10, input_length=input_len))

    # Add Hidden Layer 1 - LSTM Layer
model.add(LSTM(100))
model.add(Dropout(0.1))

    # Add Output Layer
model.add(Dense(total_words, activation='softmax'))

model.compile(loss='categorical_crossentropy', optimizer='adam')

    return model

model = create_model(max_sequence_len, total_words)
model.summary()
首先,添加一个嵌入层。将其传递给具有100个神经元的LSTM,添加dropout 控制神经元的共适应,并以密集层结束。请注意,在最后一层上应用softmax激活函数,获得属于每个类的输出概率。使用损失是分类交叉熵,它是一个多类别的分类问题。

该模型可概述为:

模型概述

b. 训练模型

现在(终于)准备训练模型!

model.fit(X, y, batch_size=256, epochs=100, verbose=True)

训练模型将开始:

Epoch 1/10

164496/164496 [==============================] - 471s 3ms/step - loss: 7.0687

Epoch 2/10

73216/164496 [============>…] - ETA: 5:12 - loss: 7.0513

在CPU上,使用训练集中的全部样本训练一次大约需要8分钟。在GPU(例如Colab)上,应该修改使用的Keras LSTM网络,因为它不能在GPU上使用。需要这个:

Modify Import

from keras.layers import Embedding, LSTM, Dense, Dropout, CuDNNLSTM

In the Moddel


model.add(CuDNNLSTM(100))

在几个步骤中,你可以停止训练,进行样本预测并在给定交叉熵值的情况下控制模型质量。

观察如下:

  1. 生成序列

如果已阅读到这里,那么下面就是你所期望的:生成新句子!要生成句子,需要对输入文本应用相同转换。构建一个循环,为下一个单词的迭代设置给定生成数量:

input_txt = “Machine”

for _ in range(10):

    # Get tokens    token_list = tokenizer.texts_to_sequences([input_txt])[0]    # Pad

the sequence
token_list = pad_sequences([token_list], maxlen=max_sequence_len-1,
padding=‘pre’) # Predict the class
predicted = model.predict_classes(token_list, verbose=0)

    output_word = "" 

   # Get the corresponding work
for word,index in tokenizer.word_index.items():
    if index == predicted:
        output_word = word
        break

            input_txt += " "+output_word

损失大约为3.1时,这是以“Google”作为输入生成的句子:

Google在全球范围内生成了大量数据

它并没有任何意义,但其成功地将谷歌与大量数据的概念联系起来,着实了不起。因为句子仅依赖于单词的共同出现,并未整合任何语法概念。

如果在训练中等待一段时间并让损失减少到2.5,并输入“随机森林”:

Random Forest是完全托管服务,旨在支持大量初创公司的视觉设施

同样,生成的内容没有任何意义,但语法结构相当正确。

在大约50次测试后,损失出现分叉并从未低于2.5。

我们认为这里制定的方法达到了极限:

• 模型仍然非常简单

• 训练数据不够干净

• 数据量非常有限

话虽如此,但结果非常有趣,例如,训练好的模型可以很容易地应用于Flask WebApp。

留言 点赞 关注

我们一起分享AI学习与发展的干货
欢迎关注全平台AI垂类自媒体 “读芯术”

(添加小编微信:dxsxbb,加入读者圈,一起讨论最新鲜的人工智能科技哦~)
————————————————
版权声明:本文为CSDN博主「读芯术」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/duxinshuxiaobian/article/details/102635915

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值