深入浅出了解GCN原理(公式+代码)_gcn公式-程序员宅基地

技术标签: GCN  机器学习  神经网络  数据挖掘  

  由于课题研究需要,这星期看了几篇GCN相关的文章和书籍,并对其进行了代码复现,现将最近学习的内容做一个梳理与总结,用于日后复习巩固。由于能力有限,文章中有错误或者不当之处,还望各位读者多多指出。之后对GCN应用方面相关的论文阅读笔记,也会及时文末跟新。(本文作为笔者的学习笔记,如有错误,希望各位读者批评指正)- - 更新时间:2020年11月1日

[学习笔记(1)]深入浅出了解GCN原理(公式+代码)
[学习笔记(2)]深入浅出了解GNN的几种变体

引言

   相信大多数读者在了解GCN(Graph Convolutional Networks)之前,对CNN(Convolutional Neural Network)都是非常熟悉的,我们知道,在连续信号中的卷积是表征函数f与g经过翻转和平移的重叠部分函数值乘积对重叠长度的积分,如下公式(1)。
∫ − ∞ + ∞ f ( τ ) g ( x − τ ) d τ ( 1 ) \quad\qquad\int_{-\infty}^{+\infty} f(\tau)g(x-\tau)d\tau \qquad\qquad\qquad (1) +f(τ)g(xτ)dτ(1)

这是一张动图
对于离散信号而言,离散卷积的本质也是平移叠加之后的加权和,所以在CNN中图像上的卷积,本质上是利用参数共享的过滤器(kernel),通过计算中心像素点以及相邻像素点的加权和来构建成Feature Map,实现空间特征的提取,而加权的系数就是卷积核的权重,这其实在很多传统图像的算法中也有体现,就比如高斯平滑算子,拉普拉斯算子,边缘检测算子(提取边缘特征),包括SIFI(Scale-invariant feature transform)特征点提取,也是一系列的卷积和后处理操作。这些在图像上通过卷积核特征提取的操作,之所以有效很大一部分原因是因为图像本身是结构化数据,它是多个像素点排列整齐的矩阵,而CNN具有平移不变性(即图像如果经过平移,得到的特征图也会相应平移)。然而在真实世界中,还含有很多非欧几里得距离的数据,比如社交网络关系、交通连通图、脑网络连接等等,对于这类具有抽象意义的拓扑图关系的数据,它们是无法保证平移不变性的,这就需要我们有类似cnn的方法,来提取和挖掘有效的空间关系进行建模学习。广义来说,对于任何数据,都可以建立一定的拓扑关联,来进一步挖掘数据内部之间的关联性,所以GCN有很大的应用空间。

GCN发展

  GCN真正开始运用和发展是从这篇于2016年提出,2017年发表的文章开始:SEMI-SUPERVISED CLASSIFICATION WITHGRAPH CONVOLUTIONAL NETWORKS ,详细文章内容,读者可以自行阅读原文。

1、基于损失函数的考虑

  在2016年以前,就有前人尝试利用正则化约束的方法,通过在损失函数中引入图的拉普拉斯正则项,来对节点分类任务进行半监督学习,如下公式(2).
