Cơ chế Attention và mô hình Transformer

Giới thiệu cơ chế Attention và các biến thể, mô hình Transformer trong bài toán Machine Translation

Note. Nội dung bài viết này phần lớn được trích từ báo cáo đồ án môn học Học thống kê của nhóm mình và bạn Trần Hữu Thiên tại HCMUS, với chủ đề là English-Vietnamese Machine Translation. Các bạn có thể xem mã nguồn của đồ án này tại đây 🚀

Trong bài viết trước, mình đã giới thiệu về bài toán Machine Translation cùng một mô hình khá quen thuộc trong bài toán đó là Seq2seq. Trong giai đoạn trước năm 2017, các mô hình Deep Learning được xây dựng cho bài toán Machine Translation thường được tổ chức theo kiến trúc Encoder-Decoder như Seq2seq với phần lõi là các biến thể cải tiến hơn của Recurrent Neural Network (RNN). Tuy nhiên, những mô hình NTM đó đều có một số hạn chế nhất định khi thực hiện dịch những câu có câu trúc phức tạp hoặc có độ dài lớn, mà thường thấy nhất là hạn chế về độ phức tạp tính toán và việc ghi nhớ quan hệ phụ thuộc giữa các từ trong câu về mặt ngữ nghĩa. Lý do của những hạn chế này phần lớn đến từ thao tác tính toán, xử lý tuần tự các từ (hay là token) của RNN.

Transformer, một mô hình được công bố vào năm 2017 đã tạo ra sự đột phá lớn trong lĩnh vực NLP với độ chính xác cũng như tốc độ xử lý đều vượt xa những mô hình Deep Learning trước đó. Dù vẫn tổ chức mô hình theo kiến trúc Encoder-Decoder thông dụng nhưng Transformer đã khắc phục được những nhược điểm của các mô hình trước nhờ vào việc tận dụng tối đa sức mạnh của cơ chế Attention với hai phép toán Self-AttentionCross-Attention. Kể từ đó, mô hình Transformer đã trở thành một tiêu chuẩn mới trong bài toán Machine Translation nói riêng và trong nhiều bài toán thuộc lĩnh vực NLP nói chung. Không dừng lại ở đó, ý tưởng từ kiến trúc của Transformer cũng đã được áp dụng một cách rất thành công ở các bài toán thuộc lĩnh vực khác như Computer Vision.

Trong bài viết này, mình sẽ trình bày về Transformer cùng cơ chế Attention (tổng quan và sau đó đi vào cụ thể với Self-Attention và Cross-Attention). Nội dung của bài này sẽ là rất dài.

Tổng quan về kiến trúc Transformer

Mô hình Transformer có kiến trúc được tổ chức dạng Encoder-Decoder với phần lõi là cơ chế Attention. Tổng quan kiến trúc của mô hình này được thể hiện trong hình bên dưới, trong đó:

  • Encoder có vai trò chính là học mối tương quan giữa các từ với nhau trong câu thuộc ngôn ngữ nguồn (cụ thể hơn là giữa từng từ với các từ còn lại trong câu). Ta có thể hiểu đây như là việc ta đọc một câu Tiếng Anh và cố gắng hiểu ý nghĩa của câu đó.
  • Decoder sẽ lần lượt sinh ra các từ cho câu thuộc ngôn ngữ đích. Trong quá trình sinh từ này thì Decoder sẽ vừa để ý đến các từ nó đã sinh ra trước đó và vừa để ý đến một số từ liên quan trong câu ngôn ngữ nguồn để dịch cho đúng. Đây cũng có thể xem là sau khi đã hiểu ý nghĩa của câu Tiếng Anh rồi thì ta từng bước dịch câu đó ra Tiếng Việt sao cho trôi chảy, mạch lạc.

Tổng quan kiến trúc của mô hình Transformer

Ta sẽ lần lượt đề cập đến những thành phần quan trọng trong kiến trúc của Transformer, mà đặc biệt là Scale Dot-Product AttentionMulti-Head Attention, thành phần đóng vai trò rất lớn trong việc tạo nên sức mạnh của mô hình này.

Word Embedding và Positional Encoding

Phần đầu tiên trong kiến trúc của Transformer là embedding, với hai thành phần là Word Embedding và Positional Encoding.

Word Embedding là một phương pháp biểu diễn các từ trong câu thành các vector đặc trưng một cách hợp lý, sao cho các vector này thể hiện được mối quan hệ giữa các từ với nhau. Mình đã có đề cập đến nó ở trong bài viết này.

  • Đối với bài toán Machine Translation, ta cần lưu ý rằng tập từ điển của ngôn ngữ nguồn và đích có thể có số từ khác nhau nên vector one-hot biểu diễn các từ trong câu thuộc ngôn ngữ nguồn và đích cũng có thể có số chiều khác nhau. Tuy nhiên, ta sẽ đều đưa chúng về các word embedding vectors với cùng số chiều và giá trị này được ký hiệu là là $d_{model}$.

Kết quả khi sử dụng thuật toán t-SNE để trực quan hóa các word embedding vector trên không gian 2 chiều của tập từ vựng Tiếng Anh

Vì mô hình Transformer không tính toán tuần tự theo thứ tự của các từ trong câu như những mô hình dựa trên phần lõi là RNN (RNN-based) nên để có thể cung cấp thông tin về vị trí của các từ trong câu cho mô hình, các tác giả của Transformer đã đề xuất một phương pháp gọi là Positional Encoding. Phần thông tin có được từ Positional Encoding sẽ được cộng vào word embedding vectors của các từ trong câu ngôn ngữ nguồn và ngôn ngữ đích. Thao tác cộng này được thể hiện ở phần ngay sau “Input Embedding” và “Output Embedding” của hình mô tả kiến trúc ở phần 1.

Công thức của Positional Encoding được xác định thông qua hàm Sinusoid. Tất nhiên là để cộng được positional encoding vector (PE) với word embedding vector (WE) thì hai vector này phải có cùng số chiều là $d_{model}$. Với từ ở vị trí thứ $pos$ trong câu, giá trị của phần tử tại ví trí $2i$ và $(2i+1)$ ($0 \leq 2i, 2i+1 \leq d_{model} - 1)$ trong PE được xác định bởi: $$ \begin{align*} PE(pos, 2i) &= \sin \left( \frac{pos}{10000^{\frac{2i}{d_{model}}}} \right) \\ PE(pos, 2i+1) &= \cos \left( \frac{pos}{10000^{\frac{2i}{d_{model}}}} \right) \end{align*} $$

Minh họa cho kết quả của PE với các từ trong câu được thể hiện trong hình bên dưới với một câu gồm 20 từ, mỗi từ được biểu diễn bằng một WE 128 chiều. Ta thấy rằng PE của các từ trong câu đều đôi một khác nhau, tức là nó đã giúp ta mã hóa được thông tin vị trí của các từ.

Minh họa kết quả của các positional encoding vectors

Ngoài ra, các tác giả của Transformer còn cho biết rằng việc sử dụng hàm Sinusoid để tính PE có thể giúp mô hình học được vị trí tương đối của các từ, khi mà $PE(pos)$ và $PE(pos + k)$ có thể được biến đổi qua lại thông qua một phép biến đổi tuyến tính, tức là tồn tại ma trận $M$ sao cho $$ \begin{equation*} M \cdot \begin{bmatrix} \sin(pos \cdot \omega_i) \\ \cos(pos \cdot \omega_i) \end{bmatrix} = \begin{bmatrix} \sin((pos + k) \cdot \omega_i) \\ \cos((pos + k) \cdot \omega_i) \end{bmatrix} \end{equation*} $$ , với $\omega_i = \left (10000^{\frac{2i}{d_{model}}} \right)^{-1}$.

Như vậy, với $x_{pos}$ là vector one-hot của từ thứ $pos$ trong câu, ta có thể biểu diễn kết quả sau việc kết hợp word embedding vector và positional encoding vector như sau: $$ \begin{equation*} z_{pos} = WE(x_{pos}) * \sqrt{d_{model}} + PE(pos) \end{equation*} $$ , trong đó $*$ là phép nhân element-wise.

Lưu ý 1. Một cách giải thích cho việc nhân WE với $\sqrt{d_{model}}$ trước khi cộng với PE là ta muốn lượng thông tin nhận được từ PE sẽ không tác động quá nhiều đến những thông tin mà WE đã cung cấp về các từ.

Lưu ý 2. Ta còn có thể hình dung ý nghĩa positional encoding vector thông qua ví dụ về cách biểu diễn nhị phân của các số nguyên. Xét hình minh họa cho 16 số nguyên đầu tiên biên dưới thì:

  • Mỗi số được biểu diễn bằng duy nhất một chuỗi nhị phân và các chuỗi này đôi một phân biệt.
  • Đi từ bit thấp đến bit cap thì “tần suất thay đổi” của các bit tăng dần. Ví dụ, từ 0 tới 7 thì bit 0 giữ nguyên, bit 1 thay đổi 1 lần, bit 2 thay đổi 3 lần, v.v. Nếu ta quan sát lại hình minh họa kết quả các positional encoding vectors ở phía trên thì cũng thấy tính chất tương tự.

Liên hệ giữa Positional Encoding và cách biểu diễn nhị phân của các số nguyên

Cơ chế Attention và sự truy xuất thông tin

Attention là một ý tưởng rất thú vị trong Deep Learning khi mà nó đã mô phỏng lại cách bộ não của con người hoạt động khi chúng ta phân tích, nhìn nhận một đối tượng. Ví dụ, mắt chúng ta có tầm nhìn rất rộng nhưng tại mỗi thời điểm thì ta chỉ tập trung vào một vùng nhất định trong tầm nhìn để lấy thông tin. Attention đã được áp dụng thành công vào nhiều lĩnh vực khác nhau, nhiều bài toán khác nhau trong Deep Learning.

Để mô tả sơ lược về quá trình tính toán của cơ chế Attention, ta xét cách Attention được áp dụng trong các mô hình trong bài toán Machine Translation, ở giai đoạn trước khi Transformer được công bố (hình bên dưới). Giả sử input của Encoder là các vector $x_i$ và output vector tương ứng là $h_i$, ta đang tính toán cho từ đầu tiên trong output của Decoder với input của Decoder là vector $y_1$. Lúc này, ta thực hiện Attention từ $y_1$ đến các vector $x_i$, với ý nghĩa là khi ta dịch từ đầu tiên này thì ta nên chú ý vào các từ nào ở trong câu nguồn.

Mô tả quá trình tính toán của cơ chế Attention

Trong Attention, ta có các khái niệm về context vector $c_i$ và attention weight $\alpha_{ij}$. Các giá trị attention weight $\alpha_{ij}$ sẽ nằm trong đoạn [0, 1] và cho biết mức độ chú ý của vector $y_i$ vào vector $x_j$ và context vector $c_i$ là kết quả thu được khi thực hiện Attention từ vector $y_i$.

Ta tính context vector $c_i$ theo công thức $$ \begin{equation*} c_i = \sum_{j=1}^N \left( \alpha_{ij} \cdot h_j \right ) \end{equation*} $$ , trong đó $N$ là độ dài của câu nguồn và attention weight $\alpha_{ij}$ được xác định như sau: $$ \begin{equation} \alpha_{ij} = \frac{\exp(e_{ij})}{\sum_{t=1}^N \exp(e_{it})} % \label{eq:attn_weights_1} \end{equation} $$ , với $e_{ij} = f(y_i, h_j)$ và $f$ là một mô hình học, nó có thể đơn giản chỉ là Neural Network với một layer, input vector sẽ là vector được nối giữa $\alpha_{ij}$ và $h_j$, output là vector 1 chiều.

Từ công thức $(1)$, ta có thể viết gọn bằng cách biểu diễn bằng các vector $N$ chiều $\alpha_i$ và $e_i$ như sau: $$ \begin{equation*} \alpha_i = \text{softmax}(e_i) \end{equation*} $$

Như vậy, $\alpha_{ij}$ càng lớn thì $y_i$ càng chú ý vào $x_j$ và rõ ràng ta có $\sum_{j=1}^N \alpha_{ij} = 1$. Về mặt trực quan, người ta thường biểu diễn attention weights bằng một ma trận như hình bên dưới, trong đó ô có màu càng sáng thì sẽ ứng với attention weights càng lớn.

Minh họa trực quan về attention weights

Trong Transformer, Attention có thể được xem như là một cơ chế truy xuất thông tin, khi mà attention weights $\alpha_{ij}$ càng lớn thì càng thể hiện sự “liên quan đến nhau” giữa của $y_i$ và $x_i$. Khi nói đến sự truy xuất thông tin, ta có 3 khái niệm được sử dụng là query, keyvalue. Một cách mô tả rất dễ hiểu về 3 khái niệm này với ví dụ Google Search trong bài viết của anh Quốc như sau:

  • Query: Vector dùng để chứa thông tin của từ được tìm kiếm, so sánh (hoặc là ma trận với mỗi dòng là vector ứng với một từ). Ví dụ, từ khóa mà ta nhập vào ô tìm kiếm của Google Search là query.
  • Key: Ma trận dùng để biểu diễn thông tin chính của các từ được so sánh với từ cần tìm kiếm ở trên, mỗi dòng là vector ứng với một từ. Ví dụ, tiêu đề các trang web mà Google sẽ so sánh với từ khóa ta đã nhập là các key.
  • Value: Ma trận biểu diễn đầy đủ nội dung, ý nghĩa của các từ có trong key, mỗi dòng là vector ứng với một từ. Nó như là nội dung các trang web được hiển thị cho người dùng sau khi tìm kiếm. \end{itemize}

Trong ví dụ về attention ở hình trên, query sẽ là vector $y_1$, keys và values sẽ đều là ma trận $H$ tạo bởi các vector $h_i$. Quá trình tính toán giữa query, keys và values trong ví dụ đó được mô tả lại trong hình bên dưới, với:

  • Attention weights được tính từ query và keys.
  • Context vector được tính từ attention weights và values.

Attention với query, key và value. Trong đó, vector key và value của các từ trong hầu hết các trường hợp là giống nhau.

Lưu ý. Với ý nghĩa của từng ma trận, trong một số tình huống thì keys và values có thể có giá trị khác nhau chứ không nhất thiết là luôn giống nhau.

Scale Dot-Product Attention

Trong mô hình Transformer, Attention được tính toán đơn giản và nhanh hơn so với hầu hết các mô hình sử dụng Attention trong Machine Translation trước đó. Lý do là vì mô hình học $f$ dùng để tính attention weights $\alpha_{ij}$ sẽ chỉ đơn giản là phép toán tích vô hướng (Dot-Product). Ngoài ra, ta còn có thêm thao tác scale các giá trị trong ma trận kết quả với một giá trị hằng số. Do đó, quá trình tính toán Attention trong Transformer được gọi là Scale Dot-Product Attention.

Tại sao lại chỉ đơn giản là dùng phép toán tích vô hướng?

  • Để ý rằng, kết quả tích vô hướng của hai vector càng lớn thì hai vector đó càng “liên quan đến nhau”. Nếu xét hai vector có chuẩn bằng 1 thì điều này đang cho biết rằng góc giữa hai vector đó đang rất nhỏ. Như vậy, phép toán tích vô hướng có thể làm được nhiệm vụ của mô hình học $f$ một cách rất nhanh gọn.

Trước hết, ký hiệu các ma trận queries, keys và values lần lượt là $Q$, $K$ và $V$, với $Q \in \mathbb{R}^{length_q \times d_q}$, $K \in \mathbb{R}^{length_k \times d_k}$ và $V \in \mathbb{R}^{length_v \times d_v}$. Gọi các vector query trong ma trận $Q$ là $q_i$ (ứng với từng dòng của ma trận), tương tự với $k_i$ trong $K$ và $v_i$ trong $V$. Ta có một số lưu ý sau:

  • Để dễ hình dung, với ví dụ về Attention ở hình này thì $length_k = length_v = 5$ và $length_q$ sẽ bằng với số từ ở phía Decoder (và trong hình đó thì là 1).
  • Ta xét ma trận $Q$ vì từ phần về Attention thì ta thấy rằng với mỗi vector $y_i$, quá trình tính toán Attention từ nó đến các vector $x_j$ là hoàn toàn độc lập với các vector $y_t$ khác, tức là ta có thể thực hiện tính toán Attention song song với tất cả các vector query.
  • Vì $f$ là phép toán tích vô hướng nên số chiều của vector query và vector key phải giống nhau, tức là $d_q = d_k$. Ngoài ra, ta thường có $length_q = length_k = length_v$.

Quá trình tính toán của Scale Dot-Product Attention được thể hiện trong hình bên dưới. Về mặt công thức, ma trận output sẽ thuộc $\mathbb{R}^{length_q \times d_v}$ và nó được xác định bằng $$ \begin{equation*} \text{Attention}(Q, K, V) = \text{softmax} \left( \frac{QK^\top}{\sqrt{d_k}} \right) V \end{equation*} $$ , trong đó phép toán $\text{softmax}$ được thực hiện trên từng dòng của ma trận $QK^\top$.

Các bước tính toán trong Scale Dot-Product Attention.

Lưu ý. Để giải thích cho sự cần thiết của việc scale các phần tử trong ma trận $QK^\top$ bằng cách chia cho $\sqrt{d_k}$ trước khi tính softmax thì ta cần quan tâm đến phương sai của các giá trị này. Nếu ban đầu các vector $q_i$ và $k_j$ có phương sai là 1 thì các phần tử trong $QK^\top$ sẽ có phương sai là $d_k$ (hoặc là $d_q$ vì ta có $d_q = d_k$). Đó là một giá trị rất lớn và nó sẽ ảnh hưởng đến quá trình tính attention weights (phép toán softmax): giảm độ chính xác và thời gian tính tăng lên khá nhiều.

Self-Attention và Cross-Attention

Khác với các mô hình RNN-based, Transformer đã thay thế toàn bộ quá trình tính toán hồi quy bằng các phép toán Attention và một số fully connected layer. Nói cách khác, Transformer đã sử dụng Attention để học mối quan hệ giữa các từ trong câu nguồn và trong câu đích.

  • Ngoài ra, quá trính tính toán này hoàn toàn có thể diễn ra song song chứ không cần phải tuần tự từng vị trí như RNN-based.
  • Để ngắn gọn hơn, từ phần này trở đi, khi nói đến Attention trong Transformer thì ta hiểu đó là Scale Dot-Product Attention.

Trong Transformer, Attention được sử dụng theo hai dạng là Self-AttentionCross-Attention. Sự khác nhau giữa Self-Attention và Cross-Attention nằm ở các cách xác định giá trị ma trận $Q$, $K$ và $V$ để tính Scale Dot-Product Attention, trong đó:

  • Self-Attention: Ba ma trận $Q$, $K$ và $V$ đều được tính toán từ input của Encoder hoặc của Decoder, tức là câu thuộc ngôn ngữ nguồn hoặc ngôn ngữ đích. Như vậy, Self-Attention sẽ diễn ra trong nội bộ câu nguồn và nội bộ câu đích.
  • Cross-Attention: Ma trận $Q$ được tính toán từ input của Decoder, tức là câu ngôn ngữ đích. Trong khi đó, $K$ và $V$ được tính toán từ câu nguồn ở phía Encoder. Điều này nghĩa là Cross-Attention thực hiện Attention từ câu đích vào câu nguồn (đây thật là ý tưởng sử dụng Attention trong những mô hình trước đó).

Self-Attention

Trước tiên, ta xét đến Self-Attention. Self-Attention sẽ thực hiện nhiệm vụ học mối quan hệ giữa các từ với nhau trong cùng một câu (thay thế quá trình tính toán hồi quy trong các mô hình RNN-based). Do đó mà các ma trận $Q$, $K$ và $V$ được tính từ các từ trong cùng một câu. Hơn nữa, ta cũng có $length_q = length_k = length_v = n$ với $n$ là số từ của câu đó.

Giả sử rằng, câu input sau khi qua các phần Embedding (Word Embedding và Positional Encoding) thì ta thu được ma trận $X \in \mathbb{R}^{n \times d_{model}}$. Khi đó:

  • Ta sử dụng 3 fully connected layer để biến đổi $X$ thành các ma trận $Q$, $K$ và $V$. Gọi 3 ma trận trọng số ứng với các layer đó là $W_Q$, $W_K$ và $W_V$. Đây chính là các ma trận tham số mà Transformer cần học cho quá trình Self-Attention.
  • Từ $Q$, $K$ và $V$, qua phép toán Scale Dot-Product Attention, ta thu được ma trận kết quả $Z \in \mathbb{R}^{n \times d_v}$.

