/
net.py
196 lines (151 loc) · 8.77 KB
/
net.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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
#coding:utf-8
#!/usr/bin/env python
import os
import math
import numpy as np
import chainer
from chainer import cuda, Function, gradient_check, Variable, optimizers, serializers, utils
from chainer import Link, Chain, ChainList
import chainer.functions as F
import chainer.links as L
class DAGMM(Chain):
def __init__(self, CN_h_units, CN_z_units, CN_o_units, EN_h_units, EN_o_units):
super(DAGMM, self).__init__()
with self.init_scope():
# Compression Network
self.ec_l1 = L.Linear(None,CN_h_units)
self.ec_l2 = L.Linear(None,CN_z_units)
self.dc_l1 = L.Linear(None,CN_h_units)
self.dc_l2 = L.Linear(None,CN_o_units)
# Estimation Network
self.en_l1 = L.Linear(None,EN_h_units)
self.en_l2 = L.Linear(None,2)
dirGMMparameters = "./tmp_GMMparameters/"
def lossFunc(self,gpu=-1):
if gpu >= 0:
xp = cuda.cupy
else:
xp = np
def lf(x):
y, energy, sigma = self.fwd(x, isTraining=True, gpu=gpu)
# reconstLoss = F.mean_squared_error(x, y) # mseで誤差は計算しない?
reconstLoss = F.sum(F.squared_error(x,y)) / len(x) # 再構築誤差(ミニバッチ平均)
avgEnergy = F.sum(energy) / len(x) # エネルギー(ミニバッチ平均)
# 正則化項について
NumOfClass, zDim, _ = sigma.shape
diagMat = Variable(xp.array(np.array(list(np.eye(zDim))*NumOfClass).reshape(NumOfClass,zDim,zDim).astype(np.float32)))
reg = F.sum((1/sigma) * diagMat)
#重みパラメータ
lambda_1 = 0.1
lambda_2 = 0.005
self.loss = reconstLoss + lambda_1 * avgEnergy + lambda_2 * reg
chainer.report( {'loss': self.loss}, observer=self )
return self.loss
return lf
def fwd(self, x, isTraining=False, gpu=-1):
if gpu >= 0:
xp = cuda.cupy
else:
xp = np
# Compression Network
# エンコード
zc = self.Encoder(x)
# デコード
y = self.Decoder(zc)
# 再構築誤差を計算 relativeユークリッド距離とコサイン類似度
rEucDist = self.relativeEuclideanDistance(x,y)
CosSim = self.cosineSimilarity(x,y)
#潜在変数と再構築誤差を合体
z = F.concat((zc,rEucDist,CosSim), axis=1)
# Estimation Network
gamma = self.Estimation(z)
NumOfData, NumOfClass = gamma.shape
_, zDim = z.shape
# GMM
# 各サンプルの各分布への帰属確率から、各分布の混合比、平均ベクトル、共分散行列を得る
if chainer.config.train:
phi = self._phi(gamma)
mu = self._mu(z,gamma)
sigma = self._sigma(z,gamma,mu)
os.makedirs(self.dirGMMparameters, exist_ok=True)
if chainer.cuda.available:
np.savetxt(self.dirGMMparameters + "phi.csv", np.array((phi.data).get()), delimiter=",")
np.savetxt(self.dirGMMparameters + "mu.csv", np.array((mu.data).get()), delimiter=",")
np.savetxt(self.dirGMMparameters + "sigma.csv", np.array((sigma.data).get()).reshape(NumOfClass,-1), delimiter=",")
else:
np.savetxt(self.dirGMMparameters + "phi.csv", np.array(phi.data), delimiter=",")
np.savetxt(self.dirGMMparameters + "mu.csv", np.array(mu.data), delimiter=",")
np.savetxt(self.dirGMMparameters + "sigma.csv", np.array(sigma.data).reshape(NumOfClass,-1), delimiter=",")
else:
phi = Variable(xp.array(np.loadtxt(self.dirGMMparameters + "phi.csv",delimiter=",").astype(np.float32)))
mu = Variable(xp.array(np.loadtxt(self.dirGMMparameters + "mu.csv",delimiter=",").astype(np.float32)))
sigma = Variable(xp.array((np.loadtxt(self.dirGMMparameters + "sigma.csv",delimiter=",")).reshape(NumOfClass,zDim,zDim).astype(np.float32)))
# エネルギーを計算
# eps = 1e-3 #ランク落ちまたは、行列式が0になってしまう対策
# sigma = sigma + Variable(xp.array(np.array(list(np.eye(zDim))*NumOfClass).reshape(NumOfClass,zDim,zDim).astype(np.float32)) * eps)
sigmaInv = F.batch_inv(sigma) # shape(3D) -> NumOfClass, zDim, zDim
z_broadcast = F.broadcast_to(z, (NumOfClass, NumOfData, zDim)) # shape(3D) -> NumOfClass, NumOfData, zDim
mu_broadcast = F.transpose(F.broadcast_to(mu, (NumOfData, NumOfClass, zDim)),axes=(1,0,2)) # shape(3D) -> NumOfClass,
sa = z_broadcast - mu_broadcast
listForEnr1 = [ F.matmul(sa[i],sigmaInv[i]) for i in range(NumOfClass)] # shape(3D) -> NumOfClass, NumOfData, zDim
listForEnr2 = [ F.sum(listForEnr1[i]*sa[i],axis=1) for i in range(NumOfClass)] # shape(2D) -> NumOfClass, NumOfData
varForEnr = F.stack([listForEnr2[i] for i in range(len(listForEnr2))], axis=0) # リストからVariableへ変換 # shape(2D) -> NumOfClass, NumOfData
numer = F.exp(-(1/2) * varForEnr) # 分子の計算 # shape(2D) -> NumOfClass, NumOfData
denom = F.transpose(F.broadcast_to(F.sqrt(F.batch_det(2*math.pi*sigma)), (NumOfData,NumOfClass)))# 分母の計算 # shape(2D) -> NumOfClass, NumOfData
phi_broadcast = F.transpose(F.broadcast_to(phi,(NumOfData,NumOfClass))) # shape(2D) -> NumOfClass, NumOfData
energy = -1 * F.log(F.sum(phi_broadcast * (numer / denom), axis=0, keepdims=True)) # shape(2D) -> 1, NumOfData
energy = F.transpose(energy) # shape(2D) -> NumOfData,1
if isTraining:
return y, energy, sigma
else:
return z, energy, gamma, y
def Encoder(self,x):
h = F.tanh(self.ec_l1(x))
z = self.ec_l2(h)
return z
def Decoder(self,z):
h = F.tanh(self.dc_l1(z))
y = self.dc_l2(h)
return y
def relativeEuclideanDistance(self, x, y):
L2NormXY = F.sqrt(F.sum(F.squared_error(x,y), axis=1, keepdims=True))
L2NormX = F.reshape(F.sqrt(F.batch_l2_norm_squared(x)), (-1,1))
return L2NormXY / (L2NormX)
def cosineSimilarity(self, x, y):
innerProduct = F.reshape(F.sum(x*y, axis=1), (-1,1))
L2NormX = F.reshape(F.sqrt(F.batch_l2_norm_squared(x)), (-1,1))
L2NormY = F.reshape(F.sqrt(F.batch_l2_norm_squared(y)), (-1,1))
return innerProduct / (L2NormX * L2NormY)
def Estimation(self,z):
h = F.dropout(F.tanh(self.en_l1(z)), 0.5)
gamma = F.softmax(self.en_l2(h))
return gamma
def _phi(self,gamma):
# 各分布の混合比phi (スカラー/各クラス)
phi = F.average(gamma, axis=0)
return phi
def _mu(self,z,gamma):
# 平均ベクトルmu (ベクトル/各クラス)
NumOfData, NumOfClass = gamma.shape
_, zDim = z.shape
gamma_T = F.transpose(gamma) # shape(2D) -> NumOfClass, NumOfData
gamma_broadcast = F.transpose(F.broadcast_to(gamma_T, (zDim, NumOfClass, NumOfData)), axes=(1,2,0)) # shape(3D) -> NumOfClass, NumOfData, zDim
z_broadcast = F.broadcast_to(z, (NumOfClass, NumOfData, zDim)) # shape(3D) -> NumOfClass, NumOfData, zDim
gamma_sum_broadcast = F.transpose(F.broadcast_to(F.sum(gamma, axis=0), (zDim,NumOfClass))) # shape(2D) -> NumOfClass, zDim
mu = F.sum(gamma_broadcast * z_broadcast, axis=1) / gamma_sum_broadcast # shape(2D) -> NumOfClass, zDim
return mu
def _sigma(self,z,gamma,mu):
# 共分散行列sigma (行列/各クラス)
NumOfData, NumOfClass = gamma.shape
_, zDim = z.shape
gamma_T = F.transpose(gamma) # shape(2D) -> NumOfClass, NumOfData
z_broadcast = F.broadcast_to(z, (NumOfClass, NumOfData, zDim)) # shape(3D) -> NumOfClass, NumOfData, zDim
mu_broadcast = F.transpose(F.broadcast_to(mu, (NumOfData, NumOfClass, zDim)),axes=(1,0,2)) # shape(3D) -> NumOfClass, NumOfData, zDim
sa = z_broadcast - mu_broadcast # shape(3D) -> NumOfClass, NumOfData, zDim
listForCov1 = [[F.matmul(F.reshape(j,(-1,1)),F.reshape(j,(1,-1))) for j in i] for i in sa] # 外積の計算
listForCov2 = [F.stack([listForCov1[i][j] for j in range(len(listForCov1[i]))], axis=0) for i in range(len(listForCov1))] # リストからVariableへ変換
vecProduct = F.stack([listForCov2[i] for i in range(len(listForCov2))], axis=0) # リストからVariableへ変換 # shape(4D) -> NumOfClass, NumOfData, zDim, zDim
gamma_broadcastForCov = F.transpose(F.broadcast_to(gamma_T, (zDim, zDim, NumOfClass, NumOfData)), axes=(2,3,1,0))
gamma_sum_broadcastForCov = F.transpose(F.broadcast_to(F.sum(gamma, axis=0), (zDim,zDim,NumOfClass)))
sigma = F.sum(vecProduct*gamma_broadcastForCov, axis=1) / gamma_sum_broadcastForCov # shape(3D) -> NumOfClass, zDim, zDim
return sigma