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://machinelearningmastery.com/text-generation-lstm-recurrent-neural-networks-python-keras/

注意:本人現已開通微信公衆號: Python爬蟲與算法(微信號爲:easy_web_scrape), 歡迎你們關注哦~~

相關文章
相關標籤/搜索