AI공부 · 선형대수

비정사각 행렬

8장. 비정사각 행렬

이 장을 마치면 m×n 행렬을 보고 “n차원에서 m차원으로 가는 변환”을 떠올리고, 행렬 곱의 크기 규칙을 외우지 않고 설명할 수 있습니다. 영상은 장 사이의 짧은 보충 편이고, AI 코드에서 가장 자주 만나는 “shape 에러”를 이해하는 데 직결됩니다.

8.1 차원을 넘나드는 선형 변환

지금까지는 2차원 → 2차원(2×2 행렬), 3차원 → 3차원(3×3 행렬) 변환만 다뤘습니다. 하지만 차원이 다른 공간 사이의 변환도 얼마든지 자연스럽습니다. 예를 들어 2차원 벡터를 받아 3차원 벡터를 돌려주는 변환입니다.

선형이라는 조건은 그대로입니다. 격자선이 평행하고 간격이 균일하게 유지되고, 원점은 원점으로 갑니다.

입력과 출력은 서로 다른 공간입니다. 지금까지는 입력 벡터가 출력 자리로 “움직이는” 그림을 그렸습니다. 차원이 다르면 그렇게 그릴 수 없습니다. 2차원 입력과 3차원 출력은 완전히 별개의, 이어져 있지 않은 공간에 사는 다른 종류의 대상입니다. 그래서 입력 공간과 출력 공간을 따로 그립니다.

현장 비유. 2차원 평면도(입력)를 보고 3차원 건물의 한 면(출력)에 타일을 붙이는 것과 같습니다. 도면의 좌표는 숫자 2개지만 현장의 위치는 숫자 3개(가로·세로·높이)로 적습니다. 두 종이는 별개의 세계입니다.

8.2 3×2 행렬: 2차원 → 3차원

행렬로 적는 방법은 똑같습니다. 각 기저 벡터가 착지한 좌표를 열로 적습니다. 예를 들어 î가 [2, 1, −2]에, ĵ가 [0, 1, 1]에 착지하는 변환이라면:

A = 2011−21   열 2개(입력 기저 2개), 행 3개(도착점의 좌표 3개)

행이 3개, 열이 2개이므로 이런 행렬을 3×2 행렬이라고 부릅니다. 크기는 항상 “행 × 열” 순서로 말합니다.

2차원 입력이 3차원 속 평면(열공간)에 착지한다입력: 2차원 평면출력: 3차원 공간îĵ3×2 행렬 Axyzî → [2, 1, −2]ĵ → [0, 1, 1]색 평면 = 열공간 (랭크 2, 풀 랭크)
3×2 행렬이 2차원 평면을 3차원 속 평면으로 보내는 모습

7장의 언어로 보면

  • 열공간(모든 벡터가 착지하는 곳)은 3차원 공간의 원점을 지나는 평면입니다. 두 열 [2, 1, −2]와 [0, 1, 1]의 스팬이기 때문입니다.
  • 그래도 이 행렬은 풀 랭크입니다. 열공간의 차원(2)이 입력 공간의 차원(2)과 같아서, 입력의 어떤 차원도 눌려 사라지지 않았습니다. 2차원 평면이 펼쳐진 채로 3차원 속에 비스듬히 놓인 셈입니다.
  • 영공간은 영벡터뿐입니다(랭크 2 + 영공간 차원 0 = 열 2개).

계산도 똑같습니다. [x, y]가 착지하는 곳은 x × (1열) + y × (2열)입니다.

A 3
1
= 3 2
1
−2
+ 1 0
1
1
= 6
4
−5

입력은 숫자 2개, 출력은 숫자 3개입니다.

8.3 행렬 크기 읽는 법

행렬 크기만 보고도 어떤 변환인지 알 수 있습니다.

m×n 행렬 = n차원에서 m차원으로 가는 변환. 열의 개수 n = 입력 기저의 개수(입력 차원). 행의 개수 m = 도착점을 적는 좌표의 개수(출력 차원).

순서가 헷갈리면 이렇게 기억합니다. “출력 × 입력”. 행렬을 벡터 왼쪽에 쓰면 오른쪽에서 들어와(입력) 왼쪽으로 나가므로(출력), 크기도 오른쪽 숫자가 입력입니다.

크기변환기하학적 모습
2×22D → 2D평면을 평면으로 (3장)
3×33D → 3D공간을 공간으로 (5장)
3×22D → 3D평면을 3차원 속 평면으로 세움
2×33D → 2D3차원을 평면으로 누름 (그림자·투영)
1×22D → 1D평면을 수직선으로 (8.4)

2×3 행렬: 3차원 → 2차원. 열이 3개라 입력은 3차원(î, ĵ, k̂)이고, 행이 2개라 각 기저의 도착점은 좌표 2개로 적힙니다. 따라서 공간을 평면으로 누르는 변환입니다. 영상 표현대로 “직접 겪는다고 생각하면 몹시 불편한” 변환입니다.

100010 x
y
z
= x
y
   높이 z를 버리고 바닥에 그림자를 떨어뜨린다

현장 비유. 3차원 건물을 평면도(2차원)로 그리는 것이 바로 2×3 변환입니다. 층 높이 정보는 사라지고 바닥 위치만 남습니다. 드론 카메라가 3차원 세계를 2차원 사진으로 찍는 것도 (원근감을 빼면) 같은 종류입니다.

8.4 1×2 행렬: 2차원 → 수직선

1차원 공간은 사실상 수직선입니다. 그래서 2차원 → 1차원 변환은 2차원 벡터를 받아 숫자 하나를 돌려주는 함수입니다.

여기서 선형이란? 모든 것이 직선 하나로 눌려 “격자선이 평행하고 등간격”이라는 그림은 보기 어렵습니다. 대신 이렇게 판단합니다. 평면 위에 일직선으로 간격이 균일한 점들을 찍었을 때, 수직선에 내려앉은 점들도 간격이 균일해야 합니다.

행렬로 적기. 기저가 2개라 열이 2개이고, 각 기저의 도착점은 수직선 위의 숫자 하나라 각 열에 숫자가 하나뿐입니다. 즉 1×2 행렬입니다. 예를 들어 î가 2에, ĵ가 1에 내려앉는다면:

21 3
4
= 3 · 2 + 4 · 1 = 10

계산은 여전히 “좌표 × 도착점을 더하기”입니다. 도착점이 숫자라서 결과도 숫자 하나가 될 뿐입니다.

예고. 이 계산, “같은 자리끼리 곱해서 더하기”를 어디서 본 적이 있을 것입니다. [2, 1]과 [3, 4]의 내적(dot product)과 정확히 같습니다. 1×2 행렬과 내적의 관계는 다음 장의 주제이고, 생각보다 훨씬 깊은 의미가 있습니다.

8.5 비정사각 행렬로 다시 보는 앞의 개념들

영상은 “행렬 곱과 연립방정식을 차원이 다른 변환에서 생각해보라”는 숙제로 끝납니다. 하나씩 풀어봅시다.

1. 행렬 곱의 크기 규칙. AB는 B를 먼저 하고 A를 합니다(4장). 그러려면 B의 출력 차원 = A의 입력 차원이어야 합니다. B가 내놓은 벡터를 A가 받을 수 있어야 하기 때문입니다.

A(m×n) B(n×p) = (AB)(m×p)
p차원 →B n차원 →A m차원

가운데 두 숫자(n)가 같아야 하고, 결과는 바깥 두 숫자(m×p)입니다. 외울 필요 없이 “중간 차원을 거쳐가는 경유지”로 이해하면 됩니다. 예: (2×3)(3×4) = 2×4는 가능하지만, (3×2)(3×2)는 불가능합니다(2차원 출력을 3차원 입력에 넣을 수 없음).

2. 행렬식은 정사각 행렬에만 있습니다. 행렬식은 “넓이가 몇 배가 되는가”였습니다. 2차원 넓이와 3차원 부피처럼 서로 다른 공간의 크기는 비교할 수 없으니, 비정사각 행렬에는 행렬식이 정의되지 않습니다.

3. 역행렬도 없습니다.

  • 3×2(2D → 3D): 출력이 3차원 속 평면뿐이라 평면 밖의 3차원 벡터는 되돌릴 곳이 없습니다.
  • 2×3(3D → 2D): 공간이 눌려 정보가 사라지므로(7장) 되돌릴 수 없습니다.
  • 대신 “가장 가까운 답”을 주는 유사 역행렬이 있습니다(뒤에서 다룸).

4. 랭크의 한계. 랭크는 입력 차원보다도, 출력 차원보다도 클 수 없습니다. 즉 랭크 ≤ min(m, n). 3×2는 최대 2, 2×3도 최대 2입니다.

5. 연립방정식.

행렬식과 변수기하학적 상황해
3×2 (키 큰)식 3개 > 변수 2개출력이 3D 속 평면뿐v가 그 평면 위일 때만 있음. 대부분 없음 → 최소제곱
2×3 (넓적한)식 2개 < 변수 3개3D가 평면으로 눌림, 영공간이 최소 직선있으면 무수히 많음

7장 7.8에서 말한 “데이터 1,000건으로 계수 5개를 맞추는 회귀”는 1000×5 행렬, 즉 아주 키 큰 행렬입니다. 그래서 최소제곱이 필요했습니다.

8.6 AI와의 연결

딥러닝의 행렬은 대부분 비정사각입니다. 이 장이 실무 코드와 가장 직접 맞닿는 장입니다.

  1. nn.Linear(입력, 출력)의 가중치는 출력×입력. PyTorch nn.Linear(784, 128)은 784차원을 128차원으로 보내는 변환이고, 가중치 행렬 크기는 128×784입니다. 8.3의 “출력 × 입력” 규칙 그대로입니다. 5장에서 미루어둔 물음의 답입니다.
  2. 신경망 = 차원을 오가는 변환의 연쇄. MNIST 숫자 분류기라면 784(28×28 픽셀) → 128 → 64 → 10(숫자 0~9 점수)처럼 층마다 차원을 바꿉니다. 각 층이 비정사각 행렬이고, 8.5의 크기 규칙 덕분에 앞 층의 출력 차원과 다음 층의 입력 차원이 맞아야 연결됩니다.
  3. 가장 흔한 에러: shape 불일치. 딥러닝 코드에서 가장 자주 보는 에러가 mat1 and mat2 shapes cannot be multiplied (32x512 and 256x128) 같은 메시지입니다. “앞 변환의 출력 차원(512)과 다음 변환의 입력 차원(256)이 다르다”는 뜻입니다. 이 장을 이해하면 에러 메시지만 보고 어느 층을 고칠지 바로 알 수 있습니다.
  4. 차원 축소와 확장. 트랜스포머의 피드포워드 층은 768 → 3072 → 768처럼 먼저 넓혔다가(3072×768, 키 큰 행렬) 다시 좁힙니다(768×3072, 넓적한 행렬). 임베딩을 더 작은 차원으로 압축하는 것은 2×3처럼 누르는 변환이고, 그만큼 정보가 사라집니다.
  5. 점수 하나를 내는 1×n 행렬. 설비 센서 n개를 받아 “고장 위험 점수” 하나를 내는 선형 모델은 1×n 행렬, 즉 8.4의 “벡터 → 숫자” 변환입니다. 다음 장의 내적과 직결됩니다.

8.7 코드 실습

실습 1 — 세 가지 비정사각 변환

import numpy as np

A = np.array([[2, 0],
              [1, 1],
              [-2, 1]])            # 3×2: 2D → 3D
print(A.shape, A @ [3, 1])         # (3, 2) [ 6  4 -5]
print(np.linalg.matrix_rank(A))    # 2 → 풀 랭크

P = np.array([[1, 0, 0],
              [0, 1, 0]])          # 2×3: 3D → 2D (높이를 버리는 투영)
print(P @ [5, -1, 7])              # [ 5 -1]

L = np.array([[2, 1]])             # 1×2: 2D → 수직선
print(L @ [3, 4])                  # [10]

# 선형성 확인: 등간격 점들은 등간격 숫자로
pts = np.array([[0, 0], [1, 1], [2, 2], [3, 3]])
print((pts @ L.T).ravel())         # [0 3 6 9] → 간격 3으로 균일

실습 2 — 크기가 안 맞으면 에러

try:
    A @ A                          # (3×2)(3×2): 2D 출력을 3D 입력에 넣으려 함
except ValueError as e:
    print('크기 불일치:', e)

print((P @ A).shape)               # (2×3)(3×2) = 2×2  가능
print((A @ P).shape)               # (3×2)(2×3) = 3×3  가능. 하지만 랭크는?
print(np.linalg.matrix_rank(A @ P))  # 2 → 3×3인데 풀 랭크가 아니다

마지막 줄이 흥미롭습니다. AP는 3차원 → 2차원 → 3차원을 거치는데, 중간에 2차원을 지나며 한 차원이 사라져 다시 되살아나지 않습니다. 그래서 3×3이지만 랭크는 2입니다. 4장 LoRA의 “좁은 공간을 거쳐가는 두 단계 합성”이 바로 이 구조입니다.

실습 3 — 신경망 층을 numpy로 흉내 내기

rng = np.random.default_rng(0)
batch = rng.standard_normal((32, 784))   # 이미지 32장, 각각 784차원
W = rng.standard_normal((128, 784))      # nn.Linear(784, 128)의 가중치: 출력×입력
b = np.zeros(128)                        # 편향(bias)

out = batch @ W.T + b                    # 각 행(이미지)에 W를 적용
print(out.shape)                         # (32, 128)
print(W.size + b.size)                   # 100480 ← 학습할 숫자 개수

# PyTorch라면 같은 일을 이렇게 쓴다
# import torch.nn as nn
# layer = nn.Linear(784, 128)
# print(layer.weight.shape)   # torch.Size([128, 784])

데이터를 한 행에 하나씩 쌓으면(32×784) 가중치를 전치(W.T, 784×128)해서 오른쪽에 곱합니다. 크기 규칙 (32×784)(784×128) = 32×128이 딱 맞습니다.

8.8 용어 정리

한국어영어뜻
비정사각 행렬non-square matrix행과 열의 개수가 다른 행렬. 차원이 다른 공간 사이의 변환
m×n 행렬m-by-n matrix행 m개, 열 n개. n차원 → m차원
수직선number line1차원 공간
투영projection높은 차원을 낮은 차원으로 누르는 변환(그림자)
전치transpose, Aᵀ행과 열을 맞바꾼 행렬. m×n → n×m (뒤에서 자세히)
shapeshape코드에서 행렬(텐서)의 크기. 예: (32, 784)

8.9 연습문제

기본

  1. î → [1, 0, 2], ĵ → [0, 1, −1]인 변환의 행렬을 쓰고 크기를 말하라. [3, 2]는 어디로 가는가?
  2. 4×7 행렬은 몇 차원에서 몇 차원으로 가는 변환인가?
  3. 1×2 행렬 [3, −1]로 [2, 5]를 변환하면 어떤 숫자가 되는가?
  4. 다음 곱 중 가능한 것과 결과 크기를 말하라. (a) (2×3)(3×4) (b) (3×2)(3×2) (c) (1×5)(5×1) (d) (5×1)(1×5)

응용

  1. 3×2 행렬에는 왜 역행렬이 없는지 그림으로 설명하라.
  2. 풀 랭크인 2×3 행렬의 영공간은 몇 차원인가? [[1, 0, 0], [0, 1, 0]]의 영공간을 구하라.
  3. nn.Linear(784, 128)의 가중치 행렬 크기와, 편향까지 포함한 학습 파라미터 개수를 구하라.
  4. 에러 mat1 and mat2 shapes cannot be multiplied (32x512 and 256x128)을 봤다. 무엇이 문제이고 어떻게 고치는가?
해설 펼치기
  1. 행 단위로 [[1, 0], [0, 1], [2, −1]], 3×2 행렬입니다. [3, 2] → 3·[1, 0, 2] + 2·[0, 1, −1] = [3, 2, 4].
  2. 열 7개 = 입력 7차원, 행 4개 = 출력 4차원. 7차원 → 4차원.
  3. 2·3 + 5·(−1) = 1.
  4. (a) 가능, 2×4. (b) 불가능. 오른쪽이 내놓는 2차원을 왼쪽이 3차원으로 받을 수 없습니다. (c) 가능, 1×1(숫자 하나, 내적). (d) 가능, 5×5(랭크 1인 행렬).
  5. 3×2의 출력은 3차원 속 평면뿐입니다. 역행렬은 모든 3차원 벡터를 되돌려야 하는데, 평면 밖의 벡터는 처음부터 어느 입력에서도 오지 않았으니 돌려보낼 곳이 없습니다.
  6. 랭크 2 + 영공간 차원 = 열 3개이므로 영공간은 1차원(직선)입니다. [[1, 0, 0], [0, 1, 0]]은 [x, y, z]를 [x, y]로 보내므로, x = y = 0인 벡터 [0, 0, t], 즉 z축 전체가 영공간입니다. 높이 정보가 사라지는 방향입니다.
  7. 출력×입력 = 128×784. 가중치 128 × 784 = 100,352개 + 편향 128개 = 100,480개.
  8. 앞에서 나온 데이터는 512차원인데, 다음 층은 256차원 입력을 기대합니다. 다음 층을 nn.Linear(512, 128)로 고치거나, 앞 층의 출력을 256으로 맞춥니다. “가운데 두 숫자가 같아야 한다”는 규칙을 어긴 것입니다.

다음 장 예고: 9장 “내적과 쌍대성”. 1×2 행렬과 내적이 사실 같은 것이라는 놀라운 연결을 다룹니다. LLM의 어텐션과 임베딩 유사도가 모두 내적입니다.