Градиент по четырём параметрам и его проверка

Градиент для нескольких параметров

У модели времени доставки четыре параметра: три веса при признаках и постоянная часть. Сдвигать надо все четыре — значит, нужно знать наклон функции ошибки по каждому параметру отдельно. Такой набор наклонов называют градиентом.

Для одного параметра производная считается по одной и той же идее:

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?

Для каждого заказа функция должна:

  1. посчитать prediction;
  2. найти разницу между прогнозом и фактическим временем;
  3. возвести эту разницу в квадрат.

После этого нужно взять среднее квадратов ошибок по всем заказам.

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 и посмотреть, как изменится результат проверки.

Числа сравниваются с допуском, а не как строки. Достаточно совпадения первых четырёх знаков.

Печатать результат не нужно: функции должны возвращать значения.


Напишите программу
Auto
       

time 10000 ms
memory 256 Mb
Правила оформления программ и список ошибок при автоматической проверке задач

Статистика успешных решений по компиляторам
 Кол-во
Python4