Gorio Tech Blog search

Graph Isomorphism Network (GIN) 설명

|

본 글에서는 2018년에 발표된 How Powerful are Graph Neural Networks라는 논문에 대한 Review를 진행할 것이다. 본 논문에서는 Graph Neural Network에서 거의 필수적으로 진행되는 Aggregation 과정에서의 문제점을 지적하고 이를 해결할 방법으로 Graph Isomorphism Network를 제시하고 있다.

앞으로 상세히 설명할 Aggregation 과정에서의 문제는 필자 역시 GraphSAGE 기반 알고리즘을 사용하면서 경험적으로 느껴왔던 부분이기에 본 논문에서 제시하고 있는 방법론과 연구 결과는 더욱 의미있게 다가왔다. 논의를 전개하면서 근간으로 사용되는 수학적 이론들은 깊이를 요구하지만, 전반적으로 이야기를 이끌어가는 핵심 아이디어는 굉장히 간단하다. 결국 Graph의 구조적인 특성을 GNN이 더 잘 표현하도록 만들고 싶다는 이야기인 것이다.

참고로 논문에서 직접적으로 언급하고 있는 GraphSAGE에 대해 궁금하다면, 이 글을 참조하면 좋을 것이다.

torch_geomectric을 이용하여 GIN를 사용하는 방법에 대해서 간단하게 Github에 올려두었으니 참고해도 좋을 것이다.

또한 Reddit Data를 이용하여 GIN을 적용하는 예시는 이 글을 참조하면 좋고, Pyspark와 Tensorflow를 이용하여 Bipartite Graph 구조에서 GIN을 적용하는 사례에 대해 참고하고 싶다면 이 곳을 확인해 보아도 좋다.


Graph Isomorphism Networks 리뷰

1. Introduction

GNN은 Neighborhood Aggregation 혹은 Message Passing이라는 반복적인 과정을 수행하여 각 Node의 새로운 Feature 벡터를 형성하기 위해 이웃 Node의 이웃을 통합하게 된다. 이러한 통합이 과정이 k번 수행되고 나면, 그 Node는 변형된 Feature 벡터로 표현될 것이고, 이는 그 Node의 k-hop Neighborhood 안에 존재하는 구조적인 정보를 포착하게 된다.

GNN이 괄목할 만한 성과를 거두어왔던 것은 사실이지만, GNN의 새로운 디자인은 대부분 경험적 직관, 휴리스틱, 혹은 실험에 기반한 것으로 GNN의 특징과 한계점에 대한 이론적인 연구가 부족했던 것을 사실이다. 또한 GNN의 Representional Capacity에 대한 이전의 연구도 제한적이었다.

따라서 본 논문에서는 GNN의 Representational Power에 대해 분석하는 이론적인 프레임워크를 제시할 것이다. 이 프레임워크는 GNN과 Weisfeiler-Lehman Graph Isomorphism Test 사이의 유사성에 기반하여 만들어졌는데, 이 WL Test는 여러 종류의 Graph를 구별할 수 있는 효과적인 테스트 방법으로 알려져 있다.

GNN과 비슷하게, WL Test도 Node가 주어졌을 때, 이 Node의 이웃들의 Feature 벡터를 통합하여 Node Feature 벡터를 반복적으로 업데이트한다. WL Test의 경우 다른 Node 이웃들을 다른 Feature 벡터로 매핑하는 Injective Aggregation Update의 존재로 인해 강력한 효과를 지니게 된다.

이 말이 무엇을 의미하는지 잘 생각해 보아야 하므로 예시를 들어 보겠다. (물론 논문 본문에서 잘 설명하고 있다.)

아래와 같은 Node와 Feature 벡터가 주어졌다고 해보자.

Node Feature Vector
알라딘 서점 [1, 0, 0.5]
스타벅스 커피 [0, 1, 0.5]
이디야 커피 [0.5, 0.5, 0.5]

이 때 알라딘 서점과 스타벅스 커피는 사람 A의 이웃 Node이고, 이디야 커피는 사람 B의 이웃 Node라고 생각해보자. 그리고 사람 A와 사람 B의 Feature 벡터를 얻기 위해 GraphSAGE에서 제시한 Mean Aggregation 과정을 진행한다고 해보자.

그렇다면 사람 A의 이웃 Node의 Feature 벡터는 [0.5, 0.5, 0.5]가 될 것이고, 사람 B의 이웃 Node의 Feature 벡터 또한 [0.5, 0.5, 0.5]가 될 것이다. 물론 위 예시에 대해 애초에 Feature 값이 이들을 표현하기에 불충분하다고 생각할 수도 있지만, 충분히 실제 데이터 상에서 나타날 수 있는 예시일 것이다. 어쨌든 위 결과에 따르면 사람 A와 사람 B의 이웃 Feature 벡터는 똑같은 형상을 하게 될 것이고, 이는 학습에 있어서 데이터 소실 혹은 왜곡이라는 문제로 귀결되게 될 것이다.

본 논문에서는 GNN의 Aggregation Scheme이 굉장히 Expressive하고 Injective Function을 모델링할 수 있다면 GNN 또한 WL Test 처럼 굉장히 강력한 Disciminative Power를 지니게 될 것이라고 이야기 하고 있다.

수학적으로 위 인사이트를 공식화하기 위해, 본 논문의 Framework는 먼저 Node가 주어졌을 때, 이 Node의 이웃들의 Feature 벡터를 Multiset, 즉 repeating elements가 존재할 수 있는 집합으로 표현할 것이다. 그러면 GNN에 있는 Neighbor Aggregation이 Aggregation Function over the multiset으로 표현될 수 있을 것이다. 따라서 강력한 Representational Power를 가지기 위해서는 GNN은 다른 multiset을 다른 representation으로 표현할 수 있어야 한다.

본 논문에서는 몇몇 multiset function의 변형 버전과 그들의 discriminative power, 즉 aggregation function이 다른 multiset들을 얼마나 잘 구별할 수 있는지를 이론적으로 분석하였다. 더 disciminative 할 수록, GNN 속에 내재되어 있는 Representational Power는 더 강력할 것이다.

본 논문의 핵심적인 결과는 아래와 같다.

1) GNN은 Graph 구조를 판별하는데에 있어 WL Test 만큼이나 효과적이다.  
2) 위 문장이 성립하기 위해서는 Neighbor Aggregation이나 Graph Readout Function에 관하여 조건들이 필요한데, 본 논문에서는 이를 제시하였다.  
3) 본 논문에서는 GCN이나 GraphSAGE는 구분할 수 없는 Graph 구조에 대해 언급하고, GNN 기반의 모델들이 포착할 수 있는 Graph 구조들에 대해 정확하게 분석하였다.  
4) 본 논문에서는 GIN이라고 부르는 간단한 신경망 구조를 고안하였고, 이 알고리즘이 WL Test 만큼이나 discriminative/representational power를 갖고 있음을 증명하였다.  

2. Preliminaries

이 Section은 GNN의 기본에 관한 것이므로 간단히만 짚고 넘어가도록 하겠다. 자세한 내용은 논문 원본을 참고하면 좋을 것이다.

Graph Neural Networks
GNN은 Graph 구조와 Node Feature $X_v$ 를 활용하여 Node에 대한 Representation 벡터 $h_v$ 혹은 전체 Graph $h_G$ 를 학습하는 것을 목적으로 한다. 현대의 GNN은 이웃의 표현을 통합하여 Head Node의 Feature를 업데이트하기 위해 Neighborhood Aggregation이란 방법을 사용한다. GNN의 k번째 layer는 아래와 같이 표현할 수 있을 것이다.

[a_v^{k} = AGGREGATE^{k} ({ h_u^{k-1}: u \in \mathcal{N}(v) }]

[h_v^{k} = COMBINE^{k} (h_v^{k-1}, a_v^{k})]

$h_v^{(k)}$ 는 Node $v$ 의 $k$ 번째 반복 혹은 layer의 Feature 벡터를 의미하게 된다. 위에서 표기한 Aggregate 및 Combine 함수는 정말로 중요한 부분이다. 이를 어떻게 정의하느냐에 따라 GNN의 디자인과 효과는 굉장히 상이할 수 있다. GraphSAGE에서는 Aggregate 부분을 아래와 같이 정의하고 있다.

[a_v^{k} = MAX( ReLU(W \cdot h_u^{k-1}), \forall u \in \mathcal{N} (v) )]

Combine 부분은 아래와 같이 concatenation 방식으로 정의된다.

[W [h_v^{k-1}, a_v^k]]

GCN에서는 Aggregate 및 Combine 방식을 아래와 같이 통합하여 element-wise mean pooling을 사용하였다.

[h_v^k = ReLU( W \cdot MEAN { h_u^{k-1}, \forall u \in \mathcal{N} (v) \cup { v } } )]

Node Classification의 경우 최종 Layer의 Representation이 예측을 위해 사용되며, Graph Classification의 경우 Readout 함수가 아래와 같이 Node Feature들을 통합하게 된다.

[h_G = Readout ( { h_v^K \vert v \in G } )]

Weisfeiler-Lehman Test
Graph Isomorphism (동형 이성) 문제는 2개의 Graph가 위상적으로 동일한지 판단하는 문제를 의미한다. 이는 굉장히 큰 문제인데, 왜냐하면 아직까지 polynomial-time 알고리즘을 알려지지 않았기 때문이다. 몇몇 극단적인 예시를 제외하고는 WL Test의 경우 Graph의 broad class를 구별하는 효과적인 테스트로 평가받고 있다.

WL Test는 반복적으로 Node의 Label과 이웃을 통합한 후, 이렇게 통합된 label 혹은 이웃을 unique한 새로운 Label로 Hash한다. 이 알고리즘은 만약 일정 수준의 반복 이후 2개의 Graph에 존재하는 Node들의 Label이 달라지게 되면 2개의 Graph는 non-isomorphic 하다고 판단한다.


3. Theoretical Frameworks: Overview

GNN은 반복적으로 Node의 Feature 벡터를 업데이트하여 Network 구조와 다른 이웃 Node의 Feature들을 포착한다. 본 논문에서는 Node Input Feature는 countable universe에서 왔다고 가정할 것이다. 유한한 수의 Graph에 대해 어떠한 고정된 모델 하의 깊은 Layer에 있는 Node Feature 벡터 또한 countable universe에서 왔다. 단순하게 기호를 표현하기 위해, 본 논문에서는 각 Feature 벡터에 대해 고유한 Label을 $a, b, c …$ 와 같이 붙여줄 것이다. 그리고 나서 이들의 이웃 Node의 Feature 벡터로 multiset을 형성할 것이다. 이 때 다른 Node라 하더라도 같은 Feature 벡터를 가질 수 있으므로 같은 element도 여러 번 등장할 수 있다.

Multiset의 정의를 다시 짚고 넘어가기 위해 아래 논문 원본을 참조하면 좋을 것이다.

GNN의 Representational Power를 연구하기 위해, 본 논문에서는 GNN의 2개의 Node를 Embedding 공간 상에서 같은 위치에 매핑할 때를 분석하였다. 직관적으로 이러한 경우가 발생하기 위해서는, 각 Node가 존재하는 subtree 구조가 동일해야 할 것이다. 이 때 subtree 구조는 Node의 이웃에 의해 재귀적으로 정의되므로 이제 논의 주제는, 과연 GNN이 2개의 이웃 집합(Neighborhood)을 같은 Embedding 혹은 Representation으로 매핑할 것인가 하는 문제로 생각해 볼 수 있다.

Maximally Powerful GNN이라면 2개의 다른 이웃 집합(혹은 multisets of feature 벡터)을 같은 Representation에 매핑하지 않을 것이다. 이 때의 Aggregation Scheme은 반드시 Injective해야 한다. 따라서 본 논문에서는 GNN의 Aggregation Scheme을 이 신경망이 표현하는 multiset에 대한 함수의 class로 추상화하고, 이 신경망이 injective multiset function을 표현할 수 있는지 분석할 것이다.

다음 Section에서는 이러한 추론을 바탕으로 가장 강력한 GNN을 소개할 것이다. Section 5에서는 유명한 GNN 변형 버전들에 대해 이야기하고, 이들의 Aggregation Scheme이 내재적으로 injective하지 않고, 그렇기 때문에 덜 powerful하며 Graph의 흥미로운 특징들을 잘 담아내지 못한다는 것을 증명할 것이다.


4. Building Powerful Graph Neural Networks

우리는 Isomorphic Graph는 같은 Representation을 갖고, Non-isomorphic Graph는 다른 Representation을 갖길 원한다. 본 분석에서는 WL Test라는 다소 약한 기준을 통해 GNN의 Representational Capacity를 분석할 것이다.

모든 lemma와 theorem에 대한 증명은 본 논문의 부록에서 찾을 수 있다. 어쨌든, 어떠한 aggregation 기반의 GNN은 다른 Graph를 구별하는 데에 있어서는 WL Test만큼이나 강력해질 수 있다. 다음에 이어질 자연스러운 질문은 정말로 WL Test만큼이나 강력한 GNN이 존재할 것인가 하는 것이다. 아래 Theorem3에 따르면 답은 ‘그렇다’이다. 만약 Neighbor Aggregation 및 Graph-level readout 함수가 injective하다면, 이에 따른 GNN은 WL Test만큼이나 강력할 것이다.

위 이론은 중요하다. 이 부분에 대한 증명은 부록에서 찾아볼 수 있다.

좀 더 Simple하게 바꿔보자. 중요한 것은 Injective Multiset Function은 아래와 같이 표현된다는 것이다.

[\varphi ( \Sigma_{x \in S} f(x) )]

이 때 $\varphi, f$ 는 어떤 비선형 함수이고, $\Sigma$ 부분은 multiset에 대한 합 연산을 의미한다. 예시를 들면 쉽다. 아래 그림을 보면, One-hot 인코딩으로 이루어진 3개의 Node Feature 벡터가 합 연산으로 통합되었을 때, 모든 정보를 보존하고 있는 것을 알 수 있다.

이 때 위 Feature 벡터들이 One-hot 인코딩된 형태가 아니라 연속형 변수들로 구성되어 있다면 이들을 한 번 더 처리하기 위해 다른 과정이 필요할 것이다. 이 다른 과정을 본 논문에서는 MLP로 처리하고 있고, 이에 대해서는 아래 4.1에서 확인할 수 있다.

유한한 수의 집합에 대해, Injectiveness는 함수가 Input의 distinctness를 잘 보존하는지를 특징화한다. 셀 수 없는 집합에 대해서는, 만약 Node Feature가 연속형 변수라면 추가적인 조건이 더 필요하다. 더 나아가 학습된 Feature가 함수의 이미지 속에서 어떻게 가깝게 존재하는지 파악해보면 좋을 것이다. 이 부분에 대해서는 추후 연구로 남겨두고, 본 논문에서는 유한한 수의 집합에 대해서만 다루도록 하겠다.

다른 Graph를 구별하는 것을 넘어서 Graph의 구조적 유사성을 포착하는 GNN의 중요한 특징에 대해 논의해보는 것도 중요하다. WL Test에 존재한는 Node Feature 벡터들은 본질적으로 One-Hot 인코딩되었기 때문에 subtree 사이의 유사성을 잡아내지 못한다. Theorem3을 만족하는 GNN은 반대로 subtree를 저차원 공간에 embed하는 법에 대해 학습함으로써 WL Test를 일반화 할 수 있다. 이 점이 GNN으로 하여금 다른 구조를 판별할 뿐만 아니라 유사한 Graph 구조를 유사한 Embedding에 매핑하고 Graph 구조 간의 의존성을 포착하도록 해준다.

4.1. Graph Isomorphism Network (GIN)

Maximally Powerful GNN에 대한 조건을 수립한 이후, 본 논문에서는 Graph Isomorphism Network: GIN이라는 간단한 구조를 고안하였고, 이 알고리즘은 Theorem3을 만족한다. 이 모델은 WL Test를 일반화하며 따라서 현존하는 GNN 중에서 가장 강한 disciminative power를 지니고 있다.

Neighbor Aggregation을 위한 Injective Multiset Function을 모델링하기 위해, 본 논문에서는 Universal Multiset Function을 신경망과 함께 파라미터화 하는 Deep Multisets 이론을 제안한다. 아래 lemma가 aggregator를 더하는 것이 multiset에 대해 injective & universal한 함수를 표현한다는 것을 말해준다.

Deep Multiset과 Sets 사이의 중요한 구별점이라고 한다면, Mean Aggregator와 같은 특정한 유명 Injective Set Function의 경우 Injective Multiset Function은 되지 못한다는 것이다. lemma5의 Universal Multiset Function를 building block으로 모델링하기 위한 메커니즘으로 Node와 그 이웃의 multiset에 대한 Universal Function을 표현하는 Aggregation Scheme은 Theorem3에 있는 Injectiveness 조건을 만족한다고 생각할 수 있다. 아래 corollary는 이러한 여러 Aggregation Scheme 사이에 존재하는 간결하고 구체적인 formulation을 제공한다.

Universal Approximation Theorem 덕분에 위에서 제시되고 있는 함수 $f, \varphi$ 를 모델링하기 위해서 본 논문에서는 MLP를 사용할 것이다. 실제로 MLP는 여러 함수들의 Composition을 표현할 수 있기 때문에 본 논문에서는 $f^{k+1} \circ \varphi^k$ 를 하나의 MLP로 모델링할 것이다.

잠시 Universal Approximation Theorem에 대해서 짚고 넘어가자. 원본 논문은 이 곳에서 확인할 수 있다.

핵심 내용은 간단하다. 충분히 큰 Hidden Dimensionality와 적절한 비선형 함수를 갖는 1-hidden-layer MLP는 일정 수준의 정확도로 어떠한 연속형 함수도 근사할 수 있다는 것이 본 이론의 내용이다.

최초의 반복에서 만약 Input Feature가 One-hot 인코딩이면 그들의 합 또한 injective할 것이기 때문에 합 연산 이전에 MLP가 필요하지는 않다. 물론 One-hot 인코딩 되어 있지 않거나 연속형 변수가 중간에 끼어 있다면 MLP가 필요할 것이다. $\epsilon$ 의 경우 학습 가능한 파라미터 혹은 고정된 스칼라로 둘 수 있다. 최종적으로 GIN은 Node Representation을 아래와 같이 업데이트하게 될 것이다.

[h_v^k = MLP^k ( (1 + \epsilon^k) \cdot h_v^{k-1} + \Sigma_{u \in \mathcal{N} (v)} h_u^{k-1} )]

효과적인 GNN은 많이 존재하지만 GIN은 굉장히 간단하면서도 굉장히 효과적인 GNN의 대표적인 예라고 할 수 있다.

4.2. Graph-level Readout of GIN

Graph-level의 readout에서 중요한 측면은, 반복 횟수가 증가할 수록 subtree 구조에 따른 Node Representation이 더욱 정제되고 global해진다는 것이다. 충분한 반복이 지속되면 훌륭한 discriminative power를 얻을 수 있지만 때로는 반복을 적정 수준에서 멈췄을 때의 feature가 일반화 측면에서 더 나은 모습을 보이기도 한다.

사실 위 문제는 GNN에서 고질적으로 지적되는 Over-smoothing 문제라고 해석할 수 있다. 이 문제는 사용하는 이웃의 수가 많아지고, GNN의 Layer 깊이가 점점 더 깊어질 수록 Node 임베딩이 서로서로 비슷해지는 경향을 보이는 현상을 말한다.이 문제에 대해서는 추후에 다른 글에서 더욱 상세하게 다루도록 하겠다.

본 논문에서는 모든 구조적인 정보를 활용하기 위해 모델에 존재하는 모든 깊이 및 반복에서 생성된 정보를 활용하였다. 이를 위해서 본 논문에서는 Jumping Knowledge Network와 비슷한 구조를 차용하였다. 본 논문에서는 GIN에 존재하는 모든 반복/Layer 속에서 Readout 방식을 아래와 같이 변형하였다는 차이가 있다. 참고로 앞서 언급한 논문은 이 곳에서 전문을 확인할 수 있다.

[h_G = CONCAT ( READOUT ( { h_v^k \vert v \in G } ) \vert k=0, 1, …, K )]


5. Less Powerful but still interesting GNNs

GNN의 여러 변형 버전들의 경우 WL Test에 비해 단순한 Graph에도 쉽게 오류를 범하고 성능 측면에서도 다소 아쉬운 모습을 보이긴 하지만, GCN에서의 Mean Aggregator의 경우도 Node Classification Task 등에서는 굉장히 잘 작동한다. 이 부분에 대해 더욱 자세히 이해하기 위해 Graph를 학습하는 데에 있어 다른 GNN의 변형 버전들이 어떻게 Graph에 대해 학습하고 정보를 습득하는지 정확히 알아볼 것이다.

5.1. 1-Layer Perceptrons are not sufficient

현존하는 많은 GNN 구조는 1-layer MLP를 사용하고 있다. 그렇다면 이 구조는 Graph 학습에 있어 충분할까? 아래 lemma는 그렇지 않은 경우가 존재한다는 것을 알려준다.

위 내용을 증명할 핵심 아이디어는, 1-layer 퍼셉트론은 linear mapping과 유사하게 작동하기 때문에 GNN layer들이 이웃 Feature들을 단순히 합산하는 수준으로 퇴보할 수 있다는 것이다. 물론 이 때의 이야기는 bias term이 존재하지 않을 때를 가정한 것이고, 만약 bias term이 존재한다면 충분히 큰 output 차원이 전제될 때, 1-layer 퍼셉트론도 다른 multiset을 구분할 가능성이 꽤 높아진다.

그럼에도 불구하고 MLP를 이용한 모델에 비해 1-layer 퍼셉트론은 multiset 함수에 대한 Universal Approximator라고 할 수 없다. 결과적으로 1-layer 퍼셉트론을 이용한 GNN은 어느 수준까지는 다른 Graph를 다른 위치에 Embed 할 수 있지만, 충분히 그 특징을 잡아내지는 못할 수도 있다.

5.2. Structures that confuse mean and max-pooling

만약 $h(X) = \Sigma_{x \in X} f(x)$ 에 있는 합 연산을 GCN이나 GraphSAGE에 있는 Mean 혹은 Max-pooling으로 대체한다면 어떤 일이 발생할까?

Mean/Max-pooling Aggregator는 permutation invariant하기 때문에 여전히 잘 정의된 multiset 함수이지만 결정적으로 이들은 injective하지 않다.

위 그림을 보자. 3가지 방법이 존재할 때 이들의 표현력을 기준으로 순위를 매기면 위와 같은 결과를 얻을 수 있다. 이 때 같은 색의 Node는 같은 Node Feature를 갖는 것을 전제로 한다. 왜 이런 결과가 나오는지 아래 그림을 통해 알아보자.

a, b, c에 있는 Graph들은 모두 다른 구조를 갖고 있지만, 위와 같이 Mean/Max-pooling Aggregator는 다른 구조임을 인지하지 못한다. 이러한 사례가 바로 구조적인 정보를 포착하지 못하는 대표적인 사례가 된다. 반대로 Sum Aggregator는 차이를 인지할 수 있다.

(후략)


(생략)


7. Experiments

본 논문에서는 GIN과 GNN의 여러 변형 버전들의 성능에 대해 평가해보았다. 이 실험의 목적은 모델이 단지 Input Node Feature에 의존하지 않고 Network 구조를 학습하도록 하는 것이다. 본 실험에서 사용한 bioinformatics graph에 대해서는 Node는 Categorical Input Feature를 가지며, Social Network 데이터에서는 feature가 없다. Reddit 데이터셋에서는 모든 feature를 같게 만들어 사실상 의미 없도록 만들었으며, 다른 데이터셋에 대해서는 node degree를 One-hot 인코딩으로 추가하여 Feature를 생성하였다.

참고로 GIN-0은 $\epsilon$ 을 0으로 고정한 모델을 의미한다. 모든 설정에서 Input Layer를 포함하여 총 5개의 layer가 사용되었고 모든 MLP는 2개의 layer를 갖게 하였다. Batch Normalization은 모든 Hidden Layer에 적용되었다. Adam Optimizer가 사용되었고 최초의 Learning Rate은 0.01이고, 50 Epoch이 진행될 때마다 0.5씩 Learning Rate에 decay를 적용하였다.

bioinformatics graph에서는 16, 32의 hidden unit을, social graph에서는 64개의 hidden unit을 사용하였다. Batch Size는 32와 128 중에 선택하였고 Dropout Rate은 0 혹은 0.5를 선택하였다. 그리고 이 Dropout layer는 Dense Layer 이후에 적용되었다.

Training Set Performance
높은 Representational Power를 갖고 있는 모델일수록 Training Set에서의 정확도도 높을 것이다. 아래 그림을 보면 그 결과가 나와 있다.

결과를 보면 GIN-$\epsilon$ 과 GIN-0이 가장 좋은 성과를 보임을 알 수 있다. 본 실험의 결과에 따르면 $\epsilon$ 을 명시적으로 학습하는 것에 있어서 큰 효과는 나타나지 않았다. GNN의 다른 변형 버전들은 상대적으로 낮은 성과를 보였다. 또한 MLP layer가 1-layer 퍼셉트론보다 더 나은 성능을 기록하였다. 그리고 Mean/Max-pooling Aggregator 보다는 Sum Aggregator가 더 나은 결과를 보여주었다.

모든 결과에서 GNN은 WL Test를 넘어서지는 못했는데, 이는 매우 당연한 것이 GNN은 WL Test에 비해 낮은 Discriminative Power를 갖고 있기 때문이다.

Test Set Performance

테스트 셋에서의 결과를 보면 꼭 GIN이 최고의 결과를 보여준 것은 아님을 알 수 있다. 본 논문에서는 분류 성능으로 GNN의 Expressive Power를 가늠해보는 정도라고 이야기하고 있는데, 이 설명은 조금은 아쉽긴 하다. 개인적으로는 추가적인 설명이 더 있었으면 좋았을 것이라는 생각이 든다.

어쨌든 이번 결과에서 보면 GIN-0이 눈에 띄는 성과를 지속적으로 보이고 있음을 알 수 있다. 물론 이는 데이터셋의 특성에 기인한 것일 수 있고, 만약 Head Node의 특성이 풀고자 하는 문제에서 가장 중요한 특성이라면 $\epsilon$ 을 활용하는 것이 더 나은 선택이 될 수 있을 것이다. 실제로 필자가 현실 데이터에서 본 알고리즘을 활용해본 경험에 의하면, User-Item 사이의 Link Prediction을 풀어내는 문제에서는 $\epsilon$ 을 학습하는 것이 분류 성능 자체에 있어서는 더욱 도움이 되었다. (다만 이것이 전반적인 Graph와 Node의 특성을 더욱 잘 학습했다는 결론으로 직접적으로 귀결되는 것은 아니다.) 어쨌든 GIN-0이 전반적으로 더 나은 성과를 보인 것은 흥미로운 부분이다.


8. Conclusion

본 논문에서는 GNN의 Expressive Power에 대한 추론을 위한 이론적인 근간을 마련하였고, 여러 유명한 GNN의 변형 버전들의 Representational Capacity에 대한 엄격한 한계를 증명하였다. 또한 Neighborhood Aggregation Framework 하에서의 가장 powerful한 GNN 구조를 고안하였다.

미래 연구로 흥미로운 방향이 있다면, 지금껏 전제로 삼아온 Neighborhood Aggregation 혹은 Message Passing을 넘어서면서 Graph를 학습하는 powerful한 구조를 만들어 보는 것을 생각해 볼 수 있다. 이를 위해서는 GNN의 최적화 뿐만 아니라 이 네트워크의 일반화 특성을 이해하고 더 개선하는 방향으로 연구가 실행되어야 할 것이다.


References

1) 논문 원본
2) Stanford University CS224W Lecture

Comment  Read more

Graph Attention Networks (GAT) 설명

|

본 글에서는 2017년에 발표된 Graph Attention Networks라는 논문에 대한 Review를 진행할 것이다. 다방면에서 적용되는 Attention 개념을 Graph 구조의 데이터에 적용하는 초석을 마련한 논문이라고 할 수 있겠다. 자세한 내용은 논문 원본에서 확인하길 바라며 본 글에서는 핵심적인 부분만 다루도록 하겠다.

torch_geomectric을 이용하여 GAT를 사용하는 방법에 대해서 간단하게 Github에 올려두었으니 참고해도 좋을 것이다.


Graph Attention Networks 리뷰

1. Introduction

CNN은 image classification, semantic segmentation, machine translation 등 많은 분야에 성공적으로 적용되었지만, 이 때 데이터는 grid 구조로 표현되어 있어야 했다. 그런데 많은 분야의 데이터는 이렇게 grid 구조로 표현하기에 난감한 경우가 많다. 3D mesh, social network, telecommunication network, biological network 등이 그 예시라고 할 수 있다. 이러한 데이터는 Graph 구조로 표현할 수 있다.

굉장히 다양한 구조의 Graph를 다루기 위해 신경망을 확장하려는 시도는 지속되어 왔고, 초기 연구는 RNN으로 Graph 구조의 데이터를 다루고자 하였다. Graph Neural Network는 RNN의 일반화된 버전으로 소개되기 시작하였고 이후에 지속적으로 발전을 이어나갔다. 이후의 발전은 크게 2가지로 구분할 수 있을 것이다.

첫 번째는 Graph를 Spectral Representation으로 표현하는 것이다. Graph Laplacian의 eigen decomposition을 계산하는 Fourier domain으로 정의되는 합성곱 연산을 수행하는 것이 대표적인 방법이라고 할 수 있을 것인데, 이 연산을 통해 intense한 연산과 non-spatially localized한 filter를 만들어 낼 수 있다. 이러한 구조의 방법은 좀 더 세부적인 구조와 Feature의 특성을 반영하는데 특화되어 있지만 그 연산의 특성으로 인해, 새로운 구조의 Graph나 새로운 Node에 대해 대응하기 어려운 단점이 있다.

두 번째는 Non-spectral 혹은 Spatial Representaion으로 정의할 수 있겠다. 이는 합성곱을 Graph에 직접적으로 적용하고, spatially close한 이웃 그룹에 대해 연산을 수행하는 방법을 의미한다. 이러한 방법의 대표적인 예시가 바로 GraphSAGE이다. 이 알고리즘에 대해 자세히 알고 싶다면 이 글을 참조하면 될 것이다. 이러한 접근은 굉장히 scale이 큰 데이터에 대해 효과적인 접근으로 평가받고 있다.

위와 같은 최근 연구의 연장선에서, 본 논문에서는 Graph 구조의 데이터에 대해 Node Classification을 수행하기 위해 Attention 기반의 구조를 소개할 것이다. 연산은 효율적이며, 이웃들에 대해 각기 다른 weight을 지정함으로써 다른 degree(Node가 갖는 이웃의 수)를 갖는 Graph Node에 적용될 수 있다는 장점을 지니고 있다. 또한 이 모델은 Inductive Learning Problem에 직접적으로 적용될 수 있기 때문에 이전에 본 적이 없는 Graph에 대해서도 일반화할 수 있다는 큰 강점을 지닌다.


2. GAT architecture

2.1. Graph Attentional Layer

single Graph Attention Layer에 대해 설명할 것인데, 사실 이 layer가 본 논문에 소개된 GAT 구조에서 범용적으로 쓰인다. 본격적인 설명에 앞서 언제나 그렇듯 기호에 대해 잠시 정리하고 진행하겠다.

기호 설명
$N$ Node 수
$F$ Node의 Feature 수
$F^{\prime}$ Hidden Layer 길이
$W$ Trainable Parameter
$\vec{a}^{T}$ Trainable Parameter

아래와 같이 표현되는 Node Feature가 존재할 때,

[\mathbf{h} = { \vec{h}_1, \vec{h}_2, …, \vec{h}_N }]

위 $\mathbf{h}$ 행렬은 Layer를 통과한 후 $\mathbf{h}^{\prime}$ 의 형상을 취하게 될 것이며, 그 shape은 $N, F^{\prime}$ 이 될 것이다.

$W = (F^{\prime}, F), \mathbf{a} = (2F^{\prime}, 1)$ 의 shape을 갖고 있을 때 Attention Coeffieicient는 아래와 같이 정의된다.

[e_{ij} = a(\mathbf{W} \vec{h}_i, \mathbf{W} \vec{h}_j)]

위 식은 Node $i$ 에 대해 Node $j$ 의 Feature가 갖는 중요도를 의미한다. 이 때 $j$ 는 모든 Node를 의미하는 것은 아니고 $N_i$ 즉, Node $i$ 의 이웃에 대해서만 계산하게 된다. 최종적으로 softmax 함수를 통과하면 아래와 같은 Normalized Attention Score를 계산할 수 있다.

[\alpha_{ij} = {softmax}j (e{ij}) = \frac{exp(e_{ij})}{\Sigma_{k \in N_i} exp(e_{ik})}]

이전 식에서 $a$ 로 표기되었던 Attention Mechanism은 single-layer feedforward 신경망으로, 아래와 같이 학습 가능한 파라미터와 LeakyRELU activation 함수로 정의할 수 있다.

[\alpha_{ij} = \frac {exp (LeakyRELU ( \vec{a}^T [ \mathbf{W} \vec{h}i \vert \mathbf{W} \vec{h}_j ] )) } { \Sigma{k \in N_i} exp ( LeakyRELU ( \vec{a}^T [ \mathbf{W} \vec{h}_i \vert \mathbf{W} \vec{h}_k ] ) ) }]

이렇게 계산된 Attention Score는 아래와 같이 Node $i$의 이웃의 중요도를 결정하여 Input 데이터를 재정의하게 된다.

[\vec{h}^{\prime}i = \sigma( \Sigma{j \in N_i} \alpha_{ij} \mathbf{W} \vec{h}_j )]

이를 그림으로 나타내면 아래와 같다.

논문에서는 방금 설명한 Self Attention을 좀 더 안정화하기 위한 방법에 대해 상술하고 있는데 그 과정에 대해서는 아래 원문을 참조하길 바란다.

GCN과 달리 GAT는 같은 이웃 집단의 Node에 대해 다른 중요도를 배정하기 때문에 Model Capacity를 개선할 수 있으며 해석에 있어서도 도움을 주게 된다.

Attention Mechanism은 Graph의 모든 Edge에 공유되어 적용되기 때문에 전체 Graph에 대한 접근 없이도 학습이 진행될 수 있으며 이에 따라 Inductive Learning을 가능하게 한다.

GraphSAGE는 각 Node에 대해 고정된 수의 이웃을 추출하기 때문에 계산량을 일정하게 유지하게 되는데, 이는 추론을 행할 때 전체 이웃집단에 대해 접근할 수 없게 만드는 현상을 초래한다. 사실 본 논문에서는 LSTM Aggregator의 성능이 가장 좋았다고 기술하고 있는데, 이는 이웃 집단 내에서 각 이웃사이의 순서가 중요하다는 것을 암시하는 것과 다름이 없다. 만약 다른 Max Pooling Aggregator나 Mean Pooling Aggregator를 사용하였는데, 각 이웃 Node 사이의 순서 혹은 다른 개별적인 특징이 중요하다면, GraphSAGE는 이러한 부분까지는 커버하지 못하는 단점을 지니게 된다. 본 논문에서 제시하는 GAT는 이러한 한계에서 자유로우며 이웃 전체에 대해 접근하면서도 효율적으로 학습을 진행할 수 있다는 장점을 지닌다.


3. Evaluation

(중략)

3.4. Results

실험 결과에 대해서는 논문 원본을 참조하길 바란다.


4. Conclusion

본 논문에서는 Graph Neural Network (GAT)를 제시하였는데, 이 알고리즘은 masked self-attentional layer를 활용하여 Graph 구조의 데이터에 적용할 수 있는 새로운 Convolution-style의 신경망이다.

효율적인 연산과, 각기 다른 이웃 Node에 다른 중요도를 부과할 수 있다는 장점을 지니고 있으며 전체 Graph에 대한 접근 없이도 학습이 가능하기 때문에 Inductive Learning이 가능한 구조이다.


References

1) 논문 원본
2) 이분 그래프에 Attention 적용한 사례

Comment  Read more

Python time, datetime 사용법(Python 시간 다루기)

|

이 글에서는 Python 라이브러리인 time과 datetime에 대해 알아본다. 가끔 쓰는데 막상 쓰려면 언제 봐도 헷갈리는 라이브러리 중 하나인 듯 하다.

추가로 numpy와 pandas에서 사용하는 datetime64에 대해서도 정리한다.


Import

import time
import datetime

공통적으로, strptime()은 str을 datetime 객체로, strftime()은 datetime을 str로 바꿔준다.

time

현재 시각: time.time()

1970년 1월 1일 0시 0분 0초 이후 경과한 시간을 초 단위로 반환한다. 2021년 기준 대략 16억의 값을 가진다.

print(time.time())
# 1620055042.444191

time 객체: time.localtime(secs)

float 형식의 seconds를 입력으로 주면 지역 시간대에 맞는 time 객체로 변환할 수 있다. 입력을 안 주면 현재 시간으로 계산한다.

반환된 값에서 .tm_year 등의 값을 그대로 가져올 수 있다. 연, 월, …, 초, 요일(tm_wday, 일요일=0, 토요일=6), 몇 번째 날짜(tm_yday, 1월 1일=0, 1월 2일=1), 일광 절약 시간(tm_isdst, 미적용=0, 적용=양수, 정보없음=음수)

print(time.localtime())
print(time.localtime(secs=time.time()))
print(time.localtime(time.time()).tm_year)

# result
time.struct_time(tm_year=2021, tm_mon=5, tm_mday=4, tm_hour=0, tm_min=19, 
                 tm_sec=51, tm_wday=1, tm_yday=124, tm_isdst=0)
time.struct_time(tm_year=2021, tm_mon=5, tm_mday=4, tm_hour=0, tm_min=19, 
                 tm_sec=51, tm_wday=1, tm_yday=124, tm_isdst=0)
2021

출력 포맷: time.strftime(format, time object)

datetime에도 비슷한 메서드가 있는데, string format time 정도라고 생각하면 된다.

time 객체가 주어지면 지정한 format으로 출력해준다.

now = time.localtime()
print(time.strftime('%Y%m%d', now))
print(time.strftime('%c', now))
print(time.strftime('%x', now))
print(time.strftime('%X', now))
print(time.strftime('%H%M%S', now))

# result
20210504
Tue May  4 00:41:07 2021
05/04/21
00:41:07
004107

출력 포맷 종류

출력 포맷은 다음과 같은 것이 있다.

Format Description Example
%c 날짜, 요일, 시간을 출력, 현재 시간대 기준 Tue May 4 00:33:26 2021
%x 날짜를 출력, 현재 시간대 기준 05/04/21
%X 시간을 출력, 현재 시간대 기준 00:33:26
%a 요일 줄임말 Sun, Mon, … Sat
%A 요일 Sunday, Monday, …, Saturday
%w 요일을 숫자로 표시, 월~일 0, 1, …, 6
%d 01, 02, …, 31
%b 월 줄임말 Jan, Feb, …, Dec
%B January, February, …, December
%m 숫자 월 01, 02, …, 12
%y 두 자릿수 연도 01, 02, …, 99
%Y 네 자릿수 연도 0001, 0002, …, 2017, 2018, 9999
%H 시(24hour) 00, 01, …, 23
%I 시(12hour) 01, 02, …, 12
%p AM, PM AM, PM
%M 00, 01, …, 59
%S 00, 01, …, 59
%Z 시간대 대한민국 표준시
%j 1월 1일부터 경과한 일수 001, 002, …, 366
%U 1년중 주차(월요일이 한 주의 시작) 00, 01, …, 53
%W 1년중 주차(월요일이 한 주의 시작) 00, 01, …, 53

datetime

datetime.time은 시간 기능(시, 분, 초, 마이크로초)을, datetime.date는 날짜 기능을(연, 월, 일), datetime.datetime은 날짜+시간을, datetime.timedelta는 시간의 차이를 구한다.

아래에서 소개하는 함수들은 datetime.date, datetime.time, datetime.datetime 모두에서 사용 가능하다.

현재 시각: datetime.datetime.today()

현재 시각 정보를 포함하는 datetime 객체를 반환한다. 연도부터 마이크로초까지 나온다.
물론 각 원소는 .year와 같이 접근할 수 있다.

print(datetime.datetime.today())
print(datetime.datetime.today().year)

# result
datetime.datetime(2021, 5, 4, 0, 44, 5, 707495)
2021

datetime.date.today()로는 현재 날짜를 구할 수 있다.

datetime.date.today() 

# result
datetime.date(2023, 4, 29)

원하는 시각으로 datetime 객체 생성하기

메서드는 다음과 같이 생겼다. 연, 월, 일 등을 지정하여 datetime 객체를 생성할 수 있다.

datetime.datetime(year, month, day, hour=0, minute=0, second=0, microsecond=0)

print(datetime.datetime(2021, 12, 25))
print(datetime.date(2021,12,25))

# result
2021-12-25 00:00:00
2021-12-25

문자열로 datetime 객체 생성하기 - strptime

문자열과 그 문자열이 어떻게 생겼는지를 지정하는 format을 같이 주면 datetime 객체가 생성된다.

d = datetime.datetime.strptime('20211225', '%Y%m%d')
print(type(d))
print(d)
# result
<class 'datetime.datetime'>
2021-12-25 00:00:00

datetime을 문자열로 바꾸기 - strftime

today = datetime.datetime.today()
print(today.strftime('%Y-%m-%d %H'))

# result
2023-04-29 21

numpy datetime64

  • 날짜 및 시간과 관련된 ISO 8601 국제표준 방식으로 str type의 값을 전달해 생성하거나
  • 유닉스 시각(UTC 1970.1.1 자정)부터 경과 시간을 초 단위로 환산해 나타낼 수 있다.
  • np의 datetime64는 마이크로초($10^{-6}$)가 아닌 아토초($10^{-18}$)까지 저장한다.
  • 단위 코드는 Y, M, W, M, h, m, s, ms, us, ns, ps, fs이다.

str로 생성하기

import numpy as np
np.datetime64('2023-04-29')

# result
numpy.datetime64('2023-04-29')

경과한 시간으로 생성하기

아래는 각각 나노초(ns), 일(Day), 초(second)으로 생성한 결과이다.

np.datetime64(1000, 'ns')
np.datetime64(100000, 'D')
np.datetime64(123456789, 's')

# result
numpy.datetime64('1970-01-01T00:00:00.000001000')
numpy.datetime64('2243-10-17')
numpy.datetime64('1973-11-29T21:33:09')

일련의 날짜 객체 생성하기

np.arange로 범위를 줄 때는 [시작, 끝) 범위로 주고, Day, Month, Year 간격으로 생성할 수 있다.

np.array(['2021-01-01', '2022-01-01', '2023-01-01'], dtype='datetime64')
# np.arange('2010-01-01', '2010-01-03', dtype='datetime64[H]') # TypeError
np.arange('2010-01', '2010-02', dtype='datetime64[D]')
np.arange('2010-01', '2011-09', dtype='datetime64[M]')
np.arange('2010-01', '2012-09', dtype='datetime64[Y]')

# result
array(['2021-01-01', '2022-01-01', '2023-01-01'], dtype='datetime64[D]')

array(['2010-01-01', '2010-01-02', '2010-01-03', '2010-01-04',
       '2010-01-05', '2010-01-06', '2010-01-07', '2010-01-08',
       '2010-01-09', '2010-01-10', '2010-01-11', '2010-01-12',
       '2010-01-13', '2010-01-14', '2010-01-15', '2010-01-16',
       '2010-01-17', '2010-01-18', '2010-01-19', '2010-01-20',
       '2010-01-21', '2010-01-22', '2010-01-23', '2010-01-24',
       '2010-01-25', '2010-01-26', '2010-01-27', '2010-01-28',
       '2010-01-29', '2010-01-30', '2010-01-31'], dtype='datetime64[D]')

array(['2010-01', '2010-02', '2010-03', '2010-04', '2010-05', '2010-06',
       '2010-07', '2010-08', '2010-09', '2010-10', '2010-11', '2010-12',
       '2011-01', '2011-02', '2011-03', '2011-04', '2011-05', '2011-06',
       '2011-07', '2011-08'], dtype='datetime64[M]')

array(['2010', '2011'], dtype='datetime64[Y]')

datetime64 날짜 간격 계산

둘 중 최소 날짜 단위로 계산해준다.

np.datetime64('2023-04-29') - np.datetime64('2021-05-18')
np.datetime64('2023-04') - np.datetime64('2021')

# result
numpy.timedelta64(711,'D')
numpy.timedelta64(27,'M')

Pandas

개념 Scalar class Array class data type 생성 방법
Date times Timestamp DatetimeIndex datetime64[ns] 또는 datetime64[ns, tz] to_datetime 또는 date_range
Time deltas Timedelta TimedeltaIndex timedelta64[ns] to_timedelta 또는 timedelta_range
Time spans Period PeriodIndex period[freq] Period 또는 period_range
Date offsets DateOffset None None DateOffset

Timestamp

  • Datetimes는 python 표준 라이브러리인 datetime이다. 특정 시점 하나를 지칭한다. (Timestamp)
  • 두 개 이상의 배열을 다룰 때는 DatetimeIndex를 사용한다.
  • date_range() 함수(또는 ~Index)는 연 또는 월 단위로 인자를 주면 [시작 연/월의 시작일, 끝 연/월의 시작일]범위로 생성해 준다.
import pandas as pd
pd.Timestamp(2323.345689, unit='D')
pd.Timestamp('2023-04-29')

pd.to_datetime('2023-04-29 21')
pd.to_datetime(['2023-04-29', '2023-04-30'])

pd.date_range('2020-02-01', '2020-02-07')
pd.date_range('2020-01', '2020-09')
# pd.date_range('2020', '2023')

# result
Timestamp('1976-05-12 08:17:47.529600017')
Timestamp('2023-04-29 00:00:00')

Timestamp('2023-04-29 21:00:00')
DatetimeIndex(['2023-04-29', '2023-04-30'], dtype='datetime64[ns]', freq=None)

DatetimeIndex(['2020-02-01', '2020-02-02', '2020-02-03', '2020-02-04',
               '2020-02-05', '2020-02-06', '2020-02-07'],
              dtype='datetime64[ns]', freq='D')
DatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-04',
               '2020-01-05', '2020-01-06', '2020-01-07', '2020-01-08',
               '2020-01-09', '2020-01-10',
               ...
               '2020-08-23', '2020-08-24', '2020-08-25', '2020-08-26',
               '2020-08-27', '2020-08-28', '2020-08-29', '2020-08-30',
               '2020-08-31', '2020-09-01'],
              dtype='datetime64[ns]', length=245, freq='D')

Time spans

  • Time spans는 특정 시점이 아닌 기간을 의미한다. 하루 데이터면 0시 0분 0초부터 23시 59분 59초까지이다. 하나면 Period, 두 개 이상이면 PeriodIndex를 사용한다.
pd.Period('2019-01')
pd.Period('2019-04', freq='D')
pd.period_range('2020-01', '2020-02', freq='D')

# result
Period('2019-01', 'M')
Period('2019-04-01', 'D')
PeriodIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-04',
             '2020-01-05', '2020-01-06', '2020-01-07', '2020-01-08',
             '2020-01-09', '2020-01-10', '2020-01-11', '2020-01-12',
             '2020-01-13', '2020-01-14', '2020-01-15', '2020-01-16',
             '2020-01-17', '2020-01-18', '2020-01-19', '2020-01-20',
             '2020-01-21', '2020-01-22', '2020-01-23', '2020-01-24',
             '2020-01-25', '2020-01-26', '2020-01-27', '2020-01-28',
             '2020-01-29', '2020-01-30', '2020-01-31', '2020-02-01'],
            dtype='period[D]')

Date times와 Time spans의 차이는 아래로 확인할 수 있다.

spans = pd.Period('2023-04-29')
stamp = pd.Timestamp('2023-04-29 12:34')
print(spans.start_time < stamp < spans.end_time)

# result
True

date_range(), period_range() 함수에 freq 인자를 다양하게 넣어줄 수 있다.

pd.date_range('2019-04', '2019-05', freq='B')
pd.date_range('2019-04', '2019-05', freq='W')
pd.date_range('2019-04', '2019-05', freq='W-MON')

# result
DatetimeIndex(['2019-04-01', '2019-04-02', '2019-04-03', '2019-04-04',
               '2019-04-05', '2019-04-08', '2019-04-09', '2019-04-10',
               '2019-04-11', '2019-04-12', '2019-04-15', '2019-04-16',
               '2019-04-17', '2019-04-18', '2019-04-19', '2019-04-22',
               '2019-04-23', '2019-04-24', '2019-04-25', '2019-04-26',
               '2019-04-29', '2019-04-30', '2019-05-01'],
              dtype='datetime64[ns]', freq='B')

DatetimeIndex(['2019-04-07', '2019-04-14', '2019-04-21', '2019-04-28'], dtype='datetime64[ns]', freq='W-SUN')

DatetimeIndex(['2019-04-01', '2019-04-08', '2019-04-15', '2019-04-22',
               '2019-04-29'],
              dtype='datetime64[ns]', freq='W-MON')

주기는 아래와 같은 것들이 있다.

Frequency Description
None 일반적인 간격, 달력상 1일 간격
B 영업일(평일), Business Days
W 주말
M 각 월의 마지막 날
MS 각 월의 첫날
BM 주말이 아닌 평일 중 각 월의 마지막 날
BMS 주말이 아닌 평일 중 각 월의 첫날
W-MON 주(월요일)



# result


References

Comment  Read more

Linux(Ubuntu) terminal 명령어 정리

|

이 글에서는 ubuntu terminal에서 사용하는 명령어를 정리한다.

Ctrl + F로 원하는 명령어를 검색하면 좋다.


파일 탐색 관련

ls 기본 옵션

심심하면 입력하는 기본 명령

ls

상세히 출력하기

# -l 옵션은 파일 권한, 생성/수정한 user, 파일 크기, 수정 시각 등의 자세한 옵션을 표시한다.
# -h 옵션은 -l과 같이 쓰면 파일 크기를 알아보기 쉽게 표시해준다.
# -a 옵션은 숨겨진 파일 혹은 링크까지 같이 표시해준다.
ls -ahl

출력 개수 제한

# 상위 4줄만 표시
ls | head -4

절대 경로 출력

# 현재 위치에서 절대 경로 출력
pwd

# Use this for dirs (the / after ** is needed in bash to limit it to directories):
ls -d -1 "$PWD/"**/

# this for files and directories directly under the current directory, whose names contain a .:
ls -d -1 "$PWD/"*.*

# this for everything:
ls -d -1 "$PWD/"**/*

# Taken from here http://www.zsh.org/mla/users/2002/msg00033.html
# In bash, ** is recursive if you enable shopt -s globstar.

개수 세기

현재 위치에 있는 디렉토리 개수

하위 디렉토리는 체크하지 않으며, 현재 위치의 파일은 개수에 포함되지 않는다.

ls -l | grep ^d | wc -l

현재 위치에 있는 파일 개수

하위 디렉토리는 체크하지 않으며, 현재 위치의 디렉토리는 개수에 포함되지 않는다.

ls -l | grep ^- | wc -l

현재 디렉토리에 포함되는 전체 파일 개수

하위 디렉토리 내의 파일을 포함하며, 디렉토리는 개수에 포함되지 않는다.

find . -type f | wc -l

Find 사용

find는 파일을 검색하는 명령어이다. lsgrep을 조합하는 방법보다 효율적인 명령어라고 한다.

fine --help 명령어를 터미널에 입력하면 도움말을 볼 수 있다.

Usage: find [-H] [-L] [-P] [-Olevel] [-D help|tree|search|stat|rates|opt|exec] [path...] [expression]

기본 옵션은 -print이므로 그냥 find만 입력하면 현재+하위 디렉토리의 모든 파일을 보여준다. find .와 같다.

.
./cuda_11.1.1_455.32.00_linux.run
./cuda
./cuda/include
./cuda/include/cudnn_cnn_train.h
...

파일 타입별로 검색

find . -type -d
# -type -d는 디렉토리 타입만
# -type -f는 파일 타입만

# 결과 예시
.
./cuda
./cuda/include
./cuda/lib64

필터링 및 대소문자 (미)구분

-name <표현식>은 표현식 형식에 맞는 파일만 검색하는 옵션이다.(대소문자 구분)
-iname <표현식>은 대소문자를 구분하지 않는다.

find . -type f -iname "*.txt"
find . -type d -name "*lib*"

파일 수정 시간 필터링

  • -mmin : 수정시간 기준(분), modify + minute
  • -mtime: 수정시간 기준(일)
  • -cmin : 생성시간 기준(분), create + minute
  • -ctime: 생성시간 기준(일)
# 수정한 지 60분 이상 된 파일을 검색한다.
find . -mmin +60 -type f

파일 크기 필터링

# 100byte 이하 파일만 검색
find . -size -100c -type f
# 128kb 이상 파일만 검색
find . -size +128k -type f

현재+하위 디렉토리에서 특정 확장자 파일 모두 제거

# 아래는 확장자가 tsv인 파일을 찾아 제거한다.
find . -type f -name "*.tsv" -exec rm {} \;

파일 용량

전체 디렉토리 총 용량 표시

du 명령어는 현재 디렉토리(혹은 지정한 디렉토리) 및 그 하위 디렉토리의 총 용량을 전부 표시한다.

du # du .와 같다.
du /etc

특정 디렉토리 총 용량 표시

-s 옵션을 붙인다. kbyte 단위로 표시되며, MB, GB 등의 단위로 편하게 보려면 -h 옵션을 추가한다.

du -s /etc
du -sh /etc
du -sh /etc/*

특정 디렉토리 및 하위 n단계 디렉토리 총 용량 표시

-d <number> 옵션을 붙인다. 아래 예시는 /etc 및 바로 아래 포함된 디렉토리만의 총 용량을 표시한다.

du -d 1 /etc

디렉토리 및 파일의 용량 표시

du 명령어에서 -a 옵션을 붙이면 하위 파일의 용량까지 같이 표시할 수 있다.

du -a /etc

디스크 사용량

df 명령어를 사용한다. 위와 마찬가지로 편한 단위로 보려면 -h 옵션을 쓴다.

df
df -h

Symbolic link 관련

hard link과 symbolic link 두 종류가 존재하는데, 둘 다 바로가기 같은 개념이지만 차이가 조금 있다.

  • ln 뒤에 -s 옵션을 주면 symbolic link로 생성된다.
  • 원본 파일과 hard link 파일은 디스크에서 완전히 동일한 파일이다.
  • symbolic link 파일은 원본과는 다른 파일이고 링크만 가지고 있다.
# 원본 경로는 파일일 수도, directory일 수도 있다.
ln -s <원본 경로> <link 경로>
ln -s ~/data/Charades/Charades_v1_480/  dataset/ag/videos
# hard link는 -s 옵션을 붙이지 않는다.
ln <원본 경로> <link 경로>

링크 삭제

rm -f <link 경로>
rm -f dataset/ag/videos
# 경로 마지막에 /를 붙이면 삭제가 되지 않는다.

폴더에 대한 삭제나 파일에 대한 삭제 모두 rm -f로 삭제할 수 있다. 원본은 삭제되지 않는다.
rm -rf로 삭제하면 원본이 삭제된다.

참고로, symbolic link의 원본을 삭제하면 link는 존재하지만 실제로 파일에 접근할 수는 없다.
ls 등의 명령어로 확인하면 빨간색으로 표시된다.

hard link의 경우에는 원본을 삭제해도 (원본의 복사본) 파일에 접근 가능하다.


압축 관련

zip, unzip

압축하기

zip <압축파일명.zip> [-r] <압축할 파일or디렉토리 path 1> [ <압축할 파일or디렉토리 path 2> ...]
zip gorio.zip gorio1.txt gorio2.txt
zip gorio.zip -r gorio_dir/
zip gorio.zip -r ./Downloads/*
# ...

압축풀기

unzip <filename>
# 해제된 파일들이 저장될 디렉토리를 지정하고 싶으면 -d 옵션을 사용한다.(d=directory)
unzip <filename> -d <path>

여러 압축파일 한 번에 풀기

unzip '<zipfiles>' [-d <dir>]의 형식이다.

따옴표를 쓰지 않으면 filename not matched 에러가 뜬다.

# 여러 압축 파일을 지정해도 된다.
unzip a.zip b.zip c.zip
# 전부를 지정할 수도 있다. 이때는 따옴표를 꼭 써 줘야 한다.
unzip '*.zip' -d data/

tar, tar.gz

용량을 줄이는 압축 방법은 tar.gz 형식으로 압축하는 것이고, tar 형식은 단지 하나의 파일로 합치는 archiving 방식이다.

tar.gz로 처리하려면 옵션에 z를 더 붙이면 된다.

옵션 설명은 아래와 같다.

  • -c : 파일을 tar로 묶음
  • -x : tar 압축을 풀때 사용함
  • -v : 묶거나 파일을 풀때 과정을 화면에 출력
  • -f : 파일이름을 지정
  • -z : gzip으로 압축하거나 해제
  • -C : 경로를 지정
  • -p : 파일 권한을 저장

압축하기: tar -cvf, tar -zcvf

# 디렉토리를 tar로 압축하기
tar -cvf [압축파일명.tar] [압축하기위한 디렉토리]
# 파일들을 tar로 압축하기
tar -cvf [압축파일명.tar] [파일1] [파일2] [...]

# 디렉토리를 tar.gz로 압축하기
tar -zcvf [압축파일명.tar.gz] [압축하기위한 디렉토리]
# 파일 tar.gz 압축하기
tar -zcvf [압축파일명.tar.gz] [파일1] [파일2] [...]

압축풀기: tar -xvf, tar -zxvf

-c 옵션을 -x로 바꿔주기만 하면 된다.

# tar 파일 압축풀기
tar -xvf [압축파일명.tar]

# tar.gz 파일 압축풀기
tar -zxvf [압축파일명.tar.gz] 

여러 압축파일 한 번에 풀기

tar 또는 tar.gz 확장자를 갖는 파일들을 찾고 위의 압축풀기 명령을 각각에 대해 수행하는 코드이다.

# tar 형식
find . -name '*.tar' -exec tar -xvf {} \;
# tar.gz 형식
find . -name '*.tar.gz' -exec tar zxvf {} \;

Process 관련

Defunct Process

Defunct Process(좀비 프로세스) 찾기

ps -ef | grep defunct | grep -v grep

Defunct Process(좀비 프로세스) 개수 출력

ps -ef | grep defunct | grep -v grep | wc -l

defunct(zombie) process(좀비 프로세스) 죽이기

ps -ef | grep defunct | awk '{print $3}' | xargs kill -9

Kill process

sudo kill -9 $PID

Nvidia-smi

0.5초마다 업데이트

watch -d -n 0.5 nvidia-smi

PATH 확인

cuda 위치 확인

보통 /usr/local/cuda에 있다.

locate cuda | grep /cuda$
# or
find / -type d -name cuda 2>/dev/null

기타

오류 해결

\r command not found

파일에 ‘\r’이 포함되어 코드가 제대로 작동하지 않는 오류(보통 운영체제의 차이로 인해 발생). 다음 코드를 수행하면 해결된다.

sed -i 's/\r$//' <filename>

(지속 업데이트 예정)

Comment  Read more

LINK 코인(라인코인) 2021 계획 및 백서 요약

|

이 글에서는 라인 블록체인 사업의 일부인 링크코인에 대한 내용을 정리한다.

2021년 링크코인 계획(notice)와 백서(white paper)를 요약 정리하였다.
그림을 포함 내용의 출처는 글의 최하단에서 찾을 수 있다.


2021년 계획

목적

  • LINE Blockchain 생태계 확장
  • LINE Blockchain 프로젝트의 투명성과 이해도를 증가, 확장하여 LINE Blockchain 생태계의 health를 보장하고자 함

요약

  • LINE Blockchain은 2018년 시작되어 토큰경제와 블록체인을 구현하기 위한 기본 플랫폼과 조절체계(regulatory system)을 구축해 왔음
  • LINE Blockchain은 LINK의 유동성과 유용성을 확장하기 위한 의도를 갖고 있으며 LINE Blockchain의 대중화를 실현시키고자 함.
    • 이는 2021년 시작한 블록체인 기반 dApps(댑)과 새로운 서비스를 확장하여 이루고자 함

계획

  1. 사업 계획
    1. LINK가 있는 곳에 환전 기능 추가
    2. 결제 시스템과 제휴
    3. NFT-관련 서비스 확장
    4. dApps의 확장
  2. LINK 발행 계획
    1. 발행 및 배포 계획: LINK를 결제와 대출의 수단으로 쓰는 사람들에 대한 reward, DApps 가속화 등에 사용
    2. 추가 유통량
      • 기간: 2021.01.01 ~ 2021.12.31
      • 실제 배포 시점에 기반한 가격으로 1~2천만 엔 수준
      • LINK로 계산하면 41만~83만 LN(30일 평균 종가 기준)
      • 6.86~13.72%만큼 발행됨
    3. 발행량 정의
      • 초기 3년간 최대 1억 LN으로 제한되며, 이후로는 연간 5%씩 증가한다. 즉 1억 5백만, 1억 1025만, …
      • 발행 한계는 유통량을 의미하지는 않는다. 실제 발행량은 시장의 상황에 따라 변동된다.
      • 첫 발행이 2018년 9월 3일이기 때문에 “초기 3년”은 2021년 9월 4일에 종료된다.

Q&A(Jan 2021)

  • 추가 상장은 검토 중에 있다.
  • 발행 한도 증가 계획은 없고 신규 발행은 전부 공식 홈페이지에서 발 수 있다.
  • Node의 수를 증가시킬 계획은 현재는 없다.
  • LN의 매매제한 정책이나 이자상품 한도 등의 변경 계획은 답변하지 어려우나 최대한 많은 사람들이 쉽게 접근할 수 있도록 할 계획이다.
  • LN의 실 사용 계획은?
    • 쉽게 사용할 수 있고 보유 혹은 결제 시 혜택을 줄 수 있도록 하는 것이 주 목표
    • 그냥 자산으로서 존재하는 것이 아닌 실생활과 긴밀하게 연결될 수 있도록 할 계획
  • 개인이나 기관에 직접 판매하지 않으며 그럴 계획은 없다고 한다.
  • 현재 정책 등은 금융규제 방향에 맞추어 계속해서 변화할 수 있다.


Factbook

  • 라인과 블록체인 역사
  • LINE Blockchain 역사
    • 거래소는 Bitmax에서 Bitfront로 2020년 변경되었다.

LINE BlockChain

라인 블록체인은 다음 4가지로 구성되어 있다.

  1. LINK(Chrypto Asset)
  2. Crypto Asset이 운용되는 MainNet
  3. MainNet의 쉽고 편리한 분산어플리케이션을 구축할 수 있게 돕는 Platform
    • 가치의 저장, 전송, 소유의 증명 등 금융 기능에 집중한 부분
    • Virtual Machine, Consensus Algorithm, Blockchain Privacy 제공
    • 안정성, 성능의 확장성, 추적 가능한 익명성을 확보
    • 거래의 확정성(BFT Altorithm), PoS, VFT, 지분 증명 기능, 탈중앙화 등을 얻을 수 있음.
  4. Line CBDC Platform

여러 곳과 협업 중:

LINE Blockchain Mainnet

  • 2초마다 블록 생성
  • 수십만 개의 contribution mining, user 수 등
  • 일본에서 28번째로 등록된 디지털 자산
  • (2021년 1월) $90M의 시가총액
  • 절반 정도의 LN이 deposited되어 있다.

BlockChain Platform

LINE Blockchain Developers

  • 자신의 시스템에 블록체인 기술을 적용가능하도록 지원한다.
  • Developer Console, Open API, Wallet Integration 등을 제공한다.

BITMAX Wallet

  • 라인 계정과 통합되며, Token Management가 이루어진다.
  • dApp Browser가 있어서 라인 블록체인에 기반한 서비스를 이용할 수 있다.

Token Econoomy Flow

서비스 성장에 적극적으로 기여(Contribution Mining)한 유저에게 보상을 할 수 있고, 그것은 LINK 화폐로 이루어질 수 있다.

  • 일본에서 주로 서비스된다.
  • 라인 메신저 안에서 거래가 가능하다(dApp Browser).
  • 상황에 따라 달라질 수 있지만 12% 이자 상품이 있다.


References

Comment  Read more