Commit
·
575aa0e
1
Parent(s):
10e6bbd
fix: 헤드 ONNX 출력 순서 수정 (v0.2.1) [AI]
Browse filesconvert_to_onnx.py의 헤드 변환 시 출력 순서를 정렬하여
원본 PyTorch 모델과 순서가 불일치하던 문제를 수정했습니다.
변경 사항:
- MultiHeadWrapper.forward(): 정렬 제거, 원본 ModuleDict 순서 유지
- output_names: sorted() 제거, list(model.heads.keys()) 사용
- 헤드 순서: ['scene', 'concept', 'object']로 통일
이제 분류 결과가 올바른 헤드에 매핑됩니다.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- head.onnx +1 -1
- inference_example.py +105 -63
head.onnx
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
size 90957
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ead5678e0ec6c628cbcdd946258d9286ddb53d8969f9a2e3e39c1d5dc718c2ec
|
| 3 |
size 90957
|
inference_example.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
| 1 |
#!/usr/bin/env python3
|
| 2 |
"""
|
| 3 |
ONNX 모델을 사용한 멀티헤드 이미지 분류 추론 예제
|
|
|
|
| 4 |
"""
|
| 5 |
|
| 6 |
import onnxruntime as ort
|
|
@@ -8,6 +9,7 @@ import numpy as np
|
|
| 8 |
from PIL import Image
|
| 9 |
import torchvision.transforms as transforms
|
| 10 |
import json
|
|
|
|
| 11 |
|
| 12 |
# 전처리 파이프라인
|
| 13 |
transform = transforms.Compose([
|
|
@@ -34,14 +36,14 @@ def softmax(x):
|
|
| 34 |
exp_x = np.exp(x - np.max(x, axis=1, keepdims=True))
|
| 35 |
return exp_x / np.sum(exp_x, axis=1, keepdims=True)
|
| 36 |
|
| 37 |
-
def
|
| 38 |
-
"""이미지 분류 예측"""
|
| 39 |
|
| 40 |
# 모델 정보 로드
|
| 41 |
model_info = load_model_info(model_info_path)
|
| 42 |
|
| 43 |
# ONNX 세션 생성
|
| 44 |
-
session = ort.InferenceSession(
|
| 45 |
|
| 46 |
# 이미지 전처리
|
| 47 |
image_array = preprocess_image(image_path)
|
|
@@ -53,71 +55,111 @@ def predict_image(onnx_model_path, model_info_path, image_path):
|
|
| 53 |
# 결과 해석
|
| 54 |
results = {}
|
| 55 |
head_names = list(model_info['output_specification']['heads'].keys())
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 93 |
|
| 94 |
return results
|
| 95 |
|
| 96 |
# 사용 예시
|
| 97 |
if __name__ == "__main__":
|
| 98 |
-
onnx_path = "image_classifier.onnx"
|
| 99 |
model_info_path = "model_info.json"
|
| 100 |
image_path = "test_image.jpg"
|
| 101 |
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
print(f"추론 실패: {e}")
|
|
|
|
| 1 |
#!/usr/bin/env python3
|
| 2 |
"""
|
| 3 |
ONNX 모델을 사용한 멀티헤드 이미지 분류 추론 예제
|
| 4 |
+
전체 모델(model.onnx) 또는 분리 모델(encoder.onnx + head.onnx) 사용 가능
|
| 5 |
"""
|
| 6 |
|
| 7 |
import onnxruntime as ort
|
|
|
|
| 9 |
from PIL import Image
|
| 10 |
import torchvision.transforms as transforms
|
| 11 |
import json
|
| 12 |
+
from pathlib import Path
|
| 13 |
|
| 14 |
# 전처리 파이프라인
|
| 15 |
transform = transforms.Compose([
|
|
|
|
| 36 |
exp_x = np.exp(x - np.max(x, axis=1, keepdims=True))
|
| 37 |
return exp_x / np.sum(exp_x, axis=1, keepdims=True)
|
| 38 |
|
| 39 |
+
def predict_image_full_model(model_path, model_info_path, image_path):
|
| 40 |
+
"""전체 모델을 사용한 이미지 분류 예측"""
|
| 41 |
|
| 42 |
# 모델 정보 로드
|
| 43 |
model_info = load_model_info(model_info_path)
|
| 44 |
|
| 45 |
# ONNX 세션 생성
|
| 46 |
+
session = ort.InferenceSession(model_path)
|
| 47 |
|
| 48 |
# 이미지 전처리
|
| 49 |
image_array = preprocess_image(image_path)
|
|
|
|
| 55 |
# 결과 해석
|
| 56 |
results = {}
|
| 57 |
head_names = list(model_info['output_specification']['heads'].keys())
|
| 58 |
+
|
| 59 |
+
for i, output_name in enumerate(head_names):
|
| 60 |
+
logits = outputs[i]
|
| 61 |
+
probabilities = softmax(logits)[0]
|
| 62 |
+
|
| 63 |
+
# 클래스 이름 매핑
|
| 64 |
+
class_names = model_info['class_mappings'].get(output_name, {})
|
| 65 |
+
|
| 66 |
+
# 최고 확률 클래스
|
| 67 |
+
pred_idx = np.argmax(probabilities)
|
| 68 |
+
pred_class = class_names.get(str(pred_idx), f"Class_{pred_idx}")
|
| 69 |
+
pred_prob = probabilities[pred_idx]
|
| 70 |
+
|
| 71 |
+
# 상위 3개 클래스
|
| 72 |
+
top3_indices = np.argsort(probabilities)[-3:][::-1]
|
| 73 |
+
top3_results = []
|
| 74 |
+
for idx in top3_indices:
|
| 75 |
+
class_name = class_names.get(str(idx), f"Class_{idx}")
|
| 76 |
+
prob = probabilities[idx]
|
| 77 |
+
top3_results.append({'class': class_name, 'probability': float(prob)})
|
| 78 |
+
|
| 79 |
+
results[output_name] = {
|
| 80 |
+
'predicted_class': pred_class,
|
| 81 |
+
'confidence': float(pred_prob),
|
| 82 |
+
'top3': top3_results
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
return results
|
| 86 |
+
|
| 87 |
+
def predict_image_split_model(encoder_path, head_path, model_info_path, image_path):
|
| 88 |
+
"""분리 모델을 사용한 이미지 분류 예측"""
|
| 89 |
+
|
| 90 |
+
# 모델 정보 로드
|
| 91 |
+
model_info = load_model_info(model_info_path)
|
| 92 |
+
|
| 93 |
+
# ONNX 세션 생성
|
| 94 |
+
encoder_session = ort.InferenceSession(encoder_path)
|
| 95 |
+
head_session = ort.InferenceSession(head_path)
|
| 96 |
+
|
| 97 |
+
# 이미지 전처리
|
| 98 |
+
image_array = preprocess_image(image_path)
|
| 99 |
+
|
| 100 |
+
# 인코더로 특징 벡터 추출
|
| 101 |
+
encoder_inputs = {'image': image_array}
|
| 102 |
+
features = encoder_session.run(None, encoder_inputs)[0]
|
| 103 |
+
|
| 104 |
+
# 헤드로 분류
|
| 105 |
+
head_inputs = {'features': features}
|
| 106 |
+
outputs = head_session.run(None, head_inputs)
|
| 107 |
+
|
| 108 |
+
# 결과 해석
|
| 109 |
+
results = {}
|
| 110 |
+
head_names = list(model_info['output_specification']['heads'].keys())
|
| 111 |
+
|
| 112 |
+
for i, output_name in enumerate(head_names):
|
| 113 |
+
logits = outputs[i]
|
| 114 |
+
probabilities = softmax(logits)[0]
|
| 115 |
+
|
| 116 |
+
# 클래스 이름 매핑
|
| 117 |
+
class_names = model_info['class_mappings'].get(output_name, {})
|
| 118 |
+
|
| 119 |
+
# 최고 확률 클래스
|
| 120 |
+
pred_idx = np.argmax(probabilities)
|
| 121 |
+
pred_class = class_names.get(str(pred_idx), f"Class_{pred_idx}")
|
| 122 |
+
pred_prob = probabilities[pred_idx]
|
| 123 |
+
|
| 124 |
+
# 상위 3개 클래스
|
| 125 |
+
top3_indices = np.argsort(probabilities)[-3:][::-1]
|
| 126 |
+
top3_results = []
|
| 127 |
+
for idx in top3_indices:
|
| 128 |
+
class_name = class_names.get(str(idx), f"Class_{idx}")
|
| 129 |
+
prob = probabilities[idx]
|
| 130 |
+
top3_results.append({'class': class_name, 'probability': float(prob)})
|
| 131 |
+
|
| 132 |
+
results[output_name] = {
|
| 133 |
+
'predicted_class': pred_class,
|
| 134 |
+
'confidence': float(pred_prob),
|
| 135 |
+
'top3': top3_results
|
| 136 |
+
}
|
| 137 |
|
| 138 |
return results
|
| 139 |
|
| 140 |
# 사용 예시
|
| 141 |
if __name__ == "__main__":
|
|
|
|
| 142 |
model_info_path = "model_info.json"
|
| 143 |
image_path = "test_image.jpg"
|
| 144 |
|
| 145 |
+
# 분리 모델이 있는지 확인
|
| 146 |
+
if Path("encoder.onnx").exists() and Path("head.onnx").exists():
|
| 147 |
+
print("분리 모델 사용")
|
| 148 |
+
results = predict_image_split_model("encoder.onnx", "head.onnx", model_info_path, image_path)
|
| 149 |
+
elif Path("model.onnx").exists():
|
| 150 |
+
print("전체 모델 사용")
|
| 151 |
+
results = predict_image_full_model("model.onnx", model_info_path, image_path)
|
| 152 |
+
else:
|
| 153 |
+
print("ONNX 모델을 찾을 수 없습니다.")
|
| 154 |
+
exit(1)
|
| 155 |
+
|
| 156 |
+
print(f"\n이미지 분류 결과: {image_path}")
|
| 157 |
+
print("=" * 50)
|
| 158 |
+
|
| 159 |
+
for output_name, result in results.items():
|
| 160 |
+
print(f"\n{output_name.upper()}:")
|
| 161 |
+
print(f" 예측 클래스: {result['predicted_class']}")
|
| 162 |
+
print(f" 신뢰도: {result['confidence']:.4f}")
|
| 163 |
+
print(f" Top 3:")
|
| 164 |
+
for i, top_result in enumerate(result['top3'], 1):
|
| 165 |
+
print(f" {i}. {top_result['class']}: {top_result['probability']:.4f}")
|
|
|