AI공부 · 선형대수

선형 변환과 행렬

3장. 선형 변환과 행렬

이 장을 마치면 행렬을 보고 “공간을 어떻게 변형하는가”를 떠올리고, 행렬-벡터 곱을 외우지 않고 원리로 계산할 수 있습니다. 선형대수에서 가장 중요한 장입니다.

3.1 변환 = 함수, 움직임으로 보기

변환(transformation)은 함수의 다른 이름입니다. 입력을 받아 출력 하나를 돌려주는 규칙입니다. 선형대수에서는 특히 벡터를 받아 벡터를 돌려주는 변환을 다룹니다.

L(v) = w   입력 벡터 v가 출력 벡터 w로 간다

같은 뜻인데 왜 굳이 “변환”이라고 부를까? 머릿속에 움직임을 떠올리라는 뜻입니다. 입력 벡터가 출력 벡터 자리로 이동한다고 상상합니다.

변환 전체를 이해하려면 모든 입력 벡터가 각자의 출력 자리로 동시에 움직이는 장면을 봐야 합니다. 화살표 수천 개를 한꺼번에 그리면 복잡하니, 2장에서 배운 요령대로 각 벡터를 끝점 하나로 봅니다. 그러면 변환이란 공간의 모든 점이 다른 점으로 옮겨 가는 것이 됩니다.

2차원에서는 무한히 펼쳐진 격자(모눈종이) 위의 점들이 움직이는 모습을 보면 변환의 전체 모양이 잘 보입니다. 원래 격자를 배경에 흐리게 남겨두면 무엇이 어디로 갔는지 비교하기 쉽습니다. 영상에서 공간 자체가 늘어나고 비틀리는 장면이 바로 이것입니다.

현장 비유. 고무판 위에 모눈을 그려놓고 잡아당기거나 비틀어 보는 것과 같습니다. 고무판의 모든 점이 동시에 새 자리로 갑니다.

3.2 선형 변환의 두 조건

아무 변환이나 허용하면 너무 복잡해집니다. 선형대수는 다루기 쉬운 특별한 종류, 선형 변환만 다룹니다. 그림으로 보면 조건은 두 가지입니다.

  1. 모든 직선은 변환 후에도 직선이다. 구부러지면 안 됩니다.
  2. 원점은 제자리에 있다. 원점이 움직이면 안 됩니다.
변환직선 유지원점 고정선형인가
격자선이 구불구불해짐아니오예아님
직선은 유지되지만 공간 전체가 평행 이동예아니오아님
가로·세로선은 곧은데 대각선이 휨아니오예아님
원점 중심 회전예예선형

세 번째 경우를 조심합시다. 가로·세로 격자선만 보면 곧아 보여도 대각선이 휘면 선형이 아닙니다. 직선이란 모든 방향의 직선을 말합니다.

한 줄 요약: 격자선이 평행하고 간격이 균일하게 유지되는 변환이 선형 변환입니다. 격자가 기울거나 늘어나거나 뒤집혀도, 격자선끼리 평행하고 같은 간격이면 됩니다.

원점 중심 회전처럼 말로 쉽게 설명되는 것도 있고, 비스듬히 늘어나는 것처럼 말로 설명하기 어려운 것도 있습니다. 그래서 다음 질문이 나옵니다. 선형 변환을 숫자로 기록하려면 무엇을 적어야 할까요?

3.3 î와 ĵ만 따라가면 된다

놀라운 답이 있습니다. 기저 벡터 î와 ĵ가 각각 어디에 도착하는지만 적어두면, 나머지는 전부 저절로 정해집니다.

예제. 벡터 v = [−1, 2]를 봅시다. 2장에서 배운 대로 이것은 선형결합 v = −1î + 2ĵ입니다.

어떤 선형 변환을 적용했더니 î는 [1, −2]에, ĵ는 [3, 0]에 도착했다고 합시다. 격자선이 평행하고 간격이 균일하게 유지되기 때문에 아주 중요한 결과가 나옵니다.

v는 “î를 −1배, ĵ를 2배”로 시작했으니, 변환 후에도 “도착한 î를 −1배, 도착한 ĵ를 2배”한 곳에 도착한다.

말하자면 선형결합의 “비율”은 변환을 거쳐도 그대로 유지됩니다. 재료(î, ĵ)만 바뀌고 레시피(−1, 2)는 그대로인 셈입니다.

î와 ĵ가 도착한 곳만 알면 v의 도착점이 정해진다변환 전변환 후îĵv = [−1, 2]선형 변환î → [1, −2]ĵ → [3, 0]v → [5, 2]−1 × [1, −2]2 × [3, 0]
선형 변환 전후 · v = −1î + 2ĵ → −1[1, −2] + 2[3, 0] = [5, 2]

계산하면 이렇습니다.

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

여기서 잠깐 멈추고 생각해볼 가치가 있습니다. 그림을 보면 v가 [5, 2]에 간 걸 바로 알 수 있습니다. 하지만 중요한 것은 변환을 직접 보지 않고도, î와 ĵ의 도착점 두 개만으로 어떤 벡터든 도착점을 계산할 수 있다는 사실입니다.

일반식으로. 아무 벡터 [x, y]는 “도착한 î를 x배 + 도착한 ĵ를 y배”에 갑니다.

x
y
→ x 1
−2
+ y 3
0
= 1x + 3y
−2x + 0y

이 공식 하나면 어떤 벡터를 주든 도착점을 바로 말할 수 있습니다. 결론적으로 2차원 선형 변환은 숫자 네 개로 완전히 기록됩니다. î의 도착 좌표 2개와 ĵ의 도착 좌표 2개입니다.

현장 비유. 도면 전체를 일정 비율로 늘리거나 기울이는 작업을 한다고 합시다. 기준 치수선 두 개(가로 1m, 세로 1m)가 어디로 갔는지만 알면, 도면 위 모든 점의 새 위치를 계산할 수 있습니다.

3.4 행렬 = 기저가 도착한 곳을 담은 상자

숫자 네 개를 2×2 격자에 담은 것을 2×2 행렬(matrix)이라고 합니다. 담는 규칙은 하나입니다. 첫째 열은 î가 도착한 곳, 둘째 열은 ĵ가 도착한 곳.

3.3의 변환이라면 î → [1, −2], ĵ → [3, 0]이므로 행렬은 이렇습니다.

13−20   1열 = î의 도착점, 2열 = ĵ의 도착점

행렬은 계산 대상이기 전에 “변환을 적어둔 기록지”입니다. 숫자 네 개가 공간 전체를 어떻게 움직일지 전부 담고 있습니다.

행렬-벡터 곱. 행렬과 벡터 [x, y]가 주어졌을 때, 그 벡터가 어디로 가는지 구하려면 3.3에서 한 그대로 합니다. 벡터의 각 좌표를 해당 열에 곱하고 더합니다.

13−20 −1
2
= −1 1
−2
+ 2 3
0
= 5
2

가장 일반적인 경우. 행렬 성분을 a, b, c, d로 둡시다. 첫째 열 [a, c]가 î의 도착점, 둘째 열 [b, d]가 ĵ의 도착점입니다.

abcd x
y
= x a
c
+ y b
d
= ax + by
cx + dy

행렬을 벡터 왼쪽에 쓰는 것은 함수 f(x)처럼 “이 변환을 벡터에 적용한다”는 뜻입니다.

외우지 말 것. 학교에서는 맨 오른쪽 결과식(ax + by, cx + dy)만 외우게 하는 경우가 많습니다. “1행과 벡터를 곱해 더하고, 2행과 벡터를 곱해 더한다”는 식입니다. 결과는 같지만 이유가 빠져 있습니다. 대신 이렇게 기억합시다.

행렬의 열 = 기저 벡터가 도착한 곳. 행렬 × 벡터 = 그 열들의 선형결합.

방법계산 순서머릿속 그림
열로 보기 (추천)x × 1열 + y × 2열도착한 기저를 x배, y배 해서 더하기
행으로 보기1행·벡터, 2행·벡터숫자 계산 절차 (그림 없음)

손으로 빨리 계산할 때는 행 방식이 편하기도 합니다. 다만 “무슨 일이 일어나는가”를 이해할 때는 언제나 열 방식으로 생각합니다.

3.5 연습: 변환 ↔ 행렬

예 1 — 반시계 방향 90° 회전. 공간 전체를 원점 중심으로 왼쪽으로 90° 돌립니다. î(오른쪽)는 위쪽으로 가서 [0, 1]에, ĵ(위쪽)는 왼쪽으로 가서 [−1, 0]에 도착합니다.

R90° = 0−110

아무 벡터나 90° 돌린 결과를 알고 싶으면 이 행렬을 곱하면 됩니다. 예: [2, 1] → 2·[0, 1] + 1·[−1, 0] = [−1, 2]. 오른쪽 위를 가리키던 화살표가 왼쪽 위로 돌아갔습니다.

예 2 — 전단(shear, 밀기). î는 그대로 두고, ĵ만 [1, 1]로 옮깁니다.

S = 1101

정사각형 격자가 오른쪽으로 비스듬히 밀려 평행사변형 격자가 됩니다. 책 더미를 옆에서 밀면 위쪽 책일수록 더 많이 밀리는 모양입니다. 바닥(x축)은 그대로이고 높이 y인 점은 오른쪽으로 y만큼 밀립니다.

예 3 — 행렬에서 변환 읽기. 거꾸로 행렬만 주고 변환을 상상해봅시다.

1321
  1. î를 [1, 2]로 옮깁니다 (오른쪽 위로 비스듬히)
  2. ĵ를 [3, 1]로 옮깁니다 (오른쪽으로 크게, 위로 조금)
  3. 나머지 격자는 평행하고 간격이 균일하게 따라옵니다

종이에 원래 격자를 그리고, 그 위에 [1, 2]와 [3, 1]을 두 변으로 하는 평행사변형 격자를 겹쳐 그려보면 변환이 보입니다.

예 4 — 공간이 찌그러지는 경우. î와 ĵ의 도착점이 선형 종속이면(한쪽이 다른 쪽의 배수) 무슨 일이 생길까요?

2−21−1   [−2, −1] = −1 × [2, 1]

두 도착점이 같은 직선 위에 있으니, 그 선형결합인 모든 벡터의 도착점도 그 직선 위에 있습니다. 2차원 평면 전체가 직선 하나로 납작하게 눌립니다. 2장의 언어로 말하면 출력은 두 종속 벡터의 1차원 스팬입니다. 이 “찌그러짐”은 뒤의 행렬식(determinant)과 역행렬 장에서 핵심이 됩니다.

대표 변환 한눈에 보기

변환행렬 (행 단위)î 도착ĵ 도착공간의 모습
아무것도 안 함 (항등)[[1, 0], [0, 1]][1, 0][0, 1]그대로
90° 회전[[0, −1], [1, 0]][0, 1][−1, 0]왼쪽으로 돌아감
전단[[1, 1], [0, 1]][1, 0][1, 1]오른쪽으로 비스듬히 밀림
2배 확대[[2, 0], [0, 2]][2, 0][0, 2]모든 방향으로 2배
찌그러짐[[2, −2], [1, −1]][2, 1][−2, −1]직선 하나로 납작

표의 [[1, 0], [0, 1]]은 행 단위로 적은 것입니다. 즉 1행이 [1, 0], 2행이 [0, 1]입니다.

3.6 정리와 AI와의 연결

3장 요약

  1. 선형 변환은 원점을 고정한 채, 격자선이 평행하고 간격이 균일하게 유지되도록 공간을 움직이는 것입니다.
  2. 선형 변환은 기저 벡터가 도착한 좌표만으로 완전히 기록됩니다. 2차원이면 숫자 4개입니다.
  3. 행렬은 그 좌표를 담는 언어입니다. 열 하나가 기저 벡터 하나의 도착점입니다.
  4. 행렬-벡터 곱은 “이 변환이 이 벡터를 어디로 보내는가”를 계산하는 방법입니다.

행렬을 볼 때마다 “공간을 어떻게 변형하는가”로 읽는다. 이것이 이 장의 핵심입니다.

이 생각에 익숙해지면 앞으로 나올 행렬 곱, 행렬식, 기저 변환, 고유값이 모두 훨씬 쉬워집니다. 다음 장은 행렬 두 개를 곱하는 의미, 즉 변환을 연달아 적용하는 것을 다룹니다.

AI에서는 이렇게 쓰입니다.

  1. 신경망 한 층 = 선형 변환 + 비선형 함수. PyTorch의 nn.Linear가 하는 일이 바로 행렬-벡터 곱입니다. 입력 벡터를 가중치 행렬로 변환한 뒤, 활성함수(ReLU 등)로 살짝 구부립니다. 선형 변환만 쌓으면 결국 선형 변환 하나와 같기 때문에 사이사이에 비선형을 넣습니다.
  2. 학습 = 행렬 숫자 맞추기. 모델 학습은 “어떤 변환을 해야 입력이 원하는 출력으로 가는가”를 찾는 일이고, 그 변환이 가중치 행렬의 숫자들로 기록됩니다. LLM의 수십억 개 파라미터는 대부분 이런 행렬 성분입니다.
  3. 트랜스포머의 Q, K, V. 어텐션은 단어 벡터 하나를 세 가지 행렬로 각각 변환해 “질문(Query)”, “열쇠(Key)”, “값(Value)” 벡터를 만듭니다. 같은 벡터를 서로 다른 관점의 공간으로 옮기는 것입니다.
  4. 이미지 증강. 비전AI 학습에서 사진을 돌리고 기울이고 늘이는 데이터 증강은 이 장의 회전·전단·확대 행렬을 픽셀 좌표에 곱하는 것입니다.

3.7 코드 실습

실습 1 — 행렬-벡터 곱 두 가지 방법으로 확인

import numpy as np

M = np.array([[1, 3],
              [-2, 0]])     # 1열 = î의 도착점 [1, -2], 2열 = ĵ의 도착점 [3, 0]
v = np.array([-1, 2])

print(M @ v)                            # [5 2]   행렬-벡터 곱 (@ 연산자)
print(v[0] * M[:, 0] + v[1] * M[:, 1])  # [5 2]   열의 선형결합으로 직접 계산

R90 = np.array([[0, -1], [1, 0]])
print(R90 @ np.array([2, 1]))           # [-1  2]  90° 회전

실습 2 — 격자가 변형되는 모습 그리기

원래 격자(회색)와 변환된 격자(파랑)를 겹쳐 그립니다. 행렬만 바꿔가며 실행해봅시다.

import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.family'] = 'AppleGothic'

def draw(M, title):
    fig, ax = plt.subplots(figsize=(6, 6))
    t = np.linspace(-4, 4, 100)
    for k in range(-4, 5):
        for line in (np.stack([np.full_like(t, k), t]),   # 세로선 x = k
                     np.stack([t, np.full_like(t, k)])):  # 가로선 y = k
            ax.plot(*line, color='lightgray', lw=0.8)       # 변환 전
            ax.plot(*(M @ line), color='tab:blue', lw=0.8)   # 변환 후
    i_new, j_new = M[:, 0], M[:, 1]
    ax.quiver(0, 0, *i_new, angles='xy', scale_units='xy', scale=1, color='tab:green', label='î 도착')
    ax.quiver(0, 0, *j_new, angles='xy', scale_units='xy', scale=1, color='tab:red', label='ĵ 도착')
    ax.set_xlim(-5, 5); ax.set_ylim(-5, 5); ax.set_aspect('equal')
    ax.set_title(title); ax.legend(); plt.show()

draw(np.array([[0, -1], [1, 0]]), '90° 회전')
draw(np.array([[1, 1], [0, 1]]),  '전단')
draw(np.array([[1, 3], [2, 1]]),  '[[1, 3], [2, 1]]')
draw(np.array([[2, -2], [1, -1]]), '찌그러짐: 직선 하나로')

실습 3 — 직접 해보기

  • 실습 2에서 회전 각도 θ를 바꿔보기: np.array([[np.cos(θ), -np.sin(θ)], [np.sin(θ), np.cos(θ)]]) (θ는 라디안, 예: np.pi/6은 30°)
  • 대각선 위 점들(np.stack([t, t]))도 변환해서 직선으로 유지되는지 확인
  • 찌그러지는 행렬을 하나 더 직접 만들어보기 (2열을 1열의 배수로)

3.8 용어 정리

한국어영어뜻
변환transformation벡터를 받아 벡터를 돌려주는 함수, 움직임으로 상상한다
선형 변환linear transformation원점 고정, 격자선이 평행·등간격으로 유지되는 변환
행렬matrix숫자를 직사각형으로 배열한 것. 열 = 기저의 도착점
열column행렬의 세로 줄
행row행렬의 가로 줄
행렬-벡터 곱matrix-vector multiplication벡터 좌표로 열들을 선형결합한 것
회전rotation원점 중심으로 돌리는 변환
전단shear한 축은 고정하고 비스듬히 밀어내는 변환
항등 행렬identity matrix아무것도 바꾸지 않는 변환 [[1, 0], [0, 1]]

3.9 연습문제

계산하기 전에 “î와 ĵ가 어디로 가는가”를 먼저 떠올리세요.

기본

  1. î가 [2, 1]에, ĵ가 [−1, 3]에 도착하는 선형 변환의 행렬을 써라.
  2. 1번 행렬로 벡터 [1, 2]를 변환하면 어디에 도착하는가? 열의 선형결합으로 계산하라.
  3. 반시계 방향 90° 회전으로 [3, −1]은 어디로 가는가?
  4. 공간을 x축 기준으로 위아래 뒤집는 변환의 행렬을 구하라.

응용

  1. [x, y]를 [x + 1, y]로 보내는 변환은 선형인가? 이유를 두 조건으로 설명하라.
  2. 시계 방향 90° 회전 행렬을 구하라.
  3. 행렬 [[1, 2], [2, 4]]은 공간을 어떻게 변형하는가? 벡터 [3, 1]은 어디로 가는가?
  4. 어떤 행렬 M이 [1, 0]을 [4, 1]로, [0, 1]을 [−2, 5]로 보낸다. M을 쓰고, M이 [2, −1]을 어디로 보내는지 구하라.
해설 펼치기
  1. 열에 도착점을 그대로 넣습니다. 1행 [2, −1], 2행 [1, 3], 즉 [[2, −1], [1, 3]].
  2. 1·[2, 1] + 2·[−1, 3] = [2 − 2, 1 + 6] = [0, 7].
  3. î → [0, 1], ĵ → [−1, 0]이므로 3·[0, 1] + (−1)·[−1, 0] = [1, 3]. 오른쪽 아래를 가리키던 화살표가 오른쪽 위로 돌아갔습니다.
  4. î는 그대로 [1, 0], ĵ는 아래로 뒤집혀 [0, −1]. 행렬은 [[1, 0], [0, −1]].
  5. 선형이 아닙니다. 직선은 직선으로 유지되지만(공간 전체를 오른쪽으로 1칸 옮기는 평행 이동), 원점 [0, 0]이 [1, 0]으로 움직입니다. 두 번째 조건을 어깁니다. 그래서 평행 이동은 2×2 행렬로 나타낼 수 없습니다.
  6. î(오른쪽)는 아래로 가서 [0, −1], ĵ(위)는 오른쪽으로 가서 [1, 0]. 행렬은 [[0, 1], [−1, 0]].
  7. 2열 [2, 4]는 1열 [1, 2]의 2배입니다. 두 도착점이 종속이라 평면 전체가 [1, 2] 방향의 직선 하나로 눌립니다. [3, 1] → 3·[1, 2] + 1·[2, 4] = [5, 10]. 실제로 [5, 10] = 5·[1, 2]라 그 직선 위에 있습니다.
  8. M = [[4, −2], [1, 5]]. M[2, −1] = 2·[4, 1] + (−1)·[−2, 5] = [8 + 2, 2 − 5] = [10, −3].

다음 장 예고: 4장 “행렬 곱과 합성 변환”. 두 변환을 연달아 적용하는 것이 왜 행렬 곱이 되는지 다룹니다.