개발 일기

머신러닝 모듈화 코드가 필요한 이유 (현업에서는 세분화로 사용)

어제보다보고싶어 2024. 11. 16. 08:35
 
 

배추 가격 예측 같은 머신러닝 모델에서 모듈화가 안된 코드모듈화된 코드의 차이를 배추 가격 예측 모델을 예로 들어 설명하겠습니다.(corsor ai 툴에서 기본적으로 모듈화로 만들어줌)

1. 모듈화되지 않은 코드 예시

모듈화가 되지 않은 코드에서는 전체 과정을 하나의 긴 코드 블록으로 작성하여, 각 기능이 독립적으로 구분되지 않습니다. 예를 들어, 데이터 전처리, 모델 정의, 훈련, 평가 등이 모두 한 파일에 이어져 있고, 반복되는 코드도 별도로 함수로 분리되지 않습니다.

import numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error

# 데이터 준비 (모의 데이터)
x = np.array([1000, 2000, 3000, 4000]).reshape(-1, 1)  # 온도 데이터 (독립 변수)
y = np.array([1500, 3000, 4500, 6000])  # 배추 가격 데이터 (종속 변수)

# 모델 생성
model = LinearRegression()

# 훈련
model.fit(x, y)

# 예측
predictions = model.predict(x)

# 성능 평가
mse = mean_squared_error(y, predictions)
print("Mean Squared Error:", mse)

이 코드는 간단히 목적을 수행할 수 있지만, 모듈화되지 않아서 다음과 같은 문제들이 발생합니다.

  • 재사용성: 특정 기능을 다른 예측 모델에서 사용하기 어렵습니다.
  • 유지보수성: 코드가 길어지면 수정 및 관리가 어려워집니다.
  • 가독성: 기능별로 구분이 없어 코드 이해가 어렵습니다.

2. 모듈화된 코드 예시

모듈화된 코드에서는 각 기능을 함수나 클래스로 구분해 반복적인 작업이나 수정이 필요한 경우 간편하게 사용할 수 있습니다. 아래 예시는 데이터 전처리, 모델 정의, 훈련, 예측, 평가를 각각의 함수로 모듈화한 경우입니다.

import numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error

# 데이터 준비
def load_data():
    x = np.array([1000, 2000, 3000, 4000]).reshape(-1, 1)  # 온도 데이터 (독립 변수)
    y = np.array([1500, 3000, 4500, 6000])  # 배추 가격 데이터 (종속 변수)
    return x, y

# 모델 생성 함수
def create_model():
    return LinearRegression()

# 모델 훈련 함수
def train_model(model, x, y):
    model.fit(x, y)

# 예측 함수
def make_predictions(model, x):
    return model.predict(x)

# 성능 평가 함수
def evaluate_model(y_true, y_pred):
    mse = mean_squared_error(y_true, y_pred)
    print("Mean Squared Error:", mse)
    return mse

# 전체 워크플로우
def run_pipeline():
    x, y = load_data()
    model = create_model()
    train_model(model, x, y)
    predictions = make_predictions(model, x)
    evaluate_model(y, predictions)

# 실행
run_pipeline()

모듈화된 코드의 장점

  1. 재사용성: 각 함수가 독립적이므로 다른 예측 작업에서도 쉽게 사용할 수 있습니다.
  2. 유지보수성: 각 기능이 분리되어 있어, 예를 들어 모델을 변경하거나 새로운 성능 평가 방법을 추가할 때 수정이 용이합니다.
  3. 확장성: 특정 함수만 수정하여 여러 모델을 실험할 수 있습니다. 예를 들어 create_model() 함수에서 모델을 바꾸거나 evaluate_model()에서 평가 방식을 바꿀 수 있습니다.
  4. 가독성: 함수별 역할이 명확히 드러나므로 코드 이해가 쉬워집니다.

요약 비교

특징모듈화되지 않은 코드모듈화된 코드
재사용성 제한적 높은 재사용성
유지보수성 어려움 각 기능별로 분리되어 수정 용이
가독성 낮음 높음, 코드가 기능별로 구분됨
확장성 수정이 어려움 새로운 기능 추가, 수정이 용이
코드 관리 비효율적, 모든 과정이 한 블록에 포함 함수별로 관리, 워크플로우가 명확

이와 같이, 머신러닝 모델에서 모듈화를 통해 코드를 세분화하면 더 유연하고 유지보수하기 쉬운 예측 시스템을 구축할 수 있습니다.

 

 

모듈화된 코드를 작성하면, 동일한 코드를 반복적으로 작성하지 않고, 이미 정의된 함수나 클래스를 여러 상황에서 재활용할 수 있습니다. 이로 인해 새로운 프로젝트나 데이터셋에서도 빠르게 모델을 구축하고, 테스트하거나, 개선할 수 있습니다. 예를 들어, 배추 가격 예측 코드의 재사용 방법을 몇 가지 시나리오와 예제로 설명해 보겠습니다.

1. 다른 데이터셋에 적용

배추 가격 예측 모델과 유사하게 다른 작물의 가격을 예측해야 할 때, 기본적인 모델 구조는 그대로 활용하되, 데이터를 다른 데이터셋으로 바꿔줄 수 있습니다. 예를 들어, 배추 대신 쌀 가격을 예측한다고 하면, 데이터 준비 함수만 다른 데이터셋을 불러오도록 수정하면 됩니다.

def load_rice_data():
    # 쌀 가격 데이터셋 불러오기
    x = np.array([1000, 2000, 3000, 4000]).reshape(-1, 1)  # 온도 데이터 (독립 변수)
    y = np.array([2000, 4000, 6000, 8000])  # 쌀 가격 데이터 (종속 변수)
    return x, y

# 기존 모델 함수들을 그대로 사용하고, 데이터만 변경하여 예측 파이프라인 실행
def run_rice_pipeline():
    x, y = load_rice_data()  # 새로운 데이터셋으로 대체
    model = create_model()
    train_model(model, x, y)
    predictions = make_predictions(model, x)
    evaluate_model(y, predictions)

run_rice_pipeline()  # 쌀 가격 예측 수행

이렇게 데이터만 변경해주면 배추 가격 예측 코드를 재활용해 쌀 가격 예측을 수행할 수 있습니다.

2. 다른 모델로 확장

같은 배추 가격 예측 프로젝트에서 다른 모델을 적용해보고 싶을 때도 기존 함수를 재활용할 수 있습니다. 예를 들어, create_model() 함수에서 LinearRegression 대신 RandomForestRegressor나 다른 모델을 사용할 수 있습니다.

from sklearn.ensemble import RandomForestRegressor

def create_random_forest_model():
    return RandomForestRegressor(n_estimators=100, random_state=42)

def run_pipeline_with_random_forest():
    x, y = load_data()  # 기존 데이터 사용
    model = create_random_forest_model()  # 새로운 모델로 대체
    train_model(model, x, y)
    predictions = make_predictions(model, x)
    evaluate_model(y, predictions)

run_pipeline_with_random_forest()  # 배추 가격 예측을 랜덤 포레스트 모델로 실행

3. 예측 파이프라인을 활용한 실시간 예측

모델을 배포하여 실시간 예측 기능을 제공할 때도 모듈화된 함수들을 재사용할 수 있습니다. 예를 들어, 웹 애플리케이션에서 사용자가 입력한 온도 값을 받아 배추 가격을 예측한다고 가정해보겠습니다.

def predict_single_value(input_value):
    model = create_model()
    x, y = load_data()
    train_model(model, x, y)  # 모델을 훈련시켜 저장
    return model.predict(np.array([[input_value]]))  # 새로운 값 예측

# 예: 사용자가 입력한 온도가 2500일 때의 배추 가격 예측
predicted_price = predict_single_value(2500)
print("Predicted Cabbage Price:", predicted_price)

4. 다른 분석에 활용

모듈화된 함수는 배추 가격 예측 이외의 다양한 분석 모델에도 활용할 수 있습니다. 예를 들어, 데이터 전처리 함수나 평가 함수 등은 다른 머신러닝 프로젝트에서도 반복적으로 사용될 수 있습니다.

def standardize_data(x):
    return (x - np.mean(x)) / np.std(x)

def load_data_standardized():
    x, y = load_data()
    x_standardized = standardize_data(x)  # 데이터 표준화
    return x_standardized, y

# 표준화된 데이터를 사용한 파이프라인 실행
def run_standardized_pipeline():
    x, y = load_data_standardized()
    model = create_model()
    train_model(model, x, y)
    predictions = make_predictions(model, x)
    evaluate_model(y, predictions)

run_standardized_pipeline()

위와 같이 데이터를 표준화하여 다른 프로젝트에서도 쉽게 사용할 수 있습니다. standardize_data 함수는 이후 다양한 데이터셋에서도 재사용할 수 있어 유연한 분석이 가능합니다.


이렇게 각 기능을 모듈화하고 함수로 만들면 유사한 데이터 분석 작업이나 예측 모델을 구축할 때 별도의 코드 수정 없이 필요한 부분만 바꿔 재사용할 수 있습니다.

 
최상위 함수란? 
 

데이터 준비, 모델 생성, 모델 훈련, 예측, 성능 평가 같은 함수들이 최상위 함수입니다. 이런 최상위 함수들은 각각의 주요 단계를 대표하며, 기능별로 독립적인 역할을 합니다.

함수를 세분화할수록 코드 재사용성이 높아지는데, 최상위 함수 안에서 또 작은 하위 함수들로 나누는 방식을 통해 코드의 유연성과 유지보수성을 더욱 높일 수 있습니다. 예를 들어, 모델 훈련 함수 내부에서 데이터 정규화나 학습 파라미터 세팅을 개별 함수로 나눌 수 있습니다.

예시

  1. 최상위 함수 (최종 기능을 대표): prepare_data, create_model, train_model, predict, evaluate_model
  2. 하위 함수 (각 최상위 함수 내의 더 작은 기능 담당):
    • prepare_data: load_data, clean_data, standardize_data
    • train_model: initialize_parameters, update_weights, compute_loss

깃허브에서 최상위 함수로 구성하기

이렇게 설계한 코드를 깃허브에 올리면, 다른 사람들도 최상위 함수들만 호출해도 기능을 이해하고 쉽게 사용할 수 있습니다.