Nền tảng Ngôn ngữ học¶
Ngôn ngữ học cung cấp vốn từ vựng cấu trúc mà các hệ thống NLP học ngầm và khai thác. File này bao gồm hình thái học (morphology), cú pháp (syntax), ngữ nghĩa (semantics), dụng học (pragmatics), âm vị học (phonology), phân tích cấu thành và phụ thuộc, cùng giả thuyết phân bố (distributional hypothesis) — những kiến thức khoa học về ngôn ngữ con người làm nền tảng cho tokenisation, ngữ pháp và ý nghĩa trong AI.
-
Trước khi có thể xây dựng các hệ thống hiểu hoặc sinh ra ngôn ngữ, chúng ta cần hiểu bản thân ngôn ngữ vận hành như thế nào.
-
Ngôn ngữ học là ngành khoa học nghiên cứu về ngôn ngữ, và nó cung cấp vốn từ vựng khái niệm mà NLP vay mượn liên tục.
-
Ngay cả các mô hình nơ-ron hiện đại — vốn học ngôn ngữ từ dữ liệu thô — cũng âm thầm tái khám phá nhiều cấu trúc mà các nhà ngôn ngữ học đã hệ thống hoá trong nhiều thập kỷ.
-
Ngôn ngữ có cấu trúc ở mọi cấp độ: các âm thanh tạo nên từ, các bộ phận tạo nên từ, các quy tắc kết hợp từ thành câu, ý nghĩa mà những câu đó mang lại, và cách ngữ cảnh định hình cách diễn giải. Chúng ta sẽ lần lượt tìm hiểu từng cấp độ từ dưới lên.
-
Hình thái học (Morphology) là nghiên cứu về cấu trúc bên trong của từ. Từ không phải là đơn vị nguyên tử; chúng được xây dựng từ các đơn vị mang ý nghĩa nhỏ hơn gọi là hình vị (morpheme).
-
Từ "unhappiness" chứa ba hình vị: "un-" (tiền tố chỉ "không"), "happy" (gốc từ), và "-ness" (hậu tố biến tính từ thành danh từ). Mỗi hình vị đều đóng góp vào ý nghĩa.
-
Gốc từ (root/stem) là hình vị lõi mang ý nghĩa chính. "Happy", "run", "compute" là các gốc từ.
-
Phụ tố (affix) là hình vị bám vào gốc từ để biến đổi nó.
-
Tiếng Anh có tiền tố (prefix) (đứng trước gốc: un-, re-, pre-) và hậu tố (suffix) (đứng sau: -ing, -ed, -tion). Một số ngôn ngữ còn có trung tố (infix, chèn vào giữa gốc) và vây tố (circumfix, bọc quanh).
-
-
Có hai loại quá trình hình thái. Biến tố (Inflection) thay đổi thuộc tính ngữ pháp của từ mà không thay đổi ý nghĩa cốt lõi hay từ loại: "run" thành "runs" (ngôi thứ ba), "running" (tiếp diễn), "ran" (quá khứ). Từ vẫn là động từ mang cùng ý nghĩa.
-
Cấu tạo từ (Derivation) tạo ra một từ mới, thường thay đổi từ loại: "happy" (tính từ) thành "happiness" (danh từ), "compute" (động từ) thành "computation" (danh từ) thành "computational" (tính từ). Mỗi lần cấu tạo đều làm dịch chuyển ý nghĩa và phạm trù ngữ pháp.
-
Các ngôn ngữ khác nhau rất nhiều về độ phức tạp hình thái. Tiếng Anh tương đối phân tích (analytic) (ít hình vị trên mỗi từ, phụ thuộc vào trật tự từ).
-
Tiếng Thổ Nhĩ Kỳ và Phần Lan thuộc loại chắp dính (agglutinative) (từ có thể chứa nhiều hình vị ghép nối tiếp nhau). Tiếng Ả Rập và Hebrew dùng hình thái khuôn mẫu (templatic) (gốc từ là bộ khung phụ âm như k-t-b cho "viết", và các mẫu nguyên âm được chèn vào để tạo các từ khác nhau: kitab "sách", kataba "anh ấy đã viết", maktub "đã được viết").
-
Hình thái học quan trọng với NLP vì nó ảnh hưởng đến tokenisation. Một tokeniser ở cấp từ xem "run", "runs", "running" và "ran" như bốn ký hiệu không liên quan.
-
Một hệ thống có nhận thức hình thái sẽ nhận ra chúng chung một gốc. Tokenisation dưới từ (subword) (BPE, WordPiece) — mà chúng ta sẽ trình bày ở file 02 — là một phép xấp xỉ thống kê của phân tích hình thái.
-
Cú pháp (Syntax) là nghiên cứu về cách các từ kết hợp thành cụm từ và câu. Mọi ngôn ngữ đều có quy tắc về trật tự từ và cấu trúc; vi phạm chúng sẽ tạo ra những câu vô nghĩa.
-
"The cat sat on the mat" là tiếng Anh đúng ngữ pháp; "Mat the on sat cat the" thì không.
-
Có hai khuôn khổ chính để mô tả cấu trúc cú pháp.
-
Ngữ pháp cấu trúc cụm (Phrase structure grammar) — còn gọi là ngữ pháp cấu thành (constituency grammar) — cho rằng câu được xây dựng bằng cách lồng ghép các cụm từ vào nhau. Một câu (S) gồm một cụm danh từ (NP) và một cụm động từ (VP).
-
Một cụm danh từ có thể gồm một từ hạn định (Det) theo sau bởi một danh từ (N). Một cụm động từ có thể gồm một động từ (V) theo sau bởi một cụm danh từ. Các quy tắc này xây dựng nên một cây:
-
-
Cây này được gọi là cây cấu thành (constituency tree) (hay cây phân tích cú pháp). Mỗi nút bên trong là một loại cụm từ, mỗi lá là một từ. Cây nắm bắt sự phân cấp theo nhóm: "on the mat" là một đơn vị (cụm giới từ), "sat on the mat" là một đơn vị (cụm động từ), và toàn bộ là một câu.
-
Văn phạm phi ngữ cảnh (Context-free grammar - CFG) hình thức hoá các quy tắc này. Nó gồm một tập các luật sản xuất, mỗi luật có dạng \(A \to \alpha\), trong đó \(A\) là một ký hiệu phi kết thúc (non-terminal — một loại cụm từ như NP hay VP) và \(\alpha\) là một chuỗi các ký hiệu kết thúc (terminal — từ) và phi kết thúc. Ví dụ:
S → NP VP
NP → Det N
NP → Det N PP
VP → V NP
VP → V PP
PP → P NP
Det → "the" | "a"
N → "cat" | "mat" | "dog"
V → "sat" | "chased"
P → "on" | "under"
-
Bắt đầu từ S và áp dụng các luật liên tiếp, bạn có thể sinh ra mọi câu mà văn phạm cho phép. Phân tích cú pháp (parsing) là quá trình ngược lại: từ một câu cho trước, tìm cây (hoặc các cây) đã sinh ra nó. Một câu có nhiều cây phân tích hợp lệ được gọi là đa nghĩa cú pháp (syntactically ambiguous). "I saw the man with the telescope" có hai cách phân tích: tôi dùng kính thiên văn để thấy người đàn ông, hoặc tôi thấy một người đàn ông có kính thiên văn.
-
Ngữ pháp phụ thuộc (Dependency grammar) có góc nhìn khác. Thay vì lồng ghép cụm từ, nó mô tả các quan hệ trực tiếp giữa các từ. Mỗi từ trong câu phụ thuộc vào đúng một từ khác (gọi là head của nó), ngoại trừ gốc của câu. Kết quả là một cây phụ thuộc (dependency tree) với các cạnh được gán nhãn quan hệ ngữ pháp (chủ ngữ, tân ngữ, bổ ngữ, v.v.).
-
-
Trong góc nhìn phụ thuộc, "sat" là gốc. "Cat" phụ thuộc vào "sat" với vai trò chủ ngữ (nsubj). "On" phụ thuộc vào "sat" với vai trò bổ ngữ giới từ. "Mat" phụ thuộc vào "on" với vai trò tân ngữ của giới từ. Mỗi từ treo vào đúng một head, tạo thành một cây.
-
Ngữ pháp phụ thuộc đã trở thành khuôn khổ thống trị trong NLP hiện đại vì cây phụ thuộc dễ sản xuất hơn với các bộ phân tích thống kê và các quan hệ ánh xạ trực tiếp hơn đến vai trò ngữ nghĩa (ai làm gì với ai).
-
Valency mô tả một động từ cần bao nhiêu tham số. "Sleep" là nội động từ (intransitive) (một tham số: người ngủ). "Eat" là ngoại động từ (transitive) (hai: người ăn và vật bị ăn). "Give" là song sở (ditransitive) (ba: người cho, vật được cho, và người nhận). Biết valency của động từ sẽ hạn chế những cây phân tích nào là hợp lệ.
-
Ngữ nghĩa (Semantics) là nghiên cứu về ý nghĩa. Cú pháp cho bạn biết câu được cấu trúc ra sao; ngữ nghĩa cho bạn biết câu đó có nghĩa gì.
-
Ngữ nghĩa từ vựng (Lexical semantics) đề cập đến ý nghĩa của từng từ riêng lẻ. Các từ có quan hệ với nhau theo những cách có hệ thống:
- Đồng nghĩa (Synonymy): các từ có ý nghĩa (gần như) giống nhau. "Big" và "large" là đồng nghĩa. Đồng nghĩa hoàn hảo thực sự rất hiếm; hầu như luôn có những khác biệt tinh tế về sắc thái hoặc cách dùng.
- Trái nghĩa (Antonymy): các từ có ý nghĩa đối lập. "Hot" và "cold", "buy" và "sell".
- Bao hàm/Hệ quả (Hypernymy/hyponymy): quan hệ "là một loại của". "Dog" là hyponym của "animal" (chó là một loại động vật). "Animal" là hypernym của "dog". Các quan hệ này tạo thành hệ thống phân cấp phân loại.
- Bộ phận (Meronymy): quan hệ "là một bộ phận của". "Wheel" là meronym của "car".
- Đa nghĩa (Polysemy): một từ duy nhất có nhiều nghĩa liên quan. "Bank" có nghĩa là ngân hàng hoặc bờ sông. Ngữ cảnh giúp phân biệt.
-
Phân giải nghĩa từ (Word sense disambiguation - WSD) là bài toán xác định nghĩa nào của một từ đa nghĩa được dùng trong một ngữ cảnh nhất định. Trong "I deposited money at the bank," nghĩa tài chính là đúng. Trong "We sat by the river bank," nghĩa địa lý là đúng. WSD từng là vấn đề trung tâm trong NLP sơ khai; các embedding ngữ cảnh hiện đại (ELMo, BERT) phần lớn đã giải quyết nó bằng cách tạo ra các biểu diễn vector khác nhau cho các cách dùng khác nhau của cùng một từ.
-
Ngữ nghĩa tổng hợp (Compositional semantics) nghiên cứu cách ý nghĩa của các từ riêng lẻ kết hợp để tạo thành ý nghĩa của một cụm từ hay câu. Nguyên lý tính tổng hợp (compositionality) (gán cho Frege) phát biểu rằng ý nghĩa của một biểu thức phức tạp được xác định bởi ý nghĩa của các bộ phận cấu thành và các quy tắc kết hợp chúng. "The cat chased the dog" có nghĩa khác với "the dog chased the cat" vì cấu trúc cú pháp (ai là chủ ngữ so với tân ngữ) tương tác với nghĩa của từ.
-
Không phải mọi nghĩa đều có tính tổng hợp. Thành ngữ (Idioms) như "kick the bucket" (nghĩa là "chết") có nghĩa không thể suy ra từ các bộ phận của nó. Đây là thách thức cho bất kỳ phương pháp tổng hợp nào.
-
Ngữ nghĩa phân bố (Distributional semantics) là phương pháp tính toán về ý nghĩa làm nền tảng cho NLP hiện đại. Giả thuyết phân bố (Distributional hypothesis) (Firth, 1957) phát biểu: "Ngươi sẽ biết một từ qua những bạn đồng hành của nó." Các từ xuất hiện trong những ngữ cảnh tương tự có xu hướng mang ý nghĩa tương tự. Đây là nền tảng lý thuyết cho word embedding (Word2Vec, GloVe) mà chúng ta sẽ khám phá ở file 03.
-
Dụng học (Pragmatics) nghiên cứu cách ngữ cảnh ảnh hưởng đến ý nghĩa. Cùng một câu có thể mang nghĩa khác nhau tuỳ vào ai nói, khi nào, ở đâu và tại sao.
-
"Can you pass the salt?" về mặt cú pháp là một câu hỏi có/không về khả năng. Về mặt dụng học, nó là một lời yêu cầu. Bạn sẽ không trả lời "Yes, I can" rồi ngồi yên. Hiểu điều này đòi hỏi kiến thức vượt ra ngoài các từ ngữ đen — cụ thể là các quy ước về hành vi ngôn ngữ (speech acts).
-
Lý thuyết hành vi ngôn ngữ (Speech act theory) (Austin, Searle) phân biệt:
- Hành vi tuyên ngôn (Locutionary act): nội dung đen (literal) ("Can you pass the salt?")
- Hành vi dụng ngôn (Illocutionary act): chức năng dự định (một lời yêu cầu)
- Hành vi tác động (Perlocutionary act): hiệu quả lên người nghe (họ đưa muối)
-
Hàm ngôn (Implicature) (Grice) là ý nghĩa được hàm ý nhưng không được nói ra tường minh. Nếu ai đó hỏi "Is John a good cook?" và bạn trả lời "He's British," bạn đã không trả lời câu hỏi theo nghĩa đen, nhưng người nghe có thể suy luận (thông qua định kiến văn hoá, công bằng hay không) rằng bạn muốn nói "không." Nguyên tắc cộng tác (Cooperative principle) của Grice nói rằng người nói thường cố gắng mang tính thông tin, chân thật, liên quan và rõ ràng, và người nghe giải thích các phát ngôn với giả định các châm ngôn này được tuân thủ.
-
Đồng tham chiếu (Coreference) là hiện tượng dụng học khi các biểu thức khác nhau cùng chỉ về một thực thể. Trong "Alice went to the store. She bought milk," "she" chỉ Alice. Giải quyết đồng tham chiếu là việc thiết yếu để hiểu văn bản nhiều câu và là một bài toán NLP quan trọng.
-
Cấu trúc diễn ngôn (Discourse structure) mô tả cách các câu kết nối với nhau để tạo thành văn bản mạch lạc. Một câu chuyện có mở đầu, thân và kết. Một lập luận có các luận điểm và bằng chứng. Lý thuyết Cấu trúc Tu từ (Rhetorical Structure Theory - RST) phân tích văn bản như một cây các quan hệ diễn ngôn (khai triển, tương phản, nguyên nhân, v.v.) giữa các đoạn.
-
Dụng học là nơi NLP khó nhất. Các mô hình ngôn ngữ hiện đại xử lý phần lớn cú pháp và ngữ nghĩa một cách ngầm thông qua dữ liệu huấn luyện, nhưng lập luận dụng học — hiểu mỉa mai, hàm ngôn và ý nghĩa phụ thuộc ngữ cảnh — vẫn là thách thức tiên phong.
-
Âm vị học (Phonology) nghiên cứu hệ thống âm thanh của ngôn ngữ. Mặc dù chương này tập trung vào văn bản, một tổng quan ngắn sẽ làm cầu nối đến chương về âm thanh và giọng nói (Chương 09).
-
Âm vị (Phoneme) là đơn vị âm thanh nhỏ nhất có thể phân biệt nghĩa. Tiếng Anh có khoảng 44 âm vị. Hai từ "bat" và "pat" khác nhau bởi một âm vị (/b/ so với /p/), và điều này thay đổi hoàn toàn ý nghĩa. Đây gọi là cặp tối thiểu (minimal pair).
-
Biến thể âm vị (Allophone) là các thể hiện vật lý khác nhau của cùng một âm vị không làm thay đổi nghĩa. Âm "p" trong "pin" (bật hơi, có luồng hơi) và "p" trong "spin" (không bật hơi) là các allophone của /p/ trong tiếng Anh; người bản ngữ coi chúng là cùng một âm.
-
Bảng Ký hiệu Ngữ âm Quốc tế (International Phonetic Alphabet - IPA) cung cấp một ký hiệu chuẩn hoá cho các âm vị trên mọi ngôn ngữ. Từ "cat" được phiên âm là /kæt/. IPA là cầu nối giữa văn bản viết và hệ thống giọng nói.
-
Âm điệu (Prosody) bao gồm nhịp điệu, trọng âm và ngữ điệu của lời nói. "I didn't say he stole the money" có bảy nghĩa khác nhau tuỳ vào từ nào được nhấn mạnh. Âm điệu mang thông tin mà chỉ văn bản thiếu, đó là lý do các hệ thống chuyển văn bản thành giọng nói (TTS) phải mô hình hoá nó cẩn thận.
-
Trong NLP, kiến thức âm vị học xuất hiện trong TTS (chuyển đổi chữ cái sang âm vị), nhận dạng giọng nói (ánh xạ tín hiệu âm thanh thành âm vị), và thậm chí trong sửa lỗi chính tả và chuyển ngữ (transliteration).
Bài tập Lập trình (dùng CoLab hoặc notebook)¶
-
Xây dựng một bộ phân tích hình thái đơn giản để tách các từ tiếng Anh thành các hình vị khả dĩ bằng cách dùng danh sách các tiền tố và hậu tố phổ biến.
prefixes = ['un', 're', 'pre', 'dis', 'mis', 'over', 'under', 'out', 'non'] suffixes = ['ing', 'ed', 'ly', 'ness', 'ment', 'tion', 'able', 'ible', 'er', 'est', 'ful', 'less', 'ous'] def analyse_morphemes(word): """Phân tích hình vị đơn giản dùng các phụ tố đã biết.""" parts = [] remaining = word.lower() # Kiểm tra tiền tố for p in sorted(prefixes, key=len, reverse=True): if remaining.startswith(p) and len(remaining) > len(p) + 2: parts.append(f"[prefix: {p}]") remaining = remaining[len(p):] break # Kiểm tra hậu tố for s in sorted(suffixes, key=len, reverse=True): if remaining.endswith(s) and len(remaining) > len(s) + 2: root = remaining[:-len(s)] parts.append(f"[root: {root}]") parts.append(f"[suffix: {s}]") remaining = None break if remaining is not None: parts.append(f"[root: {remaining}]") return parts for word in ['unhappiness', 'reusable', 'disconnected', 'overreacting', 'kindness']: print(f"{word:20s} → {' + '.join(analyse_morphemes(word))}") -
Triển khai một bộ phân tích văn phạm phi ngữ cảnh đơn giản bằng phương pháp đệ quy xuống (recursive descent). Định nghĩa một văn phạm nhỏ và phân tích một câu thành cây cấu thành.
class CFGParser: """Bộ phân tích đệ quy xuống cho một văn phạm tiếng Anh nhỏ.""" def __init__(self, tokens): self.tokens = tokens self.pos = 0 def peek(self): return self.tokens[self.pos] if self.pos < len(self.tokens) else None def consume(self, expected=None): tok = self.peek() if expected and tok != expected: return None self.pos += 1 return tok def parse_det(self): if self.peek() in ('the', 'a'): return ('Det', self.consume()) return None def parse_noun(self): if self.peek() in ('cat', 'dog', 'mat', 'man'): return ('N', self.consume()) return None def parse_verb(self): if self.peek() in ('sat', 'chased', 'saw'): return ('V', self.consume()) return None def parse_prep(self): if self.peek() in ('on', 'under', 'with'): return ('P', self.consume()) return None def parse_np(self): save = self.pos det = self.parse_det() noun = self.parse_noun() if det and noun: # Kiểm tra PP tuỳ chọn pp = self.parse_pp() if pp: return ('NP', det, noun, pp) return ('NP', det, noun) self.pos = save return None def parse_pp(self): save = self.pos prep = self.parse_prep() np = self.parse_np() if prep and np: return ('PP', prep, np) self.pos = save return None def parse_vp(self): save = self.pos verb = self.parse_verb() if verb: np = self.parse_np() if np: return ('VP', verb, np) pp = self.parse_pp() if pp: return ('VP', verb, pp) self.pos = save return None def parse_sentence(self): np = self.parse_np() vp = self.parse_vp() if np and vp and self.pos == len(self.tokens): return ('S', np, vp) return None def print_tree(tree, indent=0): if isinstance(tree, str): print(' ' * indent + tree) elif isinstance(tree, tuple): print(' ' * indent + tree[0]) for child in tree[1:]: print_tree(child, indent + 2) sentences = [ "the cat sat on the mat", "a dog chased the cat", ] for sent in sentences: tokens = sent.split() parser = CFGParser(tokens) tree = parser.parse_sentence() print(f"\n'{sent}':") if tree: print_tree(tree) else: print(" (không tìm thấy cây phân tích)") -
Khám phá các quan hệ từ vựng bằng cách xây dựng một đồ thị từ đơn giản. Với một bộ từ vựng nhỏ có các quan hệ đồng nghĩa, trái nghĩa và bao hàm, hãy tìm đường đi giữa các từ.
relations = { ('big', 'large'): 'synonym', ('big', 'small'): 'antonym', ('small', 'tiny'): 'synonym', ('dog', 'animal'): 'hypernym', ('cat', 'animal'): 'hypernym', ('puppy', 'dog'): 'hypernym', ('happy', 'glad'): 'synonym', ('happy', 'sad'): 'antonym', ('hot', 'cold'): 'antonym', ('hot', 'warm'): 'synonym', } # Xây dựng danh sách kề from collections import defaultdict, deque graph = defaultdict(list) for (w1, w2), rel in relations.items(): graph[w1].append((w2, rel)) graph[w2].append((w1, rel)) def find_path(start, end): """BFS tìm đường đi giữa hai từ qua đồ thị quan hệ.""" queue = deque([(start, [(start, None)])]) visited = {start} while queue: node, path = queue.popleft() if node == end: return path for neighbor, rel in graph[node]: if neighbor not in visited: visited.add(neighbor) queue.append((neighbor, path + [(neighbor, rel)])) return None pairs = [('big', 'tiny'), ('puppy', 'cat'), ('happy', 'sad')] for w1, w2 in pairs: path = find_path(w1, w2) if path: steps = " → ".join(f"{w}({r})" if r else w for w, r in path) print(f"{w1} → {w2}: {steps}") else: print(f"{w1} → {w2}: không tìm thấy đường đi")