Градиент для нескольких параметров
У модели времени доставки четыре параметра: три веса при признаках и постоянная часть. Сдвигать надо все четыре — значит, нужно знать наклон функции ошибки по каждому параметру отдельно. Такой набор наклонов называют градиентом.
Для одного параметра производная считается по одной и той же идее:
2 · error · значение признака при этом параметре
Здесь error = prediction - fact — разница между прогнозом модели и правильным ответом.
Каждый вес стоит при своём признаке: p[0] при километрах, p[1] при светофорах, p[2] при количестве свободных курьеров.
У свободного члена p[3] отдельного признака нет. Для него можно считать, что признак всегда равен 1. Поэтому его производная содержит просто 2 · error.
Формулу легко записать с ошибкой: забыть двойку, перепутать знак или взять не тот признак. При этом программа может запускаться и даже показывать уменьшающийся loss, хотя параметры будут изменяться неправильно.
Поэтому при разработке новой формулы градиента её полезно сверять с численным градиентом — способом «горячо-холодно» из прошлого задания. Численная производная получается совсем другим способом: функция потерь вычисляется в двух близких точках. Если результаты совпадают, это хороший признак того, что формула записана правильно.
Подробно это разобрано в учебнике: «Все веса сразу».
Задание
Ниже приведена база выполненных заказов. Первые три числа каждой строки — признаки заказа: километры, количество светофоров и количество свободных курьеров рядом. Четвёртое число — фактическое время доставки в минутах.
База уже находится в тестирующей системе, поэтому объявлять её в решении не нужно.
baza = [
(4.0, 2, 6, 20.0),
(2.0, 5, 4, 19.5),
(6.0, 1, 2, 26.5),
(1.5, 3, 8, 13.0),
(5.0, 0, 5, 20.5),
(3.0, 4, 1, 22.5),
(7.5, 2, 3, 32.0),
(2.5, 6, 7, 21.0)
]
Модель
Модель предсказывает время доставки по формуле:
prediction = p[0] · km + p[1] · traffic_lights + p[2] · couriers + p[3]
Последний параметр p[3] — свободный член модели: постоянная часть времени, одинаковая для всех заказов. Например, это может быть время на сборку заказа и передачу его курьеру.
Каждому параметру соответствует свой множитель:
p[0] — km;
p[1] — traffic_lights;
p[2] — couriers;
p[3] — 1.
Как получить градиент
Для одного заказа сначала найдите:
error = prediction - fact
Вклад этого заказа в четыре производные равен:
2 · error · km — для p[0];
2 · error · traffic_lights — для p[1];
2 · error · couriers — для p[2];
2 · error · 1 — для p[3].
В нашей базе 8 заказов. Поэтому для каждой производной сначала получится 8 вкладов — по одному от каждого заказа. Затем эти 8 значений нужно усреднить.
Важно: сначала умножьте ошибку каждого заказа на соответствующий ему признак, и только потом берите среднее. Нельзя сначала найти среднюю ошибку по всей базе, а затем умножить её на параметр.
Также обратите внимание: в формуле производной множителем является не сам параметр p[i], а признак, при котором этот параметр стоит в формуле модели.
Четвёртый столбец базы — minutes, то есть правильный ответ. Это не четвёртый признак модели. Для свободного члена p[3] множитель равен 1.
Что нужно написать
Напишите две функции:
def loss4(baza, p):
# вернуть одно число — средний квадрат ошибки модели
...
def grad4(baza, p):
# вернуть четыре числа — производные по p[0], p[1], p[2], p[3]
...
Функция loss4
loss4(baza, p) отвечает на вопрос: насколько велика общая ошибка модели с параметрами p?
Для каждого заказа функция должна:
- посчитать
prediction;
- найти разницу между прогнозом и фактическим временем;
- возвести эту разницу в квадрат.
После этого нужно взять среднее квадратов ошибок по всем заказам.
loss4 возвращает одно число — MSE модели.
Функция grad4
grad4(baza, p) показывает, как изменяется loss при изменении каждого из четырёх параметров модели.
Для каждого заказа сначала нужно найти:
error = prediction - fact
Затем найти его четыре вклада:
2 · error · km;
2 · error · traffic_lights;
2 · error · couriers;
2 · error.
После обработки всех заказов отдельно усредните вклады для p[0], p[1], p[2] и p[3].
Поэтому grad4 должна вернуть ровно четыре числа.
Почему нельзя получить grad4 только из loss4
loss4 и grad4 делают разную работу.
loss4 показывает величину общей ошибки модели;
grad4 показывает четыре производные — по одной для каждого параметра.
При вычислении MSE ошибки возводятся в квадрат. Поэтому знак отдельных ошибок теряется: ошибки -2 и 2 обе дают квадрат 4.
Но для градиента знак важен. Поэтому внутри grad4 нужно снова посчитать прогнозы и ошибки отдельных заказов.
Как проверить себя
Времена в базе построены по правилу с параметрами [3, 1.5, -0.5, 8].
Поэтому:
loss4(baza, [3, 1.5, -0.5, 8])
должна вернуть значение, равное нулю.
Дополнительная проверка градиента
Формулу градиента можно независимо проверить численным способом.
Возьмите h = 1e-5. Для каждого из четырёх параметров измените только его: один раз на +h, другой раз на -h.
Численная производная считается так:
(loss_plus - loss_minus) / (2 · h)
Полученное число должно почти совпасть с соответствующим элементом grad4. Небольшое расхождение порядка 1e-9 нормально: это машинная погрешность.
Для эксперимента можно убрать из формулы градиента множитель 2 и посмотреть, как изменится результат проверки.
Числа сравниваются с допуском, а не как строки. Достаточно совпадения первых четырёх знаков.
Печатать результат не нужно: функции должны возвращать значения.