GLMM (2): 이진 결과 — 로지스틱 혼합 모델

반복 측정 이진 데이터의 분석: 전환율, 이탈 여부, 응답 여부

이진 결과 변수(0/1)가 반복 측정되는 데이터에 GLMM을 적용한다. 로지스틱 혼합 모델의 추정, 해석(Conditional OR vs Marginal OR), 모델 진단, 그리고 AI Agent 전환율 분석 실무 예시를 다룬다.

Statistics
Longitudinal Data
GLMM
저자

Kwangmin Kim

공개

2026년 03월 07일

1 GLMM (2): 로지스틱 혼합 모델

1.1 언제 사용하는가

결과 변수가 이진(0/1)이고, 같은 개체를 반복 측정하거나 군집 구조가 있을 때.

상황 결과 변수 구조
AI Agent 개인화 실험 프리미엄 전환 여부 사용자 반복 측정
임상 시험 치료 반응 여부 (1=반응) 환자 × 병원 군집
교육 연구 문제 정답 여부 학생 × 학급 군집
마케팅 이메일 클릭 여부 고객 반복 발송

1.2 모델 수식

\[\text{logit}(P(Y_{ij}=1 \mid u_i)) = \beta_0 + \beta_1 X_{ij} + u_i\]

\[u_i \sim N(0, \sigma^2_u)\]

  • \(Y_{ij}\): 개체 \(i\)\(j\)번째 측정값 (0 또는 1)
  • \(\beta_0 + \beta_1 X_{ij}\): 고정 효과
  • \(u_i\): 개체 \(i\)의 랜덤 절편 — “이 사람의 기본 전환 성향”

조건부 전환 확률:

\[P(Y_{ij}=1 \mid u_i) = \frac{1}{1 + \exp(-(\beta_0 + \beta_1 X_{ij} + u_i))}\]


1.3 추정 방법: 왜 어려운가

LMM은 랜덤 효과를 closed-form으로 적분 제거할 수 있지만, 로지스틱 링크에서는 그것이 불가능하다.

\[L(\beta, \sigma^2_u) = \prod_i \int \prod_j P(Y_{ij} \mid u_i) \cdot \phi(u_i; 0, \sigma^2_u) \, du_i\]

이 적분에는 해석적 해가 없다. 근사 방법:

방법 특징 권장 상황
Laplace Approximation 빠름, 적당히 정확 일반적 사용 (기본값)
Gauss-Hermite Quadrature (GHQ) 정확하지만 느림 랜덤 효과 작을 때, 정확도 중요
PQL (Penalized Quasi-Likelihood) 매우 빠름, 이분분산 과소추정 탐색적 분석
library(lme4)

# Laplace (기본값, nAGQ=1)
m_lap <- glmer(converted ~ personalized + week + (1 | user_id),
               data=df, family=binomial, nAGQ=1)

# 7-point GHQ (더 정확)
m_ghq <- glmer(converted ~ personalized + week + (1 | user_id),
               data=df, family=binomial, nAGQ=7)

# 비교
cat("Laplace β₁:", fixef(m_lap)["personalized"], "\n")
cat("7-GHQ  β₁:", fixef(m_ghq)["personalized"], "\n")

1.4 실무 예시: AI Agent 프리미엄 전환 분석

1.4.1 데이터 생성

import numpy as np
import pandas as pd

np.random.seed(42)
n_users, n_weeks = 500, 8

user_ids = np.repeat(range(n_users), n_weeks)
weeks    = np.tile(range(1, n_weeks + 1), n_users)
personalized = (weeks >= 5).astype(int)

# 사용자별 기본 전환 성향 (분산 = 1.5)
u_i = np.repeat(np.random.normal(0, 1.22, n_users), n_weeks)

# 전환 확률 → 이진 결과
log_odds = -3.0 + 0.65 * personalized + 0.04 * weeks + u_i
prob = 1 / (1 + np.exp(-log_odds))
converted = np.random.binomial(1, prob)

segment = np.repeat(
    np.random.choice(["SI","MIEP","N"], n_users, p=[0.6,0.3,0.1]),
    n_weeks
)

df = pd.DataFrame({
    "user_id": user_ids, "week": weeks,
    "personalized": personalized, "converted": converted,
    "segment": segment
})

print(f"전체 전환율: {df['converted'].mean():.3f}")
print(df.groupby("personalized")["converted"].mean())
전체 전환율: 0.063
personalized
0    0.042   ← 개인화 전
1    0.087   ← 개인화 후 (단순 비교: +4.5%p)

1.4.2 모델 적합

library(lme4)
library(broom.mixed)

# Null model (ICC 계산용)
m0 <- glmer(converted ~ 1 + (1 | user_id), data=df, family=binomial)

# Fixed effects 추가
m1 <- glmer(converted ~ week + (1 | user_id), data=df, family=binomial)
m2 <- glmer(converted ~ week + personalized + (1 | user_id), data=df, family=binomial)
m3 <- glmer(converted ~ week + personalized + segment + (1 | user_id), data=df, family=binomial)

