sentencepiece_rust/
model.rs1use crate::error::{Error, Result};
7use crate::proto::Reader;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum ModelType {
12 Unigram,
13 Bpe,
14 Word,
15 Char,
16}
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum PieceType {
21 Normal,
22 Unknown,
23 Control,
24 UserDefined,
25 Byte,
26 Unused,
27}
28
29impl PieceType {
30 fn from_i32(v: i32) -> PieceType {
31 match v {
32 1 => PieceType::Normal,
33 2 => PieceType::Unknown,
34 3 => PieceType::Control,
35 4 => PieceType::UserDefined,
36 6 => PieceType::Byte,
37 5 => PieceType::Unused,
38 _ => PieceType::Normal,
39 }
40 }
41}
42
43#[derive(Debug, Clone)]
44pub struct SentencePiece {
45 pub piece: String,
46 pub score: f32,
47 pub kind: PieceType,
48}
49
50#[derive(Debug, Clone)]
52pub struct NormalizerSpec {
53 pub precompiled_charsmap: Vec<u8>,
55 pub add_dummy_prefix: bool,
56 pub remove_extra_whitespaces: bool,
57 pub escape_whitespaces: bool,
58}
59
60impl Default for NormalizerSpec {
61 fn default() -> Self {
62 NormalizerSpec {
63 precompiled_charsmap: Vec::new(),
64 add_dummy_prefix: true,
65 remove_extra_whitespaces: true,
66 escape_whitespaces: true,
67 }
68 }
69}
70
71#[derive(Debug, Clone)]
73pub struct ModelProto {
74 pub pieces: Vec<SentencePiece>,
75 pub model_type: ModelType,
76 pub byte_fallback: bool,
77 pub treat_whitespace_as_suffix: bool,
78 pub unk_id: i32,
79 pub bos_id: i32,
80 pub eos_id: i32,
81 pub pad_id: i32,
82 pub unk_surface: String,
84 pub normalizer: NormalizerSpec,
85}
86
87impl ModelProto {
88 pub fn parse(bytes: &[u8]) -> Result<ModelProto> {
90 let mut model = ModelProto {
91 pieces: Vec::new(),
92 model_type: ModelType::Unigram,
93 byte_fallback: false,
94 treat_whitespace_as_suffix: false,
95 unk_id: 0,
96 bos_id: 1,
97 eos_id: 2,
98 pad_id: -1,
99 unk_surface: " \u{2047} ".to_string(),
101 normalizer: NormalizerSpec::default(),
102 };
103
104 let mut r = Reader::new(bytes);
105 while let Some((number, value)) = r.next()? {
106 match number {
107 1 => {
109 if let Some(b) = value.as_bytes() {
110 model.pieces.push(parse_piece(b)?);
111 }
112 }
113 2 => {
115 if let Some(b) = value.as_bytes() {
116 parse_trainer_spec(b, &mut model)?;
117 }
118 }
119 3 => {
121 if let Some(b) = value.as_bytes() {
122 model.normalizer = parse_normalizer_spec(b)?;
123 }
124 }
125 _ => {} }
127 }
128
129 if model.pieces.is_empty() {
130 return Err(Error::Model("model contains no pieces".into()));
131 }
132 Ok(model)
133 }
134}
135
136fn parse_piece(bytes: &[u8]) -> Result<SentencePiece> {
137 let mut piece = String::new();
138 let mut score = 0.0f32;
139 let mut kind = PieceType::Normal;
140 let mut r = Reader::new(bytes);
141 while let Some((number, value)) = r.next()? {
142 match number {
143 1 => piece = value.as_str().unwrap_or_default(),
144 2 => score = value.as_f32().unwrap_or(0.0),
145 3 => kind = PieceType::from_i32(value.as_i32().unwrap_or(1)),
146 _ => {}
147 }
148 }
149 Ok(SentencePiece { piece, score, kind })
150}
151
152fn parse_normalizer_spec(bytes: &[u8]) -> Result<NormalizerSpec> {
153 let mut spec = NormalizerSpec::default();
154 let mut r = Reader::new(bytes);
155 while let Some((number, value)) = r.next()? {
156 match number {
157 2 => spec.precompiled_charsmap = value.as_bytes().unwrap_or_default().to_vec(),
158 3 => spec.add_dummy_prefix = value.as_bool().unwrap_or(true),
159 4 => spec.remove_extra_whitespaces = value.as_bool().unwrap_or(true),
160 5 => spec.escape_whitespaces = value.as_bool().unwrap_or(true),
161 _ => {}
162 }
163 }
164 Ok(spec)
165}
166
167fn parse_trainer_spec(bytes: &[u8], model: &mut ModelProto) -> Result<()> {
168 let mut r = Reader::new(bytes);
169 while let Some((number, value)) = r.next()? {
170 match number {
171 3 => {
172 model.model_type = match value.as_i32().unwrap_or(1) {
173 2 => ModelType::Bpe,
174 3 => ModelType::Word,
175 4 => ModelType::Char,
176 _ => ModelType::Unigram,
177 }
178 }
179 24 => model.treat_whitespace_as_suffix = value.as_bool().unwrap_or(false),
180 35 => model.byte_fallback = value.as_bool().unwrap_or(false),
181 40 => model.unk_id = value.as_i32().unwrap_or(0),
182 41 => model.bos_id = value.as_i32().unwrap_or(1),
183 42 => model.eos_id = value.as_i32().unwrap_or(2),
184 43 => model.pad_id = value.as_i32().unwrap_or(-1),
185 44 => {
186 if let Some(s) = value.as_str() {
187 model.unk_surface = s;
188 }
189 }
190 _ => {}
191 }
192 }
193 Ok(())
194}