-
Notifications
You must be signed in to change notification settings - Fork 0
/
gmm_steps.py
128 lines (80 loc) · 4.16 KB
/
gmm_steps.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import numpy
import math
def log_likelihood(X, mean, variance):
# [TODO] Complete the calculation of the LL point-estimate
# Tip: Use math.pi as a replacement for the Pi constant
#return numpy.ones(shape=len(X), dtype=float)
#### ANSWER ####
# [TODO] Complete the calculation of the LL point-estimate
# Tip: Use math.pi as a replacement for the Pi constant
# Tip 2: In this task each line corresponds to one samples and each column one dimension
# Tip 3: Remember that the inverse of a diagonal matrix A is 1/A (only diagonal elements).
# This is true for \Sigma
# Tip 4: Don't forget LL corresponds to one scalar per example
return -0.5 * (X.shape[1]*math.log(2*math.pi) + numpy.log(variance).sum()) \
-0.5 * ( (X-mean)**2 * (1./variance)).sum(axis=1)
def e_step(X, weights, means, variances):
## log-likelihoods = 2D numpy.ndarray with as many rows as examples and
## as many columns as dimensions in X
# [TODO] calculate the first_terms first:
# first_terms = log ( w_i ) + log_likelihood(x_t; mu_i, Sigma_i)
first_terms = numpy.array([math.log(w) + log_likelihood(X, mu, sigma) for (w,mu,sigma) in zip(weights, means, variances)])
# [TODO] compute the denominator - i.e., the second term:
# log ( sum_j^M [ w_j * Normal(x_t; mu_j, Sigma_j) ] )
# this is the place where we would like to use the log-add-exp trick
# to do so, you have to operate in pairs and accumulate - use a ``for`` loop
# The shape of second_term is (len(means), len(X))
second_term = first_terms[0]
for k in first_terms[1:]: second_term = logadd(second_term, k)
# WARNING: Do not change the following return string
#return numpy.exp(first_terms - second_term).T, second_term
responsibilities = numpy.exp(first_terms - second_term).T
zeroth_order_stats = numpy.sum(responsibilities, axis=0)
first_order_stats = numpy.dot(responsibilities.T, X).T
return zeroth_order_stats, first_order_stats
def acc_stats(gmm_stats):
zeroth_order_stats = numpy.zeros(gmm_stats[0][0].shape)
first_order_stats = numpy.zeros(gmm_stats[0][1].shape)
for stats in gmm_stats:
zeroth_order_stats += stats[0]
first_order_stats += stats[1]
return zeroth_order_stats, first_order_stats
def m_step(gmm_stats):
zeroth_order_stats = gmm_stats[0]
first_order_stats = gmm_stats[1]
means = first_order_stats / zeroth_order_stats
return means.T
def logadd(log_a, log_b):
"""Computes log(a+b) given log(a) and log(b) as numerically stable as possible
You can compute it, in a numerically stable way, like this:
log (a + b) = log(a) + log(1 + b/a)
= log(a) + log(1 + exp(log(b) - log(a)))
= log(a) + log1p(exp(log(b) - log(a)))
Please notice that for this to work correctly, it is expected that a > b. If
that is not the case (i.e., b > a), it is more stable if you compute:
log (b + a) = log(b) + log(1 + a/b)
= log(b) + log(1 + exp(log(a) - log(b)))
= log(b) + log1p(exp(log(a) - log(b)))
So, a test must be done in every addition. Also note that, iff:
a > b => log(a) > log(b)
because log(.) is a monotonically increasing function. This will simplify our
comparisons.
Parameters:
log_a (numpy.ndarray): A 1D array with the logarithms of another 1D array
of the same size
log_b (numpy.ndarray): A second 1D array with the logarithms of another 1D
array of the same size
Returns:
numpy.ndarray: A 1D array with the same size as the input vectors,
representing the logarithm of the sum of the two terms ``a`` and ``b``.
"""
# use numpy.where to select all elements that are bigger or smaller
smallest_log = numpy.where(log_a < log_b, log_a, log_b)
biggest_log = numpy.where(log_a > log_b, log_a, log_b)
# we now perform the addition
diff = smallest_log - biggest_log
return numpy.where(
diff < -39.14, #why? => exp(-39.14) = 1e-17, so log(1+1e-17) ~ log(1) = 0
biggest_log,
biggest_log + numpy.log1p(numpy.exp(diff))
)