aGrUM
0.21.0
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
namespace
gum
{
30
31
namespace
learning
{
32
33
/// default constructor
34
template
<
template
<
typename
>
class
ALLOC >
35
INLINE ParamEstimatorML<
ALLOC
>::
ParamEstimatorML
(
36
const
DBRowGeneratorParser
<
ALLOC
>&
parser
,
37
const
Apriori
<
ALLOC
>&
external_apriori
,
38
const
Apriori
<
ALLOC
>&
score_internal_apriori
,
39
const
std
::
vector
<
std
::
pair
<
std
::
size_t
,
std
::
size_t
>,
40
ALLOC
<
std
::
pair
<
std
::
size_t
,
std
::
size_t
> > >&
ranges
,
41
const
Bijection
<
NodeId
,
std
::
size_t
,
ALLOC
<
std
::
size_t
> >&
nodeId2columns
,
42
const
typename
ParamEstimatorML
<
ALLOC
>::
allocator_type
&
alloc
) :
43
ParamEstimator
<
ALLOC
>(
parser
,
44
external_apriori
,
45
score_internal_apriori
,
46
ranges
,
47
nodeId2columns
,
48
alloc
) {
49
GUM_CONSTRUCTOR
(
ParamEstimatorML
);
50
}
51
52
53
/// default constructor
54
template
<
template
<
typename
>
class
ALLOC
>
55
INLINE
ParamEstimatorML
<
ALLOC
>::
ParamEstimatorML
(
56
const
DBRowGeneratorParser
<
ALLOC
>&
parser
,
57
const
Apriori
<
ALLOC
>&
external_apriori
,
58
const
Apriori
<
ALLOC
>&
score_internal_apriori
,
59
const
Bijection
<
NodeId
,
std
::
size_t
,
ALLOC
<
std
::
size_t
> >&
nodeId2columns
,
60
const
typename
ParamEstimatorML
<
ALLOC
>::
allocator_type
&
alloc
) :
61
ParamEstimator
<
ALLOC
>(
parser
,
62
external_apriori
,
63
score_internal_apriori
,
64
nodeId2columns
,
65
alloc
) {
66
GUM_CONSTRUCTOR
(
ParamEstimatorML
);
67
}
68
69
70
/// copy constructor with a given allocator
71
template
<
template
<
typename
>
class
ALLOC
>
72
INLINE
ParamEstimatorML
<
ALLOC
>::
ParamEstimatorML
(
73
const
ParamEstimatorML
<
ALLOC
>&
from
,
74
const
typename
ParamEstimatorML
<
ALLOC
>::
allocator_type
&
alloc
) :
75
ParamEstimator
<
ALLOC
>(
from
,
alloc
) {
76
GUM_CONS_CPY
(
ParamEstimatorML
);
77
}
78
79
80
/// copy constructor
81
template
<
template
<
typename
>
class
ALLOC
>
82
INLINE
ParamEstimatorML
<
ALLOC
>::
ParamEstimatorML
(
const
ParamEstimatorML
<
ALLOC
>&
from
) :
83
ParamEstimatorML
<
ALLOC
>(
from
,
this
->
getAllocator
()) {}
84
85
86
/// move constructor with a given allocator
87
template
<
template
<
typename
>
class
ALLOC
>
88
INLINE
ParamEstimatorML
<
ALLOC
>::
ParamEstimatorML
(
89
ParamEstimatorML
<
ALLOC
>&&
from
,
90
const
typename
ParamEstimatorML
<
ALLOC
>::
allocator_type
&
alloc
) :
91
ParamEstimator
<
ALLOC
>(
std
::
move
(
from
),
alloc
) {
92
GUM_CONS_MOV
(
ParamEstimatorML
);
93
}
94
95
96
/// move constructor
97
template
<
template
<
typename
>
class
ALLOC
>
98
INLINE
ParamEstimatorML
<
ALLOC
>::
ParamEstimatorML
(
ParamEstimatorML
<
ALLOC
>&&
from
) :
99
ParamEstimatorML
<
ALLOC
>(
std
::
move
(
from
),
this
->
getAllocator
()) {}
100
101
102
/// virtual copy constructor with a given allocator
103
template
<
template
<
typename
>
class
ALLOC
>
104
ParamEstimatorML
<
ALLOC
>*
ParamEstimatorML
<
ALLOC
>::
clone
(
105
const
typename
ParamEstimatorML
<
ALLOC
>::
allocator_type
&
alloc
)
const
{
106
ALLOC
<
ParamEstimatorML
<
ALLOC
> >
allocator
(
alloc
);
107
ParamEstimatorML
<
ALLOC
>*
new_score
=
allocator
.
allocate
(1);
108
try
{
109
allocator
.
construct
(
new_score
, *
this
,
alloc
);
110
}
catch
(...) {
111
allocator
.
deallocate
(
new_score
, 1);
112
throw
;
113
}
114
115
return
new_score
;
116
}
117
118
119
/// virtual copy constructor
120
template
<
template
<
typename
>
class
ALLOC
>
121
ParamEstimatorML
<
ALLOC
>*
ParamEstimatorML
<
ALLOC
>::
clone
()
const
{
122
return
clone
(
this
->
getAllocator
());
123
}
124
125
126
/// destructor
127
template
<
template
<
typename
>
class
ALLOC
>
128
ParamEstimatorML
<
ALLOC
>::~
ParamEstimatorML
() {
129
GUM_DESTRUCTOR
(
ParamEstimatorML
);
130
}
131
132
133
/// copy operator
134
template
<
template
<
typename
>
class
ALLOC
>
135
ParamEstimatorML
<
ALLOC
>&
136
ParamEstimatorML
<
ALLOC
>::
operator
=(
const
ParamEstimatorML
<
ALLOC
>&
from
) {
137
ParamEstimator
<
ALLOC
>::
operator
=(
from
);
138
return
*
this
;
139
}
140
141
142
/// move operator
143
template
<
template
<
typename
>
class
ALLOC
>
144
ParamEstimatorML
<
ALLOC
>&
145
ParamEstimatorML
<
ALLOC
>::
operator
=(
ParamEstimatorML
<
ALLOC
>&&
from
) {
146
ParamEstimator
<
ALLOC
>::
operator
=(
std
::
move
(
from
));
147
return
*
this
;
148
}
149
150
151
/// returns the CPT's parameters corresponding to a given set of nodes
152
template
<
template
<
typename
>
class
ALLOC
>
153
std
::
vector
<
double
,
ALLOC
<
double
> >
ParamEstimatorML
<
ALLOC
>::
parameters
(
154
const
NodeId
target_node
,
155
const
std
::
vector
<
NodeId
,
ALLOC
<
NodeId
> >&
conditioning_nodes
) {
156
// create an idset that contains all the nodes in the following order:
157
// first, the target node, then all the conditioning nodes
158
IdCondSet
<
ALLOC
>
idset
(
target_node
,
conditioning_nodes
,
true
);
159
160
// get the counts for all the nodes in the idset and add the external and
161
// score internal aprioris
162
std
::
vector
<
double
,
ALLOC
<
double
> >
N_ijk
(
this
->
counter_
.
counts
(
idset
,
true
));
163
const
bool
informative_external_apriori
=
this
->
external_apriori_
->
isInformative
();
164
const
bool
informative_score_internal_apriori
165
=
this
->
score_internal_apriori_
->
isInformative
();
166
if
(
informative_external_apriori
)
this
->
external_apriori_
->
addAllApriori
(
idset
,
N_ijk
);
167
if
(
informative_score_internal_apriori
)
168
this
->
score_internal_apriori_
->
addAllApriori
(
idset
,
N_ijk
);
169
170
171
// now, normalize N_ijk
172
173
// here, we distinguish nodesets with conditioning nodes from those
174
// without conditioning nodes
175
if
(!
conditioning_nodes
.
empty
()) {
176
// get the counts for all the conditioning nodes, and add them the
177
// external and score internal aprioris
178
std
::
vector
<
double
,
ALLOC
<
double
> >
N_ij
(
179
this
->
counter_
.
counts
(
idset
.
conditionalIdCondSet
(),
false
));
180
if
(
informative_external_apriori
)
181
this
->
external_apriori_
->
addConditioningApriori
(
idset
,
N_ij
);
182
if
(
informative_score_internal_apriori
)
183
this
->
score_internal_apriori_
->
addConditioningApriori
(
idset
,
N_ij
);
184
185
const
std
::
size_t
conditioning_domsize
=
N_ij
.
size
();
186
const
std
::
size_t
target_domsize
=
N_ijk
.
size
() /
conditioning_domsize
;
187
188
// check that all conditioning nodes have strictly positive counts
189
for
(
std
::
size_t
j
=
std
::
size_t
(0);
j
<
conditioning_domsize
; ++
j
) {
190
if
(
N_ij
[
j
] == 0) {
191
// get the domain sizes of the conditioning nodes
192
const
std
::
size_t
cond_nb
=
conditioning_nodes
.
size
();
193
std
::
vector
<
Idx
>
cond_domsize
(
cond_nb
);
194
195
const
auto
&
node2cols
=
this
->
counter_
.
nodeId2Columns
();
196
const
auto
&
database
=
this
->
counter_
.
database
();
197
if
(
node2cols
.
empty
()) {
198
for
(
std
::
size_t
i
=
std
::
size_t
(0);
i
<
cond_nb
; ++
i
) {
199
cond_domsize
[
i
] =
database
.
domainSize
(
conditioning_nodes
[
i
]);
200
}
201
}
else
{
202
for
(
std
::
size_t
i
=
std
::
size_t
(0);
i
<
cond_nb
; ++
i
) {
203
cond_domsize
[
i
] =
database
.
domainSize
(
node2cols
.
second
(
conditioning_nodes
[
i
]));
204
}
205
}
206
207
// determine the value of each conditioning variable in N_ij[j]
208
std
::
vector
<
Idx
>
offsets
(
cond_nb
);
209
Idx
offset
= 1;
210
std
::
size_t
i
;
211
for
(
i
=
std
::
size_t
(0);
i
<
cond_nb
; ++
i
) {
212
offsets
[
i
] =
offset
;
213
offset
*=
cond_domsize
[
i
];
214
}
215
std
::
vector
<
Idx
>
values
(
cond_nb
);
216
i
= 0;
217
offset
=
j
;
218
for
(
Idx
jj
=
cond_nb
- 1;
i
<
cond_nb
; ++
i
, --
jj
) {
219
values
[
jj
] =
offset
/
offsets
[
jj
];
220
offset
%=
offsets
[
jj
];
221
}
222
223
// create the error message
224
std
::
stringstream
str
;
225
str
<<
"The conditioning set <"
;
226
bool
deja
=
false
;
227
for
(
i
=
std
::
size_t
(0);
i
<
cond_nb
; ++
i
) {
228
if
(
deja
)
229
str
<<
", "
;
230
else
231
deja
=
true
;
232
std
::
size_t
col
=
node2cols
.
empty
() ?
conditioning_nodes
[
i
]
233
:
node2cols
.
second
(
conditioning_nodes
[
i
]);
234
const
DiscreteVariable
&
var
235
=
dynamic_cast
<
const
DiscreteVariable
& >(
database
.
variable
(
col
));
236
str
<<
var
.
name
() <<
"="
<<
var
.
labels
()[
values
[
i
]];
237
}
238
auto
target_col
=
node2cols
.
empty
() ?
target_node
:
node2cols
.
second
(
target_node
);
239
const
Variable
&
var
=
database
.
variable
(
target_col
);
240
str
<<
"> for target node "
<<
var
.
name
()
241
<<
" never appears in the database. Please consider using "
242
<<
"priors such as smoothing."
;
243
244
GUM_ERROR
(
DatabaseError
,
str
.
str
())
245
}
246
}
247
248
// normalize the counts
249
for
(
std
::
size_t
j
=
std
::
size_t
(0),
k
=
std
::
size_t
(0);
j
<
conditioning_domsize
; ++
j
) {
250
for
(
std
::
size_t
i
=
std
::
size_t
(0);
i
<
target_domsize
; ++
i
, ++
k
) {
251
N_ijk
[
k
] /=
N_ij
[
j
];
252
}
253
}
254
}
else
{
255
// here, there are no conditioning nodes. Hence N_ijk is the marginal
256
// probability distribution over the target node. To normalize it, it
257
// is sufficient to divide each cell by the sum over all the cells
258
double
sum
= 0;
259
for
(
const
double
n_ijk
:
N_ijk
)
260
sum
+=
n_ijk
;
261
262
if
(
sum
!= 0) {
263
for
(
double
&
n_ijk
:
N_ijk
)
264
n_ijk
/=
sum
;
265
}
else
{
266
std
::
stringstream
str
;
267
268
const
auto
&
node2cols
=
this
->
counter_
.
nodeId2Columns
();
269
const
auto
&
database
=
this
->
counter_
.
database
();
270
auto
target_col
=
node2cols
.
empty
() ?
target_node
:
node2cols
.
second
(
target_node
);
271
const
Variable
&
var
=
database
.
variable
(
target_col
);
272
str
<<
"No data for target node "
<<
var
.
name
()
273
<<
". It is impossible to estimate the parameters by maximum "
274
"likelihood"
;
275
GUM_ERROR
(
DatabaseError
,
str
.
str
())
276
}
277
}
278
279
return
N_ijk
;
280
}
281
282
}
/* namespace learning */
283
284
}
/* namespace gum */
285
286
#
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