Python 數(shù)據(jù)科學(xué)入門教程:TensorFlow 聊天機(jī)器人

TensorFlow 聊天機(jī)器人

原文:Creating a Chatbot with Deep Learning, Python, and TensorFlow

譯者:飛龍

協(xié)議:CC BY-NC-SA 4.0

一牛哺、使用深度學(xué)習(xí)創(chuàng)建聊天機(jī)器人

你好君仆,歡迎閱讀 Python 聊天機(jī)器人系列教程登下。 在本系列中娶视,我們將介紹如何使用 Python 和 TensorFlow 創(chuàng)建一個(gè)能用的聊天機(jī)器人。 以下是一些 chatbot 的實(shí)例:

I use Google and it works.

— Charles the AI (@Charles_the_AI) November 24, 2017

I prefer cheese.

— Charles the AI (@Charles_the_AI) November 24, 2017

The internet

— Charles the AI (@Charles_the_AI) November 24, 2017

I'm not sure . I'm just a little drunk.

— Charles the AI (@Charles_the_AI) November 24, 2017

我的目標(biāo)是創(chuàng)建一個(gè)聊天機(jī)器人堤器,可以實(shí)時(shí)與 Twitch Stream 上的人交談昆庇,而不是聽起來像個(gè)白癡。為了創(chuàng)建一個(gè)聊天機(jī)器人闸溃,或者真的做任何機(jī)器學(xué)習(xí)任務(wù)整吆,當(dāng)然,你的第一個(gè)任務(wù)就是獲取訓(xùn)練數(shù)據(jù)辉川,之后你需要構(gòu)建并準(zhǔn)備表蝙,將其格式化為“輸入”和“輸出”形式,機(jī)器學(xué)習(xí)算法可以消化它乓旗「撸可以說,這就是做任何機(jī)器學(xué)習(xí)時(shí)的實(shí)際工作屿愚。建立模型和訓(xùn)練/測試步驟簡單的部分汇跨!

為了獲得聊天訓(xùn)練數(shù)據(jù),你可以查看相當(dāng)多的資源妆距。例如穷遂,康奈爾電影對話語料庫似乎是最受歡迎的語料之一。還有很多其他來源娱据,但我想要的東西更加......原始蚪黑。有些沒有美化的東西,有一些帶有為其準(zhǔn)備的特征中剩。自然忌穿,這把我?guī)У搅?Reddit。起初结啼,我認(rèn)為我會(huì)使用 Python Reddit API 包裝器掠剑,但 Reddit 對抓取的限制并不是最友好的。為了收集大量的數(shù)據(jù)郊愧,你必須打破一些規(guī)則澡腾。相反沸伏,我發(fā)現(xiàn)了一個(gè) 17 億個(gè) Reddit 評論的數(shù)據(jù)轉(zhuǎn)儲(chǔ)。那么动分,應(yīng)該使用它!

Reddit 的結(jié)構(gòu)是樹形的红选,不像論壇澜公,一切都是線性的。父評論是線性的喇肋,但父評論的回復(fù)是個(gè)分支坟乾。以防有些人不熟悉:

-Top level reply 1
--Reply to top level reply 1
--Reply to top level reply 1
---Reply to reply...
-Top level reply 2
--Reply to top level reply 1
-Top level reply 3  

我們需要用于深度學(xué)習(xí)的結(jié)構(gòu)是輸入輸出。 所以我們實(shí)際上通過評論和回復(fù)偶對的方式蝶防,試圖獲得更多的東西甚侣。 在上面的例子中,我們可以使用以下作為評論回復(fù)偶對:

-Top level reply 1 and --Reply to top level reply 1

--Reply to top level reply 1 and ---Reply to reply...

所以间学,我們需要做的是獲取這個(gè) Reddit 轉(zhuǎn)儲(chǔ)殷费,并產(chǎn)生這些偶對。 接下來我們需要考慮的是低葫,每個(gè)評論應(yīng)該只有 1 個(gè)回復(fù)详羡。 盡管許多單獨(dú)的評論可能會(huì)有很多回復(fù),但我們應(yīng)該只用一個(gè)嘿悬。 我們可以只用第一個(gè)实柠,或者我們可以用最頂上那個(gè)。 稍后再說善涨。 我們的第一個(gè)任務(wù)是獲取數(shù)據(jù)窒盐。 如果你有存儲(chǔ)限制,你可以查看一個(gè)月的 Reddit 評論钢拧,這是 2015 年 1 月蟹漓。否則,你可以獲取整個(gè)轉(zhuǎn)儲(chǔ):

magnet:?xt=urn:btih:7690f71ea949b868080401c749e878f98de34d3d&dn=reddit%5Fdata&tr=http%3A%2F%2Ftracker.pushshift.io%3A6969%2Fannounce&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80

我只下載過兩次這個(gè)種子娶靡,但根據(jù)種子和對等的不同牧牢,下載速度可能會(huì)有很大差異。

最后姿锭,你還可以通過 Google BigQuery 查看所有 Reddit 評論塔鳍。 BigQuery 表似乎隨著時(shí)間的推移而更新,而 torrent 不是呻此,所以這也是一個(gè)不錯(cuò)的選擇轮纫。 我個(gè)人將會(huì)使用 torrent,因?yàn)樗峭耆赓M(fèi)的焚鲜,所以掌唾,如果你想完全遵循它放前,就需要這樣做,但如果你愿意的話糯彬,可以隨意改變主意凭语,使用 Google BigQuery 的東西!

由于數(shù)據(jù)下載可能需要相當(dāng)長的時(shí)間撩扒,我會(huì)在這里中斷似扔。 一旦你下載了數(shù)據(jù),繼續(xù)下一個(gè)教程搓谆。 你可以僅僅下載2015-01文件來跟隨整個(gè)系列教程炒辉,你不需要整個(gè) 17 億個(gè)評論轉(zhuǎn)儲(chǔ)。 一個(gè)月的就足夠了泉手。

二黔寇、聊天數(shù)據(jù)結(jié)構(gòu)

歡迎閱讀 Python 和 TensorFlow 聊天機(jī)器人系列教程的第二部分。現(xiàn)在斩萌,我假設(shè)你已經(jīng)下載了數(shù)據(jù)缝裤,或者你只是在這里觀看。對于大多數(shù)機(jī)器學(xué)習(xí)术裸,你需要獲取數(shù)據(jù)倘是,并且某些時(shí)候需要輸入和輸出。對于神經(jīng)網(wǎng)絡(luò)袭艺,這表示實(shí)際神經(jīng)網(wǎng)絡(luò)的輸入層和輸出層搀崭。對于聊天機(jī)器人來說,這意味著我們需要將東西拆成評論和回復(fù)猾编。評論是輸入瘤睹,回復(fù)是所需的輸出≮逅耄現(xiàn)在使用 Reddit灯帮,并不是所有的評論都有回復(fù)集惋,然后很多評論會(huì)有很多回復(fù)拦键!我們需要挑一個(gè)。

我們需要考慮的另一件事是虏肾,當(dāng)我們遍歷這個(gè)文件時(shí)侣灶,我們可能會(huì)發(fā)現(xiàn)一個(gè)回復(fù)锰茉,但隨后我們可能會(huì)找到更好的回復(fù)倔既。我們可以使用一種方法是看看得票最高的恕曲。我們可能也只想要得票最高的回應(yīng)。我們可以考慮在這里很多事情渤涌,按照你的希望隨意調(diào)整佩谣!

首先,我們的數(shù)據(jù)格式实蓬,如果我們走了 torrent 路線:

{"author":"Arve","link_id":"t3_5yba3","score":0,"body":"Can we please deprecate the word \"Ajax\" now? \r\n\r\n(But yeah, this _is_ much nicer)","score_hidden":false,"author_flair_text":null,"gilded":0,"subreddit":"reddit.com","edited":false,"author_flair_css_class":null,"retrieved_on":1427426409,"name":"t1_c0299ap","created_utc":"1192450643","parent_id":"t1_c02999p","controversiality":0,"ups":0,"distinguished":null,"id":"c0299ap","subreddit_id":"t5_6","downs":0,"archived":true}

每一行就像上面那樣茸俭。我們并不需要這些數(shù)據(jù)的全部吊履,但是我們肯定需要bodycomment_idparent_id调鬓。如果你下載完整的 torrent 文件艇炎,或者正在使用 BigQuery 數(shù)據(jù)庫,那么可以使用樣例數(shù)據(jù)腾窝,所以我也將使用score冕臭。我們可以為分?jǐn)?shù)設(shè)定限制。我們也可以處理特定的subreddit燕锥,來創(chuàng)建一個(gè)說話風(fēng)格像特定 subreddit 的 AI。現(xiàn)在悯蝉,我會(huì)處理所有 subreddit归形。

現(xiàn)在,即使一個(gè)月的評論也可能超過 32GB鼻由,我也無法將其納入 RAM暇榴,我們需要通過數(shù)據(jù)進(jìn)行緩沖。我的想法是繼續(xù)并緩沖評論文件蕉世,然后將我們感興趣的數(shù)據(jù)存儲(chǔ)到 SQLite 數(shù)據(jù)庫中蔼紧。這里的想法是我們可以將評論數(shù)據(jù)插入到這個(gè)數(shù)據(jù)庫中。所有評論將按時(shí)間順序排列狠轻,所有評論最初都是“父節(jié)點(diǎn)”奸例,自己并沒有父節(jié)點(diǎn)。隨著時(shí)間的推移向楼,會(huì)有回復(fù)查吊,然后我們可以存儲(chǔ)這個(gè)“回復(fù)”,它將在數(shù)據(jù)庫中有父節(jié)點(diǎn)湖蜕,我們也可以按照 ID 拉取逻卖,然后我們可以檢索一些行,其中我們擁有父評論和回復(fù)昭抒。

