TL;DR — rank는 변환 뒤에도 살아남은 독립 방향의 수이고, 행렬은 rank가 일 때만 역행렬을 갖습니다. 방향이 눌려 서로 다른 입력이 같은 출력으로 겹치는 순간 되돌리기는 불가능해집니다.
왜 배우나요? — 행렬이 서로 다른 입력을 같은 출력으로 겹치게 만들면, 원래 입력을 되찾을 수 없습니다. rank와 역행렬은 변환이 정보를 살렸는지, 되돌리는 기계가 존재하는지를 알려 줍니다.
AI 어디에 쓰이나요? — 데이터·임베딩이 실제로 몇 개의 독립 방향을 쓰는지 진단하고, LoRA처럼 일부러 낮은 rank를 사용해 모델을 가볍게 만들 때 쓰입니다.
rank — 남아 있는 독립 방향의 수
행렬은 벡터를 다른 벡터로 바꾸는 변환입니다. 이때 rank(랭크)는 변환 결과가 몇 개의 독립적인 방향을 차지하는지를 나타냅니다. 행렬의 rank는 최대 이고, 가능한 최대값에 도달하면 full rank라고 부릅니다.
예를 들어 3차원 공간에는 가로·세로·깊이 세 방향이 있습니다. 이 공간을 바닥의 2차원 평면에 투영하면 가로·세로는 남지만 깊이 방향은 사라집니다. 출력이 2차원 평면 전체를 차지하므로 이 투영의 rank는 2입니다. rank가 단순한 행·열 개수가 아니라 변환 뒤에도 실제로 살아 있는 방향의 수라는 뜻입니다.
3차원에서 깊이 가 사라지면, 서로 다른 두 점이 같은 그림자 로 겹칩니다
버튼을 [3D 물체] → [그림자] → [겹침] 순서로 눌러 보세요.
[3D 물체]에서 파란 점 과 잉크색 점 는 바닥 격자의 같은 위에 수직으로 놓입니다. 깊이 만 만큼 다르므로 아직 서로 다른 입력입니다.
[그림자]에서는 두 점에서 바닥으로 내려가는 점선과 중심이 같은 두 테두리를 보세요. 투영은 를 버리므로 두 그림자의 거리는 0입니다. 아직 출력 이름과 복원 결론은 붙이지 않고 기하 관계만 확인합니다.
[겹침]에서는 그 공통 그림자를 출력 로 읽습니다. 이제 만 보고 원래 입력을 복원하려 하면 와 가 모두 후보이므로 어느 쪽인지 결정할 수 없습니다. 입력은 3차원이고 바닥 투영 변환의 rank는 2이므로, 깊이 방향 하나가 사라집니다. 그 정보 손실 때문에 역변환이 불가능합니다. 뒤에서 볼 2차원 행렬도 차원만 다를 뿐 같은 방식으로 한 방향을 눌러 버립니다.
한 줄 정리 — rank는 변환 뒤에 살아 있는 독립 방향의 수이며, 입력 방향이 사라지면 서로 다른 입력이 같은 출력으로 겹칩니다.
역행렬 — 되돌리는 기계가 존재하려면
되돌리는 기계가 존재하려면 무엇이 필요할까요? 가장 중요한 조건은 서로 다른 입력이 변환 뒤에도 서로 다른 출력으로 남는 것입니다. 두 입력이 한 출력으로 겹치면, 출력만 보고 어느 입력에서 왔는지 알아낼 수 없기 때문입니다.
이 조건을 만족하는 정방행렬에는 변환을 정확히 되돌리는 짝이 있습니다. 이 짝을 역행렬 이라고 부릅니다. 먼저 로 변환하고 이어서 로 되돌리면, 아무 변화도 주지 않은 것과 같은 결과가 나옵니다. 이때 '아무것도 바꾸지 않는 행렬'이 단위행렬 입니다. 숫자에서 인 것처럼 벡터에서는 입니다.
그래서 역행렬은 직관적으로 행렬 버전의 나눗셈처럼 생각할 수 있습니다. 숫자에 2를 곱한 일을 로 되돌리듯, 가 만든 변환을 이 되돌립니다. 다만 행렬곱은 순서가 중요하므로 단순한 숫자 나눗셈과 완전히 같지는 않습니다.
: 가 한 변환을 되돌리는 역행렬
: 입력을 바꾸지 않는 단위행렬
: 변환하고 되돌릴 입력 벡터
와 을 이어 적용하면 단위행렬과 같은 효과
변환했다가 정확히 원래대로 돌아오기
이제 실제로 겹치는 행렬을 보겠습니다. 의 두 번째 행은 첫 번째 행의 정확히 2배입니다. 서로 독립인 행이 하나뿐이므로 rank는 1이고, 2차원 입력을 1차원 방향으로 눌러 버립니다.
서로 다른 두 입력 과 를 넣어 보면 둘 다 같은 출력이 나옵니다.
출력 만 보고는 원래 입력이 이었는지 였는지 알 수 없습니다. 3D 그림에서 깊이가 다른 와 가 같은 그림자 로 겹친 것과 같은 정보 손실입니다. 따라서 에는 역행렬이 없습니다.
반대로 는 두 독립 방향을 모두 살립니다. 이 행렬의 역행렬은 이고, 실제로 가 됩니다.
여기서 범위를 정확히 짚어 두겠습니다. 정방행렬에서만 'rank가 으로 꽉 찼다'와 '양쪽에서 곱해 가 되는 보통의 역행렬이 있다'가 같은 말입니다. 직사각형 행렬은 full rank여도 이런 보통의 역행렬을 갖지 않습니다.
행렬식 — 넓이가 0으로 눌렸는지 보는 숫자
어떤 정방행렬이 되돌릴 수 있는지는 행렬식 로 확인할 수 있습니다. 행렬식의 절댓값 는 변환 뒤에 넓이가 몇 배가 되는지를 나타냅니다.
- : 넓이가 0으로 사라지지 않으므로 두 방향이 살아 있고, 역행렬이 있습니다.
- : 평면이 선이나 점으로 눌려 넓이가 0이 되고, 정보가 사라져 역행렬이 없습니다.
는 두 행이 같은 방향이라 평면을 한 줄로 누르고, 그래서 행렬식이 0입니다. 는 넓이를 남겨 두므로 행렬식이 1이고 되돌릴 수 있습니다.
: 의 부호 있는 넓이 배율 — 절댓값은 넓이 배율, 부호는 평면 방향(orientation)이 유지됐는지 뒤집혔는지
: 행렬 의 네 성분
이면 평면이 선이나 점으로 눌려 보통의 역행렬이 없음
넓이가 완전히 사라졌는지 확인하기
이제야 역행렬 공식이 자연스럽게 읽힙니다. 공식의 분모에는 바로 행렬식 가 들어갑니다. 행렬식이 0이면 0으로 나누어야 해서 공식을 쓸 수 없습니다. 이것은 단순한 계산 규칙이 아니라, 변환 과정에서 이미 한 방향의 정보가 사라졌다는 뜻입니다.
: 의 변환을 되돌리는 역행렬
: 행렬식 — 0이 아니어야 함
정방행렬에서 일 때만 사용할 수 있는 역행렬 공식
넓이가 남아 있을 때만 되돌리는 행렬을 만들 수 있음
한 줄 정리 — 행렬은 rank가 일 때만 역행렬이 있으며, rank가 부족하면 정보가 눌려 사라져 되돌릴 수 없습니다.
AI에서 만나는 rank — 독립 방향을 세는 눈
AI에서 데이터는 보통 행은 샘플, 열은 특성인 행렬로 놓입니다. 이때 rank는 많은 열이 있다는 사실보다, 데이터가 실제로 몇 개의 독립 방향으로 변하는지를 알려 줍니다. 예를 들어 임베딩 벡터가 768차원이어도 모든 벡터가 비슷한 몇 방향 주변에만 모여 있다면, 데이터 행렬의 유효 rank는 훨씬 작을 수 있습니다. 3D 물체가 2D 그림자에 눌린 것처럼, 저장된 좌표 수보다 실제로 쓰는 방향이 적은 상태입니다.
낮은 rank는 상황에 따라 도구가 되기도 하고 경고가 되기도 합니다.
- LoRA — 의도한 저랭크: 거대한 가중치 전체를 다시 학습하지 않고, 변화량 를 두 날씬한 행렬의 곱으로 만들어 의 rank가 최대 이 되도록 제한합니다. 원래 변화량에 개 숫자가 필요했다면, LoRA는 대략 개만 학습합니다. 필요한 변화가 몇 개의 중요한 방향으로 설명된다고 가정해 비용을 줄이는 설계입니다.
- 표현 붕괴 — 의도하지 않은 저랭크: 서로 다른 문장·이미지의 임베딩이 지나치게 비슷한 방향으로 몰리면 구별에 필요한 정보가 사라집니다. 이때 유효 rank가 급격히 낮아지는 현상은 표현 붕괴를 의심하게 하는 신호 중 하나입니다. 유효 rank 하나만으로 표현 붕괴를 확정하지는 않습니다.
따라서 낮은 rank가 항상 나쁜 것은 아닙니다. 중복된 변화를 압축하려고 일부러 낮춘 LoRA의 rank는 효율이고, 구별해야 할 데이터가 저절로 겹쳐 낮아진 rank는 손실입니다. 핵심은 '어떤 정보를 남기려고 낮췄는가'입니다.
코드로 생각하기
코드에서는 먼저 np.linalg.matrix_rank로 살아 있는 독립 방향의 수를 확인합니다. np.linalg.matrix_rank는 특이값(각 독립 방향의 세기를 나타내는 숫자) 가운데 라이브러리가 정한 허용오차보다 큰 값의 개수를 세어 numerical rank(수치 rank)를 반환합니다. 즉 '컴퓨터 오차 수준에서 0이 아닌 독립 방향이 몇 개인가'를 묻는 값입니다. 반면 유효 rank는 특이값이 얼마나 고르게 퍼졌는지, 몇 개의 큰 특이값에 정보가 몰렸는지를 보고 별도로 판단합니다. 두 용어는 같은 뜻이 아닙니다.
연립방정식 를 풀 때 수학책에는 라고 쓰지만, 코드에서는 역행렬을 직접 만든 뒤 곱하지 않고 np.linalg.solve(A, b)로 바로 풉니다. 역행렬 전체를 만드는 단계를 피하므로 더 빠르고 수치적으로도 안정적입니다. 아래 코드는 의 입력 충돌, , 그리고 solve까지 한 흐름으로 확인합니다.
A = ([[2., 1.], [1., 1.]])
B = ([[1., 2.], [2., 4.]])
p1 = ([1., 0.])
p2 = ([0., 0.5])
A_inv = ([[1., -1.], [-1., 2.]])
((A))
((B))
(B @ p1)
(B @ p2)
(A_inv @ A)
b = ([3., 2.])
((A, b))2 1 [1. 2.] [1. 2.] [[1. 0.] [0. 1.]] [1. 1.]
첫 두 출력은 가 rank 2인 full-rank 정방행렬이고, 는 rank 1이라 한 방향을 잃었다는 것을 보여 줍니다. 이어서 서로 다른 p1과 p2가 모두 [1. 2.]로 겹칩니다. A_inv @ A는 직접 적은 이 단위행렬 를 만드는지 검산합니다. 마지막 np.linalg.solve(A, b)는 역행렬을 만들지 않고 의 해 [1. 1.]을 바로 구하는 실무 방식입니다.
한 줄 정리 — 겹치지 않으면 되돌릴 수 있고, 겹치면 되돌릴 수 없습니다. 코드는 rank로 정보의 방향 수를 확인하고, 방정식은 역행렬 대신
solve로 풉니다.