2장 머신러닝 프로젝트 처음부터 끝까지¶
○ 프로젝트 순서
- 큰 그림을 봅니다.
- 데이터를 구합니다.
- 데이터로부터 통찰을 얻기 위해 탐색하고 시각화합니다.
- 머신러닝 알고리즘을 위해 데이터를 준비합니다.
- 모델을 선택하고 훈련시킵니다.
- 모델을 상세하게 조정합니다.
- 솔루션을 제시합니다.
- 시스템을 론칭하고 모니터링하고 유지 보수합니다.
# 공통 모듈 임포트
import numpy as np
import os
import tarfile
import urllib.request
# 그림을 저장할 위치
PROJECT_ROOT_DIR = "."
CHAPTER_ID = "end_to_end_project"
IMAGES_PATH = os.path.join(PROJECT_ROOT_DIR, "images", CHAPTER_ID)
os.makedirs(IMAGES_PATH, exist_ok=True)
def save_fig(fig_id, tight_layout=True, fig_extension="png", resolution=300):
path = os.path.join(IMAGES_PATH, fig_id + "." + fig_extension)
print("그림 저장:", fig_id)
if tight_layout:
plt.tight_layout()
plt.savefig(path, format=fig_extension, dpi=resolution)
# 데이터 가져오기
DOWNLOAD_ROOT = "https://raw.githubusercontent.com/rickiepark/handson-ml2/master/"
HOUSING_PATH = os.path.join("datasets", "housing")
HOUSING_URL = DOWNLOAD_ROOT + "datasets/housing/housing.tgz"
def fetch_housing_data(housing_url=HOUSING_URL, housing_path=HOUSING_PATH):
if not os.path.isdir(housing_path):
os.makedirs(housing_path)
tgz_path = os.path.join(housing_path, "housing.tgz")
urllib.request.urlretrieve(housing_url, tgz_path)
housing_tgz = tarfile.open(tgz_path)
housing_tgz.extractall(path=housing_path)
housing_tgz.close()
fetch_housing_data()
import pandas as pd
def load_housing_data(housing_path=HOUSING_PATH):
csv_path = os.path.join(housing_path, "housing.csv")
return pd.read_csv(csv_path)
housing = load_housing_data()
# 데이터 살펴보기
housing.head()
longitude | latitude | housing_median_age | total_rooms | total_bedrooms | population | households | median_income | median_house_value | ocean_proximity | |
---|---|---|---|---|---|---|---|---|---|---|
0 | -122.23 | 37.88 | 41.0 | 880.0 | 129.0 | 322.0 | 126.0 | 8.3252 | 452600.0 | NEAR BAY |
1 | -122.22 | 37.86 | 21.0 | 7099.0 | 1106.0 | 2401.0 | 1138.0 | 8.3014 | 358500.0 | NEAR BAY |
2 | -122.24 | 37.85 | 52.0 | 1467.0 | 190.0 | 496.0 | 177.0 | 7.2574 | 352100.0 | NEAR BAY |
3 | -122.25 | 37.85 | 52.0 | 1274.0 | 235.0 | 558.0 | 219.0 | 5.6431 | 341300.0 | NEAR BAY |
4 | -122.25 | 37.85 | 52.0 | 1627.0 | 280.0 | 565.0 | 259.0 | 3.8462 | 342200.0 | NEAR BAY |
2.2 큰 그림 보기¶
2.2.1 문제 정의¶
비즈니스의 목적을 분명히 하는 것이 중요
파이프라인 : 데이터 처리 컴포넌트들이 연속되어 있는 것을 데이터 파이프라인이라고 함
- 컴포넌트는 많은 데이터를 추출해 처리하고 그 결과를 다른 데이터 저장소로 보내고 일정 시간 후 파이프라인의 다음 컴포넌트가 그 데이터를 추출해 자신의 출력 결과를 만듦.
- 각 컴포넌트는 완전히 독립적, 컴포넌트 사이의 인터페이스는 데이터 저장소뿐
- 파이프라인은 시스템을 이해하기 쉽게 만들고, 각 팀은 각자의 컴포넌트에 집중할 수 있습니다.
- 한 컴포넌트가 다운되더라도 하위 컴포넌트는 문제가 생긴 컴포넌트의 마지막 출력을 사용해 평상시와 같이 계속 동작할 수 있음, 그래서 시스템이 매우 견고해짐
2.2.2 성능 측정 지표 선택¶
회귀 문제의 전형적인 성능 지표는 평균 제곱근 오차(RMSE)이다.(유클리디안 노름)
이상치가 많은 경우 평균 절대 오차(MAE)를 고려해볼 수 있다.(맨해튼 노름)
2.2.3 가정 검사¶
2.3 데이터 가져오기¶
2.3.3 데이터 구조 훑어보기¶
- 10개의 행으로 구성
# 데이터 정보 확인
housing.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 20640 entries, 0 to 20639 Data columns (total 10 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 longitude 20640 non-null float64 1 latitude 20640 non-null float64 2 housing_median_age 20640 non-null float64 3 total_rooms 20640 non-null float64 4 total_bedrooms 20433 non-null float64 5 population 20640 non-null float64 6 households 20640 non-null float64 7 median_income 20640 non-null float64 8 median_house_value 20640 non-null float64 9 ocean_proximity 20640 non-null object dtypes: float64(9), object(1) memory usage: 1.6+ MB
# 범주형 변수 ocean_proximity의 구성 확인
housing["ocean_proximity"].value_counts()
<1H OCEAN 9136 INLAND 6551 NEAR OCEAN 2658 NEAR BAY 2290 ISLAND 5 Name: ocean_proximity, dtype: int64
housing.describe()
longitude | latitude | housing_median_age | total_rooms | total_bedrooms | population | households | median_income | median_house_value | |
---|---|---|---|---|---|---|---|---|---|
count | 20640.000000 | 20640.000000 | 20640.000000 | 20640.000000 | 20433.000000 | 20640.000000 | 20640.000000 | 20640.000000 | 20640.000000 |
mean | -119.569704 | 35.631861 | 28.639486 | 2635.763081 | 537.870553 | 1425.476744 | 499.539680 | 3.870671 | 206855.816909 |
std | 2.003532 | 2.135952 | 12.585558 | 2181.615252 | 421.385070 | 1132.462122 | 382.329753 | 1.899822 | 115395.615874 |
min | -124.350000 | 32.540000 | 1.000000 | 2.000000 | 1.000000 | 3.000000 | 1.000000 | 0.499900 | 14999.000000 |
25% | -121.800000 | 33.930000 | 18.000000 | 1447.750000 | 296.000000 | 787.000000 | 280.000000 | 2.563400 | 119600.000000 |
50% | -118.490000 | 34.260000 | 29.000000 | 2127.000000 | 435.000000 | 1166.000000 | 409.000000 | 3.534800 | 179700.000000 |
75% | -118.010000 | 37.710000 | 37.000000 | 3148.000000 | 647.000000 | 1725.000000 | 605.000000 | 4.743250 | 264725.000000 |
max | -114.310000 | 41.950000 | 52.000000 | 39320.000000 | 6445.000000 | 35682.000000 | 6082.000000 | 15.000100 | 500001.000000 |
total_bedrooms 컬럼에 null 값이 포함되어있음을 확인
describe()는 null 값을 제외한 통계값 추출
데이터의 형태를 빠르게 검토하는 다른 방법은 각 숫자형 특성을 히스토그램으로 그려보는 것
# housing 데이터에 대한 히스토그램
%matplotlib inline
import matplotlib.pyplot as plt
housing.hist(bins=50, figsize=(20,15))
plt.show()
- 데이터를 더 깊게 들여다보기 전에 테스트 세트를 따로 떼어놓아야 합니다. 그리고 테스트 세트를 절대 들여다보면 안 됩니다.
2.3.4 테스트 세트 만들기¶
테스트 세트로 일반화 오차를 추정하면 매우 낙관적인 추정이 되며 시스템을 론칭했을 때 기대한 성능이 나오지 않는 데이터 스누핑 편향이 나타남
안정적인 훈련/테스트 분할을 위한 일반적인 해결책은 샘플의 식별자를 사용하여 테스트 세트로 보낼지 말지 정하는 것입니다.(샘플이 고유하고 변경 불가능한 식별자를 가지고 있다고 가정)
2.4 데이터 이해를 위한 탐색과 시각화¶
- 훈련 세트가 매우 크면 조작을 간단하고 빠르게 하기 위해 탐색을 위한 세트를 별도로 샘플링할 수 있습니다.
# 밀집지역이 잘 부각되게 한 산점도
housing.plot(kind="scatter", x="longitude", y="latitude", alpha=0.1)
<Axes: xlabel='longitude', ylabel='latitude'>
# 캘리포니아 주택 가격 : 빨간색을 높은 가격, 파란색은 낮은 가격, 큰 원은 밀집된 지역
housing.plot(kind="scatter", x="longitude", y="latitude", alpha=0.4,
s=housing["population"]/100, label="population", figsize=(10,7),
c="median_house_value", cmap=plt.get_cmap("jet"), colorbar=True,
sharex=False)
plt.legend()
<matplotlib.legend.Legend at 0x28286e76110>
# Download the California image
images_path = os.path.join(PROJECT_ROOT_DIR, "images", "end_to_end_project")
os.makedirs(images_path, exist_ok=True)
DOWNLOAD_ROOT = "https://raw.githubusercontent.com/ageron/handson-ml2/master/"
filename = "california.png"
print("Downloading", filename)
url = DOWNLOAD_ROOT + "images/end_to_end_project/" + filename
urllib.request.urlretrieve(url, os.path.join(images_path, filename))
Downloading california.png
('.\\images\\end_to_end_project\\california.png', <http.client.HTTPMessage at 0x28287e7dc50>)
import matplotlib.image as mpimg
california_img=mpimg.imread(os.path.join(images_path, filename))
ax = housing.plot(kind="scatter", x="longitude", y="latitude", figsize=(10,7),
s=housing['population']/100, label="Population",
c="median_house_value", cmap=plt.get_cmap("jet"),
colorbar=False, alpha=0.4)
plt.imshow(california_img, extent=[-124.55, -113.80, 32.45, 42.05], alpha=0.5,
cmap=plt.get_cmap("jet"))
plt.ylabel("Latitude", fontsize=14)
plt.xlabel("Longitude", fontsize=14)
prices = housing["median_house_value"]
tick_values = np.linspace(prices.min(), prices.max(), 11)
cbar = plt.colorbar(ticks=tick_values/prices.max())
cbar.ax.set_yticklabels(["$%dk"%(round(v/1000)) for v in tick_values], fontsize=14)
cbar.set_label('Median House Value', fontsize=16)
plt.legend(fontsize=16)
save_fig("california_housing_prices_plot")
plt.show()
그림 저장: california_housing_prices_plot
2.4.2 상관관계 조사¶
- 표준 상관계수(피어슨의 r이라고도 부름)를 corr() 메서드를 이용해 계산
# 상관관계
corr_matrix = housing.corr()
corr_matrix
C:\Users\20229069\AppData\Local\Temp\ipykernel_18052\369284436.py:2: FutureWarning: The default value of numeric_only in DataFrame.corr is deprecated. In a future version, it will default to False. Select only valid columns or specify the value of numeric_only to silence this warning. corr_matrix = housing.corr()
longitude | latitude | housing_median_age | total_rooms | total_bedrooms | population | households | median_income | median_house_value | |
---|---|---|---|---|---|---|---|---|---|
longitude | 1.000000 | -0.924664 | -0.108197 | 0.044568 | 0.069608 | 0.099773 | 0.055310 | -0.015176 | -0.045967 |
latitude | -0.924664 | 1.000000 | 0.011173 | -0.036100 | -0.066983 | -0.108785 | -0.071035 | -0.079809 | -0.144160 |
housing_median_age | -0.108197 | 0.011173 | 1.000000 | -0.361262 | -0.320451 | -0.296244 | -0.302916 | -0.119034 | 0.105623 |
total_rooms | 0.044568 | -0.036100 | -0.361262 | 1.000000 | 0.930380 | 0.857126 | 0.918484 | 0.198050 | 0.134153 |
total_bedrooms | 0.069608 | -0.066983 | -0.320451 | 0.930380 | 1.000000 | 0.877747 | 0.979728 | -0.007723 | 0.049686 |
population | 0.099773 | -0.108785 | -0.296244 | 0.857126 | 0.877747 | 1.000000 | 0.907222 | 0.004834 | -0.024650 |
households | 0.055310 | -0.071035 | -0.302916 | 0.918484 | 0.979728 | 0.907222 | 1.000000 | 0.013033 | 0.065843 |
median_income | -0.015176 | -0.079809 | -0.119034 | 0.198050 | -0.007723 | 0.004834 | 0.013033 | 1.000000 | 0.688075 |
median_house_value | -0.045967 | -0.144160 | 0.105623 | 0.134153 | 0.049686 | -0.024650 | 0.065843 | 0.688075 | 1.000000 |
# 집값과 변수들의 상관관계 내림 정렬
corr_matrix["median_house_value"].sort_values(ascending=False)
median_house_value 1.000000 median_income 0.688075 total_rooms 0.134153 housing_median_age 0.105623 households 0.065843 total_bedrooms 0.049686 population -0.024650 longitude -0.045967 latitude -0.144160 Name: median_house_value, dtype: float64
from pandas.plotting import scatter_matrix
attributes = ["median_house_value", "median_income", "total_rooms",
"housing_median_age"]
scatter_matrix(housing[attributes], figsize=(12, 8))
array([[<Axes: xlabel='median_house_value', ylabel='median_house_value'>, <Axes: xlabel='median_income', ylabel='median_house_value'>, <Axes: xlabel='total_rooms', ylabel='median_house_value'>, <Axes: xlabel='housing_median_age', ylabel='median_house_value'>], [<Axes: xlabel='median_house_value', ylabel='median_income'>, <Axes: xlabel='median_income', ylabel='median_income'>, <Axes: xlabel='total_rooms', ylabel='median_income'>, <Axes: xlabel='housing_median_age', ylabel='median_income'>], [<Axes: xlabel='median_house_value', ylabel='total_rooms'>, <Axes: xlabel='median_income', ylabel='total_rooms'>, <Axes: xlabel='total_rooms', ylabel='total_rooms'>, <Axes: xlabel='housing_median_age', ylabel='total_rooms'>], [<Axes: xlabel='median_house_value', ylabel='housing_median_age'>, <Axes: xlabel='median_income', ylabel='housing_median_age'>, <Axes: xlabel='total_rooms', ylabel='housing_median_age'>, <Axes: xlabel='housing_median_age', ylabel='housing_median_age'>]], dtype=object)
2.5 머신러닝 알고리즘을 위한 데이터 준비¶
○ 데이터 준비를 자동화해야 하는 이유
1. 어떤 데이터셋에 대해서도 데이터 변환을 쉽게 반복할 수 있다.
2. 향후 프로젝트에 사용할 수 있는 변환 라이브러리를 점진적으로 구축
3. 실제 시스템에서 알고리즘에 새 데이터를 주입하기 전에 변환시키는 데 이 함수를 사용할 수 있다.
4. 여러 가지 데이터 변환을 쉽게 시도해볼 수 있고 어떤 조합이 가장 좋은지 확인하는 데 편리하다.
2.5.1 데이터 정제¶
- 특정 변수의 값이 없는 경우 :
- 해당 변수 값을 제거
- 전체 특성을 삭제
- 어떤 값으로 채움(0, 평균, 중간값 등)
# 특정값 대체의 쉬운 방법
from sklearn.impute import SimpleImputer
imputer = SimpleImputer(strategy="median")
# 중간값이 수치형 특성에서만 계산될 수 있기 때문에 텍스트 특성을 삭제합니다
housing_num = housing.drop("ocean_proximity", axis=1)
# 다른 방법: housing_num = housing.select_dtypes(include=[np.number])
imputer.fit(housing_num)
imputer.statistics_
array([-1.1849e+02, 3.4260e+01, 2.9000e+01, 2.1270e+03, 4.3500e+02, 1.1660e+03, 4.0900e+02, 3.5348e+00, 1.7970e+05])
# 중앙값으로 채워진 데이터
X = imputer.transform(housing_num)
housing_tr = pd.DataFrame(X, columns=housing_num.columns,
index=housing_num.index)
housing_tr
longitude | latitude | housing_median_age | total_rooms | total_bedrooms | population | households | median_income | median_house_value | |
---|---|---|---|---|---|---|---|---|---|
0 | -122.23 | 37.88 | 41.0 | 880.0 | 129.0 | 322.0 | 126.0 | 8.3252 | 452600.0 |
1 | -122.22 | 37.86 | 21.0 | 7099.0 | 1106.0 | 2401.0 | 1138.0 | 8.3014 | 358500.0 |
2 | -122.24 | 37.85 | 52.0 | 1467.0 | 190.0 | 496.0 | 177.0 | 7.2574 | 352100.0 |
3 | -122.25 | 37.85 | 52.0 | 1274.0 | 235.0 | 558.0 | 219.0 | 5.6431 | 341300.0 |
4 | -122.25 | 37.85 | 52.0 | 1627.0 | 280.0 | 565.0 | 259.0 | 3.8462 | 342200.0 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
20635 | -121.09 | 39.48 | 25.0 | 1665.0 | 374.0 | 845.0 | 330.0 | 1.5603 | 78100.0 |
20636 | -121.21 | 39.49 | 18.0 | 697.0 | 150.0 | 356.0 | 114.0 | 2.5568 | 77100.0 |
20637 | -121.22 | 39.43 | 17.0 | 2254.0 | 485.0 | 1007.0 | 433.0 | 1.7000 | 92300.0 |
20638 | -121.32 | 39.43 | 18.0 | 1860.0 | 409.0 | 741.0 | 349.0 | 1.8672 | 84700.0 |
20639 | -121.24 | 39.37 | 16.0 | 2785.0 | 616.0 | 1387.0 | 530.0 | 2.3886 | 89400.0 |
20640 rows × 9 columns
2.5.2 텍스트와 범주형 특성 다루기¶
- OrdinalEncoder 방식보다 이진 특성을 활용한 원-핫 인코딩 방식을 더 선호
# 범주형 변수 수치형으로 변환 방법 1
housing_cat = housing[["ocean_proximity"]]
from sklearn.preprocessing import OrdinalEncoder
ordinal_encoder = OrdinalEncoder()
housing_cat_encoded = ordinal_encoder.fit_transform(housing_cat)
housing_cat_encoded[:10]
array([[3.], [3.], [3.], [3.], [3.], [3.], [3.], [3.], [3.], [3.]])
ordinal_encoder.categories_
[array(['<1H OCEAN', 'INLAND', 'ISLAND', 'NEAR BAY', 'NEAR OCEAN'], dtype=object)]
# 범주형 변수 수치형으로 변환 방법 2(원핫인코딩)
from sklearn.preprocessing import OneHotEncoder
cat_encoder = OneHotEncoder()
housing_cat_1hot = cat_encoder.fit_transform(housing_cat)
cat_encoder = OneHotEncoder(sparse=False)
housing_cat_1hot = cat_encoder.fit_transform(housing_cat)
housing_cat_1hot
C:\Users\20229069\AppData\Local\anaconda3\Lib\site-packages\sklearn\preprocessing\_encoders.py:972: FutureWarning: `sparse` was renamed to `sparse_output` in version 1.2 and will be removed in 1.4. `sparse_output` is ignored unless you leave `sparse` to its default value. warnings.warn(
array([[0., 0., 0., 1., 0.], [0., 0., 0., 1., 0.], [0., 0., 0., 1., 0.], ..., [0., 1., 0., 0., 0.], [0., 1., 0., 0., 0.], [0., 1., 0., 0., 0.]])
2.5.4 특성 스케일링¶
머신러닝 알고리즘은 입력 숫자 특성들의 스케일이 많이 다르면 잘 작동하지 않습니다.
보통 min-max 스케일링과 표준화가 널리 사용, 표준화는 이상치에 영향을 덜 받음
2.6 모델 선택과 훈련¶
2.7 모델 세부 튜닝¶
2.7.1 그리드 탐색¶
- 탐색하고자 하는 하이퍼파라미터와 시도해볼 값을 지정하여 최적의 하이퍼파라미터 조합 탐색 방법
2.7.2 랜덤 탐색¶
하이퍼파라미터 탐색 공간이 커지면 RandomizedSearchCV를 사용하는 것이 좋다
랜덤 탐색을 1,000회 반복하도록 실행하면 하이퍼파라미터마다 각기 다른 1,000개의 값을 탐색합니다.(그리드 탐색에서는 하이퍼파라미터마다 몇 개의 값만 탐색)
단순히 반복 횟수를 조절하는 것만으로 하이퍼파라미터 탐색에 투입할 컴퓨팅 자원을 제어할 수 있습니다.
2.7.3 앙상블 방법¶
2.7.4 최상의 모델과 오차 분석¶
최상의 모델을 분석하면 문제에 대한 좋은 통찰을 얻을 수 있다.
예를 들어 RandomForestRegressor 모델의 특성 상대적 중요도
2.7.5 테스트 세트로 시스템 평가하기¶
- scipy.stats.t.interval()를 사용해 일반화 오차의 95% 신뢰 구간을 계산할 수 있다.
2.8 론칭, 모니터링, 시스템 유지 보수¶
어떤 방식이든 모니터링 시스템을 준비해야 합니다.
만든 모든 모델을 백업해야 합니다. 새로운 모델이 어떤 이유로 올바르지 않게 작동하는 경우 이전 모델로 빠르게 롤백하기 위한 절차와 도구를 준비해야 합니다.
2.9 직접 해보세요!¶
from IPython.core.display import display, HTML
display(HTML("<style>.container {width:80% !important;}</style>"))
C:\Users\20229069\AppData\Local\Temp\ipykernel_18052\529043151.py:1: DeprecationWarning: Importing display from IPython.core.display is deprecated since IPython 7.14, please import from IPython display from IPython.core.display import display, HTML
'Book report > 핸즈온 머신러닝' 카테고리의 다른 글
[핸즈온 머신러닝] Chapter 6. 결정 트리 (0) | 2024.04.01 |
---|---|
[핸즈온 머신러닝] Chapter 5. 서포트 벡터 머신 (0) | 2023.09.12 |
[핸즈온 머신러닝] Chapter 4. 모델 훈련 (0) | 2023.09.06 |
[핸즈온 머신러닝] Chapter 3. 분류 (0) | 2023.08.28 |
[핸즈온 머신러닝] Chapter 1. 한눈에 보는 머신러닝 (0) | 2023.07.26 |