LIMA
Libre Multilingual Analyzer — C++ API
Loading...
Searching...
No Matches
RnnTokenizer.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2022 CEA LIST <gael.de-chalendar@cea.fr>
2//
3// SPDX-License-Identifier: MIT
4
5#include <QtCore/QTemporaryFile>
6#include <QtCore/QRegularExpression>
7#include <QDir>
8
15
23
24#include "RnnTokenizer.h"
25
27#include "deeplima/token_type.h"
28
29
30
31#define DEBUG_THIS_FILE true
32
34using namespace Lima::Common::PropertyCode;
35using namespace Lima::Common::MediaticData;
36using namespace Lima::Common::Misc;
38
39using namespace deeplima;
40
41namespace Lima
42{
43namespace LinguisticProcessing
44{
45namespace DeepLimaUnits
46{
47namespace RnnTokenizer
48{
49
51
52#if defined(DEBUG_LP) && defined(DEBUG_THIS_FILE)
53 #define LOG_MESSAGE(stream, msg) stream << msg;
54 #define LOG_MESSAGE_WITH_PROLOG(stream, msg) TOKENIZERLOGINIT; LOG_MESSAGE(stream, msg);
55#else
56 #define LOG_MESSAGE(stream, msg) ;
57 #define LOG_MESSAGE_WITH_PROLOG(stream, msg) ;
58#endif
59
61
63{
64 friend RnnTokenizer;
65public:
67 virtual ~RnnTokenizerPrivate();
68
70 {
72 TPrimitiveToken(const QString& w,
73 int pos,
74 const QString& orig=QString())
75 : wordText(w), originalText(orig), start(pos)
76 { }
77
78 QString wordText;
79 QString originalText;
80 int start;
81 };
82
83 void init(GroupConfigurationStructure& unitConfiguration);
84 void tokenize(const QString& text, std::vector<std::vector<TPrimitiveToken>>& sentences);
85
86protected:
87 void append_new_word(std::vector< TPrimitiveToken >& current_sentence,
88 const QString& current_token,
89 int current_token_offset) const;
90
91 MediaId m_language;
94 QString m_data;
95
97
98 std::map<QString, std::vector<QString>> m_trrules;
99
100 // Parameters
102
103 std::shared_ptr<segmentation::impl::SegmentationImpl> m_segm;
104
105 std::function<void()> m_load_fn;
107};
108
110 ConfigurationHelper("RnnTokenizerPrivate", THIS_FILE_LOGGING_CATEGORY()),
111 m_stringsPool(nullptr),
112 m_currentVx(0),
113 m_ignoreEOL(false),
114 m_segm(nullptr),
115 m_loaded(false)
116{
117}
118
122
127
129{
130 delete m_d;
131}
132
134 GroupConfigurationStructure& unitConfiguration,
135 Manager* manager)
136{
137 LOG_MESSAGE_WITH_PROLOG(LDEBUG, "RnnTokenizer::init");
138
139 m_d->m_language = manager->getInitializationParameters().media;
140 m_d->m_stringsPool = &MediaticData::changeable().stringsPool(m_d->m_language);
141
142 m_d->init(unitConfiguration);
143}
144
146{
148 LOG_MESSAGE_WITH_PROLOG(LINFO, "start tokenizer process");
149 TimeUtilsController RnnTokenizerProcessTime("RnnTokenizer");
150
151
152 auto anagraph = std::make_shared<AnalysisGraph>("AnalysisGraph", m_d->m_language, true, true);
153 analysis.setData("AnalysisGraph", anagraph);
154 auto graph = anagraph->getGraph();
155 m_d->m_currentVx = anagraph->firstVertex();
156 // Get text from analysis
157 auto originalText = std::dynamic_pointer_cast<LimaStringText>(analysis.getData("Text"));
158 if (originalText == nullptr)
159 {
161 LERROR << "Can't Process RnnTokenizer: missing data 'Text'";
162 return MISSING_DATA;
163 }
164
165 // Execute model on the text
166 std::vector< std::vector< RnnTokenizerPrivate::TPrimitiveToken > > sentencesTokens;
167 m_d->tokenize(*originalText, sentencesTokens);
168 LOG_MESSAGE(LDEBUG, " Number of token '" << sentencesTokens.size() << "'");
169
170 // Insert the tokens in the graph and create sentence limits
171 auto sb = std::make_shared<SegmentationData>("AnalysisGraph");
172 analysis.setData(m_d->m_data.toStdString(), sb);
173
174 // Remove the default 0 -> 1 edge before adding tokens
175 remove_edge(anagraph->firstVertex(),
176 anagraph->lastVertex(),
177 *graph);
178 LinguisticGraphVertex beginSentence = 0;
179
180 // Insert the tokens in the graph and create sentence limits
181 for (const auto& sentence: sentencesTokens)
182 {
183 if (sentence.size() < 1)
184 {
185 continue;
186 }
187
188 auto endSentence = std::numeric_limits< LinguisticGraphVertex >::max();
189 for (const auto& token: sentence)
190 {
191 const auto& str = token.wordText;
192
193 LOG_MESSAGE(LDEBUG, " Adding token '" << str << "'");
194
195 auto form = (*m_d->m_stringsPool)[str];
196 Token *tToken = new Token(form, str, token.start+1, token.wordText.size());
197 if (tToken == nullptr)
198 {
200 LERROR << "RnnFlowTokenizer::process: Can't allocate memory with \"new Token(...)\"";
201 throw MemoryErrorException();
202 }
203
204 if (token.originalText.size() > 0)
205 {
206 // tranduced token
207 // save original word as orthographic alternative
208 auto orig = (*m_d->m_stringsPool)[token.originalText];
209 tToken->addOrthographicAlternatives(orig);
210 }
211
212 m_d->computeDefaultStatus(*tToken);
213
214 LOG_MESSAGE(LDEBUG, " status is " << tToken->status().toString());
215
216 // Adds on the path
217 auto newVx = add_vertex(*graph);
218 endSentence = newVx;
219 put(vertex_token, *graph, newVx, tToken);
220 put(vertex_data, *graph, newVx, new MorphoSyntacticData());
221 add_edge(m_d->m_currentVx, newVx, *graph);
222 m_d->m_currentVx = newVx;
223 }
224
225 LOG_MESSAGE(LDEBUG, "adding sentence" << beginSentence << endSentence);
226
227 sb->add(Segment("sentence", beginSentence, endSentence, anagraph.get()));
228 beginSentence = endSentence;
229 }
230
231 add_edge(m_d->m_currentVx, anagraph->lastVertex(), *graph);
232
233 TimeUtils::logElapsedTime("RnnTokenizer");
234 return SUCCESS_ID;
235}
236
238{
239 m_data = QString(getStringParameter(unitConfiguration, "data", 0, "SentenceBoundaries").c_str());
240 auto model_prefix = QString::fromStdString(
241 getStringParameter(unitConfiguration, "model_prefix",
243
244 LOG_MESSAGE_WITH_PROLOG(LDEBUG, "RnnTokenizerPrivate::init" << model_prefix);
245
246 auto lang_str = QString::fromStdString(MediaticData::single().media(m_language));
247 auto resources_path = QString::fromStdString(MediaticData::single().getResourcesPath());
248 auto model_name = model_prefix;
249 std::string udlang;
250 MediaticData::single().getOptionValue("udlang", udlang);
251 LOG_MESSAGE(LDEBUG, "RnnTokenizerPrivate::init lang_str=" << lang_str << ", udlang=" << udlang);
252
253 if (!fix_lang_codes(lang_str, udlang))
254 {
256 "RnnTokenizerPrivate::init: Can't parse language id " << udlang.c_str(),
258 }
259
260 model_name.replace(QString("$udlang"), QString::fromStdString(udlang));
261
262 auto model_file_name = findFileInPaths(resources_path,
263 QString::fromUtf8("/RnnTokenizer/%1/%2.pt")
264 .arg(lang_str).arg(model_name));
265 if (model_file_name.isEmpty())
266 {
267 throw InvalidConfiguration("RnnTokenizerPrivate::init: tokenizer model file not found.");
268 }
269
270 m_load_fn = [this, model_file_name]()
271 {
272 // if (m_loaded)
273 // {
274 // return;
275 // }
276
277 m_segm->load(model_file_name.toStdString());
278 m_segm->init(1, 16*1024); // threads, buffer size per thread
279
280 m_loaded = true;
281 };
282
283 // if (!isInitLazy())
284 // {
285 // m_load_fn();
286 // }
287}
288
289void RnnTokenizerPrivate::append_new_word(std::vector< TPrimitiveToken >& current_sentence,
290 const QString& current_token,
291 int current_token_offset) const
292{
293 auto ctoken_lower = current_token.toLower();
294
295 auto i = m_trrules.find(ctoken_lower);
296 if (i == m_trrules.end())
297 {
298 current_sentence.push_back(TPrimitiveToken(current_token, current_token_offset));
299 }
300 else
301 {
302 size_t n = 0;
303 for (const auto& w : i->second)
304 {
305 if (n == 0)
306 {
307 current_sentence.push_back(TPrimitiveToken(w, current_token_offset, current_token));
308 }
309 else
310 {
311 current_sentence.push_back(TPrimitiveToken(w, current_token_offset));
312 }
313 }
314 }
315}
316
317void RnnTokenizerPrivate::tokenize(const QString& text, std::vector<std::vector<TPrimitiveToken>>& sentences)
318{
319 m_segm = std::make_shared<segmentation::impl::SegmentationImpl>();
320
321 m_load_fn();
322
323 LOG_MESSAGE_WITH_PROLOG(LDEBUG, "RnnTokenizerPrivate::tokenize" << text.left(100));
325 sentences.clear();
326 sentences.reserve(text.size() / 15);
327
328 std::vector< TPrimitiveToken > current_sentence;
329 int current_token_offset = 0;
330
331 auto text_utf8 = text.toStdString();
332
333 m_segm->register_handler([this, &sentences, &current_sentence, &current_token_offset]
334 (const std::vector<segmentation::token_pos>& tokens,
335 uint32_t len)
336 {
337 for (size_t i = 0; i < len; i++)
338 {
339 const auto& tok = tokens[i];
340 if (0 == tok.m_len)
341 {
342 continue;
343 }
344 append_new_word(current_sentence, QString::fromUtf8(tok.m_pch, tok.m_len), current_token_offset);
345 current_token_offset += (tok.m_offset + tok.m_len);
346 if (tok.m_flags & token_flags_t::sentence_brk)
347 {
348 sentences.push_back(current_sentence);
349 current_sentence.clear();
350 }
351 }
352 });
353
354 size_t bytes_consumed = 0;
355 m_segm->parse_from_stream([&text_utf8, &bytes_consumed]
356 (uint8_t* buffer,
357 int32_t& read,
358 size_t max)
359 {
360 read = (text_utf8.size() - bytes_consumed) > max ? max : (text_utf8.size() - bytes_consumed);
361 memcpy(buffer, text_utf8.c_str() + bytes_consumed, read);
362 bytes_consumed += read;
363 return (text_utf8.size() - bytes_consumed) > max;
364 });
365
366 LOG_MESSAGE(LINFO, "End of Tokenizer");
367 TimeUtils::logElapsedTime("RnnTokenizerPrivate");
368}
369
370} // namespace RnnTokenizer
371} // namespace DeepLimaUnits
372} // namespace LinguisticProcessing
373} // namespace Lima
#define CONFIGURATIONHELPER_LOGGING_INIT(X)
#define LOG_MESSAGE_WITH_PROLOG(stream, msg)
#define LOG_MESSAGE(stream, msg)
#define LIMA_EXCEPTION_SELECT_LOGINIT(X, Y, Z)
This macro writes the message Y to the error stream configured by its first parameter X,...
Definition LimaCommon.h:332
#define LDEBUG
Definition LimaCommon.h:157
#define LINFO
Definition LimaCommon.h:158
#define LERROR
Definition LimaCommon.h:161
@ vertex_token
LinguisticGraph::vertex_descriptor LinguisticGraphVertex
@ vertex_data
#define TOKENIZERLOGINIT
#define RNNTOKENIZER_CLASSID
Defines a Factory to create Object of type Base.
Holds all data that pass through the ProcessUnits Analysis data are shared pointers,...
std::shared_ptr< AnalysisData > getData(const QString &id)
return AnalysisData by id
void setData(const QString &id, std::shared_ptr< AnalysisData > data)
set an analysisData with the given id.
Manage initialization of InitializableObjects using configuration module and parameters.
const InitializationParameters & getInitializationParameters() const
get Initialization Parameters
Use this exception to signal an error in one of the configuration files.
Definition LimaCommon.h:345
void getStringParameter(Common::XMLConfigurationFiles::GroupConfigurationStructure &unitConfiguration, const std::string &name, std::string &value, int flags=Flags::REQUIRED, std::string default_value="")
void init(GroupConfigurationStructure &unitConfiguration)
void tokenize(const QString &text, std::vector< std::vector< TPrimitiveToken > > &sentences)
std::shared_ptr< segmentation::impl::SegmentationImpl > m_segm
void append_new_word(std::vector< TPrimitiveToken > &current_sentence, const QString &current_token, int current_token_offset) const
This is a MediaProcessUnit that is usually the first element of the pipeline.
void init(Lima::Common::XMLConfigurationFiles::GroupConfigurationStructure &unitConfiguration, Manager *manager) override
initialize with parameters from configuration file.
LimaStatusCode process(AnalysisContent &analysis) const override
Process on data in analysisContent.
virtual void computeDefaultStatus(LinguisticAnalysisStructure::TStatus &curSettings)
This file contains a class to control log of informations about time, such as logging cumulated time ...
static void logElapsedTime(const std::string &mess, const std::string &taskCategory=std::string(""))
log the number of microseconds since last UpdateCurrentTime
static void updateCurrentTime(const std::string &taskCategory=std::string(""))
store current time for new elapsed time computation
QString findFileInPaths(const QString &paths, const QString &fileName, const QChar &separator)
Find the given file in the given paths.
static SimpleFactory< MediaProcessUnit, RnnTokenizer > rnntokenizerFactory(RNNTOKENIZER_CLASSID)
bool fix_lang_codes(QString &lang_str, std::string &udlang)
NAUTITIA.
LimaStatusCode
Definition LimaCommon.h:236
@ SUCCESS_ID
Definition LimaCommon.h:237
@ MISSING_DATA
Definition LimaCommon.h:243