<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>表示学习 on Zeqiang Fang | 方泽强</title><link>https://zeqiang.fun/categories/%E8%A1%A8%E7%A4%BA%E5%AD%A6%E4%B9%A0/</link><description>Recent content in 表示学习 on Zeqiang Fang | 方泽强</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Sat, 31 Oct 2020 00:00:00 +0000</lastBuildDate><atom:link href="https://zeqiang.fun/categories/%E8%A1%A8%E7%A4%BA%E5%AD%A6%E4%B9%A0/" rel="self" type="application/rss+xml"/><item><title>文本相似度 (Text Similarity)</title><link>https://zeqiang.fun/cn/2020/10/text-similarity/</link><pubDate>Sat, 31 Oct 2020 00:00:00 +0000</pubDate><guid>https://zeqiang.fun/cn/2020/10/text-similarity/</guid><description><![CDATA[
        <p>文本相似度是指衡量两个文本的相似程度，相似程度的评价有很多角度：单纯的字面相似度（例如：我和他 v.s. 我和她），语义的相似度（例如：爸爸 v.s. 父亲）和风格的相似度（例如：我喜欢你 v.s. 我好喜欢你耶）等等。</p>
<h2 id="文本表示角度">文本表示角度</h2>
<h3 id="统计模型">统计模型</h3>
<h4 id="文本切分">文本切分</h4>
<p>在中文和拉丁语系中，文本的直观表示就存在一定的差异，拉丁语系中词与词之间存在天然的分隔符，而中文则没有。</p>
<blockquote>
<p>I can eat glass, it doesn&rsquo;t hurt me.<br>
我能吞下玻璃而不伤身体。</p>
</blockquote>
<p>因此针对拉丁语系的文本切分相对中文容易许多。</p>
<ul>
<li><strong>N 元语法</strong></li>
</ul>
<p>N-gram (N 元语法) 是一种文本表示方法，指文中连续出现的 $n$ 个词语。N-gram 模型是基于 $n-1$ 阶马尔科夫链的一种概率语言模型，可以通过前 $n-1$ 个词对第 $n$ 个词进行预测。以 <code>南京市长江大桥</code> 为例，N-gram 的表示如下：</p>
<pre><code>一元语法（unigram）：南/京/市/长/江/大/桥
二元语法（bigram）：南京/京市/市长/长江/江大/大桥
三元语法（trigram）：南京市/京市长/市长江/长江大/江大桥
</code></pre>
<pre><code class="language-python">import re
from nltk.util import ngrams

s = '南京市长江大桥'
tokens = re.sub(r'\s', '', s)

list(ngrams(tokens, 1))
# [('南',), ('京',), ('市',), ('长',), ('江',), ('大',), ('桥',)]

list(ngrams(tokens, 2))
# [('南', '京'), ('京', '市'), ('市', '长'),
#  ('长', '江'), ('江', '大'), ('大', '桥')]

list(ngrams(tokens, 3, pad_left=True, pad_right=True, left_pad_symbol='&lt;s&gt;', right_pad_symbol='&lt;/s&gt;'))
# [('&lt;s&gt;', '&lt;s&gt;', '南'),
#  ('&lt;s&gt;', '南', '京'),
#  ('南', '京', '市'),
#  ('京', '市', '长'),
#  ('市', '长', '江'),
#  ('长', '江', '大'),
#  ('江', '大', '桥'),
#  ('大', '桥', '&lt;/s&gt;'),
#  ('桥', '&lt;/s&gt;', '&lt;/s&gt;')]
</code></pre>
<ul>
<li><strong>分词</strong></li>
</ul>
<p>分词就是将连续的字序列按照一定的规范重新组合成词序列的过程。在英文的行文中，单词之间是以空格作为自然分界符的，而中文只是字、句和段能通过明显的分界符来简单划界，唯独词没有一个形式上的分界符，虽然英文也同样存在短语的划分问题，不过在词这一层上，中文比之英文要复杂得多、困难得多。</p>
<pre><code class="language-python">s = '南京市长江大桥'

# jieba
# https://github.com/fxsjy/jieba
import jieba

list(jieba.cut(s, cut_all=False))
# ['南京市', '长江大桥']

list(jieba.cut(s, cut_all=True))
# ['南京', '南京市', '京市', '市长', '长江', '长江大桥', '大桥']

list(jieba.cut_for_search(s))
# ['南京', '京市', '南京市', '长江', '大桥', '长江大桥']

# THULAC
# https://github.com/thunlp/THULAC-Python
import thulac

thulac_ins = thulac.thulac()

thulac_ins.cut(s)
# [['南京市', 'ns'], ['长江', 'ns'], ['大桥', 'n']]

# PKUSEG
# https://github.com/lancopku/PKUSeg-python
import pkuseg

seg = pkuseg.pkuseg(postag=True)

seg.cut(s)
# [('南京市', 'ns'), ('长江', 'ns'), ('大桥', 'n')]

# HanLP
# https://github.com/hankcs/HanLP
import hanlp

tokenizer = hanlp.load('LARGE_ALBERT_BASE')

tokenizer(s)
# ['南京市', '长江', '大桥']
</code></pre>
<h4 id="主题模型">主题模型</h4>
<p>除了对文本进行切分将切分后结果全部用于表示文本外，还可以用部分字词表示一篇文档。主题模型（Topic Model）在机器学习和自然语言处理等领域是用来在一系列文档中发现抽象主题的一种统计模型。</p>
<p><img src="/images/cn/2020-10-31-text-similarity/what-is-topic-model.png" alt="What is Topic Model"></p>
<p>直观来讲，如果一篇文章有一个中心思想，那么一些特定词语会更频繁的出现。比方说，如果一篇文章是在讲狗的，那“狗”和“骨头”等词出现的频率会高些。如果一篇文章是在讲猫的，那“猫”和“鱼”等词出现的频率会高些。而有些词例如“这个”、“和”大概在两篇文章中出现的频率会大致相等。但真实的情况是，一篇文章通常包含多种主题，而且每个主题所占比例各不相同。因此，如果一篇文章 10% 和猫有关，90% 和狗有关，那么和狗相关的关键字出现的次数大概会是和猫相关的关键字出现次数的 9 倍。</p>
<p>一个主题模型试图用数学框架来体现文档的这种特点。主题模型自动分析每个文档，统计文档内的词语，根据统计的信息来断定当前文档含有哪些主题，以及每个主题所占的比例各为多少 <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>。</p>
<ul>
<li><strong>TF-IDF</strong></li>
</ul>
<p>TF-IDF 是 Term Frequency - Inverse Document Frequency 的缩写，即“词频-逆文本频率”。TF-IDF 可以用于评估一个字词在语料中的一篇文档中的重要程度，基本思想是如果某个字词在一篇文档中出现的频率较高，而在其他文档中出现频率较低，则认为这个字词更能够代表这篇文档。</p>
<p>形式化地，对于文档 $y$ 中的字词 $x$ 的 TF-IDF 重要程度可以表示为：</p>
<p><code>$$ w_{x, y} = tf_{x, y} \times \log \left(\dfrac{N}{df_{x}}\right) $$</code></p>
<p>其中，<code>$tf_{x, y}$</code> 表示字词 <code>$x$</code> 在文档 <code>$y$</code> 中出现的频率，<code>$df_x$</code> 为包含字词 <code>$x$</code> 的文档数量，<code>$N$</code> 为语料中文档的总数量。</p>
<p>以 <a href="https://github.com/liuhuanyong/MusicLyricChatbot">14 万歌词语料</a> 为例，通过 TF-IDF 计算周杰伦的《简单爱》中最重要的 3 个词为 <code>['睡着', '放开', '棒球']</code>。</p>
<ul>
<li><strong>BM25</strong></li>
</ul>
<p>BM25 算法的全称为 Okapi BM25，是一种搜索引擎用于评估查询和文档之间相关程度的排序算法，其中 BM 是 Best Match 的缩写。</p>
<p>对于一个给定的查询 <code>$Q$</code>，包含的关键词为 <code>$q_1, \cdots, q_n$</code>，一个文档 <code>$D$</code> 的 BM25 值定义为：</p>
<p><code>$$ \operatorname{score}(D, Q)=\sum_{i=1}^{n} \operatorname{IDF}\left(q_{i}\right) \cdot \frac{f\left(q_{i}, D\right) \cdot\left(k_{1}+1\right)}{f\left(q_{i}, D\right)+k_{1} \cdot\left(1-b+b \cdot \frac{|D|}{\text { avgdl }}\right)} $$</code></p>
<p>其中，<code>$f\left(q_{i}, D\right)$</code> 表示 <code>$q_i$</code> 在文档 <code>$D$</code> 中的词频，<code>$|D|$</code> 表示文档 <code>$D$</code> 中的词数，<code>$\text{avgdl}$</code> 表示语料中所有文档的平均长度。<code>$k_1$</code> 和 <code>$b$</code> 为自由参数，通常取值为 <code>$k_1 \in \left[1.2, 2.0\right], b = 0.75$</code> <sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>。<code>$\operatorname{IDF} \left(q_i\right)$</code> 表示词 <code>$q_i$</code> 的逆文档频率，通常计算方式如下：</p>
<p><code>$$ \operatorname{IDF}\left(q_{i}\right)=\ln \left(\frac{N-n\left(q_{i}\right)+0.5}{n\left(q_{i}\right)+0.5}+1\right) $$</code></p>
<p>其中，<code>$N$</code> 为语料中文档的总数量，<code>$n \left(q_i\right)$</code> 表示包含 <code>$q_i$</code> 的文档数量。</p>
<p>BM25 算法是对 TF-IDF 算法的优化，在词频的计算上，BM25 限制了文档 <code>$D$</code> 中关键词 <code>$q_i$</code> 的词频对评分的影响。为了防止词频过大，BM25 将这个值的上限设置为 <code>$k_1 + 1$</code>。</p>
<p><img src="/images/cn/2020-10-31-text-similarity/bm25-tf-1.png" alt=""></p>
<p>同时，BM25 还引入了平均文档长度 <code>$\text{avgdl}$</code>，不同的平均文档长度 <code>$\text{avgdl}$</code> 对 TF 分值的影响如下图所示：</p>
<p><img src="/images/cn/2020-10-31-text-similarity/bm25-tf-2.png" alt=""></p>
<ul>
<li><strong>TextRank</strong></li>
</ul>
<p>TextRank <sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> 是基于 PageRank <sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> 算法的一种关键词提取算法。PageRank 最早是用于 Google 的网页排名，因此以公司创始人拉里·佩奇（Larry Page）的姓氏来命名。PageRank 的计算公式如下：</p>
<p><code>$$ S\left(V_{i}\right)=(1-d)+d * \sum_{V_{j} \in I n\left(V_{i}\right)} \frac{1}{\left|O u t\left(V_{j}\right)\right|} S\left(V_{j}\right) $$</code></p>
<p>其中，<code>$V_i$</code> 表示任意一个网页，<code>$V_j$</code> 表示链接到网页 <code>$V_i$</code> 的网页，<code>$S \left(V_i\right)$</code> 表示网页 <code>$V_i$</code> 的 PageRank 值，<code>$In \left(V_i\right)$</code> 表示网页 <code>$V_i$</code> 所有的入链集合，<code>$Out \left(V_j\right)$</code> 表示网页 <code>$V_j$</code> 所有的出链集合，<code>$|\cdot|$</code> 表示集合的大小，<code>$d$</code> 为阻尼系数，是为了确保每个网页的 PageRank 值都大于 0。</p>
<p>TextRank 由 PageRank 改进而来，计算公式如下：</p>
<p><code>$$ WS \left(V_{i}\right)=(1-d)+d * \sum_{V_{j} \in In\left(V_{i}\right)} \frac{w_{j i}}{\sum_{V_{k} \in Out\left(V_{j}\right)} w_{j k}} WS \left(V_{j}\right) $$</code></p>
<p>相比于 PageRank 公式增加了权重项 <code>$W_{ji}$</code>，用来表示两个节点之间的边的权重。TextRank 提取关键词的算法流程如下：</p>
<ol>
<li>将文本进行切分得到 <code>$S_i = \left[t_{i1}, t_{i2}, \cdots, t_{in}\right]$</code>。</li>
<li>将 <code>$S_i$</code> 中大小为 <code>$k$</code> 的滑动窗口中的词定义为共现关系，构建关键词图 <code>$G = \left(V, E\right)$</code>。</li>
<li>根据 TextRank 的计算公式对每个节点的值进行计算，直至收敛。</li>
<li>对节点的 TextRank 的值进行倒叙排序，获取前 <code>$n$</code> 个词作为关键词。</li>
</ol>
<ul>
<li><strong>LSA, PLSA, LDA &amp; HDP</strong></li>
</ul>
<p><strong>潜在语义分析（LSA, Latent Semantic Analysis）</strong><sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup> 的核心思想是将文本的高维词空间映射到一个低维的向量空间，我们称之为隐含语义空间。降维可以通过<a href="/cn/2017/12/evd-svd-and-pca/">奇异值分解（SVD）</a>实现，令 <code>$X$</code> 表示语料矩阵，元素 <code>$\left(i, j\right)$</code> 表示词 <code>$i$</code> 和文档 <code>$j$</code> 的共现情况（例如：词频）：</p>
<p><code>$$ X = \mathbf{d}_{j} \cdot \mathbf{t}_{i}^{T} = \left[\begin{array}{c} x_{1, j} \\ \vdots \\ x_{i, j} \\ \vdots \\ x_{m, j} \end{array}\right] \cdot \left[\begin{array}{ccccc} x_{i, 1} &amp; \ldots &amp; x_{i, j} &amp; \ldots &amp; x_{i, n} \end{array}\right] = \left[\begin{array}{ccccc} x_{1,1} &amp; \ldots &amp; x_{1, j} &amp; \ldots &amp; x_{1, n} \\ \vdots &amp; \ddots &amp; \vdots &amp; \ddots &amp; \vdots \\ x_{i, 1} &amp; \ldots &amp; x_{i, j} &amp; \ldots &amp; x_{i, n} \\ \vdots &amp; \ddots &amp; \vdots &amp; \ddots &amp; \vdots \\ x_{m, 1} &amp; \ldots &amp; x_{m, j} &amp; \ldots &amp; x_{m, n} \end{array}\right] $$</code></p>
<p>利用奇异值分解：</p>
<p><code>$$ X = U \Sigma V^{T} $$</code></p>
<p>取最大的 <code>$K$</code> 个奇异值，则可以得到原始矩阵的近似矩阵：</p>
<p><code>$$ \widetilde{X} =U \widetilde{\Sigma} V^{T} $$</code></p>
<p>在处理一个新的文档时，可以利用下面的公式将原始的词空间映射到潜在语义空间：</p>
<p><code>$$ \tilde{x} =\tilde{\Sigma} ^{-1} V^{T} x_{test} $$</code></p>
<p>LSA 的优点：</p>
<ol>
<li>低维空间可以刻画同义词</li>
<li>无监督模型</li>
<li>降维可以减少噪声，使特征更加鲁棒</li>
</ol>
<p>LSA 的缺点：</p>
<ol>
<li>未解决多义词问题</li>
<li>计算复杂度高，增加新文档时需要重新训练</li>
<li>没有明确的物理解释</li>
<li>高斯分布假设不符合文本特征（词频不为负）</li>
<li>维度的确定是 Ad hoc 的</li>
</ol>
<p><strong>概率潜语义分析（Probabilistic Latent Semantic Analysis, PLSA）</strong><sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup> 相比于 LSA 增加了概率模型，每个变量以及相应的概率分布和条件概率分布都有明确的物理解释。</p>
<p>PLSA 认为一篇文档可以由多个主题混合而成，而每个主题都是词上的概率分布，文章中的每个词都是由一个固定的主题生成的，如下图所示：</p>
<p><img src="/images/cn/2020-10-31-text-similarity/plsa.png" alt=""></p>
<p>针对第 <code>$m$</code> 篇文档 <code>$d_m$</code> 中的每个词的生成概率为：</p>
<p><code>$$ p\left(w \mid d_{m}\right)=\sum_{z=1}^{K} p(w \mid z) p\left(z \mid d_{m}\right)=\sum_{z=1}^{K} \varphi_{z w} \theta_{m z} $$</code></p>
<p>因此整篇文档的生成概率为：</p>
<p><code>$$ p\left(\vec{w} \mid d_{m}\right)=\prod_{i=1}^{n} \sum_{z=1}^{K} p\left(w_{i} \mid z\right) p\left(z \mid d_{m}\right)=\prod_{i=1}^{n} \sum_{z=1}^{K} \varphi_{z w_{i}} \theta_{d z} $$</code></p>
<p>PLSA 可以利用 EM 算法求得局部最优解。</p>
<p>PLSA 优点：</p>
<ol>
<li>定义了概率模型，有明确的物理解释</li>
<li>多项式分布假设更加符合文本特征</li>
<li>可以通过模型选择和复杂度控制来确定主题的维度</li>
<li>解决了同义词和多义词的问题</li>
</ol>
<p>PLSA 缺点：</p>
<ol>
<li>随着文本和词的增加，PLSA 模型参数也随之线性增加</li>
<li>可以生成语料中的文档的模型，但不能生成新文档的模型</li>
<li>EM 算法求解的计算量较大</li>
</ol>
<p><strong>隐含狄利克雷分布（Latent Dirichlet Allocation, LDA）</strong><sup id="fnref:7"><a href="#fn:7" class="footnote-ref" role="doc-noteref">7</a></sup> 在 PLSA 的基础上增加了参数的先验分布。在 PLSA 中，对于一个新文档，是无法获取 <code>$p \left(d\right)$</code> 的，因此这个概率模型是不完备的。LDA 对于 <code>$\vec{\theta}_m$</code> 和 <code>$\vec{\phi}_k$</code> 都增加了多项式分布的共轭分布狄利克雷分布作为先验，整个 LDA 模型如下图所示：</p>
<p><img src="/images/cn/2020-10-31-text-similarity/lda.png" alt=""></p>
<p>LDA 的参数估计可以通过<a href="/cn/2017/12/mcmc-and-gibbs-sampling/">吉布斯采样</a>实现。PLSA 和 LDA 的更多细节请参见《LDA 数学八卦》<sup id="fnref:8"><a href="#fn:8" class="footnote-ref" role="doc-noteref">8</a></sup>。</p>
<p>LDA 在使用过程中仍需要指定主题的个数，而<strong>层次狄利克雷过程（Hierarchical Dirichlet Processes, HDP）</strong><sup id="fnref:9"><a href="#fn:9" class="footnote-ref" role="doc-noteref">9</a></sup> 通过过程的构造可以自动训练出主题的个数，更多实现细节请参考论文。</p>
<p>LSA，PLSA，LDA 和 HDP 之间的演化关系如下图所示：</p>
<p><img src="/images/cn/2020-10-31-text-similarity/lsa-plsa-lda-hdp.png" alt=""></p>
<blockquote>
<p>本节相关代码详见 <a href="https://github.com/leovan/leovan.me/tree/master/scripts/cn/2020-10-31-text-similarity/topic-model.py">这里</a>。</p>
</blockquote>
<h4 id="距离度量">距离度量</h4>
<blockquote>
<p>本节内容源自 <a href="/cn/2019/01/similarity-and-distance-measurement/">相似性和距离度量 (Similarity &amp; Distance Measurement)</a>。</p>
</blockquote>
<p>相似性度量 (Similarity Measurement) 用于衡量两个元素之间的相似性程度或两者之间的距离 (Distance)。距离衡量的是指元素之间的不相似性 (Dissimilarity)，通常情况下我们可以利用一个距离函数定义集合 <code>$X$</code> 上元素间的距离，即：</p>
<p><code>$$ d: X \times X \to \mathbb{R} $$</code></p>
<ul>
<li><strong>Jaccard 系数</strong></li>
</ul>
<p><code>$$ s = \dfrac{\left|X \cap Y\right|}{\left| X \cup Y \right|} = \dfrac{\left|X \cap Y\right|}{\left|X\right| + \left|Y\right| - \left|X \cap Y\right|} $$</code></p>
<p>Jaccard 系数的取值范围为：<code>$\left[0, 1\right]$</code>，0 表示两个集合没有重合，1 表示两个集合完全重合。</p>
<ul>
<li><strong>Dice 系数</strong></li>
</ul>
<p><code>$$ s = \dfrac{2 \left| X \cap Y \right|}{\left|X\right| + \left|Y\right|} $$</code></p>
<p>与 Jaccard 系数相同，Dice 系数的取值范围为：<code>$\left[0, 1\right]$</code>，两者之间可以相互转换 <code>$s_d = 2 s_j / \left(1 + s_j\right), s_j = s_d / \left(2 - s_d\right)$</code>。不同于 Jaccard 系数，Dice 系数的差异函数 <code>$d = 1 - s$</code> 并不是一个合适的距离度量，因为其并不满足距离函数的三角不等式。</p>
<ul>
<li><strong>Tversky 系数</strong></li>
</ul>
<p><code>$$ s = \dfrac{\left| X \cap Y \right|}{\left| X \cap Y \right| + \alpha \left| X \setminus Y \right| + \beta \left| Y \setminus X \right|} $$</code></p>
<p>其中，<code>$X \setminus Y$</code> 表示集合的相对补集。Tversky 系数可以理解为 Jaccard 系数和 Dice 系数的一般化，当 <code>$\alpha = \beta = 1$</code> 时为 Jaccard 系数，当 <code>$\alpha = \beta = 0.5$</code> 时为 Dice 系数。</p>
<ul>
<li><strong>Levenshtein 距离</strong></li>
</ul>
<p>Levenshtein 距离是 <strong>编辑距离 (Editor Distance)</strong> 的一种，指两个字串之间，由一个转成另一个所需的最少编辑操作次数。允许的编辑操作包括将一个字符替换成另一个字符，插入一个字符，删除一个字符。例如将 <strong>kitten</strong> 转成 <strong>sitting</strong>，转换过程如下：</p>
<p><code>$$ \begin{equation*} \begin{split} \text{kitten} \to \text{sitten} \left(k \to s\right) \\ \text{sitten} \to \text{sittin} \left(e \to i\right) \\ \text{sittin} \to \text{sitting} \left(\  \to g\right) \end{split} \end{equation*} $$</code></p>
<p>编辑距离的求解可以利用动态规划的思想优化计算的时间复杂度。</p>
<ul>
<li><strong>Jaro-Winkler 距离</strong></li>
</ul>
<p>对于给定的两个字符串 <code>$s_1$</code> 和 <code>$s_2$</code>，Jaro 相似度定义为：</p>
<p><code>$$ sim = \begin{cases} 0 &amp; \text{if} \  m = 0 \\ \dfrac{1}{3} \left(\dfrac{m}{\left|s_1\right|} + \dfrac{m}{\left|s_2\right|} + \dfrac{m-t}{m}\right) &amp; \text{otherwise} \end{cases} $$</code></p>
<p>其中，<code>$\left|s_i\right|$</code> 为字符串 <code>$s_i$</code> 的长度，<code>$m$</code> 为匹配的字符的个数，<code>$t$</code> 换位数目的一半。如果字符串 <code>$s_1$</code> 和 <code>$s_2$</code> 相差不超过 <code>$\lfloor \dfrac{\max \left(\left|s_1\right|, \left|s_2\right|\right)}{2} \rfloor - 1$</code>，我们则认为两个字符串是匹配的。例如，对于字符串 <strong>CRATE</strong> 和 <strong>TRACE</strong>，仅 <strong>R, A, E</strong> 三个字符是匹配的，因此 <code>$m = 3$</code>，尽管 <strong>C, T</strong> 均出现在两个字符串中，但是他们的距离超过了 1 (即，<code>$\lfloor \dfrac{5}{2} \rfloor - 1$</code>)，因此 <code>$t = 0$</code>。</p>
<p>Jaro-Winkler 相似度给予了起始部分相同的字符串更高的分数，其定义为：</p>
<p><code>$$ sim_w = sim_j + l p \left(1 - sim_j\right) $$</code></p>
<p>其中，<code>$sim_j$</code> 为字符串 <code>$s_1$</code> 和 <code>$s_2$</code> 的 Jaro 相似度，<code>$l$</code> 为共同前缀的长度 (规定不超过 <code>$4$</code>)，<code>$p$</code> 为调整系数 (规定不超过 <code>$0.25$</code>)，Winkler 将其设置为 <code>$p = 0.1$</code>。</p>
<ul>
<li><strong>汉明距离</strong></li>
</ul>
<p>汉明距离为两个<strong>等长字符串</strong>对应位置的不同字符的个数，也就是将一个字符串变换成另外一个字符串所需要<strong>替换</strong>的字符个数。例如：<strong>10<span style="color:#0000ff;">1</span>1<span style="color:#0000ff;">1</span>01</strong> 与 <strong>10<span style="color:#ff0000;">0</span>1<span style="color:#ff0000;">0</span>01</strong> 之间的汉明距离是 2，<strong>“<span style="color:#0000ff;">t</span>o<span style="color:#0000ff;">n</span>e<span style="color:#0000ff;">d</span>”</strong> 与 <strong>“<span style="color:#ff0000;">r</span>o<span style="color:#ff0000;">s</span>e<span style="color:#ff0000;">s</span>”</strong> 之间的汉明距离是 3。</p>
<pre><code class="language-python">import textdistance as td

s1 = '南京市长江大桥'
s2 = '北京市三元桥'

td.jaccard(s1, s2)
# 0.6666666666666666

td.sorensen_dice(s1, s2)
# 0.46153846153846156

td.tversky(s1, s2)
# 0.3

td.levenshtein(s1, s2)
# 4

td.jaro(s1, s2)
# 0.6428571428571429

td.hamming(s1, s2)
# 5
</code></pre>
<h3 id="表示学习">表示学习</h3>
<p>基于表示学习的文本相似度计算方法的思路如下：</p>
<ol>
<li>利用表示学习方法将不定长的文本表示为定长的实值向量。</li>
<li>计算转换后的实值向量相似度，用于表示两个文本的相似度。</li>
</ol>
<p>关于文本表示学习和实值向量相似度计算请参见之前博客：<a href="/cn/2018/10/word-embeddings/">词向量 (Word Embeddings)</a>，<a href="/cn/2019/01/similarity-and-distance-measurement/">相似性和距离度量 (Similarity &amp; Distance Measurement)</a>，<a href="/cn/2020/03/pre-trained-model-for-nlp/">预训练自然语言模型 (Pre-trained Models for NLP)</a>。</p>
<h2 id="文本词法-句法和语义角度">文本词法，句法和语义角度</h2>
<blockquote>
<p>本节主要参考自《基于词法、句法和语义的句子相似度计算方法》<sup id="fnref:10"><a href="#fn:10" class="footnote-ref" role="doc-noteref">10</a></sup>。</p>
</blockquote>
<p>一段文本的内容分析由浅及深可以分为词法，句法和语义三个层次。</p>
<ol>
<li>词法，以词为对象，研究包括分词，词性和命名实体等。</li>
<li>句法，以句子为对象，研究包括句子成分和句子结构等。</li>
<li>语义，研究文字所表达的含义和蕴含的知识等。</li>
</ol>
<p>词法和句法可以统一成为语法，如下图所示：</p>
<p><img src="/images/cn/2020-10-31-text-similarity/lexical-syntax.png" alt=""></p>
<h3 id="词法">词法</h3>
<p>词法层以单个句子作为输入，其输出为已标记（词性，命名实体等）的词汇序列。</p>
<p><img src="/images/cn/2020-10-31-text-similarity/lexical-demo.png" alt=""></p>
<p>词汇序列的相似度计算可以采用上文中的距离度量等方式实现。</p>
<h3 id="句法">句法</h3>
<p>句法层用于研究句子各个组成部分及其排列顺序，将文本分解为句法单位，以理解句法元素的排列方式。句法层接收词法层分析后的将其转化为依存图。</p>
<p><img src="/images/cn/2020-10-31-text-similarity/syntax-demo.png" alt=""></p>
<p>对于依存图，我们可以利用三元组 <code>$S = \left(V_1, E, V_2\right)$</code> 表示任意一个依存关系，然后通过统计计算两个文本的依存图的三元组集合之间的相似度来评价句法层的相似度。此外，也可以从树结构的角度直接评价依存句法的相似度，更多细节可参考相关论文 <sup id="fnref:11"><a href="#fn:11" class="footnote-ref" role="doc-noteref">11</a></sup> <sup id="fnref:12"><a href="#fn:12" class="footnote-ref" role="doc-noteref">12</a></sup>。</p>
<h3 id="语义">语义</h3>
<p>语义层用于研究文本所蕴含的意义。例如“父亲”和“爸爸”在词法层完全不同，但在语义层却具有相同的含义。针对语义相似度的两种深度学习范式如下：</p>
<p><img src="/images/cn/2020-10-31-text-similarity/deep-learning-paradigms-for-text-similarity.png" alt=""></p>
<p>第一种范式首先通过神经网络获取文本的向量表示，再通过向量之间的相似度来衡量文本的语义相似度。这种范式在提取特征时不考虑另一个文本的信息，更适合做大规模的语义相似召回，例如：DSSM <sup id="fnref:13"><a href="#fn:13" class="footnote-ref" role="doc-noteref">13</a></sup>，ARC-I <sup id="fnref:14"><a href="#fn:14" class="footnote-ref" role="doc-noteref">14</a></sup>，CNTN <sup id="fnref:15"><a href="#fn:15" class="footnote-ref" role="doc-noteref">15</a></sup>，LSTM-RNN <sup id="fnref:16"><a href="#fn:16" class="footnote-ref" role="doc-noteref">16</a></sup> 等。</p>
<p>第二种范式首先通过深度模型提取两个文本的交叉特征，得到匹配信号张量，再聚合为匹配分数。这种范式同时考虑两个文本的输入信息，更适合做小规模的语义相似精排，例如：ARC-II <sup id="fnref1:14"><a href="#fn:14" class="footnote-ref" role="doc-noteref">14</a></sup>，MatchPyramid <sup id="fnref:17"><a href="#fn:17" class="footnote-ref" role="doc-noteref">17</a></sup>，Match-SRNN <sup id="fnref:18"><a href="#fn:18" class="footnote-ref" role="doc-noteref">18</a></sup>，Duet <sup id="fnref:19"><a href="#fn:19" class="footnote-ref" role="doc-noteref">19</a></sup> 等。</p>
<h2 id="文本长度角度">文本长度角度</h2>
<p>从文本长度角度出发，我们可以粗略的将文本分类为<strong>短文本</strong>和<strong>长文本</strong>。<strong>短文本</strong>包括“字词”，“短语”，“句子”等相对比较短的文本形式，<strong>长文本</strong>包括“段落”，“篇章”等相对比较长的文本形式。</p>
<h3 id="短文本-v-s-短文本">短文本 v.s. 短文本</h3>
<p>短文本同短文本的常见比较形式有：关键词（字词）同文本标题（句子）的匹配，相似查询（句子）的匹配等。如果单纯的希望获取字符层面的差异，可以通过距离度量进行相似度比较。如果需要从语义的角度获取相似度，则可以利用表示学习对需要比对的文本进行表示，在通过语义向量之间的相似程度来衡量原始文本之间的相似度，详情可参见上文。</p>
<h3 id="短文本-v-s-长文本">短文本 v.s. 长文本</h3>
<p>短文本同长文本的比较多见于文档的搜索，即给定相关的查询（字词），给出最相关的文档（段落和篇章）。对于这类问题常见的解决方式是对长文本利用 TF-IDF，BM25等方法或进行主题建模后，再同查询的关键词进行匹配计算相似度度。</p>
<h3 id="长文本-v-s-长文本">长文本 v.s. 长文本</h3>
<p>长文本同长文本的比较多见于文档的匹配和去重，对于这类问题常见的解决方式是利用关键词提取获取长文本的特征向量，然后利用特征向量之间的相似度衡量对应文本的相似程度。在针对海量文本的去重，还以应用 <a href="/cn/2020/08/nearest-neighbor-search/">SimHash</a> 等技术对文本生成一个指纹，从而实现快速去重。</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://zh.wikipedia.org/wiki/%E4%B8%BB%E9%A2%98%E6%A8%A1%E5%9E%8B">https://zh.wikipedia.org/wiki/主题模型</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Manning, C. D., Schütze, H., &amp; Raghavan, P. (2008). <em>Introduction to information retrieval</em>. Cambridge university press.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>Mihalcea, R., &amp; Tarau, P. (2004, July). Textrank: Bringing order into text. In <em>Proceedings of the 2004 conference on empirical methods in natural language processing</em> (pp. 404-411).&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>Page, L., Brin, S., Motwani, R., &amp; Winograd, T. (1999). <em>The PageRank citation ranking: Bringing order to the web</em>. Stanford InfoLab.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p>Deerwester, S., Dumais, S. T., Furnas, G. W., Landauer, T. K., &amp; Harshman, R. (1990). Indexing by latent semantic analysis. <em>Journal of the American society for information science</em>, 41(6), 391-407.&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6">
<p>Hofmann, T. (1999, August). Probabilistic latent semantic indexing. In <em>Proceedings of the 22nd annual international ACM SIGIR conference on Research and development in information retrieval</em> (pp. 50-57).&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:7">
<p>Blei, D. M., Ng, A. Y., &amp; Jordan, M. I. (2003). Latent dirichlet allocation. <em>Journal of machine Learning research</em>, 3(Jan), 993-1022.&#160;<a href="#fnref:7" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:8">
<p>Rickjin(靳志辉). 2013. LDA数学八卦&#160;<a href="#fnref:8" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:9">
<p>Teh, Y. W., Jordan, M. I., Beal, M. J., &amp; Blei, D. M. (2006). Hierarchical dirichlet processes. <em>Journal of the american statistical association</em>, 101(476), 1566-1581.&#160;<a href="#fnref:9" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:10">
<p>翟社平, 李兆兆, 段宏宇, 李婧, &amp; 董迪迪. (2019). 基于词法, 句法和语义的句子相似度计算方法. <em>东南大学学报: 自然科学版</em>, 49(6), 1094-1100.&#160;<a href="#fnref:10" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:11">
<p>Zhang, K., &amp; Shasha, D. (1989). Simple fast algorithms for the editing distance between trees and related problems. <em>SIAM journal on computing</em>, 18(6), 1245-1262.&#160;<a href="#fnref:11" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:12">
<p>Meila, M., &amp; Jordan, M. I. (2000). Learning with mixtures of trees. <em>Journal of Machine Learning Research</em>, 1(Oct), 1-48.&#160;<a href="#fnref:12" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:13">
<p>Huang, P. S., He, X., Gao, J., Deng, L., Acero, A., &amp; Heck, L. (2013, October). Learning deep structured semantic models for web search using clickthrough data. In <em>Proceedings of the 22nd ACM international conference on Information &amp; Knowledge Management</em> (pp. 2333-2338).&#160;<a href="#fnref:13" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:14">
<p>Hu, B., Lu, Z., Li, H., &amp; Chen, Q. (2014). Convolutional neural network architectures for matching natural language sentences. In <em>Advances in neural information processing systems</em> (pp. 2042-2050).&#160;<a href="#fnref:14" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:14" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:15">
<p>Qiu, X., &amp; Huang, X. (2015, June). Convolutional neural tensor network architecture for community-based question answering. In <em>Twenty-Fourth international joint conference on artificial intelligence</em>.&#160;<a href="#fnref:15" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:16">
<p>Palangi, H., Deng, L., Shen, Y., Gao, J., He, X., Chen, J., &hellip; &amp; Ward, R. (2016). Deep sentence embedding using long short-term memory networks: Analysis and application to information retrieval. <em>IEEE/ACM Transactions on Audio, Speech, and Language Processing</em>, 24(4), 694-707.&#160;<a href="#fnref:16" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:17">
<p>Pang, L., Lan, Y., Guo, J., Xu, J., Wan, S., &amp; Cheng, X. (2016). Text matching as image recognition. In <em>Proceedings of the Thirtieth AAAI Conference on Artificial Intelligence (AAAI'16)</em>. (pp. 2793–2799).&#160;<a href="#fnref:17" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:18">
<p>Wan, S., Lan, Y., Xu, J., Guo, J., Pang, L., &amp; Cheng, X. (2016, July). Match-SRNN: modeling the recursive matching structure with spatial RNN. In <em>Proceedings of the Twenty-Fifth International Joint Conference on Artificial Intelligence</em> (pp. 2922-2928).&#160;<a href="#fnref:18" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:19">
<p>Mitra, B., Diaz, F., &amp; Craswell, N. (2017, April). Learning to match using local and distributed representations of text for web search. In <em>Proceedings of the 26th International Conference on World Wide Web</em> (pp. 1291-1299).&#160;<a href="#fnref:19" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>

        ]]></description></item><item><title>图嵌入 (Graph Embedding) 和图神经网络 (Graph Neural Network)</title><link>https://zeqiang.fun/cn/2020/04/graph-embedding-and-gnn/</link><pubDate>Sat, 11 Apr 2020 00:00:00 +0000</pubDate><guid>https://zeqiang.fun/cn/2020/04/graph-embedding-and-gnn/</guid><description><![CDATA[
        <p>图（Graph / Network）数据类型可以自然地表达物体和物体之间的联系，在我们的日常生活与工作中无处不在。例如：微信和新浪微博等构成了人与人之间的社交网络；互联网上成千上万个页面构成了网页链接网络；国家城市间的运输交通构成了物流网络。</p>
<figure>
  <img data-src="/images/cn/2020-04-11-graph-embedding-and-gnn/graph.png" class="lazyload"/>
  <figcaption><p class="figcaption">图片来源：<a href="https://www.allthingsdistributed.com/2019/12/power-of-relationships.html">The power of relationships in data</a></p></figcaption>
</figure>
<p>通常定义一个图 <code>$G = \left(V, E\right)$</code>，其中 <code>$V$</code> 为<strong>顶点（Vertices）<strong>集合，<code>$E$</code> 为</strong>边（Edges）<strong>集合。对于一条边 <code>$e = u, v$</code> 包含两个</strong>端点（Endpoints）</strong> <code>$u$</code> 和 <code>$v$</code>，同时 <code>$u$</code> 可以称为 <code>$v$</code> 的<strong>邻居（Neighbor）</strong>。当所有的边为有向边时，图称之为<strong>有向（Directed）<strong>图，当所有边为无向边时，图称之为</strong>无向（Undirected）<strong>图。对于一个顶点 <code>$v$</code>，令 <code>$d \left(v\right)$</code> 表示连接的边的数量，称之为</strong>度（Degree）</strong>。对于一个图 <code>$G = \left(V, E\right)$</code>，其<strong>邻接矩阵（Adjacency Matrix）</strong> <code>$A \in \mathbb{A}^{|V| \times |V|}$</code> 定义为：</p>
<p><code>$$ A_{i j}=\left\{\begin{array}{ll} 1 &amp; \text { if }\left\{v_{i}, v_{j}\right\} \in E \text { and } i \neq j \\ 0 &amp; \text { otherwise } \end{array}\right. $$</code></p>
<p>作为一个典型的非欧式数据，对于图数据的分析主要集中在节点分类，链接预测和聚类等。对于图数据而言，**图嵌入（Graph / Network Embedding）<strong>和</strong>图神经网络（Graph Neural Networks, GNN）**是两个类似的研究领域。图嵌入旨在将图的节点表示成一个低维向量空间，同时保留网络的拓扑结构和节点信息，以便在后续的图分析任务中可以直接使用现有的机器学习算法。一些基于深度学习的图嵌入同时也属于图神经网络，例如一些基于图自编码器和利用无监督学习的图卷积神经网络等。下图描述了图嵌入和图神经网络之间的差异：</p>
<figure>
  <img data-src="/images/cn/2020-04-11-graph-embedding-and-gnn/graph-embedding-vs-graph-neural-networks.png" class="lazyload"/>
  
</figure>
<div class="blockquote" style='border-left: 4px solid #F66E40;'>本文中<strong>图嵌入</strong>和<strong>网络表示学习</strong>均表示 Graph / Network Embedding。</div>
<h2 id="图嵌入">图嵌入</h2>
<blockquote>
<p>本节内容主要参考自：<br>
A Comprehensive Survey of Graph Embedding: Problems, Techniques and Applications <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup><br>
Graph Embedding Techniques, Applications, and Performance: A Survey <sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup><br>
Representation Learning on Graphs: Methods and Applications <sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></p>
</blockquote>
<p>使用邻接矩阵的网络表示存在计算效率的问题，邻接矩阵 <code>$A$</code> 使用 <code>$|V| \times |V|$</code> 的存储空间表示一个图，随着节点个数的增长，这种表示所需的空间成指数增长。同时，在邻接矩阵中绝大多数是 0，数据的稀疏性使得快速有效的学习方式很难被应用。</p>
<p>网路表示学习是指学习得到网络中节点的低维向量表示，形式化地，网络表示学习的目标是对每个节点 <code>$v \in V$</code> 学习一个实值向量 <code>$R_v \in \mathbb{R}^k$</code>，其中 <code>$k \ll |V|$</code> 表示向量的维度。经典的 Zachary&rsquo;s karate club 网络的嵌入可视化如下图所示：</p>
<figure>
  <img data-src="/images/cn/2020-04-11-graph-embedding-and-gnn/karate-graph-embedding.png" class="lazyload"/>
  
</figure>
<h3 id="random-walk">Random Walk</h3>
<p>基于随机游走的图嵌入通过使得图上一个短距的随机游走中共现的节点具有更相似的表示的方式来优化节点的嵌入。</p>
<h4 id="deepwalk">DeepWalk</h4>
<p>DeepWalk <sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> 算法主要包含两个部分：一个随机游走序列生成器和一个更新过程。随机游走序列生成器首先在图 <code>$G$</code> 中均匀地随机抽样一个随机游走 <code>$\mathcal{W}_{v_i}$</code> 的根节点 <code>$v_i$</code>，接着从节点的邻居中均匀地随机抽样一个节点直到达到设定的最大长度 <code>$t$</code>。对于一个生成的以 <code>$v_i$</code> 为中心左右窗口为 <code>$w$</code> 的随机游走序列 <code>$v_{i-w}, \dotsc, v_{i-1}, v_i, v_{i+1}, \dotsc, v_{i+m}$</code>，DeepWalk 利用 SkipGram 算法通过最大化以 <code>$v_i$</code> 为中心，左右 <code>$w$</code> 为窗口的同其他节点共现概率来优化模型：</p>
<p><code>$$ \text{Pr} \left(\left\{v_{i-w}, \dotsc, v_{i+w}\right\} \setminus v_i \mid \Phi \left(v_i\right)\right) = \prod_{j=i-w, j \neq i}^{i+w} \text{Pr} \left(v_j \mid \Phi \left(v_i\right)\right) $$</code></p>
<p>DeepWalk 和 Word2Vec 的类比如下表所示：</p>
<table>
  <thead>
      <tr>
          <th>模型</th>
          <th>目标</th>
          <th>输入</th>
          <th>输出</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Word2Vec</td>
          <td>词</td>
          <td>句子</td>
          <td>词嵌入</td>
      </tr>
      <tr>
          <td>DeepWalk</td>
          <td>节点</td>
          <td>节点序列</td>
          <td>节点嵌入</td>
      </tr>
  </tbody>
</table>
<h4 id="node2vec">node2vec</h4>
<p>node2vec <sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup> 通过改变随机游走序列生成的方式进一步扩展了 DeepWalk 算法。DeepWalk 选取随机游走序列中下一个节点的方式是均匀随机分布的，而 node2vec 通过引入两个参数 <code>$p$</code> 和 <code>$q$</code>，将<strong>宽度优先搜索</strong>和<strong>深度优先搜索</strong>引入了随机游走序列的生成过程。 宽度优先搜索注重邻近的节点并刻画了相对局部的一种网络表示， 宽度优先中的节点一般会出现很多次，从而降低刻画中心节点的邻居节点的方差， 深度优先搜索反映了更高层面上的节点之间的同质性。</p>
<figure>
  <img data-src="/images/cn/2020-04-11-graph-embedding-and-gnn/node2vec.png" class="lazyload"/>
  
</figure>
<p>node2vec 中的两个参数 <code>$p$</code> 和 <code>$q$</code> 控制随机游走序列的跳转概率。假设上一步游走的边为 <code>$\left(t, v\right)$</code>， 那么对于节点 <code>$v$</code> 的不同邻居，node2vec 根据 <code>$p$</code> 和 <code>$q$</code> 定义了不同的邻居的跳转概率，<code>$p$</code> 控制跳向上一个节点的邻居的概率，<code>$q$</code> 控制跳向上一个节点的非邻居的概率，具体的未归一的跳转概率值 <code>$\pi_{vx} = \alpha_{pq} \left(t, x\right)$</code> 如下所示：</p>
<p><code>$$ \alpha_{p q}(t, x)=\left\{\begin{array}{cl} \dfrac{1}{p}, &amp; \text { if } d_{t x}=0 \\ 1, &amp; \text { if } d_{t x}=1 \\ \dfrac{1}{q}, &amp; \text { if } d_{t x}=2 \end{array}\right. $$</code></p>
<p>其中，<code>$d_{tx}$</code> 表示节点 <code>$t$</code> 和 <code>$x$</code> 之间的最短距离。为了获得最优的超参数 <code>$p$</code> 和 <code>$q$</code> 的取值，node2vec 通过半监督形式，利用网格搜索最合适的参数学习节点表示。</p>
<h4 id="app">APP</h4>
<p>之前的基于随机游走的图嵌入方法，例如：DeepWalk，node2vec 等，都无法保留图中的非对称信息。然而非对称性在很多问题，例如：社交网络中的链路预测、电商中的推荐等，中至关重要。在有向图和无向图中，非对称性如下图所示：</p>
<figure>
  <img data-src="/images/cn/2020-04-11-graph-embedding-and-gnn/asymmetric-proximity.png" class="lazyload"/>
  
</figure>
<p>为了保留图的非对称性，对于每个节点 <code>$v$</code> 设置两个不同的角色：源和目标，分别用 <code>$\overrightarrow{s_{v}}$</code> 和 <code>$\overrightarrow{t_{v}}$</code> 表示。对于每个从 <code>$u$</code> 开始以 <code>$v$</code> 结尾的采样序列，利用 <code>$(u, v)$</code> 表示采样的节点对。则利用源节点 <code>$u$</code> 预测目标节点 <code>$v$</code> 的概率如下：</p>
<p><code>$$ p(v | u)=\frac{\exp (\overrightarrow{s_{u}} \cdot \overrightarrow{t_{v}})}{\sum_{n \in V} \exp (\overrightarrow{s_{u}} \cdot \overrightarrow{t_{n}})} $$</code></p>
<p>通过 Skip-Gram 和负采样对模型进行优化，损失函数如下：</p>
<p><code>$$ \begin{aligned} \ell &amp;= \log \sigma(\overrightarrow{s_{u}} \cdot \overrightarrow{t_{v}})+k \cdot E_{t_{n} \sim P_{D}}[\log \sigma(-\overrightarrow{s_{u}} \cdot \overrightarrow{t_{n}})] \\ &amp;= \sum_{u} \sum_{v} \# \text {Sampled}_{u}(v) \cdot \left(\log \sigma(\overrightarrow{s_{u}} \cdot \overrightarrow{t_{v}}) + k \cdot E_{t_{n} \sim P_{D}}[\log \sigma(-\overrightarrow{s_{u}} \cdot \overrightarrow{t_{n}})]\right) \end{aligned} $$</code></p>
<p>其中，我们根据分布 <code>$P_D \left(n\right) \sim \dfrac{1}{|V|}$</code> 随机负采样 <code>$k$</code> 个节点对，<code>$\# \text{Sampled}_{u}(v)$</code> 为采样的 <code>$\left(u, v\right)$</code> 对的个数，<code>$\sigma$</code> 为 sigmoid 函数。通常情况下，<code>$\# \text{Sampled}_{u}(v) \neq \# \text{Sampled}_{v}(u)$</code>，即 <code>$\left(u, v\right)$</code> 和 <code>$\left(v, u\right)$</code> 的观测数量是不同的。模型利用 Monte-Carlo End-Point 采样方法 <sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup> 随机的以 <code>$v$</code> 为起点和 <code>$\alpha$</code> 为停止概率采样 <code>$p$</code> 条路径。这种采样方式可以用于估计任意一个节点对之间的 Rooted PageRank <sup id="fnref:7"><a href="#fn:7" class="footnote-ref" role="doc-noteref">7</a></sup> 值，模型利用这个值估计由 <code>$v$</code> 到达 <code>$u$</code> 的概率。</p>
<h3 id="matrix-fractorization">Matrix Fractorization</h3>
<h4 id="grarep">GraRep</h4>
<p>GraRep <sup id="fnref:8"><a href="#fn:8" class="footnote-ref" role="doc-noteref">8</a></sup> 提出了一种基于矩阵分解的图嵌入方法。对于一个图 <code>$G$</code>，利用邻接矩阵 <code>$S$</code> 定义图的度矩阵：</p>
<p><code>$$ D_{i j}=\left\{\begin{array}{ll} \sum_{p} S_{i p}, &amp; \text { if } i=j \\ 0, &amp; \text { if } i \neq j \end{array}\right. $$</code></p>
<p>则一阶转移概率矩阵定义如下：</p>
<p><code>$$ A = D^{-1} S $$</code></p>
<p>其中，<code>$A_{i, j}$</code> 表示通过一步由 <code>$v_i$</code> 转移到 <code>$v_j$</code> 的概率。所谓的全局特征包含两个部分：</p>
<ol>
<li>捕获两个节点之间的长距离特征</li>
<li>分别考虑按照不同转移步数的连接</li>
</ol>
<p>下图展示了 <code>$k = 1, 2, 3, 4$</code> 情况下的强（上）弱（下）关系：</p>
<figure>
  <img data-src="/images/cn/2020-04-11-graph-embedding-and-gnn/grarep.png" class="lazyload"/>
  
</figure>
<p>利用 Skip-Gram 和 NCE（noise contrastive estimation）方法，对于一个 <code>$k$</code> 阶转移，可以将模型归结到一个矩阵 <code>$Y_{i, j}^k$</code> 的分解问题：</p>
<p><code>$$ Y_{i, j}^{k}=W_{i}^{k} \cdot C_{j}^{k}=\log \left(\frac{A_{i, j}^{k}}{\sum_{t} A_{t, j}^{k}}\right)-\log (\beta) $$</code></p>
<p>其中，<code>$W$</code> 和 <code>$C$</code> 的每一行分别为节点 <code>$w$</code> 和 <code>$c$</code> 的表示，<code>$\beta = \lambda / N$</code>，<code>$\lambda$</code> 为负采样的数量，<code>$N$</code> 为图中边的个数。</p>
<p>之后为了减少噪音，模型将 <code>$Y^k$</code> 中所有的负值替换为 0，通过 SVD（方法详情见参见<a href="/cn/2017/12/evd-svd-and-pca/">之前博客</a>）得到节点的 <code>$d$</code> 维表示：</p>
<p><code>$$ \begin{aligned} X_{i, j}^{k} &amp;= \max \left(Y_{i, j}^{k}, 0\right) \\ X^{k} &amp;= U^{k} \Sigma^{k}\left(V^{k}\right)^{T} \\ X^{k} \approx X_{d}^{k} &amp;= U_{d}^{k} \Sigma_{d}^{k}\left(V_{d}^{k}\right)^{T} \\ X^{k} \approx X_{d}^{k} &amp;= W^{k} C^{k} \\ W^{k} &amp;= U_{d}^{k}\left(\Sigma_{d}^{k}\right)^{\frac{1}{2}} \\ C^{k} &amp;= \left(\Sigma_{d}^{k}\right)^{\frac{1}{2}} V_{d}^{k T} \end{aligned} $$</code></p>
<p>最终，通过对不同 <code>$k$</code> 的表示进行拼接得到节点最终的表示。</p>
<h4 id="hope">HOPE</h4>
<p>HOPE <sup id="fnref:9"><a href="#fn:9" class="footnote-ref" role="doc-noteref">9</a></sup> 对于每个节点最终生成两个嵌入表示：一个是作为源节点的嵌入表示，另一个是作为目标节点的嵌入表示。模型通过近似高阶相似性来保留非对称传递性，其优化目标为：</p>
<p><code>$$ \min \left\|\mathbf{S}-\mathbf{U}^{s} \cdot \mathbf{U}^{t^{\top}}\right\|_{F}^{2} $$</code></p>
<p>其中，<code>$\mathbf{S}$</code> 为相似矩阵，<code>$\mathbf{U}^s$</code> 和 <code>$\mathbf{U}^t$</code> 分别为源节点和目标节点的向量表示。下图展示了嵌入向量可以很好的保留非对称传递性：</p>
<figure>
  <img data-src="/images/cn/2020-04-11-graph-embedding-and-gnn/hope.png" class="lazyload"/>
  
</figure>
<p>对于 <code>$\mathbf{S}$</code> 有多种可选近似度量方法：Katz Index，Rooted PageRank（RPR），Common Neighbors（CN），Adamic-Adar（AA）。这些度量方法可以分为两类：全局近似（Katz Index 和 RPR）和局部近似（CN 和 AA）。</p>
<p>算法采用了一个广义 SVD 算法（JDGSVD）来解决使用原始 SVD 算法计算复杂度为<code>$O \left(N^3\right)$</code> 过高的问题，从而使得算法可以应用在更大规模的图上。</p>
<h3 id="meta-paths">Meta Paths</h3>
<h4 id="matapath2vec">matapath2vec</h4>
<p>matapath2vec <sup id="fnref:10"><a href="#fn:10" class="footnote-ref" role="doc-noteref">10</a></sup> 提出了一种基于元路径的异构网络表示学习方法。在此我们引入 3 个定义：</p>
<ol>
<li>**异构网络（(Heterogeneous information network，HIN）**可以定义为一个有向图 <code>$G = \left(V, E\right)$</code>，一个节点类型映射 <code>$\tau: V \to A$</code> 和一个边类型映射 <code>$\phi: E \to R$</code>，其中对于 <code>$v \in V$</code> 有 <code>$\tau \left(v\right) \in A$</code>，<code>$e \in E$</code> 有 <code>$\phi \left(e\right) \in R$</code>，且 <code>$|A| + |R| &gt; 1$</code>。</li>
<li>**网络模式（Network schema）**定义为 <code>$T_G = \left(A, R\right)$</code>，为一个包含节点类型映射 <code>$\tau \left(v\right) \in A$</code> 和边映射 <code>$\phi \left(e\right) \in R$</code> 异构网络的 <code>$G = \left(V, E\right)$</code> 的元模板。</li>
<li>**元路径（Meta-path）**定义为网络模式 <code>$T_G = \left(A, R\right)$</code> 上的一条路径 <code>$P$</code>，形式为 <code>$A_{1} \stackrel{R_{1}}{\longrightarrow} A_{2} \stackrel{R_{2}}{\longrightarrow} \cdots \stackrel{R_{l}}{\longrightarrow} A_{l+1}$</code>。</li>
</ol>
<p>下图展示了一个学术网络和部分元路径：</p>
<figure>
  <img data-src="/images/cn/2020-04-11-graph-embedding-and-gnn/metapaths.png" class="lazyload"/>
  
</figure>
<p>其中，APA 表示一篇论文的共同作者，APVPA 表示两个作者在同一个地方发表过论文。</p>
<p>metapath2vec 采用了基于元路径的随机游走来生成采样序列，这样就可以保留原始网络中的语义信息。对于一个给定的元路径模板 <code>$P: A_{1} \stackrel{R_{1}}{\longrightarrow} A_{2} \stackrel{R_{2}}{\longrightarrow} \cdots A_{t} \stackrel{R_{t}}{\longrightarrow} A_{t+1} \cdots \stackrel{R_{l}}{\longrightarrow} A_{l}$</code>，第 <code>$i$</code> 步的转移概率为：</p>
<p><code>$$ p\left(v^{i+1} | v_{t}^{i}, P\right)=\left\{\begin{array}{ll} \dfrac{1}{\left|N_{t+1}\left(v_{t}^{i}\right)\right|} &amp; \left(v_{t}^{i}, v^{i+1}\right) \in E, \phi\left(v^{i+1}\right)=A_{t+1} \\ 0 &amp; \left(v_{t}^{i}, v^{i+1}\right) \in E, \phi\left(v^{i+1}\right) \neq A_{t+1} \\ 0 &amp; \left(v_{t}^{i}, v^{i+1}\right) \notin E \end{array}\right. $$</code></p>
<p>其中，<code>$v^i_t \in A_t$</code>，<code>$N_{t+1} \left(v^i_t\right)$</code> 表示节点 <code>$v^i_t$</code> 类型为 <code>$A_{t+1}$</code> 的邻居。之后，则采用了类似 DeepWalk 的方式进行训练得到节点表示。</p>
<h4 id="hin2vec">HIN2Vec</h4>
<p>HIN2Vec <sup id="fnref:11"><a href="#fn:11" class="footnote-ref" role="doc-noteref">11</a></sup> 提出了一种利用多任务学习通过多种关系进行节点和元路径表示学习的方法。模型最初是希望通过一个多分类模型来预测任意两个节点之间所有可能的关系。假设对于任意两个节点，所有可能的关系集合为 <code>$R = \{\text{P-P, P-A, A-P, P-P-P, P-P-A, P-A-P, A-P-P, A-P-A}\}$</code>。假设一个实例 <code>$P_1$</code> 和 <code>$A_1$</code> 包含两种关系：<code>$\text{P-A}$</code> 和 <code>$\text{P-P-A}$</code>，则对应的训练数据为 <code>$\langle x: P_1, y: A_1, output: \left[0, 1, 0, 0, 1, 0, 0, 0\right] \rangle$</code>。</p>
<p>但实际上，扫描整个网络寻找所有可能的关系是不现实的，因此 HIN2Vec 将问题简化为一个给定两个节点判断之间是否存在一个关系的二分类问题，如下图所示：</p>
<figure>
  <img data-src="/images/cn/2020-04-11-graph-embedding-and-gnn/hin2vec.png" class="lazyload"/>
  
</figure>
<p>模型的三个输入分别为节点 <code>$x$</code> 和 <code>$y$</code>，以及关系 <code>$r$</code>。在隐含层输入被转换为向量 <code>$W_{X}^{\prime} \vec{x}, W_{Y}^{\prime} \vec{y}$</code> 和 <code>$f_{01}\left(W_{R}^{\prime} \vec{r}\right)$</code>。需要注意对于关系 <code>$r$</code>，模型应用了一个正则化函数 <code>$f_{01} \left(\cdot\right)$</code> 使得 <code>$r$</code> 的向量介于 <code>$0$</code> 和 <code>$1$</code> 之间。之后采用逐元素相乘对三个向量进行汇总 <code>$W_{X}^{\prime} \vec{x} \odot W_{Y}^{\prime} \vec{y} \odot f_{01}\left(W_{R}^{\prime} \vec{r}\right)$</code>。在最后的输出层，通过计算 <code>$sigmoid \left(\sum W_{X}^{\prime} \vec{x} \odot W_{Y}^{\prime} \vec{y} \odot f_{01}\left(W_{R}^{\prime} \vec{r}\right)\right)$</code> 得到最终的预测值。</p>
<p>在生成训练数据时，HIN2Vec 采用了完全随机游走进行节点采样，而非 metapath2vec 中的按照给定的元路径的方式。通过随机替换 <code>$x, y, r$</code> 中的任何一个可以生成负样本，但当网络中的关系数量较少，节点数量远远大于关系数量时，这种方式很可能产生错误的负样本，因此 HIN2Vec 只随机替换 <code>$x, y$</code>，保持 <code>$r$</code> 不变。</p>
<h3 id="deep-learning">Deep Learning</h3>
<h4 id="sdne">SDNE</h4>
<p>SDNE <sup id="fnref:12"><a href="#fn:12" class="footnote-ref" role="doc-noteref">12</a></sup> 提出了一种利用自编码器同时优化一阶和二阶相似度的图嵌入算法，学习得到的向量能够保留局部和全局的结构信息。SDNE 使用的网络结构如下图所示：</p>
<figure>
  <img data-src="/images/cn/2020-04-11-graph-embedding-and-gnn/sdne.png" class="lazyload"/>
  
</figure>
<p>对于二阶相似度，自编码器的目标是最小化输入和输出的重构误差。SDNE 采用邻接矩阵作为自编码器的输入，<code>$\mathbf{x}_i = \mathbf{s}_i$</code>，每个 <code>$\mathbf{s}_i$</code> 包含了节点 <code>$v_i$</code> 的邻居结构信息。模型的损失函数如下：</p>
<p><code>$$ \mathcal{L}=\sum_{i=1}^{n}\left\|\hat{\mathbf{x}}_{i}-\mathbf{x}_{i}\right\|_{2}^{2} $$</code></p>
<p>由于网络的稀疏性，邻接矩阵中的非零元素远远少于零元素，因此模型采用了一个带权的损失函数：</p>
<p><code>$$ \begin{aligned} \mathcal{L}_{2nd} &amp;=\sum_{i=1}^{n}\left\|\left(\hat{\mathbf{x}}_{i}-\mathbf{x}_{i}\right) \odot \mathbf{b}_{i}\right\|_{2}^{2} \\ &amp;=\|(\hat{X}-X) \odot B\|_{F}^{2} \end{aligned} $$</code></p>
<p>其中，<code>$\odot$</code> 表示按位乘，<code>$\mathbf{b}_i = \left\{b_{i, j}\right\}_{j=1}^{n}$</code>，如果 <code>$s_{i, j} = 0$</code> 则 <code>$b_{i, j} = 1$</code> 否则 <code>$b_{i, j} = \beta &gt; 1$</code>。</p>
<p>对于一阶相似度，模型利用了一个监督学习模块最小化节点在隐含空间中距离。损失函数如下：</p>
<p><code>$$ \begin{aligned} \mathcal{L}_{1st} &amp;=\sum_{i, j=1}^{n} s_{i, j}\left\|\mathbf{y}_{i}^{(K)}-\mathbf{y}_{j}^{(K)}\right\|_{2}^{2} \\ &amp;=\sum_{i, j=1}^{n} s_{i, j}\left\|\mathbf{y}_{i}-\mathbf{y}_{j}\right\|_{2}^{2} \end{aligned} $$</code></p>
<p>最终，模型联合损失函数如下：</p>
<p><code>$$ \begin{aligned} \mathcal{L}_{mix} &amp;=\mathcal{L}_{2nd}+\alpha \mathcal{L}_{1st}+\nu \mathcal{L}_{reg} \\ &amp;=\|(\hat{X}-X) \odot B\|_{F}^{2}+\alpha \sum_{i, j=1}^{n} s_{i, j}\left\|\mathbf{y}_{i}-\mathbf{y}_{j}\right\|_{2}^{2}+\nu \mathcal{L}_{reg} \end{aligned} $$</code></p>
<p>其中，<code>$\mathcal{L}_{reg}$</code> 为 L2 正则项。</p>
<h4 id="dngr">DNGR</h4>
<p>DNGR <sup id="fnref:13"><a href="#fn:13" class="footnote-ref" role="doc-noteref">13</a></sup> 提出了一种利用基于 Stacked Denoising Autoencoder（SDAE）提取特征的网络表示学习算法。算法的流程如下图所示：</p>
<figure>
  <img data-src="/images/cn/2020-04-11-graph-embedding-and-gnn/dngr.png" class="lazyload"/>
  
</figure>
<p>模型首先利用 Random Surfing 得到一个概率共现（PCO）矩阵，之后利用其计算得到 PPMI 矩阵，最后利用 SDAE 进行特征提取得到节点的向量表示。</p>
<p>对于传统的将图结构转换为一个线性序列方法存在几点缺陷：</p>
<ol>
<li>采样序列边缘的节点的上下文信息很难被捕捉。</li>
<li>很难直接确定游走的长度和步数等超参数，尤其是对于大型网络来说。</li>
</ol>
<p>受 PageRank 思想影响，作者采用了 Random Surfing 模型。定义转移矩阵 <code>$A$</code>，引入行向量 <code>$p_k$</code>，第 <code>$j$</code> 个元素表示通过 <code>$k$</code> 步转移之后到达节点 <code>$j$</code> 的概率。<code>$p_0$</code> 为一个初始向量，其仅第 <code>$i$</code> 个元素为 1，其它均为 0。在考虑以 <code>$1 - \alpha$</code> 的概率返回初始节点的情况下有：</p>
<p><code>$$ p_{k}=\alpha \cdot p_{k-1} A+(1-\alpha) p_{0} $$</code></p>
<p>在不考虑返回初始节点的情况下有：</p>
<p><code>$$ p_{k}^{*}=p_{k-1}^{*} A=p_{0} A^{k} $$</code></p>
<p>直观而言，两个节点越近，两者的关系越亲密，因此通过同当前节点的相对距离来衡量上下文节点的重要性是合理的。基于此，第 <code>$i$</code> 个节点的表示可以用如下方式构造：</p>
<p><code>$$ r=\sum_{k=1}^{K} w(k) \cdot p_{k}^{*} $$</code></p>
<p>其中，<code>$w \left(\cdot\right)$</code> 是一个衰减函数。</p>
<p>利用 PCO 计算得到 PPMI 后，再利用一个 SDAE 进行特征提取。Stacking 策略可以通过不同的网络层学习得到不同层级的表示，Denoising 策略则通过去除数据中的噪声，增加结果的鲁棒性。同时，SNGR 相比基于 SVD 的方法效率更高。</p>
<h3 id="others">Others</h3>
<h4 id="line">LINE</h4>
<p>LINE <sup id="fnref:14"><a href="#fn:14" class="footnote-ref" role="doc-noteref">14</a></sup> 提出了一个用于大规模网络嵌入的方法，其满足如下 3 个要求：</p>
<ol>
<li>同时保留节点之间的一阶相似性（first-order proximity）和二阶相似性（second-order proximity）。</li>
<li>可以处理大规模网络，例如：百万级别的顶点和十亿级别的边。</li>
<li>可以处理有向，无向和带权的多种类型的图结构。</li>
</ol>
<p>给定一个无向边 <code>$\left(i, j\right)$</code>，点 <code>$v_i$</code> 和 <code>$v_j$</code> 的联合概率如下：</p>
<p><code>$$ p_{1}\left(v_{i}, v_{j}\right)=\frac{1}{1+\exp \left(-\vec{u}_{i}^{T} \cdot \vec{u}_{j}\right)} $$</code></p>
<p>其中，<code>$\vec{u}_{i} \in R^{d}$</code> 为节点 <code>$v_i$</code> 的低维向量表示。在空间 <code>$V \times V$</code> 上，分布 <code>$p \left(\cdot, \cdot\right)$</code> 的经验概率为 <code>$\hat{p}_1 \left(i, j\right) = \dfrac{w_{ij}}{V}$</code>，其中 <code>$W = \sum_{\left(i, j\right) \in E} w_{ij}$</code>。通过最小化两个分布的 KL 散度来优化模型，则目标函数定义如下：</p>
<p><code>$$ O_{1}=-\sum_{(i, j) \in E} w_{i j} \log p_{1}\left(v_{i}, v_{j}\right) $$</code></p>
<p>需要注意的是一阶相似度仅可用于无向图，通过最小化上述目标函数，我们可以将任意顶点映射到一个 <code>$d$</code> 维空间向量。</p>
<p>二阶相似度既可以用于无向图，也可以用于有向图。二阶相似度假设共享大量同其他节点连接的节点之间是相似的，每个节点被视为一个特定的上下文，则在上下文上具有类似分布的节点是相似的。在此，引入两个向量 <code>$\vec{u}_{i}$</code> 和 <code>$\vec{u}_{\prime i}$</code>，其中 <code>$\vec{u}_{i}$</code> 是 <code>$v_i$</code> 做为节点的表示，<code>$\vec{u}_{\prime i}$</code> 是 <code>$v_i$</code> 做为上下文的表示。对于一个有向边 <code>$\left(i, j\right)$</code>，由 <code>$v_i$</code> 生成上下文 <code>$v_j$</code> 的概率为：</p>
<p><code>$$ p_{2}\left(v_{j} | v_{i}\right)=\frac{\exp \left(\vec{u}_{j}^{\prime T} \cdot \vec{u}_{i}\right)}{\sum_{k=1}^{|V|} \exp \left(\vec{u}_{k}^{\prime T} \cdot \vec{u}_{i}\right)} $$</code></p>
<p>其中，<code>$|V|$</code> 为节点或上下文的数量。在此我们引入一个参数 <code>$\lambda_i$</code> 用于表示节点 <code>$v_i$</code> 的重要性程度，重要性程度可以利用度或者 PageRank 算法进行估计。经验分布 <code>$\hat{p}_{2}\left(\cdot \mid v_{i}\right)$</code> 定义为 <code>$\hat{p}_{2}\left(v_{j} \mid v_{i}\right)=\dfrac{w_{i j}}{d_{i}}$</code>，其中 <code>$w_{ij}$</code> 为边 <code>$\left(i, j\right)$</code> 的权重，<code>$d_i$</code> 为节点 <code>$v_i$</code> 的出度。LINE 中采用 <code>$d_i$</code> 作为节点的重要性 <code>$\lambda_i$</code>，利用 KL 散度同时忽略一些常量，目标函数定义如下：</p>
<p><code>$$ O_{2}=-\sum_{(i, j) \in E} w_{i j} \log p_{2}\left(v_{j} \mid v_{i}\right) $$</code></p>
<p>LINE 采用负采样的方式对模型进行优化，同时利用 Alias 方法 <sup id="fnref:15"><a href="#fn:15" class="footnote-ref" role="doc-noteref">15</a></sup> <sup id="fnref:16"><a href="#fn:16" class="footnote-ref" role="doc-noteref">16</a></sup> 加速采样过程。</p>
<h2 id="图神经网络">图神经网络</h2>
<blockquote>
<p>本节内容主要参考自：<br>
Deep Learning on Graphs: A Survey <sup id="fnref:17"><a href="#fn:17" class="footnote-ref" role="doc-noteref">17</a></sup><br>
A Comprehensive Survey on Graph Neural Networks <sup id="fnref:18"><a href="#fn:18" class="footnote-ref" role="doc-noteref">18</a></sup><br>
Graph Neural Networks: A Review of Methods and Applications <sup id="fnref:19"><a href="#fn:19" class="footnote-ref" role="doc-noteref">19</a></sup><br>
Introduction to Graph Neural Networks <sup id="fnref:20"><a href="#fn:20" class="footnote-ref" role="doc-noteref">20</a></sup></p>
</blockquote>
<p>图神经网络（Graph Neural Network，GNN）最早由 Scarselli 等人 <sup id="fnref:21"><a href="#fn:21" class="footnote-ref" role="doc-noteref">21</a></sup> 提出。图中的一个节点可以通过其特征和相关节点进行定义，GNN 的目标是学习一个状态嵌入 <code>$\mathbf{h}_v \in \mathbb{R}^s$</code> 用于表示每个节点的邻居信息。状态嵌入 <code>$\mathbf{h}_v$</code> 可以生成输出向量 <code>$\mathbf{o}_v$</code> 用于作为预测节点标签的分布等。</p>
<p>下面三张图分别从图的类型，训练方法和传播过程角度列举了不同 GNN 的变种 <sup id="fnref1:19"><a href="#fn:19" class="footnote-ref" role="doc-noteref">19</a></sup>。</p>
<figure>
  <img data-src="/images/cn/2020-04-11-graph-embedding-and-gnn/gnn-graph-types.png" class="lazyload"/>
  
</figure>
<figure>
  <img data-src="/images/cn/2020-04-11-graph-embedding-and-gnn/gnn-training-methods.png" class="lazyload"/>
  
</figure>
<figure>
  <img data-src="/images/cn/2020-04-11-graph-embedding-and-gnn/gnn-propagation-steps.png" class="lazyload"/>
  
</figure>
<p>下面我们主要从模型的角度分别介绍不同种类的 GNN。</p>
<h3 id="graph-neural-networks">Graph Neural Networks</h3>
<p>为了根据邻居更新节点的状态，定义一个用于所有节点的函数 <code>$f$</code>，称之为 <em>local transition function</em>。定义一个函数 <code>$g$</code>，用于生成节点的输出，称之为 <em>local output function</em>。有：</p>
<p><code>$$ \begin{array}{c} \mathbf{h}_{v}=f\left(\mathbf{x}_{v}, \mathbf{x}_{co[v]}, \mathbf{h}_{ne[v]}, \mathbf{x}_{ne[v])}\right. \\ \mathbf{o}_{v}=g\left(\mathbf{h}_{v}, \mathbf{x}_{v}\right) \end{array} $$</code></p>
<p>其中，<code>$\mathbf{x}$</code> 表示输入特征，<code>$\mathbf{h}$</code> 表示隐含状态。<code>$co[v]$</code> 为连接到节点 <code>$v$</code> 的边集，<code>$ne[v]$</code> 为节点 <code>$v$</code> 的邻居。</p>
<figure>
  <img data-src="/images/cn/2020-04-11-graph-embedding-and-gnn/graph-example.png" class="lazyload"/>
  
</figure>
<p>上图中，<code>$\mathbf{x}_1$</code> 表示 <code>$l_1$</code> 的输入特征，<code>$co[l_1]$</code> 包含了边 <code>$l_{(1, 4)}, l_{(6, 1)}, l_{(1, 2)}$</code> 和 <code>$l_{(3, 1)}$</code>，<code>$ne[l_1]$</code> 包含了节点 <code>$l_2, k_3, l_4$</code> 和 <code>$l_6$</code>。</p>
<p>令 <code>$\mathbf{H}, \mathbf{O}, \mathbf{X}$</code> 和 <code>$\mathbf{X}_N$</code> 分别表示状态、输出、特征和所有节点特征的向量，有：</p>
<p><code>$$ \begin{aligned} &amp;\mathbf{H}=F(\mathbf{H}, \mathbf{X})\\ &amp;\mathbf{O}=G\left(\mathbf{H}, \mathbf{X}_{N}\right) \end{aligned} $$</code></p>
<p>其中，<code>$F$</code> 为 <em>global transition function</em>，<code>$G$</code> 为 <em>global output function</em>，分别为图中所有节点的 local transition function <code>$f$</code> 和 local output function <code>$g$</code> 的堆叠版本。依据 Banach 的 Fixed Point Theorem <sup id="fnref:22"><a href="#fn:22" class="footnote-ref" role="doc-noteref">22</a></sup>，GNN 利用传统的迭代方式计算状态：</p>
<p><code>$$ \mathbf{H}^{t+1}=F\left(\mathbf{H}^{t}, \mathbf{X}\right) $$</code></p>
<p>其中，<code>$\mathbf{H}^t$</code> 表示第  <code>$t$</code> 论循环 <code>$\mathbf{H}$</code> 的值。</p>
<p>介绍完 GNN 的框架后，下一个问题就是如果学习得到 local transition function <code>$f$</code> 和 local output function <code>$g$</code>。在包含目标信息（<code>$\mathbf{t}_v$</code> 对于特定节点）的监督学习情况下，损失为：</p>
<p><code>$$ loss = \sum_{i=1}^{p} \left(\mathbf{t}_i - \mathbf{o}_i\right) $$</code></p>
<p>其中，<code>$p$</code> 为用于监督学习的节点数量。利用基于梯度下降的学习方法优化模型后，我们可以得到针对特定任务的训练模型和图中节点的隐含状态。</p>
<p>尽管实验结果表明 GNN 是一个用于建模结构数据的强大模型，但对于一般的 GNN 模型仍存在如下缺陷：</p>
<ol>
<li>对于固定点，隐含状态的更新是低效地。</li>
<li>GNN 在每一轮计算中共享参数，而常见的神经网络结构在不同层使用不同的参数。同时，隐含节点状态的更新可以进一步应用 RNN 的思想。</li>
<li>边上的一些信息特征并没有被有效的建模，同时如何学习边的隐含状态也是一个重要问题。</li>
<li>如果我们更关注节点的表示而非图的表示，当迭代轮数 <code>$T$</code> 很大时使用固定点是不合适的。这是因为固定点表示的分布在数值上会更加平滑，从而缺少用于区分不同节点的信息。</li>
</ol>
<h3 id="graph-convolutional-networks">Graph Convolutional Networks</h3>
<p>图卷积神经网络是将用于传统数据（例如：图像）的卷积操作应用到图结构的数据中。核心思想在于学习一个函数 <code>$f$</code>，通过聚合节点 <code>$v_i$</code> 自身的特征 <code>$\mathbf{X}_i$</code> 和邻居的特征 <code>$\mathbf{X}_j$</code> 获得节点的表示，其中 <code>$j \in N\left(v_i\right)$</code> 为节点的邻居。</p>
<p>下图展示了一个用于节点表示学习的 GCN 过程：</p>
<figure>
  <img data-src="/images/cn/2020-04-11-graph-embedding-and-gnn/gcn.png" class="lazyload"/>
  
</figure>
<p>GCN 在构建更复杂的图神经网路中扮演了一个核心角色：</p>
<figure>
  <img data-src="/images/cn/2020-04-11-graph-embedding-and-gnn/gcn-classification.png" class="lazyload"/>
  <figcaption><p class="figcaption">包含 Pooling 模块用于图分类的 GCN</p></figcaption>
</figure>
<figure>
  <img data-src="/images/cn/2020-04-11-graph-embedding-and-gnn/gcn-auto-encoder.png" class="lazyload"/>
  <figcaption><p class="figcaption">包含 GCN 的图自编码器</p></figcaption>
</figure>
<figure>
  <img data-src="/images/cn/2020-04-11-graph-embedding-and-gnn/gcn-graph-spatial-temporal-network.png" class="lazyload"/>
  <figcaption><p class="figcaption">包含 GCN 的图时空网络</p></figcaption>
</figure>
<p>GCN 方法可以分为两大类：基于频谱（Spectral Methods）和基于空间（Spatial Methods）的方法。</p>
<h4 id="基于频谱的方法-spectral-methods">基于频谱的方法（Spectral Methods）</h4>
<p>基于频谱的方法将图视为无向图进行处理，图的一种鲁棒的数学表示为标准化的图拉普拉斯矩阵：</p>
<p><code>$$ \mathbf{L}=\mathbf{I}_{\mathbf{n}}-\mathbf{D}^{-\frac{1}{2}} \mathbf{A} \mathbf{D}^{-\frac{1}{2}} $$</code></p>
<p>其中，<code>$\mathbf{A}$</code> 为图的邻接矩阵，<code>$\mathbf{D}$</code> 为节点度的对角矩阵，<code>$\mathbf{D}_{ii} = \sum_{j} \left(\mathbf{A}_{i, j}\right)$</code>。标准化的拉普拉斯矩阵具有实对称半正定的性质，因此可以分解为：</p>
<p><code>$$ \mathbf{L}=\mathbf{U} \mathbf{\Lambda} \mathbf{U}^{T} $$</code></p>
<p>其中，<code>$\mathbf{U}=\left[\mathbf{u}_{\mathbf{0}}, \mathbf{u}_{\mathbf{1}}, \cdots, \mathbf{u}_{\mathbf{n}-\mathbf{1}}\right] \in \mathbf{R}^{N \times N}$</code> 是由 <code>$\mathbf{L}$</code> 的特征向量构成的矩阵，<code>$\mathbf{\Lambda}$</code> 为特征值的对角矩阵，<code>$\mathbf{\Lambda}_{ii} = \lambda_i$</code>。在图信号处理过程中，一个图信号 <code>$\mathbf{x} \in \mathbb{R}^N$</code> 是一个由图的节点构成的特征向量，其中 <code>$\mathbf{x}_i$</code> 表示第 <code>$i$</code> 个节点的值。对于信号 <code>$\mathbf{x}$</code>，图上的傅里叶变换可以定义为：</p>
<p><code>$$ \mathscr{F}(\mathbf{x})=\mathbf{U}^{T} \mathbf{x} $$</code></p>
<p>傅里叶反变换定义为：</p>
<p><code>$$ \mathscr{F}^{-1}(\hat{\mathbf{x}})=\mathbf{U} \hat{\mathbf{x}} $$</code></p>
<p>其中，<code>$\hat{\mathbf{x}}$</code> 为傅里叶变换后的结果。</p>
<p>转变后信号 <code>$\hat{\mathbf{x}}$</code> 的元素为新空间图信号的坐标，因此输入信号可以表示为：</p>
<p><code>$$ \mathbf{x}=\sum_{i} \hat{\mathbf{x}}_{i} \mathbf{u}_{i} $$</code></p>
<p>这正是傅里叶反变换的结果。那么对于输入信号 <code>$\mathbf{x}$</code> 的图卷积可以定义为：</p>
<p><code>$$ \begin{aligned} \mathbf{x} *_{G} \mathbf{g} &amp;=\mathscr{F}^{-1}(\mathscr{F}(\mathbf{x}) \odot \mathscr{F}(\mathbf{g})) \\ &amp;=\mathbf{U}\left(\mathbf{U}^{T} \mathbf{x} \odot \mathbf{U}^{T} \mathbf{g}\right) \end{aligned} $$</code></p>
<p>其中，<code>$\mathbf{g} \in \mathbb{R}^N$</code> 为滤波器，<code>$\odot$</code> 表示逐元素乘。假设定义一个滤波器 <code>$\mathbf{g}_{\theta}=\operatorname{diag}\left(\mathbf{U}^{T} \mathbf{g}\right)$</code>，则图卷积可以简写为：</p>
<p><code>$$ \mathbf{x} *_{G} \mathbf{g}_{\theta}=\mathbf{U} \mathbf{g}_{\theta} \mathbf{U}^{T} \mathbf{x} $$</code></p>
<p>基于频谱的图卷积网络都遵循这样的定义，不同之处在于不同滤波器的选择。</p>
<p>一些代表模型及其聚合和更新方式如下表所示：</p>
<table>
  <thead>
      <tr>
          <th>模型</th>
          <th>聚合方式</th>
          <th>更新方式</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>ChebNet <sup id="fnref:23"><a href="#fn:23" class="footnote-ref" role="doc-noteref">23</a></sup></td>
          <td><code>$\mathbf{N}_{k}=\mathbf{T}_{k}(\tilde{\mathbf{L}}) \mathbf{X}$</code></td>
          <td><code>$\mathbf{H}=\sum_{k=0}^{K} \mathbf{N}_{k} \mathbf{\Theta}_{k}$</code></td>
      </tr>
      <tr>
          <td>1st-order model</td>
          <td><code>$\begin{array}{l} \mathbf{N}_{0}=\mathbf{X} \\ \mathbf{N}_{1}=\mathbf{D}^{-\frac{1}{2}} \mathbf{A} \mathbf{D}^{-\frac{1}{2}} \mathbf{X} \end{array}$</code></td>
          <td><code>$\mathbf{H}=\mathbf{N}_{0} \mathbf{\Theta}_{0}+\mathbf{N}_{1} \mathbf{\Theta}_{1}$</code></td>
      </tr>
      <tr>
          <td>Single parameter</td>
          <td><code>$\mathbf{N}=\left(\mathbf{I}_{N}+\mathbf{D}^{-\frac{1}{2}} \mathbf{A} \mathbf{D}^{-\frac{1}{2}}\right) \mathbf{X}$</code></td>
          <td><code>$\mathbf{H}=\mathbf{N} \mathbf{\Theta}$</code></td>
      </tr>
      <tr>
          <td>GCN <sup id="fnref:24"><a href="#fn:24" class="footnote-ref" role="doc-noteref">24</a></sup></td>
          <td><code>$\mathbf{N}=\tilde{\mathbf{D}}^{-\frac{1}{2}} \tilde{\mathbf{A}} \tilde{\mathbf{D}}^{-\frac{1}{2}} \mathbf{X}$</code></td>
          <td><code>$\mathbf{H}=\mathbf{N} \mathbf{\Theta}$</code></td>
      </tr>
  </tbody>
</table>
<h4 id="基于空间的方法-spatial-methods">基于空间的方法（Spatial Methods）</h4>
<p>基于空间的方法通过节点的空间关系来定义图卷积操作。为了将图像和图关联起来，可以将图像视为一个特殊形式的图，每个像素点表示一个节点，如下图所示：</p>
<figure>
  <img data-src="/images/cn/2020-04-11-graph-embedding-and-gnn/spatial-based-gcn.png" class="lazyload"/>
  
</figure>
<p>每个像素同周围的像素相连，以 <code>$3 \times 3$</code> 为窗口，每个节点被 8 个邻居节点所包围。通过对中心节点和周围邻居节点的像素值进行加权平均来应用一个 <code>$3 \times 3$</code> 大小的滤波器。由于邻居节点的特定顺序，可以在不同位置共享权重。同样对于一般的图，基于空间的图卷积通过对中心和邻居节点的聚合得到节点新的表示。</p>
<p>为了使节点可以感知更深和更广的范围，通常的做法是将多个图卷积层堆叠在一起。根据堆叠方式的不同，基于空间的图卷积可以进一步分为两类：基于循环（Recurrent-based）和基于组合（Composition-based）的。基于循环的方法使用相同的图卷积层来更新隐含表示，基于组合的方式使用不同的图卷积层更新隐含表示，两者差异如下图所示：</p>
<figure>
  <img data-src="/images/cn/2020-04-11-graph-embedding-and-gnn/recurrent-based-vs-composition-based.png" class="lazyload"/>
  
</figure>
<p>一些代表模型及其聚合和更新方式如下表所示：</p>
<table>
  <thead>
      <tr>
          <th>模型</th>
          <th>聚合方式</th>
          <th>更新方式</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Neural FPs <sup id="fnref:25"><a href="#fn:25" class="footnote-ref" role="doc-noteref">25</a></sup></td>
          <td><code>$\mathbf{h}_{\mathcal{N}_{v}}^{t}=\mathbf{h}_{v}^{t-1}+\sum_{k=1}^{\mathcal{N}_{v}} \mathbf{h}_{k}^{t-1}$</code></td>
          <td><code>$\mathbf{h}_{v}^{t}=\sigma\left(\mathbf{h}_{\mathcal{N}_{v}}^{t} \mathbf{W}_{L}^{\mathcal{N}_{v}}\right)$</code></td>
      </tr>
      <tr>
          <td>DCNN <sup id="fnref:26"><a href="#fn:26" class="footnote-ref" role="doc-noteref">26</a></sup></td>
          <td>Node classification:<br/><code>$\mathbf{N}=\mathbf{P}^{*} \mathbf{X}$</code><br/> Graph classification:<br/><code>$\mathbf{N}=1_{N}^{T} \mathbf{P}^{*} \mathbf{X} / N$</code></td>
          <td><code>$\mathbf{H}=f\left(\mathbf{W}^{c} \odot \mathbf{N}\right)$</code></td>
      </tr>
      <tr>
          <td>GraphSAGE <sup id="fnref:27"><a href="#fn:27" class="footnote-ref" role="doc-noteref">27</a></sup></td>
          <td><code>$\mathbf{h}_{\mathcal{N}_{v}}^{t}=\text{AGGREGATE}_{t}\left(\left\{\mathbf{h}_{u}^{t-1}, \forall u \in \mathcal{N}_{v}\right\}\right)$</code></td>
          <td><code>$\mathbf{h}_{v}^{t}=\sigma\left(\mathbf{W}^{t} \cdot\left[\mathbf{h}_{v}^{t-1} \Vert \mathbf{h}_{\mathcal{N}_{v}}^{t}\right]\right)$</code></td>
      </tr>
  </tbody>
</table>
<h3 id="graph-recurrent-networks">Graph Recurrent Networks</h3>
<p>一些研究尝试利用门控机制（例如：GRU 或 LSTM）用于减少之前 GNN 模型在传播过程中的限制，同时改善在图结构中信息的长距离传播。GGNN <sup id="fnref:28"><a href="#fn:28" class="footnote-ref" role="doc-noteref">28</a></sup> 提出了一种使用 GRU 进行传播的方法。它将 RNN 展开至一个固定 <code>$T$</code> 步，然后通过基于时间的传导计算梯度。传播模型的基础循环方式如下：</p>
<p><code>$$ \begin{aligned} &amp;\mathbf{a}_{v}^{t}=\mathbf{A}_{v}^{T}\left[\mathbf{h}_{1}^{t-1} \ldots \mathbf{h}_{N}^{t-1}\right]^{T}+\mathbf{b}\\ &amp;\mathbf{z}_{v}^{t}=\sigma\left(\mathbf{W}^{z} \mathbf{a}_{v}^{t}+\mathbf{U}^{z} \mathbf{h}_{v}^{t-1}\right)\\ &amp;\mathbf{r}_{v}^{t}=\sigma\left(\mathbf{W}^{r} \mathbf{a}_{v}^{t}+\mathbf{U}^{r} \mathbf{h}_{v}^{t-1}\right)\\ &amp;\begin{array}{l} \widetilde{\mathbf{h}}_{v}^{t}=\tanh \left(\mathbf{W} \mathbf{a}_{v}^{t}+\mathbf{U}\left(\mathbf{r}_{v}^{t} \odot \mathbf{h}_{v}^{t-1}\right)\right) \\ \mathbf{h}_{v}^{t}=\left(1-\mathbf{z}_{v}^{t}\right) \odot \mathbf{h}_{v}^{t-1}+\mathbf{z}_{v}^{t} \odot \widetilde{\mathbf{h}}_{v}^{t} \end{array} \end{aligned} $$</code></p>
<p>节点 <code>$v$</code> 首先从邻居汇总信息，其中 <code>$\mathbf{A}_v$</code> 为图邻接矩阵 <code>$\mathbf{A}$</code> 的子矩阵表示节点 <code>$v$</code> 及其邻居的连接。类似 GRU 的更新函数，通过结合其他节点和上一时间的信息更新节点的隐状态。<code>$\mathbf{a}$</code> 用于获取节点 <code>$v$</code> 邻居的信息，<code>$\mathbf{z}$</code> 和 <code>$\mathbf{r}$</code> 分别为更新和重置门。</p>
<p>GGNN 模型设计用于解决序列生成问题，而之前的模型主要关注单个输出，例如：节点级别或图级别的分类问题。研究进一步提出了 Gated Graph Sequence Neural Networks（GGS-NNs），使用多个 GGNN 产生一个输出序列 <code>$\mathbf{o}^{(1)}, \cdots, \mathbf{o}^{(K)}$</code>，如下图所示：</p>
<figure>
  <img data-src="/images/cn/2020-04-11-graph-embedding-and-gnn/ggs-nn.png" class="lazyload"/>
  
</figure>
<p>上图中使用了两个 GGNN，<code>$\mathcal{F}_o^{(k)}$</code> 用于从 <code>$\mathcal{\boldsymbol{X}}^{(k)}$</code> 预测 <code>$\mathbf{o}^{(k)}$</code>，<code>$\mathcal{F}_x^{(k)}$</code> 用于从 <code>$\mathcal{\boldsymbol{X}}^{(k)}$</code> 预测 <code>$\mathcal{\boldsymbol{X}}^{(k+1)}$</code>。令 <code>$\mathcal{\boldsymbol{H}}^{(k, t)}$</code> 表示第 <code>$k$</code> 步输出的第 <code>$t$</code> 步传播，<code>$\mathcal{\boldsymbol{H}}^{(k, 1)}$</code> 在任意 <code>$k$</code> 步初始化为 <code>$\mathcal{\boldsymbol{X}}^{(k)}$</code>，<code>$\mathcal{\boldsymbol{H}}^{(t, 1)}$</code> 在任意 <code>$t$</code> 步初始化为 <code>$\mathcal{\boldsymbol{X}}^{(t)}$</code>，<code>$\mathcal{F}_o^{(k)}$</code> 和 <code>$\mathcal{F}_x^{(k)}$</code> 可以为不同模型也可以共享权重。</p>
<p>一些代表模型及其聚合和更新方式如下表所示：</p>
<table>
  <thead>
      <tr>
          <th>模型</th>
          <th>聚合方式</th>
          <th>更新方式</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>GGNN <sup id="fnref1:28"><a href="#fn:28" class="footnote-ref" role="doc-noteref">28</a></sup></td>
          <td><code>$\mathbf{h}_{\mathcal{N}_{v}}^{t}=\sum_{k \in \mathcal{N}_{v}} \mathbf{h}_{k}^{t-1}+\mathbf{b}$</code></td>
          <td><code>$\begin{aligned} &amp;\mathbf{z}_{v}^{t}=\sigma\left(\mathbf{W}^{z} \mathbf{h}_{\mathcal{N}_{v}}^{t}+\mathbf{U}^{z} \mathbf{h}_{v}^{t-1}\right)\\ &amp;\mathbf{r}_{v}^{t}=\sigma\left(\mathbf{W}^{r} \mathbf{h}_{\mathcal{N}_{v}}^{z}+\mathbf{U}^{r} \mathbf{h}_{v}^{t-1}\right)\\ &amp;\begin{array}{l} \widetilde{\mathbf{h}}_{v}^{t}=\tanh \left(\mathbf{W h}_{\mathcal{N}_{v}}^{t}+\mathbf{U}\left(\mathbf{r}_{v}^{t} \odot \mathbf{h}_{v}^{t-1}\right)\right) \\ \mathbf{h}_{v}^{t}=\left(1-\mathbf{z}_{v}^{t}\right) \odot \mathbf{h}_{v}^{t-1}+\mathbf{z}_{v}^{t} \odot \widetilde{\mathbf{h}}_{v}^{t} \end{array} \end{aligned}$</code></td>
      </tr>
      <tr>
          <td>Tree LSTM (Child sum) <sup id="fnref:29"><a href="#fn:29" class="footnote-ref" role="doc-noteref">29</a></sup></td>
          <td><code>$\mathbf{h}_{\mathcal{N}_{v}}^{t}=\sum_{k \in \mathcal{N}_{v}} \mathbf{h}_{k}^{t-1}$</code></td>
          <td><code>$\begin{aligned} &amp;\mathbf{i}_{v}^{t}=\sigma\left(\mathbf{W}^{i} \mathbf{x}_{v}^{t}+\mathbf{U}^{i} \mathbf{h}_{\mathcal{N}_{v}}^{t}+\mathbf{b}^{i}\right)\\ &amp;\mathbf{f}_{v k}^{t}=\sigma\left(\mathbf{W}^{f} \mathbf{x}_{v}^{t}+\mathbf{U}^{f} \mathbf{h}_{k}^{t-1}+\mathbf{b}^{f}\right)\\ &amp;\mathbf{o}_{v}^{t}=\sigma\left(\mathbf{W}^{o} \mathbf{x}_{v}^{t}+\mathbf{U}^{o} \mathbf{h}_{\mathcal{N}_{v}}^{t}+\mathbf{b}^{o}\right)\\ &amp;\mathbf{u}_{v}^{t}=\tanh \left(\mathbf{W}^{u} \mathbf{x}_{v}^{t}+\mathbf{U}^{u} \mathbf{h}_{\mathcal{N}_{v}}^{t}+\mathbf{b}^{u}\right)\\ &amp;\begin{array}{l} \mathbf{c}_{v}^{t}=\mathbf{i}_{v}^{t} \odot \mathbf{u}_{v}^{t}+\sum_{k \in \mathcal{N}_{v}} \mathbf{f}_{v k}^{t} \odot \mathbf{c}_{k}^{t-1} \\ \mathbf{h}_{v}^{t}=\mathbf{o}_{v}^{t} \odot \tanh \left(\mathbf{c}_{v}^{t}\right) \end{array} \end{aligned}$</code></td>
      </tr>
      <tr>
          <td>Tree LSTM (N-ary) <sup id="fnref1:29"><a href="#fn:29" class="footnote-ref" role="doc-noteref">29</a></sup></td>
          <td><code>$\begin{aligned} &amp;\mathbf{h}_{\mathcal{N}_{v}}^{t i}=\sum_{l=1}^{K} \mathbf{U}_{l}^{i} \mathbf{h}_{v l}^{t-1}\\ &amp;\mathbf{h}_{\mathcal{N}_{v} k}^{t f}=\sum_{l=1}^{K} \mathbf{U}_{k l}^{f} \mathbf{h}_{v l}^{t-1}\\ &amp;\mathbf{h}_{\mathcal{N}_{v}}^{t o}=\sum_{l=1}^{K} \mathbf{U}_{l}^{o} \mathbf{h}_{v l}^{t-1}\\ &amp;\mathbf{h}_{\mathcal{N}_{v}}^{t u}=\sum_{l=1}^{K} \mathbf{U}_{l}^{u} \mathbf{h}_{v l}^{t-1} \end{aligned}$</code></td>
          <td><code>$\begin{aligned} &amp;\mathbf{i}_{v}^{t}=\sigma\left(\mathbf{W}^{i} \mathbf{x}_{v}^{t}+\mathbf{h}_{\mathcal{N}_{v},}^{t i}+\mathbf{b}^{i}\right)\\ &amp;\mathbf{f}_{v k}^{t}=\sigma\left(\mathbf{W}^{f} \mathbf{x}_{v}^{t}+\mathbf{h}_{\mathcal{N}_{v} k}^{f f}+\mathbf{b}^{f}\right)\\ &amp;\mathbf{o}_{v}^{t}=\sigma\left(\mathbf{W}^{o} \mathbf{x}_{v}^{t}+\mathbf{h}_{\mathcal{N}_{v}}^{t o}+\mathbf{b}^{o}\right)\\ &amp;\mathbf{u}_{v}^{t}=\tanh \left(\mathbf{W}^{u} \mathbf{x}_{v}^{t}+\mathbf{h}_{\mathcal{N}_{v}}^{t u}+\mathbf{b}^{u}\right)\\ &amp;\mathbf{c}_{v}^{t}=\mathbf{i}_{v}^{t} \odot \mathbf{u}_{v}^{t}+\sum_{l=1}^{K} \mathbf{f}_{v l}^{t} \odot \mathbf{c}_{v l}^{t-1}\\ &amp;\mathbf{h}_{v}^{t}=\mathbf{o}_{v}^{t} \odot \tanh \left(\mathbf{c}_{v}^{t}\right) \end{aligned}$</code></td>
      </tr>
      <tr>
          <td>Graph LSTM <sup id="fnref:30"><a href="#fn:30" class="footnote-ref" role="doc-noteref">30</a></sup></td>
          <td><code>$\begin{aligned} \mathbf{h}_{\mathcal{N}_{v}}^{t i}=\sum_{k \in \mathcal{N}_{v}} \mathbf{U}_{m(v, k)}^{i} \mathbf{h}_{k}^{t-1} \\ \mathbf{h}_{\mathcal{N}_{v}}^{t o}=\sum_{k \in \mathcal{N}_{v}} \mathbf{U}_{m(v, k)}^{o} \mathbf{h}_{k}^{t-1} \\ \mathbf{h}_{\mathcal{N}_{v}}^{t u}=\sum_{k \in \mathcal{N}_{v}} \mathbf{U}_{m(v, k)}^{u} \mathbf{h}_{k}^{t-1} \end{aligned}$</code></td>
          <td><code>$\begin{aligned} &amp;\mathbf{i}_{v}^{t}=\sigma\left(\mathbf{W}^{i} \mathbf{x}_{v}^{t}+\mathbf{h}_{\mathcal{N}_{v}}^{t i}+\mathbf{b}^{i}\right)\\ &amp;\mathbf{f}_{v k}^{t}=\sigma\left(\mathbf{W}^{f} \mathbf{x}_{v}^{t}+\mathbf{U}_{m(v, k)}^{f} \mathbf{h}_{k}^{t-1}+\mathbf{b}^{f}\right)\\ &amp;\mathbf{o}_{v}^{t}=\sigma\left(\mathbf{W}^{o} \mathbf{x}_{v}^{t}+\mathbf{h}_{\mathcal{N}_{v}}^{t o}+\mathbf{b}^{o}\right)\\ &amp;\mathbf{u}_{v}^{t}=\tanh \left(\mathbf{W}^{u} \mathbf{x}_{v}^{t}+\mathbf{h}_{\mathcal{N}_{v}}^{t u}+\mathbf{b}^{u}\right)\\ &amp;\begin{array}{l} \mathbf{c}_{v}^{t}=\mathbf{i}_{v}^{t} \odot \mathbf{u}_{v}^{t}+\sum_{k \in \mathcal{N}_{v}} \mathbf{f}_{v k}^{t} \odot \mathbf{c}_{k}^{t-1} \\ \mathbf{h}_{v}^{t}=\mathbf{o}_{v}^{t} \odot \tanh \left(\mathbf{c}_{v}^{t}\right) \end{array} \end{aligned}$</code></td>
      </tr>
  </tbody>
</table>
<h3 id="graph-attention-networks">Graph Attention Networks</h3>
<p>与 GCN 对于节点所有的邻居平等对待相比，注意力机制可以为每个邻居分配不同的注意力评分，从而识别更重要的邻居。</p>
<p>GAT <sup id="fnref:31"><a href="#fn:31" class="footnote-ref" role="doc-noteref">31</a></sup> 将注意力机制引入传播过程，其遵循自注意力机制，通过对每个节点邻居的不同关注更新隐含状态。GAT 定义了一个图注意力层（<em>graph attentional layer</em>），通过堆叠构建图注意力网络。对于节点对 <code>$\left(i, j\right)$</code>，基于注意力机制的系数计算方式如下：</p>
<p><code>$$ \alpha_{i j}=\frac{\exp \left(\text { LeakyReLU }\left(\overrightarrow{\mathbf{a}}^{T}\left[\mathbf{W} \vec{h}_{i} \| \mathbf{W} \vec{h}_{j}\right]\right)\right)}{\sum_{k \in N_{i}} \exp \left(\text { LeakyReLU }\left(\overrightarrow{\mathbf{a}}^{T}\left[\mathbf{W} \vec{h}_{i} \| \mathbf{W} \vec{h}_{k}\right]\right)\right)} $$</code></p>
<p>其中，<code>$\alpha_{i j}$</code> 表示节点 <code>$j$</code> 对 <code>$i$</code> 的注意力系数，<code>$N_i$</code> 表示节点 <code>$i$</code> 的邻居。令 <code>$\mathbf{h}=\left\{\vec{h}_{1}, \vec{h}_{2}, \ldots, \vec{h}_{N}\right\}, \vec{h}_{i} \in \mathbb{R}^{F}$</code> 表示输入节点特征，其中 <code>$N$</code> 为节点的数量，<code>$F$</code> 为特征维度，则节点的输出特征（可能为不同维度 <code>$F^{\prime}$</code>）为 <code>$\mathbf{h}^{\prime}=\left\{\vec{h}_{1}^{\prime}, \vec{h}_{2}^{\prime}, \ldots, \vec{h}_{N}^{\prime}\right\}, \vec{h}_{i}^{\prime} \in \mathbb{R}^{F^{\prime}}$</code>。<code>$\mathbf{W} \in \mathbb{R}^{F^{\prime} \times F}$</code> 为所有节点共享的线性变换的权重矩阵，<code>$a: \mathbb{R}^{F^{\prime}} \times \mathbb{R}^{F^{\prime}} \rightarrow \mathbb{R}$</code> 用于计算注意力系数。最后的输出特征计算方式如下：</p>
<p><code>$$ \vec{h}_{i}^{\prime}=\sigma\left(\sum_{j \in \mathcal{N}_{i}} \alpha_{i j} \mathbf{W} \vec{h}_{j}\right) $$</code></p>
<p>注意力层采用多头注意力机制来稳定学习过程，之后应用 <code>$K$</code> 个独立的注意力机制计算隐含状态，最后通过拼接或平均得到输出表示：</p>
<p><code>$$ \vec{h}_{i}^{\prime}=\Vert_{k=1}^{K} \sigma\left(\sum_{j \in \mathcal{N}_{i}} \alpha_{i j}^{k} \mathbf{W}^{k} \vec{h}_{j}\right) $$</code></p>
<p><code>$$ \vec{h}_{i}^{\prime}=\sigma\left(\frac{1}{K} \sum_{k=1}^{K} \sum_{j \in \mathcal{N}_{i}} \alpha_{i j}^{k} \mathbf{W}^{k} \vec{h}_{j}\right) $$</code></p>
<p>其中，<code>$\Vert$</code> 表示连接操作，<code>$\alpha_{ij}^k$</code> 表示第 <code>$k$</code> 个注意力机制计算得到的标准化的注意力系数。整个模型如下图所示：</p>
<figure>
  <img data-src="/images/cn/2020-04-11-graph-embedding-and-gnn/gat.png" class="lazyload"/>
  
</figure>
<p>GAT 中的注意力架构有如下几个特点：</p>
<ol>
<li>针对节点对的计算是并行的，因此计算过程是高效的。</li>
<li>可以处理不同度的节点并对邻居分配对应的权重。</li>
<li>可以容易地应用到归纳学习问题中去。</li>
</ol>
<h3 id="应用">应用</h3>
<p>图神经网络已经被应用在监督、半监督、无监督和强化学习等多个领域。下图列举了 GNN 在不同领域内相关问题中的应用，具体模型论文请参考 Graph Neural Networks: A Review of Methods and Applications 原文 <sup id="fnref2:19"><a href="#fn:19" class="footnote-ref" role="doc-noteref">19</a></sup>。</p>
<figure>
  <img data-src="/images/cn/2020-04-11-graph-embedding-and-gnn/applications.png" class="lazyload"/>
  
</figure>
<h2 id="开放资源">开放资源</h2>
<h3 id="开源实现">开源实现</h3>
<table>
  <thead>
      <tr>
          <th>项目</th>
          <th>框架</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="https://github.com/rusty1s/pytorch_geometric">rusty1s/pytorch_geometric</a></td>
          <td><i class="icon icon-pytorch">PyTorch</i></td>
      </tr>
      <tr>
          <td><a href="https://github.com/dmlc/dgl">dmlc/dgl</a></td>
          <td><i class="icon icon-pytorch">PyTorch</i>, <i class="icon icon-tensorflow">TF</i> &amp; <i class="icon icon-mxnet">MXNet</i></td>
      </tr>
      <tr>
          <td><a href="https://github.com/alibaba/euler">alibaba/euler</a></td>
          <td><i class="icon icon-tensorflow">TF</i></td>
      </tr>
      <tr>
          <td><a href="https://github.com/alibaba/graph-learn">alibaba/graph-learn</a></td>
          <td><i class="icon icon-tensorflow">TF</i></td>
      </tr>
      <tr>
          <td><a href="https://github.com/deepmind/graph_nets">deepmind/graph_nets</a></td>
          <td><i class="icon icon-tensorflow">TF</i> &amp; <i class="icon icon-sonnet">Sonnet</i></td>
      </tr>
      <tr>
          <td><a href="https://github.com/facebookresearch/PyTorch-BigGraph">facebookresearch/PyTorch-BigGraph</a></td>
          <td><i class="icon icon-pytorch">PyTorch</i></td>
      </tr>
      <tr>
          <td><a href="https://github.com/tencent/plato">tencent/plato</a></td>
          <td></td>
      </tr>
      <tr>
          <td><a href="https://github.com/PaddlePaddle/PGL">PaddlePaddle/PGL</a></td>
          <td><i class="icon icon-paddlepaddle"></i> PaddlePaddle</td>
      </tr>
      <tr>
          <td><a href="https://github.com/Accenture/AmpliGraph">Accenture/AmpliGraph</a></td>
          <td><i class="icon icon-tensorflow">TF</i></td>
      </tr>
      <tr>
          <td><a href="https://github.com/danielegrattarola/spektral">danielegrattarola/spektral</a></td>
          <td><i class="icon icon-tensorflow">TF</i></td>
      </tr>
      <tr>
          <td><a href="https://github.com/THUDM/cogdl/">THUDM/cogdl</a></td>
          <td><i class="icon icon-pytorch">PyTorch</i></td>
      </tr>
      <tr>
          <td><a href="https://github.com/DeepGraphLearning/graphvite">DeepGraphLearning/graphvite</a></td>
          <td><i class="icon icon-pytorch">PyTorch</i></td>
      </tr>
  </tbody>
</table>
<h3 id="论文列表和评测">论文列表和评测</h3>
<ul>
<li><a href="https://github.com/thunlp/NRLPapers">Must-read papers on network representation learning (NRL) / network embedding (NE)</a></li>
<li><a href="https://github.com/thunlp/GNNPapers">Must-read papers on graph neural networks (GNN)</a></li>
<li><a href="https://github.com/DeepGraphLearning/LiteratureDL4Graph">DeepGraphLearning/LiteratureDL4Graph</a></li>
<li><a href="https://github.com/nnzhan/Awesome-Graph-Neural-Networks">nnzhan/Awesome-Graph-Neural-Networks</a></li>
<li><a href="https://github.com/graphdeeplearning/benchmarking-gnns">graphdeeplearning/benchmarking-gnns</a></li>
<li><a href="https://ogb.stanford.edu/">Open Graph Benchmark</a></li>
</ul>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Cai, H., Zheng, V. W., &amp; Chang, K. C. C. (2018). A comprehensive survey of graph embedding: Problems, techniques, and applications. <em>IEEE Transactions on Knowledge and Data Engineering</em>, 30(9), 1616-1637.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Goyal, P., &amp; Ferrara, E. (2018). Graph embedding techniques, applications, and performance: A survey. <em>Knowledge-Based Systems</em>, 151, 78-94.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>Hamilton, W. L., Ying, R., &amp; Leskovec, J. (2017). Representation learning on graphs: Methods and applications. <em>arXiv preprint arXiv:1709.05584</em>.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>Perozzi, B., Al-Rfou, R., &amp; Skiena, S. (2014). Deepwalk: Online learning of social representations. In <em>Proceedings of the 20th ACM SIGKDD international conference on Knowledge discovery and data mining</em> (pp. 701-710).&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p>Grover, A., &amp; Leskovec, J. (2016). node2vec: Scalable feature learning for networks. In <em>Proceedings of the 22nd ACM SIGKDD international conference on Knowledge discovery and data mining</em> (pp. 855-864).&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6">
<p>Fogaras, D., Rácz, B., Csalogány, K., &amp; Sarlós, T. (2005). Towards scaling fully personalized pagerank: Algorithms, lower bounds, and experiments. <em>Internet Mathematics</em>, 2(3), 333-358.&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:7">
<p>Haveliwala, T. H. (2002). Topic-sensitive PageRank. In <em>Proceedings of the 11th international conference on World Wide Web</em> (pp. 517-526).&#160;<a href="#fnref:7" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:8">
<p>Cao, S., Lu, W., &amp; Xu, Q. (2015). Grarep: Learning graph representations with global structural information. In <em>Proceedings of the 24th ACM international on conference on information and knowledge management</em> (pp. 891-900).&#160;<a href="#fnref:8" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:9">
<p>Ou, M., Cui, P., Pei, J., Zhang, Z., &amp; Zhu, W. (2016). Asymmetric transitivity preserving graph embedding. In <em>Proceedings of the 22nd ACM SIGKDD international conference on Knowledge discovery and data mining</em> (pp. 1105-1114).&#160;<a href="#fnref:9" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:10">
<p>Dong, Y., Chawla, N. V., &amp; Swami, A. (2017). metapath2vec: Scalable representation learning for heterogeneous networks. In <em>Proceedings of the 23rd ACM SIGKDD international conference on knowledge discovery and data mining</em> (pp. 135-144).&#160;<a href="#fnref:10" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:11">
<p>Fu, T. Y., Lee, W. C., &amp; Lei, Z. (2017). Hin2vec: Explore meta-paths in heterogeneous information networks for representation learning. In <em>Proceedings of the 2017 ACM on Conference on Information and Knowledge Management</em> (pp. 1797-1806).&#160;<a href="#fnref:11" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:12">
<p>Wang, D., Cui, P., &amp; Zhu, W. (2016). Structural deep network embedding. In <em>Proceedings of the 22nd ACM SIGKDD international conference on Knowledge discovery and data mining</em> (pp. 1225-1234).&#160;<a href="#fnref:12" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:13">
<p>Cao, S., Lu, W., &amp; Xu, Q. (2016). Deep neural networks for learning graph representations. In <em>Thirtieth AAAI conference on artificial intelligence</em>.&#160;<a href="#fnref:13" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:14">
<p>Tang, J., Qu, M., Wang, M., Zhang, M., Yan, J., &amp; Mei, Q. (2015). Line: Large-scale information network embedding. In <em>Proceedings of the 24th international conference on world wide web</em> (pp. 1067-1077).&#160;<a href="#fnref:14" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:15">
<p>Walker, A. J. (1974). New fast method for generating discrete random numbers with arbitrary frequency distributions. <em>Electronics Letters</em>, 10(8), 127-128.&#160;<a href="#fnref:15" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:16">
<p>Walker, A. J. (1977). An efficient method for generating discrete random variables with general distributions. <em>ACM Transactions on Mathematical Software (TOMS)</em>, 3(3), 253-256.&#160;<a href="#fnref:16" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:17">
<p>Zhang, Z., Cui, P., &amp; Zhu, W. (2020). Deep learning on graphs: A survey. <em>IEEE Transactions on Knowledge and Data Engineering</em>.&#160;<a href="#fnref:17" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:18">
<p>Wu, Z., Pan, S., Chen, F., Long, G., Zhang, C., &amp; Philip, S. Y. (2020). A comprehensive survey on graph neural networks. <em>IEEE Transactions on Neural Networks and Learning Systems</em>.&#160;<a href="#fnref:18" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:19">
<p>Zhou, J., Cui, G., Zhang, Z., Yang, C., Liu, Z., Wang, L., &hellip; &amp; Sun, M. (2018). Graph neural networks: A review of methods and applications. <em>arXiv preprint arXiv:1812.08434</em>.&#160;<a href="#fnref:19" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:19" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref2:19" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:20">
<p>Liu, Z., &amp; Zhou, J. (2020). Introduction to Graph Neural Networks. <em>Synthesis Lectures on Artificial Intelligence and Machine Learning</em>, 14(2), 1–127.&#160;<a href="#fnref:20" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:21">
<p>Scarselli, F., Gori, M., Tsoi, A. C., Hagenbuchner, M., &amp; Monfardini, G. (2008). The graph neural network model. <em>IEEE Transactions on Neural Networks</em>, 20(1), 61-80.&#160;<a href="#fnref:21" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:22">
<p>Khamsi, M. A., &amp; Kirk, W. A. (2011). <em>An introduction to metric spaces and fixed point theory</em> (Vol. 53). John Wiley &amp; Sons.&#160;<a href="#fnref:22" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:23">
<p>Defferrard, M., Bresson, X., &amp; Vandergheynst, P. (2016). Convolutional neural networks on graphs with fast localized spectral filtering. In <em>Advances in neural information processing systems</em> (pp. 3844-3852).&#160;<a href="#fnref:23" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:24">
<p>Kipf, T. N., &amp; Welling, M. (2016). Semi-supervised classification with graph convolutional networks. <em>arXiv preprint arXiv:1609.02907</em>.&#160;<a href="#fnref:24" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:25">
<p>Duvenaud, D. K., Maclaurin, D., Iparraguirre, J., Bombarell, R., Hirzel, T., Aspuru-Guzik, A., &amp; Adams, R. P. (2015). Convolutional networks on graphs for learning molecular fingerprints. In <em>Advances in neural information processing systems</em> (pp. 2224-2232).&#160;<a href="#fnref:25" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:26">
<p>Atwood, J., &amp; Towsley, D. (2016). Diffusion-convolutional neural networks. In <em>Advances in neural information processing systems</em> (pp. 1993-2001).&#160;<a href="#fnref:26" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:27">
<p>Hamilton, W., Ying, Z., &amp; Leskovec, J. (2017). Inductive representation learning on large graphs. In <em>Advances in neural information processing systems</em> (pp. 1024-1034).&#160;<a href="#fnref:27" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:28">
<p>Li, Y., Tarlow, D., Brockschmidt, M., &amp; Zemel, R. (2015). Gated graph sequence neural networks. <em>arXiv preprint arXiv:1511.05493.</em>&#160;<a href="#fnref:28" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:28" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:29">
<p>Tai, K. S., Socher, R., &amp; Manning, C. D. (2015). Improved Semantic Representations From Tree-Structured Long Short-Term Memory Networks. In <em>Proceedings of the 53rd Annual Meeting of the Association for Computational Linguistics and the 7th International Joint Conference on Natural Language Processing</em> (Volume 1: Long Papers) (pp. 1556-1566).&#160;<a href="#fnref:29" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:29" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:30">
<p>Peng, N., Poon, H., Quirk, C., Toutanova, K., &amp; Yih, W. T. (2017). Cross-sentence n-ary relation extraction with graph lstms. <em>Transactions of the Association for Computational Linguistics</em>, 5, 101-115.&#160;<a href="#fnref:30" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:31">
<p>Veličković, P., Cucurull, G., Casanova, A., Romero, A., Lio, P., &amp; Bengio, Y. (2017). Graph attention networks. <em>arXiv preprint arXiv:1710.10903.</em>&#160;<a href="#fnref:31" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>

        ]]></description></item><item><title>预训练自然语言模型 (Pre-trained Models for NLP)</title><link>https://zeqiang.fun/cn/2020/03/pre-trained-model-for-nlp/</link><pubDate>Sat, 28 Mar 2020 00:00:00 +0000</pubDate><guid>https://zeqiang.fun/cn/2020/03/pre-trained-model-for-nlp/</guid><description><![CDATA[
        <blockquote>
<p>本文为 Pre-trained Models for Natural Language Processing: A Survey 和相关模型的读书笔记 <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>。</p>
</blockquote>
<p>在当下的 NLP 研究领域，随着计算机算力的不断增强，越来越多的通用语言表征的预训练模型（Pre-trained Models，PTMs）逐渐涌现出来。这对下游的 NLP 任务非常有帮助，可以避免大量从零开始训练新的模型。PTM 大致可以分为两代：</p>
<ul>
<li>第一代 PTM 旨在学习词嵌入。由于下游任务不在需要这些模型，因此为了计算效率，这些模型往往采用浅层模型，例如 Skip-Gram <sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>，GloVe <sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> 等。尽管这些模型可以捕获词的语义，但由于未基于上下文环境，因此不能够捕捉到更深层次的概念，例如：句法结构，语义角色，指代等等。</li>
<li>第二代 PTM 专注于学习基于上下文的词嵌入，例如 CoVe <sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>，ELMo <sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup>，OpenAI GPT <sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup> 和 BERT <sup id="fnref:7"><a href="#fn:7" class="footnote-ref" role="doc-noteref">7</a></sup> 等。这些学习到的编码器在下游任务中仍会用于词在上下文中的语义表示。</li>
</ul>
<h2 id="预训练原理">预训练原理</h2>
<h3 id="语言表示学习">语言表示学习</h3>
<p>分布式表示的核心思想为用一个低维的实值向量表示一段文本，向量单独每个维度不具有任何实质含义，但整个向量表示了一个具体的概念。下图展示了一个 NLP 任务的一般神经网络架构：</p>
<figure>
  <img data-src="/images/cn/2020-03-28-pre-trained-model-for-nlp/generic-neural-architecture-for-nlp.png" class="lazyload"/>
  <figcaption><p class="figcaption">NLP 任务的一般神经网络架构</p></figcaption>
</figure>
<p>词嵌入包含两种类型：<strong>上下文无关的词嵌入</strong>和<strong>基于上下文的词嵌入</strong>。两者的不同点在于一个词的嵌入是够会随着上下文的不同而随之改变。</p>
<ul>
<li>上下文无关的词嵌入</li>
</ul>
<p>为了表征语义，我们需要将离散的语言符号映射到一个分布式嵌入空间中。对于词典 <code>$\mathcal{V}$</code> 中的一个词 <code>$x$</code>，我们将其映射为查询表 <code>$\mathbf{E} \in \mathbb{R}^{D_e \times \|\mathcal{V}\|}$</code> 中的一个向量 <code>$\mathbf{e}_x \in \mathbb{R}^{D_e}$</code>，其中 <code>$D_e$</code> 为嵌入的维度。</p>
<p>这种类型的嵌入主要有两个缺陷：一是嵌入是静态的，词在不同的上下文中的嵌入表示是相同的，因此无法处理一词多义；二是未登录词（out-of-vocabulary，OOV）问题，通常可以采用字符级嵌入表示解决该问题。更多上下文无关的词嵌入模型，请参见之前的博客 <a href="/cn/2018/10/word-embeddings/">词向量</a>。</p>
<ul>
<li>基于上下文的词嵌入</li>
</ul>
<p>为了解决上述问题，我们需要区分在不同上下文下词的含义。给定一段文本 <code>$x_1, x_2, \dotsc, x_T$</code> 其中每段标记 <code>$x_t \in \mathcal{V}$</code> 为一个词或子词，<code>$x_t$</code> 基于上下文的表示依赖于整段文本。</p>
<p><code>$$ \left[\mathbf{h}_1, \mathbf{h}_2, \dotsc, \mathbf{h}_T\right] = f_{\text{enc}} \left(x_1, x_2, \dotsc, x_T\right) $$</code></p>
<p>其中，<code>$f_{\text{enc}} \left(\cdot\right)$</code> 为神经编码器，<code>$\mathbf{h}_t$</code> 为标记 <code>$x_t$</code> 的<strong>基于上下文的嵌入</strong>或<strong>动态嵌入</strong>。</p>
<h3 id="神经上下文编码器">神经上下文编码器</h3>
<p>神经上下文编码器大致可以分为 3 类：</p>
<ol>
<li><strong>基于卷积的模型</strong>：基于卷积的模型通过卷积操作从一个词的邻居中聚合局部信息来捕获这个词的含义 <sup id="fnref:8"><a href="#fn:8" class="footnote-ref" role="doc-noteref">8</a></sup>。
<figure>
    <img data-src="/images/cn/2020-03-28-pre-trained-model-for-nlp/convolutional-model.png" class="lazyload"/>
    <figcaption><p class="figcaption">Convolutional model</p></figcaption>
  </figure></li>
<li><strong>基于序列的模型</strong>：基于序列的模型采用 RNNs（LSTM <sup id="fnref:9"><a href="#fn:9" class="footnote-ref" role="doc-noteref">9</a></sup> 和 GRU <sup id="fnref:10"><a href="#fn:10" class="footnote-ref" role="doc-noteref">10</a></sup>） 来捕获词的上下文信息。实际中，我们采用双向的 RNNs 从词的两端收集信息，不过整体效果容易收到长期依赖问题的影响。
<figure>
    <img data-src="/images/cn/2020-03-28-pre-trained-model-for-nlp/sequential-model.png" class="lazyload"/>
    <figcaption><p class="figcaption">Sequential model</p></figcaption>
  </figure></li>
<li><strong>基于图的模型</strong>：基于图的模型将字作为图中的一个节点来学习上下文表示，这个图通常是一个词之间预定义的语言结构，例如：语法结构 <sup id="fnref:11"><a href="#fn:11" class="footnote-ref" role="doc-noteref">11</a></sup> <sup id="fnref:12"><a href="#fn:12" class="footnote-ref" role="doc-noteref">12</a></sup> 或语义关系 <sup id="fnref:13"><a href="#fn:13" class="footnote-ref" role="doc-noteref">13</a></sup>。尽管基于语言学的图结构能提供有用的信息，但如何构建一个好的图结构则成为了难题。除此之外，基于语言学的图结构需要依赖专家知识和外部工具，例如：依存句法分析等。事实上，我们会采用一个更直接的方式去学习任意两个词之间的关系，通常连接的权重可以通过自注意力机制自动计算得出。Transformer <sup id="fnref:14"><a href="#fn:14" class="footnote-ref" role="doc-noteref">14</a></sup> 是一个采用了全链接自注意力架构的实现，同时也采用了位置嵌入（positional embedding），层标准化（layer normalization）和残差连接（residual connections）等网络设计理念。
<figure>
    <img data-src="/images/cn/2020-03-28-pre-trained-model-for-nlp/fully-connected-graph-based-model.png" class="lazyload"/>
    <figcaption><p class="figcaption">Fully-connected graph-based model</p></figcaption>
  </figure></li>
</ol>
<h3 id="为什么预训练">为什么预训练</h3>
<p>对于大多数的 NLP 任务，构建一个大规模的有标签的数据集是一项很大的挑战。相反，大规模的无标签语料是相对容易构建的，为了充分利用这些无标签数据，我们可以先利用它们获取一个好的语言表示，再将这些表示用于其他任务。预训练的好处如下：</p>
<ol>
<li>预训练可以从大规模语料中学习得到通用的语言表示，并用于下游任务。</li>
<li>预训练提供了更优的模型初始化方法，有助于提高模型的泛化能力和加速模型收敛。</li>
<li>预训练可以当作是在小数据集上一种避免过拟合的正则化方法。</li>
</ol>
<h3 id="预训练任务">预训练任务</h3>
<p>预训练任务对于学习语言的通用表示来说至关重要。通常情况下，预训练任务具有挑战性，同时需要大量训练数据。我们将预训练任务划分为 3 类：</p>
<ol>
<li><strong>监督学习</strong>，即从包含输入输出对的训练数据中学习一个由输入到输出的映射函数。</li>
<li><strong>非监督学习</strong>，即从无标签数据获取一些固有的知识，例如：聚类，密度，潜在表征等。</li>
<li><strong>自监督学习</strong>，是监督学习和非监督学习的混合体，核心思想是对于输入的一部分利用其他部分进行预测。</li>
</ol>
<h4 id="语言模型-language-modeling-lm">语言模型（Language Modeling，LM）</h4>
<p>NLP 中最常见的非监督任务为概率语言建模，这是一个经典的概率密度估计问题。给定一个文本序列 <code>$x_{1:T} = \left[x_1, x_2, \dotsc, x_T\right]$</code>，他的联合概率 <code>$p \left(x_{1:T}\right)$</code> 可以分解为：</p>
<p><code>$$ p \left(x_{1:T}\right) = \prod_{t=1}^{y}{p \left(x_t \mid x_{0:t-1}\right)} $$</code></p>
<p>其中 <code>$x_0$</code> 为序列开始的特殊标记。条件概率 <code>$p \left(x_t \mid x_{0:t-1}\right)$</code> 可以通过给定的语言上下文 <code>$x_{0:t-1}$</code> 词的概率分布进行建模估计。上下文 <code>$x_{0:t-1}$</code> 可以通过神经编码器 <code>$f_{\text{enc}} \left(\cdot\right)$</code> 进行建模，则条件概率可以表示为：</p>
<p><code>$$ p \left(x_t | x_{0:t-1}\right) = g_{\text{LM}} \left(f_{\text{enc}} \left(x_{0:t-1}\right)\right) $$</code></p>
<p>其中，<code>$g_{\text{LM}}$</code> 为预测层。</p>
<h4 id="遮罩语言模型-masked-language-modeling-mlm">遮罩语言模型（Masked Language Modeling，MLM）</h4>
<p>大致上来说，MLM 首先将输入句子的一些词条进行遮挡处理，其次再训练模型利用剩余的部分预测遮挡的部分。这种预训练方法会导致在预训练（pre-training）阶段和微调（fine-tuning）阶段的不一致，因为在微调阶段遮挡标记并未出现，BERT <sup id="fnref:15"><a href="#fn:15" class="footnote-ref" role="doc-noteref">15</a></sup> 通过一个特殊的符号 <code>[MASK]</code> 对其进行处理。</p>
<h5 id="sequence-to-sequence-mlm-seq2seq-mlm">Sequence-to-Sequence MLM (Seq2Seq MLM)</h5>
<p>MLM 通常以一个分类问题进行求解，我们将遮挡后的序列输入到一个神经编码器，再将输出向量传给一个 Softmax 分类器来预测遮挡的字符。我们可以采用 Encoder-Decoder（Seq2Seq）网络结构，将遮挡的序列输入到 Encoder，Decoder 则会循序的产生被遮挡的字符。MASS <sup id="fnref:16"><a href="#fn:16" class="footnote-ref" role="doc-noteref">16</a></sup> 和 T5 <sup id="fnref:17"><a href="#fn:17" class="footnote-ref" role="doc-noteref">17</a></sup> 均采用了这种序列到序列的 MLM 结构，这种结构对 Seq2Seq 风格的下游任务很有帮助，例如：问答，摘要和机器翻译。</p>
<h5 id="enhanced-masked-language-modeling-e-mlm">Enhanced Masked Language Modeling (E-MLM)</h5>
<p>同时，大量研究对于 BERT 所使用的遮罩处理进行了改进。RoBERTa <sup id="fnref:18"><a href="#fn:18" class="footnote-ref" role="doc-noteref">18</a></sup> 采用了一种动态的遮罩处理。UniLM 将遮罩任务拓展到 3 种不同的类型：单向的，双向的和 Seq2Seq 类型的。</p>
<h4 id="排列语言模型-permuted-language-modeling-plm">排列语言模型（Permuted Language Modeling，PLM）</h4>
<p>在 MLM 中一些特殊字符（例如：<code>[MASK]</code>）在下游任务中是无用的，为了解决这个问题，XLNet <sup id="fnref:19"><a href="#fn:19" class="footnote-ref" role="doc-noteref">19</a></sup> 提出了一种排列语言模型（Permuted Language Modeling，PLM）用于替代 MLM。简言之，PLM 是对输入序列的排列进行语言建模。给定一个序列，从所有可能的排列中随机抽样得到一个排列，将排列后的序列中的一些字符作为模型的预测目标，利用其他部分和目标的自然位置进行训练。需要注意的是这种排列并不会影响序列的自然位置，其仅用于定义字符预测的顺序。</p>
<h4 id="去噪自编码-denoising-autoencoder-dae">去噪自编码（Denoising Autoencoder，DAE）</h4>
<p>DAE 旨在利用部分有损的输入恢复原始无损的输入。对于语言模型，例如 Seq2Seq 模型，可以采用标准的 Transformer 来重构原始文本。有多种方式可以对文本进行破坏 <sup id="fnref:20"><a href="#fn:20" class="footnote-ref" role="doc-noteref">20</a></sup>：</p>
<ol>
<li>字符遮罩：随机采样字符并将其替换为 <code>[MASK]</code>。</li>
<li>字符删除：随机的从输入中删除字符，不同于字符遮罩，模型需要确定丢失字符的位置。</li>
<li>文本填充：采样一段文本并将其替换为一个 <code>[MASK]</code>，每段文本的长度服从泊松分布（$\lambda = 3$），模型需要确定这段文本中缺失的字符个数。</li>
<li>句子重排：将文档以终止标点进行分割，再进行随机排序。</li>
<li>文档旋转：随机均匀地选择一个字符，对文档进行旋转使得这个字符作为文档的起始字符，模型需要确定文档真实的起始位置。</li>
</ol>
<h4 id="对比学习-contrastive-learning-ctl">对比学习（Contrastive Learning，CTL）</h4>
<p>对比学习 <sup id="fnref:21"><a href="#fn:21" class="footnote-ref" role="doc-noteref">21</a></sup> 假设一些观测到的文本对比随机采样的文本具有更相似的语义。对于文本对 <code>$\left(x, y\right)$</code> 通过最小化如下目标函数来学习评分函数 <code>$s \left(x, y\right)$</code>：</p>
<p><code>$$ \mathbb{E}_{x, y^+, y^-} \left[- \log \dfrac{\exp \left(s \left(x, y^+\right)\right)}{\exp \left(s \left(x, y^+\right)\right) + \exp \left(s \left(x, y^-\right)\right)}\right] $$</code></p>
<p>其中，<code>$\left(x, y^+\right)$</code> 为一个相似对，<code>$y^-$</code> 对于 <code>$x$</code> 而言假定为不相似，<code>$y^+$</code> 和 <code>$y^-$</code> 通常称之为正样本和负样本。评分函数 <code>$s \left(x, y\right)$</code> 通过一个神经编码器计算可得，<code>$s \left(x, y\right) = f^{\top}_{\text{enc}} \left(x\right) f_{\text{enc}} \left(y\right)$</code> 或 <code>$s \left(x, y\right) = f_{\text{enc}} \left(x \oplus y\right)$</code>。CTL 的核心思想是“通过对比进行学习”。</p>
<p>下图展示了预训练模型的分类和部分代表模型：</p>

<div class="box fancy-figure caption-position-bottom caption-effect-fade" >
  <figure class="photoswipe-figure" itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
    <div class="img">
      <img itemprop="thumbnail" src="/images/cn/2020-03-28-pre-trained-model-for-nlp/ptms.png" alt="预训练模型分类及代表性模型"/>
    </div>
    <a href="/images/cn/2020-03-28-pre-trained-model-for-nlp/ptms.png" itemprop="contentUrl"></a>
      <figcaption>
          <p>预训练模型分类及代表性模型</p>
      </figcaption>
  </figure>
</div>

<h3 id="应用于下游任务">应用于下游任务</h3>
<h4 id="如何迁移">如何迁移</h4>
<h5 id="选择合适的预训练任务-模型架构和语料">选择合适的预训练任务，模型架构和语料</h5>
<p>不同的 PTMs 在相同的下游任务上有着不同的效果，这是因为 PTMs 有着不同的预训练任务，模型架构和语料。</p>
<ol>
<li>目前，语言模型是最流行的预训练任务，同时也可以有效地解决很多 NLP 问题。但是不同的预训练任务有着自己的侧重，在不同的任务上会有不同的效果。例如：NSP 任务使得 PTM 可以理解两句话之间的关系，因此 PTM 可以在例如问答（Question Answering，QA）和自然语言推理（Natural Language Inference，NLI）等下游任务上表现更好。</li>
<li>PTM 的网络架构对下游任务也至关重要。例如：尽管 BERT 可以处理大多数自然语言理解任务，对其很难生成语言。</li>
<li>下游任务的数据分布应该和 PTM 训练所用语料相似。目前，大量现成的 PTM 仅可以快速地用于特定领域或特定语言的下游任务上。</li>
</ol>
<h5 id="选择合适的网络层">选择合适的网络层</h5>
<p>给定一个预训练的模型，不同的网络层捕获了不同的信息，例如：词性标记（POS tagging），语法（parsing），长期依赖（long-term dependencies），语义角色（semantic roles），指代（coreference）等。Tenney <sup id="fnref:22"><a href="#fn:22" class="footnote-ref" role="doc-noteref">22</a></sup> 等人发现 BERT 表示方式类似传统的 NLP 流程：基础的句法信息出现在浅层的网络中，高级的语义信息出现在更高的层级中。</p>
<p>令 <code>$\mathbf{H}^{\left(l\right)} \left(1 \leq l \leq L\right)$</code> 表示共 <code>$L$</code> 层的预训练模型的第 <code>$l$</code> 层表示，<code>$g \left(\cdot\right)$</code> 表示用于特定任务的的模型。一般有 3 中情况选择表示：</p>
<ol>
<li>Embedding Only：一种情况是仅选用预训练模型的静态嵌入，模型的其他部分仍需作为一个任务从头训练。这种情况不能够获取到一些有用的深层信息，词嵌入仅能够捕获词的语义信息。</li>
<li>Top Layer：最简单有效的方式是将网络的顶层表示输入到模型中 <code>$g \left(\mathbf{H}^{\left(L\right)}\right)$</code>。</li>
<li>All Layers：另一种更灵活的方式是自动选择最合适的层，例如 ELMo：
<code>$$ \mathbf{r}_t = \gamma \sum_{l=1}^{L}{\alpha_l \mathbf{h}^{\left(l\right)}_t} $$</code>
其中 <code>$\alpha_l$</code> 是层 <code>$l$</code> 的 softmax 归一的权重，<code>$\gamma$</code> 是用于缩放预训练模型输出向量的一个标量值，再将不同层的混合输出输入到后续模型中 <code>$g \left(\mathbf{r}_t\right)$</code>。</li>
</ol>
<h5 id="是否微调">是否微调</h5>
<p>目前，主要有两种方式进行模型迁移：特征提取（预训练模型的参数是固定的）和模型微调（预训练模型的参数是经过微调的）。当采用特征提取时，预训练模型可以被看作是一个特征提取器。除此之外，我们应该采用内部层作为特征，因为他们通常是最适合迁移的特征。尽管两种不同方式都能对大多数 NLP 任务效果有显著提升，但以特征提取的方式需要更复杂的特定任务的架构。因此，微调是一种更加通用和方便的处理下游任务的方式。</p>
<h4 id="微调策略">微调策略</h4>
<p>随着 PTMs 网络层数的加深，其捕获的表示使得下游任务变得越来越简单，因此整个模型中用于特定任务的网络层一般比较简单，微调已经成为了采用 PTMs 的主要方式。但是微调的过程通常是比较不好预估的，即使采用相同的超参数，不同的随机数种子也可能导致差异较大的结果。除了标准的微调外，如下为一些有用的微调策略：</p>
<h5 id="两步骤微调">两步骤微调</h5>
<p>一种方式是两阶段的迁移，在预训练和微调之间引入了一个中间阶段。在第一个阶段，PTM 通过一个中间任务或语料转换为一个微调后的模型，在第二个阶段，再利用目标任务进行微调。</p>
<h5 id="多任务微调">多任务微调</h5>
<p>在多任务学习框架下对其进行微调。</p>
<h5 id="利用额外模块进行微调">利用额外模块进行微调</h5>
<p>微调的主要缺点就是其参数的低效性。每个下游模型都有其自己微调好的参数，因此一个更好的解决方案是将一些微调好的适配模块注入到 PTMs 中，同时固定原始参数。</p>
<h3 id="开放资源">开放资源</h3>
<h4 id="ptms-开源实现">PTMs 开源实现：</h4>
<table>
  <thead>
      <tr>
          <th>项目</th>
          <th>框架</th>
          <th>PTMs</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="https://github.com/tmikolov/word2vec">word2vec</a></td>
          <td>-</td>
          <td>CBOW, Skip-Gram</td>
      </tr>
      <tr>
          <td><a href="https://nlp.stanford.edu/projects/glove">GloVe</a></td>
          <td>-</td>
          <td>Pre-trained word vectors</td>
      </tr>
      <tr>
          <td><a href="https://github.com/facebookresearch/fastText">FastText</a></td>
          <td>-</td>
          <td>Pre-trained word vectors</td>
      </tr>
      <tr>
          <td><a href="https://github.com/huggingface/transformers">Transformers</a></td>
          <td><i class="icon icon-pytorch">PyTorch</i> &amp; <i class="icon icon-tensorflow">TF</i></td>
          <td>BERT, GPT-2, RoBERTa, XLNet, etc.</td>
      </tr>
      <tr>
          <td><a href="https://github.com/pytorch/fairseq">Fairseq</a></td>
          <td><i class="icon icon-pytorch">PyTorch</i></td>
          <td>English LM, German LM, RoBERTa, etc.</td>
      </tr>
      <tr>
          <td><a href="https://github.com/%EF%AC%82airNLP/%EF%AC%82air">Flair</a></td>
          <td><i class="icon icon-pytorch">PyTorch</i></td>
          <td>BERT, ELMo, GPT, RoBERTa, XLNet, etc.</td>
      </tr>
      <tr>
          <td><a href="https://github.com/allenai/allennlp">AllenNLP</a></td>
          <td><i class="icon icon-pytorch">PyTorch</i></td>
          <td>ELMo, BERT, GPT-2, etc.</td>
      </tr>
      <tr>
          <td><a href="https://github.com/fastnlp/fastNLP">FastNLP</a></td>
          <td><i class="icon icon-pytorch">PyTorch</i></td>
          <td>BERT, RoBERTa, GPT, etc.</td>
      </tr>
      <tr>
          <td><a href="https://github.com/ymcui/Chinese-BERT-wwm">Chinese-BERT</a></td>
          <td>-</td>
          <td>BERT, RoBERTa, etc. (for Chinese)</td>
      </tr>
      <tr>
          <td><a href="https://github.com/google-research/bert">BERT</a></td>
          <td><i class="icon icon-tensorflow">TF</i></td>
          <td>BERT, BERT-wwm</td>
      </tr>
      <tr>
          <td><a href="https://github.com/pytorch/fairseq/tree/master/examples/roberta">RoBERTa</a></td>
          <td><i class="icon icon-pytorch">PyTorch</i></td>
          <td></td>
      </tr>
      <tr>
          <td><a href="https://github.com/zihangdai/xlnet">XLNet</a></td>
          <td><i class="icon icon-tensorflow">TF</i></td>
          <td></td>
      </tr>
      <tr>
          <td><a href="https://github.com/google-research/ALBERT">ALBERT</a></td>
          <td><i class="icon icon-tensorflow">TF</i></td>
          <td></td>
      </tr>
      <tr>
          <td><a href="https://github.com/google-research/text-to-text-transfer-transformer">T5</a></td>
          <td><i class="icon icon-tensorflow">TF</i></td>
          <td></td>
      </tr>
      <tr>
          <td><a href="https://github.com/thunlp/ERNIE">ERNIE(THU)</a></td>
          <td><i class="icon icon-pytorch">PyTorch</i></td>
          <td></td>
      </tr>
      <tr>
          <td><a href="https://github.com/PaddlePaddle/ERNIE">ERNIE(Baidu)</a></td>
          <td><i class="icon icon-paddlepaddle"></i> PaddlePaddle</td>
          <td></td>
      </tr>
      <tr>
          <td><a href="https://github.com/huggingface/transformers">Hugging Face</a></td>
          <td><i class="icon icon-pytorch">PyTorch</i> &amp; <i class="icon icon-tensorflow">TF</i></td>
          <td>很多&hellip;</td>
      </tr>
  </tbody>
</table>
<h4 id="论文列表和-ptms-相关资源">论文列表和 PTMs 相关资源：</h4>
<table>
  <thead>
      <tr>
          <th>资源</th>
          <th>URL</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>论文列表</td>
          <td><a href="https://github.com/thunlp/PLMpapers">https://github.com/thunlp/PLMpapers</a></td>
      </tr>
      <tr>
          <td>论文列表</td>
          <td><a href="https://github.com/tomohideshibata/BERT-related-papers">https://github.com/tomohideshibata/BERT-related-papers</a></td>
      </tr>
      <tr>
          <td>论文列表</td>
          <td><a href="https://github.com/cedrickchee/awesome-bert-nlp">https://github.com/cedrickchee/awesome-bert-nlp</a></td>
      </tr>
      <tr>
          <td>Bert Lang Street</td>
          <td><a href="https://bertlang.unibocconi.it">https://bertlang.unibocconi.it</a></td>
      </tr>
      <tr>
          <td>BertViz</td>
          <td><a href="https://github.com/jessevig/bertviz">https://github.com/jessevig/bertviz</a></td>
      </tr>
  </tbody>
</table>
<h2 id="预训练模型">预训练模型</h2>
<h3 id="cove-2017">CoVe (2017) <sup id="fnref1:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup></h3>
<p>首先，给定一个源语言序列 <code>$w^x = \left[w^x_1, \dotsc, w^x_n\right]$</code> 和一个翻译目标语言序列 <code>$w^z = \left[w^z_1, \dotsc, w^z_n\right]$</code>。令 <code>$\text{GloVe} \left(w^x\right)$</code> 为词 <code>$w^x$</code> 对应的 GloVe 向量，<code>$z$</code> 为 <code>$w^z$</code> 中的词随机初始化的词向量。将 <code>$\text{GloVe} \left(w^x\right)$</code> 输入到一个标准的两层 biLSTM 网络中，称之为 MT-LSTM，MT-LSTM 用于计算序列的隐含状态如下：</p>
<p><code>$$ h = \text{MT-LSTM} \left(\text{GloVe} \left(w^x\right)\right) $$</code></p>
<p>对于机器翻译，MT-LSTM 的注意力机制的解码器可以对于输出的词在每一步产生一个分布 <code>$p \left(\hat{w}^z_t \mid H, w^z_1, \dotsc, w^z_{t-1}\right)$</code>。在 <code>$t$</code> 步，解码器利用一个两层的单向 LSTM 基于之前目标词嵌入 <code>$z_{t-1}$</code> 和一个基于上下文调整的隐含状态 <code>$\tilde{h}_{t-1}$</code> 生成一个隐含状态 <code>$h^{\text{dec}}_t$</code>：</p>
<p><code>$$ h^{\text{dec}}_t = \text{LSTM} \left(\left[z_{t-1}; \tilde{h}_{t-1}\right], h^{\text{dec}}_{t-1}\right) $$</code></p>
<p>之后解码器计算每一步编码到当前解码状态的注意力权重 <code>$\alpha$</code>：</p>
<p><code>$$ \alpha_t = \text{softmax} \left(H \left(W_1 h^{\text{dec}}_t + b_1\right)\right) $$</code></p>
<p>其中 <code>$H$</code> 表示 <code>$h$</code> 按照时间维度的堆叠。之后解码器将这些权重作为相关性用于计算基于上下文调整的隐含状态 <code>$\tilde{h}$</code>：</p>
<p><code>$$ \tilde{h}_t = \text{tanh} \left(W_2 \left[H^{\top} \alpha_t; h^{\text{dec}}_t\right] + b_2\right) $$</code></p>
<p>最后，输出词的分布通过基于上下文调整的隐含状态计算可得：</p>
<p><code>$$ p \left(\hat{w}^z_t \mid H, w^z_1, \dotsc, w^z_{t-1}\right) = \text{softmax} \left(W_{\text{out}} \tilde{h}_t + b_{\text{out}}\right) $$</code></p>
<p>CoVe 将 MT-LSTM 学习到的表示迁移到下游任务中，令 <code>$w$</code> 表示文字序列，<code>$\text{GloVe} \left(w\right)$</code> 表示对应的 GloVe 向量，则：</p>
<p><code>$$ \text{CoVe} \left(w\right) = \text{MT-LSTM} \left(\text{GloVe} \left(w\right)\right) $$</code></p>
<p>表示由 MT-LSTM 产生的上下文向量，对于分类和问答任务，有一个输入序列 <code>$w$</code>，我们可以将 GloVe 和 CoVe 向量进行拼接作为其嵌入表示：</p>
<p><code>$$ \tilde{w} = \left[\text{GloVe} \left(w\right); \text{CoVe} \left(w\right)\right] $$</code></p>
<p>CoVe 网络架构示意图如下：</p>
<figure>
  <img data-src="/images/cn/2020-03-28-pre-trained-model-for-nlp/cove.png" class="lazyload"/>
  
</figure>
<h3 id="elmo-2018">ELMo (2018) <sup id="fnref1:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup></h3>
<p>在 ELMo 模型中，对于每个词条 <code>$t_k$</code>，一个 <code>$L$</code> 层的 biLM 可以计算得到 <code>$2L + 1$</code> 个表示：</p>
<p><code>$$ \begin{aligned} R_k &amp;= \left\{\mathbf{x}^{LM}_k, \overrightarrow{\mathbf{h}}^{LM}_{k, j}, \overleftarrow{\mathbf{h}}^{LM}_{k, j} \mid j = 1, \dotsc, L \right\} \\ &amp;= \left\{\mathbf{h}^{LM}_{k, j} \mid j = 0, \dotsc, L\right\} \end{aligned} $$</code></p>
<p>其中 <code>$\mathbf{h}^{LM}_{k, 0}$</code> 为词条的嵌入层，<code>$\mathbf{h}^{LM}_{k, j} = \left[\overrightarrow{\mathbf{h}}^{LM}_{k, j}; \overleftarrow{\mathbf{h}}^{LM}_{k, j}\right]$</code> 为每个 biLSTM 层。</p>
<p>对于下游任务，ELMo 将 <code>$R$</code> 中的所有层汇总成一个向量 <code>$\mathbf{ELMo}_k = E \left(R_k; \mathbf{\Theta}_e\right)$</code>。在一些简单的案例中，ELMo 仅选择顶层，即：<code>$E \left(R_k\right) = \mathbf{h}^{LM}_{k, L}$</code>。更通用的，对于一个特定的任务，我们可以计算一个所有 biLM 层的加权：</p>
<p><code>$$ \mathbf{ELMo}^{task}_k = E \left(R_k; \Theta^{task}\right) = \gamma^{task} \sum_{j=0}^{L}{s^{task}_j \mathbf{h}^{LM}_{k, j}} $$</code></p>
<p>其中，<code>$s^{task}$</code> 表示 softmax 归一化后的权重，<code>$\gamma^{task}$</code> 允许模型对整个 ELMo 向量进行缩放。<code>$\gamma$</code> 对整个优化过程具有重要意义，考虑每个 biLM 层的激活具有不同的分布，在一些情况下这相当于在进行加权之前对每一个 biLM 层增加了层标准化。</p>
<p>ELMo 网络架构示意图如下 <sup id="fnref:23"><a href="#fn:23" class="footnote-ref" role="doc-noteref">23</a></sup>：</p>
<figure>
  <img data-src="/images/cn/2020-03-28-pre-trained-model-for-nlp/elmo.png" class="lazyload"/>
  
</figure>
<h3 id="gpt-2018">GPT (2018) <sup id="fnref1:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup></h3>
<p>给定一个语料 <code>$\mathcal{U} = \left\{u_1, \dotsc, u_n\right\}$</code>，使用标准的语言建模目标来最大化如下似然：</p>
<p><code>$$ L_1 \left(\mathcal{U}\right) = \sum_{i} \log P \left(u_i \mid u_{i-k}, \dotsc, u_{i-1}; \Theta\right) $$</code></p>
<p>其中，<code>$k$</code> 为上下文窗口的大小，条件概率 <code>$P$</code> 通过参数为 <code>$\Theta$</code> 的神经网络进行建模。GPT 中使用了一个多层的 Transformer Decoder 作为语言模型。模型首先对输入上下文词条应用多头自注意力机制，再通过按位置的前馈层产生目标词条的输出分布：</p>
<p><code>$$ \begin{aligned} h_0 &amp;= UW_e + W_p \\ h_l &amp;= \text{transformer_black} \left(h_{l-1}\right), \forall i \in \left[1, n\right] \\ P \left(u\right) &amp;= \text{softmax} \left(h_n W^{\top}_e\right) \end{aligned} $$</code></p>
<p>其中，<code>$U = \left(u_{-k}, \dotsc, u_{-1}\right)$</code> 为词条的上下文向量，<code>$n$</code> 为网络层数，<code>$W_e$</code> 为词条的嵌入矩阵，<code>$W_p$</code> 为位置嵌入矩阵。</p>
<p>给定一个有标签的数据集 <code>$\mathcal{C}$</code>，其中包含了输入词条序列 <code>$x^1, \dotsc, x^m$</code> 和对应的标签 <code>$y$</code>。利用上述预训练的模型获得输入对应的最后一个 Transformer 的激活输出 <code>$h^m_l$</code>，之后再将其输入到一个参数为 <code>$W_y$</code> 的线性输入层中预测 <code>$y$</code>：</p>
<p><code>$$ P \left(y \mid x^1, \dotsc, x^m\right) = \text{softmax} \left(h^m_l W_y\right) $$</code></p>
<p>模型通过最小化如下损失进行优化：</p>
<p><code>$$ L_2 \left(\mathcal{C}\right) = \sum_{\left(x, y\right)} \log P \left(y \mid x^1, \dotsc, x^m\right) $$</code></p>
<p>研究还发现将语言建模作为微调的附加目标可以帮助提高模型的泛化能力，同时可以加速模型收敛。GPT 中采用如下的优化目标：</p>
<p><code>$$ L_3 \left(\mathcal{C}\right) = L_2 \left(\mathcal{C}\right) + \lambda L_1 \left(\mathcal{C}\right) $$</code></p>
<p>GPT 网络架构示意图如下：</p>
<figure>
  <img data-src="/images/cn/2020-03-28-pre-trained-model-for-nlp/gpt.png" class="lazyload"/>
  
</figure>
<h3 id="bert-2018">BERT (2018) <sup id="fnref1:7"><a href="#fn:7" class="footnote-ref" role="doc-noteref">7</a></sup></h3>
<p>BERT 采用了一中基于 Vaswani <sup id="fnref1:14"><a href="#fn:14" class="footnote-ref" role="doc-noteref">14</a></sup> 所提出模型的多层双向 Transformer 编码器。在 BERT 中，令 <code>$L$</code> 为 Transformer Block 的层数，<code>$H$</code> 为隐层大小，<code>$A$</code> 为自注意力头的数量。在所有情况中，设置前馈层的大小为 <code>$4H$</code>，BERT 提供了两种不同大小的预训练模型：</p>
<ul>
<li><code>$\text{BERT}_{\text{BASE}}$</code>：<code>$L=12, H=768, A=12$</code>，参数总量为 100 M。</li>
<li><code>$\text{BERT}_{\text{LARGE}}$</code>：<code>$L=24, H=1024, A=16$</code>，参数总量为 340 M。</li>
</ul>
<p><code>$\text{BERT}_{\text{BASE}}$</code> 采用了同 GPT 相同的模型大小用于比较，不同与 GPT，BERT 使用了双向的注意力机制。在文献中，双向 Transformer 通常称之为 Transformer 编码器，仅利用左边上下文信息的 Transformer 由于可以用于文本生成被称之为 Transformer 解码器。BERT，GPT 和 ELMo 之间的不同如下图所示：</p>
<figure>
  <img data-src="/images/cn/2020-03-28-pre-trained-model-for-nlp/bert-gpt-elmo-model-architectures.png" class="lazyload"/>
  
</figure>
<p>BERT 的输入表示既可以表示一个单独的文本序列，也可以表示一对文本序列（例如：问题和答案）。对于一个给定的词条，其输入表示由对应的词条嵌入，分割嵌入和位置嵌入三部分加和构成，如下图所示：</p>
<figure>
  <img data-src="/images/cn/2020-03-28-pre-trained-model-for-nlp/bert-input-representation.png" class="lazyload"/>
  
</figure>
<p>具体的有：</p>
<ul>
<li>采用一个包含 30,000 个词条的 WordPiece 嵌入 <sup id="fnref:24"><a href="#fn:24" class="footnote-ref" role="doc-noteref">24</a></sup>。</li>
<li>位置嵌入最大支持 512 个词条。</li>
<li>序列的第一字符采用特殊的分类嵌入 <code>[CLS]</code>，其最终的隐含状态在分类任务中用于汇总整个序列的表示，对于非分类任务则忽视该向量。</li>
<li>句子对被整合成一个序列，首先利用一个特殊词条 <code>[SEP]</code> 对句子进行分割，其次对于第一个句子中的每个词条叠加一个学习到的 A 句子嵌入，对于第二个句子中的每个词条叠加一个学习到的 B 句子嵌入。</li>
<li>对于一个单独的句子，仅使用 A 句子嵌入。</li>
</ul>
<p>在预训练阶段，BERT 采用了两个无监督预测任务：</p>
<ol>
<li>遮罩的语言模型（Masked LM，MLM）<br>
不同于一般的仅利用 <code>[MASK]</code> 进行遮挡，BERT 选择采用 80% 的 <code>[MASK]</code>，10% 的随机词和 10% 保留原始词的方式对随机选择的 15% 的词条进行遮挡处理。由于编码器不知会预测哪个词或哪个词被随机替换了，这迫使其必须保留每个输入词条的分布式上下文表示。同时 1.5% 的随机替换也不会过多的损害模型的理解能力。</li>
<li>预测是否为下一个句子（Next Sentence Prediction）<br>
一些重要的下游任务，例如问答（Question Answering，QA）和自然语言推断（Natural Language Inference，NLI）是基于两个句子之间关系的理解，这是语言建模无法直接捕获的。BERT 通过训练一个预测是否为下一个句子的二分类任务来实现，对于一个句子对 A 和 B，50% 的 B 是句子 A 真实的下一句，剩余 50% 为随机抽取的。</li>
</ol>
<p>基于 BERT 的不同下游任务的实现形式如下图所示：</p>
<figure>
  <img data-src="/images/cn/2020-03-28-pre-trained-model-for-nlp/bert-task-specific-models.png" class="lazyload"/>
  
</figure>
<h3 id="unilm-2019">UniLM (2019) <sup id="fnref:25"><a href="#fn:25" class="footnote-ref" role="doc-noteref">25</a></sup></h3>
<p>给定一个输入序列 <code>$x = x_1 \cdots x_{|x|}$</code>，UniLM 通过下图的方式获取每个词条的基于上下文的向量表示。整个预训练过程利用单向的语言建模（unidirectional LM），双向的语言建模（bidirectional LM）和 Seq2Seq 语言建模（sequence-to-sequence LM）优化共享的 Transformer 网络。</p>
<figure>
  <img data-src="/images/cn/2020-03-28-pre-trained-model-for-nlp/unilm.png" class="lazyload"/>
  
</figure>
<p>输入序列 <code>$x$</code> 对于单向语言模型而言是一个分割的文本，对于双向语言模型和 Seq2Seq 语言模型而言是一对打包的分割文本。UniLM 在输入的起始位置添加特殊的 <code>[SOS]</code> （start-of-sequence），在结尾处添加 <code>[EOS]</code>（end-of-sequence）。<code>[EOS]</code> 对于自然语言理解（NLU）任务可以标记句子之间的界线，对于自然语言生成（NLG）任务可以确定解码过程停止的时间。输入的表示同 BERT 一样，文本利用 WordPiece 进行分割，对于每个输入词条，其向量表示为对应的词条嵌入，位置嵌入和分割嵌入的汇总。</p>
<p>对于输入向量 <code>$\left\{\mathbf{x}_i\right\}^{|x|}_{i=1}$</code> 首先将其输入到隐层 <code>$\mathbf{H}^0 = \left[\mathbf{x}_1, \dotsc, \mathbf{x}_{|x|}\right]$</code>，之后使用一个 <code>$L$</code> 层的 Transformer <code>$\mathbf{H}^l = \text{Transformer}_l \left(\mathbf{H}^{l-1}\right), l \in \left[1, L\right]$</code> 对每一层 <code>$\mathbf{H}^l = \left[\mathbf{h}^l_1, \dotsc, \mathbf{h}^l_{|x|}\right]$</code> 进行上下文表示编码。在每个 Tansformer 块中，使用多头自注意力机制对输出向量和上一层进行汇总，第 <code>$l$</code> 层 Transformer 自注意力头 <code>$\mathbf{A}_l$</code> 的输入通过如下方式计算：</p>
<p>`$$
\begin{aligned}
\mathbf{Q} &amp;= \mathbf{H}^{l-1} \mathbf{W}^Q_l, \mathbf{K} = \mathbf{H}^{l-1} \mathbf{W}^K_l, \mathbf{V} = \mathbf{H}^{l-1} \mathbf{W}^W_l \
\mathbf{M}_{ij} &amp;=
\begin{cases}
0, &amp; \text{allow to attend} \</p>
<ul>
<li>\infty, &amp; \text{prevent from attending}
\end{cases} \
\mathbf{A}_l &amp;= \text{softmax} \left(\dfrac{\mathbf{Q} \mathbf{K}^{\top}}{\sqrt{d_k}} + \mathbf{M}\right) \mathbf{V}_l
\end{aligned}
$$`</li>
</ul>
<p>其中，上一层的输出 <code>$\mathbf{H}^{l-1} \in \mathbb{R}^{|x| \times d_h}$</code> 通过参数矩阵 <code>$\mathbf{W}^Q_l, \mathbf{W}^K_l, \mathbf{W}^V_l \in \mathbb{R}^{d_h \times d_k}$</code> 线性地映射为相应的 Query，Key 和 Value，遮罩矩阵 <code>$\mathbf{M} \in \mathbb{R}^{|x| \times |x|}$</code> 用于确定一对词条是否可以被相互连接。</p>
<h3 id="transformer-xl-2019">Transformer-XL (2019) <sup id="fnref:26"><a href="#fn:26" class="footnote-ref" role="doc-noteref">26</a></sup></h3>
<p>将 Transformer 或注意力机制应用到语言建模中的核心问题是如何训练 Transformer 使其有效地将一个任意长文本编码为一个固定长度的表示。Transformer-XL 将整个语料拆分为较短的段落，仅利用每段进行训练并忽略之前段落的上下文信息。这种方式称之为 Vanilla Model <sup id="fnref:27"><a href="#fn:27" class="footnote-ref" role="doc-noteref">27</a></sup>，如下图所示：</p>
<figure>
  <img data-src="/images/cn/2020-03-28-pre-trained-model-for-nlp/transformer-xl-vanilla-model.png" class="lazyload"/>
  
</figure>
<p>在这种训练模式下，无论是前向还是后向信息都不会跨越分割的段落进行传导。利用固定长度的上下文主要有两个弊端：</p>
<ol>
<li>这限制了最大依赖的长度，虽然自注意力机制不会像 RNN 一样受到梯度弥散的影响，但 Vanilla Model 也不能完全利用到这个优势。</li>
<li>虽然可以利用补全操作来实现句子或其他语义的分割，但实际上通常会简单的将一个长文本截断成一个固定长度的分割，这样会产生上下文分裂破碎的问题。</li>
</ol>
<p>为了解决这个问题，Transformer-XL 采用了一种循环机制的 Transformer。在训练阶段，在处理新的分割段落时，之前分割分部分的隐含状态序列将被**固定（fixed）<strong>和</strong>缓存（cached）**下来作为一个扩展的上下文被复用参与计算，如下图所示：</p>
<figure>
  <img data-src="/images/cn/2020-03-28-pre-trained-model-for-nlp/transformer-xl-model.png" class="lazyload"/>
  
</figure>
<p>虽然梯度仍仅限于这个分割段落内部，但网络可以从历史中获取信息，从而实现对长期依赖的建模。令两个长度为 <code>$L$</code> 的连续分割段落为 <code>$\mathbf{s}_{\tau} = \left[x_{\tau, 1}, \dotsc, x_{\tau, L}\right]$</code> 和 <code>$\mathbf{s}_{\tau + 1} = \left[x_{\tau + 1, 1}, \dotsc, x_{\tau + 1, L}\right]$</code>，第 <code>$\tau$</code> 段分割 <code>$\mathbf{s}_{\tau}$</code> 的第 <code>$n$</code> 层隐含状态为 <code>$\mathbf{h}^n_{\tau} \in \mathbb{R}^{L \times d}$</code>，其中 <code>$d$</code> 为隐含维度。则对于分割段落 <code>$\mathbf{s}_{\tau + 1}$</code> 的第 <code>$n$</code> 层隐含状态通过如下方式进行计算：</p>
<p><code>$$ \begin{aligned} \tilde{\mathbf{h}}^{n-1}_{\tau + 1} &amp;= \left[\text{SG} \left(\mathbf{h}^{n-1}_{\tau}\right) \circ \mathbf{h}^{n-1}_{\tau + 1} \right] \\ \mathbf{q}^{n}_{\tau + 1}, \mathbf{k}^{n}_{\tau + 1}, \mathbf{v}^{n}_{\tau + 1} &amp;= \mathbf{h}^{n-1}_{\tau + 1} \mathbf{W}^{\top}_{q}, \tilde{\mathbf{h}}^{n-1}_{\tau + 1} \mathbf{W}^{\top}_{k}, \tilde{\mathbf{h}}^{n-1}_{\tau + 1} \mathbf{W}^{\top}_{v} \\ \mathbf{h}^{n}_{\tau + 1} &amp;= \text{Transformer-Layer} \left(\mathbf{q}^{n}_{\tau + 1}, \mathbf{k}^{n}_{\tau + 1}, \mathbf{v}^{n}_{\tau + 1}\right) \end{aligned} $$</code></p>
<p>其中，<code>$\text{SG} \left(\cdot\right)$</code> 表示停止梯度，<code>$\left[\mathbf{h}_u \circ \mathbf{h}_v\right]$</code> 表示将两个隐含序列按照长度维度进行拼接，<code>$\mathbf{W}$</code> 为模型的参数。与一般的 Transformer 相比，最大的不同在于 <code>$\mathbf{k}^n_{\tau + 1}$</code> 和 <code>$\mathbf{v}^n_{\tau + 1}$</code> 不仅依赖于 <code>$\tilde{\mathbf{h}}^{n-1}_{\tau - 1}$</code> 还依赖于之前分割段落的 <code>$\mathbf{h}^{n-1}_{\tau}$</code> 缓存。</p>
<p>在标准的 Transformer 中，序列的顺序信息通过位置嵌入 <code>$\mathbf{U} \in \mathbb{R}^{L_{\max} \times d}$</code> 提供，其中第 <code>$i$</code> 行 <code>$\mathbf{U}_i$</code> 对应一个分割文本内部的第 <code>$i$</code> 个<strong>绝对</strong>位置，<code>$L_{\max}$</code> 为最大可能长度。在 Transformer-XL 中则是通过一种<strong>相对</strong>位置信息对其进行编码，构建一个相对位置嵌入 <code>$\mathbf{R} \in \mathbb{R} ^{L_{\max} \times d}$</code>，其中第 <code>$i$</code> 行 <code>$\mathbf{R}_i$</code> 表示两个位置之间相对距离为 <code>$i$</code> 的嵌入表示。</p>
<p>对于一般的 Transformer，一个分割段落内部的 <code>$q_i$</code> 和 <code>$k_j$</code> 之间的注意力分数可以分解为：</p>
<p><code>$$ \begin{aligned} \mathbf{A}_{i, j}^{\mathrm{abs}} &amp;=\underbrace{\mathbf{E}_{x_{i}}^{\top} \mathbf{W}_{q}^{\top} \mathbf{W}_{k} \mathbf{E}_{x_{j}}}_{(a)}+\underbrace{\mathbf{E}_{x_{i}}^{\top} \mathbf{W}_{q}^{\top} \mathbf{W}_{k} \mathbf{U}_{j}}_{(b)} \\ &amp;+\underbrace{\mathbf{U}_{i}^{\top} \mathbf{W}_{q}^{\top} \mathbf{W}_{k} \mathbf{E}_{x_{j}}}_{(c)}+\underbrace{\mathbf{U}_{i}^{\top} \mathbf{W}_{q}^{\top} \mathbf{W}_{k} \mathbf{U}_{j}}_{(d)} \end{aligned} $$</code></p>
<p>利用相对位置思想，变化如下：</p>
<p><code>$$ \begin{aligned} \mathbf{A}_{i, j}^{\mathrm{rel}} &amp;=\underbrace{\mathbf{E}_{x_{i}}^{\top} \mathbf{W}_{q}^{\top} \mathbf{W}_{k, E} \mathbf{E}_{x_{j}}}_{(a)}+\underbrace{\mathbf{E}_{x_{i}}^{\top} \mathbf{W}_{q}^{\top} \mathbf{W}_{k, R} \textcolor{blue}{\mathbf{R}_{i-j}}}_{(b)} \\ &amp;+\underbrace{\textcolor{red}{u^{\top}} \mathbf{W}_{k, E} \mathbf{E}_{x_{j}}}_{(c)}+\underbrace{\textcolor{red}{v^{\top}} \mathbf{W}_{k, R} \textcolor{blue}{\mathbf{R}_{i-j}}}_{(d)} \end{aligned} $$</code></p>
<ol>
<li>首先，利用相对位置 <code>$\textcolor{blue}{\mathbf{R}_{i-j}}$</code> 替代绝对位置嵌入 <code>$\mathbf{U}_j$</code>，这里 <code>$\mathbf{R}$</code> 采用的是无需学习的 sinusoid 编码矩阵 <sup id="fnref2:14"><a href="#fn:14" class="footnote-ref" role="doc-noteref">14</a></sup>。</li>
<li>其次，引入了一个可训练的参数 <code>$\textcolor{red}{u} \in \mathbb{R}^d$</code> 用于替换 <code>$\mathbf{U}^{\top}_i \mathbf{W}^{\top}_q$</code>。类似的，对于 <code>$\mathbf{U}^{\top} \mathbf{W}^{\top}_q$</code> 使用一个可训练的 <code>$\textcolor{red}{v} \in \mathbb{R}^d$</code> 替换。</li>
<li>最后，有意地划分了两个权重矩阵 <code>$\mathbf{W}_{k, E}$</code> 和 <code>$\mathbf{W}_{k, R}$</code> 用于生成基于内容的 Key 向量和基于位置的 Key 向量。</li>
</ol>
<p>这样，<code>$\left(a\right)$</code> 代表了基于内容的位置信息，<code>$\left(b\right)$</code> 捕获了内容无关的位置偏置，<code>$\left(c\right)$</code> 表示了一个全局的内容偏置，<code>$\left(d\right)$</code> 捕获了一个全局的位置偏置。</p>
<p>利用一个自注意力头计算 <code>$N$</code> 层的 Transformer-XL 的过程如下，对于 <code>$n = 1, \dotsc, N$</code> 有：</p>
<p><code>$$ \begin{aligned} \widetilde{\mathbf{h}}_{\tau}^{n-1}=&amp;\left[\mathrm{SG}\left(\mathbf{m}_{\tau}^{n-1}\right) \circ \mathbf{h}_{\tau}^{n-1}\right] \\ \mathbf{q}_{\tau}^{n}, \mathbf{k}_{\tau}^{n}, \mathbf{v}_{\tau}^{n}=&amp; \mathbf{h}_{\tau}^{n-1} {\mathbf{W}_{q}^{n}}^{\top}, \widetilde{\mathbf{h}}_{\tau}^{n-1} {\mathbf{W}_{k, E}^{n}}^{\top}, \widetilde{\mathbf{h}}_{\tau}^{n-1} {\mathbf{W}_{v}^{n}}^{\top} \\ \mathbf{A}_{\tau, i, j}^{n}=&amp; {\mathbf{q}_{\tau, i}^{n}}^{\top} \mathbf{k}_{\tau, j}^{n} + {\mathbf{q}_{\tau, i}^{n}}^{\top} \mathbf{W}_{k, R}^{n} \mathbf{R}_{i-j} \\ &amp;+u^{\top} \mathbf{k}_{\tau, j}+v^{\top} \mathbf{W}_{k, R}^{n} \mathbf{R}_{i-j} \\ \mathbf{a}_{\tau}^{n}=&amp; \text { Masked-Softmax }\left(\mathbf{A}_{\tau}^{n}\right) \mathbf{v}_{\tau}^{n} \\ \mathbf{o}_{\tau}^{n}=&amp; \text { LayerNorm } \left(\text{Linear}\left(\mathbf{a}_{\tau}^{n}\right)+\mathbf{h}_{\tau}^{n-1}\right) \\ \mathbf{h}_{\tau}^{n}=&amp; \text { Positionwise-Feed-Forward }\left(\mathbf{o}_{\tau}^{n}\right) \end{aligned} $$</code></p>
<h3 id="xlnet-2019">XLNet (2019) <sup id="fnref1:19"><a href="#fn:19" class="footnote-ref" role="doc-noteref">19</a></sup></h3>
<p>给定一个序列 <code>$\mathbf{X} = \left[x_1, \dotsc, x_T\right]$</code>，AR 语言模型通过最大化如下似然进行预训练：</p>
<p><code>$$ \max_{\theta} \quad \log p_{\theta}(\mathbf{x})=\sum_{t=1}^{T} \log p_{\theta}\left(x_{t} | \mathbf{x}_{&lt;t}\right)=\sum_{t=1}^{T} \log \frac{\exp \left(h_{\theta}\left(\mathbf{x}_{1: t-1}\right)^{\top} e\left(x_{t}\right)\right)}{\sum_{x^{\prime}} \exp \left(h_{\theta}\left(\mathbf{x}_{1: t-1}\right)^{\top} e\left(x^{\prime}\right)\right)} $$</code></p>
<p>其中，<code>$h_{\theta}\left(\mathbf{x}_{1: t-1}\right)$</code> 是由 RNNs 或 Transformer 等神经网络网络模型生成的上下文表示，<code>$e \left(x\right)$</code> 为 <code>$x$</code> 的嵌入。对于一个文本序列 <code>$\mathbf{x}$</code>，BERT 首先构建了一个遮罩的数据集 <code>$\hat{\mathbf{x}}$</code>，令被遮挡的词条为 <code>$\overline{\mathbf{x}}$</code>，通过训练如下目标来利用 <code>$\hat{\mathbf{x}}$</code> 重构 <code>$\overline{\mathbf{x}}$</code>：</p>
<p><code>$$ \max_{\theta} \quad \log p_{\theta}(\overline{\mathbf{x}} | \hat{\mathbf{x}}) \approx \sum_{t=1}^{T} m_{t} \log p_{\theta}\left(x_{t} | \hat{\mathbf{x}}\right)=\sum_{t=1}^{T} m_{t} \log \frac{\exp \left(H_{\theta}(\hat{\mathbf{x}})_{t}^{\top} e\left(x_{t}\right)\right)}{\sum_{x^{\prime}} \exp \left(H_{\theta}(\hat{\mathbf{x}})_{t}^{\top} e\left(x^{\prime}\right)\right)} $$</code></p>
<p>其中 <code>$m_t = 1$</code> 表示 <code>$x_t$</code> 是被遮挡的，<code>$H_{\theta}$</code> 是一个 Transformer 将一个长度为 <code>$T$</code> 的文本序列映射到一个隐含向量序列 <code>$H_{\theta}(\mathbf{x})=\left[H_{\theta}(\mathbf{x})_{1}, H_{\theta}(\mathbf{x})_{2}, \cdots, H_{\theta}(\mathbf{x})_{T}\right]$</code>。两种不同的预训练目标的优劣势如下</p>
<ol>
<li><strong>独立假设</strong>：BERT 中联合条件概率 <code>$p(\overline{\mathbf{x}} | \hat{\mathbf{x}})$</code> 假设在给定的 <code>$\hat{\mathbf{x}}$</code> 下，遮挡的词条 <code>$\overline{\mathbf{x}}$</code> 是相关独立的，而 AR 语言模型则没有这样的假设。</li>
<li><strong>输入噪声</strong>：BERT 在预训练是使用了特殊标记 <code>[MASK]</code>，在下游任务微调时不会出现，而 AR 语言模型则不会存在这个问题。</li>
<li><strong>上下文依赖</strong>：AR 语言模型仅考虑了词条左侧的上下文，而 BERT 则可以捕获两个方向的上下文。</li>
</ol>
<p>为了利用 AR 语言模型和 BERT 的优点，XLNet 提出了排序语言模型。对于一个长度为 <code>$T$</code> 序列 <code>$\mathbf{x}$</code>，共有 <code>$T!$</code> 种不同的方式进行 AR 分解，如果模型共享不同分解顺序的参数，那么模型就能学习到两侧所有位置的信息。令 <code>$\mathcal{Z}_T$</code> 为长度为 <code>$T$</code> 的索引序列 <code>$\left[1, 2, \dotsc, T\right]$</code> 的所有可能排列，<code>$z_t$</code> 和 <code>$\mathbf{z}_{&lt;t}$</code> 分别表示一个排列 <code>$\mathbf{z} \in \mathcal{Z}_T$</code> 第 <code>$t$</code> 个和前 <code>$t-1$</code> 个元素。则排列语言模型的优化目标为：</p>
<p><code>$$ \max_{\theta} \quad \mathbb{E}_{\mathbf{z} \sim \mathcal{Z}_{T}}\left[\sum_{t=1}^{T} \log p_{\theta}\left(x_{z_{t}} | \mathbf{x}_{\mathbf{z}_{&lt;t}}\right)\right] $$</code></p>
<p>根据标准的 Transformer，下一个词条的分布 <code>$p_{\theta}\left(X_{z_{t}} | \mathbf{x}_{\mathbf{z}&lt;t}\right)$</code> 为：</p>
<p><code>$$ p_{\theta}\left(X_{z_{t}} = x | \mathbf{x}_{\mathbf{z}&lt;t}\right)=\frac{\exp \left(e(x)^{\top} h_{\theta}\left(\mathbf{x}_{\mathbf{z}&lt;t}\right)\right)}{\sum_{x^{\prime}} \exp \left(e\left(x^{\prime}\right)^{\top} h_{\theta}\left(\mathbf{x}_{\mathbf{z}&lt;t}\right)\right)} $$</code></p>
<p>其中，<code>$h_{\theta}\left(\mathbf{x}_{\mathbf{z}&lt;t}\right)$</code> 表示通过共享的 Transformer 产生的 <code>$\mathbf{X}_{\mathbf{Z}&lt;t}$</code> 的隐含表示。该表示并不依赖于所预测的位置，为了避免这个问题，我们将位置 <code>$z_t$</code> 加入到模型中：</p>
<p><code>$$ p_{\theta}\left(X_{z_{t}}=x | \mathbf{x}_{z_{&lt;t}}\right)=\frac{\exp \left(e(x)^{\top} g_{\theta}\left(\mathbf{x}_{\mathbf{z}&lt;t}, z_{t}\right)\right)}{\sum_{x^{\prime}} \exp \left(e\left(x^{\prime}\right)^{\top} g_{\theta}\left(\mathbf{x}_{\mathbf{z}&lt;t}, z_{t}\right)\right)} $$</code></p>
<p>对于 <code>$g_{\theta}\left(\mathbf{x}_{\mathbf{z}&lt;t}, z_{t}\right)$</code> 进行建模需要满足如下两个要求：</p>
<ol>
<li>预测 <code>$x_{z_t}$</code> 时，<code>$g_{\theta}\left(\mathbf{x}_{\mathbf{z}&lt;t}, z_{t}\right)$</code> 只能使用位置信息 <code>$z_t$</code> 而不能使用内容信息 <code>$x_{z_t}$</code>。</li>
<li>在预测 <code>$x_{z_t}$</code> 之后的词条时，<code>$g_{\theta}\left(\mathbf{x}_{\mathbf{z}&lt;t}, z_{t}\right)$</code> 又必须包含 <code>$x_{z_t}$</code> 的语义信息。</li>
</ol>
<p>为了解决这个问题，XLNet 提供了两种隐含表示：</p>
<ol>
<li>内容隐含表示 <code>$h_{\theta}\left(\mathbf{x}_{\mathbf{z} \leq t}\right)$</code>，简写为 <code>$h_{z_t}$</code>，它和标准的 Transformer 一样，既编码上下文也编码 <code>$x_{z_t}$</code> 的内容。</li>
<li>查询隐含表示 <code>$g_{\theta}\left(\mathbf{x}_{\mathbf{z}&lt;t}, z_{t}\right)$</code>，简写为 <code>$g_{z_t}$</code>，它仅编码上下文信息 <code>$\mathbf{X}_{\mathbf{Z}&lt;t}$</code> 和位置信息 <code>$z_t$</code>，不编码内容 <code>$x_{z_t}$</code>。</li>
</ol>
<p>模型的整个计算过程如下图所示：</p>
<figure>
  <img data-src="/images/cn/2020-03-28-pre-trained-model-for-nlp/xlnet.png" class="lazyload"/>
  
</figure>
<p>虽然排列语言模型有很多优点，但是由于计算量很大，模型很难进行优化，因此我们通过仅预测一个句子后面的一些词条解决这个问题。将 <code>$\mathbf{z}$</code> 分为两部分：非目标子序列 <code>$\mathbf{z}_{\leq c}$</code> 和目标子序列 <code>$\mathbf{z}_{&gt;c}$</code>，其中 <code>$c$</code> 为切分点。同时会设置一个超参数 <code>$K$</code>，表示仅 <code>$1 / K$</code> 的词条会被预测，有 <code>$|\mathbf{z}| /(|\mathbf{z}|-c) \approx K$</code>。对于未被选择的词条，其查询隐状态无需被计算，从而节省计算时间和资源。</p>
<h3 id="mass-2019">MASS (2019) <sup id="fnref1:16"><a href="#fn:16" class="footnote-ref" role="doc-noteref">16</a></sup></h3>
<p>MASS 是一个专门针对序列到序列的自然语言任务设计的预训练方法，对于一个给定的原始句子 <code>$x \in \mathcal{X}$</code>，令 <code>$x^{\setminus u:v}$</code> 表示将 <code>$x$</code> 从 <code>$u$</code> 到 <code>$v$</code> 位置进行遮挡处理，<code>$k = v - u + 1$</code> 为被遮挡词条的个数，<code>$x^{u:v}$</code> 为从 <code>$u$</code> 到 <code>$v$</code> 位置被遮挡的部分。MASS 利用被遮挡的序列 <code>$x^{\setminus u:v}$</code> 预测被遮挡的部分 <code>$x^{u:v}$</code>，目标函数的对数似然如下：</p>
<p><code>$$ \begin{aligned} L(\theta ; \mathcal{X}) &amp;=\frac{1}{|\mathcal{X}|} \Sigma_{x \in \mathcal{X}} \log P\left(x^{u: v} | x^{\setminus u: v} ; \theta\right) \\ &amp;=\frac{1}{|\mathcal{X}|} \Sigma_{x \in \mathcal{X}} \log \prod_{t=u}^{v} P\left(x_{t}^{u: v} | x_{&lt;t}^{u: v}, x^{\setminus u: v} ; \theta\right) \end{aligned} $$</code></p>
<p>对于一个具有 8 个词条的序列，<code>$x_3 x_4 x_5 x_6$</code> 被遮挡的示例如下：</p>
<figure>
  <img data-src="/images/cn/2020-03-28-pre-trained-model-for-nlp/mass.png" class="lazyload"/>
  
</figure>
<p>模型仅预测遮挡的部分 <code>$x_3 x_4 x_5 x_6$</code>，对于解码器中位置 <code>$4-6$</code> 利用 <code>$x_3 x_4 x_5$</code> 作为输入，利用特殊遮挡符号 <code>$\left[\mathbb{M}\right]$</code> 作为其他位置的输入。对于不同长度 <code>$k$</code>，MASS 包含了上文中提到的两种预训练模型：</p>
<table>
  <thead>
      <tr>
          <th>长度</th>
          <th>概率</th>
          <th>模型</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>$k=1$</code></td>
          <td><code>$P\left(x^{u} \mid x^{\setminus u} ; \theta\right)$</code></td>
          <td>masked LM in BERT</td>
      </tr>
      <tr>
          <td><code>$k=m$</code></td>
          <td><code>$P\left(x^{1:m} \mid x^{\setminus 1:m} ; \theta\right)$</code></td>
          <td>masked LM in GPT</td>
      </tr>
      <tr>
          <td><code>$k \in \left(1, m\right)$</code></td>
          <td><code>$P\left(x^{u:v} \mid x^{\setminus u:v} ; \theta\right)$</code></td>
          <td>两种之间</td>
      </tr>
  </tbody>
</table>
<p>对于不同 <code>$k$</code> 值，实验发现当 <code>$k$</code> 处于 <code>$m$</code> 的 <code>$50\%$</code> 至 <code>$70\%$</code> 之间时下游任务性能最优。</p>
<figure>
  <img data-src="/images/cn/2020-03-28-pre-trained-model-for-nlp/mass-k.png" class="lazyload"/>
  
</figure>
<p>当 <code>$k = 0.5 m$</code> 时，MASS 可以很好地平衡编码器和解码器的预训练。过度地偏向编码器（<code>$k=1$</code>，masked LM in BERT）和过度地偏向解码器（<code>$k=m$</code>，masked LM in GPT）均不能在下游的自然语言生成任务中取得很好的效果。</p>
<h3 id="roberta-2019">RoBERTa (2019) <sup id="fnref1:18"><a href="#fn:18" class="footnote-ref" role="doc-noteref">18</a></sup></h3>
<p>RoBERTa 主要围绕 BERT 进行了如下改进：</p>
<ol>
<li>模型采用了动态遮罩，不同于原始 BERT 中对语料预先进行遮罩处理，RoBERTa 在 40 轮训练过程中采用了 10 种不同的遮罩。</li>
<li>模型去掉了 NSP 任务，发现可以略微提升下游任务的性能。</li>
<li>模型采用了更大的训练数据和更大的 Batch 大小。</li>
<li>原始 BERT 采用一个 30K 的 BPE 词表，RoBERTa 采用了一个更大的 50K 的词表 <sup id="fnref:28"><a href="#fn:28" class="footnote-ref" role="doc-noteref">28</a></sup>。</li>
</ol>
<h3 id="bart-2019">BART (2019) <sup id="fnref1:20"><a href="#fn:20" class="footnote-ref" role="doc-noteref">20</a></sup></h3>
<p>BART 采用了一个标准的 Seq2Seq Transformer 结构，类似 GPT 将 ReLU 激活函数替换为 GeLUs。对于基线模型，采用了一个 6 层的编码和解码器，对于更大模型采用了 12 层的结构。相比于 BERT 的架构主要有以下两点不同：</p>
<ol>
<li>解码器的每一层叠加了对编码器最后一个隐含层的注意力。</li>
<li>BERT 在预测之前采用了一个前馈的网络，而 BART 没有。</li>
</ol>
<p>BART 采用了最小化破坏后的文档和原始文档之间的重构误差的方式进行预训练。不同于其他的一些去噪自编码器，BART 可以使用任意类型的文档破坏方式。极端情况下，当源文档的所有信息均丢失时，BART 就等价与一个语言模型。BART 中采用的文本破坏方式有：字符遮罩，字符删除，文本填充，句子重排，文档旋转，如下图所示：</p>
<figure>
  <img data-src="/images/cn/2020-03-28-pre-trained-model-for-nlp/bart-transformations.png" class="lazyload"/>
  
</figure>
<h3 id="t5-2019">T5 (2019) <sup id="fnref1:17"><a href="#fn:17" class="footnote-ref" role="doc-noteref">17</a></sup></h3>
<p>T5（Text-to-Text Transfer Transformer） 提出了一种 text-to-text 的框架，旨在利用相同的模型，损失函数和超参数等对机器翻译，文档摘要，问答和分类（例如：情感分析）等任务进行统一建模。我们甚至可以利用 T5 通过预测一个数字的文本表示而不是数字本身来建模一个回归任务。模型及其输入输出如下图所示：</p>
<figure>
  <img data-src="/images/cn/2020-03-28-pre-trained-model-for-nlp/t5-text-to-text-framework.gif" class="lazyload"/>
  
</figure>
<p>Google 的这项研究并不是提出一种新的方法，而是从全面的视角来概述当前 NLP 领域迁移学习的发展现状。T5 还公开了一个名为 C4（Colossal Clean Crawled Corpus）的数据集，该数据集是一个比 Wikipedia 大两个数量级的 Common Crawl 的清洗后版本的数据。更多模型的细节请参见源论文和 Google 的 <a href="https://ai.googleblog.com/2020/02/exploring-transfer-learning-with-t5.html">官方博客</a>。</p>
<h3 id="ernie-baidu-2019">ERNIE (Baidu, 2019) <sup id="fnref:29"><a href="#fn:29" class="footnote-ref" role="doc-noteref">29</a></sup> <sup id="fnref:30"><a href="#fn:30" class="footnote-ref" role="doc-noteref">30</a></sup></h3>
<p>ERNIE 1.0 <sup id="fnref1:29"><a href="#fn:29" class="footnote-ref" role="doc-noteref">29</a></sup> 通过建模海量数据中的词、实体及实体关系，学习真实世界的语义知识。相较于 BERT 学习原始语言信号，ERNIE 直接对先验语义知识单元进行建模，增强了模型语义表示能力。例如：</p>
<p><code>BERT ：哈 [mask] 滨是 [mask] 龙江的省会，[mask] 际冰 [mask] 文化名城。</code><br>
<code>ERNIE：[mask] [mask] [mask] 是黑龙江的省会，国际 [mask] [mask] 文化名城。</code></p>
<p>在 BERT 模型中，我们通过『哈』与『滨』的局部共现，即可判断出『尔』字，模型没有学习与『哈尔滨』相关的任何知识。而 ERNIE 通过学习词与实体的表达，使模型能够建模出『哈尔滨』与『黑龙江』的关系，学到『哈尔滨』是 『黑龙江』的省会以及『哈尔滨』是个冰雪城市。</p>
<p>训练数据方面，除百科类、资讯类中文语料外，ERNIE 还引入了论坛对话类数据，利用 DLM（Dialogue Language Model）建模 Query-Response 对话结构，将对话 Pair 对作为输入，引入 Dialogue Embedding 标识对话的角色，利用 Dialogue Response Loss 学习对话的隐式关系，进一步提升模型的语义表示能力。</p>
<p>ERNIE 2.0 <sup id="fnref1:30"><a href="#fn:30" class="footnote-ref" role="doc-noteref">30</a></sup> 是基于持续学习的语义理解预训练框架，使用多任务学习增量式构建预训练任务。ERNIE 2.0 中，新构建的预训练任务类型可以无缝的加入训练框架，持续的进行语义理解学习。 通过新增的实体预测、句子因果关系判断、文章句子结构重建等语义任务，ERNIE 2.0 语义理解预训练模型从训练数据中获取了词法、句法、语义等多个维度的自然语言信息，极大地增强了通用语义表示能力。</p>
<figure>
  <img data-src="/images/cn/2020-03-28-pre-trained-model-for-nlp/ernie-2-framework.png" class="lazyload"/>
  
</figure>
<h3 id="state-of-art">State-of-Art</h3>
<p>NLP 任务的 State-of-Art 模型详见：</p>
<ul>
<li><a href="https://gluebenchmark.com/leaderboard">GLUE Leaderboard</a></li>
<li><a href="https://super.gluebenchmark.com/leaderboard">SuperGLUE Leaderboard</a></li>
<li><a href="https://rajpurkar.github.io/SQuAD-explorer/">SQuAD</a></li>
<li><a href="https://nlpprogress.com/">NLP-progress</a></li>
<li><a href="https://www.cluebenchmarks.com/">中文任务基准测评</a></li>
</ul>


<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Qiu, X., Sun, T., Xu, Y., Shao, Y., Dai, N., &amp; Huang, X. (2020). Pre-trained Models for Natural Language Processing: A Survey. <em>ArXiv:2003.08271 [Cs]</em>. <a href="http://arxiv.org/abs/2003.08271">http://arxiv.org/abs/2003.08271</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Mikolov, T., Sutskever, I., Chen, K., Corrado, G. S., &amp; Dean, J. (2013). Distributed representations of words and phrases and their compositionality. In <em>Advances in neural information processing systems</em> (pp. 3111-3119).&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>Pennington, J., Socher, R., &amp; Manning, C. D. (2014, October). Glove: Global vectors for word representation. In <em>Proceedings of the 2014 conference on empirical methods in natural language processing (EMNLP)</em> (pp. 1532-1543).&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>McCann, B., Bradbury, J., Xiong, C., &amp; Socher, R. (2017). Learned in translation: Contextualized word vectors. In <em>Advances in Neural Information Processing Systems</em> (pp. 6294-6305).&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p>Peters, M. E., Neumann, M., Iyyer, M., Gardner, M., Clark, C., Lee, K., &amp; Zettlemoyer, L. (2018). Deep contextualized word representations. <em>arXiv preprint arXiv:1802.05365.</em>&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6">
<p>Radford, A., Narasimhan, K., Salimans, T., &amp; Sutskever, I. (2018). Improving language understanding by generative pre-training. <em>URL <a href="https://openai.com/blog/language-unsupervised/">https://openai.com/blog/language-unsupervised/</a></em>.&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:7">
<p>Devlin, J., Chang, M. W., Lee, K., &amp; Toutanova, K. (2018). Bert: Pre-training of deep bidirectional transformers for language understanding. <em>arXiv preprint arXiv:1810.04805.</em>&#160;<a href="#fnref:7" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:7" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:8">
<p>Kim, Y. (2014). Convolutional Neural Networks for Sentence Classification. In <em>Proceedings of the 2014 Conference on Empirical Methods in Natural Language Processing (EMNLP)</em> (pp. 1746-1751).&#160;<a href="#fnref:8" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:9">
<p>Hochreiter, S., &amp; Schmidhuber, J. (1997). Long short-term memory. <em>Neural computation</em>, 9(8), 1735-1780.&#160;<a href="#fnref:9" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:10">
<p>Chung, J., Gulcehre, C., Cho, K., &amp; Bengio, Y. (2014). Empirical evaluation of gated recurrent neural networks on sequence modeling. <em>arXiv preprint arXiv:1412.3555.</em>&#160;<a href="#fnref:10" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:11">
<p>Socher, R., Perelygin, A., Wu, J., Chuang, J., Manning, C. D., Ng, A. Y., &amp; Potts, C. (2013). Recursive deep models for semantic compositionality over a sentiment treebank. In <em>Proceedings of the 2013 conference on empirical methods in natural language processing</em> (pp. 1631-1642).&#160;<a href="#fnref:11" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:12">
<p>Tai, K. S., Socher, R., &amp; Manning, C. D. (2015). Improved Semantic Representations From Tree-Structured Long Short-Term Memory Networks. In <em>Proceedings of the 53rd Annual Meeting of the Association for Computational Linguistics and the 7th International Joint Conference on Natural Language Processing (Volume 1: Long Papers)</em> (pp. 1556-1566).&#160;<a href="#fnref:12" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:13">
<p>Marcheggiani, D., Bastings, J., &amp; Titov, I. (2018). Exploiting Semantics in Neural Machine Translation with Graph Convolutional Networks. In <em>Proceedings of the 2018 Conference of the North American Chapter of the Association for Computational Linguistics: Human Language Technologies, Volume 2 (Short Papers)</em> (pp. 486-492).&#160;<a href="#fnref:13" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:14">
<p>Vaswani, A., Shazeer, N., Parmar, N., Uszkoreit, J., Jones, L., Gomez, A. N., &hellip; &amp; Polosukhin, I. (2017). Attention is all you need. In <em>Advances in neural information processing systems</em> (pp. 5998-6008).&#160;<a href="#fnref:14" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:14" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref2:14" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:15">
<p>Devlin, J., Chang, M. W., Lee, K., &amp; Toutanova, K. (2019). BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding. In <em>Proceedings of the 2019 Conference of the North American Chapter of the Association for Computational Linguistics: Human Language Technologies, Volume 1 (Long and Short Papers)</em> (pp. 4171-4186).&#160;<a href="#fnref:15" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:16">
<p>Song, K., Tan, X., Qin, T., Lu, J., &amp; Liu, T. Y. (2019). MASS: Masked Sequence to Sequence Pre-training for Language Generation. In <em>International Conference on Machine Learning</em> (pp. 5926-5936).&#160;<a href="#fnref:16" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:16" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:17">
<p>Raffel, C., Shazeer, N., Roberts, A., Lee, K., Narang, S., Matena, M., &hellip; &amp; Liu, P. J. (2019). Exploring the limits of transfer learning with a unified text-to-text transformer. <em>arXiv preprint arXiv:1910.1068</em>&#160;<a href="#fnref:17" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:17" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:18">
<p>Liu, Y., Ott, M., Goyal, N., Du, J., Joshi, M., Chen, D., &hellip; &amp; Stoyanov, V. (2019). Roberta: A robustly optimized bert pretraining approach. <em>arXiv preprint arXiv:1907.11692.</em>&#160;<a href="#fnref:18" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:18" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:19">
<p>Yang, Z., Dai, Z., Yang, Y., Carbonell, J., Salakhutdinov, R. R., &amp; Le, Q. V. (2019). Xlnet: Generalized autoregressive pretraining for language understanding. In <em>Advances in neural information processing systems</em> (pp. 5754-5764).&#160;<a href="#fnref:19" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:19" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:20">
<p>Lewis, M., Liu, Y., Goyal, N., Ghazvininejad, M., Mohamed, A., Levy, O., &hellip; &amp; Zettlemoyer, L. (2019). Bart: Denoising sequence-to-sequence pre-training for natural language generation, translation, and comprehension. <em>arXiv preprint arXiv:1910.13461.</em>&#160;<a href="#fnref:20" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:20" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:21">
<p>Saunshi, N., Plevrakis, O., Arora, S., Khodak, M., &amp; Khandeparkar, H. (2019). A Theoretical Analysis of Contrastive Unsupervised Representation Learning. In <em>International Conference on Machine Learning</em> (pp. 5628-5637).&#160;<a href="#fnref:21" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:22">
<p>Tenney, I., Das, D., &amp; Pavlick, E. (2019). BERT Rediscovers the Classical NLP Pipeline. In <em>Proceedings of the 57th Annual Meeting of the Association for Computational Linguistics</em> (pp. 4593-4601).&#160;<a href="#fnref:22" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:23">
<p>图片来源：http://www.realworldnlpbook.com/blog/improving-sentiment-analyzer-using-elmo.html&#160;<a href="#fnref:23" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:24">
<p>Wu, Y., Schuster, M., Chen, Z., Le, Q. V., Norouzi, M., Macherey, W., &hellip; &amp; Klingner, J. (2016). Google&rsquo;s neural machine translation system: Bridging the gap between human and machine translation. <em>arXiv preprint arXiv:1609.08144.</em>&#160;<a href="#fnref:24" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:25">
<p>Dong, L., Yang, N., Wang, W., Wei, F., Liu, X., Wang, Y., &hellip; &amp; Hon, H. W. (2019). Unified language model pre-training for natural language understanding and generation. In <em>Advances in Neural Information Processing Systems</em> (pp. 13042-13054).&#160;<a href="#fnref:25" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:26">
<p>Dai, Z., Yang, Z., Yang, Y., Carbonell, J. G., Le, Q., &amp; Salakhutdinov, R. (2019, July). Transformer-XL: Attentive Language Models beyond a Fixed-Length Context. In <em>Proceedings of the 57th Annual Meeting of the Association for Computational Linguistics</em> (pp. 2978-2988).&#160;<a href="#fnref:26" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:27">
<p>Al-Rfou, R., Choe, D., Constant, N., Guo, M., &amp; Jones, L. (2019). Character-level language modeling with deeper self-attention. In <em>Proceedings of the AAAI Conference on Artificial Intelligence</em> (Vol. 33, pp. 3159-3166).&#160;<a href="#fnref:27" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:28">
<p>Radford, A., Wu, J., Child, R., Luan, D., Amodei, D., &amp; Sutskever, I. (2019). Language models are unsupervised multitask learners. <em>URL <a href="https://openai.com/blog/better-language-models/">https://openai.com/blog/better-language-models/</a></em>.&#160;<a href="#fnref:28" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:29">
<p>Sun, Y., Wang, S., Li, Y., Feng, S., Chen, X., Zhang, H., &hellip; &amp; Wu, H. (2019). Ernie: Enhanced representation through knowledge integration. <em>arXiv preprint arXiv:1904.09223.</em>&#160;<a href="#fnref:29" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:29" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:30">
<p>Sun, Y., Wang, S., Li, Y., Feng, S., Tian, H., Wu, H., &amp; Wang, H. (2019). Ernie 2.0: A continual pre-training framework for language understanding. <em>arXiv preprint arXiv:1907.12412.</em>&#160;<a href="#fnref:30" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:30" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>

        ]]></description></item><item><title>序列到序列 (Seq2Seq) 和注意力机制 (Attention Machanism)</title><link>https://zeqiang.fun/cn/2018/10/seq2seq-and-attention-machanism/</link><pubDate>Fri, 12 Oct 2018 00:00:00 +0000</pubDate><guid>https://zeqiang.fun/cn/2018/10/seq2seq-and-attention-machanism/</guid><description><![CDATA[
        <h2 id="encoder-decoder-seq2seq">Encoder-Decoder &amp; Seq2Seq</h2>
<p>Encoder-Decoder 是一种包含两个神经网络的模型，两个网络分别扮演编码器和解码器的角色。Cho 等人 <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> 提出了一个基于 RNN 的 Encoder-Decoder 神经网络用于机器翻译。网络结构如下图所示：</p>
<p><img src="/images/cn/2018-10-12-seq2seq-and-attention-machanism/rnn-encoder-decoder.png" alt="RNN-Encoder-Decoder"></p>
<p>整个模型包含编码器 (Encoder) 和解码器 (Decoder) 两部分：Encoder 将一个可变长度的序列转换成为一个固定长度的向量表示，Decoder 再将这个固定长度的向量表示转换为一个可变长度的序列。这使得模型可以处理从一个可变长度序列到另一个可变长度序例的转换，即学习到对应的条件概率 <code>$p \left(y_1, \dotsc, y_{T'} | x_1, \dotsc, x_T\right)$</code>，其中 <code>$T$</code> 和 <code>$T'$</code> 可以为不同的值，也就是说输入和输出的序列的长度不一定相同。</p>
<p>在模型中，Encoder 为一个 RNN，逐次读入输入序列 <code>$\mathbf{x}$</code> 中的每个元素，其中 RNN 隐状态的更新方式如下：</p>
<p><code>$$ \mathbf{h}_{\langle t \rangle} = f \left(\mathbf{h}_{\langle t-1 \rangle}, x_t\right) $$</code></p>
<p>在读入序列的最后一个元素后 (通常为一个结束标记)，RNN 的隐状态则为整个输入序列的概括信息 <code>$\mathbf{c}$</code>。Decoder 为另一个 RNN，用于根据隐状态 <code>$\mathbf{h}'_{\langle t \rangle}$</code> 预测下一个元素 <code>$y_t$</code>，从而生成整个输出序列。不同于 Encoder 中的 RNN，Decoder 中 RNN 的隐状态 <code>$\mathbf{h}'_{\langle t \rangle}$</code> 除了依赖上一个隐含层的状态和之前的输出外，还依赖整个输入序列的概括信息 <code>$\mathbf{c}$</code>，即：</p>
<p><code>$$ \mathbf{h}'_{\langle t \rangle} = f \left(\mathbf{h}'_{\langle t-1 \rangle}, y_{t-1}, \mathbf{c}\right) $$</code></p>
<p>类似的，下一个输出元素的条件分布为：</p>
<p><code>$$ P \left(y_t | y_{t-1}, y_{t-2}, \dotsc, y_1, \mathbf{c}\right) = g \left(\mathbf{h}_{\langle t \rangle}, y_{t-1}, \mathbf{c}\right) $$</code></p>
<p>RNN Encoder-Decoder 的两部分通过最大化如下的对数似然函数的联合训练进行优化：</p>
<p><code>$$ \max_{\theta} \dfrac{1}{N} \sum_{n=1}^{N}{\log p_{\theta} \left(\mathbf{y}_n | \mathbf{x}_n\right)} $$</code></p>
<p>其中，<code>$\theta$</code> 为模型的参数，<code>$\mathbf{x}_n$</code> 和 <code>$\mathbf{y}_n$</code> 分别为输入和输出序列的成对样本。当模型训练完毕后，我们可以利用模型根据给定的输入序列生成相应的输出序列，或是根据给定的输入和输出序列对计算概率得分 <code>$p_{\theta} \left(\mathbf{y} | \mathbf{x}\right)$</code>。同时，作者还提出了一种新的 RNN 单元 GRU (Gated Recurrent Unit)，有关 GRU 的更多介绍请参见 <a href="/cn/2018/09/rnn">之前的博客</a>。</p>
<p>序列到序列 (Sequence to Sequence, Seq2Seq) 模型从名称中不难看出来是一种用于处理序列数据到序列数据转换问题 (例如：机器翻译等) 的方法。Sutskever 等人 <sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> 提出了一种基于 Encoder-Decoder 网络结构的 Seq2Seq 模型用于机器翻译，网络结构细节同 RNN Encoder-Decoder 略有不同，如下图所示：</p>
<p><img src="/images/cn/2018-10-12-seq2seq-and-attention-machanism/seq2seq.png" alt="Seq2Seq"></p>
<p>模型的相关细节如下：</p>
<ol>
<li>对数据进行预处理，在每个句子的结尾添加特殊字符 <code>&lt;EOS&gt;</code>，如上图所示。首先计算 <code>A, B, C, &lt;EOS&gt;</code> 的表示，再利用该表示计算 <code>W, X, Y, Z, &lt;EOS&gt;</code> 的条件概率。</li>
<li>利用两个不同的 LSTM，一个用于输入序列，另一个用于输出序列。</li>
<li>选用一个较深的 LSTM 模型 (4 层) 提升模型效果。</li>
<li>对输入序列进行倒置处理，例如对于输入序列 <code>$a, b, c$</code> 和对应的输出序列 <code>$\alpha, \beta, \gamma$</code>，LSTM 需要学习的映射关系为 <code>$c, b, a \to \alpha, \beta, \gamma$</code>。</li>
</ol>
<p>在模型的解码阶段，模型采用简单的从左到右的 Beam Search，该方法维护一个大小为 <code>$B$</code> 的集合保存最好的结果。下图展示了 <code>$B = 2$</code> 情况下 Beam Search 的具体工作方式：</p>
<p><img src="/images/cn/2018-10-12-seq2seq-and-attention-machanism/beam-search.png" alt="Beam-Search"></p>
<p>其中，红色的虚线箭头表示每一步可能的搜索方向，绿色的实线箭头表示每一步概率为 Top <code>$B$</code> 的方向。例如，从 S 开始搜索：</p>
<ol>
<li>第一步搜索的可能结果为 SA 和 SB，保留 Top 2，结果为 SA 和 SB。</li>
<li>第二步搜索的可能结果为 SAC，SAD，SBE 和 SBF，保留 Top 2，结果为 SAC 和 SBE。</li>
<li>第三步搜索的可能结果为 SACG，SACH，SBEK 和 SBEL，保留 Top 2，结果为 SACH 和 SBEK。至此，整个搜索结束。</li>
</ol>
<p>Bahdanau 等人 <sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> 提出了一种基于双向 RNN (Bidirectional RNN, BiRNN) 结合注意力机制 (Attention Mechanism) 的网络结构用于机器翻译。网络结构如下：</p>
<p><img src="/images/cn/2018-10-12-seq2seq-and-attention-machanism/seq2seq-birnn-attention.png" alt="Seq2Seq-BiRNN-Attention"></p>
<p>模型的编码器使用了一个双向的 RNN，前向的 RNN <code>$\overrightarrow{f}$</code> 以从 <code>$x_1$</code> 到 <code>$x_T$</code> 的顺序读取输入序列并计算前向隐状态 <code>$\left(\overrightarrow{h}_1, \dotsc, \overrightarrow{h}_T\right)$</code>，后向的 RNN <code>$\overleftarrow{f}$</code> 以从 <code>$x_T$</code> 到 <code>$x_1$</code> 的顺序读取输入序列并计算后向隐状态 <code>$\left(\overleftarrow{h}_1, \dotsc, \overleftarrow{h}_T\right)$</code>。对于一个词 <code>$x_j$</code>，通过将对应的前向隐状态 <code>$\overrightarrow{h}_j$</code> 和后向隐状态 <code>$\overleftarrow{h}_j$</code> 进行拼接得到最终的隐状态 <code>$h_j = \left[\overrightarrow{h}_j^{\top}; \overleftarrow{h}_j^{\top}\right]^{\top}$</code>。这样的操作使得隐状态 <code>$h_j$</code> 既包含了前面词的信息也包含了后面词的信息。</p>
<p>在模型的解码器中，对于一个给定的序例 <code>$\mathbf{x}$</code>，每一个输出的条件概率为：</p>
<p><code>$$ p \left(y_i | y_1, \dotsc, y_{i-1}, \mathbf{x}\right) = g \left(y_{i-1}, s_i, c_i\right) $$</code></p>
<p>其中，<code>$s_i$</code> 为 <code>$i$</code> 时刻 RNN 隐含层的状态，即：</p>
<p><code>$$ s_i = f \left(s_{i-1}, y_{i-1}, c_i\right) $$</code></p>
<p>这里需要注意的是不同于之前的 Encoder-Decoder 模型，此处每一个输出词 <code>$y_i$</code> 的条件概率均依赖于一个单独的上下文向量 <code>$c_i$</code>。该部分的改进即结合了注意力机制，有关注意力机制的详细内容将在下个小节中展开说明。</p>
<h2 id="注意力机制-attention-mechanism">注意力机制 (Attention Mechanism)</h2>
<p>Bahdanau 等人在文中 <sup id="fnref1:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> 提出传统的 Encoder-Decoder 模型将输入序列压缩成一个固定长度的向量 <code>$c$</code>，但当输入的序例很长时，尤其是当比训练集中的语料还长时，模型的的效果会显著下降。针对这个问题，如上文所述，上下文向量 <code>$c_i$</code> 依赖于 <code>$\left(h_1, \dotsc, h_T\right)$</code>。其中，每个 <code>$h_i$</code> 都包含了整个序列的信息，同时又会更多地关注第 <code>$i$</code> 个词附近的信息。对于 <code>$c_i$</code>，计算方式如下：</p>
<p><code>$$ c_i = \sum_{j=1}^{T}{\alpha_{ij} h_j} $$</code></p>
<p>对于每个 <code>$h_j$</code> 的权重 <code>$\alpha_{ij}$</code>，计算方式如下：</p>
<p><code>$$ \alpha_{ij} = \dfrac{\exp \left(e_{ij}\right)}{\sum_{k=1}^{T}{\exp \left(e_{ik}\right)}} $$</code></p>
<p>其中，<code>$e_{ij} = a \left(s_{i-1}, h_j\right)$</code> 为一个 Alignment 模型，用于评价对于输入的位置 <code>$j$</code> 附近的信息与输出的位置 <code>$i$</code> 附近的信息的匹配程度。Alignment 模型 <code>$a$</code> 为一个用于评分的前馈神经网络，与整个模型进行联合训练，计算方式如下：</p>
<p><code>$$ a \left(s_{i-1}, h_j\right) = v_a^{\top} \tanh \left(W_a s_{i-1} + U_a h_j\right) $$</code></p>
<p>其中，<code>$W_a \in \mathbb{R}^{n \times n}, U_a \in \mathbb{R}^{n \times 2n}，v_a \in \mathbb{R}^n$</code> 为网络的参数。</p>
<h3 id="hard-soft-attention">Hard &amp; Soft Attention</h3>
<p>Xu 等人 <sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> 在图像标题生成 (Image Caption Generation) 任务中引入了注意力机制。在文中作者提出了 Hard Attenttion 和 Soft Attention 两种不同的注意力机制。</p>
<p>对于 Hard Attention 而言，令 <code>$s_t$</code> 表示在生成第 <code>$t$</code> 个词时所关注的位置变量，<code>$s_{t, i} = 1$</code> 表示当第 <code>$i$</code> 个位置用于提取视觉特征。将注意力位置视为一个中间潜变量，可以以一个参数为 <code>$\left\{\alpha_i\right\}$</code> 的多项式分布表示，同时将上下文向量 <code>$\hat{\mathbf{z}}_t$</code> 视为一个随机变量：</p>
<p><code>$$ \begin{equation} \begin{split} &amp; p \left(s_{t, i} = 1 | s_{j &lt; t}, \mathbf{a}\right) = \alpha_{t, i} \\ &amp; \hat{\mathbf{z}}_t = \sum_{i}{s_{t, i} \mathbf{a}_i} \end{split} \end{equation} $$</code></p>
<p>因此 Hard Attention 可以依据概率值从隐状态中进行采样计算得到上下文向量，同时为了实现梯度的反向传播，需要利用蒙特卡罗采样的方法来估计梯度。</p>
<p>对于 Soft Attention 而言，则直接计算上下文向量 <code>$\hat{\mathbf{z}}_t$</code> 的期望，计算方式如下：</p>
<p><code>$$ \mathbb{E}_{p \left(s_t | a\right)} \left[\hat{\mathbf{z}}_t\right] = \sum_{i=1}^{L}{\alpha_{t, i} \mathbf{a}_i} $$</code></p>
<p>其余部分的计算方式同 Bahdanau 等人 <sup id="fnref2:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> 的论文类似。Soft Attention 模型可以利用标准的反向传播算法进行求解，直接嵌入到整个模型中一同训练，相对更加简单。</p>
<p>下图展示了一些图片标题生成结果的可视化示例，其中图片内 <span style="background-color:#000; color:#FFF; font-style:bold;">白色</span> 为关注的区域，<span style="border-bottom:2px solid;">画线的文本</span> 即为生成的标题中对应的词。</p>
<p><img src="/images/cn/2018-10-12-seq2seq-and-attention-machanism/image-caption-generation-visual-attention.png" alt="Image-Caption-Generation-Visual-Attention"></p>
<h3 id="global-local-attention">Global &amp; Local Attention</h3>
<p>Luong 等人 <sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup> 提出了 Global Attention 和 Local Attention 两种不同的注意力机制用于机器翻译。Global Attention 的思想是在计算上下文向量 <code>$c_t$</code> 时将编码器的所有隐状态均考虑在内。对于对齐向量 <code>$\boldsymbol{a}_t$</code>，通过比较当前目标的隐状态 <code>$\boldsymbol{h}_t$</code> 与每一个输入的隐状态 <code>$\bar{\boldsymbol{h}}_s$</code> 得到，即：</p>
<p><code>$$ \begin{equation} \begin{split} \boldsymbol{a}_t &amp;= \text{align} \left(\boldsymbol{h}_t, \bar{\boldsymbol{h}}_s\right) \\ &amp;= \dfrac{\exp \left(\text{score} \left(\boldsymbol{h}_t, \bar{\boldsymbol{h}}_s\right)\right)}{\sum_{s'}{\exp \left(\text{score} \left(\boldsymbol{h}_t, \bar{\boldsymbol{h}}_{s'}\right)\right)}} \end{split} \end{equation} $$</code></p>
<p>其中 <code>$\text{score}$</code> 为一个基于内容 (content-based) 的函数，可选的考虑如下三种形式：</p>
<p><code>$$ \text{score} \left(\boldsymbol{h}_t, \bar{\boldsymbol{h}}_s\right) = \begin{cases} \boldsymbol{h}_t^{\top} \bar{\boldsymbol{h}}_s &amp; dot \\ \boldsymbol{h}_t^{\top} \boldsymbol{W}_a \bar{\boldsymbol{h}}_s &amp; general \\ \boldsymbol{W}_a \left[\boldsymbol{h}_t; \bar{\boldsymbol{h}}_s\right] &amp; concat \end{cases} $$</code></p>
<p>我们利用一个基于位置 (location-based) 的函数构建注意力模型，其中对齐分数通过目标的隐状态计算得到：</p>
<p><code>$$ \boldsymbol{a}_t = \text{softmax} \left(\boldsymbol{W}_a \boldsymbol{h}_t\right) $$</code></p>
<p>Global Attention 模型的网络结构如下所示：</p>
<p><img src="/images/cn/2018-10-12-seq2seq-and-attention-machanism/global-attention.png" alt="Global-Attention"></p>
<p>Global Attention 的一个问题在于任意一个输出都需要考虑输入端的所有隐状态，这对于很长的文本 (例如：一个段落或一篇文章) 计算量太大。Local Attention 为了解决这个问题，首先在 <code>$t$</code> 时刻对于每个目标词生成一个对齐位置 <code>$p_t$</code>，其次上下文向量 <code>$\boldsymbol{c}_t$</code> 则由以 <code>$p_t$</code> 为中心前后各 <code>$D$</code> 大小的窗口 <code>$\left[p_t - D, p_t + D\right]$</code> 内的输入的隐状态计算得到。不同于 Global Attention，Local Attention 的对齐向量 <code>$\boldsymbol{a}_t \in \mathbb{R}^{2D + 1}$</code> 为固定维度。</p>
<p>一个比较简单的做法是令 <code>$p_t = t$</code>，也就是假设输入和输出序列是差不多是单调对齐的，我们称这种做法为 <em>Monotonic</em> Alignment (<strong>local-m</strong>)。另一种做法是预测 <code>$p_t$</code>，即：</p>
<p><code>$$ p_t = S \cdot \text{sigmoid} \left(\boldsymbol{v}_p^{\top} \tanh \left(\boldsymbol{W}_p \boldsymbol{h}_t\right)\right) $$</code></p>
<p>其中，<code>$\boldsymbol{W}_p$</code> 和 <code>$\boldsymbol{h}_t$</code> 为预测位置模型的参数，<code>$S$</code> 为输入句子的长度。我们称这种做法为 <em>Predictive</em> Alignment (<strong>local-p</strong>)。作为 <code>$\text{sigmoid}$</code> 函数的结果，<code>$p_t \in \left[0, S\right]$</code>，则通过一个以 <code>$p_t$</code> 为中心的高斯分布定义对齐权重：</p>
<p><code>$$ \boldsymbol{a}_t \left(s\right) = \text{align} \left(\boldsymbol{h}_t, \bar{\boldsymbol{h}}_s\right) \exp \left(- \dfrac{\left(s - p_t\right)^2}{2 \sigma^2}\right) $$</code></p>
<p>其中，根据经验设置 <code>$\sigma = \dfrac{D}{2}$</code>，<code>$s$</code> 为在窗口大小内的一个整数。</p>
<p>Local Attention 模型的网络结构如下所示：</p>
<p><img src="/images/cn/2018-10-12-seq2seq-and-attention-machanism/local-attention.png" alt="Local-Attention"></p>
<h3 id="self-attention">Self Attention</h3>
<p>Vaswani 等人 <sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup> 提出了一种新的网络结构，称之为 Transformer，其中采用了自注意力 (Self-attention) 机制。自注意力是一种将同一个序列的不同位置进行自我关联从而计算一个句子表示的机制。Transformer 利用堆叠的 Self Attention 和全链接网络构建编码器 (下图左) 和解码器 (下图右)，整个网络架构如下图所示：</p>
<p><img src="/images/cn/2018-10-12-seq2seq-and-attention-machanism/self-attention.png" alt="Self-Attention"></p>
<h4 id="编码器和解码器">编码器和解码器</h4>
<p><strong>编码器</strong> 是由 <code>$N = 6$</code> 个相同的网络层构成，每层中包含两个子层。第一层为一个 Multi-Head Self-Attention 层，第二层为一个 Position-Wise 全链接的前馈神经网络。每一层再应用一个残差连接 (Residual Connection) <sup id="fnref:7"><a href="#fn:7" class="footnote-ref" role="doc-noteref">7</a></sup> 和一个层标准化 (Layer Normalization) <sup id="fnref:8"><a href="#fn:8" class="footnote-ref" role="doc-noteref">8</a></sup>。则每一层的输出为 <code>$\text{LayerNorm} \left(x + \text{Sublayer} \left(x\right)\right)$</code>，其中 <code>$\text{Sublayer} \left(x\right)$</code> 为子层本身的函数实现。为了实现残差连接，模型中所有的子层包括 Embedding 层的输出维度均为 <code>$d_{\text{model}} = 512$</code>。</p>
<p><strong>解码器</strong> 也是由 <code>$N = 6$</code> 个相同的网络层构成，但每层中包含三个子层，增加的第三层用于处理编码器的输出。同编码器一样，每一层应用一个残差连接和一个层标准化。除此之外，解码器对 Self-Attention 层进行了修改，确保对于位置 <code>$i$</code> 的预测仅依赖于位置在 <code>$i$</code> 之前的输出。</p>
<h4 id="scaled-dot-product-multi-head-attention">Scaled Dot-Product &amp; Multi-Head Attention</h4>
<p>一个 Attention 函数可以理解为从一个序列 (Query) 和一个键值对集合 (Key-Value Pairs Set) 到一个输出的映射。文中提出了一种名为 <strong>Scaled Dot-Product Attention</strong> (如下图所示)，其中输入包括 queries，维度为 <code>$d_k$</code> 的 keys 和维度为 <code>$d_v$</code> 的 values。通过计算 queries 和所有 keys 的点积，除以 <code>$\sqrt{d_k}$</code>，再应用一个 softmax 函数获取 values 的权重。</p>
<p><img src="/images/cn/2018-10-12-seq2seq-and-attention-machanism/scaled-dot-product-attention.png" alt="Scaled-Dot-Product-Attention"></p>
<p>实际中，我们会同时计算一个 Queries 集合中的 Attention，并将其整合成一个矩阵 <code>$Q$</code>。Keys 和 Values 也相应的整合成矩阵 <code>$K$</code> 和 <code>$V$</code>，则有：</p>
<p><code>$$ \text{Attention} \left(Q, K, V\right) = \text{softmax} \left(\dfrac{Q K^{\top}}{\sqrt{d_k}}\right) V $$</code></p>
<p>其中，<code>$Q \in \mathbb{R}^{n \times d_k}$</code>，<code>$Q$</code> 中的每一行为一个 query，<code>$K \in \mathbb{R}^{n \times d_k}, V \in \mathbb{R}^{n \times d_v}$</code>。<code>$\dfrac{1}{\sqrt{d_k}}$</code> 为一个归一化因子，避免点积的值过大导致 softmax 之后的梯度过小。</p>
<p><strong>Multi-Head Attention</strong> 的做法并不直接对原始的 keys，values 和 queries 应用注意力函数，而是学习一个三者各自的映射再应用 Atteneion，同时将这个过程重复 <code>$h$</code> 次。Multi-Head Attention 的网路结构如下图所示：</p>
<p><img src="/images/cn/2018-10-12-seq2seq-and-attention-machanism/multi-head-attention.png" alt="Multi-Head-Attention"></p>
<p>Multi-Head Attention 的计算过程如下所示：</p>
<p><code>$$ \begin{equation} \begin{split} \text{MultiHead} \left(Q, K, V\right) &amp;= \text{Concat} \left(\text{head}_1, \dotsc, \text{head}_h\right) W^O \\ \textbf{where } \text{head}_i &amp;= \text{Attention} \left(QW_i^Q, KW_i^K, VW_i^V\right) \end{split} \end{equation} $$</code></p>
<p>其中，<code>$W_i^Q \in \mathbb{R}^{d_{\text{model}} \times d_k}, W_i^K \in \mathbb{R}^{d_{\text{model}} \times d_k}, W_i^V \in \mathbb{R}^{d_{\text{model}} \times d_v}, W_i^O \in \mathbb{R}^{h d_v \times d_{\text{model}}}, $</code> 为映射的参数，<code>$h = 8$</code> 为重复的次数，则有 <code>$d_k = d_v = d_{\text{model}} / h = 64$</code>。</p>
<p>整个 Transformer 模型在三处使用了 Multi-Head Attention，分别是：</p>
<ol>
<li>Encoder-Decoder Attention Layers，其中 queries 来自于之前的 Decoder 层，keys 和 values 来自于 Encoder 的输出，该部分同其他 Seq2Seq 模型的 Attention 机制类似。</li>
<li>Encoder Self-Attention Layers，其中 queries，keys 和 values 均来自之前的 Encoder 层的输出，同时 Encoder 层中的每个位置都能够从之前层的所有位置获取到信息。</li>
<li>Decoder Self-Attention Layers，其中 queries，keys 和 values 均来自之前的 Decoder 层的输出，但 Decoder 层中的每个位置仅可以从之前网络层的包含当前位置之前的位置获取信息。</li>
</ol>
<h4 id="position-wise-feed-forward-networks">Position-wise Feed-Forward Networks</h4>
<p>在 Encoder 和 Decoder 中的每一层均包含一个全链接的前馈神经网络，其使用两层线性变换和一个 ReLU 激活函数实现：</p>
<p><code>$$ \text{FFN} \left(x\right) = \max \left(0, x W_1 + b_1\right) W_2 + b_2 $$</code></p>
<p>全链接层的输入和输出的维度 <code>$d_{\text{model}} = 512$</code>，内层的维度 <code>$d_{ff} = 2048$</code>。</p>
<h4 id="positional-encoding">Positional Encoding</h4>
<p>Transformer 模型由于未使用任何循环和卷积组件，因此为了利用序列的位置信息则在模型的 Embedding 输入中添加了 <strong>Position Encoding</strong>。Position Encoding 的维度同 Embedding 的维度相同，从而可以与 Embedding 进行加和，文中使用了如下两种形式：</p>
<p><code>$$ \begin{equation} \begin{split} PE_{\left(pos, 2i\right)} &amp;= \sin \left(pos / 10000^{2i / d_{\text{model}}}\right) \\ PE_{\left(pos, 2i+1\right)} &amp;= \cos \left(pos / 10000^{2i / d_{\text{model}}}\right) \end{split} \end{equation} $$</code></p>
<p>其中，<code>$pos$</code> 为位置，<code>$i$</code> 为对应的维度，选用这种表示形式的原因是对于一个固定的偏移 <code>$k$</code>，<code>$PE_{pos + k}$</code> 都可以利用 <code>$PE_{pos}$</code> 线性表示。这是因为对于正弦和余弦函数有：</p>
<p><code>$$ \begin{equation} \begin{split} \sin \left(\alpha + \beta\right) &amp;= \sin \alpha \cos \beta + \cos \alpha \sin \beta \\ \cos \left(\alpha + \beta\right) &amp;= \cos \alpha \sin \beta - \sin \alpha \sin \beta \end{split} \end{equation} $$</code></p>
<h4 id="why-self-attention">Why Self-Attention</h4>
<p>相比于循环和卷积层，Transformer 模型利用 Self-Attention 层用于一个序列 <code>$\left(x_1, \dotsc, x_n\right)$</code> 到另一个等长序例 <code>$\left(z_1, \dotsc, z_n\right)$</code> 的映射，其中 <code>$x_i, z_i \in \mathbb{R}^d$</code>。Self-Attention 与循环和卷积的对比如下表所示：</p>
<table>
  <thead>
      <tr>
          <th>层类型</th>
          <th>每层的复杂度</th>
          <th>序列操作数</th>
          <th>长距离依赖路径长度</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Self-Attention</td>
          <td><code>$O \left(n^2 \cdot d\right)$</code></td>
          <td><code>$O \left(1\right)$</code></td>
          <td><code>$O \left(1\right)$</code></td>
      </tr>
      <tr>
          <td>Recurrent</td>
          <td><code>$O \left(n \cdot d^2\right)$</code></td>
          <td><code>$O \left(n\right)$</code></td>
          <td><code>$O \left(n\right)$</code></td>
      </tr>
      <tr>
          <td>Convolutional</td>
          <td><code>$O \left(k \cdot n \cdot d^2\right)$</code></td>
          <td><code>$O \left(1\right)$</code></td>
          <td><code>$O \left(\log_k \left(n\right)\right)$</code></td>
      </tr>
      <tr>
          <td>Self-Attention (restricted)</td>
          <td><code>$O \left(r \cdot n \cdot d\right)$</code></td>
          <td><code>$O \left(1\right)$</code></td>
          <td><code>$O \left(n/r\right)$</code></td>
      </tr>
  </tbody>
</table>
<ol>
<li>对于每层的复杂度，当序例的长度 <code>$n$</code> 比表示的维度 <code>$d$</code> 小时，Self-Attention 要比循环结构计算复杂度小。为了改进在长序列上 Self-Attention 的计算性能，Self-Attention 可以被限制成仅考虑与输出位置对应的输入序列位置附近 <code>$r$</code> 窗口大小内的信息。</li>
<li>Recurrent 层的最小序列操作数为 <code>$O \left(n\right)$</code>，其他情况为 <code>$O \left(1\right)$</code>，这使得 Recurrent 的并行能力较差，即上表中的 Self-Attention (restricted)。</li>
<li>学习到长距离依赖是很多序列任务的关键，影响该能力的一个重要因素就是前向和后向信号穿越整个网络的路径长度，这个路径长度越短，越容易学习到长距离依赖。</li>
</ol>
<h4 id="attention-visualizations">Attention Visualizations</h4>
<p>第一张图展示了 Self-Attention 学到的句子内部的一个长距离依赖 <strong>“making &hellip; more diffcult”</strong>，图中不同的颜色表示不同 Head 的 Attention，颜色越深表示 Attention 的值越大。</p>
<p><img src="/images/cn/2018-10-12-seq2seq-and-attention-machanism/self-attention-long-distance-dependencies.png" alt="Self-Attention-Long-Distance-Dependencies"></p>
<p>第二张图展示了 Self-Attention 学到的一个指代消解关系 (Anaphora Resolution)，its 指代的为上文中的 law。下图 (上) 为 Head 5 的所有 Attention，下图 (下) 为 Head 5 和 6 关于词 its 的 Attention，不难看出模型学习到了 its 和 law 之间的依赖关系 (指代消解关系)。</p>
<p><img src="/images/cn/2018-10-12-seq2seq-and-attention-machanism/self-attention-anaphora-resolution.png" alt="Self-Attention-Long-Anaphora-Resolution"></p>
<h3 id="hierarchical-attention">Hierarchical Attention</h3>
<p>Yang 等人 <sup id="fnref:9"><a href="#fn:9" class="footnote-ref" role="doc-noteref">9</a></sup> 提出了一种层级的注意力 (Hierarchical Attention) 网络用于文档分类。Hierarchical Attention 共包含 4 层：一个词编码器 (Word Encoder)，一个词级别的注意力层 (Word Attention)，一个句子编码器 (Sentence Encoder) 和一个句子级别的注意力层 (Sentence Attention)。网络架构如下图所示：</p>
<p><img src="/images/cn/2018-10-12-seq2seq-and-attention-machanism/hierarchical-attention.png" alt="Hierarchical-Attention"></p>
<h4 id="word-encoder">Word Encoder</h4>
<p>对于一个给定的句子 <code>$w_{it}, t \in \left[0, T\right]$</code>，通过一个 Embedding 矩阵 <code>$W_e$</code> 得到每个词的向量表示，再应用一个双向的 GRU，即：</p>
<p><code>$$ \begin{equation} \begin{split} x_{it} &amp;= W_e w_{it}, t \in \left[1, T\right] \\ \overrightarrow{h}_{it} &amp;= \overrightarrow{\text{GRU}} \left(x_{it}\right), t \in \left[1, T\right] \\ \overleftarrow{h}_{it} &amp;= \overleftarrow{\text{GRU}} \left(x_{it}\right), t \in \left[T, 1\right] \end{split} \end{equation} $$</code></p>
<p>最后将前向的隐状态 <code>$\overrightarrow{h}_{it}$</code> 和后向的隐状态 <code>$\overleftarrow{h}_{it}$</code> 进行拼接，得到 <code>$h_{ij} = \left[\overrightarrow{h}_{it}, \overleftarrow{h}_{it}\right]$</code> 为整个句子在词 <code>$w_{ij}$</code> 附近的汇总信息。</p>
<h4 id="word-attention">Word Attention</h4>
<p>Word Attention 同一般的 Attention 机制类似，计算方式如下：</p>
<p><code>$$ \begin{equation} \begin{split} u_{it} &amp;= \tanh \left(W_w h_{it} + b_w\right) \\ a_{it} &amp;= \dfrac{\exp \left(u_{it}^{\top} u_w\right)}{\sum_{t}{\exp \left(u_{it}^{\top} u_w\right)}} \\ s_i &amp;= \sum_{t}{a_{it} h_{it}} \end{split} \end{equation} $$</code></p>
<h4 id="sentence-encoder">Sentence Encoder</h4>
<p>在 Word Attention 之后，我们得到了一个句子的表示 <code>$s_i$</code>，类似的我们利用一个双向的 GRU 编码文档中的 <code>$L$</code> 个句子：</p>
<p><code>$$ \begin{equation} \begin{split} \overrightarrow{h}_i &amp;= \overrightarrow{\text{GRU}} \left(s_i\right), i \in \left[1, L\right] \\ \overleftarrow{h}_i &amp;= \overleftarrow{\text{GRU}} \left(s_i\right), i \in \left[L, 1\right] \end{split} \end{equation} $$</code></p>
<p>最后将前向的隐状态 <code>$\overrightarrow{h}_i$</code> 和后向的隐状态 <code>$\overleftarrow{h}_i$</code> 进行拼接，得到 <code>$h_i = \left[\overrightarrow{h}_i, \overleftarrow{h}_i\right]$</code> 为整个文档关于句子 <code>$s_i$</code> 的注意力汇总信息。</p>
<h4 id="sentence-attention">Sentence Attention</h4>
<p>同理可得 Sentence Attention 的计算方式如下：</p>
<p><code>$$ \begin{equation} \begin{split} u_i &amp;= \tanh \left(W_s h_i + b_s\right) \\ a_i &amp;= \dfrac{\exp \left(u_i^{\top} u_s\right)}{\sum_{i}{\exp \left(u_i^{\top} u_s\right)}} \\ v &amp;= \sum_{i}{a_i h_i} \end{split} \end{equation} $$</code></p>
<p>最终得到整个文档的向量表示 <code>$v$</code>。</p>
<h3 id="attention-over-attention">Attention-over-Attention</h3>
<p>Cui 等人 <sup id="fnref:10"><a href="#fn:10" class="footnote-ref" role="doc-noteref">10</a></sup> 提出了 Attention-over-Attention 的模型用于阅读理解 (Reading Comprehension)。网络结构如下图所示：</p>
<p><img src="/images/cn/2018-10-12-seq2seq-and-attention-machanism/attention-over-attention.png" alt="Attention-over-Attention"></p>
<p>对于一个给定的训练集 <code>$\langle \mathcal{D}, \mathcal{Q}, \mathcal{A} \rangle$</code>，模型包含两个输入，一个文档 (Document) 和一个问题序列 (Query)。网络的工作流程如下：</p>
<ol>
<li>先获取 Document 和 Query 的 Embedding 结果，再应用一个双向的 GRU 得到对应的隐状态 <code>$h_{doc}$</code> 和 <code>$h_{query}$</code>。</li>
<li>计算一个 Document 和 Query 的匹配程度矩阵 <code>$M \in \mathbb{R}^{\lvert \mathcal{D} \rvert \times \lvert \mathcal{Q} \rvert}$</code>，其中第 <code>$i$</code> 行第 <code>$j$</code> 列的值计算方式如下：
<code>$$ M \left(i, j\right) = h_{doc} \left(i\right)^{\top} \cdot h_{query} \left(j\right) $$</code></li>
<li>按照 <strong>列</strong> 的方向对矩阵 <code>$M$</code> 应用 softmax 函数，矩阵中的每一列为考虑一个 Query 中的词的 Document 级别的 Attention，因此定义 <code>$\alpha \left(t\right) \in \mathbb{R}^{\lvert \mathcal{D} \rvert}$</code> 为 <code>$t$</code> 时刻的 Document 级别 Attention (<strong><em>query-to-document</em> attention</strong>)。计算方式如下：
<code>$$ \begin{equation} \begin{split} \alpha \left(t\right) &amp;= \text{softmax} \left(M \left(1, t\right), \dotsc, M \left(\lvert \mathcal{D} \rvert, t\right)\right) \\ \alpha &amp;= \left[\alpha \left(1\right), \alpha \left(2\right), \dotsc, \alpha \left(\lvert \mathcal{Q} \rvert\right)\right] \end{split} \end{equation} $$</code></li>
<li>同理按照 <strong>行</strong> 的方向对矩阵 <code>$M$</code> 应用 softmax 函数，可以得到 <code>$\beta \left(t\right) \in \mathbb{R}^{\lvert \mathcal{Q} \rvert}$</code> 为 <code>$t$</code> 时刻的 Query 级别的 Attention (<strong><em>document-to-query</em> attention</strong>)。计算方式如下：
<code>$$ \beta \left(t\right) = \text{softmax} \left(M \left(t, 1\right), \dotsc, M \left(t, \lvert \mathcal{Q} \rvert\right)\right) $$</code></li>
<li>对于 document-to-query attention，我们对结果进行平均得到：
<code>$$ \beta = \dfrac{1}{n} \sum_{t=1}^{\lvert \mathcal{D} \rvert}{\beta \left(t\right)} $$</code></li>
<li>最终利用 <code>$\alpha$</code> 和 <code>$\beta$</code> 的点积 <code>$s = \alpha^{\top} \beta \in \mathbb{R}^{\lvert \mathcal{D} \rvert}$</code> 得到 attended document-level attention (即 <strong><em>attention-over-attention</em></strong>)。</li>
</ol>
<h3 id="multi-step-attention">Multi-step Attention</h3>
<p>Gehring 等人 <sup id="fnref:11"><a href="#fn:11" class="footnote-ref" role="doc-noteref">11</a></sup> 提出了基于 CNN 和 Multi-step Attention 的模型用于机器翻译。网络结构如下图所示：</p>
<p><img src="/images/cn/2018-10-12-seq2seq-and-attention-machanism/multi-step-attention.png" alt="Multi-step-Attention"></p>
<h4 id="position-embeddings">Position Embeddings</h4>
<p>模型首先得到序列 <code>$\mathbf{x} = \left(x_1, \dotsc, x_m\right)$</code> 的 Embedding <code>$\mathbf{w} = \left(w_1, \dotsc , w_m\right), w_j \in \mathbb{R}^f$</code>。除此之外还将输入序列的位置信息映射为 <code>$\mathbf{p} = \left(p_1, \dotsc, p_m\right), p_j \in \mathbb{R}^f$</code>，最终将两者进行合并得到最终的输入 <code>$\mathbf{e} = \left(w_1 + p_1, \dotsc, w_m + p_m\right)$</code>。同时在解码器部分也采用类似的操作，将其与解码器网络的输出表示合并之后再喂入解码器网络 <code>$\mathbf{g} = \left(g_1, \dotsc, g_n\right)$</code> 中。</p>
<h4 id="convolutional-block-structure">Convolutional Block Structure</h4>
<p>编码器和解码器均由多个 Convolutional Block 构成，每个 Block 包含一个卷积计算和一个非线性计算。令 <code>$\mathbf{h}^l = \left(h_1^l, \dotsc, h_n^l\right)$</code> 表示解码器第 <code>$l$</code> 个 Block 的输出，<code>$\mathbf{z}^l = \left(z_1^l, \dotsc, z_m^l\right)$</code> 表示编码器第 <code>$l$</code> 个 Block 的输出。对于一个大小 <code>$k = 5$</code> 的卷积核，其结果的隐状态包含了这 5 个输入，则对于一个 6 层的堆叠结构，结果的隐状态则包含了输入中的 25 个元素。</p>
<p>在每一个 Convolutional Block 中，卷积核的参数为 <code>$W \in \mathbb{R}^{2d \times kd}, b_w \in \mathbb{R}^{2d}$</code>，其中 <code>$k$</code> 为卷积核的大小，经过卷积后的输出为 <code>$Y \in \mathbb{R}^{2d}$</code>。之后的非线性层采用了 Dauphin 等人 <sup id="fnref:12"><a href="#fn:12" class="footnote-ref" role="doc-noteref">12</a></sup> 提出的 Gated Linear Units (GLU)，对于卷积后的输出 <code>$Y = \left[A, B\right]$</code> 有：</p>
<p><code>$$ v \left(\left[A, B\right]\right) = A \otimes \sigma \left(B\right) $$</code></p>
<p>其中，<code>$A, B \in \mathbb{R}^d$</code> 为非线性单元的输入，<code>$\otimes$</code> 为逐元素相乘，<code>$\sigma \left(B\right)$</code> 为用于控制输入 <code>$A$</code> 与当前上下文相关度的门结构。</p>
<p>模型中还加入了残差连接，即：</p>
<p><code>$$ h_i^l = v \left(W^l \left[h_{i-k/2}^{l-1}, \dotsc, h_{i+k/2}^{l-1}\right] + b_w^l\right) + h_i^{l-1} $$</code></p>
<p>为了确保网络卷积层的输出同输入的长度相匹配，模型对输入数据的前后填补 <code>$k - 1$</code> 个零值，同时为了避免解码器使用当前预测位置之后的信息，模型删除了卷积输出尾部的 <code>$k$</code> 个元素。在将 Embedding 喂给编码器网络之前，在解码器输出应用 softmax 之前以及所有解码器层计算 Attention 分数之前，建立了一个从 Embedding 维度 <code>$f$</code> 到卷积输出大小 <code>$2d$</code> 的线性映射。最终，预测下一个词的概率计算方式如下：</p>
<p><code>$$ p \left(y_{i+1} | y_1, \dotsc, y_i, \mathbf{x}\right) = \text{softmax} \left(W_o h_i^L + b_o\right) \in \mathbb{R}^T $$</code></p>
<h4 id="multi-step-attention-1">Multi-step Attention</h4>
<p>模型的解码器网络中引入了一个分离的注意力机制，在计算 Attention 时，将解码器当前的隐状态 <code>$h_i^l$</code> 同之前输出元素的 Embedding 进行合并：</p>
<p><code>$$ d_i^l = W_d^l h_i^l + b_d^l + g_i $$</code></p>
<p>对于解码器网络层 <code>$l$</code> 中状态 <code>$i$</code> 和输入元素 <code>$j$</code> 之间的的 Attention <code>$a_{ij}^l$</code> 通过解码器汇总状态 <code>$d_i^l$</code> 和最后一个解码器 Block <code>$u$</code> 的输出 <code>$z_j^u$</code> 进行点积运算得到：</p>
<p><code>$$ a_{ij}^l = \dfrac{\exp \left(d_i^l \cdot z_j^u\right)}{\sum_{t=1}^{m}{\exp \left(d_i^l \cdot z_t^u\right)}} $$</code></p>
<p>条件输入 <code>$c_i^l$</code> 的计算方式如下：</p>
<p><code>$$ c_i^l = \sum_{j=1}^{m}{a_{ij}^l \left(z_j^u + e_j\right)} $$</code></p>
<p>其中，<code>$e_j$</code> 为输入元素的 Embedding。与传统的 Attention 不同，<code>$e_j$</code> 的加入提供了一个有助于预测的具体输入元素信息。</p>
<p>最终将 <code>$c_i^l$</code> 加到对应的解码器层的输出 <code>$h_i^l$</code>。这个过程与传统的单步 Attention 不同，被称之为 Multiple Hops <sup id="fnref:13"><a href="#fn:13" class="footnote-ref" role="doc-noteref">13</a></sup>。这种方式使得模型在计算 Attention 时会考虑之前已经注意过的输入信息。</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Cho, K., van Merrienboer, B., Gulcehre, C., Bahdanau, D., Bougares, F., Schwenk, H., &amp; Bengio, Y. (2014). Learning Phrase Representations using RNN Encoder–Decoder for Statistical Machine Translation. In <em>Proceedings of the 2014 Conference on Empirical Methods in Natural Language Processing (EMNLP)</em> (pp. 1724–1734).&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Sutskever, I., Vinyals, O., &amp; Le, Q. V. (2014). Sequence to Sequence Learning with Neural Networks. In Z. Ghahramani, M. Welling, C. Cortes, N. D. Lawrence, &amp; K. Q. Weinberger (Eds.), <em>Advances in Neural Information Processing Systems 27</em> (pp. 3104–3112).&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>Bahdanau, D., Cho, K., &amp; Bengio, Y. (2014). Neural Machine Translation by Jointly Learning to Align and Translate. <em>arXiv preprint arXiv:1409.0473</em>&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref2:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>Xu, K., Ba, J., Kiros, R., Cho, K., Courville, A., Salakhudinov, R., … Bengio, Y. (2015). Show, Attend and Tell: Neural Image Caption Generation with Visual Attention. In <em>International Conference on Machine Learning</em> (pp. 2048–2057).&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p>Luong, T., Pham, H., &amp; Manning, C. D. (2015). Effective Approaches to Attention-based Neural Machine Translation. In <em>Proceedings of the 2015 Conference on Empirical Methods in Natural Language Processing</em> (pp. 1412–1421).&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6">
<p>Vaswani, A., Shazeer, N., Parmar, N., Uszkoreit, J., Jones, L., Gomez, A. N., … Polosukhin, I. (2017). Attention is All you Need. In I. Guyon, U. V. Luxburg, S. Bengio, H. Wallach, R. Fergus, S. Vishwanathan, &amp; R. Garnett (Eds.), <em>Advances in Neural Information Processing Systems 30</em> (pp. 5998–6008).&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:7">
<p>He, K., Zhang, X., Ren, S., &amp; Sun, J. (2016). Deep Residual Learning for Image Recognition. In <em>2016 IEEE Conference on Computer Vision and Pattern Recognition (CVPR)</em> (pp. 770–778).&#160;<a href="#fnref:7" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:8">
<p>Ba, J. L., Kiros, J. R., &amp; Hinton, G. E. (2016). Layer Normalization. <em>arXiv preprint arXiv:1607.06450</em>&#160;<a href="#fnref:8" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:9">
<p>Yang, Z., Yang, D., Dyer, C., He, X., Smola, A., &amp; Hovy, E. (2016). Hierarchical Attention Networks for Document Classification. In <em>Proceedings of the 2016 Conference of the North American Chapter of the Association for Computational Linguistics</em>: Human Language Technologies (pp. 1480–1489).&#160;<a href="#fnref:9" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:10">
<p>Cui, Y., Chen, Z., Wei, S., Wang, S., Liu, T., &amp; Hu, G. (2017). Attention-over-Attention Neural Networks for Reading Comprehension. In <em>Proceedings of the 55th Annual Meeting of the Association for Computational Linguistics</em> (Volume 1: Long Papers) (pp. 593–602).&#160;<a href="#fnref:10" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:11">
<p>Gehring, J., Auli, M., Grangier, D., Yarats, D., &amp; Dauphin, Y. N. (2017). Convolutional Sequence to Sequence Learning. In <em>International Conference on Machine Learning</em> (pp. 1243–1252).&#160;<a href="#fnref:11" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:12">
<p>Dauphin, Y. N., Fan, A., Auli, M., &amp; Grangier, D. (2016). Language Modeling with Gated Convolutional Networks. <em>arXiv preprint arXiv:1612.08083</em>&#160;<a href="#fnref:12" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:13">
<p>Sukhbaatar, S., szlam,  arthur, Weston, J., &amp; Fergus, R. (2015). End-To-End Memory Networks. In C. Cortes, N. D. Lawrence, D. D. Lee, M. Sugiyama, &amp; R. Garnett (Eds.), <em>Advances in Neural Information Processing Systems 28</em> (pp. 2440–2448).&#160;<a href="#fnref:13" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>

        ]]></description></item><item><title>词向量 (Word Embeddings)</title><link>https://zeqiang.fun/cn/2018/10/word-embeddings/</link><pubDate>Mon, 01 Oct 2018 00:00:00 +0000</pubDate><guid>https://zeqiang.fun/cn/2018/10/word-embeddings/</guid><description><![CDATA[
        <h2 id="文本表示">文本表示</h2>
<p>文本表示是计算机处理自然语言的核心，我们希望计算机能够同人类一样对自然语言能够实现语义层面的理解，但这并非易事。在中文和拉丁语系中，文本的直观表示就存在一定的差异，拉丁语系中词与词之间存在天然的分隔符，而中文则没有。</p>
<blockquote>
<p>I can eat glass, it doesn&rsquo;t hurt me.<br>
我能吞下玻璃而不伤身体。</p>
</blockquote>
<p>所以，在处理中文之前我们往往需要对原始文本进行分词，在此我们不谈这部分工作，假设我们已经得到了分词完的文本，即我们后续需要处理的“<strong>词</strong>”。早期的词表示方法多采用独热编码 (One-Hot Encoding)，对于每一个不同的词都使用一个单独的向量进行表示。对于一个包含 <code>$n$</code> 个词的语料而言，一个词的向量表示 <code>$\text{word}_i \in \left\{0, 1\right\}^n$</code> 仅在第 <code>$i$</code> 的位置值为 1，其他位置的值均为 0。例如，我们可以将“父亲”表示为：</p>
<p><code>$$ \left[1, 0, 0, 0, 0, 0, ...\right] \nonumber $$</code></p>
<p>One-Hot Encoding 的表示方法十分简洁，但也存在着一些问题。</p>
<h3 id="维数灾难-the-curse-of-dimensionality">维数灾难 (The Curse of Dimensionality)</h3>
<p>在很多现实问题中，我们仅用少数的特征是很难利用一个线性模型将数据区分开来的，也就是线性不可分问题。一个有效的方法是利用核函数实现一个非线性变换，将非线性问题转化成线性问题，通过求解变换后的线性问题进而求解原来的非线性问题。</p>
<p>假设 <code>$\mathcal{X}$</code> 是输入空间（欧式空间 <code>$\mathbb{R}^n$</code> 的子集或离散结合），<code>$\mathcal{H}$</code> 为特征空间（希尔伯特空间），若存在一个从 <code>$\mathcal{X}$</code> 到 <code>$ \mathcal{H}$</code> 的映射：</p>
<p><code>$$\phi \left(x\right): \mathcal{X} \rightarrow \mathcal{H}$$</code></p>
<p>使得对所有 <code>$x, z \in \mathcal{X}$</code> ，函数 <code>$K\left(x, z\right)$</code> 满足条件：</p>
<p><code>$$K\left(x, z\right) = \phi \left(x\right) \cdot \phi \left(z\right)$$</code></p>
<p>则 <code>$K\left(x, z\right)$</code> 为核函数， <code>$\phi \left(x\right)$</code> 为映射函数，其中 <code>$\phi \left(x\right) \cdot \phi \left(z\right)$</code> 为 <code>$\phi \left(x\right)$</code> 和 <code>$\phi \left(z\right)$</code> 的内积。</p>
<p>例如，对于一个下图所示的二维数据，显然是线性不可分的。</p>
<p><img src="/images/cn/2018-10-01-word-embeddings/2d-points.png" alt="2d-Points"></p>
<p>构建一个映射 <code>$\phi: \mathbb{R}^2 \rightarrow \mathbb{R}^3$</code> 经 <code>$X$</code> 映射为： <code>$x = x^2, y = y^2, z = y$</code> ，则通过变换后的数据通过可视化可以明显地看出，数据是可以通过一个超平面来分开的。</p>
<p><img src="/images/cn/2018-10-01-word-embeddings/3d-points.png" alt="3d-Points"></p>
<p>可以说随着维度的增加，我们更有可能找到一个超平面（线性模型）将数据划分开来。尽管看起来，随着维度的增加似乎有助于我们构建模型，但是同时数据在高维空间的分布变得越来越<strong>稀疏</strong>。因此，在构建机器学习模型时，当我们需要更好的覆盖数据的分布时，我们需要的数据量就更大，这也就会导致需要更多的时间去训练模型。例如，假设所有特征均为0到1之间连续分布的数据，针对1维的情况，当覆盖50%的数据时，仅需全体50%的样本即可；针对2维的情况，当覆盖50%的数据时，则需全体71% ( <code>$0.71^2 \approx 0.5$</code> ) 的样本；针对3维的情况，当覆盖50%的数据时，则需全体79% ( <code>$0.79^3 \approx 0.5$</code> )，这就是我们所说的维数灾难。</p>
<h3 id="分散式表示-distributed-representations">分散式表示 (Distributed Representations)</h3>
<p>分散式表示（Distributed Representations）<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> 最早由 Hiton 提出，对比于传统的 One-Hot Representation ，Distributed Representations 可以将数据表示为低维，稠密，连续的向量，也就是说将原始空间中的潜在信息分散的表示在低维空间的不同维度上。</p>
<p>传统的 One-Hot Representation 会将数据表示成一个很长的向量，例如，在 NLP 中，利用 One-Hot Representation 表示一个单词：</p>
<pre><code>父亲: [1, 0, 0, 0, 0, 0, ...]
爸爸: [0, 1, 0, 0, 0, 0, ...]
母亲: [0, 0, 1, 0, 0, 0, ...]
妈妈: [0, 0, 0, 1, 0, 0, ...]
</code></pre>
<p>这种表示形式很简介，但也很稀疏，相当于语料库中有多少个词，则表示空间的维度就需要多少。那么，对于传统的聚类算法，高斯混合模型，最邻近算法，决策树或高斯 SVM 需要 <code>$O\left(N\right)$</code> 个参数 (或 <code>$O\left(N\right)$</code> 个样本) 将能够将 <code>$O\left(N\right)$</code> 的输入区分开来。而像 RBMs ，稀疏编码，Auto-Encoder 或多层神经网络则可以利用 <code>$O\left(N\right)$</code> 个参数表示 <code>$O\left(2^k\right)$</code> 的输入，其中 <code>$k \leq N$</code> 为稀疏表示中非零元素的个数 <sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>。</p>
<p>采用 Distributed Representation，则可以将单词表示为：</p>
<pre><code>父亲: [0.12, 0.34, 0.65, ...]
爸爸: [0.11, 0.33, 0.58, ...]
母亲: [0.34, 0.98, 0.67, ...]
妈妈: [0.29, 0.92, 0.66, ...]
</code></pre>
<p>利用这种表示，我们不仅可以将稀疏的高维空间转换为稠密的低维空间，同时我们还能学习出文本间的语义相似性来，例如实例中的 <code>父亲</code> 和 <code>爸爸</code>，从语义上看其均表示 <code>父亲</code> 的含义，但是如果利用 One-Hot Representation 编码则 <code>父亲</code> 与 <code>爸爸</code> 的距离同其与 <code>母亲</code> 或 <code>妈妈</code> 的距离时相同的，而利用 Distributed Representation 编码，则 <code>父亲</code> 同 <code>爸爸</code> 之间的距离要远小于其同 <code>母亲</code> 或 <code>妈妈</code> 之间的距离。</p>
<h2 id="word-embedding-之路">Word Embedding 之路</h2>
<h3 id="n-gram-模型">N-gram 模型</h3>
<p>N-gram (N 元语法) 是一种文本表示方法，指文中连续出现的 <code>$n$</code> 个词语。N-gram 模型是基于 <code>$n-1$</code> 阶马尔科夫链的一种概率语言模型，可以通过前 <code>$n-1$</code> 个词对第 <code>$n$</code> 个词进行预测。Bengio 等人 <sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> 提出了一个三层的神经网络的概率语言模型，其网络结构如下图所示：</p>
<p><img src="/images/cn/2018-10-01-word-embeddings/nplm-network.png" alt="NPLM-Network"></p>
<p>模型的最下面为前 <code>$n-1$</code> 个词 <code>$w_{t-n+1}, ..., w_{t-2}, w_{t-1}$</code>，每个词 <code>$w_i$</code> 通过查表的方式同输入层对应的词向量 <code>$C \left(w_i\right)$</code> 相连。词表 <code>$C$</code> 为一个 <code>$\lvert V\rvert \times m$</code> 大小的矩阵，其中 <code>$\lvert V\rvert$</code> 表示语料中词的数量，<code>$m$</code> 表示词向量的维度。输入层则为前 <code>$n-1$</code> 个词向量拼接成的向量 <code>$x$</code>，其维度为 <code>$m \left(n-1\right) \times 1$</code>。隐含层直接利用 <code>$d + Hx$</code> 计算得到，其中 <code>$H$</code> 为隐含层的权重，<code>$d$</code> 为隐含层的偏置。输出层共包含 <code>$\lvert V\rvert$</code> 个神经元，每个神经元 <code>$y_i$</code> 表示下一个词为第 <code>$i$</code> 个词的未归一化的 log 概率，即：</p>
<p><code>$$ y = b + Wx + U \tanh \left(d + Hx\right) $$</code></p>
<p>对于该问题，我们的优化目标为最大化如下的 log 似然函数：</p>
<p><code>$$ L = \dfrac{1}{T} \sum_{t}{f \left(w_t, w_{t-1}, ..., w_{t-n+1}\right) + R \left(\theta\right)} $$</code></p>
<p>其中，<code>$f \left(w_t, w_{t-1}, ..., w_{t-n+1}\right)$</code> 为利用前 <code>$n-1$</code> 个词预测当前词 <code>$w_t$</code> 的条件概率，<code>$R \left(\theta\right)$</code> 为参数的正则项，<code>$\theta = \left(b, d, W, U, H, C\right)$</code>。<code>$C$</code> 作为模型的参数之一，随着模型的训练不断优化，在模型训练完毕后，<code>$C$</code> 中保存的即为词向量。</p>
<h3 id="continuous-bag-of-words-cbow-和-skip-gram-模型">Continuous Bag-of-Words (CBOW) 和 Skip-gram 模型</h3>
<p>CBOW 和 Skip-gram 均考虑一个词的上下文信息，两种模型的结构如下图所示：</p>
<p><img src="/images/cn/2018-10-01-word-embeddings/cbow-skipgram.png" alt="CBOW-Skipgram"></p>
<p>两者在给定的上下文信息中 (即前后各 <code>$m$</code> 个词) 忽略了上下文环境的序列信息，CBOW (上图左) 是利用上下文环境中的词预测当前的词，而 Skip-gram (上图右) 则是用当前词预测上下文中的词。</p>
<p>对于 CBOW，<code>$x_{1k}, x_{2k}, ..., x_{Ck}$</code> 为上下文词的 One-Hot 表示，<code>$\mathbf{W}_{V \times N}$</code> 为所有词向量构成的矩阵 (词汇表)，<code>$y_j$</code> 为利用上下文信息预测得到的当前词的 One-Hot 表示输出，其中 <code>$C$</code> 为上下文词汇的数量，<code>$V$</code> 为词汇表中词的总数量，<code>$N$</code> 为词向量的维度。从输入层到隐含层，我们对输入层词对应的词向量进行简单的加和，即：</p>
<p><code>$$ h_i = \sum_{c=1}^{C}{x_{ck} \mathbf{W}_{V \times N}} $$</code></p>
<p>对于 Skip-gram，<code>$x_k$</code> 为当前词的 One-Hot 表示，<code>$\mathbf{W}_{V \times N}$</code> 为所有词向量构成的矩阵 (词汇表)，<code>$y_{1j}, y_{2j}, ..., y_{Cj}$</code> 为预测的上次文词汇的 One-Hot 表示输出。从输入层到隐含层，直接将 One-Hot 的输入向量转换为词向量表示即可。</p>
<p>除此之外两者还有一些其他的区别：</p>
<ol>
<li>CBOW 要比 Skip-gram 模型训练快。从模型中我们不难发现：从隐含层到输出层，CBOW 仅需要计算一个损失，而 Skip-gram 则需要计算 <code>$C$</code> 个损失再进行平均进行参数优化。</li>
<li>Skip-gram 在小数量的数据集上效果更好，同时对于生僻词的表示效果更好。CBOW 在从输入层到隐含层时，对输入的词向量进行了平均 (可以理解为进行了平滑处理)，因此对于生僻词，平滑后则容易被模型所忽视。</li>
</ol>
<h3 id="word2vec">Word2Vec</h3>
<p>Mikolov 等人 <sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> 利用上面介绍的 CBOW 和 Skip-gram 两种模型提出了经典的 Word2Vec 算法。Word2Vec 中针对 CBOW 和 Skip-gram 又提出了两种具体的实现方案 Hierarchical Softmax (层次 Softmax) 和 Negative Sampling (负采样)，因此共有 4 种不同的模型。</p>
<ul>
<li><strong>基于 Hierarchical Softmax 的模型</strong></li>
</ul>
<p><strong>基于 Hierarchical Softmax 的 CBOW 模型如下</strong>：</p>
<p><img src="/images/cn/2018-10-01-word-embeddings/hierarchical-softmax-cbow.png" alt="Hierarchical-Softmax-CBOW"></p>
<p>其中：</p>
<ol>
<li><strong>输入层</strong>：包含了 <code>$C$</code> 个词的词向量，<code>$\mathbf{v} \left(w_1\right), \mathbf{v} \left(w_2\right), ..., \mathbf{v} \left(w_C\right) \in \mathbb{R}^N$</code>，<code>$N$</code> 为词向量的维度。</li>
<li><strong>投影层</strong>：将输入层的向量进行加和，即：<code>$\mathbf{x}_w = \sum_{i=1}^{C}{\mathbf{v} \left(w_i\right)} \in \mathbb{R}^N$</code>。</li>
<li><strong>输出层</strong>：输出为一颗二叉树，是根据语料构建出来的 Huffman 树 <sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup>，其中每个叶子节点为词汇表中的一个词。</li>
</ol>
<p>Hierarchical Softmax 是解决概率语言模型中计算效率的关键，CBOW 模型去掉了隐含层，同时将输出层改为了 Huffman 树。对于该模型的优化求解，我们首先引入一些符号，对于 Huffman 树的一个叶子节点 (即词汇表中的词 <code>$w$</code>)，记：</p>
<ul>
<li><code>$p^w$</code>：从根节点出发到达 <code>$w$</code> 对应的叶子节点的路径。</li>
<li><code>$l^w$</code>：路径 <code>$p^w$</code> 包含的节点的个数。</li>
<li><code>$p_1^w, p_1^w, ..., p_{l^w}^w$</code>：路径 <code>$p^w$</code> 中的 <code>$l^w$</code> 个节点，其中 <code>$p_1^w$</code> 表示根节点，<code>$p_{l^w}^w$</code> 表示词 <code>$w$</code> 对应的叶子节点。</li>
<li><code>$d_2^w, d_3^w, ..., d_{l^w}^w \in \{0, 1\}$</code>：词 <code>$w$</code> 的 Huffman 编码，由 <code>$l^w - 1$</code> 位编码构成，<code>$d_j^w$</code> 表示路径 <code>$p^w$</code> 中第 <code>$j$</code> 个结点对应的编码。</li>
<li><code>$\theta_1^w, \theta_1^w, ..., \theta_{l^w - 1}^w \in \mathbb{R}^N$</code>：路径 <code>$p^w$</code> 中非叶子节点对应的向量，<code>$\theta_j^w$</code> 表示路径 <code>$p^w$</code> 中第 <code>$j$</code> 个非叶子节点对应的向量。</li>
</ul>
<p>首先我们需要根据向量 <code>$\mathbf{x}_w$</code> 和 Huffman 树定义条件概率 <code>$p \left(w | Context\left(w\right)\right)$</code>。我们可以将其视为一系列的二分类问题，在到达对应的叶子节点的过程中，经过的每一个非叶子节点均为对应一个取值为 0 或 1 的 Huffman 编码。因此，我们可以将编码为 1 的节点定义为负类，将编码为 0 的节点定义为正类 (即分到左边为负类，分到右边为正类)，则这条路径上对应的标签为：</p>
<p><code>$$ Label \left(p_i^w\right) = 1 - d_i^w, i = 2, 3, ..., l^w $$</code></p>
<p>则对于一个节点被分为正类的概率为 <code>$\sigma \left(\mathbf{x}_w^{\top} \theta\right)$</code>，被分为负类的概率为 <code>$1 - \sigma \left(\mathbf{x}_w^{\top} \theta\right)$</code>。则条件概率可以表示为：</p>
<p><code>$$ p \left(w | Context\left(w\right)\right) = \prod_{j=2}^{l^w}{p \left(d_j^w | \mathbf{x}_w, \theta_{j-1}^w\right)} $$</code></p>
<p>其中</p>
<p><code>$$ p \left(d_j^w | \mathbf{x}_w, \theta_{j-1}^w\right) = \begin{cases} \sigma \left(\mathbf{x}_w^{\top} \theta\right) &amp; d_j^w = 0 \\ 1 - \sigma \left(\mathbf{x}_w^{\top} \theta\right) &amp; d_j^w = 1 \end{cases} $$</code></p>
<p>或表示为：</p>
<p><code>$$ p \left(d_j^w | \mathbf{x}_w, \theta_{j-1}^w\right) = \left[\sigma \left(\mathbf{x}_w^{\top} \theta_{j-1}\right)\right]^{1 - d_j^w} \cdot \left[1 - \sigma \left(\mathbf{x}_w^{\top} \theta_{j-1}\right)\right]^{d_j^w} $$</code></p>
<p>则对数似然函数为：</p>
<p><code>$$ \begin{equation} \begin{split} \mathcal{L} &amp;= \sum_{w \in \mathcal{C}}{\log \prod_{j=2}^{l^w}{\left\{\left[\sigma \left(\mathbf{x}_w^{\top} \theta_{j-1}\right)\right]^{1 - d_j^w} \cdot \left[1 - \sigma \left(\mathbf{x}_w^{\top} \theta_{j-1}\right)\right]^{d_j^w}\right\}}} \\ &amp;= \sum_{w \in \mathcal{C}}{\sum_{j=2}^{l^w}{\left\{\left(1 - d_j^w\right) \cdot \log \left[\sigma \left(\mathbf{x}_w^{\top} \theta_{j-1}^w\right)\right] + d_j^w \cdot \log \left[1 - \sigma \left(\mathbf{x}_w^{\top} \theta_{j-1}^w\right)\right]\right\}}} \end{split} \end{equation} $$</code></p>
<p>记上式花括号中的内容为 <code>$\mathcal{L} \left(w, j\right)$</code>，则 <code>$\mathcal{L} \left(w, j\right)$</code> 关于 <code>$\theta_{j-1}^w$</code> 的梯度为：</p>
<p><code>$$ \begin{equation} \begin{split} \dfrac{\partial \mathcal{L} \left(w, j\right)}{\partial \theta_{j-1}^w} &amp;= \dfrac{\partial}{\partial \theta_{j-1}^w} \left\{\left(1 - d_j^w\right) \cdot \log \left[\sigma \left(\mathbf{x}_w^{\top} \theta_{j-1}^w\right)\right] + d_j^w \cdot \log \left[1 - \sigma \left(\mathbf{x}_w^{\top} \theta_{j-1}^w\right)\right]\right\} \\ &amp;= \left(1 - d_j^w\right) \left[1 - \sigma \left(\mathbf{x}_w^{\top} \theta_{j-1}^w\right)\right] \mathbf{x}_w - d_j^w \sigma \left(\mathbf{x}_w^{\top} \theta_{j-1}^w\right) \mathbf{x}_w \\ &amp;= \left\{\left(1 - d_j^w\right) \left[1 - \sigma \left(\mathbf{x}_w^{\top} \theta_{j-1}^w\right)\right] - d_j^w \sigma \left(\mathbf{x}_w^{\top} \theta_{j-1}^w\right)\right\} \mathbf{x}_w \\ &amp;= \left[1 - d_j^w - \sigma \left(\mathbf{x}_w^{\top} \theta_{j-1}^w\right)\right] \mathbf{x}_w \end{split} \end{equation} $$</code></p>
<p>则 <code>$\theta_{j-1}^w$</code> 的更新方式为：</p>
<p><code>$$ \theta_{j-1}^w \gets \theta_{j-1}^w + \eta \left[1 - d_j^w - \sigma \left(\mathbf{x}_w^{\top} \theta_{j-1}^w\right)\right] \mathbf{x}_w $$</code></p>
<p>同理可得，<code>$\mathcal{L} \left(w, j\right)$</code> 关于 <code>$\mathbf{x}_w$</code> 的梯度为：</p>
<p><code>$$ \dfrac{\partial \mathcal{L} \left(w, j\right)}{\partial \mathbf{x}_w} = \left[1 - d_j^w - \sigma \left(\mathbf{x}_w^{\top} \theta_{j-1}^w\right)\right] \theta_{j-1}^w $$</code></p>
<p>但 <code>$\mathbf{x}_w$</code> 为上下文词汇向量的加和，Word2Vec 的做法是将梯度贡献到上下文中的每个词向量上，即：</p>
<p><code>$$ \mathbf{v} \left(u\right) \gets \mathbf{v} \left(u\right) + \eta \sum_{j=2}^{l^w}{\dfrac{\partial \mathcal{L} \left(w, j\right)}{\partial \mathbf{x}_w}}, u \in Context \left(w\right) $$</code></p>
<p>基于 Hierarchical Softmax 的 CBOW 模型的随机梯度上升算法伪代码如下：</p>


<div><pre class="pseudocode">
\begin{algorithm}
\caption{基于 Hierarchical Softmax 的 CBOW 随机梯度上升算法}
\begin{algorithmic}
\STATE $\mathbf{e} = 0$
\STATE $\mathbf{x}_w = \sum_{u \in Context \left(w\right)}{\mathbf{v} \left(u\right)}$
\FOR{$j = 2, 3, ..., l^w$}
    \STATE $q = \sigma \left(\mathbf{x}_w^{\top} \theta_{j-1}^w\right)$
    \STATE $g = \eta \left(1 - d_j^w - q\right)$
    \STATE $\mathbf{e} \gets \mathbf{e} + g \theta_{j-1}^w$
    \STATE $\theta_{j-1}^w \gets \theta_{j-1}^w + g \mathbf{x}_w$
\ENDFOR
\FOR{$u \in Context \left(w\right)$}
    \STATE $\mathbf{v} \left(u\right) \gets \mathbf{v} \left(u\right) + \mathbf{e}$
\ENDFOR
\end{algorithmic}
\end{algorithm}
</pre></div>

<p><strong>基于 Hierarchical Softmax 的 Skip-gram 模型如下</strong>：</p>
<p><img src="/images/cn/2018-10-01-word-embeddings/hierarchical-softmax-skipgram.png" alt="Hierarchical-Softmax-Skipgram"></p>
<p>对于 Skip-gram 模型，是利用当前词 <code>$w$</code> 对上下文 <code>$Context \left(w\right)$</code> 中的词进行预测，则条件概率为：</p>
<p><code>$$ p \left(Context \left(w\right) | w\right) = \prod_{u \in Context \left(w\right)}{p \left(u | w\right)} $$</code></p>
<p>类似于 CBOW 模型的思想，有：</p>
<p><code>$$ p \left(u | w\right) = \prod_{j=2}^{l^u}{p \left(d_j^u | \mathbf{v} \left(w\right), \theta_{j-1}^u\right)} $$</code></p>
<p>其中</p>
<p><code>$$ p \left(d_j^u | \mathbf{v} \left(w\right), \theta_{j-1}^u\right) = \left[\sigma \left(\mathbf{v} \left(w\right)^{\top} \theta_{j-1}^u\right)\right]^{1 - d_j^u} \cdot \left[1 - \sigma \left(\mathbf{v} \left(w\right)^{\top} \theta_{j-1}^u\right)\right]^{d_j^u} $$</code></p>
<p>可得对数似然函数为：</p>
<p><code>$$ \begin{equation} \begin{split} \mathcal{L} &amp;= \sum_{w \in \mathcal{C}}{\log \prod_{u \in Context \left(w\right)}{\prod_{j=2}^{l^u}{\left\{\left[\sigma \left(\mathbf{v} \left(w\right)^{\top} \theta_{j-1}^{u}\right)\right]^{1 - d_j^u} \cdot \left[1 - \sigma \left(\mathbf{v} \left(w\right)^{\top} \theta_{j-1}^u\right)\right]^{d_j^u}\right\}}}} \\ &amp;= \sum_{w \in \mathcal{C}}{\sum_{u \in Context \left(w\right)}{\sum_{j=2}^{l^u}{\left\{\left(1 - d_j^u\right) \cdot \log \left[\sigma \left(\mathbf{v} \left(w\right)^{\top} \theta_{j-1}^{u}\right)\right] + d_j^u \cdot \log \left[1 - \sigma \left(\mathbf{v} \left(w\right)^{\top} \theta_{j-1}^{u}\right)\right]\right\}}}} \end{split} \end{equation} $$</code></p>
<p>记上式花括号中的内容为 <code>$\mathcal{L} \left(w, u, j\right)$</code>，在 <code>$\mathcal{L} \left(w, u, j\right)$</code> 关于 <code>$\theta_{j-1}^u$</code> 的梯度为：</p>
<p><code>$$ \begin{equation} \begin{split} \dfrac{\partial \mathcal{L} \left(w, u, j\right)}{\partial \theta_{j-1}^{u}} &amp;= \dfrac{\partial}{\partial \theta_{j-1}^{u}} \left\{\left(1 - d_j^u\right) \cdot \log \left[\sigma \left(\mathbf{v} \left(w\right)^{\top} \theta_{j-1}^{u}\right)\right] + d_j^u \cdot \log \left[1 - \sigma \left(\mathbf{v} \left(w\right)^{\top} \theta_{j-1}^{u}\right)\right]\right\} \\ &amp;= \left(1 - d_j^u\right) \cdot \left[1 - \sigma \left(\mathbf{v} \left(w\right)^{\top} \theta_{j-1}^{u}\right)\right] \mathbf{v} \left(w\right) - d_j^u \sigma \left(\mathbf{v} \left(w\right)^{\top} \theta_{j-1}^{u}\right) \mathbf{v} \left(w\right) \\ &amp;= \left\{\left(1 - d_j^u\right) \cdot \left[1 - \sigma \left(\mathbf{v} \left(w\right)^{\top} \theta_{j-1}^{u}\right)\right] - d_j^u \sigma \left(\mathbf{v} \left(w\right)^{\top} \theta_{j-1}^{u}\right)\right\} \mathbf{v} \left(w\right) \\ &amp;= \left[1 - d_j^u - \sigma \left(\mathbf{v} \left(w\right)^{\top} \theta_{j-1}^{u}\right)\right] \mathbf{v} \left(w\right) \end{split} \end{equation} $$</code></p>
<p>则 <code>$\theta_{j-1}^u$</code> 的更新方式为：</p>
<p><code>$$ \theta_{j-1}^u \gets \theta_{j-1}^u + \eta \left[1 - d_j^u - \sigma \left(\mathbf{v} \left(w\right)^{\top} \theta_{j-1}^{u}\right)\right] \mathbf{v} \left(w\right) $$</code></p>
<p>同理可得，<code>$\mathcal{L} \left(w, u, j\right)$</code> 关于 <code>$\mathbf{v} \left(w\right)$</code> 的梯度为：</p>
<p><code>$$ \dfrac{\partial \mathcal{L} \left(w, u, j\right)}{\partial \mathbf{v} \left(w\right)} = \left[1 - d_j^u - \sigma \left(\mathbf{v} \left(w\right)^{\top} \theta_{j-1}^{u}\right)\right] \theta_{j-1}^u $$</code></p>
<p>则 <code>$\mathbf{v} \left(w\right)$</code> 的更新方式为：</p>
<p><code>$$ \mathbf{v} \left(w\right) \gets \mathbf{v} \left(w\right) + \eta \sum_{u \in Context \left(w\right)}{\sum_{j=2}^{l^u}{\dfrac{\partial \mathcal{L} \left(w, u, j\right)}{\partial \mathbf{v} \left(w\right)}}} $$</code></p>
<p>基于 Hierarchical Softmax 的 Skip-gram 模型的随机梯度上升算法伪代码如下：</p>


<div><pre class="pseudocode">
\begin{algorithm}
\caption{基于 Hierarchical Softmax 的 Skig-gram 随机梯度上升算法}
\begin{algorithmic}
\STATE $\mathbf{e} = 0$
\FOR{$u \in Context \left(w\right)$}
    \FOR{$j = 2, 3, ..., l^u$}
        \STATE $q = \sigma \left(\mathbf{x}_w^{\top} \theta_{j-1}^u\right)$
        \STATE $g = \eta \left(1 - d_j^u - q\right)$
        \STATE $\mathbf{e} \gets \mathbf{e} + g \theta_{j-1}^u$
        \STATE $\theta_{j-1}^u \gets \theta_{j-1}^u + g \mathbf{v} \left(w\right)$
    \ENDFOR
\ENDFOR
\STATE $\mathbf{v} \left(w\right) \gets \mathbf{v} \left(w\right) + \mathbf{e}$
\end{algorithmic}
\end{algorithm}
</pre></div>

<ul>
<li><strong>基于 Negative Sampling 的模型</strong></li>
</ul>
<p>基于 Negative Sampling (NEG) 的模型相比于基于 Hierarchical Softmax 的模型不再使用复杂的 Huffman 树，而是使用简单的<strong>随机负采样</strong>，从而大幅的提高了模型的性能。</p>
<p><strong>基于 Negative Sampling 的 CBOW 模型如下</strong>：</p>
<p>对于基于 Negative Sampling  CBOW 模型，已知词 <code>$w$</code> 的上下文 <code>$Context \left(w\right)$</code>，预测词 <code>$w$</code>，则词 <code>$w$</code> 即为一个<strong>正样本</strong>，其他词则为<strong>负样本</strong>。对于一个给定 <code>$Context \left(w\right)$</code> 的负样本集合 <code>$NEG \left(w\right) \neq \varnothing$</code>，词典中的任意词 <code>$\forall \tilde{w} \in \mathcal{D}$</code>，其样本的标签定义为：</p>
<p><code>$$ L^w \left(\tilde{w}\right) =  \begin{cases} 1, &amp; \tilde{w} = w \\ 0, &amp; \tilde{w} \neq w \end{cases} $$</code></p>
<p>则对于一个正样本 <code>$\left(Context, \left(w\right)\right)$</code>，我们希望最大化：</p>
<p><code>$$ g \left(w\right) = \prod_{u \in \left\{w\right\} \cup NEG \left(w\right)}{p \left(u | Context \left(w\right)\right)} $$</code></p>
<p>或表示为：</p>
<p><code>$$ p \left(u | Context \left(w\right)\right) = \left[\sigma \left(\mathbf{x}_w^{\top} \theta^u\right)\right]^{L^w \left(w\right)} \cdot \left[1 - \sigma \left(\mathbf{x}_w^{\top} \theta^u\right)\right]^{1 - L^w \left(w\right)} $$</code></p>
<p>即增大正样本概率的同时减少负样本的概率。对于一个给定的语料库 <code>$\mathcal{C}$</code>，对数似然函数为：</p>
<p><code>$$ \begin{equation} \begin{split} \mathcal{L} &amp;= \sum_{w \in \mathcal{C}}{\log g \left(w\right)} \\ &amp;= \sum_{w \in \mathcal{C}}{\log \prod_{u \in \left\{w\right\} \cup NEG \left(w\right)}{\left\{\left[\sigma \left(\mathbf{x}_w^{\top} \theta^u\right)\right]^{L^w \left(u\right)} \cdot \left[1 - \sigma \left(\mathbf{x}_w^{\top} \theta^u\right)\right]^{1 - L^w \left(u\right)}\right\}}} \\ &amp;= \sum_{w \in \mathcal{C}}{\sum_{u \in \left\{w\right\} \cup NEG \left(w\right)}{\left\{L^w \left(u\right) \cdot \log \left[\sigma \left(\mathbf{x}_w^{\top} \theta^u\right)\right] + \left[1 - L^w \left(u\right)\right] \cdot \log \left[1 - \sigma \left(\mathbf{x}_w^{\top} \theta^u\right)\right]\right\}}} \end{split} \end{equation} $$</code></p>
<p>记上式花括号中的内容为 <code>$\mathcal{L} \left(w, u\right)$</code>，则 <code>$\mathcal{L} \left(w, u\right)$</code> 关于 <code>$\theta^u$</code> 的梯度为：</p>
<p><code>$$ \begin{equation} \begin{split} \dfrac{\partial \mathcal{L} \left(w, u\right)}{\partial \theta^u} &amp;= \dfrac{\partial}{\partial \theta^u} \left\{L^w \left(u\right) \cdot \log \left[\sigma \left(\mathbf{x}_w^{\top} \theta^u\right)\right] + \left[1 - L^w \left(u\right)\right] \cdot \log \left[1 - \sigma \left(\mathbf{x}_w^{\top} \theta^u\right)\right]\right\} \\ &amp;= L^w \left(u\right) \left[1 - \sigma \left(\mathbf{w}_w^{\top} \theta^u\right)\right] \mathbf{x}_w - \left[1 - L^w \left(u\right)\right] \sigma \left(\mathbf{x}_w^{\top} \theta^u\right) \mathbf{x}_w \\ &amp;= \left\{L^w \left(u\right) \left[1 - \sigma \left(\mathbf{w}_w^{\top} \theta^u\right)\right] - \left[1 - L^w \left(u\right)\right] \sigma \left(\mathbf{x}_w^{\top} \theta^u\right)\right\} \mathbf{x}_w \\ &amp;= \left[L^w \left(u\right) - \sigma \left(\mathbf{w}_w^{\top} \theta^u\right)\right] \mathbf{x}_w \end{split} \end{equation} $$</code></p>
<p>则 <code>$\theta^u$</code> 的更新方式为：</p>
<p><code>$$ \theta^u \gets \theta^u + \eta \left[L^w \left(u\right) - \sigma \left(\mathbf{w}_w^{\top} \theta^u\right)\right] \mathbf{x}_w $$</code></p>
<p>同理可得，<code>$\mathcal{L} \left(w, u\right)$</code> 关于 <code>$\mathbf{x}_w$</code> 的梯度为：</p>
<p><code>$$ \dfrac{\partial \mathcal{L} \left(w, u\right)}{\partial \mathbf{x}_w} = \left[L^w \left(u\right) - \sigma \left(\mathbf{w}_w^{\top} \theta^u\right)\right] \theta^u $$</code></p>
<p>则 <code>$\mathbf{v} \left(\tilde{w}\right), \tilde{w} \in Context \left(w\right)$</code> 的更新方式为：</p>
<p><code>$$ \mathbf{v} \left(\tilde{w}\right) \gets \mathbf{v} \left(\tilde{w}\right) + \eta \sum_{u \in \left\{w\right\} \cup NEG \left(w\right)}{\dfrac{\partial \mathcal{L} \left(w, u\right)}{\partial \mathbf{x}_w}}, \tilde{w} \in Context \left(w\right) $$</code></p>
<p>基于 Negative Sampling 的 CBOW 模型的随机梯度上升算法伪代码如下：</p>


<div><pre class="pseudocode">
\begin{algorithm}
\caption{基于 Negative Sampling 的 CBOW 随机梯度上升算法}
\begin{algorithmic}
\STATE $\mathbf{e} = 0$
\STATE $\mathbf{x}_w = \sum_{u \in Context \left(w\right)}{\mathbf{v} \left(u\right)}$
\FOR{$u \in Context \left\{w\right\} \cup NEG \left(w\right)$}
    \STATE $q = \sigma \left(\mathbf{x}_w^{\top} \theta^u\right)$
    \STATE $g = \eta \left(L^w \left(u\right) - q\right)$
    \STATE $\mathbf{e} \gets \mathbf{e} + g \theta^u$
    \STATE $\theta^u \gets \theta^u + g \mathbf{x}_w$
\ENDFOR
\FOR{$u \in Context \left(w\right)$}
    \STATE $\mathbf{v} \left(u\right) \gets \mathbf{v} \left(u\right) + \mathbf{e}$
\ENDFOR
\end{algorithmic}
\end{algorithm}
</pre></div>

<p><strong>基于 Negative Sampling 的 Skip-gram 模型如下</strong>：</p>
<p>对于 Skip-gram 模型，利用当前词 <code>$w$</code> 对上下文 <code>$Context \left(w\right)$</code> 中的词进行预测，则对于一个正样本 <code>$\left(Context, \left(w\right)\right)$</code>，我们希望最大化：</p>
<p><code>$$ g \left(w\right) = \prod_{\tilde{w} \in Context \left(w\right)}{\prod_{u \in \left\{w\right\} \cup NEG^{\tilde{w}} \left(w\right)}{p \left(u | \tilde{w}\right)}} $$</code></p>
<p>其中，<code>$NEG^{\tilde{w}} \left(w\right)$</code> 为处理词 <code>$\tilde{w}$</code> 时生成的负样本集合，且：</p>
<p><code>$$ p \left(u | \tilde{w}\right) =  \begin{cases} \sigma \left(\mathbf{v}\left(\tilde{w}\right)^{\top} \theta^u\right) &amp; L^w \left(u\right) = 1 \\ 1 - \sigma \left(\mathbf{v}\left(\tilde{w}\right)^{\top} \theta^u\right) &amp; L^w \left(u\right) = 0 \end{cases} $$</code></p>
<p>或表示为：</p>
<p><code>$$ p \left(u | \tilde{w}\right) = \left[\sigma \left(\mathbf{v}\left(\tilde{w}\right)^{\top} \theta^u\right)\right]^{L^w \left(u\right)} \cdot \left[1 - \sigma \left(\mathbf{v}\left(\tilde{w}\right)^{\top} \theta^u\right)\right]^{1 - L^w \left(u\right)} $$</code></p>
<p>对于一个给定的语料库 <code>$\mathcal{C}$</code>，对数似然函数为：</p>
<p><code>$$ \begin{equation} \begin{split} \mathcal{L} &amp;= \sum_{w \in \mathcal{C}}{\log g \left(w\right)} \\ &amp;= \sum_{w \in \mathcal{C}}{\log \prod_{\tilde{w} \in Context \left(w\right)}{\prod_{u \in \left\{w\right\} \cup NEG^{\tilde{w}} \left(w\right)}{\left\{\left[\sigma \left(\mathbf{v}\left(\tilde{w}\right)^{\top} \theta^u\right)\right]^{L^w \left(u\right)} \cdot \left[1 - \sigma \left(\mathbf{v}\left(\tilde{w}\right)^{\top} \theta^u\right)\right]^{1 - L^w \left(u\right)}\right\}}}} \\ &amp;= \sum_{w \in \mathcal{C}}{\sum_{\tilde{w} \in Context \left(w\right)}{\sum_{u \in \left\{w\right\} \cup NEG^{\tilde{w}} \left(w\right)}{\left\{L^w \left(u\right) \cdot \log \left[\sigma \left(\mathbf{v}\left(\tilde{w}\right)^{\top} \theta^u\right)\right] + \left[1 - L^w \left(u\right)\right] \cdot \log \left[1 - \sigma \left(\mathbf{v}\left(\tilde{w}\right)^{\top} \theta^u\right)\right]\right\}}}} \end{split} \end{equation} $$</code></p>
<p>记上式花括号中的内容为 <code>$\mathcal{L} \left(w, \tilde{w}, u\right)$</code>，则 <code>$\mathcal{L} \left(w, \tilde{w}, u\right)$</code> 关于 <code>$\theta^u$</code> 的梯度为：</p>
<p><code>$$ \begin{equation} \begin{split} \dfrac{\partial \mathcal{L} \left(w, \tilde{w}, u\right)}{\partial \theta^u} &amp;= \dfrac{\partial}{\partial \theta^u} \left\{L^w \left(u\right) \cdot \log \left[\sigma \left(\mathbf{v}\left(\tilde{w}\right)^{\top} \theta^u\right)\right] + \left[1 - L^w \left(u\right)\right] \cdot \log \left[1 - \sigma \left(\mathbf{v}\left(\tilde{w}\right)^{\top} \theta^u\right)\right]\right\} \\ &amp;= L^w \left(u\right) \left[1 - \sigma \left(\mathbf{v} \left(\tilde{w}\right)^{\top} \theta^u\right)\right] \mathbf{v} \left(\tilde{w}\right) - \left[1 - L^w \left(u\right)\right] \sigma \left(\mathbf{v} \left(\tilde{w}\right)^{\top} \theta^u\right) \mathbf{v} \left(\tilde{w}\right) \\ &amp;= \left\{L^w \left(u\right) \left[1 - \sigma \left(\mathbf{v} \left(\tilde{w}\right)^{\top} \theta^u\right)\right] - \left[1 - L^w \left(u\right)\right] \sigma \left(\mathbf{v} \left(\tilde{w}\right)^{\top} \theta^u\right)\right\} \mathbf{v} \left(\tilde{w}\right) \\ &amp;= \left[L^w \left(u\right) - \sigma \left(\mathbf{v} \left(\tilde{w}\right)^{\top} \theta^u\right)\right] \mathbf{v} \left(\tilde{w}\right) \end{split} \end{equation} $$</code></p>
<p>则 <code>$\theta^u$</code> 的更新方式为：</p>
<p><code>$$ \theta^u \gets \theta^u + \eta \left[L^w \left(u\right) - \sigma \left(\mathbf{v} \left(\tilde{w}\right)^{\top} \theta^u\right)\right] \mathbf{v} \left(\tilde{w}\right) $$</code></p>
<p>同理可得，<code>$\mathcal{L} \left(w, \tilde{w}, u\right)$</code> 关于 <code>$\mathbf{v} \left(\tilde{w}\right)$</code> 的梯度为：</p>
<p><code>$$ \dfrac{\partial \mathcal{L} \left(w, \tilde{w}, u\right)}{\partial \mathbf{v} \left(\tilde{w}\right)} = \left[L^w \left(u\right) - \sigma \left(\mathbf{v} \left(\tilde{w}\right)^{\top} \theta^u\right)\right] \theta^u $$</code></p>
<p>则 <code>$\mathbf{v} \left(\tilde{w}\right)$</code> 的更新方式为：</p>
<p><code>$$ \mathbf{v} \left(\tilde{w}\right) \gets \mathbf{v} \left(\tilde{w}\right) + \eta \sum_{u \in \left\{w\right\} \cup NEG^{\tilde{w}} \left(w\right)}{\dfrac{\partial \mathcal{L} \left(w, \tilde{w}, u\right)}{\partial \mathbf{v} \left(\tilde{w}\right)}} $$</code></p>
<p>基于 Negative Sampling 的 Skig-gram 模型的随机梯度上升算法伪代码如下：</p>


<div><pre class="pseudocode">
\begin{algorithm}
\caption{基于 Negative Sampling 的 Skig-gram 随机梯度上升算法}
\begin{algorithmic}
\STATE $\mathbf{e} = 0$
\FOR{$\tilde{w} \in Context \left(w\right)$}
    \FOR{$u \in \left\{w\right\} \cup NEG^{\tilde{w}} \left(w\right)$}
        \STATE $q = \sigma \left(\mathbf{v} \left(\tilde{w}\right)^{\top} \theta^u\right)$
        \STATE $g = \eta \left(L^w \left(u\right) - q\right)$
        \STATE $\mathbf{e} \gets \mathbf{e} + g \theta^u$
        \STATE $\theta^u \gets \theta^u + g \mathbf{v} \left(\tilde{w}\right)$
    \ENDFOR
\ENDFOR
\STATE $\mathbf{v} \left(\tilde{w}\right) \gets \mathbf{v} \left(\tilde{w}\right) + \mathbf{e}$
\end{algorithmic}
\end{algorithm}
</pre></div>

<p>无论是基于 Negative Sampling 的 CBOW 模型还是 Skip-gram 模型，我们都需要对于给定的词 <code>$w$</code> 生成 <code>$NEG \left(w\right)$</code>，对于一个词典 <code>$\mathcal{D}$</code> 和给定的语料 <code>$\mathcal{C}$</code>，一个词被选择中的概率为：</p>
<p><code>$$ p_{NEG} \left(w\right) = \dfrac{\#w}{\sum_{u \in \mathcal{D}}{\#u}} $$</code></p>
<p>其中 <code>$\#w$</code> 和 <code>$\#u$</code> 表示词 <code>$w$</code> 和 <code>$u$</code> 在语料 <code>$\mathcal{C}$</code> 中出现的频次。在 Word2Vec 的 C 代码中 <sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup>，并没有使用词的原始频次，而是对其做了 0.75 次幂，即：</p>
<p><code>$$ p_{NEG} \left(w\right) = \dfrac{\left(\#w\right)^{0.75}}{\sum_{u \in \mathcal{D}}{\left(\#u\right)^{0.75}}} $$</code></p>
<div class="blockquote" style='border-left: 4px solid #369BE5;'>本节内容参考了 licstar 的 <a href="http://licstar.net/archives/328">博客</a> 和 peghoty 的 <a href="https://www.cnblogs.com/peghoty/p/3857839.html">博客</a>。</div>
<h2 id="其他-embedding-方法">其他 Embedding 方法</h2>
<h3 id="glove">GloVe</h3>
<p>GloVe (Global Vector 的简写) 是由 Pennington 等人 <sup id="fnref:7"><a href="#fn:7" class="footnote-ref" role="doc-noteref">7</a></sup> 提出了一种词向量生成方法，该方法利用了语料的全局统计信息。</p>
<p>令 <code>$X$</code> 表示词与词之间的共现矩阵，<code>$X_{ij}$</code> 表示词 <code>$j$</code> 在词 <code>$i$</code> 为上下文的情况下出现的频次。则 <code>$X_i = \sum_{k}{X_{ik}}$</code> 表示在词<code>$i$</code> 为上下文的情况任意词出现的总次数。令 <code>$P_{ij} = P \left(j | i\right) = X_{ij} / X_i$</code> 表示词 <code>$j$</code> 在词 <code>$i$</code> 出现前提下出现的条件概率。</p>
<p>例如，我们令 <code>$i = ice, j = steam$</code>，则这两个词之间的关系可以利用同其他词 <code>$k$</code> 共现概率的比率学习得出。则有：</p>
<ol>
<li>与词 <code>ice</code> 相关，但与词 <code>steam</code> 不太相关，例如 <code>$k = solid$</code>，则比率 <code>$P_{ik} / P_{jk}$</code> 应该较大；类似的当词 <code>$k$</code> 与 <code>steam</code> 相关，但与词 <code>ice</code> 不太相关，则比率 <code>$P_{ik} / P_{jk}$</code> 应该较小。</li>
<li>当与词 <code>ice</code> 和词 <code>steam</code> 均相关或者均不太相关时，例如 <code>$k = water$</code> 或 <code>$k = fashion$</code>，则比率 <code>$P_{ik} / P_{jk}$</code> 应该和 1 接近。</li>
</ol>
<p>下表展示了在一个大量语料上的概率及其比率：</p>
<table>
  <thead>
      <tr>
          <th>概率和比例</th>
          <th><code>$k = solid$</code></th>
          <th><code>$k = gas$</code></th>
          <th><code>$k = water$</code></th>
          <th><code>$k = fashion$</code></th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>$P \left(k \vert ice\right)$</code></td>
          <td><code>$1.9 \times 10^{-4}$</code></td>
          <td><code>$6.6 \times 10^{-5}$</code></td>
          <td><code>$3.0 \times 10^{-3}$</code></td>
          <td><code>$1.7 \times 10^{-5}$</code></td>
      </tr>
      <tr>
          <td><code>$P \left(k \vert steam\right)$</code></td>
          <td><code>$2.2 \times 10^{-5}$</code></td>
          <td><code>$7.8 \times 10^{-4}$</code></td>
          <td><code>$2.2 \times 10^{-3}$</code></td>
          <td><code>$1.8 \times 10^{-5}$</code></td>
      </tr>
      <tr>
          <td><code>$P \left(k \vert ice\right) / P \left(k \vert steam\right)$</code></td>
          <td><code>$8.9$</code></td>
          <td><code>$8.5 \times 10^{-2}$</code></td>
          <td><code>$1.36$</code></td>
          <td><code>$0.96$</code></td>
      </tr>
  </tbody>
</table>
<p>根据如上的假设，我们可以得到一个最基础的模型：</p>
<p><code>$$ F \left(w_i, w_j, \tilde{w}_k\right) = \dfrac{P_{ik}}{P_{jk}} $$</code></p>
<p>其中 <code>$w \in \mathbb{R}^d$</code> 为词向量，<code>$\tilde{w}_k \in \mathbb{R}^d$</code> 为单独的上下文词的词向量。假设向量空间是一个线性结构，因此 <code>$F$</code> 仅依赖于两个向量之间的差异，则模型可以改写为：</p>
<p><code>$$ F \left(w_i - w_j, \tilde{w}_k\right) = \dfrac{P_{ik}}{P_{jk}} $$</code></p>
<p>上式中右面是一个标量，如果左面的参数利用一个复杂的模型进行计算，例如神经网络，则会破坏我们希望保留的线性结构。因此，我们对参数采用点积运算，即：</p>
<p><code>$$ F \left(\left(w_i - w_j\right)^{\top} \tilde{w}_k\right) = \dfrac{P_{ik}}{P_{jk}} $$</code></p>
<p>在词之间的共现矩阵中，一个词和其上下文中的一个词之间应该是可以互换角色的。首先我们要保证 <code>$F$</code> 在 <code>$\left(\mathbb{R}, +\right)$</code> 和 <code>$\left(\mathbb{R}_{&gt;0}, \times\right)$</code> 上是同态的 (homomorphism)，例如：</p>
<p><code>$$ F \left(\left(w_i - w_j\right)^{\top} \tilde{w}_k\right) = \dfrac{F \left(w_i^{\top} \tilde{w}_k\right)}{F \left(w_j^{\top} \tilde{w}_k\right)} $$</code></p>
<p>其中 <code>$F \left(w_i^{\top} \tilde{w}_k\right) = P_{ik} = \dfrac{X_{ik}}{X_i}$</code>，则上式的一个解为 <code>$F = \exp$</code>，或：</p>
<p><code>$$ w_i^{\top} \tilde{w}_k = \log \left(P_{ik}\right) = \log \left(X_{ik}\right) - \log \left(X_i\right) $$</code></p>
<p>其中 <code>$\log \left(X_i\right)$</code> 与 <code>$k$</code> 无关记为 <code>$b_i$</code>，同时为了对称性添加 <code>$\tilde{b}_k$</code>，则上式改写为：</p>
<p><code>$$ w_i^{\top} \tilde{w}_k + b_i + \tilde{b}_k = \log \left(X_{ik}\right) $$</code></p>
<p>上式中，左侧为词向量的相关运算，右侧为共现矩阵的常量信息，则给出模型的损失函数如下：</p>
<p><code>$$ J = \sum_{i,j=1}^{V}{f \left(X_{ij}\right) \left(w_i^{\top} \tilde{w}_k + b_i + \tilde{b}_k - \log X_{ij}\right)^2} $$</code></p>
<p>其中，<code>$V$</code> 为词典中词的个数，<code>$f$</code> 为一个权重函数，其应具有如下特点：</p>
<ol>
<li><code>$f \left(0\right) = 0$</code>。如果 <code>$f$</code> 为一个连续函数，则当 <code>$x \to 0$</code> 时 <code>$\lim_{x \to 0}{f \left(x\right) \log^2 x}$</code> 应足够快地趋近于无穷。</li>
<li><code>$f \left(x\right)$</code> 应为非减函数，以确保稀少的共现不会权重过大。</li>
<li><code>$f \left(x\right)$</code> 对于较大的 <code>$x$</code> 应该相对较小，以确保过大的共现不会权重过大。</li>
</ol>
<p>文中给出了一个符合要求的函数如下：</p>
<p><code>$$ f \left(x\right) =  \begin{cases} \left(x / x_{\max}\right)^{\alpha} &amp; \text{if} \  x &lt; x_{\max} \\ 1 &amp; \text{otherwise} \end{cases} $$</code></p>
<p>其中两个超参数的值建议为 <code>$x_{\max} = 100, \alpha = 0.75$</code>。</p>
<h3 id="fasttext">fastText</h3>
<p>fastText 是由 Bojanowski 和 Grave 等人 <sup id="fnref:8"><a href="#fn:8" class="footnote-ref" role="doc-noteref">8</a></sup> 提出的一种词向量表示方法。原始的 Skip-gram 模型忽略了词语内部的结构信息，fastText 利用 N-gram 方法将其考虑在内。</p>
<p>对于一个词 <code>$w$</code>，利用一系列的 N-gram 进行表示，同时在词的前后添加 <code>&lt;</code> 和 <code>&gt;</code> 边界符号以同其他文本序列进行区分。同时还将词语本身也包含在这个 N-gram 集合中，从而学习到词语的向量表示。例如，对于词 <code>$where$</code> 和 <code>$n = 3$</code>，则 N-gram 集合为：<code>&lt;wh, whe, her, ere, re&gt;</code>，同时包含词本身 <code>&lt;where&gt;</code>。需要注意的是，序列 <code>&lt;her&gt;</code> 与词 <code>$where$</code> 中的 tri-gram <code>her</code> 是两个不同的概念。模型提取所有 <code>$3 \leq n \leq 6$</code> 的 N-gram 序列。</p>
<p>假设 N-gram 词典的大小为 <code>$G$</code>，对于一个词 <code>$w$</code>，<code>$\mathcal{G}_w \subset \left\{1, ..., G\right\}$</code> 表示词中出现的 N-gram 的集合。针对任意一个 N-gram <code>$g$</code>，用向量 <code>$\mathbf{z}_g$</code> 表示，则我们利用一个词的所有 N-gram 的向量的加和表示该词。可以得到该模型的评分函数为：</p>
<p><code>$$ s \left(w, c\right) = \sum_{g \in \mathcal{G}_w}{\mathbf{z}_g^{\top} \mathbf{v}_c} $$</code></p>
<p>模型在学习不同词向量时可以共享权重 (不同词的可能包含相同的 N-gram)，使得在学习低频词时也可得到可靠的向量表示。</p>
<h3 id="wordrank">WordRank</h3>
<p>WordRank 是由 Ji 等人 <sup id="fnref:9"><a href="#fn:9" class="footnote-ref" role="doc-noteref">9</a></sup> 提出的一种词向量表示方法，其将词向量学习问题转换成一个排序问题。</p>
<p>我们令 <code>$\mathbf{u}_w$</code> 表示当前词 <code>$w$</code> 的 <code>$k$</code> 维词向量，<code>$\mathbf{v}_c$</code> 表示当前词上下文 <code>$c$</code> 的词向量。通过两者的内积 <code>$\langle \mathbf{u}_w, \mathbf{v}_c \rangle$</code> 来捕获词 <code>$w$</code> 和上下文 <code>$c$</code> 之间的关系，两者越相关则该内积越大。对于一个给定的词 <code>$w$</code>，利用上下文集合 <code>$\mathcal{C}$</code> 同词的内积分数进行排序，对于一个给定的上下文 <code>$c$</code>，排序为：</p>
<p><code>$$ \begin{equation} \begin{split} \text{rank} \left(w, c\right) &amp;= \sum_{c' \in \mathcal{C} \setminus \left\{c\right\}}{I \left(\langle \mathbf{u}_w, \mathbf{v}_c \rangle - \langle \mathbf{u}_w, \mathbf{v}_{c'} \rangle \leq 0\right)} \\ &amp;= \sum_{c' \in \mathcal{C} \setminus \left\{c\right\}}{I \left(\langle \mathbf{u}_w, \mathbf{v}_c - \mathbf{v}_{c'}  \rangle \leq 0\right)} \end{split} \end{equation} $$</code></p>
<p>其中，<code>$I \left(x \leq 0\right)$</code> 为一个 0-1 损失函数，当 <code>$x \leq 0$</code> 时为 1 其他情况为 0。由于 <code>$I \left(x \leq 0\right)$</code> 为一个非连续函数，因此我们可以将其替换为一个凸上限函数 <code>$\ell \left(\cdot\right)$</code>，其可以为任意的二分类损失函数，构建排序的凸上限如下：</p>
<p><code>$$ \text{rank} \left(w, c\right) \leq \overline{\text{rank}} \left(w, c\right) = \sum_{c' \in \mathcal{C} \setminus \left\{c\right\}}{\ell \left(\langle \mathbf{u}_w, \mathbf{v}_c - \mathbf{v}_{c'} \rangle\right)} $$</code></p>
<p>我们期望排序模型将更相关的上下文排在列表的顶部，基于此构建损失函数如下：</p>
<p><code>$$ J \left(\mathbf{U}, \mathbf{V}\right) := \sum_{w \in \mathcal{W}}{\sum_{c \in \Omega_w}{r_{w, c} \cdot \rho \left(\dfrac{\overline{\text{rank}} \left(w, c\right) + \beta}{\alpha}\right)}} $$</code></p>
<p>其中，<code>$\mathcal{W}$</code> 表示词典，<code>$\mathbf{U} := \left\{\mathbf{u}_w\right\}_{w \in \mathcal{W}}$</code> 和 <code>$\mathbf{V} := \left\{\mathbf{c}_w\right\}_{c \in \mathcal{C}}$</code> 分别表示词及其上下文词向量的参数，<code>$\Omega_w$</code> 表示与词 <code>$w$</code> 共现的上下文的集合，<code>$r_{w, c}$</code> 为衡量 <code>$w$</code> 和 <code>$c$</code> 之间关系的权重，<code>$\rho \left(\cdot\right)$</code> 为用于衡量排序好坏的单调递增的损失函数，<code>$\alpha \geq 0, \beta \geq 0$</code> 为超参数。可选的有：</p>
<p><code>$$ r_{w, c} = \begin{cases} \left(X_{w, c} / x_{\max}\right)^{\epsilon} &amp; \text{if} \ X_{w, c} &lt; x_{\max} \\ 1 &amp; \text{otherwise} \end{cases} $$</code></p>
<p>其中 <code>$x_{\max} = 100, \epsilon = 0.75$</code>。根据 <code>$\rho \left(\cdot\right)$</code> 的要求，损失函数在排序的顶部 (rank 值小) 的地方更加敏感，同时对于 rank 值较大的地方不敏感。这可以使得模型变得更加稳健 (避免语法错误和语言的非常规使用造成干扰)，因此可选的有：</p>
<p><code>$$ \begin{equation} \begin{split} \rho \left(x\right) &amp;:= \log_2 \left(1 + x\right) \\ \rho \left(x\right) &amp;:= 1 - \dfrac{1}{\log_2 \left(2 + x\right)} \\ \rho \left(x\right) &amp;:= \dfrac{x^{1 - t} - 1}{1 - t}, t \neq 1 \end{split} \end{equation} $$</code></p>
<p>损失函数可以等价的定义为：</p>
<p><code>$$ J \left(\mathbf{U}, \mathbf{V}\right) := \sum_{\left(w, c\right) \in \Omega}{r_{w, c} \cdot \rho \left(\dfrac{\overline{\text{rank}} \left(w, c\right) + \beta}{\alpha}\right)} $$</code></p>
<p>在训练过程中，外层的求和符号容易利用 SDG 算法解决，但对于内层的求和符号除非 <code>$\rho \left(\cdot\right)$</code> 是一个线性函数，否则难以求解。然而，<code>$\rho \left(\cdot\right)$</code> 函数的性质要求其不能是一个线性函数，但我们可以利用其凹函数的特性对其进行一阶泰勒分解，有：</p>
<p><code>$$ \rho \left(x\right) \leq \rho \left(\xi^{-1}\right) + \rho' \left(\xi^{-1}\right) \cdot \left(x - \xi^{-1}\right) $$</code></p>
<p>对于任意 <code>$x$</code> 和 <code>$\xi \neq 0$</code> 均成立，同时当且仅当 <code>$\xi = x^{-1}$</code> 时等号成立。因此，令 <code>$\Xi := \left\{\xi_{w, c}\right\}_{\left(w, c\right) \in \Sigma}$</code>，则可以得到 <code>$J \left(\mathbf{U}, \mathbf{V}\right)$</code> 的一个上界：</p>
<p><code>$$ \begin{equation} \begin{split} \overline{J} \left(\mathbf{U}, \mathbf{V}, \Xi\right) &amp;:= \sum_{\left(w, c\right)  \in \Omega}{r_{w, c} \cdot \left\{\rho \left(\xi_{wc}^{-1}\right) + \rho' \left(\xi_{wc}^{-1}\right) \cdot \left(\alpha^{-1} \beta + \alpha^{-1} \sum_{c' \in \mathcal{C} \setminus \left\{c\right\}}{\ell \left(\langle \mathbf{u}_w, \mathbf{v}_c - \mathbf{v}_{c'} \rangle\right) - \xi_{w, c}^{-1}}\right)\right\}} \\ &amp;= \sum_{\left(w, c, c'\right)}{r_{w, c} \cdot \left(\dfrac{\rho \left(\xi_{w, c}^{-1}\right) + \rho' \left(\xi_{w, c}^{-1}\right) \cdot \left(\alpha^{-1} \beta - \xi_{w, c}^{-1}\right)}{\lvert \mathcal{C} \rvert - 1} + \dfrac{1}{\alpha} \rho' \left(\xi_{w, c}^{-1}\right) \cdot \ell \left(\langle \mathbf{u}_w, \mathbf{v}_c - \mathbf{v}_{c'} \rangle\right)\right)} \end{split} \end{equation} $$</code></p>
<p>其中，<code>$\left(w, c, c'\right) \in \Omega \times \left(\mathcal{C} \setminus \left\{c\right\}\right)$</code>，至此我们可以通过均匀采样 <code>$\left(w, c\right) \in \Sigma$</code> 和 <code>$c' \in \mathcal{C} \setminus \left\{c\right\}$</code> 解决训练问题。</p>
<p>整个 WordRank 算法的伪代码如下：</p>


<div><pre class="pseudocode">
\begin{algorithm}
\caption{WordRank 算法}
\begin{algorithmic}
\STATE $\eta$ 为学习率
\WHILE{$\mathbf{U}$，$\mathbf{V}$ 和 $\Xi$ 未收敛}
    \STATE \COMMENT{阶段1：更新 $\mathbf{U}$ 和 $\mathbf{V}$}
    \WHILE{$\mathbf{U}$ 和 $\mathbf{V}$ 未收敛}
        \STATE 从 $\Omega$ 中均匀采样 $\left(w, c\right)$
        \STATE 从 $\mathcal{C} \setminus \left\{c\right\}$ 中均匀采样 $c'$
        \STATE \COMMENT{同时更新如下 3 个参数}
        \STATE $\mathbf{u}_w \gets \mathbf{u}_w - \eta \cdot r_{w, c} \cdot \rho' \left(\xi_{w, c}^{-1}\right) \cdot \ell' \left(\langle \mathbf{u}_w, \mathbf{v}_c - \mathbf{v}_{c'} \rangle\right) \cdot \left(\mathbf{v}_c - \mathbf{v}_{c'}\right)$
        \STATE $\mathbf{v}_c \gets \mathbf{v}_c - \eta \cdot r_{w, c} \cdot \rho' \left(\xi_{w, c}^{-1}\right) \cdot \ell' \left(\langle \mathbf{u}_w, \mathbf{v}_c - \mathbf{v}_{c'} \rangle\right) \cdot \mathbf{u}_w$
        \STATE $\mathbf{v}_{c'} \gets \mathbf{v}_{c'} - \eta \cdot r_{w, c} \cdot \rho' \left(\xi_{w, c}^{-1}\right) \cdot \ell' \left(\langle \mathbf{u}_w, \mathbf{v}_c - \mathbf{v}_{c'} \rangle\right) \cdot \mathbf{u}_w$
    \ENDWHILE
    \STATE \COMMENT{阶段2：更新 $\Xi$}
    \FOR{$w \in \mathcal{W}$}
        \FOR{$c \in \mathcal{C}$}
            \STATE $\xi_{w, c} = \alpha / \left(\sum_{c' \in \mathcal{C} \setminus \left\{c\right\}}{\ell \left(\langle \mathbf{u}_w, \mathbf{v}_c - \mathbf{v}_{c'} \rangle\right) + \beta}\right)$
        \ENDFOR
    \ENDFOR
\ENDWHILE
\end{algorithmic}
\end{algorithm}
</pre></div>

<h3 id="cw2vec">cw2vec</h3>
<p>cw2vec 是由 Cao 等人 <sup id="fnref:10"><a href="#fn:10" class="footnote-ref" role="doc-noteref">10</a></sup> 提出的一种基于汉字笔画 N-gram 的中文词向量表示方法。该方法根据汉字作为象形文字具有笔画信息的特点，提出了笔画 N-gram 的概念。针对一个词的笔画 N-gram，其生成过程如下图所示：</p>
<p><img src="/images/cn/2018-10-01-word-embeddings/cw2vec-stroke-n-gram-generation.png" alt="cw2vec-Stroke-N-gram-Generation"></p>
<p>共包含 4 个步骤：</p>
<ol>
<li>将一个词拆解成单个的汉字，例如：“大人” 拆解为 “大” 和 “人”。</li>
<li>将每个汉字拆解成笔画，例如：“大” 和 “人” 拆解为 “一，丿，乀，丿，乀”。</li>
<li>将每个笔画映射到对应的编码序列，例如： “一，丿，乀，丿，乀” 映射为 13434。</li>
<li>利用编码序列生成笔画 N-gram，例如：134，343，434；1343，3434；13434。</li>
</ol>
<p>模型中定义一个词 <code>$w$</code> 及其上下文 <code>$c$</code> 的相似度如下：</p>
<p><code>$$ sim \left(w, c\right) = \sum_{q \in S\left(w\right)}{\vec{q} \cdot \vec{c}} $$</code></p>
<p>其中，<code>$S$</code> 为由笔画 N-gram 构成的词典，<code>$S \left(w\right)$</code> 为词 <code>$w$</code> 对应的笔画 N-gram 集合，<code>$q$</code> 为该集合中的一个笔画 N-gram，<code>$\vec{q}$</code> 为 <code>$q$</code> 对应的向量。</p>
<p>该模型的损失函数为：</p>
<p><code>$$ \mathcal{L} = \sum_{w \in D}{\sum_{c \in T \left(w\right)}{\log \sigma \left(sim \left(w, c\right)\right) + \lambda \mathbb{E}_{c' \sim P} \left[\log \sigma \left(- sim \left(w, c'\right)\right)\right]}} $$</code></p>
<p>其中，<code>$D$</code> 为语料中的全部词语，<code>$T \left(w\right)$</code> 为给定的词 <code>$w$</code> 和窗口内的所有上次文词，<code>$\sigma \left(x\right) = \left(1 + \exp \left(-x\right)\right)^{-1}$</code>，<code>$\lambda$</code> 为负采样的个数，<code>$\mathbb{E}_{c' \sim P} \left[\cdot\right]$</code> 表示负样本 <code>$c'$</code> 按照 <code>$D$</code> 中词的分布 <code>$P$</code> 进行采样，该分布可以为词的一元模型的分布 <code>$U$</code>，同时为了避免数据的稀疏性问题，类似 Word2Vec 中的做法采用 <code>$U^{0.75}$</code>。</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Hinton, G. E. (1986, August). Learning distributed representations of concepts. In <em>Proceedings of the eighth annual conference of the cognitive science society</em> (Vol. 1, p. 12).&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Bengio, Y., Courville, A., &amp; Vincent, P. (2013). Representation learning: A review and new perspectives. <em>IEEE transactions on pattern analysis and machine intelligence</em>, 35(8), 1798-1828.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>Bengio, Y., Ducharme, R., Vincent, P., &amp; Jauvin, C. (2003). A Neural Probabilistic Language Model. <em>Journal of Machine Learning Research</em>, 3(Feb), 1137–1155.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>Mikolov, T., Chen, K., Corrado, G., &amp; Dean, J. (2013). Efficient Estimation of Word Representations in Vector Space. <em>arXiv preprint arXiv:1301.3781</em>&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p><a href="https://zh.wikipedia.org/zh/%E9%9C%8D%E5%A4%AB%E6%9B%BC%E7%BC%96%E7%A0%81">https://zh.wikipedia.org/zh/霍夫曼编码</a>&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6">
<p><a href="https://code.google.com/archive/p/word2vec/">https://code.google.com/archive/p/word2vec/</a>&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:7">
<p>Pennington, J., Socher, R., &amp; Manning, C. (2014). Glove: Global Vectors for Word Representation. In <em>Proceedings of the 2014 Conference on Empirical Methods in Natural Language Processing (EMNLP)</em> (pp. 1532–1543).&#160;<a href="#fnref:7" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:8">
<p>Bojanowski, P., Grave, E., Joulin, A., &amp; Mikolov, T. (2017). Enriching Word Vectors with Subword Information. <em>Transactions of the Association for Computational Linguistics</em>, 5, 135–146.&#160;<a href="#fnref:8" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:9">
<p>Ji, S., Yun, H., Yanardag, P., Matsushima, S., &amp; Vishwanathan, S. V. N. (2016). WordRank: Learning Word Embeddings via Robust Ranking. In <em>Proceedings of the 2016 Conference on Empirical Methods in Natural Language Processing</em> (pp. 658–668).&#160;<a href="#fnref:9" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:10">
<p>Cao, S., Lu, W., Zhou, J., &amp; Li, X. (2018). cw2vec: Learning Chinese Word Embeddings with Stroke n-gram Information. In <em>Thirty-Second AAAI Conference on Artificial Intelligence</em>.&#160;<a href="#fnref:10" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>

        ]]></description></item><item><title>流形学习 (Manifold Learning)</title><link>https://zeqiang.fun/cn/2018/03/manifold-learning/</link><pubDate>Fri, 16 Mar 2018 00:00:00 +0000</pubDate><guid>https://zeqiang.fun/cn/2018/03/manifold-learning/</guid><description><![CDATA[
        <h1 id="降维">降维</h1>
<p>在之前的 <a href="/cn/2017/12/evd-svd-and-pca">博客</a> 中，我们曾经介绍过 PCA 方法及其降维的作用。在原始数据中各个特征之间存在着一定的信息冗余，随着特征的不断增加就容易出现“维数灾难”的问题，因此降维的目的就是在尽可能多的保留原始信息的同时减少数据的维度。一般情况下我们将降维方法分为：<strong>线性降维方法</strong>和<strong>非线性降维方法</strong>，线性降维方法的典型算法有：</p>
<ul>
<li>主成份分析 (PCA, Principal Component Analysis) <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></li>
<li>线性判别分写 (LDA, Linear Discriminant Analysis) <sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></li>
<li>多尺度变换 (MDS, Multi-Dimensional Scaling) <sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></li>
</ul>
<p>非线性降维方法中在此我们仅列举一些基于流行学习的算法：</p>
<ul>
<li>保距特征映射 (ISOMAP) <sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup></li>
<li>局部线性嵌入 (LLE, Locally Linear Embedding) <sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup></li>
<li>拉普拉斯特征映射 (LE, Laplacian Eigenmap) <sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup></li>
</ul>
<p>在现实数据中，很多情况数据是无法通过线性的方法进行降维表示的，因此就需要非线性的降维算法出马了。</p>
<h1 id="流形">流形</h1>
<p>在调研流形相关概念时，发现要想深一步的理解这些概念还是需要详细的了解微分几何相关的内容，鉴于本文的目的主要是介绍流形学习 (主要是降维角度) 的相关内容，因此我们对流形仅做一些粗略的介绍。</p>
<p>“<strong>流形</strong>”是英文单词 <strong>Manifold</strong> 的中文译名，它源于德文术语 Mannigfaltigkeit，最早出现在 Riemann 1851 年的博士论文中，用来表示某种属性所能取到的所有值 <sup id="fnref:7"><a href="#fn:7" class="footnote-ref" role="doc-noteref">7</a></sup>。为了更好的理解流形，我们先引入几个概念：</p>
<p><strong>拓扑结构 (拓扑)</strong> 任意给定集合 <code>$X$</code> 上的一个<strong>拓扑结构 (拓扑)</strong> 是 <code>$X$</code> 的某些特定子集组成的集合 <code>$\tau \subset 2^X$</code>，其中那些特定子集称为 <code>$\tau$</code> 所声明的<strong>开集</strong>，同时满足如下性质：</p>
<ol>
<li>空集和全集是开集，即 <code>$\varnothing, X \in \tau$</code></li>
<li>任意多个开集的并集是开集</li>
<li>有限多个开集的交集是开集</li>
</ol>
<p><strong>拓扑空间</strong> 指定了拓扑结构的集合就称为一个<strong>拓扑空间</strong>。</p>
<p><img src="/images/cn/2018-03-16-manifold-learning/topological-space-sample.png" alt=""></p>
<p>上图中给出了一些拓扑空间的示例，其中左侧 4 个为正确示例，右侧 2 个为错误示例。右上角的缺少了 {2} 和 {3} 的并集 {2, 3}，右下角的缺少了 {1, 2} 和 {2, 3} 的交集 {2}。</p>
<p><strong>同胚</strong> 两个拓扑空间 <code>$\left(X, \tau_X\right)$</code> 和 <code>$\left(Y, \tau_Y\right)$</code> 之间的函数 <code>$f: X \to Y$</code> 称为<strong>同胚</strong>，如果它具有下列性质：</p>
<ol>
<li><code>$f$</code> 是双射 (单射和满射)</li>
<li><code>$f$</code> 是连续的</li>
<li>反函数 <code>$f^{−1}$</code> 也是连续的 (<code>$f$</code> 是开映射)</li>
</ol>
<p>如果拓扑空间是一个几何物体，同胚就是把物体连续延展和弯曲，使其成为一个新的物体。因此，正方形和圆是同胚的，但球面和环面就不是。用一幅图形象的理解同胚，例如下图所示的<strong>咖啡杯</strong>和<strong>甜甜圈</strong> <sup id="fnref:8"><a href="#fn:8" class="footnote-ref" role="doc-noteref">8</a></sup>：</p>
<p><img src="/images/cn/2018-03-16-manifold-learning/mug-and-torus-morph.gif" alt=""></p>
<p>最后我们回过头来解释到底什么是<strong>流形</strong>？流形并不是一个“形状”，而是一个“空间” <sup id="fnref:9"><a href="#fn:9" class="footnote-ref" role="doc-noteref">9</a></sup>。最容易定义的流形是<strong>拓扑流形</strong>，它局部看起来象一些“普通”的欧几里得空间 <code>$\mathbb{R}^n$</code>，一个拓扑流形是一个局部同胚于一个欧几里得空间的拓扑空间。根据 Whitney 嵌入理论 <sup id="fnref:10"><a href="#fn:10" class="footnote-ref" role="doc-noteref">10</a></sup>，任何一个流形都可以嵌入到高维的欧氏空间中。例如，地球的表面可以理解为一个嵌入 3 维空间的 2 维流形，其局部同胚于 2 维的欧式空间，对于一个球体的表面，用极坐标的形式可以表示为</p>
<p><code>$$ \begin{equation} \begin{split} x &amp;= r \sin \theta \cos \phi \\ y &amp;= r \sin \theta \sin \phi \\ z &amp;= r \cos \theta \end{split} \end{equation} $$</code></p>
<p>也就是说其 3 个维度实际上是由 2 个变量控制的。</p>
<h1 id="流形学习">流形学习</h1>
<p>假设 <code>$Y$</code> 为一个欧式空间 <code>$\mathbb{R}^d$</code> 的一个 <code>$d$</code> 维流形，<code>$f: Y \to \mathbb{R}^D$</code> 为一个光滑嵌入，对于 <code>$D &gt; d$</code>，流形学习的目的就是根据空间 <code>$\mathbb{R}^D$</code> 中的观测数据 <code>$\{x_i\}$</code> 重构 <code>$Y$</code> 和 <code>$f$</code> 的过程。隐含数据 <code>$\{y_i\}$</code> 由 <code>$Y$</code> 随机生成，通过光滑嵌入 <code>$f$</code> 生成观测数据，即 <code>$\{x_i = f\left(y_i\right)\}$</code>，所以我们可以将流形学习的问题看做是对于一个给定的观测数据一个生成模型的反向过程 <sup id="fnref:11"><a href="#fn:11" class="footnote-ref" role="doc-noteref">11</a></sup>。</p>
<p>在介绍具体的流形学习算法前，我们先引入几个 3 维数据用于解释后续的具体算法</p>
<p><img src="/images/cn/2018-03-16-manifold-learning/manifold-examples.png" alt=""></p>
<p>第一个为<strong>瑞士卷 (Swiss Roll)</strong>，其形状和我们日常生活中的瑞士卷相似；第二个为 <strong>S 形曲线 (S Curve)</strong>；第三个为一个被<strong>切断的球面 (Severed Sphere)</strong>。</p>
<h2 id="mds">MDS</h2>
<p>多尺度变换 (MDS, Multi-Dimensional Scaling) <sup id="fnref1:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> 是一种通过保留样本在高维空间中的不相似性 (Dissimilarity) 降低数据维度的方法，在这里不相似性可以理解为样本之间的距离。因此，根据距离的度量方式不同可以将其分为度量型 (metric) MDS 和 非度量型 (non-metric) MDS。度量型 MDS 通过计算不同样本之间距离的度量值进行降维，而非度量型则仅考虑距离的排序信息，在此我们仅对度量型 MDS 做简单介绍。</p>
<p>MDS 的目标是保留样本在高维空间中的不相似性，假设 <code>$x \in \mathbb{R}^D, x' \in \mathbb{R}^d, D &gt; d$</code>，则 MDS 的目标函数可以写为</p>
<p><code>$$ \min \sum_{i, j} \lvert dist \left(x_i, x_j\right) - dist \left(x'_i, x'_j\right) \rvert $$</code></p>
<p>则，度量型 MDS 的算法的步骤如下：</p>
<ol>
<li>计算样本的距离矩阵 <code>$\boldsymbol{D} = \left[d_{i, j}\right] = \left[dist \left(x_i, x_j\right)\right]$</code>。</li>
<li>构造矩阵 <code>$\boldsymbol{A} = \left[a_{i, j}\right] = \left[- \dfrac{1}{2} d_{i, j}^2\right]$</code>。</li>
<li>通过中心矫正的方法构造矩阵 <code>$\boldsymbol{B} = \boldsymbol{J} \boldsymbol{D} \boldsymbol{J}, \boldsymbol{J} = \boldsymbol{I} - \dfrac{1}{n} \boldsymbol{O}$</code>，其中 <code>$\boldsymbol{I}$</code> 为 <code>$n \times n$</code> 的单位阵，<code>$\boldsymbol{O}$</code> 为 <code>$n \times n$</code> 的值均为 <code>$1$</code> 的矩阵。</li>
<li>计算矩阵 <code>$\boldsymbol{B}$</code> 的特征向量 <code>$e_1, e_2, ..., e_m$</code> 及其对应的特征值 <code>$\lambda_1, \lambda_2, ..., \lambda_m$</code>。</li>
<li>确定维度 <code>$k$</code>，重构数据 <code>$\boldsymbol{X}' = \boldsymbol{E}_k \boldsymbol{\Lambda}_k^{1/2}$</code>，其中 <code>$\boldsymbol{\Lambda}_k$</code> 为前 <code>$k$</code> 个值最大的 <code>$k$</code> 个特征值构成的对角矩阵，<code>$\boldsymbol{E}_k$</code> 是对应的 <code>$k$</code> 个特征向量构成的矩阵。</li>
</ol>
<p>在《多元统计分析》<sup id="fnref:12"><a href="#fn:12" class="footnote-ref" role="doc-noteref">12</a></sup>一书中证明了，<code>$\boldsymbol{X}$</code> 的 <code>$k$</code> 维主坐标正好是将 <code>$\boldsymbol{X}$</code> 中心化后 <code>$n$</code> 个样本的前 <code>$k$</code> 个主成份的值，由此可见 MDS 和 PCA 的作用是类似的。</p>
<p>我们利用中国省会的地理位置给出 MDS 的一个示例，首先我们获取中国省会共 34 个点的坐标，其次我们计算两两之间的距离，我们仅利用距离信息利用 MDS 还原出 2 维空间中的坐标，可视化结果如下所示</p>
<p><img src="/images/cn/2018-03-16-manifold-learning/cities-mds.svg" alt=""></p>
<p>其中，黑色的点为省会的真实位置，蓝色的点为利用距离矩阵和 MDS 还原出来的位置，为了绘制还原出的位置我们对 MDS 的结果做出了适当的翻转和变换。从结果中不难看出，尽管每个点的坐标相比真实坐标都有一定的偏离，但是其很好的保持了相对距离，这也正是 MDS 算法的要求。</p>
<h2 id="isomap">ISOMAP</h2>
<p>对于一些非线性的流形，如果使用线性的降维方法得到的效果就不尽人意了，例如上文中提到的瑞士卷。在 ISOMAP 中，我们首先引入一个测地线的概念，在距离度量定义时，测地线可以定义为空间中两点的局域最短路径。形象的，在一个球面上，两点之间的测地线就是过这两个点的大圆的弧线</p>
<p><img src="/images/cn/2018-03-16-manifold-learning/spherical-triangle.svg" alt=""></p>
<p>那么，对于非线性流形，ISOMAP 则是通过构建邻接图，利用图上的最短距离来近似测地线。在构造邻接图时，我们使用最近邻算法，对于一个点 <code>$x_i$</code> 连接距离其最近的 <code>$k$</code> 个点，两点之间的距离我们则一般使用传统的欧式距离。则任意两点之间的测地线距离则可以利用构建的邻接图上的最短路径进行估计，图上的最短路问题我们可以通过 Dijkstra 或 Floyd-Warshall 算法计算。得到样本的距离矩阵后，ISOMAP 算法则使用 MDS 方法计算得到低维空间的座标映射。</p>
<p><img src="/images/cn/2018-03-16-manifold-learning/swiss-roll-isomap.png" alt=""></p>
<p>上图中，我们给出了利用 ISOMAP 对瑞士卷降至 2 维的一个格式化过程。第一幅图中，我们标注了 2 个蓝色的点，其中蓝色的直线为这 2 个点在三维空间中的欧式距离。第二幅图中，同样是相同的两个点，我们首先利用最近邻算法 (<code>$k = 10$</code>) 将瑞士卷所有的点连接为一个邻接图，其中红色的路径为这 2 个点在邻接图上的最短路。第三幅图是通过 ISOMAP 算法降维至 2 维的结果，其中蓝色的直线是这两个点在 2 维空间中的欧式距离，红色的路径是 3 维最短路在 2 维结果中的连线，可以看出两者是很相近的。</p>
<h2 id="lle">LLE</h2>
<p>局部线性嵌入 (LLE, Locally Linear Embedding) <sup id="fnref1:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup>，从这个名称上我们不难看出其不同与 ISOMAP 那种通过都建邻接图保留全局结构的，而是从局部结构出发对数据进行降维。在 LLE 方法中，主要有如下的基本假设：</p>
<ul>
<li>一个流形的局部可以近似于一个欧式空间</li>
<li>每个样本均可以利用其邻居进行线性重构</li>
</ul>
<p>基于上面的假设，LLE 算法的流程如下：</p>
<ol>
<li>对于点 <code>$X_i$</code>，计算距离其最近的 <code>$k$</code> 个点，<code>$X_j, j \in N_i$</code>。</li>
<li>计算权重 <code>$W_{ij}$</code> 是的能够通过点 <code>$X_i$</code> 的邻居节点最优的重构该点，即最小化
<code>$$ \epsilon \left(W\right) = \sum_i \left\lVert X_i - \sum_j W_{ij} X_j \right\rVert ^2 $$</code></li>
<li>通过权重 <code>$W_{ij}$</code> 计算 <code>$X$</code> 的低维最优重构 <code>$Y$</code>，即最小化
<code>$$ \phi \left(Y\right) = \sum_i \left\lVert Y_i - \sum_j W_{ij} Y_j \right\rVert ^2 $$</code></li>
</ol>
<p>具体上述问题的优化求解过程在此就不在详细描述。针对 LLE 算法，后续很多人从不同方面对其进行了改进：</p>
<ol>
<li>Hessian LLE <sup id="fnref:13"><a href="#fn:13" class="footnote-ref" role="doc-noteref">13</a></sup> 在局部中不再考虑局部的线性关系，而是保持局部的 Hessian 矩阵的二次型的关系。</li>
<li>Modified LLE <sup id="fnref:14"><a href="#fn:14" class="footnote-ref" role="doc-noteref">14</a></sup> 则是修改了寻找最临近的 <code>$k$</code> 个样本的方案，其在寻找 <code>$k$</code> 近邻时希望找到的近邻尽量分布在样本的各个方向，而不是集中在一侧。</li>
<li>LTSA (Local Tangent Space Alignment) <sup id="fnref:15"><a href="#fn:15" class="footnote-ref" role="doc-noteref">15</a></sup> 则是除了保留了局部的几何性质，同时使用的一个从局部几何到整体性质过渡的 alignment 方法，因此可以理解为是一个局部和整体的组合。</li>
</ol>
<h2 id="le">LE</h2>
<p>LE (Laplacian Eigenmap) <sup id="fnref1:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup> 的基本思想是认为在高维空间中距离近的点映射到低维空间中后其位置也相距很近。LE 从这个思想出发，最终将问题转化为求解图拉普拉斯算子的广义特征值问题，具体的一些证明不在这里详细展开说明，具体请参见原文，下面仅给出 LE 算法的流程：</p>
<ol>
<li>构建邻接图。</li>
<li>构建邻接矩阵 <code>$W$</code>，构建邻接矩阵有两种方法：对于点 <code>$i$</code> 和点 <code>$j$</code> 相连，如果利用 Hear Kernel (参数 <code>$t \in \mathbb{R}$</code>)，则令 <code>$W_{ij} = \exp \left(\dfrac{- \left\lVert x_i - x_j \right\rVert ^ 2}{t}\right)$</code>；如果使用简介方案，则令 <code>$W_{ij} = 1$</code>，对于不相连的点，则令 <code>$W_{ij} = 0$</code>。</li>
<li>进行特征映射，通过上面构造的图 <code>$G$</code>，计算如下广义特征值和特征向量
<code>$$ L f = \lambda D f $$</code>
其中 <code>$D$</code> 是一个对角矩阵，<code>$D_{ii} = \sum_{j} W_{ji}$</code>，<code>$L = D - W$</code> 即为拉普拉斯矩阵。对于上式的解 <code>$f_0, .., f_{k-1}$</code> 为根据特征值从小到大的排序，其中 <code>$0 = \lambda_0 \leq \lambda_1 \leq ... \leq \lambda_{k-1}$</code>，则降维至 <code>$d$</code> 维的后的特征即为 <code>$\left(f_1, f_2, ..., f_d\right)$</code>。</li>
</ol>
<h2 id="sne-和-t-sne">SNE 和 t-SNE</h2>
<h3 id="sne">SNE</h3>
<p>SNE (Stochastic Neighbor Embedding) <sup id="fnref:16"><a href="#fn:16" class="footnote-ref" role="doc-noteref">16</a></sup> 是由 Hinton 等人提出的一种降维算法，其方法的基本假设如下：</p>
<ol>
<li>对象之间的相似度可以用概率进行表示，即：相似的对象有更高的概率被同时选择，不相似的对象有较低的概率被同时选择。</li>
<li>在高维空间中构建的这种概率分布应该尽可能的同低维空间中的概率分布相似。</li>
</ol>
<p>对于两个点 <code>$x_i, x_j$</code>，假设 <code>$x_i$</code> 以条件概率 <code>$p_{j∣i}$</code> 选择 <code>$x_j$</code> 作为它的邻近点，因此如果两者距离更近 (更相似)，则概率值越大，反之概率值越小，则我们定义 <code>$p_{j∣i}$</code> 如下：</p>
<p><code>$$ p_{j∣i} = \dfrac{\exp \left(\dfrac{- \left\lVert x_i - x_j \right\rVert ^ 2}{2 \sigma_i^2}\right)}{\sum_{k \neq i} \exp \left(\dfrac{- \left\lVert x_i - x_k \right\rVert ^ 2}{2 \sigma_i^2}\right)} $$</code></p>
<p>其中，<code>$\sigma_i$</code> 为参数，同时我们设置 <code>$p_{i∣i} = 0$</code>，因为我们仅需衡量不同对象之间的相似度。</p>
<p>类似的，根据 SNE 的基本思想，当数据被映射到低维空间中后，其概率分布应同高维空间中的分布尽可能的相似，假设点 <code>$x_i, x_j$</code> 在低维空间中的映射点为 <code>$y_i, y_j$</code>，则在低维空间中的条件概率 <code>$q_{j∣i}$</code> 定义为：</p>
<p><code>$$ q_{j∣i} = \dfrac{\exp \left(- \left\lVert y_i - y_j \right\rVert ^ 2\right)}{\sum_{k \neq i} \exp \left(- \left\lVert y_i - y_k \right\rVert ^ 2\right)} $$</code></p>
<p>同样，我们设置 <code>$q_{i∣i} = 0$</code>。从 SNE 的基本假设出发，我们的目的是使得数据在高维空间中的条件概率尽可能的和其在低维空间中的条件概率相同，因此对于全部点样本点而言，就是保证高维空间的概率分布 <code>$P_i$</code> 和低维空间的概率分布 <code>$Q_i$</code> 尽量形同。在这里我们利用 KL 散度衡量这两个概率分布的差异，则 SNE 的损失函数可以写为：</p>
<p><code>$$ C = \sum_{i} KL \left(P_i \Vert Q_i\right) = \sum_{i} \sum_{j} p_{j∣i} \log \dfrac{p_{j∣i}}{q_{j∣i}} $$</code></p>
<p>因为 KL 散度具有不对称性可知，当在原始空间中两点距离较远而降维后的空间中距离较近 (即，<code>$q_{j|i} &lt; p_{j|i}$</code>) 时，会产生较大的 cost，相反则会产生较小的 cost。正是这种不对称性的损失函数导致了 SNE 算法更加关注局部结构，相比忽略了全局结构。</p>
<p>上文中，对于不同的点，<code>$\sigma_i$</code> 具有不同的值，SNE 算法利用困惑度 (Perplexity) 对其进行优化寻找一个最佳的 <code>$\sigma$</code>，对于一个随机变量 <code>$P_i$</code>，困惑度定义如下：</p>
<p><code>$$ Perp \left(P_i\right) = 2^{H \left(P_i\right)} $$</code></p>
<p>其中，<code>$H \left(P_i\right) = \sum_{j} p_{j|i} \log_2 p_{j|i}$</code> 表示 <code>$P_i$</code> 的熵。困惑度可以解释为一个点附近的有效近邻点个数。SNE 对困惑度的调整比较有鲁棒性，通常选择 5-50 之间，给定之后，使用二分搜索的方式寻找合适的 <code>$\sigma$</code>。</p>
<p>SNE 的损失函数对 <code>$y_i$</code> 求梯度后，可得：</p>
<p><code>$$ \dfrac{\delta C}{\delta y_i} = 2 \sum_j \left(p_{j|i} - q_{j|i} + p_{i|j} - q_{i|j}\right) \left(y_i - y_j\right) $$</code></p>
<h3 id="t-sne">t-SNE</h3>
<p>SNE 为我们提供了一种很好的降维方法，但是其本身也存在一定的问题，主要有如下两点：</p>
<ul>
<li><strong>不对称问题</strong>：损失函数中的 KL 散度具有不对称性，导致 SNE 更加关注局部结构，相比忽略了全局结构。</li>
<li><strong>拥挤问题</strong>：从高维空间映射到低维空间后，不同类别的簇容易挤在一起，无法较好地区分开。</li>
</ul>
<p>针对这两个问题，Maaten 等人又提出了 t-SNE 算法对其进行优化 <sup id="fnref:17"><a href="#fn:17" class="footnote-ref" role="doc-noteref">17</a></sup>。</p>
<p>针对不对称问题，Maaten 采用的方法是用联合概率分布来替代条件概率分布。高维控件中的联合概率分布为 <code>$P$</code>，低维空间中的联合概率分布为 <code>$Q$</code>，则对于任意的 <code>$i, j$</code>，有 <code>$p_{ij} = p_{ji}, q_{ij} = q_{ji}$</code>，联合概率定义为：</p>
<p><code>$$ \begin{align} p_{ij} &amp;= \dfrac{\exp \left(\dfrac{- \left\lVert x_i - x_j \right\rVert ^ 2}{2 \sigma^2}\right)}{\sum_{k \neq l} \exp \left(\dfrac{- \left\lVert x_k - x_l \right\rVert ^ 2}{2 \sigma^2}\right)} \\ q_{ij} &amp;= \dfrac{\exp \left(- \left\lVert y_i - y_j \right\rVert ^ 2\right)}{\sum_{k \neq l} \exp \left(- \left\lVert y_k - y_l \right\rVert ^ 2\right)} \end{align} $$</code></p>
<p>虽然这样保证了对称性，但是对于异常的情况，例如数据点 <code>$x_i$</code> 在距离群簇较远，则 <code>$\lVert x_i − x_j \rVert ^ 2$</code> 的值会很大，而 <code>$p_{ij}$</code> 会相应变得非常小，也就是说 <code>$x_i$</code> 的位置很远这件事情对损失函数影响很小 (惩罚过小)，那这个点在低维空间中将无法从其他点中区分出来。因此 Maaten 提出了对称的条件概率来重新定义上述联合概率 <code>$p_{ij}$</code> ，对于数量为 <code>$n$</code> 的数据点，新的概率公式是：</p>
<p><code>$$ p_{ij} = \dfrac{p_{j|i} + p_{i|j}}{2n} $$</code></p>
<p>则损失函数更新为：</p>
<p><code>$$ C = \sum_{i} KL \left(P_i \Vert Q_i\right) = \sum_{i} \sum_{j} p_{ij} \log \dfrac{p_{ij}}{q_{ij}} $$</code></p>
<p>梯度更新为：</p>
<p><code>$$ \dfrac{\delta C}{\delta y_i} = 4 \sum_j \left(p_{ij} - q_{ij}\right) \left(y_i - y_j\right) $$</code></p>
<p>拥挤问题 (Crowding) 就是从高维空间映射到低维空间后，不同类别的簇容易挤在一起，不能很好的地区分开。t-SNE 则是利用了 t 分布重新定义 <code>$q_{ij}$</code>，t 分布具有长尾特性，相比于高斯分布，其在尾部趋向于 0 的速度更慢，对比如图所示：</p>
<p><img src="/images/cn/2018-03-16-manifold-learning/gassion-t-comparison.png" alt=""></p>
<p>利用 t 分布重新定义的 <code>$q_{ij}$</code> 为：</p>
<p><code>$$ q_{ij} = \dfrac{\left(1 + \lVert y_i - y_j \rVert ^ 2\right) ^ {-1}}{\sum_{k \neq l} \left(1 + \lVert y_k - y_l \rVert ^ 2\right) ^ {-1}} $$</code></p>
<p>梯度更新为：</p>
<p><code>$$ \dfrac{\delta C}{\delta y_i} = 4 \sum_j \left(p_{ij} - q_{ij}\right) \left(y_i - y_j\right) \left(1 + \lVert y_i - y_j \rVert ^ 2\right) ^ {-1} $$</code></p>
<p>利用 t-SNE 对 MNIST 数据集进行降维可视化结果如下：</p>
<p><img src="/images/cn/2018-03-16-manifold-learning/mnist-t-sne.png" alt=""></p>
<h2 id="方法比较">方法比较</h2>
<p>针对上述的若干算法，我们简单列举一下每个算法的优缺点</p>
<table>
  <thead>
      <tr>
          <th>方法</th>
          <th>优点</th>
          <th>缺点</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Isomap</td>
          <td>1. 保持流形的全局几何结构 <br/> 2. 适用于学习内部平坦的低维流形</td>
          <td>1. 对于数据量较大的情况，计算效率过低 <br/> 2. 不适于学习有较大内在曲率的流形</td>
      </tr>
      <tr>
          <td>LLE</td>
          <td>1. 可以学习任意维的局部线性的低维流形 <br/> 2. 归结为稀疏矩阵特征值计算，计算复杂度相对较小</td>
          <td>1. 所学习的流形只能是不闭合的 <br/> 2. 要求样本在流形上是稠密采样的 <br/> 3.对样本中的噪声和邻域参数比较敏感</td>
      </tr>
      <tr>
          <td>LE</td>
          <td>1. 是局部非线性方法，与谱图理论有很紧密的联系 <br/> 2. 通过求解稀疏矩阵的特征值问题解析地求出整体最优解，效率非常高 <br/> 3. 使原空间中离得很近的点在低维空间也离得很近，可以用于聚类</td>
          <td>1. 对算法参数和数据采样密度较敏感 <br/> 2. 不能有效保持流形的全局几何结构</td>
      </tr>
      <tr>
          <td>SNE, t-SNE</td>
          <td>1. 非线性降维效果相较上述方法较好</td>
          <td>1. 大规模高维数据时，效率显著降低 <br/> 2. 参数对不同数据集较为敏感</td>
      </tr>
  </tbody>
</table>
<p>对于<strong>瑞士卷 (Swiss Roll)</strong>，<strong>S 形曲线 (S Curve)</strong> 和<strong>切断的球面 (Severed Sphere)</strong>，我们利用不同的流形算法对其进行降维，可视化的对比结果如下面 3 张图所示，图中同时标注了算法的运行时间，实现主要参照了 scikit-learn 关于流形学习算法的比较 <sup id="fnref:18"><a href="#fn:18" class="footnote-ref" role="doc-noteref">18</a></sup>。</p>
<p><img src="/images/cn/2018-03-16-manifold-learning/s-curve.png" alt=""></p>
<p><img src="/images/cn/2018-03-16-manifold-learning/swiss-roll.png" alt=""></p>
<p><img src="/images/cn/2018-03-16-manifold-learning/severed-sphere.png" alt=""></p>
<p>文中相关图片绘制实现详见<a href="https://github.com/leovan/leovan.me/tree/master/scripts/cn/2018-03-16-manifold-learning">代码</a>，本文部分内容参考了<strong>流形学习专题介绍</strong> <sup id="fnref:19"><a href="#fn:19" class="footnote-ref" role="doc-noteref">19</a></sup>， <strong>流形学习</strong> <sup id="fnref:20"><a href="#fn:20" class="footnote-ref" role="doc-noteref">20</a></sup>，<strong>Chrispher</strong> <sup id="fnref:21"><a href="#fn:21" class="footnote-ref" role="doc-noteref">21</a></sup> 的博客和 <strong>bingo</strong> <sup id="fnref:22"><a href="#fn:22" class="footnote-ref" role="doc-noteref">22</a></sup> 的博客。</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Jolliffe, Ian T. &ldquo;Principal component analysis and factor analysis.&rdquo; <em>Principal component analysis.</em> Springer, New York, NY, 1986. 115-128.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Balakrishnama, Suresh, and Aravind Ganapathiraju. &ldquo;Linear discriminant analysis-a brief tutorial.&rdquo; <em>Institute for Signal and information Processing</em> 18 (1998): 1-8.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>Cox, Trevor F., and Michael AA Cox. <em>Multidimensional scaling.</em> CRC press, 2000.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>Tenenbaum, Joshua B., Vin De Silva, and John C. Langford. &ldquo;A global geometric framework for nonlinear dimensionality reduction.&rdquo; <em>Science</em> 290.5500 (2000): 2319-2323.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p>Roweis, Sam T., and Lawrence K. Saul. &ldquo;Nonlinear dimensionality reduction by locally linear embedding.&rdquo; <em>Science</em> 290.5500 (2000): 2323-2326.&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6">
<p>Belkin, Mikhail, and Partha Niyogi. &ldquo;Laplacian eigenmaps for dimensionality reduction and data representation.&rdquo; <em>Neural computation</em> 15.6 (2003): 1373-1396.&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:7">
<p>梅加强. 流形与几何初步&#160;<a href="#fnref:7" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:8">
<p><a href="https://zh.wikipedia.org/zh-hans/%E6%B5%81%E5%BD%A2">https://zh.wikipedia.org/zh-hans/流形</a>&#160;<a href="#fnref:8" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:9">
<p>pluskid. <a href="http://blog.pluskid.org/?p=533">浅谈流形学习</a>&#160;<a href="#fnref:9" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:10">
<p><a href="https://en.wikipedia.org/wiki/Whitney_embedding_theorem">https://en.wikipedia.org/wiki/Whitney_embedding_theorem</a>&#160;<a href="#fnref:10" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:11">
<p>Silva, Vin D., and Joshua B. Tenenbaum. &ldquo;Global versus local methods in nonlinear dimensionality reduction.&rdquo; <em>Advances in neural information processing systems.</em> 2003.&#160;<a href="#fnref:11" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:12">
<p>何晓群. 多元统计分析&#160;<a href="#fnref:12" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:13">
<p>Donoho, David L., and Carrie Grimes. &ldquo;Hessian eigenmaps: Locally linear embedding techniques for high-dimensional data.&rdquo; <em>Proceedings of the National Academy of Sciences</em> 100.10 (2003): 5591-5596.&#160;<a href="#fnref:13" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:14">
<p>Zhang, Zhenyue, and Jing Wang. &ldquo;MLLE: Modified locally linear embedding using multiple weights.&rdquo; <em>Advances in neural information processing systems.</em> 2007.&#160;<a href="#fnref:14" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:15">
<p>Zhang, Zhenyue, and Hongyuan Zha. &ldquo;Principal manifolds and nonlinear dimensionality reduction via tangent space alignment.&rdquo; <em>SIAM journal on scientific computing</em> 26.1 (2004): 313-338.&#160;<a href="#fnref:15" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:16">
<p>Hinton, Geoffrey E., and Sam T. Roweis. &ldquo;Stochastic neighbor embedding.&rdquo; <em>Advances in neural information processing systems.</em> 2003.&#160;<a href="#fnref:16" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:17">
<p>Maaten, Laurens van der, and Geoffrey Hinton. &ldquo;Visualizing data using t-SNE.&rdquo; <em>Journal of machine learning research</em> 9.Nov (2008): 2579-2605.&#160;<a href="#fnref:17" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:18">
<p><a href="http://scikit-learn.org/stable/auto_examples/manifold/plot_compare_methods.html">http://scikit-learn.org/stable/auto_examples/manifold/plot_compare_methods.html</a>&#160;<a href="#fnref:18" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:19">
<p>王瑞平. 流形学习专题介绍&#160;<a href="#fnref:19" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:20">
<p>何晓飞. 流形学习&#160;<a href="#fnref:20" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:21">
<p><a href="http://www.datakit.cn/blog/2017/02/05/t_sne_full.html">http://www.datakit.cn/blog/2017/02/05/t_sne_full.html</a>&#160;<a href="#fnref:21" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:22">
<p><a href="http://bindog.github.io/blog/2016/06/04/from-sne-to-tsne-to-largevis/">http://bindog.github.io/blog/2016/06/04/from-sne-to-tsne-to-largevis/</a>&#160;<a href="#fnref:22" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>

        ]]></description></item><item><title>Ising 模型，Hopfield 网络和受限的玻尔兹曼机 (Ising, Hopfield and RBM)</title><link>https://zeqiang.fun/cn/2018/01/ising-hopfield-and-rbm/</link><pubDate>Wed, 17 Jan 2018 00:00:00 +0000</pubDate><guid>https://zeqiang.fun/cn/2018/01/ising-hopfield-and-rbm/</guid><description><![CDATA[
        <p><code>$\renewcommand{\sign}{\operatorname{sign}}$</code></p>
<h2 id="ising-模型">Ising 模型</h2>
<p><a href="https://zh.wikipedia.org/zh/%E6%98%93%E8%BE%9B%E6%A8%A1%E5%9E%8B">Ising 模型</a>最早是由物理学家威廉·冷次在 1920 年发明的，他把该模型当成是一个给他学生恩斯特·易辛的问题。易辛在他一篇 1924 年的论文 <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> 中求得了一维易辛模型的解析解，并且证明它不会产生相变。 二维方晶格易辛模型相对于一维的难出许多，因此其解析的描述在一段时间之后才在 1943 年由拉斯·昂萨格给出 <sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>。</p>
<p>Ising 模型假设铁磁物质是由一堆规则排列的小磁针构成，每个磁针只有上下两个方向。相邻的小磁针之间通过能量约束发生相互作用，同时受到环境热噪声的干扰而发生磁性的随机转变。涨落的大小由关键的温度参数决定，温度越高，随机涨落干扰越强，小磁针越容易发生无序而剧烈地状态转变，从而让上下两个方向的磁性相互抵消，整个系统消失磁性，如果温度很低，则小磁针相对宁静，系统处于能量约束高的状态，大量的小磁针方向一致，铁磁系统展现出磁性。而当系统处于临界温度 <code>$T_C$</code> 时，Ising 模型表现出一系列幂律行为和自相似现象 <sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>。</p>
<p>由于 Ising 模型的高度抽象，可以很容易地将它应用到其他领域之中。例如，将每个小磁针比喻为某个村落中的村民，而将小磁针上下的两种状态比喻成个体所具备的两种政治观点，相邻小磁针之间的相互作用比喻成村民之间观点的影响，环境的温度比喻成每个村民对自己意见不坚持的程度，这样 Ising 模型就可以建模该村落中不同政治见解的动态演化。在社会科学中，人们已经将 Ising 模型应用于股票市场、种族隔离、政治选择等不同的问题。另一方面，如果将小磁针比喻成神经元细胞，向上向下的状态比喻成神经元的激活与抑制，小磁针的相互作用比喻成神经元之间的信号传导，那么，Ising 模型的变种还可以用来建模神经网络系统，从而搭建可适应环境、不断学习的机器，例如 Hopfield 网络或 Boltzmann 机。</p>
<p>考虑一个二维的情况</p>
<p><img src="/images/cn/2018-01-17-ising-hopfield-and-rbm/ising-model.svg" alt=""></p>
<p>如图所示，每个节点都有两种状态 <code>$s_i \in \{+1, -1\}$</code>，则我们可以定义这个系统的能量为</p>
<p><code>$$ E = -H \sum_{i=1}^{N}{s_i} - J \sum_{&lt;i, j&gt;}{s_i s_j} $$</code></p>
<p>其中 <code>$H$</code> 为外界磁场的强度，<code>$J$</code> 为能量耦合常数，<code>$\sum_{&lt;i, j&gt;}$</code>表示对于相邻的两个节点的函数值求和。因此，可以得出</p>
<ol>
<li>当每个节点的方向同外部磁场一致时，系统能量越小；反之系统能量越大。</li>
<li>对于 <code>$J &gt; 0$</code>，当相邻的节点方向相同时，系统能量越小；反之系统能量越大。</li>
</ol>
<p>对于整个系统的演变，除了系统的总能量以外，还受到节点所处环境的热噪声影响。我们利用温度 <code>$T$</code> 表示环境对节点的影响，当 <code>$T$</code> 越高时，节点状态发生变化的可能性越大。此时，则有两种力量作用在每个节点上</p>
<ol>
<li>节点邻居和外部磁场的影响，这种影响使得当前节点尽可能的同其邻居和外部磁场保持一致，即尽可能是系统的总能量达到最小。</li>
<li>环境的影响，这种影响使得每个节点的状态以一定的概率发生随机变化。</li>
</ol>
<p>不难想像，当 <code>$T = 0$</code> 时，节点状态完全受其邻居和外部磁场影响，当 <code>$J = 0, H = 0$</code> 时，节点处于完全的随机状态。</p>
<p>对于 Ising 模型，我们利用蒙特卡罗方法进行模拟。初始化系统状态为 <code>$s_i^{\left(0\right)}$</code>，对于任意时刻 <code>$t$</code>，对其状态 <code>$s_i^{\left(t\right)}$</code>进行一个改变，将其中一个节点变为相反的状态，得到新的状态 <code>$s'_i$</code></p>
<p><code>$$ s_i^{\left(t+1\right)} = \begin{cases} s'_i &amp; \text{with probablity of } \mu \\ s_i^{\left(t\right)} &amp; \text{with probablity of } 1-\mu \end{cases} $$</code></p>
<p>其中 <code>$\mu = \min\left\lbrace\dfrac{e^{E\left(s_i^{\left(t\right)}\right) - E\left(s'_i\right)}}{kT}, 1\right\rbrace$</code> 表示接受转移的概率；<code>$k \approx 1.38 \times 10^{23}$</code> 为玻尔兹曼常数。我们利用蒙特卡罗方法对其进行模拟 <code>$T = 4J/k$</code>的情况，我们分别保留第 <code>$0, 1, 5, 50, 500, 5000$</code> 步的模拟结果</p>
<pre><code class="language-r"># 每一轮状态转移
each_round &lt;- function(current_matrix, ising_config) {
    n_row &lt;- nrow(current_matrix)
    n_col &lt;- ncol(current_matrix)
    
    for (i in 1:n_row) {
        for (j in 1:n_col) {
            current_row &lt;- sample(1:n_row, 1)
            current_col &lt;- sample(1:n_col, 1)
            s &lt;- current_matrix[current_row, current_col]
            e &lt;- -(current_matrix[(current_row-1-1)%%n_row+1, current_col] +
                current_matrix[current_row, (current_col-1-1)%%n_col+1] +
                current_matrix[(current_row+1)%%n_row, current_col] +
                current_matrix[current_row, (current_col+1)%%n_col]) *
                s * ising_config$j
            mu &lt;- min(exp((e + e) / (ising_config$k * ising_config$t)), 1)
            mu_random &lt;- runif(1)
            
            if (mu_random &lt; mu) {
                s &lt;- -1 * s
            }
            
            current_matrix[current_row, current_col] &lt;- s
        }
    }
    
    current_matrix
}

# Ising 模拟
ising_simulation &lt;- function(N, iter, ising_config, saved_steps) {
    set.seed(112358)
    current_matrix &lt;- matrix(sample(0:1, N^2, replace = T), N, N)*2-1
    saved_matrix &lt;- list()
    
    if (0 %in% saved_steps) {
        saved_matrix &lt;- c(saved_matrix, list(current_matrix))
    }
    
    for (i in 1:iter) {
        if (i %in% saved_steps) {
            saved_matrix &lt;- c(saved_matrix, list(current_matrix))
        }
        
        current_matrix &lt;- each_round(current_matrix, ising_config)
        
        if (i %% 1000 == 0) {
            cat(paste0(&quot;Steps: &quot;, i, '\n'))
        }
    }
    
    saved_matrix
}

# T = 4J/K，方便模拟取 j = 1, k = 1, t = 4
ising_config &lt;- list(j = 1, k = 1, t = 4)
diff_steps_matrix &lt;- ising_simulation(100, 5000, ising_config,
                                      c(0, 1, 5, 50, 500, 5000))
</code></pre>
<p>模拟结果可视化效果如图所示</p>
<p><img src="/images/cn/2018-01-17-ising-hopfield-and-rbm/ising-different-steps.png" alt=""></p>
<p>对于二维的 Ising 模型，存在一个相变点，在相变点上的温度 <code>$T_c$</code> 满足</p>
<p><code>$$ \sinh\left(\dfrac{2J_1}{kT_c}\right) \sinh\left(\dfrac{2J_2}{kT_c}\right) = 1 $$</code></p>
<p>若 <code>$J_1 = J_2$</code>，则</p>
<p><code>$$ T_c = \dfrac{2J}{k \ln\left(1 + \sqrt{2}\right)} \approx 2.27 \dfrac{J}{k} $$</code></p>
<p>称之为临界温度。当温度小于临界值的时候，Ising 模型中大多数节点状态相同，系统处于较为秩序的状态。当温度大于临界值的时候，大多数节点的状态较为混乱，系统处于随机的状态。而当温度接近临界的时候，系统的运行介于随机与秩序之间，也就是进入了混沌的边缘地带，这种状态称为临界状态。</p>
<p>我们模拟不同温度下，系统在运行 <code>$50$</code> 步时的状态</p>
<pre><code class="language-r">ising_config_t &lt;- c(1, 2, 2.27, 2.5, 3, 6)
diff_t_matrix &lt;- lapply(ising_config_t, function(t) {
    ising_config &lt;- list(j = 1, k = 1, t = t)
    ising_simulation(100, 50, ising_config, c(50))
})
</code></pre>
<p>模拟结果可视化效果如图所示</p>
<p><img src="/images/cn/2018-01-17-ising-hopfield-and-rbm/ising-different-t.png" alt=""></p>
<h2 id="hopfield-神经网络">Hopfield 神经网络</h2>
<p>Hopfield 神经网络 <sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> 是一种基于能量的反馈人工神经网络。Hopfield 神经网络分为离散型 (Discrete Hopfield Neural Network, DHNN) 和 连续性 (Continues Hopfield Neural Network, CHNN)。</p>
<h3 id="离散型-hopfield-神经网络">离散型 Hopfield 神经网络</h3>
<h4 id="网络结构">网络结构</h4>
<p>对于离散型 Hopfield 神经网络，其网络结果如下</p>
<p><img src="/images/cn/2018-01-17-ising-hopfield-and-rbm/hopfield-network.png" alt=""></p>
<p>对于具有 <code>$n$</code> 个神经元的网络，我们设 <code>$t$</code> 时刻的网络状态为 <code>$\boldsymbol{X}^{\left(t\right)} = \left(x_1^{\left(t\right)}, x_2^{\left(t\right)}, ..., x_n^{\left(t\right)}\right)^T$</code>，对于 <code>$t+1$</code> 时刻网络的状态</p>
<p><code>$$ x_i^{\left(t+1\right)} = f \left(net_i\right) $$</code></p>
<p>其中，DHNN 中 <code>$f$</code> 多为符号函数，即</p>
<p><code>$$ x_i = \sign \left(net_i\right) = \begin{cases} 1, net_i \geq 0 \\ -1, net_i &lt; 0 \end{cases} $$</code></p>
<p><code>$net_i$</code> 为一个节点的输入，为</p>
<p><code>$$ net_i = \sum_{j=1}^{n}{\left(w_{ij}x_j - T_i\right)} $$</code></p>
<p>其中 <code>$T_i$</code> 为每个神经元的阈值，对于 DHNN，一般有 <code>$w_{ii} = 0, w_{ij} = w_{ji}$</code>，当反馈网络稳定后，稳定后的状态即为网络的输出。网络的更新主要有两种状态，<strong>异步方式</strong>和<strong>同步方式</strong>。</p>
<p>对于异步方式的更新方法，每一次仅改变一个神经元 <code>$j$</code> 的状态，即</p>
<p><code>$$ x_i^{\left(t+1\right)} = \begin{cases} \sign\left(net_i^{\left(t\right)}\right), i = j \\ x_i^{\left(t\right)}, i \neq j \end{cases} $$</code></p>
<p>对于同步方式的更新方法，每一次需改变所有神经元的状态，即</p>
<p><code>$$ x_i^{\left(t+1\right)} = \sign\left(net_i^{\left(t\right)}\right) $$</code></p>
<h4 id="网络稳定性">网络稳定性</h4>
<p>我们可以将反馈网络看做一个非线性动力学系统，因此这个系统最后可能会收敛到一个稳态，或在有限状态之间振荡，亦或是状态为无穷多个即混沌现象。对于 DHNN 因为其网络状态是有限的，因此不会出现混沌的现象。若一个反馈网络达到一个稳态状态 <code>$\boldsymbol{X}$</code> 时，即 <code>$\boldsymbol{X}^{\left(t+1\right)} = \boldsymbol{X}^{\left(t\right)}$</code> ，则称这个状态为一个吸引子。在 Hopfield 网络结构和权重确定的情况下，其具有 <code>$M$</code> 个吸引子，因此我们可以认为这个网络具有存储 <code>$M$</code> 个记忆的能力。</p>
<p>设 <code>$\boldsymbol{X}$</code> 为网络的一个吸引子，权重矩阵 <code>$\boldsymbol{W}$</code> 是一个对称阵，则定义 <code>$t$</code> 时刻网络的能量函数为</p>
<p><code>$$ E\left(t\right) = -\dfrac{1}{2} \boldsymbol{X}^{\left(t\right)T} \boldsymbol{W} \boldsymbol{X}^{\left(t\right)} + \boldsymbol{X}^{\left(t\right)T} \boldsymbol{T} $$</code></p>
<p>则定义网络能量的变化量</p>
<p><code>$$ \Delta E\left(t\right) = E\left(t+1\right) - E\left(t\right) $$</code></p>
<p>则以<strong>异步更新</strong>方式，不难推导得出</p>
<p><code>$$ \begin{equation} \begin{split} \Delta E\left(t\right) = -\Delta x_i^{\left(t\right)} \left(\sum_{j=1}^{n}{\left(w_{ij}x_j - T_j\right)}\right) - \dfrac{1}{2} \Delta x_i^{\left(t\right)2} w_{ii} \end{split} \end{equation} $$</code></p>
<p>由于网络中的神经元不存在自反馈，即 <code>$w_{ii} = 0$</code>，则上式可以化简为</p>
<p><code>$$ \Delta E\left(t\right) = -\Delta x_i^{\left(t\right)} net_i^{\left(t\right)} $$</code></p>
<p>因此，对于如上的能量变化，可分为 3 中情况：</p>
<ol>
<li>当 <code>$x_i^{\left(t\right)} = -1, x_i^{\left(t+1\right)} = 1$</code> 时，<code>$\Delta x_i^{\left(t\right)} = 2, net_i^{\left(t\right)} \geq 0$</code>，则可得 <code>$\Delta E \left(t\right) \leq 0$</code>。</li>
<li>当 <code>$x_i^{\left(t\right)} = 1, x_i^{\left(t+1\right)} = -1$</code> 时，<code>$\Delta x_i^{\left(t\right)} = -2, net_i^{\left(t\right)} &lt; 0$</code>，则可得 <code>$\Delta E \left(t\right) &lt; 0$</code>。</li>
<li>当 <code>$x_i^{\left(t\right)} = x_i^{\left(t+1\right)}$</code> 时，<code>$\Delta x_i^{\left(t\right)} = 0$</code>，则可得 <code>$\Delta E \left(t\right) = 0$</code>。</li>
</ol>
<p>则对于任何情况，<code>$\Delta E \left(t\right) \leq 0$</code>，也就是说在网络不断变化的过程中，网络的总能量是一直下降或保持不变的，因此网络的能量最终会收敛到一个常数。</p>
<p>设 <code>$\boldsymbol{X}'$</code> 为吸引子，对于异步更新方式，若<strong>存在</strong>一个变换顺序，使得网络可以从状态 <code>$\boldsymbol{X}$</code> 转移到 <code>$\boldsymbol{X}'$</code>，则称 <code>$\boldsymbol{X}$</code> 弱吸引到 <code>$\boldsymbol{X}'$</code>，这些 <code>$\boldsymbol{X}$</code> 的集合称之为 <code>$\boldsymbol{X}$</code> 的弱吸引域；若对于<strong>任意</strong>变换顺序，都能够使得网络可以从状态 <code>$\boldsymbol{X}$</code> 转移到 <code>$\boldsymbol{X}'$</code>，则称 <code>$\boldsymbol{X}$</code> 强吸引到 <code>$\boldsymbol{X}'$</code>，对于这些 <code>$\boldsymbol{X}$</code> 称之为 <code>$\boldsymbol{X}$</code> 的强吸引域。</p>
<p>对于 Hopfield 网络的权重，我们利用 Hebbian 规则进行设计。Hebbian 规则认为如果两个神经元同步激发，则它们之间的权重增加；如果单独激发，则权重减少。则对于给定的 <code>$p$</code> 个模式样本 <code>$\boldsymbol{X}^k, k = 1, 2, ..., p$</code>，其中 <code>$x \in \{-1, 1\}^n$</code> 且样本之间两两正交，则权重计算公式为</p>
<p><code>$$ w_{ij} = \dfrac{1}{n} \sum_{k=1}^{p}{x_i^k x_j^k} $$</code></p>
<p>则对于给定的样本 <code>$\boldsymbol{X}$</code> 确定为网络的吸引子，但对于有些非给定的样本也可能是网络的吸引子，这些吸引子称之为伪吸引子。以上权重的计算是基于两两正交的样本得到的，但真实情况下很难保证样本两两正交，对于非正交的模式，网络的存储能力则会大大下降。根据 Abu-Mostafa<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup> 的研究表明，当模式的数量 <code>$p$</code> 大于 <code>$0.15 n$</code> 时，网络的推断就很可能出错，也就是结果会收敛到伪吸引子上。</p>
<h4 id="示例">示例</h4>
<p>我们通过一个手写数字识别的例子介绍一些 Hopfield 网络的功能，我们存在如下 10 个数字的图片，每张为像素 16*16 的二值化图片，其中背景色为白色，前景色为黑色 (每个图片的名称为 <code>num.png</code>，图片位于 <code>/images/cn/2018-01-17-ising-hopfield-and-rbm</code> 目录)。</p>
<p><img src="/images/cn/2018-01-17-ising-hopfield-and-rbm/digits.png" alt=""></p>
<p>首先我们载入每张图片的数据</p>
<pre><code class="language-r">library(EBImage)

# 载入数据
digits &lt;- lapply(0:9, function(num) {
    readImage(paste0(num, '.png'))
})

# 转换图像为 16*16 的一维向量
# 将 (0, 1) 转换为 (-1, 1)
digits_patterns &lt;- lapply(digits, function(digit) {
    pixels &lt;- c(digit)
    pixels * 2 - 1
})
</code></pre>
<p>接下来利用这 10 个模式训练一个 Hopfield 网络</p>
<pre><code class="language-r">#' 训练 Hopfield 网络
#' 
#' @param n 网络节点个数
#' @param pattern_list 模式列表
#' @return 训练好的 Hopfield 网络
train_hopfield &lt;- function(n, pattern_list) {
    weights &lt;- matrix(rep(0, n*n), n, n)
    n_patterns &lt;- length(pattern_list)
    
    for (i in 1:n_patterns) {
        weights &lt;- weights + pattern_list[[i]] %o% pattern_list[[i]]
    }
    diag(weights) &lt;- 0
    weights &lt;- weights / n_patterns
    
    list(weights = weights, n = n)
}

# 训练 Hopfield 网络
digits_hopfield_network &lt;- train_hopfield(16*16, digits_patterns)
</code></pre>
<p>为了测试 Hopfiled 网络的记忆能力，我们利用 10 个模式生成一些测试数据，我们分别去掉图像的右边或下边的 5 个像素，生成新的 20 张测试图片</p>
<pre><code class="language-r"># 构造测试数据
digits_test_remove_right &lt;- lapply(0:9, function(num) {
    digit_test &lt;- digits[[num+1]]
    digit_test[12:16, ] &lt;- 1
    digit_test
})
digits_test_remove_bottom &lt;- lapply(0:9, function(num) {
    digit_test &lt;- digits[[num+1]]
    digit_test[, 12:16] &lt;- 1
    digit_test
})
digits_test &lt;- c(digits_test_remove_right, digits_test_remove_bottom)

# 转换图像为 16*16 的一维向量
# 将 (0, 1) 转换为 (-1, 1)
digits_test_patterns &lt;- lapply(digits_test, function(digit) {
    pixels &lt;- c(digit)
    pixels * 2 - 1
})
</code></pre>
<p><img src="/images/cn/2018-01-17-ising-hopfield-and-rbm/digits-test.png" alt=""></p>
<p>我们利用训练好的 Hopfield 网络运行测试数据，我们迭代 300 次并保存最后的网络输出</p>
<pre><code class="language-r">#' 运行 Hopfiled 网络
#' @param hopfield_network 训练好的 Hopfield 网络
#' @param pattern 输入的模式
#' @param max_iter 最大迭代次数
#' @param save_history 是否保存状态变化历史
#' @return 最终的模式 (以及历史模式)
run_hopfield &lt;- function(hopfield_network, pattern,
                         max_iter = 100, save_history = T) {
    last_pattern &lt;- pattern
    history_patterns &lt;- list()
    
    for (iter in 1:max_iter) {
        current_pattern &lt;- last_pattern
        
        i &lt;- round(runif(1, 1, hopfield_network$n))
        net_i &lt;- hopfield_network$weights[i, ] %*% current_pattern
        current_pattern[i] &lt;- ifelse(net_i &lt; 0, -1, 1)
        
        if (save_history) {
            history_patterns[[iter]] &lt;- last_pattern
        }
        
        last_pattern &lt;- current_pattern
    }
    
    list(history_patterns = history_patterns,
         final_pattern = last_pattern)
}

# 运行 Hopfield 网络，获取测试数据结果
digits_test_results_patterns &lt;- lapply(digits_test_patterns,
                                       function(pattern) {
    run_hopfield(digits_hopfield_network, pattern, max_iter = 300)
})

# 转换测试数据结果为图片
digits_test_results &lt;- lapply(digits_test_results_patterns,
                              function(result) {
    each_dim &lt;- sqrt(digits_hopfield_network$n)
    Image((result$final_pattern + 1) / 2,
          dim = c(each_dim, each_dim),
          colormode = 'Grayscale')
})
</code></pre>
<p>网络变换过程中，图像的变换如图所示</p>
<p><img src="/images/cn/2018-01-17-ising-hopfield-and-rbm/digits-test-results.gif" alt=""></p>
<p>最终网络的输出如图所示</p>
<p><img src="/images/cn/2018-01-17-ising-hopfield-and-rbm/digits-test-results.png" alt=""></p>
<p>从结果中可以看出，部分测试图片还是得到了比较好的恢复，但如上文所说，由于我们给定的模式之间并不是两两正交的，因此，网络的推断就很可能出错 (例如：数字 5 恢复的结果更像 9 多一些)，甚至结果会收敛到伪吸引子上。</p>
<h3 id="连续型-hopfield-神经网络">连续型 Hopfield 神经网络</h3>
<h4 id="网络结构-1">网络结构</h4>
<p>连续型 Hopfield 网络相比于离散型 Hopfield 网络的主要差别在于：</p>
<ol>
<li>网络中所有的神经元随时间 <code>$t$</code> 同时更新，网络状态随时间连续变化。</li>
<li>神经元的状态转移函数为一个 S 型函数，例如
<code>$$ v_i = f\left(u_i\right) = \dfrac{1}{1 + e^{\dfrac{-2 u_i}{\gamma}}} = \dfrac{1}{2} \left(1 + \tanh \dfrac{u_i}{\gamma}\right) $$</code>
其中，<code>$v_i$</code> 表示一个神经元的输出，<code>$u_i$</code> 表示一个神经元的输入。</li>
</ol>
<p>对于理想情况，网络的能量函数可以写为<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup></p>
<p><code>$$ E = -\dfrac{1}{2} \sum_{i=1}^{n}{\sum_{j=1}^{n}{w_{ij} v_i v_j}} - \sum_{i=1}^{n} v_i I_i $$</code></p>
<p>可以得出，随着网络的演变，网络的总能量是降低的，随着网络中节点的不断变化，网络最终收敛到一个稳定的状态。</p>
<h4 id="tsp-问题求解">TSP 问题求解</h4>
<p>旅行推销员问题 (Travelling salesman problem, TSP) 是指给定一系列城市和每对城市之间的距离，求解访问每一座城市一次并回到起始城市的最短路径 <sup id="fnref:7"><a href="#fn:7" class="footnote-ref" role="doc-noteref">7</a></sup>。TSP 问题是一个 NP-hard 问题 <sup id="fnref:8"><a href="#fn:8" class="footnote-ref" role="doc-noteref">8</a></sup>。</p>
<p>对于 TSP 问题，我们给定一个城市指之间的距离矩阵</p>
<p><code>$$ D = \left\lgroup \begin{array}{cccc} d_{11} &amp; d_{12} &amp; \cdots &amp; d_{1n} \\ d_{21} &amp; d_{22} &amp; \cdots &amp; d_{2n} \\ \vdots &amp; \vdots &amp;        &amp; \vdots \\ d_{n1} &amp; d_{n2} &amp; \cdots &amp; d_{nn} \end{array} \right\rgroup $$</code></p>
<p>其中 <code>$d_{ij} = d_{ji}, i \neq j$</code> 表示城市 <code>$i$</code> 和城市 <code>$j$</code> 之间的距离，<code>$d_{ij} = 0, i = j$</code>。TSP 问题的优化目标是找到一条路径访问每一座城市一次并回到起始城市，我们利用一个矩阵表示访问城市的路径</p>
<p><code>$$ V = \left\lgroup \begin{array}{cccc} v_{11} &amp; v_{12} &amp; \cdots &amp; v_{1n} \\ v_{21} &amp; v_{22} &amp; \cdots &amp; v_{2n} \\ \vdots &amp; \vdots &amp;        &amp; \vdots \\ v_{n1} &amp; v_{n2} &amp; \cdots &amp; v_{nn} \end{array} \right\rgroup $$</code></p>
<p>其中 <code>$v_{xi} = 1$</code> 表示第 <code>$i$</code> 次访问城市 <code>$x$</code>，因此对于矩阵 <code>$V$</code>，其每一行每一列仅有一个元素值为 <code>$1$</code>，其他元素值均为 <code>$0$</code>。</p>
<p>对于 TSP 问题，我们可以得到如下约束条件</p>
<ul>
<li>城市约束</li>
</ul>
<p>因为每个城市只能访问一次，因此对于第 <code>$x$</code> 行仅能有一个元素是 <code>$1$</code>，其他均为 <code>$0$</code>，即任意两个相邻元素的乘积为 <code>$0$</code></p>
<p><code>$$ \sum_{i=1}^{n-1}{\sum_{j=i+1}^{n}{v_{xi}v_{xj}}} = 0 $$</code></p>
<p>则对于城市约束，我们得到该约束对应的能量分量为</p>
<p><code>$$ E_1 = \dfrac{1}{2} A \sum_{x=1}^{n}{\sum_{i=1}^{n-1}{\sum_{j=i+1}^{n}{v_{xi}v_{xj}}}} $$</code></p>
<ul>
<li>时间约束</li>
</ul>
<p>因为每一时刻仅能够访问一个城市，因此对于第 <code>$i$</code> 行仅能有一个元素是 <code>$1$</code>，其他均为 <code>$0$</code>，即任意两个相邻元素的乘积为 <code>$0$</code></p>
<p><code>$$ \sum_{x=1}^{n-1}{\sum_{y=x+1}^{n}{v_{xi}v_{yi}}} = 0 $$</code></p>
<p>则对于时间约束，我们得到该约束对应的能量分量为</p>
<p><code>$$ E_2 = \dfrac{1}{2} B \sum_{i=1}^{n}{\sum_{x=1}^{n-1}{\sum_{y=x+1}^{n}{v_{xi}v_{yi}}}} $$</code></p>
<ul>
<li>有效性约束</li>
</ul>
<p>当矩阵 <code>$V$</code> 中所有的元素均为 <code>$0$</code> 的时候，可得 <code>$E_1 = 0, E_2 = 0$</code>，但显然这并不是一个有效的路径，因此我们需要保证矩阵 <code>$V$</code> 中元素值为 <code>$1$</code> 的个数为 <code>$n$</code>，即</p>
<p><code>$$ \sum_{x=1}^{n}{\sum_{i=1}^{n}{v_{xi}}} = n $$</code></p>
<p>则对于有效性约束，我们得到该约束对应的能量分量为</p>
<p><code>$$ E_3 = \dfrac{1}{2} C \left(\sum_{x=1}^{n}{\sum_{i=1}^{n}{v_{xi}}} - n\right)^2 $$</code></p>
<ul>
<li>路径长度约束</li>
</ul>
<p>如上三个约束仅能够保证我们的路径是有效的，但并不一定是最优的。根绝 TSP 问题的优化目标，我们需要引入一个反映路径长度的能量分量，并保证该能量分量随着路径长度的减小而减小。访问两个城市 <code>$x, y$</code> 有两种形式，<code>$x \to y$</code> 或 <code>$y \to x$</code>，如果城市 <code>$x$</code> 和城市 <code>$y$</code> 在旅行中顺序相邻，则 <code>$v_{xi}v_{y,i+1} = 1, v_{xi}v_{y,i-1} = 0$</code>，反之亦然。则反映路径长度的能量分量可以定义为</p>
<p><code>$$ E_4 = \dfrac{1}{2} D \sum_{x=1}^{n}{\sum_{y=1}^{n}{\sum_{i=1}^{n}{d_{xy}\left(v_{xi}v_{y,i+1} + v_{xi}v_{y,i-1}\right)}}} $$</code></p>
<p>综上所述，TSP 问题的能量函数定义为</p>
<p><code>$$ E = E_1 + E_2 + E_3 + E_4 $$</code></p>
<p>其中，<code>$A, B, C, D &gt; 0$</code> 分别为每个能量分量的权重。针对这样的能量函数，我们可得对应神经元 <code>$x_i$</code> 和 <code>$y_i$</code> 之间的权重为</p>
<p><code>$$ \begin{equation} \begin{split} w_{x_i, y_i} = &amp;-2A \delta_{xy} \left(1-\delta_{xy}\right) - 2B \delta_{ij} \left(1-\delta_{xy}\right) \\ &amp;- 2C -2D d_{xy} \left(\delta_{j, i+1} + \delta_{i, j+1}\right) \end{split} \end{equation} $$</code></p>
<p>其中</p>
<p><code>$$ \delta_{xy} = \begin{cases} 1, x = y \\ 0, x \neq y \end{cases} ,  \delta_{ij} = \begin{cases} 1, i = j \\ 0, i \neq j \end{cases} $$</code></p>
<p>因此可以得到网络关于时间的导数</p>
<p><code>$$ \begin{equation} \begin{split} \dfrac{d u_{xi}}{d t} = &amp;-2A \sum_{j \neq i}^{n}{v_{xj}} - 2B \sum_{y \neq x}^{n}{v_{yi}} - 2C \left(\sum_{x=1}^{n}{\sum_{j=1}^{n}{v_{xj}}} - n\right) \\ &amp;- 2D \sum_{y \neq x}^{n}{d_{xy}\left(v_{y, i+1} + v_{y, i-1}\right)} - \dfrac{u_{xi}}{\tau} \end{split} \end{equation} $$</code></p>
<p>据此，我们以一个 10 个城市的数据为例，利用 CHNN 求解 TSP 问题，其中 10 个城市的座标为</p>
<table>
  <thead>
      <tr>
          <th style="text-align: center">城市</th>
          <th style="text-align: center">横座标</th>
          <th style="text-align: center">纵座标</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td style="text-align: center">A</td>
          <td style="text-align: center">0.4000</td>
          <td style="text-align: center">0.4439</td>
      </tr>
      <tr>
          <td style="text-align: center">B</td>
          <td style="text-align: center">0.2439</td>
          <td style="text-align: center">0.1463</td>
      </tr>
      <tr>
          <td style="text-align: center">C</td>
          <td style="text-align: center">0.1707</td>
          <td style="text-align: center">0.2293</td>
      </tr>
      <tr>
          <td style="text-align: center">D</td>
          <td style="text-align: center">0.2293</td>
          <td style="text-align: center">0.7610</td>
      </tr>
      <tr>
          <td style="text-align: center">E</td>
          <td style="text-align: center">0.5171</td>
          <td style="text-align: center">0.9414</td>
      </tr>
      <tr>
          <td style="text-align: center">F</td>
          <td style="text-align: center">0.8732</td>
          <td style="text-align: center">0.6536</td>
      </tr>
      <tr>
          <td style="text-align: center">G</td>
          <td style="text-align: center">0.6878</td>
          <td style="text-align: center">0.5219</td>
      </tr>
      <tr>
          <td style="text-align: center">H</td>
          <td style="text-align: center">0.8488</td>
          <td style="text-align: center">0.3609</td>
      </tr>
      <tr>
          <td style="text-align: center">I</td>
          <td style="text-align: center">0.6683</td>
          <td style="text-align: center">0.2536</td>
      </tr>
      <tr>
          <td style="text-align: center">J</td>
          <td style="text-align: center">0.6195</td>
          <td style="text-align: center">0.2634</td>
      </tr>
  </tbody>
</table>
<p>已知的最优路线为 <code>$A \to D \to E \to F \to G \to H \to I \to J \to B \to C \to A$</code>，最优路线的路径长度为 <code>$2.6907$</code>。我们使用如下参数求解 TSP 问题，初始化 <code>$u_{init} = -\dfrac{\gamma}{2} \ln\left(n - 1\right)$</code>，<code>$\gamma = 0.02$</code>，学习率 <code>$\alpha = 0.0001$</code>，神经元激活阈值 <code>$\theta = 0.7$</code>，<code>$\tau = 1$</code>，能量分量权重参数 <code>$A = 500, B = 500, C = 1000, D = 500$</code>，单次迭代最大次数为 1000，共模拟 100 次。</p>
<pre><code class="language-r"># 城市座标
cities &lt;- data.frame(
    l = LETTERS[1:10],
    x = c(0.4000, 0.2439, 0.1707, 0.2293, 0.5171,
          0.8732, 0.6878, 0.8488, 0.6683, 0.6195),
    y = c(0.4439, 0.1463, 0.2293, 0.7610, 0.9414,
          0.6536, 0.5219, 0.3609, 0.2536, 0.2634)
)

# 通过城市座标构建距离矩阵
distance_matrix &lt;- function(points) {
    n &lt;- nrow(points)
    d &lt;- matrix(rep(0, n^2), n, n)
    
    for (i in 1:n) {
        for (j in i:n) {
            distance &lt;- sqrt((points[i, ]$x - points[j, ]$x)^2 +
                                 (points[i, ]$y - points[j, ]$y)^2)
            d[i, j] &lt;- distance
            d[j, i] &lt;- distance
        }
    }
    
    d
}

# 结果约束校验
check_path_valid &lt;- function(v, n) {
    # 城市约束
    c1 &lt;- 0
    for (x in 1:n) {
        for (i in 1:(n-1)) {
            for (j in (i+1):n) {
                c1 &lt;- c1 + v[x, i] * v[x, j]
            }
        }
    }
    
    # 时间约束
    c2 &lt;- 0
    for (i in 1:n) {
        for (x in 1:(n-1)) {
            for (y in (x+1):n) {
                c2 &lt;- c2 + v[x, i] * v[y, i]
            }
        }
    }
    
    # 有效性约束
    c3 &lt;- sum(v)
    
    ifelse(c1 == 0 &amp; c2 == 0 &amp; c3 == n, T, F)
}

# 根据结果矩阵获取路径
v_to_path &lt;- function(v, n) {
    p &lt;- c()
    
    for (i in 1:n) {
        for (x in 1:n) {
            if (v[x, i] == 1) {
                p &lt;- c(p, x)
                break
            }
        }
    }
    
    p
}

# 计算结果矩阵的路径长度
path_distance &lt;- function(v, n, d) {
    p &lt;- v_to_path(v, n)
    p &lt;- c(p, p[1])
    distance &lt;- 0 
    for (i in 1:(length(p)-1)) {
        distance &lt;- distance + d[p[i], p[i+1]]
    }
    
    distance
}

# 构建 Hopfield 网络
tsp_chnn &lt;- function(d, n, gamma = 0.02, alpha = 0.0001,
                     theta = 0.7, tau = 1,
                     A = 500, B = 500, C = 1000, D = 500,
                     max_iter = 1000) {
    v &lt;- matrix(runif(n^2), n, n)
    u &lt;- matrix(rep(1, n^2), n, n) * (-gamma * log(n-1) / 2)
    du &lt;- matrix(rep(0, n^2), n, n)
    
    for (iter in 1:max_iter) {
        for (x in 1:n) {
            for (i in 1:n) {
                # E1
                e1 &lt;- 0
                for (j in 1:n) {
                    if (j != i) {
                        e1 &lt;- e1 + v[x, j]
                    }
                }
                e1 &lt;- -A * e1
                
                # E2
                e2 &lt;- 0
                for (y in 1:n) {
                    if (y != x) {
                        e2 &lt;- e2 + v[y, i]
                    }
                }
                e2 &lt;- -B * e2
                
                # E3
                e3 &lt;- -C * (sum(v) - n)
                
                # E4
                e4 &lt;- 0
                for (y in 1:n) {
                    if (y != x) {
                        e4 &lt;- e4 + d[x, y] *
                            (v[y, (i+1-1)%%n+1] + v[y, (i-1-1)%%n+1])
                    }
                }
                e4 &lt;- -D * e4
                
                du[x, i] &lt;- e1 + e2 + e3 + e4 - u[x, i] / tau
            }
        }
        
        u &lt;- u + alpha * du
        v &lt;- (1 + tanh(u / gamma)) / 2
        v &lt;- ifelse(v &gt;= theta, 1, 0)
    }
    
    v
}

# 利用 Hopfiled 网络求解 TSP 问题
set.seed(112358)

n &lt;- 10
d &lt;- distance_matrix(cities)

# 模拟 100 次并获取最终结果
tsp_solutions &lt;- lapply(1:100, function(round) {
    v &lt;- tsp_chnn(d, n)
    valid &lt;- check_path_valid(v, n)
    distance &lt;- ifelse(valid, path_distance(v, n, d), NA)
    
    list(round = round, valid = valid,
         distance = distance, v = v)
})

# 获取最优结果
best_tsp_solution &lt;- NA
for (tsp_solution in tsp_solutions) {
    if (tsp_solution$valid) {
        if (!is.na(best_tsp_solution)) {
            if (tsp_solution$distance &lt; best_tsp_solution$distance) {
                best_tsp_solution &lt;- tsp_solution
            }
        } else {
            best_tsp_solution &lt;- tsp_solution
        }
    }
}

# 可视化最优结果
best_tsp_solution_path &lt;- v_to_path(best_tsp_solution$v, n)
ordered_cities &lt;- cities[best_tsp_solution_path, ] %&gt;%
    mutate(ord = seq(1:10))

best_tsp_solution_path_p &lt;- ggplot(ordered_cities) +
    geom_polygon(aes(x, y), color = 'black', fill = NA) +
    geom_point(aes(x, y)) +
    geom_text(aes(x, y, label = l), vjust = -1) +
    geom_text(aes(x, y, label = ord), vjust = 2) +
    coord_fixed() + ylim(c(0, 1)) + xlim(c(0, 1)) +
    theme(axis.title = element_blank())
print(best_tsp_solution_path_p)
</code></pre>
<p><img src="/images/cn/2018-01-17-ising-hopfield-and-rbm/tsp-best-solution-path.png" alt=""></p>
<h2 id="受限的玻尔兹曼机-rbm">受限的玻尔兹曼机 (RBM)</h2>
<h3 id="网络结构及其概率表示">网络结构及其概率表示</h3>
<p><strong>受限的玻尔兹曼机</strong> (Restricted Boltzmann Machine, RBM) 或<strong>簧风琴</strong> (harmonium) 是由 Smolensky 与 1986年在<strong>玻尔兹曼机</strong> (Boltzmann Machine, BM) 基础上提出的一种随机神经网络 (Stochastic Neural Networks) <sup id="fnref:9"><a href="#fn:9" class="footnote-ref" role="doc-noteref">9</a></sup>。受限的玻尔兹曼机对于原始的玻尔兹曼机做了相应的限制，在其网络结构中包含<strong>可见节点</strong>和<strong>隐藏节点</strong>，并且<strong>可见节点</strong>和<strong>隐藏节点</strong>内部不允许存在连接，更加形象的可以将其理解为一个二分图。</p>
<p><img src="/images/cn/2018-01-17-ising-hopfield-and-rbm/rbm-network.svg" alt=""></p>
<p>对于二值版本的 RBM 而言，其中可见层 <code>$\mathbf{v} = \left(v_1, v_2, ..., v_{n_v}\right)^T$</code> 由 <code>$n_v$</code> 个二值随机变量构成；隐藏层 <code>$\mathbf{h} = \left(h_1, h_2, ..., h_{n_h}\right)^T$</code> 由 <code>$n_h$</code> 个二值随机变量构成。</p>
<p>RBM 同样作为一个基于能量的模型，其能量函数定义为：</p>
<p><code>$$ E \left(\boldsymbol{v}, \boldsymbol{h}\right) = -\sum_{i=1}^{n_v}{b_i v_i} -\sum_{j=1}^{n_h}{c_j h_j} - \sum_{i=1}^{n_v}{\sum_{j=1}^{n_h}{v_i w_{i,j} h_i}} $$</code></p>
<p>将其表示成矩阵向量的形式，可记为：</p>
<p><code>$$ E \left(\boldsymbol{v}, \boldsymbol{h}\right) = -\boldsymbol{b}^T \boldsymbol{v} - \boldsymbol{c}^T \boldsymbol{h} - \boldsymbol{v}^T \boldsymbol{W} \boldsymbol{h} $$</code></p>
<p>其中 <code>$\boldsymbol{b} \in \mathbb{R}^{n_v}$</code> 为可见层的偏置向量；<code>$\boldsymbol{c} \in \mathbb{R}^{n_h}$</code> 为隐含层的偏置向量；<code>$\boldsymbol{W} \in \mathbb{R}^{n_v \times n_h}$</code> 为可见层和隐含层之间的权重矩阵。根据能量函数，可得其联合概率分布为：</p>
<p><code>$$ P \left(\mathbf{v} = \boldsymbol{v}, \mathbf{h} = \boldsymbol{h}\right) = \dfrac{1}{Z} e^{-E \left(\boldsymbol{v}, \boldsymbol{h}\right)} $$</code></p>
<p>其中 <code>$Z$</code> 为归一化常数，成为配分函数：</p>
<p><code>$$ Z = \sum_{\boldsymbol{v}}{\sum_{\boldsymbol{h}}{e^{-E \left(\boldsymbol{v}, \boldsymbol{h}\right)}}} $$</code></p>
<p>对于 RBM 我们更加关注的的为边缘分布，即：</p>
<p><code>$$ P \left(\boldsymbol{v}\right) = \sum_{h}{P\left(\boldsymbol{v}, \boldsymbol{h}\right)} = \dfrac{1}{Z} \sum_{h}{e^{-E\left(\boldsymbol{v}, \boldsymbol{h}\right)}} $$</code></p>
<p>因为概率中包含归一化常数，我们需要计算 <code>$Z$</code>，从其定义可得，当穷举左右可能性的化，我们需要计算 <code>$2^{n_v + n_h}$</code> 个项，其计算复杂度很大。尽管 <code>$P\left(\boldsymbol{v}\right)$</code> 计算比较困难，但是其条件概率 <code>$P\left(\mathbf{h} | \mathbf{v}\right)$</code> 和 <code>$P\left(\mathbf{v} | \mathbf{h}\right)$</code> 计算和采样相对容易。为了便于推导，我们定义如下记号：</p>
<p><code>$$ \boldsymbol{h}_{-k} = \left(h_1, h_2, ..., h_{k-1}, h_{k+1}, ..., h_{n_h}\right)^T $$</code></p>
<p>则 <code>$P\left(h_k = 1 | \boldsymbol{v}\right)$</code> 定义如下：</p>
<p><code>$$ \begin{equation} \begin{split} &amp;P\left(h_k = 1 | \boldsymbol{v}\right) \\ = &amp;P\left(h_k = 1 | h_{-k}, \boldsymbol{v}\right) \\ = &amp;\dfrac{P\left(h_k = 1, h_{-k}, \boldsymbol{v}\right)}{P\left(h_{-k}, \boldsymbol{v}\right)} \\ = &amp;\dfrac{P\left(h_k = 1, h_{-k}, \boldsymbol{v}\right)}{P\left(h_k = 1 | h_{-k}, \boldsymbol{v}\right) + P\left(h_k = 0 | h_{-k}, \boldsymbol{v}\right)} \\ = &amp;\dfrac{\dfrac{1}{Z} e^{-E\left(h_k = 1, h_{-k}, \boldsymbol{v}\right)}}{\dfrac{1}{Z} e^{-E\left(h_k = 1, h_{-k}, \boldsymbol{v}\right)} + \dfrac{1}{Z} e^{-E\left(h_k = 0, h_{-k}, \boldsymbol{v}\right)}} \\ = &amp;\dfrac{e^{-E\left(h_k = 1, h_{-k}, \boldsymbol{v}\right)}}{e^{-E\left(h_k = 1, h_{-k}, \boldsymbol{v}\right)} + e^{-E\left(h_k = 0, h_{-k}, \boldsymbol{v}\right)}} \\ = &amp;\dfrac{1}{1 + e^{E\left(h_k = 1, h_{-k}, \boldsymbol{v}\right) - E\left(h_k = 0, h_{-k}, \boldsymbol{v}\right)}} \\ \end{split} \end{equation} $$</code></p>
<p>其中：</p>
<p><code>$$ \begin{equation} \begin{split} &amp;E\left(h_k = 1, h_{-k}, \boldsymbol{v}\right) \\ = &amp;E\left(h_k = 1, \boldsymbol{v}\right) \\ = &amp;-\sum_{i=1}^{n_v}{b_i v_i} - \sum_{j=1, j \neq k}^{n_h}{c_j h_j} - \sum_{i=1}^{n_v}{\sum_{j=1, j \neq k}^{n_h}{v_i W_{i, j} h_i}} - c_k - \sum_{i=1}^{n_v}{v_i W_{i, k}} \\ &amp;E\left(h_k = 0, h_{-k}, \boldsymbol{v}\right) \\ = &amp;E\left(h_k = 0, \boldsymbol{v}\right) \\ = &amp;-\sum_{i=1}^{n_v}{b_i v_i} - \sum_{j=1, j \neq k}^{n_h}{c_j h_j} - \sum_{i=1}^{n_v}{\sum_{j=1, j \neq k}^{n_h}{v_i W_{i, j} h_i}} \end{split} \end{equation} $$</code></p>
<p>因此，<code>$P\left(h_k = 1 | \boldsymbol{v}\right)$</code> 可以化简为：</p>
<p><code>$$ \begin{equation} \begin{split} &amp;P\left(h_k = 1 | \boldsymbol{v}\right) \\ = &amp;\dfrac{1}{1 + e^{-\left(c_k + \sum_{i=1}^{n_v}{v_i W_{i, k}}\right)}} \\ = &amp;\sigma\left(c_k + \sum_{i=1}^{n_v}{v_i W_{i, k}}\right) \\ = &amp;\sigma\left(c_k + \boldsymbol{v}^T \boldsymbol{W}_{:, k}\right) \end{split} \end{equation} $$</code></p>
<p>其中，<code>$\sigma$</code> 为 sigmoid 函数。因此，我们可以将条件分布表示为连乘的形式：</p>
<p><code>$$ \begin{equation} \begin{split} P\left(\boldsymbol{h} | \boldsymbol{v}\right) &amp;= \prod_{j=1}^{n_h}{P\left(h_j | \boldsymbol{v}\right)} \\ &amp;= \prod_{j=1}^{n_h}{\sigma\left(\left(2h - 1\right) \odot \left(\boldsymbol{c} + \boldsymbol{W}^T \boldsymbol{v}\right)\right)_j} \end{split} \end{equation} $$</code></p>
<p>同理可得：</p>
<p><code>$$ \begin{equation} \begin{split} P\left(\boldsymbol{v} | \boldsymbol{h}\right) &amp;= \prod_{i=1}^{n_v}{P\left(v_i | \boldsymbol{h}\right)} \\ &amp;= \prod_{i=1}^{n_v}{\sigma\left(\left(2v - 1\right) \odot \left(\boldsymbol{b} + \boldsymbol{W} \boldsymbol{h}\right)\right)_i} \end{split} \end{equation} $$</code></p>
<h3 id="模型训练">模型训练 <sup id="fnref:10"><a href="#fn:10" class="footnote-ref" role="doc-noteref">10</a></sup></h3>
<p>对于 RBM 模型的训练，假设训练样本集合为 <code>$S = \left\lbrace{\boldsymbol{v^1}, \boldsymbol{v^2}, ..., \boldsymbol{v^{n_s}}}\right\rbrace$</code>，其中 <code>$\boldsymbol{v^i} = \left(v_{1}^{i}, v_{2}^{i}, ..., v_{n_v}^{i}\right), i = 1, 2, ..., n_s$</code>。则训练 RBM 的目标可以定义为最大化如下似然：</p>
<p><code>$$ \mathcal{L}_{\theta, S} = \prod_{i=1}^{n_s}{P\left(\boldsymbol{v}^i\right)} $$</code></p>
<p>其中 <code>$\theta$</code> 为待优化的参数，为了方便计算，等价目标为最大化其对数似然：</p>
<p><code>$$ \ln\mathcal{L}_{\theta, S} = \ln\prod_{i=1}^{n_s}{P\left(\boldsymbol{v}^i\right)} = \sum_{i=1}^{n_s}{\ln P\left(\boldsymbol{v}^i\right)} $$</code></p>
<p>我们将其对数似然简写为 <code>$\ln\mathcal{L}_S$</code> ，通过梯度上升方法，我们可以得到参数的更新公式：</p>
<p><code>$$ \theta = \theta + \eta \dfrac{\partial \ln\mathcal{L}_S}{\partial \theta} $$</code></p>
<p>对于单个样本 <code>$\boldsymbol{\color{red}{v'}}$</code> ，有：</p>
<p><code>$$ \begin{equation} \begin{split} \dfrac{\partial \ln\mathcal{L}_S}{\partial \theta} &amp;= \dfrac{\partial \ln P\left(\boldsymbol{\color{red}{v'}}\right)}{\partial \theta} = \dfrac{\partial \ln \left(\dfrac{1}{Z} \sum_{\boldsymbol{h}}{e^{-E\left(\boldsymbol{\color{red}{v'}, h}\right)}}\right)}{\partial \theta} \\ &amp;= \dfrac{\partial \left(\ln \sum_{\boldsymbol{h}}{e^{-E\left(\boldsymbol{\color{red}{v'}, h}\right)}} - \ln Z\right)}{\partial \theta} = \dfrac{\partial \left(\ln \sum_{\boldsymbol{h}}{e^{-E\left(\boldsymbol{\color{red}{v'}, h}\right)}} - \ln \sum_{\boldsymbol{v, h}}{e^{-E\left(\boldsymbol{v, h}\right)}}\right)}{\partial \theta} \\ &amp;= \dfrac{\partial}{\partial \theta} \left(\ln \sum_{\boldsymbol{h}}{e^{-E\left(\boldsymbol{\color{red}{v'}, h}\right)}}\right) - \dfrac{\partial}{\partial \theta} \left(\ln \sum_{\boldsymbol{v, h}}{e^{-E\left(\boldsymbol{v, h}\right)}}\right) \\ &amp;= -\dfrac{1}{\sum_{\boldsymbol{h}}{e^{-E\left(\boldsymbol{\color{red}{v'}, h}\right)}}} \sum_{\boldsymbol{h}}{e^{-E\left(\boldsymbol{\color{red}{v'}, h}\right)} \dfrac{\partial E\left(\boldsymbol{\color{red}{v'}, h}\right)}{\partial \theta}} + \dfrac{1}{\sum_{\boldsymbol{v, h}}{e^{-E\left(\boldsymbol{v, h}\right)}}} \sum_{\boldsymbol{v, h}}{e^{-E\left(\boldsymbol{v, h}\right)} \dfrac{\partial E\left(\boldsymbol{v, h}\right)}{\partial \theta}} \\ &amp;= -\sum_{\boldsymbol{h}}{\dfrac{e^{-E\left(\boldsymbol{\color{red}{v'}, h}\right)}}{\sum_{\boldsymbol{h}}{e^{-E\left(\boldsymbol{\color{red}{v'}, h}\right)}}} \dfrac{\partial E\left(\boldsymbol{\color{red}{v'}, h}\right)}{\partial \theta}} + \sum_{\boldsymbol{v, h}}{\dfrac{e^{-E\left(\boldsymbol{v, h}\right)}}{\sum_{\boldsymbol{v, h}}{e^{-E\left(\boldsymbol{v, h}\right)}}} \dfrac{\partial E\left(\boldsymbol{v, h}\right)}{\partial \theta}} \\ &amp;= -\sum_{\boldsymbol{h}}{\dfrac{\dfrac{e^{-E\left(\boldsymbol{\color{red}{v'}, h}\right)}}{Z}}{\dfrac{\sum_{\boldsymbol{h}}{e^{-E\left(\boldsymbol{\color{red}{v'}, h}\right)}}}{Z}} \dfrac{\partial E\left(\boldsymbol{\color{red}{v'}, h}\right)}{\partial \theta}} + \sum_{\boldsymbol{v, h}}{\dfrac{e^{-E\left(\boldsymbol{v, h}\right)}}{\sum_{\boldsymbol{v, h}}{e^{-E\left(\boldsymbol{v, h}\right)}}} \dfrac{\partial E\left(\boldsymbol{v, h}\right)}{\partial \theta}} \\ &amp;= -\sum_{\boldsymbol{h}}{\dfrac{P\left(\boldsymbol{\color{red}{v'}, h}\right)}{P\left(\boldsymbol{\color{red}{v'}}\right)} \dfrac{\partial E\left(\boldsymbol{\color{red}{v'}, h}\right)}{\partial \theta}} + \sum_{\boldsymbol{v, h}}{\dfrac{e^{-E\left(\boldsymbol{v, h}\right)}}{\sum_{\boldsymbol{v, h}}{e^{-E\left(\boldsymbol{v, h}\right)}}} \dfrac{\partial E\left(\boldsymbol{v, h}\right)}{\partial \theta}} \\ &amp;= -\sum_{\boldsymbol{h}}{P\left(\boldsymbol{h | \color{red}{v'}}\right) \dfrac{\partial E\left(\boldsymbol{\color{red}{v'}, h}\right)}{\partial \theta}} + \sum_{\boldsymbol{v, h}}{P\left(\boldsymbol{h | v}\right) \dfrac{\partial E\left(\boldsymbol{v, h}\right)}{\partial \theta}} \end{split} \end{equation} $$</code></p>
<p>其中：</p>
<p><code>$$ \begin{equation} \begin{split} \sum_{\boldsymbol{v, h}}{P\left(\boldsymbol{h | v}\right) \dfrac{\partial E\left(\boldsymbol{v, h}\right)}{\partial \theta}} &amp;= \sum_{\boldsymbol{v}}{\sum_{\boldsymbol{h}}{P\left(\boldsymbol{v}\right) P\left(\boldsymbol{h | v}\right) \dfrac{\partial E\left(\boldsymbol{v, h}\right)}{\partial \theta}}} \\ &amp;= \sum_{\boldsymbol{v}}{P\left(\boldsymbol{v}\right) \sum_{\boldsymbol{h}}{P \left(\boldsymbol{h | v}\right) \dfrac{\partial E\left(\boldsymbol{v, h}\right)}{\partial \theta}}} \end{split} \end{equation} $$</code></p>
<p>则对于参数 <code>$w_{i, j}$</code> 可得：</p>
<p><code>$$ \begin{equation} \begin{split} &amp;\sum_{\boldsymbol{h}}{P\left(\boldsymbol{h|v}\right) \dfrac{\partial E\left(\boldsymbol{v, h}\right)}{\partial w_{i, j}}} \\ = &amp;-\sum_{\boldsymbol{h}}{P\left(\boldsymbol{h|v}\right) h_i v_j} \\ = &amp;-\sum_{\boldsymbol{h}}{\prod_{k=1}^{n_h}{P\left(h_k | \boldsymbol{v}\right) h_i v_j}} \\ = &amp;-\sum_{\boldsymbol{h}}{P\left(h_i | \boldsymbol{v}\right) P\left(h_{-i} | \boldsymbol{v}\right) h_i v_j} \\ = &amp;-\sum_{\boldsymbol{h_i}}{\sum_{h_{-i}}{P\left(h_i | \boldsymbol{v}\right) P\left(\boldsymbol{h_{-i}} | \boldsymbol{v}\right) h_i v_j}} \\ = &amp;-\sum_{\boldsymbol{h_i}}{P\left(h_i | \boldsymbol{v}\right) h_i v_j} \sum_{\boldsymbol{h_{-i}}}{P\left(h_{-i} | \boldsymbol{v}\right)} \\ = &amp;-\sum_{\boldsymbol{h_i}}{P\left(h_i | \boldsymbol{v}\right) h_i v_j} \\ = &amp;-\left(P\left(h_i = 0 | \boldsymbol{v}\right) \cdot 0 \cdot v_j + P\left(h_i = 1 | \boldsymbol{v}\right) \cdot 1 \cdot v_j\right) \\ = &amp;-P\left(h_i = 1 | \boldsymbol{v}\right) v_j \end{split} \end{equation} $$</code></p>
<p>则对于参数 <code>$b_i$</code> 可得：</p>
<p><code>$$ \begin{equation} \begin{split} &amp;\sum_{\boldsymbol{h}}{P\left(\boldsymbol{h|v}\right) \dfrac{\partial E\left(\boldsymbol{v, h}\right)}{\partial b_i}} \\ = &amp;-\sum_{\boldsymbol{h}}{P\left(\boldsymbol{h|v}\right) v_i} \\ = &amp;-v_i \sum_{\boldsymbol{h}}{P\left(\boldsymbol{h|v}\right)} \\ = &amp;-v_i \end{split} \end{equation} $$</code></p>
<p>则对于参数 <code>$c_j$</code> 可得：</p>
<p><code>$$ \begin{equation} \begin{split} &amp;\sum_{\boldsymbol{h}}{P\left(\boldsymbol{h|v}\right) \dfrac{\partial E\left(\boldsymbol{v, h}\right)}{\partial c_j}} \\ = &amp;-\sum_{\boldsymbol{h}}{P\left(\boldsymbol{h|v}\right) h_j} \\ = &amp;-\sum_{\boldsymbol{h}}{\prod_{k=1}^{n_h}{P\left(h_k | \boldsymbol{v}\right) h_j}} \\ = &amp;-\sum_{\boldsymbol{h}}{P\left(h_j | \boldsymbol{v}\right) P\left(h_{-j} | \boldsymbol{v}\right) h_j} \\ = &amp;-\sum_{h_j}{\sum_{h_{-j}}{P\left(h_i | \boldsymbol{v}\right) P\left(h_{-j} | \boldsymbol{v}\right) h_j}} \\ = &amp;-\sum_{h_j}{P\left(h_i | \boldsymbol{v}\right) h_j} \sum_{h_{-j}}{P\left(h_{-j} | \boldsymbol{v}\right)} \\ = &amp;-\sum_{h_j}{P\left(h_i | \boldsymbol{v}\right) h_j} \\ = &amp;-\left(P\left(h_j = 0 | \boldsymbol{v}\right) \cdot 0 + P\left(h_j = 1 | \boldsymbol{v}\right) \cdot 1\right) \\ = &amp;-P\left(h_j = 1 | \boldsymbol{v}\right) \end{split} \end{equation} $$</code></p>
<p>综上所述，可得：</p>
<p><code>$$ \begin{equation} \begin{split} \dfrac{\partial \ln P\left(\color{red}{\boldsymbol{v'}}\right)}{\partial w_{i, j}} &amp;= -\sum_{\boldsymbol{h}}{P\left(\boldsymbol{h | \color{red}{v'}}\right) \dfrac{\partial E\left(\boldsymbol{\color{red}{v'}, h}\right)}{\partial w_{i, j}}} + \sum_{\boldsymbol{v, h}}{P\left(\boldsymbol{h | v}\right) \dfrac{\partial E\left(\boldsymbol{v, h}\right)}{\partial w_{i, j}}} \\ &amp;= P\left(h_i = 1 | \boldsymbol{\color{red}{v'}}\right) \color{red}{v'_j} - \sum_{\boldsymbol{v}}{P\left(\boldsymbol{v}\right) P\left(h_i = 1 | \boldsymbol{v}\right) v_j}\\ \dfrac{\partial \ln P\left(\color{red}{\boldsymbol{v'}}\right)}{\partial b_i} &amp;= -\sum_{\boldsymbol{h}}{P\left(\boldsymbol{h | \color{red}{v'}}\right) \dfrac{\partial E\left(\boldsymbol{\color{red}{v'}, h}\right)}{\partial b_i}} + \sum_{\boldsymbol{v, h}}{P\left(\boldsymbol{h | v}\right) \dfrac{\partial E\left(\boldsymbol{v, h}\right)}{\partial b_i}} \\ &amp;= \color{red}{v'_i} - \sum_{\boldsymbol{v}}{P\left(\boldsymbol{v}\right) v_i} \\ \dfrac{\partial \ln P\left(\color{red}{\boldsymbol{v'}}\right)}{\partial c_j} &amp;= -\sum_{\boldsymbol{h}}{P\left(\boldsymbol{h | \color{red}{v'}}\right) \dfrac{\partial E\left(\boldsymbol{\color{red}{v'}, h}\right)}{\partial c_j}} + \sum_{\boldsymbol{v, h}}{P\left(\boldsymbol{h | v}\right) \dfrac{\partial E\left(\boldsymbol{v, h}\right)}{\partial c_j}} \\ &amp;= P\left(h_j = 1 | \boldsymbol{\color{red}{v'}}\right) - \sum_{\boldsymbol{v}}{P\left(\boldsymbol{v}\right) P\left(h_j = 1 | \boldsymbol{v}\right)} \\ \end{split} \end{equation} $$</code></p>
<p>对于多个样本 <code>$S = \left\lbrace{\boldsymbol{v^1}, \boldsymbol{v^2}, ..., \boldsymbol{v^{n_s}}}\right\rbrace$</code>，有：</p>
<p><code>$$ \begin{equation} \begin{split} \dfrac{\partial \ln \mathcal{L}_S}{\partial w_{i, j}} &amp;= \sum_{m=1}^{n_S}{\left[P\left(h_i = 1 | \boldsymbol{v^m}\right) v_j^m - \sum_{\boldsymbol{v}}{P\left(\boldsymbol{v}\right) P\left(h_i = 1 | \boldsymbol{v} v_j\right)}\right]} \\ \dfrac{\partial \ln \mathcal{L}_S}{\partial b_i} &amp;= \sum_{m=1}^{n_S}{\left[v_i^m - \sum_{\boldsymbol{v}}{P\left(\boldsymbol{v}\right) v_i}\right]} \\ \dfrac{\partial \ln \mathcal{L}_S}{\partial c_j} &amp;= \sum_{m=1}^{n_S}{\left[P\left(h_j = 1 | \boldsymbol{v^m}\right) - \sum_{\boldsymbol{v}}{P\left(\boldsymbol{v}\right) P\left(h_j = 1 | \boldsymbol{v}\right)}\right]} \end{split} \end{equation} $$</code></p>
<p>针对如上方法，我们需要计算 <code>$\sum_{\boldsymbol{v}}$</code> 相关项，如上文所述，其计算复杂度为 <code>$O\left(2^{n_v + n_h}\right)$</code>，因为其条件概率计算比较容易，因此我们可以用 Gibbs 采样的方法进行估计，但由于 Gibbs 采样方法存在 burn-in period，因此需要足够次数的状态转移后才能够收敛到目标分布，因此这就增大了利用这种方法训练 RBM 模型的时间。</p>
<p>针对这个问题，Hinton 于 2002 年提出了对比散度 (Contrastive Divergence, CD) 算法 <sup id="fnref:11"><a href="#fn:11" class="footnote-ref" role="doc-noteref">11</a></sup>，基本思想为将训练样本作为采样的初始值，因为目标就是让 RBM 去拟合这些样本的分布，因此这样则可以通过更少的状态转移就收敛到平稳分布。<code>$k$</code> 步 CD 算法大致步骤为：</p>
<ol>
<li>对 <code>$\forall \boldsymbol{v} \in \boldsymbol{S}$</code>，初始化 <code>$\boldsymbol{v}^{\left(0\right)} = \boldsymbol{v}$</code>。</li>
<li>执行 <code>$k$</code> 步 Gibbs 采样，对于第 <code>$t$</code> 步，分别利用 <code>$P\left(\boldsymbol{h} | \boldsymbol{v}^{\left(t-1\right)}\right)$</code> 和 <code>$P\left(\boldsymbol{v} | \boldsymbol{h}^{\left(t-1\right)}\right)$</code> 采样出 <code>$\boldsymbol{h}^{\left(t-1\right)}$</code> 和 <code>$\boldsymbol{v}^{\left(t\right)}$</code>。</li>
<li>利用采样得到的 <code>$\boldsymbol{v}^{\left(k\right)}$</code> <strong>近似估计</strong> <code>$\sum_{\boldsymbol{v}}$</code> 相关项：
<code>$$ \begin{equation} \begin{split} \dfrac{\partial \ln P\left(\boldsymbol{v}\right)}{\partial w_{i, j}} &amp;\approx P\left(h_i=1|\boldsymbol{v}^{\left(0\right)}\right) v_j^{\left(0\right)} - P\left(h_i=1|\boldsymbol{v}^{\left(k\right)}\right) v_j^{\left(k\right)} \\ \dfrac{\partial \ln P\left(\boldsymbol{v}\right)}{\partial b_i} &amp;\approx v_i^{\left(0\right)} - v_i^{\left(k\right)} \\ \dfrac{\partial \ln P\left(\boldsymbol{v}\right)}{\partial c_j} &amp;\approx P\left(h_j=1|\boldsymbol{v}^{\left(0\right)}\right) - P\left(h_j=1|\boldsymbol{v}^{\left(k\right)}\right) \end{split} \end{equation} $$</code></li>
</ol>
<p><strong>近似估计</strong>可以看做是利用</p>
<p><code>$$ CDK\left(\theta, \boldsymbol{v}\right) = -\sum_{\boldsymbol{h}}{P\left(\boldsymbol{h} | \boldsymbol{v}^{\left(0\right)}\right) \dfrac{\partial E\left(\boldsymbol{v}^{\left(0\right)}, h\right)}{\partial \theta}} + \sum_{\boldsymbol{h}}{P\left(\boldsymbol{h} | \boldsymbol{v}^{\left(k\right)}\right) \dfrac{\partial E\left(\boldsymbol{v}^{\left(k\right)}, \boldsymbol{h}\right)}{\partial \theta}} $$</code></p>
<p>近似</p>
<p><code>$$ \dfrac{\partial \ln P\left(\boldsymbol{v}\right)}{\partial \theta} = -\sum_{\boldsymbol{h}}{P\left(\boldsymbol{h} | \boldsymbol{v}^{\left(0\right)}\right) \dfrac{\partial E\left(\boldsymbol{v}^{\left(0\right)}, h\right)}{\partial \theta}} + \sum_{\boldsymbol{v, h}}{P\left(\boldsymbol{v, h}\right) \dfrac{\partial E\left(\boldsymbol{v}, \boldsymbol{h}\right)}{\partial \theta}} $$</code></p>
<p>的过程。</p>
<p>基于对比散度的 RBM 训练算法可以描述为：</p>


<div><pre class="pseudocode">
\begin{algorithm}
\caption{CDK 算法}
\begin{algorithmic}
\REQUIRE $k, \boldsymbol{S}, \text{RBM}\left(\boldsymbol{W, b, c}\right)$
\ENSURE $\Delta \boldsymbol{W}, \Delta \boldsymbol{b}, \Delta \boldsymbol{c}$
\PROCEDURE{CDK}{$k, \boldsymbol{S}, \text{RBM}\left(\boldsymbol{W, b, c}\right)$}
    \STATE $\Delta \boldsymbol{W} \gets 0, \Delta \boldsymbol{b} \gets 0, \Delta \boldsymbol{c} \gets 0$
    \FORALL{$\boldsymbol{v \in S}$}
        \STATE $\boldsymbol{v}^{\left(0\right)} \gets \boldsymbol{v}$
        \FOR{$t = 0, 1, ..., k-1$}
            \STATE $\boldsymbol{h}^{\left(t\right)} \gets \text{sample_h_given_v} \left(\boldsymbol{v}^{\left(t\right)}, \text{RBM}\left(W, b, c\right)\right)$
            \STATE $\boldsymbol{v}^{\left(t+1\right)} \gets \text{sample_v_given_h} \left(\boldsymbol{h}^{\left(t\right)}, \text{RBM}\left(W, b, c\right)\right)$
        \ENDFOR
        \FOR{$i = 1, 2, ..., n_h; j = 1, 2, ..., n_v$}
            \STATE $\Delta w_{i, j} \gets \Delta w_{i, j} + \left[P\left(h_i=1|\boldsymbol{v}^{\left(0\right)}\right) v_j^{\left(0\right)} - P\left(h_i=1|\boldsymbol{v}^{\left(k\right)}\right) v_j^{\left(k\right)}\right]$
            \STATE $\Delta b_i \gets \Delta b_i = \left[v_i^{\left(0\right)} - v_i^{\left(k\right)}\right]$
            \STATE $\Delta c_j \gets \Delta c_j = \left[P\left(h_j=1|\boldsymbol{v}^{\left(0\right)}\right) - P\left(h_j=1|\boldsymbol{v}^{\left(k\right)}\right)\right]$
        \ENDFOR
    \ENDFOR
\ENDPROCEDURE
\end{algorithmic}
\end{algorithm}
</pre></div>

<p>其中，<code>sample_h_given_v</code> 和 <code>sample_v_given_h</code> 分别表示在已知可见层时采样隐含层和在已知隐含层时采样可见层。对于 <code>sample_h_given_v</code> 其算法流程如下：</p>


<div><pre class="pseudocode">
\begin{algorithm}
\caption{sample\_h\_given\_v 算法}
\begin{algorithmic}
\FOR{$j = 1, 2, ..., n_h$}
    \STATE sample $r_j \sim Uniform[0, 1]$
    \IF{$r_j < P\left(h_j = 1 | \boldsymbol{v}\right)$}
        \STATE $h_j \gets 1$
    \ELSE
        \STATE $h_j \gets 0$
    \ENDIF
\ENDFOR
\end{algorithmic}
\end{algorithm}
</pre></div>

<p>类似的，对于 <code>sample_v_given_h</code> 其算法流程如下：</p>


<div><pre class="pseudocode">
\begin{algorithm}
\caption{sample\_v\_given\_h 算法}
\begin{algorithmic}
\FOR{$j = 1, 2, ..., n_h$}
    \STATE sample $r_j \sim Uniform[0, 1]$
    \IF{$r_i < P\left(v_i = 1 | \boldsymbol{h}\right)$}
        \STATE $v_i \gets 1$
    \ELSE
        \STATE $v_i \gets 0$
    \ENDIF
\ENDFOR
\end{algorithmic}
\end{algorithm}
</pre></div>

<p>至此，我们可以得到 RBM 模型训练的整个流程：</p>


<div><pre class="pseudocode">
\begin{algorithm}
\caption{RBM 训练算法}
\begin{algorithmic}
\FOR{$iter = 1, 2, ..., \text{max\_iter}$}
    \STATE $\Delta \boldsymbol{W}, \Delta \boldsymbol{b}, \Delta \boldsymbol{c} \gets \text{CDK} \left(k, \boldsymbol{S}, \text{RBM}\left(\boldsymbol{W, b, c}\right)\right)$
    \STATE $\boldsymbol{W} \gets \boldsymbol{W} + \eta \left(\dfrac{1}{n_s} \Delta \boldsymbol{W}\right)$
    \STATE $\boldsymbol{b} \gets \boldsymbol{b} + \eta \left(\dfrac{1}{n_s} \Delta \boldsymbol{b}\right)$
    \STATE $\boldsymbol{c} \gets \boldsymbol{c} + \eta \left(\dfrac{1}{n_s} \Delta \boldsymbol{c}\right)$
\ENDFOR
\end{algorithmic}
\end{algorithm}
</pre></div>

<p>其中，<code>$k$</code> 为 CDK 算法参数，<code>$\text{max_iter}$</code> 为最大迭代次数，<code>$\boldsymbol{S}$</code> 为训练样本，<code>$n_s = |\boldsymbol{S}|$</code>，<code>$\eta$</code> 为学习率。</p>
<p>对于模型的评估，最简单的是利用 RBM 模型的似然或对数似然，但由于涉及到归一化因子 <code>$Z$</code> 的计算，其复杂度太高。更常用的方式是利用<strong>重构误差</strong> (reconstruction error)，即输入数据和利用 RBM 模型计算得到隐含节点再重构回可见节点之间的误差。</p>
<h3 id="mnist-示例">MNIST 示例</h3>
<p>我们利用经典的 MNIST 数据作为示例，我们利用基于 tensorflow 的扩展包 tfrbm <sup id="fnref:12"><a href="#fn:12" class="footnote-ref" role="doc-noteref">12</a></sup>。tfrbm 实现了 Bernoulli-Bernoulli RBM 和 Gaussian-Bernoulli RBM 两种不同的 RBM，两者的比较详见 <sup id="fnref:13"><a href="#fn:13" class="footnote-ref" role="doc-noteref">13</a></sup> <sup id="fnref:14"><a href="#fn:14" class="footnote-ref" role="doc-noteref">14</a></sup>。</p>
<pre><code class="language-python">import numpy as np
from matplotlib import pyplot as plt, gridspec
from tfrbm import BBRBM, GBRBM
from tensorflow.examples.tutorials.mnist import input_data

# 读入训练数据和测试数据
mnist = input_data.read_data_sets('MNIST', one_hot=True)
mnist_train_images = mnist.train.images
mnist_test_images = mnist.test.images
mnist_test_labels = mnist.test.labels
</code></pre>
<p>MNIST 数据集中，训练集共包含 55000 个样本，每个样本的维度为 784，我们构建 Bernoulli-Bernoulli RBM，设置隐含节点个数为 64，学习率为 0.01，epoches 为 30，batch size 为 10。</p>
<pre><code class="language-python">bbrbm = BBRBM(n_visible=784,
              n_hidden=64,
              learning_rate=0.01,
              use_tqdm=True)

bbrbm_errs = bbrbm.fit(mnist_train_images, n_epoches=30, batch_size=10)

# Epoch: 0: 100%|##########| 5500/5500 [00:11&lt;00:00, 480.39it/s]
# Train error: 0.1267
# 
# ......
# 
# Epoch: 29: 100%|##########| 5500/5500 [00:11&lt;00:00, 482.15it/s]
# Train error: 0.0347
</code></pre>
<p>训练误差变化如下</p>
<pre><code class="language-python">plt.style.use('ggplot')
plt.plot(bbrbm_errs)
</code></pre>
<p><img src="/images/cn/2018-01-17-ising-hopfield-and-rbm/bbrbm-mnist-errs.png" alt=""></p>
<p>我们从 MNIST 的测试集中针对每个数字选取 10 个样本，共 100 个样本作为测试数据，利用训练好的 RBM 模型重构这 100 个样本</p>
<pre><code class="language-python">mnist_test_images_samples = np.zeros([10 * 10, 784])
mnist_test_images_samples_rec = np.zeros([10 * 10, 784])
mnist_test_images_samples_plt = np.zeros([10 * 10 * 2, 784])

digits_current_counts = np.zeros(10, dtype=np.int32)
digits_total_counts = np.ones(10, dtype=np.int32) * 10

for idx in range(mnist_test_images.shape[0]):
    image = mnist_test_images[idx, ]
    label = mnist_test_labels[idx, ]

    for digit in range(10):
        digit_label = np.zeros(10)
        digit_label[digit] = 1

        if (label == digit_label).all() and
               digits_current_counts[digit] &lt; 10:
            nrow = digits_current_counts[digit]
            sample_idx = nrow * 10 + digit
            mnist_test_images_samples[sample_idx, ] = image
            mnist_test_images_samples_rec[sample_idx, ] = \
                bbrbm.reconstruct(image.reshape([1, -1]))
            mnist_test_images_samples_plt[sample_idx * 2, ] = \
                mnist_test_images_samples[sample_idx, ]
            mnist_test_images_samples_plt[sample_idx * 2 + 1, ] = \
                mnist_test_images_samples_rec[sample_idx, ]
            digits_current_counts[digit] += 1

    if (digits_current_counts == digits_total_counts).all():
        break
</code></pre>
<p>对比测试输入数据和重构结果，奇数列为输入数据，偶数列为重构数据</p>
<pre><code class="language-python">def plot_mnist(mnist_images, nrows, ncols, cmap='gray'):
    fig = plt.figure(figsize=(ncols, nrows))
    gs = gridspec.GridSpec(nrows, ncols)
    gs.update(wspace=0.025, hspace=0.025)

    for nrow in range(nrows):
        for ncol in range(ncols):
            ax = plt.subplot(gs[nrow, ncol])
            idx = nrow * ncols + ncol
            minist_image = mnist_images[idx, ].reshape([28, 28])
            ax.imshow(minist_image, cmap=cmap)
            ax.axis('off')

    return fig
    
plot_mnist(mnist_test_images_samples_plt, 10, 20)
</code></pre>
<p><img src="/images/cn/2018-01-17-ising-hopfield-and-rbm/bbrbm-mnist.png" alt=""></p>
<p>测试集上的重构误差为</p>
<pre><code class="language-python">gbrbm.get_err(mnist_test_images_samples)

# 0.035245348
</code></pre>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Ernest Ising, Beitrag zur Theorie des Ferround Paramagnetismus (1924) Contribution to the Theory of Ferromagnetism (English translation of &ldquo;Beitrag zur Theorie des Ferromagnetismus&rdquo;, 1925) Goethe as a Physicist (1950)&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Onsager, L. &ldquo;A two-dimensional model with an order–disorder transition (crystal statistics I).&rdquo; <em>Phys. Rev</em> 65 (1944): 117-49.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p><a href="http://wiki.swarma.net/index.php?title=ISING%E6%A8%A1%E5%9E%8B">http://wiki.swarma.net/index.php?title=ISING模型</a>&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>Hopfield, John J. &ldquo;Neural networks and physical systems with emergent collective computational abilities.&rdquo; <em>Spin Glass Theory and Beyond: An Introduction to the Replica Method and Its Applications.</em> 1987. 411-415.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p>Abu-Mostafa, Y. A. S. E. R., and J. St Jacques. &ldquo;Information capacity of the Hopfield model.&rdquo; <em>IEEE Transactions on Information Theory</em> 31.4 (1985): 461-464.&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6">
<p>韩力群. 人工神经网络理论、设计及应用&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:7">
<p><a href="https://zh.wikipedia.org/zh-hans/%E6%97%85%E8%A1%8C%E6%8E%A8%E9%94%80%E5%91%98%E9%97%AE%E9%A2%98">https://zh.wikipedia.org/zh-hans/旅行推销员问题</a>&#160;<a href="#fnref:7" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:8">
<p><a href="https://zh.wikipedia.org/zh-hans/NP%E5%9B%B0%E9%9A%BE">https://zh.wikipedia.org/zh-hans/NP困难</a>&#160;<a href="#fnref:8" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:9">
<p>Smolensky, Paul. <em>Information processing in dynamical systems: Foundations of harmony theory.</em> No. CU-CS-321-86. COLORADO UNIV AT BOULDER DEPT OF COMPUTER SCIENCE, 1986.&#160;<a href="#fnref:9" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:10">
<p><a href="http://blog.csdn.net/itplus/article/details/19168937">http://blog.csdn.net/itplus/article/details/19168937</a>&#160;<a href="#fnref:10" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:11">
<p>Hinton, Geoffrey E. &ldquo;Training products of experts by minimizing contrastive divergence.&rdquo; <em>Neural computation</em> 14.8 (2002): 1771-1800.&#160;<a href="#fnref:11" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:12">
<p><a href="https://github.com/meownoid/tensorfow-rbm">https://github.com/meownoid/tensorfow-rbm</a>&#160;<a href="#fnref:12" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:13">
<p>Hinton, Geoffrey. &ldquo;A practical guide to training restricted Boltzmann machines.&rdquo; <em>Momentum</em> 9.1 (2010): 926.&#160;<a href="#fnref:13" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:14">
<p>Yamashita, Takayoshi, et al. &ldquo;To be Bernoulli or to be Gaussian, for a Restricted Boltzmann Machine.&rdquo; <em>Pattern Recognition (ICPR), 2014 22nd International Conference on. IEEE</em>, 2014.&#160;<a href="#fnref:14" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>

        ]]></description></item></channel></rss>