| import os |
| import numpy as np |
| import onnxruntime as ort |
| import sentencepiece as spm |
|
|
|
|
| |
| |
| |
|
|
| BASE_DIR = os.path.dirname(os.path.abspath(__file__)) |
|
|
| ENCODER_PATH = os.path.join(BASE_DIR, "encoder_model.onnx") |
| DECODER_PATH = os.path.join(BASE_DIR, "decoder_model.onnx") |
| SPM_PATH = os.path.join(BASE_DIR, "spm.model") |
|
|
|
|
| |
| |
| |
|
|
| MAX_INPUT_LEN = 256 |
| MAX_OUTPUT_LEN = 256 |
|
|
| PAD_ID = 0 |
| UNK_ID = 1 |
| BOS_ID = 2 |
| EOS_ID = 3 |
|
|
|
|
| |
| |
| |
|
|
| for path in [ENCODER_PATH, DECODER_PATH, SPM_PATH]: |
| if not os.path.exists(path): |
| raise FileNotFoundError(f"Không tìm thấy file: {path}") |
|
|
|
|
| |
| |
| |
|
|
| print("Đang load tokenizer...") |
|
|
| sp = spm.SentencePieceProcessor() |
| sp.load(SPM_PATH) |
|
|
| print( |
| f"Tokenizer: vocab={sp.get_piece_size()} | " |
| f"PAD={sp.pad_id()} BOS={sp.bos_id()} EOS={sp.eos_id()}" |
| ) |
|
|
|
|
| |
| |
| |
|
|
| print("Đang load ONNX model...") |
|
|
| providers = ["CPUExecutionProvider"] |
|
|
| encoder_session = ort.InferenceSession( |
| ENCODER_PATH, |
| providers=providers |
| ) |
|
|
| decoder_session = ort.InferenceSession( |
| DECODER_PATH, |
| providers=providers |
| ) |
|
|
|
|
| |
| |
| |
|
|
| print("\nEncoder inputs:") |
| for x in encoder_session.get_inputs(): |
| print(" ", x.name, x.shape, x.type) |
|
|
| print("\nDecoder inputs:") |
| for x in decoder_session.get_inputs(): |
| print(" ", x.name, x.shape, x.type) |
|
|
|
|
| |
| |
| |
|
|
| def translate(text): |
| text = text.strip() |
|
|
| if not text: |
| return "" |
|
|
| |
| |
| |
|
|
| src_ids = sp.encode(text, out_type=int) |
|
|
| |
| src_ids = src_ids[:MAX_INPUT_LEN - 2] |
|
|
| |
| src_ids = [BOS_ID] + src_ids + [EOS_ID] |
|
|
| input_ids = np.array( |
| [src_ids], |
| dtype=np.int64 |
| ) |
|
|
| attention_mask = np.ones_like( |
| input_ids, |
| dtype=np.int64 |
| ) |
|
|
| |
| |
| |
|
|
| encoder_outputs = encoder_session.run( |
| None, |
| { |
| "input_ids": input_ids, |
| "attention_mask": attention_mask |
| } |
| ) |
|
|
| encoder_hidden_states = encoder_outputs[0] |
|
|
| |
| |
| |
|
|
| generated = [BOS_ID] |
|
|
| for _ in range(MAX_OUTPUT_LEN): |
|
|
| decoder_input_ids = np.array( |
| [generated], |
| dtype=np.int64 |
| ) |
|
|
| decoder_inputs = {} |
|
|
| |
| for inp in decoder_session.get_inputs(): |
|
|
| name = inp.name |
|
|
| if name == "input_ids": |
| decoder_inputs[name] = decoder_input_ids |
|
|
| elif "encoder_hidden_states" in name: |
| decoder_inputs[name] = encoder_hidden_states |
|
|
| elif "encoder_attention_mask" in name: |
| decoder_inputs[name] = attention_mask |
|
|
| decoder_outputs = decoder_session.run( |
| None, |
| decoder_inputs |
| ) |
|
|
| |
| logits = decoder_outputs[0] |
|
|
| |
| next_token_logits = logits[0, -1, :] |
|
|
| next_token = int( |
| np.argmax(next_token_logits) |
| ) |
|
|
| generated.append(next_token) |
|
|
| if next_token == EOS_ID: |
| break |
|
|
| |
| |
| |
|
|
| output_ids = generated[1:] |
|
|
| if EOS_ID in output_ids: |
| output_ids = output_ids[ |
| :output_ids.index(EOS_ID) |
| ] |
|
|
| result = sp.decode(output_ids) |
|
|
| return result |
|
|
|
|
| |
| |
| |
|
|
| print("\nEN → VI translator v2 ready") |
| print("Type 'exit' để thoát\n") |
|
|
| while True: |
|
|
| try: |
| text = input("EN > ").strip() |
|
|
| except (KeyboardInterrupt, EOFError): |
| print("\nBye") |
| break |
|
|
| if text.lower() in ["exit", "quit", "q"]: |
| print("Bye") |
| break |
|
|
| if not text: |
| continue |
|
|
| result = translate(text) |
|
|
| print("VI >", result) |
| print() |