.. 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)*