Метод прямого распространения в подробностях, часть 2

Исходящие вероятности нейронной сети, конечно же, будут числами между нулём и единицей, поскольку они являются прогнозом, а не определённой величиной. Но цель в том и состоит, чтобы после обучения вероятность истинной целевой переменной была выше, чем вероятность любой другой метки. К примеру, целевой показатель может иметь вид [0, 0, 1, 0, 0], что означает, что целевая переменная помечена категорией 2, и после обучения мы хотим, чтобы исходящий прогноз имел вид вроде [0.1, 0.1, 0.5, 0.2, 0.1], так чтобы максимальная вероятность соответствовала целевой метке. Это и будет целью во время обучения модели.

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

К примеру, выделенная ячейка таблицы показывает вероятность того, что Y1 при заданном X1 принадлежит к категории 0. Следующая ячейка показывает вероятность того, что Y2 при заданном X2 принадлежит к категории 1.

Разберём простой пример, чтобы определить, соответствуют ли прогнозы целевым переменным. Слева у нас матрица вероятностей, представляющая прогнозы. Заметьте, что сумма значений в каждой строке равна единице. Справа у нас целевые переменные. Технически говоря, и здесь сумма значений в каждой строке равна единице, поскольку это также вероятности.

В первой строке 0,7 является максимальным числом и соответствует расположению единицы в таблице целевых переменных. Следовательно, этот прогноз правильный. Во второй строке максимальным значением является 0,4 и находится оно в левом столбце. Но оно соответствует нулю в таблице целевых переменных, а потому этот прогноз неверен. В третьей строке максимальным значением является 0,6. Оно находится в правом столбце и соответствует единице в таблице целевых переменных, поэтому и этот прогноз правилен. В четвёртой строке максимумом является 0,5 и соответствует нулю в таблице целевых переменных – следовательно, прогноз неверен.

В целом у нас два правильных ответа из четырёх. Этот показатель называется коэффициентом классификации или точностью классификации. В данном случае он равен 50%.

Обратите внимание, что если бы мы писали код для этой операции, то на самом деле нам пришлось бы использовать функцию argmax, а не max. Не забывайте, что функция max возвращает наибольшее значение в массиве, тогда как argmax указывает положение наибольшего значения. В Numpy это можно реализовать следующим образом:

prediction_labels = np.argmax(softmax_outputs, axis=1)

target_labels = np.argmax(target_indicator, axis=1)

accuracy = sum(prediction_labels == target_labels)/N

Запись axis=1 означает, что мы берём argmax вдоль столбцов, а не по всей матрице.

Важно отметить, что функция argmax является обратным преобразованием массива целых чисел со значениями от 0 до K-1 в матрицу показателей. Поэтому один из способов убедиться в том, что прямое кодирование правильно – это взять уже закодированные метки и использовать функцию argmax. Результат должен быть равным первоначальным данным:

Y == argmax(convert2indicator(Y), axis=1)

Теперь, когда вы уже знаете, как преобразовать список целевых переменных в матрицу показателей, давайте проделаем то же с нашим первоначальным набором данных. Напомним, что у нас было три примера и два класса, что значит, что наша матрица показателей должна иметь размерность 3x2.

Поскольку пример №1 имеет метку 1, то в матрице показателей единица должна быть в элементе с индексом 1. Так как пример №2 также имеет метку 1, то в матрице показателей единица также должна быть в элементе с индексом 1. И поскольку пример №3 имеет метку 0, то в матрице показателей единица должна стоять на месте элемента с индексом 0.

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

Тут нужно всегда иметь в виду кажущуюся несколько странной нумерацию. Действительно, некоторые вещи мы считаем, начиная с нуля, – например, классы нумеруются 0, 1, 2 и так далее. Другие же вещи мы считаем, начиная с единицы – например, объекты x1 и x2 или z1, z2, z3. Всё это осложняется ещё и тем обстоятельством, что некоторые языки, такие как MATLAB, фактически пользуются нумерацией, начинающейся с единицы, тогда как большинство компьютерных языков используют нумерацию, начинающуюся с нуля.

Итак, почему же мы так поступаем? Это связано с тем, что машинное обучение представляет собой сочетание математики и программирования. Если вы возьмёте какой-либо учебник по математике или статистике, то увидите, что счёт там начинается с единицы. В частности, если идёт суммирование, то вы увидите, что в большинстве случаев я считают от 1 до n, а не от 0 до n – 1. Но если вы возьмёте какой-либо учебник по программированию, вы заметите, что счёт там начинается с нуля. Поскольку машинное обучение является сочетанием и того, и другого, вы должны уметь пользоваться обеими системами отсчёта. Обычно, когда мы рассматриваем уравнения, мы начинаем счёт с единицы, но если мы говорим о деталях программной реализации, мы начинаем отсчёт с нуля. Что касается меток, то они нужны для индексации массивов, и поэтому мы начинаем счёт с нуля.

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

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

Не забывайте, что теперь у нас два исходящих узла, поэтому матрица весовых коэффициентов V должна иметь размерность 3x2, а свободные члены c, которые добавляются к каждому исходящему узлу, теперь представлены вектором размерности 2. Поскольку мы не знаем значений этих весовых коэффициентов, для определённости я установил их следующими:

Для данного конкретного примера мы вновь воспользуемся первым наблюдением, когда

Поскольку значения W и b также остались прежними, то и значение z остаётся неизменным:

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

Выражение для функции активации, связанного с исходящим узлом, который относится к классу 1, равно

Не забывайте, что эти a – это то, что мы получаем непосредственно перед подстановкой в функцию софтмакс.

Итак, вычислим наши a1 и a2:

Теперь, имея значения a1 и a2, мы можем вычислить значения функции софтмакс и, следовательно, исходящие вероятности:

Неудивительно, что мы получили такие же вероятности, как и прежде, поскольку я специально подобрал соответствующие значения V и c. Как вы помните, в лекции «Сигмоида vs. софтмакс» я показал, как выбрать такие исходящие весовые коэффициенты, чтобы использование функции софтмакс было эквивалентно использованию сигмоиды, поэтому вы, вероятно, захотите просмотреть эту лекцию и убедиться, что мои новые значения V и c следуют указанному правилу.

Как и прежде, мы могли всё это сделать за один раз, используя векторную операцию:

Если вы хотите самостоятельно убедиться в правильности такого решения, вы можете вывести его вручную на бумаге.

И конечно, мы можем пойти ещё дальше и вычислить исходящую величину для всех примеров одновременно. На этом месте выражение начинает походить на выражение с сигмоидой, только вместо сигмоиды стоит софтмакс:

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

Напомним, что если размерность наших входных признаков равна D, а количество скрытых единиц равно M, то W должно быть матрицей размерности DxM. При этом поскольку свободный член b прибавляется к произведению WX, то он должен быть вектором размерности M. Таким образом, если мы пытаемся вычислить исходящую величину для пакета из N примеров, произведение XW будет иметь размерность NxM.

Итак, что же будет после того, как мы перемножим X и W? Прежде всего напомним, что такое матричное умножение допустимо, так как внутренние размерности, равные D, совпадают с обоих сторон. Результатом этого произведения будет матрица размерностью NxM. Но b при этом является лишь вектором размерности M!

Напомним, что сложение матриц является поэлементным. Это значит, что когда вы складываете две матрицы, они должны иметь одинаковую размерность. Поэтому операция XW + b, о которой мы говорили ранее, математически некорректна. Чтобы всё получилось и свободный член b можно было добавить ко всем N входным примерам, мы должны «перевернуть» b, чтобы он стал горизонтальным, и повторить всё это N раз. Это даст нам матрицу размерности NxM, с которой можно проводить операцию сложения:

К счастью, библиотека Numpy не требует для этого явного указания и сразу понимает, что должна добавить одно и то же значение b к каждой строке результата, так что в конечном счёте никакой дополнительной работы не требуется, достаточно написать

X.dot(W) + b

Понравилась статья? Поделить с друзьями:
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: