usecrate::grapheme::GraphemeClusterSegmenter; usecrate::provider::*; use alloc::vec::Vec; use core::char::{decode_utf16, REPLACEMENT_CHARACTER}; use zerovec::{maps::ZeroMapBorrowed, ule::UnvalidatedStr};
mod matrix; use matrix::*;
// A word break iterator using LSTM model. Input string have to be same language.
impl<'l> LstmSegmenter<'l> { /// Returns `Err` if grapheme data is required but not present pub(super) fn new(lstm: &'l LstmDataV1<'l>, grapheme: &'color:blue'>'l RuleBreakDataV1<'l>) -> Self { let LstmDataV1::Float32(lstm) = lstm; let time_w = MatrixZero::from(&lstm.time_w); #[allow(clippy::unwrap_used)] // shape (2, 4, hunits) let timew_fw = time_w.submatrix(0).unwrap(); #[allow(clippy::unwrap_used)] // shape (2, 4, hunits) let timew_bw = time_w.submatrix(1).unwrap(); Self {
dic: lstm.dic.as_borrowed(),
embedding: MatrixZero::from(&lstm.embedding),
fw_w: MatrixZero::from(&lstm.fw_w),
fw_u: MatrixZero::from(&lstm.fw_u),
fw_b: MatrixZero::from(&lstm.fw_b),
bw_w: MatrixZero::from(&lstm.bw_w),
bw_u: MatrixZero::from(&lstm.bw_u),
bw_b: MatrixZero::from(&lstm.bw_b),
timew_fw,
timew_bw,
time_b: MatrixZero::from(&lstm.time_b),
grapheme: (lstm.model == ModelType::GraphemeClusters).then_some(grapheme),
}
}
/// Create an LSTM based break iterator for an `str` (a UTF-8 string). pub(super) fn segment_str(&'l self, input: &'l str) -> impl Iterator<Item = usize> + 'l { self.segment_str_p(input)
}
// For unit testing as we cannot inspect the opaque type's bies fn segment_str_p(&'l self, input: &'l str) -> LstmSegmenterIterator<'l> { let input_seq = iflet Some(grapheme) = self.grapheme {
GraphemeClusterSegmenter::new_and_segment_str(input, grapheme)
.collect::<Vec<usize>>()
.windows(2)
.map(|chunk| { let range = iflet [first, second, ..] = chunk {
*first..*second
} else {
unreachable!()
}; let grapheme_cluster = iflet Some(grapheme_cluster) = input.get(range) {
grapheme_cluster
} else { returnself.dic.len() as u16;
};
impl<'l> BiesIterator<'l> { // input_seq is a sequence of id numbers that represents grapheme clusters or code points in the input line. These ids are used later // in the embedding layer of the model. fn new(segmenter: &'l LstmSegmenter<'l>, input_seq: Vec<u16>) -> Self{ let hunits = segmenter.fw_u.dim().1;
// Backward LSTM letmut c_bw = MatrixOwned::<1>::new_zero([hunits]); letmut h_bw = MatrixOwned::<2>::new_zero([input_seq.len(), hunits]); for (i, &g_id) in input_seq.iter().enumerate().rev() { if i + 1 < input_seq.len() {
h_bw.as_mut().copy_submatrix::<1>(i + 1, i);
} #[allow(clippy::unwrap_used)]
compute_hc(
segmenter.embedding.submatrix::<1>(g_id as usize).unwrap(), /* shape (dict.len() + 1, hunit), g_id is at most dict.len() */
h_bw.submatrix_mut(i).unwrap(), // shape (input_seq.len(), hunits)
c_bw.as_mut(),
segmenter.bw_w,
segmenter.bw_u,
segmenter.bw_b,
);
}
impl Iterator for BiesIterator<'_> { type Item = bool;
fn next(&mutself) -> Option<Self::Item> { let (i, g_id) = self.input_seq.next()?;
#[allow(clippy::unwrap_used)]
compute_hc( self.segmenter
.embedding
.submatrix::<1>(g_id as usize)
.unwrap(), // shape (dict.len() + 1, hunit), g_id is at most dict.len() self.curr_fw.as_mut(), self.c_fw.as_mut(), self.segmenter.fw_w, self.segmenter.fw_u, self.segmenter.fw_b,
);
#[allow(clippy::unwrap_used)] // shape (input_seq.len(), hunits) let curr_bw = self.h_bw.submatrix::<1>(i).unwrap(); letmut weights = [0.0; 4]; letmut curr_est = MatrixBorrowedMut {
data: &mut weights,
dims: [4],
};
curr_est.add_dot_2d(self.curr_fw.as_borrowed(), self.segmenter.timew_fw);
curr_est.add_dot_2d(curr_bw, self.segmenter.timew_bw); #[allow(clippy::unwrap_used)] // both shape (4)
curr_est.add(self.segmenter.time_b).unwrap(); // For correct BIES weight calculation we'd now have to apply softmax, however // we're only doing a naive argmax, so a monotonic function doesn't make a difference.
#[allow(clippy::unwrap_used)] // first dimension is 4
s_t.submatrix_mut::<1>(0).unwrap().sigmoid_transform(); #[allow(clippy::unwrap_used)] // first dimension is 4
s_t.submatrix_mut::<1>(1).unwrap().sigmoid_transform(); #[allow(clippy::unwrap_used)] // first dimension is 4
s_t.submatrix_mut::<1>(2).unwrap().tanh_transform(); #[allow(clippy::unwrap_used)] // first dimension is 4
s_t.submatrix_mut::<1>(3).unwrap().sigmoid_transform();
#[allow(clippy::unwrap_used)] // first dimension is 4
c_tm1.convolve(
s_t.as_borrowed().submatrix(0).unwrap(),
s_t.as_borrowed().submatrix(2).unwrap(),
s_t.as_borrowed().submatrix(1).unwrap(),
);
#[allow(clippy::unwrap_used)] // first dimension is 4
h_tm1.mul_tanh(s_t.as_borrowed().submatrix(3).unwrap(), c_tm1.as_borrowed());
}
#[cfg(test)] mod tests { usesuper::*; use icu_locid::langid; use icu_provider::prelude::*; use serde::Deserialize;
/// `TestCase` is a struct used to store a single test case. /// Each test case has two attributes: `unseg` which denotes the unsegmented line, and `true_bies` which indicates the Bies /// sequence representing the true segmentation. #[derive(PartialEq, Debug, Deserialize)] struct TestCase {
unseg: String,
expected_bies: String,
true_bies: String,
}
/// `TestTextData` is a struct to store a vector of `TestCase` that represents a test text. #[derive(PartialEq, Debug, Deserialize)] struct TestTextData {
testcases: Vec<TestCase>,
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.