evovle перенесен на float + добавлены некоторые опциональные параметры
т.к. нет особо смысла супер быстро рассчитывать параметры. это просто вспомогательный инструмент для их параметров
This commit is contained in:
@@ -20,7 +20,9 @@
|
||||
заглушки, никак не влияющие на параметры и остальную программу
|
||||
- @ref EVOLVE_MAX_PARAMS - Максимальное количество параметров
|
||||
- @ref EVOLVE_MAX_CANDIDATES - Максимальное количество кандидатов для обучения
|
||||
|
||||
- (опционально) @ref EVOLVE_MUTATION_MIN_PCT - Минимальная мутация в процентах от Loss (по умолчанию 10%)
|
||||
- (опционально) @ref EVOLVE_MUTATION_MAX_PCT - Максимальная мутация в процентах от Loss (по умолчанию 100%)
|
||||
- (опционально) @ref ELOVLE_N_ELITE_CANDIDATE - Количество кандидатов, которые проходят в поколение без изменений
|
||||
|
||||
@par Пример использования:
|
||||
@code
|
||||
@@ -28,8 +30,8 @@
|
||||
#define N_PARAMS 4
|
||||
#define N_CANDIDATES 100
|
||||
#define N_BEST 10
|
||||
#define IQ_MUTATION (PARAM_SCALE_Q16(0.1, 0, 1))
|
||||
int32_t params[N_PARAMS];
|
||||
#define MUTATION 0.1f
|
||||
float params[N_PARAMS];
|
||||
EvolveOptimizer_t optimizer;
|
||||
|
||||
// Формирование параметров
|
||||
@@ -37,23 +39,23 @@ uint16_t param_u16 = 800;
|
||||
float param_f = 0.01f;
|
||||
uint8_t param_u8 = 40;
|
||||
int16_t param_i16 = 1600;
|
||||
params[0] = PARAM_SCALE_Q16(param_u16, 0, 1000);
|
||||
params[1] = PARAM_SCALE_Q16(param_f, 0.001f, 0.1f);
|
||||
params[2] = PARAM_SCALE_Q16(param_u8, 10, 100);
|
||||
params[3] = PARAM_SCALE_Q16(param_i16, 500, 5000);
|
||||
params[0] = PARAM_SCALE(param_u16, 0.0f, 1000.0f);
|
||||
params[1] = PARAM_SCALE(param_f, 0.001f, 0.1f);
|
||||
params[2] = PARAM_SCALE(param_u8, 10.0f, 100.0f);
|
||||
params[3] = PARAM_SCALE(param_i16, 500.0f, 5000.0f);
|
||||
|
||||
// Инициалиазция
|
||||
EvolveOptimizer_Init(&optimizer, N_PARAMS, N_CANDIDATES, N_BEST, IQ_MUTATION, params);
|
||||
EvolveOptimizer_Init(&optimizer, N_PARAMS, N_CANDIDATES, N_BEST, MUTATION, params);
|
||||
|
||||
// Шаг эволюции
|
||||
int32_t loss = calc_iq_loss(); // расчет эффективности параметров
|
||||
float loss = calc_loss(); // расчет эффективности параметров (от 0 до 1)
|
||||
EvolveOptimizer_Step(&optimizer, params, loss);
|
||||
|
||||
// Взятие следующих для эволюции параметров
|
||||
param_u16 = PARAM_UNSCALE_Q16(params[0], 0, 1000);
|
||||
param_f = PARAM_UNSCALE_Q16(params[1], 0.001f, 0.1f);
|
||||
param_u8 = PARAM_UNSCALE_Q16(params[2], 10, 100);
|
||||
param_i16 = PARAM_UNSCALE_Q16(params[3], 500, 5000);
|
||||
param_u16 = PARAM_UNSCALE(params[0], 0.0f, 1000.0f);
|
||||
param_f = PARAM_UNSCALE(params[1], 0.001f, 0.1f);
|
||||
param_u8 = PARAM_UNSCALE(params[2], 10.0f, 100.0f);
|
||||
param_i16 = PARAM_UNSCALE(params[3], 500.0f, 5000.0f);
|
||||
@endcode
|
||||
* @{
|
||||
*****************************************************************************/
|
||||
@@ -66,33 +68,54 @@ param_i16 = PARAM_UNSCALE_Q16(params[3], 500, 5000);
|
||||
|
||||
#ifdef ENABLE_EVOLVE_OPTIMIZATION
|
||||
/**
|
||||
* @brief Линейное масштабирование x из диапазона [min_val, max_val] в Q16.16 [0, 65536)
|
||||
* @brief Линейное масштабирование x из диапазона [min_val, max_val] в диапазон [0, 1)
|
||||
*/
|
||||
#define PARAM_SCALE_Q16(x, min_val, max_val) \
|
||||
((int32_t)((((float)(x) - (float)(min_val)) / ((float)(max_val) - (float)(min_val))) * 65536.0f))
|
||||
#define PARAM_SCALE(x, min_val, max_val) \
|
||||
(((float)(x) - (float)(min_val)) / ((float)(max_val) - (float)(min_val)))
|
||||
|
||||
/**
|
||||
* @brief Обратное линейное масштабирование Q16.16 значения в диапазон [min_val, max_val]
|
||||
*/
|
||||
#define PARAM_UNSCALE_Q16(q16_val, min_val, max_val) \
|
||||
(((float)(q16_val) / 65536.0f) * ((float)(max_val) - (float)(min_val)) + (float)(min_val))
|
||||
* @brief Обратное линейное масштабирование значения из [0, 1) в диапазон [min_val, max_val]
|
||||
*/
|
||||
#define PARAM_UNSCALE(val, min_val, max_val) \
|
||||
(((float)(val)) * ((float)(max_val) - (float)(min_val)) + (float)(min_val))
|
||||
|
||||
#ifndef local_time
|
||||
#define local_time() HAL_GetTick() ///< Локальное время
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifndef EVOLVE_MUTATION_MIN_PCT
|
||||
#define EVOLVE_MUTATION_MIN_PCT 10 ///< Минимальная мутация (в процентах от Loss)
|
||||
#endif
|
||||
#ifndef EVOLVE_MUTATION_MAX_PCT
|
||||
#define EVOLVE_MUTATION_MAX_PCT 100 ///< Максимальная мутация (в процентах от Loss)
|
||||
#endif
|
||||
#ifndef ELOVLE_N_ELITE_CANDIDATE
|
||||
#define ELOVLE_N_ELITE_CANDIDATE 2 ///< Количество кандидатов, которые проходят в поколение без изменений (по умолчанию 2)
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* @brief Структура эволюционного оптимизатора
|
||||
*/
|
||||
typedef struct {
|
||||
uint16_t n_params; ///< Количество параметров
|
||||
uint16_t n_cand; ///< Количество кандидатов
|
||||
uint16_t n_best; ///< Количество лучших, усредняемых
|
||||
uint16_t iq_mutation; ///< Амплитуда мутации в Q16.16
|
||||
uint16_t cand_index; ///< Индекс кандидата для обработки
|
||||
int32_t loss[EVOLVE_MAX_CANDIDATES]; ///< Loss для каждого кандидата
|
||||
int32_t candidates[EVOLVE_MAX_CANDIDATES][EVOLVE_MAX_PARAMS]; ///< Параметры кандидатов
|
||||
uint16_t sorted_idx[EVOLVE_MAX_CANDIDATES]; ///< Индексы отсортированных кандидатов
|
||||
float stability; ///< Коэффициент насколько стабильная популяция (0..1)(@ref n_cand)
|
||||
|
||||
uint16_t n_params; ///< Количество параметров
|
||||
uint16_t n_cand; ///< Количество кандидатов в популяции
|
||||
uint16_t n_best; ///< Количество лучших, усредняемых
|
||||
float mutation_amp; ///< Амплитуда мутации (0..1)
|
||||
|
||||
uint16_t cand_index; ///< Индекс кандидата для обработки
|
||||
uint16_t gen_index; ///< Индекс популяции
|
||||
|
||||
//INTERNAL
|
||||
float gen_mut; ///< Амплитуда мутации у текущей популяции
|
||||
|
||||
float loss[EVOLVE_MAX_CANDIDATES]; ///< Loss для каждого кандидата
|
||||
float candidates[EVOLVE_MAX_CANDIDATES][EVOLVE_MAX_PARAMS]; ///< Параметры кандидатов
|
||||
uint16_t sorted_idx[EVOLVE_MAX_CANDIDATES]; ///< Индексы отсортированных кандидатов
|
||||
} EvolveOptimizer_t;
|
||||
|
||||
|
||||
@@ -100,11 +123,11 @@ typedef struct {
|
||||
/**
|
||||
* @cond EVOLVE_INTERNAL
|
||||
*/
|
||||
#define Q16_MUL(a,b) ((int32_t)(((int64_t)(a) * (int64_t)(b)) >> 16))
|
||||
|
||||
// Вспомогательный указатель для сортировки
|
||||
static EvolveOptimizer_t *g_sort_opt; // глобальный указатель на текущий оптимизатор
|
||||
|
||||
// функция условия сортировки
|
||||
static int cmp_idx(const void *a, const void *b) {
|
||||
if (g_sort_opt->loss[*(const uint16_t*)a] < g_sort_opt->loss[*(const uint16_t*)b])
|
||||
return -1;
|
||||
@@ -124,8 +147,8 @@ static int cmp_idx(const void *a, const void *b) {
|
||||
* @param n_params Количество параметров в одном кандидате
|
||||
* @param n_cand Количество кандидатов
|
||||
* @param n_best Количество лучших, усредняемых
|
||||
* @param iq_mutation Амплитуда мутации в Q16.16
|
||||
* @param start_params Начальные параметры (Q16.16)
|
||||
* @param mutation_amp Амплитуда мутации (в диапазоне 0.0–1.0)
|
||||
* @param start_params Начальные параметры (в диапазоне 0.0–1.0)
|
||||
* @return 0 — если окей,
|
||||
* -1 — если ошибка
|
||||
*/
|
||||
@@ -133,10 +156,10 @@ __STATIC_INLINE int EvolveOptimizer_Init(EvolveOptimizer_t* opt,
|
||||
uint16_t n_params,
|
||||
uint16_t n_cand,
|
||||
uint16_t n_best,
|
||||
uint16_t iq_mutation,
|
||||
int32_t* start_params)
|
||||
float mutation_amp,
|
||||
float* start_params)
|
||||
{
|
||||
if((opt = NULL) || (start_params == NULL))
|
||||
if((opt == NULL) || (start_params == NULL))
|
||||
return -1;
|
||||
|
||||
if(n_params > EVOLVE_MAX_PARAMS)
|
||||
@@ -151,19 +174,34 @@ __STATIC_INLINE int EvolveOptimizer_Init(EvolveOptimizer_t* opt,
|
||||
return -1;
|
||||
opt->n_best = n_best;
|
||||
|
||||
if(iq_mutation > 32768)
|
||||
return -1;
|
||||
opt->iq_mutation = iq_mutation;
|
||||
|
||||
if((mutation_amp > 1) || (mutation_amp < 0))
|
||||
return -1;
|
||||
if(mutation_amp <= 0.001f)
|
||||
mutation_amp = 0.001f;
|
||||
opt->mutation_amp = mutation_amp;
|
||||
|
||||
for (uint16_t i = 0; i < n_cand; i++) {
|
||||
for (uint16_t j = 0; j < n_params; j++) {
|
||||
opt->candidates[i][j] = start_params[j];
|
||||
}
|
||||
opt->loss[i] = 0;
|
||||
}
|
||||
uint32_t seed = local_time() + (ADC1->DR & 0xFF);
|
||||
uint32_t seed = local_time();
|
||||
#ifdef ADC1
|
||||
seed += (ADC1->DR & 0xFF);
|
||||
#endif
|
||||
srand(seed);
|
||||
|
||||
for (uint16_t i = 0; i < n_cand; i++) {
|
||||
for (uint16_t j = 0; j < n_params; j++) {
|
||||
// Добавляем случайную мутацию вокруг стартового параметра
|
||||
float base = start_params[j];
|
||||
float inv_randmax = 1.0f / (float)RAND_MAX;
|
||||
float noise = ((float)rand() * inv_randmax * 2.0f - 1.0f) * mutation_amp;
|
||||
opt->candidates[i][j] = base + noise;
|
||||
if (opt->candidates[i][j] < 0.0f) opt->candidates[i][j] = 0.0f;
|
||||
if (opt->candidates[i][j] > 1.0f) opt->candidates[i][j] = 1.0f;
|
||||
}
|
||||
opt->loss[i] = 0.0f;
|
||||
}
|
||||
|
||||
opt->cand_index = 0;
|
||||
opt->gen_index = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -172,7 +210,7 @@ __STATIC_INLINE int EvolveOptimizer_Init(EvolveOptimizer_t* opt,
|
||||
* @brief Один шаг эволюционного оптимизатора.
|
||||
* @param opt Указатель на структуру оптимизатора
|
||||
* @param params Массив параметров, которые будут обновлены (на выходе — новые параметры)
|
||||
* @param loss Loss текущего кандидата (Q16.16)
|
||||
* @param loss Loss текущего кандидата
|
||||
* @return 0 — если окей,
|
||||
* -1 — если ошибка
|
||||
* @details
|
||||
@@ -185,10 +223,10 @@ __STATIC_INLINE int EvolveOptimizer_Init(EvolveOptimizer_t* opt,
|
||||
* Надо убедится что только один экземпляр функции запущен в момент времени
|
||||
*/
|
||||
__STATIC_INLINE int EvolveOptimizer_Step(EvolveOptimizer_t* opt,
|
||||
int32_t* params,
|
||||
int32_t loss)
|
||||
float* params,
|
||||
float loss)
|
||||
{
|
||||
if((opt = NULL) || (params == NULL))
|
||||
if((opt == NULL) || (params == NULL))
|
||||
return -1;
|
||||
|
||||
uint16_t n_params = opt->n_params;
|
||||
@@ -201,11 +239,11 @@ __STATIC_INLINE int EvolveOptimizer_Step(EvolveOptimizer_t* opt,
|
||||
|
||||
uint16_t n_best = opt->n_best;
|
||||
if(n_best > EVOLVE_MAX_CANDIDATES/2)
|
||||
return -1;
|
||||
return -1;
|
||||
|
||||
uint16_t mut = opt->iq_mutation;
|
||||
if(mut > 32768)
|
||||
return -1;
|
||||
float mut = opt->mutation_amp;
|
||||
if((mut > 1) ||(mut < 0))
|
||||
return -1;
|
||||
|
||||
// 1. Сохраняем loss текущего кандидата
|
||||
opt->loss[opt->cand_index] = loss;
|
||||
@@ -219,42 +257,56 @@ __STATIC_INLINE int EvolveOptimizer_Step(EvolveOptimizer_t* opt,
|
||||
g_sort_opt = opt;
|
||||
qsort(opt->sorted_idx, opt->n_cand, sizeof(uint16_t), cmp_idx);
|
||||
g_sort_opt = NULL;
|
||||
// for (uint16_t i = 0; i < n_cand - 1; i++) {
|
||||
// for (uint16_t j = i + 1; j < n_cand; j++) {
|
||||
// if (opt->loss[j] < opt->loss[i]) {
|
||||
// int32_t tmp_loss = opt->loss[i];
|
||||
// opt->loss[i] = opt->loss[j];
|
||||
// opt->loss[j] = tmp_loss;
|
||||
|
||||
// for (uint16_t k = 0; k < n_params; k++) {
|
||||
// int32_t tmp = opt->candidates[i][k];
|
||||
// opt->candidates[i][k] = opt->candidates[j][k];
|
||||
// opt->candidates[j][k] = tmp;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// --- Адаптивная мутация в зависимости от Loss ---
|
||||
float best_loss = opt->loss[opt->sorted_idx[0]];
|
||||
float worst_loss = opt->loss[opt->sorted_idx[opt->n_cand - 1]];
|
||||
float diff = worst_loss - best_loss;
|
||||
|
||||
// 3. Генерируем новое поколение: каждый кандидат берется случайно из лучших с мутацией
|
||||
uint16_t n_elite = 2; // количество элитных кандидатов, которые сохраняем без изменений
|
||||
float sum_loss = 0.0f;
|
||||
for (uint16_t i = 0; i < n_cand; i++)
|
||||
sum_loss += opt->loss[i];
|
||||
float avg_loss = sum_loss / (float)n_cand;
|
||||
|
||||
|
||||
float loss_ratio = (diff > 0.0f) ? ((avg_loss - best_loss) / diff) : 0.5f;
|
||||
if (loss_ratio < 0.0f) loss_ratio = 0.0f;
|
||||
if (loss_ratio > 1.0f) loss_ratio = 1.0f;
|
||||
|
||||
// Записываем стабильность популяции в структуру
|
||||
if(diff < 0.0f) diff = 0.0f;
|
||||
if(diff > 1.0f) diff = 1.0f;
|
||||
opt->stability = (1.0f - worst_loss) * (1.0f - (worst_loss - best_loss));
|
||||
if(opt->stability < 0.0f) opt->stability = 0.0f;
|
||||
if(opt->stability > 1.0f) opt->stability = 1.0f;
|
||||
|
||||
float mut_pct = EVOLVE_MUTATION_MIN_PCT +
|
||||
(EVOLVE_MUTATION_MAX_PCT - EVOLVE_MUTATION_MIN_PCT) * loss_ratio;
|
||||
float adaptive_mut = mut * (mut_pct / 100.0f);
|
||||
if (adaptive_mut < 0.0001f) adaptive_mut = 0.0001f;
|
||||
opt->gen_mut = adaptive_mut;
|
||||
|
||||
// 3. Генерируем новое поколение
|
||||
uint16_t n_elite = ELOVLE_N_ELITE_CANDIDATE;
|
||||
for (uint16_t c = 0; c < n_cand; c++) {
|
||||
if (c < n_elite) {
|
||||
// Копируем лучших кандидатов напрямую без мутации
|
||||
for (uint16_t i = 0; i < n_params; i++) {
|
||||
opt->candidates[c][i] = opt->candidates[opt->sorted_idx[c]][i]; // просто сохраняем параметры
|
||||
}
|
||||
opt->loss[c] = 0;
|
||||
for (uint16_t i = 0; i < n_params; i++)
|
||||
opt->candidates[c][i] = opt->candidates[opt->sorted_idx[c]][i];
|
||||
opt->loss[c] = 0.0f;
|
||||
} else {
|
||||
// Остальные кандидаты формируются с кроссовером и мутацией
|
||||
for (uint16_t i = 0; i < n_params; i++) {
|
||||
int32_t noise = (rand() % (2 * mut)) - mut;
|
||||
uint16_t parent = opt->sorted_idx[rand() % opt->n_best]; // каждый параметр из случайного лучшего
|
||||
float inv_randmax = 1.0f / (float)RAND_MAX;
|
||||
float noise = ((float)rand() * inv_randmax * 2.0f - 1.0f) * adaptive_mut;
|
||||
uint16_t parent = opt->sorted_idx[rand() % opt->n_best];
|
||||
opt->candidates[c][i] = opt->candidates[parent][i] + noise;
|
||||
if (opt->candidates[c][i] < 0.0f) opt->candidates[c][i] = 0.0f;
|
||||
if (opt->candidates[c][i] > 1.0f) opt->candidates[c][i] = 1.0f;
|
||||
}
|
||||
opt->loss[c] = 0;
|
||||
opt->loss[c] = 0.0f;
|
||||
}
|
||||
}
|
||||
opt->cand_index = 0;
|
||||
opt->gen_index++;
|
||||
}
|
||||
|
||||
// 4. Возвращаем параметры следующего кандидата
|
||||
@@ -269,14 +321,14 @@ typedef struct {
|
||||
uint16_t n_params;
|
||||
uint16_t n_cand;
|
||||
uint16_t n_best;
|
||||
uint16_t iq_mutation;
|
||||
int32_t loss[0];
|
||||
int32_t candidates[0][0];
|
||||
float mutation_amp;
|
||||
float loss[0];
|
||||
float candidates[0][0];
|
||||
} EvolveOptimizer_t;
|
||||
#define EvolveOptimizer_Init(opt, n_params, n_cand, n_best, iq_mutation, start_params)
|
||||
#define EvolveOptimizer_Init(opt, n_params, n_cand, n_best, mutation_amp, start_params)
|
||||
#define EvolveOptimizer_Step(opt, params, LossFunc)
|
||||
#define PARAM_SCALE_Q16(x, min_val, max_val) (x)
|
||||
#define PARAM_UNSCALE_Q16(q16_val, min_val, max_val) (q16_val)
|
||||
#define PARAM_SCALE(x, min_val, max_val) (x)
|
||||
#define PARAM_UNSCALE(val, min_val, max_val) (val)
|
||||
#endif // ENABLE_EVOLVE_OPTIMIZATION
|
||||
|
||||
#endif // __EVOLVE_OPTIMIZER_H_
|
||||
|
||||
Reference in New Issue
Block a user