[CS300 #263] 지도 학습과 비지도 학습 — 정답이 있느냐 없느냐
컴퓨터공학 300 주제 시리즈의 263번째 글이다. 전체 지도는 여기.
한 줄 요약
지도 학습은 입력과 정답(라벨) 쌍으로 입력에서 정답을 맞히는 함수를 배우고, 비지도 학습은 라벨 없이 데이터 자체의 구조(무리, 축, 밀도)를 찾는다.
왜 필요한가
머신러닝 프로젝트에서 가장 먼저 정할 것은 모델이 아니라 문제의 형태다. 고객 이탈을 예측하려는데 과거 이탈 기록이 있으면 지도 학습이다. 기록은 없고 “고객이 어떤 부류로 나뉘는지” 알고 싶으면 비지도 학습이다. 이 구분을 놓치면 존재하지 않는 라벨을 만들어 내느라 몇 주를 쓰거나, 라벨이 있는데도 군집화로 돌아가 정확도를 잴 수 없게 된다.
또 하나, 현실의 비용 구조가 이 구분에 달려 있다. 라벨은 비싸다. 의료 영상 한 장의 판독, 계약서 한 건의 분류에는 전문가 시간이 든다. 라벨 없는 데이터는 대개 넘쳐난다. 그래서 둘을 섞는 방법(준지도, 자기지도)이 중요해졌다.
핵심 개념
지도 학습
데이터는 (x, y) 쌍이다. x 는 특징 벡터, y 는 정답이다. 목표는 처음 보는 x 에 대해서도 y 를 잘 맞히는 함수 f 를 찾는 것이다.
| 과제 | y 의 형태 | 예 | 대표 방법 |
|---|---|---|---|
| 회귀 | 실수 | 집값, 응답 시간 | 선형 회귀, 트리 |
| 이진 분류 | 0/1 | 스팸 여부 | 로지스틱 회귀 |
| 다중 분류 | 여러 클래스 중 하나 | 숫자 0~9 | 소프트맥스, 신경망 |
핵심은 일반화다. 학습 데이터에서만 잘 맞는 함수는 쓸모가 없다. 그래서 데이터를 학습용과 평가용으로 나누고, 평가용 성능으로 판단한다.
비지도 학습
데이터는 x 만 있다. 무엇을 “맞혔는지” 채점할 정답이 없다.
| 과제 | 질문 | 대표 방법 |
|---|---|---|
| 군집화 | 비슷한 것끼리 묶으면? | k-평균, DBSCAN, 계층 군집 |
| 차원 축소 | 정보를 덜 잃고 축을 줄이면? | PCA, t-SNE |
| 밀도 추정·이상 탐지 | 드문 점은 어디인가? | 가우시안 혼합, Isolation Forest |
정답이 없으니 평가가 어렵다. 실루엣 계수 같은 내부 지표가 있지만, 결국 “이 군집이 업무에 의미가 있는가”를 사람이 판단해야 한다.
그 사이의 방법들
라벨 많음 ◀──────────────────────────────▶ 라벨 없음
지도 학습 준지도 학습 자기지도 학습 비지도 학습
(일부만 라벨) (데이터에서 라벨을 만들어 냄)
- 준지도: 라벨 몇 개와 라벨 없는 다수를 함께 쓴다.
- 자기지도: 데이터 자체로 문제를 만든다. 문장의 다음 단어 맞히기, 가린 단어 맞히기가 대표다. 대규모 언어 모델의 사전학습이 여기에 속한다. 정답이 데이터 안에 이미 있으므로 사람이 라벨을 붙일 필요가 없다.
- 강화학습: 정답 대신 보상 신호로 배운다. 이 파트 뒤에서 따로 다룬다.
귀납 편향
같은 데이터로 학습해도 방법마다 답이 다르다. 각 방법이 “세상은 이럴 것”이라고 가정하는 바가 다르기 때문이다. k-최근접 이웃은 “가까운 점은 같은 라벨”을, k-평균은 “무리는 둥글고 크기가 비슷하다”를 가정한다. 이 가정을 귀납 편향이라 한다. 데이터가 가정과 어긋나면 방법이 실패한다. k-평균은 초승달 모양 두 무리를 제대로 가르지 못한다.
직접 해 보기
두 무리의 2차원 점을 만들고, 라벨을 쓰는 1-최근접 이웃(지도)과 라벨을 버린 k-평균(비지도)을 나란히 돌린다. 라이브러리 없이 표준 math 와 random 만 쓴다.
import math, random
random.seed(0)
# 두 무리의 2차원 점
A = [(random.gauss(1, .4), random.gauss(1, .4)) for _ in range(10)]
B = [(random.gauss(3, .4), random.gauss(3, .4)) for _ in range(10)]
# 지도 학습: 라벨이 있다 -> 1-최근접 이웃
labeled = [(p, "A") for p in A] + [(p, "B") for p in B]
def knn(q):
return min(labeled, key=lambda t: math.dist(t[0], q))[1]
print("지도 (1-NN):", knn((1.2, 0.8)), knn((2.9, 3.3)))
# 비지도 학습: 라벨이 없다 -> k-평균(k=2)
pts = A + B
cent = [pts[0], pts[1]] # 일부러 나쁜 초기값(둘 다 A 무리)
for it in range(10):
groups = [[], []]
for p in pts:
groups[min((0, 1), key=lambda k: math.dist(p, cent[k]))].append(p)
new = [tuple(sum(x)/len(g) for x in zip(*g)) for g in groups]
if new == cent: break
cent = new
print("비지도 (k-means) 반복", it, "회, 중심:", [tuple(round(v, 2) for v in c) for c in cent])
print("군집 크기:", [len(g) for g in groups])
실행 결과:
지도 (1-NN): A B
비지도 (k-means) 반복 3 회, 중심: [(1.06, 0.81), (2.97, 2.98)]
군집 크기: [10, 10]
1-NN 은 라벨 “A”, “B” 를 그대로 돌려준다. 정답이 무엇인지 알고 있기 때문이다. k-평균은 두 무리를 찾아내지만 결과는 “군집 0”, “군집 1” 일 뿐이다. 어느 쪽이 A 인지는 모른다. 이름을 붙이는 것은 사람의 몫이다.
또 초기 중심을 일부러 같은 무리 안에 두었는데도 몇 번의 반복으로 제자리를 찾았다. 이 데이터는 무리가 잘 떨어져 있어서다. 무리가 겹치거나 모양이 길쭉하면 초기값에 따라 결과가 달라진다. scikit-learn 의 KMeans 가 기본으로 중심을 서로 멀리 흩뿌리는 k-means++ 초기화를 쓰는 이유다.
현업에서는
- 지도 학습: 사기 탐지, 수요 예측, 문서 분류. 가장 큰 일은 모델보다 라벨 품질 관리다. 라벨러 간 불일치, 시간이 지나며 바뀌는 정의가 성능의 상한을 정한다.
- 비지도 학습: 고객 세분화, 로그 패턴 묶기, 이상 탐지. 운영 환경에서 정상 상태만 잔뜩 있고 장애 라벨이 거의 없을 때, 정상의 분포를 배우고 거기서 벗어난 점을 경보로 쓰는 방식이 흔하다. 메트릭 이상 탐지가 그 예다.
- 둘의 조합: 비지도로 묶은 군집을 사람이 검토해 라벨을 붙이고, 그 라벨로 지도 학습 모델을 만든다. 라벨링 비용을 줄이는 실용적인 흐름이다.
확인 문제
- 다음은 지도/비지도 중 무엇인가? (a) 과거 매출로 다음 달 매출 예측 (b) 서버 로그를 비슷한 유형끼리 묶기 (c) 사진에 고양이가 있는지 판별
- 비지도 학습의 결과를 평가하기 어려운 이유는?
- 대규모 언어 모델의 사전학습을 “자기지도”라고 부르는 이유는?
- k-평균이 잘 못 다루는 데이터 모양의 예를 들고, 그 이유를 귀납 편향으로 설명하라.
풀이
- (a) 지도(회귀) (b) 비지도(군집화) (c) 지도(분류)
- 비교할 정답이 없기 때문이다. 내부 지표로는 군집의 응집도만 알 수 있고, 업무적 의미는 사람이 판단해야 한다.
- 다음 토큰이라는 정답을 데이터 자체에서 만들어 내므로 사람이 라벨을 붙이지 않아도 지도 학습 형태로 학습할 수 있기 때문이다.
- 초승달 두 개, 크기가 크게 다른 무리, 길쭉한 무리. k-평균은 각 점을 가장 가까운 중심에 배정하므로 무리가 중심 주변에 둥글게 모여 있다고 가정한다.
더 읽을거리 (References)
- scikit-learn User Guide, Supervised learning
- scikit-learn User Guide, Unsupervised learning, Clustering
- G. James, D. Witten, T. Hastie, R. Tibshirani, An Introduction to Statistical Learning, 2nd ed., Springer. 책 사이트