본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 12. 17. · 6 Views
아파트 실거래가 예측 모델 완벽 가이드
파이썬과 머신러닝을 활용하여 아파트 실거래가를 예측하는 프로젝트입니다. 데이터 전처리부터 모델 학습, 성능 평가까지 전 과정을 단계별로 학습합니다. 실무에서 바로 활용 가능한 부동산 가격 예측 시스템을 구축해봅니다.
목차
1. 부동산 데이터셋 소개
김개발 씨는 데이터 분석 업무를 시작한 지 2개월 차입니다. 오늘 팀장님께서 흥미로운 프로젝트를 맡기셨습니다.
"김개발 씨, 우리 동네 아파트 가격을 예측할 수 있을까요?" 처음 듣는 머신러닝 프로젝트에 설레면서도 막막한 마음이 듭니다.
부동산 데이터셋은 아파트의 실거래 정보를 담고 있는 데이터입니다. 면적, 층수, 건축연도, 거래금액 등 다양한 **피처(feature)**로 구성되어 있습니다.
이 데이터를 분석하면 아파트 가격에 영향을 미치는 요인들을 파악할 수 있습니다. 실제 부동산 시장의 패턴을 학습하여 미래 가격을 예측하는 것이 목표입니다.
다음 코드를 살펴봅시다.
import pandas as pd
import numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
# 아파트 실거래가 데이터셋 생성
data = {
'area': [59, 84, 102, 135, 164], # 전용면적(㎡)
'floor': [5, 12, 3, 15, 8], # 층수
'age': [15, 5, 20, 2, 10], # 건축연수
'price': [45000, 62000, 48000, 78000, 70000] # 거래가격(만원)
}
df = pd.DataFrame(data)
print(df.head())
김개발 씨는 회사 책상에 앉아 멍하니 모니터를 바라봅니다. 머신러닝으로 아파트 가격을 예측한다니, 대체 어디서부터 시작해야 할까요?
바로 그때 옆자리의 박시니어 선배가 다가왔습니다. "김개발 씨, 머신러닝 프로젝트의 시작은 언제나 데이터입니다.
좋은 데이터가 있어야 좋은 모델을 만들 수 있어요." 데이터셋이란 무엇일까요? 쉽게 비유하자면, 데이터셋은 마치 요리사의 재료 창고와 같습니다.
맛있는 요리를 만들려면 신선하고 다양한 재료가 필요하듯이, 정확한 예측 모델을 만들려면 양질의 데이터가 필요합니다. 아파트 실거래가 데이터셋은 바로 우리의 재료입니다.
부동산 데이터에는 어떤 정보들이 담겨 있을까요? 가장 기본적인 정보는 전용면적입니다.
59㎡ 소형 아파트부터 164㎡ 대형 아파트까지 다양합니다. 일반적으로 면적이 넓을수록 가격이 높아지는 경향이 있습니다.
두 번째로 중요한 정보는 층수입니다. 3층 저층부터 15층 고층까지 있습니다.
많은 사람들이 중간층이나 고층을 선호하기 때문에 층수도 가격에 영향을 미칩니다. 1층이나 꼭대기층은 선호도가 낮아 가격이 상대적으로 저렴한 경우가 많습니다.
세 번째는 건축연수입니다. 신축 아파트는 2년, 오래된 아파트는 20년이 넘습니다.
새 아파트일수록 최신 설비와 디자인을 갖추고 있어 가격이 높게 형성됩니다. 마지막으로 가장 중요한 거래가격이 있습니다.
이것이 바로 우리가 예측하고자 하는 타겟 변수입니다. 45,000만 원부터 78,000만 원까지 다양하게 분포되어 있습니다.
박시니어 선배가 코드를 가리키며 설명합니다. "여기 보세요.
파이썬의 딕셔너리 형태로 데이터를 정의하고, pandas의 DataFrame으로 변환했습니다. DataFrame은 표 형태로 데이터를 다루기 아주 편리한 자료구조예요." 김개발 씨가 코드를 실행해봅니다.
화면에 깔끔한 표가 출력됩니다. 각 행은 하나의 아파트 거래 사례를 나타내고, 각 열은 해당 아파트의 특성을 보여줍니다.
"하지만 선배님, 실제로는 이렇게 데이터를 직접 만들지는 않잖아요?" 김개발 씨가 궁금해합니다. "맞아요.
실무에서는 보통 정부의 공공데이터 포털이나 부동산 정보 사이트에서 CSV 파일로 다운로드받습니다. 또는 API를 통해 실시간 데이터를 가져오기도 하죠.
하지만 학습 단계에서는 이렇게 간단한 샘플 데이터로 시작하는 것이 좋습니다." 실제 프로젝트에서는 수백, 수천 건의 거래 데이터를 다룹니다. 서울시 강남구만 해도 한 달에 수백 건의 아파트 거래가 발생합니다.
이런 대량의 데이터를 수집하고 정리하는 것이 머신러닝 프로젝트의 첫 번째 단계입니다. 데이터를 바라보는 김개발 씨의 눈빛이 달라집니다.
"아, 각각의 숫자가 실제 사람들의 거주 공간이었군요. 이 데이터 뒤에는 각자의 이야기가 있겠네요." "정확합니다.
데이터 과학자는 숫자 너머의 의미를 읽을 수 있어야 해요. 59㎡ 아파트는 신혼부부가, 164㎡는 대가족이 살고 있을 가능성이 높겠죠.
이런 통찰이 더 나은 모델을 만드는 시작점입니다." 김개발 씨는 이제 데이터셋이 단순한 숫자의 나열이 아니라는 것을 이해했습니다. 각 피처는 아파트의 특성을 나타내고, 이들이 조합되어 가격을 결정합니다.
이것이 바로 머신러닝의 기본 원리입니다.
실전 팁
💡 - 실무에서는 공공데이터 포털에서 실제 부동산 데이터를 다운로드하여 활용하세요
- 데이터가 많을수록 예측 정확도가 높아지므로 최소 수백 건 이상의 데이터를 확보하세요
- 각 피처의 의미를 명확히 이해하고 도메인 지식을 쌓는 것이 중요합니다
2. 데이터 로드 및 탐색
데이터를 만들었으니 이제 본격적으로 분석을 시작할 차례입니다. 하지만 김개발 씨는 어떻게 데이터를 탐색해야 할지 막막합니다.
"선배님, 데이터를 그냥 보기만 하면 되나요?" 박시니어 선배가 미소를 지으며 말합니다. "데이터를 탐색하는 것은 보물 찾기와 같아요.
숨겨진 패턴을 찾아내야 합니다."
**데이터 탐색(EDA)**은 데이터의 구조와 특성을 이해하는 과정입니다. 기술통계량을 확인하고 데이터 타입을 파악하며 결측치를 찾아냅니다.
이 단계에서 데이터의 전체적인 모습을 그릴 수 있습니다. pandas 라이브러리의 다양한 메서드를 활용하여 효율적으로 탐색할 수 있습니다.
다음 코드를 살펴봅시다.
# 데이터 기본 정보 확인
print("데이터 형태:", df.shape) # (행, 열) 개수
print("\n데이터 타입:")
print(df.dtypes)
# 기술통계량 확인
print("\n기술통계량:")
print(df.describe())
# 결측치 확인
print("\n결측치 개수:")
print(df.isnull().sum())
# 특정 열의 분포 확인
print("\n면적 범위:", df['area'].min(), "~", df['area'].max())
김개발 씨가 첫 번째 명령어를 실행합니다. df.shape가 출력하는 (5, 4)라는 숫자를 보며 고개를 갸우뚱합니다.
"5개의 행과 4개의 열이라는 뜻입니다." 박시니어 선배가 설명합니다. "즉, 5건의 아파트 거래 데이터가 있고, 각 거래마다 4가지 속성이 기록되어 있다는 거죠." 데이터의 형태를 아는 것은 매우 중요합니다.
마치 책을 읽기 전에 목차를 보는 것과 같습니다. 전체 구조를 파악해야 어떤 분석을 할 수 있을지 계획을 세울 수 있습니다.
다음으로 df.dtypes를 실행합니다. 화면에는 각 열의 데이터 타입이 나타납니다.
area, floor, age는 모두 int64(정수형), price도 int64입니다. "왜 데이터 타입을 확인해야 하나요?" 김개발 씨가 묻습니다.
박시니어 선배가 예를 듭니다. "만약 가격이 문자열로 저장되어 있다면 계산을 할 수 없습니다.
'45000'이라는 문자열과 '62000'이라는 문자열을 더하면 '4500062000'이 되어버리죠. 숫자 연산을 하려면 반드시 숫자형 타입이어야 합니다." 이제 가장 중요한 describe() 메서드를 실행합니다.
화면에 마법처럼 많은 정보가 펼쳐집니다. count는 데이터 개수, mean은 평균, std는 표준편차, min은 최솟값, 25%는 1사분위수, 50%는 중앙값, 75%는 3사분위수, max는 최댓값을 의미합니다.
이 모든 것을 기술통계량이라고 부릅니다. 김개발 씨가 숫자들을 유심히 봅니다.
"면적의 평균이 108.8㎡네요. 가격의 평균은 60,600만 원이고요." "잘 보셨어요.
그런데 평균만 보면 안 됩니다. 표준편차도 중요해요.
가격의 표준편차가 13,266만 원이나 되네요. 이건 가격의 편차가 크다는 뜻입니다.
45,000만 원짜리도 있고 78,000만 원짜리도 있으니까요." 실무에서 가장 조심해야 할 것은 결측치입니다. 결측치란 데이터가 없는 빈 칸을 의미합니다.
df.isnull().sum()을 실행하면 각 열의 결측치 개수가 나타납니다. 다행히 모든 열에서 0이 출력됩니다.
결측치가 하나도 없다는 뜻입니다. 하지만 실제 프로젝트에서는 거의 항상 결측치가 존재합니다.
"결측치가 있으면 어떻게 하나요?" 김개발 씨가 묻습니다. "여러 방법이 있어요.
해당 행을 삭제하거나, 평균값으로 채우거나, 머신러닝으로 예측해서 채울 수도 있습니다. 상황에 따라 적절한 방법을 선택해야 해요." 마지막으로 특정 열의 범위를 확인합니다.
면적은 최소 59㎡에서 최대 164㎡까지 분포합니다. 이런 정보는 나중에 **이상치(outlier)**를 탐지할 때 유용합니다.
예를 들어 만약 면적이 500㎡인 데이터가 있다면 이상합니다. 일반 아파트로는 너무 크기 때문입니다.
이런 데이터는 오류일 가능성이 높습니다. 김개발 씨가 여러 번 코드를 실행하며 연습합니다.
각 명령어가 어떤 정보를 보여주는지 점점 익숙해집니다. "데이터 탐색은 마치 처음 만난 사람과 대화하는 것과 같아요." 박시니어 선배가 말합니다.
"이름이 뭔지, 어디 사는지, 무슨 일을 하는지 물어보며 알아가잖아요. 데이터도 마찬가지입니다." 이제 김개발 씨는 자신 있게 데이터를 탐색할 수 있습니다.
데이터의 크기, 타입, 통계량, 결측치를 확인하는 것이 습관처럼 자리 잡았습니다.
실전 팁
💡 - describe() 메서드로 기술통계량을 먼저 확인하여 데이터 분포를 파악하세요
- 결측치는 모델 성능에 큰 영향을 미치므로 반드시 처리해야 합니다
- info() 메서드도 유용합니다. 데이터 타입과 결측치를 한 번에 확인할 수 있습니다
3. 피처 엔지니어링 수행
며칠 후, 김개발 씨는 모델의 예측 정확도가 생각보다 낮아 고민에 빠졌습니다. 박시니어 선배에게 하소연하자 선배가 말합니다.
"원본 데이터만으로는 부족해요. 피처 엔지니어링으로 새로운 특성을 만들어야 합니다." 김개발 씨는 처음 듣는 용어에 귀가 번쩍 뜨입니다.
피처 엔지니어링은 기존 데이터에서 새로운 특성을 만들어내는 과정입니다. 파생 변수를 생성하거나 기존 변수를 변환하여 모델의 성능을 높입니다.
도메인 지식을 활용하여 의미 있는 피처를 만드는 것이 핵심입니다. 좋은 피처는 복잡한 모델보다 더 큰 성능 향상을 가져옵니다.
다음 코드를 살펴봅시다.
# 평당 가격 계산 (1평 = 3.3㎡)
df['price_per_pyeong'] = df['price'] / (df['area'] / 3.3)
# 건물 상태 점수 (신축일수록 높은 점수)
df['building_score'] = 100 - df['age']
# 면적 구간 분류
df['size_category'] = pd.cut(df['area'],
bins=[0, 60, 85, 135, 200],
labels=['소형', '중소형', '중대형', '대형'])
# 층수 선호도 (중간층이 선호됨)
df['floor_preference'] = df['floor'].apply(lambda x: 1 if 5 <= x <= 15 else 0)
print(df[['area', 'price', 'price_per_pyeong', 'building_score']])
김개발 씨가 의아한 표정을 짓습니다. "데이터가 이미 있는데 왜 새로 만들어야 하나요?" 박시니어 선배가 커피를 한 모금 마시며 대답합니다.
"좋은 질문이에요. 예를 들어볼까요?
59㎡ 아파트가 45,000만 원이고, 84㎡가 62,000만 원이라고 해봅시다. 어느 것이 더 가성비가 좋을까요?" "음..." 김개발 씨가 계산기를 두드립니다.
"59㎡는 평당 약 2,518만 원, 84㎡는 평당 약 2,437만 원이네요. 84㎡가 조금 더 저렴하네요!" "바로 그겁니다.
평당 가격이라는 새로운 지표를 만들면 가성비를 한눈에 비교할 수 있습니다. 이것이 바로 피처 엔지니어링의 힘입니다." 파생 변수란 기존 변수들을 조합하여 만든 새로운 변수를 말합니다.
마치 요리에서 여러 재료를 섞어 새로운 소스를 만드는 것과 같습니다. 첫 번째 피처는 평당 가격입니다.
한국에서는 ㎡보다 평 단위를 많이 사용하므로 평당 가격으로 변환하면 더 직관적입니다. 1평은 약 3.3㎡이므로 면적을 3.3으로 나누면 평수가 나옵니다.
두 번째는 건물 상태 점수입니다. 건축연수가 5년이면 점수는 95점, 20년이면 80점입니다.
100점에서 건축연수를 빼는 간단한 공식이지만, 이렇게 하면 신축일수록 높은 점수를 받게 됩니다. "왜 그냥 건축연수를 쓰지 않고 점수로 바꾸나요?" 김개발 씨가 묻습니다.
"좋은 지적입니다. 건축연수는 숫자가 클수록 오래된 건물이라 가격이 낮아집니다.
반대로 점수는 높을수록 좋습니다. 이렇게 변환하면 모든 피처가 같은 방향성을 가지게 되어 모델이 학습하기 쉬워집니다." 세 번째는 면적 구간 분류입니다.
pandas의 cut() 함수를 사용하여 연속형 변수를 범주형으로 바꿉니다. 60㎡ 이하는 소형, 85㎡ 이하는 중소형, 135㎡ 이하는 중대형, 그 이상은 대형으로 분류합니다.
이렇게 하는 이유는 무엇일까요? 부동산 시장에서는 특정 면적대가 인기가 많습니다.
예를 들어 신혼부부는 소형을, 자녀가 있는 가정은 중대형을 선호합니다. 이런 시장의 특성을 반영하는 것입니다.
네 번째는 층수 선호도입니다. 일반적으로 5층에서 15층 사이의 중간층이 인기가 많습니다.
너무 낮으면 소음과 프라이버시 문제가 있고, 너무 높으면 엘리베이터 의존도가 높아지기 때문입니다. lambda 함수를 사용하여 5층 이상 15층 이하면 1, 그 외에는 0을 부여합니다.
이것을 원-핫 인코딩의 일종이라고 볼 수 있습니다. 코드를 실행하자 새로운 열들이 추가된 DataFrame이 출력됩니다.
김개발 씨의 눈이 반짝입니다. "와, 정말 유용한 정보들이 생겼네요!" 실무에서는 이보다 훨씬 많은 피처를 만듭니다.
지하철역까지의 거리, 학군, 주변 편의시설 개수, 최근 거래 추세 등 수십 개의 피처를 조합합니다. "피처 엔지니어링은 과학인 동시에 예술입니다." 박시니어 선배가 말합니다.
"데이터에 대한 깊은 이해와 창의력이 필요해요. 부동산 전문가의 지식을 데이터로 표현하는 겁니다." 김개발 씨가 고개를 끄덕입니다.
"그렇군요. 단순히 코딩 기술만이 아니라 도메인 지식도 중요하네요." "정확합니다.
최고의 데이터 과학자는 기술과 도메인 지식을 모두 갖춘 사람입니다. 이제 김개발 씨도 부동산 시장에 대해 공부해야겠네요." 박시니어 선배가 웃으며 말합니다.
피처 엔지니어링 후 모델을 다시 학습시키면 예측 정확도가 크게 향상됩니다. 새로운 피처들이 가격을 결정하는 숨겨진 패턴을 더 잘 포착하기 때문입니다.
실전 팁
💡 - 도메인 전문가와 협업하여 의미 있는 피처를 발굴하세요
- 너무 많은 피처를 만들면 과적합이 발생할 수 있으므로 중요한 것만 선택하세요
- 피처 중요도 분석으로 실제로 유용한 피처인지 검증하세요
4. 상관관계 분석
김개발 씨는 만든 피처들이 정말 가격과 관련이 있는지 궁금해졌습니다. "선배님, 이 피처들이 진짜 의미가 있을까요?" 박시니어 선배가 노트북을 가리킵니다.
"상관관계 분석으로 확인해봅시다. 숫자로 증명할 수 있습니다."
상관관계 분석은 변수 간의 관계를 수치로 나타냅니다. 상관계수는 -1에서 1 사이의 값으로, 1에 가까우면 양의 상관관계, -1에 가까우면 음의 상관관계를 의미합니다.
타겟 변수와 피처 간의 상관관계를 파악하면 어떤 피처가 중요한지 알 수 있습니다. 시각화와 함께 분석하면 인사이트를 얻기 쉽습니다.
다음 코드를 살펴봅시다.
import matplotlib.pyplot as plt
import seaborn as sns
# 숫자형 컬럼만 선택하여 상관관계 계산
numeric_df = df.select_dtypes(include=[np.number])
correlation = numeric_df.corr()
# 가격과의 상관계수 확인
price_corr = correlation['price'].sort_values(ascending=False)
print("가격과의 상관계수:")
print(price_corr)
# 상관관계 히트맵 생성 (시각화)
plt.figure(figsize=(10, 8))
sns.heatmap(correlation, annot=True, cmap='coolwarm', center=0)
plt.title('Feature Correlation Heatmap')
김개발 씨가 상관계수라는 용어를 처음 들어봅니다. "상관계수가 뭔가요?" 박시니어 선배가 화이트보드를 가리키며 설명합니다.
"두 변수가 얼마나 함께 움직이는지를 나타내는 숫자입니다. 쉽게 말하면 친한 정도를 점수로 매기는 거예요." 상관계수는 피어슨 상관계수를 기본으로 사용합니다.
마치 친구 사이의 친밀도를 측정하는 것과 같습니다. 1점이면 완전히 함께 움직이고, 0점이면 전혀 관계없고, -1점이면 정반대로 움직입니다.
예를 들어봅시다. 면적이 넓어질수록 가격이 높아진다면 양의 상관관계입니다.
반대로 건축연수가 오래될수록 가격이 낮아진다면 음의 상관관계입니다. 코드를 실행하자 상관계수 목록이 출력됩니다.
가격 자신과의 상관계수는 당연히 1.0입니다. 그 아래로 다른 피처들의 상관계수가 나열됩니다.
"오, 면적(area)의 상관계수가 0.87이네요!" 김개발 씨가 흥분합니다. "맞아요.
0.87은 아주 높은 양의 상관관계입니다. 면적이 넓을수록 가격이 높다는 것을 숫자로 증명한 거예요." 박시니어 선배가 설명합니다.
일반적으로 상관계수의 해석은 다음과 같습니다. 0.7 이상이면 강한 상관관계, 0.40.7이면 중간 상관관계, 0.20.4면 약한 상관관계, 0.2 미만이면 거의 무관합니다.
건물 점수(building_score)는 0.65 정도로 중간 이상의 양의 상관관계를 보입니다. 새 건물일수록 가격이 높다는 예상이 맞았습니다.
반대로 건축연수(age)는 -0.65 정도의 음의 상관관계를 보입니다. 오래된 건물일수록 가격이 낮아진다는 뜻입니다.
이것은 building_score와 정확히 반대입니다. "잠깐, 그럼 age와 building_score는 사실상 같은 정보 아닌가요?" 김개발 씨가 예리하게 지적합니다.
"아주 좋은 관찰입니다! 그것을 다중공선성 문제라고 합니다.
두 변수가 너무 강하게 상관되어 있으면 하나만 사용하는 것이 좋습니다. 중복 정보는 모델을 혼란스럽게 만들 수 있어요." 이제 히트맵을 그려봅니다.
seaborn 라이브러리의 heatmap() 함수를 사용하면 상관관계를 색상으로 표현할 수 있습니다. 빨간색은 양의 상관관계, 파란색은 음의 상관관계, 흰색은 무관을 나타냅니다.
색깔이 진할수록 상관관계가 강합니다. 히트맵을 보니 한눈에 패턴이 보입니다.
area와 price가 진한 빨간색으로 표시되어 강한 양의 상관관계를 보여줍니다. age와 price는 파란색으로 음의 상관관계를 나타냅니다.
"시각화의 힘은 정말 대단해요." 박시니어 선배가 말합니다. "숫자로만 보면 놓치기 쉬운 패턴도 그림으로 보면 바로 알아챌 수 있습니다." 실무에서는 수십 개의 피처가 있을 수 있습니다.
이럴 때 히트맵은 더욱 유용합니다. 어떤 피처들이 서로 강하게 연관되어 있는지, 어떤 피처가 타겟과 관련이 높은지 한눈에 파악할 수 있습니다.
김개발 씨가 상관계수를 다시 살펴봅니다. "그런데 상관관계가 높다고 해서 꼭 인과관계는 아니겠죠?" "정확합니다!" 박시니어 선배가 감탄합니다.
"상관관계와 인과관계는 다릅니다. 아이스크림 판매량과 익사 사고가 상관관계가 있다고 해서 아이스크림이 익사를 유발하는 건 아니죠.
둘 다 여름에 증가하기 때문입니다." "통계의 함정에 빠지지 않으려면 항상 비판적으로 생각해야 해요." 선배가 덧붙입니다. "데이터 과학자는 숫자 뒤의 진실을 찾는 탐정이어야 합니다." 상관관계 분석을 마친 김개발 씨는 이제 어떤 피처가 중요한지 명확히 알게 되었습니다.
다음 단계로 모델을 만들 준비가 되었습니다.
실전 팁
💡 - 상관계수가 0.7 이상인 피처들은 다중공선성 문제가 있을 수 있으니 하나만 선택하세요
- 시각화를 함께 활용하면 패턴을 더 쉽게 발견할 수 있습니다
- 상관관계가 낮다고 무조건 중요하지 않은 것은 아닙니다. 비선형 관계일 수도 있습니다
5. 선형 회귀 모델 학습
드디어 본격적인 머신러닝 단계입니다. 김개발 씨의 가슴이 두근거립니다.
"선배님, 이제 정말 예측 모델을 만드는 건가요?" 박시니어 선배가 고개를 끄덕입니다. "네, 선형 회귀부터 시작해봅시다.
가장 기본이면서도 강력한 알고리즘입니다."
선형 회귀는 입력 변수와 출력 변수 사이의 선형 관계를 모델링합니다. 최소제곱법으로 최적의 직선을 찾아 예측합니다.
학습 데이터와 테스트 데이터를 분리하여 모델의 일반화 성능을 평가합니다. scikit-learn 라이브러리를 사용하면 몇 줄의 코드로 모델을 구축할 수 있습니다.
다음 코드를 살펴봅시다.
# 피처와 타겟 분리
X = df[['area', 'floor', 'age']] # 입력 피처
y = df['price'] # 타겟 변수
# 학습/테스트 데이터 분할 (80:20)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# 선형 회귀 모델 생성 및 학습
model = LinearRegression()
model.fit(X_train, y_train)
# 학습된 계수 확인
print("회귀 계수:", model.coef_)
print("절편:", model.intercept_)
김개발 씨가 코드를 바라보며 긴장합니다. "선형 회귀가 정확히 뭔가요?" "가장 간단한 머신러닝 알고리즘입니다." 박시니어 선배가 그래프를 그리며 설명합니다.
"데이터에 가장 잘 맞는 직선을 찾는 거예요." 선형 회귀는 마치 점들을 관통하는 최적의 선을 긋는 것과 같습니다. 학창 시절 배운 일차함수 y = ax + b를 떠올려보세요.
여기서 a는 기울기, b는 절편입니다. 다변수 선형 회귀에서는 변수가 여러 개입니다.
우리의 경우 면적, 층수, 건축연수 세 가지입니다. 수식으로 표현하면 이렇습니다.
가격 = (계수1 × 면적) + (계수2 × 층수) + (계수3 × 건축연수) + 절편 모델은 최소제곱법으로 이 계수들을 찾습니다. 실제 가격과 예측 가격의 차이를 최소화하는 값들을 수학적으로 계산합니다.
첫 번째 단계는 피처와 타겟을 분리하는 것입니다. X는 입력 피처들, y는 우리가 예측하고자 하는 타겟입니다.
이것은 문제와 정답을 나누는 것과 같습니다. 두 번째 단계는 데이터 분할입니다.
train_test_split() 함수로 데이터를 학습용과 테스트용으로 나눕니다. "왜 데이터를 나누나요?" 김개발 씨가 묻습니다.
"아주 중요한 질문입니다." 박시니어 선배가 강조합니다. "시험을 생각해보세요.
공부할 때 본 문제로만 시험을 보면 실력을 제대로 평가할 수 없죠. 처음 보는 문제로 시험을 봐야 진짜 실력을 알 수 있습니다." 학습 데이터는 모델을 훈련시키는 데 사용하고, 테스트 데이터는 모델의 성능을 평가하는 데 사용합니다.
보통 80:20이나 70:30 비율로 나눕니다. random_state=42는 무엇일까요?
이것은 난수 생성의 시드값입니다. 같은 값을 설정하면 항상 같은 방식으로 데이터가 분할되어 재현 가능한 결과를 얻을 수 있습니다.
세 번째 단계는 모델 생성과 학습입니다. LinearRegression() 객체를 만들고 fit() 메서드로 학습시킵니다.
fit()은 모델을 데이터에 맞춘다는 의미입니다. 마치 옷을 몸에 맞게 재단하는 것처럼, 데이터에 가장 잘 맞는 계수들을 찾아냅니다.
학습이 완료되면 coef_와 intercept_에 계수와 절편이 저장됩니다. 이것들을 출력해봅니다.
"계수가 [250, 50, -300] 같은 식으로 나오네요." 김개발 씨가 말합니다. "그 의미를 해석해봅시다.
첫 번째 250은 면적이 1㎡ 증가하면 가격이 250만 원 증가한다는 뜻입니다. 두 번째 50은 층수가 1층 올라가면 50만 원 증가, 세 번째 -300은 건축연수가 1년 증가하면 300만 원 감소한다는 의미입니다." 이것이 바로 선형 회귀의 장점입니다.
해석 가능성이 뛰어납니다. 딥러닝처럼 블랙박스가 아니라 각 변수가 결과에 얼마나 영향을 미치는지 명확히 알 수 있습니다.
절편은 모든 피처가 0일 때의 기본 가격을 의미합니다. 물론 현실에서는 면적이 0인 아파트는 없으므로 수학적 의미일 뿐입니다.
김개발 씨가 감탄합니다. "겨우 몇 줄의 코드로 예측 모델이 만들어지다니 신기해요!" "scikit-learn의 힘입니다." 박시니어 선배가 미소 짓습니다.
"복잡한 수학을 몰라도 라이브러리가 모두 처리해줍니다. 하지만 원리를 이해하면 더 잘 활용할 수 있어요." 모델 학습은 순식간에 완료됩니다.
데이터가 적기 때문입니다. 실제로는 수만 건의 데이터로 학습하면 시간이 더 걸립니다.
이제 모델은 학습을 마쳤습니다. 다음 단계는 이 모델이 얼마나 정확한지 평가하는 것입니다.
실전 팁
💡 - 데이터가 충분하다면 학습:검증:테스트를 60:20:20으로 나누어 사용하세요
- random_state를 설정하여 실험의 재현성을 확보하세요
- 회귀 계수를 해석하여 비즈니스 인사이트를 도출할 수 있습니다
6. 모델 성능 평가
모델을 만들었으니 이제 성능을 확인할 차례입니다. 김개발 씨가 조마조마한 마음으로 묻습니다.
"선배님, 우리 모델이 얼마나 정확한가요?" 박시니어 선배가 웃으며 답합니다. "평가 지표로 객관적으로 측정해봅시다."
모델 성능은 여러 평가 지표로 측정합니다. R² 점수는 모델이 데이터를 얼마나 잘 설명하는지, MAE는 평균 오차, RMSE는 오차의 제곱근을 나타냅니다.
테스트 데이터로 평가하여 과적합 여부를 확인합니다. 여러 지표를 종합적으로 판단해야 합니다.
다음 코드를 살펴봅시다.
from sklearn.metrics import r2_score, mean_absolute_error, mean_squared_error
# 테스트 데이터로 예측
y_pred = model.predict(X_test)
# 성능 지표 계산
r2 = r2_score(y_test, y_pred)
mae = mean_absolute_error(y_test, y_pred)
rmse = np.sqrt(mean_squared_error(y_test, y_pred))
print(f"R² 점수: {r2:.3f}")
print(f"평균 절대 오차(MAE): {mae:.0f}만원")
print(f"평균 제곱근 오차(RMSE): {rmse:.0f}만원")
# 실제값 vs 예측값 비교
comparison = pd.DataFrame({'실제': y_test, '예측': y_pred})
print("\n실제값 vs 예측값:")
print(comparison)
김개발 씨가 predict() 메서드를 실행합니다. 순식간에 예측값이 생성됩니다.
"이렇게 간단해요?" "네, 학습된 모델로 예측하는 것은 아주 빠릅니다." 박시니어 선배가 설명합니다. "이제 이 예측값이 얼마나 정확한지 측정해야 합니다." 모델 평가는 마치 시험 성적표를 받는 것과 같습니다.
여러 과목의 점수를 확인하여 종합적으로 실력을 판단합니다. 첫 번째 지표는 **R² 점수(결정계수)**입니다.
0에서 1 사이의 값으로, 1에 가까울수록 좋습니다. 0.8이면 모델이 데이터의 80%를 설명한다는 의미입니다.
"R² 점수가 0.92네요!" 김개발 씨가 기뻐합니다. "아주 좋은 점수입니다.
92%를 설명한다는 뜻이에요. 하지만 한 가지 지표만 보면 안 됩니다.
다른 지표도 확인해야 해요." 박시니어 선배가 주의를 줍니다. 두 번째는 **MAE(Mean Absolute Error, 평균 절대 오차)**입니다.
예측값과 실제값의 차이를 절댓값으로 평균낸 것입니다. 예를 들어 실제 가격이 60,000만 원인데 58,000만 원으로 예측했다면 오차는 2,000만 원입니다.
이런 오차들의 평균이 MAE입니다. MAE가 1,500만 원이라면 평균적으로 1,500만 원 정도 틀린다는 의미입니다.
부동산 가격으로는 괜찮은 수준입니다. 세 번째는 **RMSE(Root Mean Squared Error, 평균 제곱근 오차)**입니다.
오차를 제곱한 뒤 평균내고 다시 제곱근을 취합니다. "왜 이렇게 복잡하게 하나요?" 김개발 씨가 궁금해합니다.
"제곱을 하면 큰 오차에 더 큰 패널티를 줍니다." 박시니어 선배가 설명합니다. "1,000만 원 오차 10개보다 10,000만 원 오차 1개가 더 나쁘다고 판단하는 거죠.
이상치에 민감한 지표입니다." RMSE는 MAE보다 항상 크거나 같습니다. 두 값의 차이가 크면 큰 오차가 많다는 신호입니다.
실제값과 예측값을 DataFrame으로 나란히 출력해봅니다. 실제 60,000만 원, 예측 58,500만 원처럼 값들이 비교됩니다.
김개발 씨가 자세히 들여다봅니다. "대부분 비슷하네요.
그런데 하나는 오차가 좀 크네요." "그런 케이스를 **아웃라이어(이상치)**라고 합니다. 특이한 조건 때문에 예측이 어려운 경우입니다.
예를 들어 리모델링을 했거나 특별한 뷰가 있는 경우죠." 실무에서는 어떤 지표를 중요하게 볼까요? 비즈니스 목표에 따라 다릅니다.
부동산 앱이라면 사용자 경험을 위해 MAE를 중시할 수 있습니다. "평균 1,500만 원 오차"라고 설명하기 쉽기 때문입니다.
반면 투자 의사결정을 위한 모델이라면 RMSE를 중시할 수 있습니다. 큰 실수 하나가 치명적이기 때문입니다.
"모델이 완벽할 수는 없습니다." 박시니어 선배가 조언합니다. "항상 오차가 존재하고, 그것을 인정하고 관리하는 것이 중요해요." 김개발 씨가 고개를 끄덕입니다.
"네, 오차 범위를 사용자에게 알려줘야겠네요. '±1,500만 원 범위에서 예측됩니다' 이런 식으로요." "정확합니다!
불확실성을 투명하게 전달하는 것이 신뢰를 얻는 방법입니다." 성능 평가를 마친 김개발 씨는 뿌듯함을 느낍니다. 처음 만든 모델치고는 꽤 좋은 성능입니다.
실전 팁
💡 - R²만으로 판단하지 말고 MAE, RMSE를 함께 확인하세요
- 테스트 데이터 성능이 학습 데이터보다 크게 낮다면 과적합입니다
- 비즈니스 관점에서 허용 가능한 오차 범위를 정의하세요
7. 가격 예측 및 결과 해석
모든 준비가 끝났습니다. 이제 실제로 새로운 아파트의 가격을 예측해볼 차례입니다.
김개발 씨가 설레는 마음으로 말합니다. "선배님, 진짜 예측을 해볼까요?" 박시니어 선배가 노트북을 밀어줍니다.
"직접 해보세요. 이제 충분히 할 수 있습니다."
학습된 모델로 새로운 데이터를 예측합니다. 입력 피처를 동일한 형식으로 준비하여 predict() 메서드를 호출합니다.
예측 결과를 해석하고 신뢰구간을 고려합니다. 실무에서는 예측값을 시각화하거나 리포트로 만들어 전달합니다.
다음 코드를 살펴봅시다.
# 새로운 아파트 데이터 (예측하고 싶은 아파트)
new_apartment = pd.DataFrame({
'area': [75], # 75㎡
'floor': [10], # 10층
'age': [8] # 건축 8년
})
# 가격 예측
predicted_price = model.predict(new_apartment)
print(f"예측 가격: {predicted_price[0]:,.0f}만원")
# 피처별 기여도 분석
contributions = new_apartment.iloc[0] * model.coef_
print("\n피처별 가격 기여도:")
for feature, contribution in zip(['면적', '층수', '건축연수'], contributions):
print(f"{feature}: {contribution:+,.0f}만원")
print(f"기본값(절편): {model.intercept_:+,.0f}만원")
김개발 씨가 손을 떨며 새로운 아파트 정보를 입력합니다. 75㎡, 10층, 건축 8년된 아파트입니다.
"DataFrame으로 만들어야 하나요?" 김개발 씨가 묻습니다. "네, 학습할 때와 동일한 형식이어야 합니다.
컬럼 이름과 순서도 같아야 해요." 박시니어 선배가 답합니다. 새로운 데이터 예측은 마치 시험을 치르는 것과 같습니다.
공부를 마친 학생이 처음 보는 문제를 푸는 것처럼, 학습을 마친 모델이 처음 보는 아파트의 가격을 예측합니다. predict() 메서드를 호출하자 즉시 결과가 나옵니다.
"55,000만원"이라는 예측값이 출력됩니다. 김개발 씨가 눈을 반짝입니다.
"정말 예측이 되네요!" "이제 이 예측값을 해석해봅시다." 박시니어 선배가 두 번째 코드를 가리킵니다. "피처별 기여도를 분석하면 왜 이 가격이 나왔는지 설명할 수 있습니다." 각 피처에 해당 계수를 곱하면 그 피처가 가격에 얼마나 기여하는지 알 수 있습니다.
면적 75㎡는 +18,750만 원에 기여합니다. 10층은 +500만 원, 건축 8년은 -2,400만 원에 기여합니다.
절편이 38,150만 원이라면 모두 합쳐서 55,000만 원이 됩니다. "오, 건축연수가 마이너스 기여를 하네요." 김개발 씨가 관찰합니다.
"맞아요. 오래된 만큼 가격을 깎아내리는 겁니다.
만약 신축이었다면 더 비쌌을 거예요." 박시니어 선배가 설명합니다. 이런 해석 가능성이 선형 회귀의 큰 장점입니다.
고객에게 "이 아파트는 면적이 넓어서 1억 8천만 원 가치가 있지만, 8년 됐기 때문에 2,400만 원이 할인되어 최종 5억 5천만 원으로 예측됩니다"라고 설명할 수 있습니다. 실무에서는 예측값에 신뢰구간도 함께 제공합니다.
"5억 5천만 원 ± 1,500만 원" 같은 식으로 불확실성을 표현합니다. "선배님, 이 모델을 실제 서비스에 사용할 수 있을까요?" 김개발 씨가 묻습니다.
박시니어 선배가 진지하게 답합니다. "현재 모델은 시작점입니다.
실서비스에 투입하려면 더 많은 데이터, 더 많은 피처, 더 정교한 모델이 필요합니다. 하지만 원리는 똑같아요." 실제 부동산 가격 예측 서비스는 수십만 건의 거래 데이터를 학습하고, 지하철역 거리, 학군, 주변 시세, 경제 지표 등 수백 개의 피처를 사용합니다.
때로는 딥러닝이나 앙상블 모델을 활용하기도 합니다. "또한 모델을 주기적으로 업데이트해야 합니다.
부동산 시장은 계속 변하니까요. 작년 데이터로 학습한 모델은 올해는 정확하지 않을 수 있습니다." 김개발 씨가 자신의 코드를 자랑스럽게 바라봅니다.
비록 간단한 모델이지만, 머신러닝의 전체 파이프라인을 경험했습니다. 데이터 준비, 탐색, 피처 엔지니어링, 모델 학습, 평가, 예측까지 모든 단계를 거쳤습니다.
"축하합니다, 김개발 씨." 박시니어 선배가 악수를 청합니다. "이제 데이터 과학자로서의 첫걸음을 뗐습니다." 김개발 씨는 뿌듯한 마음으로 미소 짓습니다.
이제 더 복잡한 프로젝트에도 도전할 자신감이 생겼습니다. 머신러닝은 어렵지 않습니다.
기본 원리를 이해하고 한 단계씩 차근차근 진행하면 누구나 할 수 있습니다. 오늘 배운 내용을 바탕으로 여러분도 실제 프로젝트에 도전해보세요.
실전 팁
💡 - 예측값만이 아니라 신뢰구간도 함께 제공하면 신뢰도가 높아집니다
- 피처별 기여도 분석으로 예측 근거를 명확히 설명하세요
- 모델은 정기적으로 재학습하여 최신 시장 상황을 반영해야 합니다
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
Helm 마이크로서비스 패키징 완벽 가이드
Kubernetes 환경에서 마이크로서비스를 효율적으로 패키징하고 배포하는 Helm의 핵심 기능을 실무 중심으로 학습합니다. Chart 생성부터 릴리스 관리까지 체계적으로 다룹니다.
보안 아키텍처 구성 완벽 가이드
프로젝트의 보안을 처음부터 설계하는 방법을 배웁니다. AWS 환경에서 VPC부터 WAF, 암호화, 접근 제어까지 실무에서 바로 적용할 수 있는 보안 아키텍처를 단계별로 구성해봅니다.
AWS Organizations 완벽 가이드
여러 AWS 계정을 체계적으로 관리하고 통합 결제와 보안 정책을 적용하는 방법을 실무 스토리로 쉽게 배워봅니다. 초보 개발자도 바로 이해할 수 있는 친절한 설명과 실전 예제를 제공합니다.
AWS KMS 암호화 완벽 가이드
AWS KMS(Key Management Service)를 활용한 클라우드 데이터 암호화 방법을 초급 개발자를 위해 쉽게 설명합니다. CMK 생성부터 S3, EBS 암호화, 봉투 암호화까지 실무에 필요한 모든 내용을 담았습니다.
AWS Secrets Manager 완벽 가이드
AWS에서 데이터베이스 비밀번호, API 키 등 민감한 정보를 안전하게 관리하는 Secrets Manager의 핵심 개념과 실무 활용법을 배워봅니다. 초급 개발자도 쉽게 따라할 수 있도록 실전 예제와 함께 설명합니다.