rendyputrak commited on
Commit
2e27b1d
·
1 Parent(s): 7f1b73f
app.py CHANGED
@@ -1,36 +1,79 @@
 
 
 
1
  from flask import Flask, request, jsonify
2
  from transformers import AutoTokenizer, AutoModelForSequenceClassification
3
  import torch
4
 
5
  app = Flask(__name__)
6
 
7
- # Load model dan tokenizer
8
- tokenizer = AutoTokenizer.from_pretrained("./hate_speech_model")
9
- model = AutoModelForSequenceClassification.from_pretrained("./hate_speech_model")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
  @app.route("/")
12
  def home():
13
- return jsonify({"message": "Hate Speech API is running!"})
 
 
 
 
14
 
15
  @app.route("/predict", methods=["POST"])
16
  def predict():
 
 
 
 
 
17
  data = request.get_json()
18
 
19
  if not data or "text" not in data:
20
- return jsonify({"error": "Please provide 'text'"}), 400
21
 
22
  text = data["text"]
 
 
 
23
 
24
- inputs = tokenizer(text, return_tensors="pt", truncation=True, padding=True)
25
  with torch.no_grad():
26
  outputs = model(**inputs)
27
  probs = torch.nn.functional.softmax(outputs.logits, dim=1)
28
- label = "Hate" if torch.argmax(probs).item() == 1 else "Non-Hate"
 
 
 
 
 
29
 
30
  return jsonify({
31
  "label": label,
32
- "confidence": float(probs.max())
 
 
 
 
33
  })
34
 
35
  if __name__ == "__main__":
36
  app.run(host="0.0.0.0", port=7860)
 
 
1
+ import os
2
+ import json
3
+ import re
4
  from flask import Flask, request, jsonify
5
  from transformers import AutoTokenizer, AutoModelForSequenceClassification
6
  import torch
7
 
8
  app = Flask(__name__)
9
 
10
+ MODEL_PATH = "./hate_speech_model"
11
+
12
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_PATH)
13
+ model = AutoModelForSequenceClassification.from_pretrained(MODEL_PATH)
14
+
15
+ threshold = 0.5
16
+ if os.path.exists(os.path.join(MODEL_PATH, "training_config.json")):
17
+ try:
18
+ with open(os.path.join(MODEL_PATH, "training_config.json"), "r") as f:
19
+ config = json.load(f)
20
+ threshold = config.get("optimal_threshold", 0.5)
21
+ print(f"Loaded optimal threshold from config: {threshold:.4f}")
22
+ except Exception as e:
23
+ print(f"Gagal memuat training_config.json: {e}. Menggunakan threshold default 0.5")
24
+
25
+ def preprocess_text(text):
26
+
27
+ text = re.sub(r'@[^\s]+', '@USER', text)
28
+ text = re.sub(r'https?://[^\s]+', 'HTTPURL', text)
29
+ text = re.sub(r'\s+', ' ', text).strip()
30
+ return text
31
 
32
  @app.route("/")
33
  def home():
34
+ return jsonify({
35
+ "message": "Hate Speech Detection API",
36
+ "model_path": MODEL_PATH,
37
+ "optimal_threshold": threshold
38
+ })
39
 
40
  @app.route("/predict", methods=["POST"])
41
  def predict():
42
+ # Verifikasi custom API key untuk mencegah penyalahgunaan API publik
43
+ api_key = request.headers.get("X-API-Key")
44
+ if api_key != "sh1eld-hate-speech-key-2026":
45
+ return jsonify({"error": "Akses ditolak: API Key tidak valid atau tidak disediakan"}), 401
46
+
47
  data = request.get_json()
48
 
49
  if not data or "text" not in data:
50
+ return jsonify({"error": "Silakan kirimkan parameter 'text'"}), 400
51
 
52
  text = data["text"]
53
+
54
+ # Preprocessing teks sebelum diklasifikasikan
55
+ text_clean = preprocess_text(text)
56
 
57
+ inputs = tokenizer(text_clean, return_tensors="pt", truncation=True, padding=True)
58
  with torch.no_grad():
59
  outputs = model(**inputs)
60
  probs = torch.nn.functional.softmax(outputs.logits, dim=1)
61
+ hate_prob = probs[0][1].item()
62
+
63
+ # Terapkan threshold optimal untuk deteksi kelas Hate
64
+ pred_label = 1 if hate_prob >= threshold else 0
65
+ label = "Hate" if pred_label == 1 else "Non-Hate"
66
+ confidence = hate_prob if pred_label == 1 else (1 - hate_prob)
67
 
68
  return jsonify({
69
  "label": label,
70
+ "confidence": confidence,
71
+ "probabilities": {
72
+ "Non-Hate": float(probs[0][0]),
73
+ "Hate": float(probs[0][1])
74
+ }
75
  })
76
 
77
  if __name__ == "__main__":
78
  app.run(host="0.0.0.0", port=7860)
79
+
hate_speech_model/config.json CHANGED
@@ -1,17 +1,22 @@
1
  {
2
- "_num_labels": 5,
3
  "architectures": [
4
  "BertForSequenceClassification"
5
  ],
6
  "attention_probs_dropout_prob": 0.1,
 
7
  "classifier_dropout": null,
8
- "directionality": "bidi",
9
  "dtype": "float32",
 
 
 
 
10
  "hidden_act": "gelu",
11
  "hidden_dropout_prob": 0.1,
12
  "hidden_size": 768,
13
  "initializer_range": 0.02,
14
  "intermediate_size": 3072,
 
15
  "layer_norm_eps": 1e-12,
16
  "max_position_embeddings": 512,
17
  "model_type": "bert",
@@ -19,15 +24,12 @@
19
  "num_hidden_layers": 12,
20
  "output_past": true,
21
  "pad_token_id": 0,
22
- "pooler_fc_size": 768,
23
- "pooler_num_attention_heads": 12,
24
- "pooler_num_fc_layers": 3,
25
- "pooler_size_per_head": 128,
26
- "pooler_type": "first_token_transform",
27
- "position_embedding_type": "absolute",
28
- "problem_type": "single_label_classification",
29
- "transformers_version": "4.57.1",
30
  "type_vocab_size": 2,
31
- "use_cache": true,
32
- "vocab_size": 50000
 
33
  }
 
1
  {
2
+ "add_cross_attention": false,
3
  "architectures": [
4
  "BertForSequenceClassification"
5
  ],
6
  "attention_probs_dropout_prob": 0.1,
7
+ "bos_token_id": 0,
8
  "classifier_dropout": null,
 
9
  "dtype": "float32",
10
+ "eos_token_id": null,
11
+ "eos_token_ids": 0,
12
+ "finetuning_task": null,
13
+ "gradient_checkpointing": false,
14
  "hidden_act": "gelu",
15
  "hidden_dropout_prob": 0.1,
16
  "hidden_size": 768,
17
  "initializer_range": 0.02,
18
  "intermediate_size": 3072,
19
+ "is_decoder": false,
20
  "layer_norm_eps": 1e-12,
21
  "max_position_embeddings": 512,
22
  "model_type": "bert",
 
24
  "num_hidden_layers": 12,
25
  "output_past": true,
26
  "pad_token_id": 0,
27
+ "pruned_heads": {},
28
+ "tie_word_embeddings": true,
29
+ "torchscript": false,
30
+ "transformers_version": "5.0.0",
 
 
 
 
31
  "type_vocab_size": 2,
32
+ "use_bfloat16": false,
33
+ "use_cache": false,
34
+ "vocab_size": 31923
35
  }
hate_speech_model/model.safetensors CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:ad083ce4110a1a1ff4a3210ff6ba26fce90e369318f66eb70e62657e211eed46
3
- size 497795072
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f3bce66292ecf2ad3a0cc46a18cdd4dd144c2cf8fa539dea7807951a7ad1bf88
3
+ size 442262496
hate_speech_model/special_tokens_map.json DELETED
@@ -1,7 +0,0 @@
1
- {
2
- "cls_token": "[CLS]",
3
- "mask_token": "[MASK]",
4
- "pad_token": "[PAD]",
5
- "sep_token": "[SEP]",
6
- "unk_token": "[UNK]"
7
- }
 
 
 
 
 
 
 
 
hate_speech_model/tokenizer.json CHANGED
The diff for this file is too large to render. See raw diff
 
hate_speech_model/tokenizer_config.json CHANGED
@@ -1,54 +1,10 @@
1
  {
2
- "added_tokens_decoder": {
3
- "0": {
4
- "content": "[PAD]",
5
- "lstrip": false,
6
- "normalized": false,
7
- "rstrip": false,
8
- "single_word": false,
9
- "special": true
10
- },
11
- "1": {
12
- "content": "[UNK]",
13
- "lstrip": false,
14
- "normalized": false,
15
- "rstrip": false,
16
- "single_word": false,
17
- "special": true
18
- },
19
- "2": {
20
- "content": "[CLS]",
21
- "lstrip": false,
22
- "normalized": false,
23
- "rstrip": false,
24
- "single_word": false,
25
- "special": true
26
- },
27
- "3": {
28
- "content": "[SEP]",
29
- "lstrip": false,
30
- "normalized": false,
31
- "rstrip": false,
32
- "single_word": false,
33
- "special": true
34
- },
35
- "4": {
36
- "content": "[MASK]",
37
- "lstrip": false,
38
- "normalized": false,
39
- "rstrip": false,
40
- "single_word": false,
41
- "special": true
42
- }
43
- },
44
- "clean_up_tokenization_spaces": true,
45
  "cls_token": "[CLS]",
46
- "do_basic_tokenize": true,
47
- "do_lower_case": true,
48
- "extra_special_tokens": {},
49
  "mask_token": "[MASK]",
50
  "model_max_length": 1000000000000000019884624838656,
51
- "never_split": null,
52
  "pad_token": "[PAD]",
53
  "sep_token": "[SEP]",
54
  "strip_accents": null,
 
1
  {
2
+ "backend": "tokenizers",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  "cls_token": "[CLS]",
4
+ "do_lower_case": false,
5
+ "is_local": false,
 
6
  "mask_token": "[MASK]",
7
  "model_max_length": 1000000000000000019884624838656,
 
8
  "pad_token": "[PAD]",
9
  "sep_token": "[SEP]",
10
  "strip_accents": null,
hate_speech_model/training_config.json ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "optimal_threshold": 0.4299999999999998,
3
+ "class_weights": {
4
+ "non_hate": 0.5966592554586397,
5
+ "hate": 3.0864051902093776
6
+ },
7
+ "label_mapping": {
8
+ "0": "Non-Hate",
9
+ "1": "Hate"
10
+ },
11
+ "model_name": "indolem/indobertweet-base-uncased",
12
+ "max_length": 128,
13
+ "dataset": "Exqrch/IndoDiscourse",
14
+ "metrics": {
15
+ "accuracy": 0.7902178066488346,
16
+ "precision": 0.3990306946688207,
17
+ "recall": 0.5825471698113207,
18
+ "f1_score": 0.473633748801534
19
+ }
20
+ }
hate_speech_model/vocab.txt DELETED
The diff for this file is too large to render. See raw diff