-
Notifications
You must be signed in to change notification settings - Fork 0
/
local-search.xml
216 lines (104 loc) · 167 KB
/
local-search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>博客搭建总结报告</title>
<link href="/2024/04/22/others/opensource-hw1/"/>
<url>/2024/04/22/others/opensource-hw1/</url>
<content type="html"><![CDATA[<h1 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h1><p>本文将简要介绍使用 hexo 框架搭建静态博客,并部署在 Github Pages 上。<sup id="fnref:2" class="footnote-ref"><a href="#fn:2" rel="footnote"><span class="hint--top hint--rounded" aria-label="[从零开始搭建个人博客(超详细)]( https://zhuanlan.zhihu.com/p/102592286 )">[2]</span></a></sup></p><p>Hexo 是一个基于 Node.js 的快速、简单且强大的静态博客框架。它使用 Markdown 格式来书写文章,并能够通过主题和插件进行个性化定制。</p><p>使用 GitHub Pages 部署 Hexo 博客是一种简单且免费的方式,可以让你的博客快速上线,并且拥有稳定的托管服务。</p><h1 id="搭建步骤"><a href="#搭建步骤" class="headerlink" title="搭建步骤"></a>搭建步骤</h1><h2 id="安装与初始化"><a href="#安装与初始化" class="headerlink" title="安装与初始化"></a>安装与初始化</h2><ul><li><p><strong>安装 Node.js 和 Git:</strong> Hexo 基于 Node.js,首先需要在计算机上安装 Node.js。同时,你也需要安装 Git,以便于版本控制和部署。</p></li><li><p><strong>安装 Hexo:</strong> 使用 npm(Node.js 包管理器)安装 Hexo:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">npm install hexo-cli -g<br></code></pre></td></tr></tbody></table></figure></li><li><p><strong>初始化博客:</strong> 在命令行中执行以下命令来创建一个新的 Hexo 博客:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs shell">hexo init blog<br>cd blog<br>npm install<br>hexo server<br></code></pre></td></tr></tbody></table></figure></li></ul><h2 id="主题与博客撰写"><a href="#主题与博客撰写" class="headerlink" title="主题与博客撰写"></a>主题与博客撰写</h2><ul><li>Hexo 支持各种主题,根据自己的喜好选择。安装主题的方式通常是将主题文件夹放置在博客根目录下的 <code>themes</code> 文件夹中,并在 <code>_config.yml</code> 文件中进行相应配置。本 blog 使用的主题是 fluid 。</li></ul><ul><li><p><strong>书写文章:</strong> 使用 Markdown 格式书写文章,并将其保存在 Hexo 博客的 <code>source/_posts</code> 目录下。</p></li><li><p><strong>生成静态文件:</strong> 在命令行中执行以下命令来生成静态文件:</p><figure class="highlight verilog"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs verilog">hexo <span class="hljs-keyword">generate</span><br></code></pre></td></tr></tbody></table></figure></li><li><p><strong>本地预览:</strong> 生成静态文件后,可以在本地预览博客。执行以下命令:</p><figure class="highlight axapta"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs axapta">hexo <span class="hljs-keyword">server</span><br></code></pre></td></tr></tbody></table></figure><p>然后在浏览器中访问 <code>http://localhost:4000</code> 即可查看博客。</p></li></ul><h2 id="博客部署"><a href="#博客部署" class="headerlink" title="博客部署"></a>博客部署</h2><ul><li><p><strong>创建 GitHub 仓库:</strong> 在 GitHub 上创建一个新的仓库,仓库名称应该为 <code><用户名>.github.io</code>,例如 <code>username.github.io</code>。这个仓库将用于托管博客网站。</p></li><li><p><strong>配置 <code>_config.yml</code>:</strong> 打开 Hexo 博客根目录下的 <code>_config.yml</code> 文件,在文件中找到 <code>deploy</code> 部分,修改配置如下:</p><figure class="highlight stylus"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs stylus">yamlCopy codedeploy:<br> type: <span class="hljs-string">'git'</span><br> repo: git@github<span class="hljs-selector-class">.com</span>:<用户名>/<用户名><span class="hljs-selector-class">.github</span><span class="hljs-selector-class">.io</span><span class="hljs-selector-class">.git</span><br> branch: master<br></code></pre></td></tr></tbody></table></figure><p>将 <code><你的用户名></code> 替换为你的 GitHub 用户名。</p></li><li><p><strong>部署到 GitHub Pages:</strong> 在命令行中执行以下命令进行部署:</p><figure class="highlight verilog"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs verilog">hexo clean<br>hexo <span class="hljs-keyword">generate</span><br>hexo deploy<br></code></pre></td></tr></tbody></table></figure><p>执行完毕后,Hexo 会将生成的静态文件推送到 GitHub 上你创建的仓库中。</p></li><li><p><strong>访问博客:</strong> 打开浏览器,访问 <code>http://<用户名>.github.io</code>,即可查看部署在 GitHub Pages 上的博客网站。</p></li><li><p><strong>自定义域名:</strong> 在阿里云购买域名,并进行绑定。在仓库设置中添加自定义域名,并在域名服务商处进行相应的设置。本 blog 使用的域名为 <a href="vitaminzl.com">vitaminzl.com</a> .</p></li></ul><h1 id="问题"><a href="#问题" class="headerlink" title="问题"></a>问题</h1><h2 id="SSH"><a href="#SSH" class="headerlink" title="SSH"></a>SSH</h2><p><strong>个人博客第3篇——绑定GitHub并提交文件</strong>这一篇中的<strong>绑定GitHub</strong>是必要的,否则在 <em><a href="https://zhuanlan.zhihu.com/p/105715224">个人博客第5篇——安装node.js和Hexo </a></em> 最后一步,登录 <a href="http://xxxx.github.io/">http://xxxx.github.io</a> 时跳不出Hexo网站<sup id="fnref:3" class="footnote-ref"><a href="#fn:3" rel="footnote"><span class=”hint–top hint–rounded” aria-label=”</a><a href="https://blog.csdn.net/Vosang/article/details/50499300">github连接报”ssh: connect to host github.com port 22: Connection timed out”错误</a>“>[3]</sup>。</p><h2 id="22号端口被占用"><a href="#22号端口被占用" class="headerlink" title="22号端口被占用"></a>22号端口被占用</h2><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs shell"><span class="hljs-meta prompt_">$ </span><span class="language-bash">ssh -T git@github.com</span><br>ssh: connect to host github.com port 22: Connection timed out<br></code></pre></td></tr></tbody></table></figure><p>解决方法:切换到 <code>cd ~/.ssh/</code> ,进入到<code>~</code>目录下面的<code>.ssh</code>下面,修改ssh配置,新建<code>config</code>文件</p><p>将上述文件添加配置<sup id="fnref:4" class="footnote-ref"><a href="#fn:4" rel="footnote"><span class="hint--top hint--rounded" aria-label="[github默认端口22被占用,ssh: connect to host github.com port 22: Connection timed out](https://www.cnblogs.com/yinfei/p/11385722.html)">[4]</span></a></sup>:</p><figure class="highlight stylus"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs stylus">Host github<span class="hljs-selector-class">.com</span> <span class="hljs-comment">/*服务器地址为github地址*/</span><br>User <span class="hljs-string">"XXX@XX.com"</span> <span class="hljs-comment">/*github上的注册邮箱为用户账号*/</span><br>Hostname ssh<span class="hljs-selector-class">.github</span><span class="hljs-selector-class">.com</span> <span class="hljs-comment">/*服务器地址为github地址*/</span><br>PreferredAuthentications publickey <span class="hljs-comment">/*采用公匙*/</span><br>IdentityFile ~/.ssh/id_rsa <span class="hljs-comment">/*公匙文件路径*/</span><br>Port <span class="hljs-number">443</span> <span class="hljs-comment">/*修改端口为443*/</span><br></code></pre></td></tr></tbody></table></figure><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>在搭建 Hexo 博客的过程中,我学到了很多关于静态网站生成器和博客托管的知识,也收获了一些经验和体会<sup id="fnref:1" class="footnote-ref"><a href="#fn:1" rel="footnote"><span class="hint--top hint--rounded" aria-label="[chatgpt3.5]( https://chat.openai.com )">[1]</span></a></sup>。</p><p><strong>1. 简单高效:</strong> Hexo 提供了简单易用的命令行工具,可以快速生成静态文件,而且基于 Markdown 格式书写文章十分高效,让我能够专注于内容创作而不用关心繁琐的网站构建细节。</p><p><strong>2. 自定义性强:</strong> Hexo 的主题和插件系统为博客提供了丰富的个性化定制选项,我可以根据自己的喜好选择合适的主题,并通过插件添加各种功能和特性,让博客更具吸引力。</p><p><strong>3. GitHub Pages 的便利:</strong> 将博客部署到 GitHub Pages 上不仅方便快捷,而且免费稳定。通过简单的配置和命令,就能够将博客部署到全球范围内访问,让我能够与更多人分享我的思考和见解。</p><p><strong>4. 持续学习与改进:</strong> 在搭建博客的过程中,我遇到了一些问题和挑战,但通过查阅文档、搜索解决方案以及尝试不同的方法,我逐渐解决了这些问题,也提高了自己的技能水平。搭建博客不仅是一个项目,更是一个持续学习和改进的过程。</p><p>总的来说,搭建 Hexo 博客是一次愉快的经历,让我更加深入地了解了网站构建和博客托管的原理和方法,同时也提高了我的技能和经验。我期待着在这个新的博客平台上不断分享我的思考和创意,与读者们建立更深入的联系。</p><h1 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h1><section class="footnotes"><div class="footnote-list"><ol><li><span id="fn:1" class="footnote-text"><span><a href="https://chat.openai.com/">chatgpt3.5</a><a href="#fnref:1" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:2" class="footnote-text"><span><a href="https://zhuanlan.zhihu.com/p/102592286">从零开始搭建个人博客(超详细)</a><a href="#fnref:2" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:3" class="footnote-text"><span><a href="https://blog.csdn.net/Vosang/article/details/50499300">github连接报”ssh: connect to host github.com port 22: Connection timed out”错误</a><a href="#fnref:3" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:4" class="footnote-text"><span><a href="https://www.cnblogs.com/yinfei/p/11385722.html">github默认端口22被占用,ssh: connect to host github.com port 22: Connection timed out</a><a href="#fnref:4" rev="footnote" class="footnote-backref"> ↩</a></span></span></li></ol></div></section>]]></content>
<categories>
<category>课程作业</category>
</categories>
<tags>
<tag>blog</tag>
</tags>
</entry>
<entry>
<title>GNN论文精选-第二组</title>
<link href="/2023/08/30/gnn/notes-for-collection-2/"/>
<url>/2023/08/30/gnn/notes-for-collection-2/</url>
<content type="html"><![CDATA[<h1 id="DropEdge-Towards-Deep-Graph-Convolutional-Networks-On-Node-Classification"><a href="#DropEdge-Towards-Deep-Graph-Convolutional-Networks-On-Node-Classification" class="headerlink" title="DropEdge Towards Deep Graph Convolutional Networks On Node Classification"></a><strong>DropEdge</strong> Towards Deep Graph Convolutional Networks On Node Classification</h1><h2 id="Drop-Edge"><a href="#Drop-Edge" class="headerlink" title="Drop Edge"></a>Drop Edge</h2><p>Drop Edge<sup id="fnref:1" class="footnote-ref"><a href="#fn:1" rel="footnote"><span class="hint--top hint--rounded" aria-label="Y. Rong, W. Huang, T. Xu, and J. Huang, “DropEdge: Towards Deep Graph Convolutional Networks on Node Classification.” arXiv, Mar. 12, 2020. doi: [10.48550/arXiv.1907.10903](https://doi.org/10.48550/arXiv.1907.10903).">[1]</span></a></sup>,其方法顾名思义,就是丢弃图中的边。</p><p>具体来说,对于一个邻接矩阵 $A$ ,对于其中的每一个元素以 $p$ 的概率置0。 这看起来和 dropout 如出一辙,事实上在实现的时候,也是采用 dropout 算子来对邻接矩阵进行处理的(dropout 算子在每一轮梯度下降时,使每一个神经元以 $p$ 的概率随机置0,使得被置0的神经元无法进行梯度更新)。</p><p>这可以说就是该方法的全部了。没错,就是这么简单。那么作者又是怎么把这么一个两句话就能讲完的方法写成一篇论文的呢?故事由此开始。</p><h2 id="Overfitting-and-Oversmoothing"><a href="#Overfitting-and-Oversmoothing" class="headerlink" title="Overfitting and Oversmoothing"></a>Overfitting and Oversmoothing</h2><p><img src="https://imagehost.vitaminz-image.top/20230806161528.png" alt="image.png"></p><p>Overfitting 是机器学习的常用概念了,具体来说就是训练集上的效果不错,但在测试集上的效果却很差。Oversmoothing 则是由Li等人<sup id="fnref:2" class="footnote-ref"><a href="#fn:2" rel="footnote"><span class="hint--top hint--rounded" aria-label="Q. Li, Z. Han, and X.-M. Wu, “Deeper insights into graph convolutional networks for semi-supervised learning,” in _Proceedings of the AAAI conference on artificial intelligence_, 2018.">[2]</span></a></sup>提出来的概念,而后被[[Notes for Collection 1#<strong>JKN</strong> Representation Learning on Graphs with Jumping Knowledge Networks|JKN]]、[[Notes for Collection 1#<strong>PPNP</strong> Predict then Propagate Graph Neural Networks meet Personalized PageRank|PPNP]]等模型的论文里具体说明并尝试解决的问题。具体来说,它是指GNN在层数增多后,由于特征传播与聚合的操作,所有特征表示都趋向一个稳定值,其原理类似于随机游走。这使得后期的训练变得困难,具体表现为梯度下降优化时发生梯度消失,从而使得训练损失难以下降。</p><p>而 Drop Edge 的引入可以改善这2个问题。</p><h3 id="Overfitting"><a href="#Overfitting" class="headerlink" title="Overfitting"></a>Overfitting</h3><p>关于过拟合,作者定性地说明了该方法是通过对数据的随机扰动而对数据进行增强改善,其类似于图像分类中对图像进行拉伸、旋转等变换。另外,这种数据扰动从期望上看与原数据相比是无偏的。如果我们以概率 $p$ 删除边,则 DropEdge 只会将邻居聚合的期望上乘上 $(1-p)$(因为采样符合伯努利分布),而这个乘数将在对其归一化后实际上消失了,因此,DropEdge不会改变邻居聚合的期望。综上,DropEdge可以看作是一个无偏数据增强技术。</p><h3 id="Oversmoothing"><a href="#Oversmoothing" class="headerlink" title="Oversmoothing"></a>Oversmoothing</h3><p>对于过度平滑,作者花了较大笔墨去证明。其过程有些复杂,暂时看不太懂[[[WHY]]]。</p><p>但仍然可以从实验的角度来验证这一点。实验中,作者使用该层结点表示与上一层结点表示的欧几里得距离来衡量。由下图可以知道,在训练之前(模型采用初始化权重),可以发现经过DropEdge后,相邻两层结点表示的差距始终大于未使用DropEdge的模型。图b为经过150个epoch训练后的结果,发现在5和6层,其差异为0。从训练损失来看,没有通过DropEdge根本训练不动,原因很简单,第5和第6层差异为0,从而发生了严重的梯度消失现象,无法使用梯度下降优化。</p><p><img src="https://imagehost.vitaminz-image.top/20230806164029.png" alt="image.png"></p><h2 id="Hyperparameter"><a href="#Hyperparameter" class="headerlink" title="Hyperparameter"></a>Hyperparameter</h2><p>该论文的超参数也是值得一提的。作者使用的超参数由下表列出。</p><p><img src="https://imagehost.vitaminz-image.top/20230806164516.png" alt="image.png"></p><p>其中withloop为方法self feature modeling,来源于Fout等人的工作<sup id="fnref:3" class="footnote-ref"><a href="#fn:3" rel="footnote"><span class="hint--top hint--rounded" aria-label="A. Fout, J. Byrd, B. Shariat, and A. Ben-Hur, “Protein Interface Prediction using Graph Convolutional Networks,” in _Advances in Neural Information Processing Systems_, Curran Associates, Inc., 2017. Accessed: Aug. 06, 2023. [Online]. Available:[https://proceedings.neurips.cc/paper_files/paper/2017/hash/f507783927f2ec2737ba40afbd17efb5-Abstract.html](https://proceedings.neurips.cc/paper_files/paper/2017/hash/f507783927f2ec2737ba40afbd17efb5-Abstract.html)">[3]</span></a></sup>,其公式如下。<br>$$ H^{(l+1)} = \sigma (\tilde AH^{(l)}W^{(l)} + H^{(l)}W^{(l)}_{self})$$<br>表中的normalization为传播模型,它使用了如下4种模式,都是比较流行的传播模式。</p><p><img src="https://imagehost.vitaminz-image.top/20230806164542.png" alt="image.png"></p><h2 id="Dropout-and-Layer-wise-DropEdge"><a href="#Dropout-and-Layer-wise-DropEdge" class="headerlink" title="Dropout and Layer-wise DropEdge"></a>Dropout and Layer-wise DropEdge</h2><p>Dropout 可以看作是 DropEdge 的一种特殊情形,因为当结点特征被丢弃,那么所连接的边也就没了。作者通过实验说明了 Dropout 和 DropEdge 有互补的作用,联合使用可以使得效果更好。</p><p>此外,作者还提出了 Layer-wise DropEdge。上述所使用的 DropEdge 都是针对一整个模型的,也就是说,每一次梯度下降使用一次 DropEdge 得到的 $A_{drop}$ 是针对模型的所有层。而Layer-wise 指的是每一层都独立使用一次 DropEdge,也就是说每一层的 $A_{drop}$ 是不一样的。作者通过实验说明,Layer-wise DropEdge缺失可以降低训练损失,但在验证集上的损失并没有下降。因此,作者更建议使用对面向模型的 DropEdge,而非 Layer-wise,这样可以防止过拟合,并减少不必要的计算。</p><p><img src="https://imagehost.vitaminz-image.top/20230806181232.png" alt="image.png"></p><h2 id="Conclusion"><a href="#Conclusion" class="headerlink" title="Conclusion"></a>Conclusion</h2><p>该论文借鉴了 dropout,仅仅对模型进行了一个很简单的改动,却在各个数据集上取得了非常不错的效果。也从理论上证明作者的方法可以一定程度上缓解Over Smoothing的问题。文章也教会我们如何将一个简短的方法写长,即使用大量的分析、比较,再辅之以理论证明,就可以讲好一个简单又精彩的故事。</p><h1 id="AdaGCN-Adaboosting-Graph-Convolutional-Networks-into-Deep-Models"><a href="#AdaGCN-Adaboosting-Graph-Convolutional-Networks-into-Deep-Models" class="headerlink" title="AdaGCN Adaboosting Graph Convolutional Networks into Deep Models"></a><strong>AdaGCN</strong> Adaboosting Graph Convolutional Networks into Deep Models</h1><h2 id="Adaboost"><a href="#Adaboost" class="headerlink" title="Adaboost"></a>Adaboost</h2><p>论文首先是借鉴了 Adaboost 方法。</p><p>[[Ensemble Learning#Random Forest|Adaboost]] 是一种集成学习的方法。该方法可以使一组弱分类器组成一个强的集成分类。但由于其用于二分类,作者使用它的多分类形式,SAMME<sup id="fnref:4" class="footnote-ref"><a href="#fn:4" rel="footnote"><span class="hint--top hint--rounded" aria-label="T. Hastie, S. Rosset, J. Zhu, and H. Zou, “Multi-class AdaBoost,” _Stat. Interface_, vol. 2, no. 3, pp. 349–360, 2009, doi: [10.4310/SII.2009.v2.n3.a8](https://doi.org/10.4310/SII.2009.v2.n3.a8).">[4]</span></a></sup>。</p><p><img src="https://imagehost.vitaminz-image.top/20230810160006.png" alt="image.png"></p><p>其区别就在于 $\alpha$ 的式子增加了 $log(K-1)$ 这一项。直观地去理解,会发现,当每一个类别的误差率大于 $\frac{1}{K}$ 时,$\alpha$ 就会大于0,这符合原来二分类的性质。</p><p>为了方便计算以及增强模型的收敛性质,作者使用它的一个改良版本 SAMME.R (R for real)。以下为它的算法描述。</p><p><img src="https://imagehost.vitaminz-image.top/20230810150249.png" alt="image.png"></p><p>它的主要区别在于将误差率的公式,由原来的指示函数,更改为一个加权概率函数。随后的权重计算公式以及加权分类结果是通过解决以下优化方程得到的。<br>$$<br>\begin{align}<br>&\min _{h(x)} \operatorname{E} \bigg( \exp \bigg( -\frac{1}{K} Y^T\big(f^{(m−1)}(x)+h(x)) \bigg) |x \bigg) \<br>&subject<del>to</del>h_1(x)+···+ h_K(x)=0<br>\end{align}<br>$$<br>该方程旨在最小化集成分类器的指数损失。<strong>其中还需注意的一个是</strong>,其中的数据标签的向量$y =(y_1,…,y_K )^T$,它并不是一个 0/1 向量,它的定义为<br>$$<br>yk =<br>\left{<br>\begin{align}<br>&1, &&if<del>c = k, \<br>&−\frac{1}{K−1} , &&if</del>c \ne k.\<br>\end{align}<br>\right.<br>$$</p><h2 id="RNN-like"><a href="#RNN-like" class="headerlink" title="RNN-like"></a>RNN-like</h2><p>该论文提出的模型架构可以概括为:<br>$$<br>\begin{align}<br>&\hat{A}^lX = \hat A·(\hat A^{l−1} X ) \<br>&Z^{(l)} = f <em>{\theta}^{(l)} (\hat A^l X ) \<br>&Z = \operatorname{AdaBoost}(Z^{(l)}) %不能有多余的空行,不然在网页中也可能除左<br>\end{align}<br>$$<br>其中 $f</em>{\theta}^{(l)}$ 为非线性函数,比如说2层的神经网络$f^{(l)}<em>{\theta}(\hat A^l X) = \operatorname{ReLU}(\hat A^l XW ^{(0)})W^{(1)}$。但是,这里需要注意的是,该模型和Adaboost有一个重要区别,那就是对于所有的 $l$ 来说,$f</em>{\theta}^{(l)}$ 使用 $\theta$ 参数是基于上一层分类器 $f_{\theta}^{(l-1)}$。而一般的Adaboost各个基分类器的参数都是独立的。</p><p>模型架构可以由如下图表示。</p><p><img src="https://imagehost.vitaminz-image.top/20230810162254.png" alt="image.png"></p><p>可以看到,这样的结构和RNN非常相像,每一层都由来自于上一层的输入,使用的 $\theta$ 参数是共用的。也就是说,AdaGCN中每一层基分类器 $f_{\theta}^{(l)}$ 都是在上一层分类器 $f_{\theta}^{(l-1)}$ 的参数上进行更行的,这一点是和Adaboost的一个重要区别。</p><h2 id="Algorithm"><a href="#Algorithm" class="headerlink" title="Algorithm"></a>Algorithm</h2><p>以下为算法的伪代码。</p><p><img src="https://imagehost.vitaminz-image.top/20230810145230.png" alt="image.png"></p><p>该流程基本在参数更新中,基本仿照了SAMME.R的算法流程。值得注意的是,该算法和我们之前所看到的那些模型有个重要的不同,那就是最后的输出不是一个模型,而是一个最终的预测结果,这一点是有些奇怪的。</p><p>因为常理来说,在训练过程之后,我们将得到一个模型,最终再使用这个模型运用到验证集、测试集上。也就是说训练和预测应当是分开的,但这个算法流程貌似是合起来的。但如果我们回想一下GCN的训练过程也许就能明白,GCN的训练是一种transductive learning,也就是说其预测的东西都是模型所见过的。所以GCN训练时,将整个数据集都输入了进去,但只使用少量标签进行训练。事实上,每次训练都可以得到整个数据集的标签,但计算损失时仅使用训练数据的标签。</p><p>那么算法也同理,训练时输入整个数据集(包括训练集、验证集、测试集),但计算损失、拟合参数时只是用训练集部分的标签。因此它的输出也自然可以是整个数据集的标签。但如果偏要将训练和预测完分离开来,则需要记录训练时每一层分类器的参数$\theta$,还要在预测时将第3、6、7步去掉。</p><h2 id="Computational-Comlexity-and-Netowrk-Depth"><a href="#Computational-Comlexity-and-Netowrk-Depth" class="headerlink" title="Computational Comlexity and Netowrk Depth"></a>Computational Comlexity and Netowrk Depth</h2><p>AdaGCN的计算复杂度,主要的有点就是可以提前计算传播矩阵,也就是对于第 $l$ 层来说,$B^{l}=\hat A^{l}X$ 可以被提前计算,这使得不需要在训练的每个epoch都再算一遍,这和SGC类似。</p><p><img src="https://imagehost.vitaminz-image.top/20230812165605.png" alt="image.png"></p><p>经过作者的实验,也确实展现出它的高效。但是上面右边这张图就有些奇怪了,随着网络层次的加深,SGC每个epoch训练时间的增长远超AdaGCN,这不禁令人怀疑。因为对于SGC来说,同样是提前计算了传播矩阵和特征矩阵的乘积,并且其算法要比AdaGCN简单得多,怎么可能会比它慢呢?个人感觉,作者对SGC并没有采取提前计算的方法。</p><p>此外作者同样指出,AdaGCN虽然每个epoch训练时间比较短,但它所需要的epoch却更多一些。</p><p><img src="https://imagehost.vitaminz-image.top/20230812170559.png" alt="image.png"></p><p>此外,作者还比较了各个模型加大深度后性能的变化。这里需要注意的是,模型的深度指的是特征传播的深度。事实上,AdaGCN有2个深度,一个是基分类器的数量,也就是作者所谓的深度,还有一个是 $f_{\theta}$ 这一非线性函数,事实上它可以是个深度神经网络,同样也可以有深度。作者的模型,也确实将网络深度与传播深度分离开来了,这一点和[[Notes for Collection 1#<strong>PPNP</strong> Predict then Propagate Graph Neural Networks meet Personalized PageRank|PPNP]]比较类似。</p><p>在结果上,随着深度加深,其性能确实不会有什么衰减,一定程度上减轻了Oversmoothing的问题,甚至可能略有提升。</p><p>不同于其他论文,改论文还讨论了模型的深度该如何确定,也就是基分类器的个数如何选择。其引用了[[#附录#Vapnik–Chervonenkis dimension|VC-dimesion]]的理论,大该是想说明,随着基分类器个数的增加,模型的VC维上界也会增大,从而提高模型过拟合的风险。但是总之,作者也就是说在实践中用交叉验证的方法来确定模型深度(讲了和没讲一样)。</p><h2 id="Connection-with-PPNP-and-APPNP"><a href="#Connection-with-PPNP-and-APPNP" class="headerlink" title="Connection with PPNP and APPNP."></a>Connection with PPNP and APPNP.</h2><p>作者认为,AdaGCN是自适应版本的APPNP。这里我没怎么看懂。我的理解时在传播过程中,APPNP里MLP参数是固定的,而AdaGCN则不是,每一层传播的参数不一样,即基分类器参数不同。因此他更具备适应性。另外有一点和APPNP是一样的,就是MLP的深度和传播深度是独立的。</p><p><img src="https://imagehost.vitaminz-image.top/20230813211822.png" alt="image.png"></p><p>作者通过实验比较,发现在低标签率的情况下,其效果也超过APPNP(这曾是APPNP的优点之一)。</p><h2 id="Conclusion-1"><a href="#Conclusion-1" class="headerlink" title="Conclusion"></a>Conclusion</h2><p>AdaGCN借鉴集成学习中Adaboost的方法,设计了一个RNN-like的算法。其效率和性能都比较强。</p><h1 id="GCNII-Simple-and-Deep-Graph-Convolutional-Networks"><a href="#GCNII-Simple-and-Deep-Graph-Convolutional-Networks" class="headerlink" title="GCNII Simple and Deep Graph Convolutional Networks"></a><strong>GCNII</strong> Simple and Deep Graph Convolutional Networks</h1><h2 id="Residual-Connection-and-Identity-Map"><a href="#Residual-Connection-and-Identity-Map" class="headerlink" title="Residual Connection and Identity Map"></a>Residual Connection and Identity Map</h2><p>GCNII 的模型可以由如下公式表示:<br>$$<br>H^{(l+1)} =\sigma\bigg(\bigg( (1−\alpha_l) \tilde PH^{(l)} +\alpha_l H^{(0)}\bigg)\bigg( (1−\beta_l)I_n +\beta_l W^{(l)}\bigg)\bigg)<br>$$<br>它由2部分组成:</p><ul><li>初始层残差连接:$(1−\alpha_l) \tilde PH^{(l)} +\alpha_l H^{(0)}$,注意这里的 $H^{(0)}$ 不必是 $X$,它可以是 $X$ 经过线性变换的结果,如 $XW$。</li><li>自身映射:$(1−\beta_l)I_n +\beta_l W^{(l)}$</li></ul><p>著名的 ResNet 就是利用残差连接来避免梯度消失问题,从而使得神经网络可以做得很深。但 ResNet 的残差连接,是和前几层做加法运算,这事实上在 GCN 中就已经尝试过使用这种方法了,确实在一定程度上可以缓解 Oversmoothing 的问题,但效果随着深度地加深,并没有变好。</p><p>于是在本文使用的残差是与初始层做的残差。事实上这一迭代形式和 [[Notes for Collection 1#PPNP and APPNP|APPNP]] 非常相似。这里提供了一个新的视角来理解APPNP,即和初始层做残差连接的神经网络。但和 APPNP 的区别也十分明显,APPNP 在传播过程并没有参数学习的过程,但 GCNII 每一次传播乘上参数矩阵,并使用非线性函数压缩。也就是说,在 GCNII 中,它并没有讲特征传播的深度和神经网络的深度独立开来,二者是统一的。</p><p>这里 GCNII 每一层使用的参数矩阵包含了一个自身的映射。从优化的角度来看,它有2个作用<sup id="fnref:5" class="footnote-ref"><a href="#fn:5" rel="footnote"><span class="hint--top hint--rounded" aria-label="M. Hardt and T. Ma, “Identity Matters in Deep Learning.” arXiv, Jul. 20, 2018. doi: [10.48550/arXiv.1611.04231](https://doi.org/10.48550/arXiv.1611.04231).">[5]</span></a></sup>:1. 使得参数矩阵 $W$ 的范数很小,这样可以使用较大的正则化系数防止过拟合;2. 优化方程的驻点就是全局最小值。</p><p>另外作者还给出了 $\beta_l$ 的选取,它的选择对于不同深度来说应当使不以言的,随着深度的加深,为了防止过拟合以及梯度消失,$\beta_l$ 应当变小,让单位矩阵占据更大比重。作者给出了调整公式:<br>$$<br>\beta_l=log(\frac{\lambda}{l}+1)\approx\frac{\lambda}{l}<br>$$<br>其中 $\lambda$ 时超参数。但他貌似没有说原因,我也不知道这公式是哪来的[[[WHY]]]。</p><p>另外作者还介绍了自身映射和压缩感知(Compressive Sensing)领域中的算法 <a href="https://zhuanlan.zhihu.com/p/555651497">ISTA</a> (或者<a href="https://www.cnblogs.com/louisanu/p/12045861.html">iSTA</a>)进行了比较。ISTA 是线性问题的逆问题,通过求解以下 Lasso 正则化形式的优化方程<br>$$<br>\min_{x\in \mathcal{R}^n} \frac{1} {2} ‖Bx − y‖<em>2^2 + \lambda‖x‖_1.<br>$$<br>与我们常见的线性回归问题不同,这里 $B,y$ 是已知量,而 $x$ 则是待求量。它可以看作是一个信号重建的问题,经过亚采样的信号进行重建。和梯度下降类似,最终可以通过如下的迭代求解 $x$。<br>$$<br>x</em>{t+1} = P_{\mu_tλ} (x_t − \mu_tB^T Bx_t + \mu_tB^T y) ,<br>$$<br>$P_β(·)$ 是一个带阈值的线性函数,他的曲线如下图所示。</p><p><img src="https://imagehost.vitaminz-image.top/20230816154534.png" alt="image.png"></p><p>如果令 $W=-B^TB$,那么就有<br>$$<br>x_{t+1} = P_{\mu_tλ} \bigg((I_n + \mu W)x_t + \mu_tB^T y\bigg) ,<br>$$<br>于是作者认为, $(I_n + \mu W)$ 就是自身映射,而 $\mu_tB^T y$ 则可以看作是初始层残差连接。另外 $P_β(·)$ 的作用可以类比为 GCNII 的非线性函数,如 $ReLU$。作者的想法也很可能是从该算法上得到的启发。</p><p>另外,作者还提出了 GCNII 的一个变式 GCNII*,公式如下。主要区别在于,他对初始化层也乘上了一个带自身映射的参数矩阵。<br>$$<br>H^{(l+1)} =\sigma\bigg((1−\alpha_l) \tilde PH^{(l)} \bigg( (1−\beta_l)I_n +\beta_l W^{(l)}_1 \bigg)+\alpha_l H^{(0)}\bigg( (1−\beta_l)I_n +\beta_l W^{(l)}_2 \bigg)\bigg)<br>$$</p><p><img src="https://imagehost.vitaminz-image.top/20230816165539.png" alt="image.png"></p><p>根据最后的结果,可以看到 GCN 和 GCNII 即便在高达64层的深度,性能也并未有明显减弱,甚至会达到较高的水准。但值得一提的是,表格中没有展现 APPNP 的性能,事实上 APPNP 的性能也是有如此特点的。</p><h2 id="Spectral-Aanlaysis"><a href="#Spectral-Aanlaysis" class="headerlink" title="Spectral Aanlaysis"></a>Spectral Aanlaysis</h2><p>作者首先分析了<strong>带残差连接</strong>的 GCN,其最后也是会收敛到一个值,$π = \frac{\langle \tilde{D}^{1/2} 1,x\rangle} {2m+n}$。</p><p>作者对每一层的结点做了分析,最后得到对于第 $K$ 层的 $j$ 结点有:<br>$$<br>h^{(K)}(j)=\sqrt{d_j+1}\bigg(\sum_{i=1}^{n}\frac{\sqrt{d_i+1}}{2m+n}x_i\pm\frac{\sum_{i=1}^nx_i\big(1-\frac{\lambda_\tilde{G}^2}{2}\big)^K}{\sqrt{d_j+1}}\bigg)<br>$$<br>其中,$\lambda_\tilde{G}$ 带自环的规范化拉普拉斯矩阵的 $\tilde L = I_n − \tilde{D}^{−1/2} \tilde{A} \tilde{D}^{−1/2}$ 的最小非0特征值,$m,n$ 分别为结点和边的数量。可以看到,对于结点 $j$ 来说,如果有更大的度数 $d_j$,那么就有更大的 $\sqrt{d_j + 1}$,其收敛到终态的速度也就越快(注意 $\pm$ 后一项会趋向于0)。</p><p>作者也从 Spectral 的角度分析了 GCNII,作者认为,一个 K 层的 GCNII,他可以模拟以下多项式:<br>$$<br>\sum^{K}_{l=0}\theta_l\tilde{L}^{l}<br>$$<br>且参数 $\theta$ 是 arbitrary 的。这个形式在 [[Notes for Collection 1#ChebNet|GCN]] 中就有提及,但需注意的是,作者认为 GCN 中的 $\theta$ 是 fixed 的[[[WHY]]]。我对这里的 fixed 和 arbitrary 不是很理解。个人感觉,大概是说对于 GCN 来说,对于某个任务他最后学得的最优参数只有一种可能,但是对于 GCNII 来说,可以用过调节 $\alpha$ 和 $\beta$ 来获得任意可能的解。</p><p><img src="https://imagehost.vitaminz-image.top/20230817105644.png" alt="image.png"></p><p>作者也做了实验的验证。首先结点度数对 GNN 的影响。作者根据结点的根据度数的范围 $[2^i, 2^{i+1})$ , $i = 0, . . . , \infty$,分成了一个个组。并分别计算各个组的准确率。得到上图中的 figure 1。可以看到,对于度数会越高的组别,其准确率下降的越厉害。尤其在 Cora 和 Citeseer 度数 100 之后,甚至连 GCN-2(residual) 准确率降至0。</p><p>此外作者还做了消融实验,验证了添加的2个模块的有效性。但从图中也可以看出,虽然 GCN 做不深,但浅层的 GCN 效果也不错,而做深的 GCNII 其实并没有好太多,不得不怀疑做深是否有必要。</p><h2 id="Limitation"><a href="#Limitation" class="headerlink" title="Limitation"></a>Limitation</h2><p>文章聚焦于把网络做深,但是做深的效果并没有好太多。这种深度加大的行为,性价比可能并不高。</p><h2 id="Conclusion-2"><a href="#Conclusion-2" class="headerlink" title="Conclusion"></a>Conclusion</h2><p>作者通过加入初始化残差和自身映射,引入2个超参数调节模型,确实能把网络做的很深,不至于梯度消失、过拟合或者说Oversmoothing,其效果也略有上升,但是其性能的调高很有限。不过在大模型时代,不顾一切地把网络做深也许确实能够带来性能的极大提升,这或许也是为什么这么多论文想把 GNN 做深的原因了。</p><h1 id="DeeperGCN-All-You-Need-to-Train-Deeper-GCNs"><a href="#DeeperGCN-All-You-Need-to-Train-Deeper-GCNs" class="headerlink" title="DeeperGCN All You Need to Train Deeper GCNs"></a><strong>DeeperGCN</strong> All You Need to Train Deeper GCNs</h1><h2 id="Massage-Passing"><a href="#Massage-Passing" class="headerlink" title="Massage Passing"></a>Massage Passing</h2><p>论文将 GNN 抽象成了3个阶段:1. Message Construction,2. Message Aggregation,3. Vertex Update。</p><p>这在 PyG 中,有着同样的分类。具体参考 *<a href="https://pytorch-geometric.readthedocs.io/en/latest/tutorial/create_gnn.html">Creating Message Passing Networks</a>*。公式可描述如下:<br>$$<br>\mathbf{x}_i^{(k)} = \gamma^{(k)} \left( \mathbf{x}<em>i^{(k-1)}, \bigoplus</em>{j \in \mathcal{N}(i)} , \phi^{(k)}\left(\mathbf{x}_i^{(k-1)}, \mathbf{x}<em>j^{(k-1)},\mathbf{e}</em>{j,i}\right) \right),<br>$$<br>其中 $\phi,\bigoplus, \gamma$ 分别表示1、2、3这3个阶段。其中,$\bigoplus$ 是一个 Permutation Invariant 的函数,而 $\phi_k,\gamma$ 往往是一个可微函数,比如MLP。</p><p>后2个阶段是常用的,但是第1个阶段,可能并未明确点出。其实,在 GCN 中,我可以将对结点特征做 Normalize 作为 Message Construction。即所有的 Message 是带权重的特征,且权重为 $1/\sqrt{d_id_j}$。因此 $\phi$ 函数的输入需要输入结点可能需要源节点和邻居结点以及他们的边信息。</p><p>另外,在之前大部分论文中,遇到的数据集边是没有特征的,如果边存在特征,那 Message 该如何构建呢?这里提供一种论文使用的办法。</p><p><img src="https://imagehost.vitaminz-image.top/20230819213010.png" alt="image.png"></p><p>首先,我们在对离散数据编码时,往往常用 one-hot 编码,比如在 NLP 中,在语料库中存在 N 个 Token,那就编码为 N 维的 one-hot 向量。但是 one-hot 向量有时候因为过度稀疏、维度过大,并且无法表示各个向量之间的关系,比如相近语义的词语在向量表示时应当给予更接近的距离,而非 one-hot 中,所有向量都是单位正交的。所以,有一种常用的方法,就是利用线性变换,即乘上一个参数矩阵,来对每一词语重新编码,这个过程叫做 embedding。经过这样的 embedding,每一个词语会获得新的表示想浪。如上图所示,将每一个特征值都从原来的3维向量编码成了2维向量。</p><p><img src="https://imagehost.vitaminz-image.top/20230819213112.png" alt="image.png"></p><p>而在很多图任务中,结点特征以及边特征的每一维取值也是离散的,也就是说特征的每一维都可以进行 ont-hot 编码,比如结点的第一维特征可能取值是0和1,那么它可以编码为 $2 \times 2$ 的 one-hot 矩阵,同时也可以将其做一个 embedding 的操作。那么我们对特征的每一维都可以做相同的操作,将每一维的特征的每一个取值都编码成相同维度的向量。如上图所示,假设输入特征,第一维有2种取值,第2维有3种取值,第3维有2种取值。对每一维的特征都做一个 embedding,使得每一维的特征的每一个特征取值都对应到一个维度为2的 embedding 向量。那么最后输入的特征,根据每一维的特征取值,将对应的 embedding 向量取出相加,就是最后的特征向量了。</p><p>这样的操作,同时作用在结点特征和边特征上,使他们最后的 embedding 向量维度保持一致,这样就能更容易进行特征融合,比如把边的特征加到结点特征上,得到最终的 message。</p><p>需要注意的是,在做 embedding 的时候,也就是做了一个线性变换,但变换的参数矩阵中的参数是不知道的,有的可能会使用通过一些手段预训练的参数。但总之,一般会跟随整个模型一起训练。</p><h2 id="Generalized-Aggregator"><a href="#Generalized-Aggregator" class="headerlink" title="Generalized Aggregator"></a>Generalized Aggregator</h2><p>该文的一个核心内容就是作者提出了一个通用的信息聚合器。我们知道,常用的聚合器有mean, max, sum等。作者提出了2个可以根据参数连续变化的聚合器,并且它的变化范围是mean ~ max之间。但其中并不包含 sum,作者说 sum 容易被包括,但事实上他在附录中将其作为 Future Work。</p><p>首先是 Softmax 聚合器的公式如下:<br>$$<br>\operatorname{SoftMax_Agg}<em>{\beta}(\cdot)=\sum</em>{u\in \mathcal{N}(v)}\frac{\exp(\beta \mathbf{m}<em>{vu})}{\sum</em>{i\in \mathcal{N}(v)}\exp(\beta\mathbf{m}<em>{vi})}\cdot \mathbf{m}</em>{vu}<br>$$<br>其中,$m_{vu}$ 为 message。不过这里需要注意的是,以上是论文的形式,但在代码中发现一些区别,具体参考 *[[Code for Collection 2#SoftMax - Max Firstly|SoftMax - Max Firstly]]*。</p><p>可以发现,当 $\beta =0$ 时,它是 mean,$\beta \rightarrow +\infty$ 时,它是 max(直观的理解就是,当趋向于无穷时,最大的数和其他数的差距拉到了无穷大,所以只有最大数前的系数趋向于1,其余为0)。</p><p>此外,作者还提出了 PowerMean 聚合器:<br>$$<br>\operatorname{Power_Agg}(\cdot)=\bigg(\frac{1}{|\mathcal{N}(v)}\sum_{u\in\mathcal{N}{v}}\mathbf{m}^{p}_{vu}\bigg)^{\frac{1}{p}}<br>$$<br>这里 $p$ 是个非0值,当 $p =1$ 时,它是 mean,$p \rightarrow +\infty$ 时,它是 max,且当 $p=-1$ 时是几何均值,当 $p\rightarrow 0$ 是调和均值。具体可参考 *<a href="https://mathworld.wolfram.com/PowerMean.html">PowerMean</a>*。</p><p>值得注意的是,PowerMean 聚合器的特征值不能为0。实际上的实现,也和公式的表示略有区别,具体可参考 *[[Code for Collection 2#PowerMean - Clamp Firstly|PowerMean - Clamp Firstly]]*。</p><p><img src="https://imagehost.vitaminz-image.top/20230818220732.png" alt="image.png"></p><p>另外作者认为,作者用以上的图来表示它所提出的聚合器所覆盖的范围。按照上图所示,SoftMax 和 PowerMean 仅有 Mean,Max 和 Min 的交集[[[WHY]]]。</p><p>另外,作者还尝试将这2个聚合器往 sum 上靠,然后它提出了以下的聚合器形式:<br>$$<br>∣\mathcal{N}(v)∣^y \cdot \operatorname{Agg}(\cdot)<br>$$<br>他发现,当 $y=1$,且 Agg 为 Mean 时,聚合器为 Sum。另外,在他实现的代码中,也做了进一步的调整,具体可参考 <em>[[Code for Collection 2#How to get Sum|How to get Sum]]</em>.</p><h2 id="GENeralized-Aggregation-Networks-GEN"><a href="#GENeralized-Aggregation-Networks-GEN" class="headerlink" title="GENeralized Aggregation Networks (GEN)"></a>GENeralized Aggregation Networks (GEN)</h2><p>论文中,作者提出了一个新的消息传播(Message Passing)模型,或者也可将其看作是神经网络中的一个 Layer,并命名为 GEN。</p><p>第一步是 Message Construction,正如在 [[#Massage Passing|Message Passing]] 一节中所说,作者的 Message Passing,先通过了 embedding,然后<br>$$<br>\mathbf m^{(l)}<em>{vu} = \rho^{(l)}(\mathbf h^{(l)}<em>v , \mathbf h^{(l)}<em>u , \mathbf h^{(l)}</em>{e</em>{vu}} ) = \operatorname{ReLU}(\mathbf h^{(l)}<em>u + \mathbb{1}(\mathbf h^{(l)} <em>{e</em>{vu}} ) \cdot (\mathbf h^{(l)}</em>{e</em>{vu}} ) + \epsilon, u \in \mathcal{N} (v)<br>$$<br>第二步是 Message Aggregation,也就是进行消息传播,使用的聚合器可以是普通的 Mean/Sum/Max,同样可以设置为在该论文中提出的新型聚合器:Softamax、PowerMean。</p><p>第三步是 Vertex Update,作者首先对特征进行了 Normalization,再将其通过 MLP。其公式如下。其中 $s$ 是一个可学习的参数(作者也比较了采用固定参数对性能的影响,结果说明效果和聚合器有关,整体上使用可学习参数更好)。注意这里的 $\mathbf h_v$ 指的是经过<strong>第一步前</strong>的表示向量,而 $\mathbf m_v$ 则是经过<strong>第二步后</strong>的表示向量。<br>$$<br>\mathbf h^{(l+1)}_v = \phi^{(l)}(\mathbf h^{(l)}_v , \mathbf m^{(l)}_v ) = \operatorname{MLP}(\mathbf h^{(l)}_v + s\cdot\Vert \mathbf h^{(l)}_v\Vert_2 \cdot \frac{\mathbf m^{(l)}_v }{\Vert \mathbf m^{(l)}_v \Vert_2 })<br>$$<br>那么以上就是作者提出的整个 GEN Layer了。</p><p>另外作者还认为 Normalization (BatchNorm/LayerNorm) 拜访位置也会影响性能,他认为 Norm -> ReLU -> GEN 可以带来更好的性能。</p><p><img src="https://imagehost.vitaminz-image.top/20230818202859.png" alt="image.png"></p><p>接着作者将 GEN Layer 和其他结构混合,提出了5个模型,如上图所示。可以看到,主要的区别在于 Normalization (BatchNorm/LayerNorm) 以及 ReLU 的位置不同、是否有残差连接、使用的聚合器是否为论文提出的SoftMax和PowerMean、聚合器的参数是固定的还是可学习的。</p><p>通过这种组合方式,旨在说明每一个变化是否能够提高性能。那么这里要说的是,哪些属于作者的模型。某种程度了上来说都属于。就算是 Plain GCN,它使用的 GEN 采用的是 Mean / Max / Sum 这类朴素的聚合器,但 GEN 中第一步的 Message Construction 和第三步的 Vertex Update 的方式都和其他模型有所不同,因此只要这 5 个模型有一个性能高即可。另外这 5 个模型并不一定是进化的关系,可以将其视作是模型的超参数。</p><h2 id="Limitation-1"><a href="#Limitation-1" class="headerlink" title="Limitation"></a>Limitation</h2><p>对于聚合器中 $p$ 和 $\beta$ 参数,他们的变化范围最大可以到正无穷,即无边界。这是非常不好的,因为最佳参数可能造成计算溢出。</p><h2 id="Conclusion-3"><a href="#Conclusion-3" class="headerlink" title="Conclusion"></a>Conclusion</h2><p>本文的核心主要有以下几点:</p><ol><li>提出了新的 Message Passing 的模型(或者 Layer):GEN,它包含<ol><li>新的 Message Construction 的方式</li><li>新的聚合器,SoftMax 和 PowerMean,他们可以在 Mean 和 Max 聚合器中连续变化</li><li>新的 Vertex Update 的方式,即引入了 Message Normalization</li></ol></li><li>提出了新的 Normalization (BatchNorm / LayerNorm) 的摆放位置</li><li>将以上 2 点和残差连接相互组合,得到5个不同的模型。</li></ol><h1 id="GRAND-Graph-Random-Neural-Networks-for-Semi-Supervised-Learning-on-Graphs"><a href="#GRAND-Graph-Random-Neural-Networks-for-Semi-Supervised-Learning-on-Graphs" class="headerlink" title="GRAND Graph Random Neural Networks for Semi-Supervised Learning on Graphs"></a><strong>GRAND</strong> Graph Random Neural Networks for Semi-Supervised Learning on Graphs</h1><h2 id="Drop-Nodes-and-Random-Propagation"><a href="#Drop-Nodes-and-Random-Propagation" class="headerlink" title="Drop Nodes and Random Propagation"></a>Drop Nodes and Random Propagation</h2><p>第一步:和 [[#<strong>DropEdge</strong> Towards Deep Graph Convolutional Networks On Node Classification|DropEdge]] 的想法相似,该论文采用 Drop Nodes 的方法来增强原数据集。也就是说,每个结点特征 $x$ 将以概率 $p$ 随机置0,由于采样符合伯努利分布,为了使数据的期望和原数据保持一致,我们会再乘上因子 $\frac{1}{1-p}$。这一操作事实上在 dropout 中是默认的,可参考 pytorch 文档 *<a href="https://pytorch.org/docs/stable/generated/torch.nn.Dropout.html?highlight=dropout#torch.nn.Dropout">dropout</a>*。但是要注意 Drop Nodes 是将某一结点的特征全置为0,而 Dropout 是将特征矩阵中的某一元素置为0,其细粒度不同。</p><p>第二步:DropNode 之后,作者提出了另一个操作 Random Propagation。事实上就是将 0 到 K 阶的传播矩阵求个平均值,最后和特征矩阵相乘,这个操作没什么新奇的,它可以比较大程度地保留临近结点的特征。</p><p>第三步:结束以上2个操作后,将其输入到 MLP 预测。</p><p>以上过程可以用如下公式表示:<br>$$<br>\begin{align}<br>M =& \operatorname{drop}(\mathbf{1},p)\times \mathbf{1}^T\<br>% &符号要放在等号后面??<br>\widetilde{X} =& \frac{M\cdot X}{1-p} \tag{Drop Nodes}\<br>\overline{A}^{(s)} =& \frac{1}{s+1}\sum_{i=0}^s \hat A^i\<br>\overline{X}^{(s)} =& \overline{A}^{(s)}\widetilde{X}\tag{Random Propagation}\<br>\widetilde{Z}^{(s)} =& \operatorname{MLP}(\overline{X},\Theta) \tag{Prediction}<br>\end{align}<br>$$</p><p>其中 Drop Nodes 可以首先获取一个 0/1 的 mask,在让 mask 和原数据相乘。 $M$ 为 Mask,$\operatorname{drop}(\cdot,p)$ 就是以 $p$ 的概率将元素随机置 0 的操作,$\mathbf{1}\in \mathbb{R}^{N\times 1}$ 是一个全 1 向量,输入的特征矩阵 $X\in \mathbb{R}^{N\times F}$, $\hat A$ 为传播矩阵。</p><p>值得注意的是,以上公式中,假设总共传播 $S$ 次,那么可以得到 $S$ 个预测结果。</p><h2 id="Sharpening-and-Regularization"><a href="#Sharpening-and-Regularization" class="headerlink" title="Sharpening and Regularization"></a>Sharpening and Regularization</h2><p>从前面一小节得到的 $S$ 个预测结果后,在经过以下的求均值和 sharpening 的操作。<br>$$<br>\begin{align}<br>\overline{Z}&=\frac{1}{S}\sum_{s=1}^{S}\widetilde Z^{(s)}\<br>\overline{Z}<em>{ij}’&= \overline{Z}^{\frac{1}{T}}</em>{ij}\bigg/\sum_{c=0}^{C-1}\overline{Z}_{ic}^{\frac{1}{T}},(j=0,1,…,C-1)<br>\end{align}<br>$$<br>求均值是好理解的,但是可能对 sharpening 不是很能理解。该操作可以让原来的概率分布更极端化,使小的更小,大的更大,尤其当 $T\rightarrow \infty$ 时,对于每个结点对应的预测将是一个 one-hot 向量。我猜测是因为上述求均值的原因,平滑化了结点预测的概率分布,不利于优化,并且得到的分类器也是不合理的。需要注意的是 $T$ 是个超参数。</p><p>那么最后在训练时有 2 个损失,1个是各个 $\widetilde Z^{(s)}$ 和真实标签的交叉熵损失,还有一个则是各个预测如 $\widetilde{Z}^{(s_1)}$ 和 $\widetilde{Z}^{(s_2)}$ 的距离,我们成为一致性 (Consistency) 损失,即我们希望各个层次的预测应当不能相差太多,要尽量的接近。后一项损失我们可以看作是一个正则化项。总的损失可表示为如下公式。<br>$$<br>\begin{align}<br>\mathcal{L}<em>{con} &= \frac{1}{S}\sum</em>{s=1}^{S}\sum_{i=0}^{m-1} Y^T_i \log\widetilde{Z}<em>i^{(s)}\<br>\mathcal{L}</em>{con} &= \frac{1}{S}\sum_{s=1}^{S}\sum_{i=0}^{n-1}\Vert \overline{Z}<em>{i}’-\widetilde{Z}^{(x)}<em>i \Vert_2^2\<br>\mathcal{L} &= \mathcal{L}</em>{sup} + \lambda\mathcal{L}</em>{con}\<br>\end{align}<br>$$</p><h2 id="GRAND"><a href="#GRAND" class="headerlink" title="GRAND"></a>GRAND</h2><p><img src="https://imagehost.vitaminz-image.top/20230822115150.png" alt="image.png"></p><p>最后我们在整体看一下论文提出的模型,它可以用以上的图来表示,尤其是 DropNode 一块表现的是比较清晰的。</p><p>但更具体的,还可以用如下的伪代码来表示</p><p><img src="https://imagehost.vitaminz-image.top/20230822115204.png" alt="image.png"></p><h2 id="Three-Issues"><a href="#Three-Issues" class="headerlink" title="Three Issues"></a>Three Issues</h2><p>该论文从开始就提出了 GCN 广泛存在的 3 个问题:1. 过拟合;2. 鲁棒性差;3. 过度平滑。</p><p>我们来看一下该模型是否有一定程度上缓解了这些问题。</p><p><img src="https://imagehost.vitaminz-image.top/20230822144103.png" alt="image.png"></p><p>首先是过拟合,也就是模型的泛化能力(generalization),上图中的 Figure 2 表现了不同模型训练损失和验证集损失的关系,可以看到 GRAND 模型在添加了 RP(Random Propagation) 和 CR(Consistency Regularization)模块后,训练损失和验证集损失是比较接近的。</p><p><img src="https://imagehost.vitaminz-image.top/20230822144111.png" alt="image.png"></p><p>另外我们再来看一下鲁棒性,作者通过往数据集中随机增加伪边,以及基于元学习[[[WHAT]]]的增减边来对原数据集进行破坏,从 Figure 3 可以看出 GAT 对破坏后的数据集是比较敏感的,而GRAND展现出比较优良的性能。</p><h2 id="Limitation-2"><a href="#Limitation-2" class="headerlink" title="Limitation"></a>Limitation</h2><p>关于缺陷,在论文中也有提到,GRAND 是基于同配性假设的,即物以类聚。假如图缺乏同配性,即相似的结点并没有相互连接,那么效果可能会不好。这是很多半监督学习以及图神经网络共同的缺陷。</p><h2 id="Conclusion-4"><a href="#Conclusion-4" class="headerlink" title="Conclusion"></a>Conclusion</h2><p>作者的模型可以归为2个技术,一个是数据增强技术,即作者所谓的 Drop Nodes 和 Random Propagation,另一个则是正则化技术,通过让多种预测结果尽量接近,而制定正则化项。作者也确实一定程度上缓解了它提出的3个问题。</p><h1 id="附录"><a href="#附录" class="headerlink" title="附录"></a>附录</h1><h2 id="Vapnik–Chervonenkis-dimension"><a href="#Vapnik–Chervonenkis-dimension" class="headerlink" title="Vapnik–Chervonenkis dimension"></a>Vapnik–Chervonenkis dimension</h2><section class="footnotes"><div class="footnote-list"><ol><li><span id="fn:1" class="footnote-text"><span>Y. Rong, W. Huang, T. Xu, and J. Huang, “DropEdge: Towards Deep Graph Convolutional Networks on Node Classification.” arXiv, Mar. 12, 2020. doi: <a href="https://doi.org/10.48550/arXiv.1907.10903">10.48550/arXiv.1907.10903</a>.<a href="#fnref:1" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:2" class="footnote-text"><span>Q. Li, Z. Han, and X.-M. Wu, “Deeper insights into graph convolutional networks for semi-supervised learning,” in <em>Proceedings of the AAAI conference on artificial intelligence</em>, 2018.<a href="#fnref:2" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:3" class="footnote-text"><span>A. Fout, J. Byrd, B. Shariat, and A. Ben-Hur, “Protein Interface Prediction using Graph Convolutional Networks,” in <em>Advances in Neural Information Processing Systems</em>, Curran Associates, Inc., 2017. Accessed: Aug. 06, 2023. [Online]. Available:<a href="https://proceedings.neurips.cc/paper_files/paper/2017/hash/f507783927f2ec2737ba40afbd17efb5-Abstract.html">https://proceedings.neurips.cc/paper_files/paper/2017/hash/f507783927f2ec2737ba40afbd17efb5-Abstract.html</a><a href="#fnref:3" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:4" class="footnote-text"><span>T. Hastie, S. Rosset, J. Zhu, and H. Zou, “Multi-class AdaBoost,” <em>Stat. Interface</em>, vol. 2, no. 3, pp. 349–360, 2009, doi: <a href="https://doi.org/10.4310/SII.2009.v2.n3.a8">10.4310/SII.2009.v2.n3.a8</a>.<a href="#fnref:4" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:5" class="footnote-text"><span>M. Hardt and T. Ma, “Identity Matters in Deep Learning.” arXiv, Jul. 20, 2018. doi: <a href="https://doi.org/10.48550/arXiv.1611.04231">10.48550/arXiv.1611.04231</a>.<a href="#fnref:5" rev="footnote" class="footnote-backref"> ↩</a></span></span></li></ol></div></section>]]></content>
<categories>
<category>GNN</category>
</categories>
<tags>
<tag>GNN</tag>
<tag>图</tag>
<tag>综述</tag>
<tag>论文解读</tag>
</tags>
</entry>
<entry>
<title>论文速读<二>:GNN系列</title>
<link href="/2022/09/02/gnn/lun-wen-su-du-2-gnn-xi-lie/"/>
<url>/2022/09/02/gnn/lun-wen-su-du-2-gnn-xi-lie/</url>
<content type="html"><![CDATA[<h1 id="论文速读:GNN系列"><a href="#论文速读:GNN系列" class="headerlink" title="论文速读<二>:GNN系列"></a>论文速读<二>:GNN系列</h1><p>论文速读系列的第二期,论文速读主要提取论文的Key Idea,本期为GNN系列的论文速读,选取了4篇经典论文和3篇近期的论文。<a href="https://vitaminzl.com/2022/07/16/kg/lun-wen-su-du-1-guan-xi-chou-qu/">上期</a>为知识图谱领域的关系抽取。</p><h2 id="Key-Idea"><a href="#Key-Idea" class="headerlink" title="Key Idea"></a>Key Idea</h2><h3 id="Semi-supervised-Classification-with-Graph-Convolutional-Networks-1"><a href="#Semi-supervised-Classification-with-Graph-Convolutional-Networks-1" class="headerlink" title="Semi-supervised Classification with Graph Convolutional Networks[1]"></a><em>Semi-supervised Classification with Graph Convolutional Networks</em><sup id="fnref:1" class="footnote-ref"><a href="#fn:1" rel="footnote"><span class="hint--top hint--rounded" aria-label="[Kipf T N, Welling M. Semi-supervised classification with graph convolutional networks[J]. arXiv preprint arXiv:1609.02907, 2016.](https://arxiv.org/abs/1609.02907)">[1]</span></a></sup></h3><p>GCN的核心公式如下,其中$\sigma$表示激活函数,$\tilde A=A+I$,也就是给每个结点加个自环,$\tilde D=\sum_i A_{ij}$。$H^{(l)}$为第$l$层的embedding,$W$是参数矩阵。它的形式看起来非常简单。<br>$$<br>H^{(l+1)}=\sigma (\tilde D^{-\frac{1}{2}}\tilde A\tilde D^{-\frac{1}{2}}H^{(l)}W^{(l)})<br>$$<br>从卷积的参数共享角度来看,其实就是每个结点的将自己与附近的参数聚合。第1次聚合后每个结点获取了直接邻居结点的特征,但第2次聚合就可以获取间接邻居的特征(因为直接邻居包含了它的邻居的特征)。所以在多层聚合下,每个结点可以获得全局的信息。</p><p><img src="https://imagehost.vitaminz-image.top/li-spectral-cluster-21.png"></p><h3 id="Graph-Attention-Networks-2"><a href="#Graph-Attention-Networks-2" class="headerlink" title="Graph Attention Networks[2]"></a><em>Graph Attention Networks</em><sup id="fnref:2" class="footnote-ref"><a href="#fn:2" rel="footnote"><span class="hint--top hint--rounded" aria-label="[Veličković P, Cucurull G, Casanova A, et al. Graph attention networks[J]. arXiv preprint arXiv:1710.10903, 2017.](https://arxiv.org/abs/1710.10903)">[2]</span></a></sup></h3><p>GAN就是在GNN中加入注意力机制</p><p><img src="https://imagehost.vitaminz-image.top/gnn-note-3.png"></p><p>以下是注意力的计算公式,其中$\vec h_i,shape(F,1);W,shape(F’,F);\vec a,shape(2F’,1)$。$||$表示连接。看起来就是对2个向量进行线性变换后连接,再通过$\vec a$他们获取之间的相关性,再做一个归一化。</p><p>$$<br>\alpha_{ij}=\frac{\exp(LeakyReLU(\vec a^T[W\vec h_i|| W\vec h_j]))}{\sum_{k\in N_i}\exp(LeakyReLU(\vec a^T[W\vec h_i||W\vec h_j]))}<br>$$<br>和普通神经网络的注意力机制相同,GAN中也分为多头和单头的注意力机制。</p><p>$\vec h_i$的更新,单头注意力<br>$$<br>\vec h’<em>i = \sigma(\sum</em>{j\in N_i} \alpha_{ij}W\vec h^i)<br>$$<br>2种多头注意力的实现<br>$$<br>\vec h’<em>i = ||^{K}</em>{k=1} \sigma(\sum_{j\in N_i} \alpha^k_{ij}W^k\vec h^i)\<br>\vec h’<em>i = \sigma(\frac{1}{K}\sum^{K}</em>{k=1}\sum_{j\in N_i} \alpha^k_{ij}W^k\vec h^i)<br>$$</p><h3 id="How-Powerful-are-Graph-Neural-Networks-3"><a href="#How-Powerful-are-Graph-Neural-Networks-3" class="headerlink" title="How Powerful are Graph Neural Networks?[3]"></a><em>How Powerful are Graph Neural Networks?</em><sup id="fnref:3" class="footnote-ref"><a href="#fn:3" rel="footnote"><span class="hint--top hint--rounded" aria-label="[Xu K, Hu W, Leskovec J, et al. How powerful are graph neural networks?[J]. arXiv preprint arXiv:1810.00826, 2018.](https://arxiv.org/abs/1810.00826)">[3]</span></a></sup></h3><p>这篇文章理论方面证明了GNN的上限是WL test(图同构测试)的上接,总结了信息聚合的方法以及对GNN的评价指标。<br>$$<br>h_{k+1}(u) = MLP((1+\epsilon) h_k(u) + \sum_{(u,v) \in E} h_k(v))<br>$$<br>其中$\epsilon$是一个可学习的无理数参数。该论证证明了其可以达到WL test的上界。</p><h3 id="Inductive-Representation-Learning-on-Large-Graphs-4"><a href="#Inductive-Representation-Learning-on-Large-Graphs-4" class="headerlink" title="Inductive Representation Learning on Large Graphs[4]"></a><em>Inductive Representation Learning on Large Graphs</em><sup id="fnref:4" class="footnote-ref"><a href="#fn:4" rel="footnote"><span class="hint--top hint--rounded" aria-label="[Hamilton W, Ying Z, Leskovec J. Inductive representation learning on large graphs[J]. Advances in neural information processing systems, 2017, 30.](https://proceedings.neurips.cc/paper/2017/hash/5dd9db5e033da9c6fb5ba83c7a7ebea9-Abstract.html)">[4]</span></a></sup></h3><p><img src="https://imagehost.vitaminz-image.top/gnn-note-12.png"></p><p>如上图所示,GraphSAGE可以分为3个步骤:1. 对图中每个顶点邻居顶点进行采样;2. 根据聚合函数聚合邻居顶点蕴含的信息;3. 得到图中各顶点的向量表示供下游任务使用。</p><p><img src="https://imagehost.vitaminz-image.top/gnn-note-14.png"></p><p>每层的embedding生成可以用如上图的伪代码表示。其中AGGREGATE可以使用均值、池化以及LSTM函数。</p><h3 id="Finding-Global-Homophily-in-Graph-Neural-Networks-When-Meeting-Heterophily-5"><a href="#Finding-Global-Homophily-in-Graph-Neural-Networks-When-Meeting-Heterophily-5" class="headerlink" title="Finding Global Homophily in Graph Neural Networks When Meeting Heterophily[5]"></a><em>Finding Global Homophily in Graph Neural Networks When Meeting Heterophily</em><sup id="fnref:5" class="footnote-ref"><a href="#fn:5" rel="footnote"><span class="hint--top hint--rounded" aria-label="[Li X, Zhu R, Cheng Y, et al. Finding Global Homophily in Graph Neural Networks When Meeting Heterophily[J]. arXiv preprint arXiv:2205.07308, 2022.](https://arxiv.org/abs/2205.07308)">[5]</span></a></sup></h3><p>全局同质性、异质图。现有的网络通过多次的领域聚合获取远距离的信息。本文通过相关系数矩阵(类似于注意力机制)获取全局信息。且相关系数矩阵可转换为有闭式解的优化问题,经过简化,可将计算复杂度缩小至线性。</p><p>GloGNN,GloGNN++(系数矩阵加上了自相关)。</p><img src="https://imagehost.vitaminz-image.top/gnn-note-2.png" style="zoom:50%;"><p>核心公式<br>$$<br>H^{(0)}=(1-\alpha)MLP_1(X)+\alpha MLP_2(A)\<br>H^{(l+1)}=(1-\gamma)Z^{(l)<em>}H^{(l)}+\gamma H^{(0)}\<br>$$<br>$Z^{(l)</em>}$通过求解优化问题的闭式解获得。</p><p>Grouping Effect:通过证明可得,$Z^{(l*)},(Z^{(l*)})^T,H^{(l+1)}$都具有grouping effect。即当特征足够接近、结构足够接近时,对应结点的特征表示也足够接近,相关系数也足够接近。</p><h3 id="Large-Scale-Learning-on-Non-Homophilous-Graphs-New-Benchmarks-and-Strong-Simple-Methods-6"><a href="#Large-Scale-Learning-on-Non-Homophilous-Graphs-New-Benchmarks-and-Strong-Simple-Methods-6" class="headerlink" title="Large Scale Learning on Non-Homophilous Graphs: New Benchmarks and Strong Simple Methods[6]"></a><em>Large Scale Learning on Non-Homophilous Graphs: New Benchmarks and Strong Simple Methods</em><sup id="fnref:6" class="footnote-ref"><a href="#fn:6" rel="footnote"><span class="hint--top hint--rounded" aria-label="[Lim D, Hohne F, Li X, et al. Large scale learning on non-homophilous graphs: New benchmarks and strong simple methods[J]. Advances in Neural Information Processing Systems, 2021, 34: 20887-20902.](https://proceedings.neurips.cc/paper/2021/hash/ae816a80e4c1c56caa2eb4e1819cbb2f-Abstract.html)">[6]</span></a></sup></h3><p><img src="https://imagehost.vitaminz-image.top/gnn-note-4.png"></p><p>LINKX是一个看起来很简单的模型,是基于MLP,并没有作卷积操作。它首先将结点的特征矩阵与邻接矩阵经过分别经过2个MLP,然后再做一个连接,接着与参数矩阵$W$相乘,和前面经过MLP层的输出(类似于ResNet)</p><p>相加,接着经过一个激活函数和MLP,输出结果。</p><h3 id="Gnnautoscale-Scalable-and-expressive-graph-neural-networks-via-historical-embeddings-7"><a href="#Gnnautoscale-Scalable-and-expressive-graph-neural-networks-via-historical-embeddings-7" class="headerlink" title="Gnnautoscale: Scalable and expressive graph neural networks via historical embeddings[7]"></a><em>Gnnautoscale: Scalable and expressive graph neural networks via historical embeddings</em><sup id="fnref:7" class="footnote-ref"><a href="#fn:7" rel="footnote"><span class="hint--top hint--rounded" aria-label="[Fey M, Lenssen J E, Weichert F, et al. Gnnautoscale: Scalable and expressive graph neural networks via historical embeddings[C]//International Conference on Machine Learning. PMLR, 2021: 3294-3304.](http://proceedings.mlr.press/v139/fey21a.html)">[7]</span></a></sup></h3><p><img src="https://imagehost.vitaminz-image.top/gnn-note-13.png"></p><p>改论文主要聚焦于如何减小大型图数据训练的计算和内存开销。</p><p>GAS 框架有两个主要组成部分:</p><p>首先,第一部分是构建一个小批量节点(执行快速随机子采样)并修剪 GNN 计算图以仅保留小批量内的节点及其 1 跳邻居节点——这意味着 GAS 的尺度独立于 GNN 深度。其次,每当 GNN 聚合需要小批量节点嵌入时,GAS 就会从存储在 CPU 上的历史嵌入中检索它们。同时,当前小批量节点的历史嵌入也不断更新。</p><p>第二部分是与子采样的关键区别——能够使 GNN 最大限度地表达信息,并将当前的小批量数据和历史嵌入组合起来,得到完整的邻域信息并加以利用,同时确保对大型图的可扩展性。</p><p>GAS 的作者还将他们的想法整合到流行的 PyTorch 几何库中。于是可以在非常大的图上训练大多数的消息传递 GNN,同时降低 GPU 内存需求并保持接近全批次的性能(即在全图上训练时的性能)。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><section class="footnotes"><div class="footnote-list"><ol><li><span id="fn:1" class="footnote-text"><span><a href="https://arxiv.org/abs/1609.02907">Kipf T N, Welling M. Semi-supervised classification with graph convolutional networks[J]. arXiv preprint arXiv:1609.02907, 2016.</a><a href="#fnref:1" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:2" class="footnote-text"><span><a href="https://arxiv.org/abs/1710.10903">Veličković P, Cucurull G, Casanova A, et al. Graph attention networks[J]. arXiv preprint arXiv:1710.10903, 2017.</a><a href="#fnref:2" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:3" class="footnote-text"><span><a href="https://arxiv.org/abs/1810.00826">Xu K, Hu W, Leskovec J, et al. How powerful are graph neural networks?[J]. arXiv preprint arXiv:1810.00826, 2018.</a><a href="#fnref:3" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:4" class="footnote-text"><span><a href="https://proceedings.neurips.cc/paper/2017/hash/5dd9db5e033da9c6fb5ba83c7a7ebea9-Abstract.html">Hamilton W, Ying Z, Leskovec J. Inductive representation learning on large graphs[J]. Advances in neural information processing systems, 2017, 30.</a><a href="#fnref:4" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:5" class="footnote-text"><span><a href="https://arxiv.org/abs/2205.07308">Li X, Zhu R, Cheng Y, et al. Finding Global Homophily in Graph Neural Networks When Meeting Heterophily[J]. arXiv preprint arXiv:2205.07308, 2022.</a><a href="#fnref:5" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:6" class="footnote-text"><span><a href="https://proceedings.neurips.cc/paper/2021/hash/ae816a80e4c1c56caa2eb4e1819cbb2f-Abstract.html">Lim D, Hohne F, Li X, et al. Large scale learning on non-homophilous graphs: New benchmarks and strong simple methods[J]. Advances in Neural Information Processing Systems, 2021, 34: 20887-20902.</a><a href="#fnref:6" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:7" class="footnote-text"><span><a href="http://proceedings.mlr.press/v139/fey21a.html">Fey M, Lenssen J E, Weichert F, et al. Gnnautoscale: Scalable and expressive graph neural networks via historical embeddings[C]//International Conference on Machine Learning. PMLR, 2021: 3294-3304.</a><a href="#fnref:7" rev="footnote" class="footnote-backref"> ↩</a></span></span></li></ol></div></section>]]></content>
<categories>
<category>GNN</category>
</categories>
<tags>
<tag>GNN</tag>
<tag>图</tag>
<tag>论文速读</tag>
</tags>
</entry>
<entry>
<title>谱聚类</title>
<link href="/2022/08/30/gnn/spectral-cluster/"/>
<url>/2022/08/30/gnn/spectral-cluster/</url>
<content type="html"><![CDATA[<h1 id="谱聚类"><a href="#谱聚类" class="headerlink" title="谱聚类"></a>谱聚类</h1><p>本文主要对谱聚类的几篇论文进行解读,并对一部分的结果进行复现。本文首先从谱聚类的一般过程入手,介绍传统的谱聚类方法NCuts、NJW。针对传统方法的相似度量的缺陷,引入改进方法ZP;又针对特征向量的选择问题,引入改进方法PI。结合以上2种方式,加入TKNN,引入改进方法ROSC,接着对ROSC中修正相似度矩阵的缺陷,结合trace lasso正则项,引入改进方法CAST。最后,对于ROSC和CAST中都提到的Group Effect进行解读。文章结尾补充了幂代法和矩阵求导的内容。其中我分别对PI方法和ROSC方法进行代码复现。</p><p>代码的仓库地址:<a href="https://github.com/vitaminzl/SpectralCluster">https://github.com/vitaminzl/SpectralCluster</a> </p><p>备用镜像:<a href="https://gitee.com/murphy_z/spectral-cluster">https://gitee.com/murphy_z/spectral-cluster</a></p><h2 id="Pipeline"><a href="#Pipeline" class="headerlink" title="Pipeline"></a>Pipeline</h2><p><img src="https://imagehost.vitaminz-image.top/gnn-note-10.png"></p><p>谱聚类的一般过程如上图所示<sup id="fnref:2" class="footnote-ref"><a href="#fn:2" rel="footnote"><span class="hint--top hint--rounded" aria-label="[Li X, Kao B, Shan C, et al. CAST: a correlation-based adaptive spectral clustering algorithm on multi-scale data[C]//Proceedings of the 26th ACM SIGKDD International Conference on Knowledge Discovery & Data Mining. 2020: 439-449.](https://dl.acm.org/doi/abs/10.1145/3394486.3403086)">[2]</span></a></sup>。对于一系列的数据,我们首先计算数据之间的相似度矩阵$S$,常用的相似度度量为高斯核$S_{ij}=\exp(-\frac{||\vec x_i-\vec x_j||^2}{2\sigma^2})$,然后求其拉普拉斯矩阵$L=D-S$,其中$D$为对角矩阵,且$D_{ii}=\sum A_{ij}$。然后求出拉普拉斯矩阵的特征向量,选择特征值$k$小的特征向量进行k-means聚类。</p><p>以上过程可以理解为,将原数据利用相似度度量转化为图数据,即使得每个数据间连着一条”虚边“,相似度即为边的权重。接下来将数据转换到频域上,选择一些低频作为数据的特征向量,这是因为低频成分往往具有更高层次、更具信息量的特征,而高频成分则更可能是噪声。然后对这些特征向量进行聚类。</p><p>而这种一般的方法在多尺度的数据聚类中往往表现不佳。</p><h2 id="NCuts"><a href="#NCuts" class="headerlink" title="NCuts"></a>NCuts</h2><p><img src="https://imagehost.vitaminz-image.top/li-spectral-cluster-4.png"></p><p>我们首先介绍一些远古的谱聚类方法。Normalized Cut<sup id="fnref:8" class="footnote-ref"><a href="#fn:8" rel="footnote"><span class="hint--top hint--rounded" aria-label="[Shi J, Malik J. Normalized cuts and image segmentation[J]. IEEE Transactions on pattern analysis and machine intelligence, 2000, 22(8): 888-905.](https://ieeexplore.ieee.org/abstract/document/868688)">[8]</span></a></sup>的想法来源于最小割。如果存在连通图$\mathcal G={\mathcal V, \mathcal E}$,每条边$e_i\in \mathcal E$有着权重$w_i$,为了使得连通图分割成2个连通子图,那么需要移掉一些边。若移掉这些边权之和最小,那么我们称这样的分割方法为最小割。这和聚类非常相似,边权可以表示点和点的联系,若存在两个类,那么类之间的联系应当是比较小的,类内的联系比较大。</p><p>所以一种可行的想法是,首先对连通图进行一次取最小割,然后再对连通子图进一步地取最小割,直到到达某一阈值为止。但显然这样存在一个问题,如上图所示,要使去掉边权之和最小,那每次分割肯可能都会倾向于指割掉一条边,这自然不合理。</p><p>一种自然的想法使对权重进行归一化处理<br>$$<br>NCut(A,B)=\frac{cut(A,B)}{assoc(A,V)}+\frac{cut(A,B)}{assoc(B,V)}<br>$$<br>其中$assoc(A,V)$表示$A$到所有连接的结点权重之和,$cut(A,B)$则是分割后移掉边权之和。所以假如当某一侧的结点非常少时,那么这$cut$的值可能比$assoc$大,极端情况下值分割一个点,那么分母就是0了,则最后的结果是无穷大。</p><p>因此利用前面的想法,每次使用$NCut$,然后再对子图进行$NCut$,不断进行二分就可以了。</p><p>听起来很简单,但很遗憾的是,求最小割是一个NP难的问题,因此只能求其近似解。</p><p>通过一系列的复杂推导(太难了哈哈),可以得到$D^{-\frac{1}{2}}LD^{-\frac{1}{2}}$第2小的特征向量就是对应的最小割。当然这里需要设定一个阈值,因为特征向量求出来是浮点数,但其数据会偏向两级。然后利用上面的二分法求解即可。</p><p>但是现在多数使用的NCuts是$\min NCut(A,B)$问题转化为以下优化问题。<br>$$<br>\min \vec x^T(D^{-\frac{1}{2}}LD^{-\frac{1}{2}})\vec x\<br>st. \vec x^T\vec x=1<br>$$<br>上面这个形式其实就是瑞丽熵,那么只要取$k$个最小的特征值对应的特征向量进行聚类即可。</p><h2 id="NJW"><a href="#NJW" class="headerlink" title="NJW"></a>NJW</h2><p>NJW算法<sup id="fnref:6" class="footnote-ref"><a href="#fn:6" rel="footnote"><span class="hint--top hint--rounded" aria-label="[Ng A, Jordan M, Weiss Y. On spectral clustering: Analysis and an algorithm[J]. Advances in neural information processing systems, 2001, 14.](https://proceedings.neurips.cc/paper/2001/hash/801272ee79cfde7fa5960571fee36b9b-Abstract.html)">[6]</span></a></sup>是取3个人名的首字母命名的。该算法和NCuts非常类似,甚至可以看作是其变形。<br>$$<br>\begin{align*}<br>D^{-\frac{1}{2}}LD^{-\frac{1}{2}}&=D^{-\frac{1}{2}}(D-S)D^{-\frac{1}{2}}\<br>&=I-D^{-\frac{1}{2}}SD^{\frac{1}{2}}<br>\end{align*}<br>$$<br>所以我们可以转而去求$D^{-\frac{1}{2}}SD^{\frac{1}{2}}$最大的$k$个特征向量,然后进行聚类。</p><h2 id="ZP"><a href="#ZP" class="headerlink" title="ZP"></a>ZP</h2><p>前面提到计算相似度矩阵时,我们常用高斯核$S_{ij}=\exp(-\frac{||\vec x_i-\vec x_j||^2}{2\sigma^2})$,而高斯核中$\sigma$的选取是需要考虑的,很多时候常常人工设定<sup id="fnref:3" class="footnote-ref"><a href="#fn:3" rel="footnote"><span class="hint--top hint--rounded" aria-label="[Zelnik-Manor L, Perona P. Self-tuning spectral clustering[J]. Advances in neural information processing systems, 2004, 17.](https://proceedings.neurips.cc/paper/2004/hash/40173ea48d9567f1f393b20c855bb40b-Abstract.html)">[3]</span></a></sup>。但更重要的是$\sigma$是一个全局的参数,这在多尺度数据中具有一些缺陷。如设定的值较大时,稠密图的数据会趋于相似,设定较小时,稀疏图的数据则相似度过小。</p><p><img src="https://imagehost.vitaminz-image.top/li-spectral-cluster-8.png"></p><p>ZP方法提出里一种局部调整$\sigma$的设定,距离度量修正为$S_{ij}=\exp(-\frac{||\vec x_i-\vec x_j||^2}{2\sigma_i\sigma_j})$。其中$\sigma_i, \sigma_j$是依附于$\vec x_i, \vec x_j$的是一种局部的参数。这一参数的设定应通过样本的特征取选取。在论文中<sup id="fnref:3" class="footnote-ref"><a href="#fn:3" rel="footnote"><span class="hint--top hint--rounded" aria-label="[Zelnik-Manor L, Perona P. Self-tuning spectral clustering[J]. Advances in neural information processing systems, 2004, 17.](https://proceedings.neurips.cc/paper/2004/hash/40173ea48d9567f1f393b20c855bb40b-Abstract.html)">[3]</span></a></sup>中选择的方法是$\vec x_i$到第$K$个邻居的欧式距离,实验表明$K$取7在多个数据集上的效果表现良好。如上图所示,结点之间的边厚度表示数据间的权重大小(仅显示周围数据的权重)。图b是原来的高斯核距离度量,图c是修正后的。可以看到图b中靠近蓝点的边仍然比较厚,而图c则避免了的这种现象。</p><h2 id="PI"><a href="#PI" class="headerlink" title="PI"></a>PI</h2><p>PI(Power Iteration)为幂迭代法<sup id="fnref:5" class="footnote-ref"><a href="#fn:5" rel="footnote"><span class="hint--top hint--rounded" aria-label="[Lin F, Cohen W W. Power iteration clustering[C]//ICML. 2010.](https://openreview.net/forum?id=SyWcksbu-H)">[5]</span></a></sup>。其灵感来源于幂迭代法用求主特征值(在[文章的后面部分](##Dominant Eigenvalue)会更详细地说明)。</p><p>我们设$W=D^{-1}A$,该矩阵有时候叫转移矩阵,因为它和马尔可夫的状态转移矩阵非常类似。每行的和为1,每个元素$W_{i,j}$可以看作是$i$结点到$j$结点转移的概率。它和归一化随机游走矩阵$L_r=I-W$有着重要的联系。NCuts算法证明了$L_r$第2小的特征值所对应的特征向量可以作为NCuts算法的一种近似。</p><p>这里需要说明的是,$L_r$最小的特征值为0,容易证明$\vec 1=[1, 1, …, 1]$是$L_r$的0所对应的特征向量。而对于$W$来说则,其最大的特征值为1,且$\vec 1$是对应的特征向量。需要说明的是,$L_r$最小的几个特征向量或$W$最大的几个特征向量是有效的,其余可能是噪声。</p><p>首先给定一个向量$\vec v^{(0)}= c_1\vec e_1 + c_2\vec e_2,…, +c_n\vec e_n$,其中$\vec e_i$为$W$的特征向量。且$\vec e_i$所对应的特征值$\lambda_i$满足$1=\lambda_1 > \lambda_2>…>\lambda_n$。</p><p>$$<br>\vec v^{(t+1)} = \frac{W\vec v^{(t)}}{||W\vec v^{(t)}||_1}<br>$$<br>假如我们按照如上的迭代公式进行迭代,则有如下过程(暂且忽略迭代公式的分母归一化项)<br>$$<br>\begin{align*}<br>\vec v^{(1)} &= W \vec v^{(0)}<br>\&=c_1W\vec e_1 + c_2W\vec e_2,…, +c_nW\vec e_n<br>\&=c_1\lambda_1 e_1 + c_2\lambda_2\vec e_2,…, +c_n\lambda_n\vec e_n<br>\end{align*}<br>$$<br>则<br>$$<br>\begin{align*}<br>\vec v^{(t)} &= Wv^{(t−1)} = W^2v^{(t−2)} = … = W^tv^{(0)}<br>\&=c_1W^t\vec e_1 + c_2W^t\vec e_2,…, +c_nW^t\vec e_n<br>\&=c_1\lambda_1^t e_1 + c_2\lambda_2^t\vec e_2,…, +c_n\lambda_n^t\vec e_n<br>\&=c_1\lambda_1^t\bigg[e_1+\sum\frac{c_2}{c_1}\bigg(\frac{\lambda_i}{\lambda_1}\bigg)^t\vec e_i)\bigg]<br>\end{align*}<br>$$<br>当$t\rightarrow +\infty$时,$\frac{c_2}{c_1}(\frac{\lambda_i}{\lambda_1})^t$会趋向于0。该方法的提出者认为,在有效成分$\vec e_i$的$\lambda_i$往往会接近于$\lambda_1$,而高频的一些噪声成分$\lambda_j$会接近于0。在迭代过程中使得有效成分$\vec e_i$前面的权重和噪声成分前的权重的差距会迅速扩大。</p><p>但是迭代的次数不宜过多,因为最后的结果会趋向于$k\vec 1$,因为$W$的主特征向量就是$\vec 1$。因此我们需设置一个迭代的门限值,以截断迭代过程。具体的算法如下。</p><img src="https://imagehost.vitaminz-image.top/gnn-note-11.png" style="zoom:50%;"><p>这里以论文中开始提到的3圆圈数据集为例子,进行结果的复现,如下图所示。</p><img src="https://imagehost.vitaminz-image.top/li-spectral-cluster-5.png" style="zoom:50%;"><p>通过以上的算法,我选取几次的迭代结果$\vec v^{(t)}$进行可视化,同一个类别在后面的迭代过程中逐渐局部收敛到一个值。</p><img src="https://imagehost.vitaminz-image.top/li-spectral-cluster-7.png" style="zoom: 67%;"><p>在实验中发现,其结果和许多因素有关,其中包括高斯核距离度量中的$\sigma$,初始的向量$\vec v^{(0)}$(论文中取$\vec v^{(0)}= \frac{\sum_j A_{ij}}{\sum_i\sum_j A_{ij}}$),结束时的$\hat\epsilon$设置,甚至发现使用$W^T$具有更好的效果,这是因为$W^T$的主特征值的特征向量就已经具有分类的效果(其意义尚待研究),而$W$的主特征向量是$\vec 1$,但这仅仅针对于3圆圈这一数据集而言。在<a href="#%E9%97%AE%E9%A2%98%E4%B8%8E%E6%80%BB%E7%BB%93">问题与总结</a>中,会提到这一点。此外还有计算机的运算精度也会影响结果。</p><p>该方法的一个重要优点是简单高效,其收敛速度快,在百万级别的数据中也能在几秒内收敛。缺陷是过分拔高了特征值大的部分,在多尺度数据中存在一些低特征值但仍然重要的信息。</p><p>以下是主函数的代码,完整代码见:<a href="https://github.com/vitaminzl/SpectralCluster/blob/master/PI.py">https://github.com/vitaminzl/SpectralCluster/blob/master/PI.py</a></p><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">main</span>():<br> data, labels = get3CircleData(radius=[<span class="hljs-number">0.1</span>, <span class="hljs-number">6</span>, <span class="hljs-number">17</span>], nums=[<span class="hljs-number">10</span>, <span class="hljs-number">30</span>, <span class="hljs-number">80</span>])<br> draw3CircleData(x=data[:, <span class="hljs-number">0</span>], y=data[:, <span class="hljs-number">1</span>], labels=labels, title=<span class="hljs-string">"Data Set"</span>)<br> S_mtx = getSimilarMatrix(data, sigma=<span class="hljs-number">1.8</span>)<br> W = np.diag(<span class="hljs-number">1</span> / np.<span class="hljs-built_in">sum</span>(S_mtx, axis=<span class="hljs-number">0</span>)) @ S_mtx<br> v_t = PowerIter(W, iter_nums=<span class="hljs-number">300</span>, eps=<span class="hljs-number">1e-5</span>, labels=labels)<br> plt.show()<br></code></pre></td></tr></tbody></table></figure><h2 id="TKNN"><a href="#TKNN" class="headerlink" title="TKNN"></a>TKNN</h2><p><img src="https://imagehost.vitaminz-image.top/li-spectral-cluster-10.png"></p><p>聚类问题转化为图问题时,需要解决邻接问题。定义结点之间的连接常常有2种方式<sup id="fnref:4" class="footnote-ref"><a href="#fn:4" rel="footnote"><span class="hint--top hint--rounded" aria-label="[https://csustan.csustan.edu/~tom/Clustering/GraphLaplacian-tutorial.pdf](https://csustan.csustan.edu/~tom/Clustering/GraphLaplacian-tutorial.pdf)">[4]</span></a></sup>。第一种如上图左,每个数据选择自己最近的K个邻居相邻接,得到的图被称为K邻接图(K Nearest Neighbor Graph);第二种如上图右,每个结点选择半径$\epsilon$的邻居相邻接。</p><p><img src="https://imagehost.vitaminz-image.top/li-spectral-cluster-14.png"></p><p>假如我们使用KNN的方法,K取4。如上图a所示,红色结点的4个最近邻用红线连接,绿色结点的4个最近邻用绿线连接。我们会发现,虽然红色的最近邻包括绿色,但绿色不包括红色,我们称红色和绿色不是<strong>相互近邻</strong>。但如图b所示,则红色和绿色则为相互近邻。若2个结点是相互近邻,则称这2个结点<strong>相互可达</strong>。如图c所示,红色与绿色是相互近邻,绿色和黄色相互近邻,那么红色和绿色也<strong>相互可达</strong>。</p><p>Transitive K Nearest Neighbor(TKNN) Graph 是指当2个结点时相互可达的,则二者连接一条边。因此其邻接矩阵$W$中,若2个点$i,j$相互可达,$W_{i,j}=W_{j,i}=1$。</p><p>构造的过程可描述如下:</p><ul><li>step1: 构造K邻接矩阵$A$,对于结点$i$由$k$个邻居$j$,则$A_{i, j}=1$</li><li>step2: 构造相互近邻矩阵$A’=A A^T$,若为相互近邻,则为1,否则为0。</li><li>step3: 寻找$A’$的所有连通分量$S$</li><li>step4: 对于连通分量$S_i$中的每2个元素$S_{i,j}, S_{i,k}$,令$W_{S_{i,j},S_{i,k}}=W_{S_{i,k},S_{i,j}}=1$,其余为0。</li></ul><p>代码如下:</p><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">getTKNN_W</span>(<span class="hljs-params">S_mtx, K</span>):<br> N = S_mtx.shape[<span class="hljs-number">0</span>]<br> KNN_A = np.zeros((N, N), dtype=np.int32)<br> <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(N):<br> idx = np.argsort(S_mtx[i, :])<br> KNN_A[i, idx[(N-K):]] = <span class="hljs-number">1</span><br> MKNN_A = KNN_A * KNN_A.T<br> G = nx.from_numpy_array(MKNN_A)<br> compo_list = [c <span class="hljs-keyword">for</span> c <span class="hljs-keyword">in</span> nx.connected_components(G)]<br> TKNN_W = np.zeros((N, N), dtype=np.int32)<br> <span class="hljs-keyword">for</span> c_i <span class="hljs-keyword">in</span> compo_list:<br> c = np.array(<span class="hljs-built_in">list</span>(c_i), dtype=np.int32)<br> idx_c = np.tile(c, (<span class="hljs-built_in">len</span>(c), <span class="hljs-number">1</span>))<br> TKNN_W[idx_c.T, idx_c] = <span class="hljs-number">1</span> - np.identity(<span class="hljs-built_in">len</span>(c))<br><br> <span class="hljs-keyword">return</span> TKNN_W<br></code></pre></td></tr></tbody></table></figure><h2 id="ROSC"><a href="#ROSC" class="headerlink" title="ROSC"></a>ROSC</h2><p><img src="https://imagehost.vitaminz-image.top/li-spectral-cluster-9.png"></p><p>如上图所示为ROSC方法的流程图。ROSC方法<sup id="fnref:1" class="footnote-ref"><a href="#fn:1" rel="footnote"><span class="hint--top hint--rounded" aria-label="[Li X, Kao B, Luo S, et al. Rosc: Robust spectral clustering on multi-scale data[C]//Proceedings of the 2018 World Wide Web Conference. 2018: 157-166.](https://dl.acm.org/doi/abs/10.1145/3178876.3185993)">[1]</span></a></sup>结合了以上2种方法,但相比于PI方法不同的是,它并不是将PI得到的输出直接作为k-means聚类的输入,而是增加了一个求修正相似矩阵的过程。</p><p>首先随机设置不同的$\vec v^{(0)}$获取$p$个“伪特征向量”,拼成一个$p\times n$的矩阵矩阵$X$,并对$X$进行标准化,即使得$XX^T=I$。</p><p>ROSC论文中认为,相似度矩阵的意义可以表示为某一个实体能够被其他实体所描述的程度。即任何一个实体$x_{i}=\sum Z_{i,j}x_j$,这里$Z_{i,j}$即为修正相似度矩阵。因此就有:<br>$$<br>X=XZ+O<br>$$<br>其中$O$表示噪声矩阵。</p><p>定义优化问题<br>$$<br>\min_{Z}||X-XZ||_F^2+\alpha_1||Z||_F+\alpha_2||W-Z||_F<br>$$<br>优化问题的第一项表示最小化噪声,第二项则是$Z$的Frobenius 范数<sup id="fnref:12" class="footnote-ref"><a href="#fn:12" rel="footnote"><span class="hint--top hint--rounded" aria-label="[https://mathworld.wolfram.com/FrobeniusNorm.html](https://mathworld.wolfram.com/FrobeniusNorm.html)">[12]</span></a></sup>),为正则化项,用于平衡其他2项,第三项则是减小与前文中TKNN的邻接矩阵$W$的差距。$\alpha_1,\alpha_2$是平衡参数,需要人工设置。</p><p>求解以上优化问题,可以先对$Z$求导([文章的后面](##Derivatives of Matrix)还会做一些补充),使导数为0即可。对三项项求导有<br>$$<br>\begin{align*}<br>\frac{\partial ||X-XZ||^2_F}{\partial Z}&=-2X^T(X-XZ)\<br>\frac{\partial\alpha_1||Z||^2_F}{\partial Z}&=2\alpha_1Z\<br>\frac{\partial\alpha_2||W-Z||^2_F}{\partial Z}&=-2\alpha_2(W-Z)<br>\end{align*}<br>$$<br>三项相加有<br>$$<br>-X^T(X-XZ)+\alpha_1Z-\alpha_2(W-Z)=0<br>$$<br>整理可得<br>$$<br>Z^*=(2X^TX+\alpha_1 I+\alpha_2 I)^{-1}(X^TX+\alpha_2W)<br>$$<br>但这样求出来的$Z^*$可能使不对称的,且可能存在负数。所以这里再次做了一个修正$\tilde Z=(|Z^*|+|Z^*|^T)/2$。</p><p>接下来就可以执行一般的谱聚类方法了。具体的算法流程可以用如下图所示:</p><img src="https://imagehost.vitaminz-image.top/li-spectral-cluster-12.png" style="zoom: 33%;"><p>算法第4行中的whiten为白化处理<sup id="fnref:9" class="footnote-ref"><a href="#fn:9" rel="footnote"><span class="hint--top hint--rounded" aria-label="[https://en.wikipedia.org/wiki/Whitening_transformation](https://en.wikipedia.org/wiki/Whitening_transformation)">[9]</span></a></sup>,是数据预处理的一种常用方法。它类似于PCA,但与PCA不同的是,PCA往往用来降维,而白化则是利用PCA的特征向量,将数据转换到新的特征空间,然后对新的坐标进行方差归一化,目的是去除输入数据的冗余信息。</p><p>根据上述算法,以下使用python对其进行复现。</p><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">ROSC</span>(<span class="hljs-params">S, C_k, t_k, alpha1, alpha2</span>):<br> W_tknn = getTKNN_W(S, K=t_k)<br> W = np.diag(np.<span class="hljs-built_in">sum</span>(S, axis=<span class="hljs-number">0</span>)) @ S<br> X = prep.PIC_k(W, k=C_k)<br> X = prep.whiten(X)<br> X = prep.norm(X)<br> Z = getROSC_Z(X.T, W_tknn, alpha1, alpha2)<br> Z = (np.<span class="hljs-built_in">abs</span>(Z) + np.<span class="hljs-built_in">abs</span>(Z.T)) / <span class="hljs-number">2</span><br> C = postp.ncuts(Z, C_k)<br> <span class="hljs-keyword">return</span> C<br></code></pre></td></tr></tbody></table></figure><p>主函数的代码如下</p><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">main</span>(<span class="hljs-params">data_name</span>):<br> path = <span class="hljs-string">"dataset/"</span> + data_name + <span class="hljs-string">".txt"</span><br> data = np.loadtxt(<span class="hljs-string">"dataset/Syn.txt"</span>, delimiter=<span class="hljs-string">','</span>, dtype=np.float64)<br> label = np.loadtxt(<span class="hljs-string">"dataset/SynLabel.txt"</span>, dtype=np.int32)<br> C_k = <span class="hljs-built_in">len</span>(<span class="hljs-built_in">set</span>(label))<br> S = prep.getSimilarMatrix2(data=data)<br> C = ROSC(S, C_k=C_k, t_k=t, alpha1=<span class="hljs-number">1</span>, alpha2=<span class="hljs-number">0.01</span>)<br> prt, AMI, RI = postp.assess(label_true=label, label_pred=C)<br> <span class="hljs-built_in">print</span>(<span class="hljs-string">f"<span class="hljs-subst">{data_name}</span>\nPurity: <span class="hljs-subst">{prt}</span>\nAMI: <span class="hljs-subst">{AMI}</span>\nRI: <span class="hljs-subst">{RI}</span>\n"</span>)<br></code></pre></td></tr></tbody></table></figure><p>完整代码见<a href="https://github.com/vitaminzl/SpectralCluster/blob/master/ROSC.py">https://github.com/vitaminzl/SpectralCluster/blob/master/ROSC.py</a></p><p><img src="https://imagehost.vitaminz-image.top/li-spectral-cluster-20.png"></p><p>我首先使用了人工合成的数据集,如上图左所示。使用论文中的参数效果并不是很好,然后调整了一下求解TKNN矩阵的K,原文使用的是4,我调整为8,结果效果猛增,甚至优于论文的结果,如上图右所示,只有极个别点分错。不过根据数据集调参还是不科学的哈哈哈😂。其中Purity=0.9861,AMI=0.9307,RI=0.9784。</p><p>然后我对TKNN的K参数从1到12开始调整,3个指标的变化曲线如下图所示。感觉变化还是蛮明显的。</p><img src="https://imagehost.vitaminz-image.top/li-spectral-cluster-17.png" style="zoom: 67%;"><p>以下是5个数据集的实验结果,参数除了TKNN是的K是8以外,其他都和原论文相同,即$\alpha_1=1,\alpha_2=0.01$。大部分的数据集都没有论文的结果好(比论文结果好的加了粗),但也相差不多。除了MNist0127这个数据集是例外,其结果异常地差劲,也不知道是什么原因。</p><table><thead><tr><th>数据集</th><th>Purity</th><th>AMI</th><th>RI</th></tr></thead><tbody><tr><td>COIL20</td><td>0.8486</td><td>0.9339</td><td>0.9683</td></tr><tr><td>Glass</td><td><strong>0.6074</strong></td><td>0.2949</td><td><strong>0.7233</strong></td></tr><tr><td>MNIST0127</td><td>0.2767</td><td>0.0156</td><td>0.3981</td></tr><tr><td>Isolet</td><td>0.7067</td><td>0.6151</td><td>0.8459</td></tr><tr><td>Yale</td><td>0.5636</td><td>0.3215</td><td>0.7704</td></tr></tbody></table><h2 id="Trace-Lasso"><a href="#Trace-Lasso" class="headerlink" title="Trace Lasso"></a>Trace Lasso</h2><p>Trace Lasso<sup id="fnref:13" class="footnote-ref"><a href="#fn:13" rel="footnote"><span class="hint--top hint--rounded" aria-label="[Grave E, Obozinski G R, Bach F. Trace lasso: a trace norm regularization for correlated designs[J]. Advances in Neural Information Processing Systems, 2011, 24.](https://proceedings.neurips.cc/paper/2011/hash/33ceb07bf4eeb3da587e268d663aba1a-Abstract.html)">[13]</span></a></sup>是一种介于L1和L2正则的正则化项。其形式为<br>$$<br>\Omega(W)=||XDiag(W)||<em>*<br>$$<br>其中$X$为已归一化的特征矩阵,即对于特征$\vec x_i$有$\vec x_i \vec x_i^T=1$。$W$为待求参数。$||·||</em><em>$为核范数<sup id="fnref:14" class="footnote-ref"><a href="#fn:14" rel="footnote"><span class="hint--top hint--rounded" aria-label="[https://en.wikipedia.org/wiki/Matrix_norm](https://en.wikipedia.org/wiki/Matrix_norm)">[14]</span></a></sup>(或迹范数),$||Z||_</em>=tr(\sqrt{Z^TZ})$,表示所有奇异值之和。</p><p>Trace Lasso具有如下性质:</p><ul><li><p>当$X^TX=I$时,即特征之间的相关性为0,或者正交,那么<br>$$<br>\Omega(W)=||W||_1<br>$$<br>即退化为1范式。</p></li><li><p>当$X^TX=\vec 1^T\vec 1$时,即所有特征都完全相关,那么<br>$$<br>\Omega(W)=||W||_2<br>$$<br>即退化为2范式</p></li><li><p>其他情况下,在1范式和2范式之间。</p></li></ul><p>Trace Lasso的优点就是它可以根据数据的特征,接近合适的范式,这相比弹性网络更好。</p><h2 id="CAST"><a href="#CAST" class="headerlink" title="CAST"></a>CAST</h2><p>ROSC方法虽然可以加强类内数据的联系,但没有使得类间的间距增大。</p><p>而CAST相比于ROSC的区别就在于修改了优化函数<sup id="fnref:2" class="footnote-ref"><a href="#fn:2" rel="footnote"><span class="hint--top hint--rounded" aria-label="[Li X, Kao B, Shan C, et al. CAST: a correlation-based adaptive spectral clustering algorithm on multi-scale data[C]//Proceedings of the 26th ACM SIGKDD International Conference on Knowledge Discovery & Data Mining. 2020: 439-449.](https://dl.acm.org/doi/abs/10.1145/3394486.3403086)">[2]</span></a></sup>,成为如下形式:<br>$$<br>\min_{Z} \frac{1}{2} ||\vec x-X\vec z||<em>2+\alpha_1||XDiag(\vec z)||</em>*+\frac{\alpha_2}{2}||W-\vec z||_2<br>$$<br>其中$\vec x$是$X$的其中一个特征向量,$z$是修正相似度矩阵$Z$中的一个向量。于ROSC的主要区别在于范数的选择,通过trace lasso可以使得其具有类内聚合也有类外稀疏的特性。</p><p>该优化问题的求解已经超出了我的能力范围,在此直接贴出论文中的算法流程</p><img src="https://imagehost.vitaminz-image.top/li-spectral-cluster-15.png" style="zoom: 50%;"><p>整个CAST算法的流程如下:</p><img src="https://imagehost.vitaminz-image.top/li-spectral-cluster-16.png" style="zoom:50%;"><p>对比ROSC,他们的区别就在于求解$Z^*$的方法不同,其余都是一样的。</p><p>由于Inexcat ALM的算法超出了我的知识范畴,并且最近事情较多,CAST算法的代码并没有复现。若后面有时间再填补空缺。</p><h2 id="Group-Effect"><a href="#Group-Effect" class="headerlink" title="Group Effect"></a>Group Effect</h2><p>Group Effect在ROSC和CAST论文中都提及了,以及一篇关于GNN的论文<sup id="fnref:7" class="footnote-ref"><a href="#fn:7" rel="footnote"><span class="hint--top hint--rounded" aria-label="[Li X, Zhu R, Cheng Y, et al. Finding Global Homophily in Graph Neural Networks When Meeting Heterophily[J]. arXiv preprint arXiv:2205.07308, 2022](https://arxiv.org/abs/2205.07308)">[7]</span></a></sup>中也提及了,可以说精髓所在了。</p><p>首先定义一些符号:若有一系列的实体$X={x_1,x_2,…,x_n}$,设$w_q$为$W$的第$q$列,设$x_i\rightarrow x_j$表示,$x_i^Tx_j\rightarrow 1$且$||w_i-w_j||_2\rightarrow 0$。</p><p>如果矩阵$Z$满足当$x_i\rightarrow x_j$时,有$|Z_{ip}-Z_{jp}|\rightarrow 0$,则称$Z$具有Group Effect。</p><p>翻译成人话就是当2个实体足够接近,$Z$矩阵中实体对应的元素也足够的接近。这说明了$Z$矩阵确实能够反映实体之间的联系紧密程度。2个实体足够接近,它可以指实体的特征足够接近,也可以指实体附近的结构接近。事实上在另一篇关于GNN的论文<sup id="fnref:7" class="footnote-ref"><a href="#fn:7" rel="footnote"><span class="hint--top hint--rounded" aria-label="[Li X, Zhu R, Cheng Y, et al. Finding Global Homophily in Graph Neural Networks When Meeting Heterophily[J]. arXiv preprint arXiv:2205.07308, 2022](https://arxiv.org/abs/2205.07308)">[7]</span></a></sup>里还包括了实体附近的结构信息。</p><p>可以证明的是,ROSC和CAST中的稀疏矩阵都有Group Effect,证明的过程过于复杂,也超出了我的能力范围了😂。</p><h2 id="补充"><a href="#补充" class="headerlink" title="补充"></a>补充</h2><h3 id="Dominant-Eigenvalue"><a href="#Dominant-Eigenvalue" class="headerlink" title="Dominant Eigenvalue"></a>Dominant Eigenvalue</h3><p>若一个方阵$A$存在一系列特征值$\lambda_1,\lambda_2,…,\lambda_n$,且满足$|\lambda_1|>|\lambda_2|\ge…\ge|\lambda_n|$,则称$\lambda_1$为该方阵的主特征值<sup id="fnref:10" class="footnote-ref"><a href="#fn:10" rel="footnote"><span class="hint--top hint--rounded" aria-label="[https://www.cs.huji.ac.il/w~csip/tirgul2.pdf](https://www.cs.huji.ac.il/w~csip/tirgul2.pdf)">[10]</span></a></sup>。前文中的幂迭代法原本就是用来求主特征值的。<br>$$<br>\begin{align*}<br> A^kq^{(0)} &= A^{k-1}q^{(1)} = … =Aq^{(k−1)}<br>\&=a_1A^k\vec e_1 + a_2A^k\vec e_2,…, +a_nA^k\vec e_n<br>\&=a_1\lambda_1^k e_1 + a_2\lambda_2^k\vec e_2,…, +a_n\lambda_n^k\vec e_n<br>\&=a_1\lambda_1^k\bigg[e_1+\sum_{i=2}^{n}\frac{a_i}{a_1}\bigg(\frac{\lambda_i}{\lambda_1}\bigg)^k\vec e_i)\bigg]<br>\end{align*}<br>$$<br>经过一系列迭代后又如上式子。显然,当$k\rightarrow\infty$时,$q^{(k)}=Aq^{(k−1)}=A^kq^{(0)}\rightarrow a_1\lambda^kx_1$。并且$[q^{k}]^TAq^{(k)}\approx [q^{(k)}]^T\lambda q^{(k)}=\lambda|q^{(k)}|_2=\lambda$,当$|q^{(k)}|_2=1$。所以每次迭代需要对$q$进行一次标准化。这样通过经过式子就可求出主特征值了。</p><h3 id="Derivatives-of-Matrix"><a href="#Derivatives-of-Matrix" class="headerlink" title="Derivatives of Matrix"></a>Derivatives of Matrix</h3><p>矩阵求导是矩阵论中的相关知识<sup id="fnref:11" class="footnote-ref"><a href="#fn:11" rel="footnote"><span class="hint--top hint--rounded" aria-label="[Petersen K B, Pedersen M S. The matrix cookbook[J]. Technical University of Denmark, 2008, 7(15): 510.](https://ece.uwaterloo.ca/~ece602/MISC/matrixcookbook.pdf)">[11]</span></a></sup>,这里仅对前文用到的Frobenius范式矩阵的求导过程进行介绍。</p><p>Frobenius范式可以表示成如下的迹形式<br>$$<br>||X||_F=\sqrt{tr(X^TX)}<br>$$<br>首先引入2个求导法则</p><ul><li><p>法则1:若$A,X$为$m\times n$的矩阵,有<br>$$<br>\frac{\partial tr(A^TX)}{\partial X}=\frac{\partial tr(X^TA)}{\partial X}=A<br>$$</p></li><li><p>法则2:若$A$为$m\times m$的矩阵,$X$为$m\times n$的矩阵,有</p></li></ul><p>$$<br>\frac{\partial tr(X^TAX)}{\partial X}=AX+A^TX<br>$$</p><p>因此若求以下导数<br>$$<br>\frac{\partial ||A-BX||^2_F}{\partial X}<br>$$<br>利用以上法则有:<br>$$<br>\begin{align*}<br>\frac{\partial||A-BX||^2_F}{\partial X}&=\frac{\partial tr[(A-BX)^T(A-BX)]}{\partial X}\<br>&=\frac{\partial tr[(A^T-X^TB^T)(A-BX)]}{\partial X}\<br>&=\frac{\partial[tr(A^TA)-2tr(A^TBX)+tr(X^TB^TBX)]}{\partial X}\<br>&=0-2A^TB+B^TBX+B^TBX\<br>&=-2B^T(A+BX)<br>\end{align*}<br>$$</p><h2 id="问题与总结"><a href="#问题与总结" class="headerlink" title="问题与总结"></a>问题与总结</h2><p>在这次深入解读论文的过程中,有很多收获,学到了很多,但也发觉不明白的东西也很多。实验中也遇到各种问题,比如在PI方法的实验中,发现对$W^T=D^{-1}S$的最小特征向量在3圆圈数据集中,具有明显的分层,其分层特征和类别基本一致,这是一次代码写错时发现的。以及论文中出现了非常多优化问题求解,也触及到了很多知识盲区。但同时也激发了我的求知欲望,需要学的东西还有很多。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><section class="footnotes"><div class="footnote-list"><ol><li><span id="fn:1" class="footnote-text"><span><a href="https://dl.acm.org/doi/abs/10.1145/3178876.3185993">Li X, Kao B, Luo S, et al. Rosc: Robust spectral clustering on multi-scale data[C]//Proceedings of the 2018 World Wide Web Conference. 2018: 157-166.</a><a href="#fnref:1" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:2" class="footnote-text"><span><a href="https://dl.acm.org/doi/abs/10.1145/3394486.3403086">Li X, Kao B, Shan C, et al. CAST: a correlation-based adaptive spectral clustering algorithm on multi-scale data[C]//Proceedings of the 26th ACM SIGKDD International Conference on Knowledge Discovery & Data Mining. 2020: 439-449.</a><a href="#fnref:2" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:3" class="footnote-text"><span><a href="https://proceedings.neurips.cc/paper/2004/hash/40173ea48d9567f1f393b20c855bb40b-Abstract.html">Zelnik-Manor L, Perona P. Self-tuning spectral clustering[J]. Advances in neural information processing systems, 2004, 17.</a><a href="#fnref:3" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:4" class="footnote-text"><span><a href="https://csustan.csustan.edu/~tom/Clustering/GraphLaplacian-tutorial.pdf">https://csustan.csustan.edu/~tom/Clustering/GraphLaplacian-tutorial.pdf</a><a href="#fnref:4" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:5" class="footnote-text"><span><a href="https://openreview.net/forum?id=SyWcksbu-H">Lin F, Cohen W W. Power iteration clustering[C]//ICML. 2010.</a><a href="#fnref:5" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:6" class="footnote-text"><span><a href="https://proceedings.neurips.cc/paper/2001/hash/801272ee79cfde7fa5960571fee36b9b-Abstract.html">Ng A, Jordan M, Weiss Y. On spectral clustering: Analysis and an algorithm[J]. Advances in neural information processing systems, 2001, 14.</a><a href="#fnref:6" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:7" class="footnote-text"><span><a href="https://arxiv.org/abs/2205.07308">Li X, Zhu R, Cheng Y, et al. Finding Global Homophily in Graph Neural Networks When Meeting Heterophily[J]. arXiv preprint arXiv:2205.07308, 2022</a><a href="#fnref:7" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:8" class="footnote-text"><span><a href="https://ieeexplore.ieee.org/abstract/document/868688">Shi J, Malik J. Normalized cuts and image segmentation[J]. IEEE Transactions on pattern analysis and machine intelligence, 2000, 22(8): 888-905.</a><a href="#fnref:8" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:9" class="footnote-text"><span><a href="https://en.wikipedia.org/wiki/Whitening_transformation">https://en.wikipedia.org/wiki/Whitening_transformation</a><a href="#fnref:9" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:10" class="footnote-text"><span><a href="https://www.cs.huji.ac.il/w~csip/tirgul2.pdf">https://www.cs.huji.ac.il/w~csip/tirgul2.pdf</a><a href="#fnref:10" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:11" class="footnote-text"><span><a href="https://ece.uwaterloo.ca/~ece602/MISC/matrixcookbook.pdf">Petersen K B, Pedersen M S. The matrix cookbook[J]. Technical University of Denmark, 2008, 7(15): 510.</a><a href="#fnref:11" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:12" class="footnote-text"><span><a href="https://mathworld.wolfram.com/FrobeniusNorm.html">https://mathworld.wolfram.com/FrobeniusNorm.html</a><a href="#fnref:12" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:13" class="footnote-text"><span><a href="https://proceedings.neurips.cc/paper/2011/hash/33ceb07bf4eeb3da587e268d663aba1a-Abstract.html">Grave E, Obozinski G R, Bach F. Trace lasso: a trace norm regularization for correlated designs[J]. Advances in Neural Information Processing Systems, 2011, 24.</a><a href="#fnref:13" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:14" class="footnote-text"><span><a href="https://en.wikipedia.org/wiki/Matrix_norm">https://en.wikipedia.org/wiki/Matrix_norm</a><a href="#fnref:14" rev="footnote" class="footnote-backref"> ↩</a></span></span></li></ol></div></section>]]></content>
<categories>
<category>谱图理论</category>
</categories>
<tags>
<tag>频域</tag>
<tag>python</tag>
<tag>聚类</tag>
<tag>谱聚类</tag>
<tag>多尺度数据</tag>
</tags>
</entry>
<entry>
<title>谱图理论</title>
<link href="/2022/08/20/gnn/spetcral-theory/"/>
<url>/2022/08/20/gnn/spetcral-theory/</url>
<content type="html"><![CDATA[<h1 id="写在前面"><a href="#写在前面" class="headerlink" title="写在前面"></a>写在前面</h1><p>图神经网络(GNN)是近年来愈发火热,用以对图(Graph)数据进行特征提取以及各种下游任务。注意这里的图(Graph)应和图像(Image)区分,图是一种由点集与边集组成的数据结构,常记作$\mathcal{G}=(\mathcal{V,E})$。以下是我学习谱图理论时的一些记录。本文先介绍了线性代数的一些前置知识,包括内积与基、特征值与特征向量、二次型等内容。接着从基变换、频域信号的角度阐述了傅里叶变换的原理。然后通过分析拉普拉斯矩阵的特征值与特征向量,利用二次型的形式说明了其特征值及其对应的特征向量,和傅里叶变换中的频域信号之间的联系。最后使用图的数据结构表示图像,可视化其特征向量,直观感受图的低频和高频分量的差异。本人才疏学浅,错误难免,欢迎交流指正。</p><h1 id="线性代数基础"><a href="#线性代数基础" class="headerlink" title="线性代数基础"></a>线性代数基础</h1><h2 id="内积与基"><a href="#内积与基" class="headerlink" title="内积与基"></a>内积与基</h2><p>我们首先回顾一下线性代数的知识。本科第一次线性代数的时候,都是以繁杂的计算与证明为主,未曾有更直观、直觉的方式去理解。这里我想从线性变换的角度,讲述线性代数中与本文内容相关的知识。</p><p>我们定义实数向量 $\vec x=[x_1, x_2,…,x_n]^T$ 与实数向量的 $\vec x=[x_1, x_2,…,x_n]^T$ 内积为$<\vec x,\vec y>=\sum x_iy_i$。这是高中就学过的知识。但这里,我需要对向量的内积做一些扩充,即复数向量内积的定义。根据 hermitian 内积的定义<sup id="fnref:1" class="footnote-ref"><a href="#fn:1" rel="footnote"><span class="hint--top hint--rounded" aria-label="[https://mathworld.wolfram.com/HermitianInnerProduct.html](https://mathworld.wolfram.com/HermitianInnerProduct.html)">[1]</span></a></sup>,复平面内积定义为 $<\vec x,\vec y>=\sum x_i\bar y_i$。</p><p>如果我们将向量内积扩展到函数空间,我们可以将函数视为无限长的向量,如 $f(x)=[f(x_1),f(x_2),…]$,可以定义函数的内积<sup id="fnref:2" class="footnote-ref"><a href="#fn:2" rel="footnote"><span class="hint--top hint--rounded" aria-label="[https://mathworld.wolfram.com/HilbertSpace.html](https://mathworld.wolfram.com/HilbertSpace.html)">[2]</span></a></sup> $<f,g>=\int_a^b f(x)g(x)dx$ 。若函数在复平面上,则可定义为 $<f,g>=\int_a^b f(x)\overline{g(x)}dx$(有时共轭会放在左边)。</p><p>然后我们来回顾一下向量空间中的基。由一组线性无关的向量可以张成一个向量空间,空间中的任意向量都可以使用这一组向量的线性组合表示。这组向量称作基向量。如果基向量构成的矩阵 $A=[\vec\alpha_1,\vec\alpha_2,…,\vec\alpha_n]^T$ 满足 $AA^T=E$,则这组基向量称为正交基,若还满足 $||\vec\alpha_i||=1,(i=1,2,…,n)$,则称为标准正交基。如,二维向量 $[1,0]^T,[0,1]^T$ 构成一组标准正交基。在复平面中<sup id="fnref:6" class="footnote-ref"><a href="#fn:6" rel="footnote"><span class="hint--top hint--rounded" aria-label="[https://math.mit.edu/~gs/linearalgebra/](https://math.mit.edu/~gs/linearalgebra/)">[6]</span></a></sup>,转置被描述为 $(A^H)<em>{ij}=\overline{A</em>{ji}}$,相比实数域,处了转置还要做一次共轭,称为共轭转置。那么正交基构成的矩阵 $A$ 应满足 $AA^H=E$ 。 </p><p>假如有标准正交基$\vec e_1, \vec e_2,…,\vec e_n$,若该空间中向量<br>$$<br>\vec v=w_1\vec e_1+w_2\vec e_2+,…+w_n\vec e_n=[\vec e_1,\vec e_2,…, \vec e_n][w_1,w_2,…,w_m]^T<br>$$<br>则<br>$$<br>[w_1,w_2,…,w_m]^T=[\vec e_1,\vec e_2,… ,\vec e_n]^{-1}\vec v=[\vec e_1,\vec e_2,… ,\vec e_n]^{T}\vec v<br>$$<br>可以看到,若想获得一组某向量在一组标准正交基上的表示,只需要<strong>让该向量与这组正交基做内积</strong>即可。</p><p>函数也存在着基的概念,若函数 $f(x)=a_ng(x)+b_nh(x)$。那么 $g(x)$ 和 $h(x)$ 就是 $f(x)$ 的基。若内积$<g,h>=0$,$g$ 和 $h$ 是一组正交基。</p><h2 id="特征值与特征向量"><a href="#特征值与特征向量" class="headerlink" title="特征值与特征向量"></a>特征值与特征向量</h2><p>简单地回顾一些特征值与特征向量的定义,设有矩阵$A$,若存在$\lambda,\vec x$,使得$A\vec x=\lambda \vec x$,则$\lambda$称为$A$的特征值,$\vec x$则称为特征向量,$\lambda$的值可以不止一个,同一个$\lambda$可以对应多个线性无关的$\vec x$。如果想了解特征值与特征向量的几何解释,可以参考<sup id="fnref:5" class="footnote-ref"><a href="#fn:5" rel="footnote"><span class="hint--top hint--rounded" aria-label="[https://www.bilibili.com/video/BV1ys411472E?p=14&vd_source=3eafcac5a31e0009a6433cea9bc7ab45](https://www.bilibili.com/video/BV1ys411472E?p=14&vd_source=3eafcac5a31e0009a6433cea9bc7ab45)">[5]</span></a></sup>,这不是本文的重点。</p><p>接下啦我想说明的是一种特殊的矩阵:实对称矩阵,即由实数组成的对称矩阵(若$A=A^T$则称$A$为对称矩阵。该矩阵有很多优良的性质。首先,$N$ 阶是对称矩阵,具有 $N$ 个特征值以及 $N$ 个正交的特征向量。且实对称矩阵一定可以正交对角化(事实上,一个方阵能够正交对角化的<strong>充要条件</strong>就是该矩阵为对称矩阵),即<br>$$<br>Q^{-1}AQ=Q^TAQ=\Lambda<br>$$<br>对角矩阵 $\Lambda$ 对角线上为特征值 $\lambda_1,\lambda_2,…\lambda_{N}$ ,分别对应 $Q=[\vec q_1, \vec q_2,…,\vec{q}<em>{N}]$ 中的特征向量 $\vec q_1,\vec q_2,…\vec{q}</em>{N}$ 。</p><p>由于其$Q$由一组线性无关的正交特征向量组成,他们可以构成一组正交基,特征向量组成的基也成为特征基。我们举个相似对角化的简单应用。假如要求实对称矩阵$A$的幂次预算,如$A^{100}$。直接计算是很麻烦的,若首先将其转化为对角矩阵 $Q^{-1}AQ=\Lambda$,那么 $Q^{-1}AQ…Q^{-1}AQQ^{-1}AQ=Q^{-1}A^{100}Q=\Lambda^{100}$ 。则$A^{100}=Q\Lambda^{100}Q^{-1}$ 。对角矩阵的100次幂是非常容易计算的,这就使得计算量大大减小。</p><h2 id="二次型"><a href="#二次型" class="headerlink" title="二次型"></a>二次型</h2><p>二次型是线性代数中的一个重要概念,它涉及到二次多项式的表达和研究。在线性代数中,一个定义在 n 维向量空间上的二次型可以表示为一个二次多项式。一般地,一个n维向量 $\mathbf{x} = [x_1, x_2, \ldots, x_n]^T$ 的二次型可表示为:<br>$$<br>Q(\mathbf{x}) = \mathbf{x}^T A \mathbf{x}<br>$$<br>其中,$A$ 是一个对称矩阵。对称矩阵的重要性在于它保证了二次型中交叉项的系数相等。在这个表达中,$A$是二次型的系数矩阵,$\mathbf{x}$是变量向量,$\mathbf{x}^T$表示向量的转置。</p><p>对于一个实二次型 $Q(\mathbf{x}) = \mathbf{x}^T A \mathbf{x}$,其中 $A$ 是对称矩阵,展开的形式如下:<br>$$<br>Q(\mathbf{x}) = \mathbf{x}^T A \mathbf{x} = \sum_{i=1}^{n} \sum_{j=1}^{n} a_{ij} x_i x_j<br>$$<br>其中,$a_{ij}$ 是矩阵 $A$ 的元素。这个展开形式表示了二次型中所有可能的平方项和交叉项。</p><p>如果$A$是实对称矩阵,那么二次型称为实二次型。如果$A$是复对称矩阵,那么二次型称为复二次型。实二次型在应用中更为常见,因此下文主要讨论实二次型。</p><p>二次型 $Q(\mathbf{x})$ 被称为<strong>正定</strong>的,如果对于所有非零的$\mathbf{x}$,都有$Q(\mathbf{x}) > 0$,如果在 0 处可以取等号,则称之为<strong>半正定</strong>的。负定的定义类似,只是符号相反。</p><p>实际应用中,二次型在优化问题、统计学、物理学等领域发挥着重要作用。例如,正定二次型常常出现在凸优化问题中,而负定二次型则在稳定性分析中有所应用。</p><p>注:该小结内容由 chatgpt 辅助编写。</p><h1 id="谱分析"><a href="#谱分析" class="headerlink" title="谱分析"></a>谱分析</h1><h2 id="傅里叶变换"><a href="#傅里叶变换" class="headerlink" title="傅里叶变换"></a>傅里叶变换</h2><p><img src="https://imagehost.vitaminz-image.top/gnn-note-5.png"></p><p>在学习傅里叶变换时,想必类似上面的图大家已经见过很多次了,大多数教材都会先从傅里叶级数入手,周期函数可以表示为许多正弦信号的叠加,有如下形式<br>$$<br>f(t)=\frac{a_0}{2}+\sum_{n=1}^{+\infty} [a_n\sin(\frac{n\pi}{l} t )+b_n\cos({\frac{n\pi}{l} t})]<br>$$<br>其中,$n$是整数,$l$为半周期。$1,\sin(n\omega t ),\cos({n\omega t})$ 可以看作是 $f(t)$ 许许多多相互正交的基。这是因为<sup id="fnref:3" class="footnote-ref"><a href="#fn:3" rel="footnote"><span class="hint--top hint--rounded" aria-label="同济大学数学系.高等数学 [M],第七版,高等教育出版社,2014-07-04">[3]</span></a></sup>:$\int^{\pi}<em>{-\pi}\cos nxdx=0$,$\int^{\pi}</em>{-\pi}\sin nxdx=0$, $\int^{\pi}<em>{-\pi}\cos n_1x\sin n_2xdx=0$, $\int^{\pi}</em>{-\pi}\cos n_1x\cos n_2xdx=0$,$\int^{\pi}_{-\pi}\sin n_1x\sin n_2xdx=0$。</p><p>因此我们可以将傅里叶变换看作是将时序信号使用不同频率的信号来表示,而这些信号之间是正交的如果使用 Euler 公式替换,可以转换为如下形式<br>$$<br>f(t)=\sum^{+\infty}<em>{n=-\infty}c_ne^{i\omega t}<br>$$<br>其中,$c_n=\frac{1}{l}\int</em>{-l}^{l}f(t)e^{-i\omega t}dt$。$l$为半周期,$\omega=\frac{n\pi}{l}$,即频率(有些地方会将$\omega$视作角速度,指数项写作$e^{2i\pi\omega t}$)。这里 $e^{i\omega t}$ 也是许多的标准正交基,这是因为 $\int e^{i\omega_1 t}e^{-i\omega_2 t}=0$,$||e^{i\omega}||=1$。那么当 $l\rightarrow +\infty$ 时,$c_n$ 就是傅里叶变换的形式了。即<br>$$<br>\mathcal{F}[f(t)]=\hat f(\omega)=\int_{-\infty}^{+\infty}f(t)e^{-i\omega t}dt<br>$$<br>所以像函数 $\hat f(\omega)$ 其实就是$\omega$对应的傅里叶基 $e^{i\omega t}$ 的系数。上述式子也可以看作是 $f(t)$ 与正交基 $e^{i\omega t}$ 的内积,在<a href="#%E5%86%85%E7%A7%AF%E4%B8%8E%E5%9F%BA">内积与基</a>中提到了,若某个向量希望使用某组正交基来表示,拿就让这个向量和这组标准正交基做内积(函数的内积公式见小节<a href="#%E5%86%85%E7%A7%AF%E4%B8%8E%E5%9F%BA">内积与基</a>),就可以把基方向的分量提取出来了(仿佛一个筛子)。</p><h2 id="拉普拉斯矩阵"><a href="#拉普拉斯矩阵" class="headerlink" title="拉普拉斯矩阵"></a>拉普拉斯矩阵</h2><p><img src="https://imagehost.vitaminz-image.top/gnn-note-7.png"></p><p>我们假设存在图 $\mathcal{G}=(\mathcal{V}, \mathcal{E})$,结点 $v\in \mathcal{V}$ 的特征为 $x_{v}\in\mathcal{X}$。为了简单起见,我们假设图为<strong>无向图</strong>,并且结点特征 <strong>仅有 1 个维度</strong>,因此<strong>所有的结点特征</strong>可以构成一个向量 $x$。</p><p>对于如上的图数据,它的拉普拉斯矩阵如下。其计算方法是$D-A$。$D$是每个结点的度组成的对角矩阵,$A$是图的邻接矩阵。<br>$$<br>L=\left(\begin{array}{rrrrrr}<br> 2 & -1 & 0 & 0 & -1 & 0\<br> -1 & 3 & -1 & 0 & -1 & 0\<br> 0 & -1 & 2 & -1 & 0 & 0\<br> 0 & 0 & -1 & 3 & -1 & -1\<br> -1 & -1 & 0 & -1 & 3 & 0\<br> 0 & 0 & 0 & -1 & 0 & 1\<br>\end{array}\right)<br>$$<br>由于 $L\in \mathbb{R}^{n \times n}$ 是实对称、半正定矩阵,上一节中提到实对称矩阵的一些优良性质,存在标准正交基构成的矩阵 $U$ 及其对应的特征值对角矩阵 $\Lambda$ 使得拉普拉斯矩阵 $L$ 满足<br>$$<br>U^TLU=\Lambda<br>$$<br>由于拉普拉斯矩阵 $L$ 为<strong>半正定</strong>矩阵(利用瑞丽熵即可证明),其对应的特征值按从小到大排序分别为 $0=\lambda_{1},\lambda_{2},\dots,\lambda_{n}$ ,并构成对角矩阵 $\Lambda$ ;其对应的特征向量构成矩阵 $U=[ u_1, u_2,…, u_n]$ ,其中 $\ u_i$ 为 $n$ 个标准正交的特征向量。</p><p>我们<strong>暂且</strong>将 $U$ 矩阵中的标准正交基看作是一组傅里叶基,那么我们希望得到各个基的分量,在<a href="#%E5%86%85%E7%A7%AF%E4%B8%8E%E5%9F%BA">内积与基</a>以及<a href="#%E5%82%85%E9%87%8C%E5%8F%B6%E5%8F%98%E6%8D%A2">傅里叶变换</a>这两个小节都已提到相关的操作,类似的,就有以下等式:<br>$$<br>\begin{gathered}<br>x=U\hat{x}=\hat{x}<em>{1}u</em>{1}+\hat{x}<em>{2}u</em>{2}+\dots+\hat{x}<em>{n}u</em>{n} \<br>\Rightarrow~ \hat{x}=U^Tx<br>\end{gathered}<br>$$<br>$\hat{x}$ 即为各个基的分量,或者用傅里叶变换的角度来说就是各个频域的分量。</p><p>问题来了,我们前面是暂且将它视为傅里叶基,那凭什么可以这么类比呢?我们来回忆一下傅里叶变换,它是将时序信号也是通过基变换,分解成了各个频率相互正交的频域信号的加权求和,而所谓的傅里叶基就是不同频率的频域信号。问题就在于,拉普拉斯矩阵 $L$ 的特征向量,何以表示频域信号。</p><p>我们只需利用简单的变换,即可说明。<br>$$<br>u_{i}^TLu_{i}=u_{i}^T\lambda_{i} u_{i}=\lambda_{i}<br>$$<br>其中,$u_{i}^TLu_{i}$ 为二次型的形式。它可以表示什么呢?</p><p>根据小节<a href="#%E4%BA%8C%E6%AC%A1%E5%9E%8B">二次型</a>的公式,我们有以下公式推导:<br>$$<br>\begin{align*}<br>x^TLx&=\sum_{i\in \mathcal{V}}d_{i}x_{i}^2-\sum_{(i,j)\in \mathcal{E}}2x_{i}x_{j}\<br>&=\sum_{(i,j)\in \mathcal{E}}(x_{i}^2-2x_{i}x_{j}+x_{j}^2)\<br>&=\sum_{(i,j)\in \mathcal{E}}(x_{i}-x_{j})^2\<br>\end{align*}<br>$$<br>因此,二次型 $x^TLx$ 可以衡量结点特征之间的差异。其结点之间差异越大,其震荡就越大,即越粗糙;否则,震荡越小,也就越平滑。</p><p>也就是说特征值 $\lambda_{i}$ 可以体现特征向量的平滑程度。<strong>特征向量所对应的特征值越大,就说明震荡越明显,越不平滑,对应的是傅里叶变换中的高频信号;反之,则越平滑,对应傅里叶变换中的低频信号。</strong></p><p>因此,我们将拉普拉斯矩阵的特征基视作一组傅里叶基是合理,我们利用拉普拉斯的特征分解,达到了和时序信号傅里叶变换类似的效果。我们将这种分解方法称为谱分解(Spectral Decomposition),相关的分析方式称为图的谱分析(Graph Spectral Analysis),相关理论称为谱图理论(Graph Spectral Theory)。</p><h2 id="可视化"><a href="#可视化" class="headerlink" title="可视化"></a>可视化</h2><p>如何可视化呢图的频域信号呢?事实上,我们可以借助图像(Image)的结构来可视化。图像通常由一个矩阵表示,矩阵中的每一个值为像素点的值。根据通道数,图像又可以分为彩色图像和灰度图像。</p><p>事实上图像它也可表示称图的结构。如下图所示<sup id="fnref:11" class="footnote-ref"><a href="#fn:11" rel="footnote"><span class="hint--top hint--rounded" aria-label="[https://distill.pub/2021/gnn-intro/](https://distill.pub/2021/gnn-intro/)">[11]</span></a></sup>:</p><p><img src="https://imagehost.vitaminz-image.top/gnn-note-8.png" alt="gnn-note-8.png"><br><img src="https://imagehost.vitaminz-image.top/gnn-note-8.png"></p><p>其中,每个像素与上下左右对角线相邻接。类似的,一个序列也可以转变为图的结构,同样也可以借助它来做可视化 。这里暂且利用图像的结构来可视化。</p><p>可视化的算法可描述如下:</p><ul><li>step1. 选择(10, 10)尺寸的图像结构,</li><li>step2. 根据每个像素与上下左右对角线相邻接的规则构造邻接矩阵A, shape: (100, 100)</li><li>step3. 根据邻接矩阵A构造拉普拉斯矩阵L, shape: (100, 100)</li><li>step4. 对拉普拉斯矩阵进行对角化, 求得特征基矩阵U, shape(100, 100),对角矩阵P, shape(100, 100),注意上述2个矩阵均按特征值的大小升序排序。</li><li>step5. 取特征值大小前10的特征基,每个特征基重构为图像结构Image, shape(10, 10),进行可视化</li></ul><p>经过以上的操作后,最后的结果如下图所示,特征值按从小到大排序。这就是每个频率所对应的分量。可以看到图像一开始是纯色的,后来开始出现了类似波形的渐变过程,随着频率的增加,其波形也越复杂。</p><p><img src="https://imagehost.vitaminz-image.top/gnn-note-9.png"></p><p>以下为实现代码</p><figure class="highlight python"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">import</span> numpy <span class="hljs-keyword">as</span> np<br><span class="hljs-keyword">import</span> math<br><span class="hljs-keyword">import</span> matplotlib.pyplot <span class="hljs-keyword">as</span> plt<br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">getLaplacianOfImage</span>(<span class="hljs-params">M, N</span>):<br> idx = np.array([i <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(M * N)]).reshape((M, N))<br> tmp = np.ones((M+<span class="hljs-number">2</span>, N+<span class="hljs-number">2</span>), dtype=np.int32) * M * N<br> tmp[<span class="hljs-number">1</span>:M+<span class="hljs-number">1</span>, <span class="hljs-number">1</span>:N+<span class="hljs-number">1</span>] = idx<br><br> directH = [<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, <span class="hljs-number">1</span>, -<span class="hljs-number">1</span>]<br> directV = [<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, <span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, <span class="hljs-number">1</span>]<br> A = np.zeros((M * N + <span class="hljs-number">1</span>, M * N + <span class="hljs-number">1</span>))<br> <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(<span class="hljs-number">1</span>, M+<span class="hljs-number">1</span>):<br> <span class="hljs-keyword">for</span> j <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(<span class="hljs-number">1</span>, N+<span class="hljs-number">1</span>):<br> <span class="hljs-keyword">for</span> h, v <span class="hljs-keyword">in</span> <span class="hljs-built_in">zip</span>(directH, directV):<br> A[tmp[i, j], tmp[i+h, j+v]] = <span class="hljs-number">1</span><br> A = A[<span class="hljs-number">0</span>:M*N, <span class="hljs-number">0</span>:M*N]<br> L = np.diag(np.<span class="hljs-built_in">sum</span>(A, axis=<span class="hljs-number">1</span>)) - A<br> <span class="hljs-keyword">return</span> L<br><br>M, N = <span class="hljs-number">10</span>, <span class="hljs-number">10</span><br>L = getLaplacianOfImage(M, N)<br>eigenvalue, featurevector = np.linalg.eigh(L)<br>eigenbase = featurevector.T<br><br>min_val, max_val = np.<span class="hljs-built_in">min</span>(eigenbase), np.<span class="hljs-built_in">max</span>(eigenbase)<br>plt.figure(figsize=(<span class="hljs-number">15</span>, <span class="hljs-number">15</span>))<br><span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(<span class="hljs-number">9</span>):<br> fv = eigenbase[i].reshape((M, N))<br> plt.subplot(<span class="hljs-number">3</span>, <span class="hljs-number">3</span>, i+<span class="hljs-number">1</span>)<br> plt.imshow(fv, vmin=min_val, vmax=max_val)<br> plt.colorbar(fraction=<span class="hljs-number">0.045</span>)<br></code></pre></td></tr></tbody></table></figure><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>写该博客的起因是学习GNN时,不理解为什么使用$L$矩阵,而不是用邻接矩阵或者其他实对称矩阵,以及在文献中<sup id="fnref:13" class="footnote-ref"><a href="#fn:13" rel="footnote"><span class="hint--top hint--rounded" aria-label="[Li X, Zhu R, Cheng Y, et al. Finding Global Homophily in Graph Neural Networks When Meeting Heterophily[J]. arXiv preprint arXiv:2205.07308, 2022.](https://arxiv.org/abs/2205.07308)">[13]</span></a></sup>看到 $L$ 矩阵的谱分解其实就是傅里叶变换在图领域的应用。为了深入了解这二者的关系,捡起了以前学过但又未深入理解的知识。完成这篇博客,还是很有收获的。</p><h1 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h1><section class="footnotes"><div class="footnote-list"><ol><li><span id="fn:1" class="footnote-text"><span><a href="https://mathworld.wolfram.com/HermitianInnerProduct.html">https://mathworld.wolfram.com/HermitianInnerProduct.html</a><a href="#fnref:1" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:2" class="footnote-text"><span><a href="https://mathworld.wolfram.com/HilbertSpace.html">https://mathworld.wolfram.com/HilbertSpace.html</a><a href="#fnref:2" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:3" class="footnote-text"><span>同济大学数学系.高等数学 [M],第七版,高等教育出版社,2014-07-04<a href="#fnref:3" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:4" class="footnote-text"><span><a href="https://www.bilibili.com/video/BV1za411F76U?spm_id_from=333.337.search-card.all.click&vd_source=3eafcac5a31e0009a6433cea9bc7ab45">https://www.bilibili.com/video/BV1za411F76U?spm_id_from=333.337.search-card.all.click&vd_source=3eafcac5a31e0009a6433cea9bc7ab45</a><a href="#fnref:4" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:5" class="footnote-text"><span><a href="https://www.bilibili.com/video/BV1ys411472E?p=14&vd_source=3eafcac5a31e0009a6433cea9bc7ab45">https://www.bilibili.com/video/BV1ys411472E?p=14&vd_source=3eafcac5a31e0009a6433cea9bc7ab45</a><a href="#fnref:5" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:6" class="footnote-text"><span><a href="https://math.mit.edu/~gs/linearalgebra/">https://math.mit.edu/~gs/linearalgebra/</a><a href="#fnref:6" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:7" class="footnote-text"><span><a href="https://en.wikipedia.org/wiki/Hilbert_space#Fourier_analysis">https://en.wikipedia.org/wiki/Hilbert_space#Fourier_analysis</a><a href="#fnref:7" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:8" class="footnote-text"><span><a href="https://en.wikipedia.org/wiki/Hilbert_space#Fourier_analysis">https://en.wikipedia.org/wiki/Discrete_Fourier_transform</a><a href="#fnref:8" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:9" class="footnote-text"><span><a href="https://en.wikipedia.org/wiki/DFT_matrix">https://en.wikipedia.org/wiki/DFT_matrix</a><a href="#fnref:9" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:10" class="footnote-text"><span><a href="https://csustan.csustan.edu/~tom/Clustering/GraphLaplacian-tutorial.pdf">https://csustan.csustan.edu/~tom/Clustering/GraphLaplacian-tutorial.pdf</a><a href="#fnref:10" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:11" class="footnote-text"><span><a href="https://distill.pub/2021/gnn-intro/">https://distill.pub/2021/gnn-intro/</a><a href="#fnref:11" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:12" class="footnote-text"><span><a href="https://distill.pub/2021/understanding-gnns/#learning">https://distill.pub/2021/understanding-gnns/#learning</a><a href="#fnref:12" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:13" class="footnote-text"><span><a href="https://arxiv.org/abs/2205.07308">Li X, Zhu R, Cheng Y, et al. Finding Global Homophily in Graph Neural Networks When Meeting Heterophily[J]. arXiv preprint arXiv:2205.07308, 2022.</a><a href="#fnref:13" rev="footnote" class="footnote-backref"> ↩</a></span></span></li></ol></div></section>]]></content>
<categories>
<category>谱图理论</category>
</categories>
<tags>
<tag>频域</tag>
<tag>谱图理论</tag>
<tag>傅里叶变换</tag>
<tag>线性代数</tag>
<tag>python</tag>
</tags>
</entry>
<entry>
<title>论文速读<一>:关系抽取与提示学习</title>
<link href="/2022/07/16/kg/lun-wen-su-du-1-guan-xi-chou-qu/"/>
<url>/2022/07/16/kg/lun-wen-su-du-1-guan-xi-chou-qu/</url>
<content type="html"><![CDATA[<h1 id="论文速读:关系抽取与提示学习"><a href="#论文速读:关系抽取与提示学习" class="headerlink" title="论文速读<一>:关系抽取与提示学习"></a>论文速读<一>:关系抽取与提示学习</h1><p>论文速读系列为对论文的核心思想进行快速抓取,仅记录论文的Key Idea。本期为论文速读系列的第一期,选取了5篇近期知识图谱领域的关系抽取相关论文。</p><h2 id="Key-Idea"><a href="#Key-Idea" class="headerlink" title="Key Idea"></a>Key Idea</h2><h3 id="Enriching-pre-trained-language-model-with-entity-information-for-relation-classification-1"><a href="#Enriching-pre-trained-language-model-with-entity-information-for-relation-classification-1" class="headerlink" title="Enriching pre-trained language model with entity information for relation classification.[1]"></a><em>Enriching pre-trained language model with entity information for relation classification.</em><sup id="fnref:1" class="footnote-ref"><a href="#fn:1" rel="footnote"><span class="hint--top hint--rounded" aria-label="[Shanchan Wu and Yifan He. 2019. Enriching pre-trained language model with entity information for relation classification. In Proceedings of the 28th ACM international conference on information and knowledge management, pages 2361–2364.](https://dl.acm.org/doi/abs/10.1145/3357384.3358119)">[1]</span></a></sup></h3><p><img src="https://imagehost.vitaminz-image.top/SEofZJU-1.png"></p><p>R-Bert模型的框架如上图所示,其主要思路就是对每个句子的关系的头部放一个分类头,并在2个实体出使用特殊标记,放进预训练好后的Bert中,在输出中,实体所对应的embedding做一个平均值,和分类头的embedding分别经过3个MLP,最后做一个拼接,输出到softmax中去,进行分类。</p><h3 id="Relation-classification-with-entity-type-restriction"><a href="#Relation-classification-with-entity-type-restriction" class="headerlink" title="Relation classification with entity type restriction."></a>Relation classification with entity type restriction.</h3><p><img src="https://imagehost.vitaminz-image.top/SEofZJU-2.png"></p><p>该论文事实上是提出了一个算法无关的流程。</p><p>传统的关系抽取是将句子、实体以及实体类型一起丢进一个分类器中,然后输出关系的类别。但假如根据实体的类型可以一定程度上的筛掉一些不可能的关系。因此区别于传统的做法,它首先将实体丢进一个分类器,然后获得其类别之后,根据实体的类型去训练专用的分类器,最后句子一起输入到这个专用分类器中,输出对应的类别。</p><h3 id="Ptr-Prompt-tuning-with-rules-for-text-classification"><a href="#Ptr-Prompt-tuning-with-rules-for-text-classification" class="headerlink" title="Ptr: Prompt tuning with rules for text classification."></a>Ptr: Prompt tuning with rules for text classification.</h3><p><img src="https://imagehost.vitaminz-image.top/SEofZJU-3.png"></p><p>该论文提出了一种提示学习的方法对文本进行分类。提示学习就是在Fine Tuning的时候加一个模板化的提示,这样可以使得原问题更贴近于自然语言处理的问题,从而更贴合预训练的自然语言模型。</p><p>该论文的模式来源于推理规则,他在Fine Tuning时,在句子的结尾增添提示。给出句子的实体,但对实体的类型和实体间的关系做一个mask。这样可以使得模型能将实体与对应的类型结合起来,推理出他们之间的关系。</p><h3 id="Summarization-as-Indirect-Supervision-for-Relation-Extraction"><a href="#Summarization-as-Indirect-Supervision-for-Relation-Extraction" class="headerlink" title="Summarization as Indirect Supervision for Relation Extraction"></a>Summarization as Indirect Supervision for Relation Extraction</h3><p><img src="https://imagehost.vitaminz-image.top/SEofZJU-4.png"></p><p>该论文将摘要模型用于关系抽取。</p><p>若想要将摘要模型应用在句子的关系抽取上,需要将关系抽取问题转化为摘要问题。直觉上来讲,上游模型的特点和下游任务的关系月紧密,它的效果也会越好。</p><p>和通常的提示学习想法类似,它也是对原句子进行模板化改造,只不过将句子转换为一个段落而已。如上图所示,所谓的段落就是,加上“主语是。。。”,“谓语是。。。”这样的信息。</p><p>由于摘要模型输入是一个段落,输出是一个句子,而不是一个标签。因此输出的标签也应当做一个改造。改造的方式也很简单,比如”city of birth”就改在为”subj was born in the city obj”。需要注意的是,主语放在句子开头,宾语放在句子结尾。这是方便后面的预测。</p><p>其预测过程是构造一棵字典树,由于所有的句子主语放在开头,所以他们有同一个根结点。从根据结点出发,每次遇到一个分叉就使用decoder进行预测,给出每个分叉的概率。最后每个类别的概率,就是跟结点到叶结点的路上所有概率的乘积。选择最大的就是输出。</p><h3 id="Prefix-tuning-Optimizing-continuous-prompts-for-generation"><a href="#Prefix-tuning-Optimizing-continuous-prompts-for-generation" class="headerlink" title="Prefix-tuning: Optimizing continuous prompts for generation."></a>Prefix-tuning: Optimizing continuous prompts for generation.</h3><p><img src="https://imagehost.vitaminz-image.top/SEofZJU-5.png"></p><p>提示学习所用的提示模板往往是一个个离散的词。该文章提出了一种将离散词嵌入到连续空间中去,比如使用2个向量,使其起到提示的效果。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><section class="footnotes"><div class="footnote-list"><ol><li><span id="fn:1" class="footnote-text"><span><a href="https://dl.acm.org/doi/abs/10.1145/3357384.3358119">Shanchan Wu and Yifan He. 2019. Enriching pre-trained language model with entity information for relation classification. In Proceedings of the 28th ACM international conference on information and knowledge management, pages 2361–2364.</a><a href="#fnref:1" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:2" class="footnote-text"><span><a href="https://arxiv.org/abs/2105.08393">Shengfei Lyu and Huanhuan Chen. 2021. Relation classification with entity type restriction. In Findings of the Association for Computational Linguistics: ACLIJCNLP 2021, pages 390–395, Online. Association for Computational Linguistics.</a><a href="#fnref:2" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:3" class="footnote-text"><span><a href="https://arxiv.org/abs/2105.11259">Xu Han, Weilin Zhao, Ning Ding, Zhiyuan Liu, and Maosong Sun. 2021. Ptr: Prompt tuning with rules for text classification. arXiv preprint arXiv:2105.11259.</a><a href="#fnref:3" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:4" class="footnote-text"><span><a href="https://arxiv.org/abs/2205.09837">Lu K, Hsu I, Zhou W, et al. Summarization as Indirect Supervision for Relation Extraction[J]. arXiv preprint arXiv:2205.09837, 2022.</a><a href="#fnref:4" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:5" class="footnote-text"><span><a href="https://arxiv.org/abs/2101.00190">Xiang Lisa Li and Percy Liang. 2021. Prefix-tuning: Optimizing continuous prompts for generation. In Proceedings of the 59th Annual Meeting of the Association for Computational Linguistics and the 11th International Joint Conference on Natural Language Processing (V olume 1: Long Papers), pages 4582–4597, Online. Association for Computational Linguistics.</a><a href="#fnref:5" rev="footnote" class="footnote-backref"> ↩</a></span></span></li></ol></div></section>]]></content>
<categories>
<category>知识图谱</category>
</categories>
<tags>
<tag>图</tag>
<tag>知识图谱</tag>
<tag>关系抽取</tag>
<tag>提示学习</tag>
</tags>
</entry>
<entry>
<title>动手写一个编译器</title>
<link href="/2022/06/20/compiler/dong-shou-xie-yi-ge-bian-yi-qi/"/>
<url>/2022/06/20/compiler/dong-shou-xie-yi-ge-bian-yi-qi/</url>
<content type="html"><![CDATA[<h1 id="ToyC语言"><a href="#ToyC语言" class="headerlink" title="ToyC语言"></a>ToyC语言</h1><p>本项目用于学习编译原理。</p><p>将参照龙书版本的《编译原理》,</p><p>以及LLVM的编译器制作教程:<a href="https://llvm.org/docs/tutorial/MyFirstLanguageFrontend/LangImpl01.html">https://llvm.org/docs/tutorial/MyFirstLanguageFrontend/LangImpl01.html</a></p><p>制作一个完整的编译器前端。</p><h2 id="编译器的结构"><a href="#编译器的结构" class="headerlink" title="编译器的结构"></a>编译器的结构</h2><p><img src="https://imagehost.vitaminz-image.top/ToyC-1.png"></p><center>图1:摘自《Compilers Principles, Techniques & Tools》第二版Figure 2.3</center><h3 id="UML"><a href="#UML" class="headerlink" title="UML"></a>UML</h3><p><img src="https://imagehost.vitaminz-image.top/UML.png"></p><center>图2:UML</center><h3 id="词法分析器-Lexer"><a href="#词法分析器-Lexer" class="headerlink" title="词法分析器(Lexer)"></a>词法分析器(Lexer)</h3><h4 id="设计Token"><a href="#设计Token" class="headerlink" title="设计Token"></a>设计Token</h4><p>Token主要分为: 多字符保留字、标识符、数字以及其余单个字符。</p><ul><li>多字符保留字:<ul><li>控制流语句:if, else, do, while, break</li><li>布尔运算:true, false, &&, ||</li><li>比较运算:>=, <=, ==, !=</li><li>变量类型:int, float, bool, char</li></ul></li><li>标识符:<ul><li>正则表达式:[_a-zA-Z][_a-zA-Z0-9]*</li></ul></li><li>数字:<ul><li>整型正则表达式:[0-9]+</li><li>浮点型正则表达式:[0-9]+.[0-9]*</li></ul></li><li>其余单个字字符</li></ul><h4 id="识别算法"><a href="#识别算法" class="headerlink" title="识别算法"></a>识别算法</h4><figure class="highlight"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs"><br></code></pre></td></tr></tbody></table></figure><h4 id="测试"><a href="#测试" class="headerlink" title="测试"></a>测试</h4><p>输入:字符串<br>输出:按序输出token流,每个token占一行</p><p>词法分析器的步骤可用如下伪代码表示:</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs c">Input: <br>Output: <br>cache = <span class="hljs-string">' '</span><br>Scan() {<br> <span class="hljs-keyword">do</span>{<br> <span class="hljs-keyword">if</span> cache != 空格、换行、制表符<br> <span class="hljs-keyword">break</span>;<br> }<span class="hljs-keyword">while</span>(read(cache));<br><br> t = readKey(cache) <span class="hljs-comment">// 识别保留字</span><br> t = readNumber(cache); <span class="hljs-comment">// 识别实数</span><br> t = <br><br> <span class="hljs-keyword">return</span> t;<br>}<br></code></pre></td></tr></tbody></table></figure><h3 id="语法分析器-Parser"><a href="#语法分析器-Parser" class="headerlink" title="语法分析器(Parser)"></a>语法分析器(Parser)</h3><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><code class="hljs c++">PROGRAM -> BLOCK<br>BLOCK -> <span class="hljs-string">'{'</span> DECLS STMTS <span class="hljs-string">'}'</span><br>DECLS -> DECLS DECL<br> -> eps<br>DECL -> TYPE id<span class="hljs-string">';'</span><br>TYPE-> basic DIMS<br>DIMS-> <span class="hljs-string">'['</span>DIMS<span class="hljs-string">']'</span><br>-> eps<br>STMTS-> STMTS STMT<br> -> eps<br><br>STMT-> ASSIGN<span class="hljs-string">';'</span><br>-> <span class="hljs-keyword">if</span> ( BOOL ) STMT<br> -> <span class="hljs-keyword">if</span> ( BOOL ) STMT <span class="hljs-keyword">else</span> STMT<br> -> <span class="hljs-keyword">while</span> ( BOOL ) STMT<br> -> <span class="hljs-keyword">do</span> STMT <span class="hljs-keyword">while</span> ( BOOL )<br> -> <span class="hljs-keyword">break</span><span class="hljs-string">';'</span><br> -> BLOCK<br>ASSIGN -> id OFFSET = BOOL<br>OFFSET -> [ BOOL ] OFFSET<br> -> eps<br> <br>BOOL-> BOOL <span class="hljs-string">"||"</span> JOIN<br> -> JOIN<br>JOIN-> JOIN <span class="hljs-string">"&&"</span> EQAULITY<br> -> EQUALITY<br>EQUALITY-> EQUALITY <span class="hljs-string">"=="</span> CMP<br> -> EQUALITY <span class="hljs-string">"!="</span> CMP<br> -> CMP<br>CMP-> EXPR < EXPR<br> -> EXPR <= EXPR<br> -> EXPR >= EXPR<br> -> EXPR > EXPR<br> -> EXPR<br>EXPR-> EXPR + TERM<br> -> EXPR - TERM<br> -> TERM<br>TERM-> TERM * UNARY<br> -> TERM / UNARY<br> -> UNARY<br>UNARY-> <span class="hljs-string">'!'</span> UNARY<br> -> <span class="hljs-string">'-'</span> UNARY<br> -> FACTOR<br>FACTOR-> ( BOOL )<br> -> id OFFSET<br> -> number<br> -> real<br> -> <span class="hljs-literal">true</span><br> -> <span class="hljs-literal">false</span><br></code></pre></td></tr></tbody></table></figure><h3 id="符号表-Symbol-Table"><a href="#符号表-Symbol-Table" class="headerlink" title="符号表(Symbol Table)"></a>符号表(Symbol Table)</h3><img src="https://imagehost.vitaminz-image.top/ToyC-2.png" style="zoom:50%;"><center>图2:符号表示意图</center><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><code class="hljs c++">PROGRAM -> {top = null;} BLOCK<br>BLOCK -> <span class="hljs-string">'{'</span> <br>{ saved = top;<span class="hljs-comment">// 保留现场,saved</span><br> top = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Scope</span>(top); } <span class="hljs-comment">// 碰到块建立符号表,top指向当前块符号表</span><br>DECLS DECL <br>{ top = saved; }<span class="hljs-comment">// 恢复现场,</span><br><span class="hljs-string">'}'</span><br>DECLS -> DECLS DECL<br> -> eps<br>DECL -> TYPE id<span class="hljs-string">';'</span> { s = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Symbol</span>(id);<span class="hljs-comment">// </span><br> s.type = TYPE.lexeme;<br> top.<span class="hljs-built_in">put</span>(id.lexeme, s); }<br>TYPE -> basic {DIMS.type = basic; } <br> DIMS {TYPE.lexeme = DIMS.type; }<br>DIMS -> <span class="hljs-string">'['</span>num<span class="hljs-string">']'</span> DIMS { Array.sz = num * Array.sz;<br> DIMS.type = Array; }<br> -> eps { Array.sz = <span class="hljs-number">1</span>; <br> Array.type = Dims.type; }<br>STMTS -> STMTS STMT <br> -> eps<br>STMT -> BLOCK<br>STMT -> .... > ... id { s = top.<span class="hljs-built_in">get</span>(id.lexeme); } ....<br></code></pre></td></tr></tbody></table></figure><p>单元测试:</p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs c++">PROGRAM -> BLOCK<br>BLOCK -> <span class="hljs-string">'{'</span> DECLS STMTS <span class="hljs-string">'}'</span><br>DECLS -> DECLS DECL<br> -> eps<br>DECL -> TYPE id <span class="hljs-string">';'</span><br>TYPE-> basic DIMS<br>DIMS-> <span class="hljs-string">'['</span>num<span class="hljs-string">']'</span>DIMS<br>-> eps<br>STMTS-> STMTS STMT<br> -> eps<br>STMT-> BLOCK<br> -> FACTOR <span class="hljs-string">';'</span><br>FACTOR-> id<br></code></pre></td></tr></tbody></table></figure><h3 id="中间代码-Intermediate-Code"><a href="#中间代码-Intermediate-Code" class="headerlink" title="中间代码(Intermediate Code)"></a>中间代码(Intermediate Code)</h3><h4 id="表达式的计算"><a href="#表达式的计算" class="headerlink" title="表达式的计算"></a>表达式的计算</h4><img src="https://imagehost.vitaminz-image.top/ToyC-3.png" style="zoom: 25%;"><p><img src="https://imagehost.vitaminz-image.top/ToyC-5.png" style="zoom: 25%;"></p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><code class="hljs c++">PROGRAM -> STMTS<br>STMTS-> STMTS STMT<br> -> eps<br>STMT-> ASSIGN<span class="hljs-string">';'</span><br>ASSIGN -> id OFFSET = BOOL<br>OFFSET -> [ BOOL ] OFFSET<br> -> eps<br><br>BOOL-> BOOL <span class="hljs-string">"||"</span> JOIN<br> -> JOIN<br>JOIN-> JOIN <span class="hljs-string">"&&"</span> EQAULITY<br> -> EQUALITY<br>EQUALITY-> EQUALITY <span class="hljs-string">"=="</span> CMP<br> -> EQUALITY <span class="hljs-string">"!="</span> CMP<br> -> CMP<br>CMP-> EXPR < EXPR<br> -> EXPR <= EXPR<br> -> EXPR >= EXPR<br> -> EXPR > EXPR<br>EXPR-> EXPR + TERM<br> -> EXPR - TERM<br> -> TERM<br>TERM-> TERM * UNARY<br> -> TERM / UNARY<br> -> UNARY<br>UNARY-> <span class="hljs-string">'!'</span> UNARY<br> -> <span class="hljs-string">'-'</span> UNARY<br> -> FACTOR<br>FACTOR-> ( BOOL )<br> -> OFFSET<br> -> number<br> -> real<br> -> <span class="hljs-literal">true</span><br> -> <span class="hljs-literal">false</span><br> -> id OFFSET<br></code></pre></td></tr></tbody></table></figure><p>输入</p><figure class="highlight txt"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs txt">a = b + 1;<br>c = a + 32 * 43 - b / ( g - 2);<br>d = e + f;<br>c = -e[3];<br>c[33+34+sd3*c] = de*c + c2;<br>f[c[2*d]+4] = df + de[23-s[f]];<br>s[m][n][o] = -d[3][x];<br>a = !a || s && (c || d) || !f && kk ;<br>b = (a + c) > (b * 2 - 1) || a < b && c;<br></code></pre></td></tr></tbody></table></figure><p>输出</p><figure class="highlight txt"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br></pre></td><td class="code"><pre><code class="hljs txt">a = b + 1<br>L3:t1 = 32 * 43<br>t2 = a + t1<br>t3 = g - 2<br>t4 = b / t3<br>c = t2 - t4<br>L4:d = e + f<br>L5:t5 = 3 * 10<br>t6 = e[t5]<br>c = -t6<br>L6:t7 = 33 + 34<br>t8 = sd3 * c<br>t9 = t7 + t8<br>t10 = t9 * 10<br>t11 = c[t10]<br>t12 = de * c<br>t13 = t12 + c2<br>c[t11] = t13<br>L7:t14 = 2 * d<br>t15 = t14 * 10<br>t16 = c[t15]<br>t17 = t16 + 4<br>t18 = t17 * 10<br>t19 = f[t18]<br>t20 = f * 10<br>t21 = s[t20]<br>t22 = 23 - t21<br>t23 = t22 * 10<br>t24 = de[t23]<br>t25 = df + t24<br>f[t19] = t25<br>L8:t26 = m * 10<br>t27 = n * 10<br>t28 = t26 + t27<br>t29 = o * 10<br>t30 = t28 + t29<br>t31 = s[t30]<br>t32 = 3 * 10<br>t33 = x * 10<br>t34 = t32 + t33<br>t35 = d[t34]<br>t36 = -t35<br>s[t31] = t36<br>L9:if False a goto L13<br>t37 = 23 * 10<br>t38 = f * 10<br>t39 = t37 + t38<br>t40 = sp[t39]<br>if False t40 goto L14<br>if c goto L13<br>if d goto L13<br>goto L14<br>L14:if f goto L11<br>if kk goto L13<br>goto L11<br>L13:t41 = true<br>goto L12<br>L11:t41 = false<br>L12:a = t41<br>L10:t42 = a + c<br>t43 = b * 2<br>t44 = t43 - 1<br>if t42 > t44 goto L17<br>if False a < b goto L15<br>if c goto L17<br>goto L15<br>L17:t45 = true<br>goto L16<br>L15:t45 = false<br>L16:b = t45<br>L2:<br><br></code></pre></td></tr></tbody></table></figure><h4 id="控制流语句的中间代码"><a href="#控制流语句的中间代码" class="headerlink" title="控制流语句的中间代码"></a>控制流语句的中间代码</h4><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><code class="hljs c++">PROGRAM -> BLOCK<br>BLOCK -> <span class="hljs-string">'{'</span> STMTS <span class="hljs-string">'}'</span><br>STMTS-> STMTS STMT<br> -> eps<br><br>STMT-> ASSIGN<span class="hljs-string">';'</span><br>-> <span class="hljs-keyword">if</span> ( BOOL ) STMT<br> -> <span class="hljs-keyword">if</span> ( BOOL ) STMT <span class="hljs-keyword">else</span> STMT<br> -> <span class="hljs-keyword">while</span> ( BOOL ) STMT<br> -> <span class="hljs-keyword">do</span> STMT <span class="hljs-keyword">while</span> ( BOOL )<br> -> <span class="hljs-keyword">break</span><span class="hljs-string">';'</span><br> -> BLOCK<br>ASSIGN -> id OFFSET = BOOL<br>OFFSET -> [ BOOL ]<br> -> eps<br> <br>BOOL-> BOOL <span class="hljs-string">"||"</span> JOIN<br> -> JOIN<br>JOIN-> JOIN <span class="hljs-string">"&&"</span> EQAULITY<br> -> EQUALITY<br>EQUALITY-> EQUALITY <span class="hljs-string">"=="</span> CMP<br> -> EQUALITY <span class="hljs-string">"!="</span> CMP<br> -> CMP<br>CMP-> EXPR < EXPR<br> -> EXPR <= EXPR<br> -> EXPR >= EXPR<br> -> EXPR > EXPR<br><br></code></pre></td></tr></tbody></table></figure><p>输入</p><figure class="highlight txt"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs txt">{<br> if ((a + c) > (b * 2 - 1) || a < b && c){<br> a = b * 2 - c + (b + 2 * d);<br> }<br> while (a > 3){<br> a = a + 1;<br> do a = a + 3; while( b > 2);<br> if (c - 3){<br> b = c + 5;<br> d = a + 3;<br> }<br> else {<br> c = 3;<br> break;<br> }<br> }<br> while (c < b){<br> a = 3;<br> }<br>}<br></code></pre></td></tr></tbody></table></figure><p>输出</p><figure class="highlight vbnet"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><code class="hljs vbnet"><span class="hljs-symbol">L1:</span>t1 = a + c<br>t2 = b * <span class="hljs-number">2</span><br>t3 = t2 - <span class="hljs-number">1</span><br><span class="hljs-keyword">if</span> t1 > t3 <span class="hljs-keyword">goto</span> L5<br><span class="hljs-keyword">if</span> <span class="hljs-literal">False</span> a < b <span class="hljs-keyword">goto</span> L3<br><span class="hljs-keyword">if</span> c <span class="hljs-keyword">goto</span> L5<br><span class="hljs-keyword">goto</span> L3<br><span class="hljs-symbol">L5:</span>L4:t4 = b * <span class="hljs-number">2</span><br>t5 = t4 - c<br>t6 = <span class="hljs-number">2</span> * d<br>t7 = b + t6<br>a = t5 + t7<br><span class="hljs-symbol">L3:</span><span class="hljs-keyword">if</span> <span class="hljs-literal">False</span> a > <span class="hljs-number">3</span> <span class="hljs-keyword">goto</span> L6<br><span class="hljs-symbol">L7:</span>a = a + <span class="hljs-number">1</span><br><span class="hljs-symbol">L8:</span>a = a + <span class="hljs-number">3</span><br><span class="hljs-symbol">L10:</span><span class="hljs-keyword">if</span> b > <span class="hljs-number">2</span> <span class="hljs-keyword">goto</span> L8<br><span class="hljs-keyword">goto</span> L9<br><span class="hljs-symbol">L9:</span><span class="hljs-keyword">if</span> <span class="hljs-literal">False</span> c - <span class="hljs-number">3</span> <span class="hljs-keyword">goto</span> L12<br><span class="hljs-symbol">L11:</span>b = c + <span class="hljs-number">5</span><br><span class="hljs-symbol">L13:</span>d = a + <span class="hljs-number">3</span><br><span class="hljs-keyword">goto</span> L3<br><span class="hljs-symbol">L12:</span>c = <span class="hljs-number">3</span><br><span class="hljs-symbol">L14:</span><span class="hljs-keyword">goto</span> L6<br><span class="hljs-keyword">goto</span> L3<br><span class="hljs-symbol">L6:</span><span class="hljs-keyword">if</span> <span class="hljs-literal">False</span> c < b <span class="hljs-keyword">goto</span> L2<br><span class="hljs-symbol">L15:</span>a = <span class="hljs-number">3</span><br><span class="hljs-keyword">goto</span> L6<br><span class="hljs-symbol">L2:</span><br><br></code></pre></td></tr></tbody></table></figure><h4 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h4><h2 id="附录"><a href="#附录" class="headerlink" title="附录"></a>附录</h2>]]></content>
<categories>
<category>编译原理</category>
</categories>
<tags>
<tag>C++</tag>
<tag>编译原理</tag>
</tags>
</entry>
</search>