MyLibs 1.0
Расширенные библиотеки для STM32
Loading...
Searching...
No Matches
evolve_optimizer.h
Go to the documentation of this file.
1/**
2******************************************************************************
3* @file evolve_optimizer.h
4* @brief Заголовочный файл для адаптивного подбора параметров
5******************************************************************************
6* @addtogroup EVOLVE_OPTIMIZER Evolve optimizer
7* @ingroup MYLIBS_DEFINES
8* @brief Библиотека для эволюционного подбора параметров
9* @details
10Поддерживает:
11- Любое количество параметров
12- Генерацию новых параметров на основе лучших кандидатов
13- Мутацию для поиска оптимальных параметров
14- Несколько независимых оптимизаторов в одной программе
15
16
17Параметры для конфигурации:
18- @ref ENABLE_EVOLVE_OPTIMIZATION - Включить оптимизацию параметров
19 Если библиотека отключена @ref ENABLE_EVOLVE_OPTIMIZATION, то вставляются
20 заглушки, никак не влияющие на параметры и остальную программу
21- @ref EVOLVE_MAX_PARAMS - Максимальное количество параметров
22- @ref EVOLVE_MAX_CANDIDATES - Максимальное количество кандидатов для обучения
23- (опционально) @ref EVOLVE_MUTATION_MIN_PCT - Минимальная мутация в процентах от Loss (по умолчанию 10%)
24- (опционально) @ref EVOLVE_MUTATION_MAX_PCT - Максимальная мутация в процентах от Loss (по умолчанию 100%)
25- (опционально) @ref ELOVLE_N_ELITE_CANDIDATE - Количество кандидатов, которые проходят в поколение без изменений
26
27@par Пример использования:
28@code
29#include "evolve_optimizer.h"
30#define N_PARAMS 4
31#define N_CANDIDATES 100
32#define N_BEST 10
33#define MUTATION 0.1f
34float params[N_PARAMS];
35EvolveOptimizer_t optimizer;
36
37// Формирование параметров
38uint16_t param_u16 = 800;
39float param_f = 0.01f;
40uint8_t param_u8 = 40;
41int16_t param_i16 = 1600;
42params[0] = PARAM_SCALE(param_u16, 0.0f, 1000.0f);
43params[1] = PARAM_SCALE(param_f, 0.001f, 0.1f);
44params[2] = PARAM_SCALE(param_u8, 10.0f, 100.0f);
45params[3] = PARAM_SCALE(param_i16, 500.0f, 5000.0f);
46
47// Инициалиазция
48EvolveOptimizer_Init(&optimizer, N_PARAMS, N_CANDIDATES, N_BEST, MUTATION, params);
49
50// Шаг эволюции
51float loss = calc_loss(); // расчет эффективности параметров (от 0 до 1)
52EvolveOptimizer_Step(&optimizer, params, loss);
53
54// Взятие следующих для эволюции параметров
55param_u16 = PARAM_UNSCALE(params[0], 0.0f, 1000.0f);
56param_f = PARAM_UNSCALE(params[1], 0.001f, 0.1f);
57param_u8 = PARAM_UNSCALE(params[2], 10.0f, 100.0f);
58param_i16 = PARAM_UNSCALE(params[3], 500.0f, 5000.0f);
59@endcode
60* @{
61*****************************************************************************/
62#ifndef __EVOLVE_OPTIMIZER_H_
63#define __EVOLVE_OPTIMIZER_H_
64
65#include "mylibs_defs.h"
66#include <stdint.h>
67#include <stdlib.h>
68
69#ifdef ENABLE_EVOLVE_OPTIMIZATION
70/**
71 * @brief Линейное масштабирование x из диапазона [min_val, max_val] в диапазон [0, 1)
72 */
73#define PARAM_SCALE(x, min_val, max_val) \
74(((float)(x) - (float)(min_val)) / ((float)(max_val) - (float)(min_val)))
75
76/**
77 * @brief Обратное линейное масштабирование значения из [0, 1) в диапазон [min_val, max_val]
78 */
79#define PARAM_UNSCALE(val, min_val, max_val) \
80(((float)(val)) * ((float)(max_val) - (float)(min_val)) + (float)(min_val))
81
82#ifndef local_time
83#define local_time() HAL_GetTick() ///< Локальное время
84#endif
85
86
87
88#ifndef EVOLVE_MUTATION_MIN_PCT
89#define EVOLVE_MUTATION_MIN_PCT 10 ///< Минимальная мутация (в процентах от Loss)
90#endif
91#ifndef EVOLVE_MUTATION_MAX_PCT
92#define EVOLVE_MUTATION_MAX_PCT 100 ///< Максимальная мутация (в процентах от Loss)
93#endif
94#ifndef ELOVLE_N_ELITE_CANDIDATE
95#define ELOVLE_N_ELITE_CANDIDATE 2 ///< Количество кандидатов, которые проходят в поколение без изменений (по умолчанию 2)
96#endif
97
98
99/**
100 * @brief Структура эволюционного оптимизатора
101 */
102typedef struct {
103 float stability; ///< Коэффициент насколько стабильная популяция (0..1)(@ref n_cand)
104
105 uint16_t n_params; ///< Количество параметров
106 uint16_t n_cand; ///< Количество кандидатов в популяции
107 uint16_t n_best; ///< Количество лучших, усредняемых
108 float mutation_amp; ///< Амплитуда мутации (0..1)
109
110 uint16_t cand_index; ///< Индекс кандидата для обработки
111 uint16_t gen_index; ///< Индекс популяции
112
113 //INTERNAL
114 float gen_mut; ///< Амплитуда мутации у текущей популяции
115
116 float loss[EVOLVE_MAX_CANDIDATES]; ///< Loss для каждого кандидата
117 float candidates[EVOLVE_MAX_CANDIDATES][EVOLVE_MAX_PARAMS]; ///< Параметры кандидатов
118 uint16_t sorted_idx[EVOLVE_MAX_CANDIDATES]; ///< Индексы отсортированных кандидатов
120
121
122
123/**
124 * @cond EVOLVE_INTERNAL
125 */
126
127// Вспомогательный указатель для сортировки
128static EvolveOptimizer_t *g_sort_opt; // глобальный указатель на текущий оптимизатор
129
130// функция условия сортировки
131static int cmp_idx(const void *a, const void *b) {
132 if (g_sort_opt->loss[*(const uint16_t*)a] < g_sort_opt->loss[*(const uint16_t*)b])
133 return -1;
134 if (g_sort_opt->loss[*(const uint16_t*)a] > g_sort_opt->loss[*(const uint16_t*)b])
135 return 1;
136 return 0;
137}
138/** @endcond */
139
140
141
142
143
144/**
145 * @brief Инициализация эволюционного оптимизатора.
146 * @param opt Указатель на структуру оптимизатора
147 * @param n_params Количество параметров в одном кандидате
148 * @param n_cand Количество кандидатов
149 * @param n_best Количество лучших, усредняемых
150 * @param mutation_amp Амплитуда мутации (в диапазоне 0.0–1.0)
151 * @param start_params Начальные параметры (в диапазоне 0.0–1.0)
152 * @return 0 — если окей,
153 * -1 — если ошибка
154 */
156 uint16_t n_params,
157 uint16_t n_cand,
158 uint16_t n_best,
159 float mutation_amp,
160 float* start_params)
161{
162 if((opt == NULL) || (start_params == NULL))
163 return -1;
164
165 if(n_params > EVOLVE_MAX_PARAMS)
166 return -1;
167 opt->n_params = n_params;
168
169 if(n_cand > EVOLVE_MAX_CANDIDATES)
170 return -1;
171 opt->n_cand = n_cand;
172
173 if(n_best > EVOLVE_MAX_CANDIDATES/2)
174 return -1;
175 opt->n_best = n_best;
176
177
178 if((mutation_amp > 1) || (mutation_amp < 0))
179 return -1;
180 if(mutation_amp <= 0.001f)
181 mutation_amp = 0.001f;
182 opt->mutation_amp = mutation_amp;
183
184 uint32_t seed = local_time();
185#ifdef ADC1
186 seed += (ADC1->DR & 0xFF);
187#endif
188 srand(seed);
189
190 for (uint16_t i = 0; i < n_cand; i++) {
191 for (uint16_t j = 0; j < n_params; j++) {
192 // Добавляем случайную мутацию вокруг стартового параметра
193 float base = start_params[j];
194 float inv_randmax = 1.0f / (float)RAND_MAX;
195 float noise = ((float)rand() * inv_randmax * 2.0f - 1.0f) * mutation_amp;
196 opt->candidates[i][j] = base + noise;
197 if (opt->candidates[i][j] < 0.0f) opt->candidates[i][j] = 0.0f;
198 if (opt->candidates[i][j] > 1.0f) opt->candidates[i][j] = 1.0f;
199 }
200 opt->loss[i] = 0.0f;
201 }
202
203 opt->cand_index = 0;
204 opt->gen_index = 0;
205 return 0;
206}
207
208
209/**
210 * @brief Один шаг эволюционного оптимизатора.
211 * @param opt Указатель на структуру оптимизатора
212 * @param params Массив параметров, которые будут обновлены (на выходе — новые параметры)
213 * @param loss Loss текущего кандидата
214 * @return 0 — если окей,
215 * -1 — если ошибка
216 * @details
217 * Сохраняет loss текущего кандидата и формирует параметры следующего кандидата.
218 * Если накоплено n_cand кандидатов, генерируется новое поколение.
219 * Новое поколение формируется случайным выбором из n_best лучших с добавлением мутации.
220 *
221 * На выходе params содержит параметры следующего кандидата для измерений.
222 * @note Функция использует глобальную внутреннюю переменную для сортировки.
223 * Надо убедится что только один экземпляр функции запущен в момент времени
224 */
226 float* params,
227 float loss)
228{
229 if((opt == NULL) || (params == NULL))
230 return -1;
231
232 uint16_t n_params = opt->n_params;
233 if(n_params > EVOLVE_MAX_PARAMS)
234 return -1;
235
236 uint16_t n_cand = opt->n_cand;
237 if(n_cand > EVOLVE_MAX_CANDIDATES)
238 return -1;
239
240 uint16_t n_best = opt->n_best;
241 if(n_best > EVOLVE_MAX_CANDIDATES/2)
242 return -1;
243
244 float mut = opt->mutation_amp;
245 if((mut > 1) ||(mut < 0))
246 return -1;
247
248 // 1. Сохраняем loss текущего кандидата
249 opt->loss[opt->cand_index] = loss;
250 opt->cand_index++;
251
252 if (opt->cand_index >= n_cand) {
253 // 2. Сортируем текущее поколение по loss
254 for(uint16_t i = 0; i < opt->n_cand; i++)
255 opt->sorted_idx[i] = i;
256
257 g_sort_opt = opt;
258 qsort(opt->sorted_idx, opt->n_cand, sizeof(uint16_t), cmp_idx);
259 g_sort_opt = NULL;
260
261 // --- Адаптивная мутация в зависимости от Loss ---
262 float best_loss = opt->loss[opt->sorted_idx[0]];
263 float worst_loss = opt->loss[opt->sorted_idx[opt->n_cand - 1]];
264 float diff = worst_loss - best_loss;
265
266 float sum_loss = 0.0f;
267 for (uint16_t i = 0; i < n_cand; i++)
268 sum_loss += opt->loss[i];
269 float avg_loss = sum_loss / (float)n_cand;
270
271
272 float loss_ratio = (diff > 0.0f) ? ((avg_loss - best_loss) / diff) : 0.5f;
273 if (loss_ratio < 0.0f) loss_ratio = 0.0f;
274 if (loss_ratio > 1.0f) loss_ratio = 1.0f;
275
276 // Записываем стабильность популяции в структуру
277 if(diff < 0.0f) diff = 0.0f;
278 if(diff > 1.0f) diff = 1.0f;
279 opt->stability = (1.0f - worst_loss) * (1.0f - (worst_loss - best_loss));
280 if(opt->stability < 0.0f) opt->stability = 0.0f;
281 if(opt->stability > 1.0f) opt->stability = 1.0f;
282
283 float mut_pct = EVOLVE_MUTATION_MIN_PCT +
285 float adaptive_mut = mut * (mut_pct / 100.0f);
286 if (adaptive_mut < 0.0001f) adaptive_mut = 0.0001f;
287 opt->gen_mut = adaptive_mut;
288
289 // 3. Генерируем новое поколение
290 uint16_t n_elite = ELOVLE_N_ELITE_CANDIDATE;
291 for (uint16_t c = 0; c < n_cand; c++) {
292 if (c < n_elite) {
293 for (uint16_t i = 0; i < n_params; i++)
294 opt->candidates[c][i] = opt->candidates[opt->sorted_idx[c]][i];
295 opt->loss[c] = 0.0f;
296 } else {
297 for (uint16_t i = 0; i < n_params; i++) {
298 float inv_randmax = 1.0f / (float)RAND_MAX;
299 float noise = ((float)rand() * inv_randmax * 2.0f - 1.0f) * adaptive_mut;
300 uint16_t parent = opt->sorted_idx[rand() % opt->n_best];
301 opt->candidates[c][i] = opt->candidates[parent][i] + noise;
302 if (opt->candidates[c][i] < 0.0f) opt->candidates[c][i] = 0.0f;
303 if (opt->candidates[c][i] > 1.0f) opt->candidates[c][i] = 1.0f;
304 }
305 opt->loss[c] = 0.0f;
306 }
307 }
308 opt->cand_index = 0;
309 opt->gen_index++;
310 }
311
312 // 4. Возвращаем параметры следующего кандидата
313 for (uint16_t i = 0; i < opt->n_params; i++)
314 params[i] = opt->candidates[opt->cand_index][i];
315
316 return 0;
317}
318#else // ENABLE_EVOLVE_OPTIMIZATION
319//заглушки
320typedef struct {
321 uint16_t n_params;
322 uint16_t n_cand;
323 uint16_t n_best;
324 float mutation_amp;
325 float loss[0];
326 float candidates[0][0];
328#define EvolveOptimizer_Init(opt, n_params, n_cand, n_best, mutation_amp, start_params)
329#define EvolveOptimizer_Step(opt, params, LossFunc)
330#define PARAM_SCALE(x, min_val, max_val) (x)
331#define PARAM_UNSCALE(val, min_val, max_val) (val)
332#endif // ENABLE_EVOLVE_OPTIMIZATION
333
334#endif // __EVOLVE_OPTIMIZER_H_
335
336/** EVOLVE_OPTIMIZER
337 * @}
338 */
#define EVOLVE_MAX_CANDIDATES
Максимальное количество кандидатов для обучения
#define EVOLVE_MAX_PARAMS
Максимальное количество параметров
#define EVOLVE_MUTATION_MIN_PCT
Минимальная мутация (в процентах от Loss)
#define ELOVLE_N_ELITE_CANDIDATE
Количество кандидатов, которые проходят в поколение без изменений (по умолчанию 2)
__STATIC_INLINE int EvolveOptimizer_Init(EvolveOptimizer_t *opt, uint16_t n_params, uint16_t n_cand, uint16_t n_best, float mutation_amp, float *start_params)
Инициализация эволюционного оптимизатора.
#define EVOLVE_MUTATION_MAX_PCT
Максимальная мутация (в процентах от Loss)
__STATIC_INLINE int EvolveOptimizer_Step(EvolveOptimizer_t *opt, float *params, float loss)
Один шаг эволюционного оптимизатора.
#define local_time()
Локальное время
Заголочный файл для дефайнов библиотеки MyLibsGeneral.
Структура эволюционного оптимизатора
uint16_t cand_index
Индекс кандидата для обработки
uint16_t n_params
Количество параметров
float gen_mut
Амплитуда мутации у текущей популяции
uint16_t sorted_idx[100]
Индексы отсортированных кандидатов
float stability
Коэффициент насколько стабильная популяция (0..1)(n_cand)
float loss[100]
Loss для каждого кандидата
uint16_t n_best
Количество лучших, усредняемых
uint16_t n_cand
Количество кандидатов в популяции
uint16_t gen_index
Индекс популяции
float candidates[100][20]
Параметры кандидатов
float mutation_amp
Амплитуда мутации (0..1)