MyLibs 1.0 Расширенные библиотеки для STM32

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