Логистическая регрессия — это линейный классификатор. Он часто используется для бинарной классификации, когда есть два результата, например. 0/1.
Обзор логистической регрессии
Три шага логистической регрессии:
- Линейная регрессия между функциями и оценкой, оценка представляет собой непрерывное число от -Inf до Inf:
2. Сигмовидная функция вычисляет вероятность того, что эта выборка относится к классу 1, на основе оценки:
3. Определите метку (y) на основе вероятности и порога (в этом примере я использовал 0,5):
Сигмовидная функция
На шаге 2 сигмовидная функция используется для преобразования оценки в вероятность.
S-образная сигмовидная кривая:
score = list(range(-6, 7, 1)) sigmoid_score = [1/(1+np.exp(-1*x)) for x in score] y = [0,0,0,0,0,1,0,0,1,1,1,1,1] import matplotlib.pyplot as plt plt.plot(score, sigmoid_score) plt.scatter(score, y) plt.vlines(x=0, ymin=0, ymax=1, color = 'red', linestyles='--') plt.xlabel('score') plt.ylabel('Sigmoid(score)')
Если порог равен 0,5, то y = 1, если сигмоид (оценка) > 0,5, и y = 0, если сигмовид (оценка) ≤ 0,5. Порог может быть любым числом от 0 до 1 в зависимости от набора данных и цели.
Градиентный подъем
Как мы можем получить коэффициенты линейной регрессии? Градиентный подъем будет решением. Для логистической регрессии мы ищем набор коэффициентов, которые могут максимизировать вероятность.
Допустим, есть 4 образца, у меня есть вероятности того, что каждый образец принадлежит правильному классу на основе истинных меток.
Вероятность:
вероятность = p(y1=1|x1,b)*p(y2=0|x2,b)*p(y3=0|x3,b)*p(y4=1|x4,b)
Сценарий 1: Классификатор обучен и вернул набор весов (b0, .., bk), а также вероятности:
вероятность = 0,8*0,4*0,7*0,8 = 0,179
В этом случае я правильно понял 3 образца (образец 1, 3, 4) и 1 неправильно (образец 2).
Сценарий 2. Другой классификатор обучен и вернул другой набор весов (b0, …, bk), а также вероятности:
вероятность = 0,8*0,6*0,7*0,8 = 0,269
В этом случае я получил все 4 образца правильно, а также большую вероятность.
Следовательно, нам нужно искать набор весов (b0, .., bk), который может максимизировать вероятность.
Вероятность определяется как:
Журнал вероятности:
Процесс нахождения максимального правдоподобия называется «градиентным восхождением». Логарифмическое правдоподобие является вогнутой функцией для логистической регрессии.
Процесс подъема в гору таков:
Производная логарифмического правдоподобия относительно признака j:
Скажем, у нас есть 4 образца и 2 признака — feature1, feature2. Для каждого признака коэффициент b1 и b2:
Градиенты b1 и b2 на образец:
Затем мы выполняем суммирование строк, чтобы получить градиент (b1 | x) и градиент (b2 | x) по всем образцам:
градиент(b1|x) = 10 * (0–0,3) + 2 * (0–0,4) + 4 * (1–0,8) + 5*(0–0,6) = -6
градиент (b2 | x) = 1 * (0–0,3) + 3 * (0–0,4) + 4 * (1–0,8) + 2 * (0–0,6) = -1,9
что указывает на то, что на данном шаге восхождение происходит с правой стороны холма и коэффициенты — b1, b2 должны уменьшаться.
Термин 1[yi=1] — p(yi=1|xi, w)
измеряет разницу между истинным и прогнозным значением. Если мы правильно предсказываем все выборки, этот термин будет равен 0. Тогда градиент будет равен 0, больше не будет обновляться для весов.
Алгоритм градиентного восхождения:
Градиентный подъем на практике
Я использовал общедоступный набор данных от Kaggle: https://www.kaggle.com/datasets/sveneschlbeck/beginners-classification-dataset
Этот набор данных очень прост с двумя функциями и одной целевой переменной под названием «успех».
Сначала импортируйте все библиотеки и прочитайте набор данных, определите функции и цель:
import pandas as pd import numpy as np from sklearn.model_selection import train_test_split # import dataset df = pd.read_csv('/Users/keru/Downloads/classification.csv') # define feature and target x = df.drop('success', axis=1) y = df.success.values
Столбец со значением 1 необходимо вставить в матрицу признаков (x) для термина пересечения (b0):
# insert a column with value 1 for intercept x = np.insert(np.array(x), 0, np.ones(df.shape[0]), axis=1)
Нормируйте матрицу признаков по размеру вектора для каждого признака:
# normalize feature by its vector size x_normalizer = np.sqrt(sum(x**2)) x_norm = x/x_normalizer
Сплит «Тренировка поезда»:
# train and test split x_train, x_test, y_train, y_test = train_test_split(x_norm, y, test_size=0.2, random_state=0)
Определим нашу функцию градиентного восхождения:
# gradient ascent # help function to get gradient def gradient_logistic(x: np.array, y: np.array, weights: np.array) -> np.array: score = np.matmul(x, weights) proba_class1 = 1 / (1 + np.exp(-1 * score)) gradient_matrix = np.zeros((x.shape[0], x.shape[1])) for i in range(len(weights)): # loop over feature - column for j in range(x.shape[0]): # loop over sample - row if y[j] == 1: gradient_matrix[j][i] = x[j, i] * (1 - proba_class1[j]) if y[j] == 0: gradient_matrix[j][i] = x[j, i] * (0 - proba_class1[j]) # sum over row gradient = gradient_matrix.sum(axis=0) return (gradient)
Определите порог для остановки (порог = 0,01) и размер шага.
# initial weights, gradient, threshold and stepsize weights_all = dict() gradient_vector_size = dict() threshold = 0.01 stepsize_list = [0.01, 0.1, 1, 5]
Начать итерацию для каждого шага:
for stepsize in stepsize_list: # initial weights and gradient weights = np.zeros(x.shape[1]) gradient = np.ones(x.shape[1]) iter = 0 # iteration starts for each stepsize gradient_vector_size_iteration = [] while np.sqrt(np.dot(gradient, gradient)) > threshold: gradient_vector_size_iteration.append(np.sqrt(np.dot(gradient, gradient))) gradient = gradient_logistic(x_train, y_train, weights) weights = weights + stepsize * gradient weights_all[str(iter)] = weights iter += 1 gradient_vector_size[str(stepsize)] = gradient_vector_size_iteration
Постройте размер вектора градиента для каждого размера шага:
import matplotlib.pyplot as plt figure, ax = plt.subplots(1,4, figsize = (10,5)) i = 0 for key in gradient_vector_size.keys(): ax[i].plot(gradient_vector_size[key]) ax[i].set_title('stepsize ' + key) i +=1 plt.show()
Все 4 размера шага сошлись, размер шага = 5 сходился намного быстрее (при итерации 400+), чем размер шага = 0,01 (при итерации 200k+).
Окончательные коэффициенты регрессии для 4 различных размеров шага:
Коэффициенты регрессии сошлись для всех 4 размеров шага.
Теперь давайте предскажем на тестовом наборе:
# predicting on test data # 1. get score test_score = np.matmul(x_test, weights) # 2. get proba proba_class1 = 1/(1+np.exp(-1*test_score)) # 3. get pred label pred_label = [1 if x>=0.5 else 0 for x in proba_class1]
Оцените производительность на тестовом наборе:
# evaluation from sklearn.metrics import confusion_matrix cm = confusion_matrix(y_test, pred_label) print(cm) from sklearn.metrics import classification_report cr = classification_report(y_test, pred_label) print(cr)
Матрица путаницы:
Model predict True label 0 1 0 23 7 1 1 29
Есть 7 ложноположительных случаев и 1 ложноотрицательный случай
Отчет о классификации:
precision recall f1-score support 0.0 0.96 0.77 0.85 30 1.0 0.81 0.97 0.88 30 accuracy 0.87 60 macro avg 0.88 0.87 0.87 60 weighted avg 0.88 0.87 0.87 60
На этом все, спасибо за прочтение :)