1. 차원 축소
- 머신 러닝의 많은 훈련 샘플들은 여러 개의 특성을 가지고 있다.
- 특성의 개수가 많으면 훈련이 느려지고 좋은 솔루션을 찾기 어려워진다. = 차원의 저주
- 실전에서는 특성 수를 크게 줄여 불가능한 문제를 가능한 범위로 변경해야 하는 경우가 많다.
1) 차원의 저주
- 고차원에서는 많은 것이 상당히 다르게 동작한다.
- 사각형 안에서 점을 무작위로 선택하면 경계선에서 0.001 정도 거리에 존재할 확률이 0.4% 정도 되는데
- 1만 차원이 되면 경계선이 늘어나서, 경계선 근처에 몰려 존재할 확률이 99.999999% 보다 커짐
- 대부분의 훈련 데이터가 서로 멀리 떨어져 있게 되고, 새로운 샘플도 멀리 떨어져있을 가능성이 높아진다.
예측을 위해서는 훨씬 더 많은 외삽을 수행해야 한다.- 외삽: 변수의 값을 추정하는 과정 중의 하나 변수들 간의 관계를 이용해서 값을 추정한다.
- 보간법: 하나의 속성에서 값과 값 사이의 데이터를 추정하는 것
10 20 30 ? 50 => 40
외삽: 여러 변수 사이에서 값을 추정하는 것
A B Target
10 23 15
22 32 29
10 11 ? - 외삽을 정확하게 하기 위해서는 샘플의 개수를 늘리는 방법이 있지만
일반적으로 비용이 많이 소모되고 피처가 많을 때는 아주 많은 샘플을 추가해야 한다. => 불가능
2) 차원 축소의 종류
feature selection 특성 선택
- 특정 피처에 종속성이 강한 불필요한 피처는 제거하고 데이터의 특성을 잘 나타내는 주요 피처만 선택
- 통계적인 방법을 이용해서 feature들의 중요도에 순위를 정해 결정
- but 정보 손실이 발생
- 동일한 문제를 해결하는 다른 데이터 세트에서는 중요도의 순위가 달라질 수 있음
- 구현 방법
- 필터 방식
- 전처리 단계에서 통계 기법을 사용해서 변수 채택
- 높은 상관계수의 feature를 사용
- 변수 중요도나 상관도에 관련 알고리즘
- 상관관계
- 불순도(트리 모델에서 사용하는 지니계수, 엔트로피)
- 카이 제곱 검정
- 래퍼 방식
- 예측 정확도 측면에서 가장 좋은 성능을 보이는 피처 집합을 뽑아내는 방법
- 변수 선택 평가 모델
- AIC (Akaike Information Criteria)
- BIC (Bayesian Information Criteria)
- 수정 R2 (결정계수)
- 여러 번 머신러닝 알고리즘을 수행해야 하기 때문에 시간과 비용이 높게 발생하지만
가장 좋은 피처의 집합을 찾는 원리이기 때문에 모델의 정확도를 위해서는 바람직한 방법 - 알고리즘
- Forward Selection(전진 선택법): 피처가 없는 상태에서부터 반복할 때마다 피처를 추가하면서 성능이 가장 좋아지는 지점을 찾는 방식
- Backward Selection(후진 소거법): 모든 피처를 가지고 시작해서 덜 중요한 피처를 제거하면서 성능의 향상이 없을 때까지 수행하는 방식
- Stepwise Selection(단계별 선택법): 피처가 없는 상태에서 출발해서 전진 선택법과 후진 소거법을 번갈아가면서 수행하는 방식인데 가장 시간이 오래 걸리는 방식이지만 가장 우수한 예측 성능을 나타내는 변수 집합을 찾아낼 가능성이 높다.
- 임베디드 방식
- Filtering과 Wrapper의 조합
- 각각의 피처를 직접 학습하며 모델의 정확도에 기여하는 피처를 선택
- 계수가 0이 아닌 피처를 선택해서 더 낮은 복잡성으로 모델을 훈련하면서 학습 절차 최적화
- 라쏘, 릿지, 엘라스틱넷 등을 이용해서 규제를 추가할 수 있다.
피처 추출(feature extraction)
- 기존 피처를 저차원의 중요 피처로 압축해서 추출
- 새롭게 만들어진 피처는 기존의 피처와는 다른 값
- 새로 만들어지는 피처는 기존 피처를 단순하게 압축하는 것이 아니라 함축적으로 더 잘 설명할 수 있는 다른 공간으로 매핑해서 추출하는 것
- 함축적인 특성 추출은 기본 피처가 전혀 인지하기 어려웠던 잠재적인 요소를 추출
- 이 방식은 전체 피처를 사용
- 구현 방법
- Linea Methods: PCA, FA, LDA, SVD
- Non-Linear Methods: Kernel PCA, t-SNE, Isomap, Auto-Encoder(-> 생성형 AI)
3) 차원 축소의 목적
- 비용, 시간, 자원, 용량의 문제
- 불필요한 변수 저장: 용량 문제 발생
- 차원이 많아지면 비례해서 분석 시간 증가
- Overfitting 문제 해결
- 변수가 많으면 모델의 복잡도 증가: 일반화 가능성 낮음
- 민감도가 증가해서 오차가 커질 수 있는 가능성 높아짐
- 군집화 분석 결과가 좋지 않음
- 차원이 많아지면 경계선에 있을 가능성이 높아지고, 그러면 벡터 간의 거리가 유사해짐
- 군집 성능 저하
- 차원이 높으면 설명력이 떨어짐
- 2~3차원은 시각화가 가능하지만 3차원보다 큰 차원은 시각화가 어렵다.
- 잠재적인 요소를 추출할 수 있다.
- 차원을 축소하면 일부 정보가 유실되기 때문에 훈련 속도가 빨라지기는 하지만 성능은 나빠질 가능성이 높다.
2. 투영 (Projection)
- 물체의 그림자를 어떤 물체 위에 비추는 일 또는 그 비친 그림자를 의미
- 어떤 물체든 그림자는 2차원으로 표현될 수 있다는 원리
- 투영을 이용해서 구현된 알고리즘이 PCA, LDA 등
1) 특징
- MNIST 같은 이미지에는 잘 적용이 되지만 스위스 롤 같은 데이터에는 적용할 수 없다.
- 특성들의 값이 거의 비슷하고 다른 특성의 값이 많이 다르면 적용하기가 좋은데 그렇지 않은 경우는 적용하기 어렵다.
높이가 별 차이 안나니까 눌러서 높이를 없애버려도 차이가 없으니 두개만으로도 설명이 된다. MNIST 이미지 - 바깥 쪽은 거의 다 똑같다. (테두리는 항상 흰색) |
|
스위스롤: 누르면 안됨. 부딪혀버린다. 실제 값 원했던 것 |
3. Manifold 방식
- 차원을 축소하는데 특정 차원을 없애지 않고 새로운 차원을 만들어 내는 방식
- 알고리즘: LLE, t-SNE, Isomap, Autoencoer
- 직선으로 구분하는 건 불가해졌지만, 곡선으로 나누면 됨 => PCA
4. PCA (Principal Component Analysis - 주성분 분석)
- 여러 변수 간에 존재하는 상관관계를 이용하고 이를 대표하는 주성분을 추출해서 차원을 축소하는 기법
- 차원을 축소할 때 데이터의 정보는 최대한 유지하면서 고차원에서 저차원으로 축소를 하는 방식
- 데이터의 다른 부분을 보존하는 방식으로 분산을 최대한 유지하는 축을 생성
- 이 방식은 데이터의 분포가 표준정규분포가 아닌 경우에는 적용이 어려움
- 동작 원리
- 학습 데이터 셋에서 분산(변동성)이 최대인 축 (PC1)을 찾음
- PC1과 수직이면서 분산이 최대인 축 (PC2)를 찾음
- 첫 번째 축과 두 번째 축에 직교하고 분산을 최대한 보존하는 세 번째 축을 찾음
- 1~3의 방법으로 차원 수만큼 찾음
데이터 구분: 차이
구분되는 성질을 가장 적게 잃어야하기 때문에, 가장 많이 차이가 나는 것을 찾아야 한다.
애-어른을 구분하는 가장 큰 차이는 나이, 그게 첫번째 주성분이 된다.
그러면 반대편은 설명하지 못하기 때문에, 두번째 주성분은 첫번째 주성분과 직교한다.
2-3가지 정도로 판단하자!
4-1) sklearn
- 변환기를 데이터 세트에 학습시키고 나면 결과 확인 가능
- n_components 매개변수에 축소하려는 차원 개수 설정
#데이터 생성
#피처가 3개인 데이터 생성
np.random.seed(42)
m = 60
w1, w2 = 0.1, 0.3
noise = 0.1
angles = np.random.rand(m) * 3 * np.pi / 3 -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)
... |
from sklearn.decomposition import PCA
#2개의 피처로 주성분 분석을 수행
pca = PCA(n_components=2)
X2D = pca.fit_transform(X)
print(X2D[:5])
[[ 0.88527067 -0.33376821] [ 0.09028918 1.11319151] [ 0.73610082 0.50819326] [ 1.86082527 0.0634811 ] [-0.46712242 -0.51380375]] |
# 분산 비율 확인: explained_variance_ratio_
#분산 비율 확인
print(pca.explained_variance_ratio_)
[0.73833891 0.20993687] |
- Explained Variance Ratio: 분산을 얼마나 보존하는가 나타내는 지표 (70% 이상 채택)
- 첫번째 주성분이 74% 정도 데이터를 설명할 수 있고 두번째 주성분은 21% 정도 설명 가능
# 데이터 복원: inverse_transform
X3D_inv = pca.inverse_transform(X2D)
print(X3D_inv[:5])
[[ 0.97487955 0.28357613 1.30383978] [-0.4587498 0.66634022 0.5800397 ] [ 0.16053264 0.52058405 1.19513314] [ 0.65692889 0.44306826 2.29529498] [ 1.06130267 0.16980808 -0.05298289]] |
#원본 데이터와 복원된 데이터를 비교
print(np.allclose(X3D_inv, X))
#차이의 평균
print(np.mean(np.sum(np.square(X3D_inv - X), axis=1)))
False 0.07836731698475538 |
- 원본과 복원결과는 차이가 있음, 일부 데이터는 소실됨
4-2) 적절한 차원 수 선택
- 시각화를 위해서 주성분 분석을 하는 경우 2~3개로 수행
- 시각화 이외의 목적인 경우는 일반적으로 주성분의 개수를 지정하기 보다 분산의 비율을 이용해서 개수 설정
n_components에 성분의 개수가 아닌 0.0 ~ 1.0 사이의 비율을 설정하면 비율이 되는 주성분의 개수로 분석 수행 - 시각화를 수행해서 엘보가 만들어지는 지점이 있다면 엘보를 이용해서 주성분 개수 설정
#데이터 가져오기
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)
#X_train 은 784개의 피처를 가진 데이터
print(X_train.shape)
(52500, 784) |
#차원수를 지정해서 PCA 수행: 154개의 피처로 주성분분석 수행
pca = PCA(n_components=154)
pca.fit_transform(X_train)
#분산의 비율 확인
print(pca.explained_variance_ratio_)
... |
#분산 비율의 합계 - 95%
print(pca.explained_variance_ratio_.sum())
0.9500532025918321 |
#차원수를 지정해서 PCA 수행: 95% 이상되는 주성분의 개수로 주성분 분석
pca = PCA(n_components=0.95)
pca.fit_transform(X_train)
print(pca.explained_variance_ratio_.sum())
0.9504506731634907 |
#주성분의 개수
print(pca.n_components_)
154 |
4-3) Random PCA
- sklean의 기본 PCA는 모든 데이터를 메모리에 적재시킨 후 훈련을 시켜야 한다.
- 시간이 많이 소모된다. O(m * n의 제곱) + O(n의 3제곱) m은 행의 개수이고 n은 차원의 개수
- PCA에서 svd_solver 매개변수를 randomized 로 지정하면 Random PCA를 수행
- 확률적 알고리즘을 이용해서 주성분에 대한 근사값을 찾는 방식
- 이 방식은 원본 데이터의 차원은 속도에 아무런 영향을 주지 못하고 주성분의 개수만 훈련 속도에 영향을 줍니다.
- 시간 복잡도가 O(m * d의 제곱) + O(d의 3제곱) d는 줄이고자 하는 차원의 개수
- sklearn은 차원의 개수가 500개 이상이거나 줄이려는 차원의 개수가 원본 차원의 80% 보다 작으면 Random PCA를 수행
#차원수를 지정해서 랜덤 PCA 수행
pca = PCA(n_components=154, svd_solver='randomized')
pca.fit_transform(X_train)
print(pca.explained_variance_ratio_.sum())
0.9500820155151543 |
4-4) 점진적 PCA
- PCA 구현의 문제점 중 하나는 전체 훈련 세트를 메모리에 올려야 한다는 것
- 이를 해결하기 위한 방법 중 하나가 IncrementalPCA
- 메모리가 부족한 경우 와 실시간으로 데이터가 생성되는 경우 점진적 PCA를 사용
- 훈련 세트를 미니배치로 나눈 뒤 PCA 알고리즘에 한번에 하나씩 주입한다.
- 훈련 세트가 클 때 유용
- 나누어서 학습하는 것이 가능: 온라인 학습이 가능
- fit 대신 partial_fit 이라는 메서드를 이용해서 미니배치마다 호출
- 일반 PCA 보다 훈련 시간은 조금 더 걸릴 수 있다.
- 배치 처리가 미니 배치 방식보다 좋은 점은 자원을 효율적으로 사용할 수 있고 작업 시간은 단축 시킬 수 있다는 것
- 미니 배치 방식이 좋은 점은 훈련을 실시간으로 수행하기 때문에 훈련이 끝나는 시점만 따져보면 더 빠르다.
* 파이썬에서 메모리 정리 방법: (1) 가리키는 데이터가 없을 때 자동으로 정리. (2) 기존 변수가 라키는 데이터가 없도록 del 변수명 을 사용
4-5) 3가지 PCA 훈련 시간 비교
# 일반 PCA
import time
start = time.time()
pca = PCA(n_components=154, svd_solver="full")
pca.fit_transform(X_train)
end = time.time()
print("PCA 수행 시간:", (end-start))
print(pca.explained_variance_ratio_.sum())
PCA 수행 시간: 4.382986545562744 0.9504506731634907 |
# 랜덤 PCA
import time
start = time.time()
pca = PCA(n_components=154, svd_solver="randomized")
pca.fit_transform(X_train)
end = time.time()
print("랜덤 PCA 수행 시간:", (end-start))
print(pca.explained_variance_ratio_.sum())
랜덤 PCA 수행 시간: 4.955554962158203 0.9500787743598854 |
- svd_solver="randomized"를 생략해도 Random PCA - 피처의 500개가 넘으면 자동
# 점진적 PCA
from sklearn.decomposition import IncrementalPCA
#배치 사이즈: 데이터를 몇 개 씩 훈련
n_batches = 100
start = time.time()
inc_pca = IncrementalPCA(n_components=154)
for X_batch in np.array_split(X_train, n_batches):
inc_pca.partial_fit(X_batch)
X_reduced = inc_pca.transform(X_train)
end = time.time()
print("점진적 PCA 수행 시간:", (end-start))
print(inc_pca.explained_variance_ratio_.sum())
점진적 PCA 수행 시간: 29.47389006614685 0.9497567304638014 |
4-6) Kernel PCA
- 커널 트릭 PCA: 비선형 데이터에 다항을 추가하는 것 처럼 해서 비선형 결정 경계를 만드는 PCA
- 일반 PCA를 수행해서 분산의 비율이 제대로 표현되지 않는 경우 사용
- KernelPCA 클래스 (kernel, gamma)
- kernel = rbf: 가우시안 정규 분포 형태의 커널을 적용
- kernel = sigmoid: 로그 함수 형태
- gamma: 복잡도를 설정. 0.0 ~ 1.0 사이의 값
- 하이퍼 파라미터 튜닝을 거의 하지 않지만 주성분 분석을 분류나 회귀의 전처리 과정으로 사용하는 경우에 가장 좋은 성능을 발휘하는 kernel이나 gamma 값을 찾기 위해서 튜닝 하는 경우도 있다.
#데이터 생성
from sklearn.datasets import make_swiss_roll
# X는 3차원 피처, t 는 범주
X, t = make_swiss_roll(n_samples=1000, noise=0.2, random_state=42)
print(X)
[[-3.29677117 4.26198454 7.69787682] [ 5.95972743 11.45784273 12.72625276] [ 6.66051523 18.15820401 -9.84713337] ... [ 6.18364276 1.44095323 -1.71242696] [ 5.86076169 1.09185823 12.47091112] [-8.16213703 5.61235668 4.51171684]] |
- 스위스 롤 같은 데이터를 차원 축소를 수행하거나 분류를 수행하는 경우 선형으로 차원 축소를 수행하거나 분류를 수행할 수 없다.
axes = [-11.5, 14, -2, 23, -12, 15]
fig = plt.figure(figsize=(6, 5))
ax = fig.add_subplot(111, projection='3d')
ax.scatter(X[:, 0], X[:, 1], X[:, 2], c=t, cmap=plt.cm.hot)
plt.show()
from sklearn.decomposition import KernelPCA
pca = PCA(n_components=2)
result = pca.fit_transform(X)
print(pca.explained_variance_ratio_.sum())
0.7098258574944849 |
kernel_pca = KernelPCA(n_components=2, kernel="sigmoid", fit_inverse_transform=True,
coef0=1, gamma=0.0001)
result = kernel_pca.fit_transform(X)
#KernelPCA는 분산의 비율을 직접 계산
explained_variance = np.var(result, axis=0)
explained_variance_ratio = explained_variance / np.sum(explained_variance)
print(explained_variance_ratio)
[0.57463469 0.42536531] |
4-7) PCA 수행 이유
- 시각화를 하거나 단순한 모델을 만들기 위해서 PCA 수행
- PCA를 수행해서 분류나 회귀 작업을 수행하게 되면 성능은 떨어질 가능성이 높지만 일반화 가능성은 높아짐
- 실세계에 존재하는 데이터는 잡음이 섞인 경우가 많아서 잡음이 섞인 데이터를 가지고 훈련을 하게되면 overfitting 될 가능성이 높다.
# 잡음 제거: 이미지에 잡음 추가하고 PCA 수행
from sklearn.datasets import load_digits
#8*8 의 MNIST 데이터 가져오기
digits = load_digits()
digits.data.shape
(1797, 64) |
#이미지 출력 함수
def plot_digits(data):
fig, axes = plt.subplots(4, 10, figsize=(10, 4),
subplot_kw={'xticks':[], 'yticks':[]},
gridspec_kw=dict(hspace=0.1, wspace=0.1))
for i, ax in enumerate(axes.flat):
ax.imshow(data[i].reshape(8, 8), cmap='binary',
interpolation='nearest', clim=(0, 16))
#원본 이미지 출력
plot_digits(digits.data)
#주성분 분석
from sklearn.decomposition import PCA
pca = PCA(n_components = 0.6).fit(noisy)
print(pca.n_components_)
18 |
- 60%의 분산을 나타내는 데 피처 18개 필요
- 70%의의 분산을 나타내는데는 26개의 피처가 필요
#PCA를 수행한 데이터를 복원
components = pca.transform(noisy)
filtered = pca.inverse_transform(components)
plot_digits(filtered)
- 잡음 제거 완료
5. 지역 선형 임베딩 LLE (Locally Linear Embedding)
- 비선형 차원 축소 방법
- 피처 내의 모든 데이터를 이용하지 않고 가장 가까운 이웃에 얼마나 선형적으로 연관되어 있는 측정한 후 국부적인 관계가 가장 잘 보존되는 저차원을 찾는 방식
- LocallyLinearEmbedding 클래스
- n_components: 차원의 개수를 설정
- n_neighbors: 이웃의 개수를 설정
#스위스롤은 롤 케익처럼 클래스가 구분된 데이터를 생성
- 첫번째 리턴되는 값은 피처 3개로 구성된 피처의 배열
- 두번째 리턴되는 값은 범주에 해당하는 타겟
X, t = make_swiss_roll(n_samples=1000, noise=0.2, random_state=42)
from sklearn.manifold import LocallyLinearEmbedding
#10개의 이웃을 이용해서 2개의 성분으로 축소
lle = LocallyLinearEmbedding(n_components=2, n_neighbors=10, random_state=42)
X_reduced = lle.fit_transform(X)
plt.scatter(X_reduced[:, 0], X_reduced[:, 1], c=t, cmap=plt.cm.hot)
plt.show()
lle = PCA(n_components=2, random_state=42)
X_reduced = lle.fit_transform(X)
plt.scatter(X_reduced[:, 0], X_reduced[:, 1], c=t, cmap=plt.cm.hot)
plt.show()
6. 선형 판별 분석 LDA (Linear Discriminant Analysys)
- 분류 알고리즘이지만 훈련 과정에서 개별 클래스를 분별할 수 있는 축을 학습
- 각 클래스 간의 분산과 클래스 내부에서의 분산을 이용
- 클래스 간의 분산은 최대한 크게하고 내부 분산은 최대한 작게 가져가는 방식
- LDA 나 LLE를 이용해서 차원을 축소할 때는 스케일링 필요
- sklearn.discriminant_analysis.LinearDiscriminantAnalysis 클래스
- 하이퍼파라미터는 n_components
7. 기타 차원 축소 알고리즘
1) NMF: 행렬 분해를 이용해서 차원 축소
2) Random Projection: 랜덤 투영
3) Multi Dimensional Scailing: 샘플 간의 거리 유지하면서 차원 축소
4) Isomap
5) t-SNE
- 비슷한 샘플은 가까이, 비슷하지 않은 샘플은 멀리 떨어지도록 하면서 차원을 축소
- 고차원 공간에 있는 샘플의 군집을 시각화 할 때 사용
- 고도로 군집된 데이터의 경우 가.장 잘 작동하지만 속도가 느림
- 일반 PCA가 많이 사용되었던 이유는 설명 분산을 기반으로 하기 때문에 차원의 개수를 설정하는 것이 쉽고 빠름
- PCA의 단점은 비선형 관계를 보존할 수 없다는 것
- 고차원의 데이터를 축소할 때는 t-SNE 나 Isomap을 주로 이용
'Python' 카테고리의 다른 글
[Python] 연관분석 실습 _ 네이버 지식인 크롤링 (4) | 2024.03.15 |
---|---|
[Python] 감성 분석 실습 (3) | 2024.03.14 |
[Python] 지도학습 연습 _ 범주형 데이터 이진분류 (0) | 2024.03.07 |
[Python] 머신러닝_앙상블 (0) | 2024.03.07 |
[Python] 회귀 - 비선형 회귀 (0) | 2024.03.07 |