水师提督速成指南:用Keras打造你的AI水军

咱们以前在知乎上曾有篇回答,讲了美国芝加哥大学的研究人员能够用 AI 为 Yelp 上的餐馆和酒店写虚假评论,让 AI 客串了一回水军,并且效果足以以假乱真。python

今天咱们就分享一下如何用 Keras 打造 AI 水军,写出逼真的餐厅评论,速度升职为水师提督。 在阅读本篇教程以后,你就能学会如何生成一条 5 星的 Yelp 餐厅评论。git

下面是 AI 生成的一些评论示例(未编辑状态):github

我吃了牛排、贻贝,还有意式帕玛森烤鸡,全都很好吃,咱们还会再来的。json

饭菜、服务还有氛围都很棒,我会给全部的朋友推荐这里。bash

很好的氛围,很棒的饭菜,服务也很好。值得一试!网络

I had the steak, mussels with a side of chicken parmesan. All were very good. We will be back.app

The food, service, atmosphere, and service are excellent. I would recommend it to all my friendsdom

Good atmosphere, amazing food and great service.Service is also pretty good. Give them a try!ide

下面会教你:函数

  • 获取和准备训练数据。

  • 搭建字符级语言模型。

  • 训练模型时的 Tips。

  • 生成随机评论。

在 GPU 上只花几天就能很容易的训练出一个模型。幸亏,如今有很多预训练模型权重,因此你也能够直接跳到最后的生成评论部分。

准备数据

咱们在 Yelp 官网上,能够免费得到 json 格式的 Yelp 数据集 数据集

下载数据集并提取数据后,在数据集文件夹中,你会发现两个须要的文件:

Review.json

Business.json

这里提醒一下,这两个文件都很大,特别是 review.json,(3.7 G)。

Review.json 文件的每一行都是一条 json 字符串格式的评论,这两个文件夹中并无开始和结束括号 [],所以 json 文件的内容总体来看并非一个有效的 json 字符串。此外,将整个 review.json 文件放到内存中可能会很困难。所以咱们首先用脚本将它们逐行转为 CSV 格式的文件。

python json_converter.py ./dataset/review.json
python json_converter.py ./dataset/business.json
复制代码

在这以后,你会发现数据集文件夹中有两个文件,它们都是有效的 CSV 文件,能够用 Pandas 程序库打开。 咱们接下来会这么作:只从类别中有“restaurant”标签的业务中提取 5 星评论文本。

# Read thow two CSV files to pandas dataframes
df_business=pd.read_csv('../dataset/business.csv')
df_review=pd.read_csv('../dataset/review.csv')
# Filter 'Restaurants' businesses
restaurants = df_business[df_business['categories'].str.contains('Restaurants')]
# Filter 5-stars reviews
five_star=df_review[df_review['stars']==5]
# merge the reviews with restaurants by key 'business_id'
# This keep only 5-star restaurants reviews
combo=pd.merge(restaurants_clean, five_star, on='business_id')
# review texts column
rnn_fivestar_reviews_only=combo[['text']]
复制代码

接下来,咱们移除评论中的新行字符和重复的评论。

# remove new line characters
rnn_fivestar_reviews_only=rnn_fivestar_reviews_only.replace({r'\n+': ''}, regex=True)
# remove dupliated reviews
final=rnn_fivestar_reviews_only.drop_duplicates()
复制代码

为了给模型展现评论的开始和结尾在哪里,咱们须要为评论文本添加特殊的标记。 那么最终准备好的评论中会有一行达到预期,以下所示:

鹰嘴豆沙很好吃,也很新鲜!沙拉三明治也超棒。绝对值得再去!店老板人很好,员工都很和善。

"Hummus is amazing and fresh! Loved the falafels. I will definitely be back. Great owner, friendly staff"

搭建模型 咱们所搭建的模型是一个字符级语言模型,意味着最小的可区分符号为字符。你也能够试试词汇级模型,也就是输入为单词标记。 关于字符级语言模型,有优势也有缺点。

优势:

这样就没必要担忧未知词汇的问题。 可以学习较大的词汇。

缺点:

这样会形成一个很长的序列,在获取远程依赖性方面(语句较早部分对后期部分的影响)不如词汇级模型。

并且字符级模型在训练时须要消耗的计算资源也更多。

模型和 lstm_text_generation.py demo code 很像,例外之处是咱们会再堆叠几个循环神经网络单元,从而让隐藏层在输入层和输出层之间能存储更多的信息。这样能生成更加真实的 Yelp 评论。

在展现模型的代码以前,咱们先深究一下堆叠的 RNN 是如何工做的。 你可能在标准的神经网络中见过它(也就是 Keras 中的致密层,Dense layer)。

第一个层会用输入 X 计算激活值 a[1],它会堆叠第二个层来计算出下一个激活值 a[2]。

堆叠的 RNN 有点像标准的神经网络,并且能“及时展开”。

记号 a[I] 表示层 I 的激活配置,其中 表示时步 t。

咱们瞅一眼怎么计算出一个激活值。

要想计算 a[2]<3>,须要两个输入,a[2]<2> 和 a[1]<3>。

g 是激活函数,wa[2] 和 ba[2] 是层 2 的参数。

咱们能够看到,要想堆叠 RNN,以前的 RNN 须要将全部的时步 ato 返回到下面的 RNN 中。 Keras 中默认一个 RNN 层,好比 LSTM,只返回最后的时步激活值 a。为了返回全部时步的激活值,咱们要将 return_sequences 参数设为 true。

这里说说如何在 Keras 上搭建模型。每一个输入样本都是一个 60 个字符的独热表示(one-hot representation),总共有 95 个可能的字符。

每一个输出就是每一个字符的一列 95 个预测几率。

import keras
from keras import layers

model = keras.models.Sequential()
model.add(layers.LSTM(1024, input_shape=(60, 95),return_sequences=True))
model.add(layers.LSTM(1024, input_shape=(60, 95)))
model.add(layers.Dense(95, activation='softmax'))
复制代码

训练模型

训练模型的思路很简单,咱们会以输入/输出成对地训练模型。每一个输入是 60 个字符,相应的输出为紧跟在后面的字符。

在数据准备这一步,咱们建立了一列干净的 5 星评论文本。总共有 1214016 行评论。为了让训练简单一点,咱们只训练字符长度小于或等于 250 的评论,最后会获得 418955 行评论。

而后咱们打乱评论的顺序,这样咱们就不会用一行中同一家餐厅的 100 条评论来训练模型。 咱们会将全部的评论读取为一个长文本字符串,而后建立一个 Python 目录(好比一个散列表)将每一个字符映射到 0-94(总共 95 个特别字符)之间的一个索引上。

# List of unique characters in the corpus
chars = sorted(list(set(text)))
print('Unique characters:', len(chars))
# Dictionary mapping unique characters to their index in `chars`
char_indices = dict((char, chars.index(char)) for char in chars)
复制代码

文本库一共有 72662807 个字符,很难将其做为一个总体处理。所以咱们将它拆分为几个文本块,每块有 90k 个字符。

对于拆分后的每一个文本块,咱们会生成这部分的每对输入和输出。从头至尾转变文本块的指针,若是时步设为 1 的话,每次转变 1 个字符。

def getDataFromChunk(txtChunk, maxlen=60, step=1):
   sentences = []
   next_chars = []
   for i in range(0, len(txtChunk) - maxlen, step):
       sentences.append(txtChunk[i : i + maxlen])
       next_chars.append(txtChunk[i + maxlen])
   print('nb sequences:', len(sentences))
   print('Vectorization...')
   X = np.zeros((len(sentences), maxlen, len(chars)), dtype=np.bool)
   y = np.zeros((len(sentences), len(chars)), dtype=np.bool)
   for i, sentence in enumerate(sentences):
       for t, char in enumerate(sentence):
           X[i, t, char_indices[char]] = 1
           y[i, char_indices[next_chars[i]]] = 1
return [X, y]
复制代码

在 GPU(GTX1070)上训练一个文本块的话,每一个 epoch 耗时 219 秒,那么训练完整个文本库须要花费大约 2 天时间。 72662807 / 90000 * 219 /60 / 60/ 24 = 2.0 days

Keras 的两个回调函数 ModelCheckpoint 和 ReduceLROnPlateau 用起来很方便。 ModelCheckpoint 能帮咱们保存每一次优化时的权重。 当损失度量中止降低时,ReduceLROnPlateau 回调函数会自动减小学习率。它的主要益处是咱们不须要再手动调整学习率,主要缺点是它的学习率会一直降低和衰退。

# this saves the weights everytime they improve so you can let it train. Also learning rate decay
filepath="Feb-22-all-{epoch:02d}-{loss:.4f}.hdf5"
checkpoint = ModelCheckpoint(filepath, monitor='loss', verbose=1, save_best_only=True, mode='min')
reduce_lr = ReduceLROnPlateau(monitor='loss', factor=0.5,
             patience=1, min_lr=0.00001)
callbacks_list = [checkpoint, reduce_lr]
复制代码

将模型训练 20 个 epoch 的代码以下:

for iteration in range(1, 20):
   print('Iteration', iteration)
   with open("../dataset/short_reviews_shuffle.txt") as f:
       for chunk in iter(lambda: f.read(90000), ""):
           X, y = getDataFromChunk(chunk)
           model.fit(X, y, batch_size=128, epochs=1, callbacks=callbacks_list)
复制代码

若是所有训练完,大概须要 1 个月的时间。可是对咱们来讲,训练上 2 个小时就能生成很不错的结果。

生成5星评论

有了预训练模型的权重或者你本身训练的模型,咱们就能够生成有意思的 Yelp 评论了。 思路是这样:咱们用初始 60 个字符做为模型的“种子”,而后让模型预测紧接下来的字符。

“索引抽样”过程会根据给定预测生成一些随机性,为最终结果添加一些变体。

若是 temperature 的值很小,它会一直选取有最高预测几率的索引。

def sample(preds, temperature=1.0):
   ''' Generate some randomness with the given preds which is a list of numbers, if the temperature is very small, it will always pick the index with highest pred value '''
   preds = np.asarray(preds).astype('float64')
   preds = np.log(preds) / temperature
   exp_preds = np.exp(preds)
   preds = exp_preds / np.sum(exp_preds)
   probas = np.random.multinomial(1, preds, 1)
return np.argmax(probas)
复制代码

要想生成 300 个字符,代码以下:

# We generate 300 characters
for i in range(300):
   sampled = np.zeros((1, maxlen, len(chars)))
   # Turn each char to char index.
   for t, char in enumerate(generated_text):
       sampled[0, t, char_indices[char]] = 1.
   # Predict next char probabilities
   preds = model.predict(sampled, verbose=0)[0]
   # Add some randomness by sampling given probabilities.
   next_index = sample(preds, temperature)
   # Turn char index to char.
   next_char = chars[next_index]
   # Append char to generated text string
   generated_text += next_char
   # Pop the first char in generated text string.
   generated_text = generated_text[1:]
   # Print the new generated char.
   sys.stdout.write(next_char)
   sys.stdout.flush()
print(generated_text)
复制代码

结语

在本文,咱们学习了如何用 Keras 搭建和训练一个字符级文本生成模型。本项目的源代码以及所用的预训练模型,都可在 GitHub 上获取获取

emmmmmm...来都来了,你还能够看看我站其它关于文本处理的教程与文章:

这评论有毒!——文本分类的通常套路

基于TensorFlow用卷积神经网络作文本分类

相关文章
相关标签/搜索