然后评也,隨著時(shí)間的推移,我們可能會(huì)發(fā)現(xiàn)父評論的回復(fù)灭返,這些回復(fù)的投票數(shù)高于目前在那里的回復(fù)盗迟。發(fā)生這種情況時(shí),我們可以使用新信息更新該行婆殿,以便我們可以最終得到通常投票數(shù)較高的回復(fù)诈乒。

無論如何,有很多方法可以實(shí)現(xiàn)婆芦,讓我們開始吧怕磨!首先喂饥,讓我們進(jìn)行一些導(dǎo)入:

import sqlite3
import json
from datetime import datetime

我們將為我們的數(shù)據(jù)庫使用sqlite3json用于從datadump加載行肠鲫,然后datetime實(shí)際只是為了記錄员帮。 這不完全必要。

所以 torrent 轉(zhuǎn)儲(chǔ)帶有一大堆目錄导饲,其中包含實(shí)際的json數(shù)據(jù)轉(zhuǎn)儲(chǔ)捞高,按年和月(YYYY-MM)命名。 他們壓縮為.bz2渣锦。 確保你提取你打算使用的那些硝岗。 我們不打算編寫代碼來做,所以請確保你完成了袋毙!

下面型檀,我們以一些變量開始:

timeframe = '2015-05'
sql_transaction = []

connection = sqlite3.connect('{}.db'.format(timeframe))
c = connection.cursor()

timeframe值將成為我們將要使用的數(shù)據(jù)的年份和月份。 你也可以把它列在這里听盖,然后如果你喜歡胀溺,可以遍歷它們。 現(xiàn)在皆看,我將只用 2015 年 5 月的文件仓坞。 接下來,我們有sql_transaction腰吟。 所以在 SQL 中的“提交”是更昂貴的操作无埃。 如果你知道你將要插入數(shù)百萬行,你也應(yīng)該知道你真的不應(yīng)該一一提交蝎困。 相反录语,你只需在單個(gè)事務(wù)中構(gòu)建語句,然后執(zhí)行全部操作禾乘,然后提交澎埠。 接下來,我們要?jiǎng)?chuàng)建我們的表始藕。 使用 SQLite蒲稳,如果數(shù)據(jù)庫尚不存在,連接時(shí)會(huì)創(chuàng)建數(shù)據(jù)庫伍派。

def create_table():
    c.execute("CREATE TABLE IF NOT EXISTS parent_reply(parent_id TEXT PRIMARY KEY, comment_id TEXT UNIQUE, parent TEXT, comment TEXT, subreddit TEXT, unix INT, score INT)")

在這里江耀,我們正在準(zhǔn)備存儲(chǔ)parent_idcomment_id诉植,父評論祥国,回復(fù)(評論),subreddit,時(shí)間舌稀,然后最后是評論的評分(得票)啊犬。

接下來,我們可以開始我們的主代碼塊:

if __name__ == '__main__':
    create_table()

目前為止的完整代碼:

import sqlite3
import json
from datetime import datetime

timeframe = '2015-05'
sql_transaction = []

connection = sqlite3.connect('{}2.db'.format(timeframe))
c = connection.cursor()

def create_table():
    c.execute("CREATE TABLE IF NOT EXISTS parent_reply(parent_id TEXT PRIMARY KEY, comment_id TEXT UNIQUE, parent TEXT, comment TEXT, subreddit TEXT, unix INT, score INT)")

if __name__ == '__main__':
    create_table()

一旦我們建立完成壁查,我們就可以開始遍歷我們的數(shù)據(jù)文件并存儲(chǔ)這些信息觉至。 我們將在下一個(gè)教程中開始這樣做!

三睡腿、緩沖數(shù)據(jù)

你好语御,歡迎閱讀 Python TensorFlow 聊天機(jī)器人系列教程的第 3 部分。 在上一篇教程中席怪,我們討論了數(shù)據(jù)的結(jié)構(gòu)并創(chuàng)建了一個(gè)數(shù)據(jù)庫來存放我們的數(shù)據(jù)应闯。 現(xiàn)在我們準(zhǔn)備好開始處理數(shù)據(jù)了!

目前為止的代碼:

import sqlite3
import json
from datetime import datetime

timeframe = '2015-05'
sql_transaction = []

connection = sqlite3.connect('{}.db'.format(timeframe))
c = connection.cursor()

def create_table():
    c.execute("CREATE TABLE IF NOT EXISTS parent_reply(parent_id TEXT PRIMARY KEY, comment_id TEXT UNIQUE, parent TEXT, comment TEXT, subreddit TEXT, unix INT, score INT)")

if __name__ == '__main__':
    create_table()

現(xiàn)在挂捻,讓我們開始緩沖數(shù)據(jù)孽锥。 我們還將啟動(dòng)一些跟蹤時(shí)間進(jìn)度的計(jì)數(shù)器:

if __name__ == '__main__':
    create_table()
    row_counter = 0
    paired_rows = 0

    with open('J:/chatdata/reddit_data/{}/RC_{}'.format(timeframe.split('-')[0],timeframe), buffering=1000) as f:
        for row in f:

row_counter會(huì)不時(shí)輸出,讓我們知道我們在迭代的文件中走了多遠(yuǎn)细层,然后paired_rows會(huì)告訴我們有多少行數(shù)據(jù)是成對的(意味著我們有成對的評論和回復(fù),這是訓(xùn)練數(shù)據(jù))唬涧。 請注意疫赎,當(dāng)然,你的數(shù)據(jù)文件的實(shí)際路徑將與我的路徑不同碎节。

接下來捧搞,由于文件太大,我們無法在內(nèi)存中處理狮荔,所以我們將使用buffering參數(shù)胎撇,所以我們可以輕松地以小塊讀取文件,這很好殖氏,因?yàn)槲覀冃枰P(guān)心的所有東西是一次一行晚树。

現(xiàn)在,我們需要讀取json格式這一行:

if __name__ == '__main__':
    create_table()
    row_counter = 0
    paired_rows = 0

    with open('J:/chatdata/reddit_data/{}/RC_{}'.format(timeframe.split('-')[0],timeframe), buffering=1000) as f:
        for row in f:
            row_counter += 1
            row = json.loads(row)
            parent_id = row['parent_id']
            body = format_data(row['body'])
            created_utc = row['created_utc']
            score = row['score']
            comment_id = row['name']
            subreddit = row['subreddit']

請注意format_data函數(shù)調(diào)用雅采,讓我們創(chuàng)建:

def format_data(data):
    data = data.replace('\n',' newlinechar ').replace('\r',' newlinechar ').replace('"',"'")
    return data

我們將引入這個(gè)來規(guī)范平凡并將換行符轉(zhuǎn)換為一個(gè)單詞爵憎。

我們可以使用json.loads()將數(shù)據(jù)讀取到 python 對象中,這只需要json對象格式的字符串婚瓜。 如前所述宝鼓,所有評論最初都沒有父級,也就是因?yàn)樗琼敿壴u論(父級是 reddit 帖子本身)巴刻,或者是因?yàn)楦讣壊辉谖覀兊奈臋n中愚铡。 然而,在我們?yōu)g覽文檔時(shí)胡陪,我們會(huì)發(fā)現(xiàn)那些評論沥寥,父級確實(shí)在我們數(shù)據(jù)庫中碍舍。 發(fā)生這種情況時(shí),我們希望將此評論添加到現(xiàn)有的父級营曼。 一旦我們?yōu)g覽了一個(gè)文件或者一個(gè)文件列表乒验,我們就會(huì)輸出數(shù)據(jù)庫并作為訓(xùn)練數(shù)據(jù),訓(xùn)練我們的模型蒂阱,最后有一個(gè)我們可以聊天的朋友锻全! 所以,在我們把數(shù)據(jù)輸入到數(shù)據(jù)庫之前录煤,我們應(yīng)該看看能否先找到父級鳄厌!

            parent_data = find_parent(parent_id)

現(xiàn)在我們需要尋找find_parent函數(shù):

def find_parent(pid):
    try:
        sql = "SELECT comment FROM parent_reply WHERE comment_id = '{}' LIMIT 1".format(pid)
        c.execute(sql)
        result = c.fetchone()
        if result != None:
            return result[0]
        else: return False
    except Exception as e:
        #print(str(e))
        return False

有可能存在實(shí)現(xiàn)他的更有效的方法,但是這樣管用妈踊。 所以了嚎,如果我們的數(shù)據(jù)庫中存在comment_id匹配另一個(gè)評論的parent_id,那么我們應(yīng)該將這個(gè)新評論與我們已經(jīng)有的父評論匹配廊营。 在下一個(gè)教程中歪泳,我們將開始構(gòu)建確定是否插入數(shù)據(jù)所需的邏輯以及方式。

四露筒、插入邏輯

歡迎閱讀 Python TensorFlow 聊天機(jī)器人系列教程的第 4 部分呐伞。 目前為止,我們已經(jīng)獲得了我們的數(shù)據(jù)慎式,并開始遍歷伶氢。 現(xiàn)在我們準(zhǔn)備開始構(gòu)建用于輸入數(shù)據(jù)的實(shí)際邏輯。

首先瘪吏,我想對全部評論加以限制癣防,不管是否有其他評論,那就是我們只想處理毫無意義的評論掌眠。 基于這個(gè)原因蕾盯,我想說我們只想考慮兩票或以上的評論。 目前為止的代碼:

import sqlite3
import json
from datetime import datetime

timeframe = '2015-05'
sql_transaction = []

connection = sqlite3.connect('{}.db'.format(timeframe))
c = connection.cursor()

def create_table():
    c.execute("CREATE TABLE IF NOT EXISTS parent_reply(parent_id TEXT PRIMARY KEY, comment_id TEXT UNIQUE, parent TEXT, comment TEXT, subreddit TEXT, unix INT, score INT)")

def format_data(data):
    data = data.replace('\n',' newlinechar ').replace('\r',' newlinechar ').replace('"',"'")
    return data

def find_parent(pid):
    try:
        sql = "SELECT comment FROM parent_reply WHERE comment_id = '{}' LIMIT 1".format(pid)
        c.execute(sql)
        result = c.fetchone()
        if result != None:
            return result[0]
        else: return False
    except Exception as e:
        #print(str(e))
        return False


if __name__ == '__main__':
    create_table()
    row_counter = 0
    paired_rows = 0

    with open('J:/chatdata/reddit_data/{}/RC_{}'.format(timeframe.split('-')[0],timeframe), buffering=1000) as f:
        for row in f:
            row_counter += 1
            row = json.loads(row)
            parent_id = row['parent_id']
            body = format_data(row['body'])
            created_utc = row['created_utc']
            score = row['score']
            comment_id = row['name']
            subreddit = row['subreddit']
            parent_data = find_parent(parent_id)

現(xiàn)在讓我們要求票數(shù)是兩個(gè)或更多蓝丙,然后讓我們看看是否已經(jīng)有了父級的回復(fù)刑枝,以及票數(shù)是多少:

if __name__ == '__main__':
    create_table()
    row_counter = 0
    paired_rows = 0

    with open('J:/chatdata/reddit_data/{}/RC_{}'.format(timeframe.split('-')[0],timeframe), buffering=1000) as f:
        for row in f:
            row_counter += 1
            row = json.loads(row)
            parent_id = row['parent_id']
            body = format_data(row['body'])
            created_utc = row['created_utc']
            score = row['score']
            comment_id = row['name']
            subreddit = row['subreddit']
            parent_data = find_parent(parent_id)
            # maybe check for a child, if child, is our new score superior? If so, replace. If not...

            if score >= 2:
                existing_comment_score = find_existing_score(parent_id)

現(xiàn)在我們需要?jiǎng)?chuàng)建find_existing_score函數(shù):

def find_existing_score(pid):
    try:
        sql = "SELECT score FROM parent_reply WHERE parent_id = '{}' LIMIT 1".format(pid)
        c.execute(sql)
        result = c.fetchone()
        if result != None:
            return result[0]
        else: return False
    except Exception as e:
        #print(str(e))
        return False

如果有現(xiàn)有評論,并且我們的分?jǐn)?shù)高于現(xiàn)有評論的分?jǐn)?shù)迅腔,我們想替換它:

            if score >= 2:
                existing_comment_score = find_existing_score(parent_id)
                if existing_comment_score:
                    if score > existing_comment_score:

接下來装畅,很多評論都被刪除,但也有一些評論非常長沧烈,或者很短掠兄。 我們希望確保評論的長度適合于訓(xùn)練,并且評論未被刪除:

def acceptable(data):
    if len(data.split(' ')) > 50 or len(data) < 1:
        return False
    elif len(data) > 1000:
        return False
    elif data == '[deleted]':
        return False
    elif data == '[removed]':
        return False
    else:
        return True

好了,到了這里蚂夕,我們已經(jīng)準(zhǔn)備好開始插入數(shù)據(jù)了迅诬,這就是我們將在下一個(gè)教程中做的事情。

五婿牍、構(gòu)建數(shù)據(jù)庫

歡迎閱讀 Python TensorFlow 聊天機(jī)器人系列教程的第 5 部分侈贷。 在本教程之前,我們一直在處理我們的數(shù)據(jù)等脂,準(zhǔn)備插入數(shù)據(jù)的邏輯俏蛮,現(xiàn)在我們已經(jīng)準(zhǔn)備好開始插入了。 目前為止的代碼:

import sqlite3
import json
from datetime import datetime

timeframe = '2015-05'
sql_transaction = []

connection = sqlite3.connect('{}.db'.format(timeframe))
c = connection.cursor()

def create_table():
    c.execute("CREATE TABLE IF NOT EXISTS parent_reply(parent_id TEXT PRIMARY KEY, comment_id TEXT UNIQUE, parent TEXT, comment TEXT, subreddit TEXT, unix INT, score INT)")

def format_data(data):
    data = data.replace('\n',' newlinechar ').replace('\r',' newlinechar ').replace('"',"'")
    return data

def acceptable(data):
    if len(data.split(' ')) > 50 or len(data) < 1:
        return False
    elif len(data) > 1000:
        return False
    elif data == '[deleted]':
        return False
    elif data == '[removed]':
        return False
    else:
        return True

def find_parent(pid):
    try:
        sql = "SELECT comment FROM parent_reply WHERE comment_id = '{}' LIMIT 1".format(pid)
        c.execute(sql)
        result = c.fetchone()
        if result != None:
            return result[0]
        else: return False
    except Exception as e:
        #print(str(e))
        return False

def find_existing_score(pid):
    try:
        sql = "SELECT score FROM parent_reply WHERE parent_id = '{}' LIMIT 1".format(pid)
        c.execute(sql)
        result = c.fetchone()
        if result != None:
            return result[0]
        else: return False
    except Exception as e:
        #print(str(e))
        return False
    
if __name__ == '__main__':
    create_table()
    row_counter = 0
    paired_rows = 0

    with open('J:/chatdata/reddit_data/{}/RC_{}'.format(timeframe.split('-')[0],timeframe), buffering=1000) as f:
        for row in f:
            row_counter += 1
            row = json.loads(row)
            parent_id = row['parent_id']
            body = format_data(row['body'])
            created_utc = row['created_utc']
            score = row['score']
            comment_id = row['name']
            subreddit = row['subreddit']
            parent_data = find_parent(parent_id)
            if score >= 2:
                existing_comment_score = find_existing_score(parent_id)

現(xiàn)在上遥,如果有現(xiàn)有的評論分?jǐn)?shù)搏屑,這意味著已經(jīng)存在一個(gè)評論,所以這需要更新語句粉楚。 如果你還不知道 SQL辣恋,那么你可能需要閱讀 SQLite 教程。 所以我們的邏輯最初是:

            if score >= 2:
                existing_comment_score = find_existing_score(parent_id)
                if existing_comment_score:
                    if score > existing_comment_score:
                        if acceptable(body):
                            sql_insert_replace_comment(comment_id,parent_id,parent_data,body,subreddit,created_utc,score)

現(xiàn)在模软,我們需要構(gòu)建sql_insert_replace_comment函數(shù):

def sql_insert_replace_comment(commentid,parentid,parent,comment,subreddit,time,score):
    try:
        sql = """UPDATE parent_reply SET parent_id = ?, comment_id = ?, parent = ?, comment = ?, subreddit = ?, unix = ?, score = ? WHERE parent_id =?;""".format(parentid, commentid, parent, comment, subreddit, int(time), score, parentid)
        transaction_bldr(sql)
    except Exception as e:
        print('s0 insertion',str(e))

這涵蓋了評論已經(jīng)與父級配對的情況伟骨,但我們還需要處理沒有父級的評論(但可能是另一個(gè)評論的父級!)燃异,以及確實(shí)有父級底靠,并且它們的父級沒有回復(fù)的評論。 我們可以進(jìn)一步構(gòu)建插入塊:

     if score >= 2:
                existing_comment_score = find_existing_score(parent_id)
                if existing_comment_score:
                    if score > existing_comment_score:
                        if acceptable(body):
                            sql_insert_replace_comment(comment_id,parent_id,parent_data,body,subreddit,created_utc,score)

                else:
                    if acceptable(body):
                        if parent_data:
                            sql_insert_has_parent(comment_id,parent_id,parent_data,body,subreddit,created_utc,score)
                            paired_rows += 1
                        else:
                            sql_insert_no_parent(comment_id,parent_id,body,subreddit,created_utc,score)

現(xiàn)在我們需要構(gòu)建sql_insert_has_parentsql_insert_no_parent函數(shù):

def sql_insert_has_parent(commentid,parentid,parent,comment,subreddit,time,score):
    try:
        sql = """INSERT INTO parent_reply (parent_id, comment_id, parent, comment, subreddit, unix, score) VALUES ("{}","{}","{}","{}","{}",{},{});""".format(parentid, commentid, parent, comment, subreddit, int(time), score)
        transaction_bldr(sql)
    except Exception as e:
        print('s0 insertion',str(e))


def sql_insert_no_parent(commentid,parentid,comment,subreddit,time,score):
    try:
        sql = """INSERT INTO parent_reply (parent_id, comment_id, comment, subreddit, unix, score) VALUES ("{}","{}","{}","{}",{},{});""".format(parentid, commentid, comment, subreddit, int(time), score)
        transaction_bldr(sql)
    except Exception as e:
        print('s0 insertion',str(e))

所以為了看到我們在遍歷期間的位置特铝,我們將在每 10 萬行數(shù)據(jù)輸出一些信息:

            if row_counter % 100000 == 0:
                print('Total Rows Read: {}, Paired Rows: {}, Time: {}'.format(row_counter, paired_rows, str(datetime.now())))

最后,我們現(xiàn)在需要的代碼的最后一部分是壹瘟,我們需要構(gòu)建transaction_bldr函數(shù)鲫剿。 這個(gè)函數(shù)用來構(gòu)建插入語句,并以分組的形式提交它們稻轨,而不是一個(gè)接一個(gè)地提交灵莲。 這樣做會(huì)快得多:

def transaction_bldr(sql):
    global sql_transaction
    sql_transaction.append(sql)
    if len(sql_transaction) > 1000:
        c.execute('BEGIN TRANSACTION')
        for s in sql_transaction:
            try:
                c.execute(s)
            except:
                pass
        connection.commit()
        sql_transaction = []

是的,我用了個(gè)全局變量殴俱。

目前為止的代碼:

import sqlite3
import json
from datetime import datetime

timeframe = '2015-05'
sql_transaction = []

connection = sqlite3.connect('{}.db'.format(timeframe))
c = connection.cursor()

def create_table():
    c.execute("CREATE TABLE IF NOT EXISTS parent_reply(parent_id TEXT PRIMARY KEY, comment_id TEXT UNIQUE, parent TEXT, comment TEXT, subreddit TEXT, unix INT, score INT)")

def format_data(data):
    data = data.replace('\n',' newlinechar ').replace('\r',' newlinechar ').replace('"',"'")
    return data

