算法筆記(VI) 模式匹配

題目有點大了,如果是命題作文的話,絕對是吃力不討好的活。說得有點形而上學,但正如愛情公寓中的經典臺詞:“話粗理不粗”… 那麼引用滑稽劇團Python的開場詞吧:請看一場前所未有的表演!  

    計算機科學中處處有模式匹配,不僅從計算理論以及到實際工程應用,模式匹配都是一項重要的研究點和有用技術。簡單的來說模式匹配,是在目標串 中尋找 模式串的過程,因此也可以將模式匹配的問題轉化成爲搜索或者模擬問題去理解(其實程序執行的過程就可以理解成爲一個模式匹配的問題)。同時,我們也將看到在搜索問題和模式匹配上有所共通點。

從模式匹配的兩個對象來看,模式匹配技術存在兩種方法論或者思維模式:

模式(1):模式串驅動:在當前關注:匹配進行到(某個)模式串的那個位置?

模式(2):目標串驅動:在當前關注:匹配進行到目標串的那個位置?

看上去這兩種只不過是兩種說法而已,何以成爲兩種方法論呢?這兩種模式或者方法論確實是事物的對立面,充斥幾乎整個模式匹配領域,包括字符串匹配、正則式匹配、語法解析(語法規則匹配)等等;從各個領域的算法中不難找出兩種方法論的實踐。

下面將漫步常見的模式匹配技術,從中發掘出它們的異同。

 字符串匹配

字符串的精確匹配常用於入侵檢測系統、殺毒軟件等等場景,可以說應用非常廣泛。衆所周知,字符串匹配有暴力匹配、BM以及KMP方案;一般工業上使用最多的是暴力匹配,如C/C++內置的庫函數;BM算法,如snort入侵檢測系統;KMP在學術界或者算法理論界受關注,但是工業上並沒有受到青睞(也許在DNA序列匹配上有應用),甚至一些追求實用的算法書都不介紹KMP。其中原因我們將在後文進行詳細解釋。

字符串匹配的形式描述如下:

find_match(object ,pattern):

if pattern[0]== object[0]:

    find_match(object[1:end] ,pattern[1:end])

從上面的形式描述中不難看出,暴力匹配可以理解成爲一種遞歸實現;當然從另一面,我們也可以使用自動機(狀態與轉移)去描述它。其形式描述是:

new_state =trans(current_state ,input_char); 即 

圖片

 從計算理論的角度來看兩者是等同的。上述就是字符串匹配的最樸素的兩個模型,只要明白這兩個基本模型,我們就可以瞭解字符串匹配的奧妙了。其中遞歸描述比較傾向於模式串驅動的方法論,而自動機更傾向於目標串驅動的方案。後文我們會逐步的展開說明爲什麼會有這種結論。

暴力匹配的原理想必不用解釋,即逐字節查看m-前綴是否是模式串。這種方法最爲樸素,大家都知道它的最壞情況下複雜度爲O(m*n);其實,在工程實踐中,病態的案例並不多見,所以並不需要過於吹毛求疵,一般場景使用是沒問題的。算法實現簡單正確,一目瞭然。這也是工業和學術界的不同點,學術界往往追求方法的精確性和普適性,而工業界往往願意捨棄一部分精確性或者普適性,而追求在大部分場景的高效和可維護性,甚至願意將不同的方法組合適用(當然學術界的不少人也樂衷此道,尤甚灌水)。

如果我們對效率有要求的話,必須對暴力匹配進行改進。最簡單的改進是基於並行的,即並行技術,由此產生了shift-or等算法。回顧暴力匹配的過程,我們發現之所以造成暴力匹配做出很多冗餘操作是因爲目標串中的字符被反覆的比較:一旦發現不匹配,暴力匹配就會退回開始的點步進一個字節再匹配。《柔性字符串匹配》一書就提到,暴力匹配的匹配模型是一個非確定自動機NFA)的實現。因爲我們總是爲了保證不漏掉可能的匹配而反覆的調回模式串的起始位置。如果有一種並行技術可以一起執行多路過程就可以加快速度了。shift-or即利用“位”標記來實現並行技術,“位”來記錄每一條“執行路徑”的狀態,每一個“1”的位置表示某條路匹配到模式串的第幾個位置了。因爲字符串匹配並沒有副作用,因此並行化不用擔心干擾的問題。靠着和計算機本身位並行的特點,shift-or可以達到KMP兩倍的執行速度;不過shift-or的位數目受制於機器字長,不能支持太多字符的模式串。

另外,順便提一個簡單的優化技巧利用機器字長一次可以比較多個字符,最簡單的實現莫過於將4個字符(32個字長)做爲一個unsigned處理了,這種簡單的“位並行”以我的經驗可以提高1/4的速度。當然,相比於以下的算法,都是雕蟲小技了。

如果要再次提高暴力匹配的執行速度則需要考慮其他的思想了,BMKMP由此誕生。回顧暴力匹配的之所以複雜度高,原因在於我們假設我們對模式串和目標串是一無所知的,造成很多冗餘的操作。我們可以事先對模式串進行“學習”,瞭解其特徵,並用一些額外的空間存儲這些特徵。對於目標串,因爲我們使用的場景一般對目標串沒有先驗知識的,另外對目標串學習耗費的開銷過多,鑑於這兩個原因,我們不會對目標串進行預處理,而只對模式串進行學習。

    BM算法:

    BM算法可以理解成一種啓發式算法,可以從遞歸模型去講述它的思想。BM的優化思想在於利用啓發式信息去節省匹配的開銷。回想一下find_match函數:

find_match(object ,pattern):

if pattern[0]== object[0]:

    find_match(object[1:end] ,pattern[1:end])

匹配真的是要逐字節的驗證麼?舉個極端的例子如果我們發現當前檢查的object中的字符不屬於 pattern字符中的任何一個,我們就可以下結論,在這個char之前m的所有字符爲起點的串均不可能是patter的匹配起點位置。簡單的說,這是一個剪枝技術如果場景中有很多可以跳躍的情況,那麼BM能夠跳躍很多object中的字節。BM的預處理過程就是構造剪枝用的表,從而跳過很多不可能的“執行路徑”。

    這裏簡單提一點,剪枝操作在實踐中是非常有效的搜索(匹配)優化技術。事實證明,很多剪枝技術能夠去除大部分的狀態空間。不過據lda說,如果一個剪枝操作僅能將原搜索空間降低至10%,那麼這種剪枝是非常低效的,往往還不如其他的優化技術,例如優化代碼和內存使用等程序級技巧。

當然剪枝技術一般在算法書上是不會講解的,它屬於啓發式方法,執行的效率依賴於問題,因此一般出現在《人工智能》的教材中。BM在典型情況下的複雜度爲O(n/m),在病態案例的情況下仍然與暴力匹配的複雜度爲O(m*n),這也是由於其是一種啓發式方法造成的。

KMP算法:

KMP算法本質上是一種有限自動機(DFA)方案,《算法導論》就是先介紹完DFA技術後,再介紹KMP算法,並說明KMPDFA的節省了內存空間的技巧實現,而複雜度級別是等同的:O(n+m)

有限自動機方案則明顯與上面講述的方案有明顯的區別,其將暴力匹配用到的NFA進行確定化(也可以將其理解成爲一種壓縮),將等價的狀態進行合併而得到。經過確定化後,有一些等價的狀態被合併,從而減少冗餘的匹配。簡單的來說,DFA之於NFA是一個空間換時間的技巧。考慮子集族的規模是大於原集合的規模的,DFA的空間開銷爲Osigma*m),其中sigma是字符集的大小。

這裏簡單提一句,我們知道子集族規模應該是集合元素的指數級(冪集),爲什麼用於字符串匹配的DFA的空間開銷只有Osigma*m,而不是O2^m原因在於字符串是有序關係:子集族中有大量的共同後綴的情形,因此空間被節省(壓縮)下來了。後綴數組之於後綴樹也是相似的技術。這一點是字符串所特有的,正則式就沒有這種序關係。

回想一下,暴力匹配的的最大優點就是:沒有使用額外的內存(因爲模式串本身就直接可以拿來當NFA使用)。另外,DFA處理的過程也需要一定的開銷,如果是單次匹配的話,我們根本沒必要去花這個預處理的時間。這兩點也許就是爲什麼C語言庫函數使用暴力匹配的原因了。

先在回過頭來講解KMP算法。KMP算法相對DFA的實現做了一個節省空間上的優化。我們知道模式串中的字符數目是有限的,往往遠遠少於字符集規模。因此我們根本沒必要去構造一個O(sigma*m)空間複雜度的DFA,我們退而求其次,將匹配當前字符失敗都統一合併成一種轉移:失敗(fail)指針。由於合併了本來是針對特定字符構成的轉移,所以內存使用量下去了,但是在匹配的過程中,可能連續的在fail指針上進行跳躍,不過鑑於平攤分析,複雜度是與DFA一致的,均爲O(n)。當然,雖然KMP與DFA複雜度一致,但是還是有一個常數的差別:KMP因爲有失敗轉移,因此目標串中一個字符會反覆比較,但是由於平攤效應,失敗轉移的次數不會超過n,所以準確的說,KMP的複雜度爲O(2*n),而DFA每個字符都意味着一次確定的轉移,因此複雜度是準確的O(n)。實踐也證明,DFA方案要比KMP更快,這就是常數項的差別造成的。

如果KMPDFA的一部分預處理操作轉嫁到運行時根據情況處理的話,那麼我們將在後面的正則式匹配中看到真正的運行時優化技巧。

KMP的預處理過程也可以理解爲一個自匹配(即模式串被其本身的子串所匹配)的過程。KMP確實通過自匹配壓縮了很多的等價狀態。自匹配在壓縮領域應用也非常廣泛,例如LZ77LZ78的壓縮過程也是通過自匹配來完成字符串的壓縮的。

        RK算法:

RK算法與BM一樣也是一種啓發式方法,不過RK算法以hash值作爲啓發信息。依然回顧匹配過程,我們的剪枝操作可以利用字符串的hash值(即數據指紋)來作爲啓發式條件。即兩個字符串的hash值不同,則兩個串一定不同。所以我們首先計算出模式串的hash值,然後不斷的在目標串上計算hash值,如果發現hash值相同,則再進行逐字節的比較驗證。

       由於在目標串上運行hash函數可以通過滾動hash技術來優化,所以複雜度爲On)。當然RK算法的弱點在於hash值存在衝突,往往會造成很多次誤匹配引發的逐字節驗證。一個優化的技巧就是使用多個hash值來驗證,如果多個hash值均相同,則我們就認爲匹配成功了。這種技巧捨棄了正確性,而獲得了效率。在一些不是事關緊要的場合是可以這樣做的,例如暴雪遊戲公司就採用類似的思想,使用3hash值來驗證。因爲使用在遊戲上,所以即使遇到小概率事件,也不會造成重大的損失。

很多書認爲RK算法的應用有限,但其實RK算法的用途是非常的廣泛,不亞於BMKMP。很多混合算法會借鑑RK的思想,消減計算量。

回到主題,考慮現有的字符串匹配算法是否真的實踐了開頭所講的兩種模式:

從上文我們可以看出,不論是暴力匹配、shift-or(並行化執行的暴力匹配)、BMRK(剪枝的暴力匹配)都關注當前我們匹配到模式串的那個位置了。我們也可以說它符合遞歸的形式表達、也可說它們本質上是一種NFA以及有技巧實現的NFA,但是不可忽視它們都是模式串驅動方案,執行的複雜度是依賴於模式串和目標串內容。它們的共同特點就是佔用的空間較少,但是複雜度是不確定的,依賴於問題。

         DFAKMP算法本質上是一種目標串驅動(也可以叫做狀態驅動)的方案,除卻預處理的開銷,其匹配的複雜度是不依賴與模式串和目標串的內容,而只與目標串的長度n有關:無論任何案例的複雜度均穩定在On),但是它們的問題在於內存開銷。儘管由於字符串本身的序關係使得空間開銷不是很大,但是我們將看到DFA方案在後面的正則式匹配領域遇到的困境。

       一個算法的執行複雜度是否依賴於問題也是一個雙刃劍。依賴於問題的優勢在於我們可以針對問題作出優化,劣勢也很明顯:算法的執行複雜度是變化的,難免遇到病態案例,成爲算法的剋星。我們將在後面探討如何解決這種矛盾。

多模式匹配

多模式匹配是字符串匹配的在多個模式串匹配上的延伸。其本身並沒有很獨特的創新,思想均來自字符串匹配。

        WM算法:

       WM算法非常高效,是入侵檢測系統snort默認的字符串匹配算法。WM算法本質上是BM算法在多模式上的延伸和改進。我們知道BM算法之所以有效是因爲剪枝用的表,但是在多模式匹配的情況下,依賴於單個字符構成的剪枝表的剪枝效果大大折扣,因此WM算法使用一塊字符來構造剪枝表,當然這個字符塊的合適大小要依賴於問題了。由於一塊字符代表的值域範圍要遠大於單個字符的值域(0~255),因此WM算法使用一個hash表來作爲剪枝表。WM算法依然繼承了BM的傳統,仍然是一種啓發式方案,但是在典型場景下,其效率是非常的好,要優於AC自動機方案。

        AC自動機:

       AC自動機是KMP算法的延伸。不過其使用的數據結構要複雜一些,相對於KMP的自動機是帶fail指針的序列結構,AC自動機使用了帶fail指針的字典樹(又稱前綴樹,即trie)。原因很簡單,因爲KMP算法應用的場景是單模式串的匹配,AC自動機用於互不相同的模式串匹配,爲了節省內存開銷,所以依據不同模式串的相同前綴關係,構造了trie結構。

      如同KMP算法之於DFA一樣,AC自動機存在高級AC自動機實現。即去除fail指針,形成完整的DFA,稱之爲高級AC自動機。雖然在平攤意義上,AC自動機與高級AC自動機的複雜度是一致的,但高級AC自動機因爲減少fail指針會不斷的跳轉的情況,因此複雜度要少於AC自動機一個常數級別(即常數2,原因參見KMP和DFA的講解),實踐上也證明高級AC自動機是要比AC自動機要快。

      高級AC自動機的問題仍然是內存問題,並且由於多模式的規模大於單模式而變得更加嚴峻。解決辦法是在運行時動態的構建轉移條件,將一些連續的fail指針操作確定化,稱之爲“動態確定化”。動態確定化技術在正則式匹配領域應用更爲廣泛。    

       如果內存空間受限,無法實現高級AC自動機,則有一種不完全的實現方案。方法就是測試在應用場景中那些失敗轉移的頻度較高,而將這些失敗轉移進行確定化。這種實現可以是動態的也可以是靜態的。

        混合算法:

       多模式匹配相對單模式匹配並沒有本質上的創新,充其量多模式算法用到的數據更爲複雜,包括字典樹、hash表、甚至是後綴自動機(相當於增加了轉移的後綴樹,不過利用前面講到的後綴關係進行了空間上的壓縮)等等複雜的數據結構。值得一提的是,在多模式匹配中,不同算法互相借鑑,甚至互相融合,例如在工程上有實踐表明AC自動機和BM算法結合使用可以比WM算法有更好的執行效率。

       在工程上,有很多這種算法之間混搭的案例。因此能夠靈活的應用這些算法思想去優化是一個非常重要的手段。

正則式匹配

正則式匹配引擎分成兩種實現:NFA  DFA 前者可以理解成 模式串驅動方案,後者是目標串驅動方案;前者靈活多變,可由用戶調教,且內存使用量較小,但效率低於後者,不過一旦調教得當往往會超越後者;後者高效穩定,但內存開銷大。

正如《精通正則表達式》一書的比喻:正則式的兩種引擎:NFADFA相當於兩種汽車,前者是手動檔,後者是自動檔。手動擋意味着執行的效率和複雜度要依賴於問題,或者說依賴於用戶怎樣調教。如果用戶的正則式水平高,可以寫出非常優秀高效的正則式,則NFA的引擎會運行的非常快速。

DFA引擎是自動檔的汽車,其在任何情況下都有穩定的速度。但是問題在於其空間開銷較大,而空間問題相對於字符串匹配要嚴峻的多。衆所周知,正則式自身沒有像字符串的序關係,除非正則式退化成爲字符串,否則是沒有一致的前綴和後綴關係的。因此用於正則式的DFA引擎的空間無法通過字符串那樣進行壓縮,是因此是指數級的複雜度。

DFA引擎的內存空間過大缺陷是致命的,而大部分的工程應用對內存要求都是苛刻的。如果內存使用量過大或者不可控,對方案的選取都是致命的。

現在的工程實踐一般選用有技巧的NFA實現。回顧以上我們講到的運行時優化技巧。我們可以通過運行時來講NFA確定化從而模擬DFA的執行過程。NFA的確定化意味着,在匹配的過程中,我們會合並一些等價的狀態並緩存它們,從而避免冗餘的匹配。

NFA動態確定化思路本質上與動態規劃是相同的。動態規劃重要的實現思路就是記憶化:記錄曾經出現過的狀態,從而減少重複的計算量。NFA的確定化思路也是一致的,通過運行時的額外內存開銷(這部分內存開銷是依賴於問題的,可以說是按需增長),迴避了DFA的指數級內存開銷。不過,在工程上,NFA的按需增長內存仍然會給實踐造成風險,因此,一般工程實踐會設置固定的內存作爲狀態緩存,按一定規則去更新緩存,保證固定的內存使用量(通俗的將就是選擇性記憶化)。這種思路在搜索問題中也會出現,即不是完全的保存節點,而是緩存節點。上述的實現技巧也是典型的時間換空間思路,即不惜重複計算的開銷而換取可控的內存使用量。

語法解析

    語法解析是用來解析特定語言的。語法解析器處理的單元不再是字符,而是詞素,語法解析的模式串是語法規則,而目標串是詞素序列;另外,從計算理論的角度來看,語法解析是與上述的正則式匹配(字符串隸屬於正則式)有本質的區別。語法解析的模型是下推自動機,在運行的過程中比正則式匹配的模型NFADFA(兩種是等同的)多了“”這種功能強大的數據結構。之所以必須有棧,是一位語法規則是一種特殊的模式,這種模式可以嵌套其它的語法規則或者自引,一旦出現上述情形,棧就會發揮它的重要功能:記錄現場,以便後期恢復上層語法規則的匹配。

語法解析是一個大的話題,不可能用很少的語言說個明白;在此,我僅從開篇的兩種模式爲角度來概述語法解析的典型方法:

        LL解析方法:

LL解析方法的思路繼承自NFA,適於人類理解,一般的手寫解析器都是基於LL的。如NFA一樣,LL是一種模式串驅動(語法規則)的解析方案,所以在解析(匹配)的過程中,我們可以直接可以說出當前正在展開那種語法表達式。

LL解析方法的優點在於直觀,而缺陷與NFA相同,在於無法避免回溯。爲了方便實現,一般解析對象(語言)語法規則有限而簡單,可以依靠固定數量的詞素來預測,因此簡化了LL解析方法的實現。如果某種LL解析方法可以靠k個詞素可以預測語法表達式,則稱之爲LLK),當然最簡單的實現莫過於LL1)了,多數的教學語言或者手寫編譯器都是LL1)的。

LL的問題在於簡單直觀,易被人類理解,容易手寫實現。其實現有兩種方案:隱式棧實現(遞歸下降方法)和顯式棧實現(即通常教材上的LL)。兩種本質相同,正如其名,遞歸下降是依賴遞歸(靠操作系統維護棧),而顯式棧實現是顯式的維護棧。

LL的缺陷思路通過限制解析對象這一手段解決了,但是回溯問題仍然如影隨行,因爲有一些語法有一些共同的“前綴”,如C++中的對象初始化和對象聲明就是有一樣的“前綴”。爲了解決回溯的問題,LL解析器同樣借鑑了動態規劃(準確的說動態規劃有兩種實現方法:1記憶化+遞歸 2遞推)的思路:記憶化。採用記憶化的遞歸下降解析器稱作林鼠解析器(packrat parser,參見《編程語言實現模式》)。

        LR解析方法:

    在編譯原理教材中,LR一般在LL後面進行講解。原因在於LRDFA一樣不用以被人類所理解(不過非常適用於機器執行)。LR一般無法通過手寫完成,一般是由固定的算法根據固定的規則生成(稱之爲解析器的解析器),但是本質上與我們前面講到的KMP思路並沒有本質的區別:仍然是編譯模式串,只不過此時的模式串是語法規則而已(這似乎不難理解爲什麼Knuth即發明了KMP又發明了LR解析器,道理都是互通的)。

    LR的執行模型比較貼近計算理論的下推自動機的講解方法(即移入規約LL預測展開流程)。本質上LR是一種目標串驅動(或者說狀態驅動)的方案。執行的過程就在於讀入詞素並作出狀態轉移以及維護棧,而狀態並不代表具體一種展開式,往往棧中壓入了大量的字符後,才做一次歸約。

    因爲不用像LL對語法做太多的限制,LR解析方案的應用比LL更爲廣泛。但LR就像DFA一樣,不再與模式串(語法規則)有明顯而直觀的聯繫,因此非常難以理解,所以一般是規定的算法根據語法規則生成的。

    回顧LLLR,是否可以講兩者理解成爲基於棧的NFADFA呢?結論是否定的。計算理論講到儘管NFADFA能力等同,但是基於棧的NFADFA的能力不是等同的,前者的能力更強。這裏的能力與工程實踐上的執行速度不是等同概念,這一點不再展開討論,可參見計算理論。

總結

模式匹配的領域廣泛,例如音頻、圖片、視頻的匹配都是模式匹配問題,而由於上述應用一般帶有人類的主觀性,而採用近似匹配的技術(也就是模糊匹配)。雖然應用不同,但是思想有很多共同之處,例如google的圖片搜索技術即通過類似於RK算法的數據指紋進行匹配的,只不過其數據指紋的計算技術比較特殊。

鑑於本人知識面有限,在此不再探討。回顧前面所講的內容,兩種思路:1模式串驅動方案;2目標串驅動方案 貫穿整個模式匹配領域,其中簡單探討了計算機中程序的優化技術: 並行、記憶化、時空互換、緩存等。從算法的特徵來看,我們可以發現 模式驅動方案的算法的複雜度是不確定的(這也就是NFA模型的問題吧),並依賴於問題,即模式串和目標串的內容;目標驅動方案典型特徵是:複雜度確定,其匹配(不包括預處理開銷)的複雜度只與目標串的長度有關,與模式串和目標串的內容無關。從思路上和運行的特徵看,兩者區別明顯,但兩者就像同一事物的兩面一樣,對立而統一。

就如現實中的時間與空間、數學中的自變量與其函數一樣,計算機科學中也遍佈對立面,模式匹配的兩種思路就是典型的代表。其實從模式匹配出發還可以看到更多有意思的東西,例如程序語言的設計。

衆所周知,程序語言的經典範式有:函數式編程和過程式編程,如Lisp之於C語言、Mathematica之於Matlab一樣。前者利用遞歸實現語言的執行,而後者使用狀態與轉移實現語言的執行。它們也與我們的兩種處理思路有相同之處,前者比較貼近人類的思維模式,直觀簡潔,但是執行效率較低(不過值得一提的是函數式利用惰性求值進行優化,原理也相當於運行時按需計算);後者執行速度快,但是不適合人類理解,在計算機行業最令人頭疼的一大類問題就是後者引發的,如代碼閱讀問題(有些代碼不運行調試否則難以理解:假如將DFA比作壓縮後的NFA,想象一下你不運行解壓程序,直接通過壓縮文件能輕易看出原文件的內容麼?)、程序副作用問題(多線程問題)等;原因在於狀態轉移實現沒有了模式串驅動方案的直觀性,而複雜的狀態和轉移只會令本已疲憊的程序員更加的疲於奔命。

另外,計算機的同步與異步也有着相似的關係。異步的優點在於模型與時間解耦,優點是按需調配; 而同步的優點在於實時性好,但與時間緊密耦合,同步代價高。這兩種模擬的思路在計算機中的操作系統、圖形界面設計等都有充分的體現。

選擇合適的模式就像雙目標優化問題一樣,你永遠無法到達一個完美的點,所謂魚和熊掌不可兼得。不過正如我們前面探討的混合方法一樣,這兩種本來互爲矛盾的事物也在不斷的融合,互相取長補短,不斷的折中以達到tradeoff(即平衡,或者說適用某種場景的平衡)。RubyPython這類語言就是其中的典型。在此不再展開討論。

計算機科學領域之大,已經難以用基本問題和基本處理方法去概述了,更何況它其實難以說成一門科學,而是各種學科的混合體呢。在此漫談的目的在於管窺整個學科的基本問題和基本處理思路,當然只是一種拋磚引玉。

(完)

2012-6-4 

補記: 2012-7-15
    模式匹配的概念其實比我們想象的更加寬泛。從形式語言的角度來看(即從規則出發), 最簡單的匹配是字符串匹配、正則式匹配、以及語法解析器,如果要更深層次的匹配,也就是語義層面的分析,就需要動用虛擬機進行虛擬執行,因爲對於語義層次,不進行執行是無法完全完成的。基於規則的方案的技術特點在於確定性,並且可以進行人工干預。一旦出現誤報或漏報現象,可以通過人工修改規則庫進行修正。當然這也是其最大的缺陷,因爲人力成本過高,並且對人的專業素養和技術要求較高。
    形式語言如此,對於自然語言來說,由於自身所帶有的歧義性和更爲複雜的上下文依賴關係,通過確定化的匹配手段,往往不能完成對自然語言的理解。不過,可以通過統計建模的方法進行分析,當然也自然帶有一定的不確定性。統計建模的關鍵點在於通過簡化的關係依賴模型(如馬爾科夫鏈、貝葉斯定理等)來描述語言中複雜的上下文依賴關係。
    基於統計方案相對於規則方案的另一優勢在於統計方案不要求被分析語言的完整性。例如在自然語言中,即使我們的口語缺乏主謂,但是對方仍然能夠聽得懂,聽着顯然不會按照規則對語言進行分析,往往通過少數的幾個關鍵詞就可以推斷出正確的語義。在一些帶噪聲的環境中,基於統計的方法相對規則方案也有很大的優勢。
    以入侵檢測系統爲例(殺毒引擎等等也是相同的道理), 模式匹配可以從正常模型和異常模型兩個角度去出發。正常模型意味着依靠大量樣本或者人工制定正常行爲的模式。凡是不符合正常模式的行爲都將受到阻攔。正常模型或者通過機器訓練或者通過人力制定,但一般都回避不了誤判的問題,不過其優點在於可以防範一些未知的攻擊方式。反之,異常模型是基於攻擊行爲的特徵的。一般來說,由於攻擊的樣本要遠遠小於正常樣本,並且特徵明顯,因此容易歸納出模式來,例如固定的字符串、語法模式等;因此可以通過基於規則的方案實現異常行爲的檢測。
    正常模型需要從樣本中挖掘出正常的行爲模式,並且正常行爲的樣本量一般要遠遠大於異常樣本,因此宜採用統計建模方案實現。當然其固有問題也非常嚴重,不確定性、依賴樣本的純度、必要時候缺乏人工干預等等,影響了其應用,在工業界尚未看到成功的實現。一些採用類似技術的產品均用戶聲稱非常多的誤判。
    從上述的分析來看, 其實機器學習或者統計學習的方案並沒有我們想象的那麼神奇,人類無法解決的問題機器是解決不了的。特別一個問題能否解決首先要求這個問題是良好定義的。例如如果遇到一個案例甚至 人都分不清是攻擊還是正常行爲,那麼機器也是無法辦到的。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章