Линейная регрессия и градиентный спуск

Для начала ответ на главный вопрос: а где мне, дизайнеру, пригодится линейная регрессия? Она популярна для решения прикладных задач, вроде прогнозирования. Это в первую очередь таргетированная реклама, нейрон в слоях классификации, простенькие системы скоринга для банков. В общем, любые задачи, где отношения между переменными линейны по своей природе: как возраст влияет на здоровье или сколько операторов в банке справляются с поставленными KPI. Или есть ли связь между продажаи и затратами на таргет, какие площадки больше всего влияют на продажи, линейна ли наблюдаемая зависимость.

Линейная регрессия позволяет прогнозировать зависимость переменной Y на основе переменной X. Визуализируя эту зависимость на графике, получается прямая линия, часто называемая «линия наилучшего соответствия». Линейная регрессия весьма проста, мы прогнозируем Y с учетом X. Метрическая модель отвечает на метрическую гипотезу, или целевая переменная линейно зависит от признаков объектов. Типовая задача это прогноз выручки за год магазина, где гипотеза это зависимость количества магазинов и объема выручки. В общем, модель про деньги, поэтому очень популярна в банках: выдавать кредиты надо cумом, каждый параметр влияет на решение о кредите (заработок, дети, просрочки, стаж, состояние здоровья, риск дефолта).

Модели линейной регрессии используются для демонстрации или прогнозирования взаимосвязи между двумя переменными или факторами. Прогнозируемый фактор (фактор, для которого решается уравнение) называется зависимой переменной. Факторы, которые используются для предсказания значения зависимой переменной, называются независимыми переменными. Линейный регрессионный анализ используется для прогнозирования значения переменной на основе значения другой переменной.

Задача регрессии это минимизация ошибки. По математическому условию, два фактора, участвующие в простом линейном регрессионном анализе, обозначаются x и y. Уравнение, описывающее связь y с x, известно как регрессионная модель. Линейная регрессионная модель также содержит термин ошибки, который представлен Ε или греческой буквой «эпсилон». Термин ошибки используется для учета изменчивости в y, которая не может быть объяснена линейной зависимостью между x и y.

Интереснее работать с категориальными вещественными переменными, но пока что для простоты будем работать только с цифрами. Итак, у нас есть отдел дизайна с 18 дизайнерами, и мы хотим узнать, кто из наших дизайнеров работает наиболее эффективно. Для вычислений будем использовать NumPy. Типичная задача линейной регрессии это определить непрерывную переменную. Если зависимая переменная не непрерывная, тогда это задача классификации, а не регрессии. Я буду рассматривать простые линейные модели, так как обычно хватает простого суммирования значений признаков с некоторыми весами.

Условие задачи: я, как руководитель дизайн-отдела, хочу понять, кто из моих сотрудников самый эффективный. Все работы дизайнеров тестируются по метрике SUM и я точно знаю, у кого какое качество дизайна. Теперь мне надо правильно соотнести количество лет опыта и результативность работы.

В рамках статьи и в рамках терминологии машинного обучения мы будем называть наши наблюдения признаками. Признак это любая характеристика исследуемых данных, выраженная числом. Для начала укажем кол-во лет опыта у специалистов и посмотрим на форму данных командой командой x.shape. У нас всего 18 наблюдений (18 дизайнеров), это один набор признаков. Для добавления второго набора признаков создаем двухмерный массив NumPy. Второй набор данных это результативность (SUM), некий средний балл в диапазоне от 0 до 100. Весь наш набор данных будет называться «признаковым описанием». Внесли данные и визуализировали:

import numpy as np
x = np.array([[1,3,4,5,11,0,8,6,3,7,16,0,2,3,2,4,21,4]])
print (x)
print (x.shape)
y = np.array([[32,54,54,35,86,12,74,67,35,75,94,12,56,54,40,35,87,47]])
print (y)
print (y.shape)
 
import matplotlib
matplotlib.use('TkAgg')
from matplotlib import pyplot as plt
plt.scatter(x,y)

По горизонтали опыт в годах, по вертикали результат SUM. Даже на глаз некая логика в этих данных есть: чем меньше опыта, тем хуже результат работы, по мере увеличения X растет и Y. Сразу возникает желание использовать эти данные об результативности специалиста для прогнозирования, на сколько ему надо повышать зарплату, или уменьшать. Так как начало данных не из 0/0, то нужен интерсепт. Существует понятия сдвига (intercept) и наклона (slope), иногда их называют коэффициентами и параметрами.

Интерсепт это тоже признак, некий сдвиг в данных, пропишем его как x0. Можно использовать и Байеса, у него вполне схожее поведение, но в данном случае он был бы неуместен (не путать с теоремой Байеса). Немного облегчим себе задачу и представим, что у нас нет кривой насыщения, так как у опытных специалистов ранние работы априори плохие, поэтому мы взяли SUM только за последний год. Если человек имеет 0 лет опыта работы, то мы взяли данные за несколько месяцев.

Так как у нас линейная регрессия, попробуем на глаз подобрать вес для признака и интерсепт, и нарисовать по ним линию. Интерсепт может быть 15, а какой наклон? Наклон сложно определить из-за разного масштаба по горизонтали и вертикали. Но попробуем на глаз 3, 4, 5:

import numpy as np
import matplotlib
matplotlib.use('TkAgg')
from matplotlib import pyplot as plt
 
y = np.array([[32, 54, 54, 35, 86, 12, 74, 67, 35, 75, 94, 12, 56, 54, 40, 35, 87, 47]])
print(y)
print(y.shape)
 
X = np.array([[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,3,4,5,11,0,8,6,3,7,16,0,2,3,2,4,21,4]])
plt.scatter(X[1], y)
plt.plot(X[1], 15*np.ones(18) + X[1]*4)
plt.show()

Как это считывать: линия регрессии может показывать положительную линейную зависимость, отрицательную линейную зависимость или не показывать никакой связи. Если линия не наклонная, связь между двумя переменными отсутствует. Если линия регрессии идет вверх, мы имеем положительную линейную зависимость. Если линия регрессии наклоняется вниз, имеется отрицательная линейная зависимость. При этом линия стремится выше предела в 100, это решается клипированием. Более того, правильно клипировать и отрицательные значения. Если применять деревья, то там тоже значения могут выскакивать за пределы возможных, но это целиком зависит от алгоритма.

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

y_pred1 = 15*np.ones(18) + X[1]*4
print (y_pred1)
err = np.sum(y - y_pred1)
print (err)

Получаем число 179. И это весьма большое значение, значит, мы слишком мало предсказывали. Отрицательное значение значило бы перепрогноз, это более желанный результат. Лучше переработать, чем недоработать. Сейчас же мы можем ошибиться в сторону недопрогноза, и бизнес это не устроит. Есть еще один значимый недостаток: средняя ошибка равна нулю, она плоха тем, что мы можем иметь 2 наблюдения, которые отклоняются в разные стороны на одинаковое число, и при подсчете суммы они обнулятся (-1 +1 = 0). Такие противоположные объекты с одинаковым признаковым описанием скажут нам, что наша модель очень качественная. Хотя в модели могут быть сильные отклонения. Поэтому все разности приводятся к одному знаку с помощью модуля (ошибка MAE), либо все возводится в квадрат, это убирает знак минус. MAE это метод оценки параметров модели. В MAE модули не сильно увеличивают отклонения, считающиеся выбросами. Такая оценка будет более робастная и похожая на медиану, чем MSE.

Итак, регрессия это целевая переменная, например, потенциальная выручка магазина. И нам надо правильно выбрать функци потерь. MSE это квадратичная функция потерь, считаем матрицу Гессе и понимаем, что она удовлетворяет достаточному условию минимума. Но сейчас мы посмотрим на MAE, так как она дифференцируема,

def calc_mse(y, y_pred):
    err = np.mean((y - y_pred) ** 2)
    return err
 
print (calc_mse (y, y_pred1))
 
 
def calc_mae(y, y_pred):
    err = np.mean(np.abs(y - y_pred))
    return err
 
print (calc_mae(y, y_pred1))
X — матрица наблюдений и признаков размерности строк на столбцов. y — ответ (нужно предсказать).

Функции и формула для подсчета выше, принцип подсчета MAE следующий: np.sum(np.abs(y - y_pred1)) / 18, получаем 14.944444444444445. Тут остановлюсь чуть подробнее, MAE (Mean Absolute Error) это среднее абсолютное отклонение (mean absolute error = MAE), такой модуль отклонения устойчив к выбросам. Однако функция модуля не имеет производной в нуле, и её оптимизация может вызывать трудности. Поэтому для измерения отклонения можно просто посчитать квадрат разности.

И тут мы плавно переходим к среднеквадратичному отклонению (mean squared error, MSE), или квадратичная ошибка MSE: np.mean((y - y_pred1) **2), среднее берем вместо суммы, не будем усложнять расчет. Суть метода: минимизация суммы квадратов отклонений фактических значений от расчётных. Применяем, когда намн ужно решить задачу регрессии. Полученную сумму делим на число наблюдений, и получаем MSE. MSE лучше показывает ошибки, чем MAE. Итак, MSE = 326.1666666666667 и MAE = 14.944444444444445. По этим двум точкам строится линия, значит, эта модель (она же линия) будет работать лучше для точек на графике. И именно поэтому регрессия у нас линейная, т.к. ровная линия, которая точнее всего описывает зависимость в данных.

Это был способ на глазок, но куда приятнее автоматизировать процесс. Ведь новые показания SUM приходят каждый месяц и хочется отслеживать рост молодых дизайнеров и актуальность опытных специалистов. Нам нужна формула для автоматического подсчета двух коэффициентов, тут придет на выручку метод наименьших квадратов (МНК). Для работы понадобится матрица, которая по горизонтали и вертикали будет равняться количеству признаков и по форме будет квадратная (18×18). Если у нас 18 столбцов и 18 строк, то эти две матрицы можно перемножать. Итоговая матрица берется по количеству признаков, 2×2.

Вводим команду X.shape, узнаем, что у нас матрица (2, 18) . А команда X.T.shape говорит нам, что матрица (18, 2). Значит, эти две матрицы можно перемножать. Командой np.dot(X, X.T) получаем матрицу 2×2 по количеству признаков: [[ 18 100][ 100 1076]].

Следующим шагом находим из квадртаной матрицы обратную матрицу print (np.linalg.inv(np.dot(X, X.T))), результат [[ 0.11485909 -0.01067464] [-0.01067464 0.00192143]].

W = np.linalg.inv(np.dot(X, X.T)) @ X @ y.T
print (W)

И последний шаг, умножаем на транспонированную матрицу и умножаем на y. И мы получаем набор весов. Это два числа, которые ранее мы пытались угадать на глаз, в этот раз их угадала система:[[32.48548249] [ 3.64261315]]. Мы же предположили, что интерсепт 35 и 5. На практике я бы применил частные производные для вывода формулы, но в рамках нашего примера сойдет и метод наименьших квадратов.

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

X = np.array([[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,3,4,5,11,0,8,6,3,7,16,0,2,3,2,4,21,4]])
y = np.array([[32,54,54,35,86,12,74,67,35,75,94,12,56,54,40,35,87,47]])
 
print (X.shape)
print (X.T.shape)
print (np.dot(X, X.T))
print (np.linalg.inv(np.dot(X, X.T)))
W = np.linalg.inv(np.dot(X, X.T)) @ X @ y.T
print (W)
 
plt.scatter(X[1], y)
plt.plot(X[1], 15*np.ones(18) + X[1]*4)
plt.plot(X[1], W[0] + W[1] * X[1])
 
plt.show()
Оранжевая линия основана на значениях, которые мы подсчитали с использованием весов.

На примере выше видно, что построенная машиной линейная регрессия более жестко отсеивает людей, чем сделал бы это я «на глаз». Это элементарный пример использования машинного обучения для решения бизнес-задач. Теперь подсчитаем новые MAE и MSE для свеженайденных значений:

y_ml=W[0] + W[1] * X[1]
print (calc_mse (y, y_ml))
print (calc_mae(y, y_ml))

Ранее MSE = 326.1666666666667 и MAE = 14.944444444444445, сейчас MSE = 174.00144700635732 и MAE = 11.478128854730052. Уже куда лучше (помним, чем значение ближе к нулю, тем лучше).

Помимо MSE и MAE, есть логарифмическая функция потерь MSLE и MAPE как средняя средняя абсолютная ошибка в процентах.

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

Итак, метод наименьших квадратов считает хорошо, и главное, он делает это быстро. Но постоянно его применять не получится, альтернатива: вместо метода наименьших квадратов (МНК) часто применяется градиентный спуск. Градиент это итеративный метод. Он показывает направление наискорейшего возрастания функции. Есть и обратная история: антиградиент, он показывает наибольшее убывание. У МНК есть проблема: он плох при большой размерности X, а точнее, когда много данных. Градиентный спуск не уступает по точности МНК. Принцип его работы простой, сначала он находит плохое решение, потом лучше, еще лучше, еще лучше, и так до наименьшего значения ошибки. Правило: много данных = градиентный спуск. В современном мире выбор алгоритма сильно завязан на производительность, МНК сразу оценивает весь объем данных, а стохастический градиентный спуск позволяет подавать данные батчами. Это помогает экономить оперативную память, которой всегда не хватает. Хотя и МНК, и градиентный спуск легко превращают любую железку в обогреватель. А стохастический градиентный спуск (SGD) нужен когда данные не помещаются в оперативку, и данные нужно разбивать. Это позволяет быстрее достичь глобального минимума, так как в итерациях будет участвовать не весь набор данных. Очевидный недостаток это не попадание в настоящую точку минимума, но в общем то, уровень точности приемлемый. Все по аналогии с веб-аналитикой: есть семплирование, есть и проблемы.

Давайте пощупаем в деле градиентный спуск. Он перебирает разные варианты, и двигается к поставленной цели. Матрицы позволяют сразу выполнять много операций. В математическом анализе есть тема поиска частных производных итеративным перебором весов в MSE. Этим и займемся. У нас одна переменная, но мы добавили инетрсепт и это вес для псевдо-переменных. Получается два признака: инсерсепт и вес признака опыта. Классический градиентный спуск применяется редко, чаще выбирают стохастическую оптимизацию.

Процесс минимизации ошибки в градиентном спуске строится на трехмерном пространстве, образованного за счет двух значений весов (W0 = интерсепт и slope(m) опыт). Мы можем задать начальные веса -1 и 1. Найдем производную для каждого веса векторным способом: где W[0] одновременно и интерсепт и вес. Это вес при псевдо-признаке 1.

import numpy as np
X = np.array( [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 3, 4, 5, 11, 0, 8, 6, 3, 7, 16, 0, 2, 3, 2, 4, 21, 4]])
y = np.array([[32, 54, 54, 35, 86, 12, 74, 67, 35, 75, 94, 12, 56, 54, 40, 35, 87, 47]])
W = np.linalg.inv(np.dot(X, X.T)) @ X @ y.T
 
W = np.array([1, 0.75])
gradient_form_direct = 1/18 * 2 * np.sum(X[0] * W[0] - y[0])
print(gradient_form_direct)

Мы получаем значение -103.44 , и это не очень хорошо. С таким огромным шагом мы можем просто проскочить нужное нам минимальное значение. Для уменьшения длины шага, мы с каждой итерацией меняем альфу, например, делим на номер итерации. Давайте добавим альфу как скорость обучения, введя alpha = 1e-5 (10 в -5 степени, или 1/10^5). Если видим минус в степени, то отсчет нулей идет назад = — 0,00001.

Значения могут сильно различаться, но после стандартизации/нормализации веса будут предсказуемы: 1,2,10, но точно не 1000. Напомню, W[0] это и интерсепт, и вес одновременно. Вес при псевдо-признаке 1.

alpha = 1e-5
gradient_form_direct = alpha * (1/18 * 2 * np.sum(X[0] * W[0] - y[0]))
print(gradient_form_direct)
print(W[0] - gradient_form_direct)

Получили 1.001, с этим уже можно работать. Теперь надо указать минимальное значение, и если ошибка приблизилась к нему, то останавливаем алгоритм. Это называется доходимость. Или ручками задаем фиксированное количество шагов, пожалуй, так мы сейчас и поступим. Есть еще нюанс, что при малом количестве данных мы сделаем больше вычислений, чем сделали бы при МНК, но давайте симулировать ситуацию с большими данными на игрушечных данных:

import numpy as np
 
X = np.array( [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
               [1, 3, 4, 5, 11, 0, 8, 6, 3, 7, 16, 0, 2, 3, 2, 4, 21, 4]])
y = np.array([32, 54, 54, 35, 86, 12, 74, 67, 35, 75, 94, 12, 56, 54, 40, 35, 87, 47])
W = np.array([1, 0.5])
 
 
 
gradient_form_direct = 1e-4 * (1/18 * 2 * np.sum(X[0] * W[0] - y[0]))
print(gradient_form_direct)
print(W[0] - gradient_form_direct)
 
for i in range(2000):
  gradient_form = np.dot(W, X)
  W -= (1e-2 * (1/18 * 2 * np.dot((gradient_form - y), X.T)))
  if i % 200 == 0:
    print(i, W)

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

Получаем 32.4854816 и 3.64261324. В практических задачах обычно никто не стремится доказать существование глобального минимума. MSE или log loss ограничены снизу нулём, и чем ближе к нулю, тем больше «ок, сойдет». Просто доказать существование глобального минимума то можно, а достигнуть его куда сложнее. Для особо пытливых умов можно взять альтернативу градиентному спуску, это K-FAC. Или увеличить коэффициент альфа и тогда будет нужно меньше итераций, например 1e-2 и увеличить количество итераций. Так мы дойдем до тех же значений, что мы получили мне помощи МНК. Зачастую используют сразу несколько градиентных спусков, они сходятся к разным точкам, находят разные лучшие начальные приближения, так удается найти лучшую точку минимума. Градиентный спуск имеет линейную скорость сходимости, это вариант более честный. Стохастический — сублинейную скорость, это позволяет не очень долго считать, вопрос мощностей для расчета и количества данных.

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

Добавим новый фактор — месячную зарплату.

X = np.array( [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
               [1, 3, 4, 5, 11, 0, 8, 6, 3, 7, 16, 0, 2, 3, 2, 4, 21, 4],
               [65000, 80000, 85000, 75000, 120000, 25000, 65000, 65000, 29000, 36650, 260000, 12000, 35000, 45600, 25000, 65000, 175000, 73000]])

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

Гипотеза простая — чем больше платим человеку, тем лучшего результата работы мы от него ожидаем. Все признаки разномасштабные. Коэффициенты зависят от масштаба, и по коэффициентам мы не поймем значимость признаков при разном масштабе + не все алгоритмы это смогут обработать. Данные нужно нормализировать (интервал 0 до 1), после этого по весам регрессии можно будет судить, насколько важны признаки. При этом форма даных не будет изменена, просто поменяются среднее и дисперсия. Находим для каждого признака минимум и максимум: print (X[1].min(), X[1].max()), print (X[2].min(), X[2].max()). Получаем 0-21 и 12000-260000.

Нормализация по шагам: (X[1].max() -X [1].min()) получаем размах, максимальный опыт у дизайнеров 21 год. Смотрим на минимальный опыт (X[1] -X [1].min()), получаем [ 1 3 4 5 11 0 8 6 3 7 16 0 2 3 2 4 21 4], минимальный опыт ноль. И приводим все к нужному нам диапазону (X[1] - X[1].min()) / (X[1].max() -X[1].min()), результат вида [0.04761905 0.14285714 0.19047619 0.23809524 0.52380952 0. 0.38095238 0.28571429 0.14285714 0.33333333 0.76190476 0. 0.0952381 0.14285714 0.0952381 0.19047619 1. 0.19047619]. Теперь сделаем это для двух признаков:

X_norm = X.copy()
X_norm = X_norm.astype(np.float64)
X_norm[1] = (X[1] -X[1].min()) / (X[1].max() -X [1].min())
X_norm[2] = (X[2] -X[2].min()) / (X[2].max() -X [2].min())
print(X_norm)

А теперь стандартизация: мы пытаемся получить стандартное нормальное распределение, это диапазон от -1 до 1 с 0 в середине, и большинство значений скопятся в районе нуля. Так мы сможем привести все признаки к одному масштабу. Это важно для линейных моделей, так как они не смогут работать одновременно с наборами признаков: количество арбузов 100-600, и количество семечек в мандаринах 10000-30000. Также критично для метода ближайших соседей (kNN). Поэтому лучше наши данные заранее привести к виду, когда признаки находятся в примерно одном масштабе.

Смотрим на графики

import matplotlib
matplotlib.use('TkAgg')
from matplotlib import pyplot as plt
plt.hist(X[1], alpha=0.6, color='g')
 
'''либо'''
import matplotlib.pyplot as plt
plt.hist(X[1], alpha=0.6, color='g')

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

Находим среднее по первому признаку X1_mean = X[1].mean(), получаем 5.55. Это средний стаж работы дизайнера. Теперь среднее квадратичное отклонение X1_std = X[1].std(),

X1_mean = X[1].mean()
print(X1_mean)
 
X1_std = X[1].std()
print(X1_std)
 
X_standarted = X.copy().astype(np.float64)
X_standarted[1] = (X[1] - X1_mean) / X1_std
print(X_standarted)

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

def calc_feature_std(x):
    result = (x - x.mean()) / x.std()
    return result
X_standarted[2] = calc_feature_std(X[2])
print(X_standarted[2])
[-0.15764177 0.09837795 0.18371785 0.01303804 0.78109718 -0.84036101 -0.15764177 -0.15764177 -0.77208908 -0.64151903 3.17061451 -1.06224476 -0.6696812 -0.4887606 -0.84036101 -0.15764177 1.71983613 -0.02109792]

Данные стандартизировались. Алгоритмам проще работать с признаками в стандартизированном виде. На практике специалисты пробуют и стандартизацию, и нормализацию, выбирая наиболее подходящий способ масштабирования данных, аксиомы тут нет.

2 комментария

  1. Roman Kuncevich

    Хорошая статья, спасибо! Куда посоветуете копать касательно прогнозов конверсии пользователя в платящего, отталкиваясь от цепочек событий?

    • Цветков Максим (Author)

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

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

Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.