Машинное обучение? Это не так сложно. В этой статье я разберу основные принципы и их реализацию.

Для погружения рассмотрим:

  • основную парадигму обучения -- обучение с учителем (Supervised Learning);

  • математические основы машинного обучения на моделях: линейный дискриминантный анализ (LDA) и наивный байесовский классификатор;

  • пример кода из библиотеки scikit-learn, позволяющий реализовать обсуждаемые модели.

Что такое Supervised Learning?

Представьте, что вы пытаетесь научить ребенка распознавать виды животных. Вы показываете ему изображения собак, кошек и птиц и подписываете каждую картинку правильным названием животного. В этом суть обучения с учителем (Supervised Learning).

Только в машинном обучении вместо ребенка "учитель" обучает математическую модель. Например, "учитель" показывает смайлик и говорит, что этот смайлик с лицом относится к классу 1. А смайлик с рукой относится к классу 2.

Математическая модель после нескольких повторений начинает различать класс 1 и класс 2. После обучения модель может определять класс самостоятельно.

image
image

Такой тип обучения часто встречается в задачах классификации на группы.

Математическая магия

На этапе обучения модель с помощью математики составляет правило, по которому она будет распределять на группы. Этот этап для большинства людей, да и для меня тоже, является некоторой магией. Поэтому я разберу его на двух простых для понимания математических моделях: линейный дискриминантный анализ (LDA) и наивный байесовский классификатор.

Нейросети тоже являются математическими моделями, которые можно обучать. Но я оставлю их разбор на другой раз.

Линейный дискриминантный анализ

Представьте, что у вас есть красные, синие и фиолетовые стеклянные шарики. Ваша цель — разделить их на три кучки: красную, синюю и фиолетовую.

Линейный дискриминантный анализ (LDA) подобен рисованию на полу линии, разделяющей шарики. Чтобы разделить шарики:

  1. Он смотрит на характеристики шариков и распределяет шарики на графике.

    Figure_1
    Figure_1
  2. На основе этих характеристик он находит лучшую линию для разделения шариков. Эта линия нарисована таким образом, чтобы красные шарики находились на одной стороне, а синие шарики — на другой.

    Figure_1_with_lines
    Figure_1_with_lines
  3. Как только линия нарисована, она может классифицировать новые шарики.
    Если новый шарик упадет на одну сторону линии, LDA предскажет, что он красный. Если он упадет на другую сторону, LDA предскажет, что он будет синим.

Наивный байесовский классификатор

Представьте, что вы пытаетесь выяснить, является ли письмо спамом. Читаете письмо и видите:

  • Есть слова «бесплатно» и «деньги».

  • Активно просят перейти по ссылке.

Для вас это уже будет подозрительно. Вы отправили письмо в спам.

Для наивного байесовского классификатора все окажется немного сложнее. Ему нужно сначала научиться различать спам, т.е. обработать существующие 500 писем, о которых известно спам это или не спам.

Наивный байесовский классификатор, чтобы разобраться:

  1. Посчитает вероятность встретить слово в письмах без спама.

  2. Посчитает вероятность встретить слово в письмах со спамом.

  3. Повторит это для всех встречающихся слов.

  4. Получит две линии вероятности встретить слово в письме без спама и со спамом -- зеленая линия и красная линия соответсвенно.

Если построить оба графика на одной оси, видно, что в одном месте красный квадрат (линия) выше зеленого квадрата (линии). Это показывает, что слово, которое выражается числом -2, встречается чаще в спаме, чем в письмах без спама.

image
image

Классификатор называется наивным, потому что предполагает, что критерии спама независимы друг от друга.

Реализация в коде

Для примеров с кодом я возьму библиотеку scikit-learn. Она содержит не только все основные реализации моделей для обучения, но и примеры их использования, и всю нужную теорию. Большую часть информации для статьи я взяла там.

Пример реализации линейного дискриминантного анализа (LDA)

Шаг 1. Импорт библиотек и загрузка датасета

Кроме scikit-learn понадобятся также библиотеки numpy, pandas, matplotlib. Скачайте их, если они еще не установлены.

В примере кода используется готовый датасет от scikit-learn. На нем будет проще всего разобрать линейный дискриминантный анализ (LDA).

Датасет состоит из длины и ширины лепестка и чашелистика, по которым надо определить класс ириса (0, 1 или 2). Класс ирисов указан в последней колонке:

dataset-iris
dataset-iris

Импортируйте библиотеки и загрузите датасет:

# необходимый импорт
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
from sklearn.preprocessing import StandardScaler, LabelEncoder
from sklearn.model_selection import train_test_split
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, confusion_matrix

# Загрузите датасет
iris = load_iris()
dataset = pd.DataFrame(columns=iris.feature_names,
                       data=iris.data)
dataset['target'] = iris.target

Шаг 2. Разбейте датасет на тренировочный и тестовый

Обычно датасет разбивают в соотношении 80% на тренировочный набор, 20% на тестовый.

Разбейте датасет:

# Разделите набор данных на характеристики класса и указанный класс
X = dataset.iloc[:, 0:4].values # характеристики
y = dataset.iloc[:, 4].values # класс

# Подготовьте набор данных и разделите его на тренировочный и тестовый
sc = StandardScaler()
X = sc.fit_transform(X)
le = LabelEncoder()
y = le.fit_transform(y)
X_train, X_test,\
    y_train, y_test = train_test_split(X, y,
                                       test_size=0.2)

Шаг 3. Проведите линейный дискриминантный анализ (LDA)

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

Проведите LDA и постройте диаграмму с данными для будущей визуализации:

# Проведите линейный дискриминантный анализ (lda)
lda = LinearDiscriminantAnalysis(n_components=2)
X_train = lda.fit_transform(X_train, y_train)
X_test = lda.transform(X_test)

# Постройте диаграмму с данными
plt.scatter(
    X_train[:, 0], X_train[:, 1],
    c=y_train,
    cmap='rainbow',
    alpha=0.7, edgecolors='b'
)

Шаг 4. Обучите модель, проверьте ее на тестовом наборе и выведите матрицу ошибок

# Классифицируйте с помощью RandomForestClassifier
classifier = RandomForestClassifier(max_depth=2,
                                    random_state=0)
classifier.fit(X_train, y_train) # функция fit обучает модель на тренировочном наборе
y_pred = classifier.predict(X_test) # функция predict классифицирует тестовый набор

# Выведите точность и матрицу ошибок

print('Точность : ' + str(accuracy_score(y_test, y_pred)))
conf_m = confusion_matrix(y_test, y_pred)
print(conf_m)

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

sphx_glr_plot_confusion_matrix_001
sphx_glr_plot_confusion_matrix_001

Наивный байесовский классификатор

Наивный байесовский классификатор реализуется похожим образом. Пример разобран на том же датасете, что и линейный дискриминантный анализ.

Шаг 1. Импорт библиотек и загрузка датасета

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.naive_bayes import GaussianNB
from sklearn.metrics import accuracy_score, confusion_matrix
from sklearn.preprocessing import LabelEncoder
from sklearn.datasets import load_iris
   
# Загрузите датасет
iris = load_iris()
dataset = pd.DataFrame(columns=iris.feature_names,
                       data=iris.data)
dataset['target'] = iris.target

Шаг 2. Подготовьте данные и обучите модель

# Разделите набор данных на характеристики класса и указанный класс
X = dataset.iloc[:, 0:4].values # характеристики
y = dataset.iloc[:, 4].values # класс

# Перевод вида ириса в число
le = LabelEncoder()
y = le.fit_transform(y)
 
# Подготовьте набор данных и разделите его на тренировочный и тестовый
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

# Gaussian Naive Bayes классификатор
gnb = GaussianNB()
 
# Обучите классификатор
gnb.fit(X_train, y_train)

# Найдите классы для тестового набора
y_pred = gnb.predict(X_test)

Шаг 3. Рассчитайте точность модели и матрицу ошибок

# Рассчитайте точность модели
accuracy = accuracy_score(y_test, y_pred)
print(f"Точность предсказания цветка ириса: {accuracy}")

# Постройте матрицу ошибок
conf_m = confusion_matrix(y_test, y_pred)
print(conf_m)

Послесловие

Надеюсь, что машинное обучение открылось вам с новой стороны. Если будут вопросы, обязательно задавайте их в комментариях.

Если вам интересны и другие мои работы, то подписывайтесь на мой канал в телеграме -- quizzes4fun.

Рада, что вы дошли до конца статьи. До новых встреч!

Комментарии (5)


  1. skolpin
    01.07.2024 17:27

    Хорошая статья для первого знакомства с темой. Но оценивать статистики для масштабирования признаков (вызывать fit или fit_transform у Sclaler-а) следует после сплита и только на обучающих данных, а на тестовых только применять преобразование (вызывать transform) с оцененными параметрами:

    X_train, X_test,\
        y_train, y_test = train_test_split(X, y,
                                           test_size=0.2)
    
    sc = StandardScaler()
    X_train = sc.fit_transform(X_train)
    X_test = sc.transform(X_test)
    
    le = LabelEncoder()
    y_train = le.fit_transform(y_train)
    y_test = le.transform(y_test)

    Да, LabelEncoder для целевой переменной можно вызывать и до разделения, так как он не оценивает никакие статистики по данным, поставил его вызов после сплита исключительно для единообразия обращения со всем, у чего есть интерфейс (fit, transform).


    1. hitlocker
      01.07.2024 17:27

      Спасибо, что обратили внимание. хоть и не в первый раз такое читаю и вроде знаю про это, но сам пока не могу обратить внимание на такую деталь)))


      1. ElenaShliaga Автор
        01.07.2024 17:27

        на самом деле, я брала официальный пример от scikit-learn. возможно, проблема была уже там. надо будет проверить. спасибо, что указали на недочет.


    1. qss53770
      01.07.2024 17:27

      Почему после сплита? Можно и до, разницы ни какой вроде бы нету


  1. qss53770
    01.07.2024 17:27

    Для начинающих норм