일반적인 마르코프 연쇄가 "눈에 보이는 상태의 변화"를 다룬다면, HMM은 "겉으로 드러난 결과(Observation)를 통해 숨겨진 원인(Hidden State)을 추론"하는 더욱 고차원적인 모델입니다.
우리는 살면서 겉으로 드러난 현상만 보고 그 이면의 진실을 파악해야 할 때가 많습니다.
환자의 '증상(기침, 열)'을 보고 '병명(감기, 폐렴)'을 진단하거나, 친구의 '목소리 톤'을 듣고 '기분'을 맞히는 것처럼요.
이처럼 관찰 가능한 결과($O$)를 통해 숨겨진 상태($S$)를 확률적으로 추론하는 알고리즘, 은닉 마르코프 모델(HMM)에 대해 알아보겠습니다.
1. 마르코프 연쇄 vs 은닉 마르코프 모델
① 마르코프 연쇄 (Markov Chain)
- 모든 상태가 눈에 보입니다.
- 예시: 오늘의 주가(상승) $\rightarrow$ 내일의 주가(하락) 예측.
② 은닉 마르코프 모델 (HMM)
- 진짜 상태(State)는 은닉(Hidden)되어 보이지 않습니다.
- 우리는 그 상태가 뱉어내는 출력값(Observation)만 볼 수 있습니다.
- 구조:
- 은닉 상태($S$): 날씨 (맑음, 비) - 직접 안 보임
- 관측값($O$): 행동 (게임, 운동) - 눈에 보임
- 전이 확률($A$): 어제 맑았으면 오늘 맑을 확률 ($S_{t-1} \rightarrow S_t$).
- 방출(출력) 확률($B$): 맑을 때 게임을 할 확률 ($S_t \rightarrow O_t$).
2. 예제 시나리오: 영희의 추리 (날씨 vs 활동)
강의 자료(Slide 88)에 나온 상황을 풀어봅시다.
- 상황: 영희는 철수와 멀리 떨어져 삽니다. 철수가 오늘 무엇을 했는지(게임/운동)만 들을 수 있고, 그 동네 날씨는 모릅니다.
- 목표: 철수의 행동 패턴을 보고, 지난 3일간의 날씨 변화를 맞혀라! (디코딩 문제)
확률 설정 (모델 파라미터)
- 은닉 상태 (날씨): Sunny (맑음), Rainy (비)
- 관측값 (행동): Game (게임), Fitness (운동)
- 전이 확률 (날씨 변화):
- 맑음 $\rightarrow$ 맑음: 0.8 / 맑음 $\rightarrow$ 비: 0.2
- 비 $\rightarrow$ 맑음: 0.4 / 비 $\rightarrow$ 비: 0.6
- 방출 확률 (날씨별 행동):
- 맑음일 때: 게임 0.1, 운동 0.9 (맑으면 주로 운동함)
- 비올 때: 게임 0.8, 운동 0.2 (비 오면 주로 게임함)
3. 디코딩(Decoding)과 비터비(Viterbi) 알고리즘
관측된 행동이 [게임, 운동, 운동] 순서라고 합시다. 이때 날씨는 어땠을까요? 모든 경우의 수를 다 계산하면 너무 복잡합니다. 이때 우도(Likelihood)를 최대화하는 최적의 경로를 찾는 알고리즘이 바로 비터비(Viterbi) 알고리즘입니다.
4. [실습] Python & R 코드 구현
철수가 3일 동안 [Game, Fitness, Fitness]를 했다고 가정하고, 날씨를 추론해 보겠습니다.
Python Code (Custom Viterbi)
라이브러리 없이 비터비 알고리즘의 로직을 직접 구현해 이해를 돕는 코드입니다.
import numpy as np
import pandas as pd
# 1. 모델 파라미터 정의
states = ['Sunny', 'Rainy']
observations = ['Game', 'Fitness']
# 초기 확률 (모른다고 가정하고 50:50)
start_prob = {'Sunny': 0.5, 'Rainy': 0.5}
# 전이 확률 (Transition Matrix)
trans_prob = {
'Sunny': {'Sunny': 0.8, 'Rainy': 0.2},
'Rainy': {'Sunny': 0.4, 'Rainy': 0.6}
}
# 방출 확률 (Emission Matrix)
emission_prob = {
'Sunny': {'Game': 0.1, 'Fitness': 0.9},
'Rainy': {'Game': 0.8, 'Fitness': 0.2}
}
# 2. 관측 시퀀스 (철수의 행동)
obs_seq = ['Game', 'Fitness', 'Fitness']
# 3. 비터비(Viterbi) 알고리즘 구현
def viterbi(obs, states, start_p, trans_p, emit_p):
V = [{}] # 시간대별 확률 저장 (DP Tables)
path = {}
# (1) 초기화 (t=0)
for y in states:
V[0][y] = start_p[y] * emit_p[y][obs[0]]
path[y] = [y]
# (2) 재귀 단계 (t=1 ~ T)
for t in range(1, len(obs)):
V.append({})
newpath = {}
for y in states: # 현재 상태 y
# 전 단계(y0)에서 현재(y)로 올 확률 중 최대값 찾기
(prob, state) = max(
(V[t-1][y0] * trans_p[y0][y] * emit_p[y][obs[t]], y0) for y0 in states
)
V[t][y] = prob
newpath[y] = path[state] + [y]
path = newpath
# (3) 종료 (최대 확률 경로 선택)
(prob, state) = max((V[len(obs) - 1][y], y) for y in states)
return prob, path[state]
# 4. 결과 실행
prob, result_path = viterbi(obs_seq, states, start_prob, trans_prob, emission_prob)
print(f"관측된 행동: {obs_seq}")
print(f"추론된 날씨: {result_path}")
print(f"경로 확률: {prob:.6f}")
R Code (HMM 패키지)
R에서는 HMM 패키지를 사용하면 아주 간단하게 해결됩니다.
# install.packages("HMM")
library(HMM)
# 1. HMM 모델 초기화 (initHMM)
# States: 은닉 상태, Symbols: 관측값
hmm_model <- initHMM(
States = c("Sunny", "Rainy"),
Symbols = c("Game", "Fitness"),
startProbs = c(0.5, 0.5),
transProbs = matrix(c(0.8, 0.2, # Sunny -> Sunny, Rainy
0.4, 0.6), # Rainy -> Sunny, Rainy
nrow=2, byrow=TRUE),
emissionProbs = matrix(c(0.1, 0.9, # Sunny -> Game, Fitness
0.8, 0.2), # Rainy -> Game, Fitness
nrow=2, byrow=TRUE)
)
# 2. 관측 시퀀스 정의
observations <- c("Game", "Fitness", "Fitness")
# 3. 비터비 알고리즘 실행 (viterbi)
# 가장 가능성 높은 은닉 상태 경로를 반환합니다.
hidden_states <- viterbi(hmm_model, observations)
cat("관측된 행동:", observations, "\n")
cat("추론된 날씨:", hidden_states, "\n")

코드 실행 결과 및 해석
해석:
- 첫날 Game을 했습니다. 비 올 때 게임할 확률(0.8)이 맑을 때(0.1)보다 압도적으로 높으므로 첫날은 비(Rainy)였을 것입니다.
- 둘째 날 Fitness를 했습니다. 비 $\rightarrow$ 맑음으로 바뀔 확률(0.4)이 맑음 $\rightarrow$ 맑음 유지(0.8)보다는 낮지만, 운동은 맑을 때 할 확률(0.9)이 매우 높습니다. 따라서 날씨가 맑음(Sunny)으로 개었다고 봅니다.
- 셋째 날도 Fitness입니다. 맑음 상태가 유지되었을 확률이 높습니다 (Sunny).
경로 확률(Path Probability)은 비터비(Viterbi) 알고리즘이 찾아낸 "가장 유력한 은닉 상태의 시나리오가 실제로 발생했을 확률"을 의미합니다.
쉽게 말해, "철수가 [게임, 운동, 운동]을 했을 때, 날씨가 [비, 맑음, 맑음]이었을 확률이 수학적으로 10.368%다"라는 뜻입니다.
왜 이 숫자가 나왔는지, 이전에 설정한 파라미터를 가지고 역추적해서 계산해 드리겠습니다. 딱 떨어지는 숫자의 비밀을 알면 훨씬 이해가 쉽습니다.
0.103680의 탄생 과정 (Step-by-Step)
우리가 찾은 최적의 날씨 경로(Rainy → Sunny → Sunny)를 따라가며 확률을 곱해봅시다.
1단계: 첫날 (비가 옴 → 게임을 함)
- 초기 확률(비): 0.5
- 방출 확률(비 → 게임): 0.8
- 계산: $0.5 \times 0.8 = \mathbf{0.4}$
2단계: 둘째 날 (비가 그치고 맑아짐 → 운동을 함)
- 이전 단계 확률: 0.4
- 전이 확률(비 → 맑음): 0.4
- 방출 확률(맑음 → 운동): 0.9
- 계산: $0.4 \times 0.4 \times 0.9 = \mathbf{0.144}$
3단계: 셋째 날 (맑음 유지 → 운동을 함)
- 이전 단계 확률: 0.144
- 전이 확률(맑음 → 맑음): 0.8
- 방출 확률(맑음 → 운동): 0.9
- 계산: $0.144 \times 0.8 \times 0.9 = \mathbf{0.10368}$
결과: 0.10368 (이 값이 출력된 0.103680과 정확히 일치합니다!)
Insight
HMM은 의료 분야에서 "질병의 진행 단계 추적"에 매우 유용하게 쓰입니다.
- 은닉 상태($S$): 질병의 진행 단계 (1, 2, 3) - 검사를 매일 할 수 없으니 은닉됨.
- 관측값($O$): 혈액 검사 수치, 환자의 통증 호소 빈도 - 매일 관찰 가능.
- 적용: 환자의 주기적인 혈액 검사 데이터($O$)를 HMM에 넣으면, 현재 질병이 악화되고 있는지 유지되고 있는지($S$)를 확률적으로 계산하여 조기 경보를 울릴 수 있습니다.
보이는 것(데이터)을 통해 보이지 않는 것(환자의 진짜 상태)을 읽어내는 기술, 그것이 바로 HMM입니다.
'Study Note > 통계' 카테고리의 다른 글
| 어려운 적분은 가라, 뽑아서 푼다! MCMC와 메트로폴리스-헤이스팅스 (0) | 2025.12.19 |
|---|---|
| 확률은 변한다? 베이즈 통계와 켤레 사전 확률 (1) | 2025.12.19 |
| 최적의 선택을 찾아라: 마르코프 의사결정 과정 (MDP) (0) | 2025.12.19 |
| 어제는 잊어라, 오직 오늘만 본다: 마르코프 연쇄 (Markov Chain) (0) | 2025.12.19 |
| PCA와 비슷하지만 다른, 요인분석(Factor Analysis)의 모든 것 (0) | 2025.12.19 |