RNN入門(三)利用LSTM生成旅游點評


介紹

  前幾天,某個公眾號發文質疑馬蜂窩網站,認為它搬運其它網站的旅游點評,對此,馬蜂窩網站迅速地做出了回應。相信大多數關注時事的群眾已經了解了整個事情的經過,在這里,我們且不論這件事的是是非非,也不關心它是否是通過爬蟲等其他技術手段實現的。本文將會展示一種自動生成旅游點評的技術手段。我們用到的模型為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文件(部分)

得到該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())即可,輸出如下:

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), 歡迎大家關注哦~~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM