Степени, экспонента и логарифмы
Степени и корни, научная запись чисел, число e и логарифм — и почему в ML повсюду логарифмы, от cross-entropy и perplexity до графиков scaling laws.
Зачем это нужно
Заголовок раздела «Зачем это нужно»Числа в ML живут в огромном диапазоне. У DeepSeek-V3 671 млрд параметров, типичный learning rate — 0.0003, вероятность редкого токена — 0.00001. А вероятность целого абзаца текста — число с сотнями нулей после запятой. Такие числа неудобно писать и сравнивать, а компьютер может их попросту «потерять».
Степени, научная запись и логарифмы делают огромные и крошечные числа удобными. Логарифм вдобавок сидит в функциях потерь и метриках языковых моделей: cross-entropy, log-softmax, KL-дивергенция, perplexity. После этого урока вы будете понимать, зачем он там нужен.
Степени
Заголовок раздела «Степени»СтепеньстепеньEN power (exponentiation)aⁿ — a, умноженное само на себя n раз. В Python — a ** n (не a ^ n, это XOR!). — это , умноженное само на себя раз: .
Число называют основанием, — показателем. В Python степень пишется двумя звёздочками: 2 ** 10.
Все свойства степеней следуют из определения «умножить раз»:
| Правило | Формула | Пример |
|---|---|---|
| Умножение | ||
| Степень степени | ||
| Нулевая степень | ||
| Отрицательная степень | ||
| Дробная степень | , |
Первое правило легко увидеть: — всего семь двоек. Проверим остальные в Python:
print(2 ** 3 * 2 ** 4, 2 ** 7) # 128 128print((2 ** 3) ** 2, 2 ** 6) # 64 64print(2 ** -3) # 0.125print(9 ** 0.5, 8 ** (1 / 3)) # 3.0 2.0Степени двойки в мире LLM
Заголовок раздела «Степени двойки в мире LLM»Компьютер хранит всё в битах, а битов дают разных значений: 8 битов (один байт) — 256 значений,
16 битов — 65 536. Отсюда практический вывод. В nanoGPT номера токенов GPT-2 хранят в 16-битном типе uint16,
потому что словарь GPT-2 (50 257 токенов) туда помещается. У Qwen3 в словаре 151 936 позиций — больше, чем
. Значит, на номер токена нужно 18 битов, uint16 уже не подходит, и приходится брать
32-битный uint32 — вдвое больше памяти на каждый токен датасета.
Длины контекста тоже обычно степени двойки: , . Контекст «128K» — это токена.
Квадратный кореньквадратный кореньEN square root√a = a^(1/2). В ML — √d_k в attention, стандартное отклонение, RMSNorm. — число, которое в квадрате даёт : ,
. На языке степеней . В Python пишут math.sqrt(9) или 9 ** 0.5.
Корни постоянно встречаются в формулах LLM:
- в attention скалярные произведения делят на : при это (урок 4.4.2);
- стандартное отклонение — это корень из дисперсии (урок 1.0.3);
- RMSNorm в Llama, Qwen и DeepSeek делит вектор на корень из среднего квадрата его элементов (урок 4.5.3);
- Adam делит шаг на корень из скользящего среднего квадратов градиента (урок 1.4.2).
Попробуйте вычислить (-8) ** (1 / 3). Вместо ожидаемого Python вернёт комплексное число
(1.0000000000000002+1.7320508075688772j). Дробные степени отрицательных чисел лучше обходить стороной.
Научная запись
Заголовок раздела «Научная запись»Число удобнее записать как . Это научная записьнаучная (экспоненциальная) записьEN scientific notation3e-4 = 3·10⁻⁴ = 0.0003; 6.71e11 = 671 млрд.:
мантисса от 1 до 10, умноженная на 10 в нужной степени. Отрицательная степень означает маленькое число:
. В Python и в статьях то же самое пишут через букву e, и эта буква значит «умножить
на 10 в степени», а не число :
| Запись | В Python | Обычное число | Где встречается |
|---|---|---|---|
3e-4 | 0.0003 | типичный learning rate для Adam | |
1e-8 | 0.00000001 | в Adam — защита от деления на ноль | |
6.022e23 | 602 200 000 000 000 000 000 000 | число Авогадро | |
6.71e11 | 671 000 000 000 | параметры DeepSeek-V3 | |
1.48e13 | 14 800 000 000 000 | токены обучения DeepSeek-V3 |
Python сам переходит на научную запись для маленьких чисел: print(0.00001) выведет 1e-05. Чтобы задать формат
явно, используйте f-строку: f"{123456789:.2e}" даёт '1.23e+08'.
Умножение в научной записи — мантиссы перемножаем, показатели складываем (правило ). Оценим, сколько операций ушло на обучение DeepSeek-V3. Есть грубое правило: на каждый параметр и каждый токен приходится около 6 операций — 2 на прямой проход (модель считает ответ) и 4 на обратный (считает поправки к весам, модуль 3.1). У DeepSeek-V3 на каждый токен работают активных параметров, а токенов в предобучении было :
N = 37e9 # активных параметров на токенD = 14.8e12 # токенов в обученииC = 6 * N * D # правило «6ND»: грубая оценка числа операцийprint(f"{C:.2e}") # 3.29e+24
gpu_seconds = 2.664e6 * 3600 # 2.664 млн GPU-часов предобучения из техотчёта → секундыprint(f"{C / gpu_seconds:.2e}") # 3.43e+14 операций в секунду на один GPUВыходит около операций в секунду на каждый ускоритель NVIDIA H800, на которых обучали модель. Пик такой карты на обычных для обучения 16-битных числах — около операций в секунду, так что карты работали примерно на треть от пика (а большая часть умножений у DeepSeek-V3 шла в 8-битном формате FP8, где пик вдвое выше, — так что реальная загрузка ещё ниже). Для большого обучения это нормально, и цифры отчёта сходятся с прикидкой (подробно — в уроке 5.1.1). Такие расчёты «на салфетке» — обычное дело для LLM-инженера, и научная запись делает их быстрыми.
Число e и экспонента
Заголовок раздела «Число e и экспонента»Откуда берётся странное число ? Представьте банк, который даёт 100% годовых. Если проценты начисляют раз в год, рубль превращается в 2 рубля. Если дважды по 50%, получится . Если ежемесячно по , то . Чем чаще начисление, тем больше итог, но рост упирается в предел:
for n in [1, 2, 12, 365, 1_000_000]: print(n, (1 + 1 / n) ** n)1 2.02 2.2512 2.613035290224676365 2.71456748202197271000000 2.7182804690957534Этот предел и есть число число e (число Эйлера)EN Euler's number ee ≈ 2.71828 — предел (1 + 1/n)ⁿ. Основание экспоненты и натурального логарифма. В Python оно хранится в math.e.
Функция — экспонентаэкспонента (показательная функция)EN exponential functioneˣ — всегда положительна, растёт взрывообразно. Внутри softmax и сигмоиды. из прошлого урока, в коде math.exp(x) или np.exp(x).
Для неё работают все правила степеней: , , .
Почему в ML основание именно , а не 2 или 10? Скорость роста в каждой точке равна её значению. Иначе говоря, производная равна самой (модуль 1.2), и с таким основанием формулы градиентов получаются самыми простыми.
Как быстро растёт экспонента: , , .
Самое большое число в float64 — около , поэтому math.exp(709) ещё работает, а math.exp(710)
бросает OverflowError. В 16-битном формате FP16 максимум всего 65 504, и экспонента переполняется уже при
. Это одна из причин, почему softmax в обучении с пониженной точностью считают так осторожно
(урок 1.3.2 и модуль 5.4).
Логарифм — обратная операция
Заголовок раздела «Логарифм — обратная операция»Степень отвечает на вопрос «чему равно ?» (ответ: 8). ЛогарифмлогарифмEN logarithmlog_b x — в какую степень возвести b, чтобы получить x. Превращает произведение в сумму. отвечает на обратный вопрос: в какую степень нужно возвести 2, чтобы получить 8? Ответ — 3, и это записывают как .
Несколько примеров: , (столько нулей), , а при любом основании, потому что .
Логарифм по основанию 2 — это «сколько раз удвоить». Лист бумаги толщиной 0.1 мм после 10 сгибаний станет толщиной мм. Сколько раз нужно мысленно сложить лист, чтобы его толщина превысила расстояние до Луны? До Луны 384 400 км, или мм, то есть толщин листа. Ответ: , то есть 42 сгибания. Логарифм превращает «астрономическое» число в обозримое.
Графики степени и логарифма зеркальны относительно прямой : если точка лежит на , то точка лежит на .
Из графика видно главное про логарифм. Он определён только для положительных . Он растёт, но очень медленно. А для чисел меньше 1 логарифм отрицателен. Последнее важно: вероятности лежат между 0 и 1, поэтому их логарифмы всегда отрицательны или равны нулю.
Натуральный логарифм
Заголовок раздела «Натуральный логарифм»Натуральный логарифмнатуральный логарифмEN natural logarithmln x = log_e x. В статьях по ML «log» без основания почти всегда означает ln; в Python — math.log. — это логарифм по основанию . Полезно помнить несколько значений:
| 0.01 | 0.1 | 0.5 | 1 | 2 | 10 | ||
|---|---|---|---|---|---|---|---|
| −4.605 | −2.303 | −0.693 | 0 | 0.693 | 1 | 2.303 |
Важно: в статьях по ML и в библиотеках «log» без основания почти всегда означает . Это относится к
math.log, np.log и torch.log.
import mathprint(math.log(8, 2), math.log2(8), math.log10(1000)) # 3.0 3.0 3.0print(math.log(math.e), math.log(1)) # 1.0 0.0print(math.log(0.5)) # -0.6931471805599453math.log(0) # ValueError: math domain errorNumPy на np.log(0.0) не падает, а возвращает -inf и печатает предупреждение
RuntimeWarning: divide by zero encountered in log. Такой -inf потом тихо превращает loss в inf,
а следующие вычисления — в nan («не число»).
Свойства логарифма
Заголовок раздела «Свойства логарифма»| Свойство | Формула | Проверка числом |
|---|---|---|
| Произведение → сумма | ||
| Частное → разность | ||
| Степень → множитель | ||
| Смена основания |
Первое свойство — сердце этого урока. Оно следует из правила : при умножении чисел их показатели складываются. Проверим в Python:
import mathprint(math.log(0.5 * 0.1), math.log(0.5) + math.log(0.1)) # -2.995732273553991 дваждыprint(math.log(1000) / math.log(10)) # 2.9999999999999996 (!)print(math.isclose(math.log(1000) / math.log(10), 3)) # TrueВторая строка — напоминание из урока 0.2.1: дробные числа сравнивают через math.isclose, а не через ==.
Почему в ML повсюду логарифмы
Заголовок раздела «Почему в ML повсюду логарифмы»1. Произведение вероятностей превращается в сумму
Заголовок раздела «1. Произведение вероятностей превращается в сумму»Языковая модель оценивает вероятность текста как произведение вероятностей его токенов (урок 4.1.1). Возьмём 1000 токенов с вероятностью 0.01 у каждого. Их произведение равно , а самое маленькое положительное число в float64 — около . Произведение превращается в 0.0. Это называют машинным нулём (underflow). Логарифм спасает:
import mathprobs = [0.01] * 1000
p = 1.0for q in probs: p *= qprint(p) # 0.0 — информация потеряна
print(sum(math.log(q) for q in probs)) # -4605.17018598815 — обычное число2. Функция потерь — это минус логарифм
Заголовок раздела «2. Функция потерь — это минус логарифм»Cross-entropyперекрёстная энтропияEN cross-entropy для одного токена равна , где — вероятность, которую модель дала правильному токену (урок 1.3.2). Логарифм здесь полезен дважды. Во-первых, loss всего текста становится суммой (или средним) loss отдельных токенов. Во-вторых, логарифм больно наказывает за уверенные ошибки:
| Вероятность правильного токена | 0.9 | 0.5 | 0.1 | 0.01 |
|---|---|---|---|---|
| Loss | 0.105 | 0.693 | 2.303 | 4.605 |
3. Perplexity — это экспонента от среднего loss
Заголовок раздела «3. Perplexity — это экспонента от среднего loss»Средний по всем токенам — это и есть cross-entropy текста. Если взять от неё экспоненту, получится perplexityперплексияEN perplexity (перплексия). Экспонента и логарифм отменяют друг друга: . Поэтому если модель каждому правильному токену давала вероятность , то loss равен , а perplexity — ровно 4. Модель «колеблется между четырьмя равновероятными вариантами».
import mathprobs = [0.5, 0.1, 0.9, 0.3] # вероятности правильных токеновavg_nll = -sum(math.log(p) for p in probs) / len(probs)print(round(avg_nll, 3), round(math.exp(avg_nll), 3)) # 1.076 2.9344. Биты информации
Заголовок раздела «4. Биты информации»Логарифм по основанию 2 измеряет информацию в битах. Чтобы угадать один вариант из 8 равновероятных, нужно вопроса «да/нет». На этом построена энтропия (урок 1.3.3).
Логарифмическая шкала
Заголовок раздела «Логарифмическая шкала»На обычной шкале равные отрезки означают «прибавить одно и то же». На логарифмической шкалелогарифмическая шкалаEN logarithmic scaleРавные отрезки означают умножение (×10), а не прибавление. Степенные законы на ней — прямые линии. равные отрезки означают «умножить на одно и то же», например на 10. Вот размеры моделей семейства Qwen2.5 на обеих шкалах:
Логарифмическая шкала нужна, когда значения отличаются в разы, а не на единицы:
- Размеры моделей и бюджеты вычислений. Учебная модель на 100 млн параметров, обученная на 2 млрд токенов, обходится примерно в операций, DeepSeek-V3 — в , в несколько миллионов раз больше.
- Сетки learning rate. Перебирают
1e-5, 3e-5, 1e-4, 3e-4, 1e-3: каждый шаг — умножение примерно на 3, два шага — на 10. На лог-шкале точки стоят равномерно. - Кривые обучения. Ось шагов часто логарифмическая, чтобы были видны и первые сотни шагов, и последние сотни тысяч.
Степенной закон на лог-шкале — прямая. Возьмём и посчитаем десятичные логарифмы:
| 1 | 10 | 100 | 1000 | 10 000 | |
|---|---|---|---|---|---|
| 10 | 3.162 | 1 | 0.316 | 0.1 | |
| 1 | 0.5 | 0 | −0.5 | −1 |
Каждое умножение на 10 уменьшает ровно на 0.5. По свойству «степень → множитель»
, а это уравнение прямой с наклоном . Именно так выглядят
scaling laws — законы масштабирования LLM. В статье Kaplan et al. (2020) зависимость loss от вычислений,
данных и числа параметров в лог-лог координатах — почти прямые линии. В matplotlib такой график строится
одной командой plt.loglog(x, y), а для одной оси — plt.yscale("log").
Практика
Заголовок раздела «Практика»Четыре функции: «сколько раз удвоить», логарифм по любому основанию, логарифм вероятности длинной последовательности и perplexity. Два последних теста проверят, что вы не попались в ловушку машинного нуля.
import math
def doublings(start, target):
"""Сколько раз нужно удвоить start, чтобы получилось не меньше target.
doublings(1, 1000) → 10 (2**10 = 1024 ≥ 1000, а 2**9 = 512 ещё мало)
doublings(5, 3) → 0 (уже достаточно)
Можно циклом while, а можно через math.log2 и math.ceil.
"""
# TODO
...
def log_base(x, base):
"""Логарифм x по основанию base: в какую степень возвести base, чтобы получить x.
log_base(8, 2) → 3.0, log_base(1000, 10) → 3.0
Подсказка: формула перехода log_b(x) = ln(x) / ln(b).
"""
# TODO
...
def log_likelihood(probs):
"""Логарифм вероятности последовательности: ln(p1 · p2 · … · pn).
Перемножать 1000 маленьких вероятностей нельзя — произведение станет 0.0!
Используйте свойство: ln(a · b) = ln(a) + ln(b).
log_likelihood([0.5, 0.5]) → −1.386…
"""
# TODO
...
def perplexity(probs):
"""Perplexity = e^(средний −ln p) — «из скольких равных вариантов выбирала модель».
perplexity([0.25, 0.25, 0.25]) → 4.0
"""
# TODO: средний отрицательный логарифм, потом math.exp
...
print(doublings(1, 1000))
print(log_base(8, 2))
print(log_likelihood([0.5, 0.5]))
print(perplexity([0.25, 0.25, 0.25]))
Проверь себя
Заголовок раздела «Проверь себя»- Степень — повторное умножение; при умножении степеней показатели складываются. В Python степень —
**, а не^. - Научная запись (
3e-4,6.71e11) делает огромные и крошечные числа удобными, а прикидки «на салфетке» — быстрыми. - Логарифм — обратная к степени операция; в ML «log» означает . Главное свойство: .
- Поэтому вероятности складывают в логарифмах (нет машинного нуля), loss — это , а perplexity .
- На логарифмической шкале шаг — умножение, а степенные законы (scaling laws) выглядят прямыми.
Дальше: как читать формулы — греческие буквы, индексы, знаки и и разбор настоящих формул из статей, от softmax до attention.