이번에는 사람의 얼굴 데이터를 PCA로 분석하자. 올리베티 얼굴 사진 중 특정 인물의 사진 10장을 데이터로 사용한다.
from sklearn.datasets import fetch_olivetti_faces
faces_all = fetch_olivetti_faces()
K = 20 # 20번 인물의 사진만 선택
faces = faces_all.images[faces_all.target == K]
N = 2
M = 5
fig = plt.figure(figsize=(10, 5))
plt.subplots_adjust(top=1, bottom=0, hspace=0, wspace=0.05)
for i in range(N):
for j in range(M):
k = i * M + j
ax = fig.add_subplot(N, M, k+1)
ax.imshow(faces[k], cmap=plt.cm.bone)
ax.grid(False)
ax.xaxis.set_ticks([])
ax.yaxis.set_ticks([])
plt.suptitle("올리베티 얼굴 사진")
plt.tight_layout()
plt.show()
이 사진을 주성분이 2개인 PCA 분석을 하면 결과는 다음과 같다.
from sklearn.decomposition import PCA
pca3 = PCA(n_components=2)
X3 = faces_all.data[faces_all.target == K]
W3 = pca3.fit_transform(X3)
X32 = pca3.inverse_transform(W3)
다음은 주성분 분석으로 근사화한 이미지를 표시한 것이다.
N = 2
M = 5
fig = plt.figure(figsize=(10, 5))
plt.subplots_adjust(top=1, bottom=0, hspace=0, wspace=0.05)
for i in range(N):
for j in range(M):
k = i * M + j
ax = fig.add_subplot(N, M, k+1)
ax.imshow(X32[k].reshape(64, 64), cmap=plt.cm.bone)
ax.grid(False)
ax.xaxis.set_ticks([])
ax.yaxis.set_ticks([])
plt.suptitle("주성분 분석으로 근사화한 올리베티 얼굴 사진")
plt.tight_layout()
plt.show()
이 얼굴들은 모두 평균값과 2개의 주성분 얼굴의 각기 다른 선형조합이다.
평균값과 2개의 주성분이 나타내는 얼굴을 이미지로 표시하면 다음과 같다. 주성분이 나타내는 얼굴을 아이겐페이스(Eigen Face) 라고도 한다.
face_mean = pca3.mean_.reshape(64, 64)
face_p1 = pca3.components_[0].reshape(64, 64)
face_p2 = pca3.components_[1].reshape(64, 64)
plt.subplot(131)
plt.imshow(face_mean, cmap=plt.cm.bone)
plt.grid(False)
plt.xticks([])
plt.yticks([])
plt.title("평균 얼굴")
plt.subplot(132)
plt.imshow(face_p1, cmap=plt.cm.bone)
plt.grid(False)
plt.xticks([])
plt.yticks([])
plt.title("주성분 1")
plt.subplot(133)
plt.imshow(face_p2, cmap=plt.cm.bone)
plt.grid(False)
plt.xticks([])
plt.yticks([])
plt.title("주성분 2")
plt.show()
이 그림만 보아서는 주성분 얼굴이 각각 어떤 의미를 가지는지 알기 어려우므로 평균 얼굴에 주성분 얼굴을 더한 모습을 같이 그려보자. 우선 평균 얼굴에 첫 번째 주성분을 더하면 다음과 같다.
N = 2
M = 5
fig = plt.figure(figsize=(10, 5))
plt.subplots_adjust(top=1, bottom=0, hspace=0, wspace=0.05)
for i in range(N):
for j in range(M):
k = i * M + j
ax = fig.add_subplot(N, M, k+1)
w = 1.5 * (k - 5) if k < 5 else 1.5 * (k - 4)
ax.imshow(face_mean + w * face_p1, cmap=plt.cm.bone)
ax.grid(False)
ax.xaxis.set_ticks([])
ax.yaxis.set_ticks([])
plt.title("주성분1의 비중={}".format(w))
plt.suptitle("평균 얼굴에 주성분1을 더한 사진")
plt.tight_layout()
plt.show()
이 이미지들로부터 첫 번째 주성분은 왼쪽과 오른쪽에서 바라본 얼굴 이미지의 차이를 나타낸다는 것을 알 수 있다. 두 번째 주성분을 평균 얼굴에 더하면 다음과 같다.
N = 2
M = 5
fig = plt.figure(figsize=(10, 5))
plt.subplots_adjust(top=1, bottom=0, hspace=0, wspace=0.05)
for i in range(N):
for j in range(M):
k = i * M + j
ax = fig.add_subplot(N, M, k+1)
w = 1.5 * (k - 5) if k < 5 else 1.5 * (k - 4)
ax.imshow(face_mean + w * face_p2, cmap=plt.cm.bone)
ax.grid(False)
ax.xaxis.set_ticks([])
ax.yaxis.set_ticks([])
plt.title("주성분2의 비중={:.1f}".format(w))
plt.suptitle("평균 얼굴에 주성분2를 더한 사진")
plt.tight_layout()
plt.show()
이 이미지들로부터 두 번째 주성분은 미소짓는 얼굴과 그렇지 않은 얼굴 이미지의 차이를 나타낸다는 것을 알 수 있다.
올리베티 얼굴 이미지에서 다른 사람의 얼굴을 선택하여 위와 같이 두 가지 주성분을 구하라. 각 주성분은 어떤 이미지 특성을 나타내는가?
✏️
from sklearn.datasets import fetch_olivetti_faces
import matplotlib.pyplot as plt
import numpy as np
from sklearn.decomposition import PCA
faces_all = fetch_olivetti_faces()
K = 7 # 7번 인물의 사진만 선택
faces = faces_all.images[faces_all.target == K]
# faces.shape = (10, 64, 64) 인 3차원 데이터
# 이 3차원 데이터를 2차원으로 pca 해서 주성분 분석하기
# 7번 인물의 olivetti_faces를 plot하는 코드
################################################################
# N = 2
# M = 5
# fig = plt.figure(figsize=(10, 5))
# plt.subplots_adjust(top=1, bottom=0, hspace=0, wspace=0.05)
# for i in range(N):
# for j in range(M):
# k = i * M + j
# ax = fig.add_subplot(N, M, k+1)
# ax.imshow(faces[k], cmap=plt.cm.bone)
# ax.grid(False)
# ax.xaxis.set_ticks([])
# ax.yaxis.set_ticks([])
# plt.suptitle("olivetti_faces of 7th person")
# plt.tight_layout()
# plt.show()
################################################################
pca3 = PCA(n_components=2)
# .images는 64 by 64, .data는 4096로 vectorized form으로 불러온다.
X3 = faces_all.data[faces_all.target == K]
W3 = pca3.fit_transform(X3)
X32 = pca3.inverse_transform(W3)
# pca version - olivetti_faces of 7th person
################################################################
# N = 2
# M = 5
# fig = plt.figure(figsize=(10, 5))
# plt.subplots_adjust(top=1, bottom=0, hspace=0, wspace=0.05)
# for i in range(N):
# for j in range(M):
# k = i * M + j
# ax = fig.add_subplot(N, M, k+1)
# ax.imshow(X32[k].reshape(64, 64), cmap=plt.cm.bone)
# ax.grid(False)
# ax.xaxis.set_ticks([])
# ax.yaxis.set_ticks([])
# plt.suptitle("pca version - olivetti_faces of 7th person")
# plt.tight_layout()
# plt.show()
################################################################
# 평균과 주성분1, 2 계산
face_mean = pca3.mean_.reshape(64, 64)
face_p1 = pca3.components_[0].reshape(64, 64)
face_p2 = pca3.components_[1].reshape(64, 64)
# 평균 이미지에 주성분1 이미지의 비율을 달리하며 더해서 플롯하는 코드
################################################################
# N = 2
# M = 5
# fig = plt.figure(figsize=(10, 6))
# plt.subplots_adjust(top=1, bottom=0, hspace=0, wspace=0.05)
# for i in range(N):
# for j in range(M):
# k = i * M + j
# ax = fig.add_subplot(N, M, k+1)
# w = 1.5 * (k - 5) if k < 5 else 1.5 * (k - 4)
# ax.imshow(face_mean + w * face_p1, cmap=plt.cm.bone)
# ax.grid(False)
# ax.xaxis.set_ticks([])
# ax.yaxis.set_ticks([])
# plt.title("ratio of pc1={}".format(w))
# plt.suptitle("mean face + principal comp1")
# plt.tight_layout()
# plt.show()
################################################################
# 평균 이미지에 주성분2 이미지의 비율을 달리하며 더해서 플롯하는 코드
################################################################
# N = 2
# M = 5
# fig = plt.figure(figsize=(10, 6))
# plt.subplots_adjust(top=1, bottom=0, hspace=0, wspace=0.05)
# for i in range(N):
# for j in range(M):
# k = i * M + j
# ax = fig.add_subplot(N, M, k+1)
# w = 1.5 * (k - 5) if k < 5 else 1.5 * (k - 4)
# ax.imshow(face_mean + w * face_p2, cmap=plt.cm.bone)
# ax.grid(False)
# ax.xaxis.set_ticks([])
# ax.yaxis.set_ticks([])
# plt.title("ratio of pc2={:.1f}".format(w))
# plt.suptitle("mean face + principal comp2")
# plt.tight_layout()
# plt.show()
################################################################
✏️
주성분1은 왼쪽과 오른쪽에서 바라본 얼굴 이미지의 차이를 나타내고,
주성분2는 눈을 크게 뜬지 작게 뜬지의 차이를 나타낸다.