def transaction_bldr(sql):
    global sql_transaction
    sql_transaction.append(sql)
    if len(sql_transaction) > 1000:
        c.execute('BEGIN TRANSACTION')
        for s in sql_transaction:
            try:
                c.execute(s)
            except:
                pass
        connection.commit()
        sql_transaction = []

def sql_insert_replace_comment(commentid,parentid,parent,comment,subreddit,time,score):
    try:
        sql = """UPDATE parent_reply SET parent_id = ?, comment_id = ?, parent = ?, comment = ?, subreddit = ?, unix = ?, score = ? WHERE parent_id =?;""".format(parentid, commentid, parent, comment, subreddit, int(time), score, parentid)
        transaction_bldr(sql)
    except Exception as e:
        print('s0 insertion',str(e))

def sql_insert_has_parent(commentid,parentid,parent,comment,subreddit,time,score):
    try:
        sql = """INSERT INTO parent_reply (parent_id, comment_id, parent, comment, subreddit, unix, score) VALUES ("{}","{}","{}","{}","{}",{},{});""".format(parentid, commentid, parent, comment, subreddit, int(time), score)
        transaction_bldr(sql)
    except Exception as e:
        print('s0 insertion',str(e))

def sql_insert_no_parent(commentid,parentid,comment,subreddit,time,score):
    try:
        sql = """INSERT INTO parent_reply (parent_id, comment_id, comment, subreddit, unix, score) VALUES ("{}","{}","{}","{}",{},{});""".format(parentid, commentid, comment, subreddit, int(time), score)
        transaction_bldr(sql)
    except Exception as e:
        print('s0 insertion',str(e))

def acceptable(data):
    if len(data.split(' ')) > 50 or len(data) < 1:
        return False
    elif len(data) > 1000:
        return False
    elif data == '[deleted]':
        return False
    elif data == '[removed]':
        return False
    else:
        return True

def find_parent(pid):
    try:
        sql = "SELECT comment FROM parent_reply WHERE comment_id = '{}' LIMIT 1".format(pid)
        c.execute(sql)
        result = c.fetchone()
        if result != None:
            return result[0]
        else: return False
    except Exception as e:
        #print(str(e))
        return False

def find_existing_score(pid):
    try:
        sql = "SELECT score FROM parent_reply WHERE parent_id = '{}' LIMIT 1".format(pid)
        c.execute(sql)
        result = c.fetchone()
        if result != None:
            return result[0]
        else: return False
    except Exception as e:
        #print(str(e))
        return False
    
if __name__ == '__main__':
    create_table()
    row_counter = 0
    paired_rows = 0

    with open('J:/chatdata/reddit_data/{}/RC_{}'.format(timeframe.split('-')[0],timeframe), buffering=1000) as f:
        for row in f:
            row_counter += 1
            row = json.loads(row)
            parent_id = row['parent_id']
            body = format_data(row['body'])
            created_utc = row['created_utc']
            score = row['score']
            comment_id = row['name']
            subreddit = row['subreddit']
            parent_data = find_parent(parent_id)
            if score >= 2:
                existing_comment_score = find_existing_score(parent_id)
                if existing_comment_score:
                    if score > existing_comment_score:
                        if acceptable(body):
                            sql_insert_replace_comment(comment_id,parent_id,parent_data,body,subreddit,created_utc,score)
                            
                else:
                    if acceptable(body):
                        if parent_data:
                            sql_insert_has_parent(comment_id,parent_id,parent_data,body,subreddit,created_utc,score)
                            paired_rows += 1
                        else:
                            sql_insert_no_parent(comment_id,parent_id,body,subreddit,created_utc,score)
                            
            if row_counter % 100000 == 0:
                print('Total Rows Read: {}, Paired Rows: {}, Time: {}'.format(row_counter, paired_rows, str(datetime.now())))

現(xiàn)在你可以開始運(yùn)行它了政冻。隨著時(shí)間的輸出應(yīng)該是:

Total Rows Read: 100000, Paired Rows: 3221, Time: 2017-11-14 15:14:33.748595
Total Rows Read: 200000, Paired Rows: 8071, Time: 2017-11-14 15:14:55.342929
Total Rows Read: 300000, Paired Rows: 13697, Time: 2017-11-14 15:15:18.035447
Total Rows Read: 400000, Paired Rows: 19723, Time: 2017-11-14 15:15:40.311376
Total Rows Read: 500000, Paired Rows: 25643, Time: 2017-11-14 15:16:02.045075

遍歷所有的數(shù)據(jù)將取決于起始文件的大小。 隨著數(shù)據(jù)量增大插入會(huì)減慢线欲。 為了處理 2015 年 5 月的整個(gè)文件明场,可能需要 5-10 個(gè)小時(shí)。

一旦你遍歷了你想要的文件李丰,我們已經(jīng)準(zhǔn)備好苦锨,將訓(xùn)練數(shù)據(jù)轉(zhuǎn)換為我們的模型,這就是我們將在下一個(gè)教程中做的事情。

如果你正在訓(xùn)練更大的數(shù)據(jù)集舟舒,你可能會(huì)發(fā)現(xiàn)我們需要處理的數(shù)據(jù)有很大的膨脹拉庶。 這是因?yàn)橹挥写蠹s 10% 的配對評論,所以我們的數(shù)據(jù)庫中很大一部分并沒有被實(shí)際使用秃励。 我使用下面的附加代碼:

            if row_counter % cleanup == 0:
                print("Cleanin up!")
                sql = "DELETE FROM parent_reply WHERE parent IS NULL"
                c.execute(sql)
                connection.commit()
                c.execute("VACUUM")
                connection.commit()

它在另一個(gè)計(jì)數(shù)器之下氏仗。這需要新的cleanup變量,它規(guī)定了“清理”之前的多少航夺鲜。這將消除我們的數(shù)據(jù)庫膨脹皆尔,并使插入速度保持相當(dāng)高。每個(gè)“清理”似乎移除 2K 對谣旁,幾乎無論你放在哪里床佳。如果每 100K 行一次,那么每 100K 行去掉 2K 對榄审。我選擇 100 萬砌们。另一個(gè)選項(xiàng)是每 100 萬行清理一次,但不清理最后一百萬行搁进,而是清理最后 110 萬行到第 100 萬行浪感,因?yàn)榭雌饋磉@些 2K 對在最后的 100K 中。即使這樣做饼问,你仍然會(huì)失去一些偶對影兽。我覺得每 100 萬行中,100K 對中的 2K 對并不重要莱革。我還添加了一個(gè)start_row變量峻堰,所以我可以在嘗試提高速度的同時(shí),啟動(dòng)和停止數(shù)據(jù)庫插入盅视。 c.execute("VACUUM")是一個(gè) SQL 命令捐名,用于將數(shù)據(jù)庫的大小縮小到應(yīng)該的值。實(shí)際上這可能不是必需的闹击,你可能只想在最后完成此操作镶蹋。我沒有測試這個(gè)操作需要多長時(shí)間。我是這樣做的赏半,所以我可以在刪除后立即看到數(shù)據(jù)庫的大小贺归。

完整代碼是:

import sqlite3
import json
from datetime import datetime
import time

timeframe = '2017-03'
sql_transaction = []
start_row = 0
cleanup = 1000000

connection = sqlite3.connect('{}.db'.format(timeframe))
c = connection.cursor()

def create_table():
    c.execute("CREATE TABLE IF NOT EXISTS parent_reply(parent_id TEXT PRIMARY KEY, comment_id TEXT UNIQUE, parent TEXT, comment TEXT, subreddit TEXT, unix INT, score INT)")

def format_data(data):
    data = data.replace('\n',' newlinechar ').replace('\r',' newlinechar ').replace('"',"'")
    return data

def transaction_bldr(sql):
    global sql_transaction
    sql_transaction.append(sql)
    if len(sql_transaction) > 1000:
        c.execute('BEGIN TRANSACTION')
        for s in sql_transaction:
            try:
                c.execute(s)
            except:
                pass
        connection.commit()
        sql_transaction = []

def sql_insert_replace_comment(commentid,parentid,parent,comment,subreddit,time,score):
    try:
        sql = """UPDATE parent_reply SET parent_id = ?, comment_id = ?, parent = ?, comment = ?, subreddit = ?, unix = ?, score = ? WHERE parent_id =?;""".format(parentid, commentid, parent, comment, subreddit, int(time), score, parentid)
        transaction_bldr(sql)
    except Exception as e:
        print('s0 insertion',str(e))

def sql_insert_has_parent(commentid,parentid,parent,comment,subreddit,time,score):
    try:
        sql = """INSERT INTO parent_reply (parent_id, comment_id, parent, comment, subreddit, unix, score) VALUES ("{}","{}","{}","{}","{}",{},{});""".format(parentid, commentid, parent, comment, subreddit, int(time), score)
        transaction_bldr(sql)
    except Exception as e:
        print('s0 insertion',str(e))

def sql_insert_no_parent(commentid,parentid,comment,subreddit,time,score):
    try:
        sql = """INSERT INTO parent_reply (parent_id, comment_id, comment, subreddit, unix, score) VALUES ("{}","{}","{}","{}",{},{});""".format(parentid, commentid, comment, subreddit, int(time), score)
        transaction_bldr(sql)
    except Exception as e:
        print('s0 insertion',str(e))

def acceptable(data):
    if len(data.split(' ')) > 1000 or len(data) < 1:
        return False
    elif len(data) > 32000:
        return False
    elif data == '[deleted]':
        return False
    elif data == '[removed]':
        return False
    else:
        return True

def find_parent(pid):
    try:
        sql = "SELECT comment FROM parent_reply WHERE comment_id = '{}' LIMIT 1".format(pid)
        c.execute(sql)
        result = c.fetchone()
        if result != None:
            return result[0]
        else: return False
    except Exception as e:
        #print(str(e))
        return False

def find_existing_score(pid):
    try:
        sql = "SELECT score FROM parent_reply WHERE parent_id = '{}' LIMIT 1".format(pid)
        c.execute(sql)
        result = c.fetchone()
        if result != None:
            return result[0]
        else: return False
    except Exception as e:
        #print(str(e))
        return False
    
if __name__ == '__main__':
    create_table()
    row_counter = 0
    paired_rows = 0

    #with open('J:/chatdata/reddit_data/{}/RC_{}'.format(timeframe.split('-')[0],timeframe), buffering=1000) as f:
    with open('/home/paperspace/reddit_comment_dumps/RC_{}'.format(timeframe), buffering=1000) as f:
        for row in f:
            #print(row)
            #time.sleep(555)
            row_counter += 1

            if row_counter > start_row:
                try:
                    row = json.loads(row)
                    parent_id = row['parent_id'].split('_')[1]
                    body = format_data(row['body'])
                    created_utc = row['created_utc']
                    score = row['score']
                    
                    comment_id = row['id']
                    
                    subreddit = row['subreddit']
                    parent_data = find_parent(parent_id)
                    
                    existing_comment_score = find_existing_score(parent_id)
                    if existing_comment_score:
                        if score > existing_comment_score:
                            if acceptable(body):
                                sql_insert_replace_comment(comment_id,parent_id,parent_data,body,subreddit,created_utc,score)
                                
                    else:
                        if acceptable(body):
                            if parent_data:
                                if score >= 2:
                                    sql_insert_has_parent(comment_id,parent_id,parent_data,body,subreddit,created_utc,score)
                                    paired_rows += 1
                            else:
                                sql_insert_no_parent(comment_id,parent_id,body,subreddit,created_utc,score)
                except Exception as e:
                    print(str(e))
                            
            if row_counter % 100000 == 0:
                print('Total Rows Read: {}, Paired Rows: {}, Time: {}'.format(row_counter, paired_rows, str(datetime.now())))

            if row_counter > start_row:
                if row_counter % cleanup == 0:
                    print("Cleanin up!")
                    sql = "DELETE FROM parent_reply WHERE parent IS NULL"
                    c.execute(sql)
                    connection.commit()
                    c.execute("VACUUM")
                    connection.commit()
                

六、訓(xùn)練數(shù)據(jù)集

歡迎閱讀 Python TensorFlow 聊天機(jī)器人系列教程的第 6 部分断箫。 在這一部分拂酣,我們將著手創(chuàng)建我們的訓(xùn)練數(shù)據(jù)。 在本系列中仲义,我正在考慮使用兩種不同的整體模型和工作流程:我所知的一個(gè)方法(在開始時(shí)展示并在 Twitch 流上實(shí)時(shí)運(yùn)行)踱葛,另一個(gè)可能會(huì)更好丹莲,但我仍在探索它。 無論哪種方式尸诽,我們的訓(xùn)練數(shù)據(jù)設(shè)置都比較相似甥材。 我們需要?jiǎng)?chuàng)建文件,基本上是“父級”和“回復(fù)”文本性含,每一行都是一個(gè)樣本洲赵。 因此,父級文件中的第15行是父評論商蕴,然后在回復(fù)文件中的第 15 行是父文件中第 15 行的回復(fù)叠萍。

要?jiǎng)?chuàng)建這些文件,我們只需要從數(shù)據(jù)庫中獲取偶對绪商,然后將它們附加到相應(yīng)的訓(xùn)練文件中苛谷。 讓我們以這個(gè)開始:

import sqlite3
import pandas as pd

timeframes = ['2015-05']


for timeframe in timeframes:

對于這里的運(yùn)行,我只在單個(gè)月上運(yùn)行格郁,只創(chuàng)建了一個(gè)數(shù)據(jù)庫腹殿,但是你可能想創(chuàng)建一個(gè)數(shù)據(jù)庫,里面的表是月份和年份例书,或者你可以創(chuàng)建一堆 sqlite 數(shù)據(jù)庫 锣尉,表類似于我們這些,然后遍歷它們來創(chuàng)建你的文件决采。 無論如何自沧,我只有一個(gè),所以我會(huì)把timeframes作為一個(gè)單一的項(xiàng)目列表树瞭。 讓我們繼續(xù)構(gòu)建這個(gè)循環(huán):

for timeframe in timeframes:
    connection = sqlite3.connect('{}.db'.format(timeframe))
    c = connection.cursor()
    limit = 5000
    last_unix = 0
    cur_length = limit
    counter = 0
    test_done = False

第一行只是建立連接拇厢,然后我們定義游標(biāo),然后是limit晒喷。 限制是我們要從數(shù)據(jù)庫中一次抽取的塊的大小孝偎。 同樣,我們正在處理的數(shù)據(jù)比我們擁有的RAM大得多厨埋。 我們現(xiàn)在要將限制設(shè)為 5000,所以我們可以有一些測試數(shù)據(jù)捐顷。 我們可以稍后產(chǎn)生荡陷。 我們將使用last_unix來幫助我們從數(shù)據(jù)庫中提取數(shù)據(jù),cur_length會(huì)告訴我們什么時(shí)候我們完成了迅涮,counter會(huì)允許我們顯示一些調(diào)試信息废赞,而test_done用于我們完成構(gòu)建測試數(shù)據(jù)的時(shí)候。

    while cur_length == limit:

        df = pd.read_sql("SELECT * FROM parent_reply WHERE unix > {} and parent NOT NULL and score > 0 ORDER BY unix ASC LIMIT {}".format(last_unix,limit),connection)
        last_unix = df.tail(1)['unix'].values[0]
        cur_length = len(df)

只要cur_length與我們的限制相同叮姑,我們就仍然有更多的工作要做唉地。 然后据悔,我們將從數(shù)據(jù)庫中提取數(shù)據(jù)并將其轉(zhuǎn)換為數(shù)據(jù)幀。 目前耘沼,我們對數(shù)據(jù)幀沒有做太多的工作极颓,但是之后我們可以用它對我們想要考慮的數(shù)據(jù)類型設(shè)置更多限制。 我們存儲(chǔ)了last_unix群嗤,所以我們知道之后提取什么時(shí)候的菠隆。 我們也注意到回報(bào)的長度。 現(xiàn)在狂秘,建立我們的訓(xùn)練/測試文件骇径。 我們將從測試開始:

        if not test_done:
            with open('test.from','a', encoding='utf8') as f:
                for content in df['parent'].values:
                    f.write(content+'\n')

            with open('test.to','a', encoding='utf8') as f:
                for content in df['comment'].values:
                    f.write(str(content)+'\n')

            test_done = True

現(xiàn)在,如果你希望者春,你也可以在這個(gè)時(shí)候提高限制破衔。 在test_done = True之后,你也可以重新將limit定義為 100K 之類的東西钱烟。 現(xiàn)在晰筛,我們來為訓(xùn)練編寫代碼:

        else:
            with open('train.from','a', encoding='utf8') as f:
                for content in df['parent'].values:
                    f.write(content+'\n')

            with open('train.to','a', encoding='utf8') as f:
                for content in df['comment'].values:
                    f.write(str(content)+'\n')

我們可以通過把它做成一個(gè)函數(shù),來使這個(gè)代碼更簡單更好忠售,所以我們不會(huì)復(fù)制和粘貼基本相同的代碼传惠。 但是...相反...讓我們繼續(xù):

        counter += 1
        if counter % 20 == 0:
            print(counter*limit,'rows completed so far')

這里,我們每 20 步就會(huì)看到輸出稻扬,所以如果我們將限制保持為 5,000卦方,每 100K 步也是。

目前的完整代碼:

import sqlite3
import pandas as pd

timeframes = ['2015-05']

for timeframe in timeframes:
    connection = sqlite3.connect('{}.db'.format(timeframe))
    c = connection.cursor()
    limit = 5000
    last_unix = 0
    cur_length = limit
    counter = 0
    test_done = False

    while cur_length == limit:

        df = pd.read_sql("SELECT * FROM parent_reply WHERE unix > {} and parent NOT NULL and score > 0 ORDER BY unix ASC LIMIT {}".format(last_unix,limit),connection)
        last_unix = df.tail(1)['unix'].values[0]
        cur_length = len(df)

        if not test_done:
            with open('test.from','a', encoding='utf8') as f:
                for content in df['parent'].values:
                    f.write(content+'\n')

            with open('test.to','a', encoding='utf8') as f:
                for content in df['comment'].values:
                    f.write(str(content)+'\n')

            test_done = True

        else:
            with open('train.from','a', encoding='utf8') as f:
                for content in df['parent'].values:
                    f.write(content+'\n')

            with open('train.to','a', encoding='utf8') as f:
                for content in df['comment'].values:
                    f.write(str(content)+'\n')

        counter += 1
        if counter % 20 == 0:
            print(counter*limit,'rows completed so far')

好的泰佳,運(yùn)行它盼砍,當(dāng)你準(zhǔn)備好數(shù)據(jù)的時(shí)候,我就會(huì)看到逝她。

七浇坐、訓(xùn)練模型

歡迎閱讀 Python TensorFlow 聊天機(jī)器人系列教程的第 7 部分。 在這里黔宛,我們將討論我們的模型近刘。 你可以提出和使用無數(shù)的模型,或在網(wǎng)上找到并適配你的需求臀晃。 我的主要興趣是 Seq2Seq 模型觉渴,因?yàn)?Seq2Seq 可以用于聊天機(jī)器人,當(dāng)然也可以用于其他東西徽惋。 基本上案淋,生活中的所有東西都可以簡化為序列到序列的映射,所以我們可以訓(xùn)練相當(dāng)多的東西险绘。 但是對于現(xiàn)在:我想要一個(gè)聊天機(jī)器人踢京。

當(dāng)我開始尋找聊天機(jī)器人的時(shí)候誉碴,我偶然發(fā)現(xiàn)了原來的 TensorFlow seq2seq 翻譯教程,它把專注于英語到法語的翻譯上瓣距,并做了能用的工作黔帕。不幸的是,由于 seq2seq 的一些變化旨涝,現(xiàn)在這個(gè)模型已經(jīng)被棄用了蹬屹。有一個(gè)傳統(tǒng)的 seq2seq,你可以在最新的 TensorFlow 中使用白华,但我從來沒有讓它有效慨默。相反履羞,如果你想使用這個(gè)模型津函,你可能需要降級 TF(pip install tensorflow-gpu==1.0.0)≡或者管搪,你可以使用 TensorFlow 中最新虾攻,最好的 seq2seq 查看最新的神經(jīng)機(jī)器翻譯(NMT)模型。最新的 NMT 教程和來自 TensorFlow 的代碼可以在這里找到:神經(jīng)機(jī)器翻譯(seq2seq)教程更鲁。

我們打算使用一個(gè)項(xiàng)目霎箍,我一直與我的朋友丹尼爾合作來從事它。

該項(xiàng)目的位置是:NMT 機(jī)器人澡为,它是構(gòu)建在 TensorFlow 的 NMT 代碼之上的一組工具漂坏。

該項(xiàng)目可能會(huì)發(fā)生變化,因此你應(yīng)該檢查 README媒至,在撰寫本文時(shí)顶别,該文件寫了:

$ git clone --recursive https://github.com/daniel-kukiela/nmt-chatbot
$ cd nmt-chatbot
$ pip install -r requirements.txt
$ cd setup
(optional) edit settings.py to your liking. These are a decent starting point for ~4gb of VRAM, you should first start by trying to raise vocab if you can.
(optional) Edit text files containing rules in setup directory
Place training data inside "new_data" folder (train.(from|to), tst2012.(from|to)m tst2013(from|to)). We have provided some sample data for those who just want to do a quick test drive.
$ python prepare_data.py ...Run setup/prepare_data.py - new folder called "data" will be created with prepared training data
$ cd ../
$ python train.py Begin training

所以讓我們用它!我們將首先設(shè)置它拒啰,讓它運(yùn)行驯绎,然后我將解釋你應(yīng)該理解的主要概念。

如果你需要更多的處理能力谋旦,用這個(gè) 10 美元的折扣來查看 Paperspace剩失,這會(huì)給你足夠的時(shí)間來獲得一些像樣的東西。我一直在使用它們册着,并且非常喜歡我能夠快速啟動(dòng)“ML-in-a-Box”選項(xiàng)并立即訓(xùn)練模型拴孤。

確保遞歸下載軟件包,或者手動(dòng)獲取 nmt 軟件包指蚜,或者從我們的倉庫派生乞巧,或者從官方的 TensorFlow 源文件派生涨椒。我們的派生只是版本檢查的一次更改摊鸡,至少在那個(gè)時(shí)候绽媒,它需要非常特殊的 1.4.0 版本,而這實(shí)際上并不是必需的免猾。這可能會(huì)在你那個(gè)時(shí)候被修復(fù)是辕,但是我們也可能會(huì)對 NMT 核心代碼做進(jìn)一步的修改。

一旦下載完成猎提,編輯setup/settings.py获三。如果你真的不知道自己在做什么,那沒關(guān)系锨苏,你不需要修改任何東西疙教。預(yù)設(shè)設(shè)置將需要約 4GB 的 VRAM,但至少仍然應(yīng)該產(chǎn)生不錯(cuò)的模型伞租。 Charles v2 用以下設(shè)置訓(xùn)練贞谓,'vocab_size': 100000,(在腳本的前面設(shè)置):

hparams = {
    'attention': 'scaled_luong',
    'src': 'from',
    'tgt': 'to',
    'vocab_prefix': os.path.join(train_dir, "vocab"),
    'train_prefix': os.path.join(train_dir, "train"),
    'dev_prefix': os.path.join(train_dir, "tst2012"),
    'test_prefix': os.path.join(train_dir, "tst2013"),
    'out_dir': out_dir,
    'num_train_steps': 500000,
    'num_layers': 2,
    'num_units': 512,
    'override_loaded_hparams': True,
    'learning_rate':0.001,
#    'decay_factor': 0.99998,
    'decay_steps': 1,
#    'residual': True,
    'start_decay_step': 1,
    'beam_width': 10,
    'length_penalty_weight': 1.0,
    'optimizer': 'adam',
    'encoder_type': 'bi',
    'num_translations_per_input': 30
}

我手動(dòng)降低了學(xué)習(xí)率葵诈,因?yàn)?Adam 真的不需要逐漸衰減(亞當(dāng)?shù)?code>ada代表自適應(yīng)裸弦,m是時(shí)刻,所以adam就是自適應(yīng)時(shí)刻)作喘。 我以 0.001 開始理疙,然后減半到 0.0005,然后 0.00025泞坦,然后 0.0001窖贤。 根據(jù)你擁有的數(shù)據(jù)量,你不希望在每個(gè)設(shè)定的步驟上衰減暇矫。 當(dāng)使用 Adam 時(shí)主之,我會(huì)建議每 1-2 個(gè)迭代衰減一次。 默認(rèn)的批量大小是 128李根,因此如果你想要將其設(shè)置為自動(dòng)衰減槽奕,則可以計(jì)算出你的迭代的迭代步數(shù)。 如果你使用 SGD 優(yōu)化器房轿,那么注釋掉衰減因子沒有問題粤攒,并且你可能希望學(xué)習(xí)率從 1 開始。

一旦你完成了所有的設(shè)置囱持,在主目錄(utils夯接,testssetup目錄)中,把你的train.totrain.from以及匹配的tst2012tst2013文件放到new_data目錄中纷妆。 現(xiàn)在cd setup來運(yùn)行prepare_data.py文件:

$ python3 prepare_data.py

最后cd ../盔几,之后:

$ python3 train.py

在下一個(gè)教程中,我們將更深入地討論模型的工作原理掩幢,參數(shù)以及訓(xùn)練涉及的指標(biāo)逊拍。

八上鞠、探索我們的 NMT 模型的概念和參數(shù)

歡迎閱讀 Python TensorFlow 聊天機(jī)器人系列教程的第 8 部分。在這里芯丧,我們將討論我們的模型芍阎。

對你來說,最主要的區(qū)別就是分桶(bucketing)缨恒,填充(padding) 和更多的注意機(jī)制谴咸。在我們開始之前,先簡單地談?wù)勥@些事情骗露。首先岭佳,如果你熟悉神經(jīng)網(wǎng)絡(luò),請考慮 seq2seq 之類的任務(wù)萧锉,其中序列長度不完全相同驼唱。我們可以在聊天機(jī)器人范圍內(nèi)考慮這一點(diǎn),但也可以考慮其他領(lǐng)域驹暑。在聊天機(jī)器人的情況下玫恳,一個(gè)單詞的語句可以產(chǎn)生 20 個(gè)單詞的回復(fù),而長的語句可以返回單個(gè)單詞的回復(fù)优俘,并且每個(gè)輸入在字符京办,單詞等方面不同于輸出。單詞本身將被分配任意或有意義的 ID(通過單詞向量)帆焕,但是我們?nèi)绾翁幚砜勺冮L度惭婿?一個(gè)答案就是使所有的單詞串都是 50 個(gè)單詞(例如)。然后叶雹,當(dāng)語句長度為 35 個(gè)單詞時(shí)财饥,我們可以填充另外 15 個(gè)單詞。超過 50 個(gè)單詞的任何數(shù)據(jù)折晦,我們可以不用于訓(xùn)練或截?cái)唷?/p>

不幸的是钥星,這可能會(huì)讓訓(xùn)練變得困難,特別是對于可能最為常見的較短回復(fù)满着,并且大多數(shù)單詞/標(biāo)記只是填充谦炒。原始的 seq2seq(英語到法語)的例子使用分桶來解決這個(gè)問題,并用 4 個(gè)桶訓(xùn)練风喇。 5-10宁改,10-15,20-25 和 40-50魂莫,我們最終將訓(xùn)練數(shù)據(jù)放入適合輸入和輸出的最小桶中还蹲,但這不是很理想。

然后,我們有了 NMT 代碼谜喊,處理可變輸入鲸沮,沒有分桶或填充!接下來锅论,這段代碼還包含對注意機(jī)制的支持,這是一種向循環(huán)神經(jīng)網(wǎng)絡(luò)添加長期記憶的嘗試楣号。最后最易,我們還將使用雙向遞歸神經(jīng)網(wǎng)絡(luò)(BRNN)。我們來談?wù)勥@些事情炫狱。

一般來說藻懒,一個(gè) LSTM 可以很好地記住,長度達(dá)到 10-2 0的標(biāo)記的正確序列视译。然而嬉荆,在此之后,性能下降酷含,網(wǎng)絡(luò)忘記了最初的標(biāo)記鄙早,為新的標(biāo)記騰出空間。在我們的例子中椅亚,標(biāo)記是詞語限番,所以基本的 LSTM 應(yīng)該能夠?qū)W習(xí) 10-20 個(gè)單詞長度的句子,但是呀舔,當(dāng)我們比這更長的時(shí)候弥虐,輸出可能不會(huì)那么好。注意機(jī)制就引入了媚赖,提供了更長的“注意力跨度”霜瘪,這有助于網(wǎng)絡(luò)達(dá)到更多單詞,像 30惧磺,40 甚至 80 個(gè)颖对,例如。想象一下磨隘,如果只能用 3-10 個(gè)字來處理和回應(yīng)其他人的話惜互,對于你來說有多困難,在這 10 個(gè)字的標(biāo)記中琳拭,你會(huì)變得很草率训堆,像它一樣。在前面的句子中白嘁,你只需要想象一下坑鱼,如果你...在你需要以至少 10 個(gè)單詞開始建立你的回答之前,對你來說有多難÷沉ぃ滑動(dòng)一下呼股,你會(huì)得到:如果你只能這樣做,那么這將是很難的画恰,而這又不是真正有意義的彭谁,并且會(huì)很難做出很好的回應(yīng)。即使你確實(shí)知道你需要想象一些事情允扇,想象什么缠局?你必須等待,看看未來的元素考润,知道你打算想象什么...但是狭园,當(dāng)我們獲得了這些未來的元素,哦糊治,親愛的唱矛,我們早已錯(cuò)過了我們打算想象它的部分。這是雙向遞歸神經(jīng)網(wǎng)絡(luò)(BRNN)引入的地方井辜。

在許多 seq2seq 的任務(wù)中绎谦,比如語言翻譯,我們可以通過就地轉(zhuǎn)換單詞粥脚,學(xué)習(xí)簡單的語法規(guī)律燥滑,因?yàn)樵S多語言在語法上是相似的。 隨著自然語言和交際的發(fā)展阿逃,以及英語到日語等一些翻譯形式的出現(xiàn)铭拧,在語境,流動(dòng)等方面也越來越重要恃锉。 還有更多的事情要做搀菩。 雙向遞歸神經(jīng)網(wǎng)絡(luò)(BRNN)假定現(xiàn)在,過去和未來的數(shù)據(jù)在輸入序列中都是重要的破托。 雙向遞歸神經(jīng)網(wǎng)絡(luò)(BRNN)的“雙向”部分具有很好的描述性肪跋。 輸入序列是雙向的。 一個(gè)向前土砂,另一個(gè)向后州既。 為了說明這一點(diǎn):

https://pythonprogramming.net/static/images/machine-learning/bidirectional-recurrent-neural-network.png

在簡單的RNN上,你有輸入層萝映,你的輸出層吴叶,然后我們只有一個(gè)隱藏層。然后序臂,你從輸入層連接到隱藏層蚌卤,隱藏層中的每個(gè)節(jié)點(diǎn)也向下傳遞到下一個(gè)隱藏層節(jié)點(diǎn),這就是我們?nèi)绾蔚玫轿覀兊摹皶r(shí)間”,以及來自循環(huán)神經(jīng)網(wǎng)絡(luò)的非靜態(tài)特性逊彭,因?yàn)橹暗妮斎朐试S在隱藏層上向下和向下傳播咸灿。相反在 BRNN 上,你的隱藏層由相反方向的節(jié)點(diǎn)組成侮叮,所以你有輸入和輸出層避矢,然后你會(huì)有你的隱藏層。然而囊榜,與基本的 RNN 不同审胸,隱藏層向上和向下傳遞數(shù)據(jù)(或者向前和向后傳遞,取決于誰在繪制圖片)锦聊,這使得網(wǎng)絡(luò)能夠基于歷史上發(fā)生的事情,以及我們傳給序列的未來發(fā)生的事情箩绍,理解發(fā)生了什么孔庭。

下一個(gè)加入我們的網(wǎng)絡(luò)是一個(gè)注意機(jī)制,因?yàn)楸M管數(shù)據(jù)向前和向后傳遞材蛛,但是我們的網(wǎng)絡(luò)不能一次記住更長的序列(每次最多 3-10 個(gè)標(biāo)記)圆到。如果你正在給我們所用的單詞加上標(biāo)記,那么這意味著每次最多只有 3 到 10 個(gè)單詞卑吭,但是對于字符級別的模型來說芽淡,這個(gè)問題甚至更加棘手,你最多可以記住 3-10 個(gè)字符豆赏。但是挣菲,如果你做一個(gè)字符模型,你的詞匯數(shù)可能低得多掷邦。

有了注意機(jī)制白胀,我們可以處理序列中的 30, 40, 80+個(gè)標(biāo)記。下面是一個(gè)描述 BLEU 的圖片抚岗,其中包含或不包含注意機(jī)制:

https://pythonprogramming.net/static/images/machine-learning/attention-mechanism.png

BLEU代表“雙語評估替代”或杠,它可能是我們確定翻譯算法總體有效性的最佳方式。然而宣蔚,重要的是向抢,BLEU 將與我們正在翻譯的序列有關(guān)。例如胚委,我們的英語到法語的 BLEU 成績遠(yuǎn)遠(yuǎn)挟鸠,很可能高于英語到日語,甚至德語亩冬,或者單詞兄猩,思想或短語沒有任何直接翻譯的語言。在我們的例子中,我們正在將序列翻譯成序列枢冤,兩個(gè)都是英文序列鸠姨,所以我們應(yīng)該看到一個(gè)非常高的 BLEU?可能不是淹真。有了語言翻譯讶迁,對于一個(gè)輸入,經(jīng)常存在“確切”或至少一些“完美”的匹配(同樣核蘸,有些東西不能完美翻譯巍糯,但這不會(huì)是多數(shù))。有了對話數(shù)據(jù)客扎,對于某些陳述真的有一個(gè)“確切”的答案嗎祟峦?絕對不是。我們應(yīng)該期待看到徙鱼,BLEU 隨著時(shí)間的推移緩慢上升宅楞,但不期望看到 BLEU 得分與語言翻譯任務(wù)類似的。

注意機(jī)制不僅幫助我們處理更長的序列袱吆,而且還改善了短的厌衙。注意機(jī)制也允許學(xué)習(xí)比我們需要的聊天機(jī)器的更復(fù)雜。他們的主要驅(qū)動(dòng)力似乎不僅是語言绞绒,在英語和法語之間進(jìn)行翻譯相對比較容易婶希,但像日語這樣的語言結(jié)構(gòu)需要更多的注意。你可能真的需要看看 100 個(gè)單詞的日語句子的結(jié)尾蓬衡,來辨別第一個(gè)英文單詞應(yīng)該是什么喻杈,反之亦然。通過我們的聊天機(jī)器人狰晚,我們面臨類似的困擾奕塑。我們沒有將詞翻譯為詞,將名詞短語翻譯為名詞短語家肯。相反龄砰,輸入序列的結(jié)束可以并且通常完全確定輸出序列應(yīng)該是什么。我稍后可能會(huì)更深入地關(guān)注注意機(jī)制讨衣,但現(xiàn)在换棚,這對于大體思路已經(jīng)足夠了。

除了 BLEU反镇,你也要看看 Perplexity固蚤,通常是縮寫為“PPL”。Perplexity 是另一個(gè)有用的方法歹茶,衡量模型的有效性夕玩。與 BLEU 不同的是你弦,它越低越好,因?yàn)樗悄P皖A(yù)測樣本輸出效果的概率分布燎孟。同樣禽作,對于語言翻譯。

有了 BLEU 和 PPL揩页,有了翻譯旷偿,只要 BLEU 上升,PPL 下降爆侣,你通称汲蹋可以訓(xùn)練一個(gè)模型。然而兔仰,如果一個(gè)聊天機(jī)器人從來沒有或者從來不應(yīng)該是一個(gè)“正確”的答案茫负,那么只要 BLEU 和 PPL 上升,我就會(huì)警告不要繼續(xù)訓(xùn)練乎赴,因?yàn)檫@樣可能會(huì)產(chǎn)生更多的機(jī)器人似的反應(yīng)忍法,而不是高度多樣的。我們還有其他方法可以解決這個(gè)問題无虚,以后我們可以解決缔赠。

我希望這不是你第一個(gè)機(jī)器學(xué)習(xí)教程衍锚,但是友题,如果是這樣,你也應(yīng)該知道什么是損失戴质《然拢基本上損失是一個(gè)度量,衡量你的神經(jīng)網(wǎng)絡(luò)輸出層與樣本數(shù)據(jù)的“接近”程度告匠。損失越低越好戈抄。

我想提到的最后一個(gè)概念是 Beam Search。使用這個(gè)后专,我們可以從我們的模型中查看一系列頂級翻譯划鸽,而不僅僅是最頂端的一個(gè)而不考慮其他的。這樣做會(huì)導(dǎo)致翻譯時(shí)間更長戚哎,但在我看來裸诽,翻譯模型必須這樣,因?yàn)槲覀儠?huì)發(fā)現(xiàn)型凳,我們的模型仍然很有可能產(chǎn)生我們不想要的輸出丈冬,但是對訓(xùn)練這些輸出可能會(huì)導(dǎo)致其他地方的過擬合。允許多種翻譯將有助于訓(xùn)練和生產(chǎn)甘畅。

好的埂蕊,在下一個(gè)教程中往弓,我們將討論如何開始與聊天機(jī)器人進(jìn)行交互。

九蓄氧、與聊天機(jī)器人交互

歡迎閱讀 Python Tensorflow 和深度學(xué)習(xí)聊天機(jī)器人系列教程的第 9 部分函似。 在本教程中,我們將討論如何與我們的模型進(jìn)行交互匀们,甚至可能將其推入生產(chǎn)環(huán)境缴淋。

在訓(xùn)練你的模型時(shí),默認(rèn)情況下每 1,000 步將保存一個(gè)檢查點(diǎn)文件泄朴。 如果你需要或想要停止你的訓(xùn)練重抖,你可以安全地這樣做,并選擇最近的檢查點(diǎn)的備份祖灰。 每個(gè)檢查點(diǎn)的保存數(shù)據(jù)包含各種日志記錄參數(shù)钟沛,還包括模型的完整權(quán)重/偏差等。 這意味著你可以選取這些檢查點(diǎn)/模型文件局扶,并使用它們繼續(xù)訓(xùn)練或在生產(chǎn)中使用它們恨统。

檢查點(diǎn)默認(rèn)保存在模型目錄中。 你應(yīng)該看到名為translate.ckpt-XXXXX的文件三妈,其中X對應(yīng)于步驟序號畜埋。 你應(yīng)該有.data.index和一個(gè).meta文件畴蒲,以及檢查點(diǎn)文件悠鞍。 如果你打開檢查點(diǎn)文件,你會(huì)看到它看起來像:

model_checkpoint_path: "/home/paperspace/Desktop/nmt-chatbot/model/translate.ckpt-225000"
all_model_checkpoint_paths: "/home/paperspace/Desktop/nmt-chatbot/model/translate.ckpt-221000"
all_model_checkpoint_paths: "/home/paperspace/Desktop/nmt-chatbot/model/translate.ckpt-222000"
all_model_checkpoint_paths: "/home/paperspace/Desktop/nmt-chatbot/model/translate.ckpt-223000"
all_model_checkpoint_paths: "/home/paperspace/Desktop/nmt-chatbot/model/translate.ckpt-224000"
all_model_checkpoint_paths: "/home/paperspace/Desktop/nmt-chatbot/model/translate.ckpt-225000"

這僅僅讓你的模型知道使用哪些文件模燥。 如果你想使用一個(gè)特定的咖祭,較老的模型,你可以編輯它蔫骂。

因此么翰,為了加載模型,我們需要 4 個(gè)文件辽旋。 假設(shè)我們的步驟是 22.5 萬浩嫌。 這意味著我們需要以下內(nèi)容來運(yùn)行我們的模型,或者加載它來繼續(xù)訓(xùn)練:

checkpoint
translate.ckpt-225000.meta
translate.ckpt-225000.index
translate.ckpt-225000.data-00000-of-00001

因此补胚,如果你轉(zhuǎn)移到云中的某臺(tái)計(jì)算機(jī)上码耐,無論是用于訓(xùn)練還是生產(chǎn),這些都是你需要的文件糖儡。

除了每隔 1000 步保存檢查點(diǎn)外伐坏,我們還會(huì)做一些更多的示例(來自我們的tst.totst.from文件)。 這些數(shù)據(jù)每千步輸出一次握联,并進(jìn)入模型目錄以及output_devoutput_test桦沉。 你可以使用這些文件查看每隔 1000 個(gè)步驟在控制臺(tái)中完成的單個(gè)示例每瞒。 這些輸出文件純粹是測試文件的,頂級輸出語句的結(jié)果響應(yīng)纯露。 既然你可以在你的測試文件中添加你想要的任何示例剿骨,那么這是你可以與聊天機(jī)器人進(jìn)行交互的第一種方式,或者至少可以看到交互埠褪。 我寫了一個(gè)簡單的配對腳本浓利,來輸出測試文件和輸出文件的評論響應(yīng)偶對。

例如钞速,假設(shè)你已經(jīng)有了你的tst2013.from文件:

Aren ' t they streaming it for free online ... ?
try to get loud please
I ' m trying to eat a fajita here
E
It ' s been 3 innings and Spanton almost hit a dong .
Looks - wise yes , play - wise no
But we ' d both pay $ 9 . 9 9 to see that . newlinechar newlinechar Isn ' t he doing stuff for CZW ? Aren ' t they like extreme stuff , they should do a Punjabi Prison Match with CJ Parker .
' I simply feel as though the game is not for me . ' * Zaffre states , turning back to Ambrose , a frown on his face . *
The fire escape is there . You hear wood splintering , and look to see that a raptor has managed to break a hole in the top of the door , just above the dresser . Its head pokes through , then disappears . There ' s another thud , and the dresser moves forward a few inches .
[ ] ( / fritteehee ) I wonder how I ' ll make the eyes all red ...
3 6 0 , 6 7 8
I like the idea ... have an upvote !
who talks trash about Giannis ?
C
I ' m pretty sure that ' s the peace music .
Did well on my quiz today , am now eating ice cream . Good day .

之后是你的output_dev文件:

Yes they are .
I don ' t think I ' ve ever heard of this . I ' ll have to check it out .
<unk>
R
It ' s been a while since I ' ve seen it , but it ' s been a while since I ' ve seen it .
I don ' t think I ' ve ever played - wise .
I don ' t think he ' s doing anything for <unk> . I ' m sure he ' ll be fine .
' I don ' t feel as though the game is for me . '
That ' s what I was thinking as well .
[ ] ( / <unk> ) I don ' t know .
3 6 0 , 6 7 9
Thank you !
I don ' t think that ' s what he ' s talking about .
K
You ' re right , it ' s the peace music .
Good day .

我們可以手動(dòng)前后移動(dòng)贷掖,但這可能很乏味,所以我已經(jīng)做了一個(gè)快速配對腳本:

output_file_location = 'output_dev'
tst_file_location = 'tst2013.from'

if __name__ == '__main__':
    with open(output_file_location,"r") as f:
        content = f.read()
        to_data = content.split('\n')

    with open(tst_file_location,"r") as f:
        content = f.read()
        from_data = content.split('\n')

    for n, _ in enumerate(to_data[:-1]):
        print(30*'_')
        print('>',from_data[n])
        print()
        print('Reply:',to_data[n])

輸出應(yīng)該是:

> Aren ' t they streaming it for free online ... ?

Reply: Yes they are .

接下來渴语,你可能希望實(shí)際與你的機(jī)器人通信苹威,這是推理腳本的用途。

如果你運(yùn)行這個(gè)驾凶,你可以和你的機(jī)器人交互牙甫,提出問題。在寫這篇文章的時(shí)候调违,我們?nèi)匀辉谛薷脑u分結(jié)果和調(diào)整內(nèi)容窟哺。你可能對這里的結(jié)果感到滿意,或者你可能想用你自己的方法來選擇“正確”的答案技肩。舉個(gè)例子且轨,到目前為止,我訓(xùn)練過的聊天機(jī)器人有問題亩鬼,例如只是重復(fù)問題殖告,或者有時(shí)在回復(fù)完成之前沒有完成一個(gè)想法阿蝶。而且雳锋,如果機(jī)器人遇到不屬于他們詞匯表的詞語,則會(huì)產(chǎn)生 UNK 標(biāo)記羡洁,所以我們可能不想要這些標(biāo)記玷过。

如果你想從推理腳本獲得 10 個(gè)以上合理的輸出結(jié)果,你可以將beam_width和num_translations_per_input從 10 增加到 30筑煮,或者如果你喜歡辛蚊,可以增加更多。

如果你想在 Twitter 上實(shí)現(xiàn)類似于 Charles AI 的東西真仲,那么你可以稍微修改這個(gè)推理腳本袋马。例如,我打開這個(gè)腳本秸应,然后虑凛,在True循環(huán)內(nèi)碑宴,我檢查數(shù)據(jù)庫是否有任何新的社交媒體輸入。如果還沒有任何回應(yīng)桑谍,我使用該模型創(chuàng)建一個(gè)回應(yīng)并將其存儲(chǔ)到數(shù)據(jù)庫中延柠。然后使用 Twitter/Twitch/Reddit API,我實(shí)際上會(huì)產(chǎn)生一個(gè)回應(yīng)锣披。

你還需要“挑選”一個(gè)回應(yīng)贞间。你可以用機(jī)器人的第一個(gè)回應(yīng),但是由于光束 beam search雹仿,你可以看到不少的選擇增热,不妨使用它們!如果你運(yùn)行推理胧辽,你會(huì)看到有很多輸出:

https://pythonprogramming.net/static/images/machine-learning/chatbot-inference-output.png

每個(gè)聊天機(jī)器人可能會(huì)有所不同钓葫,但如前所述,我們在這里可能經(jīng)常會(huì)看到許多輸出問題票顾。例如础浮,<UNK>標(biāo)記看起來比較丑陋和不友好,也是我的機(jī)器人經(jīng)常喜歡重復(fù)問題或沒有完成的想法奠骄,因此我們可能會(huì)使用一個(gè)小型自然語言處理豆同,試圖挑最好的答案,我們 可以含鳞。 在寫這篇文章的時(shí)候影锈,我已經(jīng)寫了一個(gè)評分腳本,用來評價(jià) Daniel 所做的評分蝉绷,你可以在sentdex_lab目錄中找到它鸭廷。 基本上,如果你想使用它們熔吗,這里的所有文件都需要放在根目錄中辆床。 如果你這樣做,你可以按照你的喜好調(diào)整scoring.py桅狠。 然后讼载,你可以運(yùn)行modded-inference.py,并獲得單個(gè)最高分結(jié)果中跌,例如:

https://pythonprogramming.net/static/images/machine-learning/scored-chatbot-inference.png

好吧咨堤,現(xiàn)在已經(jīng)夠了。 這個(gè)時(shí)候漩符,你需要做很多調(diào)整一喘,然后和它玩玩。 我仍然在討論各種模型的大小嗜暴,希望通過更好的方法來表達(dá)數(shù)據(jù)凸克,從而使輸出的詞匯量可能更大铝侵。 我也有興趣選取一個(gè)通用的模型,傳入主要是諷刺的數(shù)據(jù),看看我是否可以使用遷移學(xué)習(xí),實(shí)現(xiàn)一個(gè)“有態(tài)度的查爾斯”類型的機(jī)器人员辩,但我們會(huì)看看。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末疟丙,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子鸟雏,更是在濱河造成了極大的恐慌享郊,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件孝鹊,死亡現(xiàn)場離奇詭異炊琉,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)又活,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進(jìn)店門苔咪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人柳骄,你說我怎么就攤上這事团赏。” “怎么了耐薯?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵舔清,是天一觀的道長。 經(jīng)常有香客問我曲初,道長体谒,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任臼婆,我火速辦了婚禮抒痒,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘目锭。我一直安慰自己评汰,他們只是感情好纷捞,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布痢虹。 她就那樣靜靜地躺著,像睡著了一般主儡。 火紅的嫁衣襯著肌膚如雪奖唯。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天糜值,我揣著相機(jī)與錄音丰捷,去河邊找鬼坯墨。 笑死,一個(gè)胖子當(dāng)著我的面吹牛病往,可吹牛的內(nèi)容都是我干的捣染。 我是一名探鬼主播,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼停巷,長吁一口氣:“原來是場噩夢啊……” “哼耍攘!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起畔勤,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤蕾各,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后庆揪,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體式曲,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年缸榛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了吝羞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,622評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡内颗,死狀恐怖脆贵,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情起暮,我是刑警寧澤卖氨,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站负懦,受9級特大地震影響筒捺,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜纸厉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一系吭、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧颗品,春花似錦肯尺、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至锄蹂,卻和暖如春氓仲,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工敬扛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留晰洒,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓啥箭,卻偏偏與公主長得像谍珊,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子急侥,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評論 2 348

推薦閱讀更多精彩內(nèi)容