[docs]classObjective(Protocol):""" An objective function takes a model as input, and returns a scalar value, such that a lower value is a "better" model. """
[docs]defmaximise_log_likelihood(model:Model):"""Maximise the log likelihood of the model."""return-model.log_likelihood
[docs]defvalidation_set_mse(X:Float[np.ndarray,"N D"],y:Float[np.ndarray,"N"],)->Objective:"""Minimise the mean squared error of the model on the validation set."""deffunc(model:Model):yy=model.predict(X)returnnp.mean((y-yy)**2).item()returnfunc
[docs]defvalidation_set_log_likelihood(X:Float[np.ndarray,"N D"],y:Float[np.ndarray,"N"],)->Objective:"""Minimise the log likelihood of the model on the validation set."""deffunc(model:Model):yy=model.predict(X)std=model.predictive_uncertainty(X)# normal distribution likelihoodsls=(1/(std*np.sqrt(2*np.pi))*np.exp(-((y-yy)**2)/(2*std**2)))# (N,)# take logs and sumreturnnp.sum(np.log(ls)).item()returnfunc
[docs]@ensure_1d("y")@ensure_2d("X")defoptimise_model(m:Model,objective:Objective,X:Float[np.ndarray,"N D"],y:Float[np.ndarray,"N"],*,optimise_noise:bool=False,max_iterations:int=100,):""" Optimise the model (kernel hyperparameters and noise) to minimise the objective function. Parameters ---------- m the model to optimise. objective the objective function to minimise. X the training data. y the training targets. optimise_noise whether to optimise the noise. max_iterations the maximum number of iterations. """try:fromscipy.optimizeimportminimizeexceptImportError:raiseImportError("scipy is required to optimise the model. ""Please install it with `pip install scipy`.")fromNoneconvertor=Convertor(m.kernel.params)# stuff parameters into a list in the order:# kernel params, sparse points, noisedefparams_to_model(params:list[float])->Model:noise=params.pop()ifoptimise_noiseelsem.noiseparam_dict=convertor.to_dict(params)new_kernel=m.kernel.with_new(param_dict)returnm.with_new(new_kernel,noise)def_objective(params:list[float]):try:new_model=params_to_model(list(params))new_model.fit(X,y)returnobjective(new_model)exceptException:return1e33starting_params=convertor.to_list(m.kernel.params)ifoptimise_noise:starting_params.append(m.noise)# TODO: cache models so no need to refit each timebest_params=minimize(_objective,starting_params,options={"maxiter":max_iterations},).xm=params_to_model(list(best_params))m.fit(X,y)returnm