harmonization.solver
1import numpy as np 2 3from time import time 4from itertools import cycle, product 5 6from joblib import Parallel, delayed 7from tqdm import tqdm 8 9from sklearn.utils import gen_batches 10from harmonization.elastic_net import lasso_path, lasso_crossval 11 12 13def update_D(D, A, B, X, niter=1, eps=1e-15, positivity=False): 14 15 u = np.zeros(D.shape) 16 norm2 = np.zeros(D.shape[1]) 17 to_purge = np.zeros(D.shape[1], dtype=bool) 18 19 for _ in range(niter): 20 # divide by zeros are replaced by 0, but still raise the warning, so we squelch it 21 with np.errstate(divide='ignore', invalid='ignore'): 22 u[:] = np.where(np.diag(A) != 0, (B - np.dot(D, A)) / np.diag(A) + D, 0) 23 24 norm2[:] = np.sqrt(np.sum(u**2, axis=0)) 25 26 # purge unused atoms from D 27 to_purge[:] = norm2 < eps 28 29 if np.any(to_purge): 30 indices = np.random.randint(X.shape[0], size=np.sum(to_purge)) 31 u[:, to_purge] = X[indices].reshape(np.sum(to_purge), u.shape[0]).T 32 norm2[to_purge] = np.sqrt(np.sum(u[:, to_purge]**2, axis=0)) 33 34 D[:] = u / np.maximum(1, norm2) 35 36 if positivity: 37 D[D < 0] = 0 38 39def lasso_path_parallel(D, X, nlambdas, positivity=False, variance=None, fit_intercept=True, standardize=True, use_crossval=False): 40 N = X.shape[0] 41 alpha = np.zeros((N, D.shape[1]), dtype=np.float32) 42 intercept = np.zeros(N, dtype=np.float32) 43 Xhat = np.zeros((N, D.shape[0]), dtype=np.float32) 44 lbda = np.zeros(N, dtype=np.float32) 45 46 if use_crossval: 47 # use_crossval holds the number of split, if not we default to 3 because reasons 48 if use_crossval == 1: 49 n_splits = 3 50 else: 51 n_splits = use_crossval 52 53 Xhat[i], alpha[i], intercept[i], lbda[i] = lasso_crossval(D, X[i], nlam=nlambdas, fit_intercept=fit_intercept, n_splits=n_splits, 54 pos=positivity, standardize=standardize, penalty=None) 55 else: 56 for i in range(N): 57 alpha[i], intercept[i], Xhat[i], lbda[i] = lasso_path(D, X[i], nlam=nlambdas, fit_intercept=fit_intercept, criterion='aicc', 58 pos=positivity, standardize=standardize, penalty=None) 59 return Xhat, alpha, intercept, lbda 60 61 62def solve_l1(X, D, alpha=None, return_all=False, nlambdas=100, ncores=-1, positivity=False, variance=None, fit_intercept=True, standardize=True, 63 progressbar=True, use_joblib=False, use_crossval=False, verbose=5): 64 65 if alpha is None: 66 return_alpha = True 67 alpha = np.zeros((D.shape[1], X.shape[0])) 68 else: 69 return_alpha = False 70 71 if variance is None: 72 variance = [None] * alpha.shape[1] 73 74 Xhat = np.zeros((alpha.shape[1], D.shape[0]), dtype=np.float32) 75 intercept = np.zeros((alpha.shape[1], 1), dtype=np.float32) 76 lbda = np.zeros((alpha.shape[1], 1), dtype=np.float32) 77 78 step = 10 79 arglist = ((D, 80 X[i:i+step], 81 nlambdas, 82 positivity, 83 variance[i:i+step], 84 fit_intercept, 85 standardize, 86 use_crossval) for i in range(0, alpha.shape[1], step)) 87 88 plauncher = Parallel(return_as='generator', 89 pre_dispatch='all', 90 backend='loky', 91 n_jobs=ncores) 92 93 if use_joblib: 94 stuff = plauncher(delayed(lasso_path_parallel)(*args) for args in arglist) 95 96 if progressbar: 97 stuff = tqdm(stuff, total=X.shape[0]//step+1) 98 else: 99 raise ValueError('Only joblib path is supported now.') 100 101 for i, content in enumerate(stuff): 102 slicer = np.index_exp[i * step:(i+1) * step] 103 slicer2d = np.index_exp[:, i * step:(i+1) * step] 104 105 Xhat[slicer], temp_alpha, temp_intercept, temp_lbda = content 106 alpha[slicer2d] = temp_alpha.T 107 intercept[slicer] = temp_intercept[:, None] 108 lbda[slicer] = temp_lbda[:, None] 109 110 if return_all: 111 return Xhat, alpha, intercept, lbda 112 113 if return_alpha: 114 return alpha 115 116 117def online_DL(X, D=None, n_atoms=None, niter=250, batchsize=128, rho=1.0, t0=1e-3, variance=None, 118 shuffle=True, fulldraw=False, positivity=False, fit_intercept=True, standardize=True, ncores=-1, nlambdas=100, 119 progressbar=True, use_joblib=True, eps=1e-6): 120 121 tt = time() 122 seen_patches = 0 123 124 if n_atoms is None: 125 n_atoms = 2 * np.prod(X.shape[1:]) 126 127 if n_atoms > X.shape[0]: 128 n_atoms = X.shape[0] 129 130 if D is None: 131 # we pick n_atoms indexes along each dimensions randomly 132 indices = np.random.permutation(X.shape[0])[:n_atoms] 133 D = X[indices].reshape(n_atoms, np.prod(X.shape[1:])).T 134 135 # Put to unit l2 norm, will also copy D if we passed it to the function 136 D = D / np.sqrt(np.sum(D**2, axis=1, keepdims=True)) 137 138 A = np.eye(D.shape[1]) * t0 139 B = t0 * D 140 141 A_prime = np.copy(A) 142 B_prime = np.copy(B) 143 144 x = np.zeros((batchsize, D.shape[0])) 145 alpha = np.zeros((n_atoms, batchsize)) 146 147 alpha_alpha_T = np.zeros((n_atoms, n_atoms)) 148 x_alpha_T = np.zeros((x.shape[1], n_atoms)) 149 150 if shuffle: 151 np.random.shuffle(X) 152 153 if fulldraw: 154 # one iteration goes through all elements 155 batches = gen_batches(X.shape[0], batchsize) 156 iterator = product(range(1, niter + 1), batches) 157 else: 158 # one iteration goes through one draw of size batchsize from X 159 batches = cycle(gen_batches(X.shape[0], batchsize)) 160 iterator = zip(range(1, niter + 1), batches) 161 162 for t, batch in tqdm(iterator, total=niter): 163 164 cutter = X[batch].shape[0] 165 if cutter < batchsize: 166 continue 167 168 x[:] = X[batch].reshape(batchsize, -1) 169 _, _, _, lbda = solve_l1(x, D, alpha, positivity=positivity, ncores=ncores, nlambdas=nlambdas, variance=variance, return_all=True, 170 fit_intercept=fit_intercept, standardize=standardize, use_joblib=use_joblib, progressbar=False, verbose=False) 171 172 np.dot(alpha, alpha.T, out=alpha_alpha_T) 173 # x is transposed with regards to original notation 174 np.dot(x.T, alpha.T, out=x_alpha_T) 175 176 beta = (1 - (1 / t))**rho 177 178 A *= beta 179 A += alpha_alpha_T / batchsize 180 A_prime *= beta 181 A_prime += alpha_alpha_T / batchsize 182 183 B *= beta 184 B += x_alpha_T 185 B_prime *= beta 186 B_prime += x_alpha_T 187 188 update_D(D, A, B, X, positivity=False) 189 190 # reset past information every two full epochs 191 if seen_patches > 2 * X.shape[0]: 192 A[:] = A_prime 193 B[:] = B_prime 194 195 A_prime[:] = 0 196 B_prime[:] = 0 197 198 seen_patches = 0 199 else: 200 seen_patches += batchsize 201 202 print(f'total time: {round((time() - tt) / 60, 2)} mins') 203 return D
def
update_D(D, A, B, X, niter=1, eps=1e-15, positivity=False):
14def update_D(D, A, B, X, niter=1, eps=1e-15, positivity=False): 15 16 u = np.zeros(D.shape) 17 norm2 = np.zeros(D.shape[1]) 18 to_purge = np.zeros(D.shape[1], dtype=bool) 19 20 for _ in range(niter): 21 # divide by zeros are replaced by 0, but still raise the warning, so we squelch it 22 with np.errstate(divide='ignore', invalid='ignore'): 23 u[:] = np.where(np.diag(A) != 0, (B - np.dot(D, A)) / np.diag(A) + D, 0) 24 25 norm2[:] = np.sqrt(np.sum(u**2, axis=0)) 26 27 # purge unused atoms from D 28 to_purge[:] = norm2 < eps 29 30 if np.any(to_purge): 31 indices = np.random.randint(X.shape[0], size=np.sum(to_purge)) 32 u[:, to_purge] = X[indices].reshape(np.sum(to_purge), u.shape[0]).T 33 norm2[to_purge] = np.sqrt(np.sum(u[:, to_purge]**2, axis=0)) 34 35 D[:] = u / np.maximum(1, norm2) 36 37 if positivity: 38 D[D < 0] = 0
def
lasso_path_parallel( D, X, nlambdas, positivity=False, variance=None, fit_intercept=True, standardize=True, use_crossval=False):
40def lasso_path_parallel(D, X, nlambdas, positivity=False, variance=None, fit_intercept=True, standardize=True, use_crossval=False): 41 N = X.shape[0] 42 alpha = np.zeros((N, D.shape[1]), dtype=np.float32) 43 intercept = np.zeros(N, dtype=np.float32) 44 Xhat = np.zeros((N, D.shape[0]), dtype=np.float32) 45 lbda = np.zeros(N, dtype=np.float32) 46 47 if use_crossval: 48 # use_crossval holds the number of split, if not we default to 3 because reasons 49 if use_crossval == 1: 50 n_splits = 3 51 else: 52 n_splits = use_crossval 53 54 Xhat[i], alpha[i], intercept[i], lbda[i] = lasso_crossval(D, X[i], nlam=nlambdas, fit_intercept=fit_intercept, n_splits=n_splits, 55 pos=positivity, standardize=standardize, penalty=None) 56 else: 57 for i in range(N): 58 alpha[i], intercept[i], Xhat[i], lbda[i] = lasso_path(D, X[i], nlam=nlambdas, fit_intercept=fit_intercept, criterion='aicc', 59 pos=positivity, standardize=standardize, penalty=None) 60 return Xhat, alpha, intercept, lbda
def
solve_l1( X, D, alpha=None, return_all=False, nlambdas=100, ncores=-1, positivity=False, variance=None, fit_intercept=True, standardize=True, progressbar=True, use_joblib=False, use_crossval=False, verbose=5):
63def solve_l1(X, D, alpha=None, return_all=False, nlambdas=100, ncores=-1, positivity=False, variance=None, fit_intercept=True, standardize=True, 64 progressbar=True, use_joblib=False, use_crossval=False, verbose=5): 65 66 if alpha is None: 67 return_alpha = True 68 alpha = np.zeros((D.shape[1], X.shape[0])) 69 else: 70 return_alpha = False 71 72 if variance is None: 73 variance = [None] * alpha.shape[1] 74 75 Xhat = np.zeros((alpha.shape[1], D.shape[0]), dtype=np.float32) 76 intercept = np.zeros((alpha.shape[1], 1), dtype=np.float32) 77 lbda = np.zeros((alpha.shape[1], 1), dtype=np.float32) 78 79 step = 10 80 arglist = ((D, 81 X[i:i+step], 82 nlambdas, 83 positivity, 84 variance[i:i+step], 85 fit_intercept, 86 standardize, 87 use_crossval) for i in range(0, alpha.shape[1], step)) 88 89 plauncher = Parallel(return_as='generator', 90 pre_dispatch='all', 91 backend='loky', 92 n_jobs=ncores) 93 94 if use_joblib: 95 stuff = plauncher(delayed(lasso_path_parallel)(*args) for args in arglist) 96 97 if progressbar: 98 stuff = tqdm(stuff, total=X.shape[0]//step+1) 99 else: 100 raise ValueError('Only joblib path is supported now.') 101 102 for i, content in enumerate(stuff): 103 slicer = np.index_exp[i * step:(i+1) * step] 104 slicer2d = np.index_exp[:, i * step:(i+1) * step] 105 106 Xhat[slicer], temp_alpha, temp_intercept, temp_lbda = content 107 alpha[slicer2d] = temp_alpha.T 108 intercept[slicer] = temp_intercept[:, None] 109 lbda[slicer] = temp_lbda[:, None] 110 111 if return_all: 112 return Xhat, alpha, intercept, lbda 113 114 if return_alpha: 115 return alpha
def
online_DL( X, D=None, n_atoms=None, niter=250, batchsize=128, rho=1.0, t0=0.001, variance=None, shuffle=True, fulldraw=False, positivity=False, fit_intercept=True, standardize=True, ncores=-1, nlambdas=100, progressbar=True, use_joblib=True, eps=1e-06):
118def online_DL(X, D=None, n_atoms=None, niter=250, batchsize=128, rho=1.0, t0=1e-3, variance=None, 119 shuffle=True, fulldraw=False, positivity=False, fit_intercept=True, standardize=True, ncores=-1, nlambdas=100, 120 progressbar=True, use_joblib=True, eps=1e-6): 121 122 tt = time() 123 seen_patches = 0 124 125 if n_atoms is None: 126 n_atoms = 2 * np.prod(X.shape[1:]) 127 128 if n_atoms > X.shape[0]: 129 n_atoms = X.shape[0] 130 131 if D is None: 132 # we pick n_atoms indexes along each dimensions randomly 133 indices = np.random.permutation(X.shape[0])[:n_atoms] 134 D = X[indices].reshape(n_atoms, np.prod(X.shape[1:])).T 135 136 # Put to unit l2 norm, will also copy D if we passed it to the function 137 D = D / np.sqrt(np.sum(D**2, axis=1, keepdims=True)) 138 139 A = np.eye(D.shape[1]) * t0 140 B = t0 * D 141 142 A_prime = np.copy(A) 143 B_prime = np.copy(B) 144 145 x = np.zeros((batchsize, D.shape[0])) 146 alpha = np.zeros((n_atoms, batchsize)) 147 148 alpha_alpha_T = np.zeros((n_atoms, n_atoms)) 149 x_alpha_T = np.zeros((x.shape[1], n_atoms)) 150 151 if shuffle: 152 np.random.shuffle(X) 153 154 if fulldraw: 155 # one iteration goes through all elements 156 batches = gen_batches(X.shape[0], batchsize) 157 iterator = product(range(1, niter + 1), batches) 158 else: 159 # one iteration goes through one draw of size batchsize from X 160 batches = cycle(gen_batches(X.shape[0], batchsize)) 161 iterator = zip(range(1, niter + 1), batches) 162 163 for t, batch in tqdm(iterator, total=niter): 164 165 cutter = X[batch].shape[0] 166 if cutter < batchsize: 167 continue 168 169 x[:] = X[batch].reshape(batchsize, -1) 170 _, _, _, lbda = solve_l1(x, D, alpha, positivity=positivity, ncores=ncores, nlambdas=nlambdas, variance=variance, return_all=True, 171 fit_intercept=fit_intercept, standardize=standardize, use_joblib=use_joblib, progressbar=False, verbose=False) 172 173 np.dot(alpha, alpha.T, out=alpha_alpha_T) 174 # x is transposed with regards to original notation 175 np.dot(x.T, alpha.T, out=x_alpha_T) 176 177 beta = (1 - (1 / t))**rho 178 179 A *= beta 180 A += alpha_alpha_T / batchsize 181 A_prime *= beta 182 A_prime += alpha_alpha_T / batchsize 183 184 B *= beta 185 B += x_alpha_T 186 B_prime *= beta 187 B_prime += x_alpha_T 188 189 update_D(D, A, B, X, positivity=False) 190 191 # reset past information every two full epochs 192 if seen_patches > 2 * X.shape[0]: 193 A[:] = A_prime 194 B[:] = B_prime 195 196 A_prime[:] = 0 197 B_prime[:] = 0 198 199 seen_patches = 0 200 else: 201 seen_patches += batchsize 202 203 print(f'total time: {round((time() - tt) / 60, 2)} mins') 204 return D