행렬곱으로 읽는 딥러닝 레이어의 기본 구조 | DAKER 커뮤니티

딥러닝 모델을 이해할 때 가장 자주 마주치는 연산은 결국 벡터와 행렬입니다. 픽셀, 토큰 임베딩, 센서 값, 표의 한 행처럼 서로 다른 데이터도 모델 안에 들어오면 벡터가 되고, 가중치 한 층은 행렬이 됩니다. 그래서 레이어를 읽는 가장 빠른 길은 복잡한 이름보다 먼저, 어떤 벡터가 어떤 행렬을 만나 어디로 이동하는지 보는 데 있습니다.

선형대수를 시험 공식처럼만 기억하면 nn.Linear나 어텐션의 Q·K·V 투영이 추상적으로 남기 쉽습니다. 반대로 행렬곱을 여러 선형변환을 한 번에 적용하는 연산으로 이해하면, 모델 코드의 많은 줄이 같은 구조를 반복한다는 점이 선명해집니다. 해커톤과 대회에서 반복해서 보이는 패턴도 다르지 않습니다. 입력을 숫자 배열로 만들고, 선형층과 비선형층을 교차해 쌓는 방식입니다. 25만 빌더·339+ 대회 규모의 현장에서도 뼈대는 결국 벡터를 행렬로 옮기는 연산의 조합입니다.

행렬곱과 선형변환 썸네일

벡터는 모델이 다루는 기본 단위입니다

벡터는 순서가 있는 숫자열입니다. 이미지 패치의 RGB 통계, 문장 임베딩의 수백 차원 값, 표 데이터의 키·몸무게·나이 같은 특성도 모두 벡터로 볼 수 있습니다. 기하학적으로는 방향과 크기를 가진 화살표로 그릴 수 있고, 모델은 이 화살표를 받아 다른 화살표로 바꿉니다. 차원이 3이면 상상하기 쉽고 768이면 어렵지만, 연산 규칙 자체는 달라지지 않습니다.

내적은 두 벡터가 얼마나 같은 방향을 보는지 나타내는 점수입니다. 같은 방향이면 값이 커지고, 직교에 가까우면 0에 가까워지며, 반대 방향이면 음수가 됩니다. 코사인 유사도는 이 점수를 각 벡터의 길이로 나눈 정규화 버전입니다. 검색·추천·RAG에서 임베딩을 비교할 때 쓰는 연산이 여기에 속합니다. 어텐션의 점수 계산도 본질적으로는 질의 벡터와 키 벡터의 내적입니다.

노름은 벡터의 길이입니다. 가장 흔한 것은 L2 노름입니다. 기울기 클리핑에서 기울기 벡터의 노름을 자르는 이유도 한 스텝의 이동량이 지나치게 커지는 일을 막기 위해서입니다. 배치 정규화와 레이어 정규화 역시 활성화 벡터의 통계를 맞춰 학습을 안정화하는 방식으로, 결국 벡터를 다루는 연산입니다.

고차원 벡터도 결국 방향과 크기를 가진 화살표로 읽을 수 있습니다.

행렬은 벡터를 다른 벡터로 보내는 규칙입니다

행렬 W가 있을 때 출력 z = Wx는 입력 x의 각 성분을 가중합해 새로운 성분을 만든다는 뜻입니다. 기하적으로 보면 행렬은 스케일, 회전, 기울임, 투영 같은 선형변환을 표현합니다. 여기에 편향 b를 더하면 z = Wx + b가 되어 원점을 지나지 않는 평행이동까지 포함합니다. 완전연결 선형층이 실제로 하는 일이 바로 이것입니다.

행렬은 두 방향으로 읽을 수 있습니다. 열을 기준으로 보면 Wx는 W의 열벡터들을 x의 성분으로 가중합한 결과입니다. 입력의 각 특성이 어느 방향으로 얼마나 기여하는지가 열에 담깁니다. 반대로 행을 기준으로 보면 출력의 각 뉴런이 입력을 어떻게 가중하는지가 드러납니다. 같은 행렬을 두 시선으로 읽는 습관은 shape를 디버깅할 때도 도움이 됩니다.

중요한 점은 선형변환만 여러 번 합성하면 결과가 다시 하나의 큰 선형변환이 된다는 사실입니다. W₂(W₁x) = (W₂W₁)x이기 때문입니다. 그래서 층을 깊게 쌓더라도 중간에 비선형성이 없으면 표현력은 한 층과 본질적으로 다르지 않습니다.

한 행렬은 하나의 선형 맵이고, 선형층만 쌓으면 결국 하나의 큰 선형층으로 합쳐집니다.

행렬곱이 여러 선형변환의 합성임을 보여주는 다이어그램

행렬곱은 변환을 순서대로 이어 붙이는 연산입니다

행렬곱 AB는 먼저 B를 적용하고 다음에 A를 적용한다는 뜻입니다. 함수 합성 (f∘g)(x) = f(g(x))와 같은 순서 감각으로 읽으면 됩니다. 딥러닝에서 층을 쌓는다는 말은 곧 변환을 순서대로 쌓는다는 뜻입니다. 그리고 각 층 사이에 비선형 활성화를 넣어 전체가 하나의 행렬로 붕괴하지 않게 만듭니다.

배치 차원까지 포함하면 입력은 행렬 X가 됩니다. 예를 들어 배치×특성 형태의 입력에 대해 Z = XW처럼 한 번에 여러 샘플을 처리할 수 있습니다. GPU가 빠른 이유도 여기에 가깝습니다. 같은 규칙의 곱셈과 덧셈을 큰 배열에 병렬로 적용하기 때문입니다. 배치 크기를 키웠을 때 처리량이 오르는 현상이나 혼합 정밀도 학습의 속도 이점도 이런 행렬 연산 효율과 맞닿아 있습니다.

전치 Wᵀ는 행과 열을 바꾼 행렬입니다. 역전파에서는 앞으로 곱했던 행렬을 반대로 흘려 보낼 때 전치가 자주 등장합니다. 대칭 행렬이나 직교 행렬 같은 특수한 형태도 변환의 성질을 설명하는 데 쓰이지만, 여기서는 이름만 알아 두어도 충분합니다.

모델 코드에서도 같은 그림이 반복됩니다

완전연결층은 Wx+b 그 자체입니다. 합성곱은 작은 필터를 이미지의 국소 영역에 반복해 내적하는 형태로 이해할 수 있고, 국소 연결과 가중치 공유라는 제약이 붙은 선형연산입니다. 어텐션의 Q = XW_Q, K = XW_K, V = XW_V도 입력 시퀀스를 세 가지 선형변환으로 보낸 뒤, 유사도인 내적으로 가중치를 만들고 V를 가중합하는 구조입니다. 이름은 달라도 뼈대는 벡터, 행렬, 내적입니다.

임베딩 테이블도 행렬입니다. 토큰 id가 특정 행을 고르면 그 행 벡터가 토큰의 초기 표현이 됩니다. 이는 one-hot 벡터에 임베딩 행렬을 곱한 것과 같습니다. 언어모델의 출력 로짓도 마지막 은닉 벡터에 큰 행렬을 곱해 어휘 크기만큼의 점수를 한 번에 만드는 선형층으로 볼 수 있습니다. 분류 헤드의 Dense 역시 같은 가족입니다.

차원 숫자를 읽을 때도 같은 습관이 유용합니다. (배치, 토큰, d_model) 텐서가 Linear(d_model, d_ff)를 지나면 마지막 축만 d_ff로 바뀝니다. 마지막 축에 행렬이 곱해진다고 읽으면 shape 오류를 훨씬 빨리 찾을 수 있습니다. einsum 표기나 einops 정리는 이런 축 계약을 더 명시적으로 적는 도구입니다.

새 아키텍처를 만나도 먼저 볼 것은 이름이 아니라 벡터와 행렬의 흐름입니다.

작은 숫자로 보면 추상어가 그림이 됩니다

W = [[2, 0], [0, 0.5]], x = [1, 1]이라면 Wx = [2, 0.5]입니다. x축 방향은 늘어나고 y축 방향은 줄어든 결과입니다. 또 W = [[0, −1], [1, 0]]을 같은 x에 곱하면 회전의 감각을 볼 수 있습니다. 학습은 결국 W의 숫자를 바꿔 이런 늘리기, 줄이기, 기울이기를 데이터에 맞게 조정하는 과정으로 읽을 수 있습니다.

정규화, 고유값, 특이값 같은 개념은 그다음에 얹어도 됩니다. 고유값이나 특이값이 크면 그 방향으로 신호가 더 크게 증폭됩니다. PCA, SVD, 스펙트럼 정규화도 모두 행렬이 공간을 어떻게 늘리고 줄이는지에 대한 심화 주제입니다. 다만 모델 코드를 읽는 출발점으로는 먼저 Wx가 입력 화살표를 어떻게 옮기는지 붙잡는 편이 효율적입니다. 29억+ 규모의 데이터와 연산이 오가는 플랫폼에서도 디버깅의 첫 질문은 여전히 작은 행렬 예제에서 시작합니다.

이 글에서 먼저 가져갈 핵심

벡터는 특성, 임베딩, 활성화 값을 담는 화살표이고, 행렬은 그 화살표를 다른 화살표로 보내는 선형 규칙입니다. 행렬곱은 이런 규칙을 순서대로 합성하는 연산입니다. 선형층만 쌓이면 하나의 큰 선형층으로 합쳐지기 때문에 비선형성이 필요하고, Linear·합성곱·어텐션 투영·임베딩 행렬은 모두 같은 연산 가족으로 읽을 수 있습니다. shape를 볼 때는 어느 축에 어떤 행렬이 곱해지는지 먼저 적어 보면 됩니다.

이 글은 고유값 분해의 증명이나 모든 행렬 분해 기법을 다루는 교재는 아닙니다. 특정 프레임워크의 API 레퍼런스도 아닙니다. PyTorch·JAX·TensorFlow 모두에서 공통으로 작동하는 벡터·행렬·행렬곱의 직관만 다룹니다.

행렬곱은 입력 벡터를 여러 선형변환으로 밀어내고, 레이어를 읽는 일은 그 이동을 따라가는 일입니다.

다음에 레이어 코드를 볼 때, 어떤 벡터가 어떤 행렬을 만나 어떻게 바뀌는지부터 떠올려 보면 어떨까요?