RNN入门(三)利用LSTM生成旅游点评

介绍

  前几天,某个公众号发文质疑马蜂窝网站,认为它搬运其它网站的旅游点评,对此,马蜂窝网站迅速地作出了回应。相信大多数关注时事的群众已经了解了整个事情的通过,在这里,咱们且不论这件事的是是非非,也不关心它是不是经过爬虫等其余技术手段实现的。本文将会展现一种自动生成旅游点评的技术手段。咱们用到的模型为LSTM模型。
  LSTM模型是深度学习中一种重要的模型,全称为Long Short-Term Memory,中文译为长短时间记忆网络,是RNN家族中的重要成员,它模拟了人的大脑,具备必定的记忆功能,适合于处理和预测时间序列中间隔和延迟相对较长的重要事件,在翻译语言、控制机器人、图像分析、文档摘要、语音识别图像识别、手写识别、控制聊天机器人、预测疾病、点击率和股票、合成音乐等方面有较多应用。
  在本文中,你将会看到LSTM在自动生成文字(在这里就是旅游点评)方面的应用,若是你感到好奇的话,请继续阅读~html

获取数据集

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

携程网站上关于杭州西湖的评论

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

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文件(部分)

获得该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文件(部分)

该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())便可,输出以下:app

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次太费时了。文件以下:框架

Keras训练613次后生成的HDF5文件

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

生成旅游点评

  好不容易训练完模型后,下一步,咱们将要利用这个模型来生成旅游点评。怎么样,是否是有点期待?生成旅游点评的完整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://machinelearningmaster...

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

相关文章
相关标签/搜索