{ "cells": [ { "cell_type": "markdown", "id": "d50a512e", "metadata": { "origin_pos": 0 }, "source": [ "# Các Dataset cho Pretraining Word Embeddings\n", ":label:`sec_word2vec_data`\n", "\n", "Bây giờ chúng ta đã biết các chi tiết kỹ thuật của các mô hình word2vec và các phương pháp đào tạo gần đúng, chúng ta hãy đi qua các triển khai của họ. Cụ thể, chúng tôi sẽ lấy mô hình skip-gram trong :numref:`sec_word2vec` và lấy mẫu âm trong :numref:`sec_approx_train` làm ví dụ. Trong phần này, chúng ta bắt đầu với tập dữ liệu để đào tạo trước mô hình nhúng từ: định dạng ban đầu của dữ liệu sẽ được chuyển thành các minibatches có thể được lặp lại trong quá trình đào tạo.\n" ] }, { "cell_type": "code", "execution_count": 1, "id": "7301138d", "metadata": { "execution": { "iopub.execute_input": "2022-04-26T12:17:55.396130Z", "iopub.status.busy": "2022-04-26T12:17:55.395600Z", "iopub.status.idle": "2022-04-26T12:17:58.592306Z", "shell.execute_reply": "2022-04-26T12:17:58.591581Z" }, "origin_pos": 2, "tab": [ "pytorch" ] }, "outputs": [], "source": [ "import math\n", "import os\n", "import random\n", "import torch\n", "from d2l import torch as d2l" ] }, { "cell_type": "markdown", "id": "4f781d45", "metadata": { "origin_pos": 3 }, "source": [ "## Đọc tập dữ liệu\n", "\n", "Tập dữ liệu mà chúng tôi sử dụng ở đây là [Penn Tree Bank (PTB)](https://catalog.ldc.upenn.edu/LDC99T42). Cơ sở này được lấy mẫu từ các bài báo của Wall Street Journal, được chia thành các bộ đào tạo, xác nhận và kiểm tra. Ở định dạng ban đầu, mỗi dòng của tệp văn bản đại diện cho một câu của các từ được phân tách bằng dấu cách. Ở đây chúng ta coi từng từ như một mã thông báo.\n" ] }, { "cell_type": "code", "execution_count": 2, "id": "4f76ce75", "metadata": { "execution": { "iopub.execute_input": "2022-04-26T12:17:58.595998Z", "iopub.status.busy": "2022-04-26T12:17:58.595769Z", "iopub.status.idle": "2022-04-26T12:17:59.127736Z", "shell.execute_reply": "2022-04-26T12:17:59.127042Z" }, "origin_pos": 4, "tab": [ "pytorch" ] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Downloading ../data/ptb.zip from http://d2l-data.s3-accelerate.amazonaws.com/ptb.zip...\n" ] }, { "data": { "text/plain": [ "'# sentences: 42069'" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "#@save\n", "d2l.DATA_HUB['ptb'] = (d2l.DATA_URL + 'ptb.zip',\n", " '319d85e578af0cdc590547f26231e4e31cdf1e42')\n", "\n", "#@save\n", "def read_ptb():\n", " \"\"\"Load the PTB dataset into a list of text lines.\"\"\"\n", " data_dir = d2l.download_extract('ptb')\n", " # Read the training set.\n", " with open(os.path.join(data_dir, 'ptb.train.txt')) as f:\n", " raw_text = f.read()\n", " return [line.split() for line in raw_text.split('\\n')]\n", "\n", "sentences = read_ptb()\n", "f'# sentences: {len(sentences)}'" ] }, { "cell_type": "markdown", "id": "885b16ad", "metadata": { "origin_pos": 5 }, "source": [ "Sau khi đọc bộ đào tạo, chúng tôi xây dựng một từ vựng cho corpus, trong đó bất kỳ từ nào xuất hiện dưới 10 lần được thay thế bằng mã thông báo \"\". Lưu ý rằng tập dữ liệu gốc cũng chứa \"\" token đại diện cho các từ hiếm (không xác định).\n" ] }, { "cell_type": "code", "execution_count": 3, "id": "451dc813", "metadata": { "execution": { "iopub.execute_input": "2022-04-26T12:17:59.130857Z", "iopub.status.busy": "2022-04-26T12:17:59.130635Z", "iopub.status.idle": "2022-04-26T12:17:59.288972Z", "shell.execute_reply": "2022-04-26T12:17:59.288318Z" }, "origin_pos": 6, "tab": [ "pytorch" ] }, "outputs": [ { "data": { "text/plain": [ "'vocab size: 6719'" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "vocab = d2l.Vocab(sentences, min_freq=10)\n", "f'vocab size: {len(vocab)}'" ] }, { "cell_type": "markdown", "id": "6841cfbd", "metadata": { "origin_pos": 7 }, "source": [ "## Lấy mẫu phụ\n", "\n", "Dữ liệu văn bản thường có các từ tần số cao như “the”, “a”, và “in”: chúng thậm chí có thể xảy ra hàng tỷ lần trong thể rất lớn. Tuy nhiên, những từ này thường đồng xuất hiện với nhiều từ khác nhau trong các cửa sổ ngữ cảnh, cung cấp ít tín hiệu hữu ích. Ví dụ, hãy xem xét từ “chip” trong một cửa sổ ngữ cảnh: trực giác sự xuất hiện của nó với một từ tần số thấp “intel” hữu ích hơn trong đào tạo hơn là đồng xuất hiện với một từ tần số cao “a”. Hơn nữa, đào tạo với một lượng lớn các từ (tần số cao) là chậm. Do đó, khi đào tạo từ nhúng mô hình, các từ tần số cao có thể là * mẫu con* :cite:`Mikolov.Sutskever.Chen.ea.2013`. Cụ thể, mỗi từ được lập chỉ mục $w_i$ trong tập dữ liệu sẽ bị loại bỏ với xác suất \n", "\n", "$$ P(w_i) = \\max\\left(1 - \\sqrt{\\frac{t}{f(w_i)}}, 0\\right),$$\n", "\n", "trong đó $f(w_i)$ là tỷ lệ của số từ $w_i$ với tổng số từ trong tập dữ liệu và hằng số $t$ là một siêu tham số ($10^{-4}$ trong thí nghiệm). Chúng ta có thể thấy rằng chỉ khi tần số tương đối $f(w_i) > t$, từ $w_i$ mới có thể bị loại bỏ và tần số tương đối của từ càng cao thì xác suất bị loại bỏ càng lớn.\n" ] }, { "cell_type": "code", "execution_count": 4, "id": "ea1df605", "metadata": { "execution": { "iopub.execute_input": "2022-04-26T12:17:59.293510Z", "iopub.status.busy": "2022-04-26T12:17:59.292990Z", "iopub.status.idle": "2022-04-26T12:18:00.697863Z", "shell.execute_reply": "2022-04-26T12:18:00.697128Z" }, "origin_pos": 8, "tab": [ "pytorch" ] }, "outputs": [], "source": [ "#@save\n", "def subsample(sentences, vocab):\n", " \"\"\"Subsample high-frequency words.\"\"\"\n", " # Exclude unknown tokens ''\n", " sentences = [[token for token in line if vocab[token] != vocab.unk]\n", " for line in sentences]\n", " counter = d2l.count_corpus(sentences)\n", " num_tokens = sum(counter.values())\n", "\n", " # Return True if `token` is kept during subsampling\n", " def keep(token):\n", " return(random.uniform(0, 1) <\n", " math.sqrt(1e-4 / counter[token] * num_tokens))\n", "\n", " return ([[token for token in line if keep(token)] for line in sentences],\n", " counter)\n", "\n", "subsampled, counter = subsample(sentences, vocab)" ] }, { "cell_type": "markdown", "id": "62df28cc", "metadata": { "origin_pos": 9 }, "source": [ "Đoạn mã sau vẽ biểu đồ của số lượng mã thông báo trên mỗi câu trước và sau khi lấy mẫu. Đúng như dự đoán, subsampling rút ngắn đáng kể các câu bằng cách thả các từ tần số cao, điều này sẽ dẫn đến tăng tốc đào tạo.\n" ] }, { "cell_type": "code", "execution_count": 5, "id": "b4befcbc", "metadata": { "execution": { "iopub.execute_input": "2022-04-26T12:18:00.701808Z", "iopub.status.busy": "2022-04-26T12:18:00.701314Z", "iopub.status.idle": "2022-04-26T12:18:00.925383Z", "shell.execute_reply": "2022-04-26T12:18:00.924705Z" }, "origin_pos": 10, "tab": [ "pytorch" ] }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2022-04-26T12:18:00.874464\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.5.1, https://matplotlib.org/\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n" ], "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "d2l.show_list_len_pair_hist(['origin', 'subsampled'], '# tokens per sentence',\n", " 'count', sentences, subsampled);" ] }, { "cell_type": "markdown", "id": "1fb5c4cf", "metadata": { "origin_pos": 11 }, "source": [ "Đối với mã thông báo riêng lẻ, tỷ lệ lấy mẫu của từ tần số cao “the” nhỏ hơn 1/20.\n" ] }, { "cell_type": "code", "execution_count": 6, "id": "aa8da215", "metadata": { "execution": { "iopub.execute_input": "2022-04-26T12:18:00.928837Z", "iopub.status.busy": "2022-04-26T12:18:00.928350Z", "iopub.status.idle": "2022-04-26T12:18:00.965803Z", "shell.execute_reply": "2022-04-26T12:18:00.965099Z" }, "origin_pos": 12, "tab": [ "pytorch" ] }, "outputs": [ { "data": { "text/plain": [ "'# of \"the\": before=50770, after=2033'" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def compare_counts(token):\n", " return (f'# of \"{token}\": '\n", " f'before={sum([l.count(token) for l in sentences])}, '\n", " f'after={sum([l.count(token) for l in subsampled])}')\n", "\n", "compare_counts('the')" ] }, { "cell_type": "markdown", "id": "95dc9fa1", "metadata": { "origin_pos": 13 }, "source": [ "Ngược lại, các từ tần số thấp “tham gia” được giữ hoàn toàn.\n" ] }, { "cell_type": "code", "execution_count": 7, "id": "47e3a30e", "metadata": { "execution": { "iopub.execute_input": "2022-04-26T12:18:00.969209Z", "iopub.status.busy": "2022-04-26T12:18:00.968726Z", "iopub.status.idle": "2022-04-26T12:18:01.005833Z", "shell.execute_reply": "2022-04-26T12:18:01.005116Z" }, "origin_pos": 14, "tab": [ "pytorch" ] }, "outputs": [ { "data": { "text/plain": [ "'# of \"join\": before=45, after=45'" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "compare_counts('join')" ] }, { "cell_type": "markdown", "id": "268c5170", "metadata": { "origin_pos": 15 }, "source": [ "Sau khi lấy mẫu đăng ký, chúng tôi ánh xạ mã thông báo đến các chỉ số của họ cho corpus.\n" ] }, { "cell_type": "code", "execution_count": 8, "id": "9858fc64", "metadata": { "execution": { "iopub.execute_input": "2022-04-26T12:18:01.009095Z", "iopub.status.busy": "2022-04-26T12:18:01.008614Z", "iopub.status.idle": "2022-04-26T12:18:01.401481Z", "shell.execute_reply": "2022-04-26T12:18:01.400807Z" }, "origin_pos": 16, "tab": [ "pytorch" ] }, "outputs": [ { "data": { "text/plain": [ "[[], [392, 2115, 145, 274, 406], [5277, 3054, 1580]]" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "corpus = [vocab[line] for line in subsampled]\n", "corpus[:3]" ] }, { "cell_type": "markdown", "id": "ec3a5b0b", "metadata": { "origin_pos": 17 }, "source": [ "## Trích xuất từ trung tâm và từ ngữ cảnh\n", "\n", "Hàm `get_centers_and_contexts` sau trích xuất tất cả các từ trung tâm và từ ngữ cảnh của chúng từ `corpus`. Nó đồng đều mẫu một số nguyên giữa 1 và `max_window_size` một cách ngẫu nhiên như kích thước cửa sổ ngữ cảnh. Đối với bất kỳ từ trung tâm nào, những từ có khoảng cách từ nó không vượt quá kích thước cửa sổ ngữ cảnh được lấy mẫu là các từ ngữ cảnh của nó.\n" ] }, { "cell_type": "code", "execution_count": 9, "id": "67ecd9a3", "metadata": { "execution": { "iopub.execute_input": "2022-04-26T12:18:01.404881Z", "iopub.status.busy": "2022-04-26T12:18:01.404396Z", "iopub.status.idle": "2022-04-26T12:18:01.410893Z", "shell.execute_reply": "2022-04-26T12:18:01.410209Z" }, "origin_pos": 18, "tab": [ "pytorch" ] }, "outputs": [], "source": [ "#@save\n", "def get_centers_and_contexts(corpus, max_window_size):\n", " \"\"\"Return center words and context words in skip-gram.\"\"\"\n", " centers, contexts = [], []\n", " for line in corpus:\n", " # To form a \"center word--context word\" pair, each sentence needs to\n", " # have at least 2 words\n", " if len(line) < 2:\n", " continue\n", " centers += line\n", " for i in range(len(line)): # Context window centered at `i`\n", " window_size = random.randint(1, max_window_size)\n", " indices = list(range(max(0, i - window_size),\n", " min(len(line), i + 1 + window_size)))\n", " # Exclude the center word from the context words\n", " indices.remove(i)\n", " contexts.append([line[idx] for idx in indices])\n", " return centers, contexts" ] }, { "cell_type": "markdown", "id": "1be4fe01", "metadata": { "origin_pos": 19 }, "source": [ "Tiếp theo, chúng ta tạo ra một tập dữ liệu nhân tạo có chứa hai câu 7 và 3 từ, tương ứng. Hãy để kích thước cửa sổ ngữ cảnh tối đa là 2 và in tất cả các từ trung tâm và các từ ngữ cảnh của chúng.\n" ] }, { "cell_type": "code", "execution_count": 10, "id": "f5c1982e", "metadata": { "execution": { "iopub.execute_input": "2022-04-26T12:18:01.413926Z", "iopub.status.busy": "2022-04-26T12:18:01.413415Z", "iopub.status.idle": "2022-04-26T12:18:01.418413Z", "shell.execute_reply": "2022-04-26T12:18:01.417709Z" }, "origin_pos": 20, "tab": [ "pytorch" ] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "dataset [[0, 1, 2, 3, 4, 5, 6], [7, 8, 9]]\n", "center 0 has contexts [1, 2]\n", "center 1 has contexts [0, 2, 3]\n", "center 2 has contexts [0, 1, 3, 4]\n", "center 3 has contexts [1, 2, 4, 5]\n", "center 4 has contexts [2, 3, 5, 6]\n", "center 5 has contexts [3, 4, 6]\n", "center 6 has contexts [4, 5]\n", "center 7 has contexts [8, 9]\n", "center 8 has contexts [7, 9]\n", "center 9 has contexts [8]\n" ] } ], "source": [ "tiny_dataset = [list(range(7)), list(range(7, 10))]\n", "print('dataset', tiny_dataset)\n", "for center, context in zip(*get_centers_and_contexts(tiny_dataset, 2)):\n", " print('center', center, 'has contexts', context)" ] }, { "cell_type": "markdown", "id": "19acb733", "metadata": { "origin_pos": 21 }, "source": [ "Khi đào tạo trên tập dữ liệu PTB, chúng tôi đặt kích thước cửa sổ ngữ cảnh tối đa là 5. Sau đây trích xuất tất cả các từ trung tâm và các từ ngữ cảnh của chúng trong tập dữ liệu.\n" ] }, { "cell_type": "code", "execution_count": 11, "id": "200d4cb4", "metadata": { "execution": { "iopub.execute_input": "2022-04-26T12:18:01.421448Z", "iopub.status.busy": "2022-04-26T12:18:01.420936Z", "iopub.status.idle": "2022-04-26T12:18:03.039454Z", "shell.execute_reply": "2022-04-26T12:18:03.038864Z" }, "origin_pos": 22, "tab": [ "pytorch" ] }, "outputs": [ { "data": { "text/plain": [ "'# center-context pairs: 1497165'" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "all_centers, all_contexts = get_centers_and_contexts(corpus, 5)\n", "f'# center-context pairs: {sum([len(contexts) for contexts in all_contexts])}'" ] }, { "cell_type": "markdown", "id": "38895ff3", "metadata": { "origin_pos": 23 }, "source": [ "## Lấy mẫu âm\n", "\n", "Chúng tôi sử dụng lấy mẫu tiêu cực cho đào tạo gần đúng. Để lấy mẫu các từ nhiễu theo một phân phối được xác định trước, chúng ta xác định lớp `RandomGenerator` sau, trong đó phân phối lấy mẫu (có thể không chuẩn hóa) được truyền qua đối số `sampling_weights`.\n" ] }, { "cell_type": "code", "execution_count": 12, "id": "8588ea81", "metadata": { "execution": { "iopub.execute_input": "2022-04-26T12:18:03.042879Z", "iopub.status.busy": "2022-04-26T12:18:03.042363Z", "iopub.status.idle": "2022-04-26T12:18:03.048049Z", "shell.execute_reply": "2022-04-26T12:18:03.047391Z" }, "origin_pos": 24, "tab": [ "pytorch" ] }, "outputs": [], "source": [ "#@save\n", "class RandomGenerator:\n", " \"\"\"Randomly draw among {1, ..., n} according to n sampling weights.\"\"\"\n", " def __init__(self, sampling_weights):\n", " # Exclude\n", " self.population = list(range(1, len(sampling_weights) + 1))\n", " self.sampling_weights = sampling_weights\n", " self.candidates = []\n", " self.i = 0\n", "\n", " def draw(self):\n", " if self.i == len(self.candidates):\n", " # Cache `k` random sampling results\n", " self.candidates = random.choices(\n", " self.population, self.sampling_weights, k=10000)\n", " self.i = 0\n", " self.i += 1\n", " return self.candidates[self.i - 1]" ] }, { "cell_type": "markdown", "id": "8e789082", "metadata": { "origin_pos": 25 }, "source": [ "Ví dụ: chúng ta có thể vẽ 10 biến ngẫu nhiên $X$ trong số các chỉ số 1, 2 và 3 với xác suất lấy mẫu $P(X=1)=2/9, P(X=2)=3/9$ và $P(X=3)=4/9$ như sau.\n" ] }, { "cell_type": "markdown", "id": "adcc1bae", "metadata": { "origin_pos": 27 }, "source": [ "Đối với một cặp từ trung tâm và từ ngữ cảnh, chúng tôi lấy mẫu ngẫu nhiên `K` (5 trong thí nghiệm) các từ tiếng ồn. Theo các đề xuất trong bài báo word2vec, xác suất lấy mẫu $P(w)$ của một từ tiếng ồn $w$ được đặt thành tần số tương đối của nó trong từ điển nâng lên công suất 0,75 :cite:`Mikolov.Sutskever.Chen.ea.2013`.\n" ] }, { "cell_type": "code", "execution_count": 13, "id": "ac33d1b6", "metadata": { "execution": { "iopub.execute_input": "2022-04-26T12:18:03.051251Z", "iopub.status.busy": "2022-04-26T12:18:03.050668Z", "iopub.status.idle": "2022-04-26T12:18:14.214504Z", "shell.execute_reply": "2022-04-26T12:18:14.213795Z" }, "origin_pos": 28, "tab": [ "pytorch" ] }, "outputs": [], "source": [ "#@save\n", "def get_negatives(all_contexts, vocab, counter, K):\n", " \"\"\"Return noise words in negative sampling.\"\"\"\n", " # Sampling weights for words with indices 1, 2, ... (index 0 is the\n", " # excluded unknown token) in the vocabulary\n", " sampling_weights = [counter[vocab.to_tokens(i)]**0.75\n", " for i in range(1, len(vocab))]\n", " all_negatives, generator = [], RandomGenerator(sampling_weights)\n", " for contexts in all_contexts:\n", " negatives = []\n", " while len(negatives) < len(contexts) * K:\n", " neg = generator.draw()\n", " # Noise words cannot be context words\n", " if neg not in contexts:\n", " negatives.append(neg)\n", " all_negatives.append(negatives)\n", " return all_negatives\n", "\n", "all_negatives = get_negatives(all_contexts, vocab, counter, 5)" ] }, { "cell_type": "markdown", "id": "7c44c54d", "metadata": { "origin_pos": 29 }, "source": [ "## Tải ví dụ đào tạo trong Minibatches\n", ":label:`subsec_word2vec-minibatch-loading`\n", "\n", "Sau khi tất cả các từ trung tâm cùng với các từ ngữ cảnh và các từ tiếng ồn được lấy mẫu được trích xuất, chúng sẽ được chuyển thành các ví dụ nhỏ có thể được tải lặp lại trong quá trình đào tạo. \n", "\n", "Trong một minibatch, ví dụ $i^\\mathrm{th}$ bao gồm một từ trung tâm và $n_i$ từ ngữ cảnh của nó và $m_i$ từ nhiễu. Do kích thước cửa sổ ngữ cảnh khác nhau, $n_i+m_i$ thay đổi cho $i$ khác nhau. Do đó, đối với mỗi ví dụ, chúng tôi nối các từ ngữ cảnh và các từ nhiễu của nó trong biến `contexts_negatives` và pad số không cho đến khi độ dài nối đạt $\\max_i n_i+m_i$ (`max_len`). Để loại trừ các miếng đệm trong tính toán tổn thất, chúng tôi xác định một biến mặt nạ `masks`. Có một sự tương ứng một-một giữa các phần tử trong `masks` và các phần tử trong `contexts_negatives`, trong đó số không (nếu không) trong `masks` tương ứng với các miếng đệm trong `contexts_negatives`. \n", "\n", "Để phân biệt giữa các ví dụ tích cực và tiêu cực, chúng ta tách các từ ngữ cảnh khỏi các từ nhiễu trong `contexts_negatives` thông qua một biến `labels`. Tương tự như `masks`, cũng có sự tương ứng một-một giữa các phần tử trong `labels` và các phần tử trong `contexts_negatives`, trong đó các phần tử (nếu không số không) trong `labels` tương ứng với các từ ngữ cảnh (ví dụ tích cực) trong `contexts_negatives`. \n", "\n", "Ý tưởng trên được thực hiện trong hàm `batchify` sau. Đầu vào của nó `data` là một danh sách có độ dài bằng với kích thước lô, trong đó mỗi phần tử là một ví dụ bao gồm từ trung tâm `center`, các từ ngữ cảnh của nó `context`, và các từ nhiễu của nó `negative`. Hàm này trả về một minibatch có thể được nạp để tính toán trong quá trình đào tạo, chẳng hạn như bao gồm biến mask.\n" ] }, { "cell_type": "code", "execution_count": 14, "id": "18be62a7", "metadata": { "execution": { "iopub.execute_input": "2022-04-26T12:18:14.218133Z", "iopub.status.busy": "2022-04-26T12:18:14.217638Z", "iopub.status.idle": "2022-04-26T12:18:14.223723Z", "shell.execute_reply": "2022-04-26T12:18:14.223144Z" }, "origin_pos": 30, "tab": [ "pytorch" ] }, "outputs": [], "source": [ "#@save\n", "def batchify(data):\n", " \"\"\"Return a minibatch of examples for skip-gram with negative sampling.\"\"\"\n", " max_len = max(len(c) + len(n) for _, c, n in data)\n", " centers, contexts_negatives, masks, labels = [], [], [], []\n", " for center, context, negative in data:\n", " cur_len = len(context) + len(negative)\n", " centers += [center]\n", " contexts_negatives += [context + negative + [0] * (max_len - cur_len)]\n", " masks += [[1] * cur_len + [0] * (max_len - cur_len)]\n", " labels += [[1] * len(context) + [0] * (max_len - len(context))]\n", " return (torch.tensor(centers).reshape((-1, 1)), torch.tensor(\n", " contexts_negatives), torch.tensor(masks), torch.tensor(labels))" ] }, { "cell_type": "markdown", "id": "9833f269", "metadata": { "origin_pos": 31 }, "source": [ "Hãy để chúng tôi kiểm tra chức năng này bằng cách sử dụng một minibatch gồm hai ví dụ.\n" ] }, { "cell_type": "code", "execution_count": 15, "id": "62c26d0f", "metadata": { "execution": { "iopub.execute_input": "2022-04-26T12:18:14.226878Z", "iopub.status.busy": "2022-04-26T12:18:14.226395Z", "iopub.status.idle": "2022-04-26T12:18:14.232537Z", "shell.execute_reply": "2022-04-26T12:18:14.231839Z" }, "origin_pos": 32, "tab": [ "pytorch" ] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "centers = tensor([[1],\n", " [1]])\n", "contexts_negatives = tensor([[2, 2, 3, 3, 3, 3],\n", " [2, 2, 2, 3, 3, 0]])\n", "masks = tensor([[1, 1, 1, 1, 1, 1],\n", " [1, 1, 1, 1, 1, 0]])\n", "labels = tensor([[1, 1, 0, 0, 0, 0],\n", " [1, 1, 1, 0, 0, 0]])\n" ] } ], "source": [ "x_1 = (1, [2, 2], [3, 3, 3, 3])\n", "x_2 = (1, [2, 2, 2], [3, 3])\n", "batch = batchify((x_1, x_2))\n", "\n", "names = ['centers', 'contexts_negatives', 'masks', 'labels']\n", "for name, data in zip(names, batch):\n", " print(name, '=', data)" ] }, { "cell_type": "markdown", "id": "e78f9c94", "metadata": { "origin_pos": 33 }, "source": [ "## Đặt tất cả mọi thứ lại với nhau\n", "\n", "Cuối cùng, chúng ta định nghĩa hàm `load_data_ptb` đọc tập dữ liệu PTB và trả về bộ lặp dữ liệu và từ vựng.\n" ] }, { "cell_type": "code", "execution_count": 16, "id": "1b094a2e", "metadata": { "execution": { "iopub.execute_input": "2022-04-26T12:18:14.235636Z", "iopub.status.busy": "2022-04-26T12:18:14.235169Z", "iopub.status.idle": "2022-04-26T12:18:14.242242Z", "shell.execute_reply": "2022-04-26T12:18:14.241627Z" }, "origin_pos": 35, "tab": [ "pytorch" ] }, "outputs": [], "source": [ "#@save\n", "def load_data_ptb(batch_size, max_window_size, num_noise_words):\n", " \"\"\"Download the PTB dataset and then load it into memory.\"\"\"\n", " num_workers = d2l.get_dataloader_workers()\n", " sentences = read_ptb()\n", " vocab = d2l.Vocab(sentences, min_freq=10)\n", " subsampled, counter = subsample(sentences, vocab)\n", " corpus = [vocab[line] for line in subsampled]\n", " all_centers, all_contexts = get_centers_and_contexts(\n", " corpus, max_window_size)\n", " all_negatives = get_negatives(\n", " all_contexts, vocab, counter, num_noise_words)\n", "\n", " class PTBDataset(torch.utils.data.Dataset):\n", " def __init__(self, centers, contexts, negatives):\n", " assert len(centers) == len(contexts) == len(negatives)\n", " self.centers = centers\n", " self.contexts = contexts\n", " self.negatives = negatives\n", "\n", " def __getitem__(self, index):\n", " return (self.centers[index], self.contexts[index],\n", " self.negatives[index])\n", "\n", " def __len__(self):\n", " return len(self.centers)\n", "\n", " dataset = PTBDataset(all_centers, all_contexts, all_negatives)\n", "\n", " data_iter = torch.utils.data.DataLoader(dataset, batch_size, shuffle=True,\n", " collate_fn=batchify,\n", " num_workers=num_workers)\n", " return data_iter, vocab" ] }, { "cell_type": "markdown", "id": "1f7d7c8c", "metadata": { "origin_pos": 36 }, "source": [ "Hãy để chúng tôi in minibatch đầu tiên của bộ lặp dữ liệu.\n" ] }, { "cell_type": "code", "execution_count": 17, "id": "9d34eda5", "metadata": { "execution": { "iopub.execute_input": "2022-04-26T12:18:14.245446Z", "iopub.status.busy": "2022-04-26T12:18:14.244989Z", "iopub.status.idle": "2022-04-26T12:18:29.784948Z", "shell.execute_reply": "2022-04-26T12:18:29.784150Z" }, "origin_pos": 37, "tab": [ "pytorch" ] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "centers shape: torch.Size([512, 1])\n", "contexts_negatives shape: torch.Size([512, 60])\n", "masks shape: torch.Size([512, 60])\n", "labels shape: torch.Size([512, 60])\n" ] } ], "source": [ "data_iter, vocab = load_data_ptb(512, 5, 5)\n", "for batch in data_iter:\n", " for name, data in zip(names, batch):\n", " print(name, 'shape:', data.shape)\n", " break" ] }, { "cell_type": "markdown", "id": "1684f2fe", "metadata": { "origin_pos": 38 }, "source": [ "## Tóm tắt\n", "\n", "* Các từ tần số cao có thể không hữu ích trong đào tạo. Chúng tôi có thể subsample chúng để tăng tốc trong đào tạo.\n", "* Đối với hiệu quả tính toán, chúng tôi tải các ví dụ trong minibatches. Chúng ta có thể xác định các biến khác để phân biệt các miếng đệm từ các miếng đệm không và các ví dụ tích cực với các biến tiêu cực.\n", "\n", "## Bài tập\n", "\n", "1. Làm thế nào để thời gian chạy của mã trong phần này thay đổi nếu không sử dụng subsampling?\n", "1. Các `RandomGenerator` lớp lưu trữ `k` kết quả lấy mẫu ngẫu nhiên. Đặt `k` thành các giá trị khác và xem nó ảnh hưởng đến tốc độ tải dữ liệu như thế nào.\n", "1. Những siêu tham số khác trong mã của phần này có thể ảnh hưởng đến tốc độ tải dữ liệu?\n" ] }, { "cell_type": "markdown", "id": "a456ce81", "metadata": { "origin_pos": 40, "tab": [ "pytorch" ] }, "source": [ "[Discussions](https://discuss.d2l.ai/t/1330)\n" ] } ], "metadata": { "language_info": { "name": "python" } }, "nbformat": 4, "nbformat_minor": 5 }