python自然语言处理 | 分析句子结构
- 本章解決的問題
這里寫目錄標題
- 1 一些語法困境
- 1.1 語言數據和無限可能性
- 1.2 普遍存在的歧義
- 2 文法有什么用?
- 2.1 超越 n-grams
- 3 上下文無關文法
- 3.1 一種簡單的文法
- 3.2 寫你自己的文法
- 3.3 句法結構中的遞歸
- 4 上下文無關文法分析
- 4.2 4.2 移進-歸約分析
- 4.3 左角落分析器
- 4.4 符合語句規則的子串表 WFST
- 5 依存關系和依存文法
- 5.1 配價與詞匯
- 5.2 擴大規模
- 6 文法開發
- 6.1 樹庫和文法
- 6.2 有害的歧義
- 6.3 加權文法
- 7 小結
1 一些語法困境
1.1 語言數據和無限可能性
1.2 普遍存在的歧義
#讓我們仔細看看短語 I shot an elephant in my pajamas 中的歧義。 #首先,我們需要定義一個簡單的文法: import nltk groucho_grammar = nltk.CFG.fromstring("""S -> NP VPPP -> P NPNP -> Det N | Det N PP | 'I'VP -> V NP | VP PPDet -> 'an' | 'my'N -> 'elephant' | 'pajamas'V -> 'shot'P -> 'in'""") #這個文法允許以兩種方式分析句子,取決于介詞短語 in my pajamas 是描述大象還是槍擊事件。 sent = ['I', 'shot', 'an', 'elephant', 'in', 'my', 'pajamas'] parser = nltk.ChartParser(groucho_grammar) for tree in parser.parse(sent):print(tree)tree.draw()""" (S(NP I)(VP(VP (V shot) (NP (Det an) (N elephant)))(PP (P in) (NP (Det my) (N pajamas))))) (S(NP I)(VP(V shot)(NP (Det an) (N elephant) (PP (P in) (NP (Det my) (N pajamas))))))"""本章介紹文法和分析,以形式化可計算的方法調查和建模我們一直在討論的語言現象正如我們所看到的,詞序列中符合語法規則的和不符合語法規則的模式相對于短語結構和依賴是可以被理解的。我們可以開發使用文法和分析的這些結構的形式化模型。與以前一樣,一個重要的動機是自然語言理解。當我們能可靠地識別一個文本所包含的語言結構時,我們從中可以獲得多少文本的含義?
2 文法有什么用?
2.1 超越 n-grams
- 成分結構基于對詞與其他詞結合在一起形成單元的觀察。一個詞序列形成這樣一個單元的證據是它是可替代的——也就是說,在一個符合語法規則的句子中的詞序列可以被一個更小的序列替代而不會導致句子不符合語法規則。
3 上下文無關文法
3.1 一種簡單的文法
""" #首先,讓我們看一個簡單的上下文無關文法 # 按照慣例, 第一條生產式的左端是文法的開始符號,通常是 S(句子),所有符合語法規則的樹都必須有這個符 號作為它們的根標簽。 # NLTK中,上下文無關文法定義在 nltk.grammar 模塊。 """#上下文無關文法 grammar1 = nltk.CFG.fromstring("""S -> NP VPVP -> V NP | V NP PPPP -> P NPV -> "saw" | "ate" | "walked"NP -> "John" | "Mary" | "Bob" | Det N | Det N PPDet -> "a" | "an" | "the" | "my"N -> "man" | "dog" | "cat" | "telescope" | "park"P -> "in" | "on" | "by" | "with"""") sent = "Mary saw Bob".split() rd_parser = nltk.RecursiveDescentParser(grammar1) for tree in rd_parser.parse(sent):print(tree)tree.draw()文法組成部分的不同提取方式會產生不一樣的意思:
3.2 寫你自己的文法
import nltkdef yours_grammar(gram):sent = "Mary saw Bob".split()rd_parser = nltk.RecursiveDescentParser(gram)for tree in rd_parser.parse(sent):print(tree)tree.draw()grammar1 =nltk.data.load('file:mygrammar.cfg') # #確保你的文件名后綴為.cfg,并且字符串'file:mygrammar.cfg中'間沒有空格符 print(yours_grammar(grammar1))3.3 句法結構中的遞歸
#一個文法被認為是遞歸的,如果文法類型出現在產生式左側也出現在右側, #如例 8-2 所 示。產生式 Nom -> Adj Nom(其中 Nom 是名詞性的類別)包含 Nom 類型的直接遞歸, #而 S 上的間接遞歸來自于兩個產生式的組合:S -> NP VP 與 VP -> V S。#例. 遞歸的上下文無關文法。 grammar2 = nltk.CFG.fromstring("""S -> NP VPNP -> Det Nom | PropNNom -> Adj Nom | NVP -> V Adj | V NP | V S | V NP PPPP -> P NPPropN -> 'Buster' | 'Chatterer' | 'Joe'| 'Today'Det -> 'the' | 'a'N -> 'bear' | 'squirrel' | 'tree' | 'fish' | 'log' | 'Sunday' Adj -> 'angry' | 'frightened' | 'little' | 'tall' | 'sunny'V -> 'chased' | 'saw' | 'said' | 'thought' | 'was' | 'put' | 'is'P -> 'on'""") def yours_grammar(sent, gram):rd_parser = nltk.RecursiveDescentParser(gram)for tree in rd_parser.parse(sent):print(tree)tree.draw() sent1 = "the angry bear chased the frightened little squirrel".split() sent2 = "Chatterer said Buster thought the tree was tall".split() sent3 = "Today is sunny".split() print("sent1: ") print(yours_grammar(sent1, grammar2)) print("sent2: ") print(yours_grammar(sent2, grammar2)) print("sent3: ") print(yours_grammar(sent3, grammar2)) # 不添加會報錯 Grammar does not cover some of the input words: "'Today', 'is', 'Sunday'".4 上下文無關文法分析
4.2 4.2 移進-歸約分析
簡單的自下而上分析器是移進-歸約分析器。
移進-規約分析器缺點可能會到達一個死胡同,而不能找到任何解析,即使輸入的句子是符合語法的。這種情況發生時,沒有剩余的輸入,而堆棧包含不能被規約到一個S的項目。
問題出現的原因是:較早前做出的選擇不能被分析器撤銷(雖然圖形演示中用戶可以撤消它們的選擇)。
分析器可以做兩種選擇:(a)當有多種規約可能時選擇哪個規約,(b)當移進和規約都可以時選擇哪個動作。
移進-規約分析器可以改進執行策略來解決這些沖突。例如:它可以通過只有在不能規約時才移進,解決移進-規約沖突;它可以通過優先執行規約操作,解決規約-規約沖突;它可以從堆棧移除更多的項目。(一個通用的移進-規約分析器,是一個“超前LR分析器”,普遍使用在編程語言編譯器中。)
移進-規約分析器相比遞歸下降分析器的好處是,它們只建立與輸入中的詞對應的結構。此外,每個結構它們只建立一次。例如:NP(Det(the),N(man))只建立和壓入棧一次,不管以后VP -> V NPPP規約或者NP ->NPPP規約會不會用到。
4.3 左角落分析器
4.4 符合語句規則的子串表 WFST
""" #運用動態 規劃算法設計技術分析問題 #動態規劃存儲中間結果,并在 適當的時候重用它們,能顯著提高效率。#這種技術可以應用到句法分析,使我們能夠存儲分析任務的部分解決方案, #然后在必要的時候查找它們,直到達到最終解決方案。這種分析方法被稱為圖表分析。#動態規劃使我們能夠只建立一次 PP in my pajamas。 #第一次我們建立時就把它存入一 個表格中,然后在我們需要作為對象 NP 或更高的 VP 的組成部分用到它時我們就查找表格。 #這個表格被稱為符合語法規則的子串表 或簡稱為 WFST。 """#使用符合語句規則的子串表的接收器。 def init_wfst(tokens, grammar):numtokens = len(tokens)wfst = [[None for i in range(numtokens+1)] for j in range(numtokens+1)]for i in range(numtokens):productions = grammar.productions(rhs=tokens[i])wfst[i][i+1] = productions[0].lhs()return wfstdef complete_wfst(wfst, tokens, grammar, trace=False):index = dict((p.rhs(), p.lhs()) for p in grammar.productions())numtokens = len(tokens)for span in range(2, numtokens+1):for start in range(numtokens+1-span):end = start + spanfor mid in range(start+1, end):nt1, nt2 = wfst[start][mid], wfst[mid][end]if nt1 and nt2 and (nt1,nt2) in index:wfst[start][end] = index[(nt1,nt2)]if trace:print("[%s] %3s [%s] %3s [%s] ==> [%s] %3s [%s]" % \(start, nt1, mid, nt2, end, start, index[(nt1,nt2)], end))return wfstdef display(wfst, tokens):print('\nWFST ' + ' '.join(("%-4d" % i) for i in range(1, len(wfst))))for i in range(len(wfst)-1):print("%d " % i, end=" ")for j in range(1, len(wfst)):print("%-4s" % (wfst[i][j] or '.'), end=" ")print() tokens = "I shot an elephant in my pajamas".split() wfst0 = init_wfst(tokens, groucho_grammar) display(wfst0, tokens) wfst1 = complete_wfst(wfst0, tokens, groucho_grammar) display(wfst1, tokens) wfst1 = complete_wfst(wfst0, tokens, groucho_grammar, trace=True) # 找出過程 設置參數trace5 依存關系和依存文法
5.1 配價與詞匯
-
動詞和它們的依賴
-
依賴 ADJ、NP、PP 和 S 通常被稱為各自動詞的補語,什么動詞可以和什么補語一起出現 具有很強的約束。
-
在依存文法的傳統中,在表 8-3 中的動詞被認為具有不同的配價。配價限制不僅適用于 動詞,也適用于其他類的中心詞。
-
介詞短語、形容詞和副詞通常充當修飾語。與補充不同修飾語是可選的,經常可以進行 迭代,不會像補語那樣被中心詞選擇。
5.2 擴大規模
到目前為止,我們只考慮了“玩具文法”,演示分析的關鍵環節的少量的文法,但有一個明顯的問題就是這種做法是否可以擴大到覆蓋自然語言的大型語料庫。
很難將文法模塊化,每部分文法可以獨立開發。反過來這意味著,在一個語言學家團隊中分配編寫文法的任 務是很困難的。
另一個困難是當文法擴展到包括更加廣泛的成分時,適用于任何一個句子的分析的數量也相應增加。 換句話說,歧義隨著覆蓋而增加。
6 文法開發
分析器根據短語結構文法在句子上建立樹。現在,我們上面給出的所有例子只涉及玩具文法包含少數的產生式。如果我們嘗試擴大這種方法的規模來處理現實的語言語料庫會發生什么?在本節中,我們將看到如何訪問樹庫,并看看開發廣泛覆蓋的文法的挑戰。
6.1 樹庫和文法
# corpus 模塊定義了樹庫語料的閱讀器,其中包含了賓州樹庫語料的 10%的樣本。 from nltk.corpus import treebank t = treebank.parsed_sents('wsj_0001.mrg')[0] print(t) #我們可以利用這些數據來幫助開發一個文法。 #搜索樹庫找出句子的補語。 def filter(tree):child_nodes = [child.label() for child in tree if isinstance(child, nltk.Tree)]return (tree.label() == 'VP') and ('S' in child_nodes) from nltk.corpus import treebank print([subtree for tree in treebank.parsed_sents() for subtree in tree.subtrees(filter)][1]) #NLTK 語料庫也收集了中央研究院樹庫語料,包括 10000 句已分析的句子,來自現代漢 語中央研究院平衡語料庫。 #讓我們加載并顯示這個語料庫中的一棵樹。 print(nltk.corpus.sinica_treebank.parsed_sents()[3449].draw())6.2 有害的歧義
不幸的是,隨著文法覆蓋范圍的增加和輸入句子長度的增長,分析樹的數量也迅速增長事實上,它以天文數字的速度增長。
grammar = nltk.CFG.fromstring("""S -> NP V NPNP -> NP SbarSbar -> NP VNP -> 'fish'V -> 'fish'""") tokens = ['fish'] * 5 cp = nltk.ChartParser(grammar) for tree in cp.parse(tokens):print(tree) #隨著句子長度增加到(3,5,7,...),我們得到的分析樹的數量是:1; 2; 5; 14; 42; 132; 429; 1,430; 4,862; 16,796; 58,786; 208,012; ....6.3 加權文法
- 概率上下文無關文法(probabilistic context-free grammar,PCFG)
7 小結
總結
以上是生活随笔為你收集整理的python自然语言处理 | 分析句子结构的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电信4G物联网卡、流量卡资费,13位物联
- 下一篇: 减轻剪辑工作必备——Python实现让视