Spaces:
Running
Running
File size: 10,788 Bytes
51402aa cca3f96 51402aa cca3f96 603c45b 51402aa 603c45b cca3f96 603c45b cca3f96 603c45b 51402aa 603c45b 51402aa 603c45b cca3f96 603c45b cca3f96 603c45b cca3f96 603c45b cca3f96 603c45b 51402aa 603c45b 9a1bdab cca3f96 51402aa 603c45b cca3f96 9a1bdab cca3f96 9a1bdab cca3f96 9a1bdab cca3f96 9a1bdab cca3f96 9a1bdab cca3f96 9a1bdab cca3f96 51402aa 603c45b cca3f96 603c45b cca3f96 603c45b cca3f96 e404781 cca3f96 603c45b 02ee7d8 603c45b cca3f96 603c45b cca3f96 603c45b e404781 603c45b cca3f96 603c45b cca3f96 603c45b cca3f96 603c45b cca3f96 603c45b cca3f96 e404781 cca3f96 51402aa 603c45b e404781 cca3f96 02ee7d8 603c45b 51402aa cca3f96 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 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 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
import gradio as gr
from PIL import Image
import numpy as np
import onnxruntime as rt
import os
import spaces
import torch
from transformers import AutoImageProcessor, AutoModelForCausalLM, AutoTokenizer # NEW: LLM Imports
from scipy.special import softmax
# 1. MODEL CONFIGURATION AND LOADING
# ----------------------------------------------------
# 1.1 ONNX Model (Image Classifier) Configuration
ONNX_MODEL_PATH = "model.onnx"
CLASS_LABELS_FILE = "class_labels.txt"
MODEL_ID = 'facebook/convnext-tiny-224'
# 1.2 LLM Configuration (Loaded Locally)
LLM_MODEL_NAME = "scb10x/typhoon2.5-qwen3-4b"
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"LLM Device: {device}")
# Load ONNX Runtime Session and LLM
try:
# 1. Load ONNX Runtime (ConvNeXt)
if not os.path.exists(ONNX_MODEL_PATH):
raise FileNotFoundError(f"ONNX Model file not found at: {ONNX_MODEL_PATH}")
print(f"Attempting to load ONNX model from: {ONNX_MODEL_PATH}")
sess = rt.InferenceSession(ONNX_MODEL_PATH)
onnx_input_name = sess.get_inputs()[0].name
onnx_output_name = sess.get_outputs()[0].name
processor = AutoImageProcessor.from_pretrained(MODEL_ID)
print("ONNX model and Image Processor loaded successfully.")
# 2. Load LLM (Typhoon 2.5) Locally
print(f"Attempting to load LLM model: {LLM_MODEL_NAME} onto {device}...")
llm_tokenizer = AutoTokenizer.from_pretrained(LLM_MODEL_NAME, trust_remote_code=True)
llm_model = AutoModelForCausalLM.from_pretrained(
LLM_MODEL_NAME,
trust_remote_code=True,
torch_dtype=torch.float16, # Use float16 for efficiency
low_cpu_mem_usage=True,
)
llm_model.to(device)
print("Typhoon 2.5 LLM loaded successfully.")
except Exception as e:
print(f"FATAL ERROR LOADING MODELS: {e}")
print("Please ensure GPU is available and files are uploaded correctly (including model.onnx.data).")
sess = None
llm_model = None
llm_tokenizer = None
# Load character classes
try:
with open(CLASS_LABELS_FILE, 'r', encoding='utf-8') as f:
CHARACTER_LABELS = [line.strip() for line in f.readlines()]
except FileNotFoundError:
CHARACTER_LABELS = [
'Ace',
'Akainu',
'Brook',
'Chopper',
'Crocodile',
'Franky',
'Jinbei',
'Kurohige',
'Law',
'Luffy',
'Mihawk',
'Nami',
'Rayleigh',
'Robin',
'Sanji',
'Shanks',
'Usopp',
'Zoro'
]
# 2. LLM GENERATION FUNCTION (Local Inference)
# ----------------------------------------------------
# ฐานข้อมูลข้อมูลเสริมตัวละคร
CHARACTER_INFO = {
"Ace": "โพโทกัส ดี เอส พี่ชายบุญธรรมของลูฟี่ ผู้ใช้พลังผลปีศาจเมระ เมระ",
"Akainu": "อาคาอินุ (ซาคาสุกิ) พลเรือเอกกองทัพเรือ ผู้ใช้พลังผลปีศาจมากุ มากุ (แม็กม่า)",
"Brook": "บรู๊ค นักดนตรีผู้ใช้ดาบและมีชีวิตเป็นโครงกระดูก ผู้รักการร้องเพลงและมุกตลก",
"Chopper": "โทนี่ โทนี่ ช็อปเปอร์ หมอประจำเรือ ผู้มีใจรักเพื่อนและอ่อนไหวที่สุดในกลุ่ม",
"Crocodile": "เซอร์ คร็อกโคไดล์ อดีตเจ็ดเทพโจรสลัด ผู้ใช้พลังผลปีศาจซึนะ ซึนะ (ทราย)",
"Franky": "แฟรงกี้ ช่างต่อเรือผู้สร้างเรือเธาซันด์ ซันนี่ มีพลังไซบอร์กสุดแกร่ง",
"Jinbei": "จินเบ อดีตเจ็ดเทพโจรสลัด และเป็นมนุษย์เงือกผู้เชี่ยวชาญคาราเต้เงือก",
"Kurohige": "มาร์แชล ดี ทีช หรือหนวดดำ ผู้เป็นหนึ่งในสี่จักรพรรดิ์คนปัจจุบัน",
"Law": "ทราฟาลการ์ ลอว์ กัปตันกลุ่มโจรสลัดฮาร์ท ผู้ใช้พลังผลโอเปะ โอเปะ",
"Luffy": "มังกี้ ดี ลูฟี่ กัปตันกลุ่มโจรสลัดหมวกฟาง ผู้ใฝ่ฝันจะเป็นราชาโจรสลัด",
"Mihawk": "จูราคิล มิฮอว์ค สุดยอดนักดาบผู้เป็นที่มาของฉายา 'ตาเหยี่ยว'",
"Nami": "นามิ นักเดินเรือสาวแห่งกลุ่มโจรสลัดหมวกฟาง และเป็นนักทำแผนที่มือฉมัง",
"Rayleigh": "ซิลเวอร์ส เรย์ลี่ อดีตมือขวาของราชาโจรสลัด โกลด์ ดี. โรเจอร์",
"Robin": "นิโค โรบิน นักโบราณคดี ผู้เดียวที่อ่านโพเนกลีฟได้",
"Sanji": "ซันจิ กุ๊กแห่งกลุ่มโจรสลัดหมวกฟาง และเป็นสุดยอดนักสู้ที่ใช้เท้าในการต่อสู้",
"Shanks": "แชงค์ส หนึ่งในสี่จักรพรรดิ์ ผู้มอบหมวกฟางให้กับลูฟี่",
"Usopp": "อุซป พลซุ่มยิงและนักประดิษฐ์ ผู้มีความฝันเป็นนักรบผู้กล้าหาญแห่งท้องทะเล",
"Zoro": "โรโรโนอา โซโล นักดาบสามเล่มแห่งกลุ่มหมวกฟาง ผู้มีเป้าหมายเป็นนักดาบอันดับหนึ่งของโลก",
}
def generate_typhoon_response(character_name, confidence):
"""
ฟังก์ชัน LLM ที่ใช้ Local Inference ภายใน Space
"""
if llm_model is None:
return (f"❌ LLM ไม่พร้อมใช้งาน: ตัวละครคือ **{character_name}** "
f"[ความมั่นใจ: **{confidence*100:.2f}%**]")
info = CHARACTER_INFO.get(character_name, "ตัวละครวันพีซ")
# 1. Build a clear, instructional prompt for the LLM
prompt = (
f"จากผลการวิเคราะห์ภาพ (ความมั่นใจ {confidence*100:.2f}%), ตัวละครที่ทำนายคือ '{character_name}'. "
f"ตัวละครนี้คือ {info}. "
f"กรุณาสร้างข้อความตอบกลับที่เป็นมิตรและเป็นภาษาไทย โดยขึ้นต้นด้วย 'ยืนยันผลการทำนาย!' "
f"และรวมข้อมูลทั้งหมดนี้เข้าด้วยกันในประโยคเดียวโดยใช้ Markdown bold สำหรับชื่อตัวละครและความมั่นใจ (XX.XX%)."
)
# 2. Generate text using the local LLM
messages = [{"role": "user", "content": prompt}]
input_ids = llm_tokenizer.apply_chat_template(
messages, add_generation_prompt=True, return_tensors="pt"
).to(device)
output_ids = llm_model.generate(
input_ids,
max_new_tokens=256,
temperature=0.7,
do_sample=True,
pad_token_id=llm_tokenizer.eos_token_id,
)
# 3. Decode response
response = llm_tokenizer.decode(output_ids[0], skip_special_tokens=True)
# Remove the input prompt from the response
response_text = response.split(prompt)[-1].strip()
return response_text
# 3. ONNX INFERENCE FUNCTION
# ----------------------------------------------------
# เราจะใช้ @spaces.GPU ตรงนี้เพื่อให้ LLM (ซึ่งอยู่ในฟังก์ชันที่ถูกเรียก) รันบน GPU ด้วย
@spaces.GPU # ใช้ GPU สำหรับการรัน LLM
def predict_one_piece_character(pil_image):
if pil_image is None or sess is None:
return "⚠️ โมเดลไม่พร้อมใช้งาน กรุณาตรวจสอบไฟล์ ONNX และการตั้งค่า"
try:
# 3.1 Preprocessing (ConvNeXt standard input)
inputs = processor(images=pil_image, return_tensors="np")
onnx_input = inputs['pixel_values'].astype(np.float32)
# 3.2 Run Inference (ConvNeXt ONNX)
onnx_predictions = sess.run([onnx_output_name], {onnx_input_name: onnx_input})
logits = onnx_predictions[0].squeeze()
# 3.3 Post-processing (Softmax and Argmax)
probabilities = softmax(logits)
predicted_index = np.argmax(probabilities)
predicted_character = CHARACTER_LABELS[predicted_index]
confidence = probabilities[predicted_index].item()
# 3.4 LLM Integration (Local Generation)
final_response = generate_typhoon_response(predicted_character, confidence)
return final_response
except Exception as e:
print(f"RUNTIME ERROR: {e}")
return f"เกิดข้อผิดพลาดในการทำนาย: {e}"
# 4. GRADIO INTERFACE
# ----------------------------------------------------
interface = gr.Interface(
fn=predict_one_piece_character,
inputs=gr.Image(type="pil", label="อัปโหลดรูปภาพตัวละครวันพีซ"),
outputs=gr.Textbox(label="ผลการทำนายชื่อตัวละคร (Typhoon 2.5 Local)", lines=5),
title="🏴☠️ One Piece Classifier (ConvNeXt ONNX + Typhoon 2.5 Local)",
description="แอปพลิเคชันจำแนกตัวละครวันพีซโดยรัน LLM ภายใน Space"
)
if __name__ == "__main__":
interface.launch(inbrowser=True) |