久久精品精选,精品九九视频,www久久只有这里有精品,亚洲熟女乱色综合一区
    分享

    我常說的詞典匹配到底怎么做

     520jefferson 2021-11-01

    前言:背景1是應(yīng)霍華德大佬之邀,背景2是這東西我確實(shí)聊的最多卻其實(shí)沒聊清楚這里的細(xì)節(jié),我的鍋,所以這篇文章無論如何都得寫了。

    我已經(jīng)有好多詞都聊過分類、NER甚至匹配問題都可以用詞典來做了,問題是詞典到底怎么做,詞典匹配是怎么做的,具體實(shí)現(xiàn)方法是什么,我卻從來沒聊過,這次給大家一次性說清楚,帶上核心代碼,從而讓大家更好理解和使用。

    詞典匹配的背景和場景

    詞典匹配是NLP領(lǐng)域在工業(yè)界中非常常見的技術(shù),尤其在互聯(lián)網(wǎng)領(lǐng)域,主要原因是詞典這個東西經(jīng)常比標(biāo)注樣本更易得,尤其是搜索領(lǐng)域,一方面很多數(shù)據(jù)資源其實(shí)就存在數(shù)據(jù)庫里,直接拿來清晰就能用,另一方面用戶query等數(shù)據(jù)也多容易挖掘,因此詞典匹配成為這個領(lǐng)域落地非常重要的方式,從在線流量視角,絕大部分的流量都是走的詞典匹配,尤其是高頻、簡單的問題,相比之下模型只是個泛化、兜底的功能,加上詞典匹配所特有的靈活性、可控性、高準(zhǔn)確性、高性能等優(yōu)點(diǎn),詞典匹配絕對是一個實(shí)用且必備的能力。

    詞典匹配的實(shí)現(xiàn)思路

    要做技術(shù)設(shè)計,肯定是要理解需求。

    功能上,給定一批詞典,然后給一個用戶query,找出query中包含的在詞典內(nèi)的詞,例如詞典內(nèi)有“故宮”,則對用戶query“故宮怎么走”,那要把這個“故宮”給匹配出來,復(fù)雜點(diǎn)的還要給出起點(diǎn)終點(diǎn)、正式詞“故宮博物院”等,這就是最基本的功能。其實(shí)這個就是一個NER功能,所以最直接的,詞典匹配就是一個NER任務(wù)。

    但是,作為算法工程師,肯定還要考慮準(zhǔn)確性和性能的問題,尤其是性能問題,顯然,詞典是可更新的,可大可小的,小到幾個詞(例如停止詞),大到成千上萬甚至更多,例如人名地名之類的,可枚舉但是數(shù)量非常大,一個一個在詞典里面找,那復(fù)雜度可就非常高了,尤其是當(dāng)詞典非常大的時候,逐字逐詞匹配肯定不現(xiàn)實(shí)。這里非常考驗(yàn)大家解決數(shù)據(jù)結(jié)構(gòu)、復(fù)雜度問題的功力(刷算法題是確認(rèn)有用的!)。

    直接說結(jié)論,使用的是最大逆向匹配方法,詞典的存儲則用的dict格式(數(shù)據(jù)結(jié)構(gòu)上,技術(shù)允許的話用trie樹最優(yōu),hashmap也是沒問題的),在python里面就是用dict了(據(jù)說內(nèi)部key的存儲使用的trie,不確定,有知道的大佬出來聊聊)。

    再展開沒啥必要,上代碼聊。

    代碼

    整塊核心代碼不到50行,非常舒服。

    這里我把整個詞典匹配的工具寫成一個類(應(yīng)該是工程的基本操作了),主要兩個成員函數(shù),初始化和預(yù)測。

    初始化

    初始化其實(shí)很簡單,就是加載詞典。首先來看我的詞典是一個什么結(jié)構(gòu),樣例是這樣的:

    五哈VarietyShow+++哈哈哈哈哈
    哈哈哈哈哈VarietyShow+++哈哈哈哈哈
    故宮Attraction+++故宮博物院
    南京City+++南京市
    南京博物館Attraction+++南京博物館
    斗羅大陸Novel+++斗羅大陸
    斗羅大陸Carton+++斗羅大陸

    總的來說兩列,用'\t'分隔,第一列是可匹配的詞,第二列是對應(yīng)詞匯內(nèi)部的信息,后續(xù)定義為'slot_info'這里定義是兩列,用'+++'分隔(linux環(huán)境一般用'\001'這種人為輸入幾乎不可能看見的詞匯),第一列是詞匯類型或者叫做槽位名,第二列該詞語正式的名字,后續(xù)可以用來做精確查詢。

    init部分就是加載詞典了,來看看代碼:

    def __init__(self, dict_path):
        slot_dict = {}
        with open(dict_path, encoding='utf8') as f:
            for line in f:
                ll = line.strip().split('\t')
                if len(ll) != 2:
                    continue
                slot_info = ll[1].split('+++')
                if len(slot_info) != 2:
                    continue
                if ll[0] in slot_dict:
                    slot_dict[ll[0]].append({'slot_name': slot_info[0], 'slot_value': slot_info[1]})
                else:
                    slot_dict[ll[0]] = [{'slot_name': slot_info[0], 'slot_value': slot_info[1]}]
        self.slot_dict = slot_dict

    這里讀取后,我們把第一列,也就是待匹配的詞作為dict的key,而slot_info則也是通過dict的形式存儲,但這里的value用向量形式,然后可以存多個slot_info,因?yàn)橐粋€詞可能對應(yīng)很多種不同的實(shí)體,例如“斗羅大陸”既是小說又是動漫,那這里當(dāng)然是要存兩個元素的。另外為了保護(hù)詞典的合法性,避免出現(xiàn)異常數(shù)據(jù)而報錯,所以肯定是要有一些約束過濾的。

    這樣加載好的詞典就是這樣的:

    {
     '五哈': [{
      'slot_name': 'VarietyShow',
      'slot_value': '哈哈哈哈哈'
     }],
     '哈哈哈哈哈': [{
      'slot_name': 'VarietyShow',
      'slot_value': '哈哈哈哈哈'
     }],
     '故宮': [{
      'slot_name': 'Attraction',
      'slot_value': '故宮博物院'
     }],
     '南京': [{
      'slot_name': 'City',
      'slot_value': '南京市'
     }],
     '南京博物館': [{
      'slot_name': 'Attraction',
      'slot_value': '南京博物館'
     }],
     '斗羅大陸': [{
      'slot_name': 'Novel',
      'slot_value': '斗羅大陸'
     }, {
      'slot_name': 'Carton',
      'slot_value': '斗羅大陸'
     }]
    }

    值得注意的是,在預(yù)測階段,無論如何,我們都是要看query的局部內(nèi)容是否在詞典里的,所以一定會出現(xiàn)類似if a in slot_dict的語句,python這么多原生數(shù)據(jù)結(jié)構(gòu)中,只有dict和set兩種類型的運(yùn)行速度最快(且不受到數(shù)據(jù)結(jié)構(gòu)內(nèi)部存儲的數(shù)據(jù)的大小影響),而set由只能存詞不能存slot_info,所以我們就選擇了dict,這是詞典匹配之所以能高性能的核心,這背后是對數(shù)據(jù)結(jié)構(gòu),對python這門語言原生的東西的理解。

    預(yù)測部分

    先上代碼再來聊。

    def predict(self, query):
        query_len = len(query)
        idx = 0
        idy = query_len
        slot_get = []
        tmp_slot_get_len = 0
        while idy > 0:
            while idx < idy:
                if query[idx:idy] in self.slot_dict:
                    for item in self.slot_dict[query[idx:idy]]:
                        slot_get.append({'slot_word': query[idx:idy],
                                            'slot_name': item['slot_name'],
                                            'slot_value':item['slot_value'],
                                            'slot_start':idx,
                                            'slot_end':idy
                                            })
                    break
                idx = idx + 1
            if len(slot_get) != tmp_slot_get_len:
                idy = idx
                idx = 0
                tmp_slot_get_len = len(slot_get)
            else:
                idx = 0
                idy = idy - 1
        return slot_get

    最大逆向匹配,就是從后面開始,從最大開始逐步縮小范圍,逐個匹配,查看每個局部是否在詞典內(nèi)存在,來看一個例子吧,query是“我想去南京的南京博物館”,來看匹配查詢的過程:

    我想去南京的南京博物館
    想去南京的南京博物館
    去南京的南京博物館
    南京的南京博物館
    京的南京博物館
    的南京博物館
    南京博物館 -- 識別到南京博物館-Attraction實(shí)體。
    我想去南京的
    想去南京的
    去南京的
    南京的
    京的

    我想去南京
    想去南京
    去南京
    南京 -- 識別到南京-City實(shí)體
    我想去
    想去

    我想


    最大逆向匹配的一個假設(shè)是只取最長的實(shí)體,所以南京博物館出來了而這里的南京沒有單獨(dú)出來,大家可以理解到這里的匹配流程。

    另外一個細(xì)節(jié)是在匹配到詞典后,直接就把需要輸出結(jié)果的結(jié)構(gòu)就整理好了,完成遍歷后就可以直接給出答案,還是“我想去南京的南京博物館”,這里提取的結(jié)果是這樣的:

    [{
    'slot_word': '南京博物館',
    'slot_name': 'Attraction',
    'slot_value': '南京博物館',
    'slot_start': 6,
    'slot_end': 11
    }, {
    'slot_word': '南京',
    'slot_name': 'City',
    'slot_value': '南京市',
    'slot_start': 3,
    'slot_end': 5
    }]

    小結(jié)

    坑終究是填上了,詞典匹配整個組件代碼也不復(fù)雜,結(jié)構(gòu)上也比較簡單(不排除以后你還要做一些別的優(yōu)化,例如熱更新之類的),只是比較考慮自己寫代碼的功底。后續(xù),有了這個工具,詞典匹配做起來后,就能cover更多的問題,尤其是簡單、高頻的問題。

    再留個坑,詞典具體可以怎么來,怎么挖掘,找個時間詳細(xì)聊聊~

    最后送上完整版代碼,大家可以根據(jù)自己的需求進(jìn)行修改。

    # 使用最大逆向匹配進(jìn)行提槽

    class DictSlot:
        def __init__(self, dict_path):
            slot_dict = {}
            with open(dict_path, encoding='utf8') as f:
                for line in f:
                    ll = line.strip().split('\t')
                    if len(ll) != 2:
                        continue
                    slot_info = ll[1].split('+++')
                    if len(slot_info) != 2:
                        continue
                    if ll[0] in slot_dict:
                        slot_dict[ll[0]].append({'slot_name': slot_info[0], 'slot_value': slot_info[1]})
                    else:
                        slot_dict[ll[0]] = [{'slot_name': slot_info[0], 'slot_value': slot_info[1]}]
            self.slot_dict = slot_dict
        
        def predict(self, query):
            query_len = len(query)
            idx = 0
            idy = query_len
            slot_get = []
            tmp_slot_get_len = 0
            while idy > 0:
                while idx < idy:
                    if query[idx:idy] in self.slot_dict:
                        for item in self.slot_dict[query[idx:idy]]:
                            slot_get.append({'slot_word': query[idx:idy],
                                                'slot_name': item['slot_name'],
                                                'slot_value':item['slot_value'],
                                                'slot_start':idx,
                                                'slot_end':idy
                                                })
                        break
                    idx = idx + 1
                if len(slot_get) != tmp_slot_get_len:
                    idy = idx
                    idx = 0
                    tmp_slot_get_len = len(slot_get)
                else:
                    idx = 0
                    idy = idy - 1
            return slot_get

    if __name__ == '__main__':
        dict_sloter = DictSlot('slot_dict.txt')
        print(dict_sloter.predict('我想去南京的南京博物館'))
        print(dict_sloter.predict('我想看斗羅大陸'))
        print(dict_sloter.slot_dict)

      本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報。
      轉(zhuǎn)藏 分享 獻(xiàn)花(0

      0條評論

      發(fā)表

      請遵守用戶 評論公約

      類似文章 更多

      主站蜘蛛池模板: 精品人无码一区二区三区| 国产成人AV在线免播放观看新| 伊人成伊人成综合网222| 一二三四免费中文字幕| www久久只有这里有精品| 麻豆国产传媒精品视频| 夜色爽爽影院18禁妓女影院| 麻豆亚洲精品一区二区| 无码射肉在线播放视频| 我国产码在线观看av哈哈哈网站| 亚洲欧美日产综合在线网| 性一交一乱一伦| 无遮挡免费高清羞羞视频| 欧美午夜片欧美片在线观看| 色天天天综合网色天天| 亚洲av无码精品色午夜蛋壳| 欧美亚洲日本国产综合在线美利坚| 桃子视频在线播放WWW| 人妻激情乱人伦视频| 国产精品中文字幕观看| 特级毛片在线大全免费播放| 香港日本三级亚洲三级| 久久久久国产精品免费免费搜索| 青青国产揄拍视频| 国产精品国产三级国产AV中文| 成人无码午夜在线观看| 国产成人午夜福利在线播放| 九九热在线视频观看这里只有精品| 中文字幕国产精品自拍| 呦系列视频一区二区三区| 四虎影视国产精品永久在线| 中文字幕日韩有码国产| 国产又爽又黄无码无遮挡在线观看 | 精品久久久久久无码人妻热| 国产精品午夜剧场免费观看| 又黄又爽又无遮挡免费的网站| 国产色综合久久无码有码| 日本高清在线观看WWW色| 久久精品中文闷骚内射| 亚洲VA中文字幕无码久久不卡| 爱性久久久久久久久|