// 🌌 [BioPhys 5.0 λ„€μ΄ν‹°λΈŒ BPE λ°”μ΄νŠΈνŽ˜μ–΄ ν† ν¬λ‚˜μ΄μ € μ—”μ§„] (src/native_bpe_tokenizer.rs) // μ™ΈλΆ€ 파이썬 μ˜μ‘΄μ„± 0%! μž„μ˜μ˜ ν•œκ΅­μ–΄/μ˜μ–΄/기호 ν…μŠ€νŠΈλ₯Ό BPE λ°”μ΄νŠΈ λ‹¨μœ„λ‘œ λΆ„μ ˆ 및 μ •μˆ˜ ID 인코딩/λ””μ½”λ”© use std::collections::HashMap; pub struct NativeBpeTokenizer { pub token_to_id: HashMap, pub id_to_token: Vec, pub byte_tokens: Vec, } impl NativeBpeTokenizer { pub fn new() -> Self { let mut token_to_id = HashMap::new(); let mut id_to_token = Vec::new(); // 1. 특수 토큰 등둝 let special_tokens = vec!["", "", "", "", ""]; for &tok in &special_tokens { let id = id_to_token.len(); id_to_token.push(tok.to_string()); token_to_id.insert(tok.to_string(), id); } // 2. 256개 κΈ°λ³Έ λ°”μ΄νŠΈ 토큰 등둝 (0x00 ~ 0xFF) let mut byte_tokens = Vec::new(); for b in 0..=255u8 { let tok_str = format!("<0x{:02X}>", b); let id = id_to_token.len(); id_to_token.push(tok_str.clone()); token_to_id.insert(tok_str.clone(), id); byte_tokens.push(tok_str); } // 3. μ£Όμš” ν•œκ΅­μ–΄ 음절 및 기술 ν‚€μ›Œλ“œ μ„œλΈŒμ›Œλ“œ 등둝 let common_korean_subwords = vec![ "인곡지λŠ₯", "λΈ”λž™ν™€", "μ‚¬κ±΄μ˜", "지평선", "μŠ€νŒŒμ΄ν‚Ή", "μ–‘μžν™”", "λ‰΄λŸ°", "κ°€μ€‘μΉ˜", "μ••μΆ•", "μ΄ˆμ €μ§€μ—°", "μ—”μ§„", "Rust", "Svelte", "Tauri", "ν•œκ΅­μ–΄", "문법", "항상성", "특이점", "물리", "동역학", "μž…λ‹ˆλ‹€", "ν•©λ‹ˆλ‹€", "으둜", "μ—μ„œ", "의", "을", "λ₯Ό", "이", "κ°€", "은", "λŠ”", "κ³Ό", "와" ]; for &word in &common_korean_subwords { let id = id_to_token.len(); id_to_token.push(word.to_string()); token_to_id.insert(word.to_string(), id); } NativeBpeTokenizer { token_to_id, id_to_token, byte_tokens, } } /// [ν…μŠ€νŠΈ βž” μ •κ·œ BPE 토큰 ID 벑터 인코딩] pub fn encode(&self, text: &str) -> Vec { let mut token_ids = Vec::new(); token_ids.push(1); // let mut chars = text.chars().peekable(); let mut current_buf = String::new(); while let Some(c) = chars.next() { current_buf.push(c); // λ“±λ‘λœ μ„œλΈŒμ›Œλ“œμΈμ§€ 탐색 if let Some(&id) = self.token_to_id.get(¤t_buf) { token_ids.push(id); current_buf.clear(); } else if c.is_whitespace() || chars.peek().is_none() { // 곡백 λ˜λŠ” 단어 λμ—μ„œ λ°”μ΄νŠΈ λ‹¨μœ„ 폴백 for b in current_buf.as_bytes() { let byte_tok = format!("<0x{:02X}>", b); let id = self.token_to_id.get(&byte_tok).copied().unwrap_or(3); // token_ids.push(id); } current_buf.clear(); } } token_ids.push(2); // token_ids } /// [토큰 ID 벑터 βž” μ‚¬λžŒμ΄ 읽을 수 μžˆλŠ” UTF-8 ν…μŠ€νŠΈ λ””μ½”λ”©] pub fn decode(&self, token_ids: &[usize]) -> String { let mut byte_buffer = Vec::new(); let mut decoded_text = String::new(); for &id in token_ids { if id >= self.id_to_token.len() { continue; } let tok_str = &self.id_to_token[id]; if tok_str == "" || tok_str == "" || tok_str == "" { continue; } if tok_str.starts_with("<0x") && tok_str.ends_with(">") { if let Ok(b) = u8::from_str_radix(&tok_str[3..5], 16) { byte_buffer.push(b); } } else { if !byte_buffer.is_empty() { decoded_text.push_str(&String::from_utf8_lossy(&byte_buffer)); byte_buffer.clear(); } decoded_text.push_str(tok_str); } } if !byte_buffer.is_empty() { decoded_text.push_str(&String::from_utf8_lossy(&byte_buffer)); } decoded_text } }