커스텀 scikit-learn 파이프라인을 사용한 예측

Colab 로고 이 가이드를 Colab에서 메모장으로 실행하기 GitHub 로고 GitHub에서 메모장 보기

이 가이드에서는 AI Platform Prediction을 사용하여 커스텀 변환기를 사용하는 scikit-learn 파이프라인을 배포하는 방법을 보여줍니다.

scikit-learn 파이프라인을 사용하여 여러 에스티메이터를 작성할 수 있습니다. 예를 들어 변환기를 사용하여 데이터를 사전 처리하고 변환된 데이터를 분류자에 전달할 수 있습니다. scikit-learn은 sklearn 패키지에서 많은 변환기를 제공합니다.

scikit-learn의 FunctionTransformer 또는 TransformerMixin 클래스를 사용하여 커스텀 변환기를 직접 만들 수도 있습니다. AI Platform Prediction에 커스텀 변환기를 사용하는 파이프라인을 배포하려면 AI Platform Prediction에 이 코드를 소스 배포 패키지로 제공해야 합니다.

이 가이드에서는 샘플 문제 해결 인구조사 데이터를 사용하여 다음 단계를 안내합니다.

  • AI Platform Training에서 커스텀 변환기로 scikit-learn 파이프라인 학습
  • AI Platform Prediction에 학습된 파이프라인 및 커스텀 코드 배포
  • 이 배포에서 예측 요청에 대응

데이터세트

이 가이드에서는 UC Irvine Machine Learning Repository에서 제공하는 미국 인구조사 소득 데이터 세트를 사용합니다. 이 데이터 세트에는 1994년 인구조사 데이터베이스의 인구 정보(나이, 교육 수준, 결혼 여부, 직업, 연간 소득 $50,000 초과 여부 등)가 포함되어 있습니다.

이 가이드에 사용되는 데이터는 공개 Cloud Storage 버킷에서 제공됩니다. gs://cloud-samples-data/ai-platform/sklearn/census_data/

목표

목표는 다른 인구조사 정보(특징)를 기반으로 그 사람의 연 소득이 $50,000를 초과하는지(대상 라벨) 여부를 예측하는 scikit-learn 파이프라인을 학습시키는 것입니다.

이 가이드에서는 모델 자체의 설계보다는 AI Platform Prediction에서 이 모델을 사용하는 방법에 중점을 둡니다. 하지만 머신러닝 시스템 빌드 시 항상 잠재적인 문제와 의도치 못한 결과를 생각해 보는 것이 중요합니다. 공정성에 대한 머신러닝 단기집중과정 연습을 통해 머신러닝 공정성과 인구조사 데이터세트의 편향 요인을 알아보세요.

비용

이 가이드에서는 비용이 청구될 수 있는 Google Cloud 구성요소를 사용합니다.

  • AI Platform Training
  • AI Platform Prediction
  • Cloud Storage

AI Platform Training 가격 책정, AI Platform Prediction 가격 책정, Cloud Storage 가격 책정에 대해 알아보고 가격 계산기를 사용하여 예상 사용량을 기준으로 예상 비용을 산출할 수 있습니다.

시작하기 전에

AI Platform Prediction에서 모델을 학습시키고 배포하려면 먼저 다음과 같은 몇 가지 작업을 수행해야 합니다.

  • 로컬 개발 환경을 설정합니다.
  • 결제 및 필요한 API가 사용 설정된 Google Cloud 프로젝트를 설정합니다.
  • 학습 패키지와 학습된 모델을 저장할 Cloud Storage 버킷을 만듭니다.

로컬 개발 환경 설정

이 가이드를 완료하려면 다음이 필요합니다.

  • Python 3
  • virtualenv
  • Google Cloud SDK

Python 개발 환경 설정에 대한 Google Cloud 가이드에서는 이러한 요구사항을 충족하는 절차를 상세하게 설명합니다. 다음 단계는 이 절차를 간략하게 설명한 것입니다.

  1. Python 3을 설치합니다.

  2. virtualenv를 설치하고 Python 3을 사용하는 가상 환경을 만듭니다.

  3. 이 환경을 활성화합니다.

  4. 다음 섹션의 단계를 완료하여 Google Cloud SDK를 설치합니다.

Google Cloud 프로젝트 설정

  1. Google Cloud 계정에 로그인합니다. Google Cloud를 처음 사용하는 경우 계정을 만들고 Google 제품의 실제 성능을 평가해 보세요. 신규 고객에게는 워크로드를 실행, 테스트, 배포하는 데 사용할 수 있는 $300의 무료 크레딧이 제공됩니다.
  2. Google Cloud Console의 프로젝트 선택기 페이지에서 Google Cloud 프로젝트를 선택하거나 만듭니다.

    프로젝트 선택기로 이동

  3. Google Cloud 프로젝트에 결제가 사용 설정되어 있는지 확인합니다.

  4. API AI Platform Training & Prediction and Compute Engine 사용 설정

    API 사용 설정

  5. Google Cloud CLI를 설치합니다.
  6. gcloud CLI를 초기화하려면 다음 명령어를 실행합니다.

    gcloud init
  7. Google Cloud Console의 프로젝트 선택기 페이지에서 Google Cloud 프로젝트를 선택하거나 만듭니다.

    프로젝트 선택기로 이동

  8. Google Cloud 프로젝트에 결제가 사용 설정되어 있는지 확인합니다.

  9. API AI Platform Training & Prediction and Compute Engine 사용 설정

    API 사용 설정

  10. Google Cloud CLI를 설치합니다.
  11. gcloud CLI를 초기화하려면 다음 명령어를 실행합니다.

    gcloud init

GCP 계정 인증

인증을 설정하려면 서비스 계정 키를 만들고 서비스 계정 키의 파일 경로에 해당하는 환경 변수를 설정해야 합니다.

  1. 서비스 계정을 만듭니다.

    1. Google Cloud Console에서 서비스 계정 만들기 페이지로 이동합니다.

      서비스 계정 만들기로 이동

    2. 서비스 계정 이름 필드에 이름을 입력합니다.
    3. 선택사항: 서비스 계정 설명 필드에 설명을 입력합니다.
    4. 만들기를 클릭합니다.
    5. 역할 선택 필드를 클릭합니다. 모든 역할에서 AI Platform > AI Platform 관리자를 선택합니다.
    6. 다른 역할 추가를 클릭합니다.
    7. 역할 선택 필드를 클릭합니다. 모든 역할에서 스토리지 > 스토리지 객체 관리자를 선택합니다.

    8. 완료를 클릭하여 서비스 계정을 만듭니다.

      브라우저 창을 닫지 마세요. 다음 단계에서 사용합니다.

  2. 다음과 같이 인증을 위한 서비스 계정 키를 만듭니다.

    1. Google Cloud Console에서 만든 서비스 계정의 이메일 주소를 클릭합니다.
    2. 를 클릭합니다.
    3. 키 추가를 클릭한 후 새 키 만들기를 클릭합니다.
    4. 만들기를 클릭합니다. JSON 키 파일이 컴퓨터에 다운로드됩니다.
    5. 닫기를 클릭합니다.
  3. 환경 변수 GOOGLE_APPLICATION_CREDENTIALS를 서비스 계정 키가 포함된 JSON 파일의 파일 경로로 설정합니다. 이 변수는 현재 셸 세션에만 적용되므로 새 세션을 연 경우 변수를 다시 설정합니다.

Cloud Storage 버킷 생성

이 가이드에서는 여러 가지 방법으로 Cloud Storage를 사용합니다.

  • Cloud SDK를 사용하여 학습 작업을 제출할 때 학습 코드가 포함된 Python 패키지를 Cloud Storage 버킷에 업로드합니다. AI Platform Training은 이 패키지의 코드를 실행합니다.

  • 또한 이 가이드에서 AI Platform Training은 작업 결과로 얻은 학습된 모델을 동일한 버킷에 저장합니다.

  • AI Platform Prediction에 커스텀 코드를 사용하는 scikit-learn 파이프라인을 배포하려면 파이프라인이 사용하는 커스텀 변환기를 Cloud Storage에 업로드해야 합니다.

예측을 수행하는 AI Platform Prediction 버전 리소스를 만들 때 학습된 scikit-learn 파이프라인과 커스텀 코드를 Cloud Storage URI로 제공해야 합니다.

Cloud Storage 버킷 이름을 환경 변수로 설정합니다. 이 이름은 모든 Cloud Storage 버킷에서 고유해야 합니다.

BUCKET_NAME="your-bucket-name"

AI Platform TrainingAI Platform Prediction을 사용할 수 있는 리전을 선택하고 다른 환경 변수를 만듭니다. 예를 들면 다음과 같습니다.

REGION="us-central1"

이 리전에 Cloud Storage 버킷을 만들고 나중에 같은 리전을 학습 및 예측에 사용합니다. 버킷이 아직 없으면 다음 명령어를 실행하여 버킷을 만듭니다.

gsutil mb -l $REGION gs://$BUCKET_NAME

학습 애플리케이션 및 커스텀 파이프라인 코드 만들기

인구조사 데이터로 scikit-learn 파이프라인을 학습시킬 애플리케이션을 만듭니다. 이 가이드에서는 학습 패키지에 학습된 파이프라인이 예측 중 사용하는 커스텀 코드도 포함됩니다. 파이프라인은 일반적으로 학습과 예측 중 같은 변환기를 사용하도록 설계되므로 이는 유용한 패턴입니다.

다음 단계를 사용하여 다음 구조와 일치하는 3개의 파일이 포함된 디렉터리를 만듭니다.

census_package/
    __init__.py
    my_pipeline.py
    train.py

먼저 빈 census_package/ 디렉터리를 만듭니다.

mkdir census_package

census_package/ 내에서 이름이 __init__.py인 빈 파일을 만듭니다.

touch ./census_package/__init__.py

이렇게 하면 census_package/를 Python에서 패키지로 가져올 수 있습니다.

커스텀 변환기 만들기

scikit-learn은 파이프라인의 일부로 사용할 수 있는 많은 변환기를 제공하지만 직접 커스텀 변환기를 정의할 수도 있습니다. 학습 중 이러한 변환기는 이후 예측에 사용되는 저장된 상태도 학습할 수 있습니다.

이러한 세 가지 변환기를 정의하려면 sklearn.base.TransformerMixin을 확장합니다.

  • PositionalSelector: 색인 C 목록과 행렬 M이 주어지면 C로 표시한 M열의 하위 집합이 포함된 행렬을 반환합니다.

  • StripString: 문자열 행렬이 주어지면 각 문자열에서 공백을 제거합니다.

  • SimpleOneHotEncoder: 문자열 행렬에 적용할 수 있는 샘플 원-핫 인코더

이렇게 하려면 다음 코드를 이름이 census_package/my_pipeline.py인 파일에 씁니다.

import numpy as np
from sklearn.base import BaseEstimator, TransformerMixin

class PositionalSelector(BaseEstimator, TransformerMixin):
    def __init__(self, positions):
        self.positions = positions

    def fit(self, X, y=None):
        return self

    def transform(self, X):
        return np.array(X)[:, self.positions]

class StripString(BaseEstimator, TransformerMixin):
    def fit(self, X, y=None):
        return self

    def transform(self, X):
        strip = np.vectorize(str.strip)
        return strip(np.array(X))

class SimpleOneHotEncoder(BaseEstimator, TransformerMixin):
    def fit(self, X, y=None):
        self.values = []
        for c in range(X.shape[1]):
            Y = X[:, c]
            values = {v: i for i, v in enumerate(np.unique(Y))}
            self.values.append(values)
        return self

    def transform(self, X):
        X = np.array(X)
        matrices = []
        for c in range(X.shape[1]):
            Y = X[:, c]
            matrix = np.zeros(shape=(len(Y), len(self.values[c])), dtype=np.int8)
            for i, x in enumerate(Y):
                if x in self.values[c]:
                    matrix[i][self.values[c][x]] = 1
            matrices.append(matrix)
        res = np.concatenate(matrices, axis=1)
        return res

파이프라인을 정의하고 학습 모듈 만들기

그 다음 학습 모듈을 만들어 인구조사 데이터로 scikit-learn 파이프라인을 학습시킵니다. 이 코드에는 파이프라인 정의가 포함됩니다.

이 학습 모듈은 여러 가지 작업을 수행합니다.

  • 학습 데이터를 다운로드해서 scikit-learn에서 사용 가능한 Pandas DataFrame에 로드합니다.
  • 학습시킬 scikit-learn 파이프라인을 정의합니다. 이 예시에서는 입력 데이터에서 3개의 숫자 특성('age', 'education-num', 'hours-per-week')과 3개의 카테고리 특징('workclass', 'marital-status', 'relationship')만 사용합니다. 숫자 특성은 scikit-learn의 기본 제공 StandardScaler를 사용하여 변환하며 카테고리 특성은 my_pipeline.py에 정의된 커스텀 원-핫 인코더를 사용하여 변환합니다. 그런 다음 전처리된 데이터를 분류 기준을 위한 입력으로 결합합니다.
  • 마지막으로 scikit-learn에 포함된 joblib 버전을 사용하여 모델을 내보내고 Cloud Storage 버킷에 저장합니다.

census_package/train.py에 다음 코드를 작성합니다.

import warnings
import argparse
from google.cloud import storage

import pandas as pd
import numpy as np
from sklearn.externals import joblib
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.pipeline import Pipeline, FeatureUnion, make_pipeline
import census_package.my_pipeline as mp
warnings.filterwarnings('ignore')

def download_data(bucket_name, gcs_path, local_path):
    bucket = storage.Client().bucket(bucket_name)
    blob = bucket.blob(gcs_path)
    blob.download_to_filename(local_path)

def upload_data(bucket_name, gcs_path, local_path):
    bucket = storage.Client().bucket(bucket_name)
    blob = bucket.blob(gcs_path)
    blob.upload_from_filename(local_path)

def get_features_target(local_path):
    strip = np.vectorize(str.strip)
    raw_df = pd.read_csv(local_path, header=None)
    target_index = len(raw_df.columns) - 1  # Last columns, 'income-level', is the target

    features_df = raw_df.drop(target_index, axis=1)
    features = features_df.as_matrix()
    target = strip(raw_df[target_index].values)
    return features, target

def create_pipeline():
    # We want to use 3 categorical and 3 numerical features in this sample.
    # Categorical features: age, education-num, and hours-per-week
    # Numerical features: workclass, marital-status, and relationship
    numerical_indices = [0, 4, 12]  # age, education-num, and hours-per-week
    categorical_indices = [1, 5, 7]  # workclass, marital-status, and relationship

    p1 = make_pipeline(mp.PositionalSelector(categorical_indices), mp.StripString(), mp.SimpleOneHotEncoder())
    p2 = make_pipeline(mp.PositionalSelector(numerical_indices), StandardScaler())

    feats = FeatureUnion([
        ('numericals', p1),
        ('categoricals', p2),
    ])

    pipeline = Pipeline([
        ('pre', feats),
        ('estimator', GradientBoostingClassifier(max_depth=4, n_estimators=100))
    ])
    return pipeline

def get_bucket_path(gcs_uri):
    if not gcs_uri.startswith('gs://'):
        raise Exception('{} does not start with gs://'.format(gcs_uri))
    no_gs_uri = gcs_uri[len('gs://'):]
    first_slash_index = no_gs_uri.find('/')
    bucket_name = no_gs_uri[:first_slash_index]
    gcs_path = no_gs_uri[first_slash_index + 1:]
    return bucket_name, gcs_path

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--gcs_data_path', action="store", required=True)
    parser.add_argument('--gcs_model_path', action="store", required=True)

    arguments, others = parser.parse_known_args()

    local_path = '/tmp/adul.data'
    data_bucket, data_path = get_bucket_path(arguments.gcs_data_path)
    print('Downloading the data...')
    download_data(data_bucket, data_path, local_path)
    features, target = get_features_target(local_path)
    pipeline = create_pipeline()

    print('Training the model...')
    pipeline.fit(features, target)

    joblib.dump(pipeline, './model.joblib')

    model_bucket, model_path = get_bucket_path(arguments.gcs_model_path)
    upload_data(model_bucket, model_path, './model.joblib')
    print('Model was successfully uploaded.')

AI Platform Training에서 파이프라인 학습

gcloud를 사용하여 AI Platform Training에 학습 작업을 제출합니다. 다음 명령어는 학습 애플리케이션을 패키징하여 Cloud Storage에 업로드하고 AI Platform Training에서 학습 모듈을 실행하도록 지시합니다.

-- 인수는 구분자입니다. AI Platform Training 서비스는 구분자를 따르는 인수를 사용하지 않지만 학습 모듈은 여전히 그러한 인수에 액세스할 수 있습니다.

gcloud ai-platform jobs submit training census_training_$(date +"%Y%m%d_%H%M%S") \
  --job-dir gs://$BUCKET_NAME/custom_pipeline_tutorial/job \
  --package-path ./census_package \
  --module-name census_package.train \
  --region $REGION \
  --runtime-version 1.13 \
  --python-version 3.5 \
  --scale-tier BASIC \
  --stream-logs \
  -- \
  --gcs_data_path gs://cloud-samples-data/ai-platform/census/data/adult.data.csv \
  --gcs_model_path gs://$BUCKET_NAME/custom_pipeline_tutorial/model/model.joblib

파이프라인 배포 및 예측 수행

AI Platform Prediction에서 예측을 수행하려면 모델 리소스와 버전 리소스를 배포해야 합니다. 파이프라인을 여러 번 수정하고 학습시키는 경우 모델은 복수의 배포를 정리하는 데 도움이 됩니다. 이 버전은 학습된 모델 및 커스텀 코드를 사용하여 예측을 수행합니다.

이러한 리소스를 배포하려면 두 가지 아티팩트를 제공해야 합니다.

  • 학습된 파이프라인이 포함된 Cloud Storage 디렉터리. 버킷으로 model.joblib를 내보낼 때 이 파일을 만든 이전 단계의 학습 작업.
  • 파이프라인이 사용하는 커스텀 변환기가 포함된 Cloud Storage의 .tar.gz 소스 배포 패키지. 다음 단계에서 만듭니다.

커스텀 변환기 패키징

my_pipeline.py의 코드를 제공하지 않고 배포할 경우 AI Platform 예측은 커스텀 변환기(예: mp.SimpleOneHotEncoder)를 가져올 수 없으며 예측을 제공할 수 없습니다.

다음 setup.py를 만들어 코드의 소스 배포 패키지를 정의합니다.

import setuptools
setuptools.setup(name='census_package',
      packages=['census_package'],
      version="1.0",
      )

그런 후에 다음 명령어를 실행하여 dist/census_package-1.0.tar.gz를 만듭니다.

python setup.py sdist --formats=gztar

마지막으로 Cloud Storage 버킷에 이 tarball을 업로드합니다.

gsutil cp ./dist/census_package-1.0.tar.gz gs://$BUCKET_NAME/custom_pipeline_tutorial/code/census_package-1.0.tar.gz

모델 및 버전 리소스 만들기

먼저 모델 및 버전 이름을 정의합니다.

MODEL_NAME='CensusPredictor'
VERSION_NAME='v1'

이후 다음 명령어를 사용하여 모델 리소스를 만듭니다.

gcloud ai-platform models create $MODEL_NAME \
  --regions $REGION

마지막으로 모델 디렉터리(model.joblib가 포함된 디렉터리)와 커스텀 코드(census_package-1.0.tar.gz)의 Cloud Storage 경로를 제공하여 버전 리소스를 만듭니다.

gcloud components install beta

gcloud beta ai-platform versions create $VERSION_NAME --model $MODEL_NAME \
  --origin gs://$BUCKET_NAME/custom_pipeline_tutorial/model/ \
  --runtime-version 1.13 \
  --python-version 3.5 \
  --framework SCIKIT_LEARN \
  --package-uris gs://$BUCKET_NAME/custom_pipeline_tutorial/code/census_package-1.0.tar.gz

온라인 예측 수행

온라인 예측 요청을 전송하여 배포를 시험합니다. 먼저 Python용 Google API 클라이언트 라이브러리를 설치합니다.

pip install --upgrade google-api-python-client

그런 다음 배포된 버전에 두 개의 인구조사 데이터 인스턴스를 전송합니다.

import googleapiclient.discovery

instances = [
  [39, 'State-gov', 77516, ' Bachelors .  ', 13, 'Never-married', 'Adm-clerical', 'Not-in-family',
   'White', 'Male', 2174, 0, 40, 'United-States', '<=50K'],
  [50, 'Self-emp-not-inc', 83311, 'Bachelors', 13, 'Married-civ-spouse', 'Exec-managerial', 'Husband',
   'White', 'Male', 0, 0, 13, 'United-States', '<=50K']
]

service = googleapiclient.discovery.build('ml', 'v1')
name = 'projects/{}/models/{}/versions/{}'.format(PROJECT_ID, MODEL_NAME, VERSION_NAME)

response = service.projects().predict(
    name=name,
    body={'instances': instances}
).execute()

if 'error' in response:
    raise RuntimeError(response['error'])
else:
  print(response['predictions'])

이 버전은 학습된 파이프라인을 통해 입력 데이터를 전달하고 분류자의 결과를 반환합니다. 결과는 그 사람의 소득 계층에 대한 예측에 따라 <=50K 또는 >50K입니다.

삭제

이 프로젝트에 사용된 모든 Google Cloud 리소스를 삭제하려면 이 가이드에서 사용한 Google Cloud 프로젝트를 삭제하면 됩니다.

또는 다음 명령어를 실행하여 개별 리소스를 삭제할 수 있습니다.

# Delete version resource
gcloud ai-platform versions delete $VERSION_NAME --quiet --model $MODEL_NAME

# Delete model resource
gcloud ai-platform models delete $MODEL_NAME --quiet

# Delete Cloud Storage objects that were created
gsutil -m rm -r gs://$BUCKET_NAME/custom_pipeline_tutorial

다음 단계