{ "cells": [ { "cell_type": "markdown", "id": "bb641062-5756-4915-aae0-d3ed2fc5c0b9", "metadata": {}, "source": [ "# 嵌入层 nn.Embedding() 代码示例\n", "\n", "> 引导文章:[g. 嵌入层 nn.Embedding() 详解和要点提醒(PyTorch)](https://github.com/Hoper-J/AI-Guide-and-Demos-zh_CN/blob/master/Guide/g.%20嵌入层%20nn.Embedding()%20详解和要点提醒(PyTorch).md)\n", "\n", "在线链接:[Kaggle](https://www.kaggle.com/code/aidemos/20-nn-embedding) | [Colab](https://colab.research.google.com/drive/1BLgmxasxOD1HQGrI0L1sPlYG40iba9SY?usp=sharing)" ] }, { "cell_type": "markdown", "id": "fca454d7-797e-4cd9-9e68-54833787ddbf", "metadata": {}, "source": [ "> `torch.nn.Embedding(num_embeddings, embedding_dim, padding_idx=None, max_norm=None, norm_type=2.0, scale_grad_by_freq=False, sparse=False, _weight=None, _freeze=False, device=None, dtype=None)`\n", ">\n", "> A simple lookup table that stores embeddings of a fixed dictionary and size.\n", ">\n", "> 一个简单的查找表,用于存储固定大小的字典中每个词的嵌入向量。\n", "\n", "### 参数\n", "\n", "- **num_embeddings** (int): 嵌入字典的大小,即词汇表的大小 (vocab size)。\n", "- **embedding_dim** (int): 每个嵌入向量的维度大小。\n", "- **padding_idx** (int, 可选): 指定填充(``)对应的索引值。`padding_idx` 对应的嵌入向量在训练过程中不会更新,即梯度不参与反向传播。对于新构建的 `Embedding`,`padding_idx` 处的嵌入向量默认为全零,但可以手动更新为其他值。\n", "- **max_norm** (float, 可选): 如果设置,嵌入向量的范数超过此值时将被重新归一化,使其范数等于 `max_norm`。\n", "- **norm_type** (float, 可选): 用于计算 `max_norm` 的 p-范数,默认为 2,即计算 2 范数。\n", "- **scale_grad_by_freq** (bool, 可选): 如果为 `True`,梯度将根据单词在 mini-batch 中的频率的倒数进行缩放,适用于高频词的梯度调整。默认为 `False`。\n", "- **sparse** (bool, 可选): 如果设置为 `True`,则权重矩阵的梯度为稀疏张量,适合大规模词汇表的内存优化。\n", "\n", "### 属性\n", "\n", "- **weight** (Tensor): 模块的可学习权重,形状为 `(num_embeddings, embedding_dim)`,初始值从正态分布 `N(0, 1)` 中采样。\n", "\n", "### 方法\n", "\n", "> `from_pretrained(embeddings, freeze=True, padding_idx=None, max_norm=None, norm_type=2.0, scale_grad_by_freq=False, sparse=False)`\n", ">\n", "> Create Embedding instance from given 2-dimensional FloatTensor.\n", ">\n", "> 用于从给定的 2 维浮点张量(FloatTensor)创建一个 `Embedding` 实例,通俗来讲就是自定义二维的权重矩阵。\n", "\n", "#### 参数\n", "\n", "- **embeddings** (Tensor): 一个包含嵌入权重的 `FloatTensor`。第一个维度代表 `num_embeddings`(词汇表大小,vocab_size),第二个维度代表 `embedding_dim`(嵌入向量维度)。\n", "- **freeze** (bool, 可选): 如果为 `True`,则嵌入矩阵在训练过程中保持不变,相当于设置 `embedding.weight.requires_grad = False`。默认值为 `True`。\n", "- 其余参数参考之前定义。\n", "\n", "## 数学公式\n", "\n", "假设词汇表大小为 $V$,嵌入维度为 $D$,则嵌入层可以表示为一个矩阵 $E \\in \\mathbb{R}^{V \\times D}$。\n", "\n", "给定一个输入的 token ID 序列 $\\{x_1, x_2, \\dots, x_n\\}$,嵌入层的输出为对应的嵌入向量序列 $\\{E_{x_1}, E_{x_2}, \\dots, E_{x_n}\\}$,其中每个 $E_{x_i} \\in \\mathbb{R}^D$。\n", "\n", "嵌入层接受 $x_i$ 作为输入,返回对应的行向量,公式如下:\n", "\n", "$$\n", "E(x_i) = E_{x_i}\n", "$$\n", "其中,$E$ 是嵌入矩阵,$x_i$ 是输入的 token ID,$E(x_i)$ 是对应的嵌入向量。" ] }, { "cell_type": "markdown", "id": "657b37f1-e9e0-46fa-bdd1-5c898f894231", "metadata": {}, "source": [ "## 使用示例\n" ] }, { "cell_type": "code", "execution_count": 1, "id": "2d473fa4-2377-4d20-8b80-3896d91f66fe", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "权重矩阵:\n", "tensor([[ 0.3367, 0.1288, 0.2345],\n", " [ 0.2303, -1.1229, -0.1863],\n", " [ 2.2082, -0.6380, 0.4617],\n", " [ 0.2674, 0.5349, 0.8094],\n", " [ 1.1103, -1.6898, -0.9890]])\n", "\n", "Embedding 输出:\n", "tensor([[ 0.3367, 0.1288, 0.2345],\n", " [ 2.2082, -0.6380, 0.4617],\n", " [ 1.1103, -1.6898, -0.9890]], grad_fn=)\n" ] } ], "source": [ "import torch\n", "import torch.nn as nn\n", "\n", "# 设置随机种子以确保结果可复现\n", "torch.manual_seed(42)\n", "\n", "# 定义嵌入层参数\n", "num_embeddings = 5 # 假设词汇表中有 5 个 token\n", "embedding_dim = 3 # 每个 token 对应 3 维嵌入向量\n", "\n", "# 初始化嵌入层\n", "embedding = nn.Embedding(num_embeddings, embedding_dim)\n", "\n", "# 定义整数索引\n", "input_indices = torch.tensor([0, 2, 4])\n", "\n", "# 查找嵌入向量\n", "output = embedding(input_indices)\n", "\n", "# 打印结果\n", "print(\"权重矩阵:\")\n", "print(embedding.weight.data)\n", "print(\"\\nEmbedding 输出:\")\n", "print(output)\n" ] }, { "cell_type": "markdown", "id": "0a397f47-238b-4792-ac79-d29ff3ad85d3", "metadata": {}, "source": [ "## `padding_idx` 的作用" ] }, { "cell_type": "code", "execution_count": 2, "id": "5dcbc0c3-443b-43d7-b64f-ed672884d6f2", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "权重矩阵:\n", "tensor([[ 0.0000, 0.0000, 0.0000],\n", " [-0.7658, -0.7506, 1.3525],\n", " [ 0.6863, -0.3278, 0.7950],\n", " [ 0.2815, 0.0562, 0.5227],\n", " [-0.2384, -0.0499, 0.5263]])\n" ] } ], "source": [ "import torch\n", "import torch.nn as nn\n", "\n", "# 定义嵌入层,指定 padding_idx=0\n", "embedding = nn.Embedding(num_embeddings=5, embedding_dim=3, padding_idx=0)\n", "\n", "# 打印权重矩阵\n", "print(\"权重矩阵:\")\n", "print(embedding.weight.data)" ] }, { "cell_type": "markdown", "id": "3b770a15-389b-4b31-bb86-2ceabde92126", "metadata": {}, "source": [ "**验证 `padding_idx` 对应的嵌入是否在训练过程中保持不变**:\n" ] }, { "cell_type": "code", "execution_count": 3, "id": "dda62291-7c57-4e84-a12f-d0cae4ccbbaa", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "梯度:\n", "tensor([[ 0.0000, 0.0000, 0.0000],\n", " [ 0.0000, 0.0000, 0.0000],\n", " [-0.0395, 0.1529, 0.3742],\n", " [ 0.0000, 0.0000, 0.0000],\n", " [-0.0863, 0.0353, 0.2030]])\n", "原权重矩阵:\n", "tensor([[ 0.0000, 0.0000, 0.0000],\n", " [-0.7658, -0.7506, 1.3525],\n", " [ 0.6863, -0.3278, 0.7950],\n", " [ 0.2815, 0.0562, 0.5227],\n", " [-0.2384, -0.0499, 0.5263]])\n", "权重矩阵更新后:\n", "tensor([[ 0.0000, 0.0000, 0.0000],\n", " [-0.7658, -0.7506, 1.3525],\n", " [ 0.6903, -0.3430, 0.7576],\n", " [ 0.2815, 0.0562, 0.5227],\n", " [-0.2297, -0.0534, 0.5060]])\n" ] } ], "source": [ "import torch\n", "import torch.optim as optim\n", "\n", "# 注意之前设置了 padding_idx=0\n", "input_indices = torch.tensor([0, 2, 4])\n", "\n", "# 定义一个简单的损失函数\n", "loss_fn = nn.MSELoss()\n", "\n", "# 目标值\n", "target = torch.randn(3, 3)\n", "\n", "# 定义优化器\n", "optimizer = optim.SGD(embedding.parameters(), lr=0.1)\n", "\n", "# 清空之前的梯度\n", "optimizer.zero_grad()\n", "\n", "# 前向传播\n", "output = embedding(input_indices)\n", "\n", "# 计算损失\n", "loss = loss_fn(output, target)\n", "\n", "# 反向传播,注意此时不会更新权重\n", "loss.backward()\n", "\n", "# 查看梯度\n", "print(\"梯度:\")\n", "print(embedding.weight.grad)\n", "\n", "# 打印原权重矩阵\n", "print(\"原权重矩阵:\")\n", "print(embedding.weight.data)\n", "\n", "# 更新权重\n", "optimizer.step()\n", "\n", "# 打印权重矩阵更新后\n", "print(\"权重矩阵更新后:\")\n", "print(embedding.weight.data)" ] }, { "cell_type": "markdown", "id": "498b10f1-8685-49a8-9954-52f78694c726", "metadata": {}, "source": [ "## 使用预训练的嵌入向量\n", "有时候,我们希望使用预训练的嵌入(如 GloVe、Word2Vec)来初始化嵌入层。这时候可以使用 `from_pretrained` 方法:" ] }, { "cell_type": "code", "execution_count": 4, "id": "9f11cb0b-927e-4de0-a82d-246402776b43", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "权重矩阵:\n", "tensor([[ 0.3367, 0.1288, 0.2345],\n", " [ 0.2303, -1.1229, -0.1863],\n", " [ 2.2082, -0.6380, 0.4617],\n", " [ 0.2674, 0.5349, 0.8094],\n", " [ 1.1103, -1.6898, -0.9890]])\n" ] } ], "source": [ "import torch\n", "import torch.nn as nn\n", "\n", "# 设置随机种子以确保结果可复现\n", "torch.manual_seed(42)\n", "\n", "# 假设我们有一个预训练的嵌入矩阵,这里只是随机初始化\n", "pretrained_embeddings = torch.randn(5, 3)\n", "\n", "# 使用 from_pretrained 方法创建嵌入层,不冻结权重层(默认冻结)\n", "embedding = nn.Embedding.from_pretrained(pretrained_embeddings, freeze=False)\n", "\n", "# 查看权重矩阵\n", "print(\"权重矩阵:\")\n", "print(embedding.weight.data)" ] }, { "cell_type": "markdown", "id": "10a944cb-6fd2-4946-b72d-412278606b92", "metadata": {}, "source": [ "现在设置一下 `padding_idx`:" ] }, { "cell_type": "code", "execution_count": 5, "id": "0f5692e3-dfc1-40d4-916f-eee6b2061be3", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "权重矩阵:\n", "tensor([[ 0.3367, 0.1288, 0.2345],\n", " [ 0.2303, -1.1229, -0.1863],\n", " [ 2.2082, -0.6380, 0.4617],\n", " [ 0.2674, 0.5349, 0.8094],\n", " [ 1.1103, -1.6898, -0.9890]])\n" ] } ], "source": [ "embedding = nn.Embedding.from_pretrained(pretrained_embeddings, freeze=False, padding_idx=0)\n", "\n", "# 查看权重矩阵\n", "print(\"权重矩阵:\")\n", "print(embedding.weight.data)" ] }, { "cell_type": "markdown", "id": "0af460b8-76f1-4225-ba7f-68c23b969e33", "metadata": {}, "source": [ "**注意**:虽然指定了 `padding_idx=0`,但预训练的嵌入矩阵第 0 行不会自动变为零向量。" ] }, { "cell_type": "markdown", "id": "a008de45-18a0-4d2b-a449-c53ecc0048c8", "metadata": {}, "source": [ "## 可视化" ] }, { "cell_type": "markdown", "id": "8c556b20-2d6d-4ece-9cf6-9d85f7d39dc9", "metadata": {}, "source": [ "### 环境配置\n", "\n", "假设已经安装好了 PyTorch" ] }, { "cell_type": "code", "execution_count": 6, "id": "c63fef98-6494-44f0-ad73-b6f34bfe4f5a", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Looking in indexes: http://mirrors.aliyun.com/pypi/simple\n", "Requirement already satisfied: transformers in /usr/local/lib/python3.10/dist-packages (4.57.6)\n", "Requirement already satisfied: scikit-learn in /usr/local/lib/python3.10/dist-packages (1.5.2)\n", "Requirement already satisfied: matplotlib in /usr/local/lib/python3.10/dist-packages (3.9.2)\n", "Collecting seaborn\n", " Downloading http://mirrors.aliyun.com/pypi/packages/83/11/00d3c3dfc25ad54e731d91449895a79e4bf2384dc3ac01809010ba88f6d5/seaborn-0.13.2-py3-none-any.whl (294 kB)\n", "Requirement already satisfied: filelock in /usr/local/lib/python3.10/dist-packages (from transformers) (3.25.2)\n", "Requirement already satisfied: huggingface-hub<1.0,>=0.34.0 in /usr/local/lib/python3.10/dist-packages (from transformers) (0.36.2)\n", "Requirement already satisfied: numpy>=1.17 in /usr/local/lib/python3.10/dist-packages (from transformers) (1.26.4)\n", "Requirement already satisfied: packaging>=20.0 in /usr/local/lib/python3.10/dist-packages (from transformers) (25.0)\n", "Requirement already satisfied: pyyaml>=5.1 in /usr/local/lib/python3.10/dist-packages (from transformers) (6.0.3)\n", "Requirement already satisfied: regex!=2019.12.17 in /usr/local/lib/python3.10/dist-packages (from transformers) (2026.4.4)\n", "Requirement already satisfied: requests in /usr/local/lib/python3.10/dist-packages (from transformers) (2.32.5)\n", "Requirement already satisfied: tokenizers<=0.23.0,>=0.22.0 in /usr/local/lib/python3.10/dist-packages (from transformers) (0.22.2)\n", "Requirement already satisfied: safetensors>=0.4.3 in /usr/local/lib/python3.10/dist-packages (from transformers) (0.7.0)\n", "Requirement already satisfied: tqdm>=4.27 in /usr/local/lib/python3.10/dist-packages (from transformers) (4.67.1)\n", "Requirement already satisfied: fsspec>=2023.5.0 in /usr/local/lib/python3.10/dist-packages (from huggingface-hub<1.0,>=0.34.0->transformers) (2026.2.0)\n", "Requirement already satisfied: hf-xet<2.0.0,>=1.1.3 in /usr/local/lib/python3.10/dist-packages (from huggingface-hub<1.0,>=0.34.0->transformers) (1.4.3)\n", "Requirement already satisfied: typing-extensions>=3.7.4.3 in /usr/local/lib/python3.10/dist-packages (from huggingface-hub<1.0,>=0.34.0->transformers) (4.15.0)\n", "Requirement already satisfied: scipy>=1.6.0 in /usr/local/lib/python3.10/dist-packages (from scikit-learn) (1.15.3)\n", "Requirement already satisfied: joblib>=1.2.0 in /usr/local/lib/python3.10/dist-packages (from scikit-learn) (1.5.3)\n", "Requirement already satisfied: threadpoolctl>=3.1.0 in /usr/local/lib/python3.10/dist-packages (from scikit-learn) (3.6.0)\n", "Requirement already satisfied: contourpy>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (1.3.2)\n", "Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (0.12.1)\n", "Requirement already satisfied: fonttools>=4.22.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (4.62.1)\n", "Requirement already satisfied: kiwisolver>=1.3.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (1.5.0)\n", "Requirement already satisfied: pillow>=8 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (12.0.0)\n", "Requirement already satisfied: pyparsing>=2.3.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (3.3.2)\n", "Requirement already satisfied: python-dateutil>=2.7 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (2.9.0.post0)\n", "Requirement already satisfied: pandas>=1.2 in /usr/local/lib/python3.10/dist-packages (from seaborn) (2.2.3)\n", "Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.10/dist-packages (from pandas>=1.2->seaborn) (2026.1.post1)\n", "Requirement already satisfied: tzdata>=2022.7 in /usr/local/lib/python3.10/dist-packages (from pandas>=1.2->seaborn) (2025.2)\n", "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.10/dist-packages (from python-dateutil>=2.7->matplotlib) (1.17.0)\n", "Requirement already satisfied: charset_normalizer<4,>=2 in /usr/local/lib/python3.10/dist-packages (from requests->transformers) (3.4.4)\n", "Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.10/dist-packages (from requests->transformers) (3.11)\n", "Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.10/dist-packages (from requests->transformers) (2.5.0)\n", "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.10/dist-packages (from requests->transformers) (2025.11.12)\n", "Installing collected packages: seaborn\n", "Successfully installed seaborn-0.13.2\n", "\u001b[33mWARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager, possibly rendering your system unusable. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv. Use the --root-user-action option if you know what you are doing and want to suppress this warning.\u001b[0m\u001b[33m\n", "\u001b[0m\n", "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m25.3\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m26.1.1\u001b[0m\n", "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpython3 -m pip install --upgrade pip\u001b[0m\n", "Note: you may need to restart the kernel to use updated packages.\n" ] } ], "source": [ "%pip install transformers scikit-learn matplotlib seaborn" ] }, { "cell_type": "markdown", "id": "2237de1e-6438-4214-8be6-bdb37e00b00b", "metadata": {}, "source": [ "### 完整代码" ] }, { "cell_type": "code", "execution_count": 7, "id": "2ed89523-e69a-4e33-87e9-dd15f9ed7542", "metadata": {}, "outputs": [ { "data": { "image/png": "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oUMHDR48WFeuXNH777+vMmXKOG0aMmnSJK1fv14PPfSQihUrpjNnzui9995T4cKF//UXpEtS+/btVbt2bQ0bNkwHDx5UuXLl9N133zmO826nKFetWlV58+bV66+/rujoaLm5uemBBx5QQEBAiuN37typJ554Qm3atFHDhg3l5+enEydOaN68eTp58qRmzJhx11Mkx44dq88++0z79+9XxYoVk/WfOHEi2fsrSV5eXikGbgCZJLO2UwSA1EjaVr1evXrJ+hYvXmxJsry9va0bN2449cXHx1sTJ060QkNDLRcXF6tIkSLWmDFjnLZht6ybWzE/9NBDKT730aNHrYcfftjy9PS0/P39reeee85asWLFv95OXv/YsjppW+w33njD6fFJ249//fXXKc77z63Lk45h5cqVVlhYmOXm5maVK1cu2WMty7IuXbpkjRkzxipVqpTl6upq+fv7W/Xq1bPefPNNKy4uzjHu/PnzVvfu3S0fHx/Lbrdb3bt3t3bs2JHidtuLFi2yypcvb7m5uVkVKlSwFi9enOLW4brNdvK3bhue0jbply9ftgYMGGD5+flZXl5eVvv27a39+/dbkqwpU6ZYlmVZ169ft0aMGGFVqVLF8vb2tvLnz29VqVLFeu+991J8j25n1qxZliSrdu3aKfbfWt/27dutrl27WkWLFrXc3NysgIAAq23bttbWrVudHnf27FmrY8eOlqenp1WgQAHrP//5j7Vnz55kr+nff/9tdejQwfL19bXsdrvVqVMn6+TJk8lev9RsJ3+n38F//g6fPn3aGjBggFWkSBHLxcXFCgoKspo1a2Z99NFHTsfwbz4TlnXzqwYqVapkubq6WmXLlrU+//zzZNvJr1692nrkkUeskJAQy9XV1QoJCbG6du3q9DUIt9tOPn/+/Mme89b5Levme/HEE09Y3t7elt1ut3r16mVt2rTJkmR9+eWXdz2O2bNnWyVKlLDy5s1712M/ffq0NWXKFKtx48ZWcHCwlS9fPqtAgQLWAw88YH3zzTdOY1P6fP/z+CSlaTv5lLbvB5B5bJZ1D1cvAwCQySIiIlStWjV9/vnnqVqZAu5kyZIl6tChgzZu3JhuG4oAwD9xjRcAIMu7evVqsrYZM2YoT548atSoUSZUhOzs1t+npGsYfXx8VL169UyqCkBOxzVeAIAsb+rUqdq2bZuaNm2qfPny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KzQ0VOPGjVNISIhj58Py5curdevW6tevnz744APFx8dr4MCB6tKlS6p2NAQAAACAe5GtgtfWrVvVtGlTx/2hQ4dKknr27Km5c+dq5MiRunz5sp5++mlFRUWpQYMGWrFihdzd3R2PmT9/vgYOHKhmzZopT5486tixo955550MPxYAAAAAuUe2/R6vzML3eAEAAACQcun3eAEAgKytSZMmGjRokIYMGaICBQooMDBQs2fP1uXLl9W7d295e3urVKlSWr58uSQpISFBffr0UWhoqDw8PFS2bFm9/fbbTnP26tVL7du315tvvqng4GAVLFhQAwYMUHx8fGYcIgDcFsELAABkmHnz5snf31+//fabBg0apGeeeUadOnVSvXr1tH37drVs2VLdu3fXlStXlJiYqMKFC+vrr7/W3r17NX78eL3wwgtauHCh05xr1qzRoUOHtGbNGs2bN09z587V3LlzM+cAAeA2ONUwjTjVEACAe9OkSRMlJCRow4YNkm6uaNntdj366KP69NNPJUmRkZEKDg5WeHi47r///mRzDBw4UJGRkfrmm28k3VzxWrt2rQ4dOqS8efNKkh5//HHlyZNHX375ZQYdGYDcKi3ZIFttrgEAALK3sLAwx8958+ZVwYIFVblyZUdbYGCgJDm+d3PWrFn65JNPdOzYMV29elVxcXGqWrWq05wVK1Z0hC5JCg4O1u7duw0eBQCkHacaAgCADOPi4uJ032azObXZbDZJUmJior788ksNHz5cffr00Y8//qiIiAj17t1bcXFxd50zMTHR0BEAwL1hxQsAAKS76CtxOhcbp5hr8fLxcJF/ftc0z7Fp0ybVq1dPzz77rKPt0KFD6VkmAGQYghcAAEhXJ6OuatSiXdpw4JyjrVFpf8XdSNsqVOnSpfXpp59q5cqVCg0N1WeffaYtW7YoNDQ0vUsGAOM41RAAAKSb6CtxyUKXJK0/cE5/nY3V9fiEVM/1n//8R48++qg6d+6sOnXq6Pz5806rXwCQnbCrYRqxqyEAALd36Eysmk1fd9v+1UMbq2SAVwZWBADm8AXKAAAgU8Rcu/MXF1+6Sz8A5FQELwAAkG583F3u2O99l34AyKkIXgAAIN34e7mqUWn/FPsalfaXv1fadzcEgJyA4AUAANKN3dNVUzqGJQtfjUr76/WOYbJ7ErwA5E5sJw8AANJViK+HZnatpnOxcbp0LV7e7i7y93IldAHI1QheAAAg3dk9CVoA8E+caggAAAAAhhG8AAAAAMAwghcAAAAAGEbwAgAAAADDCF4AAAAAYBjBCwAAAAAMI3gBAAAAgGEELwAAAAAwjOAFAAAAAIYRvAAAAADAMIIXAAAAABhG8AIAAAAAwwheAAAAAGAYwQsAAAAADCN4AQAAAIBhBC8AAAAAMIzgBQAAAACGEbwAAAAAwDCCFwAAAAAYRvACAAAAAMMIXgAAAABgGMELAAAAAAwjeAEAAACAYQQvAAAAADCM4AUAAAAAhhG8AAAAAMAwghcAAAAAGEbwAgAAAADDCF4AAAAAYBjBCwAAAAAMI3gBAAAAgGEELwAAAAAwjOAFAAAAAIYRvAAAAADAMIIXAAAAABhG8AIAAAAAwwheAAAAAGAYwQsAAAAADCN4AQAAAIBhBC8AAAAAMIzgBQAAAACGEbwAAAAAwDCCFwAAAAAYRvACAAAAAMMIXgAAAABgGMELAAAAAAwjeAEAAACAYQQvAAAAADCM4AUAAAAAhhG8AAAAAMAwghcAAAAAGEbwAgAAAADDCF4AAAAAYBjBCwAAZCnLli2Tr6+vEhISJEkRERGy2WwaPXq0Y0zfvn315JNPSpIWLVqkihUrys3NTcWLF9e0adOc5itevLheeeUV9ejRQ15eXipWrJi+++47nT17Vo888oi8vLwUFhamrVu3Oh5z/vx5de3aVffdd588PT1VuXJlffHFF07zNmnSRIMHD9bIkSPl5+enoKAgTZgwwdCrAiC7I3gBAIAspWHDhrp06ZJ27NghSVq3bp38/f21du1ax5h169apSZMm2rZtmx5//HF16dJFu3fv1oQJEzRu3DjNnTvXac633npL9evX144dO/TQQw+pe/fu6tGjh5588klt375dJUuWVI8ePWRZliTp2rVrqlGjhn744Qft2bNHTz/9tLp3767ffvvNad558+Ypf/782rx5s6ZOnapJkyZp1apVRl8fANmTzUr6LwxSJSYmRna7XdHR0fLx8cnscgAAyJFq1Kihrl27avjw4erQoYNq1aqliRMn6vz584qOjlbhwoX1559/asKECTp79qx+/PFHx2NHjhypH374Qb///rukmyteDRs21GeffSZJioyMVHBwsMaNG6dJkyZJkn799VfVrVtXp06dUlBQUIo1tW3bVuXK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coI0B6/Dhw+rWrVqydrd3Nx0+fLldCkKAAAAAHKSNAev0NBQRUREJGtfsWKFypcvnx41AQAAAECOkuZdDYcOHaoBAwbo2rVrsixLv/32m7744gtNnjxZH3/8sYkaAQAAACBbS3Pw6tu3rzw8PPTiiy/qypUreuKJJxQSEqK3335bXbp0MVEjAAAAAGRrNsuyrHt98JUrVxQbG6uAgID0rClLS8u3UwMAAADIudKSDe7pe7ySeHp65qrQlV1dvnxZPXr0kJeXl4KDgzVt2jQ1adJEQ4YMkSTZbDYtWbLE6TG+vr6aO3eu4/7x48f1+OOPy9fXV35+fnrkkUd05MgRp8d8/PHHKl++vNzd3VWuXDm99957jr4jR47IZrNp8eLFatq0qTw9PVWlShWFh4cbOmoAAAAg60hz8Dp//rwGDBigChUqyN/fX35+fk43ZD0jRozQunXrtHTpUv34449au3attm/fnurHx8fHq1WrVvL29taGDRu0adMmeXl5qXXr1oqLi5MkzZ8/X+PHj9err76qffv26bXXXtO4ceM0b948p7nGjh2r4cOHKyIiQmXKlFHXrl1148aNdD1eAAAAIKtJ8zVe3bt318GDB9WnTx8FBgbKZrOZqAvpJDY2Vv/973/1+eefq1mzZpKkefPmqXDhwqme46uvvlJiYqI+/vhjx/s9Z84c+fr6au3atWrZsqVeeuklTZs2TY8++qikm7tf7t27Vx9++KF69uzpmGv48OF66KGHJEkTJ05UxYoVdfDgQZUrVy69DhkAAADIctIcvDZs2KCNGzeqSpUqJupBOjt06JDi4uJUp04dR5ufn5/Kli2b6jl27typgwcPytvb26n92rVrOnTokC5fvqxDhw6pT58+6tevn6P/xo0bstvtTo8JCwtz/BwcHCxJOnPmDMELAAAAOVqag1e5cuV09epVE7XgX4q+EqdzsXGKuRYvHw8X+ed3TdXjbDabbt1jJT4+3vFzbGysatSoofnz5yd7bKFChRQbGytJmj17tlPAk6S8efM63XdxcXF6XklKTExMVZ0AAABAdpXm4PXee+9p9OjRGj9+vCpVquT0h7QkdvrLJCejrmrUol3acOCco61RaX+Na1VCLi4u2rx5s4oWLSpJunjxov788081btxY0s3wdOrUKcfjDhw4oCtXrjjuV69eXV999ZUCAgJSfH/t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ghcAAAAAGEbwAgAAAADDCF4AAAAAYBjBCwAAAAAMI3gBALItm82mJUuWZHYZd7R27VrZbDZFRUVldikAgExE8AIAZCm9evWSzWaTzWaTi4uLAgMD1aJFC33yySdKTEx0Gnvq1Cm1adMmkypNnXr16unUqVOy2+1Gn2f9+vVq166dQkJCskUgBYDchuAFAMhyWrdurVOnTunIkSNavny5mjZtqueee05t27bVjRs3HOOCgoLk5uaWiZXenaurq4KCgmSz2Yw+z+XLl1WlShXNmjXL6PMAAO4NwQsAkOW4ubkpKChI9913n6pXr64XXnhBS5cu1fLlyzV37lzHuH+u7Bw5ckQ2m00LFy5Uw4YN5eHhoVq1aunPP//Uli1bVLNmTXl5ealNmzY6e/as0/N9/PHHKl++vNzd3VWuXDm99957jr6keRcvXqymTZvK09NTVapUUXh4uGPM0aNH1a5dOxUoUED58+dXxYoV9b///U9SyqcaLlq0SBUrVpSbm5uKFy+uadOmOdVTvHhxvfbaa3rqqafk7e2tokWL6qOPPrrja9amTRu98sor6tChQ1peagBABiF4AQCyhQceeEBVqlTR4sWL7zjupZde0osvvqjt27crX758euKJJzRy5Ei9/fbb2rBhgw4ePKjx48c7xs+fP1/jx4/Xq6++qn379um1117TuHHjNG/ePKd5x44dq+HDhysiIkJlypRR165dHatvAwYM0PXr17V+/Xrt3r1br7/+ury8vFKsb9u2bXr88cfVpUsX7d69WxMmTNC4ceOcAqUkTZs2TTVr1tSOHTv07LPP6plnntH+/fvv4ZUDAGQF+TK7AAAAUqtcuXLatWvXHccMHz5crVq1kiQ999xz6tq1q1avXq369etLkvr06eMUcl566SVNmzZNjz76qCQpNDRUe/fu1YcffqiePXs6zfvQQw9JkiZOnKiKFSvq4MGDKleunI4dO6aOHTuqcuXKkqQSJUrctr7p06erWbNmGjdunCSpTJky2rt3r9544w316tXLMe7BBx/Us88+K0kaNWqU3nrrLa1Zs0Zly5ZNzUsFAMhiWPECAGQblmXd9VqpsLAwx8+BgYGS5AhESW1nzpyRdPO6qEOHDqlPnz7y8vJy3F555RUdOnTotvMGBwdLkmOewYMH65VXXlH9+vX10ksv3TEc7tu3zxECk9SvX18HDhxQQkJCis9ns9kUFBTkeD4AQPZD8AIAZBv79u1TaGjoHce4uLg4fk4Kabe2Je2OGBsbK0maPXu2IiIiHLc9e/bo119/veu8SfP07dtXf/31l7p3767du3erZs2amjlz5r0eZrLnu7VuAED2Q/ACAGQLP//8s3bv3q2OHTum25yBgYEKCQnRX3/9pVKlSjnd7hbwblWkSBH1799fixcv1rBhwzR79uwUx5UvX16bNm1yatu0aZPKlCmjvHnz3vOxAACyNq7xAgBkOdevX1dkZKQSEhJ0+vRprVixQpMnT1bbtm3Vo0ePdH2uiRMnavDgwbLb7WrdurWuX7+urVu36uLFixo6dGiq5hgyZIjatGmjMmXK6OLFi1qzZo3Kly+f4thhw4apVq1aevnll9W5c2eFh4fr3XffddpJ8V7Exsbq4MGDjvuHDx9WRESE/Pz8VLRo0X81NwDg3yN4AQCynBUrVig4OFj58uVTgQIFVKVKFb3zzjvq2bOn8uRJ35M1+vbtK09PT73xxhsaMWKE8ufPr8qVK2vIkCGpniMhIUEDBgzQ33//LR8fH7Vu3VpvvfVWimOrV6+uhQsXavz48Xr55ZcVHBysSZMmOW2scS+2bt2qpk2bOu4nhcaePXsm2zERAJDxbJZlWZldBAAAAADkZFzjBQAAAACGEbwAAAAAwDCCFwAAAAAYRvACAAAAAMMIXgAAAABgGMELAAAAAAwjeAEAAACAYQQvAAAAADCM4AUAAAAAhhG8AAAAAMAwghcAAAAAGPb/AGUh6R6m61r9AAAAAElFTkSuQmCC", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import os\n", "# 设置模型下载镜像(注意,需要在导入 transformers 等模块前进行设置才能起效)\n", "os.environ['HF_ENDPOINT'] = 'https://hf-mirror.com'\n", "\n", "import torch\n", "from transformers import AutoTokenizer, AutoModel\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", "import seaborn as sns\n", "from sklearn.manifold import TSNE\n", "\n", "# 选择预训练的 BERT 模型\n", "model_name = 'bert-base-uncased' # 对于中文模型,可使用 'bert-base-chinese'\n", "\n", "# 加载分词器和模型\n", "tokenizer = AutoTokenizer.from_pretrained(model_name)\n", "model = AutoModel.from_pretrained(model_name)\n", "\n", "# 要可视化的词语列表\n", "words = ['cat', 'dog', 'apple', 'orange', 'king', 'queen', 'man', 'woman']\n", "# words = ['猫', '狗', '苹果', '橙子', '国王', '王后', '男人', '女人']\n", "\n", "def get_input_embeddings(tokenizer, model, words):\n", " # 获取词表中的索引\n", " word_ids = [tokenizer.convert_tokens_to_ids(word) for word in words]\n", " # 从嵌入层提取对应的向量\n", " embeddings = model.embeddings.word_embeddings.weight[word_ids]\n", " return embeddings.detach().numpy()\n", "\n", "def get_output_embeddings(tokenizer, model, words):\n", " embeddings = []\n", " for word in words:\n", " inputs = tokenizer(word, return_tensors='pt')\n", " outputs = model(**inputs)\n", " # 获取 [CLS] 向量\n", " cls_embedding = outputs.last_hidden_state[0][0] # [CLS] 的向量\n", " embeddings.append(cls_embedding.detach().numpy())\n", " return np.array(embeddings)\n", " \n", "# 获取输入嵌入向量\n", "embeddings = get_input_embeddings(tokenizer, model, words)\n", "\n", "# 降维处理\n", "tsne = TSNE(n_components=2, perplexity=2, max_iter=1000, random_state=42)\n", "embeddings_2d = tsne.fit_transform(embeddings)\n", "\n", "# 可视化\n", "plt.figure(figsize=(10, 10))\n", "sns.scatterplot(x=embeddings_2d[:, 0], y=embeddings_2d[:, 1])\n", "\n", "for i, word in enumerate(words):\n", " plt.text(embeddings_2d[i, 0]+0.5, embeddings_2d[i, 1]+0.5, word)\n", "\n", "plt.title('Word Embeddings Visualized using t-SNE')\n", "plt.xlabel('Dimension 1')\n", "plt.ylabel('Dimension 2')\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": null, "id": "9005bf77-ae18-4239-83d2-a54328123c4e", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.12" } }, "nbformat": 4, "nbformat_minor": 5 }