Композиция функций 🔗
Возьми любую современную нейросеть — GPT, ResNet, что угодно — и сотри с неё всю терминологию: «слои», «активации», «эмбеддинги», «головы внимания». Что останется? Останется одна-единственная математическая конструкция: функция, подставленная в функцию, подставленную в функцию, и так семьдесят или сто раз подряд. Формально сеть с $L$ слоями — это $f_L(f_{L-1}(\dots f_2(f_1(x))\dots))$. Всё. Никакой магии, просто очень длинная композиция функций.
То же самое происходит и в куда более приземлённых местах. Ты пишешь пайплайн предобработки данных: сначала логарифмируешь цену, потом вычитаешь среднее и делишь на стандартное отклонение, потом обрезаешь выбросы. Три функции, применённые по очереди к одному числу — это композиция. Ты берёшь sklearn.pipeline.Pipeline([...]) — это буквально объект «композиция функций», просто с удобным интерфейсом. Ты нажимаешь на фотографии три фильтра подряд — это композиция. Ты переводишь рубли в доллары, а доллары в юани — композиция.
И вот тут возникает первый неочевидный вопрос, на котором спотыкаются очень многие: а важен ли порядок? Если сначала нормализовать, а потом применить ReLU — получится то же самое, что сначала ReLU, а потом нормализовать? Нет. Категорически нет, и разница между этими двумя пайплайнами может стоить тебе нескольких процентов метрики. Композиция — операция некоммутативная, и это не занудная формальность, а одна из главных практических истин про обработку данных.
В этом уроке мы разберём композицию по-настоящему: что это такое, как её вычислять и как раскладывать сложную функцию обратно на звенья, откуда берётся область определения цепочки (и почему она почти всегда уже, чем кажется по итоговой формуле), почему порядок нельзя менять, как композиция связана с обратной функцией и — под конец — почему без нелинейности между слоями глубокая сеть схлопывается в один линейный слой. Плюс правило дифференцирования сложной функции: тот самый chain rule, на котором стоит вообще всё обучение нейросетей.
🎯 Ты узнаешь:
- Что такое композиция $(g \circ f)(x) = g(f(x))$ и как её вычислять без путаницы в порядке
- Почему область определения композиции определяется не итоговой формулой, а всей цепочкой целиком
- Почему $g \circ f \neq f \circ g$ почти всегда — и что это значит для порядка шагов в ML-пайплайне
- Как разложить сложную функцию на простые звенья (навык, без которого не берётся ни одна производная)
- Как нейросеть оказывается композицией слоёв, почему без активации она вырождается в линейную и откуда берётся backpropagation
История: откуда это взялось?
Идея «функция от функции» появилась раньше, чем сам термин «функция» устоялся. Готфрид Вильгельм Лейбниц, который в 1690-х годах и ввёл в оборот слово functio, уже работал с величинами, зависящими от других зависимых величин: скорость зависит от времени, ускорение — от скорости, сила — от ускорения. Когда в 1676 году он выписал правило дифференцирования сложной функции — то самое $\frac{dy}{dx} = \frac{dy}{du} \cdot \frac{du}{dx}$ — он фактически задал вопрос, на который отвечает вся эта тема: если $y$ зависит от $u$, а $u$ зависит от $x$, то как $y$ зависит от $x$? Удобная лейбницевская запись дробями была не случайностью, а инженерным ходом: она делает цепное правило визуально очевидным, «сокращая» $du$.
Строгий язык появился позже. Леонард Эйлер в «Introductio in analysin infinitorum» (1748) систематически называл такие конструкции функциями составными (сложными), а сам символ $\circ$ для композиции вошёл в обиход только в конце XIX — начале XX века, когда математики (в первую очередь в теории групп и в зарождающейся теории множеств) начали смотреть на функции не как на формулы, а как на отображения — стрелки между множествами. С этой точки зрения композиция — это просто «пройти по двум стрелкам подряд», и вопрос «а можно ли пройти?» превращается в конкретное условие: конец первой стрелки должен попадать туда, где определена вторая. Эта, казалось бы, формальная оговорка и есть вся история про область определения композиции, которую мы разберём ниже.
Мостик в сегодня оказался неожиданно прямым. В 1986 году Румельхарт, Хинтон и Уильямс опубликовали работу про обучение многослойных сетей — и её математическое ядро было ровно лейбницевским цепным правилом, применённым к очень длинной композиции. Алгоритм назвали backpropagation: он проходит композицию слоёв справа налево, перемножая производные звеньев. Сегодня каждый вызов loss.backward() в PyTorch — это автоматическое применение правила, которому 350 лет. Причём современные фреймворки хранят модель именно так, как её увидел бы Эйлер: как граф композиции, где узлы — простые функции, а рёбра — «выход одного подаётся на вход другому».
Что такое композиция: конвейер из функций
Интуиция
Представь заводской конвейер. На первой станции берут заготовку $x$ и превращают её в деталь $f(x)$. Готовую деталь передают на вторую станцию, где из неё делают изделие $g(f(x))$. Снаружи весь цех выглядит как одна большая машина: сунул заготовку — получил изделие. Вот эта «одна большая машина», собранная из двух станций, и есть композиция.
Ключевое слово здесь — подстановка. Композиция не складывает и не умножает функции. Она подставляет результат одной на вход другой. Если $f$ — это «прибавь 3», а $g$ — «умножь на 2», то $g(f(x))$ означает: сначала прибавили 3, потом получившееся умножили на 2. Для $x = 5$: $f(5) = 8$, затем $g(8) = 16$. Не $5 \cdot 2 + 3$, не $(5+3) \cdot (5 \cdot 2)$ — а именно последовательное прохождение по цепочке.
Давай разберёмся с обозначением, потому что оно сбивает с толку буквально всех. Запись $g \circ f$ читается «$g$ после $f$» и означает $g(f(x))$ — то есть первой работает та функция, которая написана справа. Это выглядит задом наперёд, но логика простая: в записи $g(f(x))$ мы читаем изнутри наружу, от самой внутренней скобки к внешней. Аргумент $x$ сначала попадает к ближайшей к нему функции $f$, а $g$ ждёт снаружи. Символ $\circ$ просто сохраняет тот же порядок букв, что и в записи со скобками.
Определение: Пусть даны функции $f$ и $g$. Композицией функций $g$ и $f$ называется функция $(g \circ f)(x) = g(f(x))$, значение которой в точке $x$ получается подстановкой значения $f(x)$ в функцию $g$. Функция $f$ называется внутренней, функция $g$ — внешней.
Обозначения:
- $g \circ f$ — композиция, читается «$g$ после $f$» или «$g$ композиция $f$»
- $(g \circ f)(x) = g(f(x))$ — развёрнутая запись
- $f$ — внутренняя (первая по времени) функция, $g$ — внешняя (вторая по времени)
- $f \circ f = f^2$ в смысле композиции — осторожно, это не $(f(x))^2$; такую запись лучше избегать или явно оговаривать
Примеры с разбором
Пример 1 (простой): $f(x) = x + 2$, $g(x) = 3x$. Найди $(g \circ f)(4)$
Решение:
Шаг 1. Определим порядок. В записи $g \circ f$ первой работает $f$ (она внутренняя).
Шаг 2. Вычислим внутреннюю функцию: $f(4) = 4 + 2 = 6$.
Шаг 3. Подставим результат во внешнюю: $g(6) = 3 \cdot 6 = 18$.
Ответ: $(g \circ f)(4) = 18$
📌 Проверим наш ответ по формуле: $(g \circ f)(x) = g(x+2) = 3(x+2) = 3x + 6$. При $x = 4$: $3 \cdot 4 + 6 = 18$ ✅
Пример 2 (средний): $f(x) = x^2 - 1$, $g(x) = 2x + 3$. Найди формулу $(f \circ g)(x)$
Решение:
Шаг 1. Разберёмся с порядком: в $f \circ g$ внутренняя — это $g$, внешняя — $f$. Значит нам нужно $f(g(x))$.
Шаг 2. Возьмём формулу внешней функции $f(t) = t^2 - 1$ и подставим вместо $t$ всё выражение $g(x) = 2x+3$:
$$f(g(x)) = (2x+3)^2 - 1$$Шаг 3. Раскроем скобки:
$$(2x+3)^2 - 1 = 4x^2 + 12x + 9 - 1 = 4x^2 + 12x + 8$$Проверим наш ответ в точке $x = 1$. По цепочке: $g(1) = 5$, затем $f(5) = 25 - 1 = 24$. По формуле: $4 + 12 + 8 = 24$ ✅
Ответ: $(f \circ g)(x) = 4x^2 + 12x + 8$
📌 Главный технический приём: переименуй аргумент внешней функции в нейтральную букву ($t$, $u$), чтобы не путаться. Формула $f(t) = t^2 - 1$ означает «возьми что дали, возведи в квадрат, вычти единицу» — и неважно, что именно дали: число, буква $x$ или целое выражение $2x+3$.
Пример 3 (сложный): пайплайн из трёх звеньев
Данные о доходах клиентов проходят три шага: стандартизация $n(x) = \dfrac{x - 100}{20}$, затем ReLU $r(t) = \max(0, t)$, затем линейный слой $s(u) = 3u + 1$. Найди значение всей цепочки $(s \circ r \circ n)$ для $x = 160$ и для $x = 60$.
Решение:
Шаг 1. Разберёмся с порядком. Тройная композиция $s \circ r \circ n$ читается справа налево: сначала $n$, потом $r$, потом $s$. Развёрнуто: $s(r(n(x)))$.
Шаг 2. Считаем для $x = 160$:
$$n(160) = \frac{160 - 100}{20} = \frac{60}{20} = 3$$$$r(3) = \max(0, 3) = 3$$$$s(3) = 3 \cdot 3 + 1 = 10$$Шаг 3. Считаем для $x = 60$:
$$n(60) = \frac{60 - 100}{20} = \frac{-40}{20} = -2$$$$r(-2) = \max(0, -2) = 0$$$$s(0) = 3 \cdot 0 + 1 = 1$$Ответ: $(s \circ r \circ n)(160) = 10$, $(s \circ r \circ n)(60) = 1$
Вывод: обрати внимание, что произошло с $x = 60$. Он был ниже среднего, стандартизация дала минус, ReLU обнулила отрицательное значение — и вся информация о том, насколько именно клиент ниже среднего, потерялась. Клиент с доходом 60 и клиент с доходом 20 после этой цепочки дадут одинаковый выход. Это и есть та самая «нелинейность», которая одновременно даёт сети выразительность и приводит к проблеме «мёртвых ReLU-нейронов»: если нейрон постоянно получает отрицательный вход, он всегда выдаёт ноль и перестаёт учиться.
Почему это важно
Композиция — это то, как вообще устроен любой конвейер обработки данных. Когда ты пишешь Pipeline([('scaler', StandardScaler()), ('model', LogisticRegression())]), ты буквально строишь $(\text{model} \circ \text{scaler})$. Когда данные текут через слои трансформера, каждый следующий блок — внешняя функция для всего, что было раньше. И самое практичное следствие: раз это композиция, то весь пайплайн можно рассматривать как одну функцию от сырого входа до предсказания — а значит, можно спрашивать про неё то же, что про любую функцию: какова её область определения (какие входы вообще допустимы?), монотонна ли она, дифференцируема ли. Именно поэтому в проде так важно, чтобы предобработка была частью модели, а не отдельным скриптом: если ты обучил модель на композиции «нормализация → сеть», а в бою подаёшь сырые данные прямо в сеть, ты вычисляешь совсем другую функцию.
Порядок звеньев: почему $g \circ f \neq f \circ g$
Интуиция
Представь два действия: «надеть носки» и «надеть ботинки». Результат «носки, потом ботинки» и «ботинки, потом носки» — это, мягко говоря, разные результаты. Композиция функций устроена ровно так же: перестановка звеньев меняет итоговую функцию.
Более математический пример: пусть $f$ — «прибавь 1», $g$ — «возведи в квадрат». Тогда $g(f(3)) = g(4) = 16$, а $f(g(3)) = f(9) = 10$. Одни и те же две операции, одно и то же число на входе — разные ответы.
В ML это встречается на каждом шагу и стоит реальных денег. Логарифмирование перед стандартизацией и стандартизация перед логарифмированием — не одно и то же (второе вообще сломается: после стандартизации появятся отрицательные значения, а логарифм от них не определён). Аугментация до нормализации и после — разные распределения. Dropout перед батч-нормализацией и после — известный источник расхождения между train и inference.
Свойство (некоммутативность): В общем случае $g \circ f \neq f \circ g$. Равенство $g(f(x)) = f(g(x))$ для всех допустимых $x$ — редкое частное свойство конкретной пары функций, а не общее правило.
Свойство (ассоциативность): Зато композиция всегда ассоциативна: $(h \circ g) \circ f = h \circ (g \circ f)$. Порядок звеньев менять нельзя, а вот группировать их скобками можно как угодно.
Ассоциативность — не пустая формальность, а фундамент модульности: именно она позволяет «склеить» первые три слоя сети в один блок, назвать его энкодером и дальше работать с ним как с одной функцией. Результат от этого не изменится.
Примеры с разбором
Пример 1 (простой): $f(x) = x^2$, $g(x) = x + 1$. Сравни $(f \circ g)(2)$ и $(g \circ f)(2)$
Решение:
Шаг 1. $(f \circ g)(2) = f(g(2)) = f(3) = 9$.
Шаг 2. $(g \circ f)(2) = g(f(2)) = g(4) = 5$.
Ответ: $9 \neq 5$, композиции различны.
Пример 2 (средний): найди все $x$, при которых $f(g(x)) = g(f(x))$, если $f(x) = x + 2$, $g(x) = 3x$
Решение:
Шаг 1. Найдём первую композицию: $f(g(x)) = f(3x) = 3x + 2$.
Шаг 2. Найдём вторую: $g(f(x)) = g(x+2) = 3(x+2) = 3x + 6$.
Шаг 3. Приравняем:
$$3x + 2 = 3x + 6$$Шаг 4. Вычтем $3x$ из обеих частей:
$$2 = 6$$Это неверное числовое равенство — значит, уравнение не имеет решений ни при каком $x$.
Ответ: таких $x$ нет; композиции не совпадают ни в одной точке.
📌 Практический перевод: «сначала растянуть, потом сдвинуть» и «сначала сдвинуть, потом растянуть» дают разный результат всегда, а не иногда. Запомни это к следующему уроку про преобразования графиков — там это будет главным источником ошибок.
Пример 3 (сложный): когда порядок всё-таки не важен?
Докажи, что для любых линейных функций без сдвига $f(x) = ax$ и $g(x) = bx$ композиция коммутативна.
Решение:
Шаг 1. Вычислим $f(g(x))$: подставим $g(x) = bx$ в $f(t) = at$:
$$f(g(x)) = a \cdot (bx) = abx$$Шаг 2. Вычислим $g(f(x))$: подставим $f(x) = ax$ в $g(t) = bt$:
$$g(f(x)) = b \cdot (ax) = bax$$Шаг 3. Поскольку умножение чисел коммутативно ($ab = ba$), получаем $abx = bax$ для любого $x$.
Ответ: $f \circ g = g \circ f$, композиция коммутативна.
Проверим наш ответ на конкретных числах: $a = 3$, $b = 5$, $x = 2$. Тогда $f(g(2)) = f(10) = 30$ и $g(f(2)) = g(6) = 30$ ✅
Важное продолжение: а вот стоит добавить сдвиг — и коммутативность ломается. Для $f(x) = ax$ и $g(x) = x + c$: $f(g(x)) = a(x+c) = ax + ac$, а $g(f(x)) = ax + c$. Они совпадают, только если $ac = c$, то есть при $c = 0$ или $a = 1$. В линейной алгебре это ровно тот же факт, что масштабирующие матрицы коммутируют между собой, а вот масштабирование и сдвиг — нет. Отсюда и вечная путаница с порядком аффинных преобразований в графике и в компьютерном зрении.
Почему это важно
Некоммутативность — это не абстрактная вредность математики, а то, из-за чего в реальных проектах появляются баги, которые не падают с ошибкой, а просто тихо ухудшают метрику. Классика: обучили StandardScaler на всём датасете, а потом разбили на train/test — вместо того чтобы сначала разбить, а потом фитить скейлер только на train. Формально это перестановка двух шагов конвейера, а по сути — утечка данных (data leakage), которая красиво завышает валидацию и разваливается в проде. Или: применили train_test_split после oversampling — и в тесте оказались копии тех же объектов, что в трейне. Каждый раз, когда ты меняешь местами два шага пайплайна, ты меняешь функцию целиком, и в отличие от опечатки в коде интерпретатор об этом не предупредит.
Область определения композиции: где цепочка рвётся
Интуиция
Представь, что ты собрал конвейер из двух станций. Первая принимает любые заготовки, а вторая работает только с металлическими. Если первая станция выдаст на выход пластиковую деталь — конвейер встанет. То есть допустимость входа $x$ определяется не только тем, что его примет первая станция, но и тем, что результат первой станции окажется приемлемым для второй.
Это самый скользкий момент во всей теме, поэтому сформулируем железное правило: чтобы $x$ попал в область определения $(g \circ f)$, нужно два условия сразу. Во-первых, $x$ должен принадлежать $D(f)$ — иначе первая станция вообще не запустится. Во-вторых, значение $f(x)$ должно принадлежать $D(g)$ — иначе не запустится вторая.
И вот главная ловушка: нельзя определять область определения по итоговой упрощённой формуле. После упрощения формула часто «забывает» ограничения промежуточных шагов. Например, $f(t) = t^2$ и $g(x) = \sqrt{x}$ дают $(f \circ g)(x) = (\sqrt{x})^2 = x$. Итоговая формула — просто $x$, она определена на всей прямой. Но исходная цепочка требует, чтобы под корнем было неотрицательное число, значит $D(f \circ g) = [0; +\infty)$. Формула соврала — цепочка нет.
Определение: Областью определения композиции $g \circ f$ называется множество
$$D(g \circ f) = \{x \in D(f) \; : \; f(x) \in D(g)\}$$то есть множество тех $x$, которые допустимы для внутренней функции и результат которых допустим для внешней.
Примеры с разбором
Пример 1 (простой): найди $D(f \circ g)$, если $f(x) = \sqrt{x}$, $g(x) = x - 5$
Решение:
Шаг 1. Внутренняя функция $g(x) = x - 5$ определена при всех $x$, первое условие ограничений не даёт.
Шаг 2. Внешняя функция $f(t) = \sqrt{t}$ определена при $t \geq 0$. Значит нужно $g(x) \geq 0$:
$$x - 5 \geq 0 \implies x \geq 5$$Ответ: $D(f \circ g) = [5; +\infty)$
Пример 2 (средний): найди $D(f \circ g)$, если $f(x) = \dfrac{1}{x}$, $g(x) = x^2 - 4$
Решение:
Шаг 1. $g$ определена везде — ограничений нет.
Шаг 2. $f(t) = \dfrac{1}{t}$ требует $t \neq 0$, значит нужно $g(x) \neq 0$:
$$x^2 - 4 \neq 0 \implies x^2 \neq 4 \implies x \neq 2 \text{ и } x \neq -2$$Шаг 3. Итоговая формула композиции: $(f \circ g)(x) = \dfrac{1}{x^2 - 4}$ — и здесь, к счастью, формула сама подсказывает то же ограничение.
Ответ: $D(f \circ g) = (-\infty; -2) \cup (-2; 2) \cup (2; +\infty)$
Пример 3 (сложный): найди $D(f \circ g)$, если $f(x) = \sqrt{x}$, $g(x) = \dfrac{x - 1}{x + 2}$
Решение:
Шаг 1. Первое условие: $x \in D(g)$. Дробь требует $x + 2 \neq 0$, то есть $x \neq -2$.
Шаг 2. Второе условие: $g(x) \in D(f)$, то есть $\dfrac{x-1}{x+2} \geq 0$.
Шаг 3. Решим неравенство методом интервалов. Нули числителя и знаменателя: $x = 1$ и $x = -2$. Они разбивают прямую на три промежутка, проверим знак дроби пробными точками:
- $x = -3$: $\dfrac{-4}{-1} = 4 > 0$ — подходит
- $x = 0$: $\dfrac{-1}{2} = -0{,}5 < 0$ — не подходит
- $x = 2$: $\dfrac{1}{4} = 0{,}25 > 0$ — подходит
Шаг 4. В точке $x = 1$ дробь равна нулю — это допустимо, поскольку $\sqrt{0} = 0$ существует, значит $x=1$ включаем. В точке $x = -2$ дробь не определена — исключаем.
Ответ: $D(f \circ g) = (-\infty; -2) \cup [1; +\infty)$
Проверим наш ответ: при $x = -3$ получаем $g(-3) = 4$ и $\sqrt{4} = 2$ ✅; при $x = 0$ получаем $g(0) = -0{,}5$, а $\sqrt{-0{,}5}$ не существует ✅ (значит, $0$ действительно вне области).
Почему это важно
В коде «выход за область определения композиции» выглядит как nan, inf или ValueError: math domain error — и, что хуже, часто не на первом же шаге, а глубоко внутри пайплайна. Классический случай в ML: ты логарифмируешь признак (np.log), а в данных внезапно оказался ноль или отрицательное значение — потому что перед этим стояла центровка, которая сдвинула распределение. Функция $\log$ прекрасно определена сама по себе, шаг центровки прекрасно определён сам по себе, а вот их композиция — уже нет. Именно поэтому в проде используют np.log1p (то есть $\log(1+x)$, сдвигающий область определения) и явно клиппируют значения между шагами. Ещё один живой пример — вычисление кросс-энтропии: $-\log(p)$ взрывается в $+\infty$ при $p = 0$, поэтому фреймворки внутри считают log_softmax как единую композицию с защитой, а не как «сначала softmax, потом log».
Разложение сложной функции на звенья
Интуиция
До сих пор мы собирали цепочку из готовых деталей. Теперь научимся обратному — глядя на страшную формулу, увидеть, из каких простых кусков она собрана. Этот навык называется декомпозицией, и без него не берётся ни одна производная сложной функции.
Приём простой: представь, что ты калькулятор и должен вычислить значение выражения при конкретном $x$. Запиши последовательность действий, которую ты реально выполнишь — от самого внутреннего к самому внешнему. Каждое действие и есть звено цепочки.
Возьмём $h(x) = \sqrt{3x + 7}$. Что ты сделаешь для $x = 3$? Сначала посчитаешь $3 \cdot 3 + 7 = 16$, потом извлечёшь корень: $4$. Два действия — два звена: внутреннее $g(x) = 3x+7$, внешнее $f(t) = \sqrt{t}$.
Правило декомпозиции: Внутренняя функция — то, что вычисляется первым; внешняя — то, что применяется к готовому результату. При нескольких вложениях цепочка строится от самой глубокой скобки наружу.
Примеры с разбором
Пример 1 (простой): разложи $h(x) = (5x - 2)^3$
Решение:
Шаг 1. Мысленно подставим число, скажем $x = 1$: сначала считаем $5 \cdot 1 - 2 = 3$, потом возводим в куб: $27$.
Шаг 2. Значит первое действие — линейное выражение, второе — куб.
Ответ: $g(x) = 5x - 2$ (внутренняя), $f(t) = t^3$ (внешняя), $h = f \circ g$.
Пример 2 (средний): разложи $h(x) = \dfrac{1}{(x^2 + 1)^3}$ на три звена
Решение:
Шаг 1. Подставим $x = 1$ и проследим порядок действий: $1^2 + 1 = 2$ → $2^3 = 8$ → $\dfrac{1}{8}$.
Шаг 2. Получилось три последовательных действия, значит три звена:
$$u(x) = x^2 + 1, \qquad v(t) = t^3, \qquad w(s) = \frac{1}{s}$$Шаг 3. Запишем композицию: $h = w \circ v \circ u$.
Проверим наш ответ при $x = 1$: $u(1) = 2$, $v(2) = 8$, $w(8) = 0{,}125$. И напрямую: $\dfrac{1}{(1+1)^3} = \dfrac{1}{8} = 0{,}125$ ✅
Ответ: $h = w \circ v \circ u$, где $u(x) = x^2+1$, $v(t) = t^3$, $w(s) = 1/s$.
📌 Заодно найдём область определения: $u$ и $v$ определены везде, а $w$ требует $s \neq 0$. Поскольку $x^2 + 1 \geq 1 > 0$ всегда, куб этого числа тоже никогда не равен нулю. Значит $D(h) = \mathbb{R}$ — редкий случай, когда цепочка нигде не рвётся.
Пример 3 (сложный): разложи «нейрон» $a(x) = \dfrac{1}{1 + e^{-(wx + b)}}$ при фиксированных $w$ и $b$
Это стандартный логистический нейрон: линейный слой плюс сигмоида.
Решение:
Шаг 1. Проследим порядок вычислений для конкретного $x$. Сначала считается линейная комбинация $wx + b$ — это то, что в ML называют логитом или предактивацией $z$.
Шаг 2. Затем к $z$ применяется сигмоида $\sigma(z) = \dfrac{1}{1 + e^{-z}}$.
Шаг 3. Значит цепочка двухзвенная:
$$z(x) = wx + b \quad (\text{внутренняя, линейная}), \qquad \sigma(t) = \frac{1}{1+e^{-t}} \quad (\text{внешняя, нелинейная})$$$$a = \sigma \circ z$$Шаг 4. Численно проверим при $w = 2$, $b = -4$, $x = 2$: $z = 2 \cdot 2 - 4 = 0$, затем $\sigma(0) = \dfrac{1}{1 + e^{0}} = \dfrac{1}{2} = 0{,}5$.
Ответ: $a = \sigma \circ z$, где $z(x) = wx+b$; при указанных параметрах $a(2) = 0{,}5$.
Вывод: ровно такое разложение и хранит внутри себя PyTorch. nn.Linear — это внутреннее звено, nn.Sigmoid — внешнее, а nn.Sequential — оператор композиции. Понимание, где кончается одно звено и начинается другое, критично при отладке: если у тебя «взрываются» градиенты, полезно смотреть не на итоговый выход, а на промежуточное значение $z$ между звеньями.
Почему это важно
Декомпозиция — это прямой пропуск к производным. Правило дифференцирования сложной функции (урок 137) звучит так: производная композиции равна произведению производных звеньев. Но чтобы им воспользоваться, нужно сначала увидеть звенья. Человек, который не умеет с ходу разложить $\sqrt{(3x^2+1)^5}$ на три функции, не сможет взять от неё производную, как бы хорошо он ни знал таблицу производных. А в ML это ещё и способ читать чужой код: любая архитектура из статьи — это описание декомпозиции, «какая функция за какой».
Композиция и обратная функция: цепочка, которая себя отменяет
Интуиция
В прошлом уроке мы разбирали обратную функцию — ту, что «отматывает назад» действие исходной. Композиция даёт этому строгое и очень короткое определение: две функции взаимно обратны, если их композиция ничего не делает.
Функция «ничего не делает» называется тождественной: $\mathrm{id}(x) = x$. Она играет для композиции ту же роль, что единица для умножения и ноль для сложения — нейтральный элемент.
Определение: Функции $f$ и $f^{-1}$ называются взаимно обратными, если
$$(f^{-1} \circ f)(x) = x \text{ для всех } x \in D(f), \qquad (f \circ f^{-1})(y) = y \text{ для всех } y \in E(f)$$
Обрати внимание: условий два, и оба обязательны. Это не педантизм. Если функция не инъективна (принимает одно и то же значение в разных точках), одно из равенств ломается — и вот тут возникает знаменитый эффект с модулем, который мы сейчас и разберём.
Примеры с разбором
Пример 1 (простой): проверь, что $f(x) = 3x - 6$ и $g(x) = \dfrac{x+6}{3}$ взаимно обратны
Решение:
Шаг 1. Считаем $g(f(x))$:
$$g(3x - 6) = \frac{(3x-6) + 6}{3} = \frac{3x}{3} = x$$Шаг 2. Считаем $f(g(x))$:
$$f\left(\frac{x+6}{3}\right) = 3 \cdot \frac{x+6}{3} - 6 = (x + 6) - 6 = x$$Шаг 3. Оба равенства дали тождественную функцию, обе области определения — вся прямая.
Ответ: да, функции взаимно обратны.
Пример 2 (средний): почему $\sqrt{x^2} \neq x$?
Пусть $f(x) = x^2$, $g(x) = \sqrt{x}$. Найди обе композиции.
Решение:
Шаг 1. $(f \circ g)(x) = (\sqrt{x})^2 = x$, но только при $x \geq 0$ — область определения корня.
Шаг 2. $(g \circ f)(x) = \sqrt{x^2}$. Здесь внутренняя функция определена везде, внешняя тоже принимает любой $x^2 \geq 0$, значит $D = \mathbb{R}$. Но чему равно значение? Проверим на числах: при $x = 3$ получаем $\sqrt{9} = 3$; при $x = -3$ получаем $\sqrt{9} = 3$, а вовсе не $-3$.
Шаг 3. Значит $(g \circ f)(x) = |x|$.
Ответ: $(f \circ g)(x) = x$ на $[0; +\infty)$, а $(g \circ f)(x) = |x|$ на $\mathbb{R}$.
Вывод: квадрат и корень взаимно обратны только на неотрицательной полуоси. Причина — необратимость $x^2$ на всей прямой: она склеивает $3$ и $-3$ в одно значение $9$, и обратной функции неоткуда узнать, откуда мы пришли. Композиция честно возвращает $|x|$ — «модуль исходника».
Пример 3 (сложный): функция, обратная сама себе
Дана $f(x) = \dfrac{x}{x - 1}$, $x \neq 1$. Найди $(f \circ f)(x)$.
Решение:
Шаг 1. Подставим $f(x)$ саму в себя:
$$f(f(x)) = \frac{\dfrac{x}{x-1}}{\dfrac{x}{x-1} - 1}$$Шаг 2. Приведём знаменатель к общей дроби:
$$\frac{x}{x-1} - 1 = \frac{x - (x-1)}{x-1} = \frac{1}{x-1}$$Шаг 3. Разделим числитель на знаменатель (деление на дробь — умножение на перевёрнутую):
$$f(f(x)) = \frac{x}{x-1} \cdot \frac{x-1}{1} = x$$Шаг 4. Проверим область определения: нужно $x \neq 1$; кроме того, $f(x)$ не должна равняться $1$. Решим $\dfrac{x}{x-1} = 1$: получаем $x = x - 1$, то есть $0 = -1$ — решений нет, значит второе условие никогда не нарушается.
Проверим наш ответ численно: $f(3) = \dfrac{3}{2} = 1{,}5$, затем $f(1{,}5) = \dfrac{1{,}5}{0{,}5} = 3$ ✅
Ответ: $(f \circ f)(x) = x$ при $x \neq 1$ — функция обратна сама себе (такие функции называют инволюциями).
Почему это важно
Инволюции и пары «прямое-обратное преобразование» — рабочий инструмент в обработке данных. Логарифмирование признака при обучении и потенцирование предсказания при выдаче пользователю: если ты обучал модель предсказывать $\log(\text{цена})$, то в проде обязан применить $\exp$ — иначе выдашь бессмыслицу. То же с нормализацией: scaler.transform и scaler.inverse_transform — это в точности $f$ и $f^{-1}$, и их композиция обязана давать тождество (полезный юнит-тест, кстати: проверить, что inverse_transform(transform(X)) ≈ X). А в глубоком обучении на этом принципе построен целый класс архитектур — normalizing flows, где сеть намеренно собирают только из обратимых слоёв, чтобы всю композицию можно было пройти в обе стороны.
Нейросеть как композиция и цепное правило
Интуиция
Теперь соберём всё вместе. Полносвязная нейросеть с двумя слоями — это функция
$$\hat{y}(x) = W_2 \cdot \varphi(W_1 x + b_1) + b_2$$Разложим на звенья: линейное преобразование → нелинейная активация $\varphi$ → снова линейное преобразование. Три звена, композиция. Сеть с сотней слоёв — та же конструкция, только длиннее.
И здесь всплывает вопрос, который отвечает на «зачем вообще нужны активации». Давай разберёмся: что будет, если убрать $\varphi$, оставив только линейные слои? Возьмём одномерный случай: $h_1(x) = 2x + 3$ и $h_2(t) = 5t - 4$. Их композиция:
$$h_2(h_1(x)) = 5(2x + 3) - 4 = 10x + 15 - 4 = 10x + 11$$Снова линейная функция. Композиция линейных функций — всегда линейная функция, сколько бы их ни было. Значит стослойная сеть без активаций математически эквивалентна одному линейному слою и не умеет ничего, кроме линейной регрессии. Нелинейность между звеньями — не украшение, а единственное, что делает глубину осмысленной.
Второй сюжет — производная композиции. Обучение сети — это поиск ответа на вопрос «как изменится ошибка, если чуть подкрутить вот этот вес в середине сети?». Ответ даёт цепное правило.
Правило дифференцирования сложной функции (chain rule): Если $y = f(u)$ и $u = g(x)$, то
$$\frac{dy}{dx} = \frac{dy}{du} \cdot \frac{du}{dx}, \qquad \text{или в других обозначениях} \qquad (f \circ g)'(x) = f'(g(x)) \cdot g'(x)$$
По-человечески: производная цепочки — это произведение производных звеньев, причём внешняя производная берётся в точке, куда пришло внутреннее звено. Для длинной цепочки правило продолжается: производные всех звеньев просто перемножаются. Это и есть backpropagation — проход по композиции от конца к началу с перемножением локальных производных.
Примеры с разбором
Пример 1 (простой): найди производную $h(x) = (2x + 3)^3$
Решение:
Шаг 1. Разложим на звенья: $g(x) = 2x+3$ (внутренняя), $f(t) = t^3$ (внешняя).
Шаг 2. Найдём производные звеньев: $g'(x) = 2$, $f'(t) = 3t^2$.
Шаг 3. Применим цепное правило — внешнюю производную берём в точке $g(x)$:
$$h'(x) = 3(2x+3)^2 \cdot 2 = 6(2x+3)^2$$Шаг 4. Вычислим в точке $x = 0$: $h'(0) = 6 \cdot 3^2 = 54$.
Ответ: $h'(x) = 6(2x+3)^2$, $h'(0) = 54$
📌 Самая частая ошибка здесь — забыть множитель $g'(x) = 2$ и написать просто $3(2x+3)^2$. Внутреннее звено обязательно оставляет след в ответе.
Пример 2 (средний): двухслойная сеть без активации
Слой 1: $h_1(x) = 4x - 1$. Слой 2: $h_2(t) = -2t + 7$. Найди композицию и объясни, что она означает для архитектуры.
Решение:
Шаг 1. Подставим первое во второе:
$$h_2(h_1(x)) = -2(4x - 1) + 7$$Шаг 2. Раскроем скобки:
$$= -8x + 2 + 7 = -8x + 9$$Проверим наш ответ при $x = 2$: по цепочке $h_1(2) = 7$, затем $h_2(7) = -14 + 7 = -7$. По формуле: $-16 + 9 = -7$ ✅
Ответ: $(h_2 \circ h_1)(x) = -8x + 9$ — снова линейная функция.
Вывод: два слоя с восемью числами внутри дали ровно то же, что мог бы дать один слой с двумя числами ($w = -8$, $b = 9$). Никакой дополнительной выразительности глубина без активации не даёт — только лишние параметры и лишние умножения. Именно поэтому между линейными слоями всегда ставят ReLU, GELU, сигмоиду или что-то ещё нелинейное.
Пример 3 (сложный): один шаг backpropagation вручную
Логистический нейрон: $z = wx + b$, $a = \sigma(z)$, ошибка $L = (a - y)^2$. Дано $x = 1$, $w = 0$, $b = 0$, целевое значение $y = 1$. Найди $\dfrac{\partial L}{\partial w}$.
Понадобится факт: $\sigma'(z) = \sigma(z)\bigl(1 - \sigma(z)\bigr)$.
Решение:
Шаг 1. Прямой проход (вычисляем композицию слева направо по данным).
$$z = 0 \cdot 1 + 0 = 0, \qquad a = \sigma(0) = \frac{1}{1 + e^{0}} = 0{,}5, \qquad L = (0{,}5 - 1)^2 = 0{,}25$$Шаг 2. Разложим $L$ как композицию трёх звеньев по переменной $w$:
$$w \; \longrightarrow \; z \; \longrightarrow \; a \; \longrightarrow \; L$$Цепное правило для трёх звеньев:
$$\frac{\partial L}{\partial w} = \frac{\partial L}{\partial a} \cdot \frac{\partial a}{\partial z} \cdot \frac{\partial z}{\partial w}$$Шаг 3. Считаем локальные производные в найденных точках.
$$\frac{\partial L}{\partial a} = 2(a - y) = 2(0{,}5 - 1) = -1$$$$\frac{\partial a}{\partial z} = \sigma(z)(1 - \sigma(z)) = 0{,}5 \cdot 0{,}5 = 0{,}25$$$$\frac{\partial z}{\partial w} = x = 1$$Шаг 4. Перемножаем:
$$\frac{\partial L}{\partial w} = (-1) \cdot 0{,}25 \cdot 1 = -0{,}25$$Ответ: $\dfrac{\partial L}{\partial w} = -0{,}25$
Как это читать: производная отрицательна, значит при увеличении $w$ ошибка уменьшается. Градиентный спуск сделает шаг $w \leftarrow w - \eta \cdot (-0{,}25) = w + 0{,}25\eta$, то есть увеличит вес — логично, ведь мы предсказали $0{,}5$, а нужно $1$. И заметь важное: множитель $\sigma'(z) = 0{,}25$ — это максимально возможное значение производной сигмоиды. Если нейрон насыщен ($z$ большой по модулю), $\sigma'$ становится близка к нулю, и в длинной композиции такие множители перемножаются — градиент затухает экспоненциально. Это и есть проблема vanishing gradients, из-за которой глубокие сети на сигмоидах не обучались до появления ReLU.
Почему это важно
Всё обучение нейросетей — это цепное правило, применённое к очень длинной композиции, плюс аккуратная бухгалтерия по хранению промежуточных значений. Когда ты пишешь loss.backward(), фреймворк проходит граф композиции справа налево и перемножает локальные производные — ровно то, что мы сделали руками в примере 3, только для миллиардов параметров. Понимание композиционной структуры даёт прямые практические выводы: почему residual-связи ($x + f(x)$ вместо просто $f(x)$) спасают от затухания градиента — потому что добавляют в произведение слагаемое-единицу; почему LayerNorm ставят между звеньями — чтобы вход каждого следующего звена не уезжал по масштабу; почему замена сигмоиды на ReLU перевернула глубокое обучение — потому что производная ReLU равна единице на положительной полуоси и не «съедает» градиент при перемножении.
Практика: 30 заданий
Базовые (задания 1-10)
Задание 1: Даны $f(x) = x + 3$ и $g(x) = 2x$. Найди $(g \circ f)(1)$.
Задание 2: Для тех же $f(x) = x+3$ и $g(x) = 2x$ найди $(f \circ g)(1)$ и сравни с предыдущим ответом.
Задание 3: Даны $f(x) = x^2$ и $g(x) = x - 1$. Найди формулы обеих композиций.
Задание 4: Дана $f(x) = 3x - 2$. Найди $(f \circ f)(x)$ и вычисли $(f \circ f)(2)$.
Задание 5: Даны $f(x) = \sqrt{x}$ и $g(x) = x + 5$. Найди $(f \circ g)(4)$.
Задание 6: Даны $f(x) = \dfrac{1}{x}$ и $g(x) = x + 1$. Найди $(g \circ f)(2)$.
Задание 7: Найди область определения $(f \circ g)$, если $f(x) = \sqrt{x}$, $g(x) = x - 3$.
Задание 8: Разложи $h(x) = (2x + 1)^5$ на внутреннюю и внешнюю функции.
Задание 9: Нейрон: линейное звено $g(x) = 2x - 6$, затем активация $\text{ReLU}(t) = \max(0, t)$. Найди значения композиции при $x = 1$ и $x = 4$.
Задание 10: Признак стандартизуется функцией $g(x) = \dfrac{x - 50}{10}$, затем возводится в квадрат функцией $f(t) = t^2$. Найди $(f \circ g)(70)$.
Средние (задания 11-20)
Задание 11: Даны $f(x) = 2x + 1$ и $g(x) = x^2$. Найди все $x$, при которых $(f \circ g)(x) = (g \circ f)(x)$.
Задание 12: Даны $f(x) = x^2$ и $g(x) = \sqrt{x}$. Найди обе композиции вместе с их областями определения.
Задание 13: Даны $f(x) = \dfrac{1}{x - 2}$ и $g(x) = x + 2$. Найди $(f \circ g)(x)$ и её область определения.
Задание 14: Известно, что $f(x) = 3x + 1$ и $(f \circ g)(x) = 6x - 5$. Найди $g(x)$.
Задание 15: Известно, что $g(x) = x - 4$ и $(f \circ g)(x) = 2x - 3$. Найди $f(x)$.
Задание 16: Даны $f(x) = x + 1$, $g(x) = 2x$, $h(x) = x^2$. Найди $(f \circ g \circ h)(3)$ и $(h \circ g \circ f)(3)$.
Задание 17: Найди функцию, обратную к $f(x) = 2x - 6$, и проверь через композицию.
Задание 18: Пайплайн для скошенного признака: сначала $f(x) = \log_{10}(x + 1)$, затем стандартизация $g(t) = \dfrac{t - 2}{0{,}5}$. Найди значение композиции для $x = 999$.
Задание 19: Логистический нейрон: $z(x) = wx + b$ с $w = 2$, $b = -4$, затем $\sigma(t) = \dfrac{1}{1 + e^{-t}}$. Найди выход нейрона при $x = 2$.
Задание 20: Найди область определения $(f \circ g)$, если $f(x) = \sqrt{x}$, $g(x) = \dfrac{1}{x - 1}$.
Продвинутые (задания 21-30)
Задание 21: Дана $f(x) = \dfrac{x}{x-1}$, $x \neq 1$. Найди $(f \circ f)(x)$.
Задание 22: Реши уравнение $(f \circ g)(x) = 0$, если $f(x) = x^2 - 9$, $g(x) = x + 2$.
Задание 23: Найди линейную функцию $g(x) = ax + b$ такую, что $(g \circ f)(x) = 3x^2 + 5$, если $f(x) = x^2 + 1$.
Задание 24: Сеть из двух линейных слоёв без активации: $h_1(x) = 2x + 3$, $h_2(t) = 5t - 4$. Найди композицию $(h_2 \circ h_1)(x)$ и объясни вывод для архитектуры.
Задание 25: Найди производную $h(x) = (3x^2 + 1)^4$ и вычисли $h'(1)$.
Задание 26: Нейрон: $z(w) = wx$ при фиксированном $x = 2$, затем $a = \sigma(z)$. Найди $\dfrac{da}{dw}$ в точке $w = 0$, используя $\sigma'(z) = \sigma(z)(1 - \sigma(z))$.
Задание 27: Даны $f(x) = \sqrt{x - 1}$ и $g(x) = x^2 + 1$. Найди обе композиции и выясни, на каких множествах функции взаимно обратны.
Задание 28: Даны $f(x) = \dfrac{1}{x}$, $g(x) = \sqrt{x}$, $h(x) = x - 4$. Найди формулу $(f \circ g \circ h)(x)$, её область определения и значение при $x = 8$.
Задание 29: Дана $f(x) = \dfrac{x}{2} + 1$. Вычисли трёхкратную композицию $(f \circ f \circ f)(8)$ и найди неподвижную точку функции $f$.
Задание 30: Отладка кода. Найди ошибку и исправь её.
def compose(f, g):
"""Возвращает функцию x -> f(g(x))"""
return lambda x: f(g(x))
normalize = lambda x: (x - 50) / 10
relu = lambda x: max(0.0, x)
# Хотим пайплайн: сначала нормализация, потом ReLU
pipeline = compose(normalize, relu)
print(pipeline(30)) # ожидали 0.0, получили: -2.0
Частые ошибки
❌ Ошибка 1: путают порядок в записи $g \circ f$
Неправильно: считать, что в $(g \circ f)(x)$ первой применяется $g$, потому что она написана первой.
Правильно: $(g \circ f)(x) = g(f(x))$ — первой работает правая функция $f$. Читай запись изнутри наружу, как вложенные скобки.
💡 Почему важно: это не вопрос вкуса — от порядка зависит ответ. Для $f(x)=x+1$, $g(x)=x^2$ при $x=3$ получается либо $16$, либо $10$, и ровно одно из этих чисел верное.
❌ Ошибка 2: считают композицию произведением функций
Неправильно: писать $(g \circ f)(x) = g(x) \cdot f(x)$.
Правильно: композиция — это подстановка, а не умножение. Для $f(x) = x+2$, $g(x) = 3x$: композиция даёт $3(x+2) = 3x+6$, а произведение — $3x(x+2) = 3x^2 + 6x$. Это разные функции: первая линейная, вторая квадратичная.
💡 Почему важно: значок $\circ$ визуально похож на знак операции вроде $\cdot$, и рука сама тянется перемножить. Но композиция даже не обязана сохранять тип функции: подстановка линейного в квадратичное даёт квадратичное, подстановка квадратичного в квадратичное — четвёртой степени.
❌ Ошибка 3: ищут область определения по упрощённой итоговой формуле
Неправильно: для $f(t) = t^2$, $g(x) = \sqrt{x}$ упростить $(f \circ g)(x) = (\sqrt{x})^2 = x$ и заявить, что $D = \mathbb{R}$.
Правильно: область определения задаётся цепочкой, а не результатом упрощения. Внутренняя функция — корень, значит $D(f \circ g) = [0; +\infty)$, несмотря на то что итоговая формула выглядит как безобидное $x$.
💡 Почему важно: это ровно тот класс багов, который в коде даёт nan в середине пайплайна. Формула упростилась, ограничение осталось — и оно всё ещё действует.
❌ Ошибка 4: пишут $\sqrt{x^2} = x$
Неправильно: сокращать корень и квадрат «крест-накрест», считая, что они всегда друг друга отменяют.
Правильно: $\sqrt{x^2} = |x|$. Проверка на числе: при $x = -4$ получаем $\sqrt{16} = 4$, а не $-4$. Взаимно обратными $x^2$ и $\sqrt{x}$ становятся только при ограничении $x \geq 0$.
💡 Почему важно: функция $x^2$ необратима на всей прямой — она склеивает $x$ и $-x$. Композиция это честно показывает, возвращая модуль. Именно из-за таких склеек, кстати, нельзя «развернуть» большинство слоёв нейросети: информация теряется безвозвратно.
❌ Ошибка 5: забывают производную внутреннего звена (chain rule)
Неправильно: дифференцируя $h(x) = (2x+3)^3$, писать $h'(x) = 3(2x+3)^2$.
Правильно: $h'(x) = 3(2x+3)^2 \cdot 2 = 6(2x+3)^2$. Производная композиции — это произведение производных всех звеньев, включая внутреннее.
💡 Почему важно: пропущенный множитель искажает градиент в постоянное число раз. В ручных выкладках это неверный ответ; в кастомном слое нейросети — обучение, которое «почти работает», но сходится не туда, и такой баг ищут днями.
❌ Ошибка 6: считают, что глубина сама по себе даёт выразительность
Неправильно: думать, что десять линейных слоёв подряд мощнее одного.
Правильно: композиция линейных функций всегда линейна: $h_2(h_1(x)) = 5(2x+3)-4 = 10x+11$. Без нелинейной активации между звеньями любая глубина схлопывается в один слой.
💡 Почему важно: это математическое обоснование того, зачем вообще нужны ReLU, GELU и сигмоида. И это же объясняет, почему при отладке архитектуры первое, что стоит проверить, — не потерялась ли активация между линейными слоями.
Главное запомнить
✅ Композиция $(g \circ f)(x) = g(f(x))$ — это подстановка результата одной функции в другую, а не сложение и не умножение.
✅ Читай запись справа налево: в $g \circ f$ первой работает $f$ (внутренняя), затем $g$ (внешняя).
✅ Композиция некоммутативна: $g \circ f \neq f \circ g$ в общем случае. Порядок шагов пайплайна менять нельзя.
✅ Композиция ассоциативна: $(h \circ g) \circ f = h \circ (g \circ f)$. Поэтому соседние звенья можно объединять в блоки, не меняя результата.
✅ Область определения композиции: $D(g \circ f) = \{x \in D(f) : f(x) \in D(g)\}$ — нужны оба условия сразу.
✅ Область определения ищется по цепочке, а не по упрощённой итоговой формуле: $(\sqrt{x})^2 = x$, но только на $[0; +\infty)$.
✅ Разложение сложной функции на звенья: внутренняя — то, что вычисляется первым; внешняя — то, что применяется к результату.
✅ Взаимно обратные функции — это те, чьи композиции дают тождество: $f^{-1}(f(x)) = x$ и $f(f^{-1}(y)) = y$. Оба равенства обязательны.
✅ $\sqrt{x^2} = |x|$, а не $x$ — необратимость $x^2$ на всей прямой проявляется именно в композиции.
✅ Цепное правило: $(f \circ g)'(x) = f'(g(x)) \cdot g'(x)$ — производные звеньев перемножаются, внешняя берётся в точке, куда пришло внутреннее звено.
✅ Нейросеть — это композиция слоёв, а backpropagation — цепное правило, применённое к ней. Без нелинейной активации между линейными слоями композиция схлопывается в один линейный слой.
Связь с другими темами курса
Что было раньше: урок 82 про обратную функцию дал половину этой темы — теперь у нас есть точный критерий обратимости через композицию ($f^{-1} \circ f = \mathrm{id}$). А понятия области определения и области значений, разобранные ещё в базовом блоке про функции, здесь впервые заработали в полную силу: композиция — это первое место, где $E(f)$ внутренней функции напрямую влияет на $D$ всей конструкции.
Что дальше: следующий урок — преобразования графиков (урок 84) — это буквально композиция в чистом виде: $y = f(x - a)$ есть композиция сдвига и $f$, а $y = k \cdot f(x)$ — композиция $f$ и растяжения. Некоммутативность, которую мы здесь разобрали, там станет главным практическим правилом: «сначала сдвинуть, потом растянуть» и «сначала растянуть, потом сдвинуть» дают разные графики. Дальше по курсу композиция всплывёт в тригонометрии (уравнения вида $\sin(2x + \pi/3)$ — трёхзвенная композиция), в показательных и логарифмических функциях (они взаимно обратны, то есть их композиция — тождество), и, конечно, в уроке 137 «Производная сложной функции», который целиком посвящён цепному правилу.
Где это применяется в жизни и в ML/данных:
🧠 В нейросетях: любая сеть — это композиция слоёв $f_L \circ \dots \circ f_1$; backpropagation — цепное правило по этой композиции; residual-связи $x + f(x)$ придуманы, чтобы в произведении производных всегда оставалась единица и градиент не затухал.
📊 В data science: пайплайны предобработки (Pipeline, ColumnTransformer) — это операторы композиции; transform и inverse_transform — взаимно обратные функции; порядок шагов определяет, будет ли утечка данных.
💻 В программировании: функциональная композиция (pipe, compose, |> в Elixir и F#), middleware в веб-фреймворках (каждый обработчик — звено), декораторы в Python — всё это композиция функций с разным синтаксическим сахаром.
🔐 В криптографии и сжатии: шифрование и дешифрование, кодирование и декодирование — пары взаимно обратных функций; блочные шифры устроены как композиция многих одинаковых раундов.
🎨 В графике и играх: цепочка аффинных преобразований (поворот, масштаб, перенос) — композиция, и именно её некоммутативность заставляет всех спорить о порядке перемножения матриц.
Интересные факты
💡 Знак $\circ$ моложе, чем кажется. Сама операция известна со времён Лейбница, а вот компактный символ $\circ$ вошёл в широкий обиход только в XX веке, вместе с теоретико-множественным взглядом на функции как на отображения. До этого сложную функцию просто писали скобками — и, что забавно, в вычислительной практике мы к этому вернулись: в коде никто не пишет g ∘ f, все пишут g(f(x)) или строят явный список шагов.
💡 Композиция образует моноид. Множество функций из $X$ в $X$ с операцией композиции — это алгебраическая структура моноид: операция ассоциативна и есть нейтральный элемент $\mathrm{id}(x) = x$. А если оставить только обратимые функции, получится группа — та самая структура, на которой стоит вся теория симметрий, от кристаллографии до Стандартной модели в физике элементарных частиц.
💡 Теорема Колмогорова-Арнольда — про композицию. В 1957 году Владимир Арнольд (тогда девятнадцатилетний студент) и Андрей Колмогоров доказали, что любую непрерывную функцию многих переменных можно представить как композицию непрерывных функций одной переменной и сложения. Это был ответ на 13-ю проблему Гильберта — и сегодня на эту теорему прямо ссылается архитектура KAN (Kolmogorov-Arnold Networks), предложенная в 2024 году как альтернатива обычным полносвязным сетям.
💡 Универсальная теорема аппроксимации — тоже про композицию. Цыбенко (1989) и Хорник (1991) доказали, что композиция всего двух звеньев — одного линейного слоя с нелинейной активацией и ещё одного линейного — может приблизить любую непрерывную функцию на отрезке с любой точностью. Теорема ничего не говорит о том, сколько нейронов для этого понадобится (может понадобиться астрономически много) и как их обучить — но она объясняет, почему уже двухзвенная композиция принципиально способна на всё, а глубина нужна для эффективности, а не для возможности.
💡 Функция, обратная сама себе, встречается чаще, чем кажется. Кроме разобранной $\dfrac{x}{x-1}$, инволюциями являются $-x$, $\dfrac{1}{x}$, $c - x$ и, например, операция XOR с фиксированным ключом. Последнее — основа простейшего шифра: одна и та же функция и шифрует, и расшифровывает, потому что применённая дважды даёт тождество.
Лайфхаки и полезные трюки
1. Переименовывай аргумент внешней функции в нейтральную букву
Прежде чем подставлять, перепиши внешнюю функцию как $f(t) = t^2 - 1$ вместо $f(x) = x^2 - 1$. Тогда подстановка $f(2x+3) = (2x+3)^2 - 1$ становится механической и ты не перепутаешь, куда именно подставлять. Правило: «$f$ делает со своим аргументом одно и то же, что бы ему ни дали».
2. Проверяй ответ на одном конкретном числе
Нашёл формулу композиции — обязательно подставь любое удобное число двумя способами: по цепочке (шаг за шагом) и по итоговой формуле. Совпало — почти наверняка всё верно, не совпало — ошибка в раскрытии скобок. Занимает десять секунд и ловит подавляющее большинство арифметических ляпов.
3. Для декомпозиции представь себя калькулятором
Не пытайся «увидеть» звенья глазами — просто спроси: «в каком порядке я буду физически вычислять это выражение для $x = 2$?». Последовательность реальных действий и есть цепочка функций, от внутренней к внешней. Работает даже для монструозных выражений в пять уровней вложенности.
4. Область определения ищи ДО упрощения формулы
Выпиши ограничения каждого звена по отдельности, пока формула ещё не свёрнута: внутренняя функция определена там-то, её значение должно лежать там-то. Потом упрощай сколько угодно — ограничения уже зафиксированы и никуда не денутся. Обратный порядок (сначала упростить, потом искать $D$) — прямой путь к неверному ответу.
5. Для проверки обратимости считай обе композиции, а не одну
Одного равенства $f(g(x)) = x$ мало. Классическая ловушка: $f(t) = t^2$ и $g(x) = \sqrt{x}$ дают $f(g(x)) = x$, но $g(f(x)) = |x|$ — функции обратны лишь на половине прямой. Считай обе стороны, и подводных камней не будет.
6. В коде задавай пайплайн в порядке выполнения
Математическая запись $g \circ f$ читается справа налево, а мозг инженера читает пайплайн слева направо. Чтобы не путаться, заводи функцию pipe(f, g, h), применяющую шаги в том порядке, в каком они написаны, — и никогда не полагайся на память относительно того, какой аргумент у compose внутренний. Отдельный бонус: такой пайплайн самодокументируется.
7. Ищи неподвижные точки, если функция применяется многократно
Если одна и та же функция применяется в цикле ($x$, $f(x)$, $f(f(x))$, …), первое, что стоит сделать, — решить уравнение $f(x) = x$. Решения — это точки, в которых процесс замирает; к ним итерации обычно и сходятся, если функция «сжимающая». Так работает метод простой итерации, так же устроена сходимость градиентного спуска к минимуму.
Композиция функций — это, пожалуй, самая недооценённая тема школьной программы. На первый взгляд — техническое упражнение на подстановку выражений в выражения. На деле — организующий принцип всего современного машинного обучения: сложное поведение собирается из простых кусков, поставленных в цепочку, а обучение всей конструкции сводится к перемножению производных её звеньев. Когда ты в следующий раз посмотришь на схему архитектуры трансформера — этот лес из блоков, стрелок и нормализаций — ты увидишь не магию, а очень длинную композицию, каждое звено которой по отдельности вполне понятно. И это уже совсем другое ощущение от работы с нейросетями. Дальше нас ждут преобразования графиков, где композиция станет наглядной: ты будешь буквально видеть, как подстановка одной функции в другую двигает, растягивает и переворачивает кривые на плоскости.
Понял тему? Закрепи в боте! 🚀
Попрактикуйся на задачах и получи персональные рекомендации от AI
💪 Начать тренировку