minseok
π Release BioPhys 6.0 Grand Master: 16GB (14.89GB) Gemma-4 100% Devour, Ecosystem Evolution, Solar MoE, SNN Autoregressive SDK, Dynamic PhaseVM
be99550 | // π [BioPhys 5.0 λ€μ΄ν°λΈ BPE λ°μ΄νΈνμ΄ ν ν¬λμ΄μ μμ§] (src/native_bpe_tokenizer.rs) | |
| // μΈλΆ νμ΄μ¬ μμ‘΄μ± 0%! μμμ νκ΅μ΄/μμ΄/κΈ°νΈ ν μ€νΈλ₯Ό BPE λ°μ΄νΈ λ¨μλ‘ λΆμ λ° μ μ ID μΈμ½λ©/λμ½λ© | |
| use std::collections::HashMap; | |
| pub struct NativeBpeTokenizer { | |
| pub token_to_id: HashMap<String, usize>, | |
| pub id_to_token: Vec<String>, | |
| pub byte_tokens: Vec<String>, | |
| } | |
| 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!["<PAD>", "<BOS>", "<EOS>", "<UNK>", "<MASK>"]; | |
| 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<usize> { | |
| let mut token_ids = Vec::new(); | |
| token_ids.push(1); // <BOS> | |
| 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); // <UNK> | |
| token_ids.push(id); | |
| } | |
| current_buf.clear(); | |
| } | |
| } | |
| token_ids.push(2); // <EOS> | |
| 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 == "<BOS>" || tok_str == "<EOS>" || tok_str == "<PAD>" { | |
| 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 | |
| } | |
| } | |