aGrUM
0.20.3
a C++ library for (probabilistic) graphical models
paramEstimatorML_tpl.h
Go to the documentation of this file.
1
/**
2
*
3
* Copyright (c) 2005-2021 by Pierre-Henri WUILLEMIN(@LIP6) & Christophe GONZALES(@AMU)
4
* info_at_agrum_dot_org
5
*
6
* This library is free software: you can redistribute it and/or modify
7
* it under the terms of the GNU Lesser General Public License as published by
8
* the Free Software Foundation, either version 3 of the License, or
9
* (at your option) any later version.
10
*
11
* This library is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU Lesser General Public License for more details.
15
*
16
* You should have received a copy of the GNU Lesser General Public License
17
* along with this library. If not, see <http://www.gnu.org/licenses/>.
18
*
19
*/
20
21
22
/** @file
23
* @brief the class for estimating parameters of CPTs using Maximum Likelihood
24
*
25
* @author Christophe GONZALES(@AMU) and Pierre-Henri WUILLEMIN(@LIP6)
26
*/
27
#
ifndef
DOXYGEN_SHOULD_SKIP_THIS
28
29
// if this define is active, the estimator will not accept to assess numbers for a
30
// distribution with no case at all (N_ij=0). if this define is not active, the
31
// estimator will use an uniform distribution for this case.
32
//# define GUM_PARAMESTIMATOR_ERROR_WHEN_NIJ_IS_NULL
33
34
namespace
gum
{
35
36
namespace
learning
{
37
38
/// default constructor
39
template
<
template
<
typename
>
class
ALLOC >
40
INLINE ParamEstimatorML<
ALLOC
>::
ParamEstimatorML
(
41
const
DBRowGeneratorParser
<
ALLOC
>&
parser
,
42
const
Apriori
<
ALLOC
>&
external_apriori
,
43
const
Apriori
<
ALLOC
>&
score_internal_apriori
,
44
const
std
::
vector
<
std
::
pair
<
std
::
size_t
,
std
::
size_t
>,
45
ALLOC
<
std
::
pair
<
std
::
size_t
,
std
::
size_t
> > >&
ranges
,
46
const
Bijection
<
NodeId
,
std
::
size_t
,
ALLOC
<
std
::
size_t
> >&
nodeId2columns
,
47
const
typename
ParamEstimatorML
<
ALLOC
>::
allocator_type
&
alloc
) :
48
ParamEstimator
<
ALLOC
>(
parser
,
49
external_apriori
,
50
score_internal_apriori
,
51
ranges
,
52
nodeId2columns
,
53
alloc
) {
54
GUM_CONSTRUCTOR
(
ParamEstimatorML
);
55
}
56
57
58
/// default constructor
59
template
<
template
<
typename
>
class
ALLOC
>
60
INLINE
ParamEstimatorML
<
ALLOC
>::
ParamEstimatorML
(
61
const
DBRowGeneratorParser
<
ALLOC
>&
parser
,
62
const
Apriori
<
ALLOC
>&
external_apriori
,
63
const
Apriori
<
ALLOC
>&
score_internal_apriori
,
64
const
Bijection
<
NodeId
,
std
::
size_t
,
ALLOC
<
std
::
size_t
> >&
nodeId2columns
,
65
const
typename
ParamEstimatorML
<
ALLOC
>::
allocator_type
&
alloc
) :
66
ParamEstimator
<
ALLOC
>(
parser
,
67
external_apriori
,
68
score_internal_apriori
,
69
nodeId2columns
,
70
alloc
) {
71
GUM_CONSTRUCTOR
(
ParamEstimatorML
);
72
}
73
74
75
/// copy constructor with a given allocator
76
template
<
template
<
typename
>
class
ALLOC
>
77
INLINE
ParamEstimatorML
<
ALLOC
>::
ParamEstimatorML
(
78
const
ParamEstimatorML
<
ALLOC
>&
from
,
79
const
typename
ParamEstimatorML
<
ALLOC
>::
allocator_type
&
alloc
) :
80
ParamEstimator
<
ALLOC
>(
from
,
alloc
) {
81
GUM_CONS_CPY
(
ParamEstimatorML
);
82
}
83
84
85
/// copy constructor
86
template
<
template
<
typename
>
class
ALLOC
>
87
INLINE
ParamEstimatorML
<
ALLOC
>::
ParamEstimatorML
(
const
ParamEstimatorML
<
ALLOC
>&
from
) :
88
ParamEstimatorML
<
ALLOC
>(
from
,
this
->
getAllocator
()) {}
89
90
91
/// move constructor with a given allocator
92
template
<
template
<
typename
>
class
ALLOC
>
93
INLINE
ParamEstimatorML
<
ALLOC
>::
ParamEstimatorML
(
94
ParamEstimatorML
<
ALLOC
>&&
from
,
95
const
typename
ParamEstimatorML
<
ALLOC
>::
allocator_type
&
alloc
) :
96
ParamEstimator
<
ALLOC
>(
std
::
move
(
from
),
alloc
) {
97
GUM_CONS_MOV
(
ParamEstimatorML
);
98
}
99
100
101
/// move constructor
102
template
<
template
<
typename
>
class
ALLOC
>
103
INLINE
ParamEstimatorML
<
ALLOC
>::
ParamEstimatorML
(
ParamEstimatorML
<
ALLOC
>&&
from
) :
104
ParamEstimatorML
<
ALLOC
>(
std
::
move
(
from
),
this
->
getAllocator
()) {}
105
106
107
/// virtual copy constructor with a given allocator
108
template
<
template
<
typename
>
class
ALLOC
>
109
ParamEstimatorML
<
ALLOC
>*
ParamEstimatorML
<
ALLOC
>::
clone
(
110
const
typename
ParamEstimatorML
<
ALLOC
>::
allocator_type
&
alloc
)
const
{
111
ALLOC
<
ParamEstimatorML
<
ALLOC
> >
allocator
(
alloc
);
112
ParamEstimatorML
<
ALLOC
>*
new_score
=
allocator
.
allocate
(1);
113
try
{
114
allocator
.
construct
(
new_score
, *
this
,
alloc
);
115
}
catch
(...) {
116
allocator
.
deallocate
(
new_score
, 1);
117
throw
;
118
}
119
120
return
new_score
;
121
}
122
123
124
/// virtual copy constructor
125
template
<
template
<
typename
>
class
ALLOC
>
126
ParamEstimatorML
<
ALLOC
>*
ParamEstimatorML
<
ALLOC
>::
clone
()
const
{
127
return
clone
(
this
->
getAllocator
());
128
}
129
130
131
/// destructor
132
template
<
template
<
typename
>
class
ALLOC
>
133
ParamEstimatorML
<
ALLOC
>::~
ParamEstimatorML
() {
134
GUM_DESTRUCTOR
(
ParamEstimatorML
);
135
}
136
137
138
/// copy operator
139
template
<
template
<
typename
>
class
ALLOC
>
140
ParamEstimatorML
<
ALLOC
>&
141
ParamEstimatorML
<
ALLOC
>::
operator
=(
const
ParamEstimatorML
<
ALLOC
>&
from
) {
142
ParamEstimator
<
ALLOC
>::
operator
=(
from
);
143
return
*
this
;
144
}
145
146
147
/// move operator
148
template
<
template
<
typename
>
class
ALLOC
>
149
ParamEstimatorML
<
ALLOC
>&
150
ParamEstimatorML
<
ALLOC
>::
operator
=(
ParamEstimatorML
<
ALLOC
>&&
from
) {
151
ParamEstimator
<
ALLOC
>::
operator
=(
std
::
move
(
from
));
152
return
*
this
;
153
}
154
155
156
/// returns the CPT's parameters corresponding to a given set of nodes
157
template
<
template
<
typename
>
class
ALLOC
>
158
std
::
vector
<
double
,
ALLOC
<
double
> >
ParamEstimatorML
<
ALLOC
>::
parameters
(
159
const
NodeId
target_node
,
160
const
std
::
vector
<
NodeId
,
ALLOC
<
NodeId
> >&
conditioning_nodes
) {
161
// create an idset that contains all the nodes in the following order:
162
// first, the target node, then all the conditioning nodes
163
IdCondSet
<
ALLOC
>
idset
(
target_node
,
conditioning_nodes
,
true
);
164
165
// get the counts for all the nodes in the idset and add the external and
166
// score internal aprioris
167
std
::
vector
<
double
,
ALLOC
<
double
> >
N_ijk
(
this
->
counter_
.
counts
(
idset
,
true
));
168
const
bool
informative_external_apriori
=
this
->
external_apriori_
->
isInformative
();
169
const
bool
informative_score_internal_apriori
170
=
this
->
score_internal_apriori_
->
isInformative
();
171
if
(
informative_external_apriori
)
this
->
external_apriori_
->
addAllApriori
(
idset
,
N_ijk
);
172
if
(
informative_score_internal_apriori
)
173
this
->
score_internal_apriori_
->
addAllApriori
(
idset
,
N_ijk
);
174
175
176
// now, normalize N_ijk
177
178
// here, we distinguish nodesets with conditioning nodes from those
179
// without conditioning nodes
180
if
(!
conditioning_nodes
.
empty
()) {
181
// get the counts for all the conditioning nodes, and add them the
182
// external and score internal aprioris
183
std
::
vector
<
double
,
ALLOC
<
double
> >
N_ij
(
184
this
->
counter_
.
counts
(
idset
.
conditionalIdCondSet
(),
false
));
185
if
(
informative_external_apriori
)
186
this
->
external_apriori_
->
addConditioningApriori
(
idset
,
N_ij
);
187
if
(
informative_score_internal_apriori
)
188
this
->
score_internal_apriori_
->
addConditioningApriori
(
idset
,
N_ij
);
189
190
const
std
::
size_t
conditioning_domsize
=
N_ij
.
size
();
191
const
std
::
size_t
target_domsize
=
N_ijk
.
size
() /
conditioning_domsize
;
192
193
#
ifdef
GUM_PARAMESTIMATOR_ERROR_WHEN_NIJ_IS_NULL
194
// check that all conditioning nodes have strictly positive counts
195
for
(
std
::
size_t
j
=
std
::
size_t
(0);
j
<
conditioning_domsize
; ++
j
) {
196
if
(
N_ij
[
j
] == 0) {
197
// get the domain sizes of the conditioning nodes
198
const
std
::
size_t
cond_nb
=
conditioning_nodes
.
size
();
199
std
::
vector
<
Idx
>
cond_domsize
(
cond_nb
);
200
201
const
auto
&
node2cols
=
this
->
counter_
.
nodeId2Columns
();
202
const
auto
&
database
=
this
->
counter_
.
database
();
203
if
(
node2cols
.
empty
()) {
204
for
(
std
::
size_t
i
=
std
::
size_t
(0);
i
<
cond_nb
; ++
i
) {
205
cond_domsize
[
i
] =
database
.
domainSize
(
conditioning_nodes
[
i
]);
206
}
207
}
else
{
208
for
(
std
::
size_t
i
=
std
::
size_t
(0);
i
<
cond_nb
; ++
i
) {
209
cond_domsize
[
i
] =
database
.
domainSize
(
node2cols
.
second
(
conditioning_nodes
[
i
]));
210
}
211
}
212
213
// determine the value of each conditioning variable in N_ij[j]
214
std
::
vector
<
Idx
>
offsets
(
cond_nb
);
215
Idx
offset
= 1;
216
std
::
size_t
i
;
217
for
(
i
=
std
::
size_t
(0);
i
<
cond_nb
; ++
i
) {
218
offsets
[
i
] =
offset
;
219
offset
*=
cond_domsize
[
i
];
220
}
221
std
::
vector
<
Idx
>
values
(
cond_nb
);
222
i
= 0;
223
offset
=
j
;
224
for
(
Idx
jj
=
cond_nb
- 1;
i
<
cond_nb
; ++
i
, --
jj
) {
225
values
[
jj
] =
offset
/
offsets
[
jj
];
226
offset
%=
offsets
[
jj
];
227
}
228
229
// create the error message
230
std
::
stringstream
str
;
231
str
<<
"The conditioning set <"
;
232
bool
deja
=
true
;
233
for
(
i
=
std
::
size_t
(0);
i
<
cond_nb
; ++
i
) {
234
if
(
deja
)
235
str
<<
", "
;
236
else
237
deja
=
true
;
238
std
::
size_t
col
=
node2cols
.
empty
() ?
conditioning_nodes
[
i
]
239
:
node2cols
.
second
(
conditioning_nodes
[
i
]);
240
const
DiscreteVariable
&
var
241
=
dynamic_cast
<
const
DiscreteVariable
& >(
database
.
variable
(
col
));
242
str
<<
var
.
name
() <<
"="
<<
var
.
labels
()[
values
[
i
]];
243
}
244
auto
target_col
=
node2cols
.
empty
() ?
target_node
:
node2cols
.
second
(
target_node
);
245
const
Variable
&
var
=
database
.
variable
(
target_col
);
246
str
<<
"> for target node "
<<
var
.
name
()
247
<<
" never appears in the database. Please consider using "
248
<<
"priors such as smoothing."
;
249
250
GUM_ERROR
(
DatabaseError
,
str
.
str
())
251
}
252
}
253
#
endif
// GUM_PARAMESTIMATOR_ERROR_WHEN_NIJ_IS_NULL
254
255
// normalize the counts
256
for
(
std
::
size_t
j
=
std
::
size_t
(0),
k
=
std
::
size_t
(0);
j
<
conditioning_domsize
; ++
j
) {
257
for
(
std
::
size_t
i
=
std
::
size_t
(0);
i
<
target_domsize
; ++
i
, ++
k
) {
258
#
ifdef
GUM_PARAMESTIMATOR_ERROR_WHEN_NIJ_IS_NULL
259
N_ijk
[
k
] /=
N_ij
[
j
];
260
#
else
// GUM_PARAMESTIMATOR_ERROR_WHEN_NIJ_IS_NULL
261
if
(
N_ij
[
j
] != 0) {
262
N_ijk
[
k
] /=
N_ij
[
j
];
263
}
else
{
264
N_ijk
[
k
] = 1.0 /
target_domsize
;
265
}
266
#
endif
// GUM_PARAMESTIMATOR_ERROR_WHEN_NIJ_IS_NULL
267
}
268
}
269
}
else
{
270
// here, there are no conditioning nodes. Hence N_ijk is the marginal
271
// probability distribution over the target node. To normalize it, it
272
// is sufficient to divide each cell by the sum over all the cells
273
double
sum
= 0;
274
for
(
const
double
n_ijk
:
N_ijk
)
275
sum
+=
n_ijk
;
276
277
if
(
sum
!= 0) {
278
for
(
double
&
n_ijk
:
N_ijk
)
279
n_ijk
/=
sum
;
280
}
else
{
281
#
ifdef
GUM_PARAMESTIMATOR_ERROR_WHEN_NIJ_IS_NULL
282
std
::
stringstream
str
;
283
284
const
auto
&
node2cols
=
this
->
counter_
.
nodeId2Columns
();
285
const
auto
&
database
=
this
->
counter_
.
database
();
286
auto
target_col
=
node2cols
.
empty
() ?
target_node
:
node2cols
.
second
(
target_node
);
287
const
Variable
&
var
=
database
.
variable
(
target_col
);
288
str
<<
"No data for target node "
<<
var
.
name
()
289
<<
". It is impossible to estimate the parameters by maximum "
290
"likelihood"
;
291
GUM_ERROR
(
DatabaseError
,
str
.
str
())
292
#
else
// GUM_PARAMESTIMATOR_ERROR_WHEN_NIJ_IS_NULL
293
for
(
double
&
n_ijk
:
N_ijk
)
294
n_ijk
= 1.0 /
N_ijk
.
size
();
295
#
endif
// GUM_PARAMESTIMATOR_ERROR_WHEN_NIJ_IS_NULL
296
}
297
}
298
299
return
N_ijk
;
300
}
301
302
}
/* namespace learning */
303
304
}
/* namespace gum */
305
306
#
endif
/* DOXYGEN_SHOULD_SKIP_THIS */
gum::Set::emplace
INLINE void emplace(Args &&... args)
Definition:
set_tpl.h:643
gum::learning::genericBNLearner::Database::Database
Database(const std::string &filename, const BayesNet< GUM_SCALAR > &bn, const std::vector< std::string > &missing_symbols)
Definition:
genericBNLearner_tpl.h:31