注:轉(zhuǎn)自于有贊
1. 搜索算法總體架構(gòu)
在上篇文章(工程篇)中, 我們介紹了有贊搜索引擎的基本框架. 搜索引擎主要3個(gè)部件構(gòu)成. 第一, hadoop集群, 用于生成大規(guī)模搜索和實(shí)時(shí)索引; 第二, ElasticSearch集群, 提供分布式搜索方案; 第三, 高級(jí)搜索集群, 用于提供商業(yè)搜索的特殊功能.
商業(yè)電商搜索由于搜索的特殊性, 獨(dú)立的ElasticSearch集群是無法滿足多樣的算法需求的, 我們?cè)谒阉鞯母鱾€(gè)部件上都有相應(yīng)的算法插件, 用于構(gòu)建商業(yè)電商搜索引擎的算法體系.
1.1 索引過程
創(chuàng)建索引過程從原始數(shù)據(jù)創(chuàng)建倒排索引的過程. 這個(gè)過程中我們對(duì)商品(doc)進(jìn)行分析, 計(jì)算商品靜態(tài)分, 并對(duì)商品進(jìn)行相似度計(jì)算. 商品的靜態(tài)分對(duì)于提升搜索引擎質(zhì)量起到至關(guān)重要的作用, 相當(dāng)于網(wǎng)頁搜索的pagerank, 想象一下如果沒有pagerank算法, 網(wǎng)頁搜索的質(zhì)量會(huì)有多么差. 在電商搜索中, 最常見的問題是相似商品太多, 必須在建立索引過程中就對(duì)商品間的相似度進(jìn)行預(yù)計(jì)算, 以便在檢索過程中進(jìn)行有效去重.
創(chuàng)建索引的過程如下.
step 1. 計(jì)算每個(gè)doc的靜態(tài)分
step 2. 計(jì)算兩兩doc的相似度
step 3. 根據(jù)相似度和其他信息對(duì)數(shù)據(jù)進(jìn)行分庫
step 4. 建立ES索引
1.2 檢索過程
檢索過程是搜索引擎接收用戶的query進(jìn)行一系列處理并返回相關(guān)結(jié)果的過程. 商業(yè)搜索引擎在檢索過程中需要考慮2個(gè)因素: 1) 相關(guān)性 2) 重要性.
相關(guān)性是指返回結(jié)果和輸入query是否相關(guān), 這是搜索引擎基本問題之一, 目前常用的算法有BM25和空間向量模型. 這個(gè)兩個(gè)算法ElasticSearch都支持, 一般商業(yè)搜索引擎都用BM25算法. BM25算法會(huì)計(jì)算每個(gè)doc和query的相關(guān)性分, 我們使用Dscore表示.
重要性是指商品被信賴的程度, 我們應(yīng)該吧最被消費(fèi)之信賴的商品返回給消費(fèi)者, 而不是讓消費(fèi)之自己鑒別. 尤其是在商品充分競(jìng)爭的電商搜索, 我們必須賦予商品合理的重要性分?jǐn)?shù), 才能保證搜索結(jié)果的優(yōu)質(zhì). 重要性分, 又叫做靜態(tài)分, 使用Tscore表示.
搜索引擎最終的排序依據(jù)是:
Score = Dscore * Tscore
即綜合考慮靜態(tài)分和動(dòng)態(tài)分, 給用戶相關(guān)且重要的商品.
檢索的過程大致抽象為如下幾個(gè)步驟.
step 1. 對(duì)原始query進(jìn)行query分析
step 2. 在as中根據(jù)query分析結(jié)果進(jìn)行query重寫
step 3. 在as中使用重寫后的query檢索es
step 4. 在es查詢過程中根據(jù)靜態(tài)分和動(dòng)態(tài)分綜合排序
step 5. 在as中吧es返回的結(jié)果進(jìn)行重排
step 6. 返回結(jié)果
下面幾章闡述幾個(gè)重點(diǎn)技術(shù).
2. 商品靜態(tài)分計(jì)算技術(shù)
在電商搜索引擎里面商品的靜態(tài)分是有網(wǎng)頁搜索里面的pagerank同等的價(jià)值和重要性, 他們都是doc固有的和查詢query無關(guān)的價(jià)值度量. pagerank通過doc之間的投票關(guān)系進(jìn)行運(yùn)算, 相對(duì)而言商品的靜態(tài)分的因素會(huì)更多一些. 商品靜態(tài)計(jì)算過程和pagerank一樣需要解決如下2個(gè)問題: 1. 穩(wěn)定性. pagerank可以保證一個(gè)網(wǎng)站不會(huì)因?yàn)楹唵捂溄佣哑隹梢跃€性提升網(wǎng)站的排名. 同樣, 商品靜態(tài)分的計(jì)算不可以讓商品可以通過增加單一指標(biāo)線性增加分值(比如刷單對(duì)搜索引擎的質(zhì)量的影響).
2. 區(qū)分度. 在保證穩(wěn)定性的基礎(chǔ)上商品靜態(tài)分要有足夠的區(qū)分度可以保證同樣搜索的條件下, 排在前面的商品的質(zhì)量比排在后面的商品的質(zhì)量高.
我們假設(shè)商品的靜態(tài)分有3個(gè)決定性因素, 1.下單數(shù), 2. 好評(píng)率 3. 發(fā)貨速度
靜態(tài)分我們使用Tsocre表示, Tscore可以寫成如下形式:
Tscore = a * f(下單數(shù)) + b * g(好評(píng)率) + c * h(發(fā)貨速度)
a,b,c是權(quán)重參數(shù), 用于平衡各個(gè)指標(biāo)的影響程度. f,g,h是代表函數(shù)用于把原始的指標(biāo)轉(zhuǎn)化成合理的度量.
首先, 我們需要尋找合理的代表函數(shù).
首先對(duì)各個(gè)指標(biāo)取log. log的導(dǎo)數(shù)是一個(gè)減函數(shù), 表示為了獲得更好的分?jǐn)?shù)需要花費(fèi)越來越多的代價(jià).
標(biāo)準(zhǔn)化. 標(biāo)準(zhǔn)化的目的讓各個(gè)度量可以在同一區(qū)間內(nèi)進(jìn)行比較. 比如下單數(shù)的取值是0~10000, 而好評(píng)率的取值為0~1. 這種情況會(huì)影響到數(shù)據(jù)分析的結(jié)果和方便性, 為了消除指標(biāo)之間的量綱的影響, 需要進(jìn)行數(shù)據(jù)標(biāo)準(zhǔn)化處理, 以解決數(shù)據(jù)指標(biāo)之間的可比性.最常用的標(biāo)準(zhǔn)化方法是z-score標(biāo)準(zhǔn)化方法.
z-score 標(biāo)準(zhǔn)化方法
"概率論"告訴我們對(duì)于滿足正態(tài)分布的數(shù)據(jù)來說, 均值前后3個(gè)z-score的范圍可以覆蓋99%的數(shù)據(jù). 經(jīng)驗(yàn)地, 我們把>5個(gè)zscore 或者小于 -5個(gè)zscore的分?jǐn)?shù)設(shè)置成5*zscore或者-5zscore. 特別說明的是, 我們不建議使用min-max標(biāo)準(zhǔn)化方法. 這種方法又叫離差標(biāo)準(zhǔn)化, 是對(duì)原始數(shù)據(jù)的線性變換, 使結(jié)果值映射到[0-1]之間, 轉(zhuǎn)化函數(shù)如下:這種方法非常不穩(wěn)定, 假設(shè)一個(gè)奇異點(diǎn)是第二大的值的1000倍, 會(huì)讓大部分的值都集中在0~0.01, 同樣失去了歸一化的目的.
圖一是使用min-max歸一化后的數(shù)據(jù)分布, 顯然大部分?jǐn)?shù)據(jù)被"壓扁"在很小的范圍; 圖二使用log歸一化后的數(shù)據(jù)分布, 由于log緩解了增長速度, 可以看出來已經(jīng)有一個(gè)不錯(cuò)的結(jié)果了, 圖三是在log的基礎(chǔ)上進(jìn)行z-score歸一化, 可以看出來, z-score讓數(shù)據(jù)變得非常平滑.(圖三: log-zscore歸一化)
最后, 選擇合適的權(quán)重 經(jīng)過log-zscore歸一化以后, 我們基本上吧f,g,h的表示的代表函數(shù)說明清楚. Tscore = af(下單數(shù)) + bg(好評(píng)率) + c*h(發(fā)貨速度), 下一步就是確定a,b,c的參數(shù). 一般有兩個(gè)方法:
a) 專家法. 根據(jù)我們的日常經(jīng)驗(yàn)動(dòng)態(tài)調(diào)整權(quán)重參數(shù);
b) 實(shí)驗(yàn)法. 首先在專家的幫助下賦一個(gè)初始值, 然后改變單一變量的方法根據(jù)abtest的結(jié)果來動(dòng)態(tài)調(diào)整參數(shù).
3. 商品標(biāo)題去重
商品標(biāo)題去重在電商搜索中起到重要作用, 根據(jù)數(shù)據(jù), 用戶通過搜索頁購買商品80%選擇搜索的前4頁. 商品標(biāo)題的重復(fù)會(huì)導(dǎo)致重要的頁面沒有含金量, 極大降低了搜索的購買率.
舉個(gè)例子:
Title1:美味/香蕉/包郵/廣東/高州/香蕉/banana//無/催熟劑/
Title2:美味/香蕉/廣東/高州/香蕉//非/粉蕉/包郵/
首先, 進(jìn)行特征向量化
這里用到 "bag of word" 技術(shù), 將詞匯表作為空間向量的維度, 標(biāo)題的每個(gè)term的詞頻作為這個(gè)feature的值. 以這個(gè)例子來說. 這個(gè)詞匯的維度為: 美味(0), 香蕉(1), 包郵(2), 廣東(3), 高州(4), banana(5),無(6), 催熟劑(7),非(8),粉蕉(9) 位置: 0,1,2,3,4,5,6,7,8,9
Title1: 1,2,1,1,1,1,1,1,0,0
Title2: 1,2,1,1,1,0,0,0,1,1
這個(gè)每個(gè)title都用一個(gè)固定長度的向量表示.
再次, 計(jì)算兩兩相似度
相似度一般是通過計(jì)算兩個(gè)向量的距離實(shí)現(xiàn)的, 不失一般性, 在這里我們使用1-cosine(x,y)來表示兩個(gè)向量的距離. 這是一個(gè)"All Pair Similarity"的問題, 即需要兩兩比較, 復(fù)雜度在O(n^2). 在商品量巨大的時(shí)候單機(jī)很難處理. 我們給出兩種方法用于實(shí)現(xiàn)"All Pair Similarity".
方法一: spark的矩陣運(yùn)算.
rddRows = sc.parallelize(["1 0 2 0 0 1", "0 0 4 2 0 0"])
rddRows.map(lambda x: Vectors.dense([float(each) for each in str(x).split(" ")]))
mat = RowMatrix(rddRows)
simsPerfect = mat.columnSimilarities()
方法二: map-reduce 線性方法. 這個(gè)方法參考論文"Pairwise Document Similarity in Large Collections with MapReduce". 可以實(shí)現(xiàn)幾乎線性的時(shí)間復(fù)雜度. 相對(duì)于矩陣運(yùn)算在大規(guī)模(10億以上)pair similarity 運(yùn)算上面有優(yōu)勢(shì). 這個(gè)方法簡單的描述如下: 首先, 按照倒排索引的計(jì)算方式計(jì)算每個(gè)term到doc的映射. 比如3個(gè)doc:
doc1 = 我 愛 北京
doc2 = 我 北京 天安門
doc3 = 我 天安門
轉(zhuǎn)化為倒排格式, 這個(gè)需要一次mapper reduce
我 -> doc1, doc2, doc3
愛 -> doc1
北京 -> doc1, doc2
天安門 -> doc2, doc3
然后, 對(duì)于value只有一個(gè)元素的過濾掉, 對(duì)于value大于2個(gè)doc的兩兩組合:
doc1,doc2 <---- from: 我 -> doc1, doc2, doc3
doc1,doc3 <---- from: 我 -> doc1, doc2, doc3
doc2,doc3 <---- form: 我 -> doc1, doc2, doc3
doc1,doc2 <---- from: 北京 -> doc1, doc2
doc2,doc3 <---- from: 天安門 -> doc2, doc3
最后, 對(duì)于輸出進(jìn)行聚合,value為重復(fù)次數(shù)和兩個(gè)doc乘積開根號(hào)的比.
doc1,doc2 -> 2/(len(doc1)*len(doc2))^1/2 = 0.7
doc1,doc3 -> 1/(len(doc1)*len(doc3))^1/2 = 0.3
doc2,doc3 -> 2/(len(doc2)*len(doc3))^1/2 = 0.3
對(duì)于2個(gè)title1, title2, 如果X(title1, title2) > 0.7 則認(rèn)為title1和title2相似, 對(duì)于相似的兩個(gè)doc, 靜態(tài)分大的定義為主doc, 靜態(tài)分小的定義為輔doc. 主doc和輔doc分別建庫.
區(qū)別于網(wǎng)頁搜索(網(wǎng)頁搜索直接將輔doc刪除), 我們將主doc和輔doc分別建庫. 每一次搜索按比例分別搜主庫和輔庫, 并將結(jié)果融合返回. 這樣可以保證結(jié)果的多樣性.
4. 店鋪去重
店鋪去重和商品標(biāo)題去重有點(diǎn)不同. 由于電商特定場(chǎng)景的需要, 不希望搜索結(jié)果一家獨(dú)大, 這樣會(huì)引發(fā)強(qiáng)烈的馬太效應(yīng). 店鋪去重不能使用如上的方法進(jìn)行. 因?yàn)樯厦娴姆椒ǖ闹饕罁?jù)是文本相似, 在結(jié)果都相關(guān)的前提下, 進(jìn)行適當(dāng)?shù)娜∩? 但是店鋪去重不是這樣的特性.
設(shè)想一下, 如果我們根據(jù)店鋪是否相同, 把同一店鋪的商品分到主庫和從庫中, 如下圖所示.
A和B代表不同的店鋪.
在搜索香蕉的時(shí)候, 的確可以控制A店鋪結(jié)果的數(shù)量, 但是在搜索"梨"的時(shí)候就錯(cuò)誤的吧B店鋪的梨排在前面了(假設(shè)A:梨比B:梨靜態(tài)分高).
實(shí)際上想達(dá)到店鋪去重的效果通過分桶搜索是很容易做的事情. 我們假設(shè)每頁搜索20個(gè)結(jié)果, 我們把索引庫分成4個(gè)桶, 每個(gè)商品對(duì)桶數(shù)取模得到所在桶的編號(hào). 這樣可以保證同一店鋪的商品僅在一個(gè)桶里面.搜索的過程每個(gè)桶平均分?jǐn)偹阉魅蝿?wù)的25%, 并根據(jù)靜態(tài)分合并成一頁的結(jié)果. 這樣同一保證結(jié)果的相對(duì)順序, 又達(dá)到了店鋪去重的目的.
如上圖所示, 搜索"香蕉", 雖然A店鋪有10個(gè)滿足需求的結(jié)果, 但是每頁搜索醉倒只有5個(gè)結(jié)果可以展示.
query分析與Query改寫技術(shù)
上面介紹了幾個(gè)建立索引過程中幾項(xiàng)技術(shù), 檢索過程中的關(guān)鍵技術(shù)有很多. 其中最著名的是query分析技術(shù). 我們使用的query分析技術(shù)主要包括核心詞識(shí)別, 同義詞拓展, 品牌詞識(shí)別等等. query分析技術(shù)大部分都是NLP研究范圍, 本文就不詳細(xì)闡述很多理論知識(shí). 我們重點(diǎn)介紹同義詞拓展技術(shù). 這個(gè)技術(shù)一般都需要根據(jù)自己的商品和和用戶日志特定訓(xùn)練, 無法像分詞技術(shù)和品牌詞識(shí)別一樣有標(biāo)準(zhǔn)的庫可以適用.
同義詞拓展一般是通過分析用戶session日志獲取. 如果一個(gè)用戶輸入"蘋果手機(jī)"沒有得到想要的結(jié)果, 他接著輸入"iphone", 我們?cè)?蘋果手機(jī)"和"iphone"之間創(chuàng)建一個(gè)轉(zhuǎn)移關(guān)系. 基于統(tǒng)計(jì), 我們可以把用戶query創(chuàng)建一個(gè)相互聯(lián)系的權(quán)重圖.
用戶輸入query "蘋果手機(jī)", 根據(jù)query分析, "蘋果手機(jī)"有 "iphone"0.8, "iphone 6"0.5 兩個(gè)同義詞. 0.8和0.5分別表示同義的程度. 我們想要"蘋果手機(jī)", "iphone", "iphone 6" 3個(gè)query同時(shí)輸入, 并且按照同義的程度對(duì)不同的query賦予不同的權(quán)重. ElasticSearch提供的BoostingQuery可以支持這個(gè)需求. 參考: https://www.elastic.co/guide/en/elasticsearch/guide/current/boostingquery_clauses.html
原始query:
{
"query" {
"match": {
"query":"蘋果手機(jī)"
}
}
}
改寫后的Query
{
"query": {
"should": [
{ "match": {
"content": {
"query": "蘋果手機(jī)",
"boost": 10
}
}},
{ "match": {
"content": {
"query": "iphone",
"boost": 8
}
}},
{ "match": {
"content": {
"query": "iphone6",
"boost": 5
}
}}
]
}
}
其他比如核心詞識(shí)別, 歧義詞糾正等方法差不多, 本文不做詳細(xì)闡述.
其他
商業(yè)電商搜索算法另外兩個(gè)重要技術(shù), 一個(gè)是類目體系建立和應(yīng)用,另一個(gè)是個(gè)性化技術(shù). 這個(gè)兩項(xiàng)技術(shù)我們還處在探索階段. 類目體系我們主要使用機(jī)器學(xué)習(xí)的方法進(jìn)行訓(xùn)練, 個(gè)性化主要通過用戶畫像進(jìn)行Query改寫來實(shí)現(xiàn). 等我們上線有效果在與大家分享.
小結(jié)
搜索算法是一個(gè)非常值得一個(gè)電商產(chǎn)品持續(xù)投入的技術(shù). 一方面我們技術(shù)人員要有良好的技術(shù)背景, 可以借鑒很多成熟的技術(shù), 避免重復(fù)造輪子; 另一方面, 每個(gè)產(chǎn)品的搜索都有自身的特點(diǎn), 需要深入研究產(chǎn)品的特性給出合理的解決方案. 本文給出的案例都具有代表性, 靈活的運(yùn)用搜索的各方面的技術(shù). 另外, 商業(yè)搜索非常看重投入產(chǎn)出比, 我們也需要在眾多方案中尋找捷徑. 比如我們?cè)谧鲱惸矿w系時(shí)候, 沒有投入大量的人力資源用于標(biāo)注數(shù)據(jù), 而是通過爬蟲爬取其他電商的數(shù)據(jù)進(jìn)行參考, 從而節(jié)省了80%的人力資源. 由于筆者能力有限, 文中的方案不保證是問題的最優(yōu)解, 如果有指正, 請(qǐng)聯(lián)系筆者(hongbin@youzan.com).