Как покемоны связаны с логистической регрессией

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

Что такое классификация

Акт классификации определяет, к какой категории принадлежит объект. Допустим, у вас есть упругий круглый объект, и вы хотите знать, камень это или нет. Наблюдая за особенностями этого предмета, можно быстро отличить, скала это или нет. Мы можем предположить, что камни не упругие. К сожалению для вас, объект прыгает, поэтому это не камень. Человеческий мозг достаточно сложен, чтобы выполнять подобные задачи почти мгновенно, поэтому этот пример кажется тривиальным. Таким образом, классификация определяет, к какой категории относится объект.

Как классифицируют компьютеры?

А пока давайте поговорим о компьютерах, классифицирующих объекты самым простым способом, то есть определением категории объектов. Компьютеры работают в двоичном формате, то есть каждое решение, которое принимает компьютер, представляет собой выбор между 1 и 0. Допустим, ценность камня равна 1, а если не камня, то 0. Итак, если мы можем дать компьютеру данные, он определит, объект является камнем или нет, увидев, равно ли значение объекта 1.

Что такое логистическая регрессия?

Логистическая регрессия — это метод контролируемой бинарной классификации. Что это обозначает? Это означает, что вы можете дать вашей модели логистической регрессии помеченные данные, и модели решат, принадлежат ли данные к определенной группе.

Математика логистической регрессии

Вспомните свой каменный объект. Допустим, характеристики этого объекта — уровень упругости, объем и плавучесть. Мы можем представить эти функции математически как матрицу функций.

     bounciness level  volume   buoyancy 
1    0.45              56       31 

Если вы не понимаете матрицы признаков, вы можете нажать здесь. Каждый из столбцов в этой матрице признаков является независимым значением x. Отлично, а как нам рассчитать классификацию?

Еще раз, если вы не понимаете это обозначение теты и x, нажмите здесь.

Объяснение сигмовидной функции

Давайте рассмотрим сигмовидную функцию

Сигмовидная функция идеальна, потому что она выводит значение от 0 до 1.

0 <= f(t) <= 1

Это идеально подходит для двоичной классификации, потому что, как и в компьютерах, мы можем определить классификацию объекта по тому, является ли он 0 или 1. Однако мы редко получаем точное значение в большинстве случаев, когда наш вывод является десятичным. Итак, мы обычно устанавливаем точку отсечки на уровне 0,5. Таким образом, если оно больше или равно 0,5, вы можете интерпретировать значение как 1, а если меньше 0,5, вы можете интерпретировать значение как 0. Вернемся к нашему примеру с камнем. Если значение нашего круглого объекта равно 0,6, то его можно классифицировать как камень; однако, если наш круглый объект был 0,3, то мы можем классифицировать его как не камень.

Сигмовидное объяснение графически

Когда вы рисуете сигмовидную функцию, она выглядит как

На графике показано, как выход функции не может превышать 0 или 1. Также показано, что 0,5 является точкой отсечки между классификацией.

Как узнать, верна ли ваша модель?

Отличный вопрос! Позже в этом посте мы обсудим расчет точности модели. А пока давайте сосредоточимся на вашей потере. Какая потеря? Потеря — это разница между значением прогноза вашей модели и фактическим значением объектов. Например, если объект представляет собой камень, то его значение равно 1. Если ваша модель предсказала 0,5, хотя она все еще находится на приблизительном уровне для правильной оценки, поскольку вы не оценили 1, ваша потеря в прогнозе составила 0,5. Функция, которую мы можем использовать для расчета этой потери, имеет вид

Однако эту функцию можно записать в виде

Обратите внимание, что в зависимости от нашего значения y одно из дополнений будет отменено.

Всемогущие Теты!

В вашей модели логистической регрессии ваши тета важны для модели. Наша конечная цель — найти идеальные тета-значения, чтобы при любом x мы могли точно предсказать его классификацию. Таким образом, вы, по существу, хотите относиться к своим тэтам с любовью и заботой. Как можно любить своих тета? Обучение! Обучение — это жаргон машинного обучения для проведения ваших тета через серию итераций, чтобы найти их оптимальное значение, это можно сделать с помощью градиентного спуска.

Что такое градиентный спуск?

Градиентный спуск — это метод оптимизации, используемый для нахождения минимального значения функции. Вы можете представить Gradient Descent как мяч, катящийся по чаше. Цель этого шара — найти дно чаши, если вы хотите более подробное объяснение нажмите здесь. В нашем случае это приведет к тому, что мы найдем оптимальные теты. В нашем стремлении найти идеальные тэты мы будем использовать

Поговорим о покемонах

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

Набор данных

Мы используем набор данных pokemon_alopez247.csv. Вы можете использовать его, щелкнув здесь. Если на вашем компьютере установлен Kaggle API, вы можете загрузить набор данных в свой терминал с помощью этой команды:

kaggle datasets download -d alopez247/pokemon

Начнем кодировать!

Библиотеки, которые вы будете использовать:

import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split

Теперь вы можете использовать Pandas для настройки вашего фрейма данных:

data = pd.read_csv('data/pokemon_alopez247.csv')
data = data.drop(['Type_2'], axis=1)
data.dropna()

Отлично, теперь вам нужно будет очистить ваши данные. Помните, что мы прогнозируем значения от 0 до 1. Итак, в нашем столбце Type_1. Каждый покемон огненного типа получит 1 балл, а все остальные типы — 0.

def updateTypeColumn(dataframe, columnName, columnValue):
    for index, row in dataframe.iterrows():
        if row.Type_1 == columnValue:
            dataframe.loc[index, columnName] = 1
        else:
            dataframe.loc[index, columnName] = 0
updateTypeColumn(data, 'Type_1', 'Fire')
data['Type_1'] = data['Type_1'].apply(int) # Converts column to int

Запустив:

print(data.corr())

мы обнаруживаем, что существует 14% корреляция между специальной атакой и типом покемона. Кроме того, существует 17-процентная корреляция между вероятностью того, что покемон является мужчиной, и типом покемона.

Здорово! Мы собираемся использовать train_test_split из файла sklearn.model_selection. Эта модель,

«разделить массивы или матрицы на случайные поезда и тестовые подмножества»

Таким образом, мы собираемся использовать 30% наших данных в качестве данных для обучения и 70% в качестве данных для тестирования.

X = [[row[1]['Sp_Atk'], row[1]['Pr_Male']] for row in    
    data.iterrows()]
y = [row[1]['Type_1'] for row in data.iterrows()]
    
training_features, testing_features, training_output, testing_output = train_test_split(X, 
                   y, 
                   test_size=0.7,  
                   train_size=0.3,    
                   random_state=42)

Теперь мы можем установить нашу тета

theta = np.random.uniform(size=len(training_features[0]))

Наконец-то вы можете реализовать сигмовидную функцию!

def sigmoid(z):
    return 1/(1 + np.exp(-z))
# np.exp(-z) = e^-z

Теперь вы можете написать свою функцию стоимости:

def costFunction(x, y, m, theta):
    loss = 0
    for i in range(m):
        z = np.dot(
            np.transpose(theta),
            x[i]
        )
        loss += y[i] * np.log(sigmoid(z)) + (1 - y[i]) * np.log(1 -          sigmoid(z))
    return -(1/m) * loss

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

def gradientDescent(x, y, m, theta, alpha, iterations=1500):
    for iteration in range(iterations):
        for j in range(len(theta)):
            gradient = 0
            for i in range(m):
                z = np.dot(
                    np.transpose(theta),
                    x[i]
                )
                gradient += (sigmoid(z) - y[i]) * x[i][j]
            theta[j] = theta[j] - ((alpha/m) * gradient)
        print('Current Error is:', costFunction(x, y, m, theta))
    return theta

Поздравляем, теперь вы можете запустить свою модель!

print('Final theta\'s \n', 
            gradientDescent(training_features, 
            training_output, 
            len(training_features[0]), theta, 0.001))

Как вы тестируете эту модель?

Для задач классификации мы можем измерить точность следующим образом:

Accuracy = (correct predictions)/(total predictions)
Error = 1 - Accuracy 

Теперь вы можете закодировать это:

def test(x, y, m, theta):
    correct = 0
    for i in range(m):
        z = np.dot(
                np.transpose(theta),
                x[i]
            )
        predicted_value = sigmoid(z)
        if predicted_value >= 0.5 and y[i] == 1:
            correct += 1
        elif predicted_value < 0.5 and y[i] == 0:
            correct += 1
    return correct/m, (1 - (correct/m))

Проверьте свои результаты:

accuracy_rate, error_rate = test(testing_features, 
                                 testing_output,     
                                 len(testing_output), theta)
print('Accuracy: {accuracy} \nError: '
      '{error}'.format(accuracy=accuracy_rate,           
       error=error_rate))

Ваши результаты:

Accuracy: 0.8514851485148515
Error: 0.14851485148514854

Вау! Вы смогли предсказать, является ли покемон огненным типом с точностью 85%.

Весь код

Заключительные слова

Спасибо за чтение! Если у вас есть какие-либо вопросы или мысли, пожалуйста, оставьте комментарий ниже.