- 과제 문제는 한 줄이었다 — 32×32 흑백 얼굴 이미지에 PCA(SVD)를 적용해 eigenface를 찾고 얼굴 인식 성능을 보이기. 여기에 과제에 없던 질문을 하나 더 붙였다. 그렇게 얻은 주성분이 사람 얼굴의 무엇을 담고 있는가.
- Yale database에 본인 얼굴, 안경 쓴 사람, 수염 있는 사람의 사진을 따로 모아 2,738장을 만들었다. 눈썹 아래부터 입까지 crop해 32×32 흑백으로 통일하고 1,024차원 열벡터로 쌓은 뒤, 평균을 뺀 행렬에 SVD를 적용했다.
- 인식 정확도는 사람별 0.8 / 0.6 / 0.2 / 0.0 / 0.2, 평균 0.36이었다. 하필 본인 사진이 가장 높았고, 원인은 얼굴이 아니라 명도와 그림자였다.
- 안경 데이터를 넣으면 6번 고유얼굴의 계수 평균이 170.06(미착용 −22.51)으로 갈렸고, 안경 색이 진할수록 그 차이가 커졌다. 수염에서는 같은 현상이 16번 고유얼굴에서, 더 약하게 나타났다.
- 이 글을 쓰며 원문 코드 이미지를 다시 읽다가, 인식 판정에 쓴 값이 코사인 유사도가 아니라 코사인 거리였고 최대값이 아니라 최소값으로 판정했다는 것을 확인했다. 25행 중 23행이 최소값 규칙과 일치하고, 최대값 규칙과 일치하는 행은 0행이다.
| 2,738장 | 직접 구축한 얼굴 데이터셋 | Yale database + 본인 · 안경 착용자 · 수염 보유자 사진 직접 수집. 눈썹 아래~입까지 crop해 32×32 그레이스케일로 통일했다. |
| 1,024 | 이미지 한 장의 차원 | 32 × 32 흑백을 flatten해 X의 열에 적재. 고유얼굴 격자는 U의 앞 100개 열을 10×10으로 붙인 320×320 이미지다. |
| 170.06 / −22.51 | 6번 주성분 계수 평균 | 안경 착용 320장(인덱스 2,418–2,737) vs 미착용 2,418장(인덱스 0–2,417) |
| 0.36 | 인식 평균 정확도 | 사람별 0.8 / 0.6 / 0.2 / 0.0 / 0.2. 25번의 판정 중 오답이 16건이고 그중 15건이 1번·2번 두 사람에게 몰렸다. |
| 0.40 | 판정 규칙을 다시 적용한 정확도 | 원문 거리표에 최소값 규칙을 그대로 적용해 다시 판정하면 25행 중 2행이 원문 결과와 달라지고 평균이 0.36에서 0.40이 된다. |
| r = 20 · 50 · 100 · 200 · 500 | rank-r 복원 비교 | 학습에 넣지 않은 테스트 이미지를 원본과 나란히 놓고 시각 비교만 했다. 재구성 오차는 원문에 수치로 남아 있지 않다. |
과제, 그리고 과제에 없던 질문
왜 SVD여야 하는가를 대각화의 한계에서부터 짚어 내려왔다.
수치해석 계절학기 중간과제로 받은 문제는 한 줄이었다 — 32×32 흑백 얼굴 이미지에 PCA(SVD)를 적용해 eigenface를 찾고 얼굴 인식 성능을 보여주기. 개인 과제였고 제출물은 슬라이드 24장짜리 발표자료였다. 계산을 시작하기 전에 두 가지를 먼저 정리했다.
첫째는 왜 SVD여야 하는가다. 벡터공간은 집합 안의 원소들이 선형결합을 해도 여전히 그 집합에 속하는 닫힌 집합이고, 반드시 원점을 포함해야 한다. 원점을 포함하지 않으면 평행이동해 포함하도록 만들어야 한다 — 원점을 지나지 않는 직선 위의 점들은 선형결합하면 그 직선을 벗어나기 때문이다. 부분공간(subspace)도 같은 조건을 만족해야 한다. 기저(basis)는 그 공간을 span할 수 있는 최소한의 벡터 집합이고, 서로 수직이 되도록 잡으면 독립 조건을 쉽게 만족시킬 수 있으며, 길이를 1로 맞춘 orthonormal 기저로 잡으면 내적만으로 projection을 구할 수 있다. 기저 벡터의 개수가 그 벡터공간의 차원이고, 기저 자체는 유일하지 않지만 기저로 벡터를 표현할 때의 선형결합 계수는 유일하게 정해진다. 인식을 계수로 하겠다는 계획이 여기서 나온다.
고유값 쪽은 Ax = λx로 스칼라곱이 벡터를 다시 표현할 때의 λ와 x이고, (A − λI)x = 0과 det(A − λI) = 0으로 계산한다. 그런데 대각화 A = SΛS⁻¹는 정방행렬에서만 가능하다. 이미지 행렬은 임의의 모양을 가지므로 임의 형태의 행렬을 분해할 수 있는 SVD A = UΣVᵀ가 필요하다. U는 열공간의 기저, V는 행공간의 기저, Σ는 AᵀA의 특이값을 담은 대각행렬이고, U의 열벡터들도 orthonormal이라 회전 역할을 한다. Σ가 크면 벡터들이 그 방향으로 많이 분포한다는 뜻이다.
둘째는 과제에 없던 질문이었다. 그렇게 얻은 주성분이 사람 얼굴의 무엇을 담고 있는가. 이것을 확인하지 않으면 인식이 맞았는지 틀렸는지만 알고 왜 그런지는 모르게 된다고 생각했다. 결과적으로 이 질문이 과제의 절반을 차지했다.
PCA — 더 넓게 분포하는 방향으로 큰 벡터를 구한다
2차원 벡터를 벡터 하나로 표현할 때 무엇이 달라지는지가 출발점이다.
주성분의 방향은 데이터들의 분산이 가장 큰 방향벡터다. 평균을 빼서 원점을 포함하도록 만든 행렬 A로 R = AᵀA 공분산 행렬을 만들고, 특이값이 크면 더 dominant한 기저벡터다.
왜 그래야 하는지는 2차원 예시로 정리했다. 2차원 벡터를 벡터 하나로 표현하려 할 때, x축으로만 표현하면 하늘색만큼의 오차가 생기지만 첫 번째 주성분 벡터로 표현하면 빨간색의 적은 오차로 표현할 수 있고, 더 적은 수의 벡터로도 표현이 가능해진다. 더 넓게 분포하는 방향으로 큰 벡터를 구한다는 것이 원리다.
고유얼굴은 이 원리를 이미지에 그대로 적용한 것이다. 이미지들을 행렬로 만들고 SVD를 거쳐 Σ 값이 큰 순서대로 고유벡터를 고르면, 개수가 줄어든 그 기저벡터들의 선형결합으로 각 사람의 얼굴을 표현할 수 있다. 그리고 기저벡터에 해당하는 계수에 따라 사람 얼굴을 나눌 수 있다. 인식과 속성 해석이 모두 이 한 문장에서 갈라져 나온다.
2,738장을 직접 모아 X를 쌓다
공개 데이터만으로는 확인하고 싶은 속성이 없어서, 속성별 사진을 따로 모았다.
Yale database에 본인 얼굴 사진, 안경 쓴 사람의 사진, 수염 있는 사람의 사진을 따로 추가해 총 2,738장의 데이터셋을 직접 만들었다. 안경과 수염을 따로 모은 이유는 하나다 — 그 속성이 들어갔을 때와 빠졌을 때의 고유얼굴을 비교하려면, 속성이 확실히 있는 데이터를 내가 통제해서 넣어야 했다.
| 항목 | 내용 |
|---|---|
| 출처 | Yale database + 직접 수집(본인 얼굴 · 안경 착용자 · 수염 보유자) |
| 총 장수 | 2,738장 |
| 안경 미착용 구간 | 인덱스 0 – 2,417 (2,418장) |
| 안경 착용 구간 | 인덱스 2,418 – 2,737 (320장) |
| 전처리 | 눈썹 아래부터 입까지 crop → 32 × 32 · 흑백 변환 → flatten해 1,024차원 열벡터로 X의 열에 적재 |
| 중심화 | 1,024장 이상 모은 데이터로 평균 벡터 M을 구하고, 양수로만 이루어진 데이터에서 M을 빼 원점을 포함하는 벡터공간을 만들었다. Fk − M = ak, A = [a1 a2 … an] |
학습셋으로는 인물 37명분까지의 이미지를 잘라 썼다. 다만 이 37명과 전체 2,738장, 그리고 뒤에서 테스트한 다섯 사람의 관계는 원문에 정리되어 있지 않다.

평균 얼굴과 고유얼굴 100장
특이값이 정말 내림차순인지까지 직접 확인하고 넘어갔다.
평균 얼굴은 학습 얼굴들의 픽셀 평균이다. 눈·코·입의 위치가 남고 개인차는 지워진, 흐릿한 정면 얼굴 하나가 나온다. 모든 이미지에서 이 벡터를 빼는 것이 중심화이고, 이 조작이 있어야 데이터가 원점을 포함하는 벡터공간이 된다.
SVD를 돌린 뒤 가장 먼저 한 것은 확인이었다. 특이값이 실제로 내림차순으로 정렬되었는지를 직접 찍어 봤다. 뒤에서 "앞쪽 성분", "6번 성분" 같은 말을 하려면 순서가 보장돼야 하기 때문이다.
고유얼굴은 기저벡터이므로 음수와 양수를 함께 갖는다. 이를 데이터로 볼 수 있게 음수는 검은색, 양수는 밝은색으로 시각화했다. 격자는 320×320 크기의 빈 화면에 성분을 10×10으로 붙여 만든 것이라, 보이는 것은 U의 앞 100개 열이다.
격자에서 두 가지가 눈에 보였다. 왼쪽 위(특이값이 큰 쪽)에는 얼굴 구조가 뚜렷하게 남아 있고, S 값이 작아지는 아래쪽에서는 노이즈만 남는다. 그리고 성분마다 담는 요소가 다르다 — 눈이 들어간 정도를 볼 수 있는 눈썹뼈가 부각된 6 · 8번째 성분이 그 예다. 뒤에서 안경이 6번 성분에 실린다는 결과가 나오는데, 그 6번은 이 단계에서 이미 눈 주변을 담고 있던 성분이었다.



계수 Ck — 다섯 사람 × 다섯 장
인식 정확도가 무너지는 이유가 이미 이 표 안에 들어 있다.
계수는 평균을 뺀 이미지와 고유얼굴의 내적이다. 고유얼굴 행렬과 이미지 행렬의 곱 한 번으로 전부 얻는다. 다섯 사람 각각의 사진 5장에 대해 1~5번 성분의 계수를 뽑았다.
C1 — 첫 번째 사람(본인)
| 성분 | 사진1 | 사진2 | 사진3 | 사진4 | 사진5 |
|---|---|---|---|---|---|
| 1 | −1486.64 | −1604.59 | −1905.74 | −1432.73 | −1645.34 |
| 2 | 1820.56 | 1143.59 | 1554.71 | 1259.27 | 1013.77 |
| 3 | 1908.55 | 1340.17 | 1524.44 | 1351.37 | 122.23 |
| 4 | 2443.31 | 1828.40 | 1768.23 | 1629.75 | 1663.04 |
| 5 | 809.09 | 269.91 | 667.40 | 508.80 | 201.37 |
C2 — 두 번째 사람
| 성분 | 사진1 | 사진2 | 사진3 | 사진4 | 사진5 |
|---|---|---|---|---|---|
| 1 | −1046.76 | −866.17 | −461.00 | −1099.51 | −937.32 |
| 2 | 847.30 | 666.16 | 779.74 | 966.21 | 871.69 |
| 3 | 668.45 | 494.12 | 696.80 | 855.61 | 422.23 |
| 4 | 994.61 | 723.11 | 933.20 | 1174.93 | 1036.85 |
| 5 | 457.29 | 360.54 | 516.24 | 690.01 | 552.89 |
C3 — 세 번째 사람
| 성분 | 사진1 | 사진2 | 사진3 | 사진4 | 사진5 |
|---|---|---|---|---|---|
| 1 | 29.27 | 193.23 | 330.69 | −324.65 | −166.02 |
| 2 | 9.82 | −361.94 | 499.65 | 257.95 | −198.13 |
| 3 | −423.46 | −678.95 | −33.13 | −181.83 | −697.78 |
| 4 | −54.50 | −434.58 | 485.53 | 233.65 | −262.99 |
| 5 | −163.05 | −364.30 | 165.22 | 17.38 | −256.63 |
C4 — 네 번째 사람
| 성분 | 사진1 | 사진2 | 사진3 | 사진4 | 사진5 |
|---|---|---|---|---|---|
| 1 | 108.25 | −68.54 | −428.73 | 24.96 | 12.64 |
| 2 | −31.53 | −292.58 | 128.80 | 4.08 | −226.49 |
| 3 | 249.08 | −37.23 | 326.74 | 215.23 | −341.95 |
| 4 | 58.40 | −257.73 | 220.09 | 68.46 | −195.03 |
| 5 | −95.91 | −238.83 | −69.31 | −119.00 | −230.52 |
다섯 번째 사람의 계수표는 발표자료에 실려 있지 않다. 다만 9번 슬라이드의 코드 이미지에 5원소 벡터 25개가 그대로 적혀 있고, 그 1~4번째 열이 위 C1~C4의 값과 소수점 끝까지 일치한다. 그러면 남은 다섯 번째 열이 다섯 번째 사람이므로, 거기서 읽어 표로 복원했다(값은 이미지 판독이라 소수 둘째 자리로 반올림했고, 판독 오차 가능성이 있다).
C5 — 다섯 번째 사람 (코드 이미지에서 복원)
| 성분 | 사진1 | 사진2 | 사진3 | 사진4 | 사진5 |
|---|---|---|---|---|---|
| 1 | 154.62 | 72.65 | 4.51 | 435.50 | 386.54 |
| 2 | −48.54 | −19.62 | 7.82 | 16.60 | −10.64 |
| 3 | 115.13 | 55.34 | 96.77 | 76.89 | 30.32 |
| 4 | −10.94 | 18.97 | 81.32 | 81.93 | 33.71 |
| 5 | −5.70 | −45.35 | −11.27 | −30.01 | −53.35 |
다섯 표를 나란히 놓으면 두 가지가 읽힌다. 첫째, 첫 번째 · 두 번째 사람은 1번 성분이 모두 음수, 2~5번 성분이 모두 양수로 부호 패턴이 일정하고 절대값도 수백~수천 단위로 크다. 둘째, 세 번째 · 네 번째 · 다섯 번째 사람은 같은 사람의 사진 다섯 장 안에서도 부호가 뒤집힌다(C3의 1번 성분: +29.27 / +193.23 / +330.69 / −324.65 / −166.02). 뒤에서 3 · 4번 사람의 인식 정확도가 0.2 · 0.0으로 무너지는 이유가 이 표에 이미 들어 있다 — 같은 사람인데 계수 벡터가 한 방향으로 모이지 않았다.


판정 규칙을 원문에서 되짚다 — 유사도가 아니라 거리였다
이 글을 쓰면서 코드 이미지를 확대해 보고서야 표를 거꾸로 읽고 있었다는 것을 알았다.
발표자료 9번 슬라이드에는 "코사인 유사도 라이브러리를 사용하였습니다"라고 적혀 있고, 10~12번 슬라이드 표의 머리글도 전부 "n번째 사람 계수 코사인 유사도 평균"이다. 그런데 같은 슬라이드에 붙은 코드 이미지를 확대해 보면, 실제로 쓴 것은 유사도가 아니라 거리를 돌려주는 함수다. 코사인 거리는 1 − 코사인 유사도이므로 값이 작을수록 닮은 것이고, 그 결과를 담아 둔 행렬의 이름에도 거리라고 적혀 있다.
표로 확인했다. 25개 테스트 사진 각각에 대해 다섯 열 중 어느 열이 원문의 "인식 결과"와 맞는지 세어 보면 이렇다.
| 판정 규칙 | 원문 인식 결과와 일치한 행 |
|---|---|
| 최대값 열로 판정 (유사도로 읽었을 때) | 0 / 25 |
| 최소값 열로 판정 (거리로 읽었을 때) | 23 / 25 |
결론은 분명하다. 표에 적힌 값은 코사인 거리이고 판정은 가장 가까운 사람으로 했다. 슬라이드의 머리글 "코사인 유사도 평균"이 잘못 적힌 것이다. 값의 해석도 따라서 뒤집힌다 — 표의 값은 0.0337에서 0.6759 사이에 있고, 이를 유사도로 환산하면 0.32에서 0.97 사이다. 계수 벡터들이 서로 거의 직교한 것이 아니라, 반대로 대부분의 짝이 매우 비슷했다. 비슷한 정도가 다 비슷했기 때문에 판정이 흔들린 것이다.
최소값 규칙과 어긋나는 두 행은 4번 사람 사진4(최소는 2번 열 0.0442인데 결과는 첫 번째 사람)와 5번 사람 사진2(최소는 5번 열 0.1927인데 결과는 두 번째 사람)다. 이 두 행은 원인을 찾지 못했다.

거리표 25행과 사람별 정확도 0.36
평균 0.36이라는 숫자 뒤에 있는 25번의 판정을 표로 다 옮겼다.
사람별로 5장씩 테스트하고, 다섯 사람 각각의 계수와의 코사인 거리 평균이 가장 작은 사람으로 판정했다. 10~12번 슬라이드에 흩어져 있던 표를 하나로 합치고, 각 행의 최소값 열을 계산해 원문 인식 결과와 나란히 놓았다.
| 테스트 사진 | 1번 | 2번 | 3번 | 4번 | 5번 | 최소값 열 | 원문 인식 결과 |
|---|---|---|---|---|---|---|---|
| 1번 사람 사진1 | 0.0366641 | 0.03535911 | 0.1509833 | 0.1279111 | 0.1109558 | 2번 | 두 번째 사람 (오답) |
| 1번 사람 사진2 | 0.03380782 | 0.04443372 | 0.1272196 | 0.1235355 | 0.1404739 | 1번 | 첫 번째 사람 |
| 1번 사람 사진3 | 0.09291154 | 0.1299122 | 0.1768002 | 0.1936839 | 0.2791081 | 1번 | 첫 번째 사람 |
| 1번 사람 사진4 | 0.06026151 | 0.07450086 | 0.2445857 | 0.194682 | 0.1750756 | 1번 | 첫 번째 사람 |
| 1번 사람 사진5 | 0.0347737 | 0.05428117 | 0.1993093 | 0.1629946 | 0.1693596 | 1번 | 첫 번째 사람 |
| 2번 사람 사진1 | 0.03375117 | 0.03930632 | 0.1551775 | 0.1327278 | 0.1611911 | 1번 | 첫 번째 사람 (오답) |
| 2번 사람 사진2 | 0.09240281 | 0.04443372 | 0.1192917 | 0.1265623 | 0.1031009 | 2번 | 두 번째 사람 |
| 2번 사람 사진3 | 0.07335559 | 0.07920241 | 0.2643832 | 0.2074477 | 0.2516466 | 1번 | 첫 번째 사람 (오답) |
| 2번 사람 사진4 | 0.05834561 | 0.04872957 | 0.2077826 | 0.1632184 | 0.1575337 | 2번 | 두 번째 사람 |
| 2번 사람 사진5 | 0.08063185 | 0.05783064 | 0.1347938 | 0.1265966 | 0.07707359 | 2번 | 두 번째 사람 |
| 3번 사람 사진1 | 0.07613915 | 0.09396223 | 0.1347569 | 0.1526965 | 0.2106021 | 1번 | 첫 번째 사람 (오답) |
| 3번 사람 사진2 | 0.1399964 | 0.1475583 | 0.1229037 | 0.1677521 | 0.1923672 | 3번 | 세 번째 사람 |
| 3번 사람 사진3 | 0.05519096 | 0.07288747 | 0.1672717 | 0.1609231 | 0.2171752 | 1번 | 첫 번째 사람 (오답) |
| 3번 사람 사진4 | 0.05080295 | 0.05802976 | 0.1308717 | 0.1343485 | 0.1524317 | 1번 | 첫 번째 사람 (오답) |
| 3번 사람 사진5 | 0.6759164 | 0.6274462 | 0.4604554 | 0.5478957 | 0.4083644 | 5번 | 다섯 번째 사람 (오답) |
| 4번 사람 사진1 | 0.03902487 | 0.04901674 | 0.1538897 | 0.137815 | 0.1805141 | 1번 | 첫 번째 사람 (오답) |
| 4번 사람 사진2 | 0.07551048 | 0.07238591 | 0.1232504 | 0.1288949 | 0.1478374 | 2번 | 두 번째 사람 (오답) |
| 4번 사람 사진3 | 0.06869624 | 0.07379675 | 0.245271 | 0.19653 | 0.2410515 | 1번 | 첫 번째 사람 (오답) |
| 4번 사람 사진4 | 0.05184318 | 0.04417634 | 0.1882341 | 0.1523689 | 0.1560136 | 2번 | 첫 번째 사람 (오답) — 최소값과 불일치 |
| 4번 사람 사진5 | 0.05237734 | 0.04104143 | 0.1191118 | 0.1129978 | 0.1035634 | 2번 | 두 번째 사람 (오답) |
| 5번 사람 사진1 | 0.05451521 | 0.04802737 | 0.1146341 | 0.114199 | 0.1101416 | 2번 | 두 번째 사람 (오답) |
| 5번 사람 사진2 | 0.3699184 | 0.333338 | 0.2630783 | 0.3131358 | 0.192663 | 5번 | 두 번째 사람 (오답) — 최소값과 불일치 |
| 5번 사람 사진3 | 0.06776515 | 0.05202755 | 0.223509 | 0.1698024 | 0.15398 | 2번 | 두 번째 사람 (오답) |
| 5번 사람 사진4 | 0.1314573 | 0.1008268 | 0.2218798 | 0.1905923 | 0.1049439 | 2번 | 두 번째 사람 (오답) |
| 5번 사람 사진5 | 0.3739342 | 0.3253939 | 0.3158012 | 0.3366173 | 0.1739631 | 5번 | 다섯 번째 사람 |
사람별 정확도는 다음과 같다.
| 테스트 대상 | 정확도 | 오답이 향한 곳 (5장 중) |
|---|---|---|
| 첫 번째 사람 (본인) | 0.8 | 1장을 두 번째 사람으로 |
| 두 번째 사람 | 0.6 | 2장을 첫 번째 사람으로 |
| 세 번째 사람 | 0.2 | 3장을 첫 번째 사람으로, 1장을 다섯 번째 사람으로 |
| 네 번째 사람 | 0.0 | 3장을 첫 번째 사람으로, 2장을 두 번째 사람으로 |
| 다섯 번째 사람 | 0.2 | 4장을 두 번째 사람으로 |
| 평균 | 0.36 | 오답 16건 중 15건이 1번 · 2번 두 사람으로 몰렸다 |
표를 다 놓고 보면 세 가지가 보인다. 첫째, 오답이 1번 · 2번 사람에게 빨려 들어갔다. 계수 절대값이 수천 단위로 큰 두 사람이 판정을 독점한 셈이다. 둘째, 3번 사람 사진5와 5번 사람 사진2 · 사진5는 다섯 열이 전부 크게 뛴다. 거리로 읽으면 그 사진이 다섯 후보 전부에서 멀다는 뜻이고, 그런 사진에서만 정답이 나오기도 했다. 셋째, 앞 절에서 어긋난 두 행을 최소값 규칙대로 다시 판정하면 다섯 번째 사람의 정확도가 0.2에서 0.4로 올라가 평균이 0.36에서 0.40이 된다. 그래도 절반에 못 미친다.
rank-r 복원 — r을 키우면 얼굴로 돌아온다
인식은 안 됐지만, 표현 자체가 되는지는 따로 확인할 수 있었다.
X의 열벡터는 이미지이고 U는 고유얼굴이다. 학습에 포함하지 않은 테스트 이미지 x에 대해 U Uᵀ x를 계산하면 복원할 수 있다. 원문의 표현으로는, 차원이 감소된 데이터 z는 분산이 높은 주성분들을 가지는 행렬 U와 기존 이미지 x를 곱해 구하고, 복원은 U와 z를 곱해 되돌린다.
rank-r SVD 기저로 r을 20 · 50 · 100 · 200 · 500으로 바꿔 가며 원본과 나란히 놓고 비교했다. 복원에 쓰는 고유얼굴을 많이 쓸수록 실제 얼굴에 가까워진다는 것이 눈으로 확인됐다. r = 20에서는 이목구비의 위치는 잡히지만 경계가 뭉개진 흐릿한 상이고, r이 커질수록 눈매와 입술 경계, 코 옆 그림자 같은 세부가 살아난다.
다만 여기서 멈춘 것이 이 과제의 한계다. 재구성 오차를 수치로 재지 않았다. r별 MSE를 찍어 두었다면 "몇 개면 충분한가"를 말할 수 있었을 텐데, 남아 있는 것은 시각 비교뿐이다.

본인 사진만 잘 맞은 이유 — 모델이 배운 것은 조명이었다
정확도가 낮게 나온 쪽만 들여다보는 것이 아니라, 높게 나온 쪽을 의심해야 한다.
인식 평균은 0.36으로 낮았다. 그런데 하필 본인 사진의 정확도가 0.8로 가장 높았다. 여기서 멈추지 않고 이유를 따져 봤다.
원문에 적어 둔 관찰은 이렇다 — 본인 사진은 다른 네 명의 사진과 전체적인 명도나 그림자에서 확연한 차이를 시각적으로 확인할 수 있었다. 이것을 주성분 산점도로 확인하려고, 본인 사진을 두 번째 사람과 비교한 세 컷과 세 번째 사람과 비교한 세 컷을 따로 그렸다.
| 축 | 본인 vs 두 번째 사람 | 본인 vs 세 번째 사람 |
|---|---|---|
| 주성분 1–2 | 본인 값이 오른쪽 상단에 분포했지만 완전히 분리되지는 못했다 | 마찬가지로 주성분 1에서 차이가 났지만 완전히 분리되지는 못했다 |
| 주성분 3–4 | 본인 값이 왼쪽 상단에 분포하고 일부 겹치지만 비교적 분리됐다 | 본인 값이 왼쪽 상단에 분포하고 주성분 4에서 차이를 확인할 수 있었다 |
| 주성분 5–6 | 주성분 6에서 차이가 있었다 | 주성분 6에서 차이가 있고 본인의 6번이 약간 더 높게 분포했다 |
두 세트에서 반복해서 갈린 축이 6번 주성분이다. 그리고 6번은 뒤의 안경 실험에서 눈 주변의 어두운 픽셀 덩어리에 반응하던 성분이고, 격자에서 처음 볼 때부터 눈썹뼈가 부각된 성분이었다. 즉 본인 사진이 6번 축에서 갈린 것은 얼굴의 개인차라기보다 눈 주변 그림자의 밝기 차이로 설명된다. 분류기가 본인 사진을 잘 맞춘 이유는 얼굴의 형태가 아니라 촬영 조건이었다.
정확도가 낮게 나온 쪽만 들여다보는 것이 아니라 높게 나온 쪽을 의심해야 한다는 것을 이 과제에서 처음 배웠다. 모델이 무엇을 보고 배우는지 따지는 습관이 여기서 시작됐다.


안경 — 6번 고유얼굴에 이름을 붙이다
주성분이 무엇을 담는지 확인하는 방법은, 그 속성을 가진 데이터를 넣었을 때와 넣지 않았을 때를 비교하는 것이었다.
안경 쓴 사람들의 사진을 데이터에 추가한 경우와 추가하지 않은 경우의 고유얼굴을 나란히 만들어 비교했다. 기대했던 것과 달리 안경만 강조된 고유얼굴은 찾을 수 없었다. 격자 두 장을 육안으로 비교해서는 차이를 짚기 어렵다. 다만 안경 데이터를 넣었을 때 안경 부분이 positive한 고유얼굴이 있다는 것은 확인할 수 있었고, 그것이 6번이었다.
그래서 격자를 보는 대신 계수로 갔다. 6번 고유얼굴의 계수를 안경 착용 여부로 나눠 평균을 냈다.
| 그룹 | 6번 고유얼굴 계수 (앞부분) | 평균 |
|---|---|---|
| 안경 착용 320장 | 162.18 / 265.26 / 76.39 / 313.99 / 369.76 / 283.71 … | 170.06 |
| 안경 미착용 2,418장 | 21.48 / −19.90 / −99.68 / −78.55 / −12.15 / 129.05 … | −22.51 |
평균이 갈릴 뿐 아니라 부호가 갈린다. 안경 미착용 쪽 계수는 0을 중심으로 양·음이 섞이는데, 착용 쪽은 나열된 값이 전부 양수다. 산점도에서도 주성분 5–6 평면에서 안경 착용자가 6번 축을 따라 위쪽으로 분리됐다.



안경 색 진하기 3단 — 아홉 컷의 관찰
안경 실험은 한 번이 아니라 세 번이다. 전체 · 연한 색 · 진한 색으로 나눠 각각 주성분 세 쌍을 그렸다.
"6번이 안경"에서 멈추면 그 6번이 안경이라는 물건을 잡는 것인지, 안경이 만드는 어두운 픽셀을 잡는 것인지 알 수 없다. 그래서 안경 색의 진하기를 조건으로 넣어 같은 산점도를 다시 그렸다. 조건 3 × 패널 3 = 아홉 컷이고, 관찰 문장도 아홉 개다.
| 조건 | 주성분 1–2 | 주성분 3–4 | 주성분 5–6 |
|---|---|---|---|
| 안경 착용 전체 | 안경 낀 사람의 1번 주성분이 상대적으로 적어 차이를 확인할 수 있었다 | 상대적으로 고루 분포되어 변별력이 없었다 | 안경 낀 사람들의 6번 주성분이 끼지 않은 사람들보다 상대적으로 컸다 |
| 연한 색 안경 | 1번 주성분이 상대적으로 적어 차이를 확인할 수 있었다 | 고루 분포되어 변별력이 없었다 | 6번 주성분이 높게 나타났지만 진한 테 안경 데이터보다 덜 분리되었다 |
| 진한 색 안경 | 1번 주성분이 상대적으로 작았지만 일부 섞여 있어 변별력이 떨어졌다 | 왼쪽 상단에 많이 위치했으나 계수가 비교적 섞여 있어 변별력이 떨어졌다 | 진한 색 안경을 낀 사람의 6번 주성분이 덜 진한 색보다 상대적으로 크고, 계수의 차이도 상대적으로 컸다 |
여기서 얻은 결론은 "6번이 안경"이 아니다. 주성분 1–2와 3–4는 세 조건 모두에서 변별력이 없었고, 6번 축에서만 안경이 분리됐다. 그리고 분리 정도가 안경 색의 진하기에 비례했다. 6번 성분이 잡는 것은 안경이라는 물건이 아니라 눈 주변의 어두운 픽셀 덩어리라는 해석이 여기서 가능해지고, 이것이 속성과 주성분의 대응을 인과 쪽으로 한 칸 좁힌 근거다. 앞 절에서 본인 사진이 6번 축에서 갈린 것을 조명으로 설명한 것도 같은 성분에 대한 같은 해석이다.
진한 테 조건의 비교 대상은 원문에 빌게이츠 사진 데이터로 적혀 있다 — 연한 색 안경 결과를 두고 "빌게이츠 데이터보다 덜 분리되었다"고 썼다.


수염 — 9 · 12 · 16번 중 16번
같은 절차를 반복했더니, 같은 결론이 더 약하게 나왔다.
수염 있는 사람들의 데이터를 넣고 안경과 같은 절차를 반복했다. 수염이 반영된 고유얼굴 후보로 9번 · 12번 · 16번을 나란히 놓고 비교했고(21번 슬라이드 하단에 세 성분 번호가 적혀 있다), 그중 수염이 positive하게 적용된 16번째 고유얼굴을 비교 대상으로 골랐다. 산점도도 이 세 성분에 맞춰 세 컷을 그렸다.
| 산점도 축 | 관찰 |
|---|---|
| x = 주성분 1, y = 주성분 9 | 수염을 가진 사람들의 9번째 주성분이 상대적으로 적었지만 일부 겹쳐졌다 |
| x = 주성분 2, y = 주성분 12 | 12번째 주성분 역시 고르게 분포되어 계수의 차이가 크지 않았다 |
| x = 주성분 1, y = 주성분 16 | 수염을 가진 사람들의 16번째 계수가 가지지 않은 사람보다 상대적으로 컸지만 일부는 차이가 적었다 |
개별 계수는 수염 보유 쪽이 252.35 / 566.74 / 252.35 / 370.22, 미보유 쪽이 −11.64 / −88.14 / −133.04 / −89.73 / −211.41로 부호부터 갈린다. 방향은 안경과 같다. 다만 이 표의 평균값은 그대로 쓸 수 없다 — 아래 정정 절에 적었다.
안경과 비교하면 수염의 분리도는 분명히 약하다. 안경은 색 진하기 조건까지 걸었을 때 6번 축에서 거의 갈렸지만, 수염은 세 축 모두에서 겹침이 남았다. 결론 슬라이드도 이 차이를 반영해 표현의 강도를 달리했다 — 안경은 "6번 principle component를 사용해서 classification하는 데 매우 좋다", 수염은 "일부 겹쳐지기도 했지만 차이를 확인할 수 있어서 16번 principle component를 사용할 수 있다"이다.
수염 데이터를 넣은 뒤의 고유얼굴 격자에서도 확인할 것이 하나 더 있다. 앞쪽 두세 줄까지만 얼굴 구조가 남고 그 아래는 전부 노이즈다. 2,738장 · 1,024차원에서 실제로 해석할 수 있는 성분이 상위 수십 개뿐이었다는 뜻이고, 수염 후보를 9 · 12 · 16번에서 찾은 것도 그 범위 안이었다.



