1. LOF
- 밀도 기반 (Density-Based) 이상치 탐지 기법
- 지역적 밀도(Local Density)를 고려하여 내 주변 대비 얼마나 동떨여 져있는지 판단
2. 알고리즘
[Step 1] 기준 척도 만들기: k-distance(o)
- 각 데이터 포인트가 속한 "동네의 크기(밀도 수준)"를 측정
- 정의: 점 o에서 k번째로 가까운 이웃까지의 거리
- 의미: 점 o 주변의 기본적인 밀도 척도(반경)
| 상태 | 설명 | k-distance |
|---|
| Dense (빽빽한 곳) | 이웃들이 다닥다닥 붙어 있음 | 작음 |
| Sparse (널널한 곳) | 이웃들이 멀리 떨어져 있음 | 큼 |
비유: 서울 강남(o)에서의 "가까운 이웃" 반경은 10m일 수 있지만, 강원도 산골(o′)에서의 반경은 1km일 수 있습니다. LOF는 이처럼 지역마다 다른 기준을 먼저 잡습니다.
[Step 2] 거리 보정하기: Reachability Distance
- 두 점 사이의 거리를 잴 때, 단순한 물리적 거리(Euclidean Distance)를 쓰지 않고 밀도를 고려하여 보정한 거리를 사용
- 밀도가 낮은(널널한) 곳에서 우연히 가까운 두 점 때문에 밀도가 비정상적으로 높게 계산되는 것을 방지(Smoothing)
reach-distk(p,o)=max(k-distance(o),d(p,o))
작동 방식
- p가 o와 아주 가까울 때 (d(p,o)<k-distance(o)):
- 실제 거리가 너무 작으면 밀도가 무한대로 치솟을 수 있음
- 이를 막기 위해 실제 거리 대신 o의 기본 반경인 k-distance(o)를 사용
- 의미: "너네 동네는 원래 널널하니까(거리가 크니까), 우연히 붙어 있어도 최소한 그 동네 기본 거리만큼은 떨어져 있는 셈 치자."
- p가 o보다 멀리 있을 때:
- 실제 거리 d(p,o)를 그대로 사용

[Step 3] 밀도 계산하기: LRD (Local Reachability Density)
- 보정된 거리(
reach-dist)를 사용하여 점 p의 진짜 밀도를 계산
- 점 p와 그 이웃들 사이의
reach-dist 평균을 구한 뒤, 그 역수를 취함
- 거리가 짧을수록 밀도는 커짐
- 평균 거리가 짧다 → 분모가 작음 → LRD 값(밀도)이 큼 (빽빽함)
- 평균 거리가 길다 → 분모가 큼 → LRD 값(밀도)이 작음 (널널함)
LRDk(p)=avg(reach-distk(p,neighbors))1
[Step 4] 비교를 통해 이상치 판별: LOF 점수
- 마지막으로, "나의 밀도"와 "내 이웃들의 밀도"를 비교
- 분자: 내 이웃들의 밀도 평균
- 분모: 나(p)의 밀도
LOFk(p)=LRD of pAverage LRD of neighbors
결과 해석 (직관적 이해)
1. LOF≈1 (정상 데이터)
- 분자(이웃 밀도)와 분모(내 밀도)가 비슷함
- 상황: "내 주변 친구들도 빽빽하게 모여 있고, 나도 그 틈에 잘 껴 있다." (혹은 둘 다 널널하다)
- 같은 클러스터에 속해 있으므로 정상
2. LOF≫1 (이상치, Outlier)
- 분자(이웃 밀도)는 큰데, 분모(내 밀도)는 작음
- 상황: "내 친구들(o)은 자기들끼리 빽빽하게 뭉쳐 있는데(고밀도), 나(p) 혼자 멀리 떨어져 있어서 밀도가 낮다(저밀도)."
- 밀집 지역에서 튕겨져 나온 이상치 (보통 1.5 이상이면 주의 깊게 관찰)
3. LOF<1 (인라이어, Inlier)
- 분자보다 분모(내 밀도)가 더 큼
- 상황: "주변보다 내가 더 중심부에 빽빽하게 위치해 있다."
- 아주 건전한 정상 데이터
3. LOF 파라미터
LocalOutlierFactor(
n_neighbors=20,
*,
algorithm='auto',
leaf_size=30,
metric='minkowski',
p=2,
metric_params=None,
contamination='auto',
novelty=False,
n_jobs=None,
)
n_neighbors
- 관찰할 주변의 샘플의 수, 위 알고리즘에서 K와 동일
novelty
- novelty = False → 주어진 데이터셋에 대해서만 이상치 판별 (outlier detection)
- novelty = True → 학습 데이터로 정상 분포를 학습한 뒤, 새로운 데이터가 이상한지 판단
lof = LocalOutlierFactor(novelty=True)
lof.fit(X_train)
labels = lof.predict(X_new)
scores = lof.score_samples(X_new)