youssefreda9 commited on
Commit
9eee80b
·
1 Parent(s): ea6d242

revert: Restore ORIGINAL grammar params that were working (num_beams=4, max_length=512) - Remove all hallucination checks that were blocking valid corrections - The model WAS correcting ذهب→ذهبت before, only highlight was wrong - Keep word-level diff fix (in app.py) + raw output logging

Browse files
Files changed (1) hide show
  1. src/nlp/grammar/grammar_service.py +15 -64
src/nlp/grammar/grammar_service.py CHANGED
@@ -3,7 +3,6 @@ Grammar correction service using Bayan_Arabic_Grammar (T5).
3
  Lazy-loads the model on first request, keeps it resident in memory.
4
  """
5
  import logging
6
- import re
7
  import torch
8
  from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
9
 
@@ -15,8 +14,10 @@ _device = None
15
 
16
  MODEL_ID = "bayan10/Bayan_Arabic_Grammar"
17
 
18
- # Generation parameters
19
  MAX_INPUT_LENGTH = 512
 
 
20
 
21
 
22
  def get_grammar_model():
@@ -50,6 +51,7 @@ def get_grammar_model():
50
 
51
  except Exception as e:
52
  logger.error(f"Failed to load grammar model: {e}")
 
53
  try:
54
  logger.info("Retrying without local_files_only...")
55
  _grammar_tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
@@ -66,48 +68,6 @@ def get_grammar_model():
66
  return _grammar_model, _grammar_tokenizer, _device
67
 
68
 
69
- def _is_hallucinated(original: str, corrected: str) -> bool:
70
- """
71
- Check if the grammar output is hallucinated / garbled.
72
- Returns True if output should be discarded.
73
- """
74
- orig_words = re.findall(r'[\u0600-\u06FF]+', original)
75
- corr_words = re.findall(r'[\u0600-\u06FF]+', corrected)
76
-
77
- # 1. Output too long (grammar corrections are minor edits)
78
- if len(corrected) > len(original) * 1.5:
79
- logger.warning(f"Hallucination: output too long ({len(corrected)} vs {len(original)} chars)")
80
- return True
81
-
82
- # 2. Output too short
83
- if len(corrected) < len(original) * 0.5:
84
- logger.warning(f"Hallucination: output too short ({len(corrected)} vs {len(original)} chars)")
85
- return True
86
-
87
- # 3. Too many words added
88
- if len(corr_words) > len(orig_words) * 1.4:
89
- logger.warning(f"Hallucination: too many words ({len(corr_words)} vs {len(orig_words)})")
90
- return True
91
-
92
- # 4. Check for duplicated 2-word phrases (bigrams)
93
- if len(corr_words) > 4:
94
- bigrams = [f"{corr_words[i]} {corr_words[i+1]}" for i in range(len(corr_words)-1)]
95
- if len(bigrams) != len(set(bigrams)):
96
- logger.warning("Hallucination: repeated bigrams detected")
97
- return True
98
-
99
- # 5. Sentence start rearranged (grammar shouldn't move first word to end)
100
- if orig_words and corr_words:
101
- # Check if first 2 original words appear in first 4 corrected words
102
- orig_head = set(orig_words[:2])
103
- corr_head = set(corr_words[:4])
104
- if len(orig_head) > 0 and not orig_head.intersection(corr_head):
105
- logger.warning("Hallucination: sentence start completely rearranged")
106
- return True
107
-
108
- return False
109
-
110
-
111
  def correct_grammar(text: str) -> str:
112
  """
113
  Run grammar correction on input text using T5.
@@ -129,37 +89,28 @@ def correct_grammar(text: str) -> str:
129
  )
130
  inputs = {k: v.to(device) for k, v in inputs.items()}
131
 
132
- # Dynamic max_length: close to input to prevent hallucination
133
- input_len = inputs['input_ids'].shape[1]
134
- dynamic_max_len = min(int(input_len * 1.3) + 5, MAX_INPUT_LENGTH)
135
-
136
- # Strategy: greedy decoding (most stable for grammar correction)
137
  with torch.no_grad():
138
  outputs = model.generate(
139
  **inputs,
140
- max_length=dynamic_max_len,
141
- num_beams=1, # Greedy decoding — most stable
142
- do_sample=False,
143
- repetition_penalty=2.0, # Strong penalty against repeating
144
  )
145
 
146
  # Decode output
147
- raw_output = tokenizer.decode(outputs[0], skip_special_tokens=True)
 
 
148
  logger.info(f"[GRAMMAR-RAW] input({len(text)}): '{text}'")
149
- logger.info(f"[GRAMMAR-RAW] output({len(raw_output)}): '{raw_output}'")
150
 
151
  # Safety: if model returns empty, keep original
152
- if not raw_output or not raw_output.strip():
153
- logger.warning("Grammar returned empty output")
154
- return text
155
-
156
- corrected = raw_output.strip()
157
-
158
- # Check for hallucination
159
- if _is_hallucinated(text, corrected):
160
  return text
161
 
162
- return corrected
163
 
164
  except Exception as e:
165
  logger.error(f"Grammar correction failed: {e}")
 
3
  Lazy-loads the model on first request, keeps it resident in memory.
4
  """
5
  import logging
 
6
  import torch
7
  from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
8
 
 
14
 
15
  MODEL_ID = "bayan10/Bayan_Arabic_Grammar"
16
 
17
+ # Generation parameters — ORIGINAL WORKING VALUES
18
  MAX_INPUT_LENGTH = 512
19
+ MAX_OUTPUT_LENGTH = 512
20
+ NUM_BEAMS = 4
21
 
22
 
23
  def get_grammar_model():
 
51
 
52
  except Exception as e:
53
  logger.error(f"Failed to load grammar model: {e}")
54
+ # Try without local_files_only as fallback
55
  try:
56
  logger.info("Retrying without local_files_only...")
57
  _grammar_tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
 
68
  return _grammar_model, _grammar_tokenizer, _device
69
 
70
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  def correct_grammar(text: str) -> str:
72
  """
73
  Run grammar correction on input text using T5.
 
89
  )
90
  inputs = {k: v.to(device) for k, v in inputs.items()}
91
 
92
+ # Generate correction — ORIGINAL WORKING PARAMS
 
 
 
 
93
  with torch.no_grad():
94
  outputs = model.generate(
95
  **inputs,
96
+ max_length=MAX_OUTPUT_LENGTH,
97
+ num_beams=NUM_BEAMS,
98
+ early_stopping=True,
99
+ no_repeat_ngram_size=3,
100
  )
101
 
102
  # Decode output
103
+ corrected = tokenizer.decode(outputs[0], skip_special_tokens=True)
104
+
105
+ # Log for debugging
106
  logger.info(f"[GRAMMAR-RAW] input({len(text)}): '{text}'")
107
+ logger.info(f"[GRAMMAR-RAW] output({len(corrected)}): '{corrected}'")
108
 
109
  # Safety: if model returns empty, keep original
110
+ if not corrected or not corrected.strip():
 
 
 
 
 
 
 
111
  return text
112
 
113
+ return corrected.strip()
114
 
115
  except Exception as e:
116
  logger.error(f"Grammar correction failed: {e}")