|
圖片來(lái)源:Unsplash/Susan Yin 你也許每天都會(huì)逛一逛電子商務(wù)網(wǎng)站,或者從博客、新聞和媒體出版物上閱讀大量文章。 瀏覽這些東西的時(shí)候,最令讀者或者用戶煩惱的事情是什么呢? ——有太多的東西可以看,反而會(huì)經(jīng)常看不到自己正在搜索的東西。 是的,網(wǎng)上有太多的信息和文章,用戶需要一種方式來(lái)簡(jiǎn)化他們的發(fā)現(xiàn)之旅。 如果你在經(jīng)營(yíng)一家電子商務(wù)網(wǎng)站或博客,你也許會(huì)問(wèn):有這個(gè)必要嗎? 嗯……你聽(tīng)過(guò)漏斗嗎? 用戶所用的漏斗越小,產(chǎn)品的轉(zhuǎn)換就越大。這是用戶體驗(yàn)的基本原則。所以,如果減少步驟的數(shù)量可以增加網(wǎng)站頁(yè)面的瀏覽量甚至是收入,為什么不這么做呢? 推薦系統(tǒng)如何提供幫助? 簡(jiǎn)單來(lái)說(shuō),推薦系統(tǒng)就是一個(gè)發(fā)現(xiàn)系統(tǒng),該系統(tǒng)可通過(guò)分析數(shù)據(jù)向用戶提供推薦。不需要用戶去專門(mén)搜索,系統(tǒng)自動(dòng)帶來(lái)推薦商品。 這聽(tīng)起來(lái)像是魔法。 亞馬遜和Netflix幾十年前就開(kāi)始使用這種魔法了。 一打開(kāi)Spotify,它就已經(jīng)為用戶提供了一個(gè)推薦歌單(這種深度個(gè)性化推薦服務(wù)叫作Discover Weekly)。 圖片來(lái)源:Unsplash/Samuel Zeller 深入了解推薦系統(tǒng) 一般來(lái)說(shuō),我們所知的推薦系統(tǒng)有兩種——當(dāng)然并不是所有的人都知道。 1. 基于內(nèi)容的推薦系統(tǒng) 這類推薦系統(tǒng)很容易被我們的大腦消化,而且不會(huì)出現(xiàn)短路或爆炸的跡象。 例如,你是一個(gè)狂熱的小說(shuō)迷,喜歡阿加莎·克里斯蒂的《無(wú)人生還》,并從網(wǎng)上書(shū)店買(mǎi)了這本書(shū)。 那么,當(dāng)你下次再打開(kāi)網(wǎng)站時(shí),網(wǎng)上書(shū)店就會(huì)給你推薦《ABC謀殺案》。 為什么呢? 因?yàn)樗鼈兌际前⒓由た死锼沟俚淖髌贰?/p> 因此,基于內(nèi)容的推薦模型會(huì)向你推薦這本書(shū)。 就是這么簡(jiǎn)單!那就來(lái)用一用吧! 等等…… 雖然這種基于內(nèi)容的推薦很容易被我們的大腦消化,看起來(lái)也很簡(jiǎn)單,但它無(wú)法預(yù)測(cè)用戶的真實(shí)行為。 例如,你不喜歡偵探赫丘里·波羅,但喜歡阿加莎·克里斯蒂小說(shuō)中的其他偵探。在這種情況下,網(wǎng)站就不應(yīng)該向你推薦《ABC謀殺案》。 2. 協(xié)同過(guò)濾推薦系統(tǒng) 這種類型的推薦系統(tǒng)克服了上面的問(wèn)題。本質(zhì)上,該系統(tǒng)記錄了用戶在網(wǎng)站上的所有交互,并基于這些記錄提出建議。 它是什么原理呢? 請(qǐng)看下面的場(chǎng)景: 這里有兩個(gè)用戶,用戶A和用戶B。 用戶A購(gòu)買(mǎi)了商品1 用戶A購(gòu)買(mǎi)了商品2 用戶A購(gòu)買(mǎi)了商品3 用戶B購(gòu)買(mǎi)了商品1 用戶B購(gòu)買(mǎi)了商品3 那么協(xié)同過(guò)濾系統(tǒng)將會(huì)向用戶B推薦商品2,因?yàn)橛辛硗庖粋€(gè)用戶也購(gòu)買(mǎi)了商品1和商品3,同時(shí)還購(gòu)買(mǎi)了商品2。 你也許會(huì)說(shuō),得了吧,他們可能是偶然才一起買(mǎi)了那些巧合的商品。 但是,如果有100個(gè)用戶都與用戶A有相同的購(gòu)買(mǎi)行為呢? 這就是所謂的群眾的力量。 那么,你還在等什么呢?讓我們開(kāi)始在你的生產(chǎn)環(huán)境中創(chuàng)建協(xié)同過(guò)濾推薦系統(tǒng)吧! 等等,先別著急! 雖然這個(gè)系統(tǒng)性能極佳,但在嘗試創(chuàng)建可用于生產(chǎn)的系統(tǒng)時(shí),它還存在幾個(gè)嚴(yán)重問(wèn)題。 協(xié)同過(guò)濾推薦系統(tǒng)的不足 1. 它不知道用戶的購(gòu)物習(xí)慣?;趦?nèi)容的推薦系統(tǒng)會(huì)根據(jù)用戶的購(gòu)物記錄推薦相似商品,與此相反,協(xié)同過(guò)濾推薦系統(tǒng)的推薦并不是基于相似性。如果你關(guān)心這一問(wèn)題的話,解決方案就是將兩種方法混合起來(lái),結(jié)合使用。 2. 因?yàn)樾枰鎯?chǔ)用戶項(xiàng)矩陣,所以系統(tǒng)需要大量的硬件資源。假設(shè)你的電子商務(wù)網(wǎng)站有10萬(wàn)用戶;與此同時(shí),你的網(wǎng)站提供1萬(wàn)種產(chǎn)品。在這種情況下,你將需要10000 x 100000的矩陣,每個(gè)元素包含4個(gè)字節(jié)的整數(shù)。是的,光是存儲(chǔ)矩陣,不做其他事,你就需要4GB的內(nèi)存。 3. “冷啟動(dòng)”(冰冷的開(kāi)始),該系統(tǒng)并不會(huì)為新用戶帶來(lái)好處,因?yàn)橄到y(tǒng)并不了解新用戶。 4. 不變性。如果用戶沒(méi)有在網(wǎng)站上進(jìn)行搜索或購(gòu)物,系統(tǒng)的推薦將一成不變。于是用戶就會(huì)認(rèn)為網(wǎng)站上沒(méi)有什么新鮮東西,從而退出網(wǎng)站。 通過(guò)混合使用兩種推薦系統(tǒng)可以輕易解決第1個(gè)問(wèn)題,然而,其他問(wèn)題仍然令人頭痛。 本文的目的就是解決第2、第3和第4個(gè)問(wèn)題。 讓我們開(kāi)始吧! 使推薦系統(tǒng)可用于生產(chǎn)的終極指南 如何解決這些問(wèn)題?機(jī)器本身存在限制,而且就算是根據(jù)常識(shí),也不可能僅為小小的需求就部署一個(gè)巨大的服務(wù)器。 推薦下面這本書(shū): Ted Dunning 和Ellen Friedman的《實(shí)用性機(jī)器學(xué)習(xí)》 這本書(shū)告訴我們,對(duì)于一個(gè)可用于生產(chǎn)的系統(tǒng),你不需要指望它在任何方面都具備最高精度。 在實(shí)際的用例中,一個(gè)有些不準(zhǔn)確但又可以接受的方法,通常是最有效的。 關(guān)于如何做到這一點(diǎn),最有趣的部分是: 1. 對(duì)通用推薦指標(biāo)進(jìn)行批量計(jì)算。 2. 實(shí)時(shí)查詢,不使用用戶-商品矩陣,而是獲取用戶的最新交互并向系統(tǒng)查詢。 下面我們邊構(gòu)建系統(tǒng)邊解釋。 Python的推薦系統(tǒng) 為什么選擇python? 因?yàn)閜ython的語(yǔ)言簡(jiǎn)單易學(xué),只需要幾個(gè)小時(shí)就能理解它的語(yǔ)法。 for item in the_bag: print(item) 通過(guò)上面代碼,你可以打印包里的所有項(xiàng)。 請(qǐng)?jiān)L問(wèn)Python官網(wǎng)(https://www./downloads/),根據(jù)操作系統(tǒng)下載并安裝相應(yīng)安裝包。 本教程需要用到以下幾個(gè)安裝包。 pip install numpy pip install scipy pip install pandas pip install jupyter pip install requests Numpy和Scipy是處理數(shù)學(xué)計(jì)算的python包,建構(gòu)矩陣時(shí)需要用到它們。Pandas 用于數(shù)據(jù)處理。Requests用于http調(diào)用。Jupyter是一個(gè)可以交互運(yùn)行python代碼的網(wǎng)絡(luò)應(yīng)用程序。 輸入Jupyter Notebook,你會(huì)看到如下界面 在提供的單元格上編寫(xiě)代碼,代碼將以交互方式運(yùn)行。 開(kāi)始之前需要幾個(gè)工具。 1. Elasticsearch(彈性搜索)。這是一個(gè)開(kāi)源搜索引擎,可以幫助快速搜索到文檔。這個(gè)工具可用于保存計(jì)算指標(biāo),以便實(shí)時(shí)查詢。 2. Postman。這是一個(gè)API開(kāi)發(fā)工具,可用來(lái)模擬彈性搜索中的查詢,因?yàn)閺椥运阉骺梢酝ㄟ^(guò)http訪問(wèn)。 下載并安裝這兩個(gè)工具,接著就可以開(kāi)始了。 數(shù)據(jù) 先來(lái)看看Kaggle中的數(shù)據(jù)集:電子商務(wù)網(wǎng)站行為數(shù)據(jù)集(http://www.baidu.com/link?url=-uZgHHgYJmRlBX5WL_ufkLSb0S5eXU0j43iPMLh3XNtXbLq5uNoqe3Oje7AUt0PK)。下載并提取Jupyter 筆記本目錄中的數(shù)據(jù)。 在這些文件中,本教程只需要用到events.csv。 該文件由用戶對(duì)電子商務(wù)網(wǎng)站上的商品進(jìn)行的數(shù)百萬(wàn)次操作組成。 開(kāi)始探索數(shù)據(jù)吧! import pandas as pdimport numpy as np 將輸入寫(xiě)在Jupyter Notebook上,就可以開(kāi)始了。 df = pd.read_csv('events.csv')df.shape 它會(huì)輸出(2756101,5),這意味著你有270萬(wàn)行和5列。 讓我們來(lái)看看 df.head()
它有5欄。 1. 時(shí)間戳(Timestamp),事件的時(shí)間戳 2. 訪問(wèn)者ID(Visitorid),用戶的身份 3. 商品ID(Itemid), 商品的名稱 4. 事件(Event), 事件 5. 交易ID(Transactionid),如果事件是交易,則為交易ID 下面檢查一下,哪些事件是可用的 df.event.unique() 你將獲得三個(gè)事件:瀏覽、添加到購(gòu)物車(chē)和交易。 你可能嫌麻煩,不想處理所有事件,所以本教程中只需處理交易。 所以,我們只過(guò)濾交易。 trans = df[df['event'] == 'transaction']trans.shape 它將輸出(22457, 5) 也就是說(shuō)你將有22457個(gè)交易數(shù)據(jù)可以處理。這對(duì)新手來(lái)說(shuō)已經(jīng)足夠了。 下面來(lái)進(jìn)一步看看數(shù)據(jù) visitors = trans['visitorid'].unique()items = trans['itemid'].unique()print(visitors.shape)print(items.shape) 你將獲得11719個(gè)獨(dú)立訪問(wèn)者和12025個(gè)獨(dú)立商品。 創(chuàng)建一個(gè)簡(jiǎn)單而有效的推薦系統(tǒng),經(jīng)驗(yàn)之談是在不損失質(zhì)量的情況下對(duì)數(shù)據(jù)進(jìn)行抽樣。這意味著,對(duì)于每個(gè)用戶,你只需獲取50個(gè)最新交易數(shù)據(jù),卻仍然可以獲得想要的質(zhì)量,因?yàn)轭櫩托袨闀?huì)隨著時(shí)間的推移而改變。 trans2 = trans.groupby(['visitorid']).head(50)trans2.shape 現(xiàn)在你只有19939筆交易。這意味著2000筆左右的交易已經(jīng)過(guò)時(shí)。 由于訪問(wèn)者ID和商品ID是一長(zhǎng)串的數(shù)字,你很難記住每個(gè)ID。 trans2['visitors'] = trans2['visitorid'].apply(lambda x : np.argwhere(visitors == x)[0][0])trans2['items'] = trans2['itemid'].apply(lambda x : np.argwhere(items == x)[0][0])trans2 你需要其他基于0的索引列。如以下界面所示:
這樣更加清晰。接下來(lái)的所有步驟只需使用訪問(wèn)者和商品欄。
圖片來(lái)源:Unsplash/Andrei Lazarev 下一步:創(chuàng)建用戶-商品矩陣 噩夢(mèng)來(lái)了…… 一共有11719個(gè)獨(dú)立訪問(wèn)者和12025個(gè)商品,所以需要大約500MB的內(nèi)存來(lái)存儲(chǔ)矩陣。 稀疏矩陣(Sparse matrix)這時(shí)候就派上用場(chǎng)了。 稀疏矩陣是大多數(shù)元素為零的矩陣。這是有意義的,因?yàn)椴豢赡芩械挠脩舳假?gòu)買(mǎi)所有的商品,很多連接都將為零。 from scipy.sparse import csr_matrix Scipy很有用。 occurences = csr_matrix((visitors.shape[0], items.shape[0]), dtype='int8')def set_occurences(visitor, item): occurences[visitor, item] += 1trans2.apply(lambda row: set_occurences(row['visitors'], row['items']), axis=1)occurences 對(duì)數(shù)據(jù)中的每一行應(yīng)用set_occurences函數(shù)。 會(huì)輸出如下結(jié)果: <11719x12025 sparse matrix of type '<class 'numpy.int8'>' with 18905 stored elements in Compressed Sparse Row format> 在矩陣的1.4億個(gè)單元格中,只有18905個(gè)單元格是用非零數(shù)據(jù)填充的。 所以,實(shí)際上只需要把這18905個(gè)值存儲(chǔ)到內(nèi)存中,效率就能提高99.99%。 但稀疏矩陣有一個(gè)缺點(diǎn),想要實(shí)時(shí)檢索數(shù)據(jù)的話,需要很大的計(jì)算量。所以,到這里還沒(méi)有結(jié)束。 共現(xiàn)矩陣 下面建構(gòu)一個(gè)商品-商品矩陣,其中每個(gè)元素表示用戶同時(shí)購(gòu)買(mǎi)兩個(gè)商品的次數(shù),我們稱之為共現(xiàn)矩陣。 要?jiǎng)?chuàng)建共現(xiàn)矩陣,你需要將共現(xiàn)矩陣的轉(zhuǎn)置與自身做點(diǎn)積。 有人試過(guò)在沒(méi)有稀疏矩陣的情況下這樣做,結(jié)果電腦死機(jī)了。所以,千萬(wàn)不要重蹈覆轍。 cooc = occurences.transpose().dot(occurences) cooc.setdiag(0) 電腦立馬輸出了一個(gè)稀疏矩陣。 setdiag函數(shù)將對(duì)角線設(shè)置為0,這意味著你不想計(jì)算商品1的值,而商品1的位置都在一起,因?yàn)樗鼈兪窍嗤捻?xiàng)目。 異常行為 共現(xiàn)矩陣包含同時(shí)購(gòu)買(mǎi)兩種商品的次數(shù)。 但也可能會(huì)存在一種商品,購(gòu)買(mǎi)這種商品本身和用戶的購(gòu)物習(xí)慣沒(méi)有任何關(guān)系,可能是限時(shí)搶購(gòu)之類的商品。 在現(xiàn)實(shí)中,你想要捕捉的是真正的用戶行為,而非像限時(shí)搶購(gòu)那樣非常規(guī)行為。 為了消除這些影響,你需要對(duì)共現(xiàn)矩陣的得分進(jìn)行扣除。 Ted Dunnings在前一本書(shū)中提出了一種算法,叫做對(duì)數(shù)似然比(Log-Likelihood Ratio, LLR)。 def xLogX(x): return x * np.log(x) if x != 0 else 0.0def entropy(x1, x2=0, x3=0, x4=0): return xLogX(x1 + x2 + x3 + x4) - xLogX(x1) - xLogX(x2) - xLogX(x3) - xLogX(x4)def LLR(k11, k12, k21, k22): rowEntropy = entropy(k11 + k12, k21 + k22) columnEntropy = entropy(k11 + k21, k12 + k22) matrixEntropy = entropy(k11, k12, k21, k22) if rowEntropy + columnEntropy < matrixEntropy: return 0.0 return 2.0 * (rowEntropy + columnEntropy - matrixEntropy)def rootLLR(k11, k12, k21, k22): llr = LLR(k11, k12, k21, k22) sqrt = np.sqrt(llr) if k11 * 1.0 / (k11 + k12) < k21 * 1.0 / (k21 + k22): sqrt = -sqrt return sqrt LLR函數(shù)計(jì)算的是A和B兩個(gè)事件同時(shí)出現(xiàn)的可能性。 參數(shù)有 1.k11, 兩個(gè)事件同時(shí)發(fā)生的次數(shù) 2.k12, 事件B 單獨(dú)發(fā)生的次數(shù) 3.k21, 事件A單獨(dú)發(fā)生的次數(shù) 4.k22, 事件A和事件B都沒(méi)有發(fā)生的次數(shù) 現(xiàn)在計(jì)算LLR函數(shù)并將其保存到pp_score矩陣中。 row_sum = np.sum(cooc, axis=0).A.flatten()column_sum = np.sum(cooc, axis=1).A.flatten()total = np.sum(row_sum, axis=0)pp_score = csr_matrix((cooc.shape[0], cooc.shape[1]), dtype='double')cx = cooc.tocoo()for i,j,v in zip(cx.row, cx.col, cx.data): if v != 0: k11 = v k12 = row_sum[i] - k11 k21 = column_sum[j] - k11 k22 = total - k11 - k12 - k21 pp_score[i,j] = rootLLR(k11, k12, k21, k22) 對(duì)結(jié)果進(jìn)行排序,使每種商品LLR得分最高的位于每行的第一列。 result = np.flip(np.sort(pp_score.A, axis=1), axis=1) result_indices = np.flip(np.argsort(pp_score.A, axis=1), axis=1) 推薦系統(tǒng)的指標(biāo) 結(jié)果矩陣中的第一項(xiàng)指標(biāo)如果足夠高的話,可以被視為該項(xiàng)的指標(biāo)。 來(lái)看一下其中的一個(gè)結(jié)果 result[8456] 你會(huì)得到 array([15.33511076, 14.60017668, 3.62091635, ..., 0. , 0. , 0. ]) 再看看指標(biāo) result_indices[8456] 你會(huì)得到 array([8682, 380, 8501, ..., 8010, 8009, 0], dtype=int64) 可以有把握地說(shuō),商品8682和商品380的LLR分?jǐn)?shù)很高,可以作為商品8456的指標(biāo)。而商品8501分?jǐn)?shù)不是那么高,可能不能作為商品8456的指標(biāo)。 這意味著,如果有用戶購(gòu)買(mǎi)了商品8682和商品380,你可以向他推薦商品8456。 這很簡(jiǎn)單。 但是,根據(jù)經(jīng)驗(yàn),你可能想給LLR分?jǐn)?shù)施加一些限制,這樣可以刪除無(wú)關(guān)緊要的指標(biāo)。 minLLR = 5indicators = result[:, :50]indicators[indicators < minLLR] = 0.0indicators_indices = result_indices[:, :50]max_indicator_indices = (indicators==0).argmax(axis=1)max = max_indicator_indices.max()indicators = indicators[:, :max+1]indicators_indices = indicators_indices[:, :max+1] 現(xiàn)在,已經(jīng)準(zhǔn)備好將它們組合到彈性搜索中了,這樣就可以實(shí)時(shí)查詢推薦。 import requests import json 好了,現(xiàn)在可以把之前準(zhǔn)備好的東西放到彈性搜索中了。 但是,請(qǐng)注意。如果你想用 /_create/<id> API一個(gè)個(gè)地添加數(shù)據(jù),將會(huì)花費(fèi)很長(zhǎng)時(shí)間。你當(dāng)然可以這么做,但是可能需要花費(fèi)半個(gè)小時(shí)到一個(gè)小時(shí)才能把12025個(gè)商品轉(zhuǎn)移到彈性搜索中。 那怎么解決這個(gè)問(wèn)題呢? 批量更新 幸運(yùn)的是,彈性搜索擁有批量API,可以輕松地同時(shí)發(fā)送多個(gè)文檔。 因此,創(chuàng)建一個(gè)新索引(items2),讓我們來(lái)嘗試一下: actions = [] for i in range(indicators.shape[0]): length = indicators[i].nonzero()[0].shape[0] real_indicators = items[indicators_indices[i, :length]].astype('int').tolist() id = items[i] action = { 'index' : { '_index' : 'items2', '_id' : str(id) } } data = { 'id': int(id), 'indicators': real_indicators } actions.append(json.dumps(action)) actions.append(json.dumps(data)) if len(actions) == 200: actions_string = '\n'.join(actions) + '\n' actions = [] url = 'http://127.0.0.1:9200/_bulk/' headers = { 'Content-Type' : 'application/x-ndjson' } requests.post(url, headers=headers, data=actions_string)if len(actions) > 0: actions_string = '\n'.join(actions) + '\n' actions = [] url = 'http://127.0.0.1:9200/_bulk/' headers = { 'Content-Type' : 'application/x-ndjson' } requests.post(url, headers=headers, data=actions_string) 瞧,只需要幾秒鐘就能完成。 在Postman中點(diǎn)擊這個(gè)API 127.0.0.1:9200/items2/_count 你就存儲(chǔ)了數(shù)據(jù) { 'count': 12025, '_shards': { 'total': 1, 'successful': 1, 'skipped': 0, 'failed': 0 }}用/items2/240708檢查一下商品數(shù)據(jù) { 'id': 240708, 'indicators': [ 305675, 346067, 312728 ]} Id是商品的Id,而指標(biāo)則是成為該商品推薦指標(biāo)的其他商品。 實(shí)時(shí)查詢 創(chuàng)建的最棒的部分就是實(shí)時(shí)查詢 { 'query': { 'bool': { 'should': [ { 'terms': {'indicators' : [240708], 'boost': 2}} ] } }}發(fā)送請(qǐng)求到127.0.0.1:9200/items2/_search 你會(huì)得到三個(gè)結(jié)果。商品312728, 商品305675和 商品346067。正是會(huì)與商品240708一起購(gòu)買(mǎi)的三件商品。 太棒了!現(xiàn)在大量的資源需求已經(jīng)不是問(wèn)題了。那么,另外兩個(gè)問(wèn)題呢?
圖片來(lái)源:Unsplash/Sean O.
“冷啟動(dòng)”問(wèn)題:我不認(rèn)識(shí)你 創(chuàng)建推薦系統(tǒng)時(shí),最常見(jiàn)的就是冷啟動(dòng)問(wèn)題,因?yàn)橄到y(tǒng)中不會(huì)有新用戶的任何行為記錄。 那么,系統(tǒng)應(yīng)該向他們推薦什么呢? 請(qǐng)看我們最近構(gòu)建的推薦系統(tǒng)。你覺(jué)得這個(gè)結(jié)果有什么異常嗎? 是的,結(jié)果只返回3個(gè)推薦項(xiàng)——只有3個(gè)。你打算如何向客戶展示這三個(gè)可憐的推薦項(xiàng)呢? 為了更好的用戶體驗(yàn),讓我們將未受推薦的商品放在列表末尾。 { 'query': { 'bool': { 'should': [ { 'terms': {'indicators' : [240708]}}, { 'constant_score': {'filter' : {'match_all': {}}, 'boost' : 0.000001}} ] } }} 你可以使用常數(shù)分?jǐn)?shù)來(lái)返回所有其他項(xiàng)。 但是,你也需要對(duì)所有未受推薦的項(xiàng)目進(jìn)行排序,這樣即使沒(méi)有再用戶的行為中捕捉到,也有可能是用戶會(huì)喜歡的商品。 多數(shù)情況下,受歡迎的商品非常好用。 如何確定一個(gè)商品是否受歡迎呢? popular = np.zeros(items.shape[0])def inc_popular(index): popular[index] += 1trans2.apply(lambda row: inc_popular(row['items']), axis=1) 這很簡(jiǎn)單,逐個(gè)數(shù)商品的出現(xiàn)次數(shù),出現(xiàn)次數(shù)最多的就最流行。讓我們創(chuàng)建另一個(gè)索引items3。批量插入 actions = []for i in range(indicators.shape[0]): length = indicators[i].nonzero()[0].shape[0] real_indicators = items[indicators_indices[i, :length]].astype('int').tolist() id = items[i] action = { 'index' : { '_index' : 'items3', '_id' : str(id) } } # url = 'http://127.0.0.1:9200/items/_create/' + str(id) data = { 'id': int(id), 'indicators': real_indicators, 'popular': popular[i] } actions.append(json.dumps(action)) actions.append(json.dumps(data)) if len(actions) == 200: actions_string = '\n'.join(actions) + '\n' actions = [] url = 'http://127.0.0.1:9200/_bulk/' headers = { 'Content-Type' : 'application/x-ndjson' } requests.post(url, headers=headers, data=actions_string)if len(actions) > 0: actions_string = '\n'.join(actions) + '\n' actions = []url = 'http://127.0.0.1:9200/_bulk/' headers = { 'Content-Type' : 'application/x-ndjson' } requests.post(url, headers=headers, data=actions_string) 這個(gè)索引階段中也包括流行字段。所以數(shù)據(jù)會(huì)是這樣的 { 'id': 240708, 'indicators': [ 305675, 346067, 312728 ], 'popular': 3.0}你將會(huì)有三個(gè)字段。ID,指標(biāo)(與前面類似),以及流行字段(也就是用戶購(gòu)買(mǎi)的商品數(shù)量)。 在前面的查詢中加入popular。 函數(shù)得分:組合得分的方法 所以,現(xiàn)在有多個(gè)得分來(lái)源,即指標(biāo)分?jǐn)?shù)和流行分?jǐn)?shù),那么如何將分?jǐn)?shù)組合起來(lái)呢? 可以用彈性搜索的功能評(píng)分。 { 'query': { 'function_score':{ 'query': { 'bool': { 'should': [ { 'terms': {'indicators' : [240708], 'boost': 2}}, { 'constant_score': {'filter' : {'match_all': {}}, 'boost' : 0.000001}} ] } }, 'functions':[ { 'filter': {'range': {'popular': {'gt': 0}}}, 'script_score' : { 'script' : { 'source': 'doc['popular'].value * 0.1' } } } ], 'score_mode': 'sum', 'min_score' : 0 } }} 修改查詢,并添加一個(gè)功能評(píng)分,將流行值的0.1倍添加到上面的常量分?jǐn)?shù)中。不必執(zhí)著于0.1,也可以使用其他函數(shù),甚至自然對(duì)數(shù)。像這樣: Math.log(doc['popular'].value) 現(xiàn)在,可以看到最受歡迎的商品461686排在第四位,僅低于推薦商品。
下面依次是其它受歡迎的商品。
不變的、靜態(tài)的推薦 如你所見(jiàn),每次實(shí)時(shí)查詢時(shí),推薦結(jié)果都保持不變。一方面這很好,因?yàn)槲覀兊募夹g(shù)是可復(fù)制的;但另一方面,用戶可能對(duì)此并不滿意。 Ted Dunnings在他的書(shū)中說(shuō),在推薦的第20個(gè)商品后,點(diǎn)擊率將會(huì)非常低。這意味著在那之后我們推薦的任何商品都不會(huì)被用戶知道。 怎么解決這個(gè)問(wèn)題呢? 有一種技術(shù)叫做抖動(dòng)。它會(huì)在查詢時(shí)產(chǎn)生一種隨機(jī)干擾,使最不受推薦的商品的排名提前,但同時(shí)又保證受到強(qiáng)烈推薦的商品仍然在推薦列表的前幾位。 { 'query': { 'function_score':{ 'query': { 'bool': { 'should': [ { 'terms': {'indicators' : [240708], 'boost': 2}}, { 'constant_score': {'filter' : {'match_all': {}}, 'boost' : 0.000001}} ] } }, 'functions':[ { 'filter': {'range': {'popular': {'gt': 1}}}, 'script_score' : { 'script' : { 'source': '0.1 * Math.log(doc['popular'].value)' } } }, { 'filter': {'match_all': {}}, 'random_score': {} } ], 'score_mode': 'sum', 'min_score' : 0 } }}隨機(jī)分?jǐn)?shù)會(huì)給出使所有商品均勻分布的隨機(jī)干擾。得分很小,這樣最受歡迎的推薦商品排名就不會(huì)下降。 好處在于用戶將瀏覽時(shí)不必滾動(dòng)到第二或第三頁(yè),只需要點(diǎn)擊瀏覽器上的刷新按鈕,就會(huì)得到新的內(nèi)容。 這很神奇。
圖片來(lái)源:Unsplash/Marvin Meyer |
|
|
來(lái)自: 天承辦公室 > 《006模式創(chuàng)新》