phuongsuga commited on
Commit
d930fec
·
verified ·
1 Parent(s): f12a73d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +109 -57
app.py CHANGED
@@ -10,67 +10,108 @@ from PIL import Image
10
  from torchvision import transforms
11
  import timm
12
  from flask_cors import CORS
 
13
 
14
  # =====================
15
  # CONFIG
16
  # =====================
17
- TEXT_MODEL_DIR = "./text_model" # thư mục chứa model PhoBERT đã train
18
- TEXT_CKPT = "./text_model/checkpoint-3390"
19
- IMAGE_MODEL_PATH = "./image_model/efficientnet_b3.pth"
20
  DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
 
 
 
 
21
 
22
  # =====================
23
  # LOAD TEXT MODEL
24
  # =====================
25
- with open(os.path.join(TEXT_MODEL_DIR, "label2id.json"), "r", encoding="utf-8") as f:
26
- label2id = json.load(f)
27
- id2label_text = {i: l for l, i in label2id.items()}
28
-
29
- tokenizer = AutoTokenizer.from_pretrained(TEXT_MODEL_DIR, use_fast=False)
30
- text_model = AutoModelForSequenceClassification.from_pretrained(TEXT_CKPT)
31
- text_model.to(DEVICE)
32
- text_model.eval()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
 
34
  # =====================
35
  # LOAD IMAGE MODEL
36
  # =====================
 
37
  class_names = ["an_toan", "bao_luc", "khieu_dam_doi_truy", "nhay_cam_chinh_tri"]
38
 
39
  def build_model(num_classes=4):
40
  return timm.create_model("efficientnet_b3", pretrained=False, num_classes=num_classes)
41
 
42
  image_model = build_model()
43
- image_model.load_state_dict(torch.load(IMAGE_MODEL_PATH, map_location=DEVICE))
44
- image_model.to(DEVICE)
45
- image_model.eval()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
 
47
  val_transforms = transforms.Compose([
48
  transforms.Resize((224, 224)),
49
  transforms.ToTensor(),
50
- transforms.Normalize([0.485, 0.456, 0.406],
51
- [0.229, 0.224, 0.225])
52
  ])
53
 
54
  # =====================
55
  # UTILS
56
  # =====================
57
- THRESHOLD = 0.65
58
-
59
  def seg_pyvi(text: str) -> str:
60
  try:
61
- seg = ViTokenizer.tokenize(text)
62
- seg = seg.replace(" ", "_")
63
- except:
64
- seg = text
65
- return seg
66
 
67
  def split_sentences(text: str):
68
- sents = re.split(r'(?<=[.!?])\s+|\n+', text.strip())
69
- return [s for s in sents if s.strip()]
70
 
71
  def predict_text(text: str):
72
  sentences = split_sentences(text)
73
  results = []
 
 
 
 
 
74
  for sent in sentences:
75
  seg = seg_pyvi(sent)
76
  inputs = tokenizer(seg, truncation=True, padding="max_length",
@@ -78,39 +119,43 @@ def predict_text(text: str):
78
  with torch.no_grad():
79
  logits = text_model(**inputs).logits
80
  probs = F.softmax(logits, dim=-1).cpu().numpy()[0]
81
-
82
  pred_id = probs.argmax()
83
- label = id2label_text[int(pred_id)]
84
  prob = float(probs[pred_id])
85
 
86
- if label != "an_toan" and prob >= THRESHOLD:
87
  results.append({"sentence": sent, "label": label, "confidence": prob})
88
  else:
89
- results.append({"sentence": sent, "label": "an_toan", "confidence": float(probs[label2id["an_toan"]])})
 
 
 
 
 
90
  return results
91
 
92
  def predict_image(pil_image: Image.Image):
93
  img = val_transforms(pil_image).unsqueeze(0).to(DEVICE)
94
  with torch.no_grad():
95
  outputs = image_model(img)
96
- probs = F.softmax(outputs, dim=1)[0]
97
- probs = probs.cpu().numpy()
98
-
99
  pred_id = probs.argmax()
100
  label = class_names[pred_id]
101
  prob = float(probs[pred_id])
102
-
103
  if label != "an_toan" and prob >= THRESHOLD:
104
  return {"label": label, "confidence": prob}
105
  else:
106
- return {"label": "an_toan", "confidence": float(probs[class_names.index("an_toan")])}
 
 
 
107
 
108
  # =====================
109
- # FLASK APP
110
  # =====================
111
- app = Flask(__name__)
112
- CORS(app)
113
-
114
  @app.route("/")
115
  def home():
116
  return jsonify({"message": "✅ AI moderation API is running!"})
@@ -118,25 +163,32 @@ def home():
118
  @app.route("/analyze", methods=["POST"])
119
  def analyze():
120
  result = {"text_result": [], "image_result": []}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
 
122
- # 🧠 PHÂN TÍCH TEXT
123
- if "content" in request.form:
124
- text_input = request.form["content"]
125
- result["text_result"] = predict_text(text_input)
126
-
127
- # 🧠 PHÂN TÍCH NHIỀU ẢNH
128
- if "image" in request.files:
129
- image_files = request.files.getlist("image")
130
- for image_file in image_files:
131
- image = Image.open(image_file.stream).convert("RGB")
132
- image_result = predict_image(image)
133
- result["image_result"].append(image_result)
134
-
135
- return jsonify(result)
136
-
137
- # =====================
138
- # RUN APP
139
- # =====================
140
  if __name__ == "__main__":
141
- port = int(os.environ.get("PORT", 7860)) # Hugging Face Space truyền PORT vào
142
  app.run(host="0.0.0.0", port=port)
 
10
  from torchvision import transforms
11
  import timm
12
  from flask_cors import CORS
13
+ from huggingface_hub import hf_hub_download # <--- Dùng cái này để tải file config an toàn
14
 
15
  # =====================
16
  # CONFIG
17
  # =====================
18
+ os.environ["TRANSFORMERS_CACHE"] = "/tmp/hf_cache"
19
+ TEXT_MODEL_REPO = "phuongsuga/PBL6_AI_Model_Text_Image"
20
+ IMAGE_MODEL_REPO = "phuongsuga/PBL6_AI_Model_Image"
21
  DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
22
+ THRESHOLD = 0.65
23
+
24
+ app = Flask(__name__)
25
+ CORS(app)
26
 
27
  # =====================
28
  # LOAD TEXT MODEL
29
  # =====================
30
+ print("🔹 Downloading text model...")
31
+
32
+ # 1. Tải tokenizer
33
+ tokenizer = AutoTokenizer.from_pretrained(
34
+ TEXT_MODEL_REPO,
35
+ subfolder="text_model",
36
+ use_fast=False
37
+ )
38
+
39
+ # 2. Tải model
40
+ text_model = AutoModelForSequenceClassification.from_pretrained(
41
+ TEXT_MODEL_REPO,
42
+ subfolder="text_model/checkpoint-3390"
43
+ )
44
+ text_model.to(DEVICE).eval()
45
+
46
+ # 3. Tải label2id.json AN TOÀN bằng hf_hub_download
47
+ try:
48
+ print("🔹 Loading label2id.json...")
49
+ label2id_path = hf_hub_download(repo_id=TEXT_MODEL_REPO, filename="text_model/label2id.json")
50
+ with open(label2id_path, "r", encoding="utf-8") as f:
51
+ label2id = json.load(f)
52
+ id2label_text = {int(v): k for k, v in label2id.items()} # Đảm bảo key là int
53
+ print("✅ Loaded label2id successfully.")
54
+ except Exception as e:
55
+ print(f"⚠️ Warning: Could not load label2id.json from Hub. Error: {e}")
56
+ # Fallback nếu file không tồn tại: Lấy từ config của model
57
+ print("🔹 Attempting to use model config instead...")
58
+ id2label_text = text_model.config.id2label
59
+ label2id = text_model.config.label2id
60
 
61
  # =====================
62
  # LOAD IMAGE MODEL
63
  # =====================
64
+ print("🔹 Downloading image model...")
65
  class_names = ["an_toan", "bao_luc", "khieu_dam_doi_truy", "nhay_cam_chinh_tri"]
66
 
67
  def build_model(num_classes=4):
68
  return timm.create_model("efficientnet_b3", pretrained=False, num_classes=num_classes)
69
 
70
  image_model = build_model()
71
+ image_model_path = "/tmp/efficientnet_b3.pth"
72
+
73
+ # Tải weight ảnh nếu chưa có
74
+ if not os.path.exists(image_model_path):
75
+ try:
76
+ url = f"https://huggingface.co/{IMAGE_MODEL_REPO}/resolve/main/image_model/efficientnet_b3.pth"
77
+ torch.hub.download_url_to_file(url, image_model_path)
78
+ except Exception as e:
79
+ print(f"❌ Error downloading image model: {e}")
80
+
81
+ # Load state dict
82
+ if os.path.exists(image_model_path):
83
+ state_dict = torch.load(image_model_path, map_location=DEVICE)
84
+ image_model.load_state_dict(state_dict)
85
+ image_model.to(DEVICE).eval()
86
+ else:
87
+ print("❌ Image model weight file not found!")
88
 
89
  val_transforms = transforms.Compose([
90
  transforms.Resize((224, 224)),
91
  transforms.ToTensor(),
92
+ transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
 
93
  ])
94
 
95
  # =====================
96
  # UTILS
97
  # =====================
 
 
98
  def seg_pyvi(text: str) -> str:
99
  try:
100
+ return ViTokenizer.tokenize(text).replace(" ", "_")
101
+ except Exception:
102
+ return text
 
 
103
 
104
  def split_sentences(text: str):
105
+ return [s for s in re.split(r'(?<=[.!?])\s+|\n+', text.strip()) if s.strip()]
 
106
 
107
  def predict_text(text: str):
108
  sentences = split_sentences(text)
109
  results = []
110
+
111
+ # Kiểm tra xem label "an_toan" có trong dict không, nếu không lấy key đầu tiên làm safe label
112
+ safe_label = "an_toan"
113
+ safe_id = label2id.get(safe_label, 0)
114
+
115
  for sent in sentences:
116
  seg = seg_pyvi(sent)
117
  inputs = tokenizer(seg, truncation=True, padding="max_length",
 
119
  with torch.no_grad():
120
  logits = text_model(**inputs).logits
121
  probs = F.softmax(logits, dim=-1).cpu().numpy()[0]
122
+
123
  pred_id = probs.argmax()
124
+ label = id2label_text.get(int(pred_id), "Unknown")
125
  prob = float(probs[pred_id])
126
 
127
+ if label != safe_label and prob >= THRESHOLD:
128
  results.append({"sentence": sent, "label": label, "confidence": prob})
129
  else:
130
+ # Chỉ append nếu bạn muốn log cả câu an toàn
131
+ results.append({
132
+ "sentence": sent,
133
+ "label": safe_label,
134
+ "confidence": float(probs[safe_id]) if safe_id < len(probs) else 0.0
135
+ })
136
  return results
137
 
138
  def predict_image(pil_image: Image.Image):
139
  img = val_transforms(pil_image).unsqueeze(0).to(DEVICE)
140
  with torch.no_grad():
141
  outputs = image_model(img)
142
+ probs = F.softmax(outputs, dim=1)[0].cpu().numpy()
143
+
 
144
  pred_id = probs.argmax()
145
  label = class_names[pred_id]
146
  prob = float(probs[pred_id])
147
+
148
  if label != "an_toan" and prob >= THRESHOLD:
149
  return {"label": label, "confidence": prob}
150
  else:
151
+ return {
152
+ "label": "an_toan",
153
+ "confidence": float(probs[class_names.index("an_toan")])
154
+ }
155
 
156
  # =====================
157
+ # ROUTES
158
  # =====================
 
 
 
159
  @app.route("/")
160
  def home():
161
  return jsonify({"message": "✅ AI moderation API is running!"})
 
163
  @app.route("/analyze", methods=["POST"])
164
  def analyze():
165
  result = {"text_result": [], "image_result": []}
166
+
167
+ try:
168
+ # Xử lý Text
169
+ if "content" in request.form:
170
+ text_input = request.form["content"]
171
+ if text_input:
172
+ result["text_result"] = predict_text(text_input)
173
+
174
+ # Xử lý Image
175
+ if "image" in request.files:
176
+ image_files = request.files.getlist("image")
177
+ for image_file in image_files:
178
+ try:
179
+ image = Image.open(image_file.stream).convert("RGB")
180
+ image_result = predict_image(image)
181
+ result["image_result"].append(image_result)
182
+ except Exception as img_err:
183
+ print(f"❌ Error processing image: {img_err}")
184
+ result["image_result"].append({"error": "Invalid image file"})
185
+
186
+ return jsonify(result)
187
+
188
+ except Exception as e:
189
+ print(f"❌ Server Error: {str(e)}")
190
+ return jsonify({"error": str(e)}), 500
191
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
  if __name__ == "__main__":
193
+ port = int(os.environ.get("PORT", 7860))
194
  app.run(host="0.0.0.0", port=port)