-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathsmoothDTW_demo.py
executable file
·95 lines (86 loc) · 3.5 KB
/
smoothDTW_demo.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
# This code is shared under the
# Attribution-NonCommercial-ShareAlike 4.0 International
# Please find the full license in the main directory under LICENSE.MD
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Demo for getting SmoothDTW-based alignment score
@author: isma.hadji
@email: [email protected]
"""
from absl import app
import numpy as np
import tensorflow as tf
import random
def assign2Tensor(tensor,i,j,new_val):
""" function to deal with tf.Tensors being non-assignable """
# create mask
mask = np.ones(tensor.shape, dtype=np.float32)
# hack to assign a new value to tensor at position (i,j)
mask[i,j] = 0
mask = tf.convert_to_tensor(mask, dtype=tf.float32)
tensor = (tensor*mask) + (new_val * (1-mask))
return tensor
def minGamma(inputs,gamma=1):
""" continuous relaxation of min defined in the D3TW paper"""
if gamma == 0:
minG = tf.reduce_min(inputs)
else:
# log-sum-exp stabilization trick
zi = (-inputs / gamma)
max_zi = tf.reduce_max(zi)
log_sum_G = max_zi + tf.math.log(tf.reduce_sum(tf.math.exp(zi-max_zi)))
minG = -gamma * log_sum_G
return minG
def softDTW(embs1, embs2, distance_type='cosine', softning='dtw_prob', gamma_s=0.1, gamma_f=0.1, D2TW_NORM=True):
""" function to obtain a soft (differentiable version of DTW)
embs1, embs2: embedding of size N*D and M*D (N and M : number of video frames
and D: dimensionality of of the embedding vector)
"""
# first get a pairwise distance matrix
if distance_type == 'cosine':
dist = tf.matmul(embs1, embs2, transpose_b=True)
else:
raise ValueError('distance_type %s not supported for now' % distance_type)
# normalize distance column-wise
if D2TW_NORM:
dist = -tf.math.log(tf.nn.softmax(dist/gamma_f,axis=0)) # eq 8
nrows, ncols = dist.shape
# initialize soft-DTW table
sdtw = tf.zeros((nrows+1,ncols+1), dtype=tf.float32)
# obtain dtw table using min_gamma or softMin relaxation
for i in range(0,nrows+1):
for j in range(0,ncols+1):
if (i==0) and (j==0):
new_val = tf.cast(0.0, tf.float32)
sdtw = assign2Tensor(sdtw,i,j,new_val)
elif (i==0) and (j!=0):
new_val = tf.float32.max
sdtw = assign2Tensor(sdtw,i,j,new_val)
elif (i!=0) and (j==0):
new_val = tf.float32.max
sdtw = assign2Tensor(sdtw,i,j,new_val)
else:
neighbors = tf.stack([sdtw[i,j-1], sdtw[i-1,j-1], sdtw[i-1,j]])
if softning == 'dtw_minGamma':
new_val = dist[i-1,j-1] + minGamma(neighbors, gamma_s) # eq 6
sdtw = assign2Tensor(sdtw,i,j,new_val)
elif softning == 'dtw_prob':
probs = tf.nn.softmax((-neighbors)/gamma_s)
new_val = dist[i-1,j-1] + (probs[0] * sdtw[i,j-1]) + (probs[1] * sdtw[i-1,j-1]) + (probs[2] * sdtw[i-1,j]) # eq 5
sdtw = assign2Tensor(sdtw,i,j,new_val)
elif softning == 'non-diff':
new_val = dist[i-1,j-1] + tf.reduce_min([sdtw[i,j-1], sdtw[i-1,j-1], sdtw[i-1,j]]) # non-differentiable version
sdtw = assign2Tensor(sdtw,i,j,new_val)
else:
raise ValueError('only softning based on dtw_minGamma or dtw_prob supported for now.')
return sdtw, dist
def main(_):
""" run demo """
np.random.seed(0)
embs1 = np.random.rand(20,128).astype(np.float32)
embs2 = np.random.rand(30,128).astype(np.float32)
sdtw, _ = softDTW(embs1, embs2)
print('sdtw score:%0.3f' % sdtw[-1,-1])
if __name__ == '__main__':
app.run(main)