-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathmdn.py
68 lines (59 loc) · 2.6 KB
/
mdn.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
from keras import backend as K
from keras.engine.topology import Layer
import numpy as np
import math
def get_mixture_coef(output, numComonents=24, outputDim=1):
out_pi = output[:,:numComonents]
out_sigma = output[:,numComonents:2*numComonents]
out_mu = output[:,2*numComonents:]
out_mu = K.reshape(out_mu, [-1, numComonents, outputDim])
out_mu = K.permute_dimensions(out_mu,[1,0,2])
# use softmax to normalize pi into prob distribution
max_pi = K.max(out_pi, axis=1, keepdims=True)
out_pi = out_pi - max_pi
out_pi = K.exp(out_pi)
normalize_pi = 1 / K.sum(out_pi, axis=1, keepdims=True)
out_pi = normalize_pi * out_pi
# use exponential to make sure sigma is positive
out_sigma = K.exp(out_sigma)
return out_pi, out_sigma, out_mu
def tf_normal(y, mu, sigma):
oneDivSqrtTwoPI = 1 / math.sqrt(2*math.pi)
result = y - mu
result = K.permute_dimensions(result, [2,1,0])
result = result * (1 / (sigma + 1e-8))
result = -K.square(result)/2
result = K.exp(result) * (1/(sigma + 1e-8))*oneDivSqrtTwoPI
result = K.prod(result, axis=[0])
return result
def get_lossfunc(out_pi, out_sigma, out_mu, y):
result = tf_normal(y, out_mu, out_sigma)
result = result * out_pi
result = K.sum(result, axis=1, keepdims=True)
result = -K.log(result + 1e-8)
return K.mean(result)
def mdn_loss(numComponents=24, outputDim=1):
def loss(y, output):
out_pi, out_sigma, out_mu = get_mixture_coef(output, numComponents, outputDim)
return get_lossfunc(out_pi, out_sigma, out_mu, y)
return loss
class MixtureDensity(Layer):
def __init__(self, kernelDim, numComponents, **kwargs):
self.hiddenDim = 24
self.kernelDim = kernelDim
self.numComponents = numComponents
super(MixtureDensity, self).__init__(**kwargs)
def build(self, inputShape):
self.inputDim = inputShape[1]
self.outputDim = self.numComponents * (2+self.kernelDim)
self.Wh = K.variable(np.random.normal(scale=0.5,size=(self.inputDim, self.hiddenDim)))
self.bh = K.variable(np.random.normal(scale=0.5,size=(self.hiddenDim)))
self.Wo = K.variable(np.random.normal(scale=0.5,size=(self.hiddenDim, self.outputDim)))
self.bo = K.variable(np.random.normal(scale=0.5,size=(self.outputDim)))
self.trainable_weights = [self.Wh,self.bh,self.Wo,self.bo]
def call(self, x, mask=None):
hidden = K.tanh(K.dot(x, self.Wh) + self.bh)
output = K.dot(hidden,self.Wo) + self.bo
return output
def get_output_shape_for(self, inputShape):
return (inputShape[0], self.outputDim)