Micrograd: как 150 строк кода объясняют работу современных нейросетей

Andrej Karpathy 3,4 млн 2 ч 25 мин 21 мин 16.08.2022
Главное

Нейронные сети — это не «цифровой мозг», а всего лишь математические выражения, которым абсолютно всё равно, что они обучают искусственный интеллект. Чтобы понять принципы работы гигантов вроде GPT, достаточно 150 строк кода на Python и базовой интуиции: если машина едет вдвое быстрее велосипеда, а тот вчетверо быстрее пешехода, вы уже понимаете принцип цепного правила. Андрей Карпати разбирает архитектуру нейросетей на атомарном уровне, превращая абстрактные градиенты в осязаемый механизм.

🛠️ Micrograd: фундамент нейросетей и магия производных 0:00

Андрей Карпати (Andrej Karpathy), разработчик с десятилетним стажем обучения глубоких нейронных сетей, начинает свой курс с амбициозного обещания: разобрать работу ИИ «под капотом» до самого основания. Вместо использования готовых тяжеловесных решений он предлагает воссоздать всё с нуля, начав с библиотеки Micrograd .

Micrograd и философия автоматического дифференцирования 0:51

Micrograd — это миниатюрный движок автоматического дифференцирования (Autograd), который Андрей выпустил на GitHub несколько лет назад. Несмотря на свой крошечный размер, этот инструмент реализует алгоритм обратного распространения ошибки (backpropagation). Именно этот алгоритм является математическим ядром любой современной библиотеки глубокого обучения, будь то PyTorch или JAX .

Суть работы Micrograd заключается в том, чтобы позволить пользователю строить сложные математические выражения, а затем автоматически вычислять градиенты. Градиент, по определению Карпати, — это мера чувствительности функции потерь к изменению весов нейронной сети. Зная это значение, мы можем итеративно подстраивать веса, чтобы минимизировать потери и повышать точность модели .

Андрей Карпати подчеркивает важную педагогическую особенность своего проекта:

  1. Скалярные значения: В отличие от производственных решений, работающих с многомерными тензорами, Micrograd оперирует на уровне отдельных скаляров .
  2. Прозрачность: Вся библиотека занимает всего около 150 строк кода на Python . Из них движок дифференцирования (engine.py) составляет всего 100 строк, а сама надстройка для нейронных сетей (nn.py) — еще около 50.
  3. Идентичность математики: Хотя тензоры эффективнее за счет параллелизма в современных компьютерах, фундаментальная математика остается неизменной. Понимая Micrograd, вы понимаете, как обучаются гигантские модели вроде GPT .

«Нейронные сети — это просто математические выражения, которые принимают на вход данные и веса, а на выходе выдают прогнозы или значения функции потерь», — резюмирует Андрей .

Определение производной и численный градиент 8:11

Прежде чем приступать к коду, необходимо восстановить в памяти интуитивное понимание производной. В школьном курсе математики её часто выводят символьно, используя правила дифференцирования. Однако в нейросетях выражения могут содержать десятки тысяч терминов, и никто не выводит их на бумаге вручную .

Андрей обращается к классическому определению через лимит: f'(x) = lim(h -> 0) [f(x + h) - f(x)] / h .

Чтобы продемонстрировать физический смысл производной, автор рассматривает простую квадратичную функцию f(x) = 3x² - 4x + 5 и строит её график в форме параболы . Если мы возьмем точку x = 3.0, то значение функции в ней будет равно 20. Если мы слегка «подтолкнем» (nudge) x на крошечное значение h (например, 0.001), мы увидим, как изменится результат функции.

Численный эксперимент показывает :

Для точки x = 3 производная оказалась равна 14 . Это означает, что при небольшом росте x функция будет расти в 14 раз быстрее. В точке x = 2/3 наклон будет нулевым — это вершина параболы, где небольшой сдвиг x почти не влияет на результат .

Создание базового класса Value: архитектура графа 19:24

Для реализации автоматического дифференцирования Андрей проектирует специальную структуру данных — класс Value. Это объект-обертка для обычного числа, который не просто хранит данные, но и «помнит», откуда они взялись.

Первая итерация класса включает:

Однако просто считать значения недостаточно для нейронной сети. Чтобы реализовать обратный ход, объектам нужно «соединительная ткань» — информация о связях. Для этого Андрей расширяет класс :

Таким образом, при выполнении выражения `d = a

🎨 Визуализация графа и магия цепного правила 25:08

Визуализация через Graphviz: заглядываем внутрь выражений 25:08

Для того чтобы понимать, что происходит внутри математических выражений, Андрей Карпати (Andrej Karpathy) внедряет инструмент визуализации. Поскольку ранее был создан базовый класс Value, теперь необходимо наглядно увидеть, как узлы соединяются в единую сеть. Для этого используется Graphviz — программное обеспечение с открытым исходным кодом для визуализации графов .

Процесс построения изображения начинается с написания вспомогательной функции trace, которая рекурсивно обходит все узлы и ребра, начиная с выходного значения. Функция формирует два множества: все уникальные узлы и связи между ними . На основе этих данных строится визуализация в Jupyter Notebook, где каждый объект Value представлен в виде прямоугольника.

Андрей Карпати (Andrej Karpathy) вносит в код несколько важных архитектурных решений для улучшения читаемости графа:

В результате получается интерактивная схема, где, например, умножение a на b создает промежуточный узел e, который затем складывается с c, образуя финальный выход d . Это превращает сухой код в живую структуру, готовую к следующему этапу — обратному распространению ошибки.

Градиенты: ручной расчет обратного прохода 29:35

После того как прямой проход (forward pass) завершен и мы получили итоговое значение $L$ (например, -8.0), наступает время backpropagation. Задача этого этапа — вычислить производную выходного значения по отношению к каждой промежуточной переменной. Андрей Карпати (Andrej Karpathy) подчеркивает, что в контексте нейросетей нас больше всего интересует производная функции потерь $L$ относительно весов сети, так как именно их мы будем изменять для обучения .

По умолчанию для каждого узла градиент (grad) устанавливается в 0.0. Это символизирует начальное состояние «отсутствия влияния»: мы предполагаем, что изменение переменной никак не влияет на результат, пока не доказано обратное .

Процесс расчета начинается с самого конца:

  1. Базовый случай: Чему равна производная $L$ по отношению к $L$? Андрей Карпати (Andrej Karpathy) объясняет это интуитивно: если мы изменим $L$ на крошечную величину $h$, то и результат изменится ровно на $h$. Следовательно, $dL/dL = 1.0$ . Это отправная точка, которую мы вручную прописываем в графе.
  2. Производная произведения: Если $L = d \times f$, то как изменение $d$ влияет на $L$? По правилам исчисления, производная произведения по одному из множителей — это значение другого множителя. Таким образом, градиент для $f$ будет равен значению $d$, а градиент для $d$ будет равен значению $f$ .

Для проверки правильности ручных расчетов автор использует метод «проверки градиента» (gradient check), создавая функцию lol(). В ней он немного изменяет значение одной из входных переменных на $h$ и смотрит, насколько изменится итоговое $L$, используя формулу $(L2 - L1) / h$ . Этот численный метод подтверждает точность аналитических производных .

Цепное правило: как сигнал ошибки течет сквозь граф 41:47

Центральным моментом всей лекции становится объяснение цепного правила (Chain Rule). Андрей Карпати (Andrej Karpathy) называет его «сердцем обучения нейросетей» . Проблема возникает, когда нам нужно найти производную $L$ по отношению к переменной $c$, которая находится не в конце, а где-то в глубине графа. Мы знаем, как $c$ влияет на $d$, и знаем, как $d$ влияет на $L$. Как объединить эту информацию?

Для объяснения правила Андрей приводит очень доходчивую аналогию:

«Если машина едет в два раза быстрее велосипеда, а велосипед едет в четыре раза быстрее пешехода, то машина едет в $2 \times 4 = 8$ раз быстрее пешехода» .

В математических терминах это означает перемножение производных. Если переменная $z$ зависит от $y$, а $y$ зависит от $x$, то производная $z$ по отношению к $x$ — это произведение $dz/dy$ на $dy/dx$ .

Рассматривая узел сложения ($d = c + e$), Карпати показывает магию этого процесса:

Когда же дело доходит до узла умножения ($e = a \times b$), цепное правило требует умножить внешний градиент на значение «соседней» переменной. Например, градиент для $a$ будет равен градиенту $e$, умноженному на значение $b$ . Весь этот процесс — это последовательное применение умножения при движении назад по графу, что позволяет «пробросить» сигнал ошибки от функции потерь до самых первых входных данных или весов модели.

🧠 Биологический нейрон в математическом коде 53:07

После того как фундамент в виде базовых математических операций и понимания производной был заложен, Андрей Карпати переходит к следующему этапу: созданию структурного кирпичика любой нейронной сети — искусственного нейрона. Это логический переход от абстрактных вычислений к моделированию процессов, имитирующих работу мозга.

Анатомия искусственного нейрона: от биологии к формулам 53:07

Хотя биологический нейрон представляет собой сложнейшую структуру, в машинном обучении принята его упрощенная математическая модель . Карпати детально разбирает эту архитектуру, проводя параллели с реальными синапсами.

В этой модели можно выделить несколько ключевых компонентов:

  1. Входные сигналы (Inputs): Значения $x_0, x_1$ и так далее, которые поступают в нейрон.
  2. Веса (Weights): Каждому входу соответствует свой вес ($w_0, w_1$), который Карпати называет «синаптической силой» . В процессе обучения именно эти веса будут изменяться, определяя влияние конкретного входа на итоговый результат.
  3. Сумматор: Внутри «тела» нейрона происходит перемножение входов на веса ($w \cdot x$) и их суммирование.
  4. Смещение (Bias): К полученной сумме добавляется параметр $b$. Андрей объясняет его роль как «врожденную готовность нейрона к активации» (trigger happiness) . Смещение позволяет сдвигать результат суммы вверх или вниз независимо от входных данных.

Результат этого процесса — сырая сумма ($w \cdot x + b$) — затем проходит через «сжимающую» функцию, которая определяет окончательный выход нейрона. Ранее в разговоре Карпати упоминал простейшие графы, но здесь он демонстрирует, как те же принципы применяются к конкретной биологической метафоре .

Функция активации tanh: искусство нелинейности 56:58

Чтобы нейронная сеть могла решать сложные задачи, ей необходима нелинейность. Без неё любое количество слоев нейронов просто схлопнулось бы в одну линейную функцию. Для реализации этого эффекта Карпати выбирает гиперболический тангенс (tanh).

Эта функция обладает важными для обучения свойствами: она плавно «сжимает» любые входные значения в диапазон от -1 до 1 . Если на вход подается очень большое положительное число, на выходе мы получим значение, близкое к единице; если очень маленькое (отрицательное) — близкое к минус единице . В нуле функция выдает ноль.

В библиотеке Micrograd реализация tanh становится интересным вызовом. Поскольку гиперболический тангенс включает в себя экспоненты ($e^x$), его можно было бы разложить на более атомарные операции. Однако Андрей принимает стратегическое решение: реализовать tanh как единый блок .

«Нам не обязательно разбивать всё до самых атомарных составляющих. Мы можем создавать функции на любом уровне абстракции. Главное — чтобы мы знали, как вычислить локальную производную этой функции», — подчеркивает Андрей Карпати .

Для обратного распространения ошибки (backpropagation) критически важно знать производную этой функции. Карпати выводит её, опираясь на классическое исчисление: если $o = \tanh(n)$, то производная $do/dn = 1 - o^2$ . Использование уже вычисленного значения выхода ($o$) делает расчет градиента чрезвычайно эффективным.

Автоматизация процесса: метод _backward 1:09:24

Проделав ручной расчет градиентов для нейрона с двумя входами, Карпати показывает, насколько утомительным и подверженным ошибкам является этот процесс. Чтобы покончить с «ручным страданием» , он вводит в класс Value специальный атрибут — _backward.

Это функция-замыкание, которая инкапсулирует в себе правило цепного правила (Chain Rule) для конкретной операции. Теперь каждый узел графа «знает», как передать свой градиент обратно своим «детям» (входным узлам).

Андрей кодифицирует логику для основных операций:

Важным моментом является инициализация: перед началом автоматического обратного прохода градиент конечного узла должен быть равен 1.0 (производная функции по самой себе) . Это запускает цепную реакцию, которая позволяет вычислить влияние каждого веса и смещения на итоговый результат нейрона, закладывая основу для будущего обучения всей сети.

🧠 Автоматизация графа и расширение математического аппарата 1:15:24

На текущем этапе ручной вызов метода _backward для каждого узла графа превращается в трудоёмкую и подверженную ошибкам задачу. Андрей Карпати (Andrej Karpathy) переходит к фундаментальному этапу создания любого движка автоматического дифференцирования — полной автоматизации обратного распространения ошибки . Чтобы система сама понимала, в каком порядке обходить узлы, необходимо внедрить алгоритмический подход к управлению зависимостями внутри вычислительного графа.

Автоматизация backpropagation через топологическую сортировку 1:17:36

Главная сложность при вызове градиентов заключается в том, что мы никогда не должны вычислять _backward для узла до того, как будут вычислены градиенты для всех его зависимостей «выше» по потоку. В теории графов эта задача решается с помощью топологической сортировки.

Вычислительный граф в Micrograd представляет собой направленный ациклический граф (DAG). Андрей Карпати объясняет, что топологическая сортировка — это такое линейное упорядочивание узлов, при котором для любого ребра от узла $U$ к узлу $V$ узел $U$ всегда располагается в списке раньше, чем $V$ . В контексте нейросети это означает, что если мы развернём граф слева направо, то все стрелки будут указывать только в одну сторону.

Для реализации этого механизма Андрей Карпати вводит вспомогательную функцию build_topo:

Результатом становится список, где выходной узел (например, функция потерь) всегда стоит последним . Для запуска процесса обучения достаточно развернуть этот список и вызвать _backward для каждого элемента в обратном порядке. Теперь пользователю библиотеки не нужно знать внутреннюю структуру графа — достаточно вызвать один метод backward() у финального значения Value .

Исправление бага накопления градиентов 1:22:35

При попытке использовать автоматизированный метод на более сложных структурах обнаруживается критическая ошибка. Андрей Карпати демонстрирует её на простейшем примере: b = a + a . С точки зрения математики производная $b$ по $a$ должна быть равна 2. Однако текущая реализация Micrograd выдаёт 1.

Проблема кроется в способе записи градиента. В ранней версии при каждом вызове _backward значение self.grad просто перезаписывалось . Когда одна и та же переменная используется в выражении несколько раз (как в случае a + a), градиенты от разных «путей» графа не объединяются, а затирают друг друга. Последний вызов функции просто уничтожает результаты предыдущих .

Решение этой проблемы напрямую вытекает из многомерного цепного правила (multivariate chain rule). Андрей Карпати объясняет, что если узел имеет несколько путей к выходу, его общий градиент является суммой градиентов, приходящих по всем этим путям .

Это исправление делает библиотеку устойчивой к сложным графам, где переменные могут многократно переиспользоваться в разных частях выражения . Ранее упомянутые базовые операции сложения и умножения теперь корректно обрабатывают такие случаи.

Реализация сложных математических операций 1:27:16

Чтобы Micrograd стал полноценным инструментом, Андрей Карпати расширяет класс Value, добавляя поддержку атомарных математических операций. Это необходимо для того, чтобы сложные функции (такие как $tanh$) можно было не только использовать как готовые блоки, но и собирать «с нуля» из более простых элементов .

В процессе расширения библиотеки Андрей Карпати реализует:

  1. Поддержку констант: Изменение методов __add__ и __mul__ таким образом, чтобы они автоматически оборачивали обычные числа (int или float) в объекты Value. Это позволяет писать выражения вида a + 1 или a * 2 без ошибок атрибутов .
  2. Возведение в степень (**pow**): Реализация через правило $d/dx [x^n] = n \cdot x^{n-1}$. Это ключевой момент, так как через возведение в степень -1 в библиотеке реализуется деление .
  3. Деление и вычитание: Вместо создания отдельных сложных методов, Андрей Карпати элегантно определяет их через уже существующие операции. Вычитание $a - b$ представляется как $a + (-b)$, а деление $a / b$ — как $a \cdot b^{-1}$ .

Для проверки мощности обновленного движка Андрей Карпати проводит эксперимент: он заменяет встроенную функцию tanh на её полное математическое разложение: $$\tanh(x) = \frac{e^{2x} - 1}{e^{2x} + 1}$$ Несмотря на то, что граф вычислений значительно удлинился и стал включать экспоненты, деления и вычитания , итоговые градиенты на входах нейрона остались абсолютно идентичными .

«Уровень, на котором вы реализуете свои операции, полностью зависит от вас, — резюмирует Андрей Карпати. — Если вы можете вычислить локальный градиент, вы можете продолжать цепочку бэкпропагации» . Этот этап завершает формирование ядра Micrograd как универсального движка автоматического дифференцирования, подготавливая почву для сравнения с индустриальными стандартами вроде PyTorch.

🧠 От скаляров к нейросетям: архитектура MLP и магия PyTorch 1:40:30

После того как базовый движок автоматического дифференцирования был готов, Андрей Карпати переходит к демонстрации того, насколько созданный нами Micrograd близок к индустриальным стандартам. На этом этапе становится очевидно: всё, что мы писали «на коленке», — это не просто учебная абстракция, а упрощённая копия архитектуры PyTorch.

Идентичность API: Micrograd против тензоров PyTorch 1:40:45

Чтобы доказать работоспособность своей библиотеки, Карпати сравнивает её с PyTorch. Основное различие заключается в том, что PyTorch оперирует тензорами — многомерными массивами, которые значительно эффективнее за счёт параллельных вычислений . Однако на уровне единичных скалярных значений API практически идентичны.

При создании тензора в PyTorch (например, со значением 2.0), Карпати принудительно устанавливает тип double (float64), так как Python по умолчанию использует двойную точность, а PyTorch — одинарную (float32) . Важным нюансом является параметр requires_grad=True. В PyTorch он по умолчанию отключен ради экономии ресурсов, так как для входных данных (листовых узлов) градиенты обычно не требуются .

Сравнение показывает поразительное сходство:

  1. Оба движка используют атрибуты .data для хранения значений и .grad для хранения градиентов .
  2. Оба вызывают метод .backward() для запуска цепочки производных.
  3. Даже логика работы с объектами совпадает: метод .item() в PyTorch просто вытягивает скаляр из тензора, возвращая то самое число, с которым мы работаем в Micrograd .

Таким образом, Micrograd является частным случаем PyTorch, где все тензоры состоят из одного элемента. Это подтверждает, что, понимая работу Micrograd, вы фундаментально понимаете внутреннее устройство главных инструментов глубокого обучения .

Архитектура многослойного перцептрона (MLP) 1:43:57

Нейронные сети — это лишь частный вид сложных математических выражений. Андрей Карпати начинает выстраивать архитектуру MLP «снизу вверх», следуя объектно-ориентированному подходу, принятому в PyTorch.

Первый кирпичик — класс Neuron. В конструкторе нейрон получает веса (weights) — случайные числа от -1 до 1 — и смещение (bias), которое определяет общую «склонность» нейрона к активации . Логика проста: мы вычисляем скалярное произведение весов на входные сигналы ($w \cdot x + b$) и пропускаем результат через нелинейную функцию активации . Ранее в разговоре Карпати уже упоминал использование гиперболического тангенса (tanh) для этих целей.

Следующий уровень — класс Layer (Слой). Это список независимых нейронов, каждый из которых подключён ко всем входным данным . Если мы создаём слой из трёх нейронов с двумя входами, на выходе мы получим список из трёх значений .

На вершине иерархии стоит MLP (Multi-Layer Perceptron). Это последовательность слоёв, где выход предыдущего слоя становится входом для следующего . Карпати реализует MLP так, что он может принимать список размеров слоёв: например, сеть с 3 входами, двумя скрытыми слоями по 4 нейрона и одним выходным нейроном .

Для удобства управления этой махиной Андрей вводит метод parameters(). Он рекурсивно собирает все веса и смещения из каждого нейрона в единый список . В нашем примере с небольшой сетью получилось 41 обучаемое значение . Именно эти 41 параметр мы будем «подкручивать», чтобы заставить сеть работать правильно.

Расчет функции потерь: Mean Squared Error (MSE) 1:51:06

Имея готовую структуру сети, нам нужно научить её решать конкретную задачу. Карпати вводит простейший набор данных (dataset) из четырех примеров: для определённых входных векторов сеть должна выдавать конкретные значения (1.0 или -1.0) . На текущий момент сеть выдаёт случайные числа, и нам нужен способ измерить, насколько она ошибается.

Здесь в игру вступает функция потерь (Loss function). Это единое число, которое количественно выражает «плохость» работы нейросети . Андрей выбирает классическую среднюю квадратичную ошибку (Mean Squared Error, MSE). Процесс её расчёта выглядит так:

В результате мы получаем один узел в графе вычислений — loss. Если значение лосса высокое, сеть работает плохо; если лосс близок к нулю, предсказания идеально совпадают с целями . Когда мы вызываем loss.backward(), происходит «магия»: градиент от этого единственного числа течёт назад через всю структуру MLP, достигая каждого из 41 параметра .

Карпати визуализирует этот процесс: граф вычислений для четырех проходов через MLP и последующего расчёта лосса становится огромным . В этом графе есть данные (входы), которые мы не меняем, и параметры (веса), которые имеют градиенты. Отрицательный градиент на конкретном весе говорит нам о том, что небольшое увеличение этого веса приведет к уменьшению общей функции потерь . Это и есть фундаментальный сигнал, позволяющий нейросети учиться на своих ошибках.

🚀 Градиентный спуск, «забытый» zero_grad и путь к масштабам GPT 2:05:32

Магия итеративного обучения: метод градиентного спуска 2:05:32

После того как все компоненты нейронной сети собраны, а функция потерь определена, начинается процесс, который Андрей Карпати называет сутью машинного обучения — итеративная оптимизация. На этом этапе мы переходим от теоретических расчётов к практике «обучения» весов. Ранее мы рассматривали архитектуру многослойного перцептрона (MLP) и расчет MSE Loss, но теперь эти элементы должны заработать в едином цикле .

Процесс выглядит как бесконечная петля: прямой проход (forward pass), расчет градиентов через обратное распространение (backward pass) и обновление параметров. Андрей Карпати наглядно демонстрирует, как небольшое изменение весов в направлении, противоположном градиенту, заставляет лосс (ошибку) неуклонно снижаться: с 3.66 до 3.47 и далее . Когда предсказания сети начинают приближаться к желаемым значениям (например, к единице там, где она требуется), мы видим, что «магия» работает.

Однако в этом процессе скрыто то, что Андрей называет «тонким искусством» — выбор скорости обучения (learning rate).

Это приводит к «взрыву» функции потерь: сеть становится самоуверенной в неверных прогнозах, и лосс начинает расти вместо того, чтобы падать . Оптимизация — это всегда баланс между скоростью сходимости и стабильностью системы. В конечном итоге, после нескольких десятков итераций, сеть с 41 параметром находит ту конфигурацию весов, которая делает её предсказания практически идеальными .

Критическая ошибка: почему необходимо обнулять градиенты 2:10:22

Даже опытные разработчики не застрахованы от классических ошибок. В процессе демонстрации тренировочного цикла Андрей Карпати обнаруживает «тонкий и очень распространенный баг», который он сам, по собственному признанию, совершал десятки раз . Суть проблемы заключается в механизме накопления градиентов.

В библиотеках вроде Micrograd или PyTorch градиенты не перезаписываются при каждом новом проходе, а суммируются (операция +=). Это было реализовано ранее для корректной работы цепного правила в сложных графах, но в рамках тренировочного цикла это создает катастрофический эффект . Если не «смыть» (flush) старые значения перед новым обратным проходом, градиенты из предыдущих итераций будут накапливаться, искажая направление шага.

Решение этой проблемы — принудительное обнуление градиентов (zero_grad) для всех параметров сети перед вызовом backward() . Андрей Карпати подчеркивает, что в простых задачах сеть может «выкарабкаться» даже с этим багом из-за избыточной мощности, но в реальных проектах отсутствие обнуления сделает оптимизацию невозможной . Это фундаментальный гигиенический навык любого специалиста по ИИ:

  1. Сделать прямой проход.
  2. Обнулить старые градиенты.
  3. Запустить backward.
  4. Обновить веса.

От Micrograd до GPT: масштабирование принципов 2:15:15

Хотя Micrograd — это крошечная библиотека, созданная в образовательных целях, Андрей Карпати акцентирует внимание на том, что современные гиганты вроде GPT работают на абсолютно тех же принципах. Разница заключается лишь в масштабах и некоторых деталях реализации. Если наша игрушечная сеть оперировала 41 параметром, то современные модели содержат сотни миллиардов и даже триллионы параметров .

Когда вы тренируете такую «массивную глыбу нейронной ткани» на огромных массивах данных из интернета, у сети начинают проявляться эмерджентные (внезапно возникающие) свойства . Принципы остаются неизменными:

Андрей проводит краткий экскурс в исходный код PyTorch, чтобы показать, как те же самые операции реализованы в промышленном софте. Найти реализацию производной функции tanh в недрах PyTorch оказывается непросто из-за огромного количества оптимизаций под разные устройства (CPU, GPU), типов данных и ядер CUDA . Тем не менее, глубоко в коде можно обнаружить ту же самую формулу локального градиента (1 - t**2) * grad, которую мы выводили вручную .

В завершение Андрей Карпати резюмирует: нейронные сети — это просто математические выражения. Мы можем заставить этот «кусок нейронной ткани» делать всё что угодно, если правильно настроим функцию потерь и будем последовательно следовать за градиентом . Micrograd дает интуитивное понимание того, как работает «черный ящик» ИИ, убирая завесу магии и оставляя чистую, элегантную математику.

💬 Цитаты

«Нейронные сети — это просто математические выражения. Они не заботятся о том, что это нейросеть, они просто передают градиенты.»

Андрей Карпати 05:10

«Если машина едет в два раза быстрее велосипеда, а велосипед в четыре раза быстрее пешехода, то машина едет в восемь раз быстрее пешехода — это и есть интуиция цепного правила.»

Андрей Карпати 43:16

«Нам не обязательно разбивать всё до самых атомарных составляющих... Главное — чтобы мы знали, как вычислить локальную производную.»

Андрей Карпати 58:27

«Micrograd — это PyTorch, если бы ваши тензоры всегда состояли из одного элемента.»

Андрей Карпати 1:43:32

«Функция потерь — это единое число, которое измеряет общую производительность вашей нейронной сети.»

Андрей Карпати 1:52:25

«Вы просто следуете за градиентной информацией, и это минимизирует лосс. Сеть устроена так, что когда лосс минимизирован, она делает то, что вы от нее хотите.»

Андрей Карпати 2:15:02
👥 Спикер
📖 Термины
Backpropagation
Алгоритм обратного распространения ошибки, используемый для вычисления градиентов в нейронных сетях.
Градиент
Вектор производных, указывающий направление наискорейшего роста функции; используется для корректировки весов сети.
Tanh
Функция активации (гиперболический тангенс), которая сжимает входное значение в диапазон от -1 до 1.
MLP
Multi-Layer Perceptron (многослойный перцептрон), классическая архитектура нейронной сети из последовательных слоев.
Топологическая сортировка
Упорядочивание узлов графа вычислений, гарантирующее, что каждая операция обрабатывается только после своих зависимостей.
Искусственный интеллект Андрей Карпати Micrograd Backpropagation PyTorch Машинное обучение