图卷积网络Graph Convolutional Nueral Network,简称GCN,最近两年大热,取得不少进展。不得不专门为GCN开一个新篇章,表示其重要程度。本文结合大量参考文献,从理论到实践,从由来到数学推导,讲述GCN的发展和应用。

综述

在扎进GCN的汪洋大海前,我们先搞清楚GCN是做什么的,有什么用。深度学习一直都是被几大经典模型给统治着,如CNN、RNN等等,它们无论再CV还是NLP领域都取得了优异的效果,而GCN主要是针对图结构的。社交网络、信息网络中有很多类似的结构。实际上,这样的网络结构(Non Euclidean Structure)就是图论中抽象意义上的拓扑图。
图的结构一般来说是十分不规则的,可以认为是无限维的一种数据,所以它没有平移不变性。每一个节点的周围结构可能都是独一无二的,这种结构的数据,就让传统的CNN、RNN瞬间失效。所以很多学者从上个世纪就开始研究怎么处理这类数据了。这里涌现出了很多方法,例如GNN、DeepWalk、node2vec等等,GCN只是其中一种。
图卷积神经网络,实际上跟CNN的作用一样,就是一个特征提取器,只不过它的对象是图数据。GCN精妙地设计了一种从图数据中提取特征的方法,从而让我们可以使用这些特征去对图数据进行节点分类(node classification)、图分类(graph classification)、边预测(link prediction) ,还可以顺便得到 图的嵌入表示(graph embedding),可见用途广泛。因此现在人们脑洞大开,让GCN到各个领域中发光发热。
我们直接看看GCN的核心部分:
假设我们手头有一批图数据,其中有\(N\)个节点(node),每个节点都有自己的特征,我们设这些节点的特征组成一个\(N×D\)维的矩阵\(X\),然后各个节点之间的关系也会形成一个\(N×N\)维的矩阵\(A\),也称为 邻接矩阵(adjacency matrix)。\(X\)和\(A\)便是我们模型的输入。

GCN也是一个神经网络层,它的层与层之间的传播方式是:
\[
H^{(l+1)} = \sigma(\check D^{-\frac{1}{2}} \check A \check D^{-\frac{1}{2}} H^{(l)} W^{(l)} )
\]
其中:

  • \(\check A=A+I\),\(I\)是单位矩阵
  • \(\check D\)是\(\check A\)的度矩阵(degree matrix),公式为 \(\check D_{ii}=\sum_j \check A_{ii}\)
  • \(H\)是每一层的特征,对于输入层的话,\(H\)就是\(X\)
  • \(σ\)是非线性激活函数

解释

实际上,图卷积是利用其他结点的信息来推导该结点的信息。在半监督学习中,图卷积本质不是传播标签,而是在传播特征,图卷积将不知道标签的特征,传染到已知标签的特征节点上,利用已知标签节点的分类器推测其属性。
另外,图中的每个结点无时无刻不因为邻居和更远的点的影响而在改变着自己的状态直到最终的平衡,关系越亲近的邻居影响越大。
图数据中的空间特征具有以下特点:
1) 节点特征:每个节点有自己的特征;(体现在点上)
2) 结构特征:图数据中的每个节点具有结构特征,即节点与节点存在一定的联系。(体现在边上)
总地来说,图数据既要考虑节点信息,也要考虑结构信息,图卷积神经网络就可以自动化地既学习节点特征,又能学习节点与节点之间的关联信息。

图卷积的核心思想是利用边的信息节点信息进行聚合从而生成新的节点表示。GCN的本质目的就是用来提取拓扑图的空间特征。

理解

图卷积神经网络主要有两类,一类是基于空间域或顶点域vertex domain(spatial domain)的,另一类则是基于频域或谱域spectral domain的。通俗点解释,空域可以类比到直接在图片的像素点上进行卷积,而频域可以类比到对图片进行傅里叶变换后,再进行卷积。

