Download scripts/predict_single_seq2seq.py from krislette/salinlahi: direct link, hf CLI and curl.
- Browser
- Download file 4.2 kB
-
https://huggingface.co/spaces/krislette/salinlahi/resolve/main/scripts/predict_single_seq2seq.py
- Command line
-
hf download hf://spaces/krislette/salinlahi/scripts/predict_single_seq2seq.py
-
curl -L -o predict_single_seq2seq.py https://huggingface.co/spaces/krislette/salinlahi/resolve/main/scripts/predict_single_seq2seq.py
4.2 kB
| import argparse | |
| import torch | |
| import yaml | |
| import logging | |
| from src.models.recurrent.seq2seq import build_model | |
| from src.models.recurrent.tokenizer import SPMTokenizer, BOS_IDX, EOS_IDX | |
| from scripts.preprocess import preprocess_text | |
| logging.basicConfig(level=logging.INFO) | |
| logger = logging.getLogger(__name__) | |
| class Seq2SeqTranslator: | |
| """ | |
| A unified wrapper class for the Seq2Seq model. | |
| Instantiate this twice in your API server (once for TGL-WAR, once for WAR-TGL). | |
| """ | |
| def __init__( | |
| self, | |
| model_path: str, | |
| tokenizer_path: str, | |
| config_path: str = "config/model_config.yml", | |
| ): | |
| self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu") | |
| logger.info( | |
| f"Initializing Seq2Seq Translator on: {self.device} using {model_path}" | |
| ) | |
| try: | |
| # Load config | |
| with open(config_path, "r") as f: | |
| config = yaml.safe_load(f) | |
| # Load Tokenizer | |
| self.tokenizer = SPMTokenizer(str(tokenizer_path)) | |
| vocab_size = len(self.tokenizer) | |
| # Load checkpoint FIRST to get the model's saved config | |
| checkpoint = torch.load(model_path, map_location=self.device) | |
| self.config = checkpoint.get("config", config) | |
| # Initialize Architecture with checkpoint's config | |
| self.model = build_model( | |
| self.config, | |
| source_vocab_size=vocab_size, | |
| target_vocab_size=vocab_size, | |
| device=self.device, | |
| ) | |
| # Load Weights | |
| self.model.load_state_dict(checkpoint["model_state_dict"]) | |
| self.model.eval() | |
| logger.info("Model weights loaded and ready for inference.") | |
| except Exception as e: | |
| logger.error(f"Failed to load model or tokenizer: {str(e)}") | |
| raise e | |
| def predict(self, sentence: str) -> str: | |
| """Takes a single raw string and returns the translated string.""" | |
| if not sentence or not isinstance(sentence, str) or not sentence.strip(): | |
| return "" | |
| try: | |
| # Encode source sentence (limits to max 100 tokens) | |
| cleaned_sentence = preprocess_text(sentence) | |
| source_ids = self.tokenizer.encode(cleaned_sentence.lower())[:100] | |
| source_tensor = ( | |
| torch.tensor(source_ids, dtype=torch.long).unsqueeze(0).to(self.device) | |
| ) | |
| source_lengths = torch.tensor([len(source_ids)], dtype=torch.long) | |
| # Decode (greedy decoding) | |
| predicted_ids, _ = self.model.translate( | |
| source_tensor, | |
| source_lengths, | |
| sos_idx=BOS_IDX, | |
| eos_idx=EOS_IDX, | |
| max_output_length=50, | |
| ) | |
| # Decode tokens back to text | |
| translation = self.tokenizer.decode(predicted_ids, skip_special_tokens=True) | |
| return translation | |
| except Exception as e: | |
| logger.error(f"Translation generation failed: {str(e)}") | |
| return "[Error: Unable to process translation]" | |
| if __name__ == "__main__": | |
| parser = argparse.ArgumentParser(description="Single-Input Seq2Seq Inference") | |
| parser.add_argument("--text", type=str, required=True, help="The source sentence") | |
| parser.add_argument( | |
| "--direction", | |
| type=str, | |
| choices=["tgl-war", "war-tgl"], | |
| required=True, | |
| help="Translation direction", | |
| ) | |
| args = parser.parse_args() | |
| # Determine paths based on direction argument | |
| if args.direction == "war-tgl": | |
| m_path = "models/recurrent/war_fil/best_model.pt" | |
| t_path = "data/processed/war_fil/spm.model" | |
| else: # tgl-war | |
| m_path = "models/recurrent/fil_war/best_model.pt" | |
| t_path = "data/processed/fil_war/spm.model" | |
| # Initialize the specific translator | |
| translator = Seq2SeqTranslator(model_path=m_path, tokenizer_path=t_path) | |
| result = translator.predict(args.text) | |
| print("\n" + "=" * 50) | |
| print(f"Source ({args.direction.upper()}) : {args.text}") | |
| print(f"Translation: {result}") | |
| print("=" * 50 + "\n") | |