LIMA
Libre Multilingual Analyzer — C++ API
Loading...
Searching...
No Matches
feat_extractors.h
Go to the documentation of this file.
1// Copyright 2021 CEA LIST
2// SPDX-FileCopyrightText: 2022 CEA LIST <gael.de-chalendar@cea.fr>
3//
4// SPDX-License-Identifier: MIT
5
6#ifndef DEEPLIMA_FEAT_EXTRACTORS_H
7#define DEEPLIMA_FEAT_EXTRACTORS_H
8
9#include <vector>
10#include <string>
11#include <unordered_map>
12#include <unordered_set>
13#include <limits>
14
16
17namespace deeplima
18{
19
20template <typename Token>
22{
23protected:
24 inline static std::string to_lower(const std::string& src)
25 {
26 std::string copy = src;
27 std::transform(copy.begin(), copy.end(), copy.begin(),
28 [](unsigned char c){ return std::tolower(c); });
29 return copy;
30 }
31
32public:
36
37 inline static bool needs_preprocessing()
38 {
39 return false;
40 }
41
42 inline void preprocess(const Token& token)
43 {
44 }
45
46 inline static size_t get_feat_id(const std::string& name)
47 {
48 if (name == "form") return 0;
49 if (name == "lc(form)") return 1;
50 throw std::runtime_error("Unknown feature name.");
51 }
52
53 inline static std::string feat_value(const Token& token, size_t feat_no)
54 {
55 switch (feat_no)
56 {
57 case 0: // form as is
58 return token.form();
59 case 1: // form in lower case
60 return to_lower(token.form());
61 default:
62 throw std::runtime_error("Unknown feature id.");
63 }
64 }
65
66 inline bool should_preprocess_feature(int /*feat_no*/) const
67 {
68 return true;
69 }
70
71 inline static size_t size()
72 {
73 return 2;
74 }
75};
76
77template <typename Token>
79{
80public:
81 inline static size_t get_feat_id(const std::string& name)
82 {
83 if (name == "eos") return 0;
84 throw std::runtime_error("Unknown feature name.");
85 }
86
87 inline static uint64_t feat_value(const Token& token, size_t feat_no)
88 {
89 switch (feat_no)
90 {
91 case 0: // form as is
92 return token.eos();
93 default:
94 throw std::runtime_error("Unknown feature id.");
95 }
96 }
97
98 inline static size_t size()
99 {
100 return 1;
101 }
102};
103
104template <typename Token>
106{
107 std::vector<std::string> m_idx2feat;
108 std::unordered_map<std::string, int> m_feat2idx;
109 std::vector<uint32_t> m_idx2cls;
110
111public:
112 // Sentinel class id meaning "the source provides no column for this feature";
113 // feat_value() then returns 0 (the UNK / unknown-value index).
114 static constexpr uint32_t NO_COLUMN = std::numeric_limits<uint32_t>::max();
115
119
120 void add_feature(const std::string& name, size_t cls_id)
121 {
122 m_feat2idx[name] = m_idx2feat.size();
123 m_idx2feat.push_back(name);
124 m_idx2cls.push_back(cls_id);
125 }
126
127 inline size_t get_feat_id(const std::string& name) const
128 {
129 const auto it = m_feat2idx.find(name);
130 if (m_feat2idx.cend() == it)
131 {
132 throw std::runtime_error("Unknown feature name.");
133 }
134 return it->second;
135 }
136
137 inline uint64_t feat_value(const Token& token, size_t feat_no) const
138 {
139 if (feat_no < m_idx2cls.size())
140 {
141 // Feature absent from the source (e.g. a morph class the tagger does not
142 // produce but the parser was trained with): fall back to UNK (id 0).
143 if (NO_COLUMN == m_idx2cls[feat_no])
144 {
145 return 0;
146 }
147 return token.cls(m_idx2cls[feat_no]);
148 }
149
150 throw std::runtime_error("Unknown feature id.");
151 }
152
153 inline size_t size() const
154 {
155 return m_idx2feat.size();
156 }
157};
158
159template <typename Token>
161{
162protected:
163 std::vector<std::string> m_idx2feat;
164 std::unordered_map<std::string, size_t> m_feat2idx;
165 std::unordered_set<std::string> m_prohibited_feats;
166 std::unordered_set<int> m_dont_preprocess;
167 int m_form = -1;
168 int m_form_lc = -1;
169 int m_upos = -1;
170 int m_xpos = -1;
171 int m_eos = -1;
172 int m_rel = -1;
173 bool m_feats = false;
174
175 void add_feature(const std::string& name)
176 {
177 m_feat2idx[name] = m_idx2feat.size();
178 m_idx2feat.push_back(name);
179 }
180
181public:
182
184
186
187 ConlluFeatExtractor(const std::string& feats_to_train)
188 {
189 for (const std::string& s : utils::split(feats_to_train, ','))
190 {
191 assert(s.size() > 0);
192
193 bool dont_preprocess = false;
194 std::string feat_name = s;
195 if (s[0] == '*')
196 {
197 dont_preprocess = true;
198 feat_name = s.substr(1);
199 }
200
201 if (feat_name == "form")
202 {
203 add_feature(feat_name);
204 m_form = m_feat2idx[feat_name];
205 }
206 else if (feat_name == "lc(form)")
207 {
208 add_feature(feat_name);
209 m_form_lc = m_feat2idx[feat_name];
210 }
211 else if (feat_name == "upos")
212 {
213 add_feature("upos");
214 m_upos = m_feat2idx["upos"];
215 }
216 else if (feat_name == "xpos")
217 {
218 add_feature("xpos");
219 m_xpos = m_feat2idx["xpos"];
220 }
221 else if (feat_name == "feats")
222 {
223 m_feats = true;
224 }
225 else if (feat_name == "eos")
226 {
227 add_feature("eos");
228 m_eos = m_feat2idx["eos"];
229 }
230 else if (feat_name == "rel")
231 {
232 add_feature("rel");
233 m_rel = m_feat2idx["rel"];
234 }
235 else if (s[0] == '-')
236 {
237 std::string feat_name = s.substr(1);
238 assert(feat_name.size() > 0);
239 m_prohibited_feats.insert(feat_name);
240 }
241 else
242 {
243 throw std::invalid_argument("Can't parse list of features: \"" + feats_to_train + "\"");
244 }
245
246 if (dont_preprocess)
247 {
248 m_dont_preprocess.insert(m_feat2idx[s.substr(1)]);
249 }
250 }
251 }
252
253 inline static bool needs_preprocessing()
254 {
255 return true;
256 }
257
258 inline void preprocess(const Token& token)
259 {
260 if (m_feats)
261 {
262 for (const auto& fv : token.feats())
263 {
264 if (m_prohibited_feats.end() != m_prohibited_feats.find(fv.first))
265 {
266 continue;
267 }
268 if (m_feat2idx.end() == m_feat2idx.find(fv.first))
269 {
270 m_feat2idx[fv.first] = m_idx2feat.size();
271 m_idx2feat.push_back(fv.first);
272 }
273 }
274 }
275 }
276
277 inline size_t get_feat_id(const std::string& name) const
278 {
279 const auto it = m_feat2idx.find(name);
280 if (m_feat2idx.cend() == it)
281 {
282 throw std::runtime_error("Unknown feature name.");
283 }
284 return it->second;
285 }
286
287 inline std::string feat_value(const Token& token, int feat_no) const
288 {
289 assert(feat_no >= 0);
290
291 if (-1 != m_form && m_form == feat_no)
292 {
294 }
295 else if (-1 != m_form && m_form == feat_no)
296 {
298 }
299 else if (-1 != m_upos && m_upos == feat_no)
300 {
301 return token.upos();
302 }
303 else if (-1 != m_xpos && m_xpos == feat_no)
304 {
305 return token.xpos();
306 }
307 else if (-1 != m_rel && m_rel == feat_no)
308 {
309 return token.deprel();
310 }
311 else if (-1 != m_eos && m_eos == feat_no)
312 {
313 return token.eos() ? "Yes" : "No";
314 }
315 else if (m_feats)
316 {
317 assert(size_t(feat_no) < m_idx2feat.size());
318 const std::string feat_name = m_idx2feat[feat_no];
319 const auto& fv = token.feats();
320 auto it = fv.find(feat_name);
321 if (fv.end() == it)
322 {
323 return "-";
324 }
325 assert(!it->second.empty());
326 assert(it->second.size() == 1);
327 return *(it->second.begin());
328 }
329
330 throw std::invalid_argument("Unknown feature identifier");
331 }
332
333 inline bool should_preprocess_feature(int feat_no) const
334 {
335 return m_dont_preprocess.cend() == m_dont_preprocess.find(feat_no);
336 }
337
338 inline size_t size() const
339 {
340 return m_idx2feat.size();
341 }
342
343 std::vector<std::string> feats() const
344 {
345 return m_idx2feat;
346 }
347};
348
349} // namespace deeplima
350
351#endif
std::unordered_set< int > m_dont_preprocess
std::vector< std::string > m_idx2feat
bool should_preprocess_feature(int feat_no) const
ConlluFeatExtractor(const std::string &feats_to_train)
void preprocess(const Token &token)
std::unordered_map< std::string, size_t > m_feat2idx
std::unordered_set< std::string > m_prohibited_feats
std::vector< std::string > feats() const
std::string feat_value(const Token &token, int feat_no) const
ConlluFeatExtractor(const ConlluFeatExtractor &other)=default
size_t get_feat_id(const std::string &name) const
void add_feature(const std::string &name)
bool should_preprocess_feature(int) const
static size_t get_feat_id(const std::string &name)
void preprocess(const Token &token)
static std::string to_lower(const std::string &src)
static std::string feat_value(const Token &token, size_t feat_no)
void add_feature(const std::string &name, size_t cls_id)
static constexpr uint32_t NO_COLUMN
size_t get_feat_id(const std::string &name) const
uint64_t feat_value(const Token &token, size_t feat_no) const
static uint64_t feat_value(const Token &token, size_t feat_no)
static size_t get_feat_id(const std::string &name)
std::vector< std::string > split(const std::string &str, char delim)