Skip to content

Commit 9537119

Browse files
committed
Add visualize_posterior
1 parent 07ec7e4 commit 9537119

File tree

3 files changed

+160
-19
lines changed

3 files changed

+160
-19
lines changed

bayesml/hiddenmarkovnormal/_hiddenmarkovnormal.py

Lines changed: 151 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1023,10 +1023,11 @@ def update_posterior(
10231023
* 'random_responsibility': randomly assign responsibility to gamma_vecs
10241024
"""
10251025
_check.float_vecs(x,'x',DataFormatError)
1026-
if x.shape[-1] != self.c_degree:
1027-
raise(DataFormatError(
1028-
"x.shape[-1] must be self.c_degree: "
1029-
+ f"x.shape[-1]={x.shape[-1]}, self.c_degree={self.c_degree}"))
1026+
_check.shape_consistency(
1027+
x.shape[-1],"x.shape[-1]",
1028+
self.c_degree,"self.c_degree",
1029+
DataFormatError
1030+
)
10301031
x = x.reshape(-1,self.c_degree)
10311032
self._length = x.shape[0]
10321033
self._ln_rho = np.zeros([self._length,self.c_num_classes])
@@ -1105,19 +1106,24 @@ def estimate_params(self,loss="squared"):
11051106
"""Estimate the parameter under the given criterion.
11061107
11071108
Note that the criterion is applied to estimating
1108-
``xxx``, ``xxx`` and ``xxx`` independently.
1109-
Therefore, a tuple of xxx, xxx and xxx will be returned when loss=\"xxx\"
1109+
``pi_vec``, ``a_mat`` ``mu_vecs`` and ``lambda_mats`` independently.
1110+
Therefore, a tuple of the dirichlet distribution,
1111+
the student's t-distributions and
1112+
the wishart distributions will be returned when loss=\"KL\"
11101113
11111114
Parameters
11121115
----------
11131116
loss : str, optional
11141117
Loss function underlying the Bayes risk function, by default \"xxx\".
1115-
This function supports \"xxx\", \"xxx\", and \"xxx\".
1118+
This function supports \"squared\", \"0-1\", and \"KL\".
11161119
11171120
Returns
11181121
-------
11191122
Estimates : a tuple of {numpy ndarray, float, None, or rv_frozen}
1120-
* ``xxx`` : the estimate for xxx
1123+
* ``pi_vec_hat`` : the estimate for pi_vec
1124+
* ``a_mat_hat`` : the estimate for a_mat
1125+
* ``mu_vecs_hat`` : the estimate for mu_vecs
1126+
* ``Lambda_mats_hat`` : the estimate for Lambda_mats
11211127
The estimated values under the given loss function.
11221128
If it is not exist, `np.nan` will be returned.
11231129
If the loss function is \"KL\", the posterior distribution itself
@@ -1128,16 +1134,150 @@ def estimate_params(self,loss="squared"):
11281134
scipy.stats.rv_continuous
11291135
scipy.stats.rv_discrete
11301136
"""
1131-
pass
1137+
if loss == "squared":
1138+
return (self.hn_eta_vec/self.hn_eta_vec.sum(),
1139+
self.hn_zeta_vecs/self.hn_zeta_vecs.sum(axis=1,keepdims=True),
1140+
self.hn_m_vecs,
1141+
self._e_lambda_mats)
1142+
elif loss == "0-1":
1143+
pi_vec_hat = np.empty(self.c_num_classes)
1144+
if np.all(self.hn_eta_vec > 1):
1145+
pi_vec_hat[:] = (self.hn_eta_vec - 1) / (np.sum(self.hn_eta_vec) - self.c_degree)
1146+
else:
1147+
warnings.warn("MAP estimate of pi_vec doesn't exist for the current hn_eta_vec.",ResultWarning)
1148+
pi_vec_hat[:] = np.nan
1149+
a_mat_hat = np.empty([self.c_num_classes,self.c_num_classes])
1150+
for i in range(self.c_num_classes):
1151+
if np.all(self.hn_eta_vec > 1):
1152+
a_mat_hat[i] = (self.hn_zeta_vecs[i] - 1) / (np.sum(self.hn_zeta_vecs[i]) - self.c_degree)
1153+
else:
1154+
warnings.warn(f"MAP estimate of a_mat[{i}] doesn't exist for the current hn_zeta_vecs[{i}].",ResultWarning)
1155+
a_mat_hat[i] = np.nan
1156+
lambda_mats_hat = np.empty([self.c_num_classes,self.c_degree,self.c_degree])
1157+
for k in range(self.c_num_classes):
1158+
if self.hn_nus[k] >= self.c_degree + 1:
1159+
lambda_mats_hat[k] = (self.hn_nus[k] - self.c_degree - 1) * self.hn_w_mats[k]
1160+
else:
1161+
warnings.warn(f"MAP estimate of lambda_mat doesn't exist for the current hn_nus[{k}].",ResultWarning)
1162+
lambda_mats_hat[k] = np.nan
1163+
return pi_vec_hat, a_mat_hat, self.hn_m_vecs, lambda_mats_hat
1164+
elif loss == "KL":
1165+
a_mat_pdfs = []
1166+
mu_vec_pdfs = []
1167+
lambda_mat_pdfs = []
1168+
for k in range(self.c_num_classes):
1169+
a_mat_pdfs.append(ss_dirichlet(self.hn_zeta_vecs[k]))
1170+
mu_vec_pdfs.append(ss_multivariate_t(loc=self.hn_m_vecs[k],
1171+
shape=self.hn_w_mats_inv[k] / self.hn_kappas[k] / (self.hn_nus[k] - self.c_degree + 1),
1172+
df=self.hn_nus[k] - self.c_degree + 1))
1173+
lambda_mat_pdfs.append(ss_wishart(df=self.hn_nus[k],scale=self.hn_w_mats[k]))
1174+
return (ss_dirichlet(self.hn_eta_vec),
1175+
a_mat_pdfs,
1176+
mu_vec_pdfs,
1177+
lambda_mat_pdfs)
1178+
else:
1179+
raise(CriteriaError(f"loss={loss} is unsupported. "
1180+
+"This function supports \"squared\", \"0-1\", and \"KL\"."))
11321181

11331182
def visualize_posterior(self):
11341183
"""Visualize the posterior distribution for the parameter.
11351184
11361185
Examples
11371186
--------
1138-
>>> from bayesml import xxx
1187+
>>> from bayesml import hiddenmarkovnormal
1188+
>>> gen_model = hiddenmarkovnormal.GenModel(
1189+
>>> c_num_classes=2,
1190+
>>> c_degree=1,
1191+
>>> mu_vecs=np.array([[2],[-2]]),
1192+
>>> a_mat=np.array([[0.95,0.05],[0.1,0.9]])
1193+
>>> x,z = gen_model.gen_sample(100)
1194+
>>> learn_model = hiddenmarkovnormal.LearnModel(c_num_classes=2, c_degree=1)
1195+
>>> learn_model.update_posterior(x)
1196+
>>> learn_model.visualize_posterior()
1197+
hn_alpha_vec:
1198+
[103.89444088 97.10555912]
1199+
E[pi_vec]:
1200+
[0.51688777 0.48311223]
1201+
hn_zeta_vecs:
1202+
[[0.90892737 0.09107263]
1203+
[0.08810659 0.91189341]]
1204+
hn_m_vecs (equivalent to E[mu_vecs]):
1205+
[[-2.00135008]
1206+
[ 1.97461369]]
1207+
hn_kappas:
1208+
[104.39444088 97.60555912]
1209+
hn_nus:
1210+
[104.39444088 97.60555912]
1211+
hn_w_mats:
1212+
[[[0.00890667]]
1213+
1214+
[[0.01009789]]]
1215+
E[lambda_mats]=
1216+
[[[0.92980714]]
1217+
1218+
[[0.98560985]]]
1219+
1220+
.. image:: ./images/hiddenmarkovnormal_posterior.png
11391221
"""
1140-
pass
1222+
print("hn_alpha_vec:")
1223+
print(f"{self.hn_eta_vec}")
1224+
print("E[pi_vec]:")
1225+
print(f"{self.hn_eta_vec / self.hn_eta_vec.sum()}")
1226+
print("hn_zeta_vecs:")
1227+
print(f"{self.hn_zeta_vecs/self.hn_zeta_vecs.sum(axis=1,keepdims=True)}")
1228+
print("hn_m_vecs (equivalent to E[mu_vecs]):")
1229+
print(f"{self.hn_m_vecs}")
1230+
print("hn_kappas:")
1231+
print(f"{self.hn_kappas}")
1232+
print("hn_nus:")
1233+
print(f"{self.hn_nus}")
1234+
print("hn_w_mats:")
1235+
print(f"{self.hn_w_mats}")
1236+
print("E[lambda_mats]=")
1237+
print(f"{self._e_lambda_mats}")
1238+
_, _, mu_vec_pdfs, lambda_mat_pdfs = self.estimate_params(loss="KL")
1239+
if self.c_degree == 1:
1240+
fig, axes = plt.subplots(1,2)
1241+
axes[0].set_xlabel("mu_vecs")
1242+
axes[0].set_ylabel("Density")
1243+
axes[1].set_xlabel("lambda_mats")
1244+
axes[1].set_ylabel("Log density")
1245+
for k in range(self.c_num_classes):
1246+
# for mu_vecs
1247+
x = np.linspace(self.hn_m_vecs[k,0]-4.0*np.sqrt((self.hn_w_mats_inv[k] / self.hn_kappas[k] / self.hn_nus[k])[0,0]),
1248+
self.hn_m_vecs[k,0]+4.0*np.sqrt((self.hn_w_mats_inv[k] / self.hn_kappas[k] / self.hn_nus[k])[0,0]),
1249+
100)
1250+
axes[0].plot(x,mu_vec_pdfs[k].pdf(x))
1251+
# for lambda_mats
1252+
x = np.linspace(max(1.0e-8,self.hn_nus[k]*self.hn_w_mats[k]-4.0*np.sqrt(self.hn_nus[k]/2.0)*(2.0*self.hn_w_mats[k])),
1253+
self.hn_nus[k]*self.hn_w_mats[k]+4.0*np.sqrt(self.hn_nus[k]/2.0)*(2.0*self.hn_w_mats[k]),
1254+
500)
1255+
axes[1].plot(x[:,0,0],lambda_mat_pdfs[k].logpdf(x[:,0,0]))
1256+
1257+
fig.tight_layout()
1258+
plt.show()
1259+
1260+
elif self.c_degree == 2:
1261+
fig, axes = plt.subplots()
1262+
for k in range(self.c_num_classes):
1263+
x = np.linspace(self.hn_m_vecs[k,0]-3.0*np.sqrt((self.hn_w_mats_inv[k] / self.hn_kappas[k] / self.hn_nus[k])[0,0]),
1264+
self.hn_m_vecs[k,0]+3.0*np.sqrt((self.hn_w_mats_inv[k] / self.hn_kappas[k] / self.hn_nus[k])[0,0]),
1265+
100)
1266+
y = np.linspace(self.hn_m_vecs[k,1]-3.0*np.sqrt((self.hn_w_mats_inv[k] / self.hn_kappas[k] / self.hn_nus[k])[1,1]),
1267+
self.hn_m_vecs[k,1]+3.0*np.sqrt((self.hn_w_mats_inv[k] / self.hn_kappas[k] / self.hn_nus[k])[1,1]),
1268+
100)
1269+
xx, yy = np.meshgrid(x,y)
1270+
grid = np.empty((100,100,2))
1271+
grid[:,:,0] = xx
1272+
grid[:,:,1] = yy
1273+
axes.contour(xx,yy,mu_vec_pdfs[k].pdf(grid),cmap='Blues')
1274+
axes.plot(self.hn_m_vecs[k,0],self.hn_m_vecs[k,1],marker="x",color='red')
1275+
axes.set_xlabel("mu_vec[0]")
1276+
axes.set_ylabel("mu_vec[1]")
1277+
plt.show()
1278+
1279+
else:
1280+
raise(ParameterFormatError("if c_degree > 2, it is impossible to visualize the model by this function."))
11411281

11421282
def get_p_params(self):
11431283
"""Get the parameters of the predictive distribution.

bayesml/hiddenmarkovnormal/_hiddenmarkovnormal_test.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,18 @@
22
import numpy as np
33

44
gen_model = hiddenmarkovnormal.GenModel(
5-
c_num_classes=3,
6-
c_degree=1,
7-
mu_vecs=np.array([[5],[0],[-5]]),
8-
a_mat=np.array([[0.95,0.05,0.0],[0.0,0.9,0.1],[0.1,0.0,0.9]])
5+
c_num_classes=2,
6+
c_degree=2,
7+
mu_vecs=np.array([[2,2],[-2,-2]]),
8+
a_mat=np.array([[0.95,0.05],[0.1,0.9]])
99
)
10-
# gen_model.visualize_model()
10+
# model.visualize_model()
1111
x,z = gen_model.gen_sample(sample_length=200)
1212

1313
learn_model = hiddenmarkovnormal.LearnModel(
14-
c_num_classes=3,
15-
c_degree=1,
14+
c_num_classes=2,
15+
c_degree=2,
1616
)
1717
learn_model.update_posterior(x)#,init_type='random_responsibility')
18-
# print(learn_model.get_hn_params())
18+
learn_model.visualize_posterior()
19+
# print(learn_model.estimate_params(loss='KL'))
38.6 KB
Loading

0 commit comments

Comments
 (0)