Еволюційні стратегії і генетичне програмування: від правила однієї п’ятої до закону Кеплера
(1+1)-ES і правило однієї п’ятої, популяція і самоадаптація на функції Растригіна, CMA-ES, програми як дерева, символьна регресія, яка в даних про планети знаходить третій закон Кеплера, і розбухання програм.
Серія Основи ШІ Частина 28 з 52
- Мапа штучного інтелекту: що є що і як усе це пов’язано
- Коротка історія штучного інтелекту: від Тюрінга до мовних моделей
- Дані, модель, тренування, інференс: основні поняття на одному прикладі
- Як читати математичний запис: символи, індекси, суми та квантори
- Множини й відношення: конструктор, з якого складаються означення в ШІ
- Функції й відображення: чому кожна модель у ШІ — це функція
- Логіка й доведення: правила, за якими міркують у ШІ
- Графи й дерева: структура, на якій тримаються пошук і нейронні мережі
- Лінійна алгебра для машинного навчання: вектори, матриці й чому ШІ передусім множить
- Похідні, градієнт і правило ланцюжка: як модель дізнається, у якому напрямку вчитися
- Градієнтний спуск: як навчається модель
- Що таке ймовірність: від кидка кубика до аксіом Колмогорова
- Умовна ймовірність і теорема Баєса: як змінити оцінку, коли дізнаєтеся щось нове
- Випадкова величина і функція розподілу: одна функція, яка каже про розподіл усе
- Функція щільності та функція маси ймовірності: чому неперервна величина має в точці ймовірність нуль
- Математичне сподівання, дисперсія і коваріація: де лежить розподіл, наскільки він широкий і що з чим пов’язано
- Огляд розподілів ймовірностей: Бернуллі, біноміальний, Пуассона, рівномірний, нормальний і експоненційний
- Закон великих чисел і центральна гранична теорема: чому середні поводяться так передбачувано
- Статистика й оцінювання параметрів: максимальна правдоподібність, MAP і довірчі інтервали
- Теорія інформації: ентропія, перехресна ентропія і KL-дивергенція — звідки беруться функції втрат
- Пошук у просторі станів: BFS, DFS, UCS і A* з анімаціями
- Ігри та суперник: мінімакс, альфа-бета відсікання і Monte Carlo tree search
- Задоволення обмежень: бектрекінг, поширення обмежень і AC-3 на судоку та мапі чеських країв
- Логіка й системи, засновані на знаннях: пряме і зворотне виведення, резолюція та експертні системи
- Планування: STRIPS, світ кубиків, евристики з релаксації та планування в AI-агентах
- Локальний пошук та імітація відпалу: комівояжер через обласні центри Чехії
- Генетичні алгоритми: популяція, схрещування, мутація і добір на рюкзаку для походу
- Еволюційні стратегії і генетичне програмування: від правила однієї п’ятої до закону Кеплера
- Оптимізація мурашиною колонією: як феромоновий слід знаходить короткий шлях
- Оптимізація роєм частинок: інерція, власний досвід і досвід рою
- Нейроеволюція: еволюція ваг і форми нейронних мереж та алгоритм NEAT
- Нечіткі множини й функції належності: як записати «трохи тепло»
- Нечітка логіка та ймовірність: чому «трохи тепло» не означає «імовірно тепло»
- Нечітка логіка: t-норми, імплікації та правила ЯКЩО–ТО
- Нечітке керування: Мамдані та Суґено від вентилятора до маятника на візку
- Нечітка кластеризація: алгоритм fuzzy c-means і точки між кластерами
- Нейро-нечіткі системи (ANFIS): нечіткі правила, які навчаються з даних
- Марковські ланцюги: матриця переходів, стаціонарний розподіл і PageRank
- Приховані марковські моделі: алгоритм Вітербі й нечесне казино
- Байєсівські мережі: чому трава мокра і що з цього випливає для дощувача
- Фільтр Калмана: як за неточними вимірюваннями оцінити, де авто і як швидко воно їде
- Види машинного навчання: з учителем, без учителя, з частковим залученням учителя та інші
- Промпти, контекст і RAG: як дати моделі правильну інформацію
- Варіаційні автокодувальники (VAE): нові зображення з неперервного латентного простору
- Генеративні змагальні мережі (GAN): фальшувальник проти детектива
- Дифузійні моделі: як із чистого шуму постає зображення
- Марковські процеси ухвалення рішень: стани, дії, винагороди й рівняння Беллмана
- Q-навчання і DQN: як навчитися цінності дій — від таблиці до нейронної мережі
- Policy gradient і PPO: навчання безпосередньо політики
- Як працює ШІ-агент: мовна модель у циклі з інструментами
- Програмування з AI-агентами: завдання, тести й перевірка
- Оцінювання і безпека систем ШІ: як вимірювати і як стежити
Повна програма серії Готово: 52 з 83 тем
- Як еволюційні стратегії налаштовують дійсні числа і як самі встановлюють розмір кроку.
- Чому на пересіченій місцевості популяція перемагає одну особину.
- Як генетичне програмування вирощує цілі формули — і чому вони схильні розбухати.
Що варто знати заздалегідь: Генетичні алгоритми: популяція, схрещування, мутація і добір на рюкзаку для походу
У шістдесятих роках двоє студентів Берлінського технічного університету, Інго Рехенберг і Ганс-Пауль Швефель, стояли в аеродинамічній трубі над шарнірною пластиною. Вони хотіли знайти форму з найменшим опором, але для розрахунку бракувало і рівнянь, і комп’ютерів. Тож вони кидали жереб: навмання повертали шарніри, вимірювали опір, і коли він був меншим, залишали нову форму. З того досліду постала еволюційна стратегія — еволюція, яка замість одиниць і нулів налаштовує числа. Через чверть століття Джон Коза показав, що еволюцією можна вирощувати й цілі програми.
Ця частина продовжує статтю Генетичні алгоритми. Там особина була рядком бітів; тут це буде вектор дійсних чисел і дерево програми.
1. Еволюція чисел: (1+1)-ES
Алгоритм — (1+1)-ES
Мінімізуємо f : ℝn → ℝ. Особина — це точка x ∈ ℝn, σ > 0 — розмір кроку. У кожному поколінні:
- створи нащадка y = x + σ · z, де z ~ N(0, I) — вектор незалежних нормально розподілених чисел;
- якщо f(y) ≤ f(x), заміни x ← y.
Один батько, одна дитина. Дитина — це батько з дещицею випадкового шуму в кожному числі; якщо вона краща, займає його місце. Розмір кроку σ каже, наскільки великий цей шум — чи він робить дрібні поправки, чи великі стрибки.
y = x + σ · z, z ~ N(0, I)
- y
- нащадок — новий кандидат, якого ми оцінимо
- x
- батько — найкраща досі знайдена точка; нащадок виникає в її околі, бо поблизу доброї точки зазвичай лежать інші добрі точки
- σ
- розмір кроку — одне-єдине число, яке визначає, як далеко від батька шукати; шум z має дисперсію 1, і лише σ надає йому масштаб задачі
- z ~ N(0, I)
- напрям і довжина спроби — середнє 0 означає, що жоден напрям не має переваги наперед; одинична матриця I — що всі координати змінюються незалежно й однаково сильно
Означення — нормальний розподіл N(0, 1) і N(0, I)
Випадкова величина Z має стандартний нормальний розподіл N(0, 1), якщо її густина φ(z) = e−z²/2 / √(2π); її математичне сподівання 0, а дисперсія 1. Приблизно 68 % значень лежать в інтервалі [−1, 1], а 95 % — у [−1,96; 1,96].
Вектор z ~ N(0, I) у ℝn має n незалежних компонент із розподілом N(0, 1). Його довжина зазвичай близька до √n (для n = 10 близько 3,2), бо E‖z‖² = n.
Наша програма отримує нормальні числа методом Бокса–Мюллера з двох рівномірних чисел u1, u2 ∈ (0, 1): z = √(−2 ln u1) · cos(2πu2).
Спробуймо на найпростішій можливій функції — сфері f(x) = x12 + … + x102 у десяти вимірах. Мінімум, очевидно, в нулі; почнемо в точці (3, …, 3), де f = 90. Як вибрати σ? Велике значення вміє швидко спуститися з гори, але поблизу мінімуму постійно перелітає. Мале точне, але до дна йде цілу вічність.
2. Правило однієї п’ятої
Означення — правило однієї п’ятої (Рехенберг, 1973)
Якщо частка вдалих мутацій (нащадок кращий за батька) більша за 1/5, збільш σ; якщо менша — зменш. Одна з можливих форм: після успіху σ ← σ · e1/3, після невдачі σ ← σ · e−1/12. Розмір кроку в середньому не змінюється саме тоді, коли вдається п’ята частина мутацій: (1/5) · (1/3) = (4/5) · (1/12).
Коли вдається майже кожна спроба, ви зайво обережні — додайте. Коли не вдається майже жодна, ви стрибаєте задалеко — зменште. Рехенберг вивів, що найшвидше просуваєтеся, коли вдається приблизно кожна п’ята спроба. Так алгоритм сам налаштовує розмір кроку.
Обидві гілки правила можна записати однією формулою; s = 1 після успіху і 0 після невдачі:
σ ← σ · exp( 5/12 · ( s − 1/5 ) )
- σ
- розмір кроку — змінюється множенням, а не додаванням: лишається додатним, і крок може зменшитися на багато порядків (від 1 до тисячних), як того вимагає близькість мінімуму
- 5/12
- швидкість пристосування — після успіху exp(5/12 · 4/5) = e1/3 ≈ 1,40, після невдачі exp(−5/12 · 1/5) = e−1/12 ≈ 0,92; більше число реагувало б швидше, але σ коливалася б сильніше
- s
- результат останньої спроби (1 = нащадок не був гіршим) — єдина інформація, яка потрібна правилу
- 1/5
- цільова частка успіхів — коли вдається рівно п’ята частина спроб, середнє s − 1/5 дорівнює нулю і σ у середньому не змінюється
Правило однієї п’ятої опускається нижче 10−6 за 587 обчислень — і лінія на графіку спадає рівномірно, бо крок зменшується так само швидко, як наближається мінімум. Зі сталим σ = 1 алгоритм скоро натрапляє на «підлогу» близько одиниці, нижче якої з такими великими стрибками не дістатися. З σ = 0,01 він іде вниз, але повільно: навіть після 5 000 обчислень він не нижче 10−4.
import math, random
def es_1_plus_1(f, x, sigma=1.0, kroku=1000):
fx = f(x)
for _ in range(kroku):
y = [xi + sigma * random.gauss(0, 1) for xi in x]
fy = f(y)
if fy <= fx:
x, fx = y, fy
sigma *= math.exp(1 / 3) # успіх: додай
else:
sigma *= math.exp(-1 / 12) # невдача: зменш
return x, fx
Приклад — перші дванадцять кроків запуску з графіка
Старт x = (3, …, 3), f(x) = 90, σ = 1. Стовпець σ — це розмір кроку після оновлення:
| крок | f(y) | успіх? | f(x) | σ |
|---|---|---|---|---|
| 1 | 115,31 | ні | 90,00 | 0,920 |
| 2 | 95,78 | ні | 90,00 | 0,846 |
| 3 | 123,94 | ні | 90,00 | 0,779 |
| 4 | 107,86 | ні | 90,00 | 0,717 |
| 5 | 101,54 | ні | 90,00 | 0,659 |
| 6 | 113,11 | ні | 90,00 | 0,607 |
| 7 | 94,03 | ні | 90,00 | 0,558 |
| 8 | 95,09 | ні | 90,00 | 0,513 |
| 9 | 86,27 | так | 86,27 | 0,717 |
| 10 | 114,52 | ні | 86,27 | 0,659 |
| 11 | 97,72 | ні | 86,27 | 0,607 |
| 12 | 102,44 | ні | 86,27 | 0,558 |
після 8 невдач: σ = e−8/12 ≈ 0,513
9-й крок вдається (86,27 < 90): σ = 0,513 · e1/3 ≈ 0,513 · 1,396 ≈ 0,717
При σ = 1 крок у десяти вимірах має довжину близько √10 ≈ 3,2, і перші вісім нащадків гірші за старт, тож правило стягує крок. Потім успіхи приходять частіше, і σ усталюється там, де вдається приблизно кожна п’ята спроба — за весь запуск вдалися 99 з 587 спроб, тобто 17 %. У міру того як точка наближається до мінімуму, разом із нею зменшується й крок: наприкінці запуску, після 580 обчислень, σ ≈ 0,00055.
3. Популяція і самоадаптація
Сфера має єдиний мінімум. Справжні задачі часто мають їх безліч — і там однієї особини замало. Типова тестова функція — функція Растригіна: f(x) = 10n + ∑ (xi2 − 10 cos 2πxi). Це сфера, у якій хтось вибив правильну сітку ямок; глобальний мінімум 0 лежить у початку координат, а навколо нього — сотні локальних.
f(x) = 10n + ∑i=1n ( xi2 − 10 cos 2πxi )
- 10n
- зсув угору — косинус у початку координат віднімає щонайбільше 10 на кожну координату, стала це повертає, щоб глобальний мінімум мав значення рівно 0
- xi2
- сфера — загальний нахил до початку координат; без неї всі ямки були б однаково глибокі і жоден мінімум не був би глобальним
- 10 cos 2πxi
- хвилі з періодом 1 і амплітудою 10 — вони видовбують ямку біля кожного цілого числа; амплітуда значно більша за нахил сфери між сусідніми ямками, тож локальний пошук застрягне в найближчій ямці
Приклад — старт і сусідні ямки (n = 2)
старт (4,2; −3,8): x12 = 17,64, 10 cos(2π · 4,2) = 10 cos(0,4π) ≈ 3,09
x22 = 14,44, 10 cos(2π · (−3,8)) = 10 cos(0,4π) ≈ 3,09
f = 20 + (17,64 − 3,09) + (14,44 − 3,09) ≈ 45,90
дно ямок поруч із початком координат, біля (±1; 0) і (0; ±1): f ≈ 0,99 (у точці 0,995, не рівно 1 — сфера тягне її до початку координат)
дно діагональних ямок біля (±1; ±1): f ≈ 1,99
Значення 1,49 у поколінні 8 (див. нижче) отже означає популяцію в сусідній ямці, ще не на її дні.
Означення — (μ, λ)-ES і (μ + λ)-ES із самоадаптацією
Популяція має μ батьків; кожне покоління створює λ > μ нащадків. У (μ, λ) виживає μ найкращих нащадків, а батьки гинуть; у (μ + λ) вибирають із батьків і нащадків разом.
Самоадаптація: кожна особина несе власний розмір кроку σ. Нащадок спершу його мутує, σ′ = σ · eτ · N(0, 1) з τ ≈ 1/√(2n), і лише з ним зсуває положення: y = x + σ′ · N(0, I).
Розмір кроку успадковується, як будь-яка інша властивість. Хто має добре налаштований крок, має кращих дітей — і тим самим утверджується і його крок. Нікому не треба рахувати частку успіхів, еволюція налаштовує сама себе. Варіант (μ, λ), у якому батьки не виживають, до того ж не дає себе заблокувати одній особині, якій пощастило: кожне покоління починає наново.
σ′ = σ · eτ · N(0, 1), τ = 1/√(2n)
- σ′
- крок нащадка — ним одразу зсувається положення, тож пристосованість нащадка видасть, чи був крок добрим
- σ
- крок батька — успадковується; нащадок починає з того, що спрацювало в батька
- τ
- швидкість навчання кроку — наскільки крок може змінитися між поколіннями; зі зростанням кількості вимірів n вона зменшується, бо одне значення пристосованості тоді несе менше інформації про те, чи був крок добрим
- N(0, 1)
- випадкова зміна — симетрична відносно нуля, тож збільшення і зменшення однаково ймовірні; про напрям вирішить лише відбір
Приклад — мутація кроку для n = 2
τ = 1/√(2 · 2) = 1/2
N(0, 1) = +1: σ′ = σ · e0,5 ≈ 1,65 σ; N(0, 1) = −1: σ′ = σ · e−0,5 ≈ 0,61 σ
Типовий нащадок, отже, пробує крок приблизно від на 40 % меншого до на 65 % більшого, ніж у батька. У нашому запуску крок найкращої особини за 15 поколінь упав з 2 до 0,011 — майже вдвісті разів.
import math, random
def es_mi_lambda(f, start, mi=15, lam=100, sigma=2.0, generaci=80):
n = len(start)
tau = 1 / math.sqrt(2 * n)
rodice = [(start, sigma)] * mi
for _ in range(generaci):
deti = []
for _ in range(lam):
x, s = random.choice(rodice) # випадковий батько
s = s * math.exp(tau * random.gauss(0, 1)) # спершу мутуй крок
y = [xi + s * random.gauss(0, 1) for xi in x] # потім ним положення
deti.append((y, s))
deti.sort(key=lambda d: f(d[0]))
rodice = deti[:mi] # (μ, λ): виживають лише найкращі нащадки
return rodice[0]
Популяція стартує навколо точки (4,2; −3,8), де f ≈ 45,9. Спочатку вона розкидана по кількох ямках одразу і стягується до глибших. У поколінні 8 найкраща особина ще в сусідній ямці (f ≈ 1,49), але розмір кроку вже впав з 2 до 0,27. У поколінні 15 популяція в глобальному мінімумі, а крок зменшився до сотої, щоб вона могла його доточити.
Зі ста запусків із різними зернами (15, 100)-ES знайшла глобальний мінімум у 32 випадках. Для порівняння (1+1)-ES із правилом однієї п’ятої і тим самим бюджетом 8 000 обчислень: в одному-єдиному. На сферах одна особина найшвидша, на пересіченій місцевості перемагає популяція — і навіть вона часто опиняється в одній із сусідніх ямок.
4. CMA-ES
Самоадаптація налаштовує лише довжину кроку. А що, як мінімум лежить у вузькій похилій долині, де вигідно робити довгі кроки в одному напрямку і короткі в іншому? CMA-ES (Гансен і Остермаєр, 2001) вчиться з вдалих кроків цілої коваріаційної матриці мутації — форми хмари, з якої вона витягає нащадків. Під час пошуку хмара повертається і витягується вздовж долини. Для оптимізації неперервних функцій, градієнта яких ми не знаємо, CMA-ES сьогодні — один із найнадійніших методів.
Означення — мутація з коваріаційною матрицею
Для симетричної додатно визначеної матриці C ∈ ℝn×n z ~ N(0, C) — це вектор із математичним сподіванням 0 і коваріаціями E[zizj] = Cij. Його отримують зі звичайного шуму: z = Au, де u ~ N(0, I) і AAT = C. Нащадок — це y = m + σ · z, де m — центр популяції.
Точки з однаковою густиною лежать на еліпсоїді; його осі — власні вектори C, а довжини осей пропорційні квадратним кореням власних значень. Для C = I це сфера, і ми отримуємо звичайну еволюційну стратегію.
Спрощено (справжня CMA-ES додає до цього ще так звані еволюційні шляхи і власне керування σ, див. N. Hansen, The CMA Evolution Strategy: A Tutorial, 2016) матриця після кожного покоління зсувається до вдалих кроків:
C ← (1 − c) C + c ∑i=1μ wi ziziT
- C
- коваріаційна матриця мутації — форма хмари, з якої витягають нащадків
- 1 − c
- пам’ять — стара матриця лише трохи наближається до нової інформації, бо одне покоління — мала і випадкова вибірка
- c
- швидкість навчання матриці, число між 0 і 1 — скільки ваги отримують кроки останнього покоління
- ∑ wi
- зважене середнє за μ найкращими нащадками; ваги додатні, у сумі дають 1, і кращий нащадок має більшу — ми вчимося лише з кроків, які окупилися
- ziziT
- вдалий крок zi = (yi − m)/σ як матриця — витягує хмару саме в його напрямку
Приклад — один вдалий крок по діагоналі
C = I (кругла хмара), єдиний вдалий крок z = (1, 1), c = 0,2:
zzT = [[1, 1], [1, 1]]
C ← 0,8 · [[1, 0], [0, 1]] + 0,2 · [[1, 1], [1, 1]] = [[1; 0,2], [0,2; 1]]
власні значення: 1 + 0,2 = 1,2 у напрямку (1, 1) і 1 − 0,2 = 0,8 у напрямку (1, −1)
Хмара витягнулася вздовж вдалого напрямку і звузилася впоперек — після кількох таких поколінь вона лежить уздовж долини.
5. Генетичне програмування
Означення — програма як дерево
Задано множину функцій F (з арностями) і множину терміналів T (змінні та сталі). Програма — це кореневе дерево, внутрішні вузли якого — функції з F із такою кількістю дітей, яка їхня арність, а листки — термінали з T. Значення програми обчислюють від листків до кореня.
Замкненість: кожна функція мусить приймати вихід будь-якої іншої. Тому використовують захищене ділення (ділення на нуль повертає 1) і корінь з абсолютного значення.
Формула (a ÷ 5) × √a — це дерево: угорі множення, під ним ділення і корінь, унизу a і п’ятірка. Генетичне програмування схрещує програми так, що одній відтинає гілку і на її місце прищеплює гілку з другої. Мутація замінює гілку новою, випадково вирощеною. Захищені операції гарантують, що жодна програма не впаде, хіба що видаватиме нісенітниці — а їх відбір відсіє.
Джон Коза зі Стенфордського університету описав метод 1992 року в книжці Genetic Programming і показав його на десятках задач, від керування роботом до пошуку формул.
Приклад — обчислення дерева (a ÷ 5) × √a для Землі
Від листків до кореня, a = 149,6 (мільйона км):
ліва гілка: a ÷ 5 = 149,6 / 5 = 29,92
права гілка: √a = √149,6 ≈ 12,231
корінь: 29,92 × 12,231 ≈ 365,95 доби (насправді 365,26, відхилення +0,19 %)
def gp(populace, generaci, p_krizeni=0.9, max_hloubka=7):
for _ in range(generaci):
dalsi = [nejlepsi(populace)] # елітизм: одна найкраща
while len(dalsi) < len(populace):
if random.random() < p_krizeni: # схрещування обміном піддерев
a, b = turnaj(populace), turnaj(populace)
dite = nahrad(a, nahodny_uzel(a), podstrom(b, nahodny_uzel(b)))
else: # мутація: гілка → нова випадкова
a = turnaj(populace)
dite = nahrad(a, nahodny_uzel(a), nahodny_strom(hloubka=2))
if hloubka(dite) <= max_hloubka: # надто глибоких дітей відкидаємо
dalsi.append(dite)
populace = dalsi
return nejlepsi(populace) # найменша похибка; за рівності менше дерево
6. Символьна регресія: закон Кеплера
Йоганн Кеплер десять років шукав зв’язок між відстанню планети від Сонця та її періодом обертання; третій закон він опублікував 1619 року. Дамо генетичному програмуванню ту саму задачу. У нього є вісім планет — велика піввісь a у мільйонах кілометрів і період обертання T у добах —, операції +, −, ×, ÷ і √, змінна a і цифри від 1 до 9. Похибка програми — це середнє відносне відхилення від справжніх періодів обертання. Популяція має 300 програм і 40 поколінь.
Означення — символьна регресія
Для даних (xi, yi) символьна регресія шукає вираз g із заданої граматики (функцій і терміналів), який мінімізує похибку ∑ ℓ(g(xi), yi). На відміну від звичайної регресії, форма моделі не задана наперед, лише будівельні цеглинки.
Лінійна регресія зі статті про статистику шукає найкращу пряму. Символьна регресія шукає не числа у відомій формулі, а саму формулу — і результат може прочитати людина.
Точніше, похибка програми g — це середнє абсолютних різниць логарифмів:
E(g) = (1/8) ∑i=18 | ln g(ai) − ln Ti |
- (1/8) ∑
- середнє за вісьмома планетами — похибка не залежить від того, скільки в нас даних, і її можна читати як «типове відхилення на планету»
- | … |
- абсолютне значення — передбачення на 1 % більше і на 1 % менше однаково погані, і похибки різних знаків не взаємознищуються
- ln g(ai) − ln Ti
- різниця логарифмів — це ln(g/T), тобто відносне відхилення: для малих відхилень ln(1 + r) ≈ r. Періоди обертання відрізняються майже втисячу разів (88 діб Меркурій, 60 190 Нептун); зі звичайною різницею про похибку вирішував би лише Нептун
Найкраща програма початкової, випадкової популяції відхиляється від справжніх періодів обертання в середньому на 32 %. У третьому поколінні вона вже має відхилення 8 % і правильну форму a1,5, лише зі сталою 0,22. У п’ятнадцятому поколінні з’являється ось що:
T = a · √a / 5 — тобто T пропорційне a3/2, або T2 пропорційне a3. Це третій закон Кеплера. Середнє відхилення становить 0,30 %, а стала 1/5 дуже близька до справжнього значення 0,1996 для цих одиниць. З двадцяти запусків із різними зернами показник 1,5 знайшли вісімнадцять.
Звідки 0,1996? З Ньютонового закону всесвітнього тяжіння для колової орбіти (і для еліпса з великою піввіссю a, коли маса планети порівняно з Сонцем нехтовно мала) випливає форма закону разом зі сталою:
T = 2π / √(GM☉) · a3/2
- T
- період обертання — час одного оберту
- 2π
- один оберт — це кут 2π; формула виникає з кутової швидкості ω як T = 2π/ω
- √(GM☉)
- сила тяжіння Сонця (G — гравітаційна стала, M☉ — маса Сонця) — сильніше притягання означає швидший оберт, тому вона в знаменнику
- a3/2
- відстань — тяжіння слабне з a2, а шлях довший пропорційно a; з рівності доцентрової і гравітаційної сил ω2a = GM☉/a2 виходить ω ∝ a−3/2
Приклад — стала в мільйонах км і добах
Гравітаційний параметр Сонця GM☉ = 1,327 124 · 1020 м3/с2 (номінальне значення Міжнародного астрономічного союзу, резолюція B3 2015 року). Одиниця довжини — 109 м, тож a3/2 у метрах у (109)3/2 = √(1027) разів більше:
√(1027 / 1,327 124 · 1020) = √(7,535 · 106) ≈ 2 745,0 с
2π · 2 745,0 с ≈ 17 247 с
17 247 с / 86 400 с/добу ≈ 0,1996 доби
Програма зі сталою 1/5 = 0,2 відрізняється, отже, на 0,19 % — рівно стільки, скільки вийшло відхилення для Землі вище.
7. Розбухання
Проте еволюція на гарній формулі не зупинилася. До покоління 40 вона «покращила» відхилення до 0,23 % ось цим:
Дерево має замість шести вузлів шістнадцять, і додані гілки не роблять нічого іншого, як підганяють сталу й обходять заокруглення в даних. Фізично це гірша формула — перенавчена на вісім планет. Середній розмір програми в популяції при цьому зріс із 9,8 вузла на початку до 23,8 у двадцятому поколінні.
Означення — розбухання і тиск економності
Розбухання (bloat) — це зростання розміру програм у популяції, якому не відповідає покращення їхньої пристосованості. Тиск економності (parsimony pressure) штрафує за розмір: або додає його до похибки, F′(g) = F(g) + c · |g|, або використовує як другий критерій за рівних похибок.
Програми обростають кодом, який нічого не робить або робить мізерну поправку — подібно до того, як у геномах живих організмів накопичуються ділянки без функції. Велике дерево до того ж важче зруйнувати схрещуванням, тож воно легше розмножується. Наш алгоритм за однакової похибки віддає перевагу меншому дереву, але коли більше дерево знизить похибку на волосинку, воно перемагає. Та сама дилема, що й з перенавчанням моделі: точність на даних чи простота, яка узагальнює?
F′(g) = F(g) + c · |g|
- F′(g)
- скоригована похибка, за якою відбирають — менша краща
- F(g)
- похибка програми на даних — і далі головне, що ми хочемо знизити
- c
- ціна одного вузла — каже, наскільки має покращитися похибка, щоб додатковий вузол окупився; її вибирають так, щоб вона вирішувала лише за мізерних покращень
- |g|
- розмір програми, кількість вузлів дерева — міра складності, яку можна порахувати, не розуміючи програми
Приклад — яке c врятувало б закон Кеплера
Дерево з покоління 15: похибка 0,2986 %, 6 вузлів. Дерево з покоління 40: похибка 0,2312 %, 16 вузлів. Менше дерево перемагає, коли
0,002 986 + 6c < 0,002 312 + 16c
10c > 0,000 674 ⇒ c > 6,7 · 10−5
Десять додаткових вузлів покращили похибку менш ніж на соту відсотка. З ціною понад 0,007 % за вузол дерево з покоління 40 програло б у відборі читабельному закону з покоління 15.
8. Еволюційні стратегії і програми сьогодні
- Еволюційні стратегії замість навчання з підкріпленням. OpenAI 2017 року показала, що проста еволюційна стратегія здатна навчати нейронні мережі керувати симульованими роботами і грати в ігри Atari приблизно так само добре, як навчання з підкріпленням, — і її можна розподілити на тисячі процесорів, які пересилають одне одному лише кілька чисел.
- Відкриття законів із даних. Шмідт і Ліпсон 2009 року доручили символьній регресії вивести закони руху і закони збереження з вимірювань маятників. Сучасні бібліотеки, як-от PySR, використовують у фізиці й астрономії, де читабельна формула цінніша за чорну скриньку.
- Еволюція алгоритмів машинного навчання. Проєкт AutoML-Zero (Google, 2020) дав еволюції складати алгоритми навчання з елементарних математичних операцій і так заново відкрив, наприклад, зворотне поширення помилки.
- Програми з мовними моделями. Системи FunSearch і AlphaEvolve з попередньої частини — це генетичне програмування, у якому мутацію замість випадкової гілки виконує мовна модель.
9. Підсумок
- Еволюційні стратегії мутують дійсні числа нормальним шумом із розміром кроку σ.
- Правило однієї п’ятої і самоадаптація налаштовують σ автоматично; CMA-ES вчиться ще й форми мутацій.
- На сферах досить однієї особини, на пересіченій місцевості допомагає популяція: на функції Растригіна 32 успіхи зі 100 проти одного.
- Генетичне програмування вирощує програми як дерева; символьна регресія знайшла в даних про планети третій закон Кеплера.
- Програми схильні розбухати й перенавчатися — проти цього допомагає тиск економності.
Хочете спробувати самі? Попросіть ШІ-агента написати символьну регресію і дайте їй власні дані — скажімо, період коливань маятника залежно від довжини нитки, — і подивіться, чи знайде вона √L.
Маєте іншу думку? Напишіть на support@coden-app.com.