머신러닝/CustomFramework

Residual Connection

hwijin97 2022. 4. 4. 19:27

 

Residual Connection

 

Residual Connection 은 Skip Connection 이라고도하는 계층사이를 연결하는 연결방식이다. 네트워크를 더 깊게 만들수록 표현력이 오히려 떨어지는 현상을 보고, 이런일은 optimize 를 잘 수행하지 못하기 때문이라고 생각해서 이전 Layer 보다 학습이 덜되지는 않게 이전 학습된 결과에 이번 Layer에서 더 학습할 잔여 학습이 있다면 이를 학습하는 방식이다. 의미적으로는 이번에 학습할 게 전혀없고 쓸데없는 Layer 라면 최소한 이전 Layer 의 결과를 그대로 출력하는 방식을 의미한다. 그래서 신경망이 깊어질수록 optimize 가 덜 되지는 않도록 한다. 사실 실제에서는 이전 결과를 그대로 출력하는 일 ( Identity mapping ) 은 거의 발생하지 않는다.

 


계산 및 미분 그래프식

 

$ x $ : 입력

$ f(x) $ : Residual Block ( 잔여 학습을 수행하는 블럭 )

$ y $ : 출력

 

 

$ y = f(x) + x $

 

$ \frac{\partial y}{\partial x}  =  \frac{\partial y}{\partial f} \frac{\partial f}{\partial x} + 1 $

 

$ \frac{\partial L}{\partial x} = \frac{\partial L}{\partial y}\frac{\partial y}{\partial f}\frac{\partial f}{\partial x} + \frac{\partial L}{\partial y} $

 

Skip Connection 의 Gradient : $ \frac{\partial L}{\partial y} $

Residual Block 의 Gradient : $ \frac{\partial L}{\partial y}  \frac{\partial y}{\partial f} \frac{\partial f}{\partial x} $

여기서 $ \frac{\partial y}{\partial f} $ 는 1 이고, Layer F 에 흘러들어가는 Upstream Gradient 는 $ \frac{\partial L}{\partial y} $ 그 자체이다. 이후, Layer F 에서 x 로 가는 Gradient 계산은 그냥 Backprop 계산을 수행하면 됨.

같은 변수 $ x $ 에서 가지친 Gradient 는 합쳐지기 때문에 두 Gradient 를 합하면 Downstream Gradient 가 된다.


코드 구현

Residual Block 은 기존 방식 그대로 Forward 에는 $ x $ 를 입력받아 $ y $ 를 출력하고, 역전파 시에 Upstream gradient $ \frac{\partial L}{\partial y} $ 를 입력받아 $ \frac{\partial L}{\partial x} $ 를 구하면 된다.

 

Forward 출력은 입력과 Residual Block 출력을 더한 값이 출력이고, Downstream Gradient 는 Upstream Gradient $ \frac{\partial L}{\partial y} $ 과 Residual Block 에서 내려온 Gradient 를 더해주어 Downstream 으로 흘려주면된다.

 

ResNet 을 구현하기 위해서 병렬 처리가 가능한 Layer 를 정의한다.

저장된 Layer 별로 동일한 입력에 대한 출력을 만들고, 이들을 Concatenate 로 묶어주고 출력한다.

ResNet 에서는 Layer 하나는 $ y = x $ 인 Layer, 다른 하나는 Residual Block 을 수행하는 Layer 으로 지정한다.

 

class ParallelLayer(Layer):
    def __init__(self, *args):
        self.layers = args
    
    def build(self, input_shape):
        for layer in self.layers:
            layer.build(input_shape)

    def forward(self, inputs):
        x = inputs
        branches = []
        for layer in self.layers[1:]:
            branches.append(layer.forward(inputs))

        y = branches[0]
        for branch in branches[1:]:
            y = np.c_[y, branch]

        return y
    
    def backprop(self, dLdy, optimizer):
        dLdx = self.layers[0].backprop(dLdy)
        for layer in self.layers[1:]:
            dLdx += layer.backprop(dLdy, optimizer)
        
        return dLdx

    
class ResNetLayer(ParallelLayer):
    def __init__(self, residual_block):
        skip_layer = IdentityLayer()
        super(ResNetLayer, self).__init__(skip_layer, residual_block)

 

 

 

'머신러닝 > CustomFramework' 카테고리의 다른 글

Batch Normalization  (0) 2022.04.03
Dropout  (0) 2022.04.03
L1 L2 Regularizer  (0) 2022.04.03
Convolution 2D Layer  (0) 2022.03.26
Dense (Fully Connect) Layer  (0) 2022.03.21