summaryrefslogtreecommitdiff
path: root/ot/stochastic.py
diff options
context:
space:
mode:
authorKilian Fatras <kilianfatras@dhcp-206-12-53-20.eduroam.wireless.ubc.ca>2018-08-28 17:07:55 -0700
committerKilian Fatras <kilianfatras@dhcp-206-12-53-20.eduroam.wireless.ubc.ca>2018-08-28 17:07:55 -0700
commit77b68901c5415ddc5d9ab5215a6fa97723de3de9 (patch)
tree873c5c961f1e2937400f68549542007e3210c2a4 /ot/stochastic.py
parent208ff4627ba28aa3f35328c5928324019c23ddac (diff)
fixed bug in sgd dual
Diffstat (limited to 'ot/stochastic.py')
-rw-r--r--ot/stochastic.py165
1 files changed, 38 insertions, 127 deletions
diff --git a/ot/stochastic.py b/ot/stochastic.py
index 5e8206e..0788f61 100644
--- a/ot/stochastic.py
+++ b/ot/stochastic.py
@@ -435,8 +435,8 @@ def solve_semi_dual_entropic(a, b, M, reg, method, numItermax=10000, lr=None,
##############################################################################
-def batch_grad_dual_alpha(M, reg, alpha, beta, batch_size, batch_alpha,
- batch_beta):
+def batch_grad_dual(M, reg, a, b, alpha, beta, batch_size, batch_alpha,
+ batch_beta):
'''
Computes the partial gradient of F_\W_varepsilon
@@ -444,9 +444,14 @@ def batch_grad_dual_alpha(M, reg, alpha, beta, batch_size, batch_alpha,
..math:
\forall i in batch_alpha,
- grad_alpha_i = 1 * batch_size -
- sum_{j in batch_beta} exp((alpha_i + beta_j - M_{i,j})/reg)
-
+ grad_alpha_i = alpha_i * batch_size/len(beta) -
+ sum_{j in batch_beta} exp((alpha_i + beta_j - M_{i,j})/reg)
+ * a_i * b_j
+
+ \forall j in batch_alpha,
+ grad_beta_j = beta_j * batch_size/len(alpha) -
+ sum_{j in batch_alpha} exp((alpha_i + beta_j - M_{i,j})/reg)
+ * a_i * b_j
where :
- M is the (ns,nt) metric cost matrix
- alpha, beta are dual variables in R^ixR^J
@@ -478,7 +483,7 @@ def batch_grad_dual_alpha(M, reg, alpha, beta, batch_size, batch_alpha,
-------
grad : np.ndarray(ns,)
- partial grad F in alpha
+ partial grad F
Examples
--------
@@ -510,100 +515,20 @@ def batch_grad_dual_alpha(M, reg, alpha, beta, batch_size, batch_alpha,
arXiv preprint arxiv:1711.02283.
'''
- grad_alpha = np.zeros(batch_size)
- grad_alpha[:] = batch_size
- for j in batch_beta:
- grad_alpha -= np.exp((alpha[batch_alpha] + beta[j] -
- M[batch_alpha, j]) / reg)
- return grad_alpha
-
-
-def batch_grad_dual_beta(M, reg, alpha, beta, batch_size, batch_alpha,
- batch_beta):
- '''
- Computes the partial gradient of F_\W_varepsilon
-
- Compute the partial gradient of the dual problem:
-
- ..math:
- \forall j in batch_beta,
- grad_beta_j = 1 * batch_size -
- sum_{i in batch_alpha} exp((alpha_i + beta_j - M_{i,j})/reg)
-
- where :
- - M is the (ns,nt) metric cost matrix
- - alpha, beta are dual variables in R^ixR^J
- - reg is the regularization term
- - batch_alpha and batch_beta are list of index
-
- The algorithm used for solving the dual problem is the SGD algorithm
- as proposed in [19]_ [alg.1]
-
- Parameters
- ----------
-
- M : np.ndarray(ns, nt),
- cost matrix
- reg : float number,
- Regularization term > 0
- alpha : np.ndarray(ns,)
- dual variable
- beta : np.ndarray(nt,)
- dual variable
- batch_size : int number
- size of the batch
- batch_alpha : np.ndarray(bs,)
- batch of index of alpha
- batch_beta : np.ndarray(bs,)
- batch of index of beta
-
- Returns
- -------
-
- grad : np.ndarray(ns,)
- partial grad F in beta
-
- Examples
- --------
-
- >>> n_source = 7
- >>> n_target = 4
- >>> reg = 1
- >>> numItermax = 20000
- >>> lr = 0.1
- >>> batch_size = 3
- >>> log = True
- >>> a = ot.utils.unif(n_source)
- >>> b = ot.utils.unif(n_target)
- >>> rng = np.random.RandomState(0)
- >>> X_source = rng.randn(n_source, 2)
- >>> Y_target = rng.randn(n_target, 2)
- >>> M = ot.dist(X_source, Y_target)
- >>> sgd_dual_pi, log = stochastic.solve_dual_entropic(a, b, M, reg,
- batch_size,
- numItermax, lr, log)
- >>> print(log['alpha'], log['beta'])
- >>> print(sgd_dual_pi)
-
- References
- ----------
-
- [Seguy et al., 2018] :
- International Conference on Learning Representation (2018),
- arXiv preprint arxiv:1711.02283.
+ G = - (np.exp((alpha[batch_alpha, None] + beta[None, batch_beta] -
+ M[batch_alpha, :][:, batch_beta]) / reg) * a[batch_alpha, None] *
+ b[None, batch_beta])
+ grad_beta = np.zeros(np.shape(M)[1])
+ grad_alpha = np.zeros(np.shape(M)[0])
+ grad_beta[batch_beta] = (b[batch_beta] * len(batch_alpha) / np.shape(M)[0] +
+ G.sum(0))
+ grad_alpha[batch_alpha] = (a[batch_alpha] * len(batch_beta) /
+ np.shape(M)[1] + G.sum(1))
- '''
-
- grad_beta = np.zeros(batch_size)
- grad_beta[:] = batch_size
- for i in batch_alpha:
- grad_beta -= np.exp((alpha[i] +
- beta[batch_beta] - M[i, batch_beta]) / reg)
- return grad_beta
+ return grad_alpha, grad_beta
-def sgd_entropic_regularization(M, reg, batch_size, numItermax, lr,
- alternate=True):
+def sgd_entropic_regularization(M, reg, a, b, batch_size, numItermax, lr):
'''
Compute the sgd algorithm to solve the regularized discrete measures
optimal transport dual problem
@@ -628,6 +553,10 @@ def sgd_entropic_regularization(M, reg, batch_size, numItermax, lr,
cost matrix
reg : float number,
Regularization term > 0
+ alpha : np.ndarray(ns,)
+ dual variable
+ beta : np.ndarray(nt,)
+ dual variable
batch_size : int number
size of the batch
numItermax : int number
@@ -677,35 +606,17 @@ def sgd_entropic_regularization(M, reg, batch_size, numItermax, lr,
n_source = np.shape(M)[0]
n_target = np.shape(M)[1]
- cur_alpha = np.random.randn(n_source)
- cur_beta = np.random.randn(n_target)
- if alternate:
- for cur_iter in range(numItermax):
- k = np.sqrt(cur_iter + 1)
- batch_alpha = np.random.choice(n_source, batch_size, replace=False)
- batch_beta = np.random.choice(n_target, batch_size, replace=False)
- grad_F_alpha = batch_grad_dual_alpha(M, reg, cur_alpha, cur_beta,
- batch_size, batch_alpha,
- batch_beta)
- cur_alpha[batch_alpha] += (lr / k) * grad_F_alpha
- grad_F_beta = batch_grad_dual_beta(M, reg, cur_alpha, cur_beta,
- batch_size, batch_alpha,
- batch_beta)
- cur_beta[batch_beta] += (lr / k) * grad_F_beta
-
- else:
- for cur_iter in range(numItermax):
- k = np.sqrt(cur_iter + 1)
- batch_alpha = np.random.choice(n_source, batch_size, replace=False)
- batch_beta = np.random.choice(n_target, batch_size, replace=False)
- grad_F_alpha = batch_grad_dual_alpha(M, reg, cur_alpha, cur_beta,
- batch_size, batch_alpha,
- batch_beta)
- grad_F_beta = batch_grad_dual_beta(M, reg, cur_alpha, cur_beta,
- batch_size, batch_alpha,
- batch_beta)
- cur_alpha[batch_alpha] += (lr / k) * grad_F_alpha
- cur_beta[batch_beta] += (lr / k) * grad_F_beta
+ cur_alpha = np.zeros(n_source)
+ cur_beta = np.zeros(n_target)
+ for cur_iter in range(numItermax):
+ k = np.sqrt(cur_iter / 100 + 1)
+ batch_alpha = np.random.choice(n_source, batch_size, replace=False)
+ batch_beta = np.random.choice(n_target, batch_size, replace=False)
+ update_alpha, update_beta = batch_grad_dual(M, reg, a, b, cur_alpha,
+ cur_beta, batch_size,
+ batch_alpha, batch_beta)
+ cur_alpha += (lr / k) * update_alpha
+ cur_beta += (lr / k) * update_beta
return cur_alpha, cur_beta
@@ -787,7 +698,7 @@ def solve_dual_entropic(a, b, M, reg, batch_size, numItermax=10000, lr=1,
arXiv preprint arxiv:1711.02283.
'''
- opt_alpha, opt_beta = sgd_entropic_regularization(M, reg, batch_size,
+ opt_alpha, opt_beta = sgd_entropic_regularization(M, reg, a, b, batch_size,
numItermax, lr)
pi = (np.exp((opt_alpha[:, None] + opt_beta[None, :] - M[:, :]) / reg) *
a[:, None] * b[None, :])