def optimize_h_with_bayes_estimator(*args): """Assumes that optimal_h calculates gain-optimal value analytically.""" ys = flatten_first_two_dims( self.sample_predictive_y0( *args, nsamples_theta=self.H_NSAMPLES_UTILITY_TERM_THETA, nsamples_y=self.H_NSAMPLES_UTILITY_TERM_Y)) h = self.optimal_h_bayes_estimator(ys) if optimize_h_with_bayes_estimator.counter < 3 and not is_valid(h): print( "WARNING: your optimal_h_bayes_estimator=%s returns invalid decisions! Are we good here?" % self.optimal_h_bayes_estimator.__name__) optimize_h_with_bayes_estimator.counter += 1 return h
def optimal_h_numerically_scipy(ys, u, weights=None, utility_aggregator=gain_weighted, data_mask=None, max_niter=10000, tol=1e-4, tol_goal=-1, lr=0.01, start=None, optimizer="COBYLA", verbose=False, debug=False, sparse_verbose=True): """ Using numerical optimization (SciPy) finds optimal h for utility-dependent term expressed by utility_aggregator. Compatible with optimal_h_numerically. """ printv = lambda txt: (print2("[optimal_h_numerically_scipy]" + txt) if verbose else None) if sparse_verbose and not verbose: printv = lambda txt: sparse_print( "optimal_h_numerically_scipy", "[optimal_h_numerically_scipy]" + txt, 100) printd = lambda txt: (print2("[optimal_h_numerically_scipy]" + txt) if debug else None) assert len( signature(utility_aggregator).parameters )==3, \ "[optimal_h_numerically_scipy] Your utility_aggregator=%s takes wrong number of params! does not support weights?" % utility_aggregator env = torch if "cpu" in str(ys.device) else torch.cuda if data_mask is None: data_mask = (torch.ones(ys.shape[1:]) if len(ys.shape) > 1 else torch.tensor(1)).type(env.ByteTensor) if weights is None: weights = torch.ones_like(ys) weights /= weights.sum(0) #enforce normalization weights = torch.tensor(tonumpy(weights), requires_grad=False) y = torch.tensor(tonumpy(ys), requires_grad=False) if start is None: h = (y * weights).sum(0) #start from E(y) elif start is None or not is_valid(torch.tensor(start)): printd("start point is invalid. ignoring!") h = (y * weights).sum(0) #start from E(y) else: h = start start = time.time() x0 = tonumpy(h).flatten() fun = lambda h: -utility_aggregator( u(torch.tensor(h.reshape(y.shape[1:]), dtype=y.dtype), y), weights, data_mask).item() result = _scipy_minimize(fun, x0, method=optimizer, max_niter=max_niter, tol=tol, tol_goal=tol_goal, lr=lr, debug=debug) if verbose: printv( "[%.4f][optimizer=%s ys=%s max_niter=%i tol=%s tol_goal=%s lr=%s u=%s->%s] %s" % (time.time() - start, optimizer, tuple(ys.shape), max_niter, tol, tol_goal, lr, u.__name__, utility_aggregator.__name__, str(result).replace("\n", ";")[:200])) return torch.tensor(result["x"].reshape(y.shape[1:]), dtype=ys.dtype)
def optimal_h_numerically_ty(ys, u, utility_aggregator=gain, data_mask=None, max_niter=10000, tol=1e-4, tol_goal=-1, lr=0.01, start=None, optimizer=torch.optim.Adam, verbose=False, debug=False, sparse_verbose=True): """ Using numerical optimization finds optimal h for utility-dependent term expressed by utility_aggregator. Args: ys: Samples matrix. The dimensionality should match what utility_aggregator takes: #y-samples x #theta-samples x data-size. u: Utility function u(h, ys) -> utilities matrix (the same shape as ys). utility_aggregator: A function that calculate utility-dependent term. Should take exactly 2 params: utilites and data_mask. data_mask: A mask passed to utility_aggregator. """ printv = lambda txt: (print2("[optimal_h_numerically_ty] " + txt) if verbose else None) if sparse_verbose and not verbose: printv = lambda txt: sparse_print( "optimal_h_numerically_ty", "[optimal_h_numerically_ty]" + txt, 100 ) printd = lambda txt: (print2("[optimal_h_numerically_ty] " + txt) if debug else None) assert len( signature(utility_aggregator).parameters )==2, \ "[optimal_h_numerically_ty] Your utility_aggregator=%s takes wrong number of params! perhaps requires weights?" % utility_aggregator env = torch if "cpu" in str(ys.device) else torch.cuda if data_mask is None: #No data mask provided. Using all data points data_mask = (torch.ones(ys.shape[2:]) if len(ys.shape) > 2 else torch.tensor(1)).type(env.ByteTensor) y = torch.tensor(tonumpy(ys), requires_grad=False) if start is None: h = y.mean(0).mean(0).clone().detach().requires_grad_( True) #start from E(y) elif start is None or not is_valid(torch.tensor(start)): printd("start point is invalid. ignoring!") h = y.mean(0).mean(0).clone().detach().requires_grad_( True) #start from E(y) else: h = torch.tensor(tonumpy(start), requires_grad=True) optimizer = optimizer([h], lr=lr) prev_h, prev_goal = torch.tensor(tonumpy(h)), float("inf") start = time.time() for i in range(max_niter): goal = -utility_aggregator(u(h, y), data_mask) optimizer.zero_grad() goal.backward(retain_graph=False) optimizer.step() #check for convergence: if (prev_h - h).abs().max() <= tol: printv( "[%.2f][ys=%s max_niter=%i tol=%s tol_goal=%s lr=%s u=%s->%s] Finished at %i. iter (tolerance reached): obj=%.4f max-err=%.8f mean-err=%.8f" % (time.time() - start, tuple( ys.shape), max_niter, tol, tol_goal, lr, u.__name__, utility_aggregator.__name__, i + 1, goal.item(), (prev_h - h).abs().max(), (prev_h - h).abs().mean())) break if abs(prev_goal - goal.item()) <= tol_goal: printv( "[%.2f][ys=%s max_niter=%i tol=%s tol_goal=%s lr=%s u=%s->%s] Finished at %i. iter (objective tolerance reached): obj=%.4f max-err=%.8f mean-err=%.8f" % (time.time() - start, tuple( ys.shape), max_niter, tol, tol_goal, lr, u.__name__, utility_aggregator.__name__, i + 1, goal.item(), (prev_h - h).abs().max(), (prev_h - h).abs().mean())) break if i >= max_niter - 1: printv( "[%.2f][ys=%s max_niter=%i tol=%s tol_goal=%s lr=%s u=%s->%s] Finished at %i. iter (max number reached): obj=%.4f max-err=%.8f mean-err=%.8f" % (time.time() - start, tuple( ys.shape), max_niter, tol, tol_goal, lr, u.__name__, utility_aggregator.__name__, i + 1, goal.item(), (prev_h - h).abs().max(), (prev_h - h).abs().mean())) break if i % (max_niter // 10) == 0: printd("[%.2f] iter %i: objective=%.4f err=%.6f" % (time.time() - start, i, goal.item(), (prev_h - h).abs().max())) prev_h = torch.tensor(tonumpy(h)) prev_goal = goal.item() return h