728x90
반응형
Scikit-learn은 대표적인 머신러닝 라이브러리로, 다양한 모델을 빠르게 실험하고 적용할 수 있게 해줍니다.
이 글에서는 Scikit-learn 모델을 ONNX(Open Neural Network Exchange) 형식으로 변환하는 방법과, 변환된 모델의 정확도 비교 방법까지 설명합니다.
1. Scikit-learn 모델의 5가지 분류
Scikit-learn에서 제공하는 모델들은 다음과 같이 5가지 주요 유형으로 나눌 수 있습니다.
구분 | 설명 | 예시 모델 |
Classifier | 분류 문제 해결 | LogisticRegression, RandomForestClassifier |
Regressor | 연속적인 수치 예측 문제 해결 | LinearRegression, GradientBoostingRegressor |
Clusterer | 비지도 학습 기반 클러스터링 | KMeans, DBSCAN |
Transformer | 데이터 전처리 또는 특성 변환 모델 | PCA, StandardScaler |
Pipeline | 여러 모델을 연결한 파이프라인 | Pipeline, GridSearchCV |
이번 포스팅에서는 이 중에서 Classifier와 Regressor 모델을 중심으로 다뤄보겠습니다.
2. ONNX 변환 전, 꼭 알아야 할 fit() 함수의 역할
Scikit-learn의 모델은 다음과 같이 fit() 메서드를 통해 학습과 초기화가 동시에 이루어집니다.
model = LogisticRegression()
model.fit(X_train, y_train)
하지만 단순히 학습만 하는 것이 아닙니다. fit() 함수는 아래와 같은 매우 중요한 역할을 합니다.
🎯 왜 fit()이 꼭 필요한가?
- ONNX는 정적인 연산 그래프를 생성해야 하므로,
변환 시 모델 내부의 가중치, 입력 형태, 출력 차원 등을 완전히 정의하고 있어야 합니다. - Scikit-learn 모델은 생성 직후에는 내부 파라미터(coef_, intercept_ 등)가 없는 상태입니다.
- fit()을 통해 이 모든 정보를 설정한 뒤에야 convert_sklearn()을 사용해 ONNX 변환이 가능해집니다.
📦비유
- model → 부품만 있는 기계
- model.fit() → 조립 및 작동 준비 완료
- convert_sklearn() → 완성된 기계를 ONNX 박스로 포장
3. Classifier 모델을 ONNX로 변환하고 성능 비교하기
import os
import numpy as np
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.utils import all_estimators
from sklearn.base import ClassifierMixin
from sklearn.metrics import accuracy_score
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
import onnxruntime as ort
# ✅ 설정
MODEL_NAME = "KNeighborsClassifier"
SAVE_DIR = "./onnx_models"
os.makedirs(SAVE_DIR, exist_ok=True)
# ✅ 데이터셋 준비
X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
initial_type = [("float_input", FloatTensorType([None, X.shape[1]]))]
# ✅ ONNX 변환 함수
def convert_classifier_to_onnx(model_name: str, save_dir: str = SAVE_DIR):
classifiers = {
name: cls for name, cls in all_estimators(type_filter="classifier")
if issubclass(cls, ClassifierMixin)
}
if model_name not in classifiers:
raise ValueError(f"❌ '{model_name}' 는 유효한 classifier가 아닙니다.")
model_cls = classifiers[model_name]
model = model_cls()
# 학습
model.fit(X_train, y_train)
# ONNX 변환
onnx_model = convert_sklearn(model, initial_types=initial_type)
save_path = os.path.join(save_dir, f"{model_name}.onnx")
with open(save_path, "wb") as f:
f.write(onnx_model.SerializeToString())
print(f"✅ ONNX 변환 완료: {save_path}")
return save_path, model
def evaluate_model(model, onnx_path: str):
# Scikit-learn 평가
y_pred = model.predict(X_test)
sklearn_acc = accuracy_score(y_test, y_pred)
# ONNX 평가
sess = ort.InferenceSession(onnx_path) # onnx 모델 로드
input_name = sess.get_inputs()[0].name # 입력 텐서의 이름 가져옴
pred_onnx = sess.run(None, {input_name: X_test.astype(np.float32)})[0] # onnx 모델 예측 결과
# 후처리
if pred_onnx.ndim == 2 and pred_onnx.shape[1] > 1:
print("✅ multi-class")
pred_labels = np.argmax(pred_onnx, axis=1) # 다중 클래스 분류
else:
print("✅ Binary")
pred_labels = np.rint(pred_onnx).astype(int).reshape(-1) # 이진 분류 대응
print("✅ Ground Truth (y_test) :", y_test)
print("✅ Scikit-learn 예측 결과 :", y_pred)
print("✅ ONNX 모델 예측 결과 :", pred_labels)
# onnx 정확도
onnx_acc = accuracy_score(y_test, pred_labels)
print(f"✅ scikit-learn 정확도: {sklearn_acc:.4f}")
print(f"✅ ONNXRuntime 정확도: {onnx_acc:.4f}")
# ✅ 실행
if __name__ == "__main__":
onnx_path, model = convert_classifier_to_onnx(MODEL_NAME)
evaluate_model(model, onnx_path)
실행 결과
✅ ONNX 변환 완료: ./onnx_models/KNeighborsClassifier.onnx
✅ Binary
✅ Ground Truth (y_test) : [1 0 2 1 1 0 1 2 1 1 2 0 0 0 0 1 2 1 1 2 0 2 0 2 2 2 2 2 0 0]
✅ Scikit-learn 예측 결과 : [1 0 2 1 1 0 1 2 1 1 2 0 0 0 0 1 2 1 1 2 0 2 0 2 2 2 2 2 0 0]
✅ ONNX 모델 예측 결과 : [1 0 2 1 1 0 1 2 1 1 2 0 0 0 0 1 2 1 1 2 0 2 0 2 2 2 2 2 0 0]
✅ scikit-learn 정확도: 1.0000
✅ ONNXRuntime 정확도: 1.0000
ONNX로 변환된 모델도 scikit-learn과 동일한 예측 정확도를 보여주며,
CPU 환경에서도 경량화된 추론 실행이 가능합니다.
참고 : Regressor onnx 변환 코드
더보기
Regressor onnx 변환 코드
import os
import numpy as np
from sklearn.datasets import load_diabetes
from sklearn.model_selection import train_test_split
from sklearn.utils import all_estimators
from sklearn.base import RegressorMixin
from sklearn.metrics import mean_squared_error, r2_score
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
import onnxruntime as ort
# ✅ 설정
MODEL_NAME = "Ridge"
SAVE_DIR = "./onnx_models"
os.makedirs(SAVE_DIR, exist_ok=True)
# ✅ 데이터셋 준비 (회귀 데이터셋 사용)
X, y = load_diabetes(return_X_y=True) # 당뇨병
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
initial_type = [("float_input", FloatTensorType([None, X.shape[1]]))]
# ✅ ONNX 변환 함수
def convert_regressor_to_onnx(model_name: str, save_dir: str = SAVE_DIR):
regressors = {
name: cls for name, cls in all_estimators(type_filter="regressor")
if issubclass(cls, RegressorMixin)
}
if model_name not in regressors:
raise ValueError(f"❌ '{model_name}' 는 유효한 regressor가 아닙니다.")
model_cls = regressors[model_name]
model = model_cls()
model.fit(X_train, y_train)
# ONNX 변환
onnx_model = convert_sklearn(model, initial_types=initial_type)
save_path = os.path.join(save_dir, f"{model_name}.onnx")
with open(save_path, "wb") as f:
f.write(onnx_model.SerializeToString())
print(f"✅ ONNX 변환 완료: {save_path}")
return save_path, model
# ✅ 정확도 비교
def evaluate_model(model, onnx_path: str):
# Scikit-learn 평가
y_pred = model.predict(X_test)
mse_skl = mean_squared_error(y_test, y_pred)
r2_skl = r2_score(y_test, y_pred)
# ONNX 평가
sess = ort.InferenceSession(onnx_path)
input_name = sess.get_inputs()[0].name
pred_onnx = sess.run(None, {input_name: X_test.astype(np.float32)})[0].reshape(-1)
mse_onnx = mean_squared_error(y_test, pred_onnx)
r2_onnx = r2_score(y_test, pred_onnx)
# 테스트 샘플에 대한 예측
print("✅ Ground Truth (y_test):", y_test[:5])
print("✅ Sklearn 예측:", y_pred[:5])
print("✅ ONNX 예측:", pred_onnx[:5])
print(f"✅ Sklearn MSE: {mse_skl:.4f}, R2: {r2_skl:.4f}")
print(f"✅ ONNX MSE: {mse_onnx:.4f}, R2: {r2_onnx:.4f}")
# ✅ 실행
if __name__ == "__main__":
onnx_path, model = convert_regressor_to_onnx(MODEL_NAME)
evaluate_model(model, onnx_path)
정리
항목 | 설명 |
✅ 변환 대상 | Classifier / Regressor 위주 |
✅ 변환 이유 | 경량화, 다양한 추론 환경 지원 (CPU, IoT 등) |
✅ fit() 필요 | 모델 구조와 파라미터 초기화를 위해 반드시 필요 |
✅ 성능 비교 | 변환 전후 모델의 예측 성능(정확도, MSE 등) 비교 가능 |
728x90
반응형
'딥러닝 (Deep Learning) > [03] - 모델' 카테고리의 다른 글
Scikit-Learn 모델 분류 및 개념 정리 (0) | 2025.04.03 |
---|---|
Maskformer (0) | 2024.11.27 |
U-Net3++ (0) | 2024.11.22 |
DeepLab v1 아키텍쳐 분석 (1) | 2024.11.20 |
SegNet의 아키텍처 (3) | 2024.11.18 |