14.6. Embedding từ con¶
Các từ tiếng Anh thường có những cấu những trúc nội tại và phương thức cấu thành. Chẳng hạn, ta có thể suy ra mối quan hệ giữa các từ “dog”, “dogs” và “dogcatcher” thông qua cách viết của chúng. Tất cả các từ đó có cùng từ gốc là “dog” nhưng có hậu tố khác nhau làm thay đổi nghĩa của từ. Hơn nữa, sự liên kết này có thể được mở rộng ra đối với các từ khác. Chẳng hạn, mối quan hệ giữa từ “dog” và “dogs” đơn giản giống như mối quan hệ giữa từ “cat” và “cats”. Mối quan hệ giữa từ “boy” và “boyfriend” đơn giản giống mối quan hệ giữa từ “girl” và “girlfriend”. Đặc tính này không phải là duy nhất trong tiếng Anh. Trong tiếng Pháp và Tây Ban Nha, rất nhiều động từ có thể có hơn 40 dạng khác nhau tùy thuộc vào ngữ cảnh. Trong tiếng Phần Lan, một danh từ có thể có hơn 15 dạng. Thật vậy, hình thái học (morphology) là một nhánh quan trọng của ngôn ngữ học chuyên nghiên cứu về cấu trúc và hình thái của các từ.
14.6.1. fastText¶
Trong word2vec, ta không trực tiếp sử dụng thông tin hình thái học. Trong cả mô hình skip-gram và túi từ (bag-of-word) liên tục, ta sử dụng các vector khác nhau để biểu diễn các từ ở các dạng khác nhau. Chẳng hạn, “dog” và “dogs” được biểu diễn bởi hai vector khác nhau, trong khi mối quan hệ giữa hai vector đó không biểu thị trực tiếp trong mô hình. Từ quan điểm này, fastText [Bojanowski et al., 2017] đề xuất phương thức embedding từ con (subword embedding), thông qua việc thực hiện đưa thông tin hình thái học vào trong mô hình skip-gram trong word2vec.
Trong fastText, mỗi từ trung tâm được biểu diễn như một tập hợp của các từ con. Dưới đây ta sử dụng từ “where” làm ví dụ để hiểu cách các từ tố được tạo thành. Trước hết, ta thêm một số ký tự đặc biệt “<” và “>” vào phần bắt đầu và kết thúc của từ để phân biệt các từ con được dùng làm tiền tố và hậu tố. Rồi ta sẽ xem từ này như một chuỗi các ký tự để trích xuất \(n\)-grams. Chẳng hạn, khi \(n=3\), ta có thể nhận tất cả từ tố với chiều dài là \(3\):
và từ con đặc biệt \(\textrm{"<where>"}\).
Trong fastText, với một từ \(w\), ta ghi tập hợp của tất cả các từ con của nó với chiều dài từ \(3\) đến \(6\) và các từ con đặc biệt là \(\mathcal{G}_w\). Do đó, từ điển này là tập hợp các từ con của tất cả các từ. Giả sử vector của từ con \(g\) trong từ điển này là \(\mathbf{z}_g\). Thì vector từ trung tâm \(\mathbf{u}_w\) cho từ \(w\) trong mô hình skip-gram có thể biểu diễn là
Phần còn lại của tiến trình xử lý trong fastText đồng nhất với mô hình skip-gram, vì vậy ta không mô tả lại ở đây. Như chúng ta có thể thấy, so sánh với mô hình skip-gram, từ điển của fastText lớn hơn dẫn tới nhiều tham số mô hình hơn. Hơn nữa, vector của một từ đòi hỏi tính tổng của tất cả vector từ con dẫn tới độ phức tạp tính toán cao hơn. Tuy nhiên, ta có thể thu được các vector tốt hơn cho nhiều từ phức hợp ít thông dụng, thậm chí cho cả các từ không hiện diện trong từ điển này nhờ tham chiếu tới các từ khác có cấu trúc tương tự.
14.6.2. Mã hoá cặp byte¶
Trong fastText, tất cả các từ con được trích xuất phải nằm trong khoảng độ dài cho trước, ví dụ như từ \(3\) đến \(6\), do đó kích thước bộ từ vựng không thể được xác định trước. Để cho phép các từ con có độ dài biến thiên trong bộ từ vựng có kích thước cố định, chúng ta có thể áp dụng thuật toán nén gọi là mã hoá cặp byte (Byte Pair Encoding -BPE) để trích xuất các từ con [Sennrich et al., 2015].
Mã hóa cặp byte thực hiện phân tích thống kê tập dữ liệu huấn luyện để tìm các ký hiệu chung trong một từ, chẳng hạn như các ký tự liên tiếp có độ dài tùy ý. Bắt đầu từ các ký hiệu có độ dài bằng \(1\), mã hóa cặp byte lặp đi lặp lại việc gộp các cặp ký hiệu liên tiếp thường gặp nhất để tạo ra các ký hiệu mới dài hơn. Lưu ý rằng để tăng hiệu năng, các cặp vượt qua ranh giới từ sẽ không được xét. Cuối cùng, chúng ta có thể sử dụng các ký hiệu đó như từ con để phân đoạn các từ. Mã hóa cặp byte và các biến thể của nó đã được sử dụng để biểu diễn đầu vào trong các mô hình tiền huấn luyện cho xử lý ngôn ngữ tự nhiên phổ biến như GPT-2 [Radford et al., 2019] và RoBERTa [Liu et al., 2019]. Tiếp theo, chúng tôi sẽ minh hoạ cách hoạt động của mã hoá cặp byte.
Đầu tiên, ta khởi tạo bộ từ vựng của các ký hiệu dưới dạng tất cả các ký
tự viết thường trong tiếng Anh và hai ký hiệu đặc biệt: ký hiệu kết thúc
của từ '_'
, và ký hiệu không xác định '[UNK]'
.
import collections
symbols = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'_', '[UNK]']
Vì không xét các cặp ký hiệu vượt qua ranh giới của các từ, chúng ta chỉ
cần một từ điển raw_token_freqs
ánh xạ các từ tới tần suất của chúng
(số lần xuất hiện) trong một tập dữ liệu. Lưu ý rằng ký hiệu đặc biệt
'_'
được thêm vào mỗi từ để có thể dễ dàng khôi phục chuỗi từ (ví
dụ: “a taller man”) từ chuỗi ký hiệu đầu ra (ví dụ: “a_ tall er_
man”). Vì chúng ta bắt đầu quá trình gộp một từ vựng chỉ gồm các ký tự
đơn và các ký hiệu đặc biệt, khoảng trắng được chèn giữa mọi cặp ký tự
liên tiếp trong mỗi từ (các khóa của từ điển token_freqs
). Nói cách
khác, khoảng trắng là ký tự phân cách (delimiter) giữa các ký hiệu
trong một từ.
raw_token_freqs = {'fast_': 4, 'faster_': 3, 'tall_': 5, 'taller_': 4}
token_freqs = {}
for token, freq in raw_token_freqs.items():
token_freqs[' '.join(list(token))] = raw_token_freqs[token]
token_freqs
{'f a s t _': 4, 'f a s t e r _': 3, 't a l l _': 5, 't a l l e r _': 4}
Chúng ta định nghĩa hàm get_max_freq_pair
trả về cặp ký hiệu liên
tiếp thường gặp nhất trong một từ, với từ là các khóa của từ điển đầu
vào token_freqs
.
def get_max_freq_pair(token_freqs):
pairs = collections.defaultdict(int)
for token, freq in token_freqs.items():
symbols = token.split()
for i in range(len(symbols) - 1):
# Key of `pairs` is a tuple of two consecutive symbols
pairs[symbols[i], symbols[i + 1]] += freq
return max(pairs, key=pairs.get) # Key of `pairs` with the max value
Là một thuật toán tham lam dựa trên tần suất của các ký hiệu liên tiếp
nhau, mã hoá cặp byte sẽ dùng hàm merge_symbols
để gộp cặp ký hiệu
thường gặp nhất để tạo ra những ký hiệu mới.
def merge_symbols(max_freq_pair, token_freqs, symbols):
symbols.append(''.join(max_freq_pair))
new_token_freqs = dict()
for token, freq in token_freqs.items():
new_token = token.replace(' '.join(max_freq_pair),
''.join(max_freq_pair))
new_token_freqs[new_token] = token_freqs[token]
return new_token_freqs
Bây giờ ta thực hiện vòng lặp giải thuật biểu diễn cặp byte với các khóa
của từ điển token_freqs
. Ở vòng lặp đầu tiên, cặp biểu tượng liền kề
có tần suất cao nhất là 't'
và 'a'
, do đó biểu diễn cặp byte
ghép chúng lại để tạo ra một biểu tượng mới là 'ta'
. Ở vòng lặp thứ
hai, biểu diễn cặp byte tiếp tục ghép 2 biểu tượng 'ta'
và 'l'
tạo ra một biểu tượng mới khác là 'tal'
.
num_merges = 10
for i in range(num_merges):
max_freq_pair = get_max_freq_pair(token_freqs)
token_freqs = merge_symbols(max_freq_pair, token_freqs, symbols)
print(f'merge #{i + 1}:', max_freq_pair)
merge #1: ('t', 'a')
merge #2: ('ta', 'l')
merge #3: ('tal', 'l')
merge #4: ('f', 'a')
merge #5: ('fa', 's')
merge #6: ('fas', 't')
merge #7: ('e', 'r')
merge #8: ('er', '_')
merge #9: ('tall', '_')
merge #10: ('fast', '_')
Sau 10 vòng lặp biểu diễn cặp byte, ta có thể thấy là danh sách
symbols
lúc này chứa hơn 10 biểu tượng đã được lần lượt ghép từ các
biểu tượng khác.
print(symbols)
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '_', '[UNK]', 'ta', 'tal', 'tall', 'fa', 'fas', 'fast', 'er', 'er_', 'tall_', 'fast_']
Với cùng tập dữ liệu đặc tả trong các khóa của từ điển
raw_token_freqs
, mỗi từ trong tập dữ liệu này bây giờ được phân đoạn
bởi các từ con là “fast_”, “fast”, “er_”, “tall_”, và “tall” theo
giải thuật biểu diễn cặp byte. Chẳng hạn, từ “faster_” và từ “taller_”
được phân đoạn lần lượt là “fast er_” và “tall er_”.
print(list(token_freqs.keys()))
['fast_', 'fast er_', 'tall_', 'tall er_']
Chú ý là kết quả của biểu diễn cặp byte tùy thuộc vào tập dữ liệu đang
được sử dụng. Ta cũng có thể dùng các từ con đã học từ một tập dữ liệu
để phân đoạn các từ của một tập dữ liệu khác. Với cách tiếp cận tham
lam, hàm segment_BPE
sau đây cố gắng tách các từ thành các từ con
dài nhất có thể từ đối số đầu vào symbols
.
def segment_BPE(tokens, symbols):
outputs = []
for token in tokens:
start, end = 0, len(token)
cur_output = []
# Segment token with the longest possible subwords from symbols
while start < len(token) and start < end:
if token[start: end] in symbols:
cur_output.append(token[start: end])
start = end
end = len(token)
else:
end -= 1
if start < len(token):
cur_output.append('[UNK]')
outputs.append(' '.join(cur_output))
return outputs
Trong phần tiếp theo, ta sử dụng các từ con trong danh sách symbols
đã được học từ tập dữ liệu ở trên để phân đoạn các tokens
biểu diễn
tập dữ liệu khác.
tokens = ['tallest_', 'fatter_']
print(segment_BPE(tokens, symbols))
['tall e s t _', 'fa t t er_']
14.6.3. Tóm tắt¶
- FastText đề xuất phương pháp embedding cho từ con. Dựa trên mô hình skip-gram trong word2vec, phương pháp này biểu diễn vector từ trung tâm thành tổng các vector từ con của từ đó.
- Embedding cho từ con sử dụng nguyên tắc trong hình thái học, thường giúp cải thiện chất lượng biểu diễn của các từ ít gặp.
- Mã hoá cặp byte thực hiện phân tích thống kê trên tập dữ liệu huấn luyện để phát hiện các ký hiệu chung trong một từ. Là một giải thuật tham lam, mã hoá cặp byte lần lượt gộp các cặp ký hiệu liên tiếp thường gặp nhất lại với nhau.
14.6.4. Bài tập¶
- Khi có quá nhiều từ con (ví dụ, 6 từ trong tiếng Anh có thể tạo ra \(3\times 10^8\) các tổ hợp khác nhau), vấn đề gì sẽ xảy ra? Bạn có thể giải quyết vấn đề trên không? Gợi ý: Tham khảo đoạn cuối phần 3.2 của bài báo fastText [1].
- Làm sao để thiết kế một mô hình embedding cho từ con dựa trên mô hình túi từ liên tục CBOW ?
- Để thu được bộ từ vựng có kích thước \(m\), bao nhiêu phép gộp cần được thực hiện khi bộ từ vựng ký hiệu ban đầu có kích thước là \(n\)?
- Ta có thể mở rộng ý tưởng của thuật toán mã hoá cặp byte để trích xuất các cụm từ bằng cách nào?
14.6.5. Thảo luận¶
- Tiếng Anh: MXNet
- Tiếng Việt: Diễn đàn Machine Learning Cơ Bản
14.6.6. Những người thực hiện¶
Bản dịch trong trang này được thực hiện bởi:
- Đoàn Võ Duy Thanh
- Nguyễn Văn Quang
- Nguyễn Lê Quang Nhật
- Lê Khắc Hồng Phúc
- Phạm Hồng Vinh
- Nguyễn Mai Hoàng Long
- Phạm Đăng Khoa
- Nguyễn Văn Cường
Lần cập nhật gần nhất: 12/09/2020. (Cập nhật lần cuối từ nội dung gốc: 30/06/2020)