【大模型理论篇】大模型周边自然语言处理技术(NLP)原理分析及数学推导(Word2Vec、TextCNN、Gated TextCNN、FastText)

1. 背景介绍

        进入到大模型时代,似乎宣告了与过去自然语言处理技术的结束,但其实这两者并不矛盾。大模型时代,原有的自然语言处理技术,依然可以在大模型的诸多场景中应用,特别是对数据的预处理阶段。本篇主要关注TextCNN、FastText和Word2Vec等低成本的自然语言处理技术,如何在大模型时代发挥其余热。本文将重点分析这些周边技术的算法原理、数学分析,温故而知新。

2. 自然语言处理周边算法

2.1 Word2Vec

2.1.1 算法简介

        Word2Vec是一种词嵌入技术,通过将词表示成向量的方式来捕捉词与词之间的语义关系。在Word2Vec出现之前,常用的还是以one hot编码的词向量表示为主,但这种one hot模式存在诸多的不足。

        One-hot 表示法的不足:

  • 维度过高

    • 对于一个大型词汇表(例如数十万甚至上百万个单词),One-hot 向量的维度会非常高,导致存储和计算的开销极大。在大规模数据集上,这种表示方法会耗费大量内存和计算资源。
  • 无法捕捉词与词之间的语义关系

    • One-hot 向量之间是正交的(余弦相似度为 0),不能捕捉任何词与词之间的相似性或语义关系。例如,“猫”和“狗”在语义上是相似的,但在 One-hot 表示法中它们的向量完全不同,无法反映这种相似性。One-hot 表示不能表达词的多义性或上下文含义。
  • 稀疏性高

    • One-hot 向量是非常稀疏的,因为在一个长向量中,只有一个位置是 1,其他位置都是 0。这样的稀疏向量在存储和处理上效率不高。
  • 无法泛化

    • 由于每个词的表示是独立的,无法将词汇表之外的词有效地引入模型中。这意味着模型对未见过的词(Out-of-Vocabulary, OOV)没有泛化能力。无法利用现有词的表示来推断新词或未见过的词的语义信息。

        由于one hot表示法存在上述诸多的问题,因此Mikolov在2013年的论文【1】引入了Word2Vec技术。Word2Vec 通过将单词表示为低维、密集的实数向量,有效地克服了 One-hot 表示的局限性。这种方法能够捕捉单词之间的语义关系和上下文信息,并在各种自然语言处理任务中表现出较高的性能。

        Word2Vec有两种训练方法:CBOW(Continuous Bag of Words)和Skip-gram。

2.1.2  CBOW及skip gram算法原理分析    

        接下来,我们分别针对这两种训练方法,详细描述其迭代的数学过程【2】。

2.1.2.1 CBOW(Continuous Bag of Words)

        CBOW(连续词袋模型)是通过根据上下文词来预测目标词(中心词),从而学习词向量。在 CBOW 模型中,给定一个中心词(目标词)w_t的上下文词(w_{t-k}, \ldots, w_{t-1}, w_{t+1}, \ldots, w_{t+k}),CBOW 模型的目标是预测该中心词w_t。其中,窗口大小为 k,上下文词的数量为 2k。

        首先我们来考虑一个简单的场景,即单词上下文,也就是假设每个上下文中只考虑一个词,这意味着该模型将根据一个上下文词来预测一个目标词。在该场景设置中,词汇表的大小为 V,隐藏层的大小为 N。相邻层的单元是全连接的。输入是一个独热编码向量,这意味着对于给定的输入上下文词,只有 V 个单元中的一个(即 {x_1, \cdots, x_V}​)为1,其他所有单元都为0。

        模型的输入为上下文词的词向量。输入层和输出层之间的权重可以用一个V \times N 的矩阵 W来表示。矩阵 W 的每一行对应输入层的某个词的 N 维向量表示\mathbf{v}_w。形式上,W的第 i 行是 \mathbf{v}_w^T。给定一个上下文(即一个词),假设x_k = 1 且x_{k'} = 0(其中k' \neq k),我们有:

\mathbf{h} = W^T \mathbf{x} = W^T_{(k, \cdot)} := \mathbf{v}_w^T

        该公式本质上是将矩阵 W 的第 k 行复制到\mathbf{h}。其中,\mathbf{v}_{w_I} 是输入词 w_I 的向量表示。这意味着隐藏层的连接(激活)函数是线性的(即直接将输入的加权和传递到下一层)。从隐藏层到输出层,有另一个权重矩阵W' = \{w'_{ij}\},这是一个N \times V的矩阵。利用这些权重,可以为词汇表中的每个词计算一个分数 u_ju_j = v'^T_{w_j}h。 其中,\mathbf{v}'_{w_j}是矩阵 W′ 的第 j 列。然后,可以使用 softmax(对数线性分类模型)来获得词的后验分布,它是一个多项分布:

p(w_j \mid w_I) = y_j = \frac{\exp(u_j)}{\sum_{j'=1}^{V} \exp(u_{j'})}

其中y_j是输出层中第 j 个单元的输出。将上述公式进行整理后得到:

p(w_j \mid w_I) = \frac{\exp \left( \mathbf{v}'^T_{w_j} \mathbf{v}_{w_I} \right)}{\sum_{j'=1}^{V} \exp \left( \mathbf{v}'^T_{w_{j'}} \mathbf{v}_{w_I} \right)}

其中,\mathbf{v}_w 和 \mathbf{v}'_w​ 是词 w 的两种表示。\mathbf{v}_w 来自矩阵 W 的行,即输入到隐藏层的权重矩阵,而 \mathbf{v}'_w 来自矩阵 W′ 的列,即隐藏层到输出层的权重矩阵。将\mathbf{v}_w称为词 w 的“输入向量”,而\mathbf{v}'_w称为词 w 的“输出向量”。

第一步:隐藏层到输出层的权重更新公式

        接下来推导模型的权重更新公式。训练目标(对于一个训练样本)是最大化公式,即在给定输入上下文词 w_l 的情况下,观测到实际输出词 w_O 的条件概率(其在输出层的索引为j^*):

\max p(w_O \mid w_I) = \max y_{j^*}

= \max \log y_{j^*}

= u_{j^*} - \log \sum_{j' = 1}^{V} \exp(u_{j'}) := -E

其中 E = -\log p(w_O \mid w_I)是损失函数(希望最小化 E),j^* 是输出层中实际输出词的索引。这个损失函数可以理解为两个概率分布之间交叉熵度量的一个特殊情况。现在来推导隐藏层到输出层之间的权重更新公式。对第 j 个单元的净输入u_j求偏导数,我们得到:

\frac{\partial E}{\partial u_j} = y_j - t_j := e_j

其中t_j = 1(j = j^*),即当第 j 个单元是实际输出词时,t_j才为 1,否则 t_j = 0。这个导数实际上是输出层的预测误差e_j

对这里u_j的计算可以展开说明一下:

步骤 1: 计算损失函数关于u_j的偏导数

损失函数 E 是两个项的和,所以我们分别对这两个项求导数。

首先,计算损失函数E关于u_j的偏导数:

\frac{\partial E}{\partial u_j} = - \frac{\partial}{\partial u_j} \left( u_{j^*} - \log \sum_{j' = 1}^{V} \exp(u_{j'}) \right)

由于第一个项 u_{j^*} 只涉及到j = j^*,我们有:

\frac{\partial u_{j^*}}{\partial u_j} = \begin{cases} 1, & \text{if } j = j^*, \\ 0, & \text{otherwise}. \end{cases}

因此,对第一个项求导得到:

\frac{\partial (-u_{j^*})}{\partial u_j} = -t_j = -1 \text{ if } j = j^*, \text{ otherwise } 0

步骤 2: 计算第二项的导数

第二项是关于\log \sum_{j'=1}^V \exp(u_{j'})的偏导数。使用链式法则来求导:

\frac{\partial}{\partial u_j} \left( \log \sum_{j' = 1}^{V} \exp(u_{j'}) \right) = \frac{1}{\sum_{j' = 1}^{V} \exp(u_{j'})} \cdot \frac{\partial}{\partial u_j} \left( \sum_{j' = 1}^{V} \exp(u_{j'}) \right)

计算里面的求和项的导数,得到:

\frac{\partial}{\partial u_j} \left( \sum_{j' = 1}^{V} \exp(u_{j'}) \right) = \exp(u_j)

因此,第二项的导数为:

\frac{\partial}{\partial u_j} \left( \log \sum_{j' = 1}^{V} \exp(u_{j'}) \right) = \frac{\exp(u_j)}{\sum_{j' = 1}^{V} \exp(u_{j'})} = y_j

步骤 3: 合并导数项

结合这两个部分的导数得到:

\frac{\partial E}{\partial u_j} = -t_j + y_j = y_j - t_j

因此损失函数关于每个输出单元的净输入u_j的偏导数等于该单元的预测输出y_j和目标输出t_j之间的差值(即预测误差e_j)。

        接下来对w'_{ij}求偏导数,得到隐藏层到输出层权重的梯度:

\frac{\partial E}{\partial w'_{ij}} = \frac{\partial E}{\partial u_j} \cdot \frac{\partial u_j}{\partial w'_{ij}} = e_j \cdot h_i

        因此,使用随机梯度下降法,,可以得到隐藏层到输出层权重的更新公式:

w'_{ij} (\text{new}) = w'_{ij} (\text{old}) - \eta \cdot e_j \cdot h_i

或者:

\mathbf{v}'_{w_j} (\text{new}) = \mathbf{v}'_{w_j} (\text{old}) - \eta \cdot e_j \cdot \mathbf{h}, \text{ for } j = 1, 2, \ldots, V

其中\eta > 0 是学习率,e_j = y_j - t_jh_i是隐藏层中的第 i 个单元;\mathbf{v}'_{w_j} 是词w_j的输出向量。这个更新公式表明必须遍历词汇表中的每个可能的词,检查其输出概率 y_j,并将其与期望输出 t_j(0 或 1)进行比较。如果 y_j > t_j(“高估”),那么从\mathbf{v}'_{w_j}中减去隐藏向量\mathbf{h}(即\mathbf{v}_{w_I})的一部分,从而使\mathbf{v}'_{w_j}远离\mathbf{v}_{w_I};如果y_j < t_j (“低估”),这只在t_j = 1时成立,即w_j = w_O),将一些 \mathbf{h} 加到 \mathbf{v}'_{w_O}中,使\mathbf{v}'_{w_O} 更接近\mathbf{v}_{w_I}。如果y_j非常接近t_j,那么根据更新公式,权重的变化将很小。

第二步:输入→隐藏层权重的更新公式

        在得到了 W'(隐藏层→输出层权重)的更新公式后,现在来推导 W(输入层→隐藏层权重)的更新公式。首先,对隐藏层输出h_i求损失函数 E 的导数:

\frac{\partial E}{\partial h_i} = \sum_{j=1}^{V} \frac{\partial E}{\partial u_j} \cdot \frac{\partial u_j}{\partial h_i} = \sum_{j=1}^{V} e_j \cdot w'_{ij} := E_{H_i}

其中,h_i是隐藏层第 i 个单元的输出,u_j 定义为输出层第 j 个单元的净输入,e_j = y_j - t_j 是输出层中第 j 个单词的预测误差。向量 EH 是所有词汇表中词的输出向量的加权和,其中权重为它们的预测误差。

        接下来需要对 W 求导数。首先,回顾一下隐藏层对来自输入层的值执行线性计算。

h_i = \sum_{k=1}^{V} x_k \cdot w_{ki}

        现在对 W 的每个元素求导,得到:

\frac{\partial E}{\partial w_{ki}} = \frac{\partial E}{\partial h_i} \cdot \frac{\partial h_i}{\partial w_{ki}} = E_{H_i} \cdot x_k

        这里,这相当于 x 和 EH 的张量积,即:

\frac{\partial E}{\partial W} = x \otimes EH = x EH^T

        从中得到一个V \times N矩阵。因为 x 中只有一个分量是非零的(one-hot形式),所以\frac{\partial E}{\partial W}只有一行是非零的,该行的值为EH^{T},一个 N-维向量。因此,W 的更新公式为:

v_{w_I}^{(\text{new})} = v_{w_I}^{(\text{old})} - \eta EH^T

        其中,v_{w_I}是 W 的一行,即唯一的上下文单词的“输入向量”,也是 W 中唯一一个非零的导数行。所有其他行的导数为零,因此在这一轮迭代后将保持不变。

        从直观上看,向量 EH是词汇表中所有单词的输出向量的加权和,权重为它们的预测误差e_j = y_j - t_j。可以理解为将词汇表中每个输出向量的一部分加到上下文单词的输入向量上。如果在输出层中,单词w_j作为输出单词的概率被高估了(y_j > t_j),那么上下文单词 w_I的输入向量将趋向于远离 w_j 的输出向量;反之,如果 w_j作为输出单词的概率被低估了(y_j < t_j),那么输入向量 w_I 将趋向于靠近w_j的输出向量;如果 w_j 的概率被准确预测,那么它对上下文单词输入向量的移动几乎没有影响。上下文单词输入向量的移动由词汇表中所有向量的预测误差决定;预测误差越大,某个词对上下文单词输入向量的移动影响就越大。

        通过迭代地更新模型参数,从训练语料库生成的上下文-目标单词对,向量的影响会逐渐累积。一个词 w 的输出向量被其共同出现邻居的输入向量来回“拉扯”。同样,输入向量也可以看作被许多输出向量拉动。这种解释让我们联想到重力或力导向图布局。每个虚拟弦的平衡长度与相关词对之间的共现强度以及学习率有关。经过多次迭代,输入和输出向量的相对位置将最终稳定下来。

       接下来让我们回到多词上下文的场景讨论:

         当计算隐藏层输出时,CBOW模型不是直接复制输入上下文词的输入向量,而是取输入上下文词向量的平均值,并将输入到隐藏层的权重矩阵与平均向量的乘积作为输出。

h = \frac{1}{C} W^T (x_1 + x_2 + \cdots + x_C)

= \frac{1}{C} (v_{w_1} + v_{w_2} + \cdots + v_{w_C})^T

        其中,C 是上下文中的词数,w_1, \cdots, w_C是上下文中的词,v_w 是词 w 的输入向量。损失函数为:

E = - \log p(w_O | w_{I,1}, \cdots, w_{I,C})

= - u_{j^*} + \log \sum_{j'=1}^V \exp(u_{j'})

= - (v'_{w_O})^T \cdot h + \log \sum_{j'=1}^V \exp((v'_{w_j})^T \cdot h)

        这个损失函数与刚才的单词上下文模型的目标相同,只是h的定义不同,使用均值代替。

        对于隐藏层到输出层权重的更新方程,与单词上下文模型公式相同。将其应用到每个训练实例的隐藏层到输出层权重矩阵的每个元素。

v'_{w_j}(\text{new}) = v'_{w_j}(\text{old}) - \eta \cdot e_j \cdot h \quad \text{for } j = 1, 2, \cdots, V

        对于输入层到隐藏层权重的更新方程,需要对上下文中的每个词 w_{I,c} 应用以下方程:

v_{w_{I,c}}(\text{new}) = v_{w_{I,c}}(\text{old}) - \frac{1}{C} \cdot \eta \cdot EH^T \quad \text{for } c = 1, 2, \cdots, C

        其中,v_{w_{I,c}} 是输入上下文中第 c 个词的输入向量;\eta 是正的学习率;EH及其更新方程类似前述。

2.1.2.2 计算加速方法(Negative Sampling以及Hierarchical Softmax)

注意:显然上述的推导,是一种最朴素的方式,为了帮助理解参数更新的原理和过程。但因为计算复杂度过高,因此,实际上作者提出了采用负采样(Negative Sampling)以及分层Softmax(Hierarchical Softmax) 的方法,来优化目标函数。

(1)负采样(Negative Sampling)

        负采样的思想相对简单:为了处理每次迭代需要更新过多输出向量的问题,选择只更新其中的一部分。输出词(即真实标签或正样本)应该包含在我们的样本中并被更新,而我们需要从中采样一些词作为负样本(因此称为“负采样”)。采样过程需要一个概率分布,并且这个分布可以任意选择。称这个分布为噪声分布,记作P_n(w)

        在 word2vec 论文中给出简化的训练目标,可以产生高质量的词嵌入,而不是使用一种生成明确后验多项分布的负采样形式:

E = - \log \sigma(v_0^{w_O} \cdot h) - \sum_{w_j \in W_{\text{neg}}} \log \sigma(-v_0^{w_j} \cdot h) \quad

其中 w_O​ 是输出词(即正样本),v_0^{w_O}是它的输出向量;h 是隐藏层的输出值:在 CBOW 模型中,h = \frac{1}{C} \sum_{c=1}^{C} v_{w_c};在 skip-gram 模型中,h = v_{w_I}W_{\text{neg}} = \{ w_j \mid j = 1, \ldots, K \}是根据 P_n(w)采样的词集,即负样本。

为了得到负采样下的词向量更新方程,我们首先对输出单元 w_j的净输入 v_0^{w_j} \cdot h 计算 E 的导数:

\frac{\partial E}{\partial (v_0^{w_j} \cdot h)} = \begin{cases} \sigma(v_0^{w_j} \cdot h) - 1 & \text{if } w_j = w_O \\ \sigma(v_0^{w_j} \cdot h) & \text{if } w_j \in W_{\text{neg}} \end{cases}

= \sigma(v_0^{w_j} \cdot h) - t_j \quad

        其中t_j 是词w_j的“标签”。当w_j是正样本时,t = 1;否则 t = 0。

        接下来,对词w_j的输出向量 v_0^{w_j}计算 E 的导数:

\frac{\partial E}{\partial v_0^{w_j}} = \frac{\partial E}{\partial (v_0^{w_j} \cdot h)} \cdot \frac{\partial (v_0^{w_j} \cdot h)}{\partial v_0^{w_j}} = \left( \sigma(v_0^{w_j} \cdot h) - t_j \right) h \quad

        因此输出向量的更新方程:

v_0^{w_j} (\text{new}) = v_0^{w_j} (\text{old}) - \eta \left( \sigma(v_0^{w_j} \cdot h) - t_j \right) h \quad

        这个方程只需要应用于 w_j \in \{w_O\} \cup W_{\text{neg}}中的词,而不是词汇表中的每一个词。因此每次迭代可以节省大量计算工作量。这个方程可以用于 CBOW 和 skip-gram 模型。

        要将误差反向传播到隐藏层,从而更新词的输入向量,需要对隐藏层的输出 h 计算 E 的导数:

\frac{\partial E}{\partial h} = \sum_{w_j \in \{w_O\} \cup W_{\text{neg}}} \frac{\partial E}{\partial (v_0^{w_j} \cdot h)} \cdot \frac{\partial (v_0^{w_j} \cdot h)}{\partial h} = \sum_{w_j \in \{w_O\} \cup W_{\text{neg}}} \left( \sigma(v_0^{w_j} \cdot h) - t_j \right) v_0^{w_j} \quad

= EH \quad

        将 EH 代入上述CBOW推导的公式就可以得到 CBOW 模型输入向量的更新方程。对于 skip-gram 模型,需要为 skip-gram 上下文中的每个词计算 EH 值,以获得输入向量的更新方程。

(2)Hierarchical Softmax

        Hierarchical Softmax 是一种加速大型词汇表的计算方法,在预测下一个词时需要计算一个词汇表中所有单词的概率。由于标准的 Softmax 操作需要计算整个词汇表中的每个单词的得分,这在大型数据集下会非常耗时,Hierarchical Softmax 就成为了一种有效的优化方法。

工作原理

Hierarchical Softmax 使用了一种基于二叉树的数据结构,将词汇表中的所有词组织成一个 Huffman 树。在这种树结构中:

  1. 树结构表示词汇表: 每个词汇表中的词都对应叶子节点,而中间节点用于引导寻找这些叶子节点。

  2. 路径概率计算: 对于每个词来说,通过计算从根节点到叶子节点的路径上各个节点的条件概率,可以得到该词的概率。每个内部节点都包含一个二元分类器,这个分类器用于预测从该节点向下是左走还是右走。

  3. 二元决策简化计算: 通过这种分层结构,将词汇表概率分解为一系列的二元决策。这样一来,计算目标词的概率就只需要沿着树路径上的每个节点计算一次二元分类的概率,时间复杂度为 O(\log V),其中 V 是词汇表大小。

  4. Huffman 树优化路径: 通常情况下,这个二叉树会使用 Huffman 编码来构建,以便将高频词放在离根节点较近的地方,减少高频词的平均路径长度,从而进一步加快计算速度。

数学公式

设 w 是目标词汇,p(w) 是其概率。构建一个二叉树,其中每个词作为叶子节点,每个非叶子节点代表一个二元分类器。为了计算 p(w),需要计算从根节点到叶子节点的路径上每个二元决策的概率。假设从根节点到词 w 的路径用 (n_0, n_1, \dots, n_L = w)表示,路径长度为 L,每个节点 n_i的二元决策概率为\sigma(x_i)

p(w) = \prod_{i=1}^{L} \sigma(x_i)^{[n_i = \text{left}]} (1 - \sigma(x_i))^{[n_i = \text{right}]}

其中:

  • \sigma(x_i) = \frac{1}{1 + e^{-x_i}}是 Sigmoid 函数,用于计算二元分类概率。
  • [n_i = \text{left}][n_i = \text{right}] 是指示函数,分别表示从节点 n_i 往左走还是往右走的概率。

该方法评价:

         Hierarchical Softmax 的计算复杂度为 O(\log V),相比标准的 Softmax 的 O(V),大大减少了计算量。但是该方法需要预先构建 Huffman 树或其他适当的二叉树结构。而且由于其结构依赖于路径上的每个节点的计算结果,难以进行并行化处理。所以在实际场景中,更建议选择负采样的方案。

2.1.2.3 Skip-gram

        Skip-gram 模型的目标词位于输入层,而上下文词位于输出层。

        这里还是使用 v_{w_I}来表示输入层上唯一词的输入向量,因此对隐藏层输出 h 的定义与之前单词上下文的场景相同,意味着 h 是输入→隐藏权重矩阵 W 中与输入词 w_I 相关的某一行的拷贝(和转置)。h 的定义如下:

h = W^T_{(k, \cdot)} := v_{w_I}^T \quad

        在输出层,输出的是 C 个多项分布,而不是一个。每个输出都使用相同的隐藏→输出矩阵计算:

p(w_{c,j} = w_{O,c} | w_I) = y_{c,j} = \frac{\exp(u_{c,j})}{\sum_{j'=1}^V \exp(u_{j'})} \quad 

其中 w_{c,j}是输出层第 c 面板上的第 j 个词;w_{O,c} 是实际的第 c 个输出上下文词;w_I 是唯一的输入词;y_{c,j} 是输出层第 c 面板上第 j 单元的输出;u_{c,j}是第 c 面板上第 j单元的净输入。由于输出层面板共享相同的权重,因此

u_{c,j} = u_j = v_{w_j}^T \cdot h \text{ for } c = 1, 2, \ldots, C \quad

其中 v_{w_j}^0​ 是词汇表中第 j 个词的输出向量,v_{w_j}^0​ 是从隐藏→输出权重矩阵 W^0 的一列中取出的。

参数更新方程的推导与一词上下文模型相似。损失函数改为:

E = - \log p(w_{O,1}, w_{O,2}, \ldots, w_{O,C} | w_I) \quad

= - \log \prod_{c=1}^C \frac{\exp(u_{c,j^*_c})}{\sum_{j'=1}^V \exp(u_{j'})} \quad

= - \sum_{c=1}^C u_{c,j^*_c} + C \cdot \log \sum_{j'=1}^V \exp(u_{j'}) \quad

        其中j^*_c是词汇表中实际第 c 个输出上下文词的索引。

        对输出层每个面板上的每个单元u_{c,j}的净输入u_{c,j}计算 E 的导数,得到:

\frac{\partial E}{\partial u_{c,j}} = y_{c,j} - t_{c,j} := e_{c,j} \quad

        这是单元上的预测误差。为了简化符号,定义一个维度为 V 的向量 EI = \{ EI_1, \ldots, EI_V \}为所有上下文词的预测误差之和:

EI_j = \sum_{c=1}^C e_{c,j} \quad

        接下来,对隐藏→输出矩阵 W^0 计算 E 的导数,得到:

\frac{\partial E}{\partial w_{0,ij}} = \sum_{c=1}^C \frac{\partial E}{\partial u_{c,j}} \cdot \frac{\partial u_{c,j}}{\partial w_{0,ij}} = EI_j \cdot h \quad

        因此,可以得到隐藏→输出矩阵 W^0 的更新方程:

w_{0,ij} (\text{new}) = w_{0,ij} (\text{old}) - \eta \cdot EI_j \cdot h \quad

v_{w_j}^0 (\text{new}) = v_{w_j}^0 (\text{old}) - \eta \cdot EI_j \cdot h \text{ for } j = 1, 2, \ldots, V \quad

        这个更新方程中涉及的预测误差是对输出层所有上下文词求和。需要对每个训练实例的隐藏→输出矩阵的每个元素应用这个更新方程。

        输入→隐藏矩阵的更新方程:

v_{w_I} (\text{new}) = v_{w_I} (\text{old}) - \eta \cdot E^H T \quad

        其中 EH 是一个 N 维向量,每个分量定义为:

EH_i = \sum_{j=1}^V EI_j \cdot w_{0,ij} \quad

2.1.3 总结

        至此,我们对CBOW和skip gram以及负采样、分层softmax的原理分析完成。我们再总结一下:

  • 工作原理
    • CBOW:通过上下文预测目标词。
    • Skip-gram:通过目标词预测上下文。
  • 优点
    • 可以学习词的分布式表示,捕捉词的语义相似性。
    • 训练速度快,对计算资源要求低。
  • 用法:Word2Vec适用于构建词嵌入模型,尤其是需要理解文本中词语之间的语义关系时(如信息检索、推荐系统、文本聚类等)。
  • 优势:Word2Vec训练时间短,生成的词向量可以快速应用于下游任务中。预训练的Word2Vec模型可以直接用于文本数据的特征表示,避免了从零开始训练的时间和计算成本。

2.1.4 Word2Vec与大模型的关系

        像 ChatGPT 这样的大模型在理解和生成文本时,可以利用 Word2Vec 作为基础步骤。Word2Vec 生成的词嵌入用作这些模型的初始化输入,帮助它们理解词汇之间的语义关系。这种理解对于文本生成【3】。在训练大模型时,第一步可以是使用大规模语料库训练 Word2Vec 模型。该模型学习将每个词表示为高维向量。这些向量随后用作大模型的向量输入初始化。大模型接收这些向量,并学习在给定前面几个词的情况下预测句子中的下一个词。模型通过调整其参数来最大化实际下一个词的概率,从而进行训练。  

        另外,在大模型的训练开始时,词嵌入层中的词向量也可以是随机初始化的。这意味着每个词元都会被分配一个初始的随机向量。在训练过程中,词向量会随着模型的训练过程而动态调整。模型根据输入的训练数据不断学习,以最大化训练目标(如预测下一个词的概率或掩蔽词的概率),从而更新嵌入层中的词向量。这样,经过大量训练数据的调整,词向量会逐渐学到更精确的语义表示【4】。  

2.1.5 题外话(GloVe Embedding)

        既然提到了word2vec,那么这里插入对于另一种词向量编码技术的介绍,那就是GloVe Embedding【5】。GloVe Embedding是一种基于统计的词嵌入方法,由斯坦福大学在 2014 年提出。GloVe 结合了传统的基于共现矩阵的统计方法和基于局部上下文的神经网络方法(如上述的 Word2Vec)的优点,生成高质量的词向量。

        GloVe 的核心思想是基于词与词的共现信息来学习词向量。具体来说,GloVe 通过构建一个全局共现矩阵,然后对这个矩阵进行因式分解,来生成每个词的向量表示。GloVe 的训练过程试图使两个词之间的向量点积能够很好地反映它们在语料库中的共现频率。

1. 共现矩阵

        GloVe 方法首先构建一个词汇的共现矩阵 X,其中 X_{ij} 表示词 i 和词 j 在一定上下文窗口内共同出现的次数。例如,在一个包含单词的窗口大小为 5 的句子中,"king" 和 "queen" 可能会同时出现 X_{ij} = 10

  • X_{ij}​:词 i 和词 j 共同出现的次数。
  • X_i = \sum_j X_{ij}:表示词 i 出现的总次数。
2. 目标函数的定义

        GloVe 的目标是最小化以下损失函数:

J = \sum_{i,j=1}^V f(X_{ij}) \left( w_i^T \tilde{w}_j + b_i + \tilde{b}_j - \log(X_{ij}) \right)^2

        其中:

  • w_i\tilde{w}_j 是词 i 和词 j 的词向量。
  • b_i 和 \tilde{b}_j 是词 i 和词 j 的偏置项。
  • X_{ij} 是词 i 和词 j 的共现频率。
  • f(X_{ij}) 是一个加权函数,用于控制高频和低频词对损失函数的影响。
3. 加权函数 f(X_{ij})

        加权函数f(X_{ij})被设计成在小频率和大频率时对损失的影响不同:

其中:

  • X_{\text{max}}​ 是一个超参数,用于设置最大共现频率的上限。
  • \alpha 通常设置为 0.75,可以有效减少高频词的权重,避免它们对训练过程产生过大的影响。
4. 目标函数的优化

        通过最小化损失函数 J,GloVe 寻求找到一种最优的词向量表示,使得两个词的向量点积能够准确反映它们在语料库中的共现频率。

2.2 TextCNN

        TextCNN 是一种将卷积神经网络 (CNN) 应用于文本分类任务的模型,由 Yoon Kim 在其 2014 年的论文【6】中提出。TextCNN 通过对文本数据进行卷积操作,能够自动提取出有助于分类任务的特征,从而在多种自然语言处理任务中取得了很好的效果。

2.2.1 TextCNN 模型结构

        TextCNN 模型的核心结构是一个一维卷积神经网络,它使用多个卷积核(filters)来从文本中提取特征。

以下是 TextCNN 的基本结构:

  1. 输入层:词嵌入矩阵
    输入的文本首先被表示为词嵌入矩阵(通常使用预训练的词嵌入,如 Word2Vec 或 GloVe)。假设输入文本的长度为 n,词嵌入的维度为 d,则词嵌入矩阵的形状为 n × d

  2. 卷积层
    卷积层使用不同大小的卷积核(如 2、3、4-gram)来进行一维卷积操作,捕捉文本中的局部特征。每个卷积核在输入矩阵上滑动,生成一个特征图。卷积核的大小决定了它能捕捉的 n-gram 特征(如 2-gram, 3-gram 等)。

  3. 池化层(Pooling)
    在卷积操作后,使用最大池化(Max Pooling)来减少每个特征图的维度,同时保留最重要的特征。最大池化层的输出将是每个卷积核的一个单一值,表示该核在整句话中的最显著特征。

  4. 全连接层和输出层
    所有特征图的池化结果被连接成一个向量并输入到一个或多个全连接层中,用于最终的分类任务。输出层通常是一个 Softmax 层,用于多类别分类任务。

2.2.2 TextCNN 的工作原理

        【7】给出了更细致的处理流程图示:

  1. 卷积操作:通过卷积操作,TextCNN 能够自动学习文本的局部特征,如短语和关键字的组合。通过不同大小的卷积核,模型可以捕捉不同 n-gram 级别的特征,增强其对文本语义的理解能力。

  2. 特征提取:卷积层会为每个词生成一个特征图,这些特征图代表了不同卷积核提取的不同语义信息。最大池化层的作用是选择最重要的特征,减少数据维度,防止过拟合。

  3. 分类决策:最后,模型通过全连接层和输出层,基于提取的特征进行分类任务。训练过程中,模型的参数通过反向传播算法不断优化,逐渐增强对文本的分类能力。

2.2.3 TextCNN 数学公式推导

         TextCNN 结构简单直接,可以用来作为NLP的入门技术,以下是 TextCNN 的数学公式推导过程。

1. 输入表示

        假设输入文本由 n 个词组成,每个词用一个 d 维的词向量表示(例如使用 Word2Vec 或 GloVe 预训练词嵌入)。输入文本可以表示为一个矩阵 X \in \mathbb{R}^{n \times d},其中:

X = [x_1, x_2, \ldots, x_n]

        这里,x_i \in \mathbb{R}^d 表示第 i 个词的词向量。

2. 卷积操作

        TextCNN 采用一维卷积操作来处理文本,卷积核的宽度(filter size)通常设置为多个不同的值(如 2、3、4 等)以捕捉不同大小的 n-gram 特征。

  • 卷积核:假设一个卷积核的大小为 h,即它覆盖 h 个连续的词向量。卷积核的参数可以表示为一个权重矩阵 W \in \mathbb{R}^{h \times d} 和一个偏置项 b \in \mathbb{R}

        对于给定的窗口大小 h,卷积操作的输出特征映射(feature map)可以表示为:

c_i = f(W \cdot X_{i:i+h-1} + b)

        其中:

  • X_{i:i+h-1} \in \mathbb{R}^{h \times d}表示从位置 ii+h-1 的输入子矩阵。
  • W \cdot X_{i:i+h-1} 是矩阵乘法的结果,表示将卷积核应用于该窗口的线性组合。
  • b 是偏置。
  • f 是一个非线性激活函数(通常使用 ReLU:f(x) = \max(0, x)

        整个文本的卷积操作将产生一个特征映射 c \in \mathbb{R}^{n-h+1}

c = [c_1, c_2, \ldots, c_{n-h+1}]

3. 池化操作

        为了从卷积输出的特征映射中提取重要特征,TextCNN 使用 最大池化(Max Pooling) 操作。对于特征映射 c,最大池化的输出 \hat{c} 定义为:

\hat{c} = \max(c)

        即选择特征映射中的最大值,这样可以捕捉到该特征的最重要部分。

4. 多卷积核与特征连接

        TextCNN 不仅使用一个卷积核,而是使用多个卷积核,每个卷积核的窗口大小不同(如 2, 3, 4),以捕捉不同的 n-gram 特征。

        假设有 m 个不同的卷积核,每个卷积核生成的最大池化输出为\hat{c}_jj 表示第 j 个卷积核),那么最终的特征向量 z 是这些池化结果的串联:

z = [\hat{c}_1, \hat{c}_2, \ldots, \hat{c}_m] \in \mathbb{R}^m

5. 全连接层与输出

        特征向量 z 经过全连接层进行分类任务。全连接层的输出可以表示为:

o = \text{softmax}(W^{(fc)} \cdot z + b^{(fc)})

        其中:

  • W^{(fc)} \in \mathbb{R}^{k \times m}是全连接层的权重矩阵(k 是类别数量)。
  • b^{(fc)} \in \mathbb{R}^k 是全连接层的偏置。
  • \text{softmax} 函数用于将输出转换为概率分布。
6. 损失函数与训练

        TextCNN 一般使用交叉熵损失函数来进行多类别分类任务。假设输出类别的真实标签为 y,模型的预测输出为 \hat{y},损失函数为:

L = - \sum_{i=1}^{k} y_i \log(\hat{y}_i)

        通过最小化损失函数 L,模型的参数(包括卷积核的权重、偏置和全连接层的权重、偏置)在训练过程中不断调整,以优化模型性能。

2.2.4 总结以及如何在大模型中发挥作用

  • 优点
    • 能够捕捉词序信息及局部特征。
    • 计算速度较快,适合处理大规模数据。
  • 用法:TextCNN可以用来快速构建文本分类模型,尤其是当你有大量的短文本数据(如评论、推文、新闻标题等)需要分类时。我们曾在记账类型的短文本分类任务中实践证明确实有效。
  • 优势:TextCNN的结构简单,训练时间较短,可以在有限的计算资源下快速实现高效的文本分类任务。对于大模型的低成本高质量数据清洗,TextCNN是一个不错的选择

2.2.5 题外话(Gated Text-CNN)

        我们大概在2017年尝试了引入Highway结构的Text-CNN,我称之为Gated Text-CNN,效果也不错,我们在该浅层网络的池化(pooling)层与全连接 (Fully Connected,FC)层之间采用深层神经网络采用的信息高速公路(Highway)结构进行联通,有助于加快CNN模型的收敛速度【9】,并且该结构能够保留池化后的重要特征信息。网络结构类似这篇文档所述【10】。

        Highwigh网络层的公式如下:

2.3 FastText

        FastText 是由 Facebook 的 AI 研究团队(FAIR)提出的一种用于词嵌入和文本分类的高效模型【8】。它是在 Word2Vec 的基础上进行改进的(所以理解了前述的word2vec原理后,也就基本理解fasttext的核心底层技术),其方法更好地捕捉单词内部的形态特征,并且在处理稀有词和未登录词(OOV,Out-of-Vocabulary)时表现较好,主要是因为FastText 通过将单词表示为子词的组合来增强模型的泛化能力。     

2.3.1 FastText 的核心思想

        FastText 的核心思想是利用子词信息(subword information),将每个单词表示为多个 n-gram 的集合,从而捕捉单词内部的结构特征。这与 Word2Vec 不同,后者直接将每个单词表示为一个独立的向量。哈哈,这种处理方式是不是和大模型的token处理方式有点神似。

