Trời tính không bằng máy tính

blog, khám phá

Dễ hiểu hơn: Xử lý ngôn ngữ tự nhiên

Ngày nay, chúng ta không còn bất ngờ về việc “cái máy nói chuyện” tự nhiên và trôi chảy như một người bình thường nữa. Sự tiến bộ vượt bậc của machine learning trong vài năm trở lại tạo ra cả một thế giới mới để tìm hiểu. Tuy nhiên, đối với những người không học bài bản về AI như mình, việc tìm tài liệu để học hỏi lại quá gian nan. Có những tài liệu thì quá chi tiết – khó hiểu, nhưng có những trang thì lại viết quá “bề mặt”.

Vậy nên trong loạt blog này, mình sẽ thuật lại quá trình mình chập chững tìm hiểu về nhánh xử lý ngôn ngữ (NLP – natural language processing) – tiền đề của ChatGPT hiện đại. Mình không phải chuyên gia, và cách tìm hiểu của mình không theo sách vở nên sẽ khá tự do. Nội dung của bài viết sẽ cố gắng cân bằng để vừa không quá sơ sài, cũng vừa không quá đi vào chi tiết khó hiểu.

Loạt bài “Dễ hiểu hơn” này sẽ gồm 2 phần:

Vì sao phải xử lý ngôn ngữ?

Không phải tự nhiên mà ta nảy ra câu hỏi có phần “ngu ngốc” này. Đã bao giờ bạn tự hỏi, vì sao trong tiếng Việt, ta lại chỉ cần thêm “đã” hay “sẽ” trước động từ để nói về thời gian? Vì sao trong tiếng Anh, họ phải thêm đuôi -ed hay -ing để thêm ý nghĩa về thời gian? Còn tiếng Pháp / Ý / Đức… thì ôi thôi rồi, mỗi ngôi chia động từ một khác nhé, nhớ để ý cả giống cái hay giống đực trước khi chia.

Ngôn ngữ tự nhiên rất phức tạp, nhưng nó lại là cách mà loài người giao với nhau hằng ngày. Sẽ thật hữu ích nếu ít ra chúng ta tìm được một cách để “nghiên cứu” và “bóc tách” ngôn ngữ tự nhiên.

Ví dụ thế này nhé: bạn cần dịch một văn bản từ tiếng Việt sang tiếng Hàn, bạn cần biết cả 2 thứ tiếng để làm việc này.

Nhưng vì bạn biết các ngôn ngữ thể nào cũng có điểm chung, bạn lập trình ra một phần mềm để “phân tích” văn bản và tách những phần ý nghĩa chung, biến nó thành dạng “số”. Phần mềm này thậm chí có thể biến ngược lại, từ “số” về thành văn bản. Áp dụng vào công việc kể trên, bạn có thể biến tiếng Việt ==> số ==> tiếng Hàn một cách tự động, thật hữu ích phải không nào?

Thực tế, quá trình biến từ ngôn ngữ tự nhiên thành số được gọi là “encode” (mã hóa), quá trình ngược lại gọi là “decode“. Cái gọi nôm na là “phần mềm” phía trên thực chất là một “language model“, có thể hiểu như một “mô hình bộ não ảo”.

Word embedding

Trước khi nghĩ đến việc số hóa cả một câu nói hay một đoạn văn bản, chúng ta trước tiên cần quan sát tới đơn vị nhỏ nhất của ngôn ngữ, là từ vựng và chữ cái.

Ý tưởng của word embedding (word vector) đó là làm cách nào biến từ ngữ (hoặc các phần của một từ ngữ) thành dạng vector, mục đích là để lấy được một phần ý nghĩa của từ đơn lẻ. Một trong những ví dụ điển hình nhất của word vector đó là việc có thể “cộng” các từ với ý nghĩa riêng lẻ để tìm ra một từ với ý nghĩa tương tự.

Ví dụ, từ “ông” thực ra để chỉ người “con trai” có tuổi “già”, vậy nếu cộng vector “con trai” + “già” thì ta sẽ tìm thấy vector gần với từ “ông”.

Tất nhiên đây là cách giải thích nôm na, vì thực tế có rất nhiều trường hợp mà cùng một từ ngữ, nhưng ý nghĩa khác nhau trong hoàn cảnh, ví như trong tiếng Việt có từ “không” vừa có nghĩa phủ định (đối lập với “có”), lại vừa có nghĩa là câu hỏi (“phải không?”). Để bắt được nhiều lớp ý nghĩa như vậy, không gian vector không phải là 2 chiều, 3 chiều, mà thường phải có ít nhất 1000 chiều.

Để tạo ra một model có thể biến từ ngữ bình thường thành vector, người ta cần có rất nhiều dữ liệu, sau đó đưa vào để training. Trong quá trình này, máy sẽ phân tích sự liên kết giữa các từ ngữ trong một câu, một đoạn, một văn bản, để “điều chỉnh” lại danh sách các vector.

Hình sau lấy từ một thí nghiệm mà mình làm vào năm 2019

Token

Biến từ một từ ngữ hoàn chỉnh thành một vector thì đơn giản. Nhưng vấn đề thế này, ngôn ngữ tự nhiên có tính chất… sống, tức nó thay đổi theo thời gian. Ví dụ hồi xưa, người ta thường nói “hăm bảy”, nhưng bây giờ lại nói là “hai bảy”. Mỗi lần thêm một từ mới, người ta phải training lại cho model, rất kém hiệu quả.

Vậy thay vì biến cả một từ thành vector, vì sao ta không “chia” từ thành các phần khác nhau rồi biến những phần nhỏ đó thành vector? Ví dụ trong tiếng Pháp, “bonjour” được cấu tạo bởi “bon” (nghĩa là tốt) và “jour” (nghĩa là ngày). Bất ngờ là, ý tưởng này thực chất đúng với mọi ngôn ngữ: chữ viết tiếng Việt được cấu tạo bởi phụ âm đầu – nguyên âm – phụ âm cuối – thanh điệu, chữ Hán thì được cấu tạo bởi các “bộ”, ví dụ 好 (hảo) lại được cấu tạo bởi 女 (nữ) và 子 (tử),…

Trong xử lý ngôn ngữ tự nhiên, các bộ phận nhỏ này được gọi là “token”. Việc tách cả một câu nói chứa nhiều ký tự (từ ngữ) khác nhau thành các token được gọi là tokenize.

Ví dụ sau đây là cách mà ChatGPT thực hiện tokenize một văn bản:

Tuy vậy, việc tách các từ thành các bộ phận nhỏ cũng gây ra một vấn đề, đó là model sẽ phải xử lý nhiều tokens hơn, cần nhiều thời gian hơn để xử lý một văn bản. Ví dụ nếu nhập tiếng Việt vào ChatGPT, thì có những từ sẽ tách thành nhiều token nhỏ (VD từ Cộng bị tách thành C-ộ-ng), nên nhìn chung thì ChatGPT khá chậm khi dùng tiếng Việt:

Convolution neural network

Bây giờ ta đã có thể biến một đoạn văn bản thành nhiều token, rồi mỗi token thành một vector. Việc tiếp theo ta cần làm đó là tìm một phép toán nào đó để phân tích và xử lý đống số liệu này.

Một trong những cách đơn giản nhất, đó là tạo ra một Convolution neural network (CNN) và đưa hết đống token vào đó để xử lý. CNN vốn là một trong những kỹ thuật đầu tiên được phát minh ra để nhận diện hình ảnh. Để tìm hiểu xem convolution neural network hoạt động thế nào thì bạn có thể đọc số blog này do mình viết từ hồi còn quá trẻ trâu (năm 2017).

Ý tưởng ở đây là, thay vì nhập từng pixel của hình ảnh vào CNN, thì ta sẽ thay các pixel thành các token, và đầu ra sẽ là các token khác (VD nhập vào tiếng Việt thì đầu ra sẽ là tiếng Hàn)

Tuy convolution neural network về lý thuyết có thể giải quyết được bài toán đặt ra, nhưng thực ra nó lại có một vài hạn chế lớn, ví dụ như:

Thực tế, convolution neural network hoạt động khá tốt với hình ảnh, vì trường hợp tệ nhất thì bạn có thể cắt hình ảnh thành một cỡ nhất định. Ví dụ như việc tạo ra một model nhận diện chữ viết tay (MNIST dataset) là một trong những bài tập rất điển hình và dễ nghịch ngợm cho người mới tìm hiểu về machine learning.

Recurrent neural network

Ý tưởng tiếp theo thế này: thay vì input thẳng 1000 tokens cùng một lúc, vì sao ta không input từng token một?

Với ý tưởng này, model của chúng ta sẽ output một vector, và vector này sẽ dùng để biểu diễn ý nghĩa “tổng” của các token mà bạn đã nhập vào cho tới thời điểm đó. Nói cách khác, ta muốn biến cả một đoạn văn bản thành một vector.

Mỗi lần nhập token mới vào, bạn cần input vector cũ + token mới để tạo ra vector mới. Vì việc tái sử dụng vector cũ để tạo ra vector mới, nên trong tên của kỹ thuật này có từ “Recurrent” (tiếng Việt có nghĩa là “lặp lại”)

Ví dụ câu “bạn có khỏe không?”, khi mình nhập từng token “bạn” “có” “khỏe” “không” “?” vào, điều xảy ra là:

  • input vector 0 = (chưa có thông tin gì)
  • input token: “bạn” + input vector 0 => vector 1 = (hmm, có vẻ đang nói về mình)
  • input token: “có” + input vector 1 => vector 2 = (hmm, có vẻ là câu hỏi đây)
  • input token: “khỏe” + input vector 2 => vector 3 = (à, câu chào hỏi mà)
  • input token: “không” + input vector 3 => vector 4 = (khác chắc là câu chào hỏi)
  • input token: “?” + input vector 4 => vector 5 = (chắc chắn là câu chào hỏi)

Như vậy, bạn đã encode được câu hỏi thành một vector duy nhất (ta chỉ lấy vector 5, là vector cuối cùng). Nhiệm vụ tiếp theo là cần phải có một model để decode vector này, ví dụ model khi thấy vector kiểu kiểu “câu chào hỏi” thì sẽ trả lời lại là “tôi khỏe, cảm ơn”. Thực tế, ta có hoàn toàn có thể tái sử dụng cấu trúc của encoder cho phần decoder, chỉ cần lật ngược nó lại.

Thực tế, ý tưởng về Recurrent neural network (RNN) được áp dụng dưới nhiều dạng khác nhau, ví dụ như LSTM (Long short-term memory) hay GRU (Gated recurrent units). Nói một cách đơn giản, các loại neural network này sử dụng nhiều kỹ thuật khác nhau để biến một loạt các token (hay còn gọi là một sequence) thành một vector.

Cái khó của RNN nằm ở việc nó hay gặp vấn đề về “vanishing gradient”, tức các neural sau một thời gian sẽ bị “bão hòa” thành giá trị quá lớn hoặc quá bé, dù bạn có thêm bao nhiêu token đi chăng nữa thì các neural này cũng không truyền được thông tin nữa. Vấn đề này sẽ dẫn tới model bị “quên” mất mình đang nói gì và sẽ bị lặp lại câu nói vô hạn như thế này như thế này như thế này như thế này như thế này như thế này như thế này như thế này như thế này như thế này ^C

Một vấn đề nữa đó là việc training cho RNN thường rất kém hiệu quả, hầu hết là vì bạn phải nhập từng một token vào RNN, chứ không thể nhập thẳng 1000 tokens cùng một lúc.

Vậy làm thế nào mà ta đã giải quyết được vấn đề này? ChatGPT hoạt động thế nào? Hãy cũng xem số tiếp theo để tìm hiểu sâu hơn về cấu trúc Transformer nhé.

Link tới bài tiếp theo: https://blog.ngxson.com/de-hieu-hon-transformer-la-gi-gpt-hoat-dong-the-nao/

Tham khảo

MỚI! Đăng ký nhận bài viết mới nhất qua chatbot: