K7Ploypailin commited on
Commit
e404781
·
1 Parent(s): 95eb984

Add application file

Browse files
Files changed (1) hide show
  1. app.py +40 -32
app.py CHANGED
@@ -5,24 +5,25 @@ import onnxruntime as rt
5
  import os
6
  import spaces
7
  import torch
 
8
  from transformers import AutoImageProcessor
9
- from scipy.special import softmax
10
- import requests # <-- **NEW:** สำหรับการเรียก Hugging Face API
11
 
12
- # 1. การกำหนดค่าและโหลดโมเดล ONNX และ LLM
13
  # ----------------------------------------------------
14
  ONNX_MODEL_PATH = "model.onnx"
15
  CLASS_LABELS_FILE = "class_labels.txt"
16
- MODEL_ID = 'facebook/convnext-tiny-224' # ID โมเดลที่ใช้ในการฝึก
17
 
18
- # **LLM CONFIGURATION (NEW)**
19
- MODEL_NAME = "scb10x/typhoon2.5-qwen3-4b" # <--- ID โมเดล Typhoon 2.5
20
- # ดึง Hugging Face Token จาก Environment Variable (ต้องตั้งค่าใน Space Secrets)
21
  HF_TOKEN = os.getenv("HF_TOKEN")
22
  API_URL = f"https://api-inference.huggingface.co/models/{MODEL_NAME}"
23
 
24
 
25
- # โหลดชื่อคลาส (ตัวละครวันพีซ)
26
  try:
27
  with open(CLASS_LABELS_FILE, 'r', encoding='utf-8') as f:
28
  CHARACTER_LABELS = [line.strip() for line in f.readlines()]
@@ -30,45 +31,49 @@ except FileNotFoundError:
30
  CHARACTER_LABELS = ['Luffy', 'Zoro', 'Nami', 'Sanji', 'Chopper', 'Franky', 'Brook', 'Usopp', 'Jinbei', 'Robin', 'Ace', 'Law', 'Shanks', 'Kurohige', 'Mihawk', 'Rayleigh']
31
  print(f"⚠️ WARNING: {CLASS_LABELS_FILE} not found. Using default labels.")
32
 
33
- # โหลด ONNX Runtime Session
34
  try:
 
35
  sess = rt.InferenceSession(ONNX_MODEL_PATH)
36
  onnx_input_name = sess.get_inputs()[0].name
37
  onnx_output_name = sess.get_outputs()[0].name
 
38
  processor = AutoImageProcessor.from_pretrained(MODEL_ID)
39
  print("ONNX model and Image Processor loaded successfully.")
40
  except Exception as e:
41
- print(f"Error loading ONNX model or Image Processor: {e}. ตรวจสอบไฟล์ model.onnx และชื่อไฟล์.")
 
 
42
  sess = None
43
 
44
 
45
- # 2. ฟังก์ชันเรียกใช้ Typhoon 2.5 API (แทนฟังก์ชันจำลองเดิม)
46
  # ----------------------------------------------------
47
  def query_typhoon_api(payload):
48
- """ส่ง Prompt ไปยัง Hugging Face Inference API ของ Typhoon 2.5"""
49
  if not HF_TOKEN:
50
  return "Error: HF_TOKEN is not set in Hugging Face Space secrets."
51
 
52
  headers = {"Authorization": f"Bearer {HF_TOKEN}"}
53
  response = requests.post(API_URL, headers=headers, json=payload)
54
 
55
- # ตรวจสอบ Response
56
  if response.status_code != 200:
57
  return f"Error {response.status_code}: API call failed. {response.text}"
58
 
59
- # ดึงผลลัพธ์
60
  try:
61
- # ผลลัพธ์จาก API จะอยู่ในรูปแบบ [ {'generated_text': '...'} ]
62
  result = response.json()[0]['generated_text']
63
- # โมเดลอาจจะตอบกลับด้วย Prompt เดิม เราจึงลบส่วน Prompt ออก
64
  return result.split(payload['inputs'])[-1].strip()
65
  except Exception as e:
66
  return f"Error processing API response: {e}"
67
 
68
 
69
- # ฐานข้อมูลข้อมูลเสริมตัวละคร (ยังคงเก็บไว้เป็นความรู้พื้นฐาน)
 
 
70
  CHARACTER_INFO = {
71
- # ... (ข้อมูลตัวละครยังคงเหมือนเดิม) ...
72
  "Ace": "โพโทกัส ดี เอส พี่ชายบุญธรรม���องลูฟี่ ผู้ใช้พลังผลปีศาจเมระ เมระ",
73
  "Luffy": "มังกี้ ดี ลูฟี่ กัปตันกลุ่มโจรสลัดหมวกฟาง ผู้ใฝ่ฝันจะเป็นราชาโจรสลัด",
74
  "Zoro": "โรโรโนอา โซโล นักดาบสามเล่มแห่งกลุ่มหมวกฟาง ผู้มีเป้าหมายเป็นนักดาบอันดับหนึ่งของโลก",
@@ -90,11 +95,11 @@ CHARACTER_INFO = {
90
 
91
  def generate_thai_response(character_name, confidence):
92
  """
93
- ฟังก์ชัน LLM ที่ใช้ API จริง
94
  """
95
  info = CHARACTER_INFO.get(character_name, "ตัวละครวันพีซ")
96
 
97
- # 1. สร้าง Prompt ที่ชัดเจนสำหรับ Typhoon 2.5
98
  prompt = (
99
  f"จากผลการวิเคราะห์ภาพ (ความมั่นใจ {confidence*100:.2f}%), ตัวละครที่ทำนายคือ '{character_name}'. "
100
  f"ตัวละครนี้คือ {info}. "
@@ -102,7 +107,7 @@ def generate_thai_response(character_name, confidence):
102
  f"และรวมข้อมูลทั้งหมดนี้เข้าด้วยกันในประโยคเดียวโดยใช้ Markdown bold สำหรับชื่อตัวละครและความมั่นใจ (XX.XX%)."
103
  )
104
 
105
- # 2. เตรียม Payload สำหรับ API
106
  payload = {
107
  "inputs": prompt,
108
  "parameters": {
@@ -111,47 +116,50 @@ def generate_thai_response(character_name, confidence):
111
  }
112
  }
113
 
114
- # 3. เรียก API และรับคำตอบ
115
  llm_response = query_typhoon_api(payload)
116
 
117
- # 4. หาก API call สำเร็จ ให้คืนคำตอบ หากล้มเหลวให้คืน Error
118
  if llm_response.startswith("Error"):
119
- return f"⚠️ LLM Error: {llm_response}"
 
 
 
120
 
121
- # Typhoon 2.5 มักจะตอบกลับเป็นประโยคที่ถูกต้องตาม Prompt
122
  return llm_response
123
 
124
- # 3. ฟังก์ชันทำนายชื่อตัวละครด้วย ONNX (รองรับ ZeroGPU)
 
125
  @spaces.GPU
126
  def predict_one_piece_character(pil_image):
127
  if pil_image is None or sess is None:
128
  return "⚠️ โมเดลไม่พร้อมใช้งาน กรุณาตรวจสอบไฟล์ ONNX และการตั้งค่า"
129
 
130
  try:
131
- # 3.1 เตรียม Input ด้วย Image Processor
132
  inputs = processor(images=pil_image, return_tensors="np")
133
  onnx_input = inputs['pixel_values'].astype(np.float32)
134
 
135
- # 3.2 ทำนายผลด้วย ONNX Runtime
136
  onnx_predictions = sess.run([onnx_output_name], {onnx_input_name: onnx_input})
137
  logits = onnx_predictions[0].squeeze()
138
 
139
- # 3.3 ประมวลผลผลลัพธ์ (Softmax และ Argmax)
140
  probabilities = softmax(logits)
141
  predicted_index = np.argmax(probabilities)
142
  predicted_character = CHARACTER_LABELS[predicted_index]
143
  confidence = probabilities[predicted_index].item()
144
 
145
- # 3.4 การรวมผล: ใช้ LLM Logic สร้างข้อความตอบกลับ
146
  final_response = generate_thai_response(predicted_character, confidence)
147
 
148
  return final_response
149
 
150
  except Exception as e:
151
- print(f"ERROR during prediction: {e}")
152
  return f"เกิดข้อผิดพลาดในการทำนาย: {e}"
153
 
154
- # 4. การสร้าง Gradio Interface
 
155
  interface = gr.Interface(
156
  fn=predict_one_piece_character,
157
  inputs=gr.Image(type="pil", label="อัปโหลดรูปภาพตัวละครวันพีซ"),
 
5
  import os
6
  import spaces
7
  import torch
8
+ # Use AutoImageProcessor for robust image preprocessing (ConvNeXt/ViT)
9
  from transformers import AutoImageProcessor
10
+ from scipy.special import softmax # Used for computing probabilities from model output
11
+ import requests # Used for making HTTP API calls (Typhoon 2.5)
12
 
13
+ # 1. ONNX MODEL AND LLM CONFIGURATION
14
  # ----------------------------------------------------
15
  ONNX_MODEL_PATH = "model.onnx"
16
  CLASS_LABELS_FILE = "class_labels.txt"
17
+ MODEL_ID = 'facebook/convnext-tiny-224' # The base model ID used for training
18
 
19
+ # **LLM CONFIGURATION (Actual API Call Setup)**
20
+ MODEL_NAME = "scb10x/typhoon2.5-qwen3-4b" # Typhoon 2.5 Model ID
21
+ # Fetch Hugging Face Token from Space Secrets
22
  HF_TOKEN = os.getenv("HF_TOKEN")
23
  API_URL = f"https://api-inference.huggingface.co/models/{MODEL_NAME}"
24
 
25
 
26
+ # Load character classes from the file created during training
27
  try:
28
  with open(CLASS_LABELS_FILE, 'r', encoding='utf-8') as f:
29
  CHARACTER_LABELS = [line.strip() for line in f.readlines()]
 
31
  CHARACTER_LABELS = ['Luffy', 'Zoro', 'Nami', 'Sanji', 'Chopper', 'Franky', 'Brook', 'Usopp', 'Jinbei', 'Robin', 'Ace', 'Law', 'Shanks', 'Kurohige', 'Mihawk', 'Rayleigh']
32
  print(f"⚠️ WARNING: {CLASS_LABELS_FILE} not found. Using default labels.")
33
 
34
+ # Load ONNX Runtime Session
35
  try:
36
+ print(f"Attempting to load ONNX model from: {ONNX_MODEL_PATH}") # <-- NEW DEBUG LINE
37
  sess = rt.InferenceSession(ONNX_MODEL_PATH)
38
  onnx_input_name = sess.get_inputs()[0].name
39
  onnx_output_name = sess.get_outputs()[0].name
40
+ # Load Image Processor (essential for correct image preparation)
41
  processor = AutoImageProcessor.from_pretrained(MODEL_ID)
42
  print("ONNX model and Image Processor loaded successfully.")
43
  except Exception as e:
44
+ # <--- NEW DEBUG BLOCK: พิมพ์ Error จริงออกมา
45
+ print(f"FATAL ERROR LOADING ONNX MODEL: {e}")
46
+ print("Please ensure model.onnx is tracked by Git LFS and is uploaded correctly.")
47
  sess = None
48
 
49
 
50
+ # 2. LLM API FUNCTION (Replaces Placeholder)
51
  # ----------------------------------------------------
52
  def query_typhoon_api(payload):
53
+ """Sends prompt to the Hugging Face Inference API of Typhoon 2.5."""
54
  if not HF_TOKEN:
55
  return "Error: HF_TOKEN is not set in Hugging Face Space secrets."
56
 
57
  headers = {"Authorization": f"Bearer {HF_TOKEN}"}
58
  response = requests.post(API_URL, headers=headers, json=payload)
59
 
60
+ # Check for non-successful status codes (e.g., 401 Unauthorized, 503 Service Unavailable)
61
  if response.status_code != 200:
62
  return f"Error {response.status_code}: API call failed. {response.text}"
63
 
 
64
  try:
65
+ # Extract the generated text from the response structure
66
  result = response.json()[0]['generated_text']
67
+ # Remove the input prompt part from the output text
68
  return result.split(payload['inputs'])[-1].strip()
69
  except Exception as e:
70
  return f"Error processing API response: {e}"
71
 
72
 
73
+ # 3. TYPHOON 2.5 LOGIC (Knowledge Base + Prompt Generation)
74
+ # ---------------------------------------------------------
75
+ # This dictionary serves as the LLM's knowledge base for the character's full name/role
76
  CHARACTER_INFO = {
 
77
  "Ace": "โพโทกัส ดี เอส พี่ชายบุญธรรม���องลูฟี่ ผู้ใช้พลังผลปีศาจเมระ เมระ",
78
  "Luffy": "มังกี้ ดี ลูฟี่ กัปตันกลุ่มโจรสลัดหมวกฟาง ผู้ใฝ่ฝันจะเป็นราชาโจรสลัด",
79
  "Zoro": "โรโรโนอา โซโล นักดาบสามเล่มแห่งกลุ่มหมวกฟาง ผู้มีเป้าหมายเป็นนักดาบอันดับหนึ่งของโลก",
 
95
 
96
  def generate_thai_response(character_name, confidence):
97
  """
98
+ Constructs a sophisticated prompt and queries the Typhoon 2.5 API.
99
  """
100
  info = CHARACTER_INFO.get(character_name, "ตัวละครวันพีซ")
101
 
102
+ # 1. Build a clear, instructional prompt for the LLM
103
  prompt = (
104
  f"จากผลการวิเคราะห์ภาพ (ความมั่นใจ {confidence*100:.2f}%), ตัวละครที่ทำนายคือ '{character_name}'. "
105
  f"ตัวละครนี้คือ {info}. "
 
107
  f"และรวมข้อมูลทั้งหมดนี้เข้าด้วยกันในประโยคเดียวโดยใช้ Markdown bold สำหรับชื่อตัวละครและความมั่นใจ (XX.XX%)."
108
  )
109
 
110
+ # 2. Prepare Payload
111
  payload = {
112
  "inputs": prompt,
113
  "parameters": {
 
116
  }
117
  }
118
 
119
+ # 3. Call the API and handle potential errors
120
  llm_response = query_typhoon_api(payload)
121
 
 
122
  if llm_response.startswith("Error"):
123
+ # Fallback to a static, simple response if API fails
124
+ thai_name = info.split(' ')[0]
125
+ return (f"⚠️ LLM API ไม่ตอบสนอง: ตัวละครคือ **{thai_name}** ({info}) "
126
+ f"[ความมั่นใจ: **{confidence*100:.2f}%**]")
127
 
 
128
  return llm_response
129
 
130
+ # 4. ONNX INFERENCE FUNCTION
131
+ # ----------------------------------------------------
132
  @spaces.GPU
133
  def predict_one_piece_character(pil_image):
134
  if pil_image is None or sess is None:
135
  return "⚠️ โมเดลไม่พร้อมใช้งาน กรุณาตรวจสอบไฟล์ ONNX และการตั้งค่า"
136
 
137
  try:
138
+ # 4.1 Preprocessing (ConvNeXt standard input)
139
  inputs = processor(images=pil_image, return_tensors="np")
140
  onnx_input = inputs['pixel_values'].astype(np.float32)
141
 
142
+ # 4.2 Run Inference
143
  onnx_predictions = sess.run([onnx_output_name], {onnx_input_name: onnx_input})
144
  logits = onnx_predictions[0].squeeze()
145
 
146
+ # 4.3 Post-processing (Softmax and Argmax)
147
  probabilities = softmax(logits)
148
  predicted_index = np.argmax(probabilities)
149
  predicted_character = CHARACTER_LABELS[predicted_index]
150
  confidence = probabilities[predicted_index].item()
151
 
152
+ # 4.4 LLM Integration
153
  final_response = generate_thai_response(predicted_character, confidence)
154
 
155
  return final_response
156
 
157
  except Exception as e:
158
+ print(f"RUNTIME ERROR: {e}")
159
  return f"เกิดข้อผิดพลาดในการทำนาย: {e}"
160
 
161
+ # 5. GRADIO INTERFACE
162
+ # ----------------------------------------------------
163
  interface = gr.Interface(
164
  fn=predict_one_piece_character,
165
  inputs=gr.Image(type="pil", label="อัปโหลดรูปภาพตัวละครวันพีซ"),