Цель этого поста — не мотивировать и объяснить softmax, не предоставить лучшую функцию softmax для Julia и не представить язык Julia. Я воздержусь от философских рассуждений о математическом идеале и о том, почему я считаю математику поэтическим занятием. Но, придерживаясь темы моего первого сообщения об этом, я буду неявно иллюстрировать, почему акт занятия математикой сегодня означает принятие формы абстракции в сторону простоты, т.е. постоянно растущая деконструкция элегантности математической функции, доказательства или уравнения.
Занятия математикой на компьютерах и с ними — это постоянно растущая область, и она в корне отличается от того, как мы изучаем математику в школе. А именно, он требует деконструкции красноречивых уравнений, принятия приближений, сведения к дискретизации и кодирования математических примитивов как типов языка программирования.
Функция Softmax находится в центре внимания. Софтмакс - хороший выбор. В предыдущем посте я показал, как можно добраться до softmax, просто поигравшись с числом Эйлера. Нет необходимости начинать с элегантного уравнения и разбивать его на части; вместо этого можно начать, так сказать, с грязного дна и перейти к абстракции (то есть к математическому идеалу).
Softmax также является хорошим выбором, потому что это часто используемая функция в некоторых из самых передовых программ в мире. Приложения машинного обучения уже много лет используют softmax в качестве дифференцируемого argmax для многоклассовой классификации. авангардные фреймворки глубокого обучения, Tensorflow, PyTorch, MXNet, Flux, Gorgonia и т. д. — все используют softmax (обычно для функции потерь на последнем уровне сети, где нужна многоклассовая классификация). По этой причине на softmax можно найти множество ресурсов, в том числе программные реализации, встроенные в передовые программные проекты, о которых я упоминал. Обратите внимание, что в проектах Python (Tensorflow, Pytorch, MXNext) все они зависят от низкоуровневого выполнения C++, что означает, что вы, вероятно, найдете реализации как Python, так и C++, и это будет довольно шумно. Если вы хотите увидеть реализацию в передовом программном проекте, я бы посоветовал либо FluxML (написано на Julia), либо Gorgonia (написано на Go).
Юлия Реализация
Проще всего начать с наивной версии, которая максимально приближена к программному обеспечению того, что можно найти в математических представлениях.
f(x) = exp.(x) ./ sum(exp.(x))
Что мне здесь интересно, так это то, что элегантность математического представления считается наивной с вычислительной точки зрения. Во всех разделах математики и для всех работающих математиков развитие идей в сторону элегантности представления, простоты и интерпретируемости (например, абстракция в сторону простоты, которую я называю в предыдущем посте математическим идеалом) является вершиной математических достижений. Однако в вычислительном мире, где конечные ресурсы являются реальностью, а дискретные значения — необходимостью, такой идеализм представляется наивным.
Это смешное напряжение. Разработчики программного обеспечения также ценят элегантность, простоту и интерпретируемость, но не за счет неработающих вычислений и/или программ с ошибками.
Что не работает в f(x) = exp.(x) ./ sum(exp.(x))? Реализация Julia в конечном итоге переполнит свою числовую емкость, если заданы слишком большие числа.
Python, Go и Rust тоже будут. Любой язык программирования будет. Нужно понимать числовые пределы как языков программирования, так и устройств. В этом случае для Джулии мы видим, что (typemin(Float64), typemax(Float64)
чисел с плавающей запятой равно -Inf, Inf
, что удобно для представления машинного эпсилон eps
, вычисляемого как 2,220446049250313e-16; это наш верхний предел, и функция softmax выше превышает этот числовой предел, если наши числа слишком высоки.
julia> (typemin(Float64), typemax(Float64)) (-Inf, Inf) julia> (typemin(Inf), typemax(Inf)) (-Inf, Inf) julia> eps(Inf) NaN julia> eps(Float64) 2.220446049250313e-16
Чтобы обеспечить числовую стабильность, вычтите наибольшее число из всех остальных. Теперь [1.1, 5.0, 2.8, 7.3
это [1.1-7.3, 5.0-7.3, 2.8-7.3, 7.3-7.3]
.
И последнее, что мы делаем, это предоставляем параметр масштабирования θ(theta), который позволяет нам увеличивать числа. Увеличение расстояния между маленькими и большими числами позволяет легче увидеть относительные границы между большими и маленькими. Мы определили θ = 2,5. Помнить о вычислениях тоже хорошо. То есть мы получим те же результаты, если умножим все наши числа на θ и вычтем на max [два скалярно-векторных умножения, (scores4 * θ .-maximum(scores4 * θ)) ] или, если мы вычтем на max, а затем умножим на θ [одно умножение на скаляр-вектор, (scores4 .- maximum(scores4)) * θ ].
Мы перешли от исходных значений [1.1, 5.0, 2.8, 7.3] к вычтенным максимальным значениям [-6.1999999999999999, -2.3, -4.5, 0.0], к масштабированным и вычтенным максимальным значениям [-15.499999999999998, -5.75, -11.25, 0,0]. Вы можете видеть, что мы немного увеличили относительные расстояния.
Поскольку мы делаем это в Джулии, мы можем помнить о наших типах. Julia предоставляет очень интересные наборы числовых типов, а также векторных и матричных типов. Я могу использовать тип Real
для всех действительных чисел или различные виды абстрактных типов, которые позволяют мне создавать функции, которые могут принимать самые разные типы векторов, матриц, чисел с плавающей запятой и т. д.
У Джулии также есть так называемая множественная диспетчеризация, при которой параметры моих функций будут отправлять различные методы. Это означает, что я могу написать множество различных функций с именем softmax
, которые принимают разные параметры, и компилятор Джулии создаст методы для этих специально определенных функций (на основе параметров). Я могу проверять эти методы на стабильность типов и работать над созданием более быстро выполняющихся программ, используя типы и комбинации типов, которые дают лучший результат компиляции (вышеупомянутые методы показывают Union, мы, вероятно, не хотим этого, и для прохода оптимизации мы, вероятно, немного рефакторинг в зависимости от наших целей для программы).
Цель этого поста — не мотивировать и объяснить softmax, не предоставить лучшую функцию softmax для Julia и не представить язык Julia. Большинство программ, нуждающихся в softmax, уже реализованы, и они реализованы способами, которые лучше всего соответствуют целям программы, свойствам языка программирования и типам устройств и инженеров по аппаратному обеспечению, которые ожидают работать.
Вместо этого цель здесь состоит в том, чтобы подчеркнуть, насколько далека эта реализация softmax от идеального представления, которое можно найти в математических текстах. Рассматривая систему типов Джулии, то, как компилируются функции и методы, и числовые возможности этого конкретного языка, мы далеко ушли от простоты и элегантности уравнения.
Вероятность того, что кто-либо из нас будет заниматься эффективной математикой в реальном мире на бумаге, довольно низка (за исключением обучения и теории более высокого порядка). А если вы занимаетесь прикладной или вычислительной математикой, то вы, вероятно, используете бумагу для обучения, заметок, идей, набросков и личного удовольствия. Ни один из них не заменит программное обеспечение. А в программном обеспечении разрыв между математическим идеалом и реальным вычислимым значением очень велик.
Другие ресурсы для softmax
Честно говоря, Softmax в наши дни повсюду. Просто пройдите курс Udacity или Coursera по науке о данных или глубокому обучению, возьмите любую недавнюю книгу по глубокому обучению или даже более старые книги по машинному обучению (случайно взял две книги в своем офисе, опубликованные в 2009 и 2004 годах соответственно, обе имеют записи в приложение для softmax).
Но если вы хотите быстро прочитать и/или освежить в памяти… некоторые ресурсы я рекомендую; две записи в блоге:
Функция Softmax и ее производная — веб-сайт Эли Бендерски
В литературе по машинному обучению термин «градиент обычно используется для обозначения производной. Строго говоря, градиенты — это…eli.thegreenplace.net»
подборка из онлайн-книги: