[Linear algebra] 3.5 PCA (3)

JKH·약 8시간 전
0

선형대수

목록 보기
12/12

이미지 PCA

이번에는 사람의 얼굴 데이터를 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()

이 이미지들로부터 두 번째 주성분은 미소짓는 얼굴과 그렇지 않은 얼굴 이미지의 차이를 나타낸다는 것을 알 수 있다.

연습 문제 3.5.2

올리베티 얼굴 이미지에서 다른 사람의 얼굴을 선택하여 위와 같이 두 가지 주성분을 구하라. 각 주성분은 어떤 이미지 특성을 나타내는가?
✏️

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는 눈을 크게 뜬지 작게 뜬지의 차이를 나타낸다.

profile
Connecting my favorite things

0개의 댓글