728x90
반응형
1. 차원 축소
1.1) 개요
- Machine Learning 문제들은 많은 특성을 소유
- 특성이 많으면 훈련을 느리게 할 뿐 아니라 좋은 솔루션을 찾기가 어려워 짐 - 차원의 저주
1.2) 종류
- 피처 선택 : 특정 피처에 종속성이 강한 불필요한 피처는 제거하고 데이터의 특성을 잘 나타내는 주요 피처만 선택
- 피처 추출 :
기존의 피처들을 저차원의 중요 피처로 압축해서 추출
기존 피처가 압축된 것이기 때문에 기존의 피처와는 다른 값
더 함축적인 요약 특성으로 추출
1.3) 주의
- 차원 축소를 수행하면 일부 정보가 유실되기 때문에 훈련 속도는 빨라지지만 시스템의 성능이 조금 나빠 질 수 있으며 파이프라인이 복잡해짐
- 이미지나 텍스트에서 차원 축소를 통해 잠재적인 의미를 찾을 수 있음.
매우 많은 픽셀로 이루어진 이미지 데이터에서 모든 차원을 전부 활용해서 이미지 분류를 하게되면 과대 적합 될 가능성이 있기 때문에 차원을 축소하는 것이 예측 성능에 훨씬 도움이 될 수 있음.
2. 차원의 저주
- 고차원 공간에서는 많은 것이 상당히 다르게 작동
-
단위 면적(1 X 1 사각형) 안에 있는 점을 무작위로 선택한다면 경계선에서 0.001 이내에 위치할 기능성은 0.4% 정도 이지만 10,000차원의 단위 면적을 가진 초입방체에서는 이 가능성이 99.999999% 보다 커짐
-
차원이 커지면 대부분의 훈련 데이터가 서로 멀리 떨어져 있게 되며 이는 새로운 샘플도 훈련 샘플과 멀리 떨어져 있을 기능성이 높다는 의미
외삽(extrapolation - 보외법) 을 훨씬 더 많이 수행해야 해서 불확실성이 높아짐.
보간은 두개의 값 사이를 추정하기는 하는데 최소값과 최대값 사이의 중간 값들을 유추
외삽은 다른 값들 사이의 관계에 기초해서 예측
- 차원이 높아지면 아주 많은 샘플이 필요
3. 투영
- 차원이 많은 경우 거의 대부분은 특성은 변화가 없고 다른 특성들이 서로 강하게 연관되어 있다라는 가정
- 스위스 롤 같은 경우 문제가 발생
4. Manifold 방법
- 고차원 공간에서 휘어지거나 뒤틀린 모양
5. PCA(Principal Component Analysis - 주성분 분석)
5.1) 개요
- 여러 변수 간에 존재하는 상관관계를 이용해서 이를 대표하는 주성분을 추출해 차원을 축소하는 방법
- 일반적으로 변수들은 공변(covey - 같이 변하는 성질)하므로 이러한 공변이 어떻게 일어나는지 알아내는 기법
식당에서 음식 값과 팁은 어느 정도 관계가 있는데 이 관계를 찾아서 하나로 만들어내는 것이 주성분 분석
- 주성분 분석에서는 주성분에 대한 각 특성의 기여도를 찾아내는 작업
5.2) 분산 보존
- 여러 개의 축을 더 적은 개수의 축으로 변환을 할 때 분산의 값이 큰 방향으로 투영을 해야 한다.
분산의 값이 큰 방향으로 투영해야 정보가 가장 적게 손실 되기 때문
- 분산 보존의 원리를 적용해서 만든 축을 주성분이라고 합니다
이 주성분은 원점에 맞추어진 단위 벡터 형태로 표현하기 때문에 방향은 + 나 - 방향이 될 수 있음
- 선형 대수 입장에서 보면 PCA는 입력 데이터의 공분산 행렬을 고유 값 분해하고 이렇게 구한 고유 벡터에 입력 데이터를 선형 변환한 것이고 이 고유 벡터가 PCA의 주성분 벡터
이 벡터의 크기를 고유 값이라고 합니다.
행렬 분해를 이용해서 표현
5.3) 수행 순서
- 입력 데이터 세트의 공분산 행렬을 생성
- 공분산 행렬의 고유 벡터와 고유 값을 계산
- 고유 값이 큰 순서대로 K(PCA의 차수) 개 만큼 고유 벡터를 추출
- 고유 값이 큰 순서대로 추출된 고유 벡터를 가지고 새로운 입력 데이터를 추출
5.4) SVD(특이값 분해)
- 고차원의 데이터를 저차원으로 분해하는 기법
- 추천 시스템 만들 때 많이 활용
- numpy.linalg.svd 함수를 이용해서 수행 가능
from numpy.linalg import svd
m = 60
w1, w2 = 0.1, 0.3
noise = 0.1
angles = np.random.rand(m) * 3 * np.pi / 2 - 0.5
X = np.empty((m, 3))
X[:, 0] = np.cos(angles) + np.sin(angles)/2 + noise * np.random.randn(m) / 2
X[:, 1] = np.sin(angles) * 0.7 + noise * np.random.randn(m) / 2
X[:, 2] = X[:, 0] * w1 + X[:, 1] * w2 + noise * np.random.randn(m)
# X_centered = X - X.mean(axis=0)
# # 특이값 분해 수행
# U, s, Vt = np.linalg.svd(X_centered)
# #주성분
# c1 = Vt.T[:, 0]
# c2 = Vt.T[:, 1]
# # print(c1)
# # print(c2)
# #실제 변환된 결과
# W2 = Vt.T[:, :2]
# X2D = X_centered.dot(W2)
# print(X2D) # 차원이 1개 줄어듬
from sklearn.decomposition import PCA
#2개의 주성분을 추출해주는 PCA 인스턴스
pca = PCA(n_components=2)
#데이터를 2차원으로 줄이기
X2D = pca.fit_transform(X)
print(X2D)
- 위의 작업을 직접 할 필요는 없고 sklearn 의 PCA 클래스가 SVD를 이용한 주성분 분석을 구현하고 있음.
n_components 파라미터에 원하는 주성분의 개수만 설정하면 됩니다.
- 훈련을 하고 난 후 PCA 인스턴스의 explained_variance_ratio_속성에 설명 가능한 분산의 비율을 저장
이 개수는 주성분의 개수
#분산의 비율
print("설명 가능한 분산의 비율:", pca.explained_variance_ratio_)
##result
설명 가능한 분산의 비율: [0.84020148 0.15264767]
#첫번째 주성분이 0.8402 만큼의 분산을 설명
#두번째 주성분은 0.1526 만큼의 분산을 설명
# 2개의 주성분이 있으면 0.9928 만큼을 설명
print("잃어버린 분산의 비율:", (1- pca.explained_variance_ratio_.sum()))
## result
잃어버린 분산의 비율: 0.007150848406142218
- PCA 인스턴스의 inverse_transform 이라는 함수를 이용하면 원래의 데이터로 복원이 가능 - 원래의 데이터로 완전 복원은 안됨
from numpy.linalg import svd
m = 60
w1, w2 = 0.1, 0.3
noise = 0.1
angles = np.random.rand(m) * 3 * np.pi / 2 - 0.5
X = np.empty((m, 3))
X[:, 0] = np.cos(angles) + np.sin(angles)/2 + noise * np.random.randn(m) / 2
X[:, 1] = np.sin(angles) * 0.7 + noise * np.random.randn(m) / 2
X[:, 2] = X[:, 0] * w1 + X[:, 1] * w2 + noise * np.random.randn(m)
print(X[:5])
pca = PCA(n_components=2)
# 데이터 2차원으로 축소
X2D = pca.fit_transform(X)
# 데이터를 3차원로 복원
X3D_inverse = pca.inverse_transform(X2D)
print(X3D_inverse[:5])
##result
[[ 0.49231547 0.71738671 0.11025581]
[-0.97563474 -0.02066607 -0.11084926]
[ 0.22656153 0.73353587 0.39482237]
[-1.04617574 -0.54440424 -0.28796054]
[-0.11416698 0.50738498 0.01751308]]
[[ 0.47766488 0.67068561 0.25748951]
[-0.97603232 -0.02193342 -0.1068537 ]
[ 0.23931961 0.77420426 0.26660791]
[-1.04692881 -0.54680478 -0.28039241]
[-0.12575168 0.47045691 0.13393552]]
#분산의 손실에 따른 값의 차이가 있음
#2개의 배열이 같은지 확인하는 방법
print(np.allclose(X, X3D_inverse))
##result
False // 같으면 True라고 출력
#오차의 평균
print(np.mean(np.sum(np.square(X3D_inverse - X), axis=1)))
##result
0.00835058802078319
#계산된 주성분 확인 - components
#주성분 확인
print(pca.components_)
##result
[[-0.94107087 -0.28396367 -0.18371245]
[ 0.32476078 -0.9103664 -0.25644384]]
5.6) 주식 데이터를 이용한 주성분 분석
- 주성분은 분산의 크기가 큰 방향으로 생성 - 분산 보전의 법칙
path = './python_statistics-main/data/sp500_data.csv.gz'
#데이터 가져오기
sp500_px = pd.read_csv(path, index_col=0)
oil_px = sp500_px[['XOM', 'CVX']]
print(oil_px.head())
#2개로 주성분 분석
from sklearn.decomposition import PCA
pca = PCA(n_components=2)
pca.fit(oil_px)
loadings = pd.DataFrame(pca.components_, columns=oil_px.columns)
print(loadings)
##result
XOM CVX
0 -0.664711 -0.747101
1 0.747101 -0.664711
- 결과 해석
2개의 주성분으로 생성한 경우 첫번째 성분은 상관관계를 의미하는 계수이고, 두번째 성분은 값이 달라지는 지점을 의미
2개의 주식 데이터를 이용한 PCA
def abline(slope, intercept, ax):
x_vals = np.array(ax.get_xlim())
return (x_vals, intercept + slope * x_vals)
ax = oil_px.plot.scatter(x='XOM', y='CVX', alpha=0.3, figsize=(4, 4))
ax.set_xlim(-3, 3)
ax.set_ylim(-3, 3)
ax.plot(*abline(loadings.loc[0, 'CVX'] / loadings.loc[0, 'XOM'], 0, ax),
'--', color='C1')
ax.plot(*abline(loadings.loc[1, 'CVX'] / loadings.loc[1, 'XOM'], 0, ax),
'--', color='C1')
plt.tight_layout()
plt.show()
- 여러 개의 속성으로 확장하는 것이나 여러 개의 주성분을 추출하는 것도 가능
- 주성분 분석은 수치형 데이터에만 사용 가능
- 범주형은 사용하지 못함
범주형이나 문자열 그리고 날짜 타입은 분산이 근본적으로 없습니다. (구할 의미가 없음)
범주형 or 문자열은 개수나 분포의 비율이 의미를 갖는 데이터이지 평균이나 합계를 구하는 데이터가 아님
5.7) 주성분에서의 중요도
- 주성분의 상대적인 중요도를 표시해주는 시각화 방법을 scree plot 이라고 합니다.
- 상위 주성분들의 중요도를 파악해서 피처를 제거하고 분류나 회귀를 적용하는 것도 좋은 방법 중에 하나가 됩니다.
syms = sorted(['AAPL', 'MSFT', 'CSCO', 'INTC', 'CVX', 'XOM', 'SLB', 'COP',
'JPM', 'WFC', 'USB', 'AXP', 'WMT', 'TGT', 'HD', 'COST'])
top_sp = sp500_px.loc[sp500_px.index >= '2011-01-01', syms]
sp_pca = PCA()
sp_pca.fit(top_sp)
explained_variance = pd.DataFrame(sp_pca.explained_variance_)
ax = explained_variance.head(10).plot.bar(legend=False, figsize=(4, 4))
ax.set_xlabel('Component')
plt.tight_layout()
plt.show()
#설명 가능한 분산의 비율을 확인
print(sp_pca.explained_variance_)
##result
[3.21470467 1.03661197 0.75894515 0.44914799 0.38393228 0.30076841
0.20528539 0.1745774 0.15226117 0.15054555 0.12855846 0.10624002
0.05957442 0.04993226 0.02993861 0.0256697 ]
#5개의 주성분에 미치는 중요도를 데이터프레임으로 변환
loadings = pd.DataFrame(sp_pca.components_[0:5, :],
columns=top_sp.columns)
print(loadings)
##result
AAPL AXP COP COST CSCO CVX HD \
0 -0.300825 -0.246332 -0.261529 -0.273634 -0.064059 -0.444490 -0.207983
1 -0.505116 -0.139426 0.174212 -0.416307 -0.031939 0.289373 -0.278002
2 -0.786730 0.135458 -0.002367 0.465862 -0.007524 0.082374 0.166320
3 -0.120586 0.061814 -0.206026 0.092596 0.003904 -0.577665 0.162814
4 0.111576 -0.596666 -0.005813 0.555529 -0.039860 0.109016 -0.185488
INTC JPM MSFT SLB TGT USB WFC \
0 -0.076956 -0.196397 -0.105012 -0.481786 -0.148833 -0.116421 -0.145684
1 -0.033898 -0.040723 -0.053954 0.472494 -0.228123 -0.054796 -0.047427
2 -0.003518 0.062261 0.016248 -0.194822 0.160833 0.048976 0.041932
3 -0.001605 0.057687 -0.012558 0.680914 0.109895 0.016752 0.018614
4 -0.072047 -0.385160 -0.077135 0.181332 -0.055557 -0.155440 -0.216425
WMT XOM
0 -0.122304 -0.317952
1 -0.222889 0.154192
2 0.175806 0.090167
3 0.058439 -0.295204
4 0.091541 0.013277
- 시각화도 진행
maxPC = 1.01 * np.max(np.max(np.abs(loadings.loc[0:5, :])))
f, axes = plt.subplots(5, 1, figsize=(5, 5), sharex=True)
for i, ax in enumerate(axes):
pc_loadings = loadings.loc[i, :]
colors = ['C0' if l > 0 else 'C1' for l in pc_loadings]
ax.axhline(color='#888888')
pc_loadings.plot.bar(ax=ax, color=colors)
ax.set_ylabel(f'PC{i+1}')
ax.set_ylim(-maxPC, maxPC)
plt.tight_layout()
plt.show()
- 분산이 적은 데이터를 그대로 데이터 분석에 활용하는 것은 데이터 낭비 & 과대 적합의 요인이다.
5.8) 적절한 차원 수 선택
- 데이터 시각화를 위해서 차원을 축소할 때는 2개 나 3개로 축소 - 그래프는 2차원이나 3차원만 표시 가능
- 차원 수는 충분한 분산 비율을 설명될 때 까지 더해가면서 설정
- PCA 인스턴스를 만들 때 n_components 에 0.0에서 1.0 사이의 숫자를 설정하면 설명 가능한 분산 비율이 적용되서 PCA를 수행 (가장 권장)
- 차원 수에 대한 분산 설명 비율을 그래프로 그려서 elbow를 찾아서 설정하기도 함
5.9) MNIST 이미지 데이터에서 적절한 차원 수 찾기 - 압축을 해서 예측 작업에 사용
- 데이터를 가져오기
#데이터 가져오기
from sklearn.datasets import fetch_openml
from sklearn.model_selection import train_test_split
mnist = fetch_openml('mnist_784', version=1, as_frame=False)
mnist.target = mnist.target.astype(np.uint8)
X = mnist["data"]
y = mnist["target"]
X_train, X_test, y_train, y_test = train_test_split(X, y)
print(X_train.shape)
(52500, 784)
- 적절한 차원의 개수를 찾기 위해서 차원의 개수를 설정하지 않고 분산의 비율보다 큰 개수를 찾기
- 결과를 시각화해서 엘보우 찾기
#분산 비율 이상의 차원 개수 찾기
pca = PCA()
pca.fit(X_train)
cumsum = np.cumsum(pca.explained_variance_ratio_)
d = np.argmax(cumsum >= 0.95) + 1
print(d)
pca = PCA(n_components=0.95)
X_reduced = pca.fit_transform(X_train)
print(pca.n_components_)
##result
154
plt.figure(figsize=(6,4))
plt.plot(cumsum, linewidth=3)
plt.axis([0, 400, 0, 1])
plt.xlabel("차원")
plt.ylabel("분산 비율")
plt.plot([d, d], [0, 0.95], "k:")
plt.plot([0, d], [0.95, 0.95], "k:")
plt.plot(d, 0.95, "ko")
plt.annotate("엘보", xy=(65, 0.85), xytext=(70, 0.7),
arrowprops=dict(arrowstyle="->"), fontsize=16)
plt.grid(True)
#save_fig("explained_variance_plot")
plt.show()
5.10) 차원 압축
- 차원을 축소하면 데이터의 크기가 줄어듬
분산의 크기를 95% 정도 유지하면 차원의 개수가 154개
대부분의 분산은 유지되고 데이터의 크기는 20% 정도로 압축
압축을 하면 SVM 같은 알고리즘에서 속도 향상이 가능
- inverse_transform 이라는 함수를 이용하면 원래의 차원으로 복원
투영 과정에서 일정량의 정보를 유실했기 때문에, 원본과 동일하지는 않음.
원본 데이터와 재구성된 데이터(압축후 복원된 데이터) 사이의 평균 제곱 거리를 재구성 오차(reconstruction error)
#압축을 위한 pca
pca = PCA(n_components=154)
#154 개의 차원으로 압축
X_reduced = pca.fit_transform(X_train)
#데이터 복원
X_recovered = pca.inverse_transform(X_reduced)
#데이터를 이미지로 출력해주는 함수
def plot_digits(instances, images_per_row=5, **options):
size = 28
images_per_row = min(len(instances), images_per_row)
images = [instance.reshape(size,size) for instance in instances]
n_rows = (len(instances) - 1) // images_per_row + 1
row_images = []
n_empty = n_rows * images_per_row - len(instances)
images.append(np.zeros((size, size * n_empty)))
for row in range(n_rows):
rimages = images[row * images_per_row : (row + 1) * images_per_row]
row_images.append(np.concatenate(rimages, axis=1))
image = np.concatenate(row_images, axis=0)
plt.imshow(image, cmap = mpl.cm.binary, **options)
plt.axis("off")
plt.figure(figsize=(7, 4))
plt.subplot(121)
plot_digits(X_train[::2100])
plt.title("Original", fontsize=16)
plt.subplot(122)
plot_digits(X_recovered[::2100])
plt.title("Compressed", fontsize=16)
5.11) 랜덤 PCA
- PCA 인스턴스를 만들 때 svd_solver 매개변수에 randomized를 설정하면 sklearn은 랜덤 PCA를 수행하는데 랜덤 PCA는 처음 몇 개의 주성분에 대한 근사값을 빠르게 찾는 알고리즘
- 완전한 SVD를 이용하는 방식은 시간 복잡도가 O(m*n*n) + O(n*n*n)
랜덤 PCA 는 O(m* 주성분의 개수 * 주성분의 개수) + O(주성분의 개수 * 주성분의 개수 * 주성분의 개수)
- sklearn 의 PCA의 svd_solver의 기본 값은 auto 인데 auto는 주성분의 개수나 데이터의 개수나 차원의 개수의 80% 보다 작으면 랜덤 PCA를 수행하고 그렇지 않으면 완전 SVD 방식을 사용
- 완전한 SVD를 수행하고자 하면 svd_solver에 full을 설정
5.12) 점진적 PCA
- PCA는 SVD 알고리즘을 수행하기 위해서 전체 데이터를 메모리에 로드하고 수행을 해야 합니다.
- 점진적 PCA(Incremental PCA - IPCA)는 훈련 데이터를 미니 배치(mini batch)로 나눈 뒤 PCA 알고리즘에 한 번에 하나의 배치를 주입하고 훈련을 하는 방식
- 데이터가 아주 클 때 그리고 온라인 학습(데이터가 수시로 대입되는 경우) 을 할 때 유용
- numpy의 memmap 이라는 클래스를 사용하면 하드 디스크의 이진 파일에 저장된 배열을 메모리에 들어있는 것처럼 사용할 수 있는데 이 클래스는 필요할 때 데이터를 메모리에 적재
- sklearn 에서는 IncrementalPCA 클래스를 이용해서 이 기능을 제공 - 데이터를 나누어서 훈련한 후 예측을 할 수 있음
반응형
LIST
'LG 헬로비전 DX DATA SCHOOL > Python' 카테고리의 다른 글
Association_Analysis (연관 분석) (0) | 2023.09.07 |
---|---|
NLP(Natural Language Processing) 자연어 처리 (0) | 2023.09.05 |
군집 분석 2 (0) | 2023.09.04 |
(Python)군집 분석 및 과일 이미지 군집 분석 실습 (0) | 2023.09.01 |
회귀분석 (0) | 2023.08.30 |