A note on the library implementation that explores hyperparameters using Bayesian optimization in Python

Introduction

Bayesian Optimization (Reference: Introduction to Bayesian Optimization, Introduction to Bayesian Optimization for Machine Learning By using manatee / detail / id = 59393)), it may be possible to efficiently search for hyperparameters that must be decided by various Try & Error during machine learning.

I can understand the idea etc. thanks to the increase in various commentary articles recently, but I could not find it on the Web in the form of the GridSearch library, so I made it this time. I'm sure it's a reinvention of the wheel, but I'm glad that the reinvention is a learning experience.

Various versions used this time

code

bayesian_optimizer.py


from itertools import product
from sklearn.gaussian_process import GaussianProcess

# The MIT License (C) 2016 mokemokechicken


class BayesianOptimizer:
    x_list = None
    y_list = None
    yielding_index = None
    k_band = 5
    verbose = False

    def __init__(self, params):
        self.params = params
        self.keys = []
        self.values = []
        for k, v in sorted(self.params.items()):
            self.keys.append(k)
            self.values.append(v)

    @property
    def n_pattern(self):
        return len(list(product(*self.values)))

    def output(self, *args, **kwargs):
        if self.verbose:
            print(*args, **kwargs)
    
    def supply_next_param(self, max_iter=None):
        self.x_list = []
        self.y_list = []
        all_parameters = list(product(*self.values))  # [(0.01, [0, 0], 0, [10, 10]), (0.01, [0, 0], 0, [15, 15]), ...
        index_space = [list(range(len(v))) for v in self.values]  # [[0], [0, 1, 2], [0], [0, 1, 2]]
        all_index_list = list(product(*index_space))  # [(0, 0, 0, 0), (0, 0, 0, 1), ...

        # examine 2 random points initially
        idx = list(range(len(all_index_list)))
        np.random.shuffle(idx)
        searched_index_list = []
        for index in idx[:2]:
            param = self.to_param(all_parameters[index])
            self.yielding_index = all_index_list[index]
            searched_index_list.append(index)
            yield param

        # Bayesian Optimization
        max_iter = int(min(max_iter or max(np.sqrt(self.n_pattern)*4, 20), self.n_pattern))  #Calculate the maximum number of searches appropriately.
        for iteration in range(max_iter):
            k = 1 + np.exp(-iteration / max_iter * 3) * self.k_band  #Gradually reduce the value of k to emphasize search → focus on utilization
            gp = self.create_gp_and_fit(np.array(self.x_list), np.array(self.y_list))

            mean_array, mse_array = gp.predict(all_index_list, eval_MSE=True)
            next_index, acq_array = self.acquisition(mean_array, mse_array, k, excludes=searched_index_list)

            self.output("--- Most Expected Predictions")
            for acq, ps in sorted(zip(acq_array, all_parameters), reverse=True)[:3]:
                self.output("%.2f: %s" % (acq, list(zip(self.keys, ps))))
            self.output("--- Past Best Results")
            for acq, vs, ps in self.best_results(3):
                self.output("%.2f: %s" % (acq, list(zip(self.keys, vs))))

            if next_index in searched_index_list:
                break
            searched_index_list.append(next_index)
            self.yielding_index = all_index_list[next_index]
            yield self.to_param(all_parameters[next_index])

    @staticmethod
    def create_gp_and_fit(x, y, max_try=100):
        #Suspicious around here
        theta0 = 0.1
        for i in range(max_try+1):
            try:
                gp = GaussianProcess(theta0=theta0)
                gp.fit(x, y)
                return gp
            except Exception as e:
                theta0 *= 10
                if i == max_try:
                    print(theta0)
                    raise e
            
    def to_param(self, row):
        return dict(zip(self.keys, row))

    def report(self, score):
        self.x_list.append(self.yielding_index)
        self.y_list.append(score)

    def best_results(self, n=5):
        index_list = []
        param_list = []
        for xs in self.x_list:
            values = [self.values[i][x] for i, x in enumerate(xs)]
            index_list.append(values)
            param_list.append(self.to_param(values))
        return sorted(zip(self.y_list, index_list, param_list), reverse=True)[:n]

    @staticmethod
    def acquisition(mean_array, mse_array, k, excludes=None):
        excludes = excludes or []
        values = mean_array + np.sqrt(mse_array) * k
        for_argmax = np.copy(values)
        for ex in excludes:
            for_argmax[ex] = -np.Inf
        return np.argmax(for_argmax), values

How to use

The basic usage is as follows.

params = {
    "parameter1": [10, 20, 25, 50],
    "parameter2": ['p1', 'p2'],
    ...
}

bo = BayesianOptimizer(params)  #Pass a combination of parameters in a dictionary

for param in bo.supply_next_param():  #The param that should be checked next is passed by dict
    y = unknown_score_function(param)              #Evaluate the param in some way
    bo.report(y)                                    #I will tell you the evaluation value

print(bo.best_results())                            #I remember the best Score value and param

Demonstration in 2D space

Here's what I've tried on a Jupyter notebook to see if it works. https://gist.github.com/mokemokechicken/7f3cf33c71d33bf0ec242c37cb3f2a75

Search space

After making multiple undulations with sin and cos on the following 50 x 50 plane, prepare a slightly uneven two-dimensional space with noise.

i1.png

The part with the red dot (row = 3, col = 29) is the point with the highest value.

search

I will proceed like this. The number of searches is 200.

Early stage

First of all, I am investigating the space as a whole. A little Go-like w. s1.png

Early stage 2

Fortunately, he seems to have searched several times near his favorite in the middle. a2.png

Midfield

The favorite point has been investigated quite a bit. s3.png

Medium format 2

Carefully check the other mountain on the lower left that looks good. The area around the favorite in the middle is also covered.

s4.png

Late stage

Check the blank spots for anything else that looks good.

s5.png

Final state

There is nothing else that seems to be particularly good and it ends. Well, even if humans do it, it looks like this. s6.png

Impressions

at the end

When I make it, it seems that it can be used in quite a variety of scenes. Life is like this.

Recommended Posts

A note on the library implementation that explores hyperparameters using Bayesian optimization in Python
I tried using Bayesian Optimization in Python
A useful note when using Python for the first time in a while
A note on optimizing blackbox functions in Python
[Modint] Decoding the AtCoder Library ~ Implementation in Python ~
A python implementation of the Bayesian linear regression class
A note on handling variables in Python recursive functions
Write a log-scale histogram on the x-axis in python
Using the National Diet Library Search API in Python
A reminder about the implementation of recommendations in Python
[Internal_math version (2)] Decoding the AtCoder Library ~ Implementation in Python ~
A note that runs an external program in Python and parses the resulting line
A note on the default behavior of collate_fn in PyTorch
Published a library that hides character data in Python images
A note on touching Microsoft's face recognition API in Python
[Note] Import of a file in the parent directory in Python
Try building a neural network in Python without using a library
Control the motor with a motor driver using python on Raspberry Pi 3!
Play a sound in Python assuming that the keyboard is a piano keyboard
Hannari Python At the LT meeting in December, I made a presentation on "Python and Bayesian statistics".
Use networkx, a library that handles graphs in python (Part 2: Tutorial)
Easy! Implement a Twitter bot that runs on Heroku in Python
Bayesian optimization package GPyOpt in Python
About psd-tools, a library that can process psd files in Python
A function that measures the processing time of a method in python
[Ev3dev] Create a program that captures the LCD (screen) using python
[Python] I tried to make a simple program that works on the command line using argparse.
A note on using tab completion when running Python interactively on Windows
[python] A note that started to understand the behavior of matplotlib.pyplot
A note about mock (Python mock library)
Get the number of readers of a treatise on Mendeley in Python
Try using APSW, a Python library that SQLite can get serious about
[DSU Edition] AtCoder Library reading with a green coder ~ Implementation in Python ~
Estimate the probability that a coin will appear on the table using MCMC
The eval () function that calculates a string as an expression in python
A note about the functions of the Linux standard library that handles time
Create a record with attachments in KINTONE using the Python requests module
Code reading of faker, a library that generates test data in Python
I wrote FizzBuzz in python using a support vector machine (library LIVSVM).
[Python] Note: A self-made function that finds the area of the normal distribution
Try using the Kraken API in Python
Write the test in a python docstring
Install python library on Lambda using [/ tmp]
Tweet using the Twitter API in Python
Run the Python interpreter in a script
What is "mahjong" in the Python library? ??
Scraping a website using JavaScript in Python
Draw a tree in Python 3 using graphviz
A program that plays rock-paper-scissors using Python
Try using platypus, a multipurpose optimization library
[Python] A progress bar on the terminal
Notes on using code formatter in Python
[Python] A program that rounds the score
[2015/11/19] How to register a service locally using the python SDK on naoqi os
A solution to the problem that the Python version in Conda cannot be changed
I registered PyQCheck, a library that can perform QuickCheck with Python, in PyPI.
I tried using the Python library "pykakasi" that can convert kanji to romaji.
Note that I understand the least squares algorithm. And I wrote it in Python.
[Python] A program that finds the minimum and maximum values without using methods
[Python] A program that calculates the difference between the total numbers on the diagonal line.
Build a Docker image containing the private repository Python library on GitHub Actions