호텔 예약 취소 예측 AI 연습(예제)

2026. 1. 16. 16:28AI활용 멀티모달&MCP 과정

 

 

호텔 예약 취소 예측 AI 연습(예제)


학습 목표

이번 학습의 목표는 호텔 예약 정보 데이터를 기반으로 고객이 예약을 취소할지 여부를 예측하는 이진 분류 AI 모델을 만드는 것입니다. 이를 위해 다음과 같은 과정을 거칩니다.

  • 호텔 예약 데이터 구조 이해
  • EDA(탐색적 데이터 분석)를 통한 데이터 특성 파악
  • 결측치 및 이상치 처리
  • 머신러닝 학습을 위한 데이터 전처리
  • Logistic Regression 모델 학습 및 평가

호텔 예약 수요 데이터셋 소개

이번 프로젝트에서는 Kaggle에서 제공하는 Hotel Booking Demand Dataset을 사용합니다.

이 데이터셋은 호텔 예약 정보와 고객 행동 데이터를 포함하고 있으며, 예약 취소 여부(is_canceled)를 예측하는 데 적합한 구조를 가지고 있습니다.

주요 활용 목적은 다음과 같습니다.

  • 호텔 예약 취소 패턴 분석
  • 고객 행동 예측
  • 수요 예측 및 운영 전략 수립

 


데이터셋 컬럼 설명

  • hotel: 호텔 유형 (Resort Hotel, City Hotel)
  • is_canceled: 예약 취소 여부 (0: 유지, 1: 취소)
  • lead_time: 예약과 체크인 사이의 기간(일)
  • arrival_date_year: 도착 연도
  • arrival_date_month: 도착 월
  • arrival_date_week_number: 도착 주차
  • arrival_date_day_of_month: 도착 일
  • stays_in_weekend_nights: 주말 숙박일 수
  • stays_in_week_nights: 주중 숙박일 수
  • adults / children / babies: 인원 수
  • distribution_channel: 예약 채널
  • adr: 평균 일일 요금

데이터 전처리 및 EDA

데이터 불러오기

import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

# CSV 파일을 불러와 데이터프레임 생성
hotel_df = pd.read_csv('/본인의 구글드라이브 경로/hotel_bookings.csv')
hotel_df

기본 정보 확인

hotel_df.info()  
# 컬럼 타입, 결측치 여부 확인
hotel_df.describe()  
# 숫자형 데이터의 통계 정보 확인

lead_time 이상치 탐색

sns.displot(hotel_df['lead_time'])
# 예약 리드타임 분포 확인

sns.boxplot(hotel_df['lead_time'])
# 박스플롯을 통해 이상치 시각화

IQR 방식으로 이상치 제거

Q1 = hotel_df['lead_time'].quantile(0.25)
Q3 = hotel_df['lead_time'].quantile(0.75)

IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR

# 이상치 범위 밖 데이터 제거
hotel_df = hotel_df[(hotel_df['lead_time'] >= lower_bound) & (hotel_df['lead_time'] <= upper_bound)]

 


파생변수 생성

투숙 인원 수 파생변수

# adults, children, babies를 합쳐 people 변수 생성
hotel_df['people'] = hotel_df['adults'] + hotel_df['children'] + hotel_df['babies']

# 사람이 0명인 비정상 데이터 제거
hotel_df.drop(hotel_df[hotel_df['people'] == 0].index, inplace=True)

# 각 컬럼당 Null값 개수
hotel_df.isna().sum()

# 아이가 NaN인 데이터가 4개 있으므로 fillna(0)로 채워줌 수가 적으니까
hotel_df['children'] = hotel_df['children'].fillna(0)

# 하지만 어른이 한명도 없이 호텔 투숙하는 경우는 전처리 해야함
hotel_df[hotel_df['adults'] == 0]

# people 파생변수
hotel_df['people'] = hotel_df['adults'] + hotel_df['children'] + hotel_df['babies']
hotel_df.head()

# 어른이 '0'인 데이터 drop으로 삭제
hotel_df.drop(hotel_df[hotel_df['people'] == 0].index, inplace=True)

숙박일 수 파생변수

hotel_df['total_nights'] = hotel_df['stays_in_week_nights'] + hotel_df['stays_in_weekend_nights']

# 파생변수를 생성했으니 주말 호텔 숙박, 평일 호텔 숙박 데이터 삭제
hotel_df.drop(['stays_in_week_nights', 'stays_in_weekend_nights'], axis=1, inplace=True)

계절 파생변수

import calendar

season_dic = {'spring':[3,4,5], 'summer':[6,7,8], 'fall':[9,10,11], 'winter':[12,1,2]}
new_season_dic = {}

for i in season_dic:
    for j in season_dic[i]:
        new_season_dic[calendar.month_name[j]] = i

hotel_df['season'] = hotel_df['arrival_date_month'].map(new_season_dic)

# 월(month) 값을 계절(season) 값으로 바꿔서 새 컬럼을 만든다
hotel_df['season'] = hotel_df['arrival_date_month'].map(new_season_dic)

hotel_df[hotel_df['cancel_rate'].isna()] # cancel_rate 컬럼이 NaN인 행들만 골라서 보여준다
hotel_df.head()

hotel_df['agent'].value_counts(dropna=False).sort_index() # 👉 NaN(결측값)을 집계에서 버릴지 말지 정하는 옵션

범주형 데이터 처리

# 범주형 컬럼 원-핫 인코딩
hotel_df = pd.get_dummies(
    hotel_df,
    columns=hotel_df.select_dtypes(exclude=['number']).columns.tolist(),
    drop_first=True
)
from sklearn.model_selection import train_test_split  # 사이킷런(sklearn)

# is_canceled를 정답(y)으로 두고,
# 나머지 컬럼을 입력 데이터(X)로 사용하여
# 전체 데이터를 학습용 70%, 테스트용 30%로 랜덤 분할
X_train, X_test, y_train, y_test = train_test_split(
    hotel_df.drop('is_canceled', axis=1),
    hotel_df['is_canceled'],
    test_size=0.3,
    random_state=2025
)


# 숫자형 컬럼을 제외한 범주형(문자) 컬럼들을
# 원-핫 인코딩하여 모두 숫자 컬럼으로 변환
hotel_df = pd.get_dummies(
    hotel_df,
    columns=hotel_df.select_dtypes(exclude=['number']).columns.tolist(),
    drop_first=True
)

hotel_df.head()

Logistic Regression 모델

Logistic Regression은 확률 기반 이진 분류 모델로, 예약 취소 여부 예측에 적합한 알고리즘입니다.

from sklearn.linear_model import LogisticRegression

lr = LogisticRegression(max_iter=1000)
lr.fit(X_train, y_train)

데이터 스케일링

Logistic Regression은 거리 기반 최적화를 사용하므로 스케일링이 필수입니다.

from sklearn.preprocessing import StandardScaler # 각 컬럼을 평균 0, 표준편차 1로 맞춤

scaler = StandardScaler()
# # 학습 데이터 기준으로 평균과 표준편차를 계산하고 변환
x_train_scaled = scaler.fit_transform(X_train)
x_test_scaled = scaler.transform(X_test)
x_train_scaled
# 0 근처 → 평균에 가까운 값
# 음수 → 평균보다 작은 값
# 양수 → 평균보다 큰 값

 


모델 학습 및 평가

model = LogisticRegression(max_iter=1000)# max_iter 모델이 최적의 답을 찾기 위해 “반복해서 계산하는 최대 횟수
model.fit(x_train_scaled, y_train)

pred = model.predict(x_test_scaled)
from sklearn.metrics import accuracy_score  # 모델의 예측값과 실제값을 비교해 정확도를 계산하는 함수

# 테스트 데이터의 실제 정답(y_test)과
# 모델이 예측한 값(pred)을 비교하여 정확도(accuracy)를 계산
accuracy_score(y_test, pred)

# 원본 데이터프레임에서
# 실제 취소 여부(is_canceled)의 값 분포(0, 1 개수)를 확인
hotel_df['is_canceled'].value_counts()


accuracy_score(y_test, pred) -> 결과 1.0 호텔예약을 취소할지 예측을 너무 잘함 100퍼센트임

hotel_df['is_canceled'].value_counts()

 


오늘 공부한 내용 요약

  • EDA를 통해 데이터 분포와 이상치를 파악
  • IQR 방식으로 lead_time 이상치 제거
  • people, total_nights, season 파생변수 생성
  • 원-핫 인코딩으로 범주형 데이터 처리
  • StandardScaler로 데이터 표준화
  • Logistic Regression으로 예약 취소 예측

명령어 / 함수 설명 주요 옵션/인자
pd.read_csv() CSV 파일을 데이터프레임으로 불러옴 파일 경로
StandardScaler 데이터를 평균 0, 표준편차 1로 변환 fit, transform
LogisticRegression 이진 분류 모델 max_iter

```