<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:wfw="http://wellformedweb.org/CommentAPI/">
<channel>
<title>人工智能基地 - chengligen</title>
<link>https://www.blog.os-o.cn/index.php/author/1/</link>
<atom:link href="https://www.blog.os-o.cn/index.php/feed/author/1/" rel="self" type="application/rss+xml" />
<language>zh-CN</language>
<description>chengligen</description>
<lastBuildDate>Thu, 01 Feb 2024 20:47:00 +0800</lastBuildDate>
<pubDate>Thu, 01 Feb 2024 20:47:00 +0800</pubDate>
<item>
<title>【NLP基础知识五】文本分类之神经网络文本分类、多标签分类</title>
<link>https://www.blog.os-o.cn/index.php/archives/103/</link>
<guid>https://www.blog.os-o.cn/index.php/archives/103/</guid>
<pubDate>Thu, 01 Feb 2024 20:47:00 +0800</pubDate>
<dc:creator>chengligen</dc:creator>
<description><![CDATA[如果你是NLP领域初学者，欢迎关注我的博客，我不仅会分享理论知识，更会通过实例和实用技巧帮助你迅速入门。我的目标是让每个初学者都能轻松理解复杂的NLP概念，并在实践中掌握这一领域的核心技能。通过...]]></description>
<content:encoded xml:lang="zh-CN"><![CDATA[
<p>如果你是NLP领域初学者，欢迎关注我的博客，我不仅会分享理论知识，更会通过实例和实用技巧帮助你迅速入门。我的目标是让每个初学者都能轻松理解复杂的NLP概念，并在实践中掌握这一领域的核心技能。<br>通过我的博客，你将了解到：</p><ul><li>NLP的基础概念，为你打下坚实的学科基础。</li><li>实际项目中的应用案例，让你更好地理解NLP技术在现实生活中的应用。</li><li>学习和成长的资源，助你在NLP领域迅速提升自己。</li></ul><p>不论你是刚刚踏入NLP的大门，还是这个领域的资深专家，我的博客都将为你提供有益的信息。一起探索语言的边界，迎接未知的挑战，让我们共同在NLP的海洋中畅游！期待与你一同成长，感谢你的关注和支持。欢迎任何人前来讨论问题。</p><p><a href="https://blog.os-o.cn/index.php/archives/5/">【大模型实战】使用 Langchain-Chatchat 进行本地部署的完整指南</a><br><a href="https://blog.os-o.cn/index.php/archives/15/">【大模型实践】ChatGLM3-6B 微调实践，更新模型知识</a><br><a href="https://blog.os-o.cn/index.php/archives/42/">【NLP基础知识一】词嵌入（Word Embeddings）</a><br><a href="https://blog.os-o.cn/index.php/archives/51/">【NLP基础知识二】词嵌入（Word Embeddings）之“Word2Vec：一种基于预测的方法”</a><br><a href="https://blog.os-o.cn/index.php/archives/78/">【NLP基础知识三】词嵌入（Word Embeddings）之“GloVe：单词表示的全局向量”</a><br><a href="https://blog.os-o.cn/index.php/archives/96/">【NLP基础知识四】文本分类</a></p><h2>4、神经网络文本分类</h2><h3>流程概括</h3><pre><code>让神经网络学习有用的特征，而非手动定义特征。</code></pre><p>基于神经网络的分类的主要思想是：可以使用神经网络获得输入文本的特征表示。我们将输入token的词嵌入（Embedding）传入神经网络，用于提供输入文本的向量表示。 之后，再使用该向量用于分类。<br><img src="https://blog.os-o.cn/usr/uploads/2024/02/1739241307.png" alt="2024-02-01T12:35:02.png" title="2024-02-01T12:35:02.png"><br>在处理神经网络时，我们可以以非常简单的方式考虑分类部分（即如何从文本的向量表示中获取类别概率）。<br><img src="https://blog.os-o.cn/usr/uploads/2024/02/2753858722.png" alt="2024-02-01T12:35:12.png" title="2024-02-01T12:35:12.png"><br>经过神经网络的文本向量表示的维度为 $d$。而最终，我们需要一个大小为 $K$（ $K$ 个类别的概率）的向量。 要从 $d$ 大小得到 $K$ 大小的向量，我们可以使用线性层。 一旦我们有了一个 $K$ 大小的向量，剩下的就是应用 softmax 操作将原始数字转换为类概率。</p><h4>分类部分：仍是逻辑斯蒂回归！</h4><p>让我们看一下神经网络分类器。 我们使用输入文本的向量表示的方式与我们使用经典方法的逻辑斯蒂回归的方式完全相同：我们根据每个类的特征权重对特征进行加权。 与经典方法的逻辑斯蒂回归的唯一区别在于特征的来源：它们要么是手动定义的（就像我们之前所做的那样），要么是通过神经网络获得的。<br><img src="https://blog.os-o.cn/usr/uploads/2024/02/3298368103.png" alt="2024-02-01T12:39:04.png" title="2024-02-01T12:39:04.png"></p><h4>直觉：文本表示指向类表示的方向</h4><p><img src="https://blog.os-o.cn/usr/uploads/2024/02/473050333.png" alt="2024-02-01T12:39:23.png" title="2024-02-01T12:39:23.png"><br>如果我们更仔细地观察这个最后的线性层，我们会看到它的矩阵的列其实就是向量 $w_i$。这些向量可以被认为是类的向量表示。一个好的神经网络将学习以这种方式以表示输入文本，即文本向量将指向相应类向量的方向。</p><h3>训练和交叉熵损失</h3><p>训练神经网络分类器来预测类的概率分布，直观地说，就是在每一步，我们都会最大化模型分配给正确类别的概率。<br>标准损失函数是 交叉熵损失 (cross-entropy loss) 。 目标概率分布的交叉熵损失$p^{\ast} = (0,</p><pre><code>                        \dots, 0, 1, 0, \dots)_{K\times 1}$ （目标标签为 1，其余为 0），并且预测的模型分布为 $p=(p_1, \dots, p_K), p_i=p(i|x)$</code></pre><p>$$
Loss(p^{\ast}, p^{})= - p^{\ast} \log(p) = -\sum\limits_{i=1}^{K}p_i^{\ast} \log(p_i).
$$</p><p>因为只有一个 $p_i^{\ast}$ 是非零的（目标标签 k 为 1，其余为 0），我们将得到： $Loss(p^{\ast}, p) = -\log(p_{k})=-\log(p(k| x)).$ 具体的训练样本示例见下图。<br><img src="https://blog.os-o.cn/usr/uploads/2024/02/4101424115.png" alt="2024-02-01T12:42:42.png" title="2024-02-01T12:42:42.png"><br>在训练中，我们多次迭代，逐步提高模型权重，具体来说：迭代训练样本（或批量样本）并进行梯度更新。在每一步，我们最大化模型分配给正确类别的概率。同时，也最小化错误类别的概率总和：由于所有概率的总和是恒定的，通过增加一个概率我们减少所有其他概率的总和 （ 编者按：这里我通常想象一群小猫从同一个碗里吃东西: 一只小猫吃得多总是以其他小猫吃得少为代价 ）。<br>训练过程见下图。</p><h4>概括：这相当于最大化数据似然</h4><p>不要忘记在谈论最大熵分类器（逻辑回归）时，我们表明最小化交叉熵等同于最大化数据似然。因此，这里我们也试图得到模型参数的 最大似然估计（MLE） 。</p><h3>文本分类模型</h3><pre><code>我们需要一个可以为不同长度的输入生成固定大小向量的模型。</code></pre><p><img src="https://blog.os-o.cn/usr/uploads/2024/02/1427386447.png" alt="2024-02-01T13:07:08.png" title="2024-02-01T13:07:08.png"><br>在这一部分中，我们将研究使用神经网络获取输入文本的向量表示的不同方法。请注意，虽然输入文本可以有不同的长度，但文本的向量表示必须具有固定大小：否则，网络将无法“工作”。<br>我们从仅使用词嵌入的最简单方法开始（在此之上不添加模型）。然后我们看看循环和卷积网络。</p><h4>基础知识：Bag of Embeddings (BOE) 和加权 BOE</h4><p>你可以做的最简单的事情是仅使用词嵌入（Word Embedding）而不使用任何神经网络。为了获得所有文本的向量表示，我们可以对所有token的Embedding（Bag of Embeddings）求和，也可以使用这些Embedding的加权和（例如，权重为TF-IDF或其他）。<br>Bag of Embeddings（理想情况下，连同朴素贝叶斯）应该是任何具有神经网络模型的基线模型：如果你不能做得比这更好，那么根本不值得使用 NN。如果你没有太多数据，可能会出现效果低于基线模型的状况。<br><img src="https://blog.os-o.cn/usr/uploads/2024/02/1256027941.png" alt="2024-02-01T13:08:21.png" title="2024-02-01T13:08:21.png"><br>虽然Bag of Embeddings (BOE) 有时称为词袋 (BOW)，但请注意， 这两者是非常不同的。 BOE 是嵌入（Embedding）的总和，BOW 是 one-hot 向量的总和： 相较而言，BOE 对语言了解更多。预训练的嵌入（例如 Word2Vec 或 GloVe）能够理解单词之间的相似性。例如， awesome, brilliant, great 在 BOW 中是不相关的特征表示，但在 BOE 中会有相似的词向量表示。<br>另请注意，如果到这里，想使用嵌入的加权和，可能会要用一种方法来获得权重。然而，这正是我们希望通过使用神经网络来避免的：我们不想引入手动特征，而是让网络学习有用的模式。</p><h5>BOE作为 SVM 的特征</h5><p>你可以在 BOE 之上使用 SVM！与经典方法中的支持向量机SVM的唯一区别在于内核的选择，这里 RBF 核更好。</p><h3>模型：循环（RNN/LSTM/等）</h3><p>从某种意义上说，循环网络是处理文本的一种自然方式，类似于人类，它们一个接一个地“读取”一系列token并处理信息。 并希望在每一步，神经网络都会“记住”它之前读过的所有内容 （译者注：这里的“所有”是相对而言的理想状况，而受模型及参数限制，会进行一定的遗忘。）</p><h4>基础知识：循环神经网络（RNN）</h4><ul><li>RNN单元</li></ul><p>在每一步中，循环网络都会接收一个新的输入向量（例如，token embedding）和之前的序列状态（希望能够对所有之前的信息进行编码）。 使用这个输入，RNN 单元计算它作为输出的新状态。 这个新状态能够有效包含当前输入和先前序列的信息。<br><img src="https://blog.os-o.cn/usr/uploads/2024/02/4115176406.png" alt="2024-02-01T13:09:23.png" title="2024-02-01T13:09:23.png"></p><ul><li>RNN 读取一系列tokens</li></ul><p>如图所示，RNN 逐个标记读取文本标记，在每一步都使用新的token embedding和先前的状态。<br>请注意， RNN 单元在每一步是相同的 ！</p><ul><li>Vanilla RNN</li></ul><p>最简单的循环网络， Vanilla RNN，对 $h_{t-1}$ 和 $x_t$ 使用线性变换，然后应用非线性函数（最常见的是 $\tanh$ 函数）：</p><p>$$
h_t = \tanh(h_{t-1}W_h + x_tW_t).
$$</p><p><img src="https://blog.os-o.cn/usr/uploads/2024/02/2162103904.png" alt="2024-02-01T13:10:36.png" title="2024-02-01T13:10:36.png"><br>Vanilla RNN 存在梯度消失和爆炸的问题。为了缓解这个问题，更复杂的循环单元（例如 LSTM、GRU 等）对输入执行多个操作并使用门（gates）。有关 RNN 基础知识的更多详细信息，请查看 Colah 的博客文章.</p><h5>循环神经网络文本分类</h5><p>在这里，我们（终于！）看看如何使用循环模型进行文本分类。你将在此处看到的所有内容都将适用于所有循环单元，并且在本部分中，“RNN”指的是普遍意义上的的循环单元（例如 vanilla RNN、LSTM、GRU 等）。<br>让我们回忆一下我们需要什么：</p><pre><code>我们需要一个可以为不同长度的输入生成固定大小向量的模型。</code></pre><ul><li>简单模型：阅读文本，获取最终状态</li></ul><p>最简单的循环模型是一层 RNN 网络。在这个网络中，我们必须采用更了解输入文本的状态。因此，我们必须使用最后一个状态，只有这个状态才能看到所有输入token。<br><img src="https://blog.os-o.cn/usr/uploads/2024/02/808371934.png" alt="2024-02-01T13:11:53.png" title="2024-02-01T13:11:53.png"></p><ul><li>多层模型：将状态从一层 RNN 传递到下一层</li></ul><p>为了获得更好的文本表示，你可以堆叠多个层。在这种情况下，较高 RNN 的输入是来自前一层的表示。<br>主要假设是，通过多层，较低层将捕获局部现象（例如，短语），而较高层将能够学习更多抽象概念（例如，主题）。<br><img src="https://blog.os-o.cn/usr/uploads/2024/02/2064930546.png" alt="2024-02-01T13:12:17.png" title="2024-02-01T13:12:17.png"></p><ul><li>双向模型：使用前向和后向RNN 的最终状态。</li></ul><p>以前的方法可能有一个问题：最后一个状态很容易“忘记”早期的token。即使是像 LSTM 这样的强大模型仍然会受到影响！<br>为了避免这种情况，我们可以使用两个 RNN： 前向，从左到右读取输入， 后向，从右到左读取输入。 然后我们可以使用两个模型的最终状态：一个会更好地记住文本的最后部分，另一个类似地也会很好地记住文本的开头部分。这些状态可以连接，求和，或其他。这就是你的选择了！<br><img src="https://blog.os-o.cn/usr/uploads/2024/02/3713901834.png" alt="2024-02-01T13:12:34.png" title="2024-02-01T13:12:34.png"></p><ul><li>组合模型：随心所欲！</li></ul><p>你可以结合上面的想法。例如，在多层网络中，某些层可以朝相反的方向移动，等等。</p><h3>模型：卷积（CNN）</h3><p>卷积模型的详细描述在卷积模型补充文章中。在本章该部分中，我们只考虑用于文本分类的卷积。</p><h4>用于图像的卷积和平移不变性</h4><p>卷积网络最初是为计算机视觉任务而开发的。因此，让我们首先了解图像卷积模型背后的直觉。<br>想象一下，我们想要将图像分类为几个类别，例如猫、狗、飞机等。在这种情况下，如果你在图像上找到一只猫，你并不关心 这只猫在图像上的什么位置：你只关心它是否存在在某个地方。<br><img src="https://blog.os-o.cn/usr/uploads/2024/02/3169478419.png" alt="2024-02-01T13:13:14.png" title="2024-02-01T13:13:14.png"><br>卷积网络针对图像的每个局部应用相同的操作：这就是它们提取特征的方式。每个操作都在寻找与模式的匹配，并且网络会学习哪些模式是有用的。随着层数的增加，学习模式变得越来越复杂：从早期层的线条到上层非常复杂的模式（例如，整只猫或狗）。你可以查看分析与解释 章节中的示例。<br>这个属性被称为 平移不变性： 平移是因为我们谈论的是空间的变化， 不变性是因为我们认为它在此处无关紧要。</p><h5>文本卷积</h5><p>对于图像，一切都很清楚：例如我们能够移动一只猫，因为我们不在乎猫在哪里。但是文本呢？乍一看，这不是那么简单：我们不能轻易移动短语，因为意思会改变，或者我们会得到一些没有多大意义的东西。<br>但是，在某些应用程序中，我们可以想到相同的直觉。假设我们想要对文本进行分类，但不是像图像中那样对猫/狗进行分类，而是对正面/负面情绪进行分类。然后有一些单词和短语可能是非常有用的“线索”（例如 it's been great, bored to death, absolutely amazing, the best ever等） ，以及其他根本不重要的。我们不太在乎我们在文本中看到 bored to death 在文本哪一部分哪来理解情绪，对吧？<br><img src="https://blog.os-o.cn/usr/uploads/2024/02/3824707529.png" alt="2024-02-01T13:13:39.png" title="2024-02-01T13:13:39.png"></p><h5>典型模型：卷积+池化块</h5><p>按照上面的直觉，我们想检测一些模式，但我们不太关心这些模式到底在哪里。此行为通过两层实现：</p><ul><li>卷积：查找与模式匹配的内容（如我们在上面看到的猫头）；</li><li>池化：聚合这些匹配的位置（局部或全局）。</li></ul><p>用于文本分类的典型卷积模型如图所示。为了获得输入文本的向量表示，将卷积层应用于词嵌入，然后是非线性（通常是 ReLU）和池化操作。这种表示用于分类的方式与其他网络类似。<br><img src="https://blog.os-o.cn/usr/uploads/2024/02/4285115146.png" alt="2024-02-01T13:14:04.png" title="2024-02-01T13:14:04.png"><br>在下文中，我们将详细讨论主要的构建模块、卷积和池化，然后考虑总体建模。</p><h5>基础知识：文本的卷积层</h5><p>卷积神经网络最初是为计算机视觉任务而开发的，例如图像分类（猫与狗等）。卷积的想法是用滑动窗口遍历图像，并对每个窗口应用相同的 卷积Filter操作。<br><img src="https://blog.os-o.cn/usr/uploads/2024/02/2647111114.png" alt="2024-02-01T13:14:23.png" title="2024-02-01T13:14:23.png"><br>该图（取自 仓库）显示了一个Filter的卷积过程 底部是输入图像，Filter顶部是输出。由于图像具有二维（宽度和高度），因此卷积是二维的。<br>与图像不同，文本只有一维：这里的卷积是一维的：看插图。</p><h5>卷积是应用于每个窗口的线性运算</h5><p><img src="https://blog.os-o.cn/usr/uploads/2024/02/4024207209.png" alt="2024-02-01T13:14:40.png" title="2024-02-01T13:14:40.png"><br>卷积是应用于每个输入窗口的线性层（后面是非线性层）。 正式地，让我们假设</p><ul><li>$(x_1, \dots, x_n)$ - 输入词的表示 $x_i\in \mathbb{R}^d$；</li><li>$d$ （输入通道） - 输入Embedding的大小；</li><li>$k$ （卷积核大小） - 卷积窗口的长度（上图中$k=3$）；</li><li>$m$ （输出通道） - 卷积Filter的数量（即卷积产生的通道数）。</li></ul><p>那么卷积就是一个线性层 $W\in\mathbb{R}^{(k\cdot d)\times m}$ 。对于一个大小为 $k$ 的窗口 $(x_i, \dots x_{i+k-1})$，卷积将这些向量拼接起来：</p><p>$$
u_i = [x_i, \dots x_{i+k-1}]\in\mathbb{R}^{k\cdot d}
$$</p><p>然后乘以卷积矩阵：</p><p>$$
F_i = u_i \times W.
$$</p><p>卷积使用滑动窗口遍历输入，并对每个窗口应用相同的线性变换。</p><h5>直觉：每个Filter都提取一个特征</h5><p>直观地说，卷积中的每个Filter都会提取一个特征。</p><ul><li>一个Filter - 一个特征提取器</li></ul><p>Filter考虑了当前窗口中的向量表示并将它们线性转换为单个特征。 形式上，对于一个窗口 $u_i = [x_i, \dots x_{i+k-1}]\in\mathbb{R}^{k\cdot d}$ ， Filter $f\in\mathbb{R}^{k\cdot d}$ 计算了窗口内元素的点积：</p><p>$$
F_i^{(f)} = (f, u_i).
$$</p><p>$F_i^{(f)}$ （提取的“特征”）的数量是将Filter $f$ 应用于窗口 $(x_i, \dots x_{i+k-1})$ 后的结果。<br><img src="https://blog.os-o.cn/usr/uploads/2024/02/2673704290.png" alt="2024-02-01T13:20:39.png" title="2024-02-01T13:20:39.png"></p><ul><li>m 个Filter：m 个特征提取器</li></ul><p><img src="https://blog.os-o.cn/usr/uploads/2024/02/68720622.png" alt="2024-02-01T13:20:28.png" title="2024-02-01T13:20:28.png"><br>一个Filter提取一个单一的特征。通常情况下，我们想要许多特征：为此，我们必须采取几个Filter。每个Filter读取一个输入文本并提取一个不同的特征 - 请看图。Filter的数量就是你想得到的输出特征的数量。如果有 $m$ 个Filter而不是一个，我们上面讨论的卷积层的大小将变成 $(k\cdot d)\times m$<br>这是并行完成的！ 请注意，虽然我向你展示了 CNN 如何“读取”文本，但实际上这些计算是并行完成的。</p><h5>基础知识：池化操作</h5><p>在一个卷积从每个窗口中提取 $m$ 个特征之后，一个池化层总结了某个区域的特征。池化 层用于减少输入维度，从而减少网络使用的参数数量。</p><ul><li>最大池化和平均池化</li></ul><p>最流行的是最大池化（max-pooling）：它在每个维度上取最大值，即取每个特征的最大值。<br><img src="https://blog.os-o.cn/usr/uploads/2024/02/4221231340.png" alt="2024-02-01T13:21:46.png" title="2024-02-01T13:21:46.png"><br>直观地说，每个特征在看到某种模式时都会“触发”：图像中的视觉模式（线条、纹理、猫爪等）或文本模式（例如短语）。在池化操作之后，我们有一个向量来说明输入中出现了哪些模式。<br>均值池化（Mean-pooling） 的工作方式类似，但计算每个特征的均值而不是最大值。</p><ul><li>池化和全局池化</li></ul><p>与卷积类似，池化应用于多个元素的窗口。池化也有步幅参数，最常见的方法是使用非重叠窗口的池化。为此，你必须将 步长（stride） 参数设置为与池大小相同。如下图所示。<br><img src="https://blog.os-o.cn/usr/uploads/2024/02/843006140.png" alt="2024-02-01T13:22:03.png" title="2024-02-01T13:22:03.png"><br>池化和全局池化 之间的区别在于，池化独立应用于每个窗口中的特征，而全局池化对整个输入执行。对于文本，通常使用全局池化来获得表示整个文本的单个向量；这种全局池化称为 max-over-time pooling，其中“时间”轴从第一个输入token到最后一个输入token。<br><img src="https://blog.os-o.cn/usr/uploads/2024/02/4018606531.png" alt="2024-02-01T13:22:10.png" title="2024-02-01T13:22:10.png"></p><h5>用于文本分类的卷积神经网络</h5><p>现在，当我们了解卷积和池化的工作原理后，让我们来进行建模。首先，让我们回顾一下我们需要什么：</p><pre><code>我们需要一个可以为 不同 长度的输入生成 固定大小 向量的模型。</code></pre><p>因此，我们需要构建一个将文本表示为单个向量的卷积模型。 文本分类的基本卷积模型如图所示。它几乎和我们之前看到的一样：唯一改变的是我们指定了使用的池化类型。<br>文本分类的基本卷积模型如图所示。 它几乎和我们之前看到的一样：唯一改变的是我们指定了使用的池化类型。 具体来说，在卷积之后，我们使用了 global-over-time pooling 。这是关键操作：它允许将文本压缩为单个向量。模型本身可以不同，但在某些时候它必须使用全局池化将输入压缩到单个向量中。<br><img src="https://blog.os-o.cn/usr/uploads/2024/02/807599233.png" alt="2024-02-01T13:22:51.png" title="2024-02-01T13:22:51.png"></p><ul><li>不同内核大小的几个卷积</li></ul><p>你可以使用多个具有不同卷积核大小的卷积操作，而不是为你的卷积选择一个固定的卷积核大小。方法很简单：将每个卷积应用于数据，在每个卷积之后添加非线性和全局池化，然后将结果拼接起来（在插图中，为简单起见省略了非线性）。这就是你获得用于分类的数据的矢量表示的方式。<br><img src="https://blog.os-o.cn/usr/uploads/2024/02/3194278863.png" alt="2024-02-01T13:23:07.png" title="2024-02-01T13:23:07.png"><br>这个想法在论文 Convolutional Neural Networks for Sentence Classification 和许多后续文章中被使用。</p><ul><li>建模：堆叠几个块 卷积+池化</li></ul><p>你可以将多个卷积+池化操作块堆叠在一起，而不是单单的一层。在堆叠几个块之后，你可以应用其他卷积，但要在最后的输出部分使用全局池化。 记住：你必须得到一个固定大小的向量 - 为此，你需要全局池化操作。<br>当你的文本很长时，这种多层卷积会很有用；例如，如果您的模型是字符级（而不是单词级）。<br><img src="https://blog.os-o.cn/usr/uploads/2024/02/4014251345.png" alt="2024-02-01T13:23:26.png" title="2024-02-01T13:23:26.png"><br>这个想法参考了论文 Character-level Convolutional Networks for Text Classification.</p><h2>5、多标签分类</h2><p>多标签 分类与我们之前讨论的 单标签 问题不同，每个输入可以有几个正确的标签。例如，一个推特可以有多个主题标签，一个用户可以有多个感兴趣的主题，等等。<br>对于多标签问题，我们需要在之前讨论的单标签pipeline中更改两件事：</p><ul><li>模型（我们如何评估类别概率）；</li><li>损失函数</li></ul><p><img src="https://blog.os-o.cn/usr/uploads/2024/02/1546158769.png" alt="2024-02-01T13:24:29.png" title="2024-02-01T13:24:29.png"><br><img src="https://blog.os-o.cn/usr/uploads/2024/02/721629773.png" alt="2024-02-01T13:24:34.png" title="2024-02-01T13:24:34.png"></p><h3>模型： Softmax → Element-wise Sigmoid</h3><p>在最后一个线性层之后，我们有对应于 $K$ 个类别的 $K$ 个值——这些值是我们必须转换为类别概率的值。<br>对于单标签问题，我们使用softmax：它将 $K$ 个值转化为概率分布，即所有概率之和为1。这意味着类共享相同的概率质量： 如果一个类的概率高，其他类可以概率不大。 （ 编者按：再次想象一下，一群小猫从同一个碗里吃东西：一只小猫吃得多总是以牺牲其他小猫吃得少为代价 ）。<br>对于多标签问题，我们将 $K$ 个值中的每一个值都转换为对应类别的概率，独立于其他类别。 具体来说，我们将 sigmoid 函数应用于每个 $K$ 值：</p><p>$$
\sigma(x)=\frac{1}{1+e^{-x}}
$$</p><p><img src="https://blog.os-o.cn/usr/uploads/2024/02/1887375092.png" alt="2024-02-01T13:25:49.png" title="2024-02-01T13:25:49.png"><br>直观地说，我们可以认为多标签分类是 $K$ 个独立的二进制分类器组合，其中，这些分类器使用相同的文本表示。</p><h3>损失函数：每个类的二进制交叉熵</h3><p>损失函数更改以启用多个标签：对于每个类，我们使用二元交叉熵损失。 见下图。<br><img src="https://blog.os-o.cn/usr/uploads/2024/02/3587379582.png" alt="2024-02-01T13:26:14.png" title="2024-02-01T13:26:14.png"></p><h2>6、实践建议</h2><h3>词嵌入：如何解决他们？</h3><p>网络的输入由词嵌入表示。 您有三种选择如何为您的模型获取这些嵌入：<br><img src="https://blog.os-o.cn/usr/uploads/2024/02/899751236.png" alt="2024-02-01T13:26:49.png" title="2024-02-01T13:26:49.png"></p><ul><li>作为模型的一部分从头开始训练，</li><li>接受预训练（Word2Vec、GloVe 等）并固定它们（将它们用作静态向量），</li><li>使用预训练嵌入进行初始化并使用网络训练它们（“微调（fine-tune）”）。</li></ul><p>这三种选择具体该怎么选择呢？实际上，我们往往通过观察模型可用的数据来考虑这些选择。<br>用于分类的训练数据是需要标签并适用于特定于任务的，但通常带有标签的数据难以得到。 因此，这个语料库可能并不庞大（至少相对而言），或不多样化，或两者兼而有之。<br>相反，词嵌入的训练数据没有标记，只需要纯文本就足够了。 因此，这些数据集可以是巨大而多样的，这有很多东西可以学习。<br><img src="https://blog.os-o.cn/usr/uploads/2024/02/3314381178.png" alt="2024-02-01T13:27:12.png" title="2024-02-01T13:27:12.png"><br>现在让我们想想模型会根据我们对Embedding的处理方式学到什么。</p><ul><li>如果嵌入是从头开始训练的，模型将只“知道”分类数据 - 这可能不足以很好地学习单词之间的关系。</li><li>但是如果我们使用预训练的嵌入，他们（以及整个模型）将知道一个巨大的语料库 - 他们会学到很多关于这个世界的知识。</li><li>为了使这些嵌入适应您的特定任务数据，您可以通过对整个网络进行训练来微调这些嵌入 - 这可以带来性能提升（虽然不是很大）。</li></ul><p><img src="https://blog.os-o.cn/usr/uploads/2024/02/1655520276.png" alt="2024-02-01T13:27:31.png" title="2024-02-01T13:27:31.png"><br>当我们使用预训练Embedding时，就是 迁移学习 的一个典型示例：通过Embedding，我们将其训练数据的知识“迁移”到我们的任务特定模型。我们将在课程的后面部分了解有关迁移学习的更多信息。</p><p>在这个充满科技魔力的时代，自然语言处理（NLP）正如一颗璀璨的明星般照亮我们的数字世界。当我们涉足NLP的浩瀚宇宙，仿佛开启了一场语言的奇幻冒险。正如亚历克斯·康普顿所言：“语言是我们思想的工具，而NLP则是赋予语言新生命的魔法。”这篇博客将引领你走进NLP前沿，发现语言与技术的交汇点，探寻其中的无尽可能。不论你是刚刚踏入NLP的大门，还是这个领域的资深专家，我的博客都将为你提供有益的信息。一起探索语言的边界，迎接未知的挑战，让我们共同在NLP的海洋中畅游！期待与你一同成长，感谢你的关注和支持。欢迎任何人前来讨论问题。</p>
]]></content:encoded>
<slash:comments>4</slash:comments>
<comments>https://www.blog.os-o.cn/index.php/archives/103/#comments</comments>
<wfw:commentRss>https://www.blog.os-o.cn/index.php/feed/author/1/</wfw:commentRss>
</item>
<item>
<title>【NLP基础知识四】文本分类</title>
<link>https://www.blog.os-o.cn/index.php/archives/96/</link>
<guid>https://www.blog.os-o.cn/index.php/archives/96/</guid>
<pubDate>Mon, 29 Jan 2024 21:24:00 +0800</pubDate>
<dc:creator>chengligen</dc:creator>
<description><![CDATA[如果你是NLP领域初学者，欢迎关注我的博客，我不仅会分享理论知识，更会通过实例和实用技巧帮助你迅速入门。我的目标是让每个初学者都能轻松理解复杂的NLP概念，并在实践中掌握这一领域的核心技能。通过...]]></description>
<content:encoded xml:lang="zh-CN"><![CDATA[
<p>如果你是NLP领域初学者，欢迎关注我的博客，我不仅会分享理论知识，更会通过实例和实用技巧帮助你迅速入门。我的目标是让每个初学者都能轻松理解复杂的NLP概念，并在实践中掌握这一领域的核心技能。<br>通过我的博客，你将了解到：</p><ul><li>NLP的基础概念，为你打下坚实的学科基础。</li><li>实际项目中的应用案例，让你更好地理解NLP技术在现实生活中的应用。</li><li>学习和成长的资源，助你在NLP领域迅速提升自己。</li></ul><p>不论你是刚刚踏入NLP的大门，还是这个领域的资深专家，我的博客都将为你提供有益的信息。一起探索语言的边界，迎接未知的挑战，让我们共同在NLP的海洋中畅游！期待与你一同成长，感谢你的关注和支持。欢迎任何人前来讨论问题。</p><h1>一、文本分类</h1><p>文本分类是一个非常流行的任务。我们每天都会遇到它的应用，即邮件代理中的文本分类器：它对邮件进行分类并过滤垃圾邮件。其他常见应用还包括：文档分类、审查分类等。<br><img src="https://blog.os-o.cn/usr/uploads/2024/01/881024090.png" alt="2024-01-29T11:48:21.png" title="2024-01-29T11:48:21.png"><br><img src="https://blog.os-o.cn/usr/uploads/2024/01/1858714665.png" alt="2024-01-29T11:48:26.png" title="2024-01-29T11:48:26.png"><br><img src="https://blog.os-o.cn/usr/uploads/2024/01/543700346.png" alt="2024-01-29T11:48:30.png" title="2024-01-29T11:48:30.png"><br><img src="https://blog.os-o.cn/usr/uploads/2024/01/2036644068.png" alt="2024-01-29T11:48:35.png" title="2024-01-29T11:48:35.png"><br>文本分类器常常不是作为一个单独的任务使用，而是作为更大的工作流管道的一部分。例如，一个语音助手对你的话语进行分类，以了解你想要什么（例如，设置闹钟、订购出租车或只是聊天），并根据分类器的决定将你的信息传递给不同的模型。另一个例子是网络搜索引擎：它可以使用分类器来识别查询语言，预测你的查询类型（例如，信息性、导航性、交易性），了解你除了要看文件之外，是否还要看图片或视频，等等。<br>由于大多数分类数据集都假定只有一个正确类别，所以，下面这章中我们重点介绍这种分类，即单标签分类。我们会在另一个章节中详细阐述 (多标签分类).</p><h2>1、分类数据集</h2><p>文本分类的数据集在大小（包括数据集大小和例子大小）、分类内容和标签数量方面都有很大不同。请看下面的统计数据。<br><img src="https://blog.os-o.cn/usr/uploads/2024/01/2421340258.png" alt="2024-01-29T11:53:32.png" title="2024-01-29T11:53:32.png"><br><a href="https://course.fast.ai/datasets#nlp">一些数据集可以从这里下载。</a><br>最流行的数据集是情感分类数据集。它们涉及对电影、地方或餐馆以及产品的评论。也有用于问题类型分类和主题分类的数据集。</p><h4>常用数据集介绍：</h4><p><strong>SST：</strong><br>SST 是一个情感分类数据集，由电影评论（来自烂番茄 html 文件）组成。 数据集由句子的解析树组成，不仅是整个句子，而且更小的短语都有一个情感标签。</p><p>有五个标签：1（非常负面）、2（负面）、3（中性）、4（正面）和 5（非常正面）（标签有时候也可以表示为0-4）。 根据使用的标签不同，可以获得二分类的 SST-2 数据集（如果只考虑正面和负面）或细粒度的情绪分类 SST-5（使用所有标签）。</p><p>请注意，上面提到的数据集大小（train/dev/test 为 8.5k/2.2k/1.1k）是按照句子数量计算的。并且，使用了 215,154 个短语组成数据集中的每个句子。</p><p><strong>IMDb Review：</strong><br>IMDb 是来自 Internet 电影数据库的非正式电影评论的大型数据集。 该数据集中每部电影有不超过 30 条评论。 该数据集包含偶数个正面和负面评论，因此随机猜测产生 50% 的准确率。 评论高度两极分化：它们只有负面（最高分 4 分，满分 10）或正面（最低分 7 分，满分 10）。</p><p><strong>Yelp Review：</strong><br>Yelp 评论数据集来自 2015 年的 Yelp 数据集挑战赛 。根据标签的数量， Yelp Full（包含所有的 5 个标签）或 Yelp Polarity （仅含有正面和负面类别）。 Full在每个标签中有 130,000 个训练样本和 10,000 个测试样本， Polarity在每个类中有 280,000 个训练样本和 19,000 个测试样本。</p><p><strong>Amazon Review：</strong><br>亚马逊评论数据集由来自亚马逊的评论组成，其中包括产品和用户信息、评级和纯文本评论。 数据集来自 斯坦福网络分析项目 (SNAP)。 根据标签的数量，您可以获得 Amazon Full （包含所有的 5 个标签）或 Amazon Polarity（仅有正类和负类）。 Full在每个标签中有 600,000 个训练样本和 130,000 个测试样本， Polarity 在每个类中有 1800,000 个训练样本和 200,000 个测试样本。 使用的字段是评论标题和评论内容。</p><p><strong>TREC：</strong><br>TREC 是一个用于对自由事实问题进行分类的数据集。 它定义了一个两层分类法，表示 TREC 任务中典型答案的自然语义分类。 层次结构包含 6 个粗分类 (ABBREVIATION, ENTITY, DESCRIPTION, HUMAN, LOCATION and NUMERIC VALUE) 和 50 个细分类。</p><p><strong>Yahoo! Answers：</strong><br>数据集来自 Yahoo! Answers Comprehensive questions and Answers version 1.0 数据集 。 其中包含 10 个最大的主要类别： "Society & Culture", "Science & Mathematics", "Health, "Education & Reference", "Computers & Internet", "Sports", "Business & Finance", "Entertainment & Music", "Family & Relationships", "Politics & Government". 每个类包含 140,000 个训练样本和 5,000 个测试样本。数据由问题标题和内容以及最佳答案组成。</p><p><strong>AG's News：</strong><br>AG 的语料库是从 网络上的新闻文章 中获得的。 从这些文章中可以发现，只有 AG 的语料库仅包含 4 个最大类的标题和描述字段。</p><p><strong>Sogou News：</strong><br>搜狗新闻语料库是 SogouCA和SogouCS新闻语料库 由组合得到的。 该数据集由标记为 5 个类别的新闻文章（标题和内容字段）组成： “sports”, “finance”, “entertainment”, “automobile” and “technology”.</p><p>原始数据集是中文的，但您可以生成拼音 - 中文的拼音罗马字。 您可以使用 pypinyin 包结合 jieba 中文分词系统来完成（这是 介绍数据集的论文 所做的，这也是我在示例中向您展示的内容）。 然后可以将英语模型应用于此数据集而无需更改。</p><p><strong>DBPedia：</strong><br>DBpedia 是一个众包社区，旨在从维基百科中提取结构化信息。 DBpedia 本体分类数据集是通过从 DBpedia 2014中挑选 14 个不重叠的类构建的。每一个类中包含 40,000 个随机选择的训练样本和 5,000 个测试样本。 因此，训练数据集的总大小为 560,000，测试数据集大小为 70,000。</p><h2>2、通用视角</h2><p>这里我们提供了一个关于分类的通用视角，并介绍了相关符号的形式化定义。该通用视角同时适用于经典方法和基于神经网络方法。<br>我们假设有一个带真实标签的文档集合。分类器的输入是带有token序列$(x_1, \dots, x_n)$的文档 $x=(x_1, \dots, x_n)$ ，输出是标签 $y\in 1\dots k$。 通常情况下，分类器估计的是类别的概率分布，我们希望正确类别的概率是最高的。</p><h3>获得特征表示及分类</h3><p>文本分类拥有以下结构：<br><img src="https://blog.os-o.cn/usr/uploads/2024/01/622078777.png" alt="2024-01-29T12:04:30.png" title="2024-01-29T12:04:30.png"></p><ul><li>特征提取器</li></ul><p>特征提取器可以手动设计 (如经典方法)或通过”学习”获得(如神经网络).</p><ul><li>分类器</li></ul><p>一个分类器必须在给定文本的特征表示时分配类别概率。最常见的方法是使用 逻辑回归，但其他变体也是可能的（例如，朴素贝叶斯分类器或 SVM）。<br>在本文中，我们将主要研究建立文本特征表示的不同方法，并使用这种表示来获得类别概率。</p><h3>生成模型与判别模型</h3><p><img src="https://blog.os-o.cn/usr/uploads/2024/01/1180616643.png" alt="2024-01-29T12:05:14.png" title="2024-01-29T12:05:14.png"><br>一个分类模型可以是生成式的，也可以是判别式的。</p><ul><li>生成模型</li></ul><p>生成模型学习数据的联合概率分布$p(x, y) = p(x|y)\cdot p(y)$。为了对输入 $x$ 进行预测，这些模型选择一个具有最高联合概率的类别：</p><p>$$
y=\arg\max_k p(x|y=k)\cdot p(y=k)
$$</p><ul><li>判别模型</li></ul><p>判别模型只关注条件概率$p(y|x)$，即，只需学习类别之间的边界。为了对输入$x$进行预测，这些模型选择一个具有最高条件概率的类别：</p><p>$$
y = \arg \max\limits_{k}p(y=k|x)
$$</p><p>本文中，这两类模型都会出现。</p><h2>3、文本分类经典方法</h2><p>本章我们讨论经典的文本分类方法。它们在神经网络开始流行之前就已经被开发出来了，对于小型数据集来说，其性能仍可与基于神经网络的模型相媲美。</p><p>编者按: 在课程的后面，我们将学习迁移学习，可以使神经网络方法即使对于非常小的数据集也有更好的表现。但让我们一步一步来：目前，经典方法是你的模型的一个很好的基线。</p><h3>朴素贝叶斯分类器</h3><p>下面给出了朴素贝叶斯方法的总体想法：我们用贝叶斯规则重写条件类概率$P(y=k|x)$，得到$P(x|y=k)\cdot P(y=k)$。<br><img src="https://blog.os-o.cn/usr/uploads/2024/01/3928957023.png" alt="2024-01-29T12:08:26.png" title="2024-01-29T12:08:26.png"><br>这是一个生成模型!<br><img src="https://blog.os-o.cn/usr/uploads/2024/01/144090410.png" alt="2024-01-29T12:08:43.png" title="2024-01-29T12:08:43.png"><br>朴素贝叶斯是一个生成模型：它对数据的联合概率进行建模。</p><p>同时，要注意术语：</p><ul><li>先验概率 $P(y=k)$：看数据前的类概率（即在知道x之前）；</li><li>后验概率 $P(y=k|x)$：看完数据后的类别概率（即知道具体的$x$后）；</li><li>联合概率 $P(x, y)$: ：数据的联合概率（即例子$x$和标签$y$）；</li><li>最大后验（MAP）估计：我们选择具有最高后验概率的类别。</li></ul><h4>如何定义 $P(x|y=k)$ 和 $P(y=k)$？</h4><p>$P(y=k)$：标签计数<br>$P(y=k)$ 很容易得到：我们只需计算具有标签$k$的文档的比例（即最大似然估计，MLE）。也就是说，</p><p>$$
P(y=k)=\frac{N(y=k)}{\sum\limits_{i}N(y=i)},
$$</p><p>其中，$N(y=k)$ 是标签为 $k$ 的例子（文档）的数量。<br>$P(x|y=k)$：先使用 "朴素" 假设，然而计数<br>这里，我们假设文档 $X$ 被表示为一组特征，例如，它的一组词 $(x_1, \dots, x_n)$：</p><p>$$
P(x| y=k)=P(x_1, \dots, x_n|y=k).
$$</p><p>朴素贝叶斯假设为</p><ul><li>词袋假设：词的顺序并不重要，</li><li>条件独立性假设：特征（词）在不同类别间是独立的。</li></ul><p>直观地说，我们假设每个词出现在类别为k的文件中的概率不取决于上下文（既不取决于词序，也不取决于其他词）。例如，我们可以说， awesome, brilliant, great更有可能出现在有积极情绪的文件中，而 awful, boring, bad 更有可能出现在消极文件中，但我们并不知道这些（或其他）词之间如何相互影响。<br>通过这些“朴素” 假设，我们可以得到：</p><p>$$
P(x| y=k)=P(x_1, \dots, x_n|y=k)=\prod\limits_{t=1}^nP(x_t|y=k).
$$</p><p>概率 $P(x_i|y=k)$ 被估计为单词 $x_i$ 出现在第 $k$ 类的文档中的次数占所有token出现在k类文档次数比例，即：</p><p>$$
P(x_i|y=k)=\frac{N(x_i, y=k)}{\sum\limits_{t=1}^{|V|}N(x_t, y=k)},
$$</p><p>其中， $N(x_i, y=k)$ 是token $x_i$ 出现在类别为 $k$ 的文件中的次数， $V$ 是词汇表（更一般地说，所有可能特征组成的集合）。</p><h4>如果 $N(x_i, y=k)=0$ 怎么办？需要避免这种情况!</h4><p>如果 $N(x_i, y=k)=0$，即在训练中我们没有在类别 $k$ 的文档中看到token $x_i$ 怎么办？这将使整个文档的概率归零，而这不是我们想要的！例如，如果我们在训练中没有看到一些罕见的词（例如，pterodactyl 或 abracadabra），这并不意味着一个文件永远不可能包含这些词。<br><img src="https://blog.os-o.cn/usr/uploads/2024/01/1805300201.png" alt="2024-01-29T12:15:51.png" title="2024-01-29T12:15:51.png"><br>为避免这种情况，我们使用了一个小trick：我们在所有单词的计数中添加一个小的 $\delta$：</p><p>$$
P(x_i|y=k)=\frac{\color{red}{\delta} +\color{black} N(x_i, y=k)
                            }{\sum\limits_{t=1}^{|V|}(\color{red}{\delta} +\color{black}N(x_t, y=k))} =
                            \frac{\color{red}{\delta} +\color{black} N(x_i, y=k)
                            }{\color{red}{\delta\cdot |V|}\color{black} + \sum\limits_{t=1}^{|V|}\color{black}N(x_t,
                            y=k)}
                            ,
$$</p><p>其中 $\delta$ 可以使用交叉验证来选择。<br>注意：这是拉普拉斯平滑（如果$\delta=1$，又称 Add-1平滑 ）。 我们将在下一讲关于语言建模的文章中了解更多关于平滑的知识。</p><h4>预测</h4><p>正如我们已经提到的，朴素贝叶斯（以及更广泛的生成模型）根据数据和类别的联合概率进行预测：</p><p>$$
y^{\ast} = \arg \max\limits_{k}P(x, y=k) = \arg \max\limits_{k} P(y=k)\cdot
                            P(x|y=k).
$$</p><p>直观地说，朴素贝叶斯期望一些词能作为类别标志。例如，在情感分类中， awesome, brilliant, great 等token将具有更高的概率，因为它们属于positive类别而不是negative类别。同样地， awful, boring, bad 这些token在给定negative类别的情况下会有更高的概率，而不是positive类别。<br><img src="https://blog.os-o.cn/usr/uploads/2024/01/3554607583.png" alt="2024-01-29T12:17:15.png" title="2024-01-29T12:17:15.png"></p><h4>关于朴素贝叶斯的进一步说明</h4><h5>实践说明：用对数概率之和代替概率之积</h5><p>朴素贝叶斯用于分类的主要表达式是概率的乘积：</p><p>$$
P(x, y=k)=P(y=k)\cdot P(x_1, \dots, x_n|y)=P(y=k)\cdot
                            \prod\limits_{t=1}^nP(x_t|y=k).
$$</p><p>许多概率的乘积在数值上可能非常不稳定（乘积导致溢出或浮点损失）。因此，通常我们考虑 $\log P(x, y)$ 而不是 $P(x, y)$ ：</p><p>$$
\log P(x, y=k)=\log P(y=k) + \sum\limits_{t=1}^n\log P(x_t|y=k).
$$</p><p>由于我们只关心argmax，我们可以考虑 $\log P(x, y)$ 而不是 $P(x, y)$.<br>重要！ 请注意，在实践中，我们通常会处理对数概率而不是概率。</p><h5>通用框架的视角</h5><p>还记得我们关于分类任务的通用视角 吗？我们使用某种方法获得输入文本的特征表示，然后使用这种特征表示进行分类。<br><img src="https://blog.os-o.cn/usr/uploads/2024/01/920404423.png" alt="2024-01-29T12:19:04.png" title="2024-01-29T12:19:04.png"><br>在朴素贝叶斯中，我们的特征是词，而特征表示是词袋（BOW）表示（是词的one-hot表示的总和）。事实上，为了评估 $P(x, y)$ ，我们只需要计算每个token在文本中出现的次数。</p><h5>特征设计</h5><p>在标准设置中，我们使用词作为特征。然而，你也可以使用其他类型的特征。URL，用户ID，等等。</p><h3>最大熵分类器（又名逻辑回归）</h3><p>与朴素贝叶斯不同，最大熵分类器是一个判别模型，也就是说，我们对概率$P(y=k|x)$感兴趣而不是联合分布$p(x, y)$ 。 另外，我们将学习如何使用特征：这与朴素贝叶斯不同，在朴素贝叶斯中我们自己定义如何使用特征。<br>在这里，我们也必须手动定义特征，但我们有更多的发挥空间：特征不一定是分类相关的（在朴素贝叶斯中，它们必须是！）。我们可以使用BOW表示方法，或者想出一些更有趣的东西。<br>该方法中，一般分类pipeline是这样的：</p><ul><li>得到</li></ul><p>$$
\color{#7aab00}{h}\color{black}=(\color{#7aab00}{f_1}\color{black},
                                \color{#7aab00}{f_2}\color{black}, \dots,
                                \color{#7aab00}{f_n}\color{black}{)}
$$</p><ul><li>输入文本的特征表示；</li><li>取 $w^{(i)}=(w_1^{(i)}, \dots, w_n^{(i)})$ - 每个类别的特征权重的向量；</li><li>对于每一个类，对特征进行权衡，即取特征表示 $\color{#7aab00}{h}$ 与特征权重 $w^{(k)}$的点积：</li></ul><p>$$
w^{(k)}\color{#7aab00}{h}\color{black} =
                                w_1^{(k)}\cdot\color{#7aab00}{f_1}\color{black}+\dots+
                                w_n^{(k)}\cdot\color{#7aab00}{f_n}\color{black}{, \ \ \ \ \ k=1, \dots, K.}
$$</p><p>为了在上面的求和中得到一个偏差项，我们定义其中一个特征为1（例如， $\color{#7aab00}{f_0}=1$）。那么</p><p>$$
w^{(k)}\color{#7aab00}{h}\color{black} = \color{red}{w_0^{(k)}}\color{black} +
                                w_1^{(k)}\cdot\color{#7aab00}{f_1}\color{black}+\dots+
                                w_n^{(k)}\cdot\color{#7aab00}{f_{n}}\color{black}{, \ \ \ \ \ k=1, \dots, K.}
$$</p><ul><li>使用softmax得到类别概率：</li></ul><p>$$
P(class=k|\color{#7aab00}{h}\color{black})=
                                \frac{\exp(w^{(k)}\color{#7aab00}{h}\color{black})}{\sum\limits_{i=1}^K
                                \exp(w^{(i)}\color{#7aab00}{h}\color{black})}.
$$</p><p>将我们在上一步得到的 $K$ 值归一到输出类别的概率分布。<br>请看下面的插图（类别用不同颜色表示）。<br><img src="https://blog.os-o.cn/usr/uploads/2024/01/1810645823.png" alt="2024-01-29T13:16:36.png" title="2024-01-29T13:16:36.png"></p><h4>训练：最大似然估计</h4><p>给定训练实例 $x^1, \dots, x^N$ 与相应的标签 $y^i\in\{1, \dots, K\}$ ，我们选择权重 $w^{(k)}, k=1..K$ ，使训练数据的概率最大化：</p><p>$$
w^{\ast}=\arg \max\limits_{w}\sum\limits_{i=1}^N\log P(y=y^i|x^i).
$$</p><p>换言之，我们选择合适的参数使得数据更容易 出现。因此，这被称为参数的最大似然估计（MLE） 。<br>为了找到使数据对数似然可能性最大化的参数，我们使用梯度上升法：在数据的多次迭代中逐渐修改权重。在每一步，我们都要最大化模型分配给正确类别的概率。</p><h5>等价于最小化交叉熵</h5><p>请注意，最大化数据对数似然相当于最小化目标概率分布 $p^{\ast} = (0, \dots, 0, 1, 0, \dots)$ （目标标签为1，其余为0）和模型分布 $p=(p_1, \dots, p_K), p_i=p(i|x)$的交叉熵：</p><p>$$
Loss(p^{\ast}, p^{})= - p^{\ast} \log(p) = -\sum\limits_{i=1}^{K}p_i^{\ast}
                            \log(p_i).
$$</p><p>因为仅有一个 $p_i^{\ast}$ 是非零的（1代表目标标签$k$，0代表其余），我们将得到：</p><p>$$
Loss(p^{\ast}, p) = -\log(p_{k})=-\log(p(k| x)).
$$</p><p><img src="https://blog.os-o.cn/usr/uploads/2024/01/2941446932.png" alt="2024-01-29T13:19:19.png" title="2024-01-29T13:19:19.png"><br>这种等价性对于你理解非常重要：在谈论神经方法时，人们通常会说他们最小化了交叉熵损失。 不要忘记这与最大化数据对数似然是等价的。</p><h4>朴素贝叶斯与逻辑斯蒂回归</h4><p><img src="https://blog.os-o.cn/usr/uploads/2024/01/3227555483.png" alt="2024-01-29T13:20:12.png" title="2024-01-29T13:20:12.png"><br>接下来我们讨论逻辑斯蒂回归和朴素贝叶斯的优缺点。</p><ul><li>简单</li></ul><p>两种方法都很简单；朴素贝叶斯是最简单的一种。</p><ul><li>可解释性</li></ul><p>这两种方法都是可解释的：你可以查看对预测影响最大的特征（在朴素贝叶斯中，通常是单词，在逻辑斯蒂回归中 - 无论你定义什么）。</p><ul><li>训练速度</li></ul><p>朴素贝叶斯的训练速度非常快——它只需要一次遍历训练数据来统计计数。对于逻辑斯蒂回归，情况并非如此：你必须多次遍历数据，直到梯度上升收敛。</p><ul><li>独立性假设</li></ul><p>朴素贝叶斯太“朴素”了，它假设在给定类别的情况下特征（词）是条件独立的。逻辑斯蒂回归没有做出这个假设，我们可以希望它更好。</p><ul><li>文本表示：手动定义</li></ul><p>两种方法都使用手动定义的特征表示（在朴素贝叶斯中，BOW 是标准选择，但仍然可以自己选择）。虽然手动定义的特性有利于可解释性，但它们可能对性能没有那么好，你可能会错过一些对任务有用的东西。</p><h3>SVM 文本分类器</h3><p>另一种基于手动设计特征的文本分类方法是 SVM。 SVM 最基本（也是最流行的）功能是词袋 (bag-of-words) 和 ngram 袋 (bag-of-ngrams) （ngram 是 n 个词的元组）。 凭借这些简单的特征，具有线性核的 SVM 比朴素贝叶斯表现更好（例如，参见论文 使用支持向量机的问题分类)。<br><img src="https://blog.os-o.cn/usr/uploads/2024/01/228366676.png" alt="2024-01-29T13:21:13.png" title="2024-01-29T13:21:13.png"></p><h2>4、神经网络文本分类</h2><h3>今天太晚了，先到这里，Word2Vec：一种基于预测的方法，这一章篇幅有点多，明天才能产出。</h3><h3>出了链接就在这里：</h3><p>在这个充满科技魔力的时代，自然语言处理（NLP）正如一颗璀璨的明星般照亮我们的数字世界。当我们涉足NLP的浩瀚宇宙，仿佛开启了一场语言的奇幻冒险。正如亚历克斯·康普顿所言：“语言是我们思想的工具，而NLP则是赋予语言新生命的魔法。”这篇博客将引领你走进NLP前沿，发现语言与技术的交汇点，探寻其中的无尽可能。不论你是刚刚踏入NLP的大门，还是这个领域的资深专家，我的博客都将为你提供有益的信息。一起探索语言的边界，迎接未知的挑战，让我们共同在NLP的海洋中畅游！期待与你一同成长，感谢你的关注和支持。欢迎任何人前来讨论问题。</p>
]]></content:encoded>
<slash:comments>5</slash:comments>
<comments>https://www.blog.os-o.cn/index.php/archives/96/#comments</comments>
<wfw:commentRss>https://www.blog.os-o.cn/index.php/feed/author/1/</wfw:commentRss>
</item>
<item>
<title>【NLP基础知识三】词嵌入（Word Embeddings）之“GloVe：单词表示的全局向量”</title>
<link>https://www.blog.os-o.cn/index.php/archives/78/</link>
<guid>https://www.blog.os-o.cn/index.php/archives/78/</guid>
<pubDate>Sun, 28 Jan 2024 21:31:00 +0800</pubDate>
<dc:creator>chengligen</dc:creator>
<description><![CDATA[如果你是NLP领域初学者，欢迎关注我的博客，我不仅会分享理论知识，更会通过实例和实用技巧帮助你迅速入门。我的目标是让每个初学者都能轻松理解复杂的NLP概念，并在实践中掌握这一领域的核心技能。通过...]]></description>
<content:encoded xml:lang="zh-CN"><![CDATA[
<p>如果你是NLP领域初学者，欢迎关注我的博客，我不仅会分享理论知识，更会通过实例和实用技巧帮助你迅速入门。我的目标是让每个初学者都能轻松理解复杂的NLP概念，并在实践中掌握这一领域的核心技能。<br>通过我的博客，你将了解到：</p><ul><li>NLP的基础概念，为你打下坚实的学科基础。</li><li>实际项目中的应用案例，让你更好地理解NLP技术在现实生活中的应用。</li><li>学习和成长的资源，助你在NLP领域迅速提升自己。</li></ul><p>不论你是刚刚踏入NLP的大门，还是这个领域的资深专家，我的博客都将为你提供有益的信息。一起探索语言的边界，迎接未知的挑战，让我们共同在NLP的海洋中畅游！期待与你一同成长，感谢你的关注和支持。欢迎任何人前来讨论问题。</p><p><a href="https://blog.os-o.cn/index.php/archives/42/">【NLP 基础知识一】词嵌入（Word Embeddings）</a><br><a href="https://blog.os-o.cn/index.php/archives/51/">【NLP 基础知识二】词嵌入（Word Embeddings）之“Word2Vec：一种基于预测的方法”</a><br><a href="https://blog.os-o.cn/index.php/archives/78/">【NLP基础知识三】词嵌入（Word Embeddings）之“GloVe：单词表示的全局向量”</a></p><h2>5、GloVe：单词表示的全局向量</h2><p><img src="https://blog.os-o.cn/usr/uploads/2024/01/578769365.png" alt="2024-01-28T11:43:50.png" title="2024-01-28T11:43:50.png"><br>GloVe 模型是基于计数的方法和基于预测的方法（例如 Word2Vec）的组合。模型名称 GloVe 代表 Global Vectors，也体现了它的核心思想：利用语料库中的全局信息来学习词向量。<br>正如之前所见，最简单的基于计数的方法使用共现计数来衡量单词 w 和上下文 c 之间的关联：N(w, c)。类似地，GloVe 也使用这种计数来构建损失函数：<br><img src="https://blog.os-o.cn/usr/uploads/2024/01/840209397.png" alt="2024-01-28T11:44:14.png" title="2024-01-28T11:44:14.png"><br>与 Word2Vec 类似，Glove 也有中心词向量和上下文词向量的区分，这二者构成了Glove的参数。 此外，该方法对每个词向量都引入了一个标量偏置项 (即上图的 b)。<br>有趣的地方在于，GloVe 控制了少见词和频繁词的影响：对每对 (w, c) 而言，损失将以如下方式加权：</p><ul><li>对于罕见的 (w, c)，损失会受惩罚，</li><li>对于频繁出现的 (w, c)，损失也不会被过度加权。</li></ul><h2>6、词嵌入的评价</h2><p>我们如何评价一种获取词嵌入的方法要比另一种更好呢？目前学术界有两种评估手段（不仅适用于词嵌入）：内在评价和外在评价。<br>编者按：下文中我们使用词嵌入 (Word Embeddings) 来指代词向量 (Word Vectors)，但实际上它们是一个东西。</p><h3>内在评价：基于内在属性</h3><p><img src="https://blog.os-o.cn/usr/uploads/2024/01/2951854428.png" alt="2024-01-28T13:22:38.png" title="2024-01-28T13:22:38.png"><br>这种类型的评价着眼于词嵌入的内在属性，即它们捕捉单词“含义”的程度。在分析与解释部分，我们将详细讨论如何通过词相似性和词类比任务上评价词嵌入。</p><h3>外部评价：基于下游任务</h3><p><img src="https://blog.os-o.cn/usr/uploads/2024/01/528894139.png" alt="2024-01-28T13:23:04.png" title="2024-01-28T13:23:04.png"><br>这种类型的评价会告诉读者哪些嵌入更适合读者真正关心的任务（例如，文本分类、指代消解等）。<br>在这种情况下，你必须为下游任务制定一个可以搭配不同词嵌入方法的模型。然后，通过模型的性能来确定哪些词嵌入方法更好。</p><h3>如何选择</h3><p><img src="https://blog.os-o.cn/usr/uploads/2024/01/1857285160.png" alt="2024-01-28T13:23:22.png" title="2024-01-28T13:23:22.png"><br>你必须要接受的是，没有完美的词嵌入，也没有适用于所有情况的词嵌入：它总是取决于很多事情。<br>关于评价，您通常更关心下游任务本身的性能。 因此，读者可能会对外部评价更感兴趣。 然而，下游任务上的模型通常需要大量的时间和资源来训练，尤其是当你需要训练多个的时候，训练成本可能过于昂贵。<br>至于选择哪个词嵌入，最后还是要取决于读者自己 :)</p><h2>分析与解释</h2><p>编者按：对于词嵌入，本节的大部分内容通常被认为是内在评价的手段之一。 但是，由于理解模型学到的内容（不是指特定任务上的性能）是人们通常为分析所做的事情，因此有了本分析部分。</p><h3>漫步太空……语义空间！</h3><p>语义空间旨在创建捕捉单词含义的自然语言表征。词嵌入构成了语义空间，所以我们一般会将多维空间中的一组词向量称作“语义空间”。<br>下面显示了在 twitter 数据（取自 gensim）上训练的 GloVe 向量形成的语义空间。 使用 t-SNE 将向量投影到二维空间，下图中展示了前3000个最常用的单词。<br><img src="https://blog.os-o.cn/usr/uploads/2024/01/4080913010.png" alt="2024-01-28T13:26:09.png" title="2024-01-28T13:26:09.png"></p><h3>寻找最近的邻居（最近邻）</h3><p><img src="https://blog.os-o.cn/usr/uploads/2024/01/3045251532.png" alt="2024-01-28T13:26:30.png" title="2024-01-28T13:26:30.png"><br>在浏览语义空间的过程中，可能读者会注意到某些点与它的邻居通常密切相关。有时，即使是罕见词也有类似性质。请看右图中的例子：语义空间中 leptodactylidae (细趾蟾科) 或 litoria (雨滨蛙属) 这两个罕见词与 frogs (青蛙) 的距离很近。</p><h4>词相似度的测试基准</h4><p><img src="https://blog.os-o.cn/usr/uploads/2024/01/2993517215.png" alt="2024-01-28T13:26:49.png" title="2024-01-28T13:26:49.png"><br>通过余弦相似度或欧几里得距离“查看”最近的邻居是评价模型所学习到的词嵌入质量的常用方法之一。学术界中已经有一些单词相似度的测试基准（即测试集），它们由人类判断有相似性的词对组成。词嵌入的质量可以用这些词对的相似度分数（分别来自模型和人类）之间的相关性所衡量。</p><h4>线性结构</h4><p>尽管词语相似度的结果令人鼓舞，但它们并不令人惊讶：因为词嵌入是专门训练来反映单词相似度的。真正令人惊讶的是，词之间的许多语义和句法关系在词向量空间中（几乎）是线性的。<br><img src="https://blog.os-o.cn/usr/uploads/2024/01/3226563535.png" alt="2024-01-28T13:28:04.png" title="2024-01-28T13:28:04.png"><br>例如，king (国王) 和 queen (王后) 之间的差距与 man (男人) 和 woman (女人) 之间的差距是类似的。 再例如，queens 是queen 的复数，正如同 kings 是 king 的复数。 man - woman $\approx$ king - queen 的例子可能是最受欢迎的例子，但其实也有许多其他有趣的例子。<br>下面是 country-capital (国家-首都) 关系和一些句法关系的例子。<br><img src="https://blog.os-o.cn/usr/uploads/2024/01/159495480.png" alt="2024-01-28T13:28:42.png" title="2024-01-28T13:28:42.png"></p><h4>词类比的测试基准</h4><p>上面所介绍的这些近似线性的关系启发了一种新型的评估方式：词类比评估 (Word Analogy Evaluation)。<br><img src="https://blog.os-o.cn/usr/uploads/2024/01/2927550533.png" alt="2024-01-28T13:29:01.png" title="2024-01-28T13:29:01.png"><br><img src="https://blog.os-o.cn/usr/uploads/2024/01/3464665304.png" alt="2024-01-28T13:29:06.png" title="2024-01-28T13:29:06.png"><br>给定相同关系的两个词对，例如 (man, woman) 和 (king, queen)，词类比任务是检查模型是否可以根据任三个词来找到目标词。例如，我们需要检查与 king - man + woman 最接近的向量是否对应于单词 queen。<br>学术界已经有一些类比基准，其中包括 (MSR + Google analogy 测试集) 和 BATS (更大规模的类比测试集)。</p><h3>语言之间的相似性</h3><p>我们可以看到单词之间的一些关系在语义空间中（几乎）都是线性的。那么，语言之间呢？事实证明，语言之间的语义空间也是（有点）线性的，这意味着你可以将一个语言的语义空间线性映射到另一个语言的语义空间，从而两种语言中语义相同的词在联合语义空间中可以匹配上。<br><img src="https://blog.os-o.cn/usr/uploads/2024/01/1249277304.png" alt="2024-01-28T13:29:29.png" title="2024-01-28T13:29:29.png"><br>在Word2Vec提出之后不久，上图形象地解释了 Tomas Mikolov 等人提出的方法。举个例子，我们得到一组词对及其向量表示 $\{\color{#88a635}{x_i}\color{black}, \color{#547dbf}{z_i}\color{black} \}_{i=1}^n$, 其中 $\color{#88a635}{x_i}$ 和 $\color{#547dbf}{z_i}$ 分别是源语言中第 i 个单词及其在目标语言中对应单词的向量。 优化目标是找到一个变换矩阵 W，使得 $W\color{#547dbf}{z_i}$ 近似于 $\color{#88a635}{x_i}$：即从源语言空间匹配目标语言空间。 $W$ 可以通过梯度下降法来学习，最小化以下目标：</p><p>$$
W = \arg \min\limits_{W}\sum\limits_{i=1}^n\parallel W\color{#547dbf}{z_i}\color{black} -
                            \color{#88a635}{x_i}\color{black}\parallel^2
$$</p><p>在原始论文中，用来学习的词汇由源语言中5000个最常用的单词及其对应翻译单词组成，其余的词对都是通过学习得到。</p><p>在这个充满科技魔力的时代，自然语言处理（NLP）正如一颗璀璨的明星般照亮我们的数字世界。当我们涉足NLP的浩瀚宇宙，仿佛开启了一场语言的奇幻冒险。正如亚历克斯·康普顿所言：“语言是我们思想的工具，而NLP则是赋予语言新生命的魔法。”这篇博客将引领你走进NLP前沿，发现语言与技术的交汇点，探寻其中的无尽可能。不论你是刚刚踏入NLP的大门，还是这个领域的资深专家，我的博客都将为你提供有益的信息。一起探索语言的边界，迎接未知的挑战，让我们共同在NLP的海洋中畅游！期待与你一同成长，感谢你的关注和支持。欢迎任何人前来讨论问题。</p>
]]></content:encoded>
<slash:comments>9</slash:comments>
<comments>https://www.blog.os-o.cn/index.php/archives/78/#comments</comments>
<wfw:commentRss>https://www.blog.os-o.cn/index.php/feed/author/1/</wfw:commentRss>
</item>
<item>
<title>【NLP基础知识二】词嵌入（Word Embeddings）之“Word2Vec：一种基于预测的方法”</title>
<link>https://www.blog.os-o.cn/index.php/archives/51/</link>
<guid>https://www.blog.os-o.cn/index.php/archives/51/</guid>
<pubDate>Sun, 28 Jan 2024 00:11:00 +0800</pubDate>
<dc:creator>chengligen</dc:creator>
<description><![CDATA[如果你是NLP领域初学者，欢迎关注我的博客，我不仅会分享理论知识，更会通过实例和实用技巧帮助你迅速入门。我的目标是让每个初学者都能轻松理解复杂的NLP概念，并在实践中掌握这一领域的核心技能。通过...]]></description>
<content:encoded xml:lang="zh-CN"><![CDATA[
<p>如果你是NLP领域初学者，欢迎关注我的博客，我不仅会分享理论知识，更会通过实例和实用技巧帮助你迅速入门。我的目标是让每个初学者都能轻松理解复杂的NLP概念，并在实践中掌握这一领域的核心技能。<br>通过我的博客，你将了解到：</p><ul><li>NLP的基础概念，为你打下坚实的学科基础。</li><li>实际项目中的应用案例，让你更好地理解NLP技术在现实生活中的应用。</li><li>学习和成长的资源，助你在NLP领域迅速提升自己。</li></ul><p>不论你是刚刚踏入NLP的大门，还是这个领域的资深专家，我的博客都将为你提供有益的信息。一起探索语言的边界，迎接未知的挑战，让我们共同在NLP的海洋中畅游！期待与你一同成长，感谢你的关注和支持。欢迎任何人前来讨论问题。</p><ul><li><a href="https://blog.os-o.cn/index.php/archives/42/">【NLP基础知识一】词嵌入（Word Embeddings）</a></li><li><a href="https://blog.os-o.cn/index.php/archives/51/">【NLP基础知识二】词嵌入（Word Embeddings）之“Word2Vec：一种基于预测的方法”</a></li></ul><p><a href="https://blog.os-o.cn/index.php/archives/78/">【NLP基础知识三】词嵌入（Word Embeddings）之“GloVe：单词表示的全局向量”</a></p><h2>4、Word2Vec：一种基于预测的方法</h2><h3>核心思想</h3><p>让我们再次回顾一下核心思想：</p><pre><code>核心思想：我们需要将有关上下文的信息引入词向量中。</code></pre><p>虽然基于计数的方法直观地实现了这个想法，但 Word2Vec 以不同的方式实现它：</p><pre><code>方法：通过预测上下文来学习好的词向量。
Learn word vectors by teaching them to predict contexts.</code></pre><p>Word2Vec 是一个参数是词向量的模型。这些参数针对某个目标进行迭代优化。而该优化目标迫使词向量“知道”一个词可能出现的上下文：训练向量来预测相应词可能的上下文。正如在分布假设中所说的，如果向量“知道”了上下文，它们就能“知道”单词的含义。<br><img src="https://blog.os-o.cn/usr/uploads/2024/01/820990731.png" alt="2024-01-27T16:04:30.png" title="2024-01-27T16:04:30.png"><br>Word2Vec 是一种迭代方法。其核心思想如下：</p><ul><li>首先，先找一个巨大的文本语料库；</li><li>接着，使用滑动窗口浏览文本，每次移动一个单词。在每一步时，都会有一个中心词(Central Word)和上下文词（Context Words, 即同一窗口中的其他词）；</li><li>然后，计算上下文词在以此中心词作为条件下出现的概率；</li><li>最后，优化中心词向量以增加上述概率。</li></ul><p><img src="https://blog.os-o.cn/usr/uploads/2024/01/3359855529.png" alt="2024-01-27T16:05:55.png" title="2024-01-27T16:05:55.png"><br><img src="https://blog.os-o.cn/usr/uploads/2024/01/614947683.png" alt="2024-01-27T16:06:09.png" title="2024-01-27T16:06:09.png"><br><img src="https://blog.os-o.cn/usr/uploads/2024/01/3918444605.png" alt="2024-01-27T16:06:20.png" title="2024-01-27T16:06:20.png"><br><img src="https://blog.os-o.cn/usr/uploads/2024/01/2563624658.png" alt="2024-01-27T16:06:27.png" title="2024-01-27T16:06:27.png"><br><img src="https://blog.os-o.cn/usr/uploads/2024/01/610106171.png" alt="2024-01-27T16:06:36.png" title="2024-01-27T16:06:36.png"><br><img src="https://blog.os-o.cn/usr/uploads/2024/01/232056628.png" alt="2024-01-27T16:06:45.png" title="2024-01-27T16:06:45.png"></p><h3>目标函数：负对数似然</h3><p>对于文本语料库中的每个位置$t =1, \dots, T$，Word2Vec 在给定中心词的 m 大小窗口内预测上下文词$w_t$：</p><p>$$
\color{#88bd33}{\mbox{Likelihood}} \color{black}= L(\theta)=\prod\limits_{t=1}^T\prod\limits_{-m\le j \le m, j\neq0}P(\color{#888}{w_{t+j}}|\color{#88bd33}{w_t}\color{black}, \theta), 
$$</p><p>$θ$ 是需要被优化的参数（即词向量）。 目标函数（又名损失函数或成本函数） $Jθ$ 则是平均负对数似然：<br><img src="https://blog.os-o.cn/usr/uploads/2024/01/25553556.png" alt="2024-01-28T05:05:38.png" title="2024-01-28T05:05:38.png"><br>请注意损失与上面核心思想的吻合程度：使用滑动窗口浏览文本并计算概率。 现在让我们来看看该如何计算这些概率。</p><h4>如何计算 $P(\color{#888}{w_{t+j}}\color{black}|\color{#88bd33}{w_t}\color{black},\theta)?$</h4><p><img src="https://blog.os-o.cn/usr/uploads/2024/01/1391885491.png" alt="2024-01-28T05:08:57.png" title="2024-01-28T05:08:57.png"><br>对于每个单词 $w$ 我们有两个向量：</p><ul><li>当它是一个中心词时，用$v_w$ 表示该词</li><li>当它是一个上下文词时，用$u_w$ 表示该词</li></ul><p>（在训练结束后，我们通常会丢弃上下文词向量，仅使用中心词向量作为一个词的向量表征。）<br>于是，对于中心词 $c$ 和 上下文词 $o$，，上下文词在该中心词的窗口中出现的概率是：<br><img src="https://blog.os-o.cn/usr/uploads/2024/01/2896078457.png" alt="2024-01-28T05:10:51.png" title="2024-01-28T05:10:51.png"><br>注意：上式实际上就是softmax函数！</p><p>$$
softmax(x_i)=\frac{\exp(x_i)}{\sum\limits_{j=i}^n\exp(x_j)}.
$$</p><p>softmax 会映射任意值 $x_i$ 到概率分布 $p_i$:</p><ul><li>"max" 因为最大的 $x_i$ 将有最大的概率 $p_i$;</li><li>"soft" 因为所有概率都不为零。</li></ul><p>您将在自然语言（以及一般的深度学习）课程中大量地使用到此函数。<br><img src="https://blog.os-o.cn/usr/uploads/2024/01/3040269579.png" alt="2024-01-28T05:18:03.png" title="2024-01-28T05:18:03.png"><br><img src="https://blog.os-o.cn/usr/uploads/2024/01/2171324255.png" alt="2024-01-28T05:18:14.png" title="2024-01-28T05:18:14.png"><br><img src="https://blog.os-o.cn/usr/uploads/2024/01/3361606317.png" alt="2024-01-28T05:18:23.png" title="2024-01-28T05:18:23.png"><br><img src="https://blog.os-o.cn/usr/uploads/2024/01/201314379.png" alt="2024-01-28T05:18:32.png" title="2024-01-28T05:18:32.png"><br><img src="https://blog.os-o.cn/usr/uploads/2024/01/4226149681.png" alt="2024-01-28T05:18:41.png" title="2024-01-28T05:18:41.png"><br><img src="https://blog.os-o.cn/usr/uploads/2024/01/2305880578.png" alt="2024-01-28T05:18:49.png" title="2024-01-28T05:18:49.png"></p><h3>如何训练：梯度下降, 一次一词</h3><p>让我们回想一下，对于词汇表中的任一单词，Word2Vec 模型的参数 $\theta$ 都有两个向量对应，分别是向量 $\color{#88bd33}{v_w}$ 和 $\color{#888}{u_w}$。 这些向量可以通过梯度下降优化训练目标来学习（需要指定学习率 $\alpha$)：</p><p>$$
\theta^{new} = \theta^{old} - \alpha \nabla_{\theta} J(\theta).
$$</p><h4>一次一词</h4><p>每一次模型优化时，我们都会更新一次模型参数，而每次更新都只针对一个中心词和它的一个上下文词。回顾一下损失函数：</p><p>$$
\color{#88bd33}{\mbox{Loss}}\color{black} =J(\theta)= -\frac{1}{T}\log L(\theta)=-\frac{1}{T}\sum\limits_{t=1}^T\sum\limits_{-m\le j \le m, j\neq 0}\logP(\color{#888}{w_{t+j}}\color{black}|\color{#88bd33}{w_t}\color{black},\theta)=\frac{1}{T}\sum\limits_{t=1}^T\sum\limits_{-m\le j \le m, j\neq 0} J_{t,j}(\theta).
$$</p><p>对于中心词 $\color{#88bd33}{w_t}$, 每个上下文词损失函数中都包含项$J_{t,j}(\theta)=-\logP(\color{#888}{w_{t+j}}\color{black}|\color{#88bd33}{w_t}\color{black},\theta)$. 仔细看一下这一项，就可以知道该如何对此步骤进行更新。举个例子，假设我们有一个句子<br><img src="https://blog.os-o.cn/usr/uploads/2024/01/160180355.png" alt="2024-01-28T05:23:47.png" title="2024-01-28T05:23:47.png"><br>这个句子里明显可以看到有一个中心词 cat 和四个上下文词 cute, grey, playing 和 in。 由于一次只看一个词，我们将只选择一个上下文词，下面将以 cute 为例。 那么仅包含中心词 cat 和上下文词 cute 的损失项即可写成：</p><p>$$
J_{t,j}(\theta)= -\log
                            P(\color{#888}{cute}\color{black}|\color{#88bd33}{cat}\color{black}) =
                            -\log \frac{\exp\color{#888}{u_{cute}^T}\color{#88bd33}{v_{cat}}}{
                            \sum\limits_{w\in Voc}\exp{\color{#888}{u_w^T}\color{#88bd33}{v_{cat}} }} =
                            -\color{#888}{u_{cute}^T}\color{#88bd33}{v_{cat}}\color{black}
                            + \log \sum\limits_{w\in
                            Voc}\exp{\color{#888}{u_w^T}\color{#88bd33}{v_{cat}}}\color{black}{.}
$$</p><p>此步骤中，哪些参数会被更新呢？</p><ul><li>中心词向量中，被更新的仅有 $\color{#88bd33}{v_{cat}}$;</li><li>上下文词向量中，词汇表中所有单词的表示 $\color{#888}{u_w}$ 都会被更新。</li></ul><p>下面是此步骤推导的示意图。<br><img src="https://blog.os-o.cn/usr/uploads/2024/01/3572444879.png" alt="2024-01-28T05:25:32.png" title="2024-01-28T05:25:32.png"><br>直观地，上述公式过程会通过参数更新以最小化 $J_{t,j}(\theta)$，本次更新可以让 $\color{#88bd33}{v_{cat}}$ 和 $\color{#888}{u_{cute}}$ 变得更相似（点积意义上的），并同时降低 $\color{#88bd33}{v_{cat}}$ 和词汇表内所有其他单词 $\color{#888}{u_{w}}$的相似度。<br>这听起来可能有点奇怪：如果其他词中也包含有效的上下文词（例如，示例句中的 grey, playing 和 in），为什么我们要降低$\color{#88bd33}{v_{cat}}$和所有其它单词之间的相似性？<br>别担心：因为我们会更新每个上下文词（当然还有所有中心词），平均下来我们的词向量将学习到所有可能上下文的分布，即意味着本次对有效上下文词的降低会在其他的样例中被补偿。</p><h3>更快的训练：负采样</h3><p>在上面的示例中，对于每对中心词及其上下文词组成的训练样本，我们必须更新所有上下文词的向量。这无疑是非常低效的，因为每一步进行更新所需时间与总的词汇表大小成正比。<br>但是，为什么我们必须在每一步都考虑词汇表中所有上下文词的向量呢？假设还是在刚才的示例中，我们考虑的并不是所有上下文单词的向量，而是只有目标上下文词（cute）和几个随机选择的其他单词，就像下图：<br><img src="https://blog.os-o.cn/usr/uploads/2024/01/1063854501.png" alt="2024-01-28T05:27:34.png" title="2024-01-28T05:27:34.png"><br>和以前一样，参数的更新增加了 $\color{#88bd33}{v_{cat}}$ 和 $\color{#888}{u_{cute}}$ 两者之间的相似性。 不同的是，现在我们只降低了 $\color{#88bd33}{v_{cat}}$与K 个负样本子集单词上下文向量的相似性，而非所有单词。<br>由于我们有一个庞大的语料库，平均下来我们将更新每个向量足够的次数，并且向量仍然能够很好地学习单词之间的关系。<br>正式地，此步骤的新损失函数即：</p><p>$$
J_{t,j}(\theta)=
                            -\log\sigma(\color{#888}{u_{cute}^T}\color{#88bd33}{v_{cat}}\color{black}) -
                            \sum\limits_{w\in \{w_{i_1},\dots,
                            w_{i_K}\}}\log\sigma({-\color{#888}{u_w^T}\color{#88bd33}{v_{cat}}}\color{black}),
$$</p><p>$w_{i_1},\dots, w_{i_K}$ 是这一步的K个负样本 ，$\sigma(x)=\frac{1}{1+e^{-x}}$ 是sigmoid激活函数.<br>注意， $\sigma(-x)=\frac{1}{1+e^{x}}=\frac{1\cdot e^{-x}}{(1+e^{x})\cdot e^{-x}} =</p><pre><code>                        \frac{e^{-x}}{1+e^{-x}}= 1- \frac{1}{1+e^{x}}=1-\sigma(x)$. 于是loss函数可以被写成：</code></pre><p>$$
J_{t,j}(\theta)=
                            -\log\sigma(\color{#888}{u_{cute}^T}\color{#88bd33}{v_{cat}}\color{black}) -
                            \sum\limits_{w\in \{w_{i_1},\dots,
                            w_{i_K}\}}\log(1-\sigma({\color{#888}{u_w^T}\color{#88bd33}{v_{cat}}}\color{black})).
$$</p><h4>负样本的选择</h4><p>每个单词只有少数“真正”的上下文。因此，随机选择的词大概率是“否定的”，即不是真正的上下文。这个简单的想法不仅可以有效地训练 Word2Vec，还可以用于许多其他应用中，其中一些我们将在后面的课程中看到。<br>一般地，Word2Vec 根据词的先验分布随机抽取负样本。 假设 $U(w)$ 是单词出现的概率分布, 一般可以用单词在文本语料库中的频率近似计算。Word2Vec 修改了这个分布以更频繁地采样到频率较低的单词，最终它选择 $U^{3/4}(w)$ 进行负样本单词的采样.</p><h3>Word2Vec 变体：Skip-Gram 和 CBOW</h3><p>Word2Vec 有两种变体：Skip-Gram 和 CBOW。<br>Skip-Gram 就是上文所介绍的模型：给定中心词，预测上下文词。目前，带有负采样的 Skip-Gram 是最流行的方法。<br>CBOW（Continuous Bag-of-Words，连续词袋模型）则反其道而行之：给定上下文词，预测中心词。 一般来说 CBOW 直接使用上下文词向量的加和来预测，这个加和后的向量也被称为“词袋”。<br><img src="https://blog.os-o.cn/usr/uploads/2024/01/307271150.png" alt="2024-01-28T05:30:51.png" title="2024-01-28T05:30:51.png"></p><h3>附加笔记</h3><p>原始 Word2Vec 论文是这两篇：</p><ul><li><a href="https://arxiv.org/pdf/1301.3781.pdf">Efficient Estimation of Word Representations in Vector Space</a></li><li><a href="https://papers.nips.cc/paper/5021-distributed-representations-of-words-and-phrases-and-their-compositionality.pdf">Distributed Representations of Words and Phrases and their Compositionality</a></li></ul><p>你可以通过阅读原文以获取有关实验、实现和超参数的详细信息。本文将提供一些你需要知道的最重要的事。</p><h4>这并非一个全新的idea (英文原文)</h4><p>请注意，学习词向量（分布式表示）的想法并不新鲜。例如，有人尝试将词向量作为更大网络的一部分来学习，然后提取出模型的嵌入层作为词向量。有关前人方法的相关信息，可以通过阅读 Word2Vec 原论文摘要获取。</p><p>Word2Vec 让人出乎意料的是，它能够在庞大的数据集和大型词汇表上非常快地学习到高质量的词向量。 当然，我们将在分析与解释部分所要介绍的一些有趣的属性让 Word2Vec 闻名遐迩。</p><h4>为什么要设计两个向量？ (英文原文)</h4><p>正如上文所介绍的，在 Word2Vec 中，我们要为每个单词训练两个向量：一个是中心词向量，另一个是上下文词向量。 训练后，上下文词向量就被丢掉了。</p><p>那既然训练后只用到中心词向量，为什么训练的时候要搞两个向量呢？其实，这正是使 Word2Vec 如此简洁的重要技巧之一！回顾一下损失函数：</p><p>$$
J_{t,j}(\theta)=
                            -\color{#888}{u_{cute}^T}\color{#88bd33}{v_{cat}}\color{black} -
                            \log \sum\limits_{w\in V}\exp{\color{#888}{u_w^T}\color{#88bd33}{v_{cat}}}\color{black}{.}
$$</p><p>当每个单词使用不同的向量来分别表示其作为中心词和上下文词的表征时，损失函数的第一项和指数内的点积对于参数都是线性的（因为两项互不相关）。因此，梯度的计算将异常容易。</p><h2>关于词嵌入（Word Embeddings）的课程到此结束，如果有什么不明白或者看不懂的地方，以及文章中的错误，欢迎评论。</h2><p>在这个充满科技魔力的时代，自然语言处理（NLP）正如一颗璀璨的明星般照亮我们的数字世界。当我们涉足NLP的浩瀚宇宙，仿佛开启了一场语言的奇幻冒险。正如亚历克斯·康普顿所言：“语言是我们思想的工具，而NLP则是赋予语言新生命的魔法。”这篇博客将引领你走进NLP前沿，发现语言与技术的交汇点，探寻其中的无尽可能。不论你是刚刚踏入NLP的大门，还是这个领域的资深专家，我的博客都将为你提供有益的信息。一起探索语言的边界，迎接未知的挑战，让我们共同在NLP的海洋中畅游！期待与你一同成长，感谢你的关注和支持。欢迎任何人前来讨论问题。</p>
]]></content:encoded>
<slash:comments>6</slash:comments>
<comments>https://www.blog.os-o.cn/index.php/archives/51/#comments</comments>
<wfw:commentRss>https://www.blog.os-o.cn/index.php/feed/author/1/</wfw:commentRss>
</item>
<item>
<title>【NLP基础知识一】词嵌入（Word Embeddings）</title>
<link>https://www.blog.os-o.cn/index.php/archives/42/</link>
<guid>https://www.blog.os-o.cn/index.php/archives/42/</guid>
<pubDate>Thu, 25 Jan 2024 23:16:00 +0800</pubDate>
<dc:creator>chengligen</dc:creator>
<description><![CDATA[如果你是NLP领域初学者，欢迎关注我的博客，我不仅会分享理论知识，更会通过实例和实用技巧帮助你迅速入门。我的目标是让每个初学者都能轻松理解复杂的NLP概念，并在实践中掌握这一领域的核心技能。通过...]]></description>
<content:encoded xml:lang="zh-CN"><![CDATA[
<p>如果你是NLP领域初学者，欢迎关注我的博客，我不仅会分享理论知识，更会通过实例和实用技巧帮助你迅速入门。我的目标是让每个初学者都能轻松理解复杂的NLP概念，并在实践中掌握这一领域的核心技能。<br>通过我的博客，你将了解到：<br>• NLP的基础概念，为你打下坚实的学科基础。<br>• 实际项目中的应用案例，让你更好地理解NLP技术在现实生活中的应用。<br>• 学习和成长的资源，助你在NLP领域迅速提升自己。<br>不论你是刚刚踏入NLP的大门，还是这个领域的资深专家，我的博客都将为你提供有益的信息。一起探索语言的边界，迎接未知的挑战，让我们共同在NLP的海洋中畅游！期待与你一同成长，感谢你的关注和支持。欢迎任何人前来讨论问题。</p><ul><li><a href="https://blog.os-o.cn/index.php/archives/42/">【NLP 基础知识一】词嵌入（Word Embeddings）</a></li><li><a href="https://blog.os-o.cn/index.php/archives/51/">【NLP 基础知识二】词嵌入（Word Embeddings）之“Word2Vec：一种基于预测的方法”</a></li></ul><p><a href="https://blog.os-o.cn/index.php/archives/78/">【NLP基础知识三】词嵌入（Word Embeddings）之“GloVe：单词表示的全局向量”</a></p><h1>一、词嵌入</h1><h2>1、概述</h2><p>机器学习模型“查看”数据的方式与我们（人类）不同。例如，我们可以很容易地理解文本“我看到了一只猫”，但模型却不能，模型需要特征向量。这样的特征向量被称为词嵌入，是一种可以输入模型的词语表示。</p><h2>2、工作原理：查找表（词表）</h2><p>在实践中，你有一个事先确定好的词汇表。对于每个词汇表中的单词，查找表都有该单词对应的词嵌入，我们可以使用单词在词汇表中的索引找到该词嵌入。<br>为了表示未登录词（即不在词汇表中的词），词汇表通常包含一个特殊的单词 UNK。当然，我们也可以选择忽略未登录词，或者简单分配一个零值向量。</p><h1>二、如何获得这些词向量？</h1><h2>1、离散符号表示：独热向量（One-hot Vectors）</h2><p><img src="https://blog.os-o.cn/usr/uploads/2024/01/970362308.png" alt="2024-01-25T14:14:48.png" title="2024-01-25T14:14:48.png"><br>最简单的词向量表示方法是用独热（One-hot）向量表示每个单词：对于词汇表中的第 i 个单词，独热向量的第 i 个维度为 1，其余为 0。在机器学习中，独热向量是表示分类特征最简单的方法。<br>你大概能猜到为什么独热向量不是表示单词的最佳方式。它的一个已知问题是，独热向量在词汇表较大时会变得非常长，因为独热向量的维度就等于词汇表大小，这在实践中是不可取的。但这并不是最关键的问题。<br>真正重要的是，独热向量对它们所代表的词语一无所知。例如，明显背离常识的是，独热向量居然认为 “猫”到“狗”和“桌子”的语义距离一样近！因此，我们认为独热向量没有捕捉到单词含义。<br>那么问题来了，我们怎么知道什么是含义呢？</p><h2>2、分布式语义</h2><p>为了捕捉单词向量表示的含义，我们首先需要定义可以在实践中使用的“含义”的概念。为此，让我们首先来了解人类如何知道哪些词具有相似的含义。<br>看一下下面这几张图，可以帮助你更加直观的理解。<br>你知道<img src="https://blog.os-o.cn/usr/uploads/2024/01/3972156207.png" alt="2024-01-25T14:23:03.png" title="2024-01-25T14:23:03.png">这个词的含义吗？<br><img src="https://blog.os-o.cn/usr/uploads/2024/01/3906351488.png" alt="2024-01-25T14:20:17.png" title="2024-01-25T14:20:17.png"><br>下面给出一些包含这个词的句子，现在你能理解这个词的意思了吗？<br><img src="https://blog.os-o.cn/usr/uploads/2024/01/2511517367.png" alt="2024-01-25T14:20:32.png" title="2024-01-25T14:20:32.png"><br>这个词的意思是一种饮料，现在你可以理解了吧。<br><img src="https://blog.os-o.cn/usr/uploads/2024/01/174211226.png" alt="2024-01-25T14:20:43.png" title="2024-01-25T14:20:43.png"><br>然后呢？<br><img src="https://blog.os-o.cn/usr/uploads/2024/01/2632712231.png" alt="2024-01-25T14:20:55.png" title="2024-01-25T14:20:55.png"><br>有没有其他的词可以填入这些空呢？（遇到同样的句子，空的位置却是其他的词。）<br><img src="https://blog.os-o.cn/usr/uploads/2024/01/2959155736.png" alt="2024-01-25T14:21:06.png" title="2024-01-25T14:21:06.png"><br>1代表可以填入，0代表不能。<br><img src="https://blog.os-o.cn/usr/uploads/2024/01/1328013479.png" alt="2024-01-25T14:21:15.png" title="2024-01-25T14:21:15.png"><br>形成的向量，有一些行是非常相似的。<br><img src="https://blog.os-o.cn/usr/uploads/2024/01/3891590382.png" alt="2024-01-25T14:21:28.png" title="2024-01-25T14:21:28.png"><br>行与行之间越相似，我们认为这两个词越相似<br><img src="https://blog.os-o.cn/usr/uploads/2024/01/744292145.png" alt="2024-01-25T14:21:37.png" title="2024-01-25T14:21:37.png"><br>这就是分布式语义的思想<br><img src="https://blog.os-o.cn/usr/uploads/2024/01/3899251721.png" alt="2024-01-25T14:21:45.png" title="2024-01-25T14:21:45.png"><br>人类有一种能力：一旦你看到未知词语在不同语境中的使用方式，你就能够理解它的含义。那人类是怎么做的呢？<br>猜想是：你的大脑搜索了其他可用于相同语境的词，找到一些单词（例如，葡萄酒），并得出结论 tezgüino 与这些词具有相似的含义。这就是分布假设：</p><pre><code>经常出现在相似上下文中的词具有相似含义。
Words which frequently appear in similar contexts have similar meaning.</code></pre><p>这是一个非常有价值的想法：可以用在实践中让词向量捕捉单词的含义。根据分布假设，“捕捉含义”和“捕捉上下文”在本质上是一样的。 因此，我们需要做的就是将有关单词上下文的信息引入单词表示中。</p><pre><code>核心思想：我们需要将有关单词上下文的信息引入单词表示中。</code></pre><p>在本次课程中，我们要做的就是使用不同的方法来做到这一点。</p><h2>3、基于计数的方法</h2><p><img src="https://blog.os-o.cn/usr/uploads/2024/01/504332709.png" alt="2024-01-25T14:34:04.png" title="2024-01-25T14:34:04.png"><br>如果你看不懂上面的图，请继续向下看<br>再来重申一下我们的核心思想：</p><pre><code>核心思想：我们需要将有关上下文的信息引入词向量中。</code></pre><p>基于计数的方法非常直观地实现了这个想法：</p><pre><code>具体做法：根据全局的语料统计手动地放置这些信息。</code></pre><p>一般地，该方法包括两个步骤 (过程如上图所示)：(1) 构造一个单词-上下文(Word-Context) 的关联矩阵; (2) 降低该矩阵的维度。这里降维主要有两个原因：一方面，原始矩阵非常大。另一方面，由于很多词语只出现在比较少见的上下文中，这样的矩阵可能有很多没什么信息量的元素（例如，空值）。<br>要设计一个基于计数的方法，我们需要定义两件事：</p><ul><li>上下文的定义（包括单词出现在上下文中意味着什么）</li><li>关联的定义（即如何计算单词-上下文关联矩阵中每个元素）。</li></ul><p>下面我们介绍几种主流的方法来实现这两个概念。</p><h3>简单：共现计数</h3><p><img src="https://blog.os-o.cn/usr/uploads/2024/01/1735286657.png" alt="2024-01-25T14:42:10.png" title="2024-01-25T14:42:10.png"><br><img src="https://blog.os-o.cn/usr/uploads/2024/01/3835262219.png" alt="2024-01-25T14:42:17.png" title="2024-01-25T14:42:17.png"><br>在本方法中，上下文被定义为 L 大小窗口中的每个单词。而单词-上下文组成的 (w, c) 对应的关联矩阵元素是 w 在上下文 c 中出现的次数。这是获取词嵌入非常基本（且非常古老）的方法。</p><h3>正点交互信息 (PPMI)</h3><p><img src="https://blog.os-o.cn/usr/uploads/2024/01/3838614861.png" alt="2024-01-25T14:51:22.png" title="2024-01-25T14:51:22.png"><br>以上图如果看不懂，请继续往下看<br>先了解一下什么是PMI（Pointwise Mutual Information）点互信息：</p><pre><code>这一指标用来衡量两个事物之间的相关性。</code></pre><p>公式如下：<br><img src="https://blog.os-o.cn/usr/uploads/2024/01/3450714739.png" alt="2024-01-25T15:00:34.png" title="2024-01-25T15:00:34.png"><br>在概率论中，如果x和y无关，p(x,y)=p(x)p(y)；如果x和y越相关，p(x,y)和p(x)p(y)的比就越大。从后两个条件概率可能更好解释，在y出现的条件下x出现的概率除以单看x出现的概率，这个值越大表示x和y越相关。<br><img src="https://blog.os-o.cn/usr/uploads/2024/01/3945567888.png" alt="2024-01-25T15:00:47.png" title="2024-01-25T15:00:47.png"><br>log来自于信息论的理论，而且 log 1 = 0 ，也恰恰表明P(x,y) = P(x)P(y)，相关性为0，而且log是单调递增函数，所以 “P(x,y) 就相比于 P(x)P(y) 越大，x 和 y 相关性越大” 这一性质也得到保留。<br>通常会计算一个 PPMI（Positive PMI） 来避免出现 -inf，即<br><img src="https://blog.os-o.cn/usr/uploads/2024/01/4226970519.png" alt="2024-01-25T15:01:36.png" title="2024-01-25T15:01:36.png"><br>在这种方法中，上下文的定义和之前一样，但是单词和上下文之间关联矩阵的计算采用了更加聪明的PPMI (Positive Pointwise Mutual Information, 正点交互信息) 度量。 PPMI 度量被广泛认为是神经网络出现前用于度量分布相似性的最佳技术。<br>重要：本方法与神经网络密不可分！事实证明，接下来介绍的一些基于神经网络的方法 (Word2Vec) 被证明实际上是在隐式逼近（移位的）PMI 矩阵的因式分解。敬请关注！</p><h3>潜在语义分析 (LSA)：理解文档</h3><p><img src="https://blog.os-o.cn/usr/uploads/2024/01/3557457098.png" alt="2024-01-25T15:03:23.png" title="2024-01-25T15:03:23.png"><br>LSA (Latent Semantic Analysis, 潜在语义分析) 方法 需要分析文档的集合。在前人方法中，上下文仅用于获取词向量，之后就会被丢弃。但在LSA方法中，上下文也要被充分利用，用来计算文档向量。也因此，LSA 成为最简单的主题模型之一：所获得的文档向量之间的余弦相似度可以用来衡量文档之间的相似度。<br>术语 LSA 有时是指将 SVD 应用于单词-文档矩阵的通用方法，其中单词-文档矩阵的各个元素可以通过不同的方式计算（例如，简单的共现、tf-idf 或其他的衡量方法).<br>动画预告！ <a href="https://en.wikipedia.org/wiki/Latent_semantic_analysis">LSA 的Wikipedia 主页</a>在单词-文档矩阵中有一个很好的动画用于揭示文档的主题探测过程，一定要看看！</p><h2>4、Word2Vec：一种基于预测的方法</h2><h3>今天太晚了，先到这里，<code>Word2Vec：一种基于预测的方法</code>，这一章篇幅有点多，明天才能产出。</h3><h3>出了链接就在这里：<a href="https://blog.os-o.cn/index.php/archives/51/">【NLP 基础知识二】词嵌入（Word Embeddings）之“Word2Vec：一种基于预测的方法”</a></h3><ul><li><a href="https://blog.os-o.cn/index.php/archives/42/">【NLP 基础知识一】词嵌入（Word Embeddings）</a></li><li><a href="https://blog.os-o.cn/index.php/archives/51/">【NLP 基础知识二】词嵌入（Word Embeddings）之“Word2Vec：一种基于预测的方法”</a></li></ul><p>在这个充满科技魔力的时代，自然语言处理（NLP）正如一颗璀璨的明星般照亮我们的数字世界。当我们涉足NLP的浩瀚宇宙，仿佛开启了一场语言的奇幻冒险。正如亚历克斯·康普顿所言：“语言是我们思想的工具，而NLP则是赋予语言新生命的魔法。”这篇博客将引领你走进NLP前沿，发现语言与技术的交汇点，探寻其中的无尽可能。不论你是刚刚踏入NLP的大门，还是这个领域的资深专家，我的博客都将为你提供有益的信息。一起探索语言的边界，迎接未知的挑战，让我们共同在NLP的海洋中畅游！期待与你一同成长，感谢你的关注和支持。欢迎任何人前来讨论问题。</p>
]]></content:encoded>
<slash:comments>9</slash:comments>
<comments>https://www.blog.os-o.cn/index.php/archives/42/#comments</comments>
<wfw:commentRss>https://www.blog.os-o.cn/index.php/feed/author/1/</wfw:commentRss>
</item>
<item>
<title>【大模型实践】ChatGLM3-6B 微调实践，更新模型知识</title>
<link>https://www.blog.os-o.cn/index.php/archives/15/</link>
<guid>https://www.blog.os-o.cn/index.php/archives/15/</guid>
<pubDate>Wed, 24 Jan 2024 23:23:00 +0800</pubDate>
<dc:creator>chengligen</dc:creator>
<description><![CDATA[如果你是NLP领域初学者，欢迎关注我的博客，我不仅会分享理论知识，更会通过实例和实用技巧帮助你迅速入门。我的目标是让每个初学者都能轻松理解复杂的NLP概念，并在实践中掌握这一领域的核心技能。通过...]]></description>
<content:encoded xml:lang="zh-CN"><![CDATA[
<p>如果你是NLP领域初学者，欢迎关注我的博客，我不仅会分享理论知识，更会通过实例和实用技巧帮助你迅速入门。我的目标是让每个初学者都能轻松理解复杂的NLP概念，并在实践中掌握这一领域的核心技能。<br>通过我的博客，你将了解到：<br>• NLP的基础概念，为你打下坚实的学科基础。<br>• 实际项目中的应用案例，让你更好地理解NLP技术在现实生活中的应用。<br>• 学习和成长的资源，助你在NLP领域迅速提升自己。<br>不论你是刚刚踏入NLP的大门，还是这个领域的资深专家，我的博客都将为你提供有益的信息。一起探索语言的边界，迎接未知的挑战，让我们共同在NLP的海洋中畅游！期待与你一同成长，感谢你的关注和支持。欢迎任何人前来讨论问题。</p><h1>一、ChatGLM3 介绍</h1><h2>1、概述</h2><p>ChatGLM3 是智谱AI和清华大学 KEG 实验室联合发布的对话预训练模型。ChatGLM3-6B 是 ChatGLM3 系列中的开源模型，在保留了前两代模型对话流畅、部署门槛低等众多优秀特性的基础上，ChatGLM3-6B 引入了如下特性：<br><img src="https://blog.os-o.cn/usr/uploads/2024/01/2047403508.png" alt="2024-01-24T14:39:28.png" title="2024-01-24T14:39:28.png"><br>a、更强大的基础模型： ChatGLM3-6B 的基础模型 ChatGLM3-6B-Base 采用了更多样的训练数据、更充分的训练步数和更合理的训练策略。在语义、数学、推理、代码、知识等不同角度的数据集上测评显示，<em> ChatGLM3-6B-Base 具有在 10B 以下的基础模型中最强的性能</em>。<br>b、更完整的功能支持： ChatGLM3-6B 采用了全新设计的 Prompt 格式 ，除正常的多轮对话外。同时原生支持工具调用（Function Call）、代码执行（Code Interpreter）和 Agent 任务等复杂场景。<br>c、更全面的开源序列： 除了对话模型 ChatGLM3-6B 外，还开源了基础模型 ChatGLM3-6B-Base 、长文本对话模型 ChatGLM3-6B-32K。以上所有权重对学术研究完全开放 ，在填写 问卷 进行登记后亦允许免费商业使用。<br><a href="https://github.com/THUDM/ChatGLM3"><a href="https://github.com/THUDM/ChatGLM3">https://github.com/THUDM/ChatGLM3</a></a></p><h2>2、实现原理</h2><p>以下是 GPT 模型通用的实现原理：<br>a、Transformer Architecture： GPT 模型采用 Transformer 架构，包括多个编码器和解码器层。每个层都包含多头自注意力机制和前馈神经网络。<br>b、预训练： 模型首先在大规模的文本语料库上进行预训练。这个阶段模型学到了语言的统计结构、语法、语义等信息，使其能够理解和生成自然语言。<br>c、参数规模： chatglm3-6B 表示该模型包含约 6 亿（Billion）个参数，这使得它相当庞大，有能力处理多种复杂的语言任务。<br>d、微调： 在预训练之后，模型可以在特定任务或领域上进行微调，以适应具体的应用场景。微调可能需要一个任务特定的数据集。<br>e、Tokenization 和 Attention Mechanism： 输入文本通过分词（Tokenization）被转换成模型能够理解的表示，并且通过自注意力机制进行处理，以关注输入序列中不同位置的信息。<br>f、生成式模型： GPT 是生成式模型，它能够生成类似于训练数据的文本。在对话中，它可以生成连贯自然的回应。</p><h1>二、开发环境准备</h1><h2>1、硬件要求</h2><pre><code>最低显存要求: 24GB
推荐显卡: RTX 4090</code></pre><h2>2、软件要求</h2><pre><code>Linux Ubuntu 22.04.5 kernel version 6.7
Python 版本: = 3.10
CUDA 版本: &gt;= 11.7</code></pre><h1>三、部署和微调</h1><h2>1、拉取仓库</h2><pre><code>git clone https://github.com/THUDM/ChatGLM3</code></pre><h2>2、进入目录</h2><pre><code>cd ChatGLM3</code></pre><h2>3、创建虚拟环境</h2><pre><code>conda create -n chatglm python=3.10</code></pre><h2>4、激活使用虚拟环境</h2><pre><code>conda activate chatglm</code></pre><h2>5、安装全部依赖</h2><pre><code>pip install -r requirements.txt</code></pre><h2>6、下载模型</h2><h3>a、访问 <a href="https://huggingface.co/THUDM/chatglm3-6b">https://huggingface.co/THUDM/chatglm3-6b</a> 下载（需要特殊网络环境）</h3><h3>b、从hugging face下的话需要特殊网络环境，没有条件的可以从魔塔社区下载：</h3><pre><code>!pip install modelscope

from modelscope import snapshot_download
model_dir = snapshot_download(&quot;ZhipuAI/chatglm3-6b&quot;, revision = &quot;v1.0.0&quot;, cache_dir='path/to/save/dir')</code></pre><p>其中<code>cache_dir</code>参数指定了模型保存的路径。</p><h2>7、进入<code>finetune_chatmodel_demo</code>并安装微调依赖：</h2><pre><code>cd finetune_chatmodel_demo
pip install requirements.txt</code></pre><p>使用对话格式进行微调，官方给出的数据格式如下：</p><pre><code>[
  {
    &quot;conversations&quot;: [
      {
        &quot;role&quot;: &quot;system&quot;,
        &quot;content&quot;: &quot;&lt;system prompt text&gt;&quot;
      },
      {
        &quot;role&quot;: &quot;user&quot;,
        &quot;content&quot;: &quot;&lt;user prompt text&gt;&quot;
      },
      {
        &quot;role&quot;: &quot;assistant&quot;,
        &quot;content&quot;: &quot;&lt;assistant response text&gt;&quot;
      }, 
       // ... Muti Turn
      {
        &quot;role&quot;: &quot;user&quot;,
        &quot;content&quot;: &quot;&lt;user prompt text&gt;&quot;
      },
      {
        &quot;role&quot;: &quot;assistant&quot;,
        &quot;content&quot;: &quot;&lt;assistant response text&gt;&quot;
      }
    ]
  }
  // ...
]</code></pre><h2>8、下载 ToolAlpaca 数据集</h2><p>作为示例，使用 ToolAlpaca 数据集来进行微调。首先，克隆 ToolAlpaca 数据集</p><pre><code>git clone https://github.com/tangqiaoyu/ToolAlpaca</code></pre><h2>9、处理数据集格式</h2><pre><code>./scripts/format_tool_alpaca.py --path &quot;ToolAlpaca/data/train_data.json&quot;</code></pre><p>将数据集处理成上述格式。在这里，我们有意将工具处理成了了 list[str] 这样的自然语言形式，以观察模型在微调前后对工具定义的理解能力。<br>处理完成数据位置：</p><pre><code>ChatGLM3/finetune_chatmodel_demo/formatted_data/tool_alpaca.jsonl</code></pre><h2>10、微调模型</h2><p>官方提供了两种方式，一般使用P-Tuning v2 微调即可。如果有报错，请查看文文章末的异常集合有无解决方案。</p><pre><code>./scripts/finetune_ds_multiturn.sh  # 全量微调
./scripts/finetune_pt_multiturn.sh  # P-Tuning v2 微调</code></pre><p>微调过程较长，显卡大概占用23G显存：</p><pre><code>Every 1.0s: nvidia-smi                                                                                    Wed Jan 24 22:59:35 2024

Wed Jan 24 22:59:35 2024
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 515.105.01   Driver Version: 515.105.01   CUDA Version: 11.7     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  Tesla V100-PCIE...  Off  | 00000000:98:00.0 Off |                    0 |
| N/A   72C    P0   256W / 250W |  29786MiB / 32768MiB |    100%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
|   1  Tesla V100-PCIE...  Off  | 00000000:CA:00.0 Off |                    0 |
| N/A   28C    P0    34W / 250W |  19736MiB / 32768MiB |      0%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
|    0   N/A  N/A     13213      C   ...a/envs/chatglm/bin/python    23485MiB |</code></pre><p>顺利的话，窗口会输出如下内容：</p><pre><code>+ PRE_SEQ_LEN=128
+ LR=2e-2
+ NUM_GPUS=1
+ MAX_SEQ_LEN=2048
+ DEV_BATCH_SIZE=1
+ GRAD_ACCUMULARION_STEPS=16
+ MAX_STEP=1000
+ SAVE_INTERVAL=500
+ AUTORESUME_FROM_CHECKPOINT=True
++ date +%Y%m%d-%H%M%S
+ DATESTR=20240124-134556
+ RUN_NAME=tool_alpaca_pt
+ BASE_MODEL_PATH=/data/chengligen/ChatGLM3-main/models/chatglm3-6b
+ DATASET_PATH=formatted_data/tool_alpaca.jsonl
+ OUTPUT_DIR=output/tool_alpaca_pt-20240124-134556-128-2e-2
+ mkdir -p output/tool_alpaca_pt-20240124-134556-128-2e-2
+ tee output/tool_alpaca_pt-20240124-134556-128-2e-2/train.log
+ torchrun --standalone --nnodes=1 --nproc_per_node=1 finetune.py --train_format multi-turn --train_file formatted_data/tool_alpaca.jsonl --max_seq_length 2048 --preprocessing_num_workers 1 --model_name_or_path /data/chengligen/ChatGLM3-main/models/chatglm3-6b --output_dir output/tool_alpaca_pt-20240124-134556-128-2e-2 --per_device_train_batch_size 1 --gradient_accumulation_steps 16 --max_steps 1000 --logging_steps 1 --save_steps 500 --learning_rate 2e-2 --pre_seq_len 128 --resume_from_checkpoint True
master_addr is only used for static rdzv_backend and when rdzv_endpoint is not specified.
01/24/2024 13:46:02 - WARNING - __main__ - Process rank: 0, device: cuda:0, n_gpu: 1distributed training: True, 16-bits training: False
01/24/2024 13:46:02 - INFO - __main__ - Training/evaluation parameters Seq2SeqTrainingArguments(
_n_gpu=1,
adafactor=False,
adam_beta1=0.9,
adam_beta2=0.999,
adam_epsilon=1e-08,
auto_find_batch_size=False,
bf16=False,
bf16_full_eval=False,
data_seed=None,
dataloader_drop_last=False,
dataloader_num_workers=0,
dataloader_persistent_workers=False,
dataloader_pin_memory=True,
ddp_backend=None,
ddp_broadcast_buffers=None,
ddp_bucket_cap_mb=None,
ddp_find_unused_parameters=False,
ddp_timeout=1800,
debug=[],
deepspeed=None,
disable_tqdm=False,
dispatch_batches=None,
do_eval=False,
do_predict=False,
do_train=False,
eval_accumulation_steps=None,
eval_delay=0,
eval_steps=None,
evaluation_strategy=no,
fp16=False,
fp16_backend=auto,
fp16_full_eval=False,
fp16_opt_level=O1,
fsdp=[],
fsdp_config={'min_num_params': 0, 'xla': False, 'xla_fsdp_grad_ckpt': False},
fsdp_min_num_params=0,
fsdp_transformer_layer_cls_to_wrap=None,
full_determinism=False,
generation_config=None,
generation_max_length=None,
generation_num_beams=None,
gradient_accumulation_steps=16,
gradient_checkpointing=False,
gradient_checkpointing_kwargs=None,
greater_is_better=None,
group_by_length=False,
half_precision_backend=auto,
hub_always_push=False,
hub_model_id=None,
hub_private_repo=False,
hub_strategy=every_save,
hub_token=&lt;HUB_TOKEN&gt;,
ignore_data_skip=False,
include_inputs_for_metrics=False,
include_num_input_tokens_seen=False,
include_tokens_per_second=False,
jit_mode_eval=False,
label_names=None,
label_smoothing_factor=0.0,
learning_rate=0.02,
length_column_name=length,
load_best_model_at_end=False,
local_rank=0,
log_level=passive,
log_level_replica=warning,
log_on_each_node=True,
logging_dir=output/tool_alpaca_pt-20240124-134556-128-2e-2/runs/Jan24_13-46-02_nlp,
logging_first_step=False,
logging_nan_inf_filter=True,
logging_steps=1.0,
logging_strategy=steps,
lr_scheduler_kwargs={},
lr_scheduler_type=linear,
max_grad_norm=1.0,
max_steps=1000,
metric_for_best_model=None,
mp_parameters=,
neftune_noise_alpha=None,
no_cuda=False,
num_train_epochs=3.0,
optim=adamw_torch,
optim_args=None,
output_dir=output/tool_alpaca_pt-20240124-134556-128-2e-2,
overwrite_output_dir=False,
past_index=-1,
per_device_eval_batch_size=8,
per_device_train_batch_size=1,
predict_with_generate=False,
prediction_loss_only=False,
push_to_hub=False,
push_to_hub_model_id=None,
push_to_hub_organization=None,
push_to_hub_token=&lt;PUSH_TO_HUB_TOKEN&gt;,
ray_scope=last,
remove_unused_columns=True,
report_to=[],
resume_from_checkpoint=True,
run_name=output/tool_alpaca_pt-20240124-134556-128-2e-2,
save_on_each_node=False,
save_only_model=False,
save_safetensors=False,
save_steps=500,
save_strategy=steps,
save_total_limit=None,
seed=42,
skip_memory_metrics=True,
sortish_sampler=False,
split_batches=False,
tf32=None,
torch_compile=False,
torch_compile_backend=None,
torch_compile_mode=None,
torchdynamo=None,
tpu_metrics_debug=False,
tpu_num_cores=None,
use_cpu=False,
use_ipex=False,
use_legacy_prediction_loop=False,
use_mps_device=False,
warmup_ratio=0.0,
warmup_steps=0,
weight_decay=0.0,
)
[INFO|configuration_utils.py:727] 2024-01-24 13:46:02,515 &gt;&gt; loading configuration file /data/chengligen/ChatGLM3-main/models/chatglm3-6b/config.json
[INFO|configuration_utils.py:727] 2024-01-24 13:46:02,520 &gt;&gt; loading configuration file /data/chengligen/ChatGLM3-main/models/chatglm3-6b/config.json
[INFO|configuration_utils.py:792] 2024-01-24 13:46:02,522 &gt;&gt; Model config ChatGLMConfig {
  &quot;_name_or_path&quot;: &quot;/data/chengligen/ChatGLM3-main/models/chatglm3-6b&quot;,
  &quot;add_bias_linear&quot;: false,
  &quot;add_qkv_bias&quot;: true,
  &quot;apply_query_key_layer_scaling&quot;: true,
  &quot;apply_residual_connection_post_layernorm&quot;: false,
  &quot;architectures&quot;: [
    &quot;ChatGLMModel&quot;
  ],
  &quot;attention_dropout&quot;: 0.0,
  &quot;attention_softmax_in_fp32&quot;: true,
  &quot;auto_map&quot;: {
    &quot;AutoConfig&quot;: &quot;configuration_chatglm.ChatGLMConfig&quot;,
    &quot;AutoModel&quot;: &quot;modeling_chatglm.ChatGLMForConditionalGeneration&quot;,
    &quot;AutoModelForCausalLM&quot;: &quot;modeling_chatglm.ChatGLMForConditionalGeneration&quot;,
    &quot;AutoModelForSeq2SeqLM&quot;: &quot;modeling_chatglm.ChatGLMForConditionalGeneration&quot;,
    &quot;AutoModelForSequenceClassification&quot;: &quot;modeling_chatglm.ChatGLMForSequenceClassification&quot;
  },
  &quot;bias_dropout_fusion&quot;: true,
  &quot;classifier_dropout&quot;: null,
  &quot;eos_token_id&quot;: 2,
  &quot;ffn_hidden_size&quot;: 13696,
  &quot;fp32_residual_connection&quot;: false,
  &quot;hidden_dropout&quot;: 0.0,
  &quot;hidden_size&quot;: 4096,
  &quot;kv_channels&quot;: 128,
  &quot;layernorm_epsilon&quot;: 1e-05,
  &quot;model_type&quot;: &quot;chatglm&quot;,
  &quot;multi_query_attention&quot;: true,
  &quot;multi_query_group_num&quot;: 2,
  &quot;num_attention_heads&quot;: 32,
  &quot;num_layers&quot;: 28,
  &quot;original_rope&quot;: true,
  &quot;pad_token_id&quot;: 0,
  &quot;padded_vocab_size&quot;: 65024,
  &quot;post_layer_norm&quot;: true,
  &quot;pre_seq_len&quot;: null,
  &quot;prefix_projection&quot;: false,
  &quot;quantization_bit&quot;: 0,
  &quot;rmsnorm&quot;: true,
  &quot;seq_length&quot;: 8192,
  &quot;tie_word_embeddings&quot;: false,
  &quot;torch_dtype&quot;: &quot;float16&quot;,
  &quot;transformers_version&quot;: &quot;4.37.0&quot;,
  &quot;use_cache&quot;: true,
  &quot;vocab_size&quot;: 65024
}

[INFO|tokenization_utils_base.py:2025] 2024-01-24 13:46:02,527 &gt;&gt; loading file tokenizer.model
[INFO|tokenization_utils_base.py:2025] 2024-01-24 13:46:02,527 &gt;&gt; loading file added_tokens.json
[INFO|tokenization_utils_base.py:2025] 2024-01-24 13:46:02,527 &gt;&gt; loading file special_tokens_map.json
[INFO|tokenization_utils_base.py:2025] 2024-01-24 13:46:02,527 &gt;&gt; loading file tokenizer_config.json
[INFO|tokenization_utils_base.py:2025] 2024-01-24 13:46:02,527 &gt;&gt; loading file tokenizer.json
[INFO|modeling_utils.py:3475] 2024-01-24 13:46:02,787 &gt;&gt; loading weights file /data/chengligen/ChatGLM3-main/models/chatglm3-6b/pytorch_model.bin.index.json
[INFO|configuration_utils.py:826] 2024-01-24 13:46:02,788 &gt;&gt; Generate config GenerationConfig {
  &quot;eos_token_id&quot;: 2,
  &quot;pad_token_id&quot;: 0,
  &quot;use_cache&quot;: false
}

Loading checkpoint shards: 100%|██████████| 7/7 [00:10&lt;00:00,  1.50s/it]
[INFO|modeling_utils.py:4352] 2024-01-24 13:46:13,332 &gt;&gt; All model checkpoint weights were used when initializing ChatGLMForConditionalGeneration.
[INFO|modeling_utils.py:3897] 2024-01-24 13:46:13,335 &gt;&gt; Generation config file not found, using a generation config created from the model config.
Sanity Check &gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;
           '[gMASK]':  64790 -&gt;   -100
               'sop':  64792 -&gt;   -100
        '&lt;|system|&gt;':  64794 -&gt;   -100
                  '':  30910 -&gt;   -100
                '\n':     13 -&gt;   -100
            'Answer':  20115 -&gt;   -100
               'the':    267 -&gt;   -100

                 ......................

                 '0':  30940 -&gt;  30940
                 '0':  30940 -&gt;  30940
                 '.':  30930 -&gt;  30930
                  '':      2 -&gt;      2
&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt; Sanity Check
[INFO|trainer.py:522] 2024-01-24 13:46:23,002 &gt;&gt; max_steps is given, it will override any value given in num_train_epochs
[INFO|trainer.py:1721] 2024-01-24 13:46:23,749 &gt;&gt; ***** Running training *****
[INFO|trainer.py:1722] 2024-01-24 13:46:23,749 &gt;&gt;   Num examples = 4,048
[INFO|trainer.py:1723] 2024-01-24 13:46:23,749 &gt;&gt;   Num Epochs = 4
[INFO|trainer.py:1724] 2024-01-24 13:46:23,749 &gt;&gt;   Instantaneous batch size per device = 1
[INFO|trainer.py:1727] 2024-01-24 13:46:23,749 &gt;&gt;   Total train batch size (w. parallel, distributed &amp; accumulation) = 16
[INFO|trainer.py:1728] 2024-01-24 13:46:23,749 &gt;&gt;   Gradient Accumulation steps = 16
[INFO|trainer.py:1729] 2024-01-24 13:46:23,749 &gt;&gt;   Total optimization steps = 1,000
[INFO|trainer.py:1730] 2024-01-24 13:46:23,751 &gt;&gt;   Number of trainable parameters = 1,835,008
{'loss': 0.9685, 'learning_rate': 0.01998, 'epoch': 0.0}
{'loss': 0.9597, 'learning_rate': 0.019960000000000002, 'epoch': 0.01}
{'loss': 0.9796, 'learning_rate': 0.01994, 'epoch': 0.01}

.......................................................

{'loss': 0.292, 'learning_rate': 0.0165, 'epoch': 0.69}
 18%|█▊        | 175/1000 [1:08:58&lt;5:24:24, 23.59s/it]</code></pre><h1>四、部署微调后的模型</h1><p>MODEL_PATH为自己的chatglm3-6b的路径，PT_PATH指向微调后输出的路径，PT_PATH的路径一般如下位置和格式：</p><pre><code>&quot;ChatGLM3/finetune_chatmodel_demo/output/tool_alpaca_pt-20231227-061735-128-2e-2&quot;</code></pre><h2>1、对于全量微调，可以使用以下方式进行部署</h2><pre><code>cd ../composite_demo
MODEL_PATH=&quot;path to finetuned model checkpoint&quot; TOKENIZER_PATH=&quot;THUDM/chatglm3-6b&quot; streamlit run main.py</code></pre><h2>2、对于 P-Tuning v2 微调，可以使用以下方式进行部署</h2><pre><code>cd ../composite_demo
MODEL_PATH=&quot;THUDM/chatglm3-6b&quot; PT_PATH=&quot;path to p-tuning checkpoint&quot; streamlit run main.py</code></pre><h1>五、异常集合</h1><h2>1、异常一</h2><h3>场景：</h3><p>运行微调代码时候</p><pre><code>./scripts/finetune_ds.sh  # 全量微调
./scripts/finetune_pt.sh  # P-Tuning v2 微调</code></pre><h3>问题：</h3><p>出现无限循环网络问题</p><pre><code>[W socket.cpp:601] [c10d] The IPv6 network addresses of (nlp, 56126) cannot be retrieved (gai error: -2 - Name or service not known).
[W socket.cpp:601] [c10d] The IPv6 network addresses of (nlp, 56126) cannot be retrieved (gai error: -2 - Name or service not known).
[W socket.cpp:601] [c10d] The IPv6 network addresses of (nlp, 56126) cannot be retrieved (gai error: -2 - Name or service not known).
[W socket.cpp:601] [c10d] The IPv6 network addresses of (nlp, 56126) cannot be retrieved (gai error: -2 - Name or service not known).</code></pre><h3>解决：</h3><p>大概意思是没能连接上本地的网络，查了很多资料都没有结果，下面这个解决方法对我有用。<br>手动添加本地网络</p><pre><code>vim /etc/hosts</code></pre><p>添上本地网络</p><pre><code>127.0.0.1 nlp</code></pre><p>nlp为机器名字，如果没有127.0.0.1这一行，自行添加即可<br><img src="https://blog.os-o.cn/usr/uploads/2024/01/436742732.png" alt="2024-01-24T15:17:44.png" title="2024-01-24T15:17:44.png"></p><h2>2、异常二</h2><h3>场景：</h3><p>运行微调代码时候</p><pre><code>./scripts/finetune_ds.sh  # 全量微调
./scripts/finetune_pt.sh  # P-Tuning v2 微调</code></pre><h3>问题：</h3><p>端口占用</p><pre><code>(chatglm) root@nlp:/data/chengligen/ChatGLM3-main/finetune_chatmodel_demo# ./scripts/finetune_pt_multiturn.sh
+ PRE_SEQ_LEN=128
+ LR=2e-2
+ NUM_GPUS=1
+ MAX_SEQ_LEN=2048
+ DEV_BATCH_SIZE=1
+ GRAD_ACCUMULARION_STEPS=16
+ MAX_STEP=1000
+ SAVE_INTERVAL=500
+ AUTORESUME_FROM_CHECKPOINT=True
++ date +%Y%m%d-%H%M%S
+ DATESTR=20240124-134257
+ RUN_NAME=tool_alpaca_pt
+ BASE_MODEL_PATH=/data/chengligen/ChatGLM3-main/models/chatglm3-6b
+ DATASET_PATH=formatted_data/tool_alpaca.jsonl
+ OUTPUT_DIR=output/tool_alpaca_pt-20240124-134257-128-2e-2
+ mkdir -p output/tool_alpaca_pt-20240124-134257-128-2e-2
+ torchrun --standalone --nnodes=1 --nproc_per_node=1 finetune.py --train_format multi-turn --train_file formatted_data/tool_alpaca.jsonl --max_seq_length 2048 --preprocessing_num_workers 1 --model_name_or_path /data/chengligen/ChatGLM3-main/models/chatglm3-6b --output_dir output/tool_alpaca_pt-20240124-134257-128-2e-2 --per_device_train_batch_size 1 --gradient_accumulation_steps 16 --max_steps 1000 --logging_steps 1 --save_steps 500 --learning_rate 2e-2 --pre_seq_len 128 --resume_from_checkpoint True
+ tee output/tool_alpaca_pt-20240124-134257-128-2e-2/train.log
master_addr is only used for static rdzv_backend and when rdzv_endpoint is not specified.
[W socket.cpp:426] [c10d] The server socket has failed to bind to [::]:29400 (errno: 98 - Address already in use).
[W socket.cpp:426] [c10d] The server socket has failed to bind to 0.0.0.0:29400 (errno: 98 - Address already in use).
[E socket.cpp:462] [c10d] The server socket has failed to listen on any local network address.</code></pre><h3>解决：</h3><p>找出端口占用程序的pid，杀掉即可，我的显示端口号29400被占用<br>找到占用程序的pid</p><pre><code>lsof -i :29400</code></pre><pre><code>(chatglm) root@nlp:/data/chengligen/ChatGLM3-main/finetune_chatmodel_demo# lsof -i :29400
COMMAND   PID USER   FD   TYPE    DEVICE SIZE/OFF NODE NAME
torchrun 2653 root    3u  IPv6  98686958      0t0  TCP *:29400 (LISTEN)
torchrun 2653 root    6u  IPv6  98686964      0t0  TCP localhost:44330-&gt;localhost:29400 (ESTABLISHED)
torchrun 2653 root    7u  IPv6  98686967      0t0  TCP localhost:44332-&gt;localhost:29400 (ESTABLISHED)
torchrun 2653 root    8u  IPv6  98697430      0t0  TCP localhost:29400-&gt;localhost:44330 (ESTABLISHED)
torchrun 2653 root   11u  IPv6  98697431      0t0  TCP localhost:29400-&gt;localhost:44332 (ESTABLISHED)
torchrun 2855 root    3u  IPv6 100832571      0t0  TCP localhost:57180-&gt;localhost:29400 (ESTABLISHED)
torchrun 2855 root    4u  IPv6 100832574      0t0  TCP localhost:57182-&gt;localhost:29400 (ESTABLISHED)</code></pre><p>杀掉pid为2653的程序</p><pre><code>kill -9 2653</code></pre><p>在这个充满科技魔力的时代，自然语言处理（NLP）正如一颗璀璨的明星般照亮我们的数字世界。当我们涉足NLP的浩瀚宇宙，仿佛开启了一场语言的奇幻冒险。正如亚历克斯·康普顿所言：“语言是我们思想的工具，而NLP则是赋予语言新生命的魔法。”这篇博客将引领你走进NLP前沿，发现语言与技术的交汇点，探寻其中的无尽可能。不论你是刚刚踏入NLP的大门，还是这个领域的资深专家，我的博客都将为你提供有益的信息。一起探索语言的边界，迎接未知的挑战，让我们共同在NLP的海洋中畅游！期待与你一同成长，感谢你的关注和支持。欢迎任何人前来讨论问题。</p>
]]></content:encoded>
<slash:comments>0</slash:comments>
<comments>https://www.blog.os-o.cn/index.php/archives/15/#comments</comments>
<wfw:commentRss>https://www.blog.os-o.cn/index.php/feed/author/1/</wfw:commentRss>
</item>
<item>
<title>【大模型实战】使用 Langchain-Chatchat 进行本地部署的完整指南</title>
<link>https://www.blog.os-o.cn/index.php/archives/5/</link>
<guid>https://www.blog.os-o.cn/index.php/archives/5/</guid>
<pubDate>Mon, 22 Jan 2024 16:02:00 +0800</pubDate>
<dc:creator>chengligen</dc:creator>
<description><![CDATA[一、Langchain-Chatchat1、概述Langchain-Chatchat是一个基于ChatGLM大语言模型与Langchain应用框架实现，开源、可离线部署的检索增强生成(RAG)大...]]></description>
<content:encoded xml:lang="zh-CN"><![CDATA[
<h1>一、Langchain-Chatchat</h1><h2>1、概述</h2><p>Langchain-Chatchat是一个基于ChatGLM大语言模型与Langchain应用框架实现，开源、可离线部署的检索增强生成(RAG)大模型的本地知识库问答应用项目。<br><a href="https://github.com/chatchat-space/Langchain-Chatchat">GitHub：<a href="https://github.com/chatchat-space/Langchain-Chatchat">https://github.com/chatchat-space/Langchain-Chatchat</a></a></p><h2>2、实现原理</h2><p>本项目实现原理如下图所示，过程包括加载文件 -&gt; 读取文本 -&gt; 文本分割 -&gt; 文本向量化 -&gt; 问句向量化 -&gt; 在文本向量中匹配出与问句向量最相似的 top k个 -&gt; 匹配出的文本作为上下文和问题一起添加到 prompt中 -&gt; 提交给 LLM生成回答。<br><img src="https://blog.os-o.cn/usr/uploads/2024/01/3584925895.png" alt="2024-01-22T08:07:52.png" title="2024-01-22T08:07:52.png"><br>从文档处理角度来看，实现流程如下：<br><img src="https://blog.os-o.cn/usr/uploads/2024/01/2713513671.png" alt="2024-01-22T08:08:05.png" title="2024-01-22T08:08:05.png"></p><h1>二、开发环境准备</h1><h2>1、软件要求</h2><pre><code>Linux Ubuntu 22.04.5 kernel version 6.7
Python 版本: &gt;= 3.8(很不稳定), &lt; 3.11
CUDA 版本: &gt;= 12.1</code></pre><h2>2、硬件要求（根据模型参数多少确定）</h2><h3>ChatGLM2-6B & LLaMA-7B</h3><p>最低显存要求: 7GB<br>推荐显卡: RTX 3060, RTX 2060</p><h3>LLaMA-13B</h3><p>最低显存要求: 11GB<br>推荐显卡: RTX 2060 12GB, RTX 3060 12GB, RTX 3080, RTX A2000</p><h3>Qwen-14B-Chat</h3><p>最低显存要求: 13GB<br>推荐显卡: RTX 3090</p><h3>LLaMA-30B</h3><p>最低显存要求: 22GB<br>推荐显卡: RTX A5000, RTX 3090, RTX 4090, RTX 6000, Tesla V100, RTX Tesla P40</p><h3>LLaMA-65B</h3><p>最低显存要求: 40GB<br>推荐显卡: A100, A40, A6000</p><h3>注意</h3><p>若使用 int8 推理，则显存大致为 int4 推理要求的 1.5 倍<br>若使用 fp16 推理，则显存大致为 int4 推理要求的 2.5 倍<br>数据仅为估算，实际情况以 nvidia-smi 占用为准。<br>同时，Embedding 模型将会占用 1-2G 的显存，历史记录最多会占用数 G 显存，因此，需要多冗余一些显存。<br>内存最低要求: 内存要求至少应该比模型运行的显存大。</p><h1>三、部署</h1><h2>1、拉取仓库</h2><pre><code>git clone https://github.com/chatchat-space/Langchain-Chatchat.git</code></pre><p>若网络原因无法拉取，手动下载、解压即可</p><h2>2、进入目录</h2><pre><code>cd Langchain-Chatchat</code></pre><h2>3、创建虚拟环境</h2><pre><code>conda create -n chatchat python=3.10</code></pre><h2>4、激活使用虚拟环境</h2><pre><code>conda activate chatchat</code></pre><h2>5、安装全部依赖</h2><pre><code>pip install -r requirements.txt 
pip install -r requirements_api.txt
pip install -r requirements_webui.txt  </code></pre><p>默认依赖包括基本运行环境（FAISS向量库）。如果要使用 milvus/pg_vector 等向量库，请将 requirements.txt 中相应依赖取消注释再安装。</p><h2>6、初始化配置文件</h2><pre><code>python copy_config_example.py</code></pre><p>脚本将会将所有config目录下的配置文件样例复制一份到config目录下，方便开发者进行配置。 接着，开发者可以根据自己的需求，对配置文件进行修改。</p><p>basic_config.py：基础配置项：配置记录日志的格式和储存路径，通常不需要修改。</p><p>kb_config.py：数据库配置：配置分词器、知识库、向量数据库等信息</p><p>model_config.py：模型配置项：包含本地LLM模型、本地Embeddings模型、在线LLM模型API的相关配置</p><p>prompt_config.py：提示词配置项：提示词配置分为三个板块，分别对应三种聊天类型：基础的对话提示词、与知识库对话的提示词、与Agent对话的提示词。</p><p>server_config.py：服务和端口配置项：不需要进行大量的修改，仅需确保对应的端口打开，并不互相冲突即可。server_config.py中的配置优先于startup.py中的默认值，注意避免配置文件覆盖</p><h2>7、模型下载</h2><p>模型下载取决于自己的网络情况，这里需要提前下载THUDM/chatglm2-6b与BAAI/bge-large-zh到本地，然后在model_config.py中配置<br>1.若网络良好(全球畅通无阻)则完全不需要先下载模型，在执行过程中会自动下载相关模型。<br>2.如果网络存在问题，则可以事先下载好需要的模型，然后在model_config.py文件中配置，具体配置参考异常3中的处理办法<br>注意：<br>Windows环境下，会默认自动将该模型下载到C:\Users\Admin.cache\torch\sentence_transformers目录下，若下载失败，参考异常3中的处理办法</p><h2>8、初始化知识库</h2><p>第一次运行本项目，知识库尚未建立，或者配置文件中的知识库类型、嵌入模型发生变化，需要以下命令初始化或重建知识库：</p><pre><code>python init_database.py --recreate-vs</code></pre><p>如果已经有创建过知识库，可以先执行以下命令创建或更新数据库表：</p><pre><code>python init_database.py --create-tables</code></pre><p>第一次使用时，会自动下载BAAI/bge-large-zh模型，用于知识库的初始化构建</p><pre><code>(chatchat) root@master:~/Langchain-Chatchat# python init_database.py --recreate-vs
recreating all vector stores
2023-12-20 21:40:48,647 - faiss_cache.py[line:80] - INFO: loading vector store in 'samples/vector_store/bge-large-zh' from disk.
2023-12-20 21:40:48,999 - SentenceTransformer.py[line:66] - INFO: Load pretrained SentenceTransformer: /root/models/bge-large-zh
Batches: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:00&lt;00:00,  1.80it/s]
2023-12-20 21:40:51,466 - loader.py[line:54] - INFO: Loading faiss with AVX2 support.
2023-12-20 21:40:51,751 - loader.py[line:56] - INFO: Successfully loaded faiss with AVX2 support.
2023-12-20 21:40:51,761 - faiss_cache.py[line:80] - INFO: loading vector store in 'samples/vector_store/bge-large-zh' from disk.
Batches: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:00&lt;00:00, 72.05it/s]
2023-12-20 21:40:51,783 - utils.py[line:286] - INFO: RapidOCRLoader used for /root/onethingai-tmp/Langchain-Chatchat/knowledge_base/samples/content/llm/img/分布式训练技术原理-幕布图片-124076-270516.jpg
2023-12-20 21:40:51,784 - utils.py[line:286] - INFO: RapidOCRLoader used for /root/onethingai-tmp/Langchain-Chatchat/knowledge_base/samples/content/llm/img/分布式训练技术原理-幕布图片-20096-279847.jpg
2023-12-20 21:40:51,785 - utils.py[line:286] - INFO: RapidOCRLoader used for /root/onethingai-tmp/Langchain-Chatchat/knowledge_base/samples/content/llm/img/分布式训练技术原理-幕布图片-220157-552735.jpg
2023-12-20 21:40:51,785 - utils.py[line:286] - INFO: RapidOCRLoader used for /root/onethingai-tmp/Langchain-Chatchat/knowledge_base/samples/content/llm/img/分布式训练技术原理-幕布图片-36114-765327.jpg
2023-12-20 21:40:51,786 - utils.py[line:286] - INFO: RapidOCRLoader used



Batches: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 6/6 [00:01&lt;00:00,  3.43it/s]
正在将 samples//root/onethingai-tmp/Langchain-Chatchat/knowledge_base/samples/content/test_files/test.txt 添加到向量库，共包含59条文档████████████████████████████████████▋                               | 5/6 [00:01&lt;00:00,  3.05it/s]
Batches: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 2/2 [00:00&lt;00:00,  2.41it/s]
正在将 samples//root/onethingai-tmp/Langchain-Chatchat/knowledge_base/samples/content/llm/img/大模型推理优化策略-幕布图片-930255-616209.jpg 添加到向量库，共包含3条文档███████████████████████████████████| 2/2 [00:00&lt;00:00,  2.52it/s]
Batches: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:00&lt;00:00, 43.48it/s]
正在将 samples//root/onethingai-tmp/Langchain-Chatchat/knowledge_base/samples/content/llm/img/大模型推理优化策略-幕布图片-789705-122117.jpg 添加到向量库，共包含1条文档                                           | 0/1 [00:00&lt;?, ?it/s]
Batches: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:00&lt;00:00, 63.58it/s]
RapidOCRPDFLoader context page index: 7: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:48&lt;00:00,  6.11s/it]
文档切分示例：page_content='See discussions, stats, and author profiles for this publication at: https://www.researchgate.net/publication/372669736\nCreating Large Language Model Applications Utilizing LangChain: A Primer on\nDeveloping LLM Apps Fast\nArticle\xa0\xa0in\xa0\xa0International Conference on Applied Engineering and Natural Sciences · July 2023\nDOI: 10.59287/icaens.1127\nCITATIONS\n0\nREADS\n47\n2 authors:\nSome of the authors of this publication are also working on these related projects:\nTHALIA: Test Harness for the Assessment of Legacy Information Integration Approaches View project\nAnalysis of Feroresonance with Signal Processing Technique View project\nOguzhan Topsakal' metadata={'source': '/root/onethingai-tmp/Langchain-Chatchat/knowledge_base/samples/content/test_files/langchain.pdf'}
正在将 samples//root/onethingai-tmp/Langchain-Chatchat/knowledge_base/samples/content/test_files/langchain.pdf 添加到向量库，共包含52条文档
Batches: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 2/2 [00:00&lt;00:00,  3.70it/s]
2023-12-20 21:44:07,719 - faiss_cache.py[line:24] - INFO: 已将向量库 ('samples', 'bge-large-zh') 保存到磁盘
总计用时： 0:03:19.085059</code></pre><h2>9、启动项目</h2><h3>启动项目整个过程中，坑也比较多，参考异常2与异常3中的处理办法。</h3><p>一键启动脚本 startup.py， 一键启动所有 Fastchat 服务、API 服务、WebUI 服务</p><pre><code>python startup.py -a   </code></pre><p>启动时，如果没用在model_config.py中配置配置模型信息，则会自动模型下载THUDM/chatglm3-6b到本地使用</p><pre><code>==============================Langchain-Chatchat Configuration==============================
操作系统：Linux-6.1.56-1.2.3-x86_64-with-glibc2.35.
python版本：3.10.13 | packaged by conda-forge | (main, Oct 26 2023, 18:07:37) [GCC 12.3.0]
项目版本：v0.2.8
langchain版本：0.0.344. fastchat版本：0.2.34


当前使用的分词器：ChineseRecursiveTextSplitter
当前启动的LLM模型：['chatglm3-6b', 'zhipu-api', 'openai-api'] @ cuda
{'device': 'cuda',
 'host': '0.0.0.0',
 'infer_turbo': False,
 'model_path': '/root/models/chatglm3-6b',
 'model_path_exists': True,
 'port': 20002}
{'api_key': '',
 'device': 'auto',
 'host': '0.0.0.0',
 'infer_turbo': False,
 'online_api': True,
 'port': 21001,
 'provider': 'ChatGLMWorker',
 'version': 'chatglm_turbo',
 'worker_class': &lt;class 'server.model_workers.zhipu.ChatGLMWorker'&gt;}
{'api_base_url': 'https://api.openai.com/v1',
 'api_key': '',
 'device': 'auto',
 'host': '0.0.0.0',
 'infer_turbo': False,
 'model_name': 'gpt-3.5-turbo',
 'online_api': True,
 'openai_proxy': '',
 'port': 20002}
当前Embbedings模型： bge-large-zh @ cuda
==============================Langchain-Chatchat Configuration==============================


2023-12-20 21:44:16,058 - startup.py[line:650] - INFO: 正在启动服务：
2023-12-20 21:44:16,058 - startup.py[line:651] - INFO: 如需查看 llm_api 日志，请前往 /root/Langchain-Chatchat/logs
2023-12-20 21:44:20 | INFO | model_worker | Register to controller
2023-12-20 21:44:20 | ERROR | stderr | INFO:     Started server process [8455]
2023-12-20 21:44:20 | ERROR | stderr | INFO:     Waiting for application startup.
2023-12-20 21:44:20 | ERROR | stderr | INFO:     Application startup complete.
2023-12-20 21:44:20 | ERROR | stderr | INFO:     Uvicorn running on http://0.0.0.0:20000 (Press CTRL+C to quit)
2023-12-20 21:44:21 | INFO | model_worker | Loading the model ['chatglm3-6b'] on worker 6c239f49 ...
Loading checkpoint shards:   0%|                                                                                                                                                                                  | 0/7 [00:00&lt;?, ?it/s]
Loading checkpoint shards:  14%|████████████████████████▎                                                                                                                                                 | 1/7 [00:02&lt;00:14,  2.42s/it]
Loading checkpoint shards:  29%|████████████████████████████████████████████████▌                                                                                                                         | 2/7 [00:04&lt;00:12,  2.42s/it]
Loading checkpoint shards:  43%|████████████████████████████████████████████████████████████████████████▊                                                                                                 | 3/7 [00:06&lt;00:07,  1.98s/it]
Loading checkpoint shards:  57%|█████████████████████████████████████████████████████████████████████████████████████████████████▏                                                                        | 4/7 [00:08&lt;00:05,  1.91s/it]
Loading checkpoint shards:  71%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▍                                                | 5/7 [00:10&lt;00:04,  2.09s/it]
Loading checkpoint shards:  86%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▋                        | 6/7 [00:12&lt;00:02,  2.19s/it]
Loading checkpoint shards: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 7/7 [00:14&lt;00:00,  1.95s/it]
Loading checkpoint shards: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 7/7 [00:14&lt;00:00,  2.05s/it]
2023-12-20 21:44:35 | ERROR | stderr | 
2023-12-20 21:44:38 | INFO | model_worker | Register to controller
INFO:     Started server process [9192]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
ERROR:    [Errno 98] error while attempting to bind on address ('0.0.0.0', 7861): address already in use
INFO:     Waiting for application shutdown.
INFO:     Application shutdown complete.


==============================Langchain-Chatchat Configuration==============================
操作系统：Linux-6.1.56-1.2.3-x86_64-with-glibc2.35.
python版本：3.10.13 | packaged by conda-forge | (main, Oct 26 2023, 18:07:37) [GCC 12.3.0]
项目版本：v0.2.8
langchain版本：0.0.344. fastchat版本：0.2.34


当前使用的分词器：ChineseRecursiveTextSplitter
当前启动的LLM模型：['chatglm3-6b', 'zhipu-api', 'openai-api'] @ cuda
{'device': 'cuda',
 'host': '0.0.0.0',
 'infer_turbo': False,
 'model_path': '/root/models/chatglm3-6b',
 'model_path_exists': True,
 'port': 20002}
{'api_key': '',
 'device': 'auto',
 'host': '0.0.0.0',
 'infer_turbo': False,
 'online_api': True,
 'port': 21001,
 'provider': 'ChatGLMWorker',
 'version': 'chatglm_turbo',
 'worker_class': &lt;class 'server.model_workers.zhipu.ChatGLMWorker'&gt;}
{'api_base_url': 'https://api.openai.com/v1',
 'api_key': '',
 'device': 'auto',
 'host': '0.0.0.0',
 'infer_turbo': False,
 'model_name': 'gpt-3.5-turbo',
 'online_api': True,
 'openai_proxy': '',
 'port': 20002}
当前Embbedings模型： bge-large-zh @ cuda


服务端运行信息：
    OpenAI API Server: http://127.0.0.1:20000/v1
    Chatchat  API  Server: http://127.0.0.1:7861
    Chatchat WEBUI Server: http://0.0.0.0:8501
==============================Langchain-Chatchat Configuration==============================


Collecting usage statistics. To deactivate, set browser.gatherUsageStats to False.


  You can now view your Streamlit app in your browser.

  URL: http://0.0.0.0:8501</code></pre><h2>10、访问Web UI</h2><p>Web UI地址：<a href="http://127.0.0.1:8501">http://127.0.0.1:8501</a></p><h2>11、API服务</h2><p>不是一键启动，则可以单独启动API服务：</p><pre><code>python server/api.py</code></pre><p>访问：<a href="http://0.0.0.0:7861/docs">http://0.0.0.0:7861/docs</a></p><h2>12、Web UI服务</h2><p>不是一键启动，则可以单独启动Web UI服务：</p><pre><code>streamlit run webui.py</code></pre><p>访问：<a href="http://localhost:8501/">http://localhost:8501/</a></p><h1>四、异常集合</h1><h2>1、异常1</h2><h3>场景：</h3><p>初始化配置文件</p><pre><code>python init_database.py --recreate-vs</code></pre><h3>问题：</h3><pre><code>cannot import name 'Doc' from 'typing_extensions'</code></pre><h3>解决：</h3><p>因为安装的typing_extensions版本不正确，需要重新安装</p><pre><code>pip install typing_extensions==4.8.0</code></pre><h2>2、异常2</h2><h3>场景：</h3><p>启动项目</p><pre><code>python startup.py -a</code></pre><h3>问题：</h3><pre><code>OMP: Error #15: Initializing libiomp5md.dll, but found libiomp5md.dll already initialized.
OMP: Hint This means that multiple copies of the OpenMP runtime have been linked into the program. That is dangerous, since it can degrade performance or cause incorrect results. The best thing to do is to ensure that only a singl
e OpenMP runtime is linked into the process, e.g. by avoiding static linking of the OpenMP runtime in any library. As an unsafe, unsupported, undocumented workaround you can set the environment variable KMP_DUPLICATE_LIB_OK=TRUE t
o allow the program to continue to execute, but that may cause crashes or silently produce incorrect results. For more information, please see http://www.intel.com/software/products/support/.</code></pre><h3>解决1：</h3><p>这里使用Anaconda创建虚拟环境，其中有ibiomp5md.dll文件，重命名以备份该文件，如：libiomp5md.dll.back</p><h3>解决2：</h3><p>在startup.py文件上方设置环境变量，保证前后顺序</p><pre><code>import os
os.environ['KMP_DUPLICATE_LIB_OK']='True'</code></pre><h2>3、异常3</h2><h3>场景：</h3><p>启动项目过程中</p><pre><code>python startup.py -a</code></pre><h3>问题：</h3><pre><code>| OSError: We couldn't connect to 'https://huggingface.co' to load this file, couldn't find it in the cached files and it looks like THUDM/chatglm3-6b is not the path to a directory containing 
a file named config.json.</code></pre><h3>解决：</h3><p>默认使用的LLM 模型 THUDM/ChatGLM3-6B 与 Embedding 模型BAAI/bge-large-zh，会远程连接模型网站。这里使用魔法也不得行，不知为啥，具体模型网站能访问的。</p><h3>下载LLM 模型THUDM/ChatGLM3-6B与Embedding模型BAAI/bge-large-zh</h3><p>1.访问<a href="https://huggingface.co/BAAI/bge-large-zh">https://huggingface.co/BAAI/bge-large-zh</a>下载<br><img src="https://blog.os-o.cn/usr/uploads/2024/01/2524456219.png" alt="2024-01-22T08:50:26.png" title="2024-01-22T08:50:26.png"><br>2.访问<a href="https://huggingface.co/THUDM/chatglm3-6b">https://huggingface.co/THUDM/chatglm3-6b</a>下载<br><img src="https://blog.os-o.cn/usr/uploads/2024/01/3612958029.png" alt="2024-01-22T08:50:55.png" title="2024-01-22T08:50:55.png"><br>然后修改configs/model_config.py文件，指定模型存放位置与使用模型名称，需保证存放模型目录下的模型文件名与model_config.py文件使用的名称一致。</p><pre><code># 统一存放模型位置
MODEL_ROOT_PATH = &quot;../../../models&quot;

# 选用的 Embedding 名称
EMBEDDING_MODEL = &quot;bge-large-zh&quot;

# 要运行的LLM名称，可以包括本地模型和在线模型
LLM_MODELS = [&quot;chatglm3-6b&quot;, &quot;zhipu-api&quot;, &quot;openai-api&quot;]

MODEL_PATH = {
    &quot;embed_model&quot;: {
        &quot;ernie-tiny&quot;: &quot;nghuyong/ernie-3.0-nano-zh&quot;,
        &quot;ernie-base&quot;: &quot;nghuyong/ernie-3.0-base-zh&quot;,
        &quot;text2vec-base&quot;: &quot;shibing624/text2vec-base-chinese&quot;,
        &quot;text2vec&quot;: &quot;GanymedeNil/text2vec-large-chinese&quot;,
        &quot;text2vec-paraphrase&quot;: &quot;shibing624/text2vec-base-chinese-paraphrase&quot;,
        &quot;text2vec-sentence&quot;: &quot;shibing624/text2vec-base-chinese-sentence&quot;,
        &quot;text2vec-multilingual&quot;: &quot;shibing624/text2vec-base-multilingual&quot;,
        &quot;text2vec-bge-large-chinese&quot;: &quot;shibing624/text2vec-bge-large-chinese&quot;,
        &quot;m3e-small&quot;: &quot;moka-ai/m3e-small&quot;,
        &quot;m3e-base&quot;: &quot;moka-ai/m3e-base&quot;,
        &quot;m3e-large&quot;: &quot;moka-ai/m3e-large&quot;,
        &quot;bge-small-zh&quot;: &quot;BAAI/bge-small-zh&quot;,
        &quot;bge-base-zh&quot;: &quot;BAAI/bge-base-zh&quot;,
         # &quot;bge-large-zh&quot;: &quot;BAAI/bge-large-zh&quot;,
         # 如果模型目录名称和 MODEL_PATH 中的 key 或 value 相同，程序会自动检测加载，无需修改 MODEL_PATH 中的路径。
        &quot;bge-large-zh&quot;: &quot;bge-large-zh&quot;,
        &quot;bge-large-zh-noinstruct&quot;: &quot;BAAI/bge-large-zh-noinstruct&quot;,
        &quot;bge-base-zh-v1.5&quot;: &quot;BAAI/bge-base-zh-v1.5&quot;,
        &quot;bge-large-zh-v1.5&quot;: &quot;BAAI/bge-large-zh-v1.5&quot;,
        &quot;piccolo-base-zh&quot;: &quot;sensenova/piccolo-base-zh&quot;,
        &quot;piccolo-large-zh&quot;: &quot;sensenova/piccolo-large-zh&quot;,
        &quot;nlp_gte_sentence-embedding_chinese-large&quot;: &quot;damo/nlp_gte_sentence-embedding_chinese-large&quot;,
        &quot;text-embedding-ada-002&quot;: &quot;your OPENAI_API_KEY&quot;,
    },

    &quot;llm_model&quot;: {
        # 以下部分模型并未完全测试，仅根据fastchat和vllm模型的模型列表推定支持 
        # &quot;chatglm2-6b&quot;: &quot;THUDM/chatglm2-6b&quot;,
        # 如果模型目录名称和 MODEL_PATH 中的 key 或 value 相同，程序会自动检测加载，无需修改 MODEL_PATH 中的路径。
        &quot;chatglm2-6b&quot;: &quot;chatglm2-6b&quot;,
        &quot;chatglm2-6b-32k&quot;: &quot;THUDM/chatglm2-6b-32k&quot;,

        &quot;chatglm3-6b&quot;: &quot;THUDM/chatglm3-6b&quot;,
        &quot;chatglm3-6b-32k&quot;: &quot;THUDM/chatglm3-6b-32k&quot;,
        &quot;chatglm3-6b-base&quot;: &quot;THUDM/chatglm3-6b-base&quot;,
    },</code></pre><p>在这个充满科技魔力的时代，自然语言处理（NLP）正如一颗璀璨的明星般照亮我们的数字世界。当我们涉足NLP的浩瀚宇宙，仿佛开启了一场语言的奇幻冒险。正如亚历克斯·康普顿所言：“语言是我们思想的工具，而NLP则是赋予语言新生命的魔法。”这篇博客将引领你走进NLP前沿，发现语言与技术的交汇点，探寻其中的无尽可能。不论你是刚刚踏入NLP的大门，还是这个领域的资深专家，我的博客都将为你提供有益的信息。一起探索语言的边界，迎接未知的挑战，让我们共同在NLP的海洋中畅游！期待与你一同成长，感谢你的关注和支持。欢迎任何人前来讨论问题。</p>
]]></content:encoded>
<slash:comments>0</slash:comments>
<comments>https://www.blog.os-o.cn/index.php/archives/5/#comments</comments>
<wfw:commentRss>https://www.blog.os-o.cn/index.php/feed/author/1/</wfw:commentRss>
</item>
<item>
<title>欢迎使用 Typecho</title>
<link>https://www.blog.os-o.cn/index.php/archives/1/</link>
<guid>https://www.blog.os-o.cn/index.php/archives/1/</guid>
<pubDate>Sun, 21 Jan 2024 20:10:09 +0800</pubDate>
<dc:creator>chengligen</dc:creator>
<description><![CDATA[如果您看到这篇文章,表示您的 blog 已经安装成功.]]></description>
<content:encoded xml:lang="zh-CN"><![CDATA[
<p>如果您看到这篇文章,表示您的 blog 已经安装成功.</p>
]]></content:encoded>
<slash:comments>1</slash:comments>
<comments>https://www.blog.os-o.cn/index.php/archives/1/#comments</comments>
<wfw:commentRss>https://www.blog.os-o.cn/index.php/feed/author/1/</wfw:commentRss>
</item>
</channel>
</rss>