: int,float
: 범주형
-> o p359
LDA(Linear Discriminant Analysis)는 선형 판별법으로 불리며, PCA와 매우 유사하게 입력 dataset를 저차원 공간에 투영하여 차원을 축소하는 기법이지만, 중요한 차이는 LDA는 지도학습의 분류(Classification)에서 사용하기 쉽도록 개별 클래스를 분별할 수 있는 기준을 최대한 유지하면서 차원을 축소한다.
즉, PCA는 입력 데이터의 변동성의 가장 큰 축을 찾았지만, LDA는 입력 데이터의 결정 값 클래스
를 최대한 분리할 수 있는 축을 찾는다.
LDA는 특정 공간상에서 틀래스 분리를 최대화하는 축을 찾기 위해 클래스
간 분산(between-class scatter)과 클래스 내부 분산(width-class scatter)의 비율을 최대화하는 방식으로 차원을 축소한다.
즉, 클래스간 분산은 최대한 크게 가져가고, 클래스 내부의 분산은 최대한 작게 가져가는 방식이다.
일반적으로 LDA를 구하는 스텝은 PCA와 유사하지만 가장 큰 차이는 공분산 행렬이 아닌 클래스간 분산과 클래스 내부 분산 행렬을 생성한 뒤, 이 행렬에 기반해 교유벡터를 구하고 입력 데이터를 투영한다는 점이다.
사이킷런의 LDA를 이용해 변환하고, 그 결과를 품종별로 시각화해 보겠다.
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.preprocessing import StandardScaler
from sklearn.datasets import load_iris
iris = load_iris()
iris_scaled = StandardScaler().fit_transform(iris.data)
lda = LinearDiscriminantAnalysis(n_components=2)
lda.fit(iris_scaled, iris.target)
iris_lda = lda.transform(iris_scaled)
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
lda_columns=['lda_component_1','lda_component_2']
irisDF_lda = pd.DataFrame(iris_lda,columns=lda_columns)
irisDF_lda['target']=iris.target
#setosa는 세모, versicolor는 네모, virginica는 동그라미로 표현
markers=['^', 's', 'o']
#setosa의 target 값은 0, versicolor는 1, virginica는 2. 각 target 별로 다른 shape으로 scatter plot
for i, marker in enumerate(markers):
x_axis_data = irisDF_lda[irisDF_lda['target']==i]['lda_component_1']
y_axis_data = irisDF_lda[irisDF_lda['target']==i]['lda_component_2']
plt.scatter(x_axis_data, y_axis_data, marker=marker,label=iris.target_names[i])
plt.legend(loc='upper right')
plt.xlabel('lda_component_1')
plt.ylabel('lda_component_2')
plt.show()
사이킷런의 TruncatedSVD 클래스는 PCA 클래스와 유사하게 fit()과 transform()을 호출해 몇 개의 주요 component로 차원을 축소하여 변환한다. iris dataset을 이용하여 변환해 보겠다.
from sklearn.decomposition import TruncatedSVD
from sklearn.datasets import load_iris
import matplotlib.pyplot as plt
%matplotlib inline
iris = load_iris()
iris_ftrs = iris.data
# 2개의 주요 component로 TruncatedSVD 변환
tsvd = TruncatedSVD(n_components=2)
tsvd.fit(iris_ftrs)
iris_tsvd = tsvd.transform(iris_ftrs)
# Scatter plot 2차원으로 TruncatedSVD 변환 된 데이터 표현. 품종은 색깔로 구분
plt.scatter(x=iris_tsvd[:,0], y= iris_tsvd[:,1], c= iris.target)
plt.xlabel('TruncatedSVD Component 1')
plt.ylabel('TruncatedSVD Component 2')
from sklearn.preprocessing import StandardScaler
# iris 데이터를 StandardScaler로 변환
scaler = StandardScaler()
iris_scaled = scaler.fit_transform(iris_ftrs)
# 스케일링된 데이터를 기반으로 TruncatedSVD 변환 수행
tsvd = TruncatedSVD(n_components=2)
tsvd.fit(iris_scaled)
iris_tsvd = tsvd.transform(iris_scaled)
# 스케일링된 데이터를 기반으로 PCA 변환 수행
pca = PCA(n_components=2)
pca.fit(iris_scaled)
iris_pca = pca.transform(iris_scaled)
# TruncatedSVD 변환 데이터를 왼쪽에, PCA변환 데이터를 오른쪽에 표현
fig, (ax1, ax2) = plt.subplots(figsize=(9,4), ncols=2)
ax1.scatter(x=iris_tsvd[:,0], y= iris_tsvd[:,1], c= iris.target)
ax2.scatter(x=iris_pca[:,0], y= iris_pca[:,1], c= iris.target)
ax1.set_title('Truncated SVD Transformed')
ax2.set_title('PCA Transformed')
print((iris_pca - iris_tsvd).mean())
print((pca.components_ - tsvd.components_).mean())
SVD는 PCA와 유사하게 컴퓨터 비전 영역에서 이미지 압축을 통한 패턴 인식과 신호 처리 분야에 사용된다.
또한 텍스트의 토픽 모델링 기법인 LSA(Latent Semantic Analysis)의 기반 알고리즘이다.
: 데이터 내에 숨어있는 패턴, 그룹을 파악하여 서로 묶는 것이라고 할 수 있다.
가장 가까운
포인트들을 선택하는 군집화 기법이다. <K-평균>
- 먼저, 군집화의 기준이 되는 중심을 구성하려는 군집화 개수만큼 임의의 위치에 가져다 놓는다.
- 전체 데이터를 2개로 군집화하려면 2개의 중심을 임의의 위치에 가져다 놓는 것이다.
- 임의의 위치에 군집 중심점을 가져다 놓으면 반복적인 이동 수행을 너무 많이 해서 수행 시간이 오래 걸리기 때문에 초기화 알고리즘으로 적합한 위치에 중심점을 가져다 놓지만, 여기서는 설명을 위해 임의의 위치로 가정한다.
- 각 데이터는 가장 가까운 곳에 위치한 중심점에 소속된다.
- 이렇게 소속이 결정되면 군집 중심점을 소속된 데이터의 평균 중심으로 이동한다.
- 중심점이 이동했기 때문에 각 데이터는 기존에 속한 중심점보다 더 가까운 중심적이 있다면 해당 중심점으로 다시 소속을 변경한다.
- 다시 중심을 소속된 데이터의 평균 중심으로 이동한다.
- 중심점을 이동했는데 데이터의 중심점 소속 변경이 없으면 군집화를 종료한다.
- 그렇지 않다면 다시 4번 과정을 거쳐서 소속을 변경하고 이 과정을 반복한다.
<그림 설명>
<K-평균의 장점>
- 일반적인 군집화에서 가장 많이 활용되는 알고리즘이다.
- 알고리즘이 쉽고 간결하다.
<K-평균의 단점>
- 거리 기반 알고리즘으로 속성의 개수가 매우 많을 경우 군집화 정확도가 떨어진다. 이를 위해 PCA로 차원 감소를 적용해야 할 수도 있다.
- 반복을 수행하는데, 반복 횟수가 많을 경우 수행시간이 매우 느려진다.
- 몇 개의 군집(cluster)을 선택해야 할지 가이드하기가 어렵다.
## p433
from sklearn.preprocessing import scale
from sklearn.datasets import load_iris
from sklearn.cluster import KMeans
import numpy as np
import pandas as pd
iris = load_iris()
# 보다 편리한 데이터 Handling을 위해 DataFrame으로 변환
irisDF = pd.DataFrame(data=iris.data, columns=['sepal_length','sepal_width','petal_length','petal_width'])
#irisDF.head(3)
# 3개를 군집화 해보겠다.
# n_clusters=3 : 3군집
# init='k-means++' : 초기중심설정 방식 , 디폴트값
# max_iter=300 : 최대 반복 횟수 , 디폴트값
kmeans = KMeans(n_clusters=3, init='k-means++', max_iter=300,random_state=0)
kmeans.fit(irisDF)
print(kmeans.labels_)
# PCA를 가져와 군집을 줄여줍시다.
from sklearn.decomposition import PCA
# 2개로 줄일것이다.
pca = PCA(n_components=2)
pca_transformed = pca.fit_transform(iris.data)
pca_transformed
kmeans = KMeans(n_clusters=3, init='k-means++', max_iter=300,random_state=0)
kmeans.fit(pca_transformed)
print(kmeans.labels_)
군집화 평가 방법 중 하나인 실루엣 분석은 각 군집 간의 거리가 얼마나 효율적으로 분리돼 있는지를 나타낸다.
여기서 효율적으로 분리되어 있다는 것은 다른 군집과의 거리는 떨어져 있고 동일 군집끼리의 데이터는 서로 가깝게 잘 뭉쳐 있다는 의미이다.
실루엣 분석은 실루엣 계수(silhouette coefficient)를 기반으로 하고, 개별 데이터가 가지는 군집화 지표이다.
개별 데이터가 가지는 실루엣 계수는 해당 데이터가 같은 군집 내의 데이터와 얼마나 가깝게 군집화돼 있고, 다른 군집에 있는 데이터와는 얼마나 멀리 분리돼 있는지를 나타내는 지표이다.
특정 데이터 포인트의 실루엣 계수 값은 해당 데이터 포인트와 같은 군집 내
에 있는 다른 데이터 포인트와의 거리를 평균한 값
a(i),
해당 데이터 포인트가 속하지 않은 군집
중 가장 가까운 군집과의 평균 거리
b(i)를 기반으로 계산된다.
두 군집 간의 거리
는 b(i) - a(i)이며 이 값을 정규화하기 위해 MAX(a(i), b(i)) 로 나눈다.
이렇게 i번째 데이터 포인트의 실루엣 계수
값 s(i)를 계산한다.
- i: 각 데이터 포인트에 대한 인덱스
- si:
실루엣 계수
- ai: 해당 데이터 포인트와
같은 군집 내
에 있는 다른 데이터 포인트와의 거리를 평균한 값- bi: 해당 데이터 포인트가
속하지 않은 군집 내
에 있는 데이터 포인트와의 거리를 군집 별로 평균을 낸 후, 그 중 작은 값 (해당 데이터 포인트와 평균적으로 가장 가까운 군집과의 거리)
-1에서 1
사이의 값을 가지며, 1로 가까워질수록 근처 군집과 더 멀리 떨어져 있는 것이고 0에 가까워질수록 근처 군집과 가까운 것
이다. 아예 다른 군집에 데이터 포인트
가 할당된 것을 의미한다.
실루엣 분석은 각 군집 간의 거리가 얼마나 효율적으로 분리돼 있는지를 나타낸다. 여기서
"효율"
은 다른 군집과의 거리는 떨어져 있고 동일 군집 간의 데이터는 서로 가깝게 잘 뭉쳐있음을 의미한다.군집화가 잘 될수록 군집 내의 거리는 가깝고, 군집 간의 거리는 멀 것이다.
실루엣 분석은
실루엣 계수
(silhouette coefficient)를 기반으로 하는데, 개별 데이터는 각각 실루엣 계수를 가진다. 이때 개별 데이터가 가지는 실루엣 계수는 해당 데이터가 같은 군집 내의 데이터와 얼마나 가깝게 군집화되어 있고, 다른 군집에 있는 데이터와는 얼마나 멀리 분리되어 있는지를 나타내는 지표이다.
< 사이킷런은 이러한 실루엣 분석을 위해 제공하는 method가 있다. > p443
sklearn.metrics.silhouette_samples(X, labels, ...) : 각 데이터 포인트의 실루엣 계수를 계산해 반환.
sklearn.metrics.silhouette_score(X, labels, ...) : 전체 데이터의 실루엣 계수 값을 평균해 반환. np.mean(silhouette_samples())와 동일.
일반적으로 높은 silhouette_score를 가질수록 군집화가 잘되었다고 판단 가능하나 보장되는 것은 아니다.
<일반적으로 좋은 군집화가 되기 위한 조건으로 제시하는 것>
- 전체 실루엣 계수의 평균값이 0~1사이의 값을 가지며, 1에 가까울 수록 좋음.
- 전체 실루엣 계수의 평균값과 더불어, 개별 군집의 실루엣 계수 평균값의 편차가 크지 않아야 함.
즉, 개별 군집의 실루엣 계수 평균값이 전체 실루엣 계수의 평균값에서 크게 벗어나지 않는 것이 중요함.
만약 전체 실루엣 계수의 평균값은 높지만, 특정 군집의 실루엣 계수 평균값만 유난히 높고 나머지 군집의 실루엣 계수 평균값이 낮다면 좋은 군집화라고 할 수 없을 것. (평균의 역설을 생각해보자)
from sklearn.metrics import silhouette_samples, silhouette_score
iris = load_iris()
kmeans = KMeans(n_clusters= 2, init="k-means++", max_iter= 300, random_state= 0)
kmeans.fit(iris.data)
# kmeans.labels_
# silhouette_score(iris.data, kmeans.labels_)
# silhouette_samples(iris.data, kmeans.labels_)
from sklearn.datasets import make_blobs
X, y = make_blobs(n_samples= 200, n_features= 2, centers= 3, cluster_std= .8, random_state= 0)
sns.scatterplot(x = X[:,0], y = X[:,1])
평균 이동(Mean Shift)은 K-평균과 유사하게 중심을 군집의 중심으로 지속적으로 움직이면서 군집화를 수행한다.
하지만 K-평균이 중심에 소속된 데이터의 평균 거리 중심으로 이동하는 데 반해, 평균 이동은 중심을 데이터가 모여 있는 밀도가 가장 높은 곳으로 이동시킨다.
평균 이동 군집화는 데이터의 분포도를 이용하여 군집 중심적을 찾는다. 군집 중심점은 데이터 포인트가 모여있는 곳이라는 생각에서 착안한 것이며 이를 위해 확률 밀도 함수(probability density function)를 이용한다.
가장 집중적으로 데이터가 모여있어 확률 밀도 함수가 피크인 점을 군집 중심점으로 선정하며 일반적으로 주어진 모델의 확률 밀도 함수를 찾기 위해서 KDE(Kernel Density Estimation)를 이용한다.
KDE(Kernel Density Estimation)는 Kernal 함수를 통해 어떤 변수의 확률 밀도 함수를 추정하는 대표적인 방법이다.
관측된 데이터 각각에 커널 함수를 적용한 값을 모두 더한 뒤 데이터 건수로 나눠 확률 밀도 함수를 추정한다.
확률 밀도 함수 PDF는 확률 변수의 분포를 나타내는 함수로, 널리 알려진 정규분포 함수를 포함하여 감마 분포, t-분포 등이 있으며 대표적으로 가우시안 분포 함수를 사용한다.
확률 밀도 함수를 알면 특정 변수가 어떤 값을 갖게 될지에 대한 확률을 알게 되므로 이를 통해 변수의 특성, 확률 분포 등 변수의 많은 요소를 알 수 있다.
대역폭 h는 KDE 형태를 부드러운 형태로 평활화(Smoothing)하는 데 적용되며, 이 h를 어떻게 설정하느냐에 따라 확률 밀도 추정 성능을 크게 좌우할 수 있다.
아래 그림은 h를 작은 값에서 큰 값으로 증가하면서 KDE가 어떻게 변화하는지 본 것이다.
h가 작을 경우엔 좁고 뾰족한 KDE를 가지게 되며, 이는 변동성이 큰 방식으로 확률 밀도 함수를 추정하므로 과적합(over-fitting)하기 쉽다.
반대로 매우 큰 h값은 과도하게 smoothing 된 KDE로 인해 지나치게 단순화된 방식으로 확률 밀도 함수를 추정하며 결과적으로 과소적합(under-fitting)하기 쉽다.
따라서 적절한 KDE의 대역폭
h를 계산하는 것은 KDE 기반의 평균 이동
(Mean Shift) 군집화에서 매우 중요하다.
평균 이동 군집화는 대역폭 설정에 따라 군집 중심정의 개수가 달라지며, 따로 개수를 지정하지 않는다.
사이킷런은 평균 이동 군집화를 위해 MeanShift 클래스를 제공한다.
대역폭 크기 설정이 군집화의 품질에 큰 영향을 미치기 때문에 사이킷런은 최적의 대역폭 계산을 위해 estimate_bandwidth() 함수를 제공한다.
make_blobs()로 3개의 군집 데이터를 만든 후 bandwidth에 따라 군집을 생성해보겠다.
import numpy as np
bandwidth가 0.8로 작게 설정되었을 경우 6개의 군집으로 분류되었지만 1로 설정하자 3개의 군집으로 잘 군집화되었다.
from sklearn.cluster import estimate_bandwidth
bandwidth = estimate_bandwidth(X)
print('bandwidth 값:', round(bandwidth,3))
DBSCAN(Density Based Spatial Clustering of Applications with Noise)는 밀도 기반 군집화의 대표적인 알고리즘이다. DBSCAN은 간단하고 직관적인 알고리즘임에도 데이터의 분포가 기하학적으로 복잡한 dataset에도 효과적인 군집화가 가능하다.
DBSCAN을 구성하는 가장 중요한 파라미터는 epsilon으로 표기하는 주변 영역과 이 입실론 주변 영역에 포함되는 최소 데이터의 개수 min points이다. 입실론 주변 영역 내에 포함되는 최소 데이터 개수를 충족시키는지 아닌지에 따라 데이터 포인트를 구분하여 정의한다.
DBSCAN을 적용할 떄는 특정 군집 개수로 군집을 강제하지 않고 적절한 eps과 min_samples 파라미터를 통해 최적의 군집을 찾는 것이 중요하다.
그리고 일반적으로 eps의 값을 크게 하면 반경이 커져 포함하는 데이터가 많아지므로 노이즈 데이터 개수가 작아지고, min_samples를 크게 하면 주어진 반경 내에서 더 많은 데이터를 포함시켜야 하므로 노이즈 데이터 개수가 커지게 된다.
이번엔 make_circles() 함수를 이용하여 내부 원과 외부 원 형태로 되어 있는 2차원 dataset을 만들어보겠다.
from sklearn.datasets import make_circles
X, y = make_circles(n_samples=1000, shuffle=True, noise=0.05, random_state=0, factor=0.5)
clusterDF = pd.DataFrame(data=X, columns=['ftr1', 'ftr2'])
clusterDF['target'] = y
visualize_cluster_plot(None, clusterDF, 'target', iscenter=False)
그 다음 K-평균과 GMM, DBSCAN이 어떻게 이 dataset을 군집화하는지 확인해보겠다.
# KMeans로 make_circles( ) 데이터 셋을 클러스터링 수행.
from sklearn.cluster import KMeans
kmeans = KMeans(n_clusters=2, max_iter=1000, random_state=0)
kmeans_labels = kmeans.fit_predict(X)
clusterDF['kmeans_cluster'] = kmeans_labels
visualize_cluster_plot(kmeans, clusterDF, 'kmeans_cluster', iscenter=True)
K-평균은 위와 아래로 반반씩 군집화하였다. 거리 기반의 군집화로는 데이터가 특정 형태로 지속해서 이어지는 부분을 찾아내기 어렵다.
# GMM으로 make_circles( ) 데이터 셋을 클러스터링 수행.
from sklearn.mixture import GaussianMixture
gmm = GaussianMixture(n_components=2, random_state=0)
gmm_label = gmm.fit(X).predict(X)
clusterDF['gmm_cluster'] = gmm_label
visualize_cluster_plot(gmm, clusterDF, 'gmm_cluster', iscenter=False)
GMM도 일렬로 늘어선 dataset에서는 효과적으로 군집화 적용이 가능했으나, 내부와 외부의 원형으로 구성된 더 복잡한 형태의 dataset에서는 군집화가 원하는 방향으로 되지 않는다.
# DBSCAN으로 make_circles( ) 데이터 셋을 클러스터링 수행.
from sklearn.cluster import DBSCAN
dbscan = DBSCAN(eps=0.2, min_samples=10, metric='euclidean')
dbscan_labels = dbscan.fit_predict(X)
clusterDF['dbscan_cluster'] = dbscan_labels
visualize_cluster_plot(dbscan, clusterDF, 'dbscan_cluster', iscenter=False)
출처: