16
16
from .. import _check
17
17
18
18
class GenModel (base .Generative ):
19
+ """The stochastic data generative model and the prior distribution.
20
+
21
+ Parameters
22
+ ----------
23
+ c_num_classes : int
24
+ a positive integer
25
+ c_degree : int
26
+ a positive integer
27
+ pi_vec : numpy.ndarray, optional
28
+ A vector of real numbers in :math:`[0, 1]`,
29
+ by default [1/c_num_classes, 1/c_num_classes, ... , 1/c_num_classes].
30
+ Sum of its elements must be 1.0.
31
+ a_mat : numpy.ndarray, optional
32
+ A matrix of real numbers in :math:`[0, 1]`,
33
+ by default a matrix obtained by stacking
34
+ [1/c_num_classes, 1/c_num_classes, ... , 1/c_num_classes].
35
+ Sum of the elements of each row vector must be 1.0.
36
+ If a single vector is input, will be broadcasted.
37
+ mu_vecs : numpy.ndarray, optional
38
+ Vectors of real numbers,
39
+ by default zero vectors.
40
+ If a single vector is input, will be broadcasted.
41
+ lambda_mats : numpy.ndarray, optional
42
+ Positive definite symetric matrices,
43
+ by default the identity matrices.
44
+ If a single matrix is input, it will be broadcasted.
45
+ h_eta_vec : numpy.ndarray, optional
46
+ A vector of positive real numbers,
47
+ by default [1/2, 1/2, ... , 1/2]
48
+ h_zeta_vecs : numpy.ndarray, optional
49
+ Vectors of positive numbers,
50
+ by default vectors whose elements are all 1/2
51
+ If a single vector is input, will be broadcasted.
52
+ h_m_vecs : numpy.ndarray, optional
53
+ Vectors of real numbers,
54
+ by default zero vectors
55
+ If a single vector is input, will be broadcasted.
56
+ h_kappas : float or numpy.ndarray, optional
57
+ Positive real numbers,
58
+ by default [1.0, 1.0, ... , 1.0].
59
+ If a single real number is input, it will be broadcasted.
60
+ h_nus : float or numpy.ndarray, optional
61
+ Real numbers greater than ``c_degree-1``,
62
+ by default [c_degree, c_degree, ... , c_degree]
63
+ If a single real number is input, it will be broadcasted.
64
+ h_w_mats : numpy.ndarray, optional
65
+ Positive definite symetric matrices,
66
+ by default the identity matrices.
67
+ If a single matrix is input, it will be broadcasted.
68
+ seed : {None, int}, optional
69
+ A seed to initialize numpy.random.default_rng(),
70
+ by default None
71
+ """
19
72
def __init__ (
20
73
self ,
21
74
c_num_classes ,
@@ -77,14 +130,24 @@ def set_params(
77
130
78
131
Parameters
79
132
----------
80
- pi_vec : numpy.ndarray
81
- a real vector in :math:`[0, 1]^K`. The sum of its elements must be 1.
82
- a_mat : numpy.ndarray
83
- a real matrix in :math:`[0, 1]^{KxK}`. The sum of each row elements must be 1.
84
- mu_vecs : numpy.ndarray
85
- vectors of real numbers
86
- lambda_mats : numpy.ndarray
87
- positive definite symetric matrices
133
+ pi_vec : numpy.ndarray, optional
134
+ A vector of real numbers in :math:`[0, 1]`,
135
+ by default [1/c_num_classes, 1/c_num_classes, ... , 1/c_num_classes].
136
+ Sum of its elements must be 1.0.
137
+ a_mat : numpy.ndarray, optional
138
+ A matrix of real numbers in :math:`[0, 1]`,
139
+ by default a matrix obtained by stacking
140
+ [1/c_num_classes, 1/c_num_classes, ... , 1/c_num_classes].
141
+ Sum of the elements of each row vector must be 1.0.
142
+ If a single vector is input, will be broadcasted.
143
+ mu_vecs : numpy.ndarray, optional
144
+ Vectors of real numbers,
145
+ by default zero vectors.
146
+ If a single vector is input, will be broadcasted.
147
+ lambda_mats : numpy.ndarray, optional
148
+ Positive definite symetric matrices,
149
+ by default the identity matrices.
150
+ If a single matrix is input, it will be broadcasted.
88
151
"""
89
152
if pi_vec is not None :
90
153
_check .float_vec_sum_1 (pi_vec , "pi_vec" , ParameterFormatError )
@@ -131,7 +194,34 @@ def set_h_params(
131
194
h_nus = None ,
132
195
h_w_mats = None ,
133
196
):
197
+ """Set the hyperparameters of the prior distribution.
134
198
199
+ Parameters
200
+ ----------
201
+ h_eta_vec : numpy.ndarray, optional
202
+ A vector of positive real numbers,
203
+ by default [1/2, 1/2, ... , 1/2]
204
+ h_zeta_vecs : numpy.ndarray, optional
205
+ Vectors of positive numbers,
206
+ by default vectors whose elements are all 1/2
207
+ If a single vector is input, will be broadcasted.
208
+ h_m_vecs : numpy.ndarray, optional
209
+ Vectors of real numbers,
210
+ by default zero vectors
211
+ If a single vector is input, will be broadcasted.
212
+ h_kappas : float or numpy.ndarray, optional
213
+ Positive real numbers,
214
+ by default [1.0, 1.0, ... , 1.0].
215
+ If a single real number is input, it will be broadcasted.
216
+ h_nus : float or numpy.ndarray, optional
217
+ Real numbers greater than ``c_degree-1``,
218
+ by default [c_degree, c_degree, ... , c_degree]
219
+ If a single real number is input, it will be broadcasted.
220
+ h_w_mats : numpy.ndarray, optional
221
+ Positive definite symetric matrices,
222
+ by default the identity matrices.
223
+ If a single matrix is input, it will be broadcasted.
224
+ """
135
225
if h_eta_vec is not None :
136
226
_check .pos_floats (h_eta_vec ,'h_eta_vec' ,ParameterFormatError )
137
227
self .h_eta_vec [:] = h_eta_vec
@@ -171,12 +261,34 @@ def set_h_params(
171
261
self .h_w_mats [:] = h_w_mats
172
262
173
263
def get_params (self ):
264
+ """Get the parameter of the sthocastic data generative model.
265
+
266
+ Returns
267
+ -------
268
+ params : {str:float, numpy.ndarray}
269
+ * ``"pi_vec"`` : The value of ``self.pi_vec``
270
+ * ``"a_mat"`` : The value of ``self.a_mat``
271
+ * ``"mu_vecs"`` : The value of ``self.mu_vecs``
272
+ * ``"lambda_mats"`` : The value of ``self.lambda_mats``
273
+ """
174
274
return {'pi_vec' :self .pi_vec ,
175
275
'a_mat' :self .a_mat ,
176
276
'mu_vecs' :self .mu_vecs ,
177
277
'lambda_mats' : self .lambda_mats }
178
278
179
279
def get_h_params (self ):
280
+ """Get the hyperparameters of the prior distribution.
281
+
282
+ Returns
283
+ -------
284
+ h_params : {str:float, np.ndarray}
285
+ * ``"h_eta_vec"`` : The value of ``self.h_eta_vec``
286
+ * ``"h_zeta_vecs"`` : The value of ``self.h_zeta_vecs``
287
+ * ``"h_m_vecs"`` : The value of ``self.h_m_vecs``
288
+ * ``"h_kappas"`` : The value of ``self.h_kappas``
289
+ * ``"h_nus"`` : The value of ``self.h_nus``
290
+ * ``"h_w_mats"`` : The value of ``self.h_w_mats``
291
+ """
180
292
return {'h_eta_vec' :self .h_eta_vec ,
181
293
'h_zeta_vecs' :self .h_zeta_vecs ,
182
294
'h_m_vecs' :self .h_m_vecs ,
@@ -185,17 +297,138 @@ def get_h_params(self):
185
297
'h_w_mats' :self .h_w_mats }
186
298
187
299
def gen_params (self ):
188
- pass
300
+ """Generate the parameter from the prior distribution.
301
+
302
+ To confirm the generated vaules, use `self.get_params()`.
303
+ """
304
+ self .pi_vec [:] = self .rng .dirichlet (self .h_eta_vec )
305
+ for k in range (self .c_num_classes ):
306
+ self .a_mat [k ] = self .rng .dirichlet (self .h_zeta_vecs [k ])
307
+ for k in range (self .c_num_classes ):
308
+ self .lambda_mats [k ] = ss_wishart .rvs (df = self .h_nus [k ],scale = self .h_w_mats [k ],random_state = self .rng )
309
+ self .mu_vecs [k ] = self .rng .multivariate_normal (mean = self .h_m_vecs [k ],cov = np .linalg .inv (self .h_kappas [k ]* self .lambda_mats [k ]))
189
310
190
- def gen_sample (self ):
191
- pass
311
+ def gen_sample (self ,sample_length ):
312
+ """Generate a sample from the stochastic data generative model.
313
+
314
+ Parameters
315
+ ----------
316
+ sample_length : int
317
+ A positive integer
318
+
319
+ Returns
320
+ -------
321
+ x : numpy ndarray
322
+ 2-dimensional array whose shape is
323
+ ``(sample_length,c_degree)`` .
324
+ Its elements are real numbers.
325
+ z : numpy ndarray
326
+ 2-dimensional array whose shape is
327
+ ``(sample_length,c_num_classes)``
328
+ whose rows are one-hot vectors.
329
+ """
330
+ _check .pos_int (sample_length ,'sample_length' ,DataFormatError )
331
+ z = np .zeros ([sample_length ,self .c_num_classes ],dtype = int )
332
+ x = np .empty ([sample_length ,self .c_degree ])
333
+ _lambda_mats_inv = np .linalg .inv (self .lambda_mats )
334
+
335
+ # i=0
336
+ k = self .rng .choice (self .c_num_classes ,p = self .pi_vec )
337
+ z [0 ,k ] = 1
338
+ x [0 ] = self .rng .multivariate_normal (mean = self .mu_vecs [k ],cov = _lambda_mats_inv [k ])
339
+ # i>0
340
+ for i in range (1 ,sample_length ):
341
+ k = self .rng .choice (self .c_num_classes ,p = self .a_mat [np .argmax (z [i - 1 ])])
342
+ z [i ,k ] = 1
343
+ x [i ] = self .rng .multivariate_normal (mean = self .mu_vecs [k ],cov = _lambda_mats_inv [k ])
344
+ return x ,z
192
345
193
- def save_sample (self ):
194
- pass
346
+ def save_sample (self ,filename ,sample_length ):
347
+ """Save the generated sample as NumPy ``.npz`` format.
348
+
349
+ It is saved as a NpzFile with keyword: \" x\" , \" z\" .
350
+
351
+ Parameters
352
+ ----------
353
+ filename : str
354
+ The filename to which the sample is saved.
355
+ ``.npz`` will be appended if it isn't there.
356
+ sample_length : int
357
+ A positive integer
358
+
359
+ See Also
360
+ --------
361
+ numpy.savez_compressed
362
+ """
363
+ x ,z = self .gen_sample (sample_length )
364
+ np .savez_compressed (filename ,x = x ,z = z )
195
365
196
- def visualize_model (self ):
197
- pass
366
+ def visualize_model (self , sample_length = 200 ):
367
+ """Visualize the stochastic data generative model and generated samples.
198
368
369
+ Parameters
370
+ ----------
371
+ sample_length : int, optional
372
+ A positive integer, by default 100
373
+
374
+ Examples
375
+ --------
376
+ >>> from bayesml import hiddenmarkovnormal
377
+ >>> import numpy as np
378
+ >>> model = hiddenmarkovnormal.GenModel(
379
+ c_num_classes=2,
380
+ c_degree=1,
381
+ mu_vecs=np.array([[5],[-5]]),
382
+ a_mat=np.array([[0.95,0.05],[0.1,0.9]]))
383
+ >>> model.visualize_model()
384
+ pi_vec:
385
+ [0.5 0.5]
386
+ a_mat:
387
+ [[0.95 0.05]
388
+ [0.1 0.9 ]]
389
+ mu_vecs:
390
+ [[ 5.]
391
+ [-5.]]
392
+ lambda_mats:
393
+ [[[1.]]
394
+
395
+ [[1.]]]
396
+
397
+ .. image:: ./images/hiddenmarkovnormal_example.png
398
+ """
399
+ if self .c_degree == 1 :
400
+ print (f"pi_vec:\n { self .pi_vec } " )
401
+ print (f"a_mat:\n { self .a_mat } " )
402
+ print (f"mu_vecs:\n { self .mu_vecs } " )
403
+ print (f"lambda_mats:\n { self .lambda_mats } " )
404
+ _lambda_mats_inv = np .linalg .inv (self .lambda_mats )
405
+ fig , axes = plt .subplots ()
406
+ sample , latent_vars = self .gen_sample (sample_length )
407
+
408
+ change_points = [0 ]
409
+ for i in range (1 ,sample_length ):
410
+ if np .any (latent_vars [i - 1 ] != latent_vars [i ]):
411
+ change_points .append (i )
412
+ change_points .append (sample_length )
413
+
414
+ cm = plt .get_cmap ('jet' )
415
+ for i in range (1 ,len (change_points )):
416
+ axes .axvspan (
417
+ change_points [i - 1 ],
418
+ change_points [i ],
419
+ color = cm (
420
+ int ((np .argmax (latent_vars [change_points [i - 1 ]])
421
+ / (self .c_num_classes - 1 )) * 255 )
422
+ ),
423
+ alpha = 0.3 ,
424
+ ls = '' ,
425
+ )
426
+ axes .plot (np .arange (sample .shape [0 ]),sample )
427
+ axes .set_xlabel ("time" )
428
+ axes .set_ylabel ("x" )
429
+ plt .show ()
430
+ else :
431
+ raise (ParameterFormatError ("if c_degree > 1, it is impossible to visualize the model by this function." ))
199
432
200
433
class LearnModel (base .Posterior ,base .PredictiveMixin ):
201
434
def __init__ (
0 commit comments