import os import shutil import zipfile import gradio as gr import torch from transformers import AutoModelForSequenceClassification, AutoTokenizer ZIP_PATH = "model.zip" EXTRACT_DIR = "./model" MODEL_DIR = os.path.join(EXTRACT_DIR, "final_model") DEVICE = torch.device("cpu") def prepare_model_files(): config_path = os.path.join(MODEL_DIR, "config.json") weights_path = os.path.join(MODEL_DIR, "model.safetensors") if os.path.isfile(config_path) and os.path.isfile(weights_path): print("模型已經解壓完成。", flush=True) return if not os.path.isfile(ZIP_PATH): raise FileNotFoundError("找不到 model.zip") if os.path.exists(EXTRACT_DIR): shutil.rmtree(EXTRACT_DIR) os.makedirs(EXTRACT_DIR, exist_ok=True) print("正在解壓模型……", flush=True) with zipfile.ZipFile(ZIP_PATH, "r") as zip_ref: zip_ref.extractall(EXTRACT_DIR) print("模型解壓完成。", flush=True) if not os.path.isfile(config_path): raise FileNotFoundError( f"找不到 {config_path},請確認 ZIP 裡面有 final_model 資料夾。" ) if not os.path.isfile(weights_path): raise FileNotFoundError( f"找不到 {weights_path},請確認 model.safetensors 已包含在 ZIP 中。" ) prepare_model_files() print("正在載入 tokenizer……", flush=True) tokenizer = AutoTokenizer.from_pretrained( MODEL_DIR, local_files_only=True ) print("正在載入 BERT 模型到 CPU……", flush=True) model = AutoModelForSequenceClassification.from_pretrained( MODEL_DIR, local_files_only=True ) model.to(DEVICE) model.eval() print("模型載入成功。", flush=True) LABEL_MAP = { 0: "詐騙訊息", 1: "真實訊息" } def predict(text: str) -> str: text = text.strip() if not text: return "請先輸入要判斷的訊息。" inputs = tokenizer( text, return_tensors="pt", truncation=True, padding=True, max_length=256 ) inputs = { key: value.to(DEVICE) for key, value in inputs.items() } with torch.inference_mode(): outputs = model(**inputs) probabilities = torch.softmax(outputs.logits, dim=-1)[0] probabilities = probabilities.cpu() pred_id = int(torch.argmax(probabilities).item()) confidence = float(probabilities[pred_id].item() * 100) scam_probability = float(probabilities[0].item() * 100) real_probability = float(probabilities[1].item() * 100) pred_label = LABEL_MAP.get( pred_id, f"未知類別 {pred_id}" ) if pred_id == 0: advice = ( "請勿立即匯款、提供銀行帳號、密碼、信用卡資料或簡訊驗證碼。" "建議透過官方管道查證,必要時撥打 165 反詐騙專線。" ) else: advice = ( "模型判斷較接近真實訊息,但模型仍可能誤判。" "若內容涉及金錢、帳戶或個人資料,仍應再次查證。" ) return ( f"🔍 判斷結果:{pred_label}\n\n" f"📊 模型信心度:{confidence:.2f}%\n\n" f"🚨 詐騙機率:{scam_probability:.2f}%\n" f"✅ 真實機率:{real_probability:.2f}%\n\n" f"⚠️ 提醒:{advice}" ) demo = gr.Interface( fn=predict, inputs=gr.Textbox( lines=6, label="訊息內容", placeholder="請輸入要判斷的新聞、貼文或可疑訊息……" ), outputs=gr.Textbox( label="辨識結果", lines=8 ), title="LINE Bot BERT 詐騙/真實訊息辨識 API", description=( "輸入文字後,系統會判斷內容較接近詐騙訊息或真實訊息。" "建議輸入較完整的內容,文字過短可能降低辨識效果。" ), api_name="predict" ) if __name__ == "__main__": demo.launch( server_name="0.0.0.0", server_port=7860, ssr_mode=False, show_error=True )