Глядя на некоторые удобные функции оптимизации, чтобы переключиться с прогнозов на рецепты!

Наличие надежной модели, которая воспроизводит наземную истину, — это здорово, но возможность найти значения функций, которые минимизируют или максимизируют ее отклик, может быть еще лучше. Поймите, почему с печеньем с шоколадной крошкой 🍪!

Контекст

Представьте, что вас только что наняли на фабрику по производству шоколадного печенья!
Отличная работа, не так ли?

Как Data Scientist, ваш начальник просит вас поработать на самом первом этапе процесса, когда все ингредиенты (мука, масло, сахар и т. д.) смешиваются, потому что основная проблема, с которой сталкиваются операторы, заключается в том, что они иногда получают комки. тесто… а это ставит под угрозу качество печенья! 😱

Хорошая новость заключается в том, что они смогли измерить:

  • основные свойства ингредиентов (такие как влажность муки, мягкость масла),
  • настройки миксера (его скорость, температура и т. д.),
  • и количество комочков в тесте от 0 до 10 % (5 % — критический порог).

Данные чистые и хорошо структурированные (излишне напоминать вам, что это вымысел 😂), и вскоре вы сможете создать модель, которая с уверенностью предскажет процент комочков в тесте.

Чтобы показать, насколько надежна ваша модель, вы собираете всех вокруг миксера, кормите модель свойствами текущей партии и объявляете, что тесто будет иметь (катастрофическую!) долю комков 7%.

Когда замешивание закончено, все видят, что ваша модель была права… но, в конце концов, тесто приходится выбрасывать…

Начальник смотрит на тебя и говорит:

«Некоторыми параметрами мы можем управлять (скорость миксера, количество добавляемой воды), а некоторыми другими мы не можем (влажность муки, точная температура в помещении и т. д.).

Просто используйте свою модель, чтобы сообщить нам для каждой партии, как точно настроить параметры производства, чтобы свести к минимуму единовременную норму.

И тут вас озаряет суровая правда: ваша модель случайного леса работает хорошо, но как комбинация 100 деревьев решений она совершенно нелинейна… что делает невозможным поиск оптимального значения, которое можно было бы решить с помощью простое линейное уравнение…

Хорошей новостью является то, что SciPy предоставляет несколько удобных функций оптимизации, которые помогут вам; давайте узнаем как!

Моделирование

Для примера мы будем использовать «слишком простую» модель, но принцип остается тем же для более сложных задач.

Предположим, что на скорость комков теста (Y) влияют два основных параметра, хранящихся в массиве «X» NumPy:

nb_samples = 15
np.random.seed(22)
X = np.random.randint(1, high=10, size=(nb_samples, 2))
print(X)
y = X[:, 0] / X[:, 1]
print(y)

Выход:

[[6 5]
 [1 5]
 [7 7]
 [5 9]
 [5 3]
 [9 8]
 [3 9]
 [9 6]
 [5 3]
 [3 2]
 [7 4]
 [4 3]
 [8 8]
 [8 8]
 [5 5]]
[1.2        0.2        1.         0.55555556 1.66666667 1.125  0.33333333 1.5        1.66666667 1.5        1.75       1.33333333  1.         1.         1.        ]

Примечание: ссылка на полный блокнот находится в конце статьи📑

Поскольку у нас есть правильный (но легкий) набор данных, давайте создадим нелинейную модель с помощью простого Scikit-Learn DecisionTreeRegressor:

model = DecisionTreeRegressor(max_depth=4)
model.fit(X, y)
model.score(X, y)
#0.9343996768065437

И постройте, как это выглядит:

plt.figure(figsize=(15, 15))
tree.plot_tree(model)
plt.show()

Мы видим, что из этого дерева решений можно получить 8 возможных результатов в зависимости от значений X[0] и X[1].

Библиотека оптимизации SciPy

Библиотека SciPy Optimize предоставляет набор функций для минимизации (или максимизации) целевых функций. Единственная мера предосторожности заключается в том, что вы должны выбрать тот, который соответствует вашему варианту использования:

  • Локальная (многомерная) оптимизация с «минимизацией»=› полезна, когда вам нужно решить скалярную/линейную функцию, которая, как предполагается, имеет единственный оптимум, как приведенная ниже выпуклая функция (x²+y²) :

  • Глобальная оптимизация с «двойным отжигом»=› полезна, когда у вас есть нелинейная функция, предполагающая стохастический поиск оптимума. Обычно с этим мы сталкиваемся при использовании таких моделей, как Ensemble Trees (Random Forest, XGBoost и т. д.).

Перед запуском поиска оптимума нам нужно создать целевую функцию, которая возвращает результат нашей нелинейной модели:

def objective(v):
    return model.predict(np.array([v]))[0]

А дальше остается только установить границы каждого признака (X[0] и X[1]) и запустить процесс оптимизации:

bounds = [[1, 10], [1, 10]]
result = dual_annealing(objective, bounds, maxiter=100)
print(f"Status: {result['message']}")
print(f"Total Evaluations: {result['nfev']}")
print(f"Minimum reached: {result['fun']}")
print(f"Solution vector: {result['x']}")

Выход:

Status: ['Maximum number of iteration reached']
Total Evaluations: 410
Minimum reached: 0.2
Solution vector: [1.78224412 9.35220569]

Установив границы, равные пространству исходных признаков, функция может предложить пару значений, которая эффективно минимизирует целевую функцию:

Теперь давайте воспроизведем процесс с ограничением на X[1], меньшим 4:

bounds = [[1, 10], [1, 4]]
result = dual_annealing(objective, bounds, maxiter=100)
print(f"Status: {result['message']}")
print(f"Total Evaluations: {result['nfev']}")
print(f"Minimum reached: {result['fun']}")
print(f"Solution vector: {result['x']}")

Выход:

Status: ['Maximum number of iteration reached']
Total Evaluations: 404
Minimum reached: 1.3333333333333333
Solution vector: [4.32296802 1.87272909]

Алгоритм снова может найти минимально возможное значение (1,333) в заданных границах.

Максимизация или таргетинг

Вместо минимизации функции вы могли бы также попытаться максимизировать ее или достичь определенного значения. В этом случае вам нужно только изменить целевую функцию.

  • Максимизация=› добавлением «-» перед результатом
  • Таргетинг=› путем включения цели в расчет результатов

Пример с максимизацией:

def objective_max(v):
    return -model.predict(np.array([v]))[0]
bounds = [[1, 10], [1, 10]]
result = dual_annealing(objective_max, bounds, maxiter=100)
print(f"Status: {result['message']}")
print(f"Total Evaluations: {result['nfev']}")
print(f"Maximum reached: {-result['fun']}")
print(f"Solution vector: {result['x']}")
# Output
Status: ['Maximum number of iteration reached']
Total Evaluations: 410
Maximum reached: 1.75
Solution vector: [8.0829203 4.2638071]

Отныне в тесте больше не будет комочков, а качество печенья с шоколадной крошкой безопасное 🍪🍪🍪

Соответствующий код хранится здесь:



И не стесняйтесь просматривать другие мои публикации на Medium: