문자나 숫자를 컴퓨터끼리 송수신하기위해서는 문자나 숫자를 그대로 전송하는 것이 아니라, 이러한 숫자나 문자를 비트단위로 끊어서 전송하게된다. 예를들어 숫자 3을 보내기 위해서는 0011으로 전송하게된다. 우리가 알고 있는 디지털신호(0아니면 1)로 변환해서 보내기 용히하기 때문이다. 이때, 숫자를 16진수로 보내면 최대 한 전송단위 16까지를 보낼수있다. 이렇게 한 전송단위(16비트)을 다시 2진수로 변환하여 송수신할 수 있다. 즉, 파이썬 내부에서 다뤄지는 문자열, 숫자는 파이썬에서 생성된 객체이고, 이를 바로 트랜스포트에 그대로 싣는 것은 안된다는 것이다.

따라서, 우리는 객체를 byte으로 적절히 인코딩을 해줘서 보내고, 컴퓨터는 이를 디지털신호로 바꿔 전송한후, 컴퓨터가 16진수는 숫자를 4bit단위로 끊어서, 인식하게 된다. 아래와 같이, 16진법 앞에는 "0x"을 붙여주어, 사용한다. 앞에 접두사로 "0x"을 붙이는 이유는 컴퓨터 입장에서는 0001을 이라는 디지털 신호가 왔을때, 이게 2진법인지 8진법으로 해석해야하는지 16진법으로 해석해야하는지 모르기 때문이다. "0x"은 16진수로 신호를 해석하라는 의미가 된다.

 

그 다음에 아래의 16진법 표기법과 같이, 해석을한다. 16진법에서는 컴퓨터가 받아들이는 입장에서는 "10"에서의 "1"과 "01"에서의 "1" 동일하기 때문에, "10"을 A로 표기한다. 그 이후에 11은 B, 12은 C... 15은 F까지 표기를한다. 

10진법 Hex(16진법) Binary
0 0x00 0000
1 0x01 0001
2 0x02 0010
3 0x03 0011
4 0x04 0100
5 0x05 0101
6 0x06 0110
7 0x07 0111
8 0x08 1000
9 0x09 1001
10 0x0A 1010
11 0x0B 1011
12 0x0C 1100
13 0x0D 1101
14 0x0E 1110
15 0x0F 1111

 

Python 내부에서 이러한 구조체를 사용할 수 있게 내장함수가 구현되어있는데, stuct (structure)을 사용하면 구현할 수 있다. 예를 들어, 아래와 같이 5를 한 byte 사이즈로 표현하면 0x05가 될 것이고, 4개의 byte로 표현하면 0x가 4개로 이루어진 hexadecimal이 표현된다.

# hexadecimal.py
import struct

a = struct.pack('b', 5)
print(a)

b = struct.pack('i', 5)
print(b)

b'\x05'
b'\x05\x00\x00\x00'

 

아래와 같이 2개 이상의 문자또는 숫자를 보내야하는 경우에는 포맷에 그 크기에 맞춰 포맷팅을 보내면 된다. 그렇지 않으면 아래의 예시의 a와 같이 패킹을 할 수 없다. 5,5을 16진법으로 표현할 방법이 없기 때문이다. 

import struct

b = struct.pack('ii', 5, 5)
print(b)
# b'\x05\x00\x00\x00\x05\x00\x00\x00


a = struct.pack('b', 5, 5)
print(a)
# Traceback (most recent call last):
#   File "hexadecimal.py", line 3, in <module>
#     a = struct.pack('b', 5, 5)
# struct.error: pack expected 1 items for packing (got 2)
반응형

 

 

fixture 란?


단위 테스트를 위해서, fixture을 이용하면 사전에 미리 정의된 신뢰할만하고 일관성있는 테스트를 이용할 수  있다. fixture은 환경이나 데이터셋에 대해서도 테스트해볼 수 있다. 이 fixture은 "step"과 "data"을 정의하고 이 테스트를 

 

fixture을 이용한 서비스, 상태, 조작환경등이 인자(argument)을 통해 모두 접근가능하고, 각 fixture는 보통 파라미터를 지정해준다. 그리고 pytest을 이용할 때, 어떤 함수를 테스트할것인지 @pytest.fixture 와 같이 "@"데코레이터를 이용해서 설정할 수 있다.

 

@pytest.mark.parametrize(ids=?) 내 ids란?? 

parameterize로 전달해주는 파라미터에 대해서 string으로 각 아이디를 줄 수 있고, 이는 "-k"을 옵션을 이용해서 특별한 파라미터(케이스)에 대해서만 실행해볼 수도 있다.

 

@pytest.fixture(scope=??):


scope범위에 따라 데코레이터를 달아준 함수의 실행범위가 달라진다. 예를 들어, scope="function"인 경우는 데코레이터를 달아준 함수를 매번 call해서 사용하고, 소멸된다. 한편, scope="module"로 지정하면 ".py"파일 내에서는 무조건 1번만 생성된다. 한편 scope="session"인 경우는 최초 테스트 실행시 단 한번만 객체가 실행되며, 각 테스트에서 이 하나의 객체가 호출된다. 

Reference: https://velog.io/@gyuseok-dev/pytest-pytest.fixture

https://velog.io/@dahunyoo/Test-fixture-on-Pytest

 

yeild 사용


yield은 보통 제너레이터에서 lazy하게 데이터를 불러올떄 사용한다. pytest에서는 테스트 실해 후에 환경정리, 테스트 데이터들을 정리할 때 yield 을 이용할 수 있다. yield는 호출한 테스트케이스가 실행된 후 종료되어도 마지막까지 실행되게끔 해주는 키워드이다.

반응형

 

Key contribution


• We cast few-shot learning as a supervised message passing task which is trained end-to-end using graph neural networks.

• We match state-of-the-art performance on Omniglot and Mini-Imagenet tasks with fewer parameters.

• We extend the model in the semi-supervised and active learning regimes

=> 그레프에서 노드분류에 많이 사용되는 메시지 패싱(A노드를 분류하고자할 때, A와 인접한 노드의 라벨을 참조하여 라벨을 예측하는 방법)을 이용해서 few-shot을 진행함. 위의 전략으로 실제 데이터 Omnigolot(알파벳분류), 이미지분류(Mini-imagenet)에서 우수한 성능을 보임. 이 모델을 준지도학습과 엑티브러닝에 확장함.

 

Problem setting


아래와 같이 입력값(T)과 출력(Y)가 있다고하자. T을 자세히보면 s까지 x와 l개가 있는데, 이는 라벨이 있는데이터가 s개가 있다는 것이다. 그리고 x_r까지 있는 데이터는 라벨이 없는 입력값이며, x_t은 분류하고자하는 test data이다. l의 라벨은 K개로, 멀티라벨분류문제이다. 또한, 각 입력x끼리는 독립적이며, 같은분포p 에서 나왔다고 가정한다(=train, test split가 랜덤하게 주어진것과 비슷한 상황).

Y는 x_t의 라벨을 의미한다.

 

이런 데이터셋에서 다음의 3가지의 문제가 등장한다.

1.  Few shot learning: r=0 (라벨이 없는 데이터는 없으며), 라벨이 있는 데이터만 있고, 예측해야할 데이터가 1개일 경우(t=1). 그리고, 훈련데이터 샘플의 개수인 s 가 비교적 넉넉하여 s=qk 인경우. 예를 들어 예측해야할 라벨(K=5) 5개인데, q=10이어서, 데이터가 50개인경우를 의미한다. 이러한 경우는 10-shot, 5 way learning이라고 한다.

 

2. Semi-supervised learning: r>1 인경우여서, 라벨이 없는 훈련데이터세트가 존재하는 경우를 의미한다. 위의 문제는 같이 훈련데이터 내 라벨이 있든,없든 같은 분포에서 x가 나왔기 때문에, 라벨이없는데이터를 최대한 활용하는 것을 목표로 한다. 주요한 가정은 x와 x'(라벨이없는데이터)는 같은 분포에서 나왔기 때문에, 둘은 같은 라벨일 것이다라는 것이 주요 가정이다.

 

3. Active learning: 모델이 학습하고자하는 데이터의 라벨을 인간에게 요청하고, 계속학습하는 학습방법. Semi-superivsed의 특별한 경우라고 생각되며, s+r << s0 , S <<S0. 라벨이 없는 학습데이터의 개수보다 라벨이 있는 학습데이터의 개수가 충분히큰 상황. 본 연구에서는 semi-supervised보다 active learning이 얼마나 더효과적인지를 분석하고자한다.

- 몇가지 배경지식이 필요하다. Human In The Loop (HITL)은 컴퓨터는 빠르고, 정확하지만, 문제를 푸는 방법은 멍청하고, 인간은 느리고, 똑똑하지만 문제푸는 방법은 현명하다. 이 둘을 머신러닝 학습에 혼합하는게 HITL이라는 개념이다. 사실 모든 기계학습과정은 HITL의 연속이다. 예를 들어, 사소한 하이퍼파라미터만 해도 사람의 과정이 들어가기 때문이다. Active learning은 이 HITL에서 인간의 개입이 명시적으로 들어간 반복적인 작업이다. 

  1. Oracle(인간)이 모델과 라벨이 있는 데이터를 생성함. ground truth을 제공하는 원천. 라벨러+ 모델생성자로 생각할 수 있음.
  2. 모델은 오라클이 준 라벨을 학습함. (추가적으로 인간이 준 검증용 데이터 세트에 학습로그를 제공)
  3. 모델은 데이터에 라벨을 붙이고자하는 라벨없는 데이터를 인간에게 요청함
  4. 이 과정을 반복

 

 

모델링


 

반응형

기본적으로 언어는 계층구조를 갖는다. 구(Phrase)와 절(Clause)처럼 단어가 모여서 구(phrase)을 이루고, 구가 모여서 절(clause)을 이룬다. 예를 들어, 형용사절을 들어본적이 있는가? "men in black"과 같이 "in black"이 형용사절을 이루고, 이러한 구가 모여서 명사절 등을 이룬다. 사람은 이와 같이 계층구조를 이루는 것을 이애할 수 있다. 유사하게, "이런 계층구조의 모델링을 명시하진 않았지만 Tree 구조를 이용한다면, 더 좋게 할 수 있지는 않을까"에 대한 질문이 본 페이퍼의 핵심이다.

 

위의 그림처럼 Interest expensae은 명사구이며, inthe 1988 thir quater도 형용사구이지만 이를 또 나눌수도있고, 계층구조로 사람은 이해할 수 있다.

 

본 페이퍼를 읽기 위해 다음의 배경지식이 필요함

1. Inductive bias (aka learning bias): 귀납적 편향. 학습편향. 모델이 경험하지 않은 데이터를 입력받았을 때, 출력을 예측하는데 사용하는 가정의 집합임. 정확히 학습되지 않은 것들을에 대해서 귀납적으로 학습한 것들이 있으니, 예측을 할 수 있지 않느냐의 대한 가정임. 

=> 본 연구에서는 이러한 학습편향을 정보의 주기(생성~삭제)를 따로따로 관리하여(differentiation) 증진하겠다는 전략이다. High ranking neuronlow ranking neuron으로 나눠서, 고순위의 뉴런들에서는 정보를 오래 가지고 있게하고, 저순위 뉴런에서는 정보를 빨리 잊게하는 테크닉을 보임.

 

Key technique: ON-LSTM


1. Cumax: Cumax은 Cumsum(cummulative summation)과 softmax을 합친것이다(합성함수다). 이를 사용하는 이유는뉴런의 랭킹을 결정할때 지나치게 명확히 high, low랭킹을 구분하는것을 피하기위해 사용되었다고한다. " To avoid a strict division between high-ranking and low-ranking neurons, we propose a new activation function, the cumulative softmax, or cumax(), to actively allocate neurons to store long/short-term information". Cumax에서 반환되는 값은 누적합이기에 연산도중에 1이뜨면, 그 이후의 인덱스의 값들은 모두 1일 것이다. 따라서, 어느 인덱스부터 1인지에 대한 기준이 되는 지를 표기했는데, $d_{t}^{f}$이다.

import tensorflow as tf

# Equation (6)
def cumax(x, axis=-1):
    return tf.math.cumsum(tf.nn.softmax(x, axis), axis)

 

def split_point(x):
    x = x.numpy()
    return np.where(x == 1)[0].min()

 

2. LSTM : Ordered neuron LSTM은 기본적인 LSTM연산을 필요로하고, 그 위에다가 master gate을 부착한다. 그렇기에 LSTM연산도 포함된다.

-

    
class ONLSTM(tf.keras.layers.Layer):
    def __init__(self, input_dim, hidden_unit):
        super(ONLSTM, self).__init__()
        self.input_dim = input_dim
        self.hidden_unit = hidden_unit
    
    def build(self):
        # Equation 1-5: basic LSTM model
        self.w_f = self.add_weight(shape=(self.input_dim, self.hidden_unit), name='standard_lstm_kernel')
        self.w_i = self.add_weight(shape=(self.input_dim, self.hidden_unit), name='lstm_input_kernel')
        self.w_o = self.add_weight(shape=(self.input_dim, self.hidden_unit), name='lstm_output_kernel')
        self.w_c = self.add_weight(shape=(self.input_dim, self.hidden_unit), name='cell_memory_kernel')

        self.u_f = self.add_weight(shape=(self.hidden_unit, self.hidden_unit), name='standard_recurrent_kernewl')  # u_f
        self.u_i = self.add_weight(shape=(self.hidden_unit, self.hidden_unit), name='lstm_input_recurrent_kernel')
        self.u_o = self.add_weight(shape=(self.hidden_unit, self.hidden_unit), name='lstm_input_recurrent_kernel')
        self.u_c = self.add_weight(shape=(self.hidden_unit, self.hidden_unit), name='lstm_input_recurrent_kernel')

        self.b_f = self.add_weight(shape=(1, self.hidden_unit), name='forget_bias')
        self.b_i = self.add_weight(shape=(1, self.hidden_unit), name='input_bias')
        self.b_o = self.add_weight(shape=(1, self.hidden_unit), name='output_bias')
        self.b_c = self.add_weight(shape=(1, self.hidden_unit), name='cell_bias')
        
        
   def _base_lstm_op(self, x, h):
        f_out = tf.nn.sigmoid(tf.tensordot(x, self.w_f) + tf.tensordot(h, self.u_f) + self.b_f)
        i_out = tf.nn.sigmoid(tf.tensordot(x, self.w_i) + tf.tensordot(h, self.u_i) + self.b_i)
        o_out = tf.nn.sigmoid(tf.tensordot(x, self.w_o) + tf.tensordot(h, self.u_o) + self.b_o)
        c_out = tf.nn.tanh(tf.tensordot(x, self.w_c) + tf.tensordot(h, self.u_c) + self.b_c)
        return o_out * tf.nn.tanh(c_out)

 

3. ON-LSTM: strucure gate mechanism:

    def call(self, x, h):
        h, outputs = self._base_lstm_op(x, h)

        lstm_f = outputs[0]
        lstm_i = outputs[1]
        lstm_c_hat = outputs[2]


        # Equation 9: master forget gate.
        wx = tf.einsum('ij,jk->ik', x, self.w_f)  # (1xhidden)
        uh = tf.einsum('ij,jk->ik', h, self.u_f)  # (1xhidden)
        f_t = cumax(wx + uh + self.b_f)  # (1 x hidden)

        # Equation 10: master input gate.
        i_t = 1 - cumax(wx + uh + self.b_i)  # (1 x hidden)

        # Equation 11:
        w_t = f_t * i_t #  (1 x hidden)

        # Equation 12:
        f_t_hat = f_t * (lstm_f * i_t + 1 - i_t)

        # Equation 13:
        i_t_hat = i_t * (lstm_i * f_t + 1 - f_t)

        # Equation 14: first d_t neuron of the previous cell state will be complete erased.
        c_t = f_t_hat * self.c_t + i_t_hat + lstm_c_hat
        self.c_t = c_t

        return h

 

 

Reference: https://openreview.net/forum?id=B1l6qiR5F7

반응형

 

 

맥쿼리인프라가 "영산에너지", "보문에너지"의 두 에너지사에 3,977억(조달비용포함 23억)을 투입하기위해, 유상증자를 진행한다. 유상증자는 주주배정 후 실권주 일반공모로 진행한다.

 

1. 유상증자 주주배정 후, 실권주 일반공모란?

이 회사는 2021년 7월 2을 신주배정 기준일로 하여서, 장부에 포함된 주주들(7월 2일전 등록된 "구주주")에게 지분율에 따라 주식을 우선적으로 청약할수 있는 신주인수권을 주기로함. 만약 신주인수권 행사를 통해 청약/초과청약이 끝났음에도, 미청약 신주가 있다면, 일반공모로 진행한다. 구주주가 부여받은 신주인수권은 따로 거래소, 장외시장에서 5일동안 팔수도 있다. (판매가능일 2071년 7월 21일~27일).

=> 살수있는 기회를 우선적으로 배정함, 남으면 일반공모로 바꿔서 주식이 없는사람도 신규청약으로 가능함.

 

2. 가격은?

최종발행가액은 7월 30일에 확정할 것을 예상하지만, 할인율은 2.4%정도이다. 즉 7월 30일에 1) 최근 1개월에 거래량과 거래대금을 가중치로하여, 매일 종가곱한 "가중산술평균"을 기준으로 산정한 가액, 또는 2) 1주일 가중산술평가를 본 산정한 가액중 낮은금액 => 이 가격에서 아마도 할인율은 2.4%쯤.

 

https://www.mkif.com/assets/mkif/ko-kr/investor-centre/public-filings-and-reports/2021/ir-release-2106-mkif-follow-on-offering-kor.pdf

 

 

3. 영산 에너지 / 보문에너지

영산에너지 및 보문에너지는 SPC(특수목적법인, 말그대로, Special Purpose Company)이다. 대규모 금융조달이 필요한 사업은 회사가 직접 하기에는 재무구조가 온전치 못할 수도 있으니, SPC을 설립한다. SPC을 설립하면 FI(재무적투자자)들이 투자를 SPC에만 한다. 즉, SPC회사의 모회사에 투자하는게아니라, 알맹이인 사업에만 투자를 할수 있다. 모회사입자에서는 SPC의 부채가 모기업의 부채로 잡히지않고, FI들도 굳이 모회사에 투자할 필요도 없다. 즉, 현금흐름계산을 분리하여계산한다.

=> 특수목적법인은 모회사와 재무상태를 공유하지않아 프로젝트의 흥망성쇄를 공유하지않고, 투자자들도 특수목적법인에만 투자할 수 있다.

 MKIF가 직접마련한 자본금과, 주주들에게 유상증자한 금액을 합쳐서 각각  두  SPC에 투자한다. 각 SPC은 SPC 자회사인 운영회사를 직접 100% 소유한다.

 

4. MKIF은 왜 갑자기 SPC두 회사에 투자하나?

일단 두SPC은 도시가스소매업자(도시가스 판매사)이다. 정부의 탄소중립2050에 따른, 천연가스 수요증가. 아직 미확보된 도시가스 권역에 가스 공급체계를 보강해서 신규수요를 확보하여, 신규 캐시플로우를 확보할 수 있음.

 

영산클린에너지(=>해양에너지) 전남권에 도시가스를 공급하고있다. 이 해양에너지만 놓고보면 자산규모 4,800억원, 매출액은 5,200억원이다. 해양에너지의 광주광역시전라남도 권역에 도시가스 보급율은 각 100% 33%이다. 이때문에 광주광역시 및 전라남도 권역의 CAGR은 1.2%, 6.5%이다.

한편, 보문클린에너지(=>서라벌도시가스)은 경상북도에 도시가스를 공급하고있다. 자산규모는 약 1,000억, 매출액은 1,224억원. 경상북도 도시가스 보급율은 71%, 매 성장율은 1.4%이다 (이미 약간 수렴상태에 가까워져있는 듯하다.)

아래의 그림처럼, 검정은 지분, 하늘색은 도시가스 흐름이다. 한국가스공사가 운영회사인 해양에너지 및 서라벌도시가스에 천연가스를 주면, 그걸 이용한 발전한 에너지를 최종소비자에게 두 회사가 공급한다. 이 공급시에는 가스는 가스파이프로 공급하애햐기때문에 대규모 초기 투자금이 들어간다.

긴 가스관 증설등 대규모의 초기투자가 요구되어, 정부에서는 공급설비의 중복투자를 막고, 요금안정화/도시가스 공급을 위해 몇 없는 사업자에게만 일정 공급권 권역내 도시가스를 공급할수 있게 사업권을 줌 (소수에게만 제한함). 가스 가격은 도시가스사업법 /도시가스회사 공급비용산정 기준 등 정부지침으로 산출됨. 원료비는  도매단가, 적정원가, 정적투자보수등으로 소매공급비용을 정함. 

 

아래의 기준과 같이 어찌어찌해서, 소매단가를 정했으면, 소매단가에 * 공급물량을 곱해서 결국에 수익을본다 (P는 정부에서 정해주지만 Q가 늘어난다면 이득이라는 가정하에 투자하는 것으로 보인다). 가스가격이 상승해도 소비자에게 전가가 가능한 구조로 보인다.

맥쿼리인프라 IR자료. 도시가스요금산정방식

부록. 천연가스도 태우면 탄소가 나오는데 왜 탄소중립? 

탈탄소화를 추진하겠다는 정부목표하에, 산업/수송/발전/건물등 전분야에서 저탄소 연료의 전환을 목포로 하고 있다. 이 천연가스는 석탄발전 등 다른 에너지원을 태우는것보다 상대적으로 발생하는 탄소가 적다. 아래의 기사처럼, 석탄:992, 석유:782, LNG:549 (단위:g/kwh)으로 다른에너지원보다 탄소가 적게나오고 태양광은 에초에 발전량이 많지않아 LNG을 타깃으로 발전하고자하는 정부추이가보인다. 549도 많은숫자아니야? 라고 할 수있지만, 인간이 가진 에너지원중에 그나마 현실적으로 저탄소에 가까운 에너지원이라고 생각할 수 있다.

https://www.sedaily.com/NewsVIew/1ZBVW8Y960

 

탄소중립에 가장 적합한 발전원은 원전....온실가스 배출량 LNG보다 50배 적어

정부가 탈원전 정책을 고수하면서 오는 2050년 온실가스 실질 배출량을 ‘제로(0)’로 만드는 탄소 중립 달성을 선언한 것은 모순적이라는 지적이 끊이지 않고 있다. 정부가 이산화탄소 배출이

www.sedaily.com

 

5. 기대효과?

5.1. KMIF의 신규현금흐름이 더 생긴다. 가스쪽의 자금조달이 외부차입금도 꽤있지만, 이자(금융조달비용)을 낮추고자 주주대여금을 확보했기에 추가적인 현금흐름을 마련하고자 하는 것으로 보인다. 

5.2 위험분산. 코로나 19로 유료도로의 수입이 감소(MRG)이 있긴하지만, 떡상할일은 없어보인다. 대신 도시가스 코인을 탑승할 수 있다.

 

6. 발행가액 시나리오별 배당/ 및 주가수익율

 

투자시 유의점

1. 2.4%할인율을 보고 신규발행의 차익을 위해, 신규매수가 증가 -> 주가 상승 -> 발행가액 상승 -> 고점 매수 구조로 될 수 있음.

2. 그외 사업부문에서 MRG 끝나는 사업부문에서 캐시플로우 감소 확률

 

 

부록1. 역대 KMIF 배당율, DPS, 주가추이

부록2. 주가별 현금배당수익율 밴드

 

부록3. 

부록4. 해양에너지 최근 결산 Finantial sheet

 

부록5. 서라벌에너지

* 글쓴이는 맥쿼리인프라 주식을 매수를 절대 권유하지 않았음.

 

반응형

GAT(Graph attention network)은 생각보다 오래전에 나왔다. 2018 ICLR(International Conference Learning Representation)에 발표되어, 현재 인용수만해도 3,000이 넘는다.

 

Key technical contribution


이전 그레프의 상태를 다음 그레프 상태에 Self-attention을 적용하여, 이전 그레프의 노드에 대한 다음 그레프의 노드의 중요도를 파악하는 것. 이를 attention coefficient 이라고함.

Attention coefficient: 아래와 같이 $h_{i}$(i번재 상태에서의 특징값)을 $h_{j}$번째의 상태에서의 특징값에 대해서 self-attention함. 결국 다음의 식에 따라, self-attention을 돌리고, 마지막에 attention weight을 곱하여, i번째의 상태의 그레프의 특징값을 반환

 

Multihead-attention coefficient: 저자들은 위의 attention coefficient을 k개를 만들어서 concat하는 방법으로 Multi-head attention을 계산도 해봄.

 

Single attention만 코드로 간략히 핵심만 구현하면 다음과 같다.

class GraphAttentionLayer(tf.keras.layers.Layer):
    '''
    Single head attention GAT
    Callable instance of class

    Parameters
    ----------
    input_dim: int. the number of feature space of given X
    output_dim: int. the number of expected feature space
    head_num: int. (defaualt 1)

    Output
    ------
    tf.Tensor (N, output_dim)
    '''
    def __init__(self, input_dim, output_dim, head_num=1):
        super(GraphAttentionLayer, self).__init__()
        self.input_dim = input_dim
        self.output_dim = output_dim
        self.head_num = head_num

    def build(self, input_shape):
        '''

        Parameters
        ----------
        input: h = (N, F)
        output: h' = (N, F')
        Returns
        -------

        '''
        # parameterized by a weight matrix W (F', F)
        self.kernel = self.add_weight(shape=(input_shape[-1], self.output_dim), name='W')  # W:(F, F')

        # Parameterized by a weight vector a (2F')
        self.a = self.add_weight(shape=(2*self.output_dim, 1), name='a')
        self.built = True

    def call(self, X):
        # Eqation 1) mapping F feature space to F' features space
        features_i = tf.einsum('ij,jk->ik', X, self.kernel)  # (NxF) (FxF') => WH_i (NxF')
        features_j = tf.einsum('ij,jk->ik', X, self.kernel)  # (NxF) (FxF') => WH_j (NxF')

        # Equation 3) Attention coefficient
        e_ij = tf.tensordot(tf.concat([features_i, features_j], axis=1), self.a, axes=1)
        a_ij = tf.nn.softmax(e_ij, axis=0)  # (N,1)

        # Equation 4) Applying non-linearity with sigma
        context_vec = tf.einsum('ij,ik->ik', a_ij, features_i)  # (N,1) (NxF')
        h = tf.nn.sigmoid(context_vec, name='attention_coefficient')

        return h

 

모델성능: 성능은 Transductive, inductive 두 가지 방법으로 설명함. (Transductive, inductive을 모른다면, 다음의 포스팅을 클릭). 1) Transductive dataset은 Cora, Citeseer, Pubmed라는 논문 인용에 관한 데이터. Node: 논문, edge: 인용. Node feature: Bag of words in document. Node label: class label.  2) Inductive learning: PPI(단백질-단백질-교호작용) 데이터세트. 그레프 분류

 

 

텐서플로2.0 으로 구현한 소스코드와 설명은 다음을 참조

- Github: https://github.com/4pygmalion/GAT_tensorflow

반응형

+ Recent posts