介绍

  前几天,某个公众号发文质疑马蜂窝网站,认为它搬运其它网站的旅游点评,对此,马蜂窝网站迅速地做出了回应。相信大多数关注时事的群众已经了解了整个事情的经过,在这里,我们且不论这件事的是是非非,也不关心它是否是通过爬虫等其他技术手段实现的。本文将会展示一种自动生成旅游点评的技术手段。我们用到的模型为LSTM模型。

  LSTM模型是深度学习中一种重要的模型,全称为Long Short-Term Memory,中文译为长短期记忆网络,是RNN家族中的重要成员,它模拟了人的大脑,具有一定的记忆功能,适合于处理和预测时间序列中间隔和延迟相对较长的重要事件,在翻译语言、控制机器人、图像分析、文档摘要、语音识别图像识别、手写识别、控制聊天机器人、预测疾病、点击率和股票、合成音乐等方面有较多应用。

  在本文中,你将会看到LSTM在自动生成文字(在这里就是旅游点评)方面的应用,如果你感到好奇的话,请继续阅读~

获取数据集

  第一步,就是获取数据集,我们利用Python爬虫来实现。我们需要爬取的旅游评论来自于携程网站上的旅游评论,在本文中,我们以杭州西湖景点的旅游评论为例,页面如下:

我们爬取这些评论中的第1至10页,采用concurrent.futures模块实现并发爬取。完整的Python代码如下:

import requests
import pandas as pd
from bs4 import BeautifulSoup
from concurrent.futures import ThreadPoolExecutor, wait, ALL_COMPLETED # 评论列表
comments = [] # 提取评论,传入参数为网址url
def get_comment(url): global comments try:
# 发送HTTP请求
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 \
(KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36'}
r = requests.get(url=url, headers=headers) # 解析网页,定位到评论部分
soup = BeautifulSoup(r.text, 'lxml')
main_content = soup.find_all('div', class_='comment_single') # 提取评论
for para in main_content:
comment = para.find('span', class_='heightbox')
#print(comment.text)
comments.append(comment.text.replace('&quot', '')) except Exception as err:
print(err) def main():
# 请求网址
urls = ["http://you.ctrip.com/sight/hangzhou14/49894-dianping-p%d.html"%x for x in range(1,11)]
urls[0] = urls[0].replace('-p1', '') # 利用多线程爬取景点评论
executor = ThreadPoolExecutor(max_workers=10) # 可以自己调整max_workers,即线程的个数
# submit()的参数: 第一个为函数, 之后为该函数的传入参数,允许有多个
future_tasks = [executor.submit(get_comment, url) for url in urls]
# 等待所有的线程完成,才进入后续的执行
wait(future_tasks, return_when=ALL_COMPLETED) # 创建DataFrame并保存到csv文件
comments_table = pd.DataFrame({'id': range(1, len(comments)+1),
'comments': comments})
print(comments_table) comments_table.to_csv(r"E://LSTM/hangzhou.csv", index=False) main()

运行完该代码,就会得到hangzhou.csv文件,在这个文件中,我们需要把旅游评论中的文字做一些修改,比如去掉特殊字符,添加掉电,去掉换行,修改个别错别字等。修改完后的csv文件(部分)如下:

得到该csv文件后,我们需要将这些评论(只包含评论)转移到txt文件,以便后续的操作,利用下面的Python

代码即可完成:

import pandas as pd

# 读取csv文件
df = pd.read_csv('E://LSTM/hangzhou.csv')['comments']
# 将pandas中的评论修改后,写入txt文件
for item in df:
comments = item.replace('\n','').replace('&quot','') \
.replace(r' ', '').replace(r'#', '').replace(r'&','') \
.replace(r'<', '《').replace(r'>', '》') \
.replace(r'↑', '').replace(r'[', '').replace(r']', '') \
.replace(r'❤', '')
with open('E://LSTM/comments.txt', 'a', encoding='utf-8') as f:
f.write(comments)
f.write('\n')
#print(comments)

txt文件部分如下:

该txt文件一共含有41412个文字。我们将会以这些评论为数据集,在此基础上利用Keras建立LSTM模型,训练完模型后,能自动生成其他的旅游点评。

LSTM模型

  Keras是一个高级的神经网络API,利用它能够轻松地搭建一些复杂的神经网络模型,是一个不错的深度学习框架。对于刚才得到的旅游点评,为了能够生成其他的旅游点评(人类可读),我们将会用到LSTM模型,之所以使用这个模型,是因为LSTM具有长短时记忆功能,能够很好地处理文本中的文字之间的联系,而不是将文字看成是独立的个体。

  在搭建LSTM模型之前,我们需要做一些准备工作。首先我们需要将每个文字对应到一个数字,该模型的输入特征向量为前10个文字对应的数字组成的向量,目标变量为该10个文字的下一个文字对应的数字。该txt文件中一共有1949个文字(包括汉子和标点符号),按照我们的处理模式,共有41402个样本,将这些样本传入到LSTM模型中。我们建立的模型很简单,先是一个LSTM层,利用含有256个LSTM结构,然后是一个Dropout层,能有效防止模型发生过拟合,最后是Softmax层,将它转化为多分类的问题,采用交叉熵作为模型的损失函数。

  训练模型的Python代码如下:

# 搭建简单的LSTM模型用于生成旅游评论
import numpy
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Dropout
from keras.layers import LSTM
from keras.callbacks import ModelCheckpoint
from keras.utils import np_utils # 读取txt文件
filename = "E://LSTM/comments.txt"
with open(filename, 'r', encoding='utf-8') as f:
raw_text = f.read().lower() # 创建文字和对应数字的字典
chars = sorted(list(set(raw_text)))
#print(chars)
char_to_int = dict((c, i) for i, c in enumerate(chars))
int_to_char = dict((i, c) for i, c in enumerate(chars))
# 对加载数据做总结
n_chars = len(raw_text)
n_vocab = len(chars)
print("总的文字数: ", n_chars)
print("总的文字类别: ", n_vocab) # 解析数据集,转化为输入向量和输出向量
seq_length = 10
dataX = []
dataY = []
for i in range(0, n_chars-seq_length, 1):
seq_in = raw_text[i:i + seq_length]
seq_out = raw_text[i + seq_length]
dataX.append([char_to_int[char] for char in seq_in])
dataY.append(char_to_int[seq_out])
n_patterns = len(dataX)
print("Total Patterns: ", n_patterns)
# 将X重新转化为[samples, time steps, features]的形状
X = numpy.reshape(dataX, (n_patterns, seq_length, 1))
# 正则化
X = X/n_vocab
# 对输出变量做one-hot编码
y = np_utils.to_categorical(dataY) # 定义LSTM模型
model = Sequential()
model.add(LSTM(256, input_shape=(X.shape[1], X.shape[2])))
model.add(Dropout(0.2))
model.add(Dense(y.shape[1], activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='adam')
#print(model.summary()) # 定义checkpoint
filepath="E://LSTM/weights-improvement-{epoch:02d}-{loss:.4f}.hdf5"
checkpoint = ModelCheckpoint(filepath, monitor='loss', verbose=1, save_best_only=True, mode='min')
callbacks_list = [checkpoint]
# 训练模型,每批训练128个样本,总共训练1000次
epochs = 1000
model.fit(X, y, epochs=epochs, batch_size=128, callbacks=callbacks_list)

首先让我们看一下模型的结构及参数情况, 使用代码中的print(model.summary())即可,输出如下:

Layer (type)                 Output Shape              Param #
=================================================================
lstm_1 (LSTM) (None, 256) 264192
_________________________________________________________________
dropout_1 (Dropout) (None, 256) 0
_________________________________________________________________
dense_1 (Dense) (None, 1949) 500893
=================================================================
Total params: 765,085
Trainable params: 765,085
Non-trainable params: 0
_________________________________________________________________

虽然是一个很简单的LSTM模型,但也有76万多个参数,深度学习的参数的个数可见一斑~

  运行代码,训练该模型,在训练了漫长的4,5个小时后,在613次的时候,损失值为0.3040,我们就以这个文件作为模型训练的结果,而不是1000次,因为1000次太费时了。文件如下:

请注意删除没用的文件,因为这些生成的文件都很大。

生成旅游点评

  好不容易训练完模型后,下一步,我们将要利用这个模型来生成旅游点评。怎么样,是不是有点期待?生成旅游点评的完整Python如下(我们以输入的句子“杭州西湖天下闻名,西”为例,请注意,每次输入正好10个文字,因为模型训练的输入向量为含10个元素的向量):

# 搭建简单的LSTM模型用于生成旅游评论
import numpy
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Dropout
from keras.layers import LSTM
from keras.callbacks import ModelCheckpoint
from keras.utils import np_utils # 读取txt文件
filename = "E://LSTM/comments.txt"
with open(filename, 'r', encoding='utf-8') as f:
raw_text = f.read().lower() # 创建文字和对应数字的字典
chars = sorted(list(set(raw_text)))
#print(chars)
char_to_int = dict((c, i) for i, c in enumerate(chars))
int_to_char = dict((i, c) for i, c in enumerate(chars))
# 对加载数据做总结
n_chars = len(raw_text)
n_vocab = len(chars)
print("总的文字数: ", n_chars)
print("总的文字类别: ", n_vocab) # 解析数据集,转化为输入向量和输出向量
seq_length = 10
dataX = []
dataY = []
for i in range(0, n_chars-seq_length, 1):
seq_in = raw_text[i:i + seq_length]
seq_out = raw_text[i + seq_length]
dataX.append([char_to_int[char] for char in seq_in])
dataY.append(char_to_int[seq_out])
n_patterns = len(dataX)
print("Total Patterns: ", n_patterns)
# 将X重新转化为[samples, time steps, features]的形状
X = numpy.reshape(dataX, (n_patterns, seq_length, 1))
# 正则化
X = X/n_vocab
# 对输出变量做one-hot编码
y = np_utils.to_categorical(dataY) # 定义LSTM模型
model = Sequential()
model.add(LSTM(256, input_shape=(X.shape[1], X.shape[2])))
model.add(Dropout(0.2))
model.add(Dense(y.shape[1], activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='adam') # 导入训练完后的文件
filename = "E://LSTM/weights-improvement-613-0.3040.hdf5"
model.load_weights(filename)
# 示例的输入句子
input = '杭州西湖天下闻名,西'
pattern = [char_to_int[value] for value in input]
print("输入:")
print(''.join([int_to_char[value] for value in pattern]))
print("输出:")
# 生成1000个文字
for i in range(1000):
x = numpy.reshape(pattern, (1, len(pattern), 1))
x = x / float(n_vocab)
prediction = model.predict(x, verbose=0)
index = numpy.argmax(prediction)
result = int_to_char[index]
print(result, end='')
seq_in = [int_to_char[value] for value in pattern]
pattern.append(index)
pattern = pattern[1:len(pattern)]
print("\n生成完毕。")

运行该代码,就能看到生成的文字如下:

输入:
杭州西湖天下闻名,西
输出:
湖一年四季特色纷呈,西湖有湖光山色交相辉应,既可看湖又可看山,西湖上的桥也是兼具南北特色,长桥不长,断桥不断。
西湖古迹遍布,山水秀丽,景色宜人,西湖处处有胜景。最著名的西湖十景,西湖十景”是指浙江省杭州市着名旅游景点西湖上的十处特色风景,分别是苏堤春晓、曲苑风荷、平湖秋月、断桥残雪、柳浪闻莺、花港观鱼、雷峰夕照、双峰插云、南屏晚钟、三潭印月等十个景点是杭州游览的热点,不用按照目前的时髦话说什么“非看不可”、“非去不可”,但是,如果去了杭州不看这些景点,不看这些景点的碑刻,就有点可惜了,特别是有康熙爷、乾隆爷的亲笔题词,不去看看,多少会对老祖宗的“大不敬”。
如果天气晴朗下午五点以后去西湖,如果拍视频你就会拍到天蓝蓝水蓝蓝的西湖和夕阳西下渐黄昏的完美,西湖边上有小凳子可以坐下来静静的欣赏西湖美景也可和同行的伙伴聊聊接下来的行程,目的是等待晚上更值得期待的音乐喷泉,得早早坐下来否则就看不到了,看喷泉不允许站着凳子坐满其余人站凳子后面。
西湖游船是游西湖必不可少的。另外早晨可以早点去,人少,而且凉爽。花港观鱼的景色也是不错。雷峰塔就看你需求了,可以俯瞰整个西湖(其实去雷峰塔也是为了看西湖吧,雷峰塔本身貌似没什么看的)吃饭的话,别在景区吃,强烈推荐去弄堂里。好吃又便宜。
8月份出差路过杭州,可以轻松的偷闲半天,顺便预约了离西湖比较近便的酒店入住。8月底的清晨已经有了丝丝的凉意,顺着西湖边开始溜达。早上人还是比较少,本地起来锻炼的大爷大妈比较多,有蘸水在地上练字的,有在小公园练嗓子的,还有坐在长条凳上练二胡的。9点多点,游客开始多了,各种游船排队等候,暑假期间带小孩匆匆而过的人也不少,基本都是到此一游,很少有仔细的欣赏和解说景点的出处。乘凉的地方还是不少,但是蚊子也多的吓人。苏堤的景色不错,特别是两边的高树遮挡了大多数的阳光,即使在这里溜达也不会觉得热,如果时间允许的话,建议溜达过去,步行约30分钟左右,南边的出口还有苏东坡博物馆,比较小,免费开放。
西湖的范围蛮大的,从北山路(北山路上的老建筑,浙江博物馆,断桥残雪,锦带桥,保俶塔,楼外楼,孤山,西冷印社,平湖秋月等。)到南线景区(南山路,西湖天地,御码头,钱王祠,柳浪闻莺,万松书院,长桥,南屏晚钟,净慈寺,雷锋塔太子湾等)以及三堤,外西湖,西里湖等,小南湖,小瀛洲,从白堤_苏堤_曲院风荷_杨公堤_鵒鸪湾_茅家埠。飞来峰景
生成完毕。

让我们来看一下这段生成的文字,首先,这段文字的可读性还是很高的,基本上人类能够理解,其次,与原文相对比,这段文字并不是一味地抄袭原文,而是有选择地将原文件中的旅游点评组合起来,虽然每部分的旅游点评与原先的相差不多,但重新组合后,是能够生成新的旅游点评的,并不算真正意义上的抄袭。

  用LSTM训练出来的模型生成的文本,是能够作为新的旅游点评的,并不是完全的抄袭,但是对于未在原文中出现的输入向量,可能训练出来的效果就不会太理想,这是需要改进的地方。

总结

  本文纯属自娱自乐,如果不当之处,还望大家批评指正~~

  当然,对于该项目,还有一些值得改进的地方,比如数据集不够大,这个可以爬取更多的评论;数据预处理过于简单,仅仅做了文字与向量的一一对应以及输入向量的正则化;模型过于简单,读者可以尝试搭建多个LSTM层或其他结构;模型训练过于耗时,可以尝试GPU或改进模型结构或数据预处理方式,等等等等。希望读者在阅读完本文后,能对LSTM模型在文字生成方面有一定的了解,欢迎拥抱LSTM~~

参考文献

Text Generation With LSTM Recurrent Neural Networks in Python with Keras: https://machinelearningmastery.com/text-generation-lstm-recurrent-neural-networks-python-keras/

注意:本人现已开通微信公众号: Python爬虫与算法(微信号为:easy_web_scrape), 欢迎大家关注哦~~

RNN入门(三)利用LSTM生成旅游点评的更多相关文章

  1. 深度学习原理与框架-递归神经网络-RNN网络基本框架(代码?) 1.rnn.LSTMCell(生成单层LSTM) 2.rnn.DropoutWrapper(对rnn进行dropout操作) 3.tf.contrib.rnn.MultiRNNCell(堆叠多层LSTM) 4.mlstm_cell.zero_state(state初始化) 5.mlstm_cell(进行LSTM求解)

    问题:LSTM的输出值output和state是否是一样的 1. rnn.LSTMCell(num_hidden, reuse=tf.get_variable_scope().reuse)  # 构建 ...

  2. NLP教程(5) - 语言模型、RNN、GRU与LSTM

    作者:韩信子@ShowMeAI 教程地址:http://www.showmeai.tech/tutorials/36 本文地址:http://www.showmeai.tech/article-det ...

  3. RNN 入门教程 Part 3 – 介绍 BPTT 算法和梯度消失问题

    转载 - Recurrent Neural Networks Tutorial, Part 3 – Backpropagation Through Time and Vanishing Gradien ...

  4. TensorFlow 实现 RNN 入门教程

    转子:https://www.leiphone.com/news/201705/zW49Eo8YfYu9K03J.html 最近在看RNN模型,为简单起见,本篇就以简单的二进制序列作为训练数据,而不实 ...

  5. RNN入门

    RNN入门学习 原文地址:http://blog.csdn.net/hjimce/article/details/49095371 作者:hjimce 一.相关理论 RNN(Recurrent Neu ...

  6. RNN,GRU,LSTM

    2019-08-29 17:17:15 问题描述:比较RNN,GRU,LSTM. 问题求解: 循环神经网络 RNN 传统的RNN是维护了一个隐变量 ht 用来保存序列信息,ht 基于 xt 和 ht- ...

  7. Spring事务管理----声明式:利用TransactionProxyFactoryBean生成事务代理

    通常建议采用声明式事务管理.声明式事务管理的优势非常明显:代码中无需关于关注事务逻辑,让spring声明式事务管理负责事务逻辑,声明式事务管理无需与具体的事务逻辑耦合,可以方便地在不同事务逻辑之间切换 ...

  8. Android 利用Gson生成或解析json

    目前手机端和服务端数据交流格式一般是json,而谷歌提供了Gson来解析json.下载Gson:https://code.google.com/p/google-gson/ 下载的放在lib并导入,若 ...

  9. 黄聪:利用OpenXml生成Word2007文档(转)

    原文:http://blog.csdn.net/francislaw/article/details/7568317 版权声明:本文为博主原创文章,未经博主允许不得转载.   目录(?)[-] 一Op ...

随机推荐

  1. 如何更改github工程的语言属性

    当创建github项目的时候,github本身会根据提交文件的数量来自动推断工程的开发语言,有时这种推断结果会与实际情况不太相符.比如上传一个java的web工程,如果在工程里存在大量的html.ja ...

  2. python 单例模式的四种实现方法及注意事项

    一.模块单例 Python 的模块就是天然的单例模式,因为模块在第一次导入时,会生成 .pyc 文件,当第二次导入时,就会直接加载 .pyc 文件,而不会再次执行模块代码. #foo1.py clas ...

  3. 逆向暴力求解 538.D Weird Chess

    11.12.2018 逆向暴力求解 538.D Weird Chess New Point: 没有读好题 越界的情况无法判断,所以输出任何一种就可以 所以他给你的样例输出完全是误导 输出还搞错了~ 输 ...

  4. 利用java解压,并重命名

    由于工作需要,写了一个小工具,利用java来解压文件然后对文件进行重命名 主要针对三种格式,分别是zip,rar,7z,经过我的多次实践我发现网上的类库并不能解压最新的压缩格式 对于zip格式: ma ...

  5. [转] IPTables for KVM Host

    IPTables for KVM Host January 26, 2012 By Andrew Galdes Use the following IPTables rules “/etc/sysco ...

  6. VSCode插件开发全攻略(五)跳转到定义、自动补全、悬停提示

    更多文章请戳VSCode插件开发全攻略系列目录导航. 跳转到定义 跳转到定义其实很简单,通过vscode.languages.registerDefinitionProvider注册一个provide ...

  7. 在Spring-Boot中实现通用Auth认证的几种方式

    code[class*="language-"], pre[class*="language-"] { background-color: #fdfdfd; - ...

  8. 项目Alpha冲刺(团队6/10)

    项目Alpha冲刺(团队6/10) 团队名称: 云打印 作业要求: 项目Alpha冲刺(团队) 作业目标: 完成项目Alpha版本 团队队员 队员学号 队员姓名 个人博客地址 备注 221600412 ...

  9. 深入分析.NET应用程序SQL注入【危害】

    前言:   前面我们已经简单的剖析了一下.NET应用程序SQL注入.没有看过的朋友移步:http://bbs.ichunqiu.com/thread-7636-1-1.html,在上一篇文章我们已经了 ...

  10. Javascript高级编程学习笔记(78)—— 表单(6)HTML约束验证API

    自动切换焦点 使用JS可以极大地提升表单的易用性 其中最常用的一种就是当用户填写完当前字段后焦点自动切换到下一个字段 以下方的HTML代码为例: <input type="text&q ...