Spaces:
Sleeping
Sleeping
File size: 7,544 Bytes
7994ca9 9024f27 7994ca9 2751fc7 7994ca9 2751fc7 9024f27 2751fc7 7994ca9 2751fc7 a4d0c85 2751fc7 a4d0c85 2751fc7 6aafe97 7994ca9 a4d0c85 7994ca9 | 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 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | import spaces # MUST be the very first import for ZeroGPU Spaces
import os
import gradio as gr
import torch
import regex
import unicodedata
import re
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PeftModel
# ─── Model Configuration ──────────────────────────────────────────────────────
BASE_MODEL = "unsloth/gemma-3-270m-it"
LORA_PATH = "Razon2006/gemma3-tamil-Grapheme-NewChange-r32-lr"
hf_token = os.environ.get("HF_TOKEN")
print("Loading tokenizer globally...")
tokenizer = AutoTokenizer.from_pretrained(LORA_PATH, token=hf_token)
# Global model variable for lazy loading
model = None
# ─── Grapheme Tokenizer & Constants ───────────────────────────────────────────
INSTRUCTION = (
"கீழ்க்காணும் தமிழ் வெண்பாவை, அதன் சொற்களை சரியான எல்லைகளுடன் "
"பிரித்து எழுதுக (பதப்பிரிவு). சொற்களின் வரிசை மாறாமல், "
"ஒட்டிய சொற்களை பிரிக்கவும், தேவையெனில் பிரிந்த எழுத்துக்களை "
"இணைத்து சரியான சொல்லை உருவாக்கவும். பதப்பிரிவை மட்டும் தரவும்."
)
PROBLEMATIC_AKSHARAS = {
'யு', 'றா', 'ஞா', 'யே', 'யை', 'றே', 'கெ', 'றோ', 'யெ', 'யொ',
'றெ', 'ழா', 'நூ', 'லெ', 'வொ', 'ரெ', 'றொ', 'கீ', 'டே', 'லொ',
'னொ', 'டொ', 'ணே', 'ளெ', 'ணோ', 'ரொ', 'றூ', 'ணீ', 'றீ', 'ழீ',
'னூ', 'ணெ', 'வூ', 'யீ', 'டீ', 'ளொ', 'லூ', 'நொ', 'ணொ', 'டூ',
'நை', 'கௌ', 'ளூ', 'ழே', 'ழூ', 'ஞை', 'ளீ', 'ஜை', 'ணூ', 'ழொ',
'ழெ', 'ஞு', 'ஞெ', 'ஞூ', 'ஸா', 'ஜூ', 'எா', 'எீ', 'ழோ', 'பௌ',
}
def clean_grapheme_split(text, tokenizer):
words = text.split(' ')
result_words = []
for word in words:
if not word.strip():
continue
clusters = regex.findall(r'\X', word)
clusters = [
unicodedata.normalize('NFC', c) for c in clusters
if c.strip() and any('\u0B80' <= ch <= '\u0BFF' for ch in c)
]
if not clusters:
result_words.append(word)
continue
merged = []
i = 0
while i < len(clusters):
c = clusters[i]
tokens = tokenizer.tokenize(c)
if (c in PROBLEMATIC_AKSHARAS or len(tokens) > 1) and i + 1 < len(clusters):
combined = c + clusters[i+1]
while len(tokenizer.tokenize(combined)) > 1 and i + 2 < len(clusters):
i += 1
combined = combined + clusters[i+1]
merged.append(combined)
i += 2
else:
merged.append(c)
i += 1
result_words.append('|'.join(merged))
return ' '.join(result_words)
def post_process_grapheme(text):
return text.replace('|', '')
# ─── Inference Pipeline ───────────────────────────────────────────────────────
@spaces.GPU(duration=60)
def generate_pathaseth(venba, max_new_tokens=256):
global model
# LAZY LOADING: Load the model here where CUDA is fully unlocked by ZeroGPU
if model is None:
print("Loading base model directly into GPU...")
base_model = AutoModelForCausalLM.from_pretrained(
BASE_MODEL,
torch_dtype=torch.bfloat16,
device_map="cuda", # <--- FIX: Forces native loading to GPU to avoid precision loss
token=hf_token
)
print("Loading LoRA adapter...")
model = PeftModel.from_pretrained(base_model, LORA_PATH, token=hf_token)
model.eval()
print("Model ready for inference!")
venba = venba.strip()
if not venba:
return "Please enter a Venba verse."
venba_clean = re.sub(r'\s+', ' ', venba).replace('்்', '்').strip()
venba_no_space = venba_clean.replace(' ', '')
venba_g = clean_grapheme_split(venba_no_space, tokenizer)
messages = [{
"role": "user",
"content": f"{INSTRUCTION}\n\nவெண்பா: {venba_g}\nபதப்பிரிவு:"
}]
prompt = tokenizer.apply_chat_template(
messages, tokenize=False, add_generation_prompt=True
)
bos = tokenizer.bos_token
if bos and prompt.startswith(bos):
prompt = prompt[len(bos):]
inputs = tokenizer(prompt, return_tensors="pt").to("cuda")
with torch.no_grad():
outputs = model.generate(
**inputs,
max_new_tokens=max_new_tokens,
use_cache=True,
do_sample=False, # <--- FIX: Ensures deterministic output (stops hallucination)
)
new_tokens = outputs[0][inputs['input_ids'].shape[1]:]
raw = tokenizer.decode(new_tokens, skip_special_tokens=True).strip()
return post_process_grapheme(raw)
# ─── Example Venbas ───────────────────────────────────────────────────────────
EXAMPLES = [
["இறுதி பயப்பினு மெஞ்சா திறைவற் குறுதி பயப்பதாந் தூது"],
]
# ─── Gradio Interface ─────────────────────────────────────────────────────────
with gr.Blocks(
title="Tamil Venba Pathasetham",
theme=gr.themes.Soft()
) as demo:
gr.HTML("""
<div style="text-align: center; font-size: 2em; font-weight: bold; color: #4B0082;">Tamil Venba Pathasetham (பதசேதம்)</div>
<div style="text-align: center; color: #666; margin-bottom: 20px;">
Automated word segmentation of classical Tamil Venba poetry using Gemma 3 + Grapheme Tokenization
</div>
""")
with gr.Row():
with gr.Column():
venba_input = gr.Textbox(
label="Enter Tamil Venba (வெண்பா)",
placeholder="வண்மைதரு மாகமநூன் வைத்த பொருள்வழுவா...",
lines=4,
)
submit_btn = gr.Button("Generate Pathasetham →", variant="primary", size="lg")
with gr.Column():
pathaseth_output = gr.Textbox(
label="Pathasetham (பதசேதம்) Output",
lines=4,
interactive=False,
)
gr.Examples(
examples=EXAMPLES,
inputs=venba_input,
outputs=pathaseth_output,
fn=generate_pathaseth,
cache_examples=False,
label="Click any example to test instantly"
)
submit_btn.click(
fn=generate_pathaseth,
inputs=venba_input,
outputs=pathaseth_output,
)
if __name__ == "__main__":
demo.launch() |