NewGeoCoding:一種外賣場景下的GeoCoding算法

地理編碼(GeoCoding)是地圖服務中一項重要的功能,它提供了將詳細的結構化文本地址轉換爲經緯度座標的能力,比如:

  • 北京市朝陽區阜通東大街6號 轉換後經緯度:116.480881,39.989410

在外賣場景下,GeoCoding將用戶填寫的送餐地址文本轉換成經緯度,作爲騎手送餐的目標經緯度,所以GeoCoding算法準確性的高低直接影響到騎手送達效率。目前訂單地址文檔到經緯度的映射是通過高德GeoCoding接口實現的,由於高德GeoCoding接口提供的是一種通用的地理編碼功能,對外賣場景下訂單的識別效果很多情況下不夠完美。體現在以下幾個方面:

  1. Poi庫中小區的樓棟建築覆蓋不全
    高德的使用場景更多是從一個poi導航到另一個poi,而對於一個小區內部的樓棟覆蓋率並不是最重要的考量,導致很多小區內部的樓棟缺失。而外賣場景下,用戶填寫的下單地址很多是小區裏面的某個樓棟,這種情況下使用高德GC服務進行經緯度反解效果不夠理想。例如:


  2. 新樓棟發現和更新比較慢
    對於poi庫中缺失的小區內部樓棟,高德更新頻率比較慢,相比之下只要該樓棟有用戶下單,我們就可以發現潛在的缺失樓棟信息,並及時補充到我們的poi庫中

  3. 地址匹配算法的效果不夠精準
    外賣場景的地址匹配單純從文本相似度進行計算是不夠的,還需要同時加入對多級地址實體(Entity)間相似度的判別,比如下面三個地址文本:
    領秀慧谷13號樓
    領秀慧谷133號樓
    領秀慧谷9號樓
    如果單純從文本相似度來看,“領秀慧谷13號樓”和“領秀慧谷133號樓”相似度應該高於和“領秀慧谷9號樓”的相似度,但是如果從地址實體的角度,13號樓和9號樓的位置應該更接近,而不是133號樓,這樣來看“領秀慧谷13號樓”和“領秀慧谷9號樓”相似度應該更高。

  4. 對兩段地址支持不夠好
    在餓了麼app上用戶下單時,採用的是兩段地址的格式,也就是用戶先選擇一個poi,然後自己手填一部分詳細地址信息。這兩段地址往往是簡單的首尾拼接起來,然後進行GeoCoding。如果這兩段地址存在互相矛盾,重複,順序顛倒的情況,高德GC反解的效果也會大打折扣甚至錯誤。


基於以上的現狀,我們設計並實現了一套針對外賣場景的地址反解算法,我們稱爲NGC(NewGeoCoding)。NGC的核心功能是將用戶填寫的兩段地址綁定到poi庫中匹配程度最高的一條poi記錄上,然後使用該poi的經緯度作爲地址反解的經緯度。對該算法有如下的要求:

  1. 能夠準確的理解用戶地址中對道路、門牌號、小區名稱、片區、樓棟、單元、樓層、房間號的表述,從文本相似度+實體相似度兩個維度計算相似度
  2. 能夠處理兩段地址中存在的前後矛盾、重複、部分地址實體缺失、實體位置顛倒等錯誤,有較高的魯棒性
  3. 對於綁定失敗的地址,能夠返回地址中表述的小區+樓棟號,作爲新poi挖掘的候選數據
  4. 要能處理重名poi的匹配
  5. 匹配準確度要足夠高,同時保持較低的耗時

NGC的整體流程包含三個主要步驟:兩段地址合併,獲取周邊相似poi,地址和周邊poi匹配:


兩段地址合併

用戶在餓了麼下單時地址是兩段式地址,理想情況下兩段地址應該清晰準確,互爲補充,但由於第二段用戶手動填寫的地址部分是不受限制的,所以用戶可以隨意填寫,導致了一系列的地址問題,包括這兩段地址存在互相矛盾,重複,對同一個小區或樓棟前後採用不同的表述,地址中實體成分順序顛倒混亂等情況。所以首先要將兩段地址合併成一段完整、合理、不存在歧義和矛盾、不包含重複內容、條理清晰的地址,見下面的例子。


下文中我們使用/表示兩段文本地址,如“餓了麼/建材城中路27號”,代表第一段地址是“餓了麼”,第二段地址是“建材城中路27號”。

兩段地址內容重複是兩段地址合併中首先需要處理的問題,內容重複包含幾個層面的含義:

  1. 文字層面完全重複,如“安河家園六裏/北京市海淀區安河家園六裏4號樓”,第二段地址完全包含了第一段地址“安河家園六裏”。這是一種比較好解決的問題,通過字符串包含判斷就可以簡單處理。
  2. 文字層面分段完全重複,如“蘇莊一里13號樓/蘇莊一里東區13號樓”,第二段地址雖然沒有完全連續包含第一段地址,但是如果把第一段地址切分成“蘇莊一里”“13號樓”兩部分,就分別被第二段地址包含了,這種情況我們稱爲分段完全包含,也屬於重複的情況,這種情況採用分段匹配算法可以解決。
  3. 包含標點和大小寫的重複,如“燕保·阜盛家園(N1門) 2號樓/燕保阜盛家園n1門二號樓”,直接進行字符串比較並不包含,但是去掉標點,大寫轉小寫,並將漢字的數字轉換成阿拉伯數字後,就可以判斷出重複的情況了。
  4. 樓棟重複,如“領秀慧谷38號樓/領秀慧谷38棟”,兩段地址的樓棟號後綴不同,但實際表述的是同一個樓棟號,這種情況我們先對樓棟後綴歸一化成相同的形式,在進行重複性判斷。
  5. 地址別名重複,如“金科城二樓/金科世界城二樓”,這裏“金科城二樓”是“金科世界城二樓”的別名,所以它們是等價的。這種情況需要預生成地址別名數據庫,將二者對應起來。目前我們採用拼音Tri-Gram方法生成了幾十萬條地址別名數據可用於別名處理。
  6. 地址實體部分重複,如“惠康嘉園6區/6區2號樓二單元604”,兩段地址中都包含了“6區”這個小區片區信息,這種情況我們利用NER技術首先對兩段地址進行實體解析,然後對相同類型的實體判斷是否重複,做去重處理。我們支持的NER類型如下:


經過上面的預處理後,我們完成了對重複內容的檢測,接下來就是怎麼拼接兩段地址,我們基於觀察總結出兩段文本的四種拼接方式:承接式、包含式、描述式和插入式
承接式地址指的是兩段地址存在地址實體結構的由高到低的關係,比如“管莊西里24號樓/2單元302”,兩段地址從前往後依次是“小區/樓棟號/單元號/房間號”,這種情況只需要把兩段地址順序拼接就可以了。
包含式地址指的是兩段地址存在一段包含另一段地址的情況,這種情況直接使用較長地址即可。
描述式地址指的是第一段地址是一個poi,第二段地址是對這個poi位置的描述,比如“巫山烤魚/昌平區長江街一號院奇點中心3樓”,對於這種地址需要交換兩段地址的位置,第二段地址在前,第一段地址在後,組合成“昌平區長江街一號院奇點中心3樓巫山烤魚”。
插入式地址指的是需要將第一段地址插入第二段地址的某個位置,比如“信達大廈/北京西路1399號3樓”,需要將“信達大廈”插入到“北京西路1399號”和“3樓”中間,構成“北京西路1399號信達大廈3樓”。
這樣,我們完成了兩段地址合併成一段地址的處理。

獲取周邊相似poi

獲取周邊相似poi是基於用戶訂單的經緯度和地址文本,搜索其周圍相似度最高的top N條poi數據,是一個粗篩的過程。拿到這N條記錄後,後續再進行更爲精確的相似度打分。
我們首先計算當前訂單經緯度所在的精度爲7的GeoHash網格,連同周邊八個鄰居網格,共9個網格。將poi數據預處理後存入ElasticSearch,首先過濾出9個GeoHash網格的poi數據,然後利用ES的Match方法獲取其中top N的記錄,進行後續的poi綁定。如果poi綁定失敗了,我們會擴大網格範圍到精度爲6繼續搜索,如果還是綁定失敗了繼續擴大到整個城市範圍,返回最終匹配的結果,或者null。所以搜索的過程是一個逐層擴大的過程,這樣可以提高綁定的精度,減少同城同名poi帶來的影響。

地址和周邊poi相似度計算

現在我們已經有了合併後的訂單地址文本,同時拿到了周邊相似度最高的N條poi記錄,接下來需要計算該地址文本和哪條poi記錄綁定最合適。
該過程分兩步,第一步是衝訂單地址文本中抽取可能的poi名稱,對於一個訂單會從不同的維度抽取出多個poi名稱,比如“朝陽門內大街15號富力小區91號樓2單元401室”,可以抽取出如下的候選poi名稱:

朝陽門內大街15號
朝陽門內大街15號富力小區
富力小區91號樓
富力小區91號樓2單元
富力小區

我們拿這些候選poi名稱分別和周邊N條poi進行匹配,計算出得分最高的一條poi地址作爲最終綁定的結果。打分的過程主要基於兩方面,一方面看候選poi和周邊poi的名稱相似度,相似度越高得分越高;另一方面看候選poi本身所包含的實體的豐富程度,細節越豐富,得分越高。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章