Quá trình tính toán này của Self-Attention được mô tả trong hình bên dưới:

Quá trình tính toán của Self-Attention với d = d_model

Minh họa cho kết quả của phép toán Self-Attention trong một câu Tiếng Anh được thể hiện trong hình bên dưới. Trong đó, màu càng đậm thể hiện cho attention weights từ từ “it” vào từ tương ứng là càng lớn.

Minh họa kết quả của Self-Attention tại từ "it" trong câu đầu vào

Như vậy, Self-Attention đã có thể học được mối quan hệ giữa các từ trong cùng một câu về mặt ngữ nghĩa một cách đơn giản và hiệu quả hơn so với quá trình tính toán hồi quy trong các mô hình RNN-based.

Cross-Attention

Cross-Attention thực ra chính là cách sử dụng Attention trong nhiều mô hình trước đó với bài toán Machine Translation. Nhiệm vụ của nó trong bài toán Machine Translation là thực hiện Attention từ câu đích vào câu nguồn. Điều này có thể xem như là việc ta sử dụng những gì đã hiểu được ở câu thuộc ngôn ngữ nguồn để dịch dần câu đó ra câu ngôn ngữ đích.

Cụ thể hơn, ta có:

  • Giá trị của ma trận $K$ và $V$ sẽ được tính toán từ câu nguồn theo một cách nào đó (các phần về EncoderDecoder sẽ đề cập kĩ hơn về chi tiết này).
  • Đối với ma trận $Q$ thì nó cũng được xác định từ các từ hiện có trong câu ngôn ngữ đích tính đến vị trí đang xét. Ta vẫn sẽ đảm bảo rằng $d_q = d_k$ và quá trình tính toán của Cross-Attention hoàn toàn tương tự như Self-Attention.
  • Mô hình Transformer cũng cần học ba ma trận tham số tương ứng để tính ra $Q$, $K$ và $V$ trước khi thực hiện Scale Dot-Product Attention).

Để dễ hình dung hơn về sự khác nhau giữa $Q$, $K$ và $V$ trong Self-Attention và Cross-Attention, ta có minh họa trong hình bên dưới, với các hàm $f$, $g$ và $h$ đại diện cho quá trình tính toán ra ba ma trận đó.

Sự khác nhau về cách tính toán $Q$, $K$ và $V$ trong Self-Attention và Cross-Attention

Lưu ý. Cross-Attention là một thành phần rất thú vị. Nhờ có Cross-Attention mà kiến trúc Transformer có thể được áp dụng hiệu quả vào nhiều bài toán khác nhau. Ví dụ, với bài toán Image Captioning, ta có thể sử dụng Encoder để “hiểu” ý nghĩa của ảnh đầu vào, sau đó, trong quá trình sinh câu mô tả cho ảnh, ta có thể sử dụng Cross-Attention đến các đặc trưng rút ra từ ảnh đó để thu được các câu mô tả tốt hơn.

Multi-Head Attention và Masked Multi-Head Attention

Mutli-Head Attention

Để ý rằng, Self-Attention đang thực hiện Attention từ một từ trong câu đến toàn bộ các từ còn lại trong câu (kể cả chính từ đó). Tương tự như với Cross-Attention. Điều này có nghĩa là ta đang thực hiện Global Attention. Tuy nhiên, trong ngôn ngữ, đôi khi từ X có thể có quan hệ “mạnh” với một từ Y theo một phương diện nào đó, và nó cũng có thể có quan hệ mạnh với từ Z theo một phương diện khác nữa. Do đó, nếu thực hiện Global Attention thì các quan hệ này có thể bị trung hòa lẫn nhau và khiến ta mất đi những đặc trưng có ích.

Ta xét một ví dụ được đề cập trong bài giảng của ProtonX. Với câu “Tôi đi học ở Hà Nội” và ta đang xét từ “Tôi” như là query vector. Nếu xét theo 3 phương diện, hay là 3 câu hỏi, sau đây: “Ai?”, “Làm gì?”, “Ở đâu?”, thì với mỗi phương diện, mối quan hệ giữa từ “Tôi” với các từ con lại được thể hiện mạnh nhất ở lần lượt các từ “Tôi”, “đi” và “học”, “ở” và “Hà” và “Nội” (minh họa ở hình bên dưới).

Xét từ "Tôi" trong ba phương diện khác nhau khi thực hiện Self-Attention

Chi tiết này là xuất phát cho ý tưởng của Multi-Head Attention. Nó sẽ hạn chế ảnh hưởng của Global Attention trong việc trung hòa mối quan hệ giữa các từ với nhau trong nhiều phương diện.

Để đạt được điều này, Multi-Head Attention sẽ chia nhỏ từng ma trận trong các ma trận $Q$, $K$ và $V$ thành $h$ phần. Ví dụ, với $Q \in \mathbb{R}^{length_q \times d_q}$ thì ta sẽ có $h$ ma trận $Q_i \in \mathbb{R}^{length_q \times (d_k / h)}$ (hay là chia thành $h$ heads). Sau đó, Scale Dot-Product Attention sẽ được thực hiện trên $h$ bộ ba ma trận $(Q_i, K_i, V_i)$ và các kết quả sẽ được tổng hợp lại. Ta có thể hình dung rằng mỗi phần nhỏ của các ma trận $Q$, $K$ và $V$ sẽ cố gắng biểu diễn đặc trưng của các từ ở một phương diện nào đó.

Quá trình tính toán của Multi-Head Attention được thể hiện trong hình bên dưới. Có một số lưu ý như sau:

  • Với Multi-Head Attention trong Transformer, ta sẽ xét $length_q = length_k = length_v = n$ với $n$ là độ dài của câu đầu vào
  • $d_q = d_k = d_v = d_{model}$ (đây là số chiều của embedding vector của các từ, sau khi bổ sung thông tin về Positional Encoding trong phần \ref{sec:pos-enc}).

Quá trình tính toán trong Multi-Head Attention

Đặt $d_x = d_{model} / h$. Về mặt công thức, ma trận kết quả của Multi-Head Attention với $h$ head có thể xác định bằng $$ \begin{equation*} \begin{aligned} \text{MultiHead}(Q, K, V) &= \text{Concat}(\text{head}_1; \dots; \text{head}_h)W^O \\ \text{với head}_i &= \text{Attention}(QW^Q_i, KW^K_i, VW^V_i) \end{aligned} \end{equation*} $$ , trong đó $Q, K, V \in \mathbb{R}^{n \times d_{model}}$, các ma trận trọng số $W^Q_i, W^V_i, W^K_i \in \mathbb{R}^{d_{model} \times d_x}$, kết quả Scale Dot-Product Attention $\text{head}_i \in \mathbb{R}^{n \times d_x}$ và $W^O \in \mathbb{R}^{d_{model} \times d_{model}}$. Như vậy, ma trận kết quả của Multi-Head Attention thuộc $\mathbb{R}^{n \times d_{model}}$.

Masked-Multi Head Attention

Masked-Multi Head Attention là một biến thể của Multi-Head Attention và nó được sử dụng trong Decoder của Transformer. Mục đích của Masked-Multi Head Attention là ngăn chặn quá trình Attention tại một số vị trí trong câu. Ta có thể gọi phép tính Attention ở trong Masked-Multi Head Attention là Masked Scale Dot-Product Attention, và Self-Attention tại đây có thể được gọi là Masked Self-Attention.

Lý do ta cần đến thành phần này ở Decoder là để tránh việc mô hình “nhìn thấy” được các từ ở phía sau từ hiện tại khi nó đang đưa ra dự đoán khi ta thực hiện Self-Attention tại Decoder. Minh họa cho ý tưởng này được thể hiện trong hình bên dưới.

Minh họa ý tưởng của Masked Self-Attention

Lưu ý. Bản chất của phép toán là ta sẽ làm cho giá trị attention weights đến các từ nên được bỏ qua thành một giá trị rất nhỏ để nó hầu như không thể tác động gì đến kết quả chung của quá trình Attention.

  • Để thực hiện được điều đó, ta sẽ dùng một mặt nạ (mask) để đánh dấu những từ bị bỏ qua khi đang xét đến một từ nào đó trong câu.
  • Ví dụ, với câu “Tôi đi học ở Hà Nội”, ta xét từ “đi” thì giá trị của mask sẽ là [0, 0, 1, 1, 1, 1], với phần tử bằng 1 có nghĩa là giá trị tại thành phần đó bị bỏ qua. Lúc đó, trước khi tính attention weights, ta kiểm tra mask xem giá trị nào bằng 1 thì gán cho phần tử tương ứng ở đó là $-\infty$. Khi đó, sau phép toán softmax, giá trị attention weight tại đó sẽ xấp xỉ 0.

Như vậy, trong Masked Multi-Head Attention, quá trình tính toán của Scale Dot-Product Attention sẽ bổ sung thêm một phần kiểm tra mask và gán lại giá trị trước khi tính softmax. Quá trình này được thể hiện trong hình bên dưới.

Quá trình tính toán trong Scale Dot-Product Attention khi thực hiện thêm thao tác kiểm tra mask

Layer Normalization

Trong các mô hình Deep Learning, ta thường thấy sự xuất hiện của các normalization layer nhằm cải thiện khả năng hội tụ của mô hình. Hai loại normalization layer thường thấy nhất là Batch Normalization (chuẩn hóa theo batch) và Layer Normalization (chuẩn hóa theo từng mẫu). Đối với các mô hình thuộc bài toán NLP nói chung và Transformer nói riêng thì loại layer được sử dụng thường là Layer Normalization. Sự khác nhau giữa hai loại layer này khi áp dụng vào bài toán ngôn ngữ được thể hiện trong hình bên dưới, trong đó:

  • Layer Normalization: Việc chuẩn hóa của từng mẫu sẽ độc lập với nhau. Ta sẽ chuẩn hóa các vector trên từng đặc trưng một (tổng cộng có $d_model$ đặc trưng).
  • Batch Normalization: Đối với loại layer này, ta sẽ chuẩn hóa từng phần tử của vector đặc trưng tại từng vị trí trong câu đầu vào và việc tính toán được dựa theo toàn bộ các mẫu trong cùng batch.

Layer Normalization và Batch Normalization khi áp dụng vào bài toán ngôn ngữ

Qua sự khác biệt đó, ta có thể thấy rằng Batch Normalization không nên được áp dụng vào các bài toán NLP vì vấn đề sự khác nhau về độ dài thật sự của các câu trong cùng một batch sẽ ảnh hưởng đến kết quả chuẩn hóa (dù ta đã sử dụng kĩ thuật padding để đưa các câu đó về cùng một độ dài nhưng các vị trí được padding lại có vector đặc trưng với giá trị khá vô nghĩa).

  • Cụ thể hơn, nếu trong cùng một batch, có một câu có độ dài lớn và nhiều câu có độ dài nhỏ thì đặc trưng của các từ ở vị trí phía sau của câu dài hơn đó sẽ có khả năng cao bị mất đi khi ta áp dụng Batch Normalization.

Feed Forward Network và skip connection

Bên cạnh các quá trình tính toán Attention, các tác giả của Transformer sử dụng thêm một số Feed Forward Network (gồm các fully connected layer) và kỹ thuật Skip connection để tăng thêm khả năng học các đặc trưng của mô hình (các ô màu xanh dương trong hình ở phần này).

Đầu tiên, Feed Forward Network (FFN) trong Transformer sử dụng 2 fully connected layers với số unit lần lượt là $d_{ff}$ và $d_{model}$ cùng activation function ReLU trong layer đầu tiên. Input của FFN là một ma trận thuộc $\mathbb{R}^{n \times d_{model}}$. Như vậy, ta có thể biểu diễn kết quả đầu ra của FFN theo công thức $$ \begin{equation*} FFN(X) = \max(0, XW_1 + b_1) W_2 + b_2 \end{equation*} $$ , với hai ma trận trọng số $W_1 \in \mathbb{R}^{d_{model \times d_{ff}}}$ và $W_2 \in \mathbb{R}^{d_{ff} \times d_{model}}$ và $b_1$, $b_2$ là các bias vector. Do đó $FFN(X) \in \mathbb{R}^{n \times d_{model}}$.

Ngoài ra, skip connection là một ý tưởng rất hay và phổ biến trong Deep Learning kể từ khi nó được áp dụng thành công trong mô hình ResNet. Skip connection có thể giúp cho gradient được lan truyền tốt hơn trong quá trình huấn luyện mô hình, từ đó góp phần làm giảm hiện tượng vanishing gradient. Mình đã đề cập đến ý tưởng này trong bài viết về ResNet.

Trong kiến trúc Transformer, các tác giả đã áp dụng skip connection cùng với Layer Normalization ở rất nhiều vị trí (các khối “Add & Norm” trong hình ở phần này). Ta có thể biểu diễn output của các khối đó ở dạng $$\text{LayerNorm}(X + \text{Sublayer}(X)))$$ , với $\text{Sublayer}$ được sử dụng để đại diện cho những thành phần ở phía trước các khối đó.

Lưu ý. Một hướng giải thích cho chi tiết Feed Forward Network chỉ áp dụng activation function ReLU cho layer đầu tiên là vì ngay sau đó ta đã sử dụng skip connection. Đây là một kĩ thuật thường được sử dụng khi làm việc với skip connection.

  • Nếu ta sử dụng activation function ReLU rồi sau đó áp dụng skip connection thì có thể nói là giá trị các phần tử trong ma trận đầu vào sẽ luôn không giảm, và hiệu ứng này có thể sẽ có ảnh hưởng không tốt đến mô hình.

Encoder

Sau khi đã trình bày về các thành phần quan trọng trong kiến trúc của Transformer, ta sẽ đi vào các nhánh chính của kiến trúc và nhánh đầu tiên là Encoder. Kiến trúc của Encoder được thể hiện trong hình bên dưới:

Layer Kiến trúc của Transformer Encoder

Thành phần Encoder trong Transformer là sự kết hợp của Word Embedding, Positional Encoding và một dãy gồm $N$ Encoder Layer liên tiếp nhau. Trong đó, Encoder Layer bao gồm nhiều thành phần như Multi-Head Attention, Feed Forward, skip connection và Layer Normalization.

Cụ thể hơn, trong bài toán Machine Translation, input của Encoder sẽ là câu văn thuộc ngôn ngữ nguồn gồm $L$ từ. Trong đó, các “từ” trong câu lúc này là các con số ứng với vị trí của từ đó trong từ điển của ngôn ngữ nguồn. Khi đó:

  • Sau khi qua Word Embedding và Positional Encoding, ta được một ma trận $X \in \mathbb{R}^{L \times d_{model}}$.
  • Tại mỗi Encoder Layer, từ ma trận input $X’ \in \mathbb{R}^{N \times d_{model}}$, ta sẽ sử dụng ba ma trận trọng số $W_Q, W_K, W_V$ để tính ra $Q$ (được mô tả ở phần , $K$, $V$ từ $X’$ và sau đó bắt đầu đi qua các thành phần trong đó. Ma trận output của Encoder Layer cũng sẽ là một ma trận thuộc $\mathbb{R}^{L \times d_{model}}$.

Lưu ý. Encoder output sẽ là một ma trận thuộc $\mathbb{R}^{L \times d_{model}}$, một ma trận có shape giống với ma trận đầu vào $X$ của Encoder. Như vậy, ta có thể hình dung rằng Encoder đang làm nhiệm vụ là bổ sung thêm những đặc trưng quan trọng vào embedding vector ban đầu của các từ trong câu.

Đối với Encoder output, ta sẽ sử dụng ma trận này để tham gia vào phép tính Cross-Attention trong Decoder.

Decoder

Tổng quan về kiến trúc

Kiến trúc của Decoder được thể hiện trong hình bên dưới. Decoder cũng có cách tổ chức khá tương tự Encoder khi ta bắt đầu bằng Word Embedding và Positional Encoding, sau đó là dãy gồm $N$ Decoder Layer liên tiếp nhau. Phần cuối của Decoder là một fully connected layer kèm theo hàm softmax để ta chọn ra từ phù hợp nhất làm output của mô hình đối với vị trí hiện tại trong câu thuộc ngôn ngữ đích.

Kiến trúc của Transformer Decoder

Ta cần để ý rằng, trong Decoder Layer, ta sẽ thực hiện cả hai phép tính Self-Attention và Cross-Attention. Trong đó:

  • Self-Attention được thực hiện trước để tiến hành Attention đến các vị trí ở phía trước vị trí hiện tại trong câu, tức là ta đang sử dụng Masked Multi-Head Attention.
  • Sau đó, Cross-Attention được thực hiện với hai thành phần $K$ và $V$ được tính toán từ Encoder output. Lúc này thì ta đang thực hiện Attention đến toàn bộ các vị trí trong Encoder output nên Multi-Head Attention được sử dụng.

Ngoài ra, ta sẽ đặt số lượng từ trong câu nguồn và câu đích của mô hình là bằng nhau và bằng $L$ (trong trường hợp câu nào ngắn hơn thì ta sẽ sử dụng kỹ thuật padding để thêm các từ vào). Do đó:

  • Trước khi đến với fully connected layer cuối cùng của Decoder để tiến hành phân lớp, ma trận output ta nhận được cũng sẽ thuộc $\mathbb{R}^{L \times d_{model}}$.
  • Số chiều của output vector của fully connected layer cuối cùng sẽ bằng với kích thước tập từ điển của ngôn ngữ đích.

Decoder trong quá trình huấn luyện

Trong quá trình huấn luyện mô hình, ta sẽ tạo ra các cặp (input, ground truth) của Decoder theo cách khá dặc biệt. Đầu tiên, ta sẽ thêm các từ đánh dấu cho việc “bắt đầu” và “kết thúc” của quá trình dịch. Ta gọi đây là các từ “” và “”. Các cặp (input, ground truth) dùng để huấn luyện Decoder sẽ được tạo ra bằng cách “shifted right” một câu thuộc ngôn ngữ đích. Ví dụ, với câu Tiếng Việt là “tôi đi học”, ta sẽ có các cặp (input, ground truth) tương ứng như sau:

  • Input: “”, “tôi”, “đi”, “học”.
  • Ground truth: “tôi”, “đi”, “học”, “

Ngoài ra, cho dù Decoder có dự đoán ra từ nào ở vị trí $t$ của câu đích đi nữa thì input của Decoder ở vị trí $t+1$ cũng luôn là một từ đúng (tức là ground truth của vị trí $t$). Kỹ thuật này gọi là Teacher Forcing và nó được sử dụng rất nhiều trong các bài toán NLP. Quá trình này được minh họa trong hình bên dưới.

Kỹ thuật Teacher Forcing với Decoder trong huấn luyện Transformer

Decoder trong quá trình dự đoán

Đối với quá trình dự đoán (hay là dịch) của Transformer, Decoder sẽ bắt đầu với một từ là “<start>” và quá trình dịch sẽ tiếp tục cho đến khi mô hình dự đoán ra từ “<end>”. Hơn nữa, từ được mô hình dự đoán ra ở vị trí $t$ sẽ được dùng làm input của Decoder cho vị trí $t+1$. Quá trình này được thể hiện trong hình bên dưới.

Decoder trong quá trình dự đoán

Kết luận

Như vậy, sau một bài viết rất dài thì mình đã trình bày về Transformer với khá nhiều phân tích vào ý tưởng và bản chất của các thành phần. Từ sự thành công của Transformer trong Machine Translation, một kỷ nguyên mới thật sự đã mở ra đối với NLP nói riêng và Deep Learning nói chung:

  • Các mô hình ngôn ngữ lớn như ChatGPT, Bard,… đều dựa trên nền tảng kiến trúc của Transformer. Rõ hơn một chút thì chúng sẽ sử dụng Transformer Decoder và sẽ không dùng đến Cross Attention 😉
  • Đối với các bài toán thuộc lĩnh vực CV, hay là multi-model như text-to-image (Stable Diffusion,…) thì cũng đều đang tận dụng sức mạnh của Transformer và các thành phần của chúng, đặc biệt là Cross Attention.

Tài liệu tham khảo

Lưu ý. Nếu phần Comment không load ra được thì các bạn vào DNS setting của Wifi/LAN và đổi thành "8.8.8.8" nhé (server của Google)!

Built with Hugo
Theme Stack designed by Jimmy