게스트 여정 데이터, 왜 지금 주목해야 할까?
에어비앤비에서 여행을 계획하는 과정은 단일 세션에서 끝나지 않습니다. 한 게스트가 샌프란시스코 숙소를 찾기 위해 며칠에 걸쳐 수십 개의 리스팅을 보고, 지난 수년간의 예약, 리뷰, 취소 이력까지 쌓여 있죠. 이런 데이터는 게스트가 어떤 숙소를 선호하는지 강력하게 암시합니다.
기존에는 이런 이력을 수동 피처(예: 총 예약 수, 평균 가격)로 집계해 검색 랭킹에 반영했습니다. 하지만 피처가 수백 개로 늘어나면서 확장성과 표현력에 한계가 드러났고, 에어비앤비는 트랜스포머 기반 시퀀스 모델을 도입해 게스트의 전체 여정을 인코딩하는 방향으로 전환했습니다.
이번 글에서는 에어비앤비 엔지니어링 블로그에 공개된 게스트 여정 학습을 통한 검색 개인화 사례를 바탕으로, 시퀀스 모델링 설계의 핵심 포인트와 실무 적용 시 고려할 점을 정리해 보겠습니다.

핵심 설계: 장기 시퀀스와 단기 시퀀스의 분리
에어비앤비가 마주한 첫 번째 난관은 게스트당 이벤트 수가 수십만 개에 달할 정도로 방대하다는 점이었습니다. 모든 이벤트를 하나의 시퀀스로 모델링하는 것은 계산적으로 불가능에 가깝습니다.
해결책으로, 게스트 여정을 두 갈래로 나누었습니다.
- 장기 시퀀스: 지난 7년간의 예약, 리뷰, 취소 등 정보량이 많지 않지만 중요한 이벤트를 최대 80개까지 포함
- 단기 시퀀스: 최근 21일간의 리스팅 조회 이벤트를 최대 200개까지 포함
이렇게 분리함으로써 모델은 게스트의 깊은 예약 이력과 최근 브라우징 맥락을 동시에 학습할 수 있습니다. 이벤트는 카디널리티가 높은 리스팅 ID, 호스트 ID를 위한 임베딩 테이블과 계층적 지리 ID를 공유하는 피처 풀로 인코딩됩니다.
학습 및 서빙 효율화: 4배 개선의 비결
에어비앤비는 세 가지 전략을 통해 학습 처리량을 약 4배 향상시켰습니다.
-
검색 배칭 (Batching of Searches): 인코더는 인과적 마스크(causal mask)를 사용하므로, 전체 이벤트 시퀀스를 한 번만 실행해도 각 검색 시점에 해당하는 중간 임베딩을 얻을 수 있습니다. 여러 검색이 하나의 인코더 패스를 공유합니다.
-
시퀀스 길이 버케팅 (Length Bucketing): 패딩 낭비를 줄이기 위해 시퀀스를 길이별로 묶어 처리합니다.
-
패딩 검색 제거 (Sparse Search Calculation): 랭킹 모델에서 패딩된 검색을 계산하지 않도록 희소 연산을 적용합니다.
서빙 단계에서는 추론을 두 단계로 분리했습니다. 시퀀스 인코더는 일일 배치 작업으로 실행되어 게스트 임베딩을 생성하고, 실시간 검색 시에는 이 임베딩을 조회해 랭킹 모델에 결합합니다. 이 설계는 낮은 지연 시간을 유지하면서도 전체 게스트 이력을 반영할 수 있게 해줍니다.
코드로 보는 핵심 개념
실제 에어비앤비 코드는 공개되지 않았지만, 위 설계를 파이썬으로 개념적으로 구현하면 다음과 같습니다.
import torch
import torch.nn as nn
class GuestSequenceEncoder(nn.Module):
"""
게스트 여정 시퀀스를 인코딩하는 트랜스포머 모델
장기/단기 시퀀스를 분리해 입력받고, 검색 시점의 임베딩을 반환한다.
"""
def __init__(self, d_model=128, nhead=8, num_layers=6):
super().__init__()
self.embedding = nn.Embedding(num_embeddings=10000, embedding_dim=d_model)
self.positional_encoding = nn.Embedding(num_embeddings=512, embedding_dim=d_model)
encoder_layer = nn.TransformerEncoderLayer(d_model=d_model, nhead=nhead)
self.transformer = nn.TransformerEncoder(encoder_layer, num_layers=num_layers)
self.output_proj = nn.Linear(d_model, d_model)
def forward(self, event_ids, event_times, search_positions):
# 이벤트 ID를 임베딩하고 위치 인코딩 추가
seq_len = event_ids.size(1)
positions = torch.arange(seq_len, device=event_ids.device).unsqueeze(0)
x = self.embedding(event_ids) + self.positional_encoding(positions)
# 트랜스포머 인코더 통과 (인과 마스크 적용)
mask = torch.triu(torch.ones(seq_len, seq_len) * float('-inf'), diagonal=1)
output = self.transformer(x, mask=mask)
# 검색 시점의 임베딩 추출
search_embeddings = output[:, search_positions, :] # [batch, num_searches, d_model]
return self.output_proj(search_embeddings)
이 예제는 장기/단기 시퀀스를 하나로 합쳐 처리하는 단순화된 버전이지만, 핵심 아이디어인 인과 마스크를 통한 검색 시점 임베딩 공유를 보여줍니다.

