9
9
from __future__ import division
10
10
import numpy as np
11
11
from six .moves import xrange
12
+ from scipy .spatial .distance import pdist , squareform
12
13
13
14
from .base_metric import BaseMetricLearner
14
15
15
16
class MLKR (BaseMetricLearner ):
16
17
"""Metric Learning for Kernel Regression (MLKR)"""
17
- def __init__ (self , A = None , epsilon = 0.01 ):
18
+ def __init__ (self , A0 = None , epsilon = 0.01 , alpha = 0.0001 ):
18
19
"""
19
20
MLKR initialization
20
21
21
22
Parameters
22
23
----------
23
- A: Initialization of matrix A. Defaults to the identity matrix.
24
- epsilon: Step size for gradient descent
24
+ A0: Initialization of matrix A. Defaults to the identity matrix.
25
+ epsilon: Step size for gradient descent.
26
+ alpha: Stopping criterion for loss function in gradient descent.
25
27
"""
26
28
self .params = {
27
- "A" : A ,
28
- "epsilon" : epsilon
29
+ "A0" : A0 ,
30
+ "epsilon" : epsilon ,
31
+ "alpha" : alpha
29
32
}
30
33
31
34
def _process_inputs (self , X , y ):
35
+ self .X = np .array (X , copy = False )
36
+ y = np .array (y , copy = False )
32
37
if X .ndim == 1 :
33
38
X = X [:, np .newaxis ]
34
39
if y .ndim == 1 :
35
- y == y [:, np .newaxis ]
36
- self .X = X
40
+ y = y [:, np .newaxis ]
37
41
n , d = X .shape
38
- assert y .shape [0 ] == n
42
+ if y .shape [0 ] != n :
43
+ raise ValueError ('Data and label lengths mismatch: %d != %d'
44
+ % (n , y .shape [0 ]))
39
45
return y , n , d
40
46
41
47
def fit (self , X , y ):
@@ -52,54 +58,48 @@ def fit(self, X, y):
52
58
self: Instance of self
53
59
"""
54
60
y , n , d = self ._process_inputs (X , y )
55
- alpha = 0.0001 # Stopping criterion
56
- if self .params ['A' ] is None :
61
+ if self .params ['A0' ] is None :
57
62
A = np .identity (d ) # Initialize A as eye matrix
58
63
else :
59
- A = self .params ['A ' ]
64
+ A = self .params ['A0 ' ]
60
65
assert A .shape == (d , d )
61
66
cost = np .Inf
62
67
# Gradient descent procedure
63
- while cost > alpha :
64
- K = self ._computeK (X , A , n )
68
+ while cost > self . params [ ' alpha' ] :
69
+ K = self ._computeK (X , A )
65
70
yhat = self ._computeyhat (y , K )
66
71
sum_i = 0
67
72
for i in xrange (n ):
68
73
sum_j = 0
69
74
for j in xrange (n ):
70
- sum_j += (yhat [j ] - y [j ]) * K [i ][j ] * \
71
- (X [i , :] - X [j , :])[:, np .newaxis ].dot \
72
- ((X [i , :] - X [j , :])[:, np .newaxis ].T )
75
+ diffK = (yhat [j ] - y [j ]) * K [i , j ]
76
+ x_ij = (X [i , :] - X [j , :])[:, np .newaxis ]
77
+ x_ijT = x_ij .T
78
+ sum_j += diffK * x_ij .dot (x_ijT )
73
79
sum_i += (yhat [i ] - y [i ]) * sum_j
74
80
gradient = 4 * A .dot (sum_i )
75
81
A -= self .params ['epsilon' ] * gradient
76
82
cost = np .sum (np .square (yhat - y ))
77
83
self ._transformer = A
78
84
return self
79
85
80
- def _computeK (self , X , A , n ):
86
+ def _computeK (self , X , A ):
81
87
"""
82
88
Internal helper function to compute K matrix.
83
89
84
90
Parameters:
85
91
----------
86
92
X: (n x d) array of samples
87
93
A: (d x d) 'A' matrix
88
- n: number of rows in X
89
94
90
95
Returns:
91
96
-------
92
97
K: (n x n) K matrix where Kij = exp(-distance(x_i, x_j)) where
93
98
distance is defined as squared L2 norm of (x_i - x_j)
94
99
"""
95
- dist_mat = np .zeros (shape = (n , n ))
96
- for i in xrange (n ):
97
- for j in xrange (n ):
98
- if i == j :
99
- dist = 0
100
- else :
101
- dist = np .sum (np .square (A .dot ((X [i , :] - X [j , :]))))
102
- dist_mat [i , j ] = dist
100
+ dist_mat = pdist (X , metric = 'mahalanobis' , VI = A .T .dot (A ))
101
+ dist_mat = np .square (dist_mat )
102
+ dist_mat = squareform (dist_mat )
103
103
return np .exp (- dist_mat )
104
104
105
105
def _computeyhat (self , y , K ):
0 commit comments