LIMA
Libre Multilingual Analyzer — C++ API
Loading...
Searching...
No Matches
SvmToolPosTagger.cpp
Go to the documentation of this file.
1// Copyright 2002-2013 CEA LIST
2// SPDX-FileCopyrightText: 2022 CEA LIST <gael.de-chalendar@cea.fr>
3//
4// SPDX-License-Identifier: MIT
5
6#include "SvmToolPosTagger.h"
7
15#include "svmtool/tagger.h"
16
17#include <boost/algorithm/string.hpp>
18
19
20int verbose = FALSE;
21
22using namespace Lima::Common::AnnotationGraphs;
23using namespace Lima::Common::Misc;
24using namespace Lima::Common::MediaticData;
25using namespace Lima::Common::PropertyCode;
28
29namespace Lima
30{
31namespace LinguisticProcessing
32{
33namespace PosTagger
34{
35
36SvmToolPosTaggerFactory* SvmToolPosTaggerFactory::s_instance=new SvmToolPosTaggerFactory(SVMTOOLPOSTAGGER_CLASSID);
37
38SvmToolPosTaggerFactory::SvmToolPosTaggerFactory(const std::string& id) :
40{}
41
42std::shared_ptr<MediaProcessUnit> SvmToolPosTaggerFactory::create(
44 MediaProcessUnit::Manager* manager) const
45{
46// PTLOGINIT;
47 auto posTagger = std::make_shared<SvmToolPosTagger>();
48 posTagger->init(unitConfiguration, manager);
49
50 return posTagger;
51}
52
53 struct PredData
54 {
60 uint64_t m_predIndex;
61 std::vector<LinguisticCode> m_predPredMicros;
62 inline bool operator<(const PredData& pd) const { return m_predMicro<pd.m_predMicro; }
63 };
64
65 typedef std::map< LinguisticCode, std::vector<PredData> > MicroCatDataMap;
66 typedef std::map< LinguisticCode, std::vector<PredData> >::iterator MicroCatDataMapItr;
67 typedef std::vector<PredData>::iterator PredDataVectorItr;
68 typedef std::vector<PredData>::const_iterator PredDataVectorCItr;
69
76
77 typedef std::vector<StepData> StepDataVector;
78 typedef std::vector<StepData>::iterator StepDataVectorItr;
79
81 {
84 std::vector<LinguisticCode> m_preds;
85 bool operator<(const TargetVertexId& tvi) const
86 {
87 if (m_sourceVx!=tvi.m_sourceVx) return m_sourceVx<tvi.m_sourceVx;
88 if (m_categ!=tvi.m_categ) return m_categ<tvi.m_categ;
89 return m_preds<tvi.m_preds;
90 }
91 };
92
94{
95 friend class SvmToolPosTagger;
96
98 ~SvmToolPosTaggerPrivate() = default;
100 SvmToolPosTaggerPrivate& operator=(const SvmToolPosTaggerPrivate&) = delete;
101
102 MediaId m_language;
103 std::unique_ptr<tagger> m_tagger;
104 std::string m_model;
105 bool m_allFeatures;
106 QStringList m_features;
107};
108
109SvmToolPosTaggerPrivate::SvmToolPosTaggerPrivate() :
110 m_language(0),
111 m_tagger(nullptr),
112 m_model(),
113 m_allFeatures(false),
114 m_features()
115{
116}
117
122
124{
125 delete m_d;
126}
127
128
130 GroupConfigurationStructure& unitConfiguration,
131 Manager* manager)
132
133{
145 PTLOGINIT;
146 m_d->m_language = manager->getInitializationParameters().media;
147 auto resourcesPath = MediaticData::single().getResourcesPath();
148 try
149 {
150 auto modelName = unitConfiguration.getParamsValueAtKey("model");
151 // add .DICT to find the file, remove it to get the generic model name + path
152 m_d->m_model = findFileInPaths(resourcesPath.c_str(),
153 modelName.append(".DICT").c_str()).toUtf8().constData();
154 boost::replace_last(m_d->m_model,".DICT","");
155 }
157 {
158 LWARN << "No SVMTool model defined in configuration file !";
159 throw InvalidConfiguration();
160 }
161 try
162 {
163 m_d->m_allFeatures = unitConfiguration.getBooleanParameter("allFeatures");
164 }
166 {
167 // Ignored parameters allFeatures and features are optional. Then use only
168 // main tag (micro category)
169 }
170 if (!m_d->m_allFeatures)
171 {
172 try
173 {
174 auto features = unitConfiguration.getListsValueAtKey("features");
175 for (const auto& feature: features)
176 {
177 m_d->m_features << QString::fromUtf8(feature.c_str());
178 }
179 }
181 {
182 // Ignored parameters allFeatures and features are optional. Then use only
183 // main tag (micro category)
184 }
185 }
186 LDEBUG << "Creating SVM Tagger with model: " << m_d->m_model;
187 erCompRegExp();
188 m_d->m_tagger = std::make_unique<tagger>(m_d->m_model.c_str());
189// // m_d->m_taggertaggerPutBackupDictionary(const std::string& dictName);
190// m_d->m_tagger->taggerPutStrategy(0);
191// m_d->m_tagger->taggerPutFlow("RL");
192// m_d->m_tagger->taggerPutKWeightFilter(0.455);
193// m_d->m_tagger->taggerPutUWeightFilter(0.1535);
194 m_d->m_tagger->taggerLoadModelsForTagging();
195
196// WinIndex and WinLength are loaded from model.WIN at run time
197// m_d->m_tagger->taggerPutWinLength(5);
198// m_d->m_tagger->taggerPutWinIndex(2);
199
200}
201
203{
204 Lima::TimeUtilsController timer("SvmToolPosTagger");
205
206 // start postagging here !
207#ifdef DEBUG_LP
208 PTLOGINIT;
209 LINFO << "start SvmToolPosTager";
210#endif
211 const auto& ldata = static_cast<const LanguageData&>(
213 const auto& microManager = ldata.getPropertyCodeManager().getPropertyManager("MICRO");
214 auto& microAccessor = microManager.getPropertyAccessor();
215 // Retrieve morphosyntactic graph
216 auto anagraph = std::dynamic_pointer_cast<AnalysisGraph>(analysis.getData("AnalysisGraph"));
217 auto srcgraph = anagraph->getGraph();
218 auto endVx = anagraph->lastVertex();
219
224 auto posgraph = new LinguisticAnalysisStructure::AnalysisGraph("PosGraph",
225 m_d->m_language,
226 false,
227 true);
228 analysis.setData("PosGraph",posgraph);
229
231 auto annotationData = std::dynamic_pointer_cast<AnnotationData>(analysis.getData("AnnotationData"));
232 if (annotationData==0)
233 {
234 annotationData = std::make_shared<AnnotationData>();
238 if (std::dynamic_pointer_cast<AnalysisGraph>(analysis.getData("AnalysisGraph")) != 0)
239 {
240 static_cast<AnalysisGraph*>(
241 analysis.getData("AnalysisGraph").get())->populateAnnotationGraph(
242 annotationData.get(),
243 "AnalysisGraph");
244 }
245 analysis.setData("AnnotationData",annotationData);
246 }
247
248 // if graph is empty then do nothing
249 // graph is empty if it has only 2 vertices, start (0) and end (0)
250 if (num_vertices(*srcgraph)<=2)
251 {
252 return SUCCESS_ID;
253 }
254 VertexTokenPropertyMap tokens = get(vertex_token, *srcgraph);
255
256 // Create postagging graph
257 LinguisticGraph* resultgraph=posgraph->getGraph();
258 remove_edge(posgraph->firstVertex(),posgraph->lastVertex(),*resultgraph);
259
260 const auto& propertyCodeManager = static_cast<const LanguageData&>(
261 MediaticData::single().mediaData(m_d->m_language)).getPropertyCodeManager();
262 // to add tokens possible tags to the tagger dictionary
263 const auto& propertyManagers = propertyCodeManager.getPropertyManagers();
264
265 // TODO create a wrapper on the analysis graph to read tokens in a stream
266 std::ostringstream oss("");
267 LinguisticGraphVertex currentVx=anagraph->firstVertex();
268 std::vector< LinguisticGraphVertex > anaVertices;
269 while (currentVx != endVx)
270 {
271 if (currentVx != 0 && tokens[currentVx] != 0)
272 {
273 auto tok = tokens[currentVx];
274 QString token = tok->stringForm();
275 // if token is a newline, the SVMToolPosTagger will fail, replace it by
276 // Unicode char U+200B ZERO WIDTH SPACE
277 if (token.contains('\n'))
278 {
279 PTLOGINIT;
280 LWARN << "Error in SVMTagger. Invalid token with newline(s):" << token;
281 LWARN << "Avoiding the problem but the tokenizer should be checked.";
282 }
283 if (token == QString::fromUtf8("\n")) token = QString::fromUtf8((char*)u8"\u200B");
284 token = token.trimmed();
285 token.replace(" ", "_");
286 token.replace("\n", QString::fromUtf8((char*)u8"\u200B"));
287 std::ostringstream lineoss("");
288 lineoss << token.toStdString() << " (";
289 auto morphoData = get(vertex_data,*srcgraph,currentVx);
290 for (auto morphDataIt = morphoData->begin();
291 morphDataIt != morphoData->end(); morphDataIt++)
292 {
293 if (morphDataIt != morphoData->begin())
294 {
295 lineoss << ",";
296 }
297 QString fullTag;
298 QTextStream tagStream(&fullTag);
299 auto tag = microManager.getPropertySymbolicValue(
300 (*morphDataIt).properties);
301 tagStream << QString::fromUtf8(tag.c_str());
302 if (m_d->m_allFeatures ||!m_d->m_features.isEmpty())
303 {
304 QStringList features;
305 for (auto propItr = propertyManagers.cbegin();
306 propItr != propertyManagers.cend(); propItr++)
307 {
308 if (!propItr->second.getPropertyAccessor().empty((*morphDataIt).properties))
309 {
310 auto property = QString::fromUtf8(propItr->first.c_str());
311 auto value = QString::fromUtf8(
312 propItr->second.getPropertySymbolicValue(
313 (*morphDataIt).properties).c_str());
314 if (property != "MACRO" && property != "MICRO"
315 && (m_d->m_allFeatures || m_d->m_features.contains(property)))
316 {
317 features << QString("%1=%2").arg(property).arg(value);
318 }
319 }
320 }
321 features.sort();
322 if (!features.isEmpty())
323 {
324 tagStream << "-";
325 for (auto it = features.cbegin(); it != features.cend(); it++)
326 {
327 if (it != features.cbegin())
328 {
329 tagStream << "|";
330 }
331 tagStream << *it;
332 }
333 }
334 }
335 lineoss << fullTag.toUtf8().constData();;
336 }
337 lineoss << ")";
338 lineoss << std::endl;
339 oss << lineoss.str();
340 anaVertices.push_back(currentVx);
341 }
342 LinguisticGraphOutEdgeIt it, it_end;
343 boost::tie(it, it_end) = boost::out_edges(currentVx, *srcgraph);
344 if (it != it_end)
345 {
346 currentVx = boost::target(*it, *srcgraph);
347 }
348 else
349 {
350 currentVx = endVx;
351 }
352 }
353#ifdef DEBUG_LP
354 LDEBUG << "Tagging '" << oss.str() << "'";
355#endif
356 std::istringstream iss(oss.str());
357
358 std::stringstream resOss;
359 // TODO create a wrapper with a ostream interface to put results into the pos graph
360 // TODO have to take data from the analysis graph nodes too
361
362 // start processing postagging
363 //m_tagger->taggerShowNoComments();
364 m_d->m_tagger->taggerInit(iss, resOss);
365 m_d->m_tagger->taggerRun();
366
367 // read results
368 resOss.seekp(0, std::ios::beg);
369 std::string resultLine;
370 std::vector<LinguisticGraphVertex>::size_type anaVerticesIndex = 0;
371 LinguisticGraphVertex previousPosVertex = posgraph->firstVertex();
372 while (anaVerticesIndex < anaVertices.size()
373 && std::getline(resOss,resultLine))
374 {
375#ifdef DEBUG_LP
376 LDEBUG << "Result line: '" << resultLine << "'";
377#endif
378// if (resultLine.empty())
379// {
380// continue;
381// }
382 auto elements = QString::fromStdString(resultLine).split(" ");
383 elements[0] = elements[0].trimmed();
384 if (elements.size() < 2)
385 {
386 PTLOGINIT;
387 LERROR << "Error in SVMTagger result line: did not get 2 elements in '"
388 << resultLine << "'";
389 LERROR << "Was tagging '" << oss.str() << "'";
390 return UNKNOWN_ERROR;
391 }
392 auto anaVertex = anaVertices[anaVerticesIndex];
393 auto currentAnaToken = tokens[anaVertex];
394 QString token = currentAnaToken->stringForm();
395 // if token is a newline, the SVMToolPosTagger will fail, replace it by
396 // Unicode char U+200B ZERO WIDTH SPACE
397 if (token.contains('\n'))
398 {
399 PTLOGINIT;
400 LWARN << "Error in SVMTagger. Invalid token with newline(s):" << token;
401 LWARN << "Avoiding the problem but the tokenizer should be checked.";
402 }
403 if (token == QString::fromUtf8("\n")) token = QString::fromUtf8((char*)u8"\u200B");
404 token = token.trimmed();
405 token.replace(" ", "_");
406 token.replace("\n", QString::fromUtf8((char*)u8"\u200B"));
407 if (token != elements[0])
408 {
409 PTLOGINIT;
410 LERROR << "Error in SVMTagger result alignement with analysis graph: got '"
411 << elements[0] << "' with tag '"<< elements[1] <<"' from SVMTagger and '"
412 << token << "' from graph";
413 return UNKNOWN_ERROR;
414 }
415
416 auto newVx = boost::add_vertex(*resultgraph);
417 auto agv = annotationData->createAnnotationVertex();
418 annotationData->addMatching("PosGraph", newVx, "annot", agv);
419 annotationData->addMatching("AnalysisGraph", anaVertex, "PosGraph", newVx);
420 annotationData->annotate(agv, QString::fromUtf8("PosGraph"), newVx);
421
422 // set linguistic infos
423 auto morphoData = get(vertex_data,*srcgraph,anaVertex);
424 auto srcToken = get(vertex_token,*srcgraph,anaVertex);
425 if (morphoData!=0)
426 {
427 auto posData = new MorphoSyntacticData();
428 CheckDifferentPropertyPredicate differentMicro(
429 microAccessor,
430 microManager.getPropertyValue(elements[1].toStdString()));
431 std::back_insert_iterator<MorphoSyntacticData> backInsertItr(*posData);
432 remove_copy_if(morphoData->begin(),
433 morphoData->end(),
434 backInsertItr,
435 differentMicro);
436 if (posData->empty() || morphoData->empty())
437 {
438 PTLOGINIT;
439 LWARN << "No matching category found for tagger result "
440 << elements[0] << " " << elements[1];
441 if (!morphoData->empty())
442 {
443 LWARN << "Taking any one";
444 posData->push_back(morphoData->front());
445 }
446 }
447 put(vertex_data,*resultgraph,newVx,posData);
448 put(vertex_token,*resultgraph,newVx,srcToken);
449 }
450
451 boost::add_edge(previousPosVertex, newVx, *resultgraph);
452
453 previousPosVertex = newVx;
454 anaVerticesIndex++;
455 }
456 boost::add_edge(previousPosVertex, posgraph->lastVertex(), *resultgraph);
457
458#ifdef DEBUG_LP
459 LINFO << "SvmToolPosTagger postagging done.";
460#endif
461
462 return SUCCESS_ID;
463}
464
465 typedef std::vector<StepData> StepDataVector;
466
467
468} // PosTagger
469
470} // LinguisticProcessing
471
472} // Lima
#define LWARN
Definition LimaCommon.h:160
#define LDEBUG
Definition LimaCommon.h:157
#define LINFO
Definition LimaCommon.h:158
#define LERROR
Definition LimaCommon.h:161
boost::property_map< LinguisticGraph, vertex_token_t >::type VertexTokenPropertyMap
@ vertex_token
LinguisticGraph::vertex_descriptor LinguisticGraphVertex
@ vertex_data
LinguisticGraph::out_edge_iterator LinguisticGraphOutEdgeIt
boost::adjacency_list< boost::vecS, boost::vecS, boost::bidirectionalS, LinguisticVertexProperties > LinguisticGraph
Property to identify the chains in the graph.
#define PTLOGINIT
int verbose
#define SVMTOOLPOSTAGGER_CLASSID
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.
const PropertyCode::PropertyCodeManager & getPropertyCodeManager() const
const MediaData & mediaData(MediaId media) const
const PropertyManager & getPropertyManager(const std::string &propertyName) const
Get the PropertyManager associated to a property.
const PropertyAccessor & getPropertyAccessor() const
give the corresponding PropertyAccessor
std::deque< std::string > & getListsValueAtKey(const std::string &key)
return a message when a 'param' was not found
Defines Factory for an Initializable Object.
Manage initialization of InitializableObjects using configuration module and parameters.
Use this exception to signal an error in one of the configuration files.
Definition LimaCommon.h:345
An AnalysisData containing a LinguisticGraph with a language and an id.
virtual std::shared_ptr< MediaProcessUnit > create(Common::XMLConfigurationFiles::GroupConfigurationStructure &unitConfiguration, MediaProcessUnit::Manager *manager) const override
A PoS tagger using SVMTool as a backend.
void init(Common::XMLConfigurationFiles::GroupConfigurationStructure &unitConfiguration, Manager *manager) override
initialize with parameters from configuration file.
LimaStatusCode process(AnalysisContent &analysis) const override
Process on data in analysisContent.
static const MediaticData & single()
const singleton accessor
Definition Singleton.h:51
This file contains a class to control log of informations about time, such as logging cumulated time ...
QString findFileInPaths(const QString &paths, const QString &fileName, const QChar &separator)
Find the given file in the given paths.
std::vector< StepData >::iterator StepDataVectorItr
std::vector< PredData >::iterator PredDataVectorItr
std::map< LinguisticCode, std::vector< PredData > >::iterator MicroCatDataMapItr
std::map< LinguisticCode, std::vector< PredData > > MicroCatDataMap
std::vector< PredData >::const_iterator PredDataVectorCItr
NAUTITIA.
LimaStatusCode
Definition LimaCommon.h:236
@ UNKNOWN_ERROR
Definition LimaCommon.h:238
@ SUCCESS_ID
Definition LimaCommon.h:237
bool operator<(const TargetVertexId &tvi) const
launch exception related to the configuration file parsing