주의사항 및 심화 분석
1. 데이터의 희소성과 노이즈
에어비앤비는 소셜 미디어와 달리 참여(engagement)가 아닌 예약 전환(conversion)에 최적화합니다. 예약 이벤트는 드물고 신중하게 발생하는 반면, 리스팅 조회는 단순한 둘러보기일 수도 있습니다. 이런 희소하고 노이즈가 많은 신호에서 일반화하기 위해서는 시퀀스 길이 캡핑(80/200)과 같은 도메인 지식이 필수적입니다.
2. 패딩과 비용 최적화
시퀀스 모델은 본질적으로 계산 비용이 높습니다. 에어비앤비는 길이 버케팅과 희소 연산을 통해 패딩 낭비를 줄였지만, 이는 인프라 수준의 최적화가 동반되어야 실효성이 있습니다. 단순히 모델만 바꾼다고 해서 학습이 빨라지지는 않습니다.
3. 오프라인 지표와 온라인 A/B 테스트의 간극
에어비앤비는 오프라인 NDCG 개선(+3.78%)이 온라인 비즈니스 지표(예약자 수, 조회 수)로 이어지는 것을 A/B 테스트로 확인했습니다. 이는 오프라인 평가가 온라인 성과와 일치할 수 있도록 신중한 실험 설계가 필요함을 시사합니다.
한국 개발 생태계에서의 적용 맥락
국내 이커머스나 OTA(온라인 여행사)에서도 유사한 시퀀스 모델링을 도입하는 사례가 늘고 있습니다. 다만, 트래픽 규모가 상대적으로 작은 경우에는 에어비앤비처럼 복잡한 장기/단기 시퀀스 분리 없이도 세션 기반 어텐션이나 간단한 LSTM으로도 충분한 효과를 볼 수 있습니다. 인프라 비용과 데이터 희소성을 고려해 단계적으로 접근하는 것이 좋습니다.
또한, 추천 시스템 파이프라인을 최적화하는 데 관심이 있다면, Netflix가 JDK 벡터 API로 추천 시스템을 최적화한 실제 사례도 참고해 보세요.
![]()
결론: 시퀀스 모델링이 가져올 변화
에어비앤비는 수동 피처 집계에서 학습된 시퀀스 표현으로 전환함으로써, 게스트가 과거에 무엇을 했는지뿐 아니라 지금 무엇을 찾고 있는지에 대한 깊이 있는 이해를 얻었습니다. 이 신호는 검색 랭킹뿐 아니라 프로모션 이메일에도 일반화되어 +5.04%의 클릭률 상승을 이끌었습니다.
이 사례가 주는 실무 교훈은 다음과 같습니다.
- 시퀀스 데이터를 무작정 모델링하지 말고, 도메인 지식으로 장기/단기로 분리하라.
- 인과 마스크를 활용하면 여러 검색 시점을 하나의 패스로 처리할 수 있다.
- 서빙 지연 시간을 낮추기 위해 배치 임베딩과 실시간 조회를 분리하라.
다음 단계 학습 방향
- 트랜스포머의 인과 마스크와 멀티헤드 어텐션의 수학적 배경을 깊이 있게 학습
- 추천 시스템에서의
setwise ranking과pointwise ranking의 차이 이해 - 실제 유저 행동 로그를 시퀀스 데이터로 변환하는 ETL 파이프라인 설계 연습
에어비앤비의 여정을 통해, 단순히 모델을 바꾸는 것이 아니라 데이터를 어떻게 바라볼 것인지에 대한 통찰을 얻을 수 있었기를 바랍니다.