.. raw:: html .. raw:: html .. raw:: html .. _sec_machine_translation: Dịch Máy và Tập dữ liệu ======================= .. raw:: html Đến nay ta đã thấy cách sử dụng mạng nơ-ron hồi tiếp cho các mô hình ngôn ngữ, mà ở đó ta dự đoán token tiếp theo khi biết tất cả token trước đó. Bây giờ ta sẽ xem xét một ứng dụng khác để dự đoán một chuỗi token thay vì chỉ một token đơn lẻ. .. raw:: html Dịch máy (*Machine translation* - MT) đề cập đến việc dịch tự động một đoạn văn bản từ ngôn ngữ này sang ngôn ngữ khác. Giải quyết bài toán này với các mạng nơ-ron thường được gọi là dịch máy nơ-ron (*neural machine translation* - NMT). So với các mô hình ngôn ngữ (:numref:`sec_language_model`), trong đó kho ngữ liệu chỉ chứa một ngôn ngữ duy nhất, bộ dữ liệu dịch máy có ít nhất hai ngôn ngữ, ngôn ngữ nguồn và ngôn ngữ đích. Ngoài ra, mỗi câu trong ngôn ngữ nguồn được ánh xạ tới bản dịch tương ứng trong ngôn ngữ đích. Do đó, cách tiền xử lý dữ liệu dịch máy sẽ khác so với mô hình ngôn ngữ. Phần này được dành riêng để trình bày cách tiền xử lý và nạp một tập dữ liệu như vậy vào các minibatch. .. code:: python from d2l import mxnet as d2l from mxnet import np, npx, gluon import os npx.set_np() .. raw:: html Đọc và Tiền Xử lý Dữ liệu ------------------------- .. raw:: html Trước tiên ta tải xuống bộ dữ liệu chứa một tập các câu tiếng Anh cùng với các bản dịch tiếng Pháp tương ứng. Có thể thấy mỗi dòng chứa một câu tiếng Anh cùng với bản dịch tiếng Pháp tương ứng, cách nhau bởi một dấu ``TAB``. .. code:: python # Saved in the d2l package for later use d2l.DATA_HUB['fra-eng'] = (d2l.DATA_URL + 'fra-eng.zip', '94646ad1522d915e7b0f9296181140edcf86a4f5') # Saved in the d2l package for later use def read_data_nmt(): data_dir = d2l.download_extract('fra-eng') with open(os.path.join(data_dir, 'fra.txt'), 'r') as f: return f.read() raw_text = read_data_nmt() print(raw_text[0:106]) .. parsed-literal:: :class: output Go. Va ! Hi. Salut ! Run! Cours ! Run! Courez ! Who? Qui ? Wow! Ça alors ! Fire! Au feu ! Help! À l'aide ! .. raw:: html Ta sẽ thực hiện một số bước tiền xử lý trên dữ liệu văn bản thô, bao gồm chuyển đổi tất cả ký tự sang chữ thường, thay thế các ký tự khoảng trắng không ngắt (*non-breaking space*) UTF-8 bằng dấu cách, thêm dấu cách vào giữa các từ và các dấu câu. .. code:: python # Saved in the d2l package for later use def preprocess_nmt(text): def no_space(char, prev_char): return char in set(',.!') and prev_char != ' ' text = text.replace('\u202f', ' ').replace('\xa0', ' ').lower() out = [' ' + char if i > 0 and no_space(char, text[i-1]) else char for i, char in enumerate(text)] return ''.join(out) text = preprocess_nmt(raw_text) print(text[0:95]) .. parsed-literal:: :class: output go . va ! hi . salut ! run ! cours ! run ! courez ! who? qui ? wow ! ça alors ! fire ! au feu ! .. raw:: html .. raw:: html .. raw:: html Token hóa --------- .. raw:: html Khác với việc sử dụng ký tự làm token trong :numref:`sec_language_model`, ở đây một token là một từ hoặc dấu câu. Hàm sau đây sẽ token hóa dữ liệu văn bản để trả về ``source`` và ``target`` là hai danh sách chứa các danh sách token, với ``source [i]`` là câu thứ :math:`i` trong ngôn ngữ nguồn và ``target [i]`` là câu thứ :math:`i` trong ngôn ngữ đích. Để việc huấn luyện sau này nhanh hơn, chúng ta chỉ lấy mẫu ``num_examples`` cặp câu đầu tiên. .. code:: python # Saved in the d2l package for later use def tokenize_nmt(text, num_examples=None): source, target = [], [] for i, line in enumerate(text.split('\n')): if num_examples and i > num_examples: break parts = line.split('\t') if len(parts) == 2: source.append(parts[0].split(' ')) target.append(parts[1].split(' ')) return source, target source, target = tokenize_nmt(text) source[0:3], target[0:3] .. parsed-literal:: :class: output ([['go', '.'], ['hi', '.'], ['run', '!']], [['va', '!'], ['salut', '!'], ['cours', '!']]) .. raw:: html Dưới đây là biểu đồ tần suất của số lượng token cho mỗi câu. Có thể thấy, trung bình một câu chứa 5 token và hầu hết các câu có ít hơn 10 token. .. code:: python d2l.set_figsize((3.5, 2.5)) d2l.plt.hist([[len(l) for l in source], [len(l) for l in target]], label=['source', 'target']) d2l.plt.legend(loc='upper right'); .. figure:: output_machine-translation-and-dataset_vn_d45ae3_9_0.svg .. raw:: html Bộ Từ vựng ---------- .. raw:: html Vì các token trong ngôn ngữ nguồn có thể khác với các token trong ngôn ngữ đích, ta cần xây dựng một bộ từ vựng cho mỗi ngôn ngữ. Do ta đang sử dụng các từ để làm token chứ không dùng ký tự, kích thước bộ từ vựng sẽ lớn hơn đáng kể. Ở đây ta sẽ ánh xạ mọi token xuất hiện ít hơn 3 lần vào token như trong :numref:`sec_text_preprocessing`. Ngoài ra, ta cần các token đặc biệt khác như token đệm , hay token bắt đầu câu . .. code:: python src_vocab = d2l.Vocab(source, min_freq=3, reserved_tokens=['', '', '']) len(src_vocab) .. parsed-literal:: :class: output 9140 .. raw:: html Nạp Dữ liệu ----------- .. raw:: html Trong các mô hình ngôn ngữ, mỗi mẫu là một chuỗi có độ dài ``num_steps`` từ kho ngữ liệu, mà có thể là một phân đoạn của một câu hoặc trải dài trên nhiều câu. Trong dịch máy, một mẫu bao gồm một cặp câu nguồn và câu đích. Những câu này có thể có độ dài khác nhau, trong khi đó ta cần các mẫu có cùng độ dài để tạo minibatch. .. raw:: html Một cách giải quyết vấn đề này là nếu một câu dài hơn ``num_steps``, ta sẽ cắt bớt độ dài của nó, ngược lại nếu một câu ngắn hơn ``num_steps``, thì ta sẽ đệm thêm token . Bằng cách này, ta có thể chuyển bất cứ câu nào về một độ dài cố định. .. code:: python # Saved in the d2l package for later use def truncate_pad(line, num_steps, padding_token): if len(line) > num_steps: return line[:num_steps] # Trim return line + [padding_token] * (num_steps - len(line)) # Pad truncate_pad(src_vocab[source[0]], 10, src_vocab['']) .. parsed-literal:: :class: output [47, 4, 1, 1, 1, 1, 1, 1, 1, 1] .. raw:: html .. raw:: html .. raw:: html Bây giờ ta có thể chuyển đổi danh sách các câu thành mảng chỉ số có kích thước ``(num_example, num_steps)``. Ta cũng ghi lại độ dài của mỗi câu khi không có token đệm, được gọi là *độ dài hợp lệ - valid length*. Thông tin này có thể được sử dụng bởi một số mô hình. Ngoài ra, ta sẽ thêm các token đặc biệt “” và “” vào các câu đích để mô hình biết thời điểm bắt đầu và kết thúc dự đoán. .. code:: python # Saved in the d2l package for later use def build_array(lines, vocab, num_steps, is_source): lines = [vocab[l] for l in lines] if not is_source: lines = [[vocab['']] + l + [vocab['']] for l in lines] array = np.array([truncate_pad( l, num_steps, vocab['']) for l in lines]) valid_len = (array != vocab['']).sum(axis=1) return array, valid_len .. raw:: html Sau đó, ta có thể xây dựng các minibatch dựa trên các mảng này. .. raw:: html Kết hợp Tất cả lại ------------------ .. raw:: html Cuối cùng, ta định nghĩa hàm ``load_data_nmt`` để trả về iterator cho dữ liệu cùng với các bộ từ vựng cho ngôn ngữ nguồn và ngôn ngữ đích. .. code:: python # Saved in the d2l package for later use def load_data_nmt(batch_size, num_steps, num_examples=1000): text = preprocess_nmt(read_data_nmt()) source, target = tokenize_nmt(text, num_examples) src_vocab = d2l.Vocab(source, min_freq=3, reserved_tokens=['', '', '']) tgt_vocab = d2l.Vocab(target, min_freq=3, reserved_tokens=['', '', '']) src_array, src_valid_len = build_array( source, src_vocab, num_steps, True) tgt_array, tgt_valid_len = build_array( target, tgt_vocab, num_steps, False) data_arrays = (src_array, src_valid_len, tgt_array, tgt_valid_len) data_iter = d2l.load_array(data_arrays, batch_size) return src_vocab, tgt_vocab, data_iter .. raw:: html Hãy thử đọc batch đầu tiên. .. code:: python src_vocab, tgt_vocab, train_iter = load_data_nmt(batch_size=2, num_steps=8) for X, X_vlen, Y, Y_vlen in train_iter: print('X:', X.astype('int32')) print('Valid lengths for X:', X_vlen) print('Y:', Y.astype('int32')) print('Valid lengths for Y:', Y_vlen) break .. parsed-literal:: :class: output X: [[12 0 4 1 1 1 1 1] [ 0 53 4 1 1 1 1 1]] Valid lengths for X: [3 3] Y: [[ 2 10 21 0 4 3 1 1] [ 2 0 4 3 1 1 1 1]] Valid lengths for Y: [6 4] .. raw:: html Tóm tắt ------- .. raw:: html - Dịch máy (*machine translation* - MT) là việc dịch tự động một đoạn văn bản từ ngôn ngữ này sang ngôn ngữ khác. - Ta đọc, tiền xử lý và token hóa bộ dữ liệu từ cả ngôn ngữ nguồn và ngôn ngữ đích. .. raw:: html Bài tập ------- .. raw:: html Tìm và xử lý một bộ dữ liệu dịch máy. .. raw:: html .. raw:: html Thảo luận --------- - `Tiếng Anh `__ - `Tiếng Việt `__ 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 Duy Du - Nguyễn Văn Quang - Phạm Minh Đức - Lê Khắc Hồng Phúc - Nguyễn Văn Cường - Phạm Hồng Vinh