Xử Lý Ngôn Ngữ Tự Nhiên với Python - P7

Ở các phần trước, chúng ta đã tìm hiểu về khái niệm Corpus hay Corpora và cách sử dụng các Corpus mà NLTK cung cấp cũng như làm các phép thống kê đơn giản với các dữ liệu trên Corpus này. Ở phần tiếp này, chúng ta sẽ cùng tìm hiểu một khái niệm mới có tên là Lexical Resources, hay nguồn từ vựng học. Ta có thể hiểu nôm na nó là Tử Điển. Từ điển là một thứ rất quan trọng trong việc học và hiểu bất cữ ngôn ngữ nào trên thế giới, do vậy trong NLP, người ta cũng rất quan tâm tới vấn đề sử dụng từ điển.

Lexical Resources

Từ điển, là một tập hợp của các từ hoặc cụm từ mà đi kèm nó có cả từ loại (part-of-speech), nghĩa của từ - cụm từ. Trong NLP thì từ điển là thứ yếu của văn bản, thương được dùng để bổ nghĩa, làm giàu hơn cho ý nghĩa của văn bản. Trong các phần trước, khi ta sắp xếp các văn bản "vocab = sorted(set(my_text))" hay chạy Phân bố theo tuần xuất của văn bản "word_freq = FreqDist(my_text)" thì kết quả là tạo ra một từ điển đơn giản. Thuật ngữ tiêu chuẩn dùng để chỉ Từ điển trong NLP được gọi là WordNet, ta sẽ tìm hiểu phần đó sau. Các mục từ bao gồm headword (hay bổ đề) cùng với các thông tin như part-of-speech hay ý nghĩa của mục từ. Hai từ có ý nghĩa khác nhau nhưng có cùng cách viết được gọi là homonyms (đồng âm). Ví dụ Loại từ điển đơn giản nhất là một danh sách các từ đã được sắp xếp. Trong bài này, ta sẽ tìm hiểu các từ điển mà NLTK cung cấp.

Wordlist Corpora

Hay tạm hiểu là Corpus chứa danh sách các từ. Có thể nói đây là 1 danh sách từ chuẩn và thường sử dụng trong tiếng anh, do vậy ta có thể sử dụng lọc để lọc ra các từ ít được sử dụng, hoặc các từ sai chính tả trong văn bản. Cùng xem đoạn Code sau.

import nltk
def unusual_words(text):
    text_vocab = set(w.lower() for w in text if w.isalpha())
    english_vocab = set(w.lower() for w in nltk.corpus.words.words())
    unusual = text_vocab.difference(english_vocab)
    return sorted(unusual)
print(unusual_words(nltk.corpus.gutenberg.words('austen-sense.txt')))
# Kết quả
['abbeyland', 'abhorred', 'abilities', 'abounded', 'abridgement', 'abused', 'abuses', 'accents', 'accepting'...]

Hoặc

>>> print(unusual_words(nltk.corpus.nps_chat.words()))
# Kết quả
['aaaaaaaaaaaaaaaaa', 'aaahhhh', 'abortions', 'abou', 'abourted', 'abs', 'ack', 'acros', 'actualy', 'adams', 'adds', 'adduser', 'adjusts'....]

Đoạn Code trên làm nhiệm vụ so sánh các từ trong văn bản với các từ chuẩn, thường dùng trong Wordlist để lấy ra các từ ít được sử dụng hoặc sai chính tả.

Bài Tập: Một bài tập nhỏ, cũng là ứng dụng khá hay. Khi học tiếng Anh, bạn thường được học 3 ngàn từ thông dụng để giao tiếp, vậy bây giờ hãy lập danh sách 3 ngàn từ thông dụng sau đó áp dụng cách lọc từ trên để tìm ra các từ mới trong ebook, báo mà không nằm trong 3000 từ đó. Như vậy, bạn đã xây dựng được 1 từ điển từ mới phục vụ cho việc học và đọc tài liệu mà bạn đang dùng.

Stopwords

Stopwords hiểu đơn giản là các từ có tần số xuất hiện nhiều như the, to... các từ này thường mang ít giá trị ý nghĩa và không khác nhau nhiều trong các văn bản khác nhau. Ví dụ từ "the" hay "to" thì ở văn bản nào nó cũng không bị thay đổi về ý nghĩa. Bạn có thể liệt kê các Stopwords trong tiếng Anh như sau.

>>> from nltk.corpus import stopwords
>>> stopwords.words('english')
['i', 'me', 'my', 'myself', 'we', 'our', 'ours', 'ourselves', 'you', 'your', 'yours', 'yourself', 'yourselves', 'he', 'him', 'his', 'himself', 'she', 'her', 'hers', 'herself', 'it', 'its', 'itself', 'they', 'them', 'their', 'theirs', 'themselves', 'what', 'which', 'who', 'whom', 'this', 'that', 'these', 'those', 'am', 'is', 'are', 'was', 'were', 'be', 'been', 'being', 'have', 'has', 'had', 'having', 'do', 'does', 'did', 'doing', 'a', 'an', 'the', 'and', 'but', 'if', 'or', 'because', 'as', 'until', 'while', 'of', 'at', 'by', 'for', 'with', 'about', 'against', 'between', 'into', 'through', 'during', 'before', 'after', 'above', 'below', 'to', 'from', 'up', 'down', 'in', 'out', 'on', 'off', 'over', 'under', 'again', 'further', 'then', 'once', 'here', 'there', 'when', 'where', 'why', 'how', 'all', 'any', 'both', 'each', 'few', 'more', 'most', 'other', 'some', 'such', 'no', 'nor', 'not', 'only', 'own', 'same', 'so', 'than', 'too', 'very', 's', 't', 'can', 'will', 'just', 'don', 'should', 'now', 'd', 'll', 'm', 'o', 're', 've', 'y', 'ain', 'aren', 'couldn', 'didn', 'doesn', 'hadn', 'hasn', 'haven', 'isn', 'ma', 'mightn', 'mustn', 'needn', 'shan', 'shouldn', 'wasn', 'weren', 'won', 'wouldn']

Trong NLP, người ta thường làm 1 bước là lọc các từ gây nhiễu, và Stopwords chính là một trong số chúng. Vậy bây giờ ta sẽ cùng viết một chương trình để xem trong 1 văn bản, có bao nhiêu từ không phải là Stopwords nhé.

import nltk
def content_fraction(text):
    stopwords = nltk.corpus.stopwords.words('english')
    content = [w for w in text if w.lower() not in stopwords]
    return len(content) / len(text)
print(content_fraction(nltk.corpus.reuters.words()))

#Kết quả
0.735240435097661

Vậy là có khoảng 27% từ trong các bản tin của Reuters là Stopswords, còn lại 73% là các từ không phải Stopwords. Như vậy, với Wordlist và Stopwords của NLTK, ta đã loại bỏ được cơ số các từ sai chính tả, ít sử dụng và các từ mang ít ý nghĩa của văn bản.

Giải câu đố: Có bao nhiêu từ có từ 4 ký tự trở lên tạo thành từ các ký tự trong hình dưới. Trong đó, ký tự từ chỉ được dùng 1 lần trong 1 từ và mỗi từ phải chứa ký tự ở giữa (ở đây là "r"), và có ít nhất 1 từ có 9 ký tự. Không từ nào kết thúc bằng "s" và không từ ngoại ngữ, không phải tên riêng.

Cùng xem lời giải:

import nltk
puzzle_letter = nltk.FreqDist("egivrvoln")
obligatory = 'r'
wordlist = nltk.corpus.words.words()
result = [w for w in wordlist if len(w) >= 4 and obligatory in w and nltk.FreqDist(w) < puzzle_letter]
print(result)

Các bạn hãy thử diễn giải các hiểu của mình bằng cách đọc Code trên nhé.

Name Corpus

Có một Wordlist nữa mà NLTK cung cấp đó là Name Corpus, chưa hơn 8000 first names được phân loại theo giới tính. Bạn có thể xem ở đây

>>> names = nltk.corpus.names
>>> names.fileids()
['female.txt', 'male.txt']
>>> male_names = names.words('male.txt')
>>> female_names = names.words('female.txt')
>>> [w for w in male_names if w in female_names]
['Abbey', 'Abbie', 'Abby', 'Addie', 'Adrian', 'Adrien', 'Ajay', 'Alex', 'Alexis', 'Alfie', 'Ali', 'Alix', 'Allie', 'Allyn', 'Andie', 'Andrea', 'Andy', 'Angel', 'Angie', 'Ariel', 'Ashley', 'Aubrey', 'Augustine', 'Austin', 'Averil', ...]
Bài tập: Hãy thống kê và tạo biểu đồ cho biết số lượng tên của cả Nam và Nữ được kết thúc với mỗi ký tự của bảng chữ cái tiếng Anh.

A Pronouncing Dictionary

Hay từ điển phát âm, là một bảng chứa các từ và một vài đặc tính của nó trong mỗi dòng. NLTK cung cấp CMU Pronouncing Dictionary cho tiếng Anh, nó được thiết kế để sử dụng cho tổng hợp tiếng nói (speech synthesizers). Hãy thử in ra bảng này

import nltk
entries = nltk.corpus.cmudict.entries()
for entry in entries[42371:42379]:
    print(entry)
# Result
('fir', ['F', 'ER1'])
('fire', ['F', 'AY1', 'ER0'])
('fire', ['F', 'AY1', 'R'])
('firearm', ['F', 'AY1', 'ER0', 'AA2', 'R', 'M'])
('firearm', ['F', 'AY1', 'R', 'AA2', 'R', 'M'])
('firearms', ['F', 'AY1', 'ER0', 'AA2', 'R', 'M', 'Z'])
('firearms', ['F', 'AY1', 'R', 'AA2', 'R', 'M', 'Z'])
('fireball', ['F', 'AY1', 'ER0', 'B', 'AO2', 'L'])

Với mỗi từ, từ điển này cung cấp 1 danh sách các mã phát âm. Mỗi nhãn thì tượng trưng cho 1 cách phát âm. Ví dụ từ "fire" sẽ có 2 cách phát âm trong tiếng anh, một âm tiết sẽ ký hiệu là "F AY1 R" và 2 âm tiết là "F AY1 ER0". Các ký hiệu này được lấy từ Arpabet, cụ thể hơn bạn có thể đọc ở https://en.wikipedia.org/wiki/ARPABET. Mỗi mục bao gồm 2 phần, chúng ta có thể sử dụng vòng lặp sau để lấy ra các phần này, ở đây ta lấy ra các từ mà có 3 cách phát âm. Ví dụ với từ "pate", bạn có thể xem các cách phát âm ở đây https://en.oxforddictionaries.com/definition/pate

>>> for word, pron in entries: 
...     if len(pron) == 3: 
...         ph1, ph2, ph3 = pron 
...         if ph1 == 'P' and ph3 == 'T':
...             print(word, ph2, end=' ')
...
pait EY1 pat AE1 pate EY1 patt AE1 peart ER1 peat IY1 peet IY1 peete IY1 pert ER1
pet EH1 pete IY1 pett EH1 piet IY1 piette IY1 pit IH1 pitt IH1 pot AA1 pote OW1
pott AA1 pout AW1 puett UW1 purt ER1 put UH1 putt AH1

Bây giờ chúng ta hãy cùng tìm các từ có cách đọc kết thúc giống như từ "nicks", ta có thể sử dụng phương pháp dưới đây để tìm các từ có cùng vần.

>>> syllable = ['N', 'IH0', 'K', 'S']
>>> [word for word, pron in entries if pron[-4:] == syllable]
["atlantic's", 'audiotronics', 'avionics', 'beatniks', 'calisthenics', 'centronics',
'chamonix', 'chetniks', "clinic's", 'clinics', 'conics', 'conics', 'cryogenics',
'cynics', 'diasonics', "dominic's", 'ebonics', 'electronics', "electronics'", ...]

Bạn có thể tra cách phát âm 1 từ theo bảng ARPABET ở đây http://www.speech.cs.cmu.edu/cgi-bin/cmudict. Ta thấy rằng, một cách phát âm được đọc theo nhiều cách. Hãy cho biết đoạn Code dưới đây làm gì?

>>> [w for w, pron in entries if pron[-1] == 'M' and w[-1] == 'n']
['autumn', 'column', 'condemn', 'damn', 'goddamn', 'hymn', 'solemn']
>>> sorted(set(w[:2] for w, pron in entries if pron[0] == 'N' and w[0] != 'n'))
['gn', 'kn', 'mn', 'pn']

Cách phát âm thì có thêm cả một số cho biết cách nhấn âm. Trong đó 1 là trọng âm thứ nhất, 2 là trọng âm thứ 2 và 0 là không có trọng âm. Ta có thể xem ví dụ dưới đây

>>> def stress(pron):
...     return [char for phone in pron for char in phone if char.isdigit()]
>>> [w for w, pron in entries if stress(pron) == ['0', '1', '0', '2', '0']]
['abbreviated', 'abbreviated', 'abbreviating', 'accelerated', 'accelerating',
'accelerator', 'accelerators', 'accentuated', 'accentuating', 'accommodated',
'accommodating', 'accommodative', 'accumulated', 'accumulating', 'accumulative', ...]
>>> [w for w, pron in entries if stress(pron) == ['0', '2', '0', '1', '0']]
['abbreviation', 'abbreviations', 'abomination', 'abortifacient', 'abortifacients',
'academicians', 'accommodation', 'accommodations', 'accreditation', 'accreditations',
'accumulation', 'accumulations', 'acetylcholine', 'acetylcholine', 'adjudication', ...]

Đoạn mã trên lấy ra các từ có 5 âm tiết và đáp ứng được qui tắc về trọng âm. Ta có thể truy cập tới từng từ bằng cách.

>>> prondict = nltk.corpus.cmudict.dict()
>>> prondict['fire'] [1]
[['F', 'AY1', 'ER0'], ['F', 'AY1', 'R']]
>>> prondict['blog'] = [['B', 'L', 'AA1', 'G']] [3]
>>> prondict['blog']
[['B', 'L', 'AA1', 'G']]

Cuối cùng, như ta đã biết, cách phát âm của mỗi từ được lưu trong bảng Pronunciation Dictionary, do vậy, ta hãy cùng Convert một số từ sang dạng phát âm của nó trong bảng ARPABET

>>> text = ['natural', 'language', 'processing']
>>> [ph for w in text for ph in prondict[w][0]]
['N', 'AE1', 'CH', 'ER0', 'AH0', 'L', 'L', 'AE1', 'NG', 'G', 'W', 'AH0', 'JH', 'P', 'R', 'AA1', 'S', 'EH0', 'S', 'IH0', 'NG']

Đây cũng là cách mà các trình Text to Speech thường làm để phát âm từ mà chúng ta nhập vào.

Comparative Wordlists

Hay danh sách từ đối chiếu. NLTK có một wordlists là Swadesh, chứa khoảng 200 từ phổ biến của một vài ngôn ngữ. Ví dụ

>>> from nltk.corpus import swadesh
>>> swadesh.fileids()
['be', 'bg', 'bs', 'ca', 'cs', 'cu', 'de', 'en', 'es', 'fr', 'hr', 'it', 'la', 'mk',
'nl', 'pl', 'pt', 'ro', 'ru', 'sk', 'sl', 'sr', 'sw', 'uk']
>>> swadesh.words('en')
['I', 'you (singular), thou', 'he', 'we', 'you (plural)', 'they', 'this', 'that',
'here', 'there', 'who', 'what', 'where', 'when', 'how', 'not', 'all', 'many', 'some',
'few', 'other', 'one', 'two', 'three', 'four', 'five', 'big', 'long', 'wide', ...]

Ta có thể truy cập vào các từ cùng ý nghĩa, nhưng ở một ngôn ngữ khác bằng cách thêm các ngôn ngữ vào phương thức entries(). Và ta có thể làm một bộ từ điển đơn giản, đa ngôn ngữ bằng cách đơn giản như sau

>>> de2en = swadesh.entries(['de', 'en'])    # German-English
>>> es2en = swadesh.entries(['es', 'en'])    # Spanish-English
>>> translate.update(dict(de2en))
>>> translate.update(dict(es2en))
>>> translate['Hund']
'dog'
>>> translate['perro']
'dog'

Hoặc ta có thể so sánh các từ ở các ngôn ngữ khác nhau

>>> languages = ['en', 'de', 'nl', 'es', 'fr', 'pt', 'la']
>>> for i in [139, 140, 141, 142]:
...     print(swadesh.entries(languages)[i])
...
('say', 'sagen', 'zeggen', 'decir', 'dire', 'dizer', 'dicere')
('sing', 'singen', 'zingen', 'cantar', 'chanter', 'cantar', 'canere')
('play', 'spielen', 'spelen', 'jugar', 'jouer', 'jogar, brincar', 'ludere')
('float', 'schweben', 'zweven', 'flotar', 'flotter', 'flutuar, boiar', 'fluctuare')

OK. Ta kết thúc phần này ở đây, bài sau ta cùng tìm hiểu về WordNet.

DEHA AI Lab - Công ty cổ phần DEHA Việt Nam.