File size: 5,079 Bytes
3a8e2ae
 
 
 
 
 
 
 
 
 
 
bf6ec13
3a8e2ae
 
 
 
 
8a4f04c
dc37c6d
 
3a8e2ae
 
 
 
 
 
 
76c71df
 
 
 
 
 
 
 
 
 
 
3a8e2ae
 
8a4f04c
dc37c6d
3a8e2ae
 
 
 
 
 
 
8a4f04c
3a8e2ae
 
 
 
 
 
bf6ec13
8a4f04c
 
 
 
 
3a8e2ae
8a4f04c
 
dc37c6d
8a4f04c
3a8e2ae
 
 
 
 
 
 
 
 
 
 
 
 
 
8a4f04c
3a8e2ae
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bf6ec13
 
 
 
 
3a8e2ae
 
 
 
 
 
 
 
 
 
 
 
 
bf6ec13
 
 
 
3a8e2ae
 
 
 
 
 
 
8a4f04c
 
 
 
3a8e2ae
 
 
bf6ec13
a0c838b
3a8e2ae
a0c838b
 
 
 
3a8e2ae
a0c838b
 
3a8e2ae
a0c838b
bf6ec13
 
3a8e2ae
 
bf6ec13
 
 
3a8e2ae
8a4f04c
d991985
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
import os
import re
import json
import torch
import torch.nn.functional as F
from flask import Flask, request, jsonify
from transformers import AutoTokenizer, AutoModelForSequenceClassification
from pyvi import ViTokenizer
from PIL import Image
from torchvision import transforms
import timm
import requests
from flask_cors import CORS

# =====================
# CONFIG
# =====================
os.environ["TRANSFORMERS_CACHE"] = "/tmp/hf_cache"  # tránh vượt storage limit
TEXT_MODEL_REPO = "phuongsuga/PBL6_AI_Model_Text_Image"
IMAGE_MODEL_REPO = "phuongsuga/PBL6_AI_Model_Image"
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
THRESHOLD = 0.65

# =====================
# LOAD TEXT MODEL
# =====================
print("🔹 Downloading text model...")

tokenizer = AutoTokenizer.from_pretrained(
    TEXT_MODEL_REPO,
    subfolder="text_model",
    use_fast=False
)

text_model = AutoModelForSequenceClassification.from_pretrained(
    TEXT_MODEL_REPO,
    subfolder="text_model/checkpoint-3390"
)
text_model.to(DEVICE).eval()

# load label2id.json
label_url = f"https://huggingface.co/{TEXT_MODEL_REPO}/resolve/main/text_model/label2id.json"
label2id = requests.get(label_url).json()
id2label_text = {i: l for l, i in label2id.items()}

# =====================
# LOAD IMAGE MODEL
# =====================
print("🔹 Downloading image model...")

class_names = ["an_toan", "bao_luc", "khieu_dam_doi_truy", "nhay_cam_chinh_tri"]

def build_model(num_classes=4):
    return timm.create_model("efficientnet_b3", pretrained=False, num_classes=num_classes)

image_model = build_model()

# tải model tạm trong /tmp để không chiếm storage
image_model_path = "/tmp/efficientnet_b3.pth"
if not os.path.exists(image_model_path):
    url = f"https://huggingface.co/{IMAGE_MODEL_REPO}/resolve/main/image_model/efficientnet_b3.pth"
    torch.hub.download_url_to_file(url, image_model_path)

image_model.load_state_dict(torch.load(image_model_path, map_location=DEVICE))
image_model.to(DEVICE).eval()

# chuẩn hóa ảnh
val_transforms = transforms.Compose([
    transforms.Resize((224, 224)),
    transforms.ToTensor(),
    transforms.Normalize([0.485, 0.456, 0.406],
                         [0.229, 0.224, 0.225])
])

# =====================
# UTILS
# =====================
def seg_pyvi(text: str) -> str:
    try:
        seg = ViTokenizer.tokenize(text)
        seg = seg.replace(" ", "_")
    except Exception:
        seg = text
    return seg

def split_sentences(text: str):
    sents = re.split(r'(?<=[.!?])\s+|\n+', text.strip())
    return [s for s in sents if s.strip()]

def predict_text(text: str):
    sentences = split_sentences(text)
    results = []
    for sent in sentences:
        seg = seg_pyvi(sent)
        inputs = tokenizer(seg, truncation=True, padding="max_length",
                           max_length=128, return_tensors="pt").to(DEVICE)
        with torch.no_grad():
            logits = text_model(**inputs).logits
            probs = F.softmax(logits, dim=-1).cpu().numpy()[0]
        pred_id = probs.argmax()
        label = id2label_text[int(pred_id)]
        prob = float(probs[pred_id])
        if label != "an_toan" and prob >= THRESHOLD:
            results.append({"sentence": sent, "label": label, "confidence": prob})
        else:
            results.append({
                "sentence": sent,
                "label": "an_toan",
                "confidence": float(probs[label2id["an_toan"]])
            })
    return results

def predict_image(pil_image: Image.Image):
    img = val_transforms(pil_image).unsqueeze(0).to(DEVICE)
    with torch.no_grad():
        outputs = image_model(img)
        probs = F.softmax(outputs, dim=1)[0].cpu().numpy()
    pred_id = probs.argmax()
    label = class_names[pred_id]
    prob = float(probs[pred_id])
    if label != "an_toan" and prob >= THRESHOLD:
        return {"label": label, "confidence": prob}
    else:
        return {
            "label": "an_toan",
            "confidence": float(probs[class_names.index("an_toan")])
        }

# =====================
# FLASK APP
# =====================
app = Flask(__name__)
CORS(app)

@app.route("/")
def home():
    return jsonify({"message": "✅ AI moderation API is running!"})

@app.route("/analyze", methods=["POST"])
def analyze():
    result = {"text_result": [], "image_result": []}

    # 🧠 PHÂN TÍCH TEXT
    if "content" in request.form:
        text_input = request.form["content"]
        result["text_result"] = predict_text(text_input)

    # 🧠 PHÂN TÍCH NHIỀU ẢNH
    if "image" in request.files:
        image_files = request.files.getlist("image")
        for image_file in image_files:
            image = Image.open(image_file.stream).convert("RGB")
            image_result = predict_image(image)
            result["image_result"].append(image_result)

    return jsonify(result)

# =====================
# RUN APP
# =====================
if __name__ == "__main__":
    port = int(os.environ.get("PORT", 7860))  # Hugging Face Space truyền PORT vào
    app.run(host="0.0.0.0", port=port)