justin-onda Claude commited on
Commit
575aa0e
·
1 Parent(s): 10e6bbd

fix: 헤드 ONNX 출력 순서 수정 (v0.2.1) [AI]

Browse files

convert_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>

Files changed (2) hide show
  1. head.onnx +1 -1
  2. inference_example.py +105 -63
head.onnx CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:acf595e6f5208e5ebd833868b62a8eb513d56a681b160231f2f10bf25d4b48d1
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 predict_image(onnx_model_path, model_info_path, image_path):
38
- """이미지 분류 예측"""
39
 
40
  # 모델 정보 로드
41
  model_info = load_model_info(model_info_path)
42
 
43
  # ONNX 세션 생성
44
- session = ort.InferenceSession(onnx_model_path)
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
- output_names = head_names + ['features'] # features 추가
57
-
58
- for i, output_name in enumerate(output_names):
59
- if output_name == 'features':
60
- # 특징 벡터 처리
61
- features = outputs[i][0] # 번째 배치
62
- results[output_name] = {
63
- 'embedding': features.tolist(),
64
- 'dimension': len(features),
65
- 'description': 'DINOv2 backbone features'
66
- }
67
- else:
68
- # 분류 헤드 처리
69
- logits = outputs[i]
70
- probabilities = softmax(logits)[0] # 첫 번째 배치
71
-
72
- # 클래스 이름 매핑
73
- class_names = model_info['class_mappings'].get(output_name, {})
74
-
75
- # 최고 확률 클래스
76
- pred_idx = np.argmax(probabilities)
77
- pred_class = class_names.get(str(pred_idx), f"Class_{pred_idx}")
78
- pred_prob = probabilities[pred_idx]
79
-
80
- # 상위 3개 클래스
81
- top3_indices = np.argsort(probabilities)[-3:][::-1]
82
- top3_results = []
83
- for idx in top3_indices:
84
- class_name = class_names.get(str(idx), f"Class_{idx}")
85
- prob = probabilities[idx]
86
- top3_results.append({'class': class_name, 'probability': float(prob)})
87
-
88
- results[output_name] = {
89
- 'predicted_class': pred_class,
90
- 'confidence': float(pred_prob),
91
- 'top3': top3_results
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
- try:
103
- results = predict_image(onnx_path, model_info_path, image_path)
104
-
105
- print(f"이미지 분류 결과: {image_path}")
106
- print("=" * 50)
107
-
108
- for output_name, result in results.items():
109
- if output_name == 'features':
110
- print(f"\n{output_name.upper()}:")
111
- print(f" 차원: {result['dimension']}")
112
- print(f" 설명: {result['description']}")
113
- print(f" 특징 벡터 (처음 10개): {result['embedding'][:10]}")
114
- else:
115
- print(f"\n{output_name.upper()}:")
116
- print(f" 예측 클래스: {result['predicted_class']}")
117
- print(f" 신뢰도: {result['confidence']:.4f}")
118
- print(f" Top 3:")
119
- for i, top_result in enumerate(result['top3'], 1):
120
- print(f" {i}. {top_result['class']}: {top_result['probability']:.4f}")
121
-
122
- except Exception as e:
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}")