K7Ploypailin commited on
Commit
cca3f96
·
1 Parent(s): 897707d

Add application file

Browse files
Files changed (1) hide show
  1. app.py +148 -38
app.py CHANGED
@@ -1,54 +1,164 @@
1
- import os
2
- import torch
3
  import gradio as gr
4
- from transformers import AutoTokenizer, AutoModelForCausalLM
 
 
 
5
  import spaces
 
 
 
 
6
 
7
- model_name = "scb10x/typhoon2.5-qwen3-4b"
8
- token = os.getenv("HF_TOKEN")
 
 
 
9
 
10
- tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True)
11
- model = AutoModelForCausalLM.from_pretrained(
12
- model_name,
13
- trust_remote_code=True,
14
- torch_dtype=torch.float16,
15
- low_cpu_mem_usage=True,
16
- )
17
 
18
- model.to("cuda" if torch.cuda.is_available() else "cpu")
19
 
20
- def generate_chat(message):
21
- messages = [{"role": "user", "content": message}]
 
 
 
 
 
22
 
23
- input_ids = tokenizer.apply_chat_template(
24
- messages,
25
- add_generation_prompt=True,
26
- return_tensors="pt"
27
- ).to(model.device)
 
 
 
 
 
28
 
29
- output_ids = model.generate(
30
- input_ids,
31
- max_new_tokens=256,
32
- temperature=0.7,
33
- do_sample=True,
34
- top_p=0.9,
35
- pad_token_id=tokenizer.eos_token_id,
36
- )
37
 
38
- response = tokenizer.decode(output_ids[0], skip_special_tokens=True)
39
- response_text = response.split(message)[-1].strip()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
 
41
- return response_text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  @spaces.GPU
44
- def predict(message, history=None):
45
- response = generate_chat(message)
46
- return response
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
 
48
- chat_ui = gr.ChatInterface(
49
- fn=predict,
50
- title="Typhoon 2.5 ZeroGPU",
 
 
 
 
51
  )
52
 
53
  if __name__ == "__main__":
54
- chat_ui.launch()
 
 
 
1
  import gradio as gr
2
+ from PIL import Image
3
+ import numpy as np
4
+ 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()]
29
+ 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": "โรโรโนอา โซโล นักดาบสามเล่มแห่ง���ลุ่มหมวกฟาง ผู้มีเป้าหมายเป็นนักดาบอันดับหนึ่งของโลก",
75
+ "Nami": "นามิ นักเดินเรือสาวแห่งกลุ่มหมวกฟาง และเป็นนักทำแผนที่มือฉมัง",
76
+ "Sanji": "ซันจิ กุ๊กแห่งกลุ่มโจรสลัดหมวกฟาง และเป็นสุดยอดนักสู้ที่ใช้เท้าในการต่อสู้",
77
+ "Chopper": "โทนี่ โทนี่ ช็อปเปอร์ หมอประจำเรือ ผู้มีใจรักเพื่อนและอ่อนไหวที่สุดในกลุ่ม",
78
+ "Robin": "นิโค โรบิน นักโบราณคดี ผู้เดียวที่อ่านโพเนกลีฟได้",
79
+ "Usopp": "อุซป พลซุ่มยิงและนักประดิษฐ์ ผู้มีความฝันเป็นนักรบผู้กล้าหาญแห่งท้องทะเล",
80
+ "Franky": "แฟรงกี้ ช่างต่อเรือผู้สร้างเรือเธาซันด์ ซันนี่ มีพลังไซบอร์กสุดแกร่ง",
81
+ "Brook": "บรู๊ค นักดนตรีผู้ใช้ดาบและมีชีวิตเป็นโครงกระดูก ผู้รักการร้องเพลงและมุกตลก",
82
+ "Jinbei": "จินเบ อดีตเจ็ดเทพโจรสลัด และเป็นมนุษย์เงือกผู้เชี่ยวชาญคาราเต้เงือก",
83
+ "Law": "ทราฟาลการ์ ลอว์ กัปตันกลุ่มโจรสลัดฮาร์ท ผู้ใช้พลังผลโอเปะ โอเปะ",
84
+ "Shanks": "แชงค์ส หนึ่งในสี่จักรพรรดิ์ ผู้มอบหมวกฟางให้กับลูฟี่",
85
+ "Mihawk": "จูราคิล มิฮอว์ค สุดยอดนักดาบผู้เป็นที่มาของฉายา 'ตาเหยี่ยว'",
86
+ "Kurohige": "มาร์แชล ดี ทีช หรือหนวดดำ ผู้เป็นหนึ่งในสี่จักรพรรดิ์คนปัจจุบัน",
87
+ "Rayleigh": "ซิลเวอร์ส เรย์ลี่ อดีตมือขวาของราชาโจรสลัด โกลด์ ดี. โรเจอร์",
88
+ }
89
 
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}. "
101
+ f"กรุณาสร้างข้อความตอบกลับที่เป็นมิตรและเป็นภาษาไทย โดยขึ้นต้นด้วย 'ยืนยันผลการทำนาย!' "
102
+ f"และรวมข้อมูลทั้งหมดนี้เข้าด้วยกันในประโยคเดียวโดยใช้ Markdown bold สำหรับชื่อตัวละครและความมั่นใจ (XX.XX%)."
103
+ )
104
+
105
+ # 2. เตรียม Payload สำหรับ API
106
+ payload = {
107
+ "inputs": prompt,
108
+ "parameters": {
109
+ "max_new_tokens": 100,
110
+ "return_full_text": False
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="อัปโหลดรูปภาพตัวละครวันพีซ"),
158
+ outputs=gr.Textbox(label="ผลการทำนายชื่อตัวละคร (Powered by Typhoon 2.5)"),
159
+ title="🏴‍☠️ One Piece Classifier (ConvNeXt ONNX + Typhoon 2.5)",
160
+ description="อัปโหลดภาพตัวละครวันพีซ เพื่อให้ AI ทำนายชื่อตัวละครพร้อมสร้างข้อความตอบกลับที่น่าประทับใจ"
161
  )
162
 
163
  if __name__ == "__main__":
164
+ interface.launch(inbrowser=True)