vertex domain(spatial domain):顶点域(空间域)

基于空域卷积的方法直接将卷积操作定义在每个结点的连接关系上,它跟传统的卷积神经网络中的卷积更相似一些。在这个类别中比较有代表性的方法有 Message Passing Neural Networks(MPNN)[1], GraphSage[2], Diffusion Convolution Neural Networks(DCNN)[3], PATCHY-SAN[4]等。

spectral domain:频域方法(谱方法)

这就是谱域图卷积网络的理论基础了。这种思路就是希望借助图谱的理论来实现拓扑图上的卷积操作。GCN主要是借助于图的拉普拉斯矩阵的特征值和特征向量来研究图的性质,这与谱聚类的思想不谋而合,不妨先了解下谱聚类。从整个研究的时间进程来看:首先研究GSP(graph signal processing)的学者定义了graph上的Fourier Transformation,进而定义了graph上的convolution,最后与深度学习结合提出了Graph Convolutional Network。
基于频域卷积的方法则从图信号处理起家,包括 Spectral CNN[5], Cheybyshev Spectral CNN(ChebNet)[6], 和 First order of ChebNet(1stChebNet)[7] 等

拉普拉斯矩阵

Graph Fourier Transformation及Graph Convolution的定义都用到图的拉普拉斯矩阵,那么首先来介绍一下拉普拉斯矩阵。拉普拉斯矩阵(Laplacian matrix) 也叫做导纳矩阵、基尔霍夫矩阵或离散拉普拉斯算子,主要应用在图论中,作为一个图的矩阵表示。
对于图 \(G=(V,E)\),其Laplacian 矩阵的定义为 \(L=D-A\),其中 \(L\) 是Laplacian 矩阵, \(D\) 是顶点的度矩阵(对角矩阵),对角线上元素依次为各个顶点的度,\(A\) 是图的邻接矩阵。
这里要说明的是:常用的拉普拉斯矩阵实际有三种:

  • \(L=D-A\)定义的Laplacian 矩阵更专业的名称叫Combinatorial Laplacian
  • \(L^{sys}=D^{-1/2}LD^{-1/2}\)定义的叫Symmetric normalized Laplacian,很多GCN的论文中应用的是这种拉普拉斯矩阵
  • \(L^{rw}=D^{-1}L\)定义的叫Random walk normalized Laplacian

为什么GCN要用拉普拉斯矩阵?
拉普拉斯矩阵矩阵有很多良好的性质,主要有三点:
(1)拉普拉斯矩阵是对称矩阵,可以进行特征分解(谱分解),这就和GCN的spectral domain对应
(2)拉普拉斯矩阵只在中心顶点和一阶相连的顶点上(1-hop neighbor)有非0元素,其余之处均为0
(3)通过拉普拉斯算子与拉普拉斯矩阵进行类比

拉普拉斯矩阵的谱分解(特征分解)

GCN的核心基于拉普拉斯矩阵的谱分解。
矩阵的谱分解,特征分解,对角化都是同一个概念。不是所有的矩阵都可以特征分解,其 充要条件为n阶方阵存在n个线性无关的特征向量。
但是拉普拉斯矩阵是半正定对称矩阵,有如下性质:

  • 对称矩阵一定n个线性无关的特征向量
  • 半正定矩阵的特征值一定非负
  • 对阵矩阵的特征向量相互正交,即所有特征向量构成的矩阵为正交矩阵。
  • 特征值中0出现的次数就是图连通区域的个数。
  • 最小特征值是0,因为拉普拉斯矩阵(普通形式:\(L=D−A\))每一行的和均为0,并且最小特征值对应的特征向量是每个值全为1的向量;
  • 最小非零特征值是图的代数连通度。

由上可以知道拉普拉斯矩阵一定可以谱分解,且分解后有特殊的形式。

对于拉普拉斯矩阵其谱分解为:
\[
\mathbf{L} = \mathbf{U} \left( \begin{array}{ccc} \lambda_1 & \ldots & \ldots \\ \ldots & \lambda_2 & \ldots \\ \vdots & \vdots & \ddots \\ \ldots & \ldots & \lambda_n \end{array} \right) \mathbf{U^{T}}
\]

拉普拉斯算子

定义:拉普拉斯算子是\(n\)维欧几里德空间中的一个二阶微分算子,定义为梯度(\(∇f\))的散度(\(∇⋅f\),即\(∇f⋅f\))。因此如果\(f\)是二阶可微的实函数,则\(f\)的拉普拉斯算子\(∆\)定义为:
\[
∆f = ∇^2 f = ∇ \cdot ∇f
\]
\(f\)的拉普拉斯算子也是笛卡尔坐标系\(x_i\)中的所有非混合二阶偏导数:
\[
∆f = \sum_{i=1}^n \frac{\partial^2 f}{\partial x_i^2}
\]
函数\(f\)的拉普拉斯算子也是该函数的海塞矩阵(是一个多元函数的二阶偏导数构成的方阵)的迹:
\[
Δf=tr(H(f))
\]

拉普拉斯算子(Laplacian operator) 的物理意义是空间二阶导,准确定义是:标量梯度场中的散度,一般可用于描述物理量的流入流出。比如说在二维空间中的温度传播规律,一般可以用拉普拉斯算子来描述。

拉普拉斯矩阵也叫做离散的拉普拉斯算子。

卷积

在泛函分析中,卷积是通过两个函数\(f(x)\)和\(g(x)\)生成第三个函数的一种算子,它代表的意义是:两个函数中的一个(取\(g(x)\))函数,把\(g(x)\)经过翻转平移,然后与\(f(x)\)相乘,得到的一个新的函数,对这个函数积分,也就是对这个新的函数求它所围成的曲边梯形的面积。

设\(f(t)\),\(g(t)\)是两个可积函数,\(f(t)\)与\(g(t)\)的卷积记为\(f(t)∗g(t)\),它是其中一个函数翻转并平移后与另一个函数乘积的积分,是一个自变量是平移量的函数。也就是:
\[
f(t)∗g(t) = \int_{-\infty}^{\infty} f(\tau)g(t-\tau) d\tau
\]

傅里叶变换

傅里叶级数其实就是用一组sin,cos的函数来逼近一个周期函数,那么每个sin,cos函数就是一组基,这组基上的系数就是频域,会发现随着频域越来越多(基越来越多),函数的拟合就越准确。
假设f(x)周期为T,其傅里叶级数可以写作:
\[
f(x) = \frac{a_0}{2}+\sum_{n=1}^{\infty}(a_n cos(\frac{2 \pi nx}{T})+b_nsin(\frac{2 \pi nx}{T}))
\]
其中:
\[
a_n = \frac{2}{T} \int_{x_0}^{x_0+T} f(x) \cdot cos(\frac{2 \pi nx}{T})dx \\
b_n = \frac{2}{T} \int_{x_0}^{x_0+T} f(x) \cdot sin(\frac{2 \pi nx}{T})dx
\]
利用欧拉公式\(e^{ix}=cos x+i sinx\),我们发现\(cos x\),\(sin x\)可表示成
\[
cos x = \frac{e^{ix}+e^{-ix}}{2} \\
sin x = \frac{e^{ix}-e^{-ix}}{2i}
\]
得到了傅里叶变换就是
\[
F(w) = \int_{-\infty}^{\infty}f(x)e^{-iwx}dx
\]

傅立叶级数是基于周期函数的,如果我们把周期推广到\(T=\infty\),那么也就变为了非周期函数,这就是傅立叶变换。其实可以发现这个对信号\(f(x)\)的傅立叶变换\(F(ω)\)形式上是\(f(x)\) 与基函数\(e^{−iωx}\)的积分,本质上将函数\(f(x)\)映射到了以\(e^{−iωx}\)为基向量的空间中。

从传统的傅里叶变换、卷积到Graph上的傅里叶变换及卷积

把传统的傅里叶变换以及卷积迁移到Graph上来,核心工作其实就是把拉普拉斯算子的特征函数 \(e^{-i\omega t}\)变为Graph对应的拉普拉斯矩阵的特征向量。

(1)推广傅里叶变换

(a)Graph上的傅里叶变换
传统的傅里叶变换定义为:
\[
F(\omega) = {\bf F}|f(t)| = \int f(t)e^{-i\omega t}dt
\]
信号 f(t) 与基函数 \(e^{-i\omega t}\) 的积分,那么为什么要找\(e^{-i\omega t}\) 作为基函数呢?从数学上看,\(e^{-i\omega t}\)是拉普拉斯算子的特征函数(满足特征方程),\(\omega\)就和特征值有关。
广义的特征方程定义为:
\[
AV = \lambda V
\]
其中\(A\)是一种变换,\(V\)是特征向量或者特征函数(无穷维的向量), \(\lambda\)是特征值。
\(e^{-i\omega t}\)满足:
\[
e^{-i\omega t} = \frac{\partial ^2}{\partial t ^2} e^{-i\omega t} = -\omega^2 e^{-i \omega t}
\]
当然\(e^{-i \omega t}\)就是变换\(\Delta\)的特征函数,\(\omega\)和特征值密切相关。

处理Graph问题的时候,用到拉普拉斯矩阵,自然就去找拉普拉斯矩阵的特征向量了。

\(L\)是拉普拉斯矩阵,\(V\)是其特征向量,自然满足下式
\[
LV=\lambda V
\]
离散积分就是一种内积形式,仿上定义Graph上的傅里叶变换:
\[
F(\lambda_l)=\hat f(\lambda_l) = \sum_{i=1}^N f(i)u_i^*(i)
\]
\(f\) 是Graph上的 \(N\) 维向量, \(f(i)\) 与Graph的顶点一一对应, \(u_l(i)\)表示第 \(l\) 个特征向量的第 \(i\) 个分量。那么特征值(频率)\(\lambda_l\)下的, \(f\) 的Graph 傅里叶变换就是与 \(\lambda_l\) 对应的特征向量 \(u_l\) 进行内积运算。

利用矩阵乘法将Graph上的傅里叶变换推广到矩阵形式:

\[
\left( \begin{array}{ccc} \hat f(\lambda_1) \\ \hat f(\lambda_2) \\ \vdots & \\ \hat f(\lambda_N) \\ \end{array} \right) =
\left( \begin{array}{ccc}
u_1(1) & u_1(2) & \ldots & u_1(N) \\
u_2(1) & u_2(2) & \ldots & u_2(N) \\
\vdots & \vdots & \ddots & \vdots & \\
u_N(1) & u_N(2) & \ldots & u_N(N) \\ \end{array}
\right)
\left( \begin{array}{ccc} f(1) \\ f(2) \\ \vdots & \\ f(N) \\ \end{array} \right)
\]

即\(f\)在Graph上傅里叶变换的矩阵形式为:
\[
\hat f=U^Tf
\]

(b)Graph上的傅里叶逆变换
类似地,传统的傅里叶逆变换是对频率\(\omega\)求积分:
\[
{\bf F}^{-1} |F(\omega)| = \frac{1}{2 \pi} \int F(\omega) e^{i \omega t} d \omega
\]
迁移到Graph上变为对特征值\(\lambda_l\)求和:
\[
f(i) = \sum_{l=1}^N \hat f (\lambda_l) u_l(i)
\]
利用矩阵乘法将Graph上的傅里叶逆变换推广到矩阵形式:
\[
\left( \begin{array}{ccc} f(1) \\ f(2) \\ \vdots & \\ f(N) \\ \end{array} \right) =
\left( \begin{array}{ccc}
u_1(1) & u_2(1) & \ldots & u_N(1) \\
u_1(2) & u_2(2) & \ldots & u_N(2) \\
\vdots & \vdots & \ddots & \vdots & \\
u_1(N) & u_2(N) & \ldots & u_N(N) \\ \end{array}
\right)
\left( \begin{array}{ccc} \hat f(\lambda_1) \\ \hat f(\lambda_2) \\ \vdots & \\ \hat f(\lambda_N) \\ \end{array} \right)
\]
即\(f\)在Graph上傅里叶逆变换的矩阵形式为: \(f=U \hat f\)

(2)推广卷积

在上面的基础上,利用卷积定理类比来将卷积运算,推广到Graph上。
卷积定理:函数卷积的傅里叶变换是函数傅立叶变换的乘积,即对于函数\(f(t)\)与\(h(t)\)两者的卷积是其函数傅立叶变换乘积的逆变换:
\[
f * h = {\bf F}^{-1} [\hat f(\omega) \hat h(\omega)] = \frac{1}{2 \pi}\int \hat f(\omega) \hat h(\omega) e^{i \omega t} d \omega
\]
类比到Graph上并把傅里叶变换的定义带入, \(f\) 与卷积核 \(h\) 在Graph上的卷积可按下列步骤求出
\(f\)的傅里叶变换为\(\hat f = U^T f\)
卷积核\(h\)的傅里叶变换写成对角矩阵的形式即为:
\[
\left( \begin{array}{ccc} \hat h(\lambda_1) & \\ & \ddots & & \\ & & & \hat h(\lambda_N) \\ \end{array} \right)
\]

\(\hat h(\lambda_l)=\sum_{i=1}^N h(i)u_l^*(i)\) 是根据需要设计的卷积核\(h\) 在Graph上的傅里叶变换。

在处理Graph时,用到的是傅里叶变换的离散形式。由于拉普拉斯矩阵进行谱分解以后,可以得到n个线性无关的特征向量,构成空间中的一组正交基,因此归一化拉普拉斯矩阵算子的特征向量构成了图傅里叶变换的基。图傅里叶变换将输入图的信号投影到了正交空间,相当于把图上定义的任意向量,表示成了拉普拉斯矩阵特征向量的线性组合。

两者的傅立叶变换乘积即为:
\[
\left( \begin{array}{ccc} \hat h(\lambda_1) & \\ & \ddots & & \\ & & & \hat h(\lambda_N) \\ \end{array} \right) U^T f
\]
再乘以\(U\)求两者傅立叶变换乘积的逆变换,则求出卷积:
\[
(f*h)_G = U \left( \begin{array}{ccc} \hat h(\lambda_1) & \\ & \ddots & & \\ & & & \hat h(\lambda_N) \\ \end{array} \right) U^T f
\]

注:很多论文中的Graph卷积公式为:\((f*h)_G=U((U^Th) \bigodot (U^T f))\)。其中$ \bigodot$表示Hadamard product(哈达马积),对于两个维度相同的向量、矩阵、张量进行对应位置的逐元素乘积运算。其实与上式是完全相同的。

Deep Learning中的Graph Convolution

Deep learning 中的Graph Convolution直接看上去会和之前推导出的图卷积公式有很大的不同,但是万变不离其宗,都是根据下式来推导的:
\[
(f*h)_G = U \left( \begin{array}{ccc} \hat h(\lambda_1) & \\ & \ddots & & \\ & & & \hat h(\lambda_N) \\ \end{array} \right) U^T f
\]
Deep learning 中的Convolution就是要设计含有trainable共享参数的kernel。
上式计算量很大,因为特征向量矩阵U 的复杂度是\(O(N^2)\)。此外,对于大型图来说,\(L\)特征值分解的计算量也很大。
基于上面最原始的卷积公式,深度学习中的GCN主要是从下面几篇文章演变而来的(引用次数都很高),值得深入探讨:

  • 【1】Bruna, Joan, et al. “Spectral networks and locally connected networks on graphs.” 源于ICLR 2014(被引740次)
  • 【2】Defferrard, Michaël, Xavier Bresson, and Pierre Vandergheynst. “Convolutional neural networks on graphs with fast localized spectral filtering.” 源于NIPS 2016(被引997次)
  • 【3】Hammond, David K., Pierre Vandergheynst, and Rémi Gribonval. “Wavelets on graphs via spectral graph theory.” Applied and Computational Harmonic Analysis 30.2 (2011)(被引874次)
  • 【4】Kipf, Thomas N., and Max Welling. “Semi-supervised classification with graph convolutional networks.” 源于ICML 2017 (被引1537次)

应用

GCN特别之处在于,即使不训练,完全使用随机初始化的参数W,GCN提取出来的特征就以及十分优秀了!这跟CNN不训练是完全不一样的,后者不训练是根本得不到什么有效特征的。

为什么GCN要用拉普拉斯矩阵?

拉普拉斯矩阵是对称矩阵,可以进行特征分解(谱分解)
由于卷积在傅里叶域的计算相对简单,为了在graph上做傅里叶变换,需要找到graph的连续的正交基对应于傅里叶变换的基,因此要使用拉普拉斯矩阵的特征向量。

为什么拉普拉斯矩阵的特征向量可以作为傅里叶变换的基?

傅里叶变换一个本质理解就是:把任意一个函数表示成了若干个正交函数(由sin,cos 构成)的线性组合。 graph傅里叶变换也把graph上定义的任意向量\(f\),表示成了拉普拉斯矩阵特征向量的线性组合,即:$f = \hat f(\lambda_1)u_1 + \hat f(\lambda_2)u_2 + ... + \hat f(\lambda_n)u_n $

怎么理解拉普拉斯矩阵的特征值表示频率?

在graph空间上无法可视化展示“频率”这个概念,那么从特征方程上来抽象理解。
在由Graph确定的\(n\)维空间中,越小的特征值\(\lambda_l\)表明:拉普拉斯矩阵\(L\)其所对应的基\(u_l\) 上的分量、“信息”越少,那么当然就是可以忽略的低频部分了。
其实图像压缩就是这个原理,把像素矩阵特征分解后,把小的特征值(低频部分)全部变成0,PCA降维也是同样的,把协方差矩阵特征分解后,按从大到小取出前K个特征值对应的特征向量作为新的“坐标轴”。

参考:
图卷积网络 GCN Graph Convolutional Network(谱域GCN)的理解和详细推导
图卷积神经网络(Graph Convolutional Network, GCN)
何时能懂你的心——图卷积神经网络(GCN)
如何理解 Graph Convolutional Network(GCN)?

图卷积神经网络(GCN)入门的更多相关文章

  1. 最全面的图卷积网络GCN的理解和详细推导,都在这里了!

    目录 目录 1. 为什么会出现图卷积神经网络? 2. 图卷积网络的两种理解方式 2.1 vertex domain(spatial domain):顶点域(空间域) 2.2 spectral doma ...

  2. 论文解读丨基于局部特征保留的图卷积神经网络架构(LPD-GCN)

    摘要:本文提出一种基于局部特征保留的图卷积网络架构,与最新的对比算法相比,该方法在多个数据集上的图分类性能得到大幅度提升,泛化性能也得到了改善. 本文分享自华为云社区<论文解读:基于局部特征保留 ...

  3. 卷积神经网络快速入门【基于TensorFlow】

    一.概述 卷积神经网络[Convolutional neural networks]里面最重要的构建单元是卷积层.神经元在第一个卷积层不是连接输入图片的每一个像素,只是连接它们感受野1的像素,以此类推 ...

  4. CNN(卷积神经网络)入门

    参考博文: 深度学习基础--卷积--1*1的卷积核与全连接的区别:https://blog.csdn.net/wydbyxr/article/details/84061410 如何理解卷积神经网络中的 ...

  5. Graph 卷积神经网络:概述、样例及最新进展

    http://www.52ml.net/20031.html [新智元导读]Graph Convolutional Network(GCN)是直接作用于图的卷积神经网络,GCN 允许对结构化数据进行端 ...

  6. GCN入门理解

    图是信息的最佳表示方式.在一个图中,有通过边(谓之“关系”)连接起来的节点(谓之“实体”).想一想,你的Facebook社交网络是个什么样子的:以你为中心连接上你的朋友们,他们又以不同的方式相互联系. ...

  7. Graph Neural Networks:谱域图卷积

    以下学习内容参考了:1,2, 0.首先回忆CNN,卷积神经网络的结构和特点 处理的数据特征:具有规则的空间结构(Euclidean domains),都可以采用一维或者二维的矩阵描述.(Convolu ...

  8. 【GCN】图卷积网络初探——基于图(Graph)的傅里叶变换和卷积

    [GCN]图卷积网络初探——基于图(Graph)的傅里叶变换和卷积 2018年11月29日 11:50:38 夏至夏至520 阅读数 5980更多 分类专栏: # MachineLearning   ...

  9. 卷积神经网络(CNN)学习笔记1:基础入门

    卷积神经网络(CNN)学习笔记1:基础入门 Posted on 2016-03-01   |   In Machine Learning  |   9 Comments  |   14935  Vie ...

随机推荐

  1. Flutter 踩坑之build函数返回了null

    今天遇到一个bug,内容都正常显示没问题,但是控制台里报错,如图: 翻译了下,说是函数不能返回空值,搜索了下,网上相同问题的是少写了个return,我检查了下也没发现少return的,后来突然发现if ...

  2. vue中使用axios下载文件,兼容IE11

    一.设置axios返回值为blob 二.使用a标签的down属性下载,如果是IE浏览器,可以使用navigator.msSaveBlob进行下载 // data的数据类型是blob downloadF ...

  3. jqGrid取消所有选中

    // 获取所有选中行id var jqGridRowid=$("#jqGrid").jqGrid("getGridParam","selarrrow& ...

  4. Nginx 413 Request Entity Too Large

    用户上传图片的时候,报错. 发现,原来是图片太大导致. 咦?后台配置图片支持5M啊? 哦!原来是Nginx配置问题. Nginx默认支持1M的POST数据! 修改Nginx配置! 修改nginx.co ...

  5. Centos7安装vim8.0 + YouCompleteMe

    更新yum sudo yum upgrade sduo yum update 下载git sudo yum install git 升级vim以及gcc 升级gcc sudo yum install ...

  6. @ConfigurationProperties和@Value的区别

    @ConfigurationProperties @Value 功能: 批量注入配置文件中的属性 一个个指定,多个属性多个@Value 松散绑定: 支持 不支持 SpEL: 不支持    支持 JSR ...

  7. [DEBUG] Springboot打包jar/war后访问包外的路径

    ======================================================== 我是Ruriko,我爱这个世界:)

  8. Linux_Ubantu下编译c++文件

    1. 编译单个文件 利用cmake进行编译 首先在项目文件夹中创建.cpp文件  利用最简单的 hello world #include<iostream> using namespace ...

  9. 2020企业python真面试题持续更新中

    目录 1.软件的生命周期 2.如何知道一个python对象的类型 3.简述Django的设计模式MVC,以及你对各层的理解和用途 4.什么是lambda函数,说明其使用场景 5.python是否支持函 ...

  10. python学习-46 时间模块

    时间模块 ····时间戳 print(time.time()) 运行结果: 1564294158.0389376 Process finished with exit code 0 ·····结构化时 ...