본문 바로가기
로봇/인공지능, AI

[핸즈온 머신러닝 3판] 2.머신러닝 프로젝트 처음부터 끝까지 2.1~2.4

by 33곰탱 2024. 9. 14.

1부 2장

오늘 정리할 부분은 2장 머신러닝 프로젝트 처음부터 끝까지 부분이다.

2장 머신러닝 프로젝트 처음부터 끝까지!!

  • 2.1 실제 데이터로 작업하기
  • 2.2 큰 그림 보기
  • 2.3 데이터 가져오기
  • 2.4 데이터 이해를 위한 탐색과 시각화
  • 2.5 머신러닝 알고리즘을 위한 데이터 준비
  • 2.6 모델 선택과 훈련
  • 2.7 모델 미세 튜닝
  • 2.8 론칭, 모니터링, 시스템 유지 보수
  • 2.9 직접 해보세요!

많다.. 하핫

2.1 실제 데이터로 작업하기

2.1 에서는 인공적으로 만들어진 데이터셋보다는 실제 데이터셋으로 실험해보는 것을 추천한다.

 

유명한 공개 데이터 저장소

메타 포털 (공개 데이터 저장소가 나열되어 있는 페이지)

인기 있는 공개 데이터 저장소가 나열되어 있는 다른 페이지

이 장에서는 StatLib 저장소에 있는 캘리포니아 주택 가격 California Housing Prices 데이터셋을 사용한다..! 교육 목적으로 사용하기 위해 범주형 특성을 추가하고 몇 가지 특성을 제외했다고 한다.

2.2 큰 그림 보기

2.2에서는 먼저 캘리포니아 인구 조사 데이터를 사용해 캘리포니아의 주택 가격 모델을 만드는 것이 처음 할 일로 주어진다.

 

만약 내가 머신러닝 주택 회사에 다닌다고 가정해보자. 인구 조사 데이터에는 블록 그룹마다 인구, 중간 소득, 중간 주택 가격등을 담고 있는데 이 데이터로 모델을 학습시켜서 다른 측정 데이터가 주어졌을 때 구역의 중간 주택 가격을 예측해야 한다.

2.2.1 문제정의

무언가를 할 때, 목적을 정확히 아는 것은 모델을 구성하는 데 매우 중요한 요소이다. 목적을 알면 문제를 어떻게 구성할지, 어떤 알고리즘을 선택할지, 모델 평가에 어떤 성능 지표를 사용할지 결정하는 데 큰 도움이 되기 때문이다.

 

위에서 든 예시의 경우 (구역의 중간 주택 가격 예측)이 여러 신호와 함께 머신러닝 시스템에 입력되어 머신러닝 주택 회사가 해당 지역에 투자할 가치가 있는지를 결정하는 데 중요한 역할을 한다고 한다.

 

아래 사진은 부동산 투자를 위해서 내가 하는 단계가 구역 가격 결정 단계 인것을 보여준다

 

 

여기서 파이프라인이라는 개념이 나온다.

파이프라인 : 데이터를 처리하는 여러 단계가 순서대로 연결된 흐름

파이프라인이란 각 단계(컴포넌트)가 데이터를 받고, 처리한 다음 그 결과를 다음 단계로 넘겨주는 방식이다.!

여기서 컴포넌트는 각 단계라고 생각하면 편하다.

 

예를 들어, 파이프라인의 첫 번째 단계에서는 데이터를 모으고, 두 번째 단계에서는 그 데이터를 깨끗하게 정리하고, 세 번째 단계에서는 정리된 데이터를 분석하는 방식으로, 이 모든 단계들이 연결되어 있기 때문에, 하나라도 제대로 작동하지 않으면 전체 시스템이 문제가 생길 수 있다.

 

하지만 파이프라인의 좋은 점은 각 단계가 독립적으로 동작할 수 있다는 것이다. 만약 어느 한 단계에서 문제가 생기면, 그 부분만 고치면 되기 때문에 시스템 유지 관리가 쉬워진다고 한다.

 

현재 머신러닝 주택 회사의 솔루션이 구역의 중간 주택 가격을 예측하기 위해 전문가의 판단과 복잡한 규칙을 사용하지만, 비용이 많이 들고 시간이 오래 걸리는 방식이라고 한다면, 중간 주택 가격을 예측하는 머신러닝 모델이 이를 대체할 수 있다. (우리가 앞으로 해야할 일이다..^^)

 

이 데이터에는 레이블된 훈련 샘플이 있으므로, 지도 학습을 수행한다. 여기서 다중 회귀 분석단변량 회귀 분석을 통해 다양한 예측 모델을 적용할 수 있으며, 데이터가 작으므로 배치 학습이 적합하다고 한다.

2.2.2 성능 측정 지표 선택

성능 측정은 머신러닝 모델을 평가하고 개선하는 데 필수적이다. 뭐 당연하지만 사람이 얼마나 뛰어난지 판단하는 지표중 하나가 성적인데 머신러닝 모델도 비슷하게 성적으로 측정한다. 주로 사용되는 성능 지표는 평균 제곱근 오차 (RMSE)평균 절대 오차 (MAE) 이다.

 

  1. 평균 제곱근 오차 (RMSE)
    1. RMSE는 모델 예측과 실제 값의 차이를 제곱하여 평균을 낸 다음, 그 제곱근을 취합니다. 이는 오차의 크기를 과대평가하는 경향이 있어, 큰 오차에 민감하여, 큰 오차가 발생하면 더 큰 영향력을 줍니다.
    2. 수식:  $RMSE(X, h) = \sqrt{\frac{1}{m} \sum_{i=1}^{m} (h(x^{(i)}) - y^{(i)})^2}$
  1. 평균 절대 오차 (MAE)
    1. MAE는 모든 개별 오차의 절대값을 평균내어 계산합니다. 예측 값과 실제 값의 차이를 절대값으로 변환한 후 그 평균을 구하는 방식입니다. RMSE와 달리 큰 오차에도 동일한 가중치를 주며, 전반적인 오차의 크기를 실제 값에 가깝게 나타냅니다.
    2. 수식: $MAE(X, h) = \frac{1}{m} \sum_{i=1}^{m} |h(x^{(i)}) - y^{(i)}|$

 

$ y^{(i)} = 156,400 $

$ h(x^{(i)}) = 158,400 $

$ \text{오차} = h(x^{(i)}) - y^{(i)} = 158,400 - 156,400 = 2,000 $

 

일반적으로 RMSE는 큰 오차에 민감하게 반응하는 반면, MAE는 모든 오차를 균등하게 평가하여, 상황에 따라 더 적합한 성능 지표를 선택해야 한다...!

2.3 데이터 가져오기

이제 실제 데이터를 가져오는법에 대해 알아보자!! (책에 있는 예제 코드는 아래 깃허브에서 참고 하세용)

https://github.com/rickiepark/handson-ml3

 

GitHub - rickiepark/handson-ml3: <핸즈온 머신러닝 3판>의 주피터 노트북 저장소

<핸즈온 머신러닝 3판>의 주피터 노트북 저장소. Contribute to rickiepark/handson-ml3 development by creating an account on GitHub.

github.com

 

2.3.1 구글 코랩을 사용하여 예제 코드 실행하기

https://colab.research.google.com/github/rickiepark/handson-ml3/blob/main/

 

Google Colab Notebook

Run, share, and edit Python notebooks

colab.research.google.com

브라우저를 열고 위의 주소로 들어가보면 책 제작자님이 만드신 노트북들이 여러가지 있다.

예제 코드 실행해보기!!

2.3.2 코드와 데이터 저장하기

브라우저를 닫으면 변경 내용이 사라지기 때문에 수정한 내용을 유지하려면 구글 드라이브에 저장하기!!!

2.3.3 대화식 환경의 편리함과 위험

편리함:

  1. 실시간 피드백: 코드를 작성하고 바로 실행하여 결과를 즉시 확인할 수 있습니다. 이를 통해 빠른 실험과 반복적인 테스트가 가능해집니다.
  2. 코드와 설명의 통합: 코드 셀과 마크다운 셀을 함께 사용하여, 코드에 대한 설명이나 수식을 추가할 수 있어 이해하기 쉬운 문서를 만들 수 있습니다.
  3. 데이터 분석에 최적: 데이터를 불러와서 시각화하고, 분석하는 과정을 쉽게 진행할 수 있으며, 데이터 처리의 중간 결과를 단계별로 확인할 수 있습니다.

위험:

  1. 재현성 문제: 대화식 환경에서 순서대로 셀을 실행하지 않거나, 일부 셀만 실행하는 경우, 전체 코드의 실행 순서가 꼬일 수 있어 결과가 일관되지 않을 수 있습니다.
  2. 성능 문제: 대화식 환경은 모든 셀을 메모리에 유지하고 있어, 셀이 많아지면 메모리 사용량이 급격히 증가하여 성능이 저하될 수 있습니다.
  3. 코드 관리의 어려움: 짧은 실험에는 적합하지만, 프로젝트가 커지면 코드 관리가 복잡해질 수 있습니다. 체계적인 버전 관리가 어렵고, 모듈화가 잘되지 않으면 유지보수에 어려움이 생길 수 있습니다.

2.3.4 책의 코드와 노트북의 코드

책에 있는 코드와 노트북에 있는 코드는 조금 다를 수 있다.. 왜냐면 시간이 지나면 새로운 것이 생기는 건 당연하니깐...!!!

따라서 코드가 다르면 노트북에 있는 코드를 참고하자~

2.3.5 데이터 다운로드

캐글에서 많이 해보았죠? 

 

머신러닝 프로젝트에서는 일반적으로 CSV 파일이나 데이터베이스에서 데이터를 내려받아 사용한다. 이번 예제에서는 캘리포니아 주택 가격 데이터를 housing.csv 파일로 추출해 로드를 한다..!

 

tqz는 뭔가해서 찾아봤는데

  • TGZ 파일은 여러 개의 파일(예: housing.csv)을 하나로 압축하여 저장할 수 있고, 이를 해제하여 CSV 파일을 사용할 수 있습니다.
  • 위 예시에서 housing.tgz는 데이터를 압축한 파일이며, 이 파일을 풀면 그 안에 housing.csv라는 데이터 파일이 있습니다. 이 CSV 파일은 판다스(pandas)로 불러와서 데이터 분석에 사용할 수 있습니다.

따라서, TGZ는 데이터를 압축해 놓은 파일 형식, CSV는 데이터를 저장하는 파일 형식이라고 보시면 됩니다.

 

(그렇군아 GPT 고마워)

 

아래는 그 코드이다.

from pathlib import Path
import pandas as pd
import tarfile
import urllib.request

def load_housing_data():
    # 압축 파일의 경로 설정
    tarball_path = Path("datasets/housing.tgz")
    
    # 파일이 존재하지 않으면 다운로드
    if not tarball_path.is_file():
        Path("datasets").mkdir(parents=True, exist_ok=True)
        url = "https://github.com/ageron/data/raw/main/housing.tgz"
        urllib.request.urlretrieve(url, tarball_path)
    
    # 압축 해제
    with tarfile.open(tarball_path) as housing_tarball:
        housing_tarball.extractall(path="datasets")
    
    # CSV 파일 로드
    return pd.read_csv(Path("datasets/housing/housing.csv"))

# 데이터 불러오기
housing = load_housing_data()

# 데이터 출력 (첫 5개 행)
print(housing.head())

 

 

데이터 다운로드 과정:

  1. 데이터를 내려받기 위해 datasets/housing.tgz 파일을 다운로드
  2. 파일이 존재하지 않으면 디렉터리를 생성하고 데이터를 내려받아 압축을 품
  3. 최종적으로 datasets/housing/housing.csv 파일을 판다스(Pandas)를 사용하여 데이터프레임으로 로드

파이썬 코드 설명:

  • pathlib, pandas, tarfile, urllib.request 모듈을 사용
  • load_housing_data() 함수는 housing.tgz 파일을 다운로드하고 압축을 풀어 CSV 파일을 로드하는 기능을 수행
  • 다운로드한 데이터를 읽어서 판다스 데이터프레임으로 변환

2.3.6 데이터 구조 훑어보기

# 데이터 출력 (첫 5개 행)
print(housing.head())

데이터프레임의 처음 다섯 개 행을 출력하여, 데이터가 어떻게 생겼는지 구조를 확인할 수 있다!

 

각 행은 하나의 구역을 나타내며, 데이터에는 경도(longitude), 위도(latitude), 중간 주택 연령(housing_median_age), 중간 소득(median_income), 해안 근접도(ocean_proximity) 등의 특성이 있는 것을 알 수 있다.

# 데이터 정보 확인
housing.info()

데이터프레임의 전체적인 정보를 볼 수 있다. 각 열의 데이터 타입, 누락된 값의 수, 전체 행 수 등을 확인할 수 있어, 데이터의 상태를 파악하는 데 매우 유용하게 쓸 수 있다.

# 'ocean_proximity' 열에 있는 값들의 개수를 세어 출력
housing["ocean_proximity"].value_counts()

범주형 데이터의 분포를 확인할 수 있다. 범주형 값이 몇 번 나타났는지 계산해준다.

# 전체 데이터의 기초 통계량을 계산 (평균, 표준편차, 최소값, 사분위수, 최대값 등)
housing.describe()

수치형 열들의 기초 통계량을 보여준다.

 

예를 들어, 평균, 표준편차, 최소값, 25%, 50%, 75% 사분위수, 최대값 등의 정보가 제공되며, 데이터의 분포를 이해하는 데 도움을 줍니다.

import matplotlib.pyplot as plt

# 추가 코드 – 다음 다섯 라인은 기본 폰트 크기를 지정합니다
plt.rc('font', size=14)
plt.rc('axes', labelsize=14, titlesize=14)
plt.rc('legend', fontsize=14)
plt.rc('xtick', labelsize=10)
plt.rc('ytick', labelsize=10)

housing.hist(bins=50, figsize=(12, 8))
save_fig("attribute_histogram_plots")  # 추가 코드
plt.show()

히스토그램(histogram): 데이터를 시각화하는 좋은 방법으로, 수치형 특성의 분포를 보여줍니다. 각 히스토그램은 샘플 수에 따른 값의 분포를 시각적으로 나타냅니다.

 

예시에서는 중간 소득, 중간 주택 가격, 주택 연령 등 여러 특성의 분포를 확인할 수 있습니다.

 

2.3.7 테스트 세트 만들기

우리의 뇌는 과적합되기 쉬운 패턴 감지 시스템이라고 한다. 알다시피 우리가 A과목을 족보로 공부했는데 족보 문제가 아닌 새로운 문제가 나오면 풀기 어려워하는 경우가 많은 것처럼, 기계 학습 모델도 동일한 상황에 직면할 수 있다고 한다!

 

만약 테스트 세트를 따로 떼지 않고 학습 데이터에 포함된 패턴만을 기준으로 모델을 최적화하면 모델이 학습 데이터에서만 성능이 우수하고, 새로운 데이터에서는 성능이 떨어지는 '과적합(overfitting)' 문제를 초래하게 된다. 이 문제를 해결하기 위해 학습에 사용되지 않은 별도의 테스트 세트를 설정하는 것이 필요하다.

 

그렇지 않으면 '데이터 스누핑(Data Snooping)'이라는 문제가 발생할 수 있다. 데이터 스누핑은 모델을 개발하는 과정에서 테스트 데이터를 너무 자주 참조하거나 모델 성능을 향상시키기 위해 이를 학습 데이터처럼 사용하게 되는 현상이다. 예를 들어, 모델을 여러 번 수정하면서 매번 테스트 세트를 사용해 성능을 확인하는 경우, 결국 모델은 테스트 세트에 특화된 방식으로 동작하게 되어 실제 새로운 데이터에 대한 예측력이 떨어질 가능성이 크다. 그렇다면 이제 테스트 세트를 만들어 보자!!.

 

train_test_split

from sklearn.model_selection import train_test_split

train_set, test_set = train_test_split(housing, test_size=0.2, random_state=42)

책에서는 간단한 방법으로 train_test_split 함수를 사용해 데이터를 무작위로 분리하는 방법을 설명한다. 하지만 이 방법에는  문제점이 있는데, 랜덤으로 나눈다면 특정 중요한 데이터 특성이 훈련 세트나 테스트 세트에 불균형하게 분포될 수 있기 때문이다.

 

예를 들어, 미국 인구의 51.1%가 여성이고 48.9%가 남성이라면, 잘 구성된 설문 조사는 샘플에서도 이 비율을 유지해야 한다. 하지만 위의 방법을 사용하면 불균형하게 랜덤으로 분포 될 수 있다는 점이다!!!

 

계층적 샘플링 (Stratified Sampling)

이 문제를 해결하기 위한 대안으로, 책에서는 계층적 샘플링(Stratified Sampling)을 소개한다. 계층적 샘플링은 특정한 데이터 속성(예: 소득)을 기준으로 데이터를 나누어, 그 속성의 분포가 훈련 세트와 테스트 세트에서 동일하게 유지되도록 하는 방법이다.

 

예를 들어, 데이터셋에서 '소득'이라는 속성을 고려할 때, 단순히 데이터를 무작위로 나누는 것이 아니라 소득 카테고리에 따라 비율을 유지하여 나누는 것이 계층적 샘플링이라고 생각하면 된다. 이를 통해 훈련 세트와 테스트 세트에서 소득 분포가 동일하게 유지되며, 모델은 이 속성을 더 정확하게 반영한 예측을 할 수 있게 된다.

 

strat_train_set, strat_test_set = train_test_split(
    housing, test_size=0.2, stratify=housing["income_cat"], random_state=42)

코드는 위와 같다. train_test_split 함수와 stratify 매개변수를 사용하여 간편하게 만들 수 있다.

2.4 데이터 이해를 위한 탐색과 시각화

테스트를 떼어놓았다고 가정하고 훈련 세트에 대해서 데이터를 탐색 해보자!!

그렇다고 훈련 세트 자체를 바꾸면 돌아가기 힘드니까 훈련 세트 복사본을 만들어주고 그걸로 진행하도록 하겠다

 

housing = strat_train_set.copy()

2.4.1 지리적 데이터 시각화 하기

이 데이터셋에는 위도와 경도가 포함되어 있어서 모든 구역을 산점도로 만들어서 잘 볼수있게 시각화 해보자~

housing.plot(kind="scatter", x="longitude", y="latitude", grid=True)
save_fig("bad_visualization_plot")  # extra code
plt.show()

근데 이렇게만 하면 어디가 집중되어 있는지 대충 알 수 있고 자세히는 알 수 없다 따라서.. 아래와 같이 alpha를 추가해 주자

housing.plot(kind="scatter", x="longitude", y="latitude", grid=True, alpha=0.2)
save_fig("better_visualization_plot")  # extra code
plt.show()

그러면 위와 같이 밀집된 영역을 잘 볼 수 있다

주택 가격을 캘리포니아 배경에 대해서 나타내 보자

# 추가 코드 – 이 셀은 이 장의 첫 번째 그림을 생성합니다

#  캘리포니아 이미지를 다운로드합니다
filename = "california.png"
if not (IMAGES_PATH / filename).is_file():
    homl3_root = "https://github.com/ageron/handson-ml3/raw/main/"
    url = homl3_root + "images/end_to_end_project/" + filename
    print("Downloading", filename)
    urllib.request.urlretrieve(url, IMAGES_PATH / filename)

housing_renamed = housing.rename(columns={
    "latitude": "Latitude", "longitude": "Longitude",
    "population": "Population",
    "median_house_value": "Median house value (ᴜsᴅ)"})
housing_renamed.plot(
             kind="scatter", x="Longitude", y="Latitude",
             s=housing_renamed["Population"] / 100, label="Population",
             c="Median house value (ᴜsᴅ)", cmap="jet", colorbar=True,
             legend=True, figsize=(10, 7))

california_img = plt.imread(IMAGES_PATH / filename)
axis = -124.55, -113.95, 32.45, 42.05
plt.axis(axis)
plt.imshow(california_img, extent=axis)

save_fig("california_housing_prices_plot")
plt.show()

보면 알 수 있듯이 해안 주변 구역의 집값이 높다는 사실을 알 수 있다. 하지만 이러한 데이터 시각화만으로는 단순히 결정짓고, 간단한 규칙을 적용하기는 어렵기 때문에 좀 더 조사를 해보아야한다..

2.4.2 상관관계 조사하기

각 데이터간의 상관관계를 조사해보자! 어떻게 조사 할까??

1. 상관관계 행렬 구하기

데이터셋이 크지 않을 때는 모든 특성간의 표준 상관계수corr() 메서드를 사용하여 손쉽게 계산할 수 있다.

# 판다스 1.5버전부터 수치형 데이터만 포함하는지 여부를 결정하는 `numeric_only` 매개변수가 추가되었습니다.
# 이 매개변수의 기본값은 `True`입니다.
# 판다스 2.0버전에서 기본값이 `False`로 바뀌므로 명시적으로 `numeric_only=True`로 지정합니다.
corr_matrix = housing.corr(numeric_only=True)

corr_matrix["median_house_value"].sort_values(ascending=False)

 

위의 코드를 통해서 중간 주택 가격과 다른 특성 사이의 상관관계 크기가 얼마나 되는지 확인할 수 있다!

 

상관계수(correlation coefficient)는 두 변수 간의 선형적 관계를 수치화한 값으로, 상관계수가 1에 가까울수록 두 변수 간의 관계가 강한 양의 상관관계를 나타내며, -1에 가까울수록 강한 음의 상관관계를 의미한다. 따라서 상관계수가 0에 가까울수록 두 변수 간에 선형적 관계가 없다는 뜻을 나타낸다.

 

2. 산점도 행렬 (Scatter Matrix)

위의 상관관계는 두 변수 간의 선형적 관계를 나타내지만, 데이터의 전체적인 분포나 비선형적 관계를 파악하는 데에는 한계가 있다. 이를 보완하기 위해 책에서는 판다스의 scatter_matrix 함수를 사용하여 각 특성 간의 산점도 행렬을 그려본다. 산점도 행렬은 변수들 간의 관계를 시각적으로 파악하는 데 매우 유용하며, 특히 비선형적 관계나 특이점을 쉽게 찾아낼 수 있다!

 

밑의 예시를 보자.

from pandas.plotting import scatter_matrix

attributes = ["median_house_value", "median_income", "total_rooms",
              "housing_median_age"]
scatter_matrix(housing[attributes], figsize=(12, 8))
save_fig("scatter_matrix_plot")  # 추가 코드
plt.show()

 

중간 주택 가격(median_house_value)를 예측하는 데 중간 소득과 가장 선형성이 있어 보인다!! (이모티콘 첨부)

자세히 봐보도록 하자

housing.plot(kind="scatter", x="median_income", y="median_house_value",
             alpha=0.1, grid=True)
save_fig("income_vs_house_value_scatterplot")  # e추가 코드
plt.show()

 

자세히 보니 수평선이 몇개 보이는 것 같다. 이러한 경우 알고리즘이 데이터에서 이상한 형태를 학습하지 않도록 해당 구역을 제거하게 할 수 있다!

 

상관관계에서는 이러한 것들을 잡아내지 못하기 때문에 산점도 분석등 같이 사용하면 좋다.. (물론 현재는 수치형 데이터만 있어서 편리하지만)

2.4.3 특성 조합으로 실험하기

데이터를 준비하기 전 또 할 수 있는 일은 특성을 여러가지로 조합해보는 것이다.

 

예를 들어 특정 구역의 방 개수는 가구 수를 모른다면 그다지 유용하
지 않습니다. 진짜 필요한 것은 가구당 방 개수입니다. 비슷하게 전체 침실 개수도 그 자체로는
유용하지 않습니다. 즉, 방 개수와 비교하는 게 낫습니다(이 부분 무슨 소린지 모르겠음). 가구당 인원도 흥미로운 특성 조합일
것 같습니다. 이런 특성들을 만들어봅시다.

housing["rooms_per_house"] = housing["total_rooms"] / housing["households"]
housing["bedrooms_ratio"] = housing["total_bedrooms"] / housing["total_rooms"]
housing["people_per_house"] = housing["population"] / housing["households"]

 

만들고 나서 다시 한번 상관 관계를 보자

corr_matrix = housing.corr(numeric_only=True)
corr_matrix["median_house_value"].sort_values(ascending=False)

 

이렇게 새로 만든 데이터가 더 높은 상관관계를 가진 것을 확인할 수 있으니 새로운 데이터를 만들어보길 추천!!

 

(LG AIMERS 할 때도 새로운 열 만드니까 점수가 더 올라갔던 경험이 있다!)