這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
大家好,我是來自 PaddlePaddle 開源社區的李釗(@livc),目前是一名大三學生。我曾經在手機百度實習,參與推薦演算法和反作弊的研發工作,目前是 IDL 的一名實習生。很開心作為 Paddle Tutorials 系列的作者之一參加 GitChat 的分享。
在 Paddle 深度學習系列 Chat 的第一課中,官方開發組的張睿卿同學通過介紹一些深度學習的應用情境,帶領大家瞭解深度學習的基本原理和工作方式,我們先來簡單回顧下。
“人工智慧”並不是一個很新的概念,它其實已經有 60 歲了,它的發展經曆了三起三落,像極了數學史上的“三次危機”。作為燃料的大資料和硬體(GPU)騰興帶來的並行運算,促成了深度學習在 2012 年左右的大爆發。深度學習有很多有趣的應用,比如,搭載 GoPro 的小車的“自動駕駛”可以視為一個迴歸問題,普通的照片可以模仿出著名藝術家畫作的風格,在機器翻譯、序列產生等領域也有所突破。此外,深度學習並不“完美”,還有很多理論基礎問題等待我們去解決,比如說存在可解釋性的局限:很多東西不能稱為“方法”,只能稱為“竅門”(trick),南大周志華教授將其比作“老中醫看病”。
從去年年底開始,Paddle 社區將理論與實踐結合,開始撰寫一份深度學習教程,其中包括:新手入門、識別數字、映像分類、詞向量、情感分析、文本序列標註、機器翻譯、個人化推薦。這份教程的每一章都對應一個真實問題,從背景介紹到代碼實踐,帶領大家完整地解決問題。
本次 Chat 的主題是個人化推薦。在系列教程個人化推薦一文中,我們介紹了推薦系統的背景和經典模型,並以電影推薦為例,使用 MovieLens 資料集和 PaddlePaddle 訓練了一個神經網路模型。
什麼是推薦系統
隨著資訊技術和互連網的發展,人們逐漸從資訊匱乏的時代走入了資訊過載(information overload)的時代 。在這個時代,無論是資訊消費者還是資訊生產者都遇到了很大的挑戰:作為資訊消費者,如何從大量資訊中找到自己感興趣的資訊是一件非常困難的事情;作為資訊生產者, 如何讓自己生產的資訊脫穎而出,受到廣大使用者的關注,也是一件非常困難的事情。推薦系統就是解決這一矛盾的重要工具。
—— 項亮 《推薦系統實踐》
喬布斯曾說,“消費者並不知道自己需要什麼,直到我們拿出自己的產品,他們就發現,這是我要的東西”。同樣,我們也可以說,資訊爆炸的時代,面對琳琅滿目的商品,使用者很可能不知道自己真正喜歡什麼,如果沒有推薦系統,使用者也許永遠不知道有更喜歡、更適合的商品沒有瀏覽到。
推薦系統和搜尋引擎是人們擷取資訊的兩種主要方法,與搜尋引擎相比,推薦系統並不需要使用者主動地尋找資訊或商品,也不需要使用者輸入難以用簡練文字描述的需求。但二者並不矛盾,在很多業務情境上推薦和搜尋是相互結合的,比如說,搜尋“周杰倫”時側欄會推薦《聽媽媽的話》。
從使用者的角度講,人們往往喜歡花 2 個小時看一部電影,卻不願意花 20 分鐘去挑選一部電影;從企業的角度看,Data Science Central 編輯總監 Bill Vorhies 曾撰文[1]表示,“據估計,對亞馬遜和 Netflix 這樣的主要電商平台來說,個人化推薦的使用者可能帶來多達 10% 到 25% 的增量收入”,這就是推薦系統的意義。
長尾效應
長尾(The Long Tail)最初由《連線》的總編輯克裡斯·安德森(Chris Anderson)於 2004 年提出,用來描述諸如亞馬遜和 Netflix 之類的網站的商業和經濟模式,指那些不受到重視的銷量小但種類多的產品或服務,由於總量巨大,累積起來的總收益超過主流產品的現象。在互連網領域,長尾效應尤為顯著[2]。
如所示,圖中橫軸表示資料類型,縱軸表示頻率,大部分資料的頻率都很低,但都是大於零的(圖中右側黃色部分),這就是長尾。比如,人們生活中常用的漢字其實並不多,但因頻率較高,所以這些為數不多的漢字佔據了左側綠色地區,而絕大部分的漢字罕有使用,它們就屬於長尾。
一個優秀的推薦系統不僅能推薦全域熱點,更應該能夠準確地理解“長尾”需求:通過挖掘某種使用者群體的小眾需求,將符合條件但並不熱門的商品或資訊推薦給使用者。由於並非每個人的偏好都與主流完全一致,長尾資料的成功挖掘將帶來遠遠高於平均的效益。
百度研究院的王益老師曾在《分布式機器學習系統》系列講座上分享過一個真實的 case:使用者搜尋“紅酒木瓜湯”,如果推薦系統能夠理解出“豐胸”、“美容”、“減肥”等方面的語義,那點擊(或購買)的幾率將遠遠高於平均,推薦系統的任務就是將長尾需求和使用者偏好挖掘出來並匹配。亞馬遜進階副總裁 Steve Kessel 曾說“如果我有 10 萬種書,哪怕一次僅賣掉一本,10 年後加起來它們的銷售就會超過最新出版的《哈利·傳輸速率》!”說的其實也是這個道理。
傳統的推薦方法
傳統的推薦方法可以分為協同過濾推薦、基於內容過濾推薦和組合推薦,其中協同過濾的應用最為廣泛,我們的教程中有更詳細的介紹。
協同過濾推薦和基於內容過濾推薦各有優缺點,所以在工業界中往往採用模型的組合方式,克服各自的缺點,達到更好的效果。在剛剛結束不久的 AAAI-17 大會上,1999 年的一篇論文因發現了將協同過濾與基於內容過濾結合起來的有效方式,被評為經典論文提名獎(Honorable Mention)。
深度學習具有優秀的提取特徵的能力,能夠學習多層次的抽象特徵表示,並對異質或跨域的內容資訊進行學習,因此近年來在推薦系統上的應用和探索也漸漸增多。
基於深度學習的推薦系統
這一部分,我們會介紹 Google 提出的 YouTube 深度神經網路推薦模型和寬度&深度學習模型,以及我們使用 PaddlePaddle 實現的融合推薦模型。
YouTube 的深度神經網路推薦系統
經常上 YouTube 看視頻的同學可能知道,它的首頁視頻幾乎全部是個人化的,足以見得推薦系統對這個世界上最大的視頻網站的重要性。
YouTube 的推薦演算法系統經曆過幾次改動,其團隊也發布了很多相關的論文。在 2016 年 9 月的 RecSys 會議(推薦系統領域頂級會議)上,Google 發布了 YouTube 的深度神經網路推薦模型[3]。
這個模型由兩個神經網路組成:候選產生網路和排序網路。這樣劃分是一個常見的做法:為了節省計算資源,首先從大規模樣本中召回候選集,降低資料規模,然後進行更精細的運算,得到 top k。
候選產生網路將推薦問題建模為一個類別數極大的多類分類問題(如所示),它首先將使用者的曆史資訊(如觀看曆史、搜尋曆史)和其他特徵拼接成向量,輸入給非線形多層感知器(MLP)。
在訓練階段,將 MLP 的結果輸入 Softmax 進行多分類,預測時計算使用者的綜合特徵(MLP的輸出)與所有視頻的相似性,取得分較高的 k 個視頻輸入給排序網路。
這裡 YouTube 團隊介紹了“視頻發布時間”(也可以稱作 Example Age,樣本年齡)這一特徵,因為經過觀察,使用者更喜歡新發布的視頻,哪怕有點和自己不相關,對於這樣一個視頻數目龐大的網站,新視頻的推薦也是極其重要的。由於機器學習系統都是使用曆史的行為資料來訓練,這樣就對過去存在一個隱式的偏差(bias),因此把 Example Age 特徵加入模型後,可以發現模型結果和經驗上的分布更相符。
候選產生網路結構[4]
排序網路模型結構與候選產生網路類似,它添加了許多用於描述使用者和視頻相關性的更精細的特徵,從而進行更細緻的打分,比如使用者很可能根據首頁視頻的縮圖去選擇。此外,排序網路頂部使用加權羅吉斯迴歸(weighted logistic regression)進行訓練,使用 e^x 作為測試階段的啟用函數。
Google 的寬度&深度學習(Wide & Deep learning)
Google 在 2016 年 6 月發布了一篇關於“寬度&深度學習”的論文[5],業內一些公司也在紛紛學習。這裡的推薦情境是 Google Play 市集,但其實 Wide & Deep 的方法可以泛化應用在更廣義的推薦情境上。
簡單來說,人腦就是一個不斷記憶(memorization)並且歸納(generalization)的過程。比如說人們通過記憶“麻雀會飛”和“鴿子會飛”,歸納出“有翅膀的動物就會飛”的結論。由此獲得啟發,將寬線性模型(用於記憶,左側)和深度神經網路模型(用于歸納,右側)結合,汲取各自優勢形成了 Wide & Deep 模型用於推薦排序(中間),這是一個非常有啟發的探索。
寬度&深度模型[6]
寬度模型的輸入是使用者安裝應用和為使用者展示(impression)的應用間的向量積(叉乘),模型通常訓練 one-hot 編碼後的二值特徵(比如安裝 netflix app 並展示了 pandora app 是 1,沒有展示是 0),這種操作不會歸納出訓練集中未出現的特徵對。
基於 embedding 的深度模型可以探索出過去從未或很少出現的新的特徵組合,提升了推薦商品的多樣性。它可以添加小顆粒特徵(比如安裝了視頻類應用,展示的是音樂類應用),同時也需要手動完成特徵工程。高維稀疏的類別特徵(如人口學特徵和裝置類別)映射為低緯稠密的向量後,與其他連續特徵(使用者年齡、應用安裝數等)拼接在一起,輸入 MLP 中,最後輸入邏輯輸出單元。
預測(服務)時,寬度&深度學習模型會將所有候選應用的分數從高到低排序後返回給使用者。
應用推薦中的寬度&深度模型[7]
儘管“ 寬度&深度學習”這一想法很簡單,但經過實驗,它顯著提高了 Google Play 商店中應用的下載率,同時滿足了訓練和測試階段的速度要求。值得一提的是,寬度&深度學習模型和整合(ensemble)學習並不是一回事,因為整合學習中的模型是分別獨立訓練的,互不干擾,只有在預測時才會聯絡在一起。
融合推薦模型
我們將使用 Paddle 實現電影推薦模型,資料集包含了 6,000 位使用者對 4,000 部電影的 1,000,000 條評價(評分範圍 1~5 分,均為整數),訓練完成後,通過輸入電影和使用者的 ID,模型能夠預測出該使用者對該電影的評分,以代表喜好程度。這裡只介紹主要的網路設定,完整版請見教程。
設定 batch size、網路初始學習率,使用 RMSProp 最佳化方法。
settings( batch_size=1600, learning_rate=1e-3, learning_method=RMSPropOptimizer())
構造使用者、電影特徵(以使用者特徵為例)
# 將使用者ID,性別,職業,年齡四個屬性分別映射到其特徵隱層。user_id_emb = embedding_layer(input=user_id, size=embsize)user_id_hidden = fc_layer(input=user_id_emb, size=embsize)gender_emb = embedding_layer(input=gender, size=embsize)gender_hidden = fc_layer(input=gender_emb, size=embsize)age_emb = embedding_layer(input=age, size=embsize)age_hidden = fc_layer(input=age_emb, size=embsize)occup_emb = embedding_layer(input=occupation, size=embsize)occup_hidden = fc_layer(input=occup_emb, size=embsize)# 將這四個屬性分別全串連並相加形成使用者特徵的最終表示。user_feature = fc_layer( input=[user_id_hidden, gender_hidden, age_hidden, occup_hidden], size=embsize)
計算餘弦相似性,定義損失函數和網路輸出。
similarity = cos_sim(a=movie_feature, b=user_feature, scale=2)# 訓練時,採用regression_cost作為損失Function Compute迴歸誤差代價,並作為網路的輸出。# 預測時,網路的輸出即為餘弦相似性。if not is_predict: lbl=data_layer('rating', size=1) cost=regression_cost(input=similarity, label=lbl) outputs(cost)else: outputs(similarity)
訓練完成後,我們可以通過 ./evaluate.py log.txt
評估模型,找出效果最好的模型輪數。接下來,我們搭建一個簡單的 ChatBot 完成電影推薦的預測,作為融合推薦模型的應用。
融合推薦模型的 ChatBot 應用
近些年湧現出一大批聊天機器人和智能家庭裝置,它們幾乎全部支援個人化,比如“識別不同的人”,“根據不同人的喜歡推薦不同的內容”。Facebook 創始人紮克伯格使用多種 AI 技術為自己家裡構建了一個自動控制系統,命名為 Jarvis,它能夠根據家庭成員的喜好播放不同風格的音樂。所以,基於聊天機器人的個人化服務是未來的趨勢。
Bot 的開發非常簡單,我們藉助 Telegram 來完成這個任務。Telegram 是一款開源的即時通訊軟體(類似、WhatsApp 等),它的機器人平台(Telegram Bot Platform)極大地豐富了生態,比如可以使用 Bot SSH 登入 VPS 、接收 RSS 訂閱新聞或部落格、下載 YouTube 視頻、接收訊息甚至是玩遊戲等等。由於 Bot 是面向 API 的,我們可以開發某個 Workflow (比如 IFTTT)完成一系列的任務,有人為其創造了一個新名詞,叫“r2r - robots 2 robots”。
由於其服務在中國的網路環境下並不容易訪問,這裡我推薦使用 proxychains 運行 Python 檔案或 IPython 互動環境,當然也可以直接搭建在國外的 VPS 上。基於融合推薦模型的 ChatBot 最終效果
1. 建立 Bot 並安裝依賴
首先需要找官方的機器人老爹 @BotFather 發送/newbot
命令申請建立,設定 bot 的基本資料後會得到一串 Token,協助訪問 HTTP API,這是 Bot 的唯一標識,不能泄露出去。
在 GitHub 上,很多熱心的開發人員使用不同的語言封裝了原生 API,讓開發變得更加容易。我們使用 Python 完成開發,因此首先安裝依賴 pip install python-telegram-bot --upgrade
。
2. 接入 Paddle 預測檔案
變數 MODEL_PATH
是模型評估 ./evaluate.py log.txt
的結果,函數cal_with_paddle
實際就是教程中 prediction.py
的功能,輸入是電影和使用者的 ID,輸出預測的評分。
Paddle 社區即將發布新版 API,資料讀入、訓練、預測的過程將變得更加簡潔,因此你完全可以不關心這裡究竟做了什麼。
from py_paddle import swig_paddle, DataProviderConverterfrom common_utils import *from paddle.trainer.config_parser import parse_configtry: import cPickle as pickleexcept ImportError: import pickle# 模型路徑MODEL_PATH = 'output/pass-00004/'def cal_with_paddle(movie_id, user_id): # 載入參數 swig_paddle.initPaddle('--use_gpu=0') conf = parse_config("trainer_config.py", "is_predict=1") network = swig_paddle.GradientMachine.createFromConfigProto( conf.model_config) assert isinstance(network, swig_paddle.GradientMachine) network.loadParameters(MODEL_PATH) # 讀入資料並預測 with open('./data/meta.bin', 'rb') as f: meta = pickle.load(f) headers = [h[1] for h in meta_to_header(meta, 'movie')] headers.extend([h[1] for h in meta_to_header(meta, 'user')]) cvt = DataProviderConverter(headers) movie_meta = meta['movie'][movie_id] user_meta = meta['user'][user_id] data = [movie_id - 1] data.extend(movie_meta) data.append(user_id - 1) data.extend(user_meta) return '%.2f' % (network.forwardTest(cvt.convert([data]))[0]['value'][0][0] + 3)
3. 變數聲明與函數定義
替換 TOKEN
為實際申請的字串,定義按鍵介面,和互動函數。
from telegram import ReplyKeyboardMarkupfrom telegram.ext import (Updater, CommandHandler, MessageHandler, Filters, RegexHandler, ConversationHandler)# 兩種互動方式,分別是按鍵選擇回複和輸入文本回複CHOOSING, TYPING_REPLY = range(2)TOKEN ='123456789:AAG6xe24v748h4G6rUcxzxEZTFI932ECWaE'# 選擇回複介面的三個按鍵,分別是輸入電影ID、使用者ID和預測reply_keyboard = [['Movie_ID', 'User_ID'], ['Predict']]markup = ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True)# 定義輸出格式def facts_to_str(user_data): facts = list() for key, value in user_data.items(): facts.append('%s : %s' % (key, value)) return "\n".join(facts).join(['\n', '\n'])# `/start` 命令def start(bot, update): update.message.reply_text( "Welcome to the movie recommender bot!", reply_markup=markup) return CHOOSING# 記錄按鍵(key)並要求使用者輸入對應文本(value)def regular_choice(bot, update, user_data): text = update.message.text user_data['choice'] = text update.message.reply_text('Input %s please!' % text) return TYPING_REPLY# 記錄文本(value)def received_information(bot, update, user_data): user_data[user_data['choice']] = update.message.text del user_data['choice'] update.message.reply_text("Neat! This is what you already told me:" "%s" % facts_to_str(user_data), reply_markup=markup) return CHOOSING# 調用Paddle預測函數並輸出結果def predict(bot, update, user_data): if 'choice' in user_data: del user_data['choice'] score = cal_with_paddle(int(user_data['Movie_ID']), int(user_data['User_ID'])) update.message.reply_text("Predicting with Paddle!\n" "Prediction Score is %s" % score) user_data.clear() return ConversationHandler.END
4. 開始運行
updater = Updater(TOKEN)dp = updater.dispatcher# 定義對話handlerconv_handler = ConversationHandler( # 以`/start`命令作為入口 entry_points=[CommandHandler('start', start)], # 定義互動方式,支援正則匹配 states={ CHOOSING: [RegexHandler('^User_ID|Movie_ID$', regular_choice, pass_user_data=True), ], TYPING_REPLY: [MessageHandler(Filters.text, received_information, pass_user_data=True), ], }, # 預測 fallbacks=[RegexHandler('^Predict$', predict, pass_user_data=True)])# 添加對話handlerdp.add_handler(conv_handler)# 開始運行updater.start_polling()
至此,我們已經完成了 ChatBot 推薦模型的準系統。Bot 中還有更豐富的功能值得探索,此外我們還可以接入雲端服務的 API,例如使用 Google Cloud Speech API 完成語音轉文字的功能。
總結
近些年來,深度學習已經極大地推進了影像處理、語音辨識、NLP 等領域的發展與進步,而在推薦系統上面的應用還處於早期階段,同時也意味著有很大的發展空間。此外,深度學習正在為醫學、生物資訊學、邏輯推理、量化投資甚至圍棋等領域帶來新的啟發與思考。我曾與學校的神經科學研究所合作,使用深度學習技術來分析食蟹猴基因特徵,預測 microRNA 的堿基序列,獲得了不錯的效果,而最基本的神經網路結構也是從大腦的生物機理獲得的啟發,這形成了推動學科進步的良性迴圈。
2016 年的最後一天,羅振宇在他的“跨年演講”中提到,“人工智慧不是人的延伸,它是人的替代”;英偉達 CEO 黃仁勳在《智能工業革命》中認為:“繼蒸汽機(發明)、大規模生產以及自動化之後,AI 技術將引發第四次工業革命” ;周志華教授在採訪中說,“2017 年,機器學習技術將在更多行業帶來更大價值”。各個行業的人們都在關注和見證著 AI 的發展,與此同時,很多工程師和社區(如 Paddle)正在努力著降低學習和應用的門檻。
我們有幸親身經曆了這次發展的浪潮,但仍需清醒地意識到其實還有很漫長的路等待人們的探索,我們期待更多如 GAN(產生對抗網路) 一樣的新思想的爆發,這需要我們見素抱樸,不忘初心。
感謝
感謝訂閱本次 Chat,個人化推薦這一章節的網路結構其實很簡單,更多的知識和內容,還請關注該系列的後續分享。
大家熟知的許多任務,如:機器翻譯,看圖說話,為你寫詩,對話機器人,標題黨改寫等等,背後都有著共同的模型。下一課我們將會介紹這些任務背後的深度神經網路模型,一起進入自然語言處理任務中一個非常有意思的問題:自動文本產生。
我們將在下一課介紹自然語言處理任務中的重要積木:迴圈神經網路。圍繞迴圈神經網路,我們會一起討論,如何對抗梯度消失和梯度爆炸,為什麼需要深度迴圈神經網路,如何有效地訓練深度迴圈神經網路。在此基礎上,我們會繼續開發本課中的對話機器人,引入神經圖靈機的概念,介紹去年最火的技術之一:“注意力機制”,利用已有的積木,讓迴圈神經網路從資料中學習,自動產生回複與使用者進行有趣地互動。最後,我們會一起討論現有技術面臨的挑戰,探討一些加速文本產生任務的技術,期待大家的參與。
Paddle 不僅屬於百度,更屬於開源社區,我們希望對深度學習感興趣的研究人員、工程師和開源愛好者能夠加入 PaddlePaddle Tech Writer,撰寫您所擅長的深度學習教程或設計有趣的樣本,讓更多的人感受到深度學習的魅力。如果您在使用 Paddle 過程中遇到任何問題,都可以去 GitHub 發起 Issue,社區的小夥伴們將在第一時間為您解答,希望 Paddle 與您共同成長。
參考資料
- http://www.datasciencecentral.com/profiles/blogs/understanding-and-selecting-recommenders-1
- https://zh.wikipedia.org/wiki/%E9%95%BF%E5%B0%BE
- https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/45530.pdf
- 引自論文[3]中圖3
- https://arxiv.org/abs/1606.07792
- 引自論文[5]中圖1
- 引自論文[5]中圖4
Chat實錄:《李釗:如何入門深度學習個人化推薦》