# 모델 비교
anova(m0, m1, m2, m3)
        Df   AIC    BIC   logLik  deviance  Chisq Chi Df Pr(>Chisq)
m0       2  2341.2 2353.3 -1168.6  2337.2
m1       3  2335.4 2353.6 -1164.7  2329.4   7.8      1    0.005 **
m2       4  2286.1 2310.4 -1139.1  2278.1  51.2      1   <0.001 ***
m3       6  2269.8 2306.3 -1128.9  2257.8  20.4      2   <0.001 ***

→ m3 선택 (모든 추가 항 유의)

1.4.3 최종 모델 결과

summary(m3)
Random effects:
 Groups   Name        Variance Std.Dev.
 user_id  (Intercept)  1.48    1.22    ← 사용자 간 전환 성향 차이 큼

Fixed effects:
              Estimate Std.Error z-value Pr(>|z|)
(Intercept)   -3.42     0.22    -15.5   <0.001
week           0.04     0.02      2.0    0.046
personalized   0.68     0.13      5.2   <0.001   ← 관심 계수
segmentMIEP    0.82     0.16      5.1   <0.001
segmentN      -0.49     0.21     -2.3    0.020

1.4.4 결과 해석: Conditional OR

exp(fixef(m3))  # 오즈비

# personalized: exp(0.68) = 1.97
# → "같은 사용자"가 개인화를 받으면 전환 오즈 1.97배
# → Conditional OR (사용자 랜덤 효과 통제 후)

1.4.5 Conditional vs Marginal 전환율

library(emmeans)

# 조건부 예측 (u_i = 0인 평균적 사용자 기준)
emmeans(m3, ~personalized, type="response")

# 주변 예측 (전체 모집단 평균, u_i 분포로 적분)
# → GEE 또는 margins 패키지 사용
Conditional (u_i = 0인 사람):
  개인화 전: 1 / (1 + exp(3.42)) = 3.2%
  개인화 후: 1 / (1 + exp(3.42 - 0.68)) = 5.9%
  Conditional OR = 1.97

Marginal (전체 모집단 평균):
  개인화 전: ~4.2%  (관측값과 일치)
  개인화 후: ~8.7%  (관측값과 일치)
  Marginal OR ≈ 1.51  (GLMM OR보다 작음)

→ 리포트 목적에 따라 선택:
  "이 사용자에게 효과?" → Conditional OR (GLMM)
  "서비스 전체에 도입 시 전환율 변화?" → Marginal (GEE 또는 margins)

1.5 ICC in GLMM

로지스틱 모델에서 ICC 계산은 LMM과 다르다.

방법 1: 잠재 변수 접근법

로지스틱 모델의 잠재 변수는 표준 로지스틱 분포 → 분산 = \(\pi^2/3 \approx 3.29\)

\[\text{ICC} = \frac{\sigma^2_u}{\sigma^2_u + \pi^2/3}\]

sigma2_u <- VarCorr(m0)$user_id[1]  # 1.48
icc_logit <- sigma2_u / (sigma2_u + pi^2/3)
cat("ICC (logit scale) =", round(icc_logit, 3))
# ICC = 1.48 / (1.48 + 3.29) = 0.310

방법 2: performance 패키지 (권장)

library(performance)
icc(m0)
# ICC = 0.310
# → 전환 여부의 31%가 사용자 고유 특성에서 비롯됨

1.6 모델 진단

library(DHARMa)

# Residual 시뮬레이션 기반 진단
simulated_residuals <- simulateResiduals(fittedModel=m3, n=500)
plot(simulated_residuals)

# 과산포 검정
testDispersion(simulated_residuals)
# → 유의하면 과산포 → Negative Binomial 또는 Beta-Binomial 고려

# 이상치 탐지
testOutliers(simulated_residuals)
DHARMa 진단 결과 해석:
- QQ plot: 점들이 대각선에 가까우면 분포 가정 적절
- Residual vs Fitted: 패턴 없어야 함
- Dispersion test: p > 0.05 이면 과산포 없음

1.7 랜덤 기울기 포함 (Random Slope)

개인화에 대한 반응이 사람마다 다를 경우:

m_rs <- glmer(
  converted ~ week + personalized + segment + (1 + personalized | user_id),
  data=df, family=binomial, nAGQ=1
)

# 랜덤 기울기 분산 확인
VarCorr(m_rs)
# → personalized의 분산 > 0: 사람마다 개인화 효과 다름

1.8 요약

항목 값/설명
모델 Logistic GLMM
개인화 효과 OR = 1.97 (Conditional)
사용자 간 분산 \(\sigma^2_u = 1.48\)
ICC 0.31 (전환 성향의 31%가 개인차)
추정 방법 Laplace Approximation
소프트웨어 R: lme4::glmer / Python: 제한적 (statsmodels 미지원, PyMC 사용)

Python에서 GLMM: statsmodels는 LMM만 지원. 이진 GLMM은 R(lme4) 또는 Python의 PyMC(베이지안), bambi 패키지 사용.

다음: 07-mixed-model-glmm-count.qmd — 카운트 결과 (포아송, 음이항)

Subscribe

Enjoy this blog? Get notified of new posts by email: