Пример #1
0
    def test_hyperparameter_opt(self):
        kdict = [{'type': 'gaussian', 'width': width, 'scaling': scaling}]
        gp_hyp = GaussianProcess(
            kernel_list=kdict, train_fp=train, train_target=target,
            gradients=gradients, scale_data=True,
            optimize_hyperparameters=False, regularization=np.sqrt(1e-3))
        gp_hyp.optimize_hyperparameters(global_opt=False)
        bigKtilde_hyp = np.linalg.inv(gp_hyp.cinv)
        bigKdg_hyp = bigKtilde_hyp[n_train:n_train + n_train * D, :n_train]
        bigKgd_hyp = bigKtilde_hyp[:n_train, n_train:n_train + n_train * D]

        print('Checking block matrices bigKgd and bigKdg (opt. hyper.).')
        np.testing.assert_array_almost_equal(bigKdg_hyp, bigKgd_hyp.T,
                                             decimal=10)
Пример #2
0
class MLMin(object):

    def __init__(self, x0, prev_calculations=None, restart=False,
                 trajectory='catlearn_opt.traj'):

        """Optimization setup.

        Parameters
        ----------
        x0 : Atoms object or trajectory file in ASE format.
            Initial guess.
        restart: boolean
            Restart calculation. Only if a trajectory file with the same
            name as the one introduced in the 'trajectory' flag is found.
            This file must be in the same directory than the direction in
            which the optimization was initiated.
        prev_calculations: list of Atoms or trajectory file (ASE format).
            The user can feed previously calculated data for the same
            hypersurface (i.e. the calculations must be done using the same
            calculator and parameters).
        trajectory: string
            Filename to store the output.
        """

        # General variables.
        self.prev_calculations = prev_calculations
        self.filename = trajectory
        self.iter = 0
        self.fmax = 0.
        self.gp = None
        self.opt_type = 'MLMin'
        self.version = self.opt_type + ' ' + __version__
        print_version(self.version)

        if restart is True and prev_calculations is None:
            if os.path.isfile(self.filename):
                self.prev_calculations = self.filename

        msg = 'You must set an initial Atoms structure.'
        assert x0 is not None, msg

        # Read trajectory file.
        if isinstance(x0, str):
            x0 = read(x0, '-1')

        self.ase_ini = x0
        self.ase_calc = self.ase_ini.get_calculator()
        msg = 'ASE calculator not found.'
        assert self.ase_calc, msg
        self.constraints = self.ase_ini.constraints
        self.x0 = self.ase_ini.get_positions().flatten()
        self.num_atoms = self.ase_ini.get_number_of_atoms()

        # Information from the initial structure.
        new_atoms = self.ase_ini
        self.list_atoms = [new_atoms]

        if self.prev_calculations is not None:
            if isinstance(self.prev_calculations, str):
                print('Reading previous calculations from a traj. file.')
                self.prev_calculations = read(self.prev_calculations, ':')

            # Check for duplicates.
            for prev_atoms in self.prev_calculations:
                duplicate = False
                prev_pos = prev_atoms.get_positions().reshape(-1)
                for atoms in self.list_atoms:
                    pos_atoms = atoms.get_positions().reshape(-1)
                    d_ij = euclidean(pos_atoms, prev_pos)
                    if d_ij == 0:
                        duplicate = True
                if duplicate is False:
                    self.list_atoms += [prev_atoms]

        # Extract information from previous calculations.
        self.list_train = []
        self.list_targets = []
        self.list_gradients = []
        for i in range(0, len(self.list_atoms)):
            pos_atoms = list(self.list_atoms[i].get_positions().reshape(-1))
            energy_atoms = self.list_atoms[i].get_potential_energy()
            grad_atoms = list(-self.list_atoms[i].get_forces().reshape(-1))
            self.list_train.append(pos_atoms)
            self.list_targets.append(energy_atoms)
            self.list_gradients.append(grad_atoms)
        self.list_max_abs_forces = []
        for i in self.list_gradients:
            self.list_fmax = get_fmax(np.array([i]))
            self.max_abs_forces = np.max(np.abs(self.list_fmax))
            self.list_max_abs_forces.append(self.max_abs_forces)

        # Get constraints.
        self.index_mask = create_mask(self.ase_ini, self.constraints)

        # Dump all Atoms to file.
        write(self.filename, self.list_atoms)
        print_info(self)

        print(self.list_targets)

    def run(self, fmax=0.05, steps=200, kernel='SQE', max_step=0.25,
            acq='min_energy', full_output=False):

        """Executing run will start the optimization process.

        Parameters
        ----------
        fmax : float
            Convergence criteria (in eV/Angstrom).
        steps : int
            Max. number of optimization steps.
        kernel: string
            Type of covariance function to be used.
            Implemented are: SQE (fixed hyperparamters), SQE_opt and ARD_SQE.
        max_step: float
            Early stopping criteria. Maximum uncertainty before stopping the
            optimization in the predicted landscape.
        acq : string
            The acquisition function that decides the next point to
            evaluate. Implemented are: 'lcb', 'ucb', 'min_energy'.
        full_output: boolean
            Whether to print on screen the full output (True) or not (False).

        Returns
        -------
        Optimized atom structure.

        """

        self.fmax = fmax
        self.acq = acq
        success_hyper = False

        while not converged(self):

            # 1. Train Machine Learning model.
            train = np.copy(self.list_train)
            targets = np.copy(self.list_targets)
            gradients = np.copy(self.list_gradients)

            self.u_prior = np.max(targets)
            scaled_targets = targets - self.u_prior
            sigma_f = 1e-3 + np.std(scaled_targets)**2

            if kernel == 'SQE_fixed':
                opt_hyper = False
                kdict = [{'type': 'gaussian', 'width': 0.4,
                          'dimension': 'single',
                          'bounds': ((0.4, 0.4),),
                          'scaling': 1.0,
                          'scaling_bounds': ((1.0, 1.0),)},
                         {'type': 'noise_multi',
                          'hyperparameters': [0.005 * 0.4**2, 0.005],
                          'bounds': ((0.005 * 0.4**2, 0.005 * 0.4**2),
                                     (0.005, 0.005),)}
                         ]

            if kernel == 'SQE':
                opt_hyper = True
                kdict = [{'type': 'gaussian', 'width': 0.4,
                          'dimension': 'single',
                          'bounds': ((0.01, 1.0),),
                          'scaling': sigma_f,
                          'scaling_bounds': ((sigma_f, sigma_f),)},
                         {'type': 'noise_multi',
                          'hyperparameters': [0.005, 0.005 * 0.4**2],
                          'bounds': ((0.001, 0.050),
                                     (0.001 * 0.4**2, 0.050),)}
                         ]

            if kernel == 'ARD_SQE':
                opt_hyper = True
                kdict = [{'type': 'gaussian', 'width': 0.4,
                          'dimension': 'features',
                          'bounds': ((0.01, 1.0),) * len(self.index_mask),
                          'scaling': sigma_f,
                          'scaling_bounds': ((sigma_f, sigma_f),)},
                         {'type': 'noise_multi',
                          'hyperparameters': [0.005, 0.0005],
                          'bounds': ((0.001, 0.005),
                                     (0.0005, 0.002),)}
                         ]

                if success_hyper is not False:
                    kdict = success_hyper

            if self.index_mask is not None:
                train = apply_mask(list_to_mask=train,
                                   mask_index=self.index_mask)[1]
                gradients = apply_mask(list_to_mask=gradients,
                                       mask_index=self.index_mask)[1]

            print('Training a GP process...')
            print('Number of training points:', len(scaled_targets))

            train = train.tolist()
            gradients = gradients.tolist()

            self.gp = GaussianProcess(kernel_list=kdict,
                                      regularization=0.0,
                                      regularization_bounds=(0.0, 0.0),
                                      train_fp=train,
                                      train_target=scaled_targets,
                                      gradients=gradients,
                                      optimize_hyperparameters=False)

            print('GP process trained.')

            if opt_hyper is True:
                if len(self.list_targets) > 5:
                    self.gp.optimize_hyperparameters()
                    if self.gp.theta_opt.success is True:
                        if full_output is True:
                            print('Hyperparam. optimization was successful.')
                            print('Updating kernel list...')
                        success_hyper = self.gp.kernel_list

                    if self.gp.theta_opt.success is not True:
                        if full_output is True:
                            print('Hyperparam. optimization unsuccessful.')
                        if success_hyper is False:
                            if full_output is True:
                                print('Not enough data...')
                        if success_hyper is not False:
                            if full_output is True:
                                print('Using the last optimized '
                                      'hyperparamters.')
                    if full_output is True:
                        print('Kernel list:', self.gp.kernel_list)

            # 2. Optimize Machine Learning model.

            self.list_interesting_points = []
            self.list_interesting_energies = []
            self.list_interesting_uncertainties = []

            guess = self.ase_ini
            guess_pos = np.array(self.list_train[-1])
            guess.positions = guess_pos.reshape(-1, 3)
            guess.set_calculator(ASECalc(gp=self.gp,
                                         index_constraints=self.index_mask,
                                         scaling_targets=self.u_prior)
                                 )

            # Optimization in the predicted landscape:
            ml_opt = MDMin(guess, trajectory=None, logfile=None, dt=0.020)

            if full_output is True:
                print('Starting optimization on the predicted landscape...')
            ml_converged = False

            n_steps_performed = 0

            while ml_converged is False:
                ml_opt.run(fmax=fmax*0.90, steps=1)
                pos_ml = np.array(guess.positions).flatten()
                self.list_interesting_points.append(pos_ml)
                pos_ml = apply_mask([pos_ml],
                                    mask_index=self.index_mask)[1]
                pred_ml = self.gp.predict(test_fp=pos_ml, uncertainty=True)
                energy_ml = pred_ml['prediction'][0][0]
                unc_ml = pred_ml['uncertainty_with_reg'][0]

                self.list_interesting_energies.append(energy_ml)
                self.list_interesting_uncertainties.append(unc_ml)

                n_steps_performed += 1
                if n_steps_performed > 1000:
                        if full_output is True:
                            print('Not converged yet...')
                        ml_converged = True
                if unc_ml >= max_step:
                    if full_output is True:
                        print('Maximum uncertainty reach. Early stop.')
                    ml_converged = True
                if ml_opt.converged():
                    if full_output is True:
                        print('ML optimized.')
                    ml_converged = True

            # Acquisition functions:
            acq_pred = np.array(self.list_interesting_energies)
            acq_unc = np.array(self.list_interesting_uncertainties)

            if self.acq == 'ucb':
                acq_values = UCB(predictions=acq_pred, uncertainty=acq_unc,
                                 objective='min', kappa=-1.0)
            if self.acq == 'lcb':
                e_minus_unc = np.array(self.list_interesting_energies) - \
                              np.array(self.list_interesting_uncertainties)
                acq_values = -e_minus_unc
            if self.acq == 'min_energy':
                acq_values = -np.array(self.list_interesting_energies)

            max_score = np.argmax(acq_values)

            self.interesting_point = self.list_interesting_points[max_score]

            # 3. Evaluate and append interesting point.
            if full_output is True:
                print('Performing evaluation in the real landscape...')

            eval_atom = self.ase_ini
            pos_atom = self.interesting_point
            eval_atom.positions = np.array(pos_atom).reshape((-1, 3))
            eval_atom.set_calculator(self.ase_calc)
            energy_atom = eval_atom.get_potential_energy()
            forces_atom = -eval_atom.get_forces().reshape(-1)

            # 4. Convergence and output.
            self.list_train.append(pos_atom)
            self.list_targets.append(energy_atom)
            self.list_gradients.append(forces_atom)

            self.list_fmax = get_fmax(np.array([self.list_gradients[-1]]))
            self.max_abs_forces = np.max(np.abs(self.list_fmax))
            self.list_max_abs_forces.append(self.max_abs_forces)

            self.iter += 1

            print_info(self)

            # Save evaluated image.
            self.list_atoms += [eval_atom.copy()]
            write(self.filename, self.list_atoms)

            # Maximum number of iterations reached.
            if self.iter >= steps:
                print('Not converged. Maximum number of iterations reached.')
                break
Пример #3
0
# There are several ways. One that does not require any further changes to the GP code is fitting another GP to the magnitude of error.

# In[9]:


# Get training errors.
optimized_train = gp.predict(test_fp=std['train'], uncertainty=True)
predict_train = np.vstack(optimized_train['prediction']) *     train_targets['std'] + train_targets['mean']
uncertainty_train = np.vstack(optimized_train['uncertainty_with_reg']) *     train_targets['std']
train_error = get_error(predict_train.reshape(-1), afunc(train).reshape(-1))
gp_heteroscedastic = GaussianProcess(kernel_list=kdict,
                                     regularization=sdt3,
                                     train_fp=std['train'],
                                     train_target=train_error['rmse_all']-uncertainty_train.reshape(-1),
                                     optimize_hyperparameters=True)
gp.optimize_hyperparameters(global_opt=True)
heteroscedastic_uncertainty = gp_heteroscedastic.predict(test_fp=std['test'],
                                                         uncertainty=False)


# In[10]:


# Plot heteroscedastic uncertainty.
plt.figure(3)
plt.plot(linex, au(linex), '-', lw=1, color='black')
plt.plot(train, train_error['rmse_all']-uncertainty_train.reshape(-1), 'o', alpha=0.2, color='black')
plt.plot(test, heteroscedastic_uncertainty['prediction'], 'g-', lw=1, alpha=0.4)
plt.xlabel('X')
plt.axis('tight')
Пример #4
0
    'scaling': scaling,
    'scaling_bounds': scaling_bounds
}]

gp = GaussianProcess(kernel_list=kdict,
                     regularization=sdt1,
                     regularization_bounds=reg_bounds,
                     train_fp=train,
                     train_target=target,
                     optimize_hyperparameters=False,
                     gradients=gradients,
                     scale_optimizer=False,
                     scale_data=True)

# Hyperaparam optimization algorithms change from default.
gp.optimize_hyperparameters(algomin='L-BFGS-B', global_opt=False)
print('Optimized kernel:', gp.kernel_list)

# Do the optimized predictions.
pred = gp.predict(test_fp=test, uncertainty=True)
prediction = np.array(pred['prediction'][:, 0])

# Calculate the uncertainty of the predictions.
uncertainty = np.array(pred['uncertainty_with_reg'])

# Get confidence interval on predictions.
upper = prediction + uncertainty
lower = prediction - uncertainty

interval = upper - prediction