#!/usr/bin/env python3


얼굴이미지에 NMF 적용하기


NMF의 핵심 매개변수는 추출할 성분의 갯수

보통 이 값은 특성의 갯수보다 작음(픽셀하나가 두개의 성분으로 나뉘어 표현될 수 있음)


1. NMF를 사용하여 데이터를 재구성하는데 성분의 갯수가 어떤 영향을 주는지 살펴보기

# library import

from sklearn.datasets import fetch_lfw_people

import matplotlib

import matplotlib.pyplot as plt

import numpy as np

import mglearn


# matplotlib 설정

matplotlib.rc('font', family='AppleGothic'# 한글 설정

plt.rcParams['axes.unicode_minus'] = False # 축 -설정


people = fetch_lfw_people(min_faces_per_person=20, resize=0.7, color=False) # 겹치지 않는 최소사람 수, 사이즈비율, 흑백

images_shape = people.images[0].shape # 이미지 한개의 사이즈 저장


counts = np.bincount(people.target) # 갯수 새기

for i, (count, names) in enumerate(zip(counts, people.target_names)): # counts와 people.target_names에서 원소 하나씩 할당하여 인덱스 부여

    print('{0:25} {1:3}'.format(names, count), end='\t'# names에서 25자리, count에서 3자리, 끝에 탭할당

    if i % 3 == 0: # 3의 배수

        print() # 개행


idx = np.zeros(people.target.shape, dtype=np.bool) # False배열 3023개 설정

people_target_unq = np.unique(people.target) # target에서 unique값 할당

for target in people_target_unq: # perploe_target_unq에서 원소 하나씩 target에 할당

    idx[np.where(people.target == target)[0][:50]] = 1 # 전체 리스트 3023개에서 target과 일치하는 것의 수가를 50개 까지 True로 변경

x_people = people.data[idx] # 훈련 데이터

y_people = people.target[idx] # 테스트 데이터


# library import

from sklearn.preprocessing import MinMaxScaler

from sklearn.model_selection import train_test_split


scaler = MinMaxScaler()

x_people_scaled = scaler.fit_transform(x_people) # 훈련 데이터를 적용하여 전처리


x_train, x_test, y_train, y_test = \ 

  train_test_split(x_people_scaled, y_people, # 분할할 데이터

                   stratify=y_people, random_state=0, test_size=0.3) # 그룹화할데이터, 랜덤상태, 테스트 비율


mglearn.plots.plot_nmf_faces(x_train, x_test, images_shape)

plt.gray() # 그림 흑백

plt.show() # 그림 출력

NMF 성분 갯수에 따른 얼굴 이미지의 재구성


변환을 되돌린 결과는 PCA를 사용했을 때와 비슷하지만 품질이 떨어짐

PCA가 재구성 측면에서 최선의 방향을 찾기 때문

NMF는 데이터에 있는 유용한 패턴을 찾는데 활용


2. 비음수 성분 15개로 시각화

# library import , model 생성 , 학습

from sklearn.decomposition import NMF

nmf = NMF(n_components=15, random_state=0)

nmf.fit(x_train)


# 모델 적용

x_train_nmf = nmf.transform(x_train)

x_test_nmf = nmf.transform(x_test)


# 시각화

fig, axes = plt.subplots(3, 5, # axes에 plots객체를 3x5 만큼 할당

                         subplot_kw={'xticks':(), 'yticks':()}) # subplot 축 없애기


for i, (comp, ax) in enumerate(zip(nmf.components_, axes.ravel())): # nmf.components_, axes.ravel()에서 하나씩 comp와 ax에 인덱스부여

    ax.imshow(comp.reshape(images_shape)) # images_shape= (87, 65)

    ax.set_title('성분 {}'.format(i)) # subplot 타이틀

plt.gray() # 그림 흑백

plt.show()

얼굴 데이터셋에서 NMF로 찾은 성분 15개


이 성분들은 모두 양수 값이어서 PCA 성분보다 훨씬 더 얼굴 원형처럼 보임


비음수 성분이 특별히 강하게 나타난 이미지 분석(1)

### argsort : 자료를 정렬한 것의 index


idx = np.argsort(x_train_nmf[:, 3])[::-1] # [start : end : stpe] , step=-1 :내림차순

fig, axes = plt.subplots(2, 5, # subplots객체 (2x5) 를 axes에 할당

                         subplot_kw={'xticks':(), 'yticks':()}) # subplots에 축 없애기


for idx, ax in zip(idx, axes.ravel()): # idx와 aexs.ravel()을 하나씩 idx, ax에 할당

    ax.imshow(x_train[idx].reshape(images_shape))

plt.gray() # 그림 흑백

plt.show() # 그림 출력

성분 3의 계수가 큰 얼굴들 10개


비음수 성분이 특별히 강하게 나타난 이미지 분석(2)

idx = np.argsort(x_train_nmf[:, 10])[::-1] # [start : end : stpe] , step=-1 :내림차순

fig, axes = plt.subplots(2, 5, # subplots객체 (2x5) 를 axes에 할당

                         subplot_kw={'xticks':(), 'yticks':()}) # subplots에 축 없애기


for idx, ax in zip(idx, axes.ravel()): # idx와 aexs.ravel()을 하나씩 idx, ax에 할당

    ax.imshow(x_train[idx].reshape(images_shape)) 

plt.gray() # 그림 흑백

plt.show() # 그림 출력   

성분 10의 계수가 큰 얼굴들 10개


성분 3의 계수가 큰 얼굴은 왼쪽으로 돌아가 있고 성분 10의 계수가 큰 얼굴을 왼쪽으로 돌아가 있음을 확인

이런 패턴으로 데이터를 추출




#!/usr/bin/env python3


NMF(비음수 행렬 분해)


NMF(non-negative matrix factorization)는 유용한 특성을 뽑아내기 위한 또 다른 비지도 학습 알고리즘

NMF에서는 음수가 아닌 성분과 계수 값을 찾음.

즉 주성분과 계수가 모두 0보다 크거나 같아야 함 ==> 이 방식은 음수가 아닌 특성을 가진 데이터에만 적용


음수가 아닌 가중치 합으로 데이터를 분해하는 기능오디오 트랙이나 음악처럼 독립된 소스를 추가하여 만들어진 데이터에 특히 유용. 

이럴 때 NMF는 섞여 있는 데이터에서 원본 성분을 구분할 수 있음

음수로 된 성분이나 계수가 만드는 상쇄 효과를 이해하기 어려운 PCA보다 대체로 NMF의 주성분이 해석하기는 쉬움


인위적 데이터에 NMF 적용하기

NMF로 데이터를 다루려면 주어진 데이터가 양수인지 확인해야함

즉 원점 (0, 0)에서 상대적으로 어디에 놓여 있는지(위치 벡터)가 NMF에서는 중요함

그렇기 대문에 원점 (0, 0)에서 데이터로 가는 방향을 추출한 것으로 음수 미포함 성분을 이해가능


# library import

import mglearn

import matplotlib

import matplotlib.pyplot as plt


# matplotlib 설정

plt.rcParams['axes.unicode_minus'] = False # 축- 설정

matplotlib.rc('font', family='AppleGothic'# 한글출력


# 예제 데이터

mglearn.plots.plot_nmf_illustration()

plt.show() # 그래프 출력

NMF로 찾은 성분이 2개일때(왼쪽)와 1개일 때(오른쪽)


왼쪽은 성분이 둘인 NMF로, 데이터셋의 모든 포인트를 양수로 이뤄진 두 개의 성분으로 표현할 수 있음.

데이터를 완벽하게 재구성할 수 있을 만큼 성분이 아주 많다면(특성의 수가 많다면) 알고리즘은 데이터의 각 특성의 끝에 위치한 포인트를 가리키는 방향(위치벡터)을 선택


하나의 성분만을 사용한다면 NMF는 데이터를 가장 잘 표현할 수 있는 평균으로 향하는 성분을 만듬

PCA와는 반대로 성분 개수를 줄이면 특정 방향이 제거되고 전체성분이 완전 바뀜

NMF에서 성분은 특정 방식으로 정렬되어 있지도 않아서 첫번째, 두번째 같은 것이 없고 모든 성분은 동등하게 취급


NMF는 무작위로 초기화하기 때문에 난수 생성 초기값에 따라 결과가 달라짐 두 개의 성분으로 모든 데이터를 완벽하게 나타낼 수 있는 간단한 데이터에서는 난수가 거의 영향을 주지 않음(성분의 크기나 순서가 바뀔 수는 있음)


인위적인 데이터셋을 사용한 NMF분석

# library import

import mglearn

import matplotlib

import matplotlib.pyplot as plt


# matplotlib 설정

matplotlib.rc('font', family='AppleGothic'# 한글 출력

plt.rcParams['axes.unicode_minus'] = False # 축 - 설정


# data load

S = mglearn.datasets.make_signals() 


# visualization

plt.plot(S) # 라인그래프

plt.xlabel('time'# x 축

plt.ylabel('signal'# y 축

plt.show() # 그래프 출력

원본신호


3개가 섞인 신호만 관찰할 수 있는 상황

합쳐진 신호를 분해해서 원본 신호를 복원


NMF와 PCA를 통한 데이터 복원

# library import

from sklearn.decomposition import NMF 

from sklearn.decomposition import PCA

import numpy as np

A = np.random.RandomState(0).uniform(size=(100, 3)) # 100x3  형태의 균일분포에서 랜덤 데이터 생성

X = np.dot(S, A.T) # S(2000x3)에 A에 전치행렬(3X100)을 행렬 곱


print('X.shape \n측정데이터 형태:{}'.format(X.shape)) # (2000, 3)


nmf = NMF(n_components=3, random_state=0) # 주성분 3개, 랜덤상태

S_ = nmf.fit_transform(X) # nmf적용

print('S_.shape \n복원한 신호 데이터 형태: {}'.format(S_.shape)) # (2000, 3)


pca = PCA(n_components=3) # PCA주성분 3

H = pca.fit_transform(X) # PCA적용


# visualization

models = [X, S, S_, H] 

names = ['측정 신호(처음 3개)', '원본 신호', 'NMF로 복원' ,'PCA로 복원']


fig, axes = plt.subplots(4, # subplot의 객체4개를 axes에 할당

                         gridspec_kw={'hspace':0.5}, # 수평간격 비율

                         subplot_kw={'xticks':(), 'yticks':()}) # subplot의 축 눈금 없애기


for model, name, ax in zip(models, names, axes.ravel()): # models와 names, axes.ravel()에서 하나씩 선택

    ax.set_title(name) # 타이틀 이름

    ax.plot(model[:,:3]) # 모든행, :3 ==> 3개의 열

plt.show() # 그래프 출력

NMF와 PCA를 사용해 복원한 신호


NMF는 원본신호를 잘 복원했지만 PCA는 실패했고 데이터 변동의 대부분을 첫번째 성분을 사용해 나타냄

NMF로 생성한 성분은 순서가 없음을 알 수 있음

NMF 성분의 선수가 원본신호와 같지만 우연의 일치


PCA나 NMF처럼 데이터 포인트를 일정 개수의 성분을 사용해 가중치 합으로 분해하는 알고리즘은 다수 있음

ICA(독립 성분분석), FA(요인 분석), sparse coding(희소 분석)등이 있음 

    










+ Recent posts