.. raw:: html
.. _sec_bert:
Biểu diễn Mã hóa hai chiều từ Transformer (BERT)
================================================
.. raw:: html
Chúng tôi đã giới thiệu một vài mô hình embedding từ cho bài toán hiểu
ngôn ngữ tự nhiên. Sau khi tiền huấn luyện, đầu ra của các mô hình này
có thể xem là một ma trận trong đó mỗi hàng là một vector biểu diễn một
từ trong bộ từ vựng được định nghĩa trước. Trong thực tế, tất cả các mô
hình embedding từ này đều *độc lập ngữ cảnh* (*context-independent*).
Hãy bắt đầu bằng việc minh họa tính chất này.
.. raw:: html
Từ Độc lập Ngữ cảnh đến Nhạy Ngữ cảnh
-------------------------------------
.. raw:: html
Hãy nhớ lại các thí nghiệm trong :numref:`sec_word2vec_pretraining` và
:numref:`sec_synonyms`. Cả word2vec và GloVe đều gán cùng một vector
được tiền huấn luyện cho cùng một từ bất kể ngữ cảnh (nếu có) của nó như
thế nào. Về mặt hình thức, biểu diễn độc lập ngữ cảnh của một token bất
kỳ :math:`x` là một hàm :math:`f(x)` chỉ nhận :math:`x` làm đầu vào. Do
hiện tượng đa nghĩa cũng như sự phức tạp ngữ nghĩa xuất hiện khá phổ
biến trong ngôn ngữ tự nhiên, biểu diễn độc lập ngữ cảnh có những hạn
chế rõ ràng. Ví dụ, từ “crane” trong ngữ cảnh “a crane is flying (một
con sếu đang bay)” và ngữ cảnh “a crane driver came (tài xế xe cần cẩu
đã tới)” có nghĩa hoàn toàn khác nhau; do đó, cùng một từ nên được gán
các biểu diễn khác nhau tùy ngữ cảnh.
.. raw:: html
Điều này thúc đẩy sự phát triển của các biểu diễn từ *nhạy ngữ cảnh*
(*context-sensitive*), trong đó biểu diễn của từ phụ thuộc vào ngữ cảnh
của từ đó. Do đó, biểu diễn nhạy ngữ cảnh của một token bất kỳ :math:`x`
là hàm :math:`f(x, c(x))` phụ thuộc vào cả từ :math:`x` lẫn ngữ cảnh của
từ :math:`c(x)`. Các biểu diễn nhạy ngữ cảnh phổ biến bao gồm TagLM (Bộ
Tag chuỗi được tăng cường với mô hình ngôn ngữ
(*language-model-augmented sequence tagger*))
:cite:`Peters.Ammar.Bhagavatula.ea.2017`, CoVe (vector ngữ cảnh
(*Context Vectors*)) :cite:`McCann.Bradbury.Xiong.ea.2017`, và ELMo
(embedding từ các mô hình ngôn ngữ (*Embeddings from Language Models*))
:cite:`Peters.Neumann.Iyyer.ea.2018`.
.. raw:: html
Ví dụ, bằng cách lấy toàn bộ chuỗi làm đầu vào, ELMo gán một biểu diễn
cho mỗi từ trong chuỗi đầu vào. Cụ thể, ELMo kết hợp tất cả các biểu
diễn tầng trung gian từ LSTM hai chiều đã được tiền huấn luyện làm biểu
diễn đầu ra. Sau đó, biểu diễn ELMo sẽ được đưa vào một mô hình học có
giám sát cho các tác vụ xuôi dòng như một đặc trưng bổ sung, chẳng hạn
bằng cách nối biểu diễn ELMo và biểu diễn gốc (ví dụ như GloVe) của
token trong mô hình hiện tại. Một mặt, tất cả các trọng số trong mô hình
LSTM hai chiều được tiền huấn luyện đều bị đóng băng sau khi các biểu
diễn ELMo được thêm vào. Mặt khác, mô hình học có giám sát được tùy biến
cụ thể cho một tác vụ nhất định. Tại thời điểm được công bố, thêm ELMo
vào các mô hình tân tiến nhất giúp cải thiện chất lượng các mô hình này
trên sáu tác vụ xử lý ngôn ngữ tự nhiên: phân tích cảm xúc (*sentiment
analysis*), suy luận ngôn ngữ tự nhiên (*natural language inference*),
gán nhãn vai trò ngữ nghĩa (*semantic role labeling*), phân giải đồng
tham chiếu (*coreference resolution*), nhận dạng thực thể có tên (*named
entity recognition*) và trả lời câu hỏi (*question answering*).
.. raw:: html
Từ Đặc thù Tác vụ đến Không phân biệt Tác vụ
--------------------------------------------
.. raw:: html
Mặc dù ELMo đã cải thiện đáng kể giải pháp cho một loạt các tác vụ xử lý
ngôn ngữ tự nhiên, mỗi giải pháp vẫn dựa trên một kiến trúc *đặc thù
cho tác vụ* (*task-specific*). Tuy nhiên trong thực tế, xây dựng một
kiến trúc đặc thù cho mỗi tác vụ xử lý ngôn ngữ tự nhiên là điều không
đơn giản. Phương pháp GPT (Generative Pre-Training) thể hiện nỗ lực
thiết kế một mô hình *không phân biệt tác vụ* (*task-agnostic*) chung
cho các biểu diễn nhạy ngữ cảnh
:cite:`Radford.Narasimhan.Salimans.ea.2018`. Được xây dựng dựa trên bộ
giải mã Transformer, GPT tiền huấn luyện mô hình ngôn ngữ được sử dụng
để biểu diễn chuỗi văn bản. Khi áp dụng GPT cho một tác vụ xuôi dòng,
đầu ra của mô hình ngôn ngữ sẽ được truyền tới một tầng đầu ra tuyến
tính được bổ sung để dự đoán nhãn cho tác vụ đó. Trái ngược hoàn toàn
với cách ELMo đóng băng các tham số của mô hình tiền huấn luyện, GPT
tinh chỉnh *tất cả* các tham số trong bộ giải mã Transformer tiền huấn
luyện trong suốt quá trình học có giám sát trên tác vụ xuôi dòng. GPT
được đánh giá trên mười hai tác vụ về suy luận ngôn ngữ tự nhiên, trả
lời câu hỏi, độ tương tự của câu, và bài toán phân loại, và cải thiện
kết quả tân tiến nhất của chín tác vụ với vài thay đổi tối thiểu trong
kiến trúc mô hình.
.. raw:: html
Tuy nhiên, do tính chất tự hồi quy của các mô hình ngôn ngữ, GPT chỉ
nhìn theo chiều xuôi (từ trái sang phải). Trong các ngữ cảnh “I went to
the bank to deposit cash” (“tôi đến ngân hàng để gửi tiền”) và “I went
to the bank to sit down”(“tôi ra bờ hồ ngồi”), do từ “bank” nhạy với ngữ
cảnh bên trái, GPT sẽ trả về cùng một biểu diễn cho từ “bank”, mặc dù nó
có nghĩa khác nhau.
.. raw:: html
BERT: Kết hợp những Điều Tốt nhất của Hai Phương pháp
-----------------------------------------------------
.. raw:: html
Như ta đã thấy, ELMo mã hóa ngữ cảnh hai chiều nhưng sử dụng các kiến
trúc đặc thù cho từng tác vụ; trong khi đó GPT có kiến trúc không phân
biệt tác vụ nhưng mã hóa ngữ cảnh từ trái sang phải. Kết hợp những điều
tốt nhất của hai phương pháp trên, BERT (biểu diễn mã hóa hai chiều từ
Transformer - *Bidirectional Encoder Representations from Transformers*)
mã hóa ngữ cảnh theo hai chiều và chỉ yêu cầu vài thay đổi kiến trúc
tối thiểu cho một loạt các tác vụ xử lý ngôn ngữ tự nhiên
:cite:`Devlin.Chang.Lee.ea.2018`. Sử dụng bộ mã hóa Transformer được
tiền huấn luyện, BERT có thể biểu diễn bất kỳ token nào dựa trên ngữ
cảnh hai chiều của nó. Trong quá trình học có giám sát trên các tác vụ
xuôi dòng, BERT tương tự như GPT ở hai khía cạnh. Đầu tiên, các biểu
diễn BERT sẽ được truyền vào một tầng đầu ra được bổ sung, với những
thay đổi tối thiểu tới kiến trúc mô hình tùy thuộc vào bản chất của
tác vụ, chẳng hạn như dự đoán cho mỗi token hay dự đoán cho toàn bộ
chuỗi. Thứ hai, tất cả các tham số của bộ mã hóa Transformer đã tiền
huấn luyện đều được tinh chỉnh, trong khi tầng đầu ra bổ sung sẽ được
huấn luyện từ đầu. :numref:`fig_elmo-gpt-bert` mô tả những điểm khác
biệt giữa ELMo, GPT, và BERT.
.. raw:: html
.. _fig_elmo-gpt-bert:
.. figure:: ../img/elmo-gpt-bert.svg
So sánh giữa ELMO, GPT, và BERT.
.. raw:: html
BERT cải thiện kết quả tân tiến nhất đối với mười một tác vụ xử lý ngôn
ngữ tự nhiên trải khắp các hạng mục gồm: i) phân loại văn bản đơn (như
phân tích cảm xúc), ii) phân loại cặp văn bản (như suy luận ngôn ngữ tự
nhiên), iii) trả lời câu hỏi, và iv) gán thẻ văn bản (như nhận dạng thực
thể có tên). Tất cả các kỹ thuật được đề xuất trong năm 2018, từ ELMo
nhạy ngữ cảnh cho tới GPT không phân biệt tác vụ và BERT, tuy về ý tưởng
đều đơn giản nhưng trên thực nghiệm là những phương pháp tiền huấn luyện
hiệu quả cho các biểu diễn sâu của ngôn ngữ tự nhiên, và đã mang đến
những giải pháp mang tính cách mạng cho nhiều tác vụ xử lý ngôn ngữ tự
nhiên.
.. raw:: html
Ở phần còn lại của chương này, ta sẽ đi sâu vào tiền huấn luyện BERT.
Sau khi những ứng dụng xử lý ngôn ngữ tự nhiên đã được giải thích trong
:numref:`chap_nlp_app`, ta sẽ minh họa việc tinh chỉnh BERT cho các
ứng dụng xuôi dòng.
.. code:: python
from d2l import mxnet as d2l
from mxnet import gluon, np, npx
from mxnet.gluon import nn
npx.set_np()
.. raw:: html
.. _subsec_bert_input_rep:
Biểu diễn Đầu vào
-----------------
.. raw:: html
Trong xử lý ngôn ngữ tự nhiên, một số nhiệm vụ (như phân tích cảm xúc)
lấy một câu văn làm đầu vào, trong khi một số tác vụ khác (như suy diễn
ngôn ngữ tự nhiên), đầu vào là một cặp chuỗi văn bản. Chuỗi đầu vào BERT
biểu diễn một cách tường minh cả văn bản đơn và cặp văn bản. Với văn bản
đơn, chuỗi đầu vào BERT là sự ghép nối của token phân loại đặc biệt
“”, token của chuỗi văn bản, và token phân tách đặc biệt “”.
Với cặp văn bản, chuỗi đầu vào BERT là sự ghép nối của “”, token
của chuỗi văn bản đầu, “”, token của chuỗi văn bản thứ hai, và
“”. Ta sẽ phân biệt nhất quán thuật ngữ “chuỗi đầu vào BERT” với
các kiểu “chuỗi” khác. Chẳng hạn, một *chuỗi đầu vào BERT* có thể bao
gồm cả *một chuỗi văn bản* hoặc *hai chuỗi văn bản*.
.. raw:: html
Để phân biệt cặp văn bản, các embedding đoạn đã học :math:`\mathbf{e}_A`
và :math:`\mathbf{e}_B` được cộng tương ứng vào các embedding token của
chuỗi thứ nhất và chuỗi thứ hai. Đối với đầu vào là văn bản đơn, ta chỉ
sử dụng :math:`\mathbf{e}_A`.
.. raw:: html
Hàm ``get_tokens_and_segments`` sau đây có thể lấy một hoặc hai câu làm
đầu vào, rồi trả về các token của chuỗi đầu vào BERT và các ID đoạn
tương ứng của chúng.
.. code:: python
#@save
def get_tokens_and_segments(tokens_a, tokens_b=None):
tokens = [''] + tokens_a + ['']
# 0 and 1 are marking segment A and B, respectively
segments = [0] * (len(tokens_a) + 2)
if tokens_b is not None:
tokens += tokens_b + ['']
segments += [1] * (len(tokens_b) + 1)
return tokens, segments
.. raw:: html
Kiến trúc hai chiều của BERT là bộ mã hóa Transformer. Thông thường
trong bộ mã hóa Transformer, các embedding vị trí được cộng vào mỗi vị
trí của chuỗi đầu vào BERT. Tuy nhiên, khác với bộ mã hóa Transformer
nguyên bản, BERT sử dụng các embedding vị trí *có thể học được*.
:numref:`fig_bert-input` cho thấy các embedding của chuỗi đầu vào BERT
là tổng các embedding của token, embedding đoạn và embedding vị trí.
.. raw:: html
.. _fig_bert-input:
.. figure:: ../img/bert-input.svg
Embedding của chuỗi đầu vào BERT là tổng các embedding của token,
embedding đoạn và embedding vị trí.
.. raw:: html
Lớp ``BERTEncoder`` dưới đây tương tự như lớp ``TransformerEncoder``
trong :numref:`sec_transformer`. Khác với ``TransformerEncoder``,
``BERTEncoder`` sử dụng các embedding đoạn và các embedding vị trí có
thể học được.
.. code:: python
#@save
class BERTEncoder(nn.Block):
def __init__(self, vocab_size, num_hiddens, ffn_num_hiddens, num_heads,
num_layers, dropout, max_len=1000, **kwargs):
super(BERTEncoder, self).__init__(**kwargs)
self.token_embedding = nn.Embedding(vocab_size, num_hiddens)
self.segment_embedding = nn.Embedding(2, num_hiddens)
self.blks = nn.Sequential()
for _ in range(num_layers):
self.blks.add(d2l.EncoderBlock(
num_hiddens, ffn_num_hiddens, num_heads, dropout, True))
# In BERT, positional embeddings are learnable, thus we create a
# parameter of positional embeddings that are long enough
self.pos_embedding = self.params.get('pos_embedding',
shape=(1, max_len, num_hiddens))
def forward(self, tokens, segments, valid_lens):
# Shape of `X` remains unchanged in the following code snippet:
# (batch size, max sequence length, `num_hiddens`)
X = self.token_embedding(tokens) + self.segment_embedding(segments)
X = X + self.pos_embedding.data(ctx=X.ctx)[:, :X.shape[1], :]
for blk in self.blks:
X = blk(X, valid_lens)
return X
.. raw:: html
Giả sử kích thước bộ từ vựng là 10,000. Để minh họa suy luận xuôi của
``BERTEncoder``, hãy tạo ra một thực thể của nó và khởi tạo các thông
số.
.. code:: python
vocab_size, num_hiddens, ffn_num_hiddens, num_heads = 10000, 768, 1024, 4
num_layers, dropout = 2, 0.2
encoder = BERTEncoder(vocab_size, num_hiddens, ffn_num_hiddens, num_heads,
num_layers, dropout)
encoder.initialize()
.. raw:: html
Ta định nghĩa ``tokens`` là hai chuỗi đầu vào BERT có độ dài là 8, mỗi
token là một chỉ mục của bộ từ vựng. Lượt suy luận xuôi của
``BERTEncoder`` với đầu vào ``tokens`` trả về kết quả được mã hóa, với
mỗi token được biểu diễn bởi một vector có chiều dài được định nghĩa
trước bởi siêu tham số ``num_hiddens``, là *kích thước ẩn* (số lượng nút
ẩn) của bộ mã hóa Transformer.
.. code:: python
tokens = np.random.randint(0, vocab_size, (2, 8))
segments = np.array([[0, 0, 0, 0, 1, 1, 1, 1], [0, 0, 0, 1, 1, 1, 1, 1]])
encoded_X = encoder(tokens, segments, None)
encoded_X.shape
.. parsed-literal::
:class: output
(2, 8, 768)
.. raw:: html
.. _subsec_bert_pretraining_tasks:
Những tác vụ Tiền huấn luyện
----------------------------
.. raw:: html
Suy luận xuôi của ``BERTEncoder`` cho ra biểu diễn BERT của mỗi token
của văn bản đầu vào và các token đặc biệt được thêm vào “” và
“”. Kế tiếp, ta sẽ sử dụng các biểu diễn này để tính toán hàm mất
mát khi tiền huấn luyện BERT. Tiền huấn luyện gồm hai tác vụ: mô hình
ngôn ngữ có mặt nạ (*masked language modeling*) và dự đoán câu tiếp
theo.
.. raw:: html
.. _subsec_mlm:
Mô hình Ngôn ngữ có Mặt nạ
~~~~~~~~~~~~~~~~~~~~~~~~~~
.. raw:: html
Như mô tả trong :numref:`sec_language_model`, một mô hình ngôn ngữ dự
đoán một token bằng cách sử dụng ngữ cảnh phía bên trái của nó. Để mã
hóa ngữ cảnh hai chiều khi biểu diễn mỗi token, BERT ngẫu nhiên che mặt
nạ các token và sử dụng các token lấy từ ngữ cảnh hai chiều để dự đoán
các token mặt nạ đó. Tác vụ này được gọi là *mô hình hóa ngôn ngữ có mặt
nạ*.
.. raw:: html
Trong tác vụ tiền huấn luyện này, 15% số token sẽ được lựa chọn ngẫu
nhiên để làm các token mặt nạ cho việc dự đoán. Để dự đoán một token mặt
nạ mà không sử dụng nhãn, một hướng tiếp cận đơn giản là luôn luôn thay
thế nó bằng token đặc biệt “” trong chuỗi đầu vào BERT. Tuy nhiên,
token “” sẽ không bao giờ xuất hiện khi tinh chỉnh. Để tránh sự
không đồng nhất giữa tiền huấn luyện và tinh chỉnh, nếu một token được
che mặt nạ để dự đoán (ví dụ, từ “great” được chọn để che mặt nạ và dự
đoán trong câu “this movie is great”), trong đầu vào nó sẽ được thay thế
bởi:
.. raw:: html
- token đặc biệt “”, 80% số lần (ví dụ, “this movie is great” trở
thành “this movie is ”);
- token ngẫu nhiên, 10% số lần (ví dụ, “this movie is great” trở thành
“this movie is drink”);
- chính token đó, 10% số lần (ví dụ, “this movie is great” trở thành
“this movie is great”).
.. raw:: html
Lưu ý rằng trong 15% token được chọn để che mặt nạ, 10% số token đó sẽ
được thay thế bằng một token ngẫu nhiên. Việc thi thoảng thêm nhiễu sẽ
giúp BERT giảm thiên kiến về phía token có mặt nạ (đặc biệt khi token
nhãn không đổi) khi mã hóa ngữ cảnh hai chiều.
.. raw:: html
Ta lập trình lớp ``MaskLM`` sau để dự đoán token có mặt nạ trong tác vụ
mô hình hóa ngôn ngữ có mặt nạ khi tiền huấn luyện BERT. MLP một-tầng-ẩn
(``self.mlp``) được dùng cho việc dự đoán. Lượt suy luận xuôi nhận hai
đầu vào: kết quả mã hóa của ``BERTEncoder`` và vị trí token để dự đoán.
Đầu ra là kết quả dự đoán tại các vị trí này.
.. code:: python
#@save
class MaskLM(nn.Block):
def __init__(self, vocab_size, num_hiddens, **kwargs):
super(MaskLM, self).__init__(**kwargs)
self.mlp = nn.Sequential()
self.mlp.add(
nn.Dense(num_hiddens, flatten=False, activation='relu'))
self.mlp.add(nn.LayerNorm())
self.mlp.add(nn.Dense(vocab_size, flatten=False))
def forward(self, X, pred_positions):
num_pred_positions = pred_positions.shape[1]
pred_positions = pred_positions.reshape(-1)
batch_size = X.shape[0]
batch_idx = np.arange(0, batch_size)
# Suppose that `batch_size` = 2, `num_pred_positions` = 3, then
# `batch_idx` is `np.array([0, 0, 0, 1, 1, 1])`
batch_idx = np.repeat(batch_idx, num_pred_positions)
masked_X = X[batch_idx, pred_positions]
masked_X = masked_X.reshape((batch_size, num_pred_positions, -1))
mlm_Y_hat = self.mlp(masked_X)
return mlm_Y_hat
.. raw:: html
Để minh họa lượt suy luận xuôi của ``MaskLM``, ta sẽ khởi tạo một thực
thể ``mlm``. Hãy nhớ lại rằng ``encoded_X`` từ lượt suy luận xuôi của
``BERTEncoder`` biểu diễn 2 chuỗi đầu vào BERT. Ta định nghĩa
``mlm_positions`` là 3 chỉ số để dự đoán ở một trong hai chuỗi đầu vào
BERT của ``encoded_X``. Lượt suy luận xuôi của ``mlm`` trả về kết quả dự
đoán ``mlm_Y_hat`` tại tất cả các vị trí mặt nạ ``mlm_positions`` của
``encoded_X``. Với mỗi dự đoán, kích thước của kết quả bằng với kích
thước bộ từ vựng.
.. code:: python
mlm = MaskLM(vocab_size, num_hiddens)
mlm.initialize()
mlm_positions = np.array([[1, 5, 2], [6, 1, 5]])
mlm_Y_hat = mlm(encoded_X, mlm_positions)
mlm_Y_hat.shape
.. parsed-literal::
:class: output
(2, 3, 10000)
.. raw:: html
Với nhãn gốc ``mlm_Y`` của token có mặt nạ được dự đoán ``mlm_Y_hat``,
ta có thể tính mất mát entropy chéo của tác vụ mô hình hóa ngôn ngữ có
mặt nạ trong quá trình tiền huấn luyện BERT.
.. code:: python
mlm_Y = np.array([[7, 8, 9], [10, 20, 30]])
loss = gluon.loss.SoftmaxCrossEntropyLoss()
mlm_l = loss(mlm_Y_hat.reshape((-1, vocab_size)), mlm_Y.reshape(-1))
mlm_l.shape
.. parsed-literal::
:class: output
(6,)
.. raw:: html
.. _subsec_nsp:
Dự đoán Câu tiếp theo
~~~~~~~~~~~~~~~~~~~~~
.. raw:: html
Mặc dù mô hình hóa ngôn ngữ có mặt nạ có thể mã hóa ngữ cảnh hai chiều
để biểu diễn từ ngữ, nó không thể mô hình hóa các mối quan hệ logic giữa
các cặp văn bản một cách tường minh. Để hiểu hơn về mối quan hệ giữa hai
chuỗi văn bản, BERT sử dụng tác vụ phân loại nhị phân, *dự đoán câu tiếp
theo* (*next sentence prediction*) trong quá trình tiền huấn luyện. Khi
sinh các cặp câu cho quá trình tiền huấn luyện, một nửa trong số đó là
các cặp câu liên tiếp nhau trong thực tế và được gán nhãn “Đúng”
(*True*); và trong nửa còn lại, câu thứ hai được lấy mẫu ngẫu nhiên từ
kho ngữ liệu và cặp này được gán nhãn “Sai” (*False*).
.. raw:: html
Lớp ``NextSentencePred`` dưới đây sử dụng MLP một tầng ẩn để dự đoán câu
thứ hai có phải là câu kế tiếp của câu thứ nhất trong chuỗi đầu vào BERT
hay không. Do cơ chế tự tập trung trong bộ mã hóa Transformer, biểu diễn
BERT của token đặc biệt “” mã hóa cả hai câu đầu vào. Vì vậy, tầng
đầu ra (``self.output``) của bộ phân loại MLP nhận đầu vào ``X`` là đầu
ra của tầng ẩn MLP có đầu vào là token được mã hóa “”.
.. code:: python
#@save
class NextSentencePred(nn.Block):
def __init__(self, **kwargs):
super(NextSentencePred, self).__init__(**kwargs)
self.output = nn.Dense(2)
def forward(self, X):
# `X` shape: (batch size, `num_hiddens`)
return self.output(X)
.. raw:: html
Ta có thể thấy lượt suy luận xuôi của thực thể ``NextSentencePred`` trả
về dự đoán nhị phân cho mỗi chuỗi đầu vào BERT.
.. code:: python
nsp = NextSentencePred()
nsp.initialize()
nsp_Y_hat = nsp(encoded_X)
nsp_Y_hat.shape
.. parsed-literal::
:class: output
(2, 2)
.. raw:: html
Mất mát entropy chéo của 2 tác vụ phân loại nhị phân có thể được tính
như sau.
.. code:: python
nsp_y = np.array([0, 1])
nsp_l = loss(nsp_Y_hat, nsp_y)
nsp_l.shape
.. parsed-literal::
:class: output
(2,)
.. raw:: html
Đáng chú ý là tất cả nhãn trong hai tác vụ tiền huấn luyện nói trên đều
có thể thu được từ kho ngữ liệu tiền huấn luyện mà không cần công sức
gán nhãn thủ công. Phiên bản gốc của BERT được tiền huấn luyện trên cả
hai kho ngữ liệu BookCorpus :cite:`Zhu.Kiros.Zemel.ea.2015` và
Wikipedia tiếng Anh. Hai kho ngữ liệu văn bản này cực kỳ lớn, chứa lần
lượt khoảng 800 triệu từ và 2.5 tỉ từ.
.. raw:: html
Kết hợp Tất cả lại
------------------
.. raw:: html
Khi tiền huấn luyện BERT, hàm mất mát cuối cùng là tổ hợp tuyến tính của
cả hai hàm mất mát trong tác vụ mô hình hóa ngôn ngữ có mặt nạ và dự
đoán câu tiếp theo. Bây giờ ta có thể định nghĩa lớp ``BERTModel`` bằng
cách khởi tạo ba lớp ``BERTEncoder``, ``MaskLM``, và
``NextSentencePred``. Lượt suy luận xuôi trả về biểu diễn BERT được mã
hóa ``encoded_X``, các dự đoán ``mlm_Y_hat`` của tác vụ mô hình hóa ngôn
ngữ có mặt nạ, và ``nsp_Y_hat`` của tác vụ dự đoán câu tiếp theo.
.. code:: python
#@save
class BERTModel(nn.Block):
def __init__(self, vocab_size, num_hiddens, ffn_num_hiddens, num_heads,
num_layers, dropout, max_len=1000):
super(BERTModel, self).__init__()
self.encoder = BERTEncoder(vocab_size, num_hiddens, ffn_num_hiddens,
num_heads, num_layers, dropout, max_len)
self.hidden = nn.Dense(num_hiddens, activation='tanh')
self.mlm = MaskLM(vocab_size, num_hiddens)
self.nsp = NextSentencePred()
def forward(self, tokens, segments, valid_lens=None, pred_positions=None):
encoded_X = self.encoder(tokens, segments, valid_lens)
if pred_positions is not None:
mlm_Y_hat = self.mlm(encoded_X, pred_positions)
else:
mlm_Y_hat = None
# The hidden layer of the MLP classifier for next sentence prediction.
# 0 is the index of the '' token
nsp_Y_hat = self.nsp(self.hidden(encoded_X[:, 0, :]))
return encoded_X, mlm_Y_hat, nsp_Y_hat
Tóm tắt
-------
.. raw:: html
- Các mô hình embedding từ như word2vec và GloVe có tính chất độc lập
với ngữ cảnh. Hai mô hình này gán cùng một vector được tiền huấn
luyện cho cùng một từ bất kể ngữ cảnh xung quanh của từ đó là gì (nếu
có). Do đó, rất khó để các mô hình này xử lý tốt các trường hợp phức
tạp về ngữ nghĩa hay đa nghĩa trong các ngôn ngữ tự nhiên.
- Đối với các biểu diễn từ nhạy ngữ cảnh như ELMo và GPT, biểu diễn của
từ phụ thuộc vào ngữ cảnh của từ đó.
- ELMo mã hóa ngữ cảnh theo hai chiều nhưng sử dụng kiến trúc đặc thù
cho tác vụ (tuy nhiên, trên thực tế không dễ để tạo ra một kiến
trúc đặc thù cho mọi tác vụ xử lý ngôn ngữ tự nhiên); trong khi đó
GPT không phân biệt tác vụ nhưng chỉ mã hóa ngữ cảnh theo chiều từ
trái sang phải.
- BERT kết hợp những gì tốt nhất của cả hai mô hình trên: mã hóa ngữ
cảnh theo hai chiều và chỉ yêu cầu những thay đổi kiến trúc tối
thiểu cho một loạt các tác vụ xử lý ngôn ngữ tự nhiên.
- Các embedding của chuỗi đầu vào BERT là tổng các embedding cho token,
embedding đoạn và embedding vị trí.
- Quá trình tiền huấn luyện BERT gồm có hai tác vụ: tác vụ mô hình hóa
ngôn ngữ có mặt nạ và tác vụ dự đoán câu tiếp theo. Tác vụ đầu có thể
mã hóa ngữ cảnh hai chiều để biểu diễn từ, trong khi tác vụ sau mô
hình hóa mối quan hệ logic giữa các cặp văn bản một cách tường minh.
Bài tập
-------
.. raw:: html
1. Tại sao BERT lại gặt hái được thành công?
2. Giữ nguyên các yếu tố khác, liệu một mô hình ngôn ngữ có mặt nạ sẽ
đòi hỏi số bước tiền huấn luyện nhiều hơn hay ít hơn để hội tụ so với
mô hình ngôn ngữ từ trái sang phải. Tại sao?
3. Trong mã nguồn gốc của BERT, mạng truyền xuôi theo vị trí
(*position-wise feed-forward network*) trong ``BERTEncoder`` (thông
qua ``d2l.EncoderBlock``) và tầng kết nối đầy đủ trong ``MaskLM`` đều
sử dụng Đơn vị lỗi tuyến tính Gauss (*Gaussian error linear unit*
(GELU)) :cite:`Hendrycks.Gimpel.2016` làm hàm kích họat. Hãy nghiên
cứu sự khác biệt giữa GELU và ReLU.
Thảo luận
---------
- Tiếng Anh: `MXNet `__
- Tiếng Việt: `Diễn đàn Machine Learning Cơ
Bản `__
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 Mai Hoàng Long
- Trần Yến Thy
- Lê Khắc Hồng Phúc
- Phạm Hồng Vinh
- Phạm Minh Đức
- 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:
01/07/2020)*