2.3.2 FastText 的基本原理

        FastText 的基本原理可以分为以下几个步骤:

1. 子词表示(Subword Representation)

        在 FastText 中,每个单词不仅仅表示为一个独立的向量,还表示为一组字符 n-gram 的集合。例如,假设有一个单词 “apple”,可以将它分解为不同的 n-gram,如下:

  • 对于 n=3 的情况,字符 n-gram 包括:<ap, app, ppl, ple, le>.
  • 其中,<> 是特殊字符,用于标识单词的开始和结束。

        这样,单词 "apple" 可以表示为这些 n-gram 的集合之和。每个 n-gram 都有一个向量表示,单词的向量表示是其所有 n-gram 向量的平均值或加和。

2. 训练目标(Training Objective)

        FastText 采用与 Word2Vec 类似的训练目标,使用Skip-GramCBOW(Continuous Bag of Words) 模型。

  • Skip-Gram 模型:给定一个中心词,预测上下文词。
  • CBOW 模型:给定上下文词,预测中心词。

        例如,在 Skip-Gram 模型中,给定一个单词 w,目标是最大化其上下文词 c 的条件概率:

P(c \mid w) = \frac{\exp(v_c \cdot v_w)}{\sum_{c' \in C} \exp(v_{c'} \cdot v_w)}

        其中:

  • v_w​ 是单词 w 的向量表示。
  • v_c 是上下文词 c 的向量表示。
  • C 是所有上下文词的集合。

        在 FastText 中,词向量 v_w​ 是通过子词 n-gram 向量的和来表示的:

v_w = \sum_{g \in G_w} z_g

        其中:

  • G_w 是单词 w 的所有 n-gram 集合。
  • z_g 是 n-gram g 的向量表示。

        通过将单词表示为子词的组合,FastText 能够更好地捕捉词内部的形态特征,并能有效处理未登录词。

3. 高效训练(Efficient Training)

        FastText 通过使用分层 Softmax(Hierarchical Softmax)或负采样(Negative Sampling)来加速训练过程:

  • 分层 Softmax:通过构建一个 Huffman 树,将类别分层表示,从而减少计算全概率分布的复杂度。
  • 负采样:仅从噪声分布中随机选择一些负样本来计算更新,从而避免了计算整个词汇表。
4. 文本分类(Text Classification)

FastText 被广泛应用于文本分类任务。其文本分类流程如下:

  1. 单词表示:将每个单词表示为一组 n-gram 的向量。
  2. 文档表示:将文档中所有单词的向量求和或取平均,得到文档向量。
  3. 线性分类器:将文档向量输入到线性分类器(例如 Softmax 分类器)中,得到类别预测结果。

2.3.3 FastText 的优势

  1. 子词级表示:通过使用子词 n-gram,FastText 能够捕捉词内部的形态特征,对于处理形态丰富的语言以及未登录词非常有效。

  2. 快速训练:利用分层 Softmax 和负采样,FastText 可以在大规模数据集上高效地进行训练。

  3. 处理未登录词:由于 FastText 将词表示为 n-gram 的组合,因此即使是未在训练集中出现的词也可以通过其子词表示获得合理的向量。

  4. 适用于文本分类:FastText 在文本分类任务上表现良好,因为它能快速生成文档向量,并通过简单的线性分类器进行分类。

2.3.4 总结以及如何在大模型中发挥作用

  • 工作原理:FastText通过嵌入矩阵将每个单词转化为向量,并通过求平均得到整个文本的表示,之后利用softmax或其他线性分类器进行分类。
  • 优点
    • 训练速度快,适合大规模数据集。
    • 对低资源语言、稀疏数据表现良好。
    • 能够捕捉词的形态特征(如前后缀)。
  • 用法:FastText适合用来构建轻量级的文本分类器和词向量模型。它支持高效地处理大规模数据集,是低资源条件下的理想选择。因此在大模型的数据预处理阶段,完全可以使用FastText快速对大规模数据进行分门别类,形成不同类目的文档体系。
  • 优势:训练速度极快,内存占用少。尤其适合在特征工程或者数据处理中使用。我们曾在对话系统的意图初步识别中,调用FastText做初筛,效果不错。

2.4  总结 

        这三种技术分别代表了不同的文本处理方法。TextCNN和FastText更多用于文本分类,而Word2Vec用于词向量表示。在大模型时代,我们仍然可以借助TextCNN、FastText、Word2Vec等周边自然语言处理技术来实现数据的低成本预处理。这些技术在数据预处理阶段可以帮助减少计算开销、加速数据处理流程,并为更复杂的大模型(如GPT、Llama等)提供优质的输入数据。

3. 参考材料

【1】Efficient Estimation of Word Representations in Vector Space

【2】word2vec Parameter Learning Explained

【3】What is Word2Vec: LLMs Explained

【4】Demystifying Embeddings 101: The Foundation of Large Language Models

【5】GloVe: Global Vectors for Word Representation

【6】Convolutional Neural Networks for Sentence Classification

【7】Twitter Sentiment Analysis with CNNs and LSTMs

【8】Bag of Tricks for Efficient Text Classification

【9】短文本分类方法及装置

【10】多轮对话机器之话题意图识别

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/424468.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用Python生成多种不同类型的Excel图表

目录 一、使用工具 二、生成Excel图表的基本步骤 三、使用Python创建Excel图表 柱形图饼图折线图条形图散点图面积图组合图瀑布图树形图箱线图旭日图漏斗图直方图不使用工作表数据生成图表 四、总结 Excel图表是数据可视化的重要工具&#xff0c;它通过直观的方式将数字信…

PCIe进阶之TL:First/Last DW Byte Enables Rules Traffic Class Field

1 First/Last DW Byte Enables Rules & Attributes Field 1.1 First/Last DW Byte Enables Rules Byte Enable 包含在 Memory、I/O 和 Configuration Request 中。本文定义了相应的规则。Byte Enable 位于 header 的 byte 7 。对于 TH 字段值为 1 的 Memory Read Request…

【STM32】esp8266通过MQTT连接服务器|订阅发布

1. MQTT协议 该协议为应用层协议&#xff0c;传输层使用的是tcp,MQTT的订阅和发布&#xff0c;就相当于在抖音中你关注了某个领域的博主&#xff08;订阅&#xff09;&#xff0c;如果有其他人发了作品就会推给你&#xff08;发布&#xff09;&#xff0c;默认已经安装好了 简…

哈希表、算法

哈希表 hash&#xff1a; 在编程和数据结构中&#xff0c;"hash" 通常指的是哈希函数&#xff0c;它是一种算法&#xff0c;用于将数据&#xff08;通常是字符 串&#xff09;映射到一个固定大小的数字&#xff08;哈希值&#xff09;。哈希函数在哈希表中尤为重要…

探索图论中的关键算法(Java 实现)

“日出东海落西山 愁也一天 喜也一天 遇事不钻牛角尖” 文章目录 前言文章有误敬请斧正 不胜感恩&#xff01;||Day031. 最短路径算法Dijkstra算法Java 实现&#xff1a; Bellman-Ford算法Java 实现&#xff1a; 2. 最小生成树算法Prim算法Java 实现&#xff1a; Kruskal算法Ja…

C++速通LeetCode简单第9题-二叉树的最大深度

深度优先算法递归&#xff1a; /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : val(x), left(nullptr), right…

Conmi的正确答案——MySQL的层级递归查询(递归公共表表达式,CTE)

数据库&#xff1a;oceanbase-ce 递归sql主体&#xff1a; WITH RECURSIVE country_area_tree AS (-- 非递归部分&#xff0c;初始化查询SELECT id, area_name, parent_id, 0 AS levelFROM country_areaWHERE id 589004044419077UNION ALL-- 递归部分&#xff0c;找到子节点S…

聚类_K均值

import numpy as np import matplotlib.pyplot as plt from sklearn.datasets import make_blobs1.数据预处理 #创建基于高斯分布的样本点, x是点的坐标&#xff0c;y是所属聚类值 x, y make_blobs(n_samples100, centers6, random_state100, cluster_std0.6) # 设置图形尺寸…

2. 变量和指令(omron 机器自动化控制器)——1

机器自动化控制器——第二章 变量和指令 1 2-1 变量一览表MC通用变量轴变量▶ 轴组变量 运动控制指令的输入变量输入变量的有效范围▶ 枚举体一览表 运动控制指令的输出变量运动控制指令的输入输出变量 2-1 变量一览表 MC功能模块使用的变量分为两类。 一类是监视轴等的状态及…

电脑提示丢失mfc140u.dll的详细解决方案,mfc140u.dll文件是什么

遇到电脑显示“缺少 mfc140u.dll 文件”的错误其实是比较常见的。这种提示通常表示某个应用程序在尝试运行时未能找到它所需的关键 DLL 文件&#xff0c;导致无法正常启动。不过&#xff0c;别担心&#xff0c;本文将一步步引导你通过几种不同的方法来解决这个问题&#xff0c;…

VSCode拉取远程项目

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

el-input设置后缀显示单位并阻止滚轮微调

项目中收集form表单信息时&#xff0c;有时会需要在el-input后面显示单位&#xff0c;效果如图&#xff1a; 当然&#xff0c;我们可以直接在输入框后面加上单位&#xff0c;但直接给输入框上加单位不管是视图上还是用户体验上看起来都要好一点 element-plus / element-ui给我…

[基于 Vue CLI 5 + Vue 3 + Ant Design Vue 4 搭建项目] 01 安装 nodejs 环境

文章目录 下载安装测试 这里让我们去看看如何安装一下 nodejs 的环境 下载 通过官网进行下载安装包 官网 https://nodejs.org/zh-cn点击 下载 Node.js (LTS) 开始下载 安装 下载完成之后&#xff0c;双击进行安装 开始进行安装了 这样&#xff0c;node.js 就安装好了 测试 …

unity3d入门教程三

unity3d入门教程三 8.1游戏脚本8.2脚本的使用8.3认识脚本组件8.4帧率9.1游戏脚本9.2获取节点和组件9.3MonoBehaviour9.4父节点与子节点9.5组件的属性9.6脚本的单步调试 8.1游戏脚本 通过程序控制对象属性&#xff08;如运动&#xff0c;修改transform的位置属性&#xff09; …

SpringCloudAlibaba:Seata

1. 面试题 2.1 你简历上写用微服务boot/cloud做过项目&#xff0c;你不可能只有一个数据库吧&#xff1f;谈谈多个数据库之间如何处理分布式事务&#xff1f; 2.2 阿里巴巴的Seata-AT模式如何做到对业务的无侵入&#xff1f; 在一阶段&#xff0c;Seata 会拦截“业务 SQL”&a…

逆向学习系列(三)adb的使用

由于是记录学习&#xff0c;我就用结合自己的理解&#xff0c;用最通俗的语言进行讲解。 adb是android debug bridge的简写&#xff0c;其作用就是将电脑和手机相连接&#xff0c;用电脑控制手机。 一、adb哪里来 我使用的adb一般都是安装模拟器的时候&#xff0c;模拟器自带…

MySQL基础——DQL

DQL&#xff08;Data Query Language&#xff0c;数据查询语言&#xff09;是SQL中的一个子集&#xff0c;主要用于查询数据库中的数据。DQL的核心语句是 SELECT&#xff0c;它用于从一个或多个表中提取数据&#xff0c;并能够通过各种条件进行过滤、排序和聚合操作。下面是DQL…

【学习笔记】手写Tomcat 二

目录 响应静态资源 HTTP协议请求格式 1. 解析请求信息 创建解析请求类 HttpRequest 2. 创建静态资源目录 webs 3. 封装响应信息 创建静态资源处理器 StaticResourceHandler 创建响应类 HttpResponse 然后就可以调用响应类了 测试 静态资源的路径说明 作业 1. 绘制…

JNI 详细介绍

一 介绍 java调⽤c&#xff0c;c代码可以通过JNIEnv执行java代码。 安卓NDK 已经对JNI环境进行了集成&#xff0c;我们可以通过android studio来快速搭建一个项目。 二 项目搭建 打开android studio 创建工程&#xff0c;创建工程选择模板Native C 三 模板格式介绍 生成的…

非关系型数据库Redis

文章目录 一&#xff0c;关系型数据库和非关系型数据可区别1.关系型数据库2.非关系型数据库3.区别3.1存储方式3.2扩展方式3.2事务性的支持 二&#xff0c;非关系型数据为什么产生三&#xff0c;Redis1.Redis是什么2.Redis优点3.Redis适用范围4. Redis 快的原因4.1 基于内存运行…