학습 목표
- 정답이 없는 데이터를 다루는 비지도 학습인 군집화 알고리즘 학습
비지도 학습
- 답을 알려주지 않고 데이터 간 유사성을 이용해 답을 지정하는 방법
예시
- 고객 특성에 따른 그룹화 ➡️ 헤비 유저, 일반 유저
- 구매 내역별로 데이터 그룹화 ➡️ 생필품 구매
- 비지도 학습은 데이터를 기반으로 레이블링을 하는 작업
- 정답이 없으므로 지도 학습보다 조금 어렵고 주관적인 판단이 개입하게 됨
K-평균 알고리즘(군집화)
iris 데이터
- 꽃 받침의 길이, 꽃 받침의 너비, 꽃 잎의 길이, 꽃 잎의 너비, 붓꽃 종(Y) 5개의 컬럼으로 되어있는 데이터
- 여기서 꽃에 대한 정보로 Y를 맞추는 문제는 지도 학습임
- 반면, Y가 없다면 정보에 따라 데이터를 분류 가능
- 그룹화할 때 2개, 3개 등등으로 가능하다
K-Means 군집화
- 수행 순서
- K개 군집 수 설정
- 임의의 중심을 선정
- 해당 중심점과 거리가 가까운 데이터를 그룹화
- 데이터의 그룹의 무게 중심으로 중심점을 이동
- 중심점 이동했기 때문에 다시 거리가 가까운 데이터를 그룹화(위 과정 반복)
- 장점
- 일반적이고 적용하기 쉬움
- 단점
- 거리 기반으로 가까움 측정하기 때문에 차원이 많을 수록 정확도가 떨어짐
- 반복 횟수 많을 수록 느림
- 몇 개의 군집을 선정할지 주관적임
- 평균 이용하기 때문에 이상치에 취약
- Python 라이브러리
- sklearn.cluster.KMeans
- 함수 입력 값
- n_cluster: 군집화 개수
- max_iter: 최대 반복 회수
- 메소드
- labels_: 각 데이터 포인트가 속한 군집 중심점 레이블
- cluster_centers_: 각 군집 중심점의 좌표
- 함수 입력 값
- sklearn.cluster.KMeans
군집평가 지표
실루엣 계수
- 비지도 학습 특성 상 답이 없기 때문에 평가가 쉽지 않음
- 다만, 군집화가 잘 되어있다는 것은 다른 군집간의 거리는 멀고, 동일한 군집끼리는 가까이 있다는 것을 의미
실루엣 분석
- 각 군집 간의 거리가 얼마나 효율적으로 분리되어있는지 측정하는 것으로 실루엣 계수를 정량화한 것
- 좋은 군집화의 조건
- 실루엣 값이 높을수록(1에 근접)
- 개별 군집의 평균 값의 편차가 크지 않아야 함
- Python 라이브러리
- sklearn.metrics.sihouette_score: 전체 데이터의 실루엣 계수 평균 값 반환
- 함수 입력 값
- X: 데이터 셋
- labels: 레이블
- metrics: 측정 기준(기본은 euclidean)
- 함수 입력 값
- sklearn.metrics.sihouette_score: 전체 데이터의 실루엣 계수 평균 값 반환
(실습) iris 데이터 이용한 군집화
- hue 파라미터로 각 species의 색을 다르게 표현 가능
sns.scatterplot(data=data, x='sepal_length', y='sepal_width', hue='species')
from sklearn.cluster import KMeans
# 군집화 위해 데이터에서 label 제거
data2 = data.drop('species', axis=1)
# 군집화
kmeans = KMeans(n_clusters=3, init='k-means++', max_iter=300, random_state=42)
kmeans.fit(data2)
# 비교 위해 기존 데이터에서 label 가져옴
data2['target'] = data['species']
data2['cluster'] = kmeans.labels_
plt.figure(figsize=(12,6))
plt.subplot(1, 2, 1)
sns.scatterplot(x='sepal_length', y='sepal_width', hue='target', data=data2)
plt.title("Original")
plt.subplot(1, 2, 2)
sns.scatterplot(x='sepal_length', y='sepal_width', hue='cluster', data=data2, palette='viridis')
plt.title("Cluster")
(실습) 고객 세그먼테이션
- 다양한 기준으로 고객을 분류하는 기법
- 주로 타겟 마케팅이라 불리는 고객 특성에 맞게 세분화하여 유형에 따라 맞춤형 마케팅이나 서비스를 제공하는 것이 목표
RFM의 개념
- Recency: 가장 최근 구입 일에서 오늘까지의 시간
- Frequency: 상품 구매 횟수
- Monetary value: 총 구매 금액
UCI 데이터 셋 호출
- 컬럼 정보
- InvoiceNo: 6자리 주문번호(취소된 주문은 c로 시작)
- StockCode: 5자리 제품 코드
- Description: 제품 이름
- Quantity: 제품 수량
- InvoiceDate: 주문 일자, 날짜 자료형
- UnitPrice: 제품 단가
- CustomerID: 5자리의 고객 번호
- Country: 국가명
- EDA
➡️ customerID에 결측치 다수 존재, 가격과 수량에 음수가 존재함
- 데이터 전처리
- customerID 결측치 제거
- InvoiceNo, UnitPrice, Quantity 데이터확인 및 삭제
- 영국데이터만 취함
# 주문 수량이 음수인 것을 확인 ➡️ InovoiceNo가 C로 시작하는 경우
cond1 = data['Quantity'] < 0
data[cond1]
# 따라서
# 1. customerID 결측치 제거
cond_cust = (data['CustomerID'].notnull())
# 2. Invoice가 C로 시작
cond_invo = (data['InvoiceNo'].astype(str).str[0] != 'C')
# 3. quantity가 음수 | unitprice가 음수인 것 제거
cond_minus = (data['Quantity'] > 0) & (data['UnitPrice'] > 0)
data2 = data[cond_cust & cond_invo & cond_minus]
# 각 국적의 수 ➡️ 대부분 영국
data2.value_counts('Country')
# 따라서
# 영국의 데이터만 가져옴
cond_uk = (data['Country'] == 'United Kingdom')
data3 = data2[cond_uk]
- RFM 기반 데이터 가공
- 날짜 데이터 가공
- 최종목표: 손님별로 가장 최근 구매하고 오늘까지의 기간, 구매 횟수, 총 구매 금액 표시
- StandardScaler 적용
import datetime as dt
# 2011.12.10 기준으로 각 날짜 빼고 + 1
# 추후 CustomerID 기준으로 최소와 Period 구하면 그것이 Recency
data3['Period'] = (dt.datetime(2011,12,10) - data3['InvoiceDate']).apply(lambda x: x.days + 1)
rfm = data3.groupby('CustomerID').agg({
'Period' : 'min',
'InvoiceNo' : 'count',
'Amt' : 'sum'
})
rfm.columns = ['Recency', 'Frequency', 'Monetary']
# RFM 기반 데이터 가공
# 1. 구매 수량과 구매 가격을 곱함
data3['Amt'] = (data3['Quantity'] * data3['UnitPrice']).astype(int)
data3
# 2. 각 손님 별 총 구매 가격 구하고 구매 가격으로 내림차순 정렬
data3[['CustomerID']].drop_duplicates()
data3.pivot_table(index='CustomerID', values='Amt', aggfunc='sum').sort_values('Amt', ascending=False)
# 분포 확인 ➡️ 스케일링 작업 필요
fig, ax = plt.subplots(2, 2, figsize=(20,20))
sns.histplot(rfm['Recency'], ax=ax[0,0], kde=True)
sns.histplot(rfm['Frequency'], ax=ax[0,1], kde=True)
sns.histplot(rfm['Monetary'], ax=ax[1,0], kde=True)
# 데이터 정규화
from sklearn.preprocessing import StandardScaler
sc = StandardScaler()
X_features = sc.fit_transform(rfm[['Recency', 'Frequency', 'Monetary']])
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
kmeans = KMeans(n_clusters=3, random_state=42)
labels = kmeans.fit_predict(X_features)
rfm['label'] = labels
silhouette_score(X_features, labels) # 0.5930190673437187
# log스케일링 통한 추가전처리
rfm['Reccency_log'] = np.log1p(rfm['Recency'])
rfm['Frequency_log'] = np.log1p(rfm['Frequency'])
rfm['Monetary_log'] = np.log1p(rfm['Monetary'])
X_features2 = rfm[['Reccency_log', 'Frequency_log', 'Monetary_log']]
sc2 = StandardScaler()
X_features2_sc = sc2.fit_transform(X_features2)
- 정규화를 진행하고 k-means를 진행해 실루엣 계수를 확인해봤을때 k가 5일때 실루엣 계수는 좋게 나왔지만 시각화를 하였을때 3개의 군집밖에 보이지 않았음
- 그래서 log스케일링을 적용하고 k-means를 진행했을때 실루엣 계수는 전보다 작아졌지만 시각화를 진행했을 때 각 군집들이 균등하게 표시됨
+ 추가적으로 Gaussian Mixture Model, DBSCAN, PCA 등이 있음
'AI > 머신러닝' 카테고리의 다른 글
시계열 다루기 2일차 (0) | 2025.02.27 |
---|---|
시계열 다루기 1일차 (0) | 2025.02.26 |
머신러닝의 이해와 라이브러리 활용 심화 2주차(의사결정나무, 랜덤포레스트, 최근접이웃, 부스팅알고리즘) (0) | 2025.02.13 |
머신러닝의 이해와 라이브러리 활용 심화 1주차(EDA, 전처리, 인코딩, 스케일링, 데이터분리, 교차검증, GridSearch) (0) | 2025.02.13 |
수업[2.4](군집, DBSCAN, PCA) (0) | 2025.02.04 |