tf
공식문서를 보면 subclassing으로 layer block을 구성하고 model을 만드는 방법이 나와있다. 거기다가 tf.keras.layers
에 있는 것들은 tf.nn
에도 같은 기능을 하도록 구현하고 있다. low level에서는 결국 tf.nn
이 돌아갈 거라고 생각은 했지만 그러려니 하면서 넘겨왔다. 그러다 소스코드를 봤는데 있어야 되나 싶은 조건문들이 많이 붙어있었고 이걸 다 걷어내고 싶었다.
그래서 굳이 해보는 subclassing을 활용한 ResNet구현. 단, tf.keras
에서 상속은 받고 시작하기로 한다. 나중에는 이것도 덜어내고 순수 tf
로만 구현하고 싶기는 하다. 여기서 전체 코드를 쓰면 너무 길어지므로 subclassing을 어떻게 활용하는지에 대해서 서술하고 세부코드는 링크로 대체한다.
먼저 custom layer를 만드는 것을 개괄적으로 보면 이렇게 생겼다.
class CustomLayer(tf.keras.layers.Layer):
def __init__(self,...):
super().__init__()
...
def build(self, input_shape):
super().build(input_shape)
self.weight = self.add_weight(...)
def call(self, X):
...
return x
기본적으로 build
에서 weight의 모양과 크기, 범위를 초기화하고 여기에 필요한 변수들은 __init__
에서 선언한다. call
에서 layer의 입력값과 함께 연산하는 algorithm을 짜도록 정해놨다. 이런 식으로 Conv2d
, BatchNorm
, FC layer
를 만들면 ResBlock을 만들 수 있다.
ReLU
는 clipping이나 음수에 대해 기울기를 따로 주지 않으므로 따로 구현하지 않고 ResBlock을 연산할 때 강제할 수 있도록 한다.GAP
는 tf.maht.reduce_mean
을 활용하는 것으로 대체할 수 있으므로 따로 구현하지 않았다.class CustomBlock(tf.keras.Model):
def __init__(self,...):
super().__init__()
...
def call(self, X):
...
return x
필요한 layer들은 __init__
에서 불러와서 call
에서 layer들을 용도에 맞게 쓰면 된다. tf.keras.Model
을 상속받을지, tf.keras.layers.Layer
를 상속받을지 생각해보면 쓸 수 있는 메서드가 달라서 그때 그때 상황에 맞는 것을 고르면 되는 것 같다.
class CustomModel(tf.keras.Model):
def __init__(self,...):
super().__init__()
...
def call(self, X):
...
return x
이렇게 model을 만드는 건 subblock을 만드는 것과 크게 다르지 않다.
원래는 model구현까지가 목표였지만 하는 김에 training loop, LR scheduler, dataloader까지 그냥 구현해봤다. 하는 내내 어차피 tf.keras
를 쓰면 편하고 빠르게 구현할 수 있는데 굳이 이렇게 해야 하나 싶다가 빠르게 구현해서 시험한 다음에 고도화하면서 이렇게 뜯어고치면 되지 않을까하는 걸로 고쳐먹기로 했다.