true
Вы подписаны на Машинное обучение доступно
Отлично! завершите оплату для полного доступа к Машинное обучение доступно
Снова приветствуем Вас! Вы успешно авторизовались
Успех! Аккаунт активирован, и Вы имеете полный доступ к контенту.
Пайплайн (Pipeline)

Пайплайн (Pipeline)

in

Пайплайн – 1. Последовательные стадии преобразования данных, предшествующие их загрузке в Модель (Model). 2. Класс библиотеки Scikit-learn, последовательно применяющий к исходным данным настраиваемые преобразования. 3. Автоматизируемая последовательность обучения и оптимизации модели в PyTorch и других библиотеках.

Пайплайн как последовательность

Извлечение

Этот этап включает сбор данных из Интернета или баз данных и конвертация в определенные форматы.

Например, приложение Apple. Здоровье позволяет экспортировать свои медданные в формате .xml, и чтобы взаимодействовать с данными в дальнейшем, мы можем использовать библиотеку applehealthdata и конвертировать их в .csv:

Результат конверсии файлов "экспорт.xml", "export_cda.xml" Apple. Здоровье в .csv

Скраббинг

Это очистка данных и самый трудоемкий этап и требует наибольших усилий. Он делится на два этапа:

Изучение

Очистка

  • Замена или заполнение отсутствующих значений
  • Исправление повреждений

Разведочный анализ данных (EDA)

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

Великолепная библиотека для всестороннего разведочного анализа – pandas-profiling. На изображении ниже отдельно взятый признак Датасета (Dataset) о клиентах банка – длительность телефонного разговора, выраженный Вещественным числом (Real Number), исследуется с разных точек зрения: количества уникальных значений (Distinct), пропусков (Missing), Слишком удаленных значений (Infinite), Среднего арифметического (Mean), Минимума (Minimum), Максимума (Maximum), нулевых значений (Zeros), используемой признаком памяти (Memory size).

Моделирование

Это этап обработки, где на сцену выходит Машинное обучение (ML). Модели – это не что иное, как обнаруженные статистические закономерности, которые используются для принятия лучших бизнес-решений.

На изображении ниже – Модель Хольта-Винтерса (Holt-Winters Model), предсказывающая энергопотребление с помощью Датасета (Dataset) компании Duquesne Light Company. Прежде чем использовать модель на реальных задачах, мы производим оценку ее предсказательной способности. Мы производим предсказание для прошлого (в данном случае, 2017 - 2018 гг.) и сравниваем с реальным энергопотреблением. Предсказание, обозначенное розовым, довольно сильно отличается от объективной реальности (Ground Truth), так что модель предстоит перенастраивать. Нередко для самых актуальных проблем современности удается добиться лишь посредственной предсказательной способности (70-80%), и каждый следующий процент Дата-сайентистам (Data Scientist) приходится отвоевывать.

Визуализация предсказания энергопотребления

Интерпретация

Это перефразирование Инсайтов (Insight) Вашей модели, ключевой этап, на котором с использованием психологических методов, специфических знаний о бизнесе и навыков презентации Вы можете преподнести свою модель заказчику и широкой аудитории вообще.

Пересмотр

По мере изменения бизнес-ситуации появляются новые переменные, которые могут снизить качество существующих моделей. Потому периодические обновления – неотъемлемая часть работы.

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

Пайплайн Scikit-learn

В узком смысле пайплайн – это модуль sklearn.pipeline, который позволяет автоматизировать предварительные преобразования данных перед обучением модели. Посмотрите, как использует пайплайны @baghern с целью идентифицировать авторов по их стилю (оригинал на английском). Импортируем все необходимые библиотеки:

import nltk
nltk.download('stopwords') # Для удаления брани

import numpy as np 
import pandas as pd

import sklearn 
from sklearn.pipeline import FeatureUnion # Для удаления брани
from sklearn.model_selection import train_test_split

# Для дополнительных преобразований числовых и текстовых признаков
from sklearn.base import BaseEstimator, TransformerMixin 

# Ансамблевый классификатор "Случайный лес"
from sklearn.ensemble import RandomForestClassifier

# Непосредственно пайплайн для подготовки датафрейма
from sklearn.pipeline import Pipeline

# TfidVectorizer конвертирует датасет в матрицу Меры оценки важности слова в контексте документа (TF-IDF)
from sklearn.feature_extraction.text import TfidfVectorizer

# Стандартизация дополнительного признака
from sklearn.preprocessing import StandardScaler

Импортируем данные, которые для Вашего удобства залиты на Dropbox. Чтобы повторить это позднее, установите параметр dl=1 после вопросительного знака в конце ссылки, это разрешит скачивание файла:

df = pd.read_csv('https://www.dropbox.com/s/pvqawholxi3l2uq/pipeline-author-identification.csv?dl=1')

# Опустим пустые ячейки
df.dropna(axis = 0)

# Зададим индекс
df.set_index('id', inplace = True)
df.head()

Для того, чтобы сконцентрировать внимание на пайплайнах, мы будем использовать датасет с выполненным Конструированием признаков (Feature Engineering). Последнее добавило Датафрейму (Dataframe) следующие признаки:

  • PROCESSED (тот же текст, но без ругани)
  • LENGTH (количество символов)
  • WORDS (количество слов)
  • WORDS_NOT_STOPWORDS (количество небранных слов)
  • AVG_WORD_LENGTH (Средняя длительность слова)
  • COMMAS (Количество запятых)

Как просто, на первый взгляд, определить авторский стиль! Всего-то определить, насколько автор склонен ругаться, да подсчитать запятые... Но все не так просто. Мы будем работать с таким датафреймом:

# Разделим признаки на числовые (numeric_features) и остальные (features)
features = [c for c in df.columns.values if c  not in ['id', 'text', 'author']]
numeric_features = [c for c in df.columns.values if c  not in ['id', 'text', 'author', 'processed']]

# Выберем целевую переменную – мы хотим идентифицировать автора поста по его стилю
target = 'author'

# Разделим датасет на тренировочную и тестовую части случайным образом в пропорции 67:33
X_train, X_test, y_train, y_test = train_test_split(df[features], df[target], test_size = 0.33, random_state = 42)
X_train.head()

Тренировочные данные подобраны случайным образом, и это становится заметно с помощью индекса:

Зададим вспомогательные функции, выбирающие из датафрейма текстовые и числовые признаки для автоматизации преобразований в пайплайнах:

class TextSelector(BaseEstimator, TransformerMixin):
    # Функция, выбирающая каждый текстовый признак датасета 
    # для дополнительных преобразований
    
    def __init__(self, key):
        self.key = key

    def fit(self, X, y = None):
        return self

    def transform(self, X):
        return X[self.key]
    
class NumberSelector(BaseEstimator, TransformerMixin):
    # Функция, выбирающая каждый числовой признак датасета 
    # для дополнительных преобразований
       
    def __init__(self, key):
        self.key = key

    def fit(self, X, y = None):
        return self

    def transform(self, X):
        return X[[self.key]]

Создадим пайплайн, последовательно выбирающий каждый столбец, затем выполним преобразование в матрицу TF-IDF только для этого столбца и вернем результат. Для каждого использованного слова мы определим Меру оценки его важности в контексте документа (TF-IDF):

text = Pipeline([
                ('selector', TextSelector(key = 'processed')),
                ('tfidf', TfidfVectorizer(stop_words = 'english'))
            ])

# Передадим модели часть данных
text.fit_transform(X_train)

Компилятор уведомляет нас, что матрица существенно выросла и состоит из 13 с лишним тысяч столбцов вещественных чисел, причем некоторые ячейки пустуют (sparse). В сжатом виде ячеек, несущих значения, чуть больше 148 тысяч.

<13117x21516 sparse matrix of type '<class 'numpy.float64'>'
	with 148061 stored elements in Compressed Sparse Row format>

Стандартизуем признак "длина" [поста] с использованием селектора числовых признаков:

length =  Pipeline([
                ('selector', NumberSelector(key = 'length')),
                ('standard', StandardScaler())
            ])

# Передадим модели еще часть
length.fit_transform(X_train)

Пайплайн показывает нам, что ряд, прошедший Стандартизацию (Standartization), выглядит так:

array([[-0.50769254],
       [ 0.88000324],
       [ 2.24907223],
       ...,
       [-0.46112557],
       [-0.14447015],
       [-0.39593181]])

Объединим признаки, характеризующие стиль автора, с помощью FeatureUnion:

feats = FeatureUnion([('text', text), # Текст поста              
                      ('length', length), # Длина поста
                      ('words', words), # Список уникальных использованных слов
                      ('words_not_stopword', words_not_stopword), # Очищенный список без ругательств
                      ('avg_word_length', avg_word_length), # Средняя длина слова
                      ('commas', commas)]) # Количество запятых


# Объединим результаты нескольких преобразованных переменных в единый набор данных. 
# Мы сделаем конвейер для каждой переменной, затем объединим их.
words =  Pipeline([
                ('selector', NumberSelector(key = 'words')),
                ('standard', StandardScaler())
            ])
words_not_stopword =  Pipeline([
                ('selector', NumberSelector(key = 'words_not_stopword')),
                ('standard', StandardScaler())
            ])
avg_word_length =  Pipeline([
                ('selector', NumberSelector(key = 'avg_word_length')),
                ('standard', StandardScaler())
            ])
commas =  Pipeline([
                ('selector', NumberSelector(key = 'commas')),
                ('standard', StandardScaler()),
            ])

Теперь прогоним объединение признаков, характеризующих уникальность автора, через пайплайн:

feature_processing = Pipeline([('feats', feats)])

# Передадим модели еще часть данных
feature_processing.fit_transform(X_train)

Несмотря на то, что форма матрицы осталась прежней, ее наполненность увеличилась до 213+ тысяч:

<13117x21521 sparse matrix of type '<class 'numpy.float64'>'
	with 213646 stored elements in Compressed Sparse Row format>

Используем сгенерированные признаки, характеризующие авторский стиль, для предсказания# с помощью классификатора "случайный лес":

pipeline = Pipeline([
    ('features', feats),
    ('classifier', RandomForestClassifier(random_state = 42)),
])

# Теперь модель получила все учебные данные
pipeline.fit(X_train, y_train)

# Определим точность
preds = pipeline.predict(X_test)
np.mean(preds == y_test)

Аккуратность (Accuracy) достигла 67%, и для начала это хороший результат. Но для реальных проектов этого недостаточно. Интуитивное ощущение, что такого скромного набора метаданных для идентификации автора среди миллионов других, оказалось верным.

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

Фото: @xoforoct