5. TÌM HIỂU MỘT SỐ CHỨC NĂNG TRONG NLTK
5.4. Parsing (phân tích cấu trúc câu)
Các phần trước đã đề cập đến từ (words): cách xác định, phân tích cấu trúc, gán nhãn từ, truy xuất nghĩa của từ.
Mục tiêu của phần này:
Văn phạm hình thức: mô tả cấu trúc tập không giới hạn câu. Parser phân tích một câu và tự động xây dựng cây cú pháp.
5.4.1. Văn phạm phi ngữ cảnh (Context Free Grammar)
Sử dụng với định nghĩa văn phạm.
Trong NLTK CFG được định nghĩa trong module nltk.grammar, ví dụ định nghĩa văn phạm sau:
grammar1 = nltk.parse_cfg(""" S -> NP VP
VP -> V NP | V NP PP PP -> P NP
V -> "saw" | "ate" | "walked"
NP -> "John" | "Mary" | "Bob" | Det N | Det N PP Det -> "a" | "an" | "the" | "my"
N -> "man" | "dog" | "cat" | "telescope" | "park" P -> "in" | "on" | "by" | "with" P -> "in" | "on" | "by" | "with"
""")
Phân tích với câu đơn giản như sau:
>>> sent = "Mary saw Bob".split()
>>> rd_parser = nltk.RecursiveDescentParser(grammar1) >>> for tree in rd_parser.nbest_parse(sent):
... print tree
(S (NP Mary) (VP (V saw) (NP Bob)))
Danh mục từ loại (nhãn) Symbol S NP VP PP Det N V P
Nếu thử phân tích câu “the dog saw a man in the park”, chúng ta sẽ thấy có 2 cây cú pháp được hình thành như sau
a b
Sử dụng văn phạm được định nghĩa ở trên, rõ ràng câu trên là câu mơ hồ về nghĩa. Sự mơ hồ này do cụm từ gọi là PP(preposition) gây ra. Do PP được nằm ở 2 vị trí khác nhau:
(a) PP thuộc VP: lúc này PP bổ trợ cho VP nên ý nghĩa của câu sẽ nhấn mạnh việc nhìn thấy các sự kiện trong công viên.
(b) PP thuộc NP: PP hỗ trợ cho NP nên nhấn mạnh người đàn ông trong công viên. Sự khác nhau cơ bản giữa (a) và (b) là trong (b) , nhấn mạnh người đàn ông trong công viên chứ không nhấn mạnh chủ ngữ thấy, do đó chủ ngữ này(con chó), có thể không ở trong công viên mà có thể ở nơi nào đó bên ngoài nhìn vào.
Định nghĩa văn phạm trong file
Các văn phạm có thể được định nghĩa trong một file và lưu trữ từ trước. Để sử dụng chúng chỉ cần load lên và thực hiện các công việc như phần trên. Lưu ý rằng phần mở rộng của file bắt buộc phải là cfg và không có khoảng trống tên file.
>>> grammar1 = nltk.data.load('file:mygrammar.cfg') >>> sent = "Mary saw Bob".split()
>>> rd_parser = nltk.RecursiveDescentParser(grammar1) >>> for tree in rd_parser.nbest_parse(sent):
Kiểm tra các luật bằng cách liệt kê chi tiết: >>> for x in grammar1.productions():
print x
Khi định nghĩa CFG trong file, NLTK không cho phép định nghĩa kết hợp các từ loại
và từ vựng chung vào bên phải của luật ví dụ như: PP ‘of’ NP. Thêm vào đó cũng không cho phép định nghĩa một từ vựng bao gồm nhiều từ(word), ví dụ thay vì viết NP ‘New York’ chúng ta cần đổi thành NP ‘New_York’.
Đệ quy trong cấu trúc cú pháp
Một văn phạm được gọi là đệ quy nếu như cụm từ loại(category)(*) bên trái của luật cũng xuất hiện bên phải của luật. Trong ví dụ minh họa dưới, luật Nom-> Adj Nom
liên quan đến đệ quy trực tiếp về từ loại Nom, trong khi đệ quy gián tiếp của S sinh ra từ sự kết hợp của 2 luật sinh cụ thể là : S->NP VP và VP->V S
>>> sent = 'the angry bear chased the frightened little squirrel'.split()
>>> sent
['the', 'angry', 'bear', 'chased', 'the', 'frightened', 'little', 'squirrel']
>>> grammar2 = nltk.parse_cfg(""" S -> NP VP
NP -> Det Nom | PropN Nom -> Adj Nom | N
VP -> V Adj | V NP | V S | V NP PP PP -> P NP
PropN -> 'Buster' | 'Chatterer' | 'Joe' Det -> 'the' | 'a'
N -> 'bear' | 'squirrel' | 'tree' | 'fish' | 'log' Adj -> 'angry' | 'frightened' | 'little' | 'tall'
V -> 'chased' | 'saw' | 'said' | 'thought' | 'was' | 'put' P -> 'on'
""")
>>> rdparser=nltk.RecursiveDescentParser(grammar2) >>> trees = rdparser.nbest_parse(sent)
>>> for tree in trees: print tree
(S
(NP (Det the) (Nom (Adj angry) (Nom (N bear)))) (VP
(V chased) (NP
(Det the)
(Nom (Adj frightened) (Nom (Adj little) (Nom (N squirrel)))))))
>>> sent2= 'Chatterer said Buster thought the tree was tall'.split()
>>> trees2 = rdparser.nbest_parse(sent2) >>> trees2[0].draw()
Chúng ta đã chỉ minh họa hai cấp độ của đệ quy ở đây, nhưng không có giới hạn trên về độ sâu. Bạn có thể thử nghiệm với các câu phân tích cú pháp có liên quan đến sâu hơn cấu trúc lồng nhau. Ghi chú rằng RecursiveDescentParser là không thể để xử lý đệ quy bên trái luật sinh dạng X -> X Y
Phân tích cú pháp với văn phạm phi ngữ cảnh(CFG)
Phân tích cú pháp xử lý câu đầu vào theo các quy tắc duy diễn của văn phạm, và xây dựng một hoặc nhiều cấu trúc thành phần phù hợp với văn phạm. Văn phạm là một đặc tả khai báo chính xác- nó thực sự chỉ là một chuỗi, không phải là một chương trình. Phân tích cú pháp là biên dịch thủ tục văn phạm. Nó tìm kiếm thông qua các không gian cây được cấp phép bởi văn phạm để tìm một trong đó có câu yêu cầu dọc theo biên của nó.
Phân tích cú pháp cho phép một văn phạm được đánh giá theo một tập hợp các câu kiểm tra, giúp các nhà ngôn ngữ học để khám phá những sai lầm trong phân tích văn phạm của họ. Một phân tích cú pháp có thể xem như là một mô hình xử lý tâm lý, giúp giải thích những khó khăn mà con người xử lý với quá trình xây dựng một số cú pháp.
Nhiều ứng dụng ngôn ngữ tự nhiên liên quan đến phân tích tại một số điểm, ví dụ, chúng ta sẽ mong đợi những câu hỏi ngôn ngữ tự nhiên được gửi đến một hệ thống câu hỏi/trả lời phải trải qua phân tích cú pháp như là một bước khởi đầu.
Trong phần này chúng ta thấy hai thuật toán phân tích cú pháp đơn giản, một phương pháp top-down(từ trên xuống) được gọi là recursive descent parsing(đệ quy gốc phân tích cú pháp), và một phương pháp bottom-up(từ dưới lên) được gọi là shift-reduce parsing. Chúng ta cũng tìm hiểu một số thuật toán phức tạp hơn, một phương pháp top-down kết hợp lọc bottom-up được gọi là left-corner parsing, và một kỹ thuật lập trình động với kỹ thuật gọi là chart parsing.
5.4.2. Recursive Descent Parsing
Kiểu phân tích cú pháp đơn giản nhất thông dịch một văn phạm đặc tả cách chia mục tiêu (vế phải của luật sinh) cấp cao thành các mục tiêu cấp thấp hơn.
Mục cao nhất là tìm S. Luật S-> NP VP cho phép phân tích thay thế mục tiêu này với 2 mục tiêu thấp hơn là tìm NP và VP. Mỗi mục tiêu cấp 2 này sẽ được phân rã thành các mục tiêu cấp thấp hơn. Cuối cùng, quá trình này sẽ tiến đến các từ thu gọn hoặc các từ loại. Các từ này sẽ được so sánh trực tiếp với chuỗi đầu vào. Nếu thành công từ kế tiếp sẽ được so khớp. Nếu thất bại nó sẽ dò tìm với sự thay thế khác
Quá trình này sẽ thực hiên từ gốc là mục tiêu ban đầu S. Thử debug quá trình phân tích câu “the dog saw a man in the park” với văn phạm như ví dụ ban đầu.
S -> NP VP
VP -> V NP | V NP PP PP -> P NP
V -> "saw" | "ate" | "walked"
NP -> "John" | "Mary" | "Bob" | Det N | Det N PP Det -> "a" | "an" | "the" | "my"
N -> "man" | "dog" | "cat" | "telescope" | "park" P -> "in" | "on" | "by" | "with"
Chọn Edit Grammar (Ctrl +g)
Nhấn phím space bar để xem các bước thực hiện ( quay lại bằng Ctrl +Z)
1. Khởi tạo 2. Khớp: S NP VP
3. Thử: NP “John” 4. Không khớp: “John” quay lui
7. Hoàn tất phân tích 8. Quay lui: không khớp N khác
Trong quá trình này, bộ phân tích cú pháp được thường buộc phải lựa chọn giữa nhiều luật sinh có thể. Ví dụ, đi từ bước 5 đến bước 6 (bõ qua hình chụp một số bước thử không đúng để qua kết quả khớp là bước 6), nó sẽ cố gắng để tìm luật với
Det ở phía bên tay trái. Việc đầu tiên trong số này là Det ’a’. Khi so sánh không
đúng với đầu vào nó sẽ quay lui và cố gắng với các luật Det khác, cho đến khi nó lấy được Det ‘the’ phù hợp với từ kế tiếp trong câu đầu vào. Tương tự như vậy cho các phần còn lại cho tới khi phân tích hoàn tất ở bước 7. Đây là một cây bao gồm toàn bộ câu mà không có bất cứ nhánh nào bị cô lập. Sau khi một cú pháp đã được tìm thấy, chúng ta có thể tìm nhiều kết quả hơn. Một lần nữa nó sẽ quay lui và khám phá những sự lựa chọn khác của luật sinh để tìm ra tất cả các cú pháp có thể. NLTK cung cấp bộ phân tích đệ quy nltk.RecursiveDescentParser(grammar)
>>> rd_parser = nltk.RecursiveDescentParser(grammar1) >>> sent = 'Mary saw a dog'.split()
>>> for t in rd_parser.nbest_parse(sent): ... print t
(S (NP Mary) (VP (V saw) (NP (Det a) (N dog))))
Chú ý:
RecursiveDescentParser() có thêm tham số tùy chọn là trace, nếu trace>0 nó sẽ thể hiện kết quả dưới dạng text mà nó chạy.
Recursive Descent Parsing có ba nhược điểm quan trọng:
• Đầu tiên, các luật đệ quy bên trái như NP NP PP gửi nó vào một vòng lặp vô hạn.
• Thứ hai, phân tích cú pháp tốn nhiều thời gian xem xét các từ và cấu trúc không tương ứng với câu đầu vào.
• Thứ ba, quá trình quay lui có thể loại bỏ các thành phần cấu tạo phân tích cú pháp sẽ cần được xây dựng lại một lần nữa sau đó. Ví dụ, quay lùi hơn VP V NP sẽ loại bỏ các cây con được tạo ra cho NP. Nếu phân tích cú pháp sau đó tiến hành với VP V NP PP, sau đó NP cây con phải được tạo ra trên một lần nữa.