잔차연결은 딥러닝의 구조를 딥하게 만드는 발전에 있어서 아주 중요한 역할을 해 왔고 지금도 디퓨전의 U-Net이나 트랜스포머를 비롯한 다양한 구조에서 필수적으로 사용되고 있는 구조입니다.



잔차연결이 도입된 이유는 딥러닝모델의 층을 깊게 쌓게 되면 뒤로 갈 수록 신호가 약해지는 문제 (vanishing gradient) 를 해결하기 위해서였습니다. 잔차연결을 통해 기존 CNN 의 구조를 크게 건드리지 않으면서 층을 깊게 깊게 쌓아도 안정적인 학습이 가능해진다는 것은 딥러닝 발전의 획기적인 돌파구였지요.


보통의 경우 잔차연결은 위의 그림처럼 기본적으로 존재하는 경로의 옆길로 연결을 추가하는 형태로 표현하곤 합니다. 


그런데 anthropic 의 blog 를 살펴보다가 잔차연결은 기존 연결에 부수적으로 따라붙은 존재가 아니라, 오히려 아무것도 하지 않는 잔차연결이 큰 줄기이고 뭔가를 변경하는 어텐션 레이어나 CNN 레이어, MLP 레이어등이 부수적인 줄기로 보는 것을 제안하고 있습니다. 이게 무슨 소리인걸까 함께 살펴보시죠

https://transformer-circuits.pub/2021/framework/index.html


위 블로그에서는 잔차연결이라는 표현 대신 residual stream 이라는 표현을 쓰고 있습니다. 트랜스포머를 조금 다르게 표현한 위의 그림을 보면 맨 아래에서부터 데이터의 흐름이 시작되어 토큰들이 임베딩 벡터들로 변환되고 (x0) 기본 줄기는 블럭들을 타고 올라가면서 x1, x2, x3, ... 식으로 벡터들의 값이 변화하는 것을 표현했습니다. h0, h1, ... 의 블럭들과 mlp 블럭 m 은 기본줄기에서 갈라져 나온 옆줄기로 옮겨가서 각각의 변환을 거친 다음 다시 원래 줄기로 돌아가는 구조를 하고 있습니다


굳이 게임 유닛으로 비유를 해보자면 메인 줄기는 스타크래프트에 등장하는 프로토스의 캐리어 본체의 역할을 하고 갈라져나온 줄기는 인터셉터 역할을 한다고 생각을 해봅니다

본체 자체는 하는 일이 없이 이동하기만 하지만 거기에서 튀어나온 여러개의 헤드들이 뭔가 작업을 수행하고 (어텐션 or mlp) 다시 그 성과를 싣고 본체로 돌아가는 느낌을 상상해보세요


트랜스포머의 상위 레벨 아키텍처의 주요 특징 중 하나는 각 레이어가 그 결과를 '잔차 스트림'이라고 부르는 것에 합산한다는 것입니다. 잔차 스트림은 단순히 이전 모든 레이어의 출력과 원래 임베딩의 합입니다. 잔차 스트림은 자체적으로는 어떤 처리도 하지 않지만 모든 레이어가 이를 통해 통신하기 때문에 통신 채널의 일반화된 형태로 생각할 수 있습니다.


잔차 스트림의 구조로 생각하게 되면 우리는 '가상 가중치' 라는 요소를 발견할 수 있게 됩니다. 명시적인 파라메터로 존재하진 않지만 실질적으로 가중치처럼 영향을 주는 요소입니다

서로 다른 레이어의 가중치들은 구조상으로는 떨어져 있지만 잔차스트림을 타고 올라가는 데이터에다 읽기, 쓰기 같은 작업을 할 수 있기 때문에 (왼쪽 그림), 하단의 레이어에 있던 웨이트 (오른쪽 그림의 W3W1)가 그 윗단에 있는 블럭의 가중치에 간접적으로 영향을 끼칠 수 있다는 것이지요.


이렇게 서로 다른 레이어의 가중치들이 협력해서 좀 더 복잡한 구조의 패턴을 파악할 수 있게 됩니다. tinystories 에서의 다양한 블럭갯수에 따른 실험결과 (어텐션 헤드를 늘리거나 임베딩의 차원수를 늘리는 것보다는 블럭을 깊이 쌓는 것이 성능향상에 더 도움이 됨) 나 모델의 블럭을 selfmerge 한 것만으로도 모델의 지능이 올라간 사례들을 보면 트랜스포머 블럭의 갯수가 절대적인 지능에 상당한 영향을 준다는 것을 확인할 수 있습니다. 조금 다르게 생각해보면, 트랜스포머의 레이어의 갯수와 지능은 어느정도 비례한다는 점에 동의할 수 있을 것입니다. 마치 쉐이더 프로그램을 짤 때 더 많은 명령어를 사용하면 더 복잡한 패턴을 표현할 수 있게 되는 것처럼요. 



기본적으로 잔차 스트림을 통해 올라가는 벡터들은 차원이 높은 벡터들입니다. 그리고 옆길로 가서 각각의 역할에 따라 연산을 수행하는 어텐션 레이어들은 그보다 실질적으로는 차원이 낮은 부분공간안에서 활동하는 벡터들입니다 (멀티헤드 어텐션 구조이기 때문에)

서로 다른 공간에 관심을 갖고 활동하는 어텐션 헤드들 (예: 문법을 따지는 헤드, 감정을 따지는 헤드) 은 서로 공간이 별로 겹치지 않기 때문에 각자 병렬적으로 잔차 스트림에 올라가는 데이터에 이런저런 변형을 가하게 됩니다. 한편으로는 아래 레이어에서 추가했던 어떤 정보가 위쪽의 레이어에서는 제거되는 경우도 있을 것입니다 예를 들면 아래쪽 레이어에서는 어떤 표현을 하려고 했는데, 위쪽 레이어에서는 이건 검열대상이다 라고 다른 정보 (as an language model, I cannot specify...) 로 드리프트를 줄 수도 있겠죠


저러한 잔차 스트림의 대상은 토큰 1개에 대해 표현한 것입니다. 실제로 언어모델이 문장을 처리할 때에는 여러개의 토큰을 대상으로 하는 만큼, 토큰 갯수만큼의 잔차 스트림이 생겨서 병렬적으로 쭉 올라가게 됩니다. 그런데 트랜스포머의 셀프어텐션에서는 자기보다 왼쪽에 있는 토큰의 흐름에 대해서는 어텐션 대상이 될 수가 있기 때문에 위의 그림처럼 토큰 A 의 흐름에서 만들어진 정보가 토큰 B 로 옮겨갈 수 있는 것을 볼 수 있습니다. 


이 글의 베이스가 된 원 블로그에 보면 위와 같은 잔차 스트림의 구조를 전제로 하고 다양하게 트랜스포머에 대한 해석을 전개하고 있습니다. 예를 들면 모든 트랜스포머 블럭들을 제거한다던가, 블럭을 1개만 남긴다던가, 블럭을 2개만 남긴다던가 하는 식으로 극단적인 형태의 모델을 만들어서 구조의 변화에 따른 모델 출력의 변화를 분석한 것이죠. 아주 중요한 글들이지만 내용이 방대해서 이 글에서는 그 전제에 해당하는 잔차 스트림에 대한 부분만 다뤄보았습니다.


마지막으로, 원 트랜스포머 모델 구조의 변화에 대해 잠깐 살펴보겠습니다. 


처음 2017년 트랜스포머 논문이 발표되었을 때에는 Post-LN 에 해당하는 구조였습니다만, 후에 실험을 통해 레이어 정규화의 위치를 변경한 버전이 더 학습이 잘된다는 것을 발견하게 됩니다. 이렇게 구조를 바꿔야 좋은 이유가 무엇인지를 좀 더 엄밀하게 분석한 논문은 2020년에 나오게 됩니다   https://arxiv.org/abs/2002.04745


이러한 분석의 근간에는 잔차연결을 부수적인 도구로 바라보던 시각에서 잔차 스트림이 안정적인 중심 통로이자 기준점으로 바라보는 발상의 전환이 있습니다.


이제 딥러닝 모델에 대한 글이나 그림을 볼 때 잔차연결이 나타나는 부분이 보이면 캐리어와 인터셉터를 연상하면서 잔차 스트림에서 뻗어나간 블럭이 일단 자세를 바르게 하고 (Layer Norm) 뭔가 패턴을 포착하고 읽고, 쓰고 하는 연산을 수행한 다음 그 결과를 메인 스트림에 가져다주는 모습을 연상해보면 어떨까요?


읽어주셔서 감사합니다.