<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                # PythonProgramming.net TensorFlow 聊天機器人 > 原文:[Creating a Chatbot with Deep Learning, Python, and TensorFlow](https://pythonprogramming.net/chatbot-deep-learning-python-tensorflow/) > 譯者:[飛龍](https://github.com/) > 協議:[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/) ## 一、使用深度學習創建聊天機器人 你好,歡迎閱讀 Python 聊天機器人系列教程。 在本系列中,我們將介紹如何使用 Python 和 TensorFlow 創建一個能用的聊天機器人。 以下是一些 chatbot 的實例: > 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 我的目標是創建一個聊天機器人,可以實時與 Twitch Stream 上的人交談,而不是聽起來像個白癡。為了創建一個聊天機器人,或者真的做任何機器學習任務,當然,你的第一個任務就是獲取訓練數據,之后你需要構建并準備,將其格式化為“輸入”和“輸出”形式,機器學習算法可以消化它。可以說,這就是做任何機器學習時的實際工作。建立模型和訓練/測試步驟簡單的部分! 為了獲得聊天訓練數據,你可以查看相當多的資源。例如,[康奈爾電影對話語料庫](https://www.cs.cornell.edu/~cristian/Cornell_Movie-Dialogs_Corpus.html)似乎是最受歡迎的語料之一。還有很多其他來源,但我想要的東西更加......原始。有些沒有美化的東西,有一些帶有為其準備的特征。自然,這把我帶到了 Reddit。起初,我認為我會使用 Python Reddit API 包裝器,但 Reddit 對抓取的限制并不是最友好的。為了收集大量的數據,你必須打破一些規則。相反,我發現了一個 [17 億個 Reddit 評論的數據轉儲](https://www.reddit.com/r/datasets/comments/3bxlg7/i_have_every_publicly_available_reddit_comment/?st=j9udbxta&sh=69e4fee7)。那么,應該使用它! Reddit 的結構是樹形的,不像論壇,一切都是線性的。父評論是線性的,但父評論的回復是個分支。以防有些人不熟悉: ``` -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 ``` 我們需要用于深度學習的結構是輸入輸出。 所以我們實際上通過評論和回復偶對的方式,試圖獲得更多的東西。 在上面的例子中,我們可以使用以下作為評論回復偶對: ``` -Top level reply 1 and --Reply to top level reply 1 --Reply to top level reply 1 and ---Reply to reply... ``` 所以,我們需要做的是獲取這個 Reddit 轉儲,并產生這些偶對。 接下來我們需要考慮的是,每個評論應該只有 1 個回復。 盡管許多單獨的評論可能會有很多回復,但我們應該只用一個。 我們可以只用第一個,或者我們可以用最頂上那個。 稍后再說。 我們的第一個任務是獲取數據。 如果你有存儲限制,你可以查看一個月的 Reddit 評論,這是 2015 年 1 月。否則,你可以獲取整個轉儲: ``` 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 ``` 我只下載過兩次這個種子,但根據種子和對等的不同,下載速度可能會有很大差異。 最后,你還可以通過 [Google BigQuery](https://www.reddit.com/r/bigquery/comments/3cej2b/17_billion_reddit_comments_loaded_on_bigquery/?st=j9xmvats&sh=5843d18e) 查看所有 Reddit 評論。 BigQuery 表似乎隨著時間的推移而更新,而 torrent 不是,所以這也是一個不錯的選擇。 我個人將會使用 torrent,因為它是完全免費的,所以,如果你想完全遵循它,就需要這樣做,但如果你愿意的話,可以隨意改變主意,使用 Google BigQuery 的東西! 由于數據下載可能需要相當長的時間,我會在這里中斷。 一旦你下載了數據,繼續下一個教程。 你可以僅僅下載`2015-01`文件來跟隨整個系列教程,你不需要整個 17 億個評論轉儲。 一個月的就足夠了。 ## 二、聊天數據結構 歡迎閱讀 Python 和 TensorFlow 聊天機器人系列教程的第二部分。現在,我假設你已經下載了數據,或者你只是在這里觀看。對于大多數機器學習,你需要獲取數據,并且某些時候需要輸入和輸出。對于神經網絡,這表示實際神經網絡的輸入層和輸出層。對于聊天機器人來說,這意味著我們需要將東西拆成評論和回復。評論是輸入,回復是所需的輸出。現在使用 Reddit,并不是所有的評論都有回復,然后很多評論會有很多回復!我們需要挑一個。 我們需要考慮的另一件事是,當我們遍歷這個文件時,我們可能會發現一個回復,但隨后我們可能會找到更好的回復。我們可以使用一種方法是看看得票最高的。我們可能也只想要得票最高的回應。我們可以考慮在這里很多事情,按照你的希望隨意調整! 首先,我們的數據格式,如果我們走了 torrent 路線: ```json {"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} ``` 每一行就像上面那樣。我們并不需要這些數據的全部,但是我們肯定需要`body`,`comment_id`和`parent_id`。如果你下載完整的 torrent 文件,或者正在使用 BigQuery 數據庫,那么可以使用樣例數據,所以我也將使用`score`。我們可以為分數設定限制。我們也可以處理特定的`subreddit`,來創建一個說話風格像特定 subreddit 的 AI。現在,我會處理所有 subreddit。 現在,即使一個月的評論也可能超過 32GB,我也無法將其納入 RAM,我們需要通過數據進行緩沖。我的想法是繼續并緩沖評論文件,然后將我們感興趣的數據存儲到 SQLite 數據庫中。這里的想法是我們可以將評論數據插入到這個數據庫中。所有評論將按時間順序排列,所有評論最初都是“父節點”,自己并沒有父節點。隨著時間的推移,會有回復,然后我們可以存儲這個“回復”,它將在數據庫中有父節點,我們也可以按照 ID 拉取,然后我們可以檢索一些行,其中我們擁有父評論和回復。 然后,隨著時間的推移,我們可能會發現父評論的回復,這些回復的投票數高于目前在那里的回復。發生這種情況時,我們可以使用新信息更新該行,以便我們可以最終得到通常投票數較高的回復。 無論如何,有很多方法可以實現,讓我們開始吧!首先,讓我們進行一些導入: ```py import sqlite3 import json from datetime import datetime ``` 我們將為我們的數據庫使用`sqlite3`,`json`用于從`datadump`加載行,然后`datetime`實際只是為了記錄。 這不完全必要。 所以 torrent 轉儲帶有一大堆目錄,其中包含實際的`json`數據轉儲,按年和月(YYYY-MM)命名。 他們壓縮為`.bz2`。 確保你提取你打算使用的那些。 我們不打算編寫代碼來做,所以請確保你完成了! 下面,我們以一些變量開始: ```py timeframe = '2015-05' sql_transaction = [] connection = sqlite3.connect('{}.db'.format(timeframe)) c = connection.cursor() ``` `timeframe`值將成為我們將要使用的數據的年份和月份。 你也可以把它列在這里,然后如果你喜歡,可以遍歷它們。 現在,我將只用 2015 年 5 月的文件。 接下來,我們有`sql_transaction`。 所以在 SQL 中的“提交”是更昂貴的操作。 如果你知道你將要插入數百萬行,你也應該知道你*真的*不應該一一提交。 相反,你只需在單個事務中構建語句,然后執行全部操作,然后提交。 接下來,我們要創建我們的表。 使用 SQLite,如果數據庫尚不存在,連接時會創建數據庫。 ```py 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)") ``` 在這里,我們正在準備存儲`parent_id`,`comment_id`,父評論,回復(評論),subreddit,時間,然后最后是評論的評分(得票)。 接下來,我們可以開始我們的主代碼塊: ```py if __name__ == '__main__': create_table() ``` 目前為止的完整代碼: ```py 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() ``` 一旦我們建立完成,我們就可以開始遍歷我們的數據文件并存儲這些信息。 我們將在下一個教程中開始這樣做! ## 三、緩沖數據 你好,歡迎閱讀 Python TensorFlow 聊天機器人系列教程的第 3 部分。 在上一篇教程中,我們討論了數據的結構并創建了一個數據庫來存放我們的數據。 現在我們準備好開始處理數據了! 目前為止的代碼: ```py 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() ``` 現在,讓我們開始緩沖數據。 我們還將啟動一些跟蹤時間進度的計數器: ```py 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`會不時輸出,讓我們知道我們在迭代的文件中走了多遠,然后`paired_rows`會告訴我們有多少行數據是成對的(意味著我們有成對的評論和回復,這是訓練數據)。 請注意,當然,你的數據文件的實際路徑將與我的路徑不同。 接下來,由于文件太大,我們無法在內存中處理,所以我們將使用`buffering`參數,所以我們可以輕松地以小塊讀取文件,這很好,因為我們需要關心的所有東西是一次一行。 現在,我們需要讀取`json`格式這一行: ```py 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`函數調用,讓我們創建: ```py def format_data(data): data = data.replace('\n',' newlinechar ').replace('\r',' newlinechar ').replace('"',"'") return data ``` 我們將引入這個來規范平凡并將換行符轉換為一個單詞。 我們可以使用`json.loads()`將數據讀取到 python 對象中,這只需要`json`對象格式的字符串。 如前所述,所有評論最初都沒有父級,也就是因為它是頂級評論(父級是 reddit 帖子本身),或者是因為父級不在我們的文檔中。 然而,在我們瀏覽文檔時,我們會發現那些評論,父級確實在我們數據庫中。 發生這種情況時,我們希望將此評論添加到現有的父級。 一旦我們瀏覽了一個文件或者一個文件列表,我們就會輸出數據庫并作為訓練數據,訓練我們的模型,最后有一個我們可以聊天的朋友! 所以,在我們把數據輸入到數據庫之前,我們應該看看能否先找到父級! ```py parent_data = find_parent(parent_id) ``` 現在我們需要尋找`find_parent`函數: ```py 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 ``` 有可能存在實現他的更有效的方法,但是這樣管用。 所以,如果我們的數據庫中存在`comment_id`匹配另一個評論的`parent_id`,那么我們應該將這個新評論與我們已經有的父評論匹配。 在下一個教程中,我們將開始構建確定是否插入數據所需的邏輯以及方式。 ## 四、插入邏輯 歡迎閱讀 Python TensorFlow 聊天機器人系列教程的第 4 部分。 目前為止,我們已經獲得了我們的數據,并開始遍歷。 現在我們準備開始構建用于輸入數據的實際邏輯。 首先,我想對*全部*評論加以限制,不管是否有其他評論,那就是我們只想處理毫無意義的評論。 基于這個原因,我想說我們只想考慮兩票或以上的評論。 目前為止的代碼: ```py 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) ``` 現在讓我們要求票數是兩個或更多,然后讓我們看看是否已經有了父級的回復,以及票數是多少: ```py 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) ``` 現在我們需要創建`find_existing_score`函數: ```py 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 ``` 如果有現有評論,并且我們的分數高于現有評論的分數,我們想替換它: ```py if score >= 2: existing_comment_score = find_existing_score(parent_id) if existing_comment_score: if score > existing_comment_score: ``` 接下來,很多評論都被刪除,但也有一些評論非常長,或者很短。 我們希望確保評論的長度適合于訓練,并且評論未被刪除: ```py 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 ``` 好了,到了這里,我們已經準備好開始插入數據了,這就是我們將在下一個教程中做的事情。 ## 五、構建數據庫 歡迎閱讀 Python TensorFlow 聊天機器人系列教程的第 5 部分。 在本教程之前,我們一直在處理我們的數據,準備插入數據的邏輯,現在我們已經準備好開始插入了。 目前為止的代碼: ```py 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) ``` 現在,如果有現有的評論分數,這意味著已經存在一個評論,所以這需要更新語句。 如果你還不知道 SQL,那么你可能需要閱讀 SQLite 教程。 所以我們的邏輯最初是: ```py 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) ``` 現在,我們需要構建`sql_insert_replace_comment`函數: ```py 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)) ``` 這涵蓋了評論已經與父級配對的情況,但我們還需要處理沒有父級的評論(但可能是另一個評論的父級!),以及確實有父級,并且它們的父級沒有回復的評論。 我們可以進一步構建插入塊: ```py 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) ``` 現在我們需要構建`sql_insert_has_parent`和`sql_insert_no_parent`函數: ```py 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 萬行數據輸出一些信息: ```py if row_counter % 100000 == 0: print('Total Rows Read: {}, Paired Rows: {}, Time: {}'.format(row_counter, paired_rows, str(datetime.now()))) ``` 最后,我們現在需要的代碼的最后一部分是,我們需要構建`transaction_bldr`函數。 這個函數用來構建插入語句,并以分組的形式提交它們,而不是一個接一個地提交。 這樣做會快得多: ```py 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 = [] ``` 是的,我用了個全局變量。 目前為止的代碼: ```py 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()))) ``` 現在你可以開始運行它了。隨著時間的輸出應該是: ``` 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 ``` 遍歷所有的數據將取決于起始文件的大小。 隨著數據量增大插入會減慢。 為了處理 2015 年 5 月的整個文件,可能需要 5-10 個小時。 一旦你遍歷了你想要的文件,我們已經準備好,將訓練數據轉換為我們的模型,這就是我們將在下一個教程中做的事情。 如果你正在訓練更大的數據集,你可能會發現我們需要處理的數據有很大的膨脹。 這是因為只有大約 10% 的配對評論,所以我們的數據庫中很大一部分并沒有被實際使用。 我使用下面的附加代碼: ```py 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() ``` 它在另一個計數器之下。這需要新的`cleanup`變量,它規定了“清理”之前的多少航。這將消除我們的數據庫膨脹,并使插入速度保持相當高。每個“清理”似乎移除 2K 對,幾乎無論你放在哪里。如果每 100K 行一次,那么每 100K 行去掉 2K 對。我選擇 100 萬。另一個選項是每 100 萬行清理一次,但不清理最后一百萬行,而是清理最后 110 萬行到第 100 萬行,因為看起來這些 2K 對在最后的 100K 中。即使這樣做,你仍然會失去一些偶對。我覺得每 100 萬行中,100K 對中的 2K 對并不重要。我還添加了一個`start_row`變量,所以我可以在嘗試提高速度的同時,啟動和停止數據庫插入。 `c.execute("VACUUM")`是一個 SQL 命令,用于將數據庫的大小縮小到應該的值。實際上這可能不是必需的,你可能只想在最后完成此操作。我沒有測試這個操作需要多長時間。我是這樣做的,所以我可以在刪除后立即看到數據庫的大小。 完整代碼是: ```py 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() ``` ## 六、訓練數據集 歡迎閱讀 Python TensorFlow 聊天機器人系列教程的第 6 部分。 在這一部分,我們將著手創建我們的訓練數據。 在本系列中,我正在考慮使用兩種不同的整體模型和工作流程:我所知的一個方法(在開始時展示并在 Twitch 流上實時運行),另一個可能會更好,但我仍在探索它。 無論哪種方式,我們的訓練數據設置都比較相似。 我們需要創建文件,基本上是“父級”和“回復”文本,每一行都是一個樣本。 因此,父級文件中的第15行是父評論,然后在回復文件中的第 15 行是父文件中第 15 行的回復。 要創建這些文件,我們只需要從數據庫中獲取偶對,然后將它們附加到相應的訓練文件中。 讓我們以這個開始: ```py import sqlite3 import pandas as pd timeframes = ['2015-05'] for timeframe in timeframes: ``` 對于這里的運行,我只在單個月上運行,只創建了一個數據庫,但是你可能想創建一個數據庫,里面的表是月份和年份,或者你可以創建一堆 sqlite 數據庫 ,表類似于我們這些,然后遍歷它們來創建你的文件。 無論如何,我只有一個,所以我會把`timeframes `作為一個單一的項目列表。 讓我們繼續構建這個循環: ```py 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 ``` 第一行只是建立連接,然后我們定義游標,然后是`limit`。 限制是我們要從數據庫中一次抽取的塊的大小。 同樣,我們正在處理的數據比我們擁有的RAM大得多。 我們現在要將限制設為 5000,所以我們可以有一些測試數據。 我們可以稍后產生。 我們將使用`last_unix`來幫助我們從數據庫中提取數據,`cur_length`會告訴我們什么時候我們完成了,`counter`會允許我們顯示一些調試信息,而`test_done`用于我們完成構建測試數據的時候。 ```py 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`與我們的限制相同,我們就仍然有更多的工作要做。 然后,我們將從數據庫中提取數據并將其轉換為數據幀。 目前,我們對數據幀沒有做太多的工作,但是之后我們可以用它對我們想要考慮的數據類型設置更多限制。 我們存儲了`last_unix`,所以我們知道之后提取什么時候的。 我們也注意到回報的長度。 現在,建立我們的訓練/測試文件。 我們將從測試開始: ```py 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 ``` 現在,如果你希望,你也可以在這個時候提高限制。 在`test_done = True`之后,你也可以重新將`limit`定義為 100K 之類的東西。 現在,我們來為訓練編寫代碼: ```py 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') ``` 我們可以通過把它做成一個函數,來使這個代碼更簡單更好,所以我們不會復制和粘貼基本相同的代碼。 但是...相反...讓我們繼續: ```py counter += 1 if counter % 20 == 0: print(counter*limit,'rows completed so far') ``` 這里,我們每 20 步就會看到輸出,所以如果我們將限制保持為 5,000,每 100K 步也是。 目前的完整代碼: ```py 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') ``` 好的,運行它,當你準備好數據的時候,我就會看到。 ## 七、訓練模型 歡迎閱讀 Python TensorFlow 聊天機器人系列教程的第 7 部分。 在這里,我們將討論我們的模型。 你可以提出和使用無數的模型,或在網上找到并適配你的需求。 我的主要興趣是 Seq2Seq 模型,因為 Seq2Seq 可以用于聊天機器人,當然也可以用于其他東西。 基本上,生活中的所有東西都可以簡化為序列到序列的映射,所以我們可以訓練相當多的東西。 但是對于現在:我想要一個聊天機器人。 當我開始尋找聊天機器人的時候,我偶然發現了原來的 TensorFlow seq2seq 翻譯教程,它把專注于英語到法語的翻譯上,并做了能用的工作。不幸的是,由于 seq2seq 的一些變化,現在這個模型已經被棄用了。有一個傳統的 seq2seq,你可以在最新的 TensorFlow 中使用,但我從來沒有讓它有效。相反,如果你想使用這個模型,你可能需要降級 TF(`pip install tensorflow-gpu==1.0.0`)。或者,你可以使用 TensorFlow 中最新,最好的 seq2seq 查看最新的神經機器翻譯(NMT)模型。最新的 NMT 教程和來自 TensorFlow 的代碼可以在這里找到:[神經機器翻譯(seq2seq)教程](https://github.com/tensorflow/nmt)。 我們打算使用一個項目,我一直與我的朋友丹尼爾合作來從事它。 該項目的位置是:[NMT 機器人](https://github.com/daniel-kukiela/nmt-chatbot),它是構建在 [TensorFlow 的 NMT 代碼](https://github.com/tensorflow/nmt)之上的一組工具。 該項目可能會發生變化,因此你應該檢查 README,在撰寫本文時,該文件寫了: ``` $ 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 ``` 所以讓我們用它!我們將首先設置它,讓它運行,然后我將解釋你應該理解的主要概念。 如果你需要更多的處理能力,用這個 10 美元的折扣來查看 Paperspace,這會給你足夠的時間來獲得一些像樣的東西。我一直在使用它們,并且非常喜歡我能夠快速啟動“ML-in-a-Box”選項并立即訓練模型。 確保遞歸下載軟件包,或者手動獲取 nmt 軟件包,或者從我們的倉庫派生,或者從官方的 TensorFlow 源文件派生。我們的派生只是版本檢查的一次更改,至少在那個時候,它需要非常特殊的 1.4.0 版本,而這實際上并不是必需的。這可能會在你那個時候被修復,但是我們也可能會對 NMT 核心代碼做進一步的修改。 一旦下載完成,編輯`setup/settings.py`。如果你真的不知道自己在做什么,那沒關系,你不需要修改任何東西。預設設置將需要約 4GB 的 VRAM,但至少仍然應該產生不錯的模型。 Charles v2 用以下設置訓練,`'vocab_size': 100000`,(在腳本的前面設置): ```py 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 } ``` 我手動降低了學習率,因為 Adam 真的不需要逐漸衰減(亞當的`ada`代表自適應,`m`是時刻,所以`adam`就是自適應時刻)。 我以 0.001 開始,然后減半到 0.0005,然后 0.00025,然后 0.0001。 根據你擁有的數據量,你不希望在每個設定的步驟上衰減。 當使用 Adam 時,我會建議每 1-2 個迭代衰減一次。 默認的批量大小是 128,因此如果你想要將其設置為自動衰減,則可以計算出你的迭代的迭代步數。 如果你使用 SGD 優化器,那么注釋掉衰減因子沒有問題,并且你可能希望學習率從 1 開始。 一旦你完成了所有的設置,在主目錄(`utils`,`tests`和`setup`目錄)中,把你的`train.to`和`train.from`以及匹配的`tst2012`和`tst2013`文件放到`new_data`目錄中。 現在`cd setup `來運行`prepare_data.py`文件: ```py $ python3 prepare_data.py ``` 最后`cd ../`,之后: ```py $ python3 train.py ``` 在下一個教程中,我們將更深入地討論模型的工作原理,參數以及訓練涉及的指標。 ## 八、探索我們的 NMT 模型的概念和參數 歡迎閱讀 Python TensorFlow 聊天機器人系列教程的第 8 部分。在這里,我們將討論我們的模型。 對你來說,最主要的區別就是分桶(bucketing),填充(padding) 和更多的注意機制。在我們開始之前,先簡單地談談這些事情。首先,如果你熟悉神經網絡,請考慮 seq2seq 之類的任務,其中序列長度不完全相同。我們可以在聊天機器人范圍內考慮這一點,但也可以考慮其他領域。在聊天機器人的情況下,一個單詞的語句可以產生 20 個單詞的回復,而長的語句可以返回單個單詞的回復,并且每個輸入在字符,單詞等方面不同于輸出。單詞本身將被分配任意或有意義的 ID(通過單詞向量),但是我們如何處理可變長度?一個答案就是使所有的單詞串都是 50 個單詞(例如)。然后,當語句長度為 35 個單詞時,我們可以填充另外 15 個單詞。超過 50 個單詞的任何數據,我們可以不用于訓練或截斷。 不幸的是,這可能會讓訓練變得困難,特別是對于可能最為常見的較短回復,并且大多數單詞/標記只是填充。原始的 seq2seq(英語到法語)的例子使用分桶來解決這個問題,并用 4 個桶訓練。 5-10,10-15,20-25 和 40-50,我們最終將訓練數據放入適合輸入和輸出的最小桶中,但這不是很理想。 然后,我們有了 NMT 代碼,處理可變輸入,沒有分桶或填充!接下來,這段代碼還包含對注意機制的支持,這是一種向循環神經網絡添加長期記憶的嘗試。最后,我們還將使用雙向遞歸神經網絡(BRNN)。我們來談談這些事情。 一般來說,一個 LSTM 可以很好地記住,長度達到 10-2 0的標記的正確序列。然而,在此之后,性能下降,網絡忘記了最初的標記,為新的標記騰出空間。在我們的例子中,標記是詞語,所以基本的 LSTM 應該能夠學習 10-20 個單詞長度的句子,但是,當我們比這更長的時候,輸出可能不會那么好。注意機制就引入了,提供了更長的“注意力跨度”,這有助于網絡達到更多單詞,像 30,40 甚至 80 個,例如。想象一下,如果只能用 3-10 個字來處理和回應其他人的話,對于你來說有多困難,在這 10 個字的標記中,你會變得很草率,像它一樣。在前面的句子中,你只需要想象一下,如果你...在你需要以至少 10 個單詞開始建立你的回答之前,對你來說有多難。滑動一下,你會得到:如果你只能這樣做,那么這將是很難的,而這又不是真正有意義的,并且會很難做出很好的回應。即使你確實知道你需要想象一些事情,想象什么?你必須等待,看看未來的元素,知道你打算想象什么...但是,當我們獲得了這些未來的元素,哦,親愛的,我們早已錯過了我們打算想象它的部分。這是雙向遞歸神經網絡(BRNN)引入的地方。 在許多 seq2seq 的任務中,比如語言翻譯,我們可以通過就地轉換單詞,學習簡單的語法規律,因為許多語言在語法上是相似的。 隨著自然語言和交際的發展,以及英語到日語等一些翻譯形式的出現,在語境,流動等方面也越來越重要。 還有更多的事情要做。 雙向遞歸神經網絡(BRNN)假定現在,過去和未來的數據在輸入序列中都是重要的。 雙向遞歸神經網絡(BRNN)的“雙向”部分具有很好的描述性。 輸入序列是雙向的。 一個向前,另一個向后。 為了說明這一點: ![](https://pythonprogramming.net/static/images/machine-learning/bidirectional-recurrent-neural-network.png) 在簡單的RNN上,你有輸入層,你的輸出層,然后我們只有一個隱藏層。然后,你從輸入層連接到隱藏層,隱藏層中的每個節點也向下傳遞到下一個隱藏層節點,這就是我們如何得到我們的“時間”,以及來自循環神經網絡的非靜態特性,因為之前的輸入允許在隱藏層上向下和向下傳播。相反在 BRNN 上,你的隱藏層由相反方向的節點組成,所以你有輸入和輸出層,然后你會有你的隱藏層。然而,與基本的 RNN 不同,隱藏層向上和向下傳遞數據(或者向前和向后傳遞,取決于誰在繪制圖片),這使得網絡能夠基于歷史上發生的事情,以及我們傳給序列的未來發生的事情,理解發生了什么。 下一個加入我們的網絡是一個注意機制,因為盡管數據向前和向后傳遞,但是我們的網絡不能一次記住更長的序列(每次最多 3-10 個標記)。如果你正在給我們所用的單詞加上標記,那么這意味著每次最多只有 3 到 10 個單詞,但是對于字符級別的模型來說,這個問題甚至更加棘手,你最多可以記住 3-10 個字符。但是,如果你做一個字符模型,你的詞匯數可能低得多。 有了注意機制,我們可以處理序列中的 30, 40, 80+個標記。下面是一個描述 BLEU 的圖片,其中包含或不包含注意機制: ![](https://pythonprogramming.net/static/images/machine-learning/attention-mechanism.png) BLEU代表“雙語評估替代”,它可能是我們確定翻譯算法總體有效性的最佳方式。然而,重要的是,BLEU 將與我們正在翻譯的序列有關。例如,我們的英語到法語的 BLEU 成績遠遠,很可能高于英語到日語,甚至德語,或者單詞,思想或短語沒有任何直接翻譯的語言。在我們的例子中,我們正在將序列翻譯成序列,兩個都是英文序列,所以我們應該看到一個非常高的 BLEU?可能不是。有了語言翻譯,對于一個輸入,經常存在“確切”或至少一些“完美”的匹配(同樣,有些東西不能完美翻譯,但這不會是多數)。有了對話數據,對于某些陳述真的有一個“確切”的答案嗎?絕對不是。我們應該期待看到,BLEU 隨著時間的推移緩慢上升,但不期望看到 BLEU 得分與語言翻譯任務類似的。 注意機制不僅幫助我們處理更長的序列,而且還改善了短的。注意機制也允許學習比我們需要的聊天機器的更復雜。他們的主要驅動力似乎不僅是語言,在英語和法語之間進行翻譯相對比較容易,但像日語這樣的語言結構需要更多的注意。你可能真的需要看看 100 個單詞的日語句子的結尾,來辨別第一個英文單詞應該是什么,反之亦然。通過我們的聊天機器人,我們面臨類似的困擾。我們沒有將詞翻譯為詞,將名詞短語翻譯為名詞短語。相反,輸入序列的結束可以并且通常完全確定輸出序列應該是什么。我稍后可能會更深入地關注注意機制,但現在,這對于大體思路已經足夠了。 除了 BLEU,你也要看看 Perplexity,通常是縮寫為“PPL”。Perplexity 是另一個有用的方法,衡量模型的有效性。與 BLEU 不同的是,它越低越好,因為它是模型預測樣本輸出效果的概率分布。同樣,對于語言翻譯。 有了 BLEU 和 PPL,有了翻譯,只要 BLEU 上升,PPL 下降,你通常可以訓練一個模型。然而,如果一個聊天機器人從來沒有或者從來不應該是一個“正確”的答案,那么只要 BLEU 和 PPL 上升,我就會警告不要繼續訓練,因為這樣可能會產生更多的機器人似的反應,而不是高度多樣的。我們還有其他方法可以解決這個問題,以后我們可以解決。 我希望這不是你第一個機器學習教程,但是,如果是這樣,你也應該知道什么是損失。基本上損失是一個度量,衡量你的神經網絡輸出層與樣本數據的“接近”程度。損失越低越好。 我想提到的最后一個概念是 Beam Search。使用這個,我們可以從我們的模型中查看一系列頂級翻譯,而不僅僅是最頂端的一個而不考慮其他的。這樣做會導致翻譯時間更長,但在我看來,翻譯模型必須這樣,因為我們會發現,我們的模型仍然很有可能產生我們不想要的輸出,但是對訓練這些輸出可能會導致其他地方的過擬合。允許多種翻譯將有助于訓練和生產。 好的,在下一個教程中,我們將討論如何開始與聊天機器人進行交互。 ## 九、與聊天機器人交互 歡迎閱讀 Python Tensorflow 和深度學習聊天機器人系列教程的第 9 部分。 在本教程中,我們將討論如何與我們的模型進行交互,甚至可能將其推入生產環境。 在訓練你的模型時,默認情況下每 1,000 步將保存一個檢查點文件。 如果你需要或想要停止你的訓練,你可以安全地這樣做,并選擇最近的檢查點的備份。 每個檢查點的保存數據包含各種日志記錄參數,還包括模型的完整權重/偏差等。 這意味著你可以選取這些檢查點/模型文件,并使用它們繼續訓練或在生產中使用它們。 檢查點默認保存在模型目錄中。 你應該看到名為`translate.ckpt-XXXXX`的文件,其中`X`對應于步驟序號。 你應該有`.data`,`.index`和一個`.meta`文件,以及檢查點文件。 如果你打開檢查點文件,你會看到它看起來像: ``` 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" ``` 這僅僅讓你的模型知道使用哪些文件。 如果你想使用一個特定的,較老的模型,你可以編輯它。 因此,為了加載模型,我們需要 4 個文件。 假設我們的步驟是 22.5 萬。 這意味著我們需要以下內容來運行我們的模型,或者加載它來繼續訓練: ``` checkpoint translate.ckpt-225000.meta translate.ckpt-225000.index translate.ckpt-225000.data-00000-of-00001 ``` 因此,如果你轉移到云中的某臺計算機上,無論是用于訓練還是生產,這些都是你需要的文件。 除了每隔 1000 步保存檢查點外,我們還會做一些更多的示例(來自我們的`tst.to`和`tst.from`文件)。 這些數據每千步輸出一次,并進入模型目錄以及`output_dev`和`output_test`。 你可以使用這些文件查看每隔 1000 個步驟在控制臺中完成的單個示例。 這些輸出文件純粹是測試文件的,頂級輸出語句的結果響應。 既然你可以在你的測試文件中添加你想要的任何示例,那么這是你可以與聊天機器人進行交互的第一種方式,或者至少可以看到交互。 我寫了一個簡單的配對腳本,來輸出測試文件和輸出文件的評論響應偶對。 例如,假設你已經有了你的`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 . ``` 我們可以手動前后移動,但這可能很乏味,所以我已經做了一個快速配對腳本: ```py 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]) ``` 輸出應該是: ``` > Aren ' t they streaming it for free online ... ? Reply: Yes they are . ``` 接下來,你可能希望實際與你的機器人通信,這是推理腳本的用途。 如果你運行這個,你可以和你的機器人交互,提出問題。在寫這篇文章的時候,我們仍然在修改評分結果和調整內容。你可能對這里的結果感到滿意,或者你可能想用你自己的方法來選擇“正確”的答案。舉個例子,到目前為止,我訓練過的聊天機器人有問題,例如只是重復問題,或者有時在回復完成之前沒有完成一個想法。而且,如果機器人遇到不屬于他們詞匯表的詞語,則會產生 UNK 標記,所以我們可能不想要這些標記。 如果你想從推理腳本獲得 10 個以上合理的輸出結果,你可以將`beam_widt`h和`num_translations_per_input`從 10 增加到 30,或者如果你喜歡,可以增加更多。 如果你想在 Twitter 上實現類似于 Charles AI 的東西,那么你可以稍微修改這個推理腳本。例如,我打開這個腳本,然后,在`True`循環內,我檢查數據庫是否有任何新的社交媒體輸入。如果還沒有任何回應,我使用該模型創建一個回應并將其存儲到數據庫中。然后使用 Twitter/Twitch/Reddit API,我實際上會產生一個回應。 你還需要“挑選”一個回應。你可以用機器人的第一個回應,但是由于光束 beam search,你可以看到不少的選擇,不妨使用它們!如果你運行推理,你會看到有很多輸出: ![](https://pythonprogramming.net/static/images/machine-learning/chatbot-inference-output.png) 每個聊天機器人可能會有所不同,但如前所述,我們在這里可能經常會看到許多輸出問題。例如,`<UNK>`標記看起來比較丑陋和不友好,也是我的機器人經常喜歡重復問題或沒有完成的想法,因此我們可能會使用一個小型自然語言處理,試圖挑最好的答案,我們 可以。 在寫這篇文章的時候,我已經寫了一個評分腳本,用來評價 Daniel 所做的評分,你可以在`sentdex_lab`目錄中找到它。 基本上,如果你想使用它們,這里的所有文件都需要放在根目錄中。 如果你這樣做,你可以按照你的喜好調整`scoring.py`。 然后,你可以運行`modded-inference.py`,并獲得單個最高分結果,例如: ![](https://pythonprogramming.net/static/images/machine-learning/scored-chatbot-inference.png) 好吧,現在已經夠了。 這個時候,你需要做很多調整,然后和它玩玩。 我仍然在討論各種模型的大小,希望通過更好的方法來表達數據,從而使輸出的詞匯量可能更大。 我也有興趣選取一個通用的模型,傳入主要是諷刺的數據,看看我是否可以使用遷移學習,實現一個“有態度的查爾斯”類型的機器人,但我們會看看。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看