L = L 0 + λ L r e g w i t h L r e g = ∑ i , j A i j ∣ ∣ f ( X i ) − f ( X j ) ∣ ∣ 2 = f ( X ) T △ f ( X ) . ( 2 ) \mathcal{L}=\mathcal{L}_0+\lambda\mathcal{L_{reg}}\qquad with\quad \mathcal{L_{reg}}=\sum_{i,j}A_{ij}||f(X_i)-f(X_j)||^{2}=f(X)^T\vartriangle f(X). \qquad(2) L=L0+λLregwithLreg=i,jAijf(Xi)f(Xj)2=f(X)Tf(X).(2)
其中 L 0 \mathcal{L}_0 L0为监督损失(与label定义的损失), L r e g \mathcal{L}_{reg} Lreg为约束项, f ( . ) f(.) f(.)可以是神经网络可微函数, X X X是节点特征向量 X i X_i Xi的矩阵, △ = D − A \vartriangle=D-A =DA是无向图 G = ( V , E ) G=(V,E) G=(V,E)的非标准化拉普拉斯矩阵。这里说明下,拉普拉斯矩阵是用来研究图结构性质的核心对象,其定义为 L = D − A L=D-A L=DA,其中D是一个对角矩阵, A A A是邻接矩阵, D i i = ∑ j A i j D_{ii}=\sum_jA_{ij} Dii=jAij 表示 i i i节点的度。拉普拉斯矩阵还有一种正则化的形式(symmetric normalized laplacian) L s y m = D − 1 2 L D − 1 2 = I N − D − 1 2 A D − 1 2 ( 3 ) \qquad L_{sym}=D^{-\frac{1}{2}}LD^{-\frac{1}{2}}=I_N-D^{-\frac{1}{2}}AD^{-\frac{1}{2}} \qquad\qquad\qquad(3) Lsym=D21LD21=IND21AD21(3),其元素级别的定义如下:
L s y m [ j , j ] { 1 i f   i = j − 1 d e g ( v i ) d e g ( v j ) i f   e i j ∈ E 0 o t h e r w i s e ( 4 ) \qquad L_{sym}[j,j]\left\{ \begin{aligned} 1\qquad\quad& &{ if\ i=j\quad}\\ \frac{-1}{\sqrt{deg(v_i)deg(v_j)}} & &{if\ e_{ij} \in E} \\ 0 \qquad\quad& & {otherwise } \end{aligned} \right.{\quad \qquad \qquad \qquad (4)} Lsym[j,j]1deg(vi)deg(vj) 10if i=jif eijEotherwise(4)
拉普拉斯矩阵的定义源自于拉普拉斯算子,拉普拉斯算子它是 n n n维欧式空间种的二阶微分算子,在二维空间的图像中表示就是我们熟悉的边缘检测算子(两者之间的关系这里不多展开,感兴趣的读者可以看看这篇文章 拉普拉斯算子与拉普拉斯矩阵的关系
arrat

图1 拉普拉斯矩阵计算(图片来源维基百科)

回到公式(2),从表达式上我们可以清晰的看到,正则部分相当于衡量相邻节点之间的差异性,因为 A i , j A_{i,j} Ai,j是邻接矩阵上的元素,两个节点处于邻接关系时为1,其他为0.这样的正则项,使得拓扑图上相邻节点尽可能相似,物以类聚,这是很自然的想法。从信号的角度上来看,减少该正则项,就是期望经过模型之后的图信号更加平滑。从频域上来看,是对图信号做了低通滤波的处理,但是这样的假设可能会限制模型的表达能力,因为图上边不只包含相似度的信息,还可能包含其他相关信息。

2、从谱图卷积开始

  最初标准的第一代GCN出自Spectral Networks and Locally Connected Networks on Graphs,其对于图上的卷积有如下定义:
g θ ∗ x = U g θ U T x ( 5 ) \qquad \qquad g_{\theta}*x=Ug_{\theta}U^Tx \qquad\qquad\qquad\quad(5) gθx=UgθUTx(5)
这里 U U U是图的标准化拉普拉斯(Laplacian)矩阵 L s y m L_{sym} Lsym(公式3)的特征向量矩阵,因此有 L s y m = U Λ U T L_{sym}=U\Lambda U^T Lsym=UΛUT, x x x是数据中提取出来的特征,也就是输入,定义 g θ = d i a g ( h ^ ( λ l ) ) g_{\theta}=diag(\hat{h}(\lambda_l)) gθ=diag(h^(λl))是特征值的响应函数,进一步为了类比CNN中的共享卷积核的设计,在神经网络中将其设置为可训练参数的卷积核,如 d i a g ( θ l ) diag(\theta_l) diag(θl)。而 U T x U^Tx UTx的本质是将 x x x映射到傅里叶空间, U U U是傅里叶空间的一组正交基。对于如何将傅里叶分解推广到图卷积,可以参考这篇The Emerging Field of Signal Processing on Graphs: Extending High-Dimensional Data Analysis to Networks and Other Irregular Domains ,太过复杂的推导和证明这里不过多解释。
对于第一代版本的GCN主要有以下几点弊端:

  1. 每一层的计算涉及 U d i a g ( h ^ ( θ l ) ) U T Udiag(\hat{h}(\theta_l))U^T Udiag(h^(θl))UT三个矩阵乘法,复杂度为 O ( N 2 ) O(N^2) O(N2),代价较高
  2. 卷积核具有n个参数,n是L的特征值个数。
  3. 计算Hermitian Matrix特征值的代价非常昂贵

过高的计算复杂度和过大的计算参数,在实际运用中都是不允许的,于是基于以上两点的考虑,就产生了第二代版本的GCN,详细内容和公式推导可参考论文:Convolutional Neural Networks on Graphs with Fast Localized Spectral Filtering
,文中作者将卷积核巧妙地设计成了如下公式:
g θ ( Λ ) = ∑ k = 0 K − 1 θ k Λ k = ( ∑ j = 0 k − 1 θ j λ 1 j ⋱ ∑ j = 0 k − 1 θ j λ n j ) ( 6 ) g_{\theta}(\Lambda)=\sum_{k=0}^{K-1}\theta_k\Lambda^k= \begin{pmatrix} \sum_{j=0}^{k-1}\theta_j\lambda_1^j &&\\ &\ddots&\\ &&\sum_{j=0}^{k-1}\theta_j\lambda_n^j \end{pmatrix}\qquad\qquad(6) gθ(Λ)=k=0K1θkΛk=j=0k1θjλ1jj=0k1θjλnj(6)
经过这样的设计,原本需要训练n个参数,缩小到了K个,同时将公式(6)带入公式(5)得到公式(7),从公式(7)中不难发现,此时我们不在需要在求矩阵的特征向量了,可直接使用拉普拉斯矩阵进行运算。第二代版本的GCN解决了第一代的2、3两个问题的弊端,但是矩阵乘法的复杂度依旧是 O ( N 2 ) O(N^2) O(N2)
g θ ∗ x = ∑ j = 0 k − 1 θ j L j x ( 7 ) \qquad\qquad g_{\theta}*x=\sum_{j=0}^{k-1}\theta_jL^jx \qquad \qquad \qquad\quad(7) gθx=j=0k1θjLjx(7)

3、切比雪夫卷积核

  计算多个大型稠密矩阵的乘法复杂度是很高的,这运用中在实际场景是不被允许的,尤其是计算像社交关系网络这种超大拓扑图的数据,显然是无法实现的,为解决这个问题,我们采用切比雪夫多项式(Chebyshev polynomials)来近似 g θ ( Λ ) g_{\theta}(\Lambda) gθ(Λ),具体内容的讨论读者可阅读知乎:chevyshev多项式作为卷积核 或Hammond等人对这一问题深入的讨论Wavelets on graphs via spectral graph theory,我们直接给出近似公式:
g θ ’ ( Λ ) ≈ ∑ k = 0 K θ k ′ T k ( Λ ~ ) ( 8 ) \qquad \qquad g_{\theta^’}(\Lambda)\approx\sum_{k=0}^{K}\theta_k^{'}T_k(\tilde{\Lambda})\qquad\qquad\qquad(8) gθ(Λ)k=0KθkTk(Λ~)(8)
这里 Λ ~ = 2 λ m a x Λ − I N \tilde{\Lambda}=\frac{2}{\lambda_{max}}\Lambda-I_N Λ~=λmax2ΛIN是对 Λ \Lambda Λ的一种缩放, λ m a x \lambda_{max} λmax L L L的最大特征值, θ ′ ∈ R k \theta^{'}\in\mathbb{R}^k θRk是切比雪夫系数向量,切比雪夫的递归式定义为 T k ( x ) = 2 x T k − 1 ( x ) − T k − 2 ( x ) T_k(x)=2xT_{k-1}(x)-T_{k-2}(x) Tk(x)=2xTk1(x)Tk2(x),设 T 0 ( x ) = 1 T_0(x)=1 T0(x)=1, T 1 ( x ) = x T_1(x)=x T1(x)=x,由此就可以的得到一个计算近似特征值的方法,将公式(8)带入之前的卷积定义公式(5),我们有: g θ ∗ x = ∑ k = 0 K θ k ′ T k ( L ~ ) x ( 9 ) \qquad\qquad g_{\theta}*x=\sum_{k=0}^{K}\theta_k^{'}T_k(\tilde{L})x \qquad\qquad\qquad(9) gθx=k=0KθkTk(L~)x(9)这里 L ~ = 2 λ m a x L − I N \tilde{L}=\frac{2}{\lambda_{max}}L-I_N L~=λmax2LIN,这个表达式是局部化的,它是拉普拉斯式的一个K阶多项式,也就是说,它只依赖于距离中心节点(K阶邻域),公式(9)的复杂度是 O ( ∣ ε ∣ ) O(|\varepsilon|) O(ε),即计算复杂度取决于图的边的数量。

4、GCN图卷积网络

  为了简化问题,同时减轻由于局部节点度过大而导致过拟合的问题,我们设置K=1,即只考虑中心节点的一阶邻近,将 T 0 ( x ) = 1 T_0(x)=1 T0(x)=1, T 1 ( L ~ ) = L ~ T_1(\tilde{L})=\tilde{L} T1(L~)=L~带入公式(9),进一步,我们将尺度变换的 λ m a x \lambda_{max} λmax固定为2。于是我们就得到卷积新的近似表达:

g θ ∗ x ≈ θ 0 ′ x + θ 1 ′ ( L − I N ) x = θ ( I N + D − 1 2 A D − 1 2 ) x . ( 10 ) g_{\theta}*x\approx\theta_0^{'}x+\theta_1^{'}(L-I_N)x=\theta(I_N+D^{-\frac{1}{2}}AD^{-\frac{1}{2}})x.\qquad\qquad(10) gθxθ0x+θ1(LIN)x=θ(IN+D21AD21)x.(10)
盖尔圆盘定理我们知道,公式(10)中 I N + D − 1 / 2 A D − 1 / 2 I_N+D^{-1/2}AD^{-1/2} IN+D1/2AD1/2的特征值范围为[0,2],重复的进行卷积操作,可能会导致数值不稳定,在神经网络中多层的图卷积之后,容易导致梯度消失或者梯度爆炸。为了解决这个问题,我们将其进一步标准化:
I N + D − 1 2 A D − 1 2 → D ~ − 1 2 A ~ D ~ − 1 2 ( 11 ) \qquad \qquad \quad I_N+D^{-\frac{1}{2}}AD^{-\frac{1}{2}}\to \tilde{D}^{-\frac{1}{2}}\tilde{A}\tilde{D}^{-\frac{1}{2}}\qquad\qquad\qquad\qquad(11) IN+D21AD21D~21A~D~21(11)
其中 A ~ = A + I N \tilde{A}=A+I_N A~=A+IN, D ~ = ∑ j A ~ i j \tilde{D}=\sum_j\tilde{A}_{ij} D~=jA~ij,此时我们就将其特征值规范到了[0,1]之间。可以看到, A ~ \tilde{A} A~相当于每个节点增加了自连接关系,直观上来看,就是每个节点的跟新与邻近节点的表征以及节点上一层的表征有关。
将公式(11)带入公式(10)我们的图卷积表达式最终可写成如下形式:
Z = D ~ − 1 2 A ~ D ~ − 1 2 X Θ ( 12 ) \qquad\qquad\quad\qquad Z=\tilde{D}^{-\frac{1}{2}}\tilde{A}\tilde{D}^{-\frac{1}{2}}X\Theta \qquad \qquad\qquad\qquad\qquad\quad(12) Z=D~21A~D~21XΘ(12)
这里 X ∈ R N × C X\in\mathbb{R}^{N\times C} XRN×C,N表示节点数,C表示节点的特征向量维度如C-dimensional, Θ ∈ R C × F \Theta\in\mathbb{R}^{C\times F} ΘRC×F表示可训练的参数矩阵,F表示输出维度, Z ∈ R N × F Z\in \mathbb{R}^{N\times F} ZRN×F就是卷积后的信号,这个公式的计算复杂度为 O ( ∣ ε ∣ F C ) O(|\varepsilon|FC) O(εFC),由于 A ~ X \tilde{A}X A~X可以执行稀疏矩阵乘法运算,所以实际计算速度会很快。
在这里插入图片描述

图2 GCN模型对节点分类任务半监督的训练迭代过程,颜色表示类别

代码设计与实现

在GCN中,将公式(12)写成如下多层传播的形式:
H ( l + 1 ) = σ ( D ~ − 1 2 A ~ D ~ − 1 2 H ( l ) W ( l ) ) ( 13 ) \qquad\qquad \qquad H^{(l+1)}=\sigma(\tilde{D}^{-\frac{1}{2}}\tilde{A}\tilde{D}^{-\frac{1}{2}}H^{(l)}W^{(l)})\qquad\qquad\qquad \quad(13) H(l+1)=σ(D~21A~D~21H(l)W(l))(13)
代码实现方面非常简单,主要是前期对邻接矩阵的处理,以及图卷积层的构建,这里是一个pytorch实现的代码:

import torch
import torch.nn as nn
import torch.nn.init as init
import torch.nn.functional as F
# 图卷积层
class GraphConvolution(nn.Module):
    def __init__(self, input_dim, output_dim, use_bias=True):
        """图卷积:L*X*\theta

        Args:
        ----------
            input_dim: int
                节点输入特征的维度
            output_dim: int
                输出特征维度
            use_bias : bool, optional
                是否使用偏置
        """
        super(GraphConvolution, self).__init__()
        self.input_dim = input_dim
        self.output_dim = output_dim
        self.use_bias = use_bias
        self.weight = nn.Parameter(torch.Tensor(input_dim, output_dim))#权重
        if self.use_bias:
            self.bias = nn.Parameter(torch.Tensor(output_dim))
        else:
            self.register_parameter('bias', None)
        self.reset_parameters()

    def reset_parameters(self):
        init.kaiming_uniform_(self.weight)
        if self.use_bias:
            init.zeros_(self.bias)

    def forward(self, adjacency, input_feature):
        """邻接矩阵是稀疏矩阵,因此在计算时使用稀疏矩阵乘法"""
        support = torch.mm(input_feature, self.weight)
        output = torch.sparse.mm(adjacency, support)
        if self.use_bias: 
            output += self.bias
        return output

    def __repr__(self):
        return self.__class__.__name__ + ' (' \
            + str(self.input_dim) + ' -> ' \
            + str(self.output_dim) + ')'
# 将邻接矩阵标准化
def normalization(adjacency):
    """计算 L=D^-0.5 * (A+I) * D^-0.5,

    Args:
        adjacency: sp.csr_matrix.

    Returns:
        归一化后的邻接矩阵,类型为 torch.sparse.FloatTensor
    """
    adjacency += sp.eye(adjacency.shape[0])    # 增加自连接
    degree = np.array(adjacency.sum(1))
    d_hat = sp.diags(np.power(degree, -0.5).flatten())
    L = d_hat.dot(adjacency).dot(d_hat).tocoo()
    # 转换为 torch.sparse.FloatTensor
    indices = torch.from_numpy(np.asarray([L.row, L.col])).long()
    values = torch.from_numpy(L.data.astype(np.float32))
    tensor_adjacency = torch.sparse.FloatTensor(indices, values, L.shape)
    return tensor_adjacency

更多相关代码,可以从GitHub上下载:
《深入浅出图神经网络:GNN原理解析》配套代码

图神经网络相关算法详述及实现

关于GCN的论文集合:GCN相关论文汇总

总结

  从第一个版本到最终图卷积网络,每一次都在前一次的基础上大大减少了计算复杂度,最终才使得GCN的端到端训练成为可能,然而科学是不断进步和发展的,GCN还存在很多问题。

1、谱域卷积

在谱域图卷积中,我们对图的拉普拉斯矩阵进行谱分解,并通过在傅里叶空间特征分解帮助我们理解潜在的子图结构,GCN和ChebNet就是典型的应用。但是对于有向图的邻接矩阵是非对称矩阵,此时不能对拉普拉斯矩阵进行谱分解,需要我们重新定义邻接关系,或者通过其他形式的GCN来挖掘拓扑图上的关系。

2、空域卷积

空域卷积作用在节点的邻域上,我们通过聚合距离中心节点k-hop邻居来得到节点的特征表示。空域卷积相比谱域卷积更加简单和高效,GraphSAGE(Graph SAmple and aggreGatE)和GAT(Graph Attention Network) 是空域卷积的典型代表(GCN变体)。

未来围绕GCN的工作可以从以下几点围绕展开:
1、过度平滑问题
2、下游任务的处理应用
3、可解释性
4、处理有向图
5、inductive任务


参考文献

[1] T. N. Kipf and M. Welling, “Semi-supervised classification with graph convolutional networks,” 5th Int. Conf. Learn. Represent. ICLR 2017 - Conf. Tra

[2] M. Defferrard, X. Bresson, and P. Vandergheynst, “Convolutional neural networks on graphs with fast localized spectral filtering,” Adv. Neural Inf. Process. Syst., no. 59, pp. 3844–3852, 2016.

[3] Y. Jin, N. Duffield, P. Haffner, S. Sen, and Z. L. Zhang, “Learning Convolutional Neural Networks for Graphs Mathias,” 2010 22nd Int. Teletraffic Congr. - Proceedings, ITC 22, vol. 1, 2010.

[4] J. Bruna, W. Zaremba, A. Szlam, and Y. LeCun, “Spectral networks and deep locally connected networks on graphs,” 2nd Int. Conf. Learn. Represent. ICLR 2014 - Conf. Track Proc., pp. 1–14, 2014.

[5] 《深入浅出图神经网络:GNN原理解析》刘忠雨,李彦霖,周洋

[6] 知乎:如何理解 Graph Convolutional Network(GCN)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_26593695/article/details/109393775

智能推荐

使用nginx解决浏览器跨域问题_nginx不停的xhr-程序员宅基地

文章浏览阅读1k次。通过使用ajax方法跨域请求是浏览器所不允许的,浏览器出于安全考虑是禁止的。警告信息如下:不过jQuery对跨域问题也有解决方案,使用jsonp的方式解决,方法如下:$.ajax({ async:false, url: 'http://www.mysite.com/demo.do', // 跨域URL ty..._nginx不停的xhr

在 Oracle 中配置 extproc 以访问 ST_Geometry-程序员宅基地

文章浏览阅读2k次。关于在 Oracle 中配置 extproc 以访问 ST_Geometry,也就是我们所说的 使用空间SQL 的方法,官方文档链接如下。http://desktop.arcgis.com/zh-cn/arcmap/latest/manage-data/gdbs-in-oracle/configure-oracle-extproc.htm其实简单总结一下,主要就分为以下几个步骤。..._extproc

Linux C++ gbk转为utf-8_linux c++ gbk->utf8-程序员宅基地

文章浏览阅读1.5w次。linux下没有上面的两个函数,需要使用函数 mbstowcs和wcstombsmbstowcs将多字节编码转换为宽字节编码wcstombs将宽字节编码转换为多字节编码这两个函数,转换过程中受到系统编码类型的影响,需要通过设置来设定转换前和转换后的编码类型。通过函数setlocale进行系统编码的设置。linux下输入命名locale -a查看系统支持的编码_linux c++ gbk->utf8

IMP-00009: 导出文件异常结束-程序员宅基地

文章浏览阅读750次。今天准备从生产库向测试库进行数据导入,结果在imp导入的时候遇到“ IMP-00009:导出文件异常结束” 错误,google一下,发现可能有如下原因导致imp的数据太大,没有写buffer和commit两个数据库字符集不同从低版本exp的dmp文件,向高版本imp导出的dmp文件出错传输dmp文件时,文件损坏解决办法:imp时指定..._imp-00009导出文件异常结束

python程序员需要深入掌握的技能_Python用数据说明程序员需要掌握的技能-程序员宅基地

文章浏览阅读143次。当下是一个大数据的时代,各个行业都离不开数据的支持。因此,网络爬虫就应运而生。网络爬虫当下最为火热的是Python,Python开发爬虫相对简单,而且功能库相当完善,力压众多开发语言。本次教程我们爬取前程无忧的招聘信息来分析Python程序员需要掌握那些编程技术。首先在谷歌浏览器打开前程无忧的首页,按F12打开浏览器的开发者工具。浏览器开发者工具是用于捕捉网站的请求信息,通过分析请求信息可以了解请..._初级python程序员能力要求

Spring @Service生成bean名称的规则(当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致)_@service beanname-程序员宅基地

文章浏览阅读7.6k次,点赞2次,收藏6次。@Service标注的bean,类名:ABDemoService查看源码后发现,原来是经过一个特殊处理:当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致public class AnnotationBeanNameGenerator implements BeanNameGenerator { private static final String C..._@service beanname

随便推点

二叉树的各种创建方法_二叉树的建立-程序员宅基地

文章浏览阅读6.9w次,点赞73次,收藏463次。1.前序创建#include<stdio.h>#include<string.h>#include<stdlib.h>#include<malloc.h>#include<iostream>#include<stack>#include<queue>using namespace std;typed_二叉树的建立

解决asp.net导出excel时中文文件名乱码_asp.net utf8 导出中文字符乱码-程序员宅基地

文章浏览阅读7.1k次。在Asp.net上使用Excel导出功能,如果文件名出现中文,便会以乱码视之。 解决方法: fileName = HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8);_asp.net utf8 导出中文字符乱码

笔记-编译原理-实验一-词法分析器设计_对pl/0作以下修改扩充。增加单词-程序员宅基地

文章浏览阅读2.1k次,点赞4次,收藏23次。第一次实验 词法分析实验报告设计思想词法分析的主要任务是根据文法的词汇表以及对应约定的编码进行一定的识别,找出文件中所有的合法的单词,并给出一定的信息作为最后的结果,用于后续语法分析程序的使用;本实验针对 PL/0 语言 的文法、词汇表编写一个词法分析程序,对于每个单词根据词汇表输出: (单词种类, 单词的值) 二元对。词汇表:种别编码单词符号助记符0beginb..._对pl/0作以下修改扩充。增加单词

android adb shell 权限,android adb shell权限被拒绝-程序员宅基地

文章浏览阅读773次。我在使用adb.exe时遇到了麻烦.我想使用与bash相同的adb.exe shell提示符,所以我决定更改默认的bash二进制文件(当然二进制文件是交叉编译的,一切都很完美)更改bash二进制文件遵循以下顺序> adb remount> adb push bash / system / bin /> adb shell> cd / system / bin> chm..._adb shell mv 权限

投影仪-相机标定_相机-投影仪标定-程序员宅基地

文章浏览阅读6.8k次,点赞12次,收藏125次。1. 单目相机标定引言相机标定已经研究多年,标定的算法可以分为基于摄影测量的标定和自标定。其中,应用最为广泛的还是张正友标定法。这是一种简单灵活、高鲁棒性、低成本的相机标定算法。仅需要一台相机和一块平面标定板构建相机标定系统,在标定过程中,相机拍摄多个角度下(至少两个角度,推荐10~20个角度)的标定板图像(相机和标定板都可以移动),即可对相机的内外参数进行标定。下面介绍张氏标定法(以下也这么称呼)的原理。原理相机模型和单应矩阵相机标定,就是对相机的内外参数进行计算的过程,从而得到物体到图像的投影_相机-投影仪标定

Wayland架构、渲染、硬件支持-程序员宅基地

文章浏览阅读2.2k次。文章目录Wayland 架构Wayland 渲染Wayland的 硬件支持简 述: 翻译一篇关于和 wayland 有关的技术文章, 其英文标题为Wayland Architecture .Wayland 架构若是想要更好的理解 Wayland 架构及其与 X (X11 or X Window System) 结构;一种很好的方法是将事件从输入设备就开始跟踪, 查看期间所有的屏幕上出现的变化。这就是我们现在对 X 的理解。 内核是从一个输入设备中获取一个事件,并通过 evdev 输入_wayland

推荐文章

热门文章

相关标签