원문을 다시 읽으며 정정한 것
결과를 다시 검산하는 일은 결과를 만드는 일과 같은 무게였다.
이 글을 쓰기 위해 발표자료를 슬라이드 단위로 다시 읽으면서, 본문과 표·코드가 어긋나는 지점을 찾았다. 지우지 않고 그대로 적는다.
| 지점 | 원문 서술 | 확인한 사실 · 정정 |
|---|---|---|
| 10~12번 슬라이드 표 머리글, 9번 슬라이드 본문 | "n번째 사람 계수 코사인 유사도 평균", "코사인 유사도 라이브러리를 사용하였습니다" | 실제로 쓴 것은 코사인 거리를 돌려주는 함수이고, 결과 행렬의 이름에도 거리라고 적혀 있다. 표의 값은 코사인 거리이고 판정은 최소값으로 했다. 25행 중 최소값 규칙과 일치하는 행이 23행, 최대값 규칙과 일치하는 행이 0행이다 |
| 같은 표의 두 행 | 4번 사람 사진4 → 첫 번째 사람 / 5번 사람 사진2 → 두 번째 사람 | 두 행은 최소값 열(각각 2번 0.0442, 5번 0.1927)과 결과가 어긋난다. 최소값 규칙대로 다시 판정하면 평균 정확도가 0.36 → 0.40이 된다. 원인은 찾지 못했다 |
| 21번 슬라이드 (수염) 표 | 머리글이 "안경 쓴 사람들의 eigenface16의 계수"이고 평균이 170.06 / −22.51로 적혀 있다 | 이 평균값은 17번 슬라이드의 안경(6번) 표와 완전히 같다. 개별 계수는 서로 다르므로 안경 표에서 옮긴 뒤 고치지 않은 것으로 보인다. 이 글에서는 안경(6번)만 정량으로 쓰고, 수염은 개별 계수와 방향성만 적었다 |
| 같은 슬라이드의 계수 표기 | 수염 절의 계수 평균을 6번 성분에서 뽑은 것으로 표기되어 있다 | 16번 성분의 계수를 뽑아야 하는 자리다. 위 항목과 같은 원인으로 보인다 |
| 21번 슬라이드 수염 계수 나열 | 252.3451346643795 / 566.7403047267693 / 252.3451346643795 / 370.2195505215134 | 첫 번째와 세 번째 값이 소수점 끝까지 동일하다. 서로 다른 사진의 계수가 같을 가능성은 낮으므로 옮겨 적는 과정의 중복으로 보인다 |
| 22번 슬라이드 (수염 산점도) | 설명이 "안경 낀 사람과 안경 안 낀 사람 사진이 …"이고, 패널 설명 중 하나가 "주성분 6이 안경이 강조된 부분"이다 | 내용은 수염 실험이다(관찰 문장은 9 · 12 · 16번 주성분과 수염을 말한다). 실제 그림의 범례도 수염 있음 / 없음이다. 안경 슬라이드를 복제한 뒤 문구를 고치지 않은 것이다 |
| 13 · 15 · 16 · 17 · 18 · 21번 슬라이드 | 같은 문단이 반복되고 "수염에 관한 eigenface를"이라는 미완성 문장이 그대로 남아 있다 | 템플릿 텍스트가 복제된 흔적이다. 같은 문단의 "5번과 6번 principle component를 사용해서 classification하는 데 매우 좋음"은 결론 슬라이드에서 6번에 대해 확정되므로, 이 글에서는 결론 슬라이드 표현을 기준으로 삼았다 |
이 과제가 남긴 것과, 남긴 구멍
특징을 얻는 것보다, 그 특징이 무엇을 담고 있는지 확인하는 데 시간을 더 썼다.
이 과제에서 실제로 한 일은 두 갈래다. 하나는 요구받은 것 — 데이터를 모아 SVD를 돌리고, 계수로 인식을 해 보고, rank-r로 복원해 본 것. 여기서 나온 숫자는 좋지 않다. 평균 정확도 0.36, 다섯 명 중 한 명은 0.0이다. 다른 하나는 요구받지 않은 것 — 주성분이 무엇을 담는지 데이터 구성을 바꿔 넣어 확인한 것이고, 결과적으로 안경에는 6번, 수염에는 16번이라는 이름을 붙일 수 있었다.
두 갈래가 만나는 지점이 이 과제의 결론이다. 인식이 가장 잘 된 사진(본인, 0.8)이 갈린 축도 6번이었고, 그 6번은 안경 실험에서 눈 주변의 어두운 덩어리에 반응하던 성분이었다. 즉 잘 맞은 이유는 얼굴이 아니라 조명이었다. 속성 해석 실험을 하지 않았다면 0.8이라는 숫자를 그냥 좋은 결과로 적고 넘어갔을 것이다.
남긴 구멍도 분명하다. rank-r 복원의 오차를 수치로 재지 않았고, 학습/테스트 분리 기준을 기록하지 않았으며, 수염 계수의 평균은 안경 표에서 옮겨 온 값이 그대로 남아 검증되지 않았다. 판정에 쓴 값이 유사도인지 거리인지조차 4년 뒤 코드 이미지를 확대해서야 확정했다. 결과를 만드는 일과 그 결과를 다시 검산하는 일은 같은 무게라는 것을, 이 과제를 다시 정리하면서 한 번 더 확인했다.
여기서 배운 것
- 특징은 해석할 수 있어야 쓸 수 있다. 주성분에 안경(6번) · 수염(16번)이라는 이름을 붙일 수 있었던 것은 데이터 구성을 바꿔 넣어 비교했기 때문이고, 안경 색의 진하기까지 나눠 봤을 때 계수 차이가 함께 커지는 것을 확인해 "안경이라는 물건"이 아니라 "눈 주변의 어두운 픽셀"로 해석을 좁힐 수 있었다.
- 정확도는 결론이 아니라 질문의 시작이다. 가장 잘 맞은 본인 사진(0.8)을 의심해 들어가지 않았다면, 모델이 배운 것이 얼굴이 아니라 조명이었다는 사실을 찾지 못했을 것이다.
- 결과가 무너진 이유는 대개 결과 앞의 표에 이미 적혀 있다. 3 · 4번 사람의 정확도 0.2 · 0.0은 계수표에서 같은 사람의 사진 다섯 장이 부호부터 뒤집히는 것을 본 순간 예고되어 있었다.
- 쓴 함수가 유사도를 돌려주는지 거리를 돌려주는지 확인하지 않으면, 표를 정반대로 읽고도 모른다. 25행 전부에 대해 최대값 규칙과 최소값 규칙을 각각 대조해 보고서야 판정 규칙을 확정할 수 있었다.
- 왜 이 방법이어야 하는지를 한계에서부터 짚고 들어가면 구현이 흔들리지 않는다. 대각화가 정방행렬에서만 된다는 사실에서 SVD로 넘어온 정리가, 뒤에서 U를 열공간의 기저로 다루는 모든 조작의 근거가 됐다.