LIMA
Libre Multilingual Analyzer — C++ API
Loading...
Searching...
No Matches
RnnTokensAnalyzer.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
9
14
23
24#include "RnnTokensAnalyzer.h"
26#include "deeplima/token_type.h"
31
32#include <set>
33
34#define DEBUG_THIS_FILE true
35
37using namespace Lima::Common::PropertyCode;
38using namespace Lima::Common::MediaticData;
39using namespace Lima::Common::Misc;
41using namespace Lima::Common::AnnotationGraphs;
42using namespace std;
43using namespace deeplima;
44
45
47{
48#if defined(DEBUG_LP) && defined(DEBUG_THIS_FILE)
49#define LOG_MESSAGE(stream, msg) stream << msg;
50#define LOG_MESSAGE_WITH_PROLOG(stream, msg) LEMMALOGINIT; LOG_MESSAGE(stream, msg);
51#else
52 #define LOG_MESSAGE(stream, msg) ;
53 #define LOG_MESSAGE_WITH_PROLOG(stream, msg) ;
54#endif
55
57
59
61{
62public:
65 void init(GroupConfigurationStructure& unitConfiguration);
66 void analyzer(vector<segmentation::token_pos>& buffer);
69 void reset();
70
72 MediaId m_language;
74 QString m_data;
75 std::shared_ptr< TokenSequenceAnalyzer<> > m_tokensAnalyzer;
76 std::function<void()> m_load_fn;
79 std::vector< std::map<std::string, std::string> > m_tags;
80 std::vector<QString> m_lemmas;
83 // TokenSequenceAnalyzer<>::OutputMatrix* m_matrix;
84};
85
87 "RnnTokensAnalyzerPrivate", THIS_FILE_LOGGING_CATEGORY()),
88 m_stringsPool(nullptr), m_stridx(),
89 m_pResolver(MediaticData::single().getResourcesPath()),
90 m_loaded(false)
91{
92}
93
95
96
100
102{
103 delete m_d;
104}
105
107{
108 LOG_MESSAGE_WITH_PROLOG(LDEBUG, "RnnTokensAnalyzer::init");
109
110 m_d->m_language = manager->getInitializationParameters().media;
111 m_d->m_stringsPool = &MediaticData::changeable().stringsPool(m_d->m_language);
112
113 m_d->init(unitConfiguration);
114}
115
117{
119 TimeUtilsController RnnTokensAnalyzerProcessTime("RnnTokensAnalyzer");
120 LOG_MESSAGE_WITH_PROLOG(LDEBUG, "RnnTokensAnalyzer::process");
121 m_d->m_analysis = &analysis;
122 auto anagraph = std::dynamic_pointer_cast<AnalysisGraph>(analysis.getData("AnalysisGraph"));
123 if (anagraph == nullptr)
124 {
126 LERROR << "Can't Process RnnTokensAnalyzer: missing data 'AnalysisGraph'";
127 return MISSING_DATA;
128 }
129 auto srcgraph = anagraph->getGraph();
130 auto endVx = anagraph->lastVertex();
135 auto posgraph = new LinguisticAnalysisStructure::AnalysisGraph("PosGraph",
136 m_d->m_language,
137 false,
138 true);
139 analysis.setData("PosGraph", posgraph);
140 const auto& propertyCodeManager = dynamic_cast<const LanguageData&>(
141 MediaticData::single().mediaData(m_d->m_language)).getPropertyCodeManager();
142 const auto& microManager = propertyCodeManager.getPropertyManager("MICRO");
144 auto annotationData = std::dynamic_pointer_cast< AnnotationData >(analysis.getData("AnnotationData"));
145 if (annotationData == nullptr)
146 {
147 annotationData = std::make_shared<AnnotationData>();
151 if (std::dynamic_pointer_cast<AnalysisGraph>(analysis.getData("AnalysisGraph")) != nullptr)
152 {
153 std::dynamic_pointer_cast<AnalysisGraph>(analysis.getData("AnalysisGraph"))->populateAnnotationGraph(
154 annotationData.get(), "AnalysisGraph");
155 }
156 analysis.setData("AnnotationData", annotationData);
157 }
158 if (num_vertices(*srcgraph)<=2)
159 {
160 return SUCCESS_ID;
161 }
162
163 auto vTokens = get(vertex_token, *srcgraph);
164 auto currentVx = anagraph->firstVertex();
165 auto resultgraph = posgraph->getGraph();
166 remove_edge(posgraph->firstVertex(), posgraph->lastVertex(), *resultgraph);
167
168 // Sentence boundaries are stored in SegmentationData (not in token statuses),
169 // so collect the last real token of each sentence here. We mark those with the
170 // deeplima sentence_brk flag so downstream units fed by this token stream
171 // (e.g. the dependency parser) split the text per sentence instead of parsing
172 // it as one long sequence. Segment::getLastVertex() is the vertex *after* the
173 // last token, so the sentence-final token is its in-neighbour.
174 std::set<LinguisticGraphVertex> sentenceFinalVertices;
175 {
176 auto sd = std::dynamic_pointer_cast<SegmentationData>(
177 analysis.getData("SentenceBoundaries"));
178 if (sd != nullptr)
179 {
180 for (const auto& segment : sd->getSegments())
181 {
182 const auto endV = segment.getLastVertex();
183 if (vTokens[endV] != nullptr)
184 {
185 // getLastVertex() is itself the sentence's last token.
186 sentenceFinalVertices.insert(endV);
187 }
188 else
189 {
190 // getLastVertex() is the boundary after the last token: mark
191 // its in-neighbour token(s).
192 LinguisticGraphInEdgeIt ie, ie_end;
193 for (boost::tie(ie, ie_end) = boost::in_edges(endV, *srcgraph);
194 ie != ie_end; ++ie)
195 {
196 const auto s = boost::source(*ie, *srcgraph);
197 if (vTokens[s] != nullptr)
198 {
199 sentenceFinalVertices.insert(s);
200 }
201 }
202 }
203 }
204 }
205 }
206
207 std::vector<segmentation::token_pos> buffer;
208 std::vector< LinguisticGraphVertex > anaVertices;
209 std::vector<std::string> v;
210
211 while(currentVx != endVx)
212 {
213 if (currentVx != 0 && vTokens[currentVx] != nullptr)
214 {
215 const auto& src = vTokens[currentVx];
216 v.push_back(src->stringForm().toStdString());
217 buffer.emplace_back();
218 anaVertices.push_back(currentVx);
219 }
220 LinguisticGraphOutEdgeIt it, it_end;
221 boost::tie(it, it_end) = boost::out_edges(currentVx, *srcgraph);
222 if (it != it_end)
223 {
224 currentVx = boost::target(*it, *srcgraph);
225 }
226 else
227 {
228 currentVx = endVx;
229 }
230 }
234 for(unsigned long k = 0; k < anaVertices.size(); k++)
235 {
236 currentVx = anaVertices[k];
237 if (currentVx != 0 && vTokens[currentVx] != nullptr)
238 {
239 const auto& src = vTokens[currentVx];
240 auto& token = buffer[k];
241 token.m_offset = src->position();
242 // deeplima's token_pos::m_len is a BYTE length and m_pch points to the
243 // UTF-8 bytes of v[k]. src->length() is the LIMA character count, which
244 // truncates multi-byte UTF-8 forms (e.g. "à" → 1 instead of 2 bytes),
245 // leaving the downstream form an incomplete byte sequence that decodes
246 // to an empty string (and crashed the neural lemmatizer). Use the UTF-8
247 // byte length so m_len matches m_pch.
248 token.m_len = v[k].size();
249 token.m_pch = v[k].c_str();
250 // Mark the last token of each sentence (from SegmentationData) so the
251 // stream carries real sentence breaks. The previous code AND-ed the
252 // sequential StatusType enum against a deeplima bit flag, which never
253 // produced a valid sentence_brk.
254 token.m_flags = (sentenceFinalVertices.count(currentVx) > 0)
255 ? token_flags_t::sentence_brk
256 : token_flags_t::none;
257 }
258 }
259 m_d->analyzer(buffer);
260 std::vector<LinguisticGraphVertex>::size_type anaVerticesIndex = 0;
261 auto previousPosVertex = posgraph->firstVertex();
262 /*
263 * Here we add the part of speech data to the tokens
264 * Adding link beetween the node in the analysis graph and the pos graph.
265 */
266 while (anaVerticesIndex < anaVertices.size())
267 {
268 auto anaVertex = anaVertices[anaVerticesIndex];
269 auto newVx = boost::add_vertex(*resultgraph);
270 auto agv = annotationData->createAnnotationVertex();
271 annotationData->addMatching("PosGraph", newVx, "annot", agv);
272 annotationData->addMatching("AnalysisGraph", anaVertex, "PosGraph", newVx);
273 annotationData->annotate(agv, QString::fromUtf8("PosGraph"), newVx);
274
275 auto srcToken = get(vertex_token,*srcgraph,anaVertex);
276
277 auto posData = new MorphoSyntacticData();
278
280 for(const auto& name: m_d->m_tokensAnalyzer->get_class_names())
281 {
282 auto propertyManager = microManager;
283 if(name == "upos")
284 {
285 propertyManager = propertyCodeManager.getPropertyManager("MICRO");
286 }
287 else if(name=="ExtPos" || name=="Style")
288 {
289 continue;
290 }
291 else if(name=="xpos")
292 {
293 propertyManager = propertyCodeManager.getPropertyManager("MACRO");
294 }
295 else
296 {
297 propertyManager = propertyCodeManager.getPropertyManager(name);
298 }
299 auto value = propertyManager.getPropertyValue(m_d->m_tags[anaVerticesIndex][name]);
300 if(value.toBool())
301 {
302 LOG_MESSAGE(LDEBUG, "value: " << value);
303 propertyManager.getPropertyAccessor().writeValue(value, lElement.properties);
304 }
305 }
306 auto sp = &MediaticData::changeable().stringsPool(m_d->m_language);
307 lElement.lemma = (*sp)[m_d->m_lemmas[anaVerticesIndex]];
308 posData->push_back(lElement);
309
310 put(vertex_data, *resultgraph, newVx, posData);
311 put(vertex_token, *resultgraph, newVx, srcToken);
312
313 boost::add_edge(previousPosVertex, newVx, *resultgraph);
314
315 previousPosVertex = newVx;
316 anaVerticesIndex++;
317 }
318 boost::add_edge(previousPosVertex, posgraph->lastVertex(), *resultgraph);
319 LOG_MESSAGE(LDEBUG, "RnnPosLemmatizer postagging done.");
320 TimeUtils::logElapsedTime("RnnTokensAnalyzer");
321 return SUCCESS_ID;
322}
323
325{
326 m_data = QString(getStringParameter(unitConfiguration, "data", 0, "SentenceBoundaries").c_str());
327 QString tagger_model_prefix = getStringParameter(unitConfiguration, "tagger_model_prefix", ConfigurationHelper::REQUIRED | ConfigurationHelper::NOT_EMPTY).c_str();
328 QString lemmatizer_model_prefix = getStringParameter(unitConfiguration, "lemmatizer_model_prefix", ConfigurationHelper::REQUIRED | ConfigurationHelper::NOT_EMPTY).c_str();
329 LOG_MESSAGE_WITH_PROLOG(LDEBUG, "RnnTokensAnalyzerPrivate::init tagger model: " << tagger_model_prefix << " lemmatizer model: " << lemmatizer_model_prefix);
330
331 QString lang_str = MediaticData::single().media(m_language).c_str();
332 QString resources_path = MediaticData::single().getResourcesPath().c_str();
333 QString tagger_model_name = tagger_model_prefix;
334 QString lemmatizer_model_name = lemmatizer_model_prefix;
335 string udlang;
336 MediaticData::single().getOptionValue("udlang", udlang);
337
338 if (!fix_lang_codes(lang_str, udlang))
339 {
341 "RnnTokensAnalyzerPrivate::init: Can't parse language id " << udlang.c_str(),
343 }
344
345 tagger_model_name.replace(QString("$udlang"), QString(udlang.c_str()));
346 lemmatizer_model_name.replace(QString("$udlang"), QString(udlang.c_str()));
347
348 auto tagger_model_file_name = findFileInPaths(resources_path,
349 QString::fromUtf8("/RnnTagger/%1/%2.pt")
350 .arg(lang_str, tagger_model_name));
351 auto lemmatizer_model_file_name = findFileInPaths(resources_path,
352 QString::fromUtf8("/RnnLemmatizer/%1/%2.pt")
353 .arg(lang_str, lemmatizer_model_name));
354 auto lemmatizer_dictionary_file_name = findFileInPaths(resources_path,
355 QString::fromUtf8("/RnnLemmatizer/%1/%2.dic")
356 .arg(lang_str, lemmatizer_model_name));
357 if (tagger_model_file_name.isEmpty())
358 {
359 throw InvalidConfiguration("RnnTokensAnalyzerPrivate::init: tagger model file not found.");
360 }
361 if (lemmatizer_model_file_name.isEmpty())
362 {
363 //throw InvalidConfiguration("RnnTokensAnalyzerPrivate::init: lemmatizer model file not found.");
364 lemmatizer_model_file_name = "";
365 }
366
367 m_load_fn = [this, tagger_model_file_name, lemmatizer_model_file_name, lemmatizer_dictionary_file_name]()
368 {
369 if (m_loaded)
370 {
371 return;
372 }
373 // TODO give the correct parameters for fixed_ini, lower_ini and lower_lemm
374 m_tokensAnalyzer = std::make_shared< TokenSequenceAnalyzer<> >(tagger_model_file_name.toStdString(),
375 lemmatizer_model_file_name.toStdString(),
376 lemmatizer_dictionary_file_name.toStdString(),
377 "", "", "",
378 m_pResolver, 1024, 8);
379 m_loaded = true;
380 };
381
382 if (!isInitLazy())
383 {
384 m_load_fn();
385 }
386 for (size_t i = 0; i < m_tokensAnalyzer->get_classes().size(); ++i)
387 {
388 m_dumper.set_classes(i, m_tokensAnalyzer->get_class_names()[i], m_tokensAnalyzer->get_classes()[i]);
389 }
390
391}
392
394{
395 m_tags.clear();
396 m_lemmas.clear();
397}
398
399void RnnTokensAnalyzerPrivate::analyzer(std::vector<segmentation::token_pos> &buffer)
400{
401 reset();
402 m_tokensAnalyzer->register_handler([this](std::shared_ptr< StringIndex > stridx,
403 const token_buffer_t<>& tokens,
404 const std::vector<StringIndex::idx_t>& lemmata,
405 std::shared_ptr< StdMatrix<uint8_t> > classes,
406 size_t begin,
407 size_t end)
408 {
409 auto ti = std::make_shared<TokenSequenceAnalyzer<>::TokenIterator>(*stridx, tokens, lemmata, classes, begin, end);
410 auto tiData = std::make_shared<TokenIteratorData>();
411 tiData->setTokenIterator(ti);
412 // auto si = std::make_shared<deeplima::StringIndex>(stridx);
413 tiData->setStringIndex(stridx);
414 m_analysis->setData("TokenIterator", tiData);
415 // m_matrix = new TokenSequenceAnalyzer<>::OutputMatrix(classes);
416 insertTokenInfo(*ti);
417 });
418 LOG_MESSAGE_WITH_PROLOG(LDEBUG, buffer[0].m_pch);
419 (*m_tokensAnalyzer)(buffer, buffer.size());
420 m_tokensAnalyzer->finalize();
421}
422
424{
425 auto classes = m_dumper.getMClasses();
426 auto class_names = m_tokensAnalyzer->get_class_names();
427 LOG_MESSAGE_WITH_PROLOG(LDEBUG, "classes: " << class_names);
428 while(!ti.end())
429 {
430 auto tag = std::map<std::string, std::string>();
431 LOG_MESSAGE(LDEBUG, ti.lemma());
432 for(uint cat = 0; cat < class_names.size(); cat++)
433 {
434 tag.insert({class_names[cat], classes[cat][ti.token_class(cat)]});
435 }
436 m_tags.push_back(tag);
437 m_lemmas.emplace_back(ti.lemma());
438 ti.next();
439 }
440 ti.reset();
441}
442
443
444}
This file is the main header file for the data related to annotation graphs.
#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 LERROR
Definition LimaCommon.h:161
A graph structure for linguistic analysis.
LinguisticGraph::in_edge_iterator LinguisticGraphInEdgeIt
@ vertex_token
@ vertex_data
LinguisticGraph::out_edge_iterator LinguisticGraphOutEdgeIt
#define LEMMALOGINIT
#define RNNTOKENSANALYZER_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.
Holds linguistic data for one language.
holds data about codes and names for grammatical categories, etc.
Provide function to read write and check a property.
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="")
dumper::AnalysisToConllU< TokenSequenceAnalyzer<>::TokenIterator > m_dumper
LimaStatusCode process(AnalysisContent &analysis) const override
Process on data in analysisContent.
void init(Lima::Common::XMLConfigurationFiles::GroupConfigurationStructure &unitConfiguration, Manager *manager) override
initialize with parameters from configuration file.
An AnalysisData containing a LinguisticGraph with a language and an id.
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
void set_classes(size_t idx, const std::string &class_name, const std::vector< std::string > &data)
const std::vector< std::vector< std::string > > & getMClasses() const
QString findFileInPaths(const QString &paths, const QString &fileName, const QChar &separator)
Find the given file in the given paths.
static SimpleFactory< MediaProcessUnit, RnnTokensAnalyzer > RnnTokensAnalyzerFactory(RNNTOKENSANALYZER_CLASSID)
bool fix_lang_codes(QString &lang_str, std::string &udlang)
LimaStatusCode
Definition LimaCommon.h:236
@ SUCCESS_ID
Definition LimaCommon.h:237
@ MISSING_DATA
Definition LimaCommon.h:243
STL namespace.