행렬 노름은 “행렬이 벡터를 얼마나 늘리는가”의 최악값이다. 조건수는 “입력 오차가 해에 얼마나 증폭되는가”의 상한이다. 이 포스트는 Strang §9.2 를 뼈대로 삼아 벡터 노름 → 행렬 노름의 공리 → 연산자 노름의 정의 → Rayleigh 몫으로 \(\|A\| = \sigma_{\max}\) 유도 → 조건수 \(c = \|A\|\|A^{-1}\|\) → 상대오차 부등식의 증명까지를 한 줄기로 정리한다. SVD의 타원체 기하, “\(\log c\) 자리수 손실” 규칙의 의미, 실무 회귀·역문제·PageRank에서의 함의도 함께 다룬다.
Mathematics
Machine Learning
저자
Kwangmin Kim
공개
2026년 04월 13일
1 개요
선형 시스템 \(\mathbf{A}\mathbf{x} = \mathbf{b}\) 를 풀 때 입력 \(\mathbf{b}\) 에 작은 오차 \(\Delta \mathbf{b}\) 가 있으면 해 \(\mathbf{x}\) 의 오차 \(\Delta \mathbf{x}\) 는 얼마나 되는가? 답은 한 문장이다.
이 부등식이 수치 선형대수의 핵심 등식이고, 여기에 등장하는 두 양 \(\|\mathbf{A}\|\) (행렬 노름)과 \(\kappa(\mathbf{A})\) (조건수)가 이 포스트의 주인공이다.
직관적으로
행렬 노름\(\|\mathbf{A}\|\) = “행렬 \(\mathbf{A}\) 가 벡터를 얼마나 늘리는가”의 최악 비율 (늘림의 최댓값)
조건수\(\kappa(\mathbf{A})\) = “늘림의 최댓값 대 최솟값 비율” (얼마나 찌그러져 있는가)
이 둘이 작으면 문제는 안정적이고, 크면 같은 입력 오차라도 해가 크게 흔들린다.
이 포스트는 Strang (2009) Introduction to Linear Algebra Ch.9 §9.2 “Norms and Condition Numbers” 의 논리 흐름을 따르되, Rayleigh 몫과 SVD의 기하학적 해석, 실무에서 자주 마주치는 조건수 계산과 “\(\log \kappa\) 자리수 손실” 규칙까지 포함해 한 편에서 완결되도록 정리한다.
2 벡터 노름에서 시작한다
행렬 노름을 논하기 전에 벡터 노름의 공리를 확인한다. 벡터 \(\mathbf{x} \in \mathbb{R}^n\) 의 노름 \(\|\mathbf{x}\|\) 은 다음 세 성질을 만족하는 함수다.
가장 흔한 예는 2-노름(유클리드 길이)\(\|\mathbf{x}\|_2 = \sqrt{\sum x_i^2}\) 이다. 이하 기본 노름은 2-노름으로 고정한다.
직관: 세 공리가 말하는 것
(1)은 “길이는 0 이상이고 0이면 영벡터” — 길이의 기본. (2)는 “벡터를 \(c\) 배 늘리면 길이도 \(|c|\) 배” — 방향에 무관한 스케일. (3)은 “두 변의 합은 제3변보다 크다” — 유클리드 기하의 삼각형 부등식 그 자체다. 이 세 조건을 만족하면 “길이 비슷한 무언가”로 인정한다. 2-노름 외에 \(\ell^1\)(절댓값 합), \(\ell^\infty\)(최댓값) 등 무수한 변형이 같은 공리를 따른다.
3 행렬 노름: 크기 측정에 필요한 추가 공리
행렬은 “공간을 변환하는 연산자”이므로, 벡터 노름 공리 외에 곱셈 호환성 이 필요하다. 행렬 노름 \(\|\mathbf{A}\|\) 은 다음을 만족해야 한다.
벡터 노름의 세 공리 (양정치성, 스케일 동차성, 삼각 부등식)
성장 조건 (Strang의 식 (2)): \[
\|\mathbf{A}\mathbf{x}\| \leq \|\mathbf{A}\| \cdot \|\mathbf{x}\|, \qquad \|\mathbf{A}\mathbf{B}\| \leq \|\mathbf{A}\| \cdot \|\mathbf{B}\|
\]
첫 부등식은 “\(\mathbf{A}\) 가 벡터 \(\mathbf{x}\) 의 길이를 얼마까지 늘릴 수 있는가”를 \(\|\mathbf{A}\|\) 로 상한을 둔다. 두 번째는 행렬 곱의 노름이 각각의 곱을 넘지 않는다는 보장이다.
이는 \(\mathbf{A}\) 를 길이 \(mn\) 짜리 긴 벡터로 보고 2-노름을 잰 것이다. 계산이 간단하고 유용한 경우가 많지만, 행렬 곱의 성장 조건을 가장 타이트하게 반영하지는 못한다. 구체적으로 \(\|\mathbf{A}\mathbf{x}\|_2 \leq \|\mathbf{A}\|_F \cdot \|\mathbf{x}\|_2\) 는 성립하지만, 이 상한이 느슨할 수 있다.
3.2 연산자 노름 (Operator Norm): 정의
더 정확한 행렬 크기는 “벡터를 가장 많이 늘릴 때의 비율” 로 정의한다. Strang의 식 (3):
로 정의된다. 즉 단위 구 위의 모든 \(\mathbf{x}\) 에 대해 \(\mathbf{A}\) 를 적용했을 때 얻을 수 있는 최대 길이다.
정의에서 바로 \(\|\mathbf{A}\mathbf{x}\| \leq \|\mathbf{A}\|\cdot\|\mathbf{x}\|\) 이 따라 나온다 (최댓값의 성질). 또한 \(\|\mathbf{A}\mathbf{B}\mathbf{x}\| \leq \|\mathbf{A}\|\cdot\|\mathbf{B}\mathbf{x}\| \leq \|\mathbf{A}\|\cdot\|\mathbf{B}\|\cdot\|\mathbf{x}\|\) 에서 \(\|\mathbf{A}\mathbf{B}\| \leq \|\mathbf{A}\|\cdot\|\mathbf{B}\|\) 도 따라 나온다 (Strang §9.2 Problem 3).
직관: 연산자 노름은 “가장 긴 늘림”
행렬은 단위 구 \(\{\mathbf{x} : \|\mathbf{x}\|_2 = 1\}\) 를 타원체로 보낸다. 이 타원체의 가장 긴 반축의 길이가 \(\|\mathbf{A}\|\) 다. 즉 \(\mathbf{A}\) 가 할 수 있는 “최대 스트레칭”의 크기이다.
\(\mathbf{A} = \mathbf{I}\) (항등): 구 → 구. 반축 1. \(\|\mathbf{I}\| = 1\).
\(\mathbf{A} = \mathbf{Q}\) (직교): 구 → 구 (회전·반사만). \(\|\mathbf{Q}\| = 1\).
예 2: 대각 행렬. \(\mathbf{A} = \text{diag}(d_1, \dots, d_n)\) 의 노름은 \(\|\mathbf{A}\| = \max_i |d_i|\) 이다. 이 최댓값은 해당 \(d_i\) 에 대응하는 표준기저 \(\mathbf{e}_i\) 에서 달성된다: \(\mathbf{A}\mathbf{e}_i = d_i \mathbf{e}_i\).
이 오른쪽이 바로 대칭 행렬 \(\mathbf{M} = \mathbf{A}^\top \mathbf{A}\) 에 대한 Rayleigh 몫이다. \(\mathbf{A}^\top \mathbf{A}\) 는 항상 대칭 반양정치(symmetric positive semidefinite)이므로 스펙트럼 분해 \(\mathbf{A}^\top \mathbf{A} = \sum \lambda_i \mathbf{q}_i \mathbf{q}_i^\top\) 를 갖는다. \(\mathbf{x} = \sum c_i \mathbf{q}_i\) 로 분해하면 직교성 \(\mathbf{q}_i^\top \mathbf{q}_j = \delta_{ij}\) 에 의해
고유값은 둘 다 0이지만 \(\mathbf{A}\begin{pmatrix}0\\1\end{pmatrix} = \begin{pmatrix}2\\0\end{pmatrix}\) 이므로 이 벡터의 늘림은 2이다. 실제로 \(\mathbf{A}^\top \mathbf{A} = \text{diag}(0, 4)\) 이고 \(\lambda_{\max}(\mathbf{A}^\top\mathbf{A}) = 4\), 따라서 \(\|\mathbf{A}\| = 2\).
직관: 왜 \(\mathbf{A}^\top \mathbf{A}\) 로 가는가
비대칭 행렬은 벡터를 “밀고 회전시킨다”를 동시에 한다. 고유값은 “특정 방향이 스케일만 받을 때의 배율”이라, 회전이 섞이면 최대 늘림을 놓친다. 반면 \(\mathbf{A}^\top \mathbf{A}\) 는 대칭이므로 회전을 제거한 “순수 스트레칭”만 남는다 — \(\mathbf{A}\) 로 한 번 늘린 뒤 \(\mathbf{A}^\top\) 으로 되돌리는 과정에서 회전이 상쇄되기 때문이다. 이 대칭화된 행렬의 최대 고유값이 원래 \(\mathbf{A}\) 의 최대 늘림 제곱이다.
4.3 SVD와의 일치
\(\mathbf{A} = \mathbf{U} \boldsymbol{\Sigma} \mathbf{V}^\top\) (SVD) 에서 \(\mathbf{A}^\top \mathbf{A} = \mathbf{V} \boldsymbol{\Sigma}^2 \mathbf{V}^\top\) 이므로 \(\mathbf{A}^\top \mathbf{A}\) 의 고유값은 \(\sigma_i^2\), 따라서
\[
\|\mathbf{A}\| = \sigma_{\max}
\]
이 공식이 노름을 SVD와 연결하는 다리다. 단위 구 → 타원체 변환의 가장 긴 반축이 정확히 \(\sigma_{\max}\) 이고, 이것이 노름의 기하학적 의미다.
5 조건수: 오차의 증폭률
5.1 동기: \(\|\mathbf{A}^{-1}\|\) 만으로는 부족하다
\(\mathbf{A}\mathbf{x} = \mathbf{b}\) 에서 \(\mathbf{b}\) 가 \(\mathbf{b} + \Delta \mathbf{b}\) 로 바뀌면 해는 \(\mathbf{x} + \Delta \mathbf{x}\) 로 바뀐다. 두 방정식을 빼면 Strang의 식 (5):
정확히 \(\kappa = 3\). 부등식의 상한이 실제로 달성된다는 증거다. 조건수는 최악의 방향에서 빡빡한(tight) 상한이다 (Strang §9.2 Example 4).
6.3 예시 3: Hilbert 행렬 — 악조건의 교과서
\(\mathbf{H}_{ij} = 1/(i + j - 1)\). \(n = 5\) 에서 \(\kappa_2 \approx 4.8 \times 10^5\), \(n = 10\) 에서 \(\kappa_2 \approx 1.6 \times 10^{13}\). double precision으로는 \(n \geq 12\) 에서 사실상 풀 수 없다. 다항 회귀의 Vandermonde 행렬, 푸리에 기저의 이산화 등이 유사한 악조건을 보인다.
Ridge 회귀 \((\mathbf{X}^\top \mathbf{X} + \lambda \mathbf{I}) \boldsymbol{\beta} = \mathbf{X}^\top \mathbf{y}\) 에서 새 행렬의 고유값은 \(\sigma_i^2 + \lambda\) 이다 (\(\sigma_i\) 는 \(\mathbf{X}\) 의 특이값). 따라서
\(\sigma_{\min} \to 0\) 일 때 \(\lambda\) 가 없으면 조건수가 폭증하지만, \(\lambda > 0\) 이면 분모에 바닥이 생겨 조건수가 제한된다. Ridge의 통계적 해석(편의-분산 트레이드오프)과 별개로, 수치적 안정화라는 독립된 효과가 있다.
7.2 구체적 사례: 딥러닝 초기화
신경망 가중치 \(\mathbf{W}\) 의 조건수가 크면 초기 학습이 불안정하다. He 초기화, Xavier 초기화는 가중치의 분산을 조절하여 층별 활성화의 조건수를 보존하는 것이 목표다. 직교 초기화(orthogonal init)는 한 걸음 더 나아가 \(\mathbf{W}\) 를 직교 행렬로 시작시켜 초기 \(\kappa = 1\) 을 보장한다.
8 코드 예시
8.1 Step 1: 순수 Python — 노름과 조건수의 관계
코드
import numpy as npdef spectral_norm(A):"""연산자 2-노름 = 최대 특이값 = sqrt(max eigenvalue of A^T A)"""return np.sqrt(np.max(np.linalg.eigvalsh(A.T @ A)))def cond_number(A):"""조건수 = sigma_max / sigma_min""" s = np.linalg.svd(A, compute_uv=False)return s[0] / s[-1]# 예시 1: SPD 대각A1 = np.diag([6.0, 2.0])print(f"||A1|| = {spectral_norm(A1):.4f} (이론: 6)")print(f"kappa(A1) = {cond_number(A1):.4f} (이론: 3)")# 예시 2: 비대칭, 고유값은 0이지만 노름은 0 아님A2 = np.array([[0.0, 2.0], [0.0, 0.0]])print(f"||A2|| = {spectral_norm(A2):.4f} (이론: 2)")print(f"eigvals(A2) = {np.linalg.eigvals(A2)} (둘 다 0)")# 예시 3: HilbertH = np.array([[1.0/(i+j+1) for j inrange(10)] for i inrange(10)])print(f"kappa(H_10) = {cond_number(H):.2e} (double로 풀기 어려움)")
8.2 Step 2: 최악의 오차 방향 실험
코드
import numpy as npA = np.diag([6.0, 2.0])b = np.array([1.0, 0.0]) # 첫 고유벡터 방향x = np.linalg.solve(A, b) # x = (1/6, 0)eps =1e-6delta_b_worst = np.array([0.0, eps]) # 두 번째 고유벡터 — 최악 방향delta_b_best = np.array([eps, 0.0]) # 첫 고유벡터 — 최선 방향dx_worst = np.linalg.solve(A, delta_b_worst)dx_best = np.linalg.solve(A, delta_b_best)kappa = np.linalg.cond(A)print(f"kappa(A) = {kappa}")amp_worst = (np.linalg.norm(dx_worst)/np.linalg.norm(x)) / (np.linalg.norm(delta_b_worst)/np.linalg.norm(b))amp_best = (np.linalg.norm(dx_best )/np.linalg.norm(x)) / (np.linalg.norm(delta_b_best )/np.linalg.norm(b))print(f"최악 방향 증폭률: {amp_worst} (이론 kappa = 3)")print(f"최선 방향 증폭률: {amp_best } (이론 1)")
이 코드의 핵심: 조건수는 최악의 방향에서의 증폭률이다. 입력 오차가 운 좋게 “긴 축” 방향에 정렬되면 증폭되지 않지만, 가장 작은 특이값 방향에 정렬되면 \(\kappa\) 배 증폭된다.
\(\lambda\) 가 0일 때 조건수는 \(\sigma_{\min}^{-2}\) 수준으로 폭증하지만, \(\lambda = 10^{-3}\) 만 넣어도 \(\sigma_{\min}^2 + \lambda \approx \lambda\) 로 바닥이 생겨 조건수가 극적으로 낮아진다.