有贊搜索引擎實(shí)踐(算法篇)

注:轉(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é)果

image

下面幾章闡述幾個(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ù).

  1. 首先對(duì)各個(gè)指標(biāo)取log. log的導(dǎo)數(shù)是一個(gè)減函數(shù), 表示為了獲得更好的分?jǐn)?shù)需要花費(fèi)越來越多的代價(jià).

  2. 標(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ù)如下:
image

這種方法非常不穩(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ù)變得非常平滑.
image

(圖一: min-max歸一化)
image

(圖二: log歸一化)
image

(圖三: 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分別建庫.

image

區(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ù)店鋪是否相同, 把同一店鋪的商品分到主庫和從庫中, 如下圖所示.

image

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è)桶里面.
image

搜索的過程每個(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)重圖.


image

用戶輸入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).

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末求晶,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖秩仆,帶你破解...
    沈念sama閱讀 216,692評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件混滔,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡鸠姨,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門掏导,熙熙樓的掌柜王于貴愁眉苦臉地迎上來享怀,“玉大人,你說我怎么就攤上這事趟咆√泶桑” “怎么了?”我有些...
    開封第一講書人閱讀 162,995評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵值纱,是天一觀的道長鳞贷。 經(jīng)常有香客問我,道長虐唠,這世上最難降的妖魔是什么搀愧? 我笑而不...
    開封第一講書人閱讀 58,223評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮疆偿,結(jié)果婚禮上咱筛,老公的妹妹穿的比我還像新娘。我一直安慰自己杆故,他們只是感情好桥嗤,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,245評(píng)論 6 388
  • 文/花漫 我一把揭開白布撬码。 她就那樣靜靜地躺著偶洋,像睡著了一般幽邓。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上撤蟆,一...
    開封第一講書人閱讀 51,208評(píng)論 1 299
  • 那天奕塑,我揣著相機(jī)與錄音,去河邊找鬼家肯。 笑死龄砰,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的息楔。 我是一名探鬼主播寝贡,決...
    沈念sama閱讀 40,091評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼扒披,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼值依!你這毒婦竟也來了圃泡?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,929評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤愿险,失蹤者是張志新(化名)和其女友劉穎颇蜡,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體辆亏,經(jīng)...
    沈念sama閱讀 45,346評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡风秤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,570評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了扮叨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片缤弦。...
    茶點(diǎn)故事閱讀 39,739評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖彻磁,靈堂內(nèi)的尸體忽然破棺而出碍沐,到底是詐尸還是另有隱情,我是刑警寧澤衷蜓,帶...
    沈念sama閱讀 35,437評(píng)論 5 344
  • 正文 年R本政府宣布累提,位于F島的核電站,受9級(jí)特大地震影響磁浇,放射性物質(zhì)發(fā)生泄漏斋陪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,037評(píng)論 3 326
  • 文/蒙蒙 一置吓、第九天 我趴在偏房一處隱蔽的房頂上張望无虚。 院中可真熱鬧,春花似錦衍锚、人聲如沸友题。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽咆爽。三九已至,卻和暖如春置森,著一層夾襖步出監(jiān)牢的瞬間斗埂,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評(píng)論 1 269
  • 我被黑心中介騙來泰國打工凫海, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留呛凶,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,760評(píng)論 2 369
  • 正文 我出身青樓行贪,卻偏偏與公主長得像漾稀,于是被迫代替她去往敵國和親模闲。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,647評(píng)論 2 354