Category: Data Science

27
Окт
2022

📊 8 паттернов проектирования, которые должен знать каждый ML-разработчик

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

Данная статья является переводом. Автор: Eugene Yan. Ссылка на оригинал.

Паттерны проектирования в библиотеках и коде

Паттерн «фабрика»

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

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

PyTorch Dataset — хороший пример. Чтобы создать наши собственные наборы данных, мы должны создать подкласс Dataset и переопределить методы __len__и __getitem__. Первый возвращает размер набора данных, а второй поддерживает индексирование для получения i-го примера. Вот пример создания пользовательского набора данных.

        from torch.utils.data import Dataset

class SequencesDataset(Dataset):
    def __init__(self, sequences: Sequences, neg_sample_size=5):
        self.sequences = sequences
        self.neg_sample_size = neg_sample_size

    def __len__(self):
        return self.sequences.n_sequences

    def __getitem__(self, idx):
        pairs = self.sequences.get_pairs(idx)
        neg_samples = []
        for center, context in pairs:
            neg_samples.append(self.sequences.get_negative_samples(context))

        return pairs, neg_samples
    

Gensim textcorpus — еще один пример, упрощающий чтение текстовых файлов для последующего языкового моделирования. Пользователям необходимо переопределить get_texts()метод для чтения и обработки одной строки (или документа) и возврата ее в виде последовательности слов.

        from gensim.corpora.textcorpus import TextCorpus
from gensim.test.utils import datapath
from gensim import utils

class CorpusMiislita(TextCorpus):
    stopwords = set('for a of the and to in on'.split())
    
    def get_texts(self):
        for doc in self.getstream():
            yield [word for word in utils.to_unicode(doc).lower().split() if word not in self.stopwords]

    def __len__(self):
        self.length = sum(1 for _ in self.get_texts())
        return self.length

corpus = CorpusMiislita(datapath('head500.noblanks.cor.bz2'))
>>> len(corpus)
250
document = next(iter(corpus.get_texts()))
    

Последний пример — Hugging Face Dataset, не требующий подкласса. Он предоставляет пользователям простой способ загрузки данных в Apache Arrow, который обеспечивает быстрый поиск с низкими требованиями к памяти. Он также включает методы потоковой передачи, чередования, перемешивания и т. д.

        from datasets import load_dataset
from datasets import interleave_datasets

# Load as streaming = True
en_dataset = load_dataset('oscar', "unshuffled_deduplicated_en", split='train', streaming=True)
fr_dataset = load_dataset('oscar', "unshuffled_deduplicated_fr", split='train', streaming=True)

# Interleave
multilingual_dataset = interleave_datasets([en_dataset, fr_dataset])

# Shuffle
shuffled_dataset = multilingual_dataset.shuffle(seed=42, buffer_size=10_000)

    

Паттерн «адаптер»

Паттерн «адаптер» повышает совместимость между интерфейсами, такими форматы данных как CSV, Parquet, JSON и т.д. Это позволяет объектам (например, сохраненным данным) с несовместимыми интерфейсами взаимодействовать. В конкретных конвейерах обработки данных адаптеры часто используются для чтения данных, хранящихся в различных форматах, в стандартный объект данных, такой как Dataframe.

Например, у Pandas есть почти 20 адаптеров для чтения большинства типов файловых хранилищ в pandas Dataframe.

Неполный список адаптеров для чтения различных форматов хранения файлов в Pandas. Источник: eugeneyan.com
Неполный список адаптеров для чтения различных форматов хранения файлов в Pandas. Источник: eugeneyan.com

Точно так же Spark имеет адаптеры для чтения из различных форматов данных, таких как Parquet, JSON, CSV, Hive и текстовые файлы.

        val parquetDf = spark.read.parquet("people.parquet")
val jsonDf = spark.read.json("examples/src/main/resources/people.json")
val hiveDF = sql("SELECT name, job_family FROM people WHERE age < 60 ORDER BY age")
val csvDf = spark.read.csv("examples/src/main/resources/people.csv")
val textDf = spark.read.text("examples/src/main/resources/people.txt")
    

Другим примером является Apache Arrow, который предоставляет стандартный формат столбцов для нескольких платформ данных, таких как Pandas, Spark, Parquet, Cassandra и других. (Набор данных Hugging Face использует Arrow для своей локальной системы кэширования.)

Apache Arrow стандартизирует столбцовые данные в памяти для нескольких платформ данных. Источник: eugeneyan.com
Apache Arrow стандартизирует столбцовые данные в памяти для нескольких платформ данных. Источник: eugeneyan.com

Паттерн «декоратор»

Паттерн «декоратор» позволяет пользователям легко добавлять функциональные возможности в свой существующий код. Объекты можно «декорировать» (т. е. добавлять функциональные возможности) во время выполнения без необходимости обновления структуры или поведения других объектов того же класса.

В Python методы декорирования легко реализуются с помощью @синтаксиса. Использование @decorator эквивалентно вызову method()method = decorator(method).

Удобным примером встроенного оператора является оператор functool lru_cache(). Он сохраняет самые последние вызовы x в словаре, который сопоставляет входные аргументы с возвращаемыми результатами. Вот пример использования кэша для эффективного вычисления чисел Фибоначчи.

        from functools import lru_cache

@lru_cache(maxsize=None)
def fib(n):
    if n < 2:
        return n
    return fib(n-1) + fib(n-2)

>>> [fib(n) for n in range(16)]
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]

>>> fib.cache_info()
CacheInfo(hits=28, misses=16, maxsize=None, currsize=16)
    

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

        import pytest
import numpy as np

from src.data_prep.prep_titanic import load_df, prep_df, split_df, get_feats_and_labels
from src.tree.decision_tree import DecisionTree
from src.tree.random_forest import RandomForest

# Returns data for training and evaluating our models
@pytest.fixture
def dummy_dataset():
    df = load_df()
    df = prep_df(df)

    train, test = split_df(df)
    X_train, y_train = get_feats_and_labels(train)
    X_test, y_test = get_feats_and_labels(test)
    return X_train, y_train, X_test, y_test

# Returns a trained DecisionTree that is evaluated on implementation and behavior
@pytest.fixture
def dummy_decision_tree(dummy_dataset):
    X_train, y_train, _, _ = dummy_dataset
    dt = DecisionTree(depth_limit=5)
    dt.fit(X_train, y_train)
    return dt

# Returns a trained RandomForest that is evaluated on implementation and behavior
@pytest.fixture
def dummy_random_forest(dummy_dataset):
    X_train, y_train, _, _ = dummy_dataset
    rf = RandomForest(num_trees=8, depth_limit=5, col_subsampling=0.8, row_subsampling=0.8)
    rf.fit(X_train, y_train)
    return rf
    

Простой декоратор, который я часто использую — это таймер, который измеряет, сколько времени занимает вызов метода, и возвращает результаты. Это полезно при создании прототипов, которые вызывают разные модели — с разной задержкой — для проверки времени, затрачиваемого на каждый вызов.

        from functools import wraps
from time import perf_counter
from typing import Callable
from typing import Tuple

def timer(func: Callable) -> Callable:
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = perf_counter()
        results = func(*args, **kwargs)
        end = perf_counter()
        run_time = end - start
        return results, run_time

    return wrapper

@timer
def predict_with_time(model, X_test: np.array) -> Tuple[np.array]:
    return model.predict(X_test)
    

Паттерн «стратегия»

Паттерн «стратегия» позволяет пользователям изменять предполагаемое поведение объекта или алгоритм. Пользователи могут создавать новые объекты для каждой стратегии (или алгоритма), и в зависимости от используемого объекта стратегии поведение контекста может меняться во время выполнения. Это отделяет алгоритмы от клиентов, добавляя гибкости и возможности повторного использования кода.

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

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

        import numpy as np
import xgboost as xgb
from typing import Tuple

def gradient(predt: np.ndarray, dtrain: xgb.DMatrix) -> np.ndarray:
    y = dtrain.get_label()
    return (np.log1p(predt) - np.log1p(y)) / (predt + 1)

def hessian(predt: np.ndarray, dtrain: xgb.DMatrix) -> np.ndarray:
    y = dtrain.get_label()
    return ((-np.log1p(predt) + np.log1p(y) + 1) / np.power(predt + 1, 2))

def squared_log(predt: np.ndarray, dtrain: xgb.DMatrix) -> Tuple[np.ndarray, np.ndarray]:
    predt[predt < -1] = -1 + 1e-6
    grad = gradient(predt, dtrain)
    hess = hessian(predt, dtrain)
    return grad, hess

xgb.train({'tree_method': 'hist', 'seed': 1994},
           dtrain=dtrain,
           num_boost_round=10,
           obj=squared_log)  # Using the custom objective function
    

Точно так же конвейер Hugging Face позволяет легко использовать различные стратегии (также известные как языковые модели) для логического вывода. Pipelines поддерживает такие задачи, как анализ настроений, перевод, ответы на вопросы и многое другое.

        from transformers import pipeline

pipe = pipeline("sentiment-analysis")
>>> pipe(["This restaurant is awesome", "This restaurant is aweful"])
[{'label': 'POSITIVE', 'score': 0.9998743534088135},
 {'label': 'NEGATIVE', 'score': 0.9996669292449951}]

en_fr_translator = pipeline("translation_en_to_fr")
>>> en_fr_translator("How old are you?")
[{'translation_text': ' quel âge êtes-vous?'}]

qa_model = pipeline("question-answering")
question = "Where do I live?"
context = "My name is Merve and I live in İstanbul."
>>> qa_model(question = question, context = context)
{'answer': 'İstanbul', 'end': 39, 'score': 0.953, 'start': 31}
    

Паттерн «итератор»

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

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

        from torch.utils.data import DataLoader

dataset = SequencesDataset(sequences)

def collate(batches):
    batch_list = []

    for batch in batches:
        pairs = np.array(batch[0])
        negs = np.array(batch[1])
        negs = np.vstack((pairs[:, 0].repeat(negs.shape[1]), negs.ravel())).T

        pairs_arr = np.ones((pairs.shape[0], pairs.shape[1] + 1), dtype=int)
        pairs_arr[:, :-1] = pairs

        negs_arr = np.zeros((negs.shape[0], negs.shape[1] + 1), dtype=int)
        negs_arr[:, :-1] = negs

        all_arr = np.vstack((pairs_arr, negs_arr))
        batch_list.append(all_arr)

    batch_array = np.vstack(batch_list)

    # Return item1, item2, label
    return (torch.LongTensor(batch_array[:, 0]), torch.LongTensor(batch_array[:, 1]),
            torch.FloatTensor(batch_array[:, 2]))

# We can customize the batch size, number of works, shuffle, etc.
train_dataloader = DataLoader(dataset, batch_size=128, shuffle=True, num_workers=8, collate_fn=collate)
    

Паттерн «конвейер»

Паттерн «конвейер» («pipeline») позволяет пользователям связывать последовательность преобразований. Преобразования — это этапы обработки данных, такие как очистка данных, разработка функций, уменьшение размерности и т. д. В конец конвейера можно добавить блок оценки (также известный как модель машинного обучения). Таким образом, конвейер принимает данные в качестве входных данных, преобразует их и в конце обучает модель.

ИМХО, в конвейерах ML все, от подготовки данных до разработки функций и гиперпараметров модели, является параметром для настройки. Возможность связывать и варьировать преобразования и блоки оценки обеспечивает удобную настройку параметров конвейера. Как изменятся показатели модели, если мы будем использовать непрерывные переменные или добавим триграммы к нашим текстовым функциям?

При использовании конвейера мы должны знать, что ввод и вывод каждого шага преобразования должны иметь стандартизированный формат, такой как кадр данных Pandas. Кроме того, каждое преобразование должно иметь необходимые методы, такие как .fit() и .transform().

Pipeline из Sklearn позволяет пользователям предоставлять список преобразований и окончательную оценку. Мы также можем использовать TransformerMixin для создания пользовательских преобразований.

        from sklearn.svm import SVC
from sklearn.preprocessing import StandardScaler
from sklearn.impute import SimpleImputer
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline

X, y = make_classification(random_state=0)
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

# Create and train pipeline
pipe = Pipeline([('imputer', SimpleImputer(strategy="median")), 
                 ('scaler', StandardScaler()), 
                 ('svc', SVC())])
pipe.fit(X_train, y_train)

# Evaluate the pipeline
>>> pipe.score(X_test, y_test)
0.88
    

MLlib из Spark также использует концепцию конвейеров, построенных на основе фреймов данных, преобразователей и блоков оценки.

        import org.apache.spark.ml.{Pipeline, PipelineModel}
import org.apache.spark.ml.classification.LogisticRegression
import org.apache.spark.ml.feature.{HashingTF, Tokenizer}
import org.apache.spark.ml.linalg.Vector
import org.apache.spark.sql.Row

// Prepare training documents from a list of (id, text, label) tuples.
val training = spark.createDataFrame(Seq(
  (0L, "a b c d e spark", 1.0),
  (1L, "b d", 0.0),
  (2L, "spark f g h", 1.0),
  (3L, "hadoop mapreduce", 0.0)
)).toDF("id", "text", "label")

// Configure an ML pipeline, which consists of three stages: tokenizer, hashingTF, and lr.
val tokenizer = new Tokenizer()
  .setInputCol("text")
  .setOutputCol("words")
val hashingTF = new HashingTF()
  .setNumFeatures(1000)
  .setInputCol(tokenizer.getOutputCol)
  .setOutputCol("features")
val lr = new LogisticRegression()
  .setMaxIter(10)
  .setRegParam(0.001)
val pipeline = new Pipeline()
  .setStages(Array(tokenizer, hashingTF, lr))

// Fit the pipeline to training documents.
val model = pipeline.fit(training)

// Now we can optionally save the fitted pipeline to disk
model.write.overwrite().save("/tmp/spark-logistic-regression-model")
    

Паттерны проектирования в системах

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

Паттерн «Заместитель»

Паттерн «заместитель» («proxy») позволяет нам заменить рабочую базу данных или службу. Затем прокси-сервер может выполнять задачи от имени конечной точки.

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

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

Использование кэша прокси-сервера для обслуживания общих запросов и снижения затрат на вычисления за счет логического вывода в реальном времени. Источник: eugeneyan.com
Использование кэша прокси-сервера для обслуживания общих запросов и снижения затрат на вычисления за счет логического вывода в реальном времени. Источник: eugeneyan.com

Другой пример — использование обратного прокси для обслуживания моделей. Один из способов масштабирования — обслуживание через несколько машин (т. е. горизонтальное масштабирование). Тем не менее, мы по-прежнему хотим предоставить единый адрес для доступа. Обратный прокси может принимать входящие запросы через один адрес и распределять их по нескольким серверам, чтобы распределить нагрузку и обеспечить низкую задержку.

Использование обратного прокси-сервера для распределения входящих запросов на несколько внутренних серверов. Источник: eugeneyan.com
Использование обратного прокси-сервера для распределения входящих запросов на несколько внутренних серверов. Источник: eugeneyan.com

В чем разница между балансировщиком нагрузки и обратным прокси-сервером?

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

Однако обратный прокси-сервер, как правило, имеет ряд функций:

  1. Балансировка нагрузки: как обсуждалось выше.
  2. Кэширование: он может кэшировать содержимое веб-сервера (серверов), расположенного за ним, и тем самым снижать нагрузку на веб-сервер (серверы) и возвращать статическое содержимое обратно запрашивающему без необходимости получать данные с веб-сервера (серверов).
  3. Безопасность: он может защитить веб-сервер(ы), предотвращая прямой доступ из интернета; он может сделать это простыми средствами, просто скрыв веб-сервер(ы), или он может использовать активные компоненты, которые фактически просматривают входящие запросы в поисках вредоносного кода.

Ускорение SSL: при использовании SSL, он может служить точкой завершения для этих SSL-сессий, чтобы снять нагрузку по шифрованию с веб-сервера(ов)». (источник)

Паттерн «посредник»

Паттерн «посредник» предоставляет посредника, который ограничивает прямую связь между службами, заставляя службы сотрудничать — косвенно — через посредника. Это способствует слабой связи, не позволяя службам явно ссылаться друг на друга и реализовывать интерфейс других служб.

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

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

Использование посредника для распределения виджетов по местам размещения на главной странице. Источник: eugeneyan.com
Использование посредника для распределения виджетов по местам размещения на главной странице. Источник: eugeneyan.com
***

Хочу подтянуть знания по математике, но не знаю, с чего начать. Что делать?

Если базовые концепции языка программирования можно достаточно быстро освоить самостоятельно, то с математикой могут возникнуть сложности. Чтобы помочь освоить математический инструментарий, «Библиотека программиста» совместно с преподавателями ВМК МГУ разработала курс по математике для Data Science, на котором вы:

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

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

11
Окт
2022

📊🙊 SkyNet придет не скоро: 10 вещей в Data Science, о которых вам никто не расскажет

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

Данная статья является переводом. Ссылка на оригинал.

1. Data Scientist вряд ли будет использовать глубокое обучение в своей работе

Несомненно, глубокое обучение пользуется особой популярностью в мире Data Science. Лишь немногие Data Scientists имеют ресурсы для таких затратных проектов. Сотни тысяч (и даже миллионы) долларов необходимы для ввода, хранения и разметки данных. После этого огромное количество аппаратного обеспечения и параметрические эксперименты потребуют больших затрат на проведение исследования. Соедините это со склонностью глубокого обучения к переобучению и вы поймете, почему большинство компаний предпочитает линейную или логистическую регрессию.

2. SQL — самая ценная технология, которую вам надо изучить


SQL, которому около 50 лет, до сих пор остается востребованным и используется для запросов в сотнях платформ баз данных. Почему? Потому что он просто работает.

Когда платформы Big Data, такие как NoSql и Apache Spark, вышли на сцену в 2015 году, было много ожиданий, что они заменят SQL. Иронично, но в них были, наоборот, добавлены SQL-интерфейсы по требованиям пользователей. SQL продолжает оставаться языком данных и не потерял свою актуальность в эпоху бума Big Data. Его логический синтаксис позволяет получать краткие и понятные инструкции по извлечению и манипулированию данными.

Многие входят в область Data Science, ожидая использовать машинное обучение и различные статистические инструменты. В реальности же они тратят 99% процентов усилий на работу по извлечению, структурированию и сохранению сырых данных. Поэтому, если не знаешь SQL, тяжело быть продуктивным.

Конечно, есть Data Scientists , которые не знают SQL, но они зависят от тех, кто извлекает им данные. Они также усложняют свою работу, выполняя через Python/Pandas задачи, которые могут быть легко решены в несколько строк на SQL или в которых используется такое огромное количество данных, что их лучше сделать на сервере с использованием SQL. Поэтому неудивительно, что таких специалистов немного.

Если вы хотите перейти в Data Science, первым делом изучите SQL. Ваши модели и анализ не будут иметь ценности, если вы не сможете достать необходимые данные.

3. Когда у вас есть молоток, всё начинает казаться гвоздями


Data science наполнена профессионалами, пытающимися решить всё с помощью машинного обучения, вместо того чтобы начать с самой задачи и поиска правильного решения. Из-за этого Data Scientists упускают из виду мощные и эффективные средства просто потому, что эти алгоритмы старые, забытые и это не машинное обучение.

Отложите на мгновение машинное обучение! Изучите регулярные выражения, эвристику, алгоритмы поиска, системы на правилах, линейное программирование, оптимизацию и другие концепции старой школы, которые выдержали испытание временем и способны решать задачи, которые машинное обучение решить не может. Можно заметить, что самые эффективные решения часто не попадают в СМИ. Есть много анекдотов, в которых команда Data Science крупной технологической компании пыталась использовать обработку естественного языка, решая задачу шаблонов текстов, и затем недавно нанятый программист «старой закалки» решает эту задачу с помощью регулярных выражений в течение часа. Слишком часто специалисты по данным принимают неоправданные решения.

4. Работа Data Scientist напрямую связана с выполнением задач IT-сектора

Многие специалисты Data Science разочаровываются, когда они нанимаются для статистики и машинного обучения, но вместо этого становятся штатными экспертами IT. Явление не новое и предшествует самой науке о данных.

Теневые информационные технологии (теневое IT) описывает работников, которые создают системы вне отдела IT. Это включает в себя базы данных, дашборды, скрипты, код. Раньше подобная практика в организациях не приветствовалась, так как не регулировалась и выходила за сферу контроля IT-отдела. Однако одно из преимуществ движения data science заключается в том, что она сделала теневое IT более приемлемым, как необходимость для инноваций.

Вместо того чтобы разочаровываться, Data Scientist может получить знания в области SQL, программирования, облачных технологий, веб-разработки и других полезных технологий. Это поможет упростить его работу, сделать ее более доступной для других и открывает новые возможности для статистических и ML-моделей.

Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека data scientist’а»

5. Компьютеры и машинное обучение не могут находить смещение в данных

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

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

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

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

6. Компьютеры не могут отличить корреляцию от причинно-следственной связи


Когда есть корреляция между x и y, компьютеры не могут определить, влияет ли x на y, y на x, или какая-то третья переменная z влияет на x и y. Он не может определить, что переменные не зависят друг от друга вовсе, и что корреляция случайна.

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

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

7. Анализ данных – это научный метод, применяемый в обратном направлении

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

Но разве машинное обучение и глубокое обучение — это анализ данных? Да, фактически они автоматизируют процесс, выполняя поиск данных как большого пространства гипотез. А переобучение — принятие неверной гипотезы. Для имитации свежих данных выделяются test- и validation-выборки, чтобы проверить, правильно ли подогнана модель.

8. Не все индустрии одинаковы


Давайте сравним две индустрии: стриминговое кино (например, Netflix) и воздушно-космическая оборона (например, Lockheed Martin). Имеют ли они что-то общее? Вряд ли! Обе компании являются ориентированными на технологии, но одна на стриминговое кино для пользователей, а другая — на строительство самолетов.

Data science и машинное обучение — это аппроксимация, а это значит ошибка неизбежна. Важно отметить, что у этих компаний разные допуски к риску. Компания стриминга может рекламировать, что она имеет систему искусственного интеллекта, которая учится тому, какой фильм посоветовать пользователям, но что критического может случиться, если она даст плохую рекомендацию? Что ж, в самом худшем случае у вас будет слегка раздраженный пользователь, который потратил 2 часа на просмотр фильма, который ему не понравился.

А что насчёт компании воздушно-космической обороны? Если истребитель имеет искусственный интеллект на борту, который автоматически поражает цели, как критична может быть ошибка? Сейчас мы говорим о жизнях людей, а не о рекомендациях фильмов! Пропасть между допустимыми рисками в этих компаниях очень велика. Естественно, компания воздушно-космической обороны будет более консервативной при внедрении экспериментальной системы.

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

9. Внедрение машинного обучения в производство — очень тяжелый процесс

Внедрение моделей машинного обучения в продакшн действительно тяжелый процесс. Результаты на тестовом и валидационном датасетах могут быть хороши в лабораторной среде, но на «живых» данных они могут заметно отличаться и будут полны неожиданными событиями. Затем возникают проблемы с «гниением» данных, когда у данных есть срок использования, так как тенденции приходят и уходят. Это также происходит, когда датчики на «само-управляемом автомобиле» перенастраиваются, что делает предыдущие данные и параметры модели бесполезными.

Существует огромный разрыв между Data Scientists и Software-разработчиками. И именно поэтому многие специалисты Data Science работают, не отрываясь от своего ноутбука. Вдобавок к проблемам с данными специалисты Data Science оказываются в положении, когда навыки разработки им просто необходимы. Как отмечалось ранее, важно спрашивать не только что говорят данные, но и откуда они и насколько контролируемой или неконтролируемой является среда, из которой будут собираться данные. Кроме того, перед тем как тратить сотни тысяч (если не миллионы) долларов на приобретение размеченных данных, убедитесь, что вы учитываете срок годности этих данных!

10. Очень важна приоритезация того, что надо учить

В Data Science общепринято постоянно учиться и проявлять любопытство. Это прекрасно, но все-таки более важный навык это расстановка приоритетов в получении знаний.

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

Вывод

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

***

Материалы о теме

29
Авг
2022

🤖🎨 ИИ для рисования: раскрываем секреты нейронного переноса стиля

Раскладываем по полочками, как «думает» нейронная сеть VGG-19, когда ей прилетает задача скопировать стиль художника из вида Homo sapiens.

В мире, в котором NFT продаются за миллионы долларов, следующим прибыльным бизнесом может стать создание уникальных виртуальных сущностей, и кто же лучше выполнит эту работу, чем искусственный интеллект? Фактически задолго до бума NFT, в октябре 2018-го, первый портрет, сгенерированный ИИ, был продан за $432.500. С этого момента люди использовали свое глубокое знание передовых алгоритмов для создания потрясающих предметов искусства. Например, Рефик Анадол – художник, использующий ИИ для создания захватывающих картин (ознакомиться с его работами можно по этой ссылке). Другой цифровой художник, Петрос Вреллис, в 2012 году выставил интерактивную анимацию знаменитой работы Ван Гога «Звездная ночь», которая собрала более 1.5 миллиона просмотров за три месяца.

Однако творческие возможности ИИ не ограничиваются живописью. Люди создают интеллектуальные системы, способные писать стихи, песни, сценарии, и, возможно, делать и другую творческую работу. Движемся ли мы к миру, в котором всем творческим людям придется конкурировать с компьютерами? Возможно, но это не то, что я собирался обсудить. Моя цель – погрузиться в принципы работы элегантного алгоритма нейронного переноса стиля (neural style transfer), способного превратить даже ужасных художников вроде меня в виртуозов.

1. Идея нейронного переноса стиля

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

<span>Фото </span><a href="https://unsplash.com/@k_drew" target="_blank" rel="noopener noreferrer nofollow"><span>Кирстен Дрю</span></a><span> на </span><a href="https://unsplash.com" target="_blank" rel="noopener noreferrer nofollow"><span>Unsplash</span></a>
Фото Кирстен Дрю на Unsplash

Идея нейронного переноса стиля (NST) в том, чтобы взять одно изображение и переделать его содержимое в стиле какого-нибудь другого изображения. Перенос темы (стиля) с более привлекательного изображения – такого, как знаменитая картина «Звездная ночь» Ван Гога – может привести к потрясающим переделкам.


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

Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека data scientist’а»

2. Сверточные нейронные сети для переноса стиля

Поскольку мы работаем с изображениями, вас не должно удивлять, что основным механизмом переноса стиля являются сверточные нейронные сети. Также, поскольку множество сверточных нейронных сетей добились прекрасных результатов в задачах обработки изображений, нам не придется обучать отдельную сеть для переноса стиля. Я использую сеть VGG-19 (transfer learning для переноса стиля), но вы можете использовать любую другую предобученную сеть или обучить свою.

Сеть VGG-19 имеет следующую архитектуру:


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

<span>Block_1_conv_1</span>
Block_1_conv_1
<span>Block_2_conv_1</span>
Block_2_conv_1
<span>Block_3_conv_1</span>
Block_3_conv_1
<span>Block_4_conv_1</span>
Block_4_conv_1
<span>Block_5_conv_1</span>
Block_5_conv_1
<span>Block_5_conv_2</span>
Block_5_conv_2

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

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

<span>Выход слоя block_5_conv_2 (фильтр 4)</span>
Выход слоя block_5_conv_2 (фильтр 4)
<span>Выход слоя block_5_conv_2 (фильтр 13)</span>
Выход слоя block_5_conv_2 (фильтр 13)

Разделимость глобальных и локальных компонентов изображения при обработке с помощью сверточных нейронных сетей – именно то, что лежит в основе передачи стиля. Авторы нейронного алгоритма художественного стиля и оригинальной статьи о нейронной передаче стиля (Гейтис и пр.) сообщают, что поскольку глубокие слои хорошо идентифицируют детализированные признаки, они используются для получения содержимого изображения. Аналогично, начальные слои, способные хорошо усваивать контуры объектов, используются для получения стиля изображения (темы или глобальных признаков).

3. Создание нового изображения

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

3.1. Информация о содержимом

В прошлом разделе мы установили, что выходы глубоких слоев нейронной сети помогают определить содержимое изображения. Теперь нам нужен алгоритм для получения этого содержимого. Для этого мы используем уже знакомые математические методы: функции потерь (loss) и обратное распространение (backpropagation).

<span>Изображение из шума (сгенерированное на Python)</span>
Изображение из шума (сгенерированное на Python)

Рассмотрим приведенное выше изображение, состоящее из шума (X). Для простоты примем, что сверточная нейронная сеть, которую мы выбрали для передачи стиля, состоит всего из двух слоев, A1 и A2. Передавая целевое изображение (Y) через сеть и используя выходы A2, A2(A1(Y)), мы получаем информацию о его содержимом. Чтобы изучить/выделить эту информацию, мы передаем X через ту же сеть. Поскольку X состоит из случайно распределенных значений пикселей, A2(A1(X)) также будет случайным. Затем мы определяем функцию потерь содержимого как:

Lcontent=12∑i,j(A2(A1(X)−A2(A1(Y)))2

Здесь i и j – обозначения индексов массива изображения (размеры целевого изображения с содержимым и изображения с шумом должны быть одинаковыми). Фактически, если результирующие массивы A2(A1(X)) и A2(A1(Y)) имеют размерность (m, n), функция потерь вычисляет их разницу в каждой точке, возводит ее в квадрат, а потом суммирует все эти квадраты. С помощью такой функции потерь мы заставляем X стать таким изображением, которое выдает такую же информацию о содержимом, что и Y. Мы можем минимизировать потери с помощью обратного распространения, как мы делали это в любой нейронной сети, но есть небольшая разница. В нашем случае обучаемые параметры – не веса нейронной сети, а сам массив X. Вот как выглядит финальное уравнение:

Xnew=X−α∂Lcontent∂X

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

3.2. Информация о стиле

Получение информации о стиле сложнее, чем получение информации о содержимом. Оно требует понимания матриц Грама.

3.2.1. Матрицы Грама

Рассмотрев архитектуру VGG-19 (см. пункт 2), вы можете увидеть, что выход слоя block_1_conv_1 имеет размерность (m, n, 64). Для простоты примем, что m,n = 3,2, и количество фильтров тоже равно 2, а не 64.

<span>Выходы двух фильтров</span>
Выходы двух фильтров

Как вы знаете, фильтры сверточной нейронной сети обучаются распознавать различные пространственные признаки вроде краев/линий, интенсивности цветов и т.д. Давайте скажем, что приведенные выше фильтры распознают оттенки красного и синего цветов в разных локациях. То есть, значение пикселей (от 0 до 1) в каждой ячейке фильтра 1 представляет интенсивность красного цвета в этой локации, а в фильтре 2 – интенсивность синего цвета. Теперь склеим оба выходных массива вместе и умножим результат на его же транспонированное значение. Что мы получим в результате?


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


Следовательно, матрица Грама, в контексте сверточных сетей – это матрица корреляции между всеми признаками, извлекаемыми всеми фильтрами слоя. Давайте посмотрим, как они помогают в переносе стиля.

3.2.2. Стиль и комбинированные потери

<span>Фото </span><a href="https://unsplash.com/@steve_j" target="_blank" rel="noopener noreferrer nofollow"><span>Стива Джонсона</span></a><span> на </span><a href="https://unsplash.com" target="_blank" rel="noopener noreferrer nofollow"><span>Unsplash</span></a>
Фото Стива Джонсона на Unsplash

Предположим, мы хотим передать стиль из приведенного выше изображения. Заметьте, что наличие множества полос (вертикальных и горизонтальных линий) голубого цвета, переходящего в белый. Матрицы Грама двух фильтров, обнаруживающих линии и светло-голубой цвет, покажут высокую корреляцию между этими признаками. Следовательно, алгоритм обеспечит, чтобы в реконструированном изображении эти два признака встречались вместе, что прекрасно послужит для передачи стиля. В реальности каждый слой сверточной сети имеет множество фильтров, что приводит к огромным матрицам Грама, но идея остается все той же. Например, матрица Грама для выходов слоя block_1_conv_1 в VGG19, будет матрицей корреляции между 64 признаками.

Затем нам нужно определить функцию потерь для стиля. Предположим, что пропустив наше целевое изображение через block_1_conv_1, мы получили матрицу Грама G. Мы пропускаем через тот же слой наше зашумленное изображение X и рассчитываем ее матрицу Грама P. Тогда потери стиля можно рассчитать так:

Lstyle,layer=l=1(2∗H∗W∗N)2∑i,j(Pi,j−Gi,j)2

Здесь (i,j) – обозначения индексов в матрицах Грама. Минимизируя эту функцию, мы обеспечиваем, что матрица X будет иметь такую же корреляцию между признаками, как наше целевое изображение со стилем.

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

Lstyle=∑0lwlLstyle,layer=l

W – это веса, назначенные потерям каждого слоя. Это гиперпараметры, которые можно настраивать, чтобы увидеть, как изменится итоговая композиция. Мы обсудим результаты изменения этих весов, а также использование единственного слоя для передачи стиля в последнем разделе.

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

Loss=aLstyle+bLcontent
Xnew=X−α∂Loss∂X

4. Изменение гиперпараметров

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

  1. Путь к изображению с содержимым.
  2. Список путей к изображениям со стилями.
  3. Вес потерь содержимого.
  4. Вес потерь стиля.
  5. Количество итераций (обратного распространения).
  6. Список весов для изображений стиля – если вы хотите передать стиль из нескольких изображений, нужно передать вес для каждого изображения стиля.
  7. Список весов для слоев стиля – веса, назначенные каждому слою, используемые в функции потерь.

Мы используем для своих экспериментов следующие изображения:


4.1. 5 слоев с одинаковыми весами потерь, 1 слой содержимого

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


4.2. Повышаем вес функции потерь стиля с 0.005 до 0.1

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


4.3. Используем один слой для стиля

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


4.4. Используем другой слой в функции потерь содержимого

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


4.5. Назначаем веса слоев стиля вручную [0.4, 0.3, 0.2, 0.05, 0.05]

Хотя разница между изображением из п. 4.1 и этим невелика, мне кажется, что это изображение имеют общую светлую тень. Возможно, это следствие большего веса, назначенного слою block_1_conv_1, что, в соответствии с п. 4.3, генерирует белый тон из картины «Звездная ночь».


4.6. Назначаем веса слоев стиля вручную [0.05, 0.05, 0.2, 0.3, 0.4]

Это куда более удачная комбинация цветов, чем 4.5. Назначение больших весов глубоким слоям обеспечивает гораздо более привлекательные результаты. Возможно, матрицы Грама для этих слоев вычисляют корреляцию между более доминантными глобальными признаками из «Звездной ночи».


4.7. Передача стиля из нескольких изображений

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

Lstyle,image=i=∑0lwlLstyle,layer=l

Однако, общая функция потерь выглядит вот так:

Lstyle=Ai∑0i∑0lwlLstyle,layer=l,image=i

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


5. Заметки

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

6. Ссылки

***

Материалы по теме

23
Авг
2022

📰 Weekly #14: новости, подкасты, отборные статьи и обучающие материалы по Python, Data Science, Go, C#, C/C++ и мобильной разработке

В этом выпуске: питонический способ подсчета объектов; как работает Autoplay в онлайн-кинотеатре; использование SQLite в Go на примере Telegram-бота; разработка параметрического генератора зданий на Unity; знакомство с Kotlin Multiplatform …

16
Авг
2022

🤖 5 классических алгоритмов машинного обучения, о которых вам обязательно следует знать

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

Данная статья является переводом. Ссылка на оригинал.

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

Почему именно такой формат?

  1. Практическое применение. Знания бесполезны, если они не могут быть применены. Разбивка на основные группы по применению даст лучшее понимание того, какие задачи вы можете решить, используя тот или иной алгоритм.
  2. Актуальность. Правда в том, что не все алгоритмы машинного обучения сохраняют свою актуальность. Вы сразу увидите, что такие традиционные алгоритмы, как наивный байесовский алгоритм, не включены в статью просто потому, что они деклассированы более совершенными алгоритмами.
  3. Усвояемость. Есть тысячи онлайн ресурсов, которые научат тебя реализовывать модели, о которых пойдет далее разговор. Мы же больше сфокусированы на оптимальном применении каждого типа алгоритмов.

С учетом вышесказанного разделим алгоритмы машинного обучения на 5 наиболее важных классов:

  1. Ансамблевые алгоритмы.
  2. Объяснительные алгоритмы.
  3. Алгоритмы кластеризации.
  4. Алгоритмы понижения размерности.
  5. Алгоритмы схожести.

1. Ансамблевые алгоритмы (Random forest, XGBoost, LightGBM, CatBoost)


Что такое ансамблевые алгоритмы?

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

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

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

Когда используются?

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

Примеры алгоритмов

  • Random Forest (Случайный лес);
  • XGBoost;
  • LightGBM;
  • CatBoost.
Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека data scientist’а»

2. Объяснительные алгоритмы (Линейная регрессия, Логистическая регрессия, SHAP, LIME)


Что такое объяснительные алгоритмы?

Объяснительные алгоритмы позволяют нам обнаруживать и понимать переменные, которые оказывают статистически значимое влияние на выходное значение (результат). Вместо создания алгоритмов для предсказания значений, мы создаем объяснительные модели для понимания взаимосвязи между переменными в модели.

С точки зрения регрессии, очень много внимания уделяется статистически значимым переменным. Почему? Практически всегда вы будете работать с выборкой данных, являющейся подмножеством всей совокупности. Для того чтобы сделать верные выводы о совокупности по выборке, важно убедиться, что результат статистически значим.

Недавно появились два метода: SHAP и LIME. Они используются для интерпретации моделей машинного обучения.

Когда используются?

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

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

Примеры алгоритмов

Традиционные объяснительные модели, основанные на проверке гипотез:

  1. Линейная регрессия.
  2. Логистическая регрессия.

Алгоритмы для объяснения моделей машинного обучения:

  • SHAP;
  • LIME.

3. Алгоритмы кластеризации


Что такое алгоритмы кластеризации?

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

Когда используются?

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

Также кластеризация позволяет нам идентифицировать отдельные сегменты в наборе данных, основанном на различных переменных. Одна из самых распространенных задач кластерной сегментации – сегментация пользователей.

Примеры алгоритмов

Самые распространенные алгоритмы кластеризации – k-means и иерархическая кластеризация, хотя существуют и другие.

  • K-means;
  • иерархическая кластеризация.

4. Алгоритмы понижения размерности (PCA, LDA)


Что такое алгоритмы понижения размерности?

К алгоритмам понижения размерности относятся методы, уменьшающие число поступающих на вход переменных (или признаков) в датасете. Понижение размерности тесно связано с феноменом «проклятие размерности», утверждающим что с ростом размерности (число поступающих на вход переменных) увеличивается объем пространства, в результате чего данные становятся разреженными.

Когда используются?

Алгоритмы понижения размерности используются во многих случаях:

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

Примеры алгоритмов

Ниже представлены два самых распространенных алгоритма понижения размерности:

  • метод главных компонент (PCA);
  • линейный дискриминантный анализ (LDA).

5. Алгоритмы схожести (KNN, расстояния: Евклида, косинусное, Левенштейна, Джаро-Винклера, SVD и т. д.)


Что такое алгоритмы схожести?

Алгоритмы схожести вычисляют сходство пары записей/узлов/точек/текстов. Есть алгоритмы, которые вычисляют расстояние между двумя значениями данных, такими как расстояние Евклида. А также есть алгоритмы, вычисляющие схожесть текстов, такие как алгоритм Левенштейна.

Когда используются?

Алгоритмы сходства применяются повсеместно, но чаще всего в рекомендательных системах.

  1. Какие статьи предложит тебе Medium, основываясь на прочитанном тобой ранее?
  2. Какие ингредиенты вы можете использовать для замены голубики?
  3. Какие треки предложит тебе Spotify, основываясь на треках, которые тебе уже нравятся?
  4. Какие продукты Amazon предложит тебе, основываясь на истории покупок?

Это только некоторые примеры применения алгоритмов сходства в повседневной жизни.

Примеры алгоритмов

Ниже приведены самые популярные алгоритмы сходства:

  • К-ближайших соседей;
  • расстояние Евклида;
  • косинусное сходство;
  • алгоритм Левенштейна;
  • алгоритм Джаро-Винклера;
  • сингулярное разложение (SVD).
***

Материалы по теме

11
Июл
2022

📰 Weekly #8: новости, подкасты, отборные статьи и обучающие материалы по Python, Data Science, Go, C#, C/C++ и мобильной разработке

В этом выпуске: Экспертное Android-собеседование: Дудаков — Иванов; 5 продвинутых концепций SQL, которые вы должны знать в 2022 году; Создание одностраничного приложения с помощью Pyodide; Краткий обзор популярных веб-фреймворков для Go; Fe…

06
Июл
2022

🎲 Орел или решка? Основы теории вероятностей простыми словами

Теория вероятностей – это раздел математики, изучающий случайные события, их свойства и действия над ними. В этой статье мы рассмотрим ее определение, основы и применение. Плюс три простых задачи с решениями.

Данная статья является переводом. Ссылка на оригинальную статью.

❓ Что такое теория вероятностей?

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

Определение теории вероятностей

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

Пример теории вероятностей

Предположим, нам необходимо определить вероятность выпадения числа 4 при бросании игральной кости. Число благоприятных исходов равно 1. Возможные исходы игральной кости – {1, 2, 3, 4, 5, 6}. Из этого следует, что всего существует 6 исходов. Таким образом, вероятность выпадения 4 при бросании игральной кости, используя теорию вероятности, можно вычислить как 1 / 6 ≈ 0,167.

🎲 Основы теории вероятностей

Мы можем понять эту область математики с помощью нескольких основных терминов, напрямую связанных с теорией вероятностей.

Случайный эксперимент

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

Пространство выборки

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

Событие

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

Примеры событий:

  1. Независимые – те, на которые не влияют другие события, являются независимыми.
  2. Зависимые – те, на которые влияют другие события.
  3. Взаимоисключающие – события, которые не могут произойти в одно и то же время.
  4. Равновероятные – два или более события, которые имеют одинаковые шансы произойти.
  5. Исчерпывающие – это события, которые равны выборочному пространству эксперимента.

Случайная величина

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

Существует два типа случайных величин:

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

Вероятность

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

Формула вероятности P(A): количество благоприятных исходов для A делимое на общее количество возможных исходов.
Формула вероятности P(A): количество благоприятных исходов для A делимое на общее количество возможных исходов.

Условная вероятность

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

Обозначается как P(A | B).

Если хочешь подтянуть свои знания по математике, загляни на наш курс «Математика для Data Science», на котором ты:

  • Усвоишь специальную терминологию и сможешь читать статьи по Data Science без постоянных обращений к поисковику.
  • Подготовишься к успешной сдачи вступительных экзаменов в Школу анализа данных Яндекс.
  • Овладеешь математическим аппаратом, который необходим, чтобы стать специалистом в Data Science.

Ожидание

Ожидание случайной величины X можно определить как среднее значение результатов эксперимента, проводимого многократно. Ожидание обозначается как E[X]. Также известно как среднее значение случайной величины.

Дисперсия

Дисперсия – это мера, которая показывает, как распределение случайной величины изменяется относительно среднего значения. Дисперсия определяется как среднее квадратичное отклонение от среднего значения случайной величины. Обозначается как Var[X].

Функция распределения теории вероятностей

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

Массовая функция вероятности

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

Функция плотности вероятности

Функция плотности вероятности – это вероятность того, что непрерывная случайная величина принимает множество возможных значений.

Формулы теории вероятностей

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

Наиболее важные формулы:

  1. Теоретическая вероятность: Число благоприятных исходов / Число возможных исходов.
  2. Эмпирическая вероятность: Число случаев, когда событие происходит / Общее число испытаний.
  3. Правило сложения: P(A ∪ B) = P(A) + P(B) – P(A∩B), где A и B – события.
  4. Правило комплементарности: P(A’) = 1 – P(A). P(A’) означает вероятность того, что событие не произойдет.
  5. Независимые события: P(A∩B) = P(A) ⋅ P(B).
  6. Условная вероятность: P(A | B) = P(A∩B) / P(B).
  7. Теорема Байеса: P(A | B) = P(B | A) ⋅ P(A) / P(B).
  8. Массовая функция вероятности: f(x) = P(X = x).
  9. Функция плотности вероятности: p(x) = p(x) = dF(x) / dx, где F(x) – кумулятивная функция распределения.
  10. Ожидание непрерывной случайной величины: ∫xf(x)dx, где f(x) является МФВ (Массовой функцией вероятности).
  11. Ожидание дискретной случайной величины: ∑xp(x), где p(x) – это ФПВ (Функцией плотности вероятности).
  12. Дисперсия: Var(X) = E[X2] – (E[X])2.

Применение теории вероятностей

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

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

🏋️ Практические задания


Задача 1: При бросании двух игральных костей, какова вероятность того, что выпадет комбинация, сумма которой будет равна 8?

Решение

Задача 2: Какова вероятность вытащить карту королеву из колоды?

Решение

Задача 3: Из 10 человек 3 купили карандаши, 5 купили тетради, а 2 купили и карандаши, и тетради. Если покупатель купил тетрадь, какова вероятность того, что он также купил карандаш?

Решение

В заключение

Подведем итоги:

  • Теория вероятностей – это раздел математики, в котором рассматриваются вероятности случайных событий.
  • Понятие вероятности объясняет возможность наступления того или иного события.
  • Значение вероятности всегда лежит между 0 и 1.
  • В теории вероятностей все возможные исходы случайного эксперимента составляют пространство выборки.
  • Теория вероятностей использует такие важные понятия, как случайные величины и кумулятивные функции распределения для моделирования случайного события. Сюда же относится определение различных вероятностей, связанных с этим.

Если хочешь подтянуть свои знания по математике, загляни на наш курс «Математика для Data Science», который включает в себя:

  • 47 видеолекций и 150 практических заданий.
  • Консультации с преподавателями курса.
30
Июн
2022

🗣️ Решаем задачу перевода русской речи в текст с помощью Python и библиотеки Vosk

В статье научимся конвертировать русскую речь в текст (транскрибация) с помощью Python и библиотеки Vosk с её ML-моделями.

Продолжаем серию статей по практическому применению Python. Попробуем решить задачу транскрибации записи речи из аудио в текст. Это не rocket science 🙂 Такие задачи уже решаются продуктами на рынке и довольно неплохо (Сбер, Yandex). Моя цель – не конкурировать, а показать, что такие серьезные задачи можно начать решать с минимальным порогом входа: достаточно базовых знаний в программировании на Python.

Направление естественного анализа речи – целая область в NLP (Natural Language Processing). Дело в том, что компьютер очень быстро считает, но вот с пониманием смысла у него проблемы. Программа может быстро подсчитать количество слов в произведении «Война и мир», но с анализом смысла будут проблемы. А вот NLP пытается докопаться до смыслов.

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

Для работы нам понадобится Python 3.8+, библиотека для распознавания речи – Vosk. Немного про библиотеку Vosk:

  • Поддерживает 20+ языков и диалектов.
  • Работает без доступа к сети даже на мобильных устройствах – Raspberry Pi, Android, iOS.
  • Устанавливается с помощью простой команды pip3 install vosk без дополнительных шагов.
  • Сделана для потоковой обработки звука, что позволяет реализовать мгновенную реакцию на команды.
  • Поддерживает несколько популярных языков программирования – Java, C#, Javascript, Python.
  • Позволяет быстро настраивать словарь распознавания для улучшения точности распознавания.
  • Позволяет идентифицировать говорящего.
  • Библиотека активно контрибьютится и поддерживается.
Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека data scientist’а»

Перевод аудио в текст

Чтобы реализовать транскрибацию из аудио в текст, нам необходимо решить следующие задачи:

  1. Вытащить части речи из аудио.
  2. Расставить пробелы на паузах между частями речи.
  3. Добавить пунктуацию в текст.

Все действия буду делать на машине с Ubuntu 20 (Python 3.8) со следующей конфигурацией:

  • CPU 2vCPU.
  • RAM 12GB.
  • HDD 20GB.

Причина использования такого количества RAM в том, что мы делаем распознавание на универсальной модели, то есть модели размером 50 Мб, которая требует в разы меньше оперативной памяти в работе, чем полноценная модель. Правда, качество распознавания в этом случае уменьшится.

Создаем директорию speech:

        mkdir speech
cd speech
    

Далее необходимо поставить зависимости для Python:

        apt install python3-pip
pip3 install ffmpeg
pip3 install pydub
pip3 install vosk
pip3 install torch
pip3 install transformers
    

Также скачиваем и распаковываем модель для распознавания русской речи, выполнив команды:

        curl -o ./model.zip https://alphacephei.com/vosk/models/vosk-model-ru-0.22.zip
unzip model.zip
mv vosk-model-ru-0.22/ model
rm -rf model.zip
    

В результате этих действий мы скопировали к себе модель, разархивировали ее и переименовали директорию. Также удалили скачанный архив. Всё-таки он весит 1.5 Гб. Для расстановки пунктуации делаем похожие действия: скачиваем еще одну модель весом 1.5 Гб.

        curl -o recasepunc.zip https://alphacephei.com/vosk/models/vosk-recasepunc-ru-0.22.zip
unzip recasepunc.zip
mv vosk-recasepunc-ru-0.22/ recasepunc
rm -rf recasepunc.zip
    

Код файла app.py, который выполняет перевод аудио в текст.

        from vosk import Model, KaldiRecognizer, SetLogLevel
from pydub import AudioSegment
import subprocess
import json
import os

SetLogLevel(0)

# Проверяем наличие модели
if not os.path.exists("model"):
    print ("Please download the model from https://alphacephei.com/vosk/models and unpack as 'model' in the current folder.")
    exit (1)

# Устанавливаем Frame Rate
FRAME_RATE = 16000
CHANNELS=1

model = Model("model")
rec = KaldiRecognizer(model, FRAME_RATE)
rec.SetWords(True)

# Используя библиотеку pydub делаем предобработку аудио
mp3 = AudioSegment.from_mp3('Song.mp3')
mp3 = mp3.set_channels(CHANNELS)
mp3 = mp3.set_frame_rate(FRAME_RATE)

# Преобразуем вывод в json
rec.AcceptWaveform(mp3.raw_data)
result = rec.Result()
text = json.loads(result)["text"]

# Добавляем пунктуацию
cased = subprocess.check_output('python3 recasepunc/recasepunc.py predict recasepunc/checkpoint', shell=True, text=True, input=text)

# Записываем результат в файл "data.txt"
with open('data.txt', 'w') as f:
    json.dump(cased, f, ensure_ascii=False, indent=4)
    

Последний штрих – разместить файл Song.mp3 в нашей директории с исполняемым файлом app.py. Затем запускаем app.py. В результате наша программа обработает файл .mp3 и на основе натренированных моделей из библиотеки Vosk сделает транскрибацию аудио в текст с сохранением результата в файл data.txt.

***

Наша реализация решает поставленные задачи в начале статьи. Но это скорее MVP, чем продуманное решение для продакшена. Если мы начнем углубляться, то перед нами встанут задачи обработки больших аудио (от часа и более), организации многопоточности, балансировки и горизонтального масштабирования и много чего интересного. Библиотека VOSK позволяет со всем этим справиться. Но это уже другая история 🙂

Материалы по теме

13
Июн
2022

🌳 Деревья и графы: что это такое и почему их обязательно нужно знать каждому программисту

В этой части материала мы рассмотрим деревья и графы, а также теорию, которая стоит за ними. Выполним реализацию на языке Python и покажем, как можно использовать эти понятия на примере челленджей Leetcode.

Деревья. Теория

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

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

Один из типов деревьев – двоичное дерево поиска (BST – binary search tree). В предыдущих частях материала мы упоминали, насколько эффективно BST извлекают данные. Эта эффективность обусловлена двумя важными правилами, которые определяют структуру BST:

  1. Узел может иметь не более двух дочерних узлов.
  2. Каждый узел в левом поддереве должен содержать меньшее значение, а каждый узел в правом поддереве – большее.

Поиск значения в BST занимает O(log n) времени. Это означает, что можно очень быстро найти требуемое значение среди миллионов или даже миллиардов записей.

Предположим, мы ищем узел со значением x. Чтобы быстро найти его в BST, воспользуемся следующим алгоритмом:

  1. Начать с корня дерева.
  2. Если x = значению узла: остановиться.
  3. Если x < значения узла: перейти к левому дочернему узлу.
  4. Если x > значения узла: перейти к правому дочернему узлу.
  5. Перейти к шагу 2.

При отсутствии уверенности в существовании искомого узла, необходимо изменить шаги 3 и 4 для остановки поиска.

***

Если хочешь подтянуть свои знания по алгоритмам, загляни на наш курс «Алгоритмы и структуры данных», на котором ты:

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

Реализация

Создание узла TreeNode идентично созданию узла ListNode. Единственное отличие в том, что вместо одного атрибута у нас два: left и right, которые ссылаются на левые и правые дочерние узлы.

        class TreeNode:
    def __init__(self, val=None, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

    def __repr__(self):
      return f"TreeNode со значением {self.val}"
    

Таким образом, мы создаем дерево с тремя уровнями. Стоит отметить, что данное дерево – просто бинарное, а не BST, потому что значения не отсортированы.

        # Уровень 0
root = TreeNode('a')

# Уровень 1
root.left = TreeNode('b')
root.right = TreeNode('c')

# Уровень 2
root.left.left = TreeNode('d')
root.left.right = TreeNode('e')

root.right.left = TreeNode('f')
root.right.right = TreeNode('g')

#            a
#          /   \
#         b     c
#        / \   / \
#       d   e f   g
    

Примеры

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

Обход дерева начинается с корневого узла. Далее, используем набор шагов для обработки каждого узла, включая дочерние. Порядок обработки узлов зависит от того, в какой последовательности идет подготовка родительского узла относительно дочерних: до (pre-order), между левым и правым (in-order) и после (post-order). На примерах, приведенных ниже, обход начинался с корневого узла. Однако, порядок обработки узлов был различен.


Эти три типа обхода могут быть реализованы следующими методами:

  • с итерацией – использование цикла while и стека. В этом случае удаление данных возможно только с конца.
  • с рекурсией – использование функции, которая вызывает сама себя.

Есть четвертый тип обхода – порядок уровней (level-order traversal). Этот способ использует очередь (queue). Удаление данных здесь возможно только с начала.


Для первых трех типов обработки узлов паттерны практически идентичны. Просто выберем обход в порядке возрастания. Ниже разберем итеративный и рекурсивный методы для LC 94: Binary Tree Inorder Traversal, начиная с итеративной версии:

        # Итеративный обход
def traverse_in_order(root: TreeNode) -> List[int]:
    """
    Обход двоичного дерева, возвращается список значений по порядку
    """
    answer = []
    stack = [(root, False)]   # (узел, посещен ли он еще)

    while stack:
        node, visited = stack.pop()

        if node:

            if visited:
                answer.append(node.val)
            else:
                stack.append((node.right, False))
                stack.append((node, True))
                stack.append((node.left, False))

    return answer
    

Алгоритм действий:

  1. Строки 6-7: Создаем список для ответа (answer) и для стека (stack). List содержит кортеж корневого узла и переменную False, которая указывает на то, что этот узел еще не был затронут.
  2. Строка 9: Запускаем цикл while. Он будет выполняться до тех пор, пока в стеке есть хотя бы один элемент.
  3. Строки 10-12: Удаляем последний элемент из стека с помощью .pop. Далее проверяем существование node (узла). Node не всегда будет существовать в последующих итерациях для узлов без дочерних элементов.
  4. Строки 14-15: Добавляем значение узла к ответу. Действие происходит только в том случае, когда node существует и мы уже затрагивали этот узел.
  5. Строки 16-19: Добавляем правый дочерний, текущий и левый дочерний узлы. На правый и левый узлы ставим пометку (флаг), которая показывает, что они не были проиндексированы на данном этапе выполнения программы. На текущий узел ставим флаг, который показывает, что он был замечен программой. Действие происходит до момента, когда мы посетили этот узел.
  6. Строки 9-19: Повторяем шаги 3-6, пока не обработаем все узлы.
  7. Строка 21: Возвращаем список значений узлов, которые отсортированы по порядку.

Примечание: Добавление узлов в стек (строки 17-19), может показаться неупорядоченным. Однако, стеки работают по принципу Last In First Out. Первый узел, который нужно обработать, должен быть последним узлом, который добавляется в стек. Поэтому, сначала добавляется правый, и только потом левый узел.

Далее расскажем о рекурсивном методе:

        # Рекурсивный обход
class Solution:
    def __init__(self):
        self.answer = []

    def traverse_inorder(self, root: TreeNode) -> List[Any]:
        """
        Проходим двоичное дерево, и возвращаем список значений в порядке их следования
        """
        self._traverse(root)
        return self.answer

    def _traverse(self, node: Optional[TreeNode]) -> None:
      """
      Рекурсивная функция для последовательного обхода
      """
        if not node:
            return None

        self._traverse(node.left)
        self.answer.append(node.val)
        self._traverse(node.right)
    

Алгоритм действий:

  1. Строки 2-4: Создаем класс, который инициализируется списком для ответа (self.answer).
  2. Строки 6-11: Определяем функцию traverse_inorder, которая принимает корневой узел дерева, вызывает рекурсивную функцию _traverse и возвращает self.answer.
  3. Строки 13-22: Определяем рекурсивную функцию _traverse, которая принимает узел. Функция проверяет существование node перед тем как вызвать саму себя на левом дочернем узле. Далее она добавляет значение узла к self.answer. Затем повторно происходит операция самовызова уже на правом дочернем узле.

Подведем итоги. Данные примеры мы рассматривали в контексте обхода in-order. Для того чтобы изменить порядок следования на pre-order или post-order, в обоих случаях нужно поменять порядок обработки узла относительно его дочерних элементов. Остальная часть кода остается неизменной.

        # Изменение обхода
def iterative(root):
    ...
    stack.append((node.right, False))
    stack.append((node.left, False))
    stack.append((node, True))     # <- изменение
    ...

def _traverse(self, node):
    ...
    self.answer.append(node.val)   # <- изменение
    self._traverse(node.left)
    self._traverse(node.right)
    

Графы. Теория

Деревья расширяют область применения связанных списков, позволяя иметь более одного дочернего узла. При помощи графов можно расширить область применения и ослабить строгую «родительскую» связь в деревьях. Узлы графа не имеют явной иерархии. Каждый узел может быть соединен с любым другим узлом.


Как правило, графы представлены в виде матриц смежности (adjacency matrix). Так, у приведенного выше графа будет следующая матрица.


Каждая строка и столбец представляют собой узел. Единица в строке i и столбце j, или A_{ij}=1, означает связь между узлом i и узлом j.

A_{ij}=0 означает, что узлы i и j не связаны.

Ни один из узлов в этом графе не связан с самим собой. Следовательно, диагональ матрицы равна нулю. Аналогично, A_{ij} = A_{ji}, потому что связи ненаправленны. То есть если узел A связан с узлом B, то B связан с A. В результате матрица смежности симметрична по диагонали.

Рассмотрим пример, который поможет нам понять описанную выше теорию.


На представленных рисунках мы видим взвешенный граф с направленными ребрами. Обратите внимание, что связи больше не симметричны – вторая строка матрицы смежности пуста, потому что у B нет исходящих связей. Числа от 0 до 1 отражают силу связи. Например, граф C влияет на граф A сильнее, чем A на C.


Реализация

Реализуем невзвешенный и неориентированный граф. Основной структурой класса является список списков Python. Каждый из них – это строка. Индексы в списке представляют собой столбцы. При создании объекта Graph необходимо указать количество узлов n, чтобы создать список списков. Затем мы можем получить доступ к соединению между узлами a и b с помощью self.graph[a][b].

        from typing import List

class Graph:
    def __init__(self, n: int):
        self.graph = [[0]*n for _ in range(n)]

    def connect(self, a: int, b: int) -> List[List[int]]:
        """
        Обновляем self.graph для соединения узлов A и B.
        """
        self.graph[a][b] = 1
        self.graph[b][a] = 1
        return self.graph

    def disconnect(self, a: int, b: int) -> List[List[int]]:
        """
        Обновляем self.graph для разъединения узлов A и B.
        """
        self.graph[a][b] = 0
        self.graph[b][a] = 0
        return self.graph
    

Воссоздать график из первого примера можно следующим образом:

        g = Graph(4)

# соединения A
g.connect(0, 1)
g.connect(0, 2)

# соединения B (исключая A)
g.connect(1, 2)

# соединения C (исключая B)
g.connect(2, 3)
    

Пример

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


Чтобы ответить на вопрос LC 323: Количество связных компонентов, изучим каждый узел графа. Далее «посетим» соседние графы. Повторяем операцию до тех пор, пока не встретим только те узлы, которые уже были замечены программой. После этого проверим наличие в графе узлов, которые еще не были замечены. Если такие узлы присутствуют, то существует еще как минимум один кластер, поэтому нужно взять новый узел и повторить процесс.

        def get_n_components(self, mat: List[List[int]]) -> int:
    """
    Учитывая матрицу смежности, возвращает количество связанных компонентов
    """
    q = []
    unseen = [*range(len(mat))]

    answer = 0

    while q or unseen:

        # Если все соседние узлы прошли через цикл, переходим к новому кластеру
        if not q:
            q.append(unseen.pop(0))
            answer += 1

        # Выбираем узел из текущего кластера
        focal = q.pop(0)
        i = 0

        # Поиск связей во всех оставшихся узлах
        while i < len(unseen):
            node = unseen[i]

            # Если узел подключен к центру, добавляем его в очередь
            # чтобы перебрать его соседей
            # из невидимых узлов и избежать бесконечного цикла
            if mat[focal][node] == 1:
                q.append(node)
                unseen.remove(node)
            else:
                i += 1

    return answer
    

Алгоритм действий:

  1. Строки 5-8: Создаем очередь (q), список узлов (unseen) и количество компонентов (answer).
  2. Строка 10: Запускаем цикл while, который выполняем до тех пор, пока в очереди есть узлы, которые нужно обработать или узлы, которые не были замечены.
  3. Строки 13-15: Если очередь пуста, удаляем первый узел из непросмотренных и увеличиваем количество компонентов.
  4. Строки 18-19: Выбираем следующий доступный узел в очереди (focal).
  5. Строка 22: Запускаем цикл while, который выполняем до тех пор, пока не обработаем все оставшиеся узлы.
  6. Строка 23: Даем имя текущему узлу для оптимизации кода.
  7. Строки 28-30: Если текущий узел подключен к центру, добавляем его в очередь узлов текущего кластера. Удаляем его из списка тех узлов, которые могут находиться в невидимом кластере. Благодаря этому действию, мы избегаем бесконечного цикла.
  8. Строки 31-32: Если текущий узел не подключен к focal (центру), переходим к следующему узлу.

Заключение

Графы и деревья – основные структуры данных. Спектр их применения огромен. Например, графы используются там, где необходим алгоритм поиска решений. Реальный пример их использования – sea-of-nodes JIT-компилятора.

Деревья используются тогда, когда мы должны произвести быстрое добавление/удаление объекта с поиском по ключу. Например, в различных словарях и индексах БД (Баз данных). Кроме того, деревья являются неотъемлемой частью случайного леса – алгоритма машинного обучения.

В следующей части материала мы приступим к изучению хэш-таблиц.

***

Базовый и продвинутый курс «Алгоритмы и структуры данных» включает в себя:

  1. Живые вебинары 2 раза в неделю.
  2. 47 видеолекций и 150 практических заданий.
  3. Консультации с преподавателями курса.

Материалы по теме

28
Апр
2022

🗄️ ✔️ 10 лучших практик написания SQL-запросов

Делимся рекомендациями по решению распространенных проблем с SQL, с которыми часто сталкиваются специалисты по работе с данными.

Статья является переводом. Оригинал доступен по ссылке.

Я работаю с данными уже 3 года, и меня до сих пор удивляет, что есть люди, которые хотят стать аналитиками, практически не зная SQL. Хочу особо подчеркнуть, что SQL является фундаментальным языком независимо от того, кем вы будете работать в сфере анализа данных.

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

Данные советы предназначены для всех специалистов, независимо от опыта. Я перечислил самые распространенные случаи в моей практике, разместив в порядке возрастания сложности.

Для примеров я буду использовать базу данных SQLite: sql-practice.com

1. Проверка уникальных значений в таблице

        SELECT count(*), count(distinct patient_id) FROM patients

    

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

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

Однако становится немного сложнее, когда у вас есть несколько столбцов, которые создают первичный ключ. Чтобы решить эту проблему, просто объедините столбцы, составляющие первичный ключ, после ключевого слова DISTINCT. Простой пример — объединение имени и фамилии для создания первичного ключа.

        SELECT count(*), count(distinct first_name || last_name) FROM patients
    

2. Поиск повторяющихся записей

        SELECT 
    first_name 
    , count(*) as ct
    
FROM patients
GROUP BY
    first_name
HAVING
    count(*) > 1
ORDER BY 
    COUNT(*) DESC
;
    

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

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

Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека программиста»

3. Обработка NULL с DISTINCT

        with new_table as (
select patient_id from patients
UNION
select null
)

select 
    count(*)
  , count(distinct patient_id)
  , count(patient_id) 

from new_table
    

Результатом запроса будет значение 4531 для столбца COUNT(*) и 4530 для двух оставшихся столбцов. Когда вы указываете столбец, ключевое слово COUNT исключает нулевые значения. Однако, при использовании звездочки в подсчет включаются значения NULL. Это может сбивать с толку при проверке, является ли столбец первичным ключом, поэтому я посчитал нужным упомянуть об этом.

4. CTE > Подзапросы

        -- Use of CTE
with combined_table as (
select
  *
 
FROM patients p
JOIN admissions a 
  on p.patient_id = a.patient_id
)

, name_most_admissions as (
select
    first_name || ' ' || last_name as full_name
  , count(*)                       as admission_ct
  
FROM combined_table
)

select * from name_most_admissions
;

-- Use of sub-queries :(
select * from 
   (select
        first_name || ' ' || last_name as full_name
      , count(*)                       as admission_ct
  
    FROM (select
             *
 
          FROM patients p
          JOIN admissions a 
              on p.patient_id = a.patient_id
          ) combined_table
    ) name_most_admissions
;
    

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

5. Использование SUM и CASE WHEN вместе

        select 
     sum(case when allergies = 'Penicillin' and city = 'Burlington' then 1 else 0 end) as allergies_burl
   , sum(case when allergies = 'Penicillin' and city = 'Oakville' then 1 else 0 end)   as allergies_oak
  
from patients
    

Предложение WHERE может работать, если вы хотите суммировать количество пациентов, отвечающих определенным условиям. Но если вы хотите проверить несколько условий, вы можете использовать ключевые слова SUM и CASE WHEN вместе. Это делает код лаконичным и легко читаемым.

Данную комбинацию также можно использовать в выражении WHERE, как в примере ниже.

        select
  * 
FROM patients
WHERE TRUE
  and 1 = (case when allergies = 'Penicillin' and city = 'Burlington' then 1 else 0 end)
    

6. Будьте осторожны с датами

        with new_table as (
select
    patient_id
  , first_name
  , last_name
  , time(birth_date, '+1 second') as birth_date

from patients
where TRUE
   and patient_id = 1

UNION
  
select
    patient_id
  , first_name
  , last_name
  , birth_date 

from patients
WHERE TRUE
  and patient_id != 1
)

select 
  birth_date 
  
from new_table 
where TRUE 
  and birth_date between '1953-12-05' and '1953-12-06'
    

В этой базе данных все даты сокращены до дня. Это означает, что все значения времени столбца Birthday_date в этом примере равны 00:00:00. Однако в реальных наборах данных это обычно не так.

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

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

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

7. Не забывайте об оконных функциях

        select
    p.*
  , MAX(weight) over (partition by city) as maxwt_by_city
   
 from patients p
    

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

Я видел, как некоторые аналитики пробовали обходные пути, когда оконная функция делала код короче и читабельнее и, скорее всего, также экономила им время.

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

8. По возможности избегайте DISTINCT

Последние 3 совета не содержат примеров программного кода, но они так же важны, как и приведенные выше. По моему опыту, специалисты по работе с данными слишком часто используют distinct, чтобы предотвратить дублирование, не разбираясь в причине.

Это ошибка. Если вы не можете с самого начала объяснить, почему в данных есть дубликаты, возможно, вы исключили из своего анализа какую-то полезную информацию. Вы всегда должны быть в состоянии объяснить, почему вы помещаете distinct в таблицу и почему есть дубликаты. Использование WHERE обычно предпочтительнее, так как вы можете увидеть то, что исключается.

9. Форматирование SQL

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

Вы можете заметить, что в примерах я использовал TRUE в WHERE выражении. Это было сделано для того, чтобы все аргументы в выражении WHERE начинались с AND. Таким образом, аргументы начинаются с одного и того же места.

Еще один быстрый совет — добавить запятые в начале столбца в выражении SELECT. Это позволяет легко найти пропущенные запятые, поскольку все они будут упорядочены.

10. Совет по отладке

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

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

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

***

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

Материалы по теме

31
Мар
2022

📊 ТОП-30 ресурсов с данными для машинного обучения

Сбор данных (Data collection) является техникой профессионалов, а готовые данные в интернете часто ограничены авторскими правами. В этом материале мы расскажем о некоторых ресурсах с предобработанной «датой», которую вы можете использовать в своей работе.

Где искать датасеты?

1. Kaggle

Если вы когда-нибудь проходили курсы или хакатоны, связанные с наукой о данных, вы наверняка сталкивались с Kaggle. Это сообщество специалистов по Data Science. Изначально оно было соревновательной платформой, однако со временем на Kaggle появились другие разделы, в том числе возможность делиться данными.

2. Data World

Каталог, о котором редко упоминают – Data world. По способу поиска он похож на поисковик Google. Разница в том, что глубина поиска больше, например, он включает в себя подфайлы, которые могут содержать нужные данные. Это особенно важно при поиске вторичных данных.

3. UCI Machine Learning Repository

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

Датасеты общего назначения

Датасеты общего назначения можно использовать в простых Pet-проектах. Для анализа (EDA) или прогнозирования на их основе не нужны углубленные знания Data Science. Например, вы можете использовать простые техники машинного обучения, не углубляясь в Deep Learning.

В этом разделе мы расскажем о датасетах, которые являются «классикой» машинного обучения.

Государственные датасеты

1. Данные федерального правительства США

Этот портал позволяет загружать данные из различных государственных учреждений США – от бюджетов организаций до документов школ. Спектр тем настолько широк, что делает этот ресурс идеальным для применения в разных сферах вашей деятельности, связанной с датой.

При поиске открытых данных на сайте data.gov пользователи могут применять дополнительные фильтры по категориям: темы, тип набора данных, местоположение, теги, формат файла, организации и т. д.

2. Данные министерства здравоохранения РФ

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

3. Данные министерства культуры РФ

Этот ресурс предоставляет информацию о данных на тех же условиях, что и Министерство здравоохранения РФ.

Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека data scientist’а»

Данные о жилье


1. Lincolnshire (UK) House Prices

Среднемесячные цены на жилье (£) для графства Линкольншир (Англия, UK) и округов.

Все цифры включают сделки с недвижимостью от 10 000 фунтов стерлингов до более чем 1 млн. Данные отфильтрованы по типам домов. Набор данных обновляется ежемесячно за 12-месячный период.

2. Zillow Housing data

Этот набор данных состоит из нескольких датасетов:

  • Цена дома (Home values) – скорректированный на сезон показатель стоимости дома и изменения на рынке жилья в данном регионе.

В этом датасете используется мерка ZHVI – Zillow Home Values Index.

Существует ZHVI верхнего уровня (стоимость домов в диапазоне от 65-го до 95-го процентиля для данного региона) и ZHVI нижнего уровня (стоимость домов в диапазоне от 5-го до 35-го процентиля для данного региона). Zillow также публикует ZHVI для всех типов домов и апартаментов, учитывая стоимость, количество спален и метраж.

  • Прогноз цены дома (Zillow Home Values Forecast) – прогноз индекса стоимости жилья Zillow (ZHVI) на один год. ZHVF создается с использованием среза данных ZHVI по всем домам и доступен как в необработанном, так и в скорректированном виде.
  • Аренда (Rentals) – показатель рыночной ставки арендной платы в данном регионе. ZORI (Zillow Observed Rent Index) – индекс арендной платы, который определяется по всей выборке арендного жилья, обеспечивая репрезентативность данных для всего рынка аренды.

Индекс рассчитывается в долларах путем вычисления среднего значения объявленной арендной платы, которая попадает в диапазон от 40-го до 60-го процентиля для всех домов и квартир в данном регионе. Подробную информацию можно найти в методологии ZORI.

Экономика и финансы

1. Глобальный датасет инфляции в мире

Экспертная группа Всемирного банка создала глобальную базу данных инфляции, которая охватывает 196 стран за период с 1970 до 2021 гг. Она включает шесть показателей инфляции с тремя частотами – годовая, квартальная и месячная:

  • общий индекс цен
  • индекс цен на продукты питания
  • индекс цен на энергоносители,
  • базовый индекс потребительских цен
  • индекс цен производителей
  • дефлятор ВВП

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

2. Рост населения

Производная от общей численности населения. Данные можно загрузить в формате .csv, xml и .excel.

3. Рост ВВП

Данные Всемирного банка и файлы данных национальных счетов ОЭСР (Организация экономического сотрудничества и развития).

Датасеты для глубокого обучения

В сфере Deep Learning данные, в большинстве случаев, выражены не табличной датой, а изображениями, видео или текстом. В настоящее время, во многих научных работах используют запатентованные наборы данных, которые не принято публиковать для широкой публики. Это становится проблемой, если вы хотите учиться и практиковать свои навыки. Ниже мы привели самые интересные датасеты, которые можно использовать в сфере глубокого обучения. Они доступны каждому, а для их использования не требуется никаких дополнительных условий.

Компьютерное зрение

1. CIFAR-10

Набор данных CIFAR-10 состоит из 60 000 цветных изображений 32×32 в 10 классах, по 6000 изображений в каждом классе. Он содержит 50 000 обучающих и 10 000 тестовых изображений. Изображения разделены на пять обучающих и одну тестовую партию по 10 000 изображений. Тестовая партия включает в себя 1000 случайно выбранных изображений из каждого класса. Обучающие партии содержат остальные изображения в случайном порядке. Однако, некоторые из обучающих партий могут содержать больше изображений из одного класса, чем из другого. Между собой обучающие партии включают 5000 изображений из каждого класса.

2. CityScapes

Это новый масштабный набор данных, который содержит разнообразные стерео видеопоследовательности, записанные в уличных сценах из 50 городов. В них содержатся высококачественные аннотации на уровне пикселей (pixel-level) для 5000 кадров, в дополнение к набору из 20 000 слабо аннотированных кадров. Таким образом, CityScapes предлагает значительно большой набор данных, чем аналогичные ресурсы.

CityScapes предназначен:

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

3. Objectron

Набор данных Objectron представляет собой коллекцию коротких, ориентированных на объект видеоклипов, которые сопровождаются метаданными AR-сессии. Они включают в себя расположения камеры, разреженные облака точек и характеристику плоских поверхностей в окружающей среде. В каждом видеоролике камера перемещается вокруг объекта, снимая его под разными углами.

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

Для обеспечения географического разнообразия набор данных собран в 10 странах на 5 континентах. Вместе с «датой» ресурс предлагает решение для обнаружения 3D-объектов четырех категорий: обуви, стульев, кружек и камер. Модели, приведенные в качестве примера, обучены с использованием данных Objectron и выпущены в MediaPipe.

Анализ тональности текста


1. Sentiment analysis

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

Так, данные, взятые с IMDb – это бинарный набор для анализа настроений. Он состоит из 50 000 отзывов из базы данных фильмов (IMDb), помеченных как положительные или отрицательные. Данные содержат только поляризованные отзывы. Отрицательный отзыв имеет оценку ≤ 4 из 10, положительный – ≥ 7 из 10. На каждый фильм включается не более 30 рецензий. Модели оцениваются по точности.

2. SMS спам

Коллекция SMS-спама v.1 – общедоступный набор SMS-сообщений с метками, которые были собраны для исследования спама с мобильных телефонов. Данные состоят из 5574 англоязычных, реальных и неконсолидированных сообщений, помеченных как легитимные (ham) или спам.

Сообщения SMS-спама были вручную извлечены с веб-сайта Grumbletext. Это британский форум, на котором пользователи мобильных телефонов публично заявляют о спамовых SMS-сообщениях. Идентификация текста спам-сообщений в претензиях – сложная и трудоемкая задача. Она включает тщательное сканирование сотен веб-страниц.

3. WikiQA

WikiQA представляет собой набор пар вопросов и предложений. Они были собраны и аннотированы для исследования ответов на вопросы в открытых доменах.

Большинство предыдущих работ по выбору предложений для ответа сосредоточено на наборе данных, созданном на основе данных TREC-QA, который включает вопросы, созданные редакторами, и предложения-кандидаты для ответа, отобранные по совпадению содержательных слов в вопросе.

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

Обработка естественного языка (NLP)


1. Text classification

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

Так, TREC – это набор данных для классификации вопросов, который состоит из открытых вопросов, основанных на фактах. Они разделены на широкие семантические категории. Датасет имеет шестиклассную (TREC-6) и пятидесятиклассную (TREC-50) версии. Обе версии включают 5452 обучающих и 500 тестовых примеров.

2. Amazon Reviews dataset

Этот набор данных состоит из нескольких миллионов отзывов покупателей Amazon и их оценок. Датасет используется для возможности обучения fastText, анализируя настроения покупателей. Идея состоит в том, что несмотря на огромный объем данных – это реальная бизнес-задача. Модель обучается за считанные минуты. Именно это отличает Amazon Reviews от аналогов.

3. Yelp dataset

Набор данных Yelp – это множество предприятий, отзывов и пользовательских данных, которые можно применить в Pet-проекте и научной работе. Также можно использовать Yelp для обучения студентов во время работы с базами данных, при изучении NLP и в качестве образца производственных данных. Датасет доступен в виде файлов JSON и является «классикой» в обработке естественного языка.

Автопилоты

1. ONCE Dataset

Набор данных ONCE – крупномасштабный набор данных автономного вождения с аннотациями 2D и 3D объектов.

Включает в себя:

  1. 1 млн кадров LiDAR, 7 млн изображений с камер.
  1. 200 км² регионов вождения, 144 часа вождения.
  1. 15 000 полностью аннотированных сцен с 5 классами: автомобиль, автобус, грузовик, пешеход, велосипедист.
  1. Разнообразные условия: день/ночь, солнце/дождь, город/пригород.

2. Ford AV dataset

Ford AV dataset создан в рамках программы AWS Public Dataset Program. Представленные данные организованы на основе временных рядов. Все разделы содержат подразделы для каждого транспортного средства и карт. Каждый подраздел Vehicle включает журналы в формате rosbag, изображения PNG и файлы калибровки всех датчиков. Калибровочные данные для каждого автомобиля предоставляются отдельно.

3. Canadian Adverse Driving Conditions Dataset

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

Включает в себя:

  1. 56 000 изображений с камер
  2. 7 000 разверток LiDAR
  3. 75 сцен по 50-100 кадров в каждой
  4. 10 классов аннотаций
  5. 28 194 Автомобиля
  6. 62 851 Пешеход
  7. 20 441 Грузовик
  8. 4867 Автобусов
  9. 4808 Мусорных контейнеров
  10. 3205 Объектов, направляющих движение
  11. 705 Велосипедов
  12. 638 Пешеходов с объектом
  13. 75 Лошадей и колясок
  14. 26 Животных
  15. Полный набор датчиков: 1 LiDAR, 8 камер, постобработанный GPS/IMU
  16. Неблагоприятные погодные условия вождения, включая снег

Медицинские данные


1. Health Science Library

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

HSL предлагает клинические данные шести основных типов:

  1. Электронные медицинские карты
  2. Административные данные
  3. Данные о претензиях
  4. Регистры пациентов/заболеваний
  5. Медицинские опросы
  6. Данные клинических исследований

2. DeepLesion

Датасет хронических заболеваний в США (US chronic diseases).

DL предлагает набор данных, которые были получены в результате более 10 тыс. исследований на 4 тыс. уникальных пациентов.

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

Список лучших агрегаторов баз данных ML

Лучшие открытые наборы данных (для машинного обучения и анализа)

Приведенные выше датасеты были найдены в результате мониторинга большого количества ресурсов. Поиск этих данных – процесс трудоемкий и требует времени. Он подходит, когда данные нужны вам разово. Однако, в случае, когда данные нужны постоянно, рекомендуется использовать агрегаторы. Наиболее известными из них являются open-data от GitHub, Dataset Search от Google и Microsoft Azure.

Открытые базы данных на Github

1. Congress legislators: база данных людей, избранных в конгресс США

Члены Конгресса США (1789 – настоящее время), комитеты Конгресса (1973 – настоящее время), состав комитетов (только текущий), а также президенты и вице-президенты США в формате YAML, JSON и CSV.

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

2. Covid data

Полный набор данных COVID-19 – коллекция данных о коронавирусе, которую ведет компания Our World in Data. Ресурс обновляется ежедневно в течение всего периода пандемии COVID-19.

Dataset Search от Google

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

По данным Google, их Dataset Search проиндексировал около 25 миллионов наборов данных. Компания считает, что поиск данных поможет создать экосистему обмена, в которой правительства и частные компании смогут обмениваться данными, используя передовые методы хранения и публикации. Большинство открытых датасетов используют schema.org, который является стандартом. Это означает, что любой желающий может свободно загружать и использовать эти наборы данных для исследований, бизнес-аналитики, или обучения ML-модели.

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

Открытые наборы данных Microsoft Azure

1. Russian Open Speech To Text

Коллекция образцов речи, полученных из различных аудиоисточников. Набор данных содержит короткие аудиоклипы на русском языке. Все файлы были преобразованы в opus, за исключением тех, которые служат для проверки. Основная цель набора данных – обучение моделей преобразования речи в текст.

Russian speech to text (STT) включает:

  1. ~16 миллионов высказываний
  2. ~20 000 часов
  3. 2,3 ТБ (без сжатия в формате .wav в int16), 356 ГБ

2. TartanAir

Одновременная локализация и картирование (SLAM) – одна из самых фундаментальных возможностей, необходимых для роботов. Благодаря повсеместной доступности изображений, визуальная SLAM (V-SLAM) стала важным компонентом многих автономных систем.

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

Больше о Microsoft azure datasets вы узнаете по этой ссылке.

***

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

Материалы по теме

31
Мар
2022

📊 ТОП-30 ресурсов с данными для машинного обучения

Сбор данных (Data collection) является техникой профессионалов, а готовые данные в интернете часто ограничены авторскими правами. В этом материале мы расскажем о некоторых ресурсах с предобработанной «датой», которую вы можете использовать в своей работе.

Где искать датасеты?

1. Kaggle

Если вы когда-нибудь проходили курсы или хакатоны, связанные с наукой о данных, вы наверняка сталкивались с Kaggle. Это сообщество специалистов по Data Science. Изначально оно было соревновательной платформой, однако со временем на Kaggle появились другие разделы, в том числе возможность делиться данными.

2. Data World

Каталог, о котором редко упоминают – Data world. По способу поиска он похож на поисковик Google. Разница в том, что глубина поиска больше, например, он включает в себя подфайлы, которые могут содержать нужные данные. Это особенно важно при поиске вторичных данных.

3. UCI Machine Learning Repository

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

Датасеты общего назначения

Датасеты общего назначения можно использовать в простых Pet-проектах. Для анализа (EDA) или прогнозирования на их основе не нужны углубленные знания Data Science. Например, вы можете использовать простые техники машинного обучения, не углубляясь в Deep Learning.

В этом разделе мы расскажем о датасетах, которые являются «классикой» машинного обучения.

Государственные датасеты

1. Данные федерального правительства США

Этот портал позволяет загружать данные из различных государственных учреждений США – от бюджетов организаций до документов школ. Спектр тем настолько широк, что делает этот ресурс идеальным для применения в разных сферах вашей деятельности, связанной с датой.

При поиске открытых данных на сайте data.gov пользователи могут применять дополнительные фильтры по категориям: темы, тип набора данных, местоположение, теги, формат файла, организации и т. д.

2. Данные министерства здравоохранения РФ

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

3. Данные министерства культуры РФ

Этот ресурс предоставляет информацию о данных на тех же условиях, что и Министерство здравоохранения РФ.

Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека data scientist’а»

Данные о жилье


1. Lincolnshire (UK) House Prices

Среднемесячные цены на жилье (£) для графства Линкольншир (Англия, UK) и округов.

Все цифры включают сделки с недвижимостью от 10 000 фунтов стерлингов до более чем 1 млн. Данные отфильтрованы по типам домов. Набор данных обновляется ежемесячно за 12-месячный период.

2. Zillow Housing data

Этот набор данных состоит из нескольких датасетов:

  • Цена дома (Home values) – скорректированный на сезон показатель стоимости дома и изменения на рынке жилья в данном регионе.

В этом датасете используется мерка ZHVI – Zillow Home Values Index.

Существует ZHVI верхнего уровня (стоимость домов в диапазоне от 65-го до 95-го процентиля для данного региона) и ZHVI нижнего уровня (стоимость домов в диапазоне от 5-го до 35-го процентиля для данного региона). Zillow также публикует ZHVI для всех типов домов и апартаментов, учитывая стоимость, количество спален и метраж.

  • Прогноз цены дома (Zillow Home Values Forecast) – прогноз индекса стоимости жилья Zillow (ZHVI) на один год. ZHVF создается с использованием среза данных ZHVI по всем домам и доступен как в необработанном, так и в скорректированном виде.
  • Аренда (Rentals) – показатель рыночной ставки арендной платы в данном регионе. ZORI (Zillow Observed Rent Index) – индекс арендной платы, который определяется по всей выборке арендного жилья, обеспечивая репрезентативность данных для всего рынка аренды.

Индекс рассчитывается в долларах путем вычисления среднего значения объявленной арендной платы, которая попадает в диапазон от 40-го до 60-го процентиля для всех домов и квартир в данном регионе. Подробную информацию можно найти в методологии ZORI.

Экономика и финансы

1. Глобальный датасет инфляции в мире

Экспертная группа Всемирного банка создала глобальную базу данных инфляции, которая охватывает 196 стран за период с 1970 до 2021 гг. Она включает шесть показателей инфляции с тремя частотами – годовая, квартальная и месячная:

  • общий индекс цен
  • индекс цен на продукты питания
  • индекс цен на энергоносители,
  • базовый индекс потребительских цен
  • индекс цен производителей
  • дефлятор ВВП

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

2. Рост населения

Производная от общей численности населения. Данные можно загрузить в формате .csv, xml и .excel.

3. Рост ВВП

Данные Всемирного банка и файлы данных национальных счетов ОЭСР (Организация экономического сотрудничества и развития).

Датасеты для глубокого обучения

В сфере Deep Learning данные, в большинстве случаев, выражены не табличной датой, а изображениями, видео или текстом. В настоящее время, во многих научных работах используют запатентованные наборы данных, которые не принято публиковать для широкой публики. Это становится проблемой, если вы хотите учиться и практиковать свои навыки. Ниже мы привели самые интересные датасеты, которые можно использовать в сфере глубокого обучения. Они доступны каждому, а для их использования не требуется никаких дополнительных условий.

Компьютерное зрение

1. CIFAR-10

Набор данных CIFAR-10 состоит из 60 000 цветных изображений 32×32 в 10 классах, по 6000 изображений в каждом классе. Он содержит 50 000 обучающих и 10 000 тестовых изображений. Изображения разделены на пять обучающих и одну тестовую партию по 10 000 изображений. Тестовая партия включает в себя 1000 случайно выбранных изображений из каждого класса. Обучающие партии содержат остальные изображения в случайном порядке. Однако, некоторые из обучающих партий могут содержать больше изображений из одного класса, чем из другого. Между собой обучающие партии включают 5000 изображений из каждого класса.

2. CityScapes

Это новый масштабный набор данных, который содержит разнообразные стерео видеопоследовательности, записанные в уличных сценах из 50 городов. В них содержатся высококачественные аннотации на уровне пикселей (pixel-level) для 5000 кадров, в дополнение к набору из 20 000 слабо аннотированных кадров. Таким образом, CityScapes предлагает значительно большой набор данных, чем аналогичные ресурсы.

CityScapes предназначен:

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

3. Objectron

Набор данных Objectron представляет собой коллекцию коротких, ориентированных на объект видеоклипов, которые сопровождаются метаданными AR-сессии. Они включают в себя расположения камеры, разреженные облака точек и характеристику плоских поверхностей в окружающей среде. В каждом видеоролике камера перемещается вокруг объекта, снимая его под разными углами.

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

Для обеспечения географического разнообразия набор данных собран в 10 странах на 5 континентах. Вместе с «датой» ресурс предлагает решение для обнаружения 3D-объектов четырех категорий: обуви, стульев, кружек и камер. Модели, приведенные в качестве примера, обучены с использованием данных Objectron и выпущены в MediaPipe.

Анализ тональности текста


1. Sentiment analysis

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

Так, данные, взятые с IMDb – это бинарный набор для анализа настроений. Он состоит из 50 000 отзывов из базы данных фильмов (IMDb), помеченных как положительные или отрицательные. Данные содержат только поляризованные отзывы. Отрицательный отзыв имеет оценку ≤ 4 из 10, положительный – ≥ 7 из 10. На каждый фильм включается не более 30 рецензий. Модели оцениваются по точности.

2. SMS спам

Коллекция SMS-спама v.1 – общедоступный набор SMS-сообщений с метками, которые были собраны для исследования спама с мобильных телефонов. Данные состоят из 5574 англоязычных, реальных и неконсолидированных сообщений, помеченных как легитимные (ham) или спам.

Сообщения SMS-спама были вручную извлечены с веб-сайта Grumbletext. Это британский форум, на котором пользователи мобильных телефонов публично заявляют о спамовых SMS-сообщениях. Идентификация текста спам-сообщений в претензиях – сложная и трудоемкая задача. Она включает тщательное сканирование сотен веб-страниц.

3. WikiQA

WikiQA представляет собой набор пар вопросов и предложений. Они были собраны и аннотированы для исследования ответов на вопросы в открытых доменах.

Большинство предыдущих работ по выбору предложений для ответа сосредоточено на наборе данных, созданном на основе данных TREC-QA, который включает вопросы, созданные редакторами, и предложения-кандидаты для ответа, отобранные по совпадению содержательных слов в вопросе.

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

Обработка естественного языка (NLP)


1. Text classification

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

Так, TREC – это набор данных для классификации вопросов, который состоит из открытых вопросов, основанных на фактах. Они разделены на широкие семантические категории. Датасет имеет шестиклассную (TREC-6) и пятидесятиклассную (TREC-50) версии. Обе версии включают 5452 обучающих и 500 тестовых примеров.

2. Amazon Reviews dataset

Этот набор данных состоит из нескольких миллионов отзывов покупателей Amazon и их оценок. Датасет используется для возможности обучения fastText, анализируя настроения покупателей. Идея состоит в том, что несмотря на огромный объем данных – это реальная бизнес-задача. Модель обучается за считанные минуты. Именно это отличает Amazon Reviews от аналогов.

3. Yelp dataset

Набор данных Yelp – это множество предприятий, отзывов и пользовательских данных, которые можно применить в Pet-проекте и научной работе. Также можно использовать Yelp для обучения студентов во время работы с базами данных, при изучении NLP и в качестве образца производственных данных. Датасет доступен в виде файлов JSON и является «классикой» в обработке естественного языка.

Автопилоты

1. ONCE Dataset

Набор данных ONCE – крупномасштабный набор данных автономного вождения с аннотациями 2D и 3D объектов.

Включает в себя:

  1. 1 млн кадров LiDAR, 7 млн изображений с камер.
  1. 200 км² регионов вождения, 144 часа вождения.
  1. 15 000 полностью аннотированных сцен с 5 классами: автомобиль, автобус, грузовик, пешеход, велосипедист.
  1. Разнообразные условия: день/ночь, солнце/дождь, город/пригород.

2. Ford AV dataset

Ford AV dataset создан в рамках программы AWS Public Dataset Program. Представленные данные организованы на основе временных рядов. Все разделы содержат подразделы для каждого транспортного средства и карт. Каждый подраздел Vehicle включает журналы в формате rosbag, изображения PNG и файлы калибровки всех датчиков. Калибровочные данные для каждого автомобиля предоставляются отдельно.

3. Canadian Adverse Driving Conditions Dataset

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

Включает в себя:

  1. 56 000 изображений с камер
  2. 7 000 разверток LiDAR
  3. 75 сцен по 50-100 кадров в каждой
  4. 10 классов аннотаций
  5. 28 194 Автомобиля
  6. 62 851 Пешеход
  7. 20 441 Грузовик
  8. 4867 Автобусов
  9. 4808 Мусорных контейнеров
  10. 3205 Объектов, направляющих движение
  11. 705 Велосипедов
  12. 638 Пешеходов с объектом
  13. 75 Лошадей и колясок
  14. 26 Животных
  15. Полный набор датчиков: 1 LiDAR, 8 камер, постобработанный GPS/IMU
  16. Неблагоприятные погодные условия вождения, включая снег

Медицинские данные


1. Health Science Library

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

HSL предлагает клинические данные шести основных типов:

  1. Электронные медицинские карты
  2. Административные данные
  3. Данные о претензиях
  4. Регистры пациентов/заболеваний
  5. Медицинские опросы
  6. Данные клинических исследований

2. DeepLesion

Датасет хронических заболеваний в США (US chronic diseases).

DL предлагает набор данных, которые были получены в результате более 10 тыс. исследований на 4 тыс. уникальных пациентов.

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

Список лучших агрегаторов баз данных ML

Лучшие открытые наборы данных (для машинного обучения и анализа)

Приведенные выше датасеты были найдены в результате мониторинга большого количества ресурсов. Поиск этих данных – процесс трудоемкий и требует времени. Он подходит, когда данные нужны вам разово. Однако, в случае, когда данные нужны постоянно, рекомендуется использовать агрегаторы. Наиболее известными из них являются open-data от GitHub, Dataset Search от Google и Microsoft Azure.

Открытые базы данных на Github

1. Congress legislators: база данных людей, избранных в конгресс США

Члены Конгресса США (1789 – настоящее время), комитеты Конгресса (1973 – настоящее время), состав комитетов (только текущий), а также президенты и вице-президенты США в формате YAML, JSON и CSV.

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

2. Covid data

Полный набор данных COVID-19 – коллекция данных о коронавирусе, которую ведет компания Our World in Data. Ресурс обновляется ежедневно в течение всего периода пандемии COVID-19.

Dataset Search от Google

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

По данным Google, их Dataset Search проиндексировал около 25 миллионов наборов данных. Компания считает, что поиск данных поможет создать экосистему обмена, в которой правительства и частные компании смогут обмениваться данными, используя передовые методы хранения и публикации. Большинство открытых датасетов используют schema.org, который является стандартом. Это означает, что любой желающий может свободно загружать и использовать эти наборы данных для исследований, бизнес-аналитики, или обучения ML-модели.

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

Открытые наборы данных Microsoft Azure

1. Russian Open Speech To Text

Коллекция образцов речи, полученных из различных аудиоисточников. Набор данных содержит короткие аудиоклипы на русском языке. Все файлы были преобразованы в opus, за исключением тех, которые служат для проверки. Основная цель набора данных – обучение моделей преобразования речи в текст.

Russian speech to text (STT) включает:

  1. ~16 миллионов высказываний
  2. ~20 000 часов
  3. 2,3 ТБ (без сжатия в формате .wav в int16), 356 ГБ

2. TartanAir

Одновременная локализация и картирование (SLAM) – одна из самых фундаментальных возможностей, необходимых для роботов. Благодаря повсеместной доступности изображений, визуальная SLAM (V-SLAM) стала важным компонентом многих автономных систем.

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

Больше о Microsoft azure datasets вы узнаете по этой ссылке.

***

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

Материалы по теме

21
Мар
2022

Интенсив «Как выбрать язык программирования и найти работу»

Познакомитесь с различными языками и направлениями в программировании, узнаете о профессии и карьере разработчика. Поймёте, что вам интереснее, и подберёте подходящую специализацию.
— Читать дальше «Интенсив «Как выбрать язык программирования и найти р…

03
Мар
2022

🎞️ Улучшение видео нейросетью: ТОП 3 программ (бесплатные и платные)

Применение нейросетей и искусственного интеллекта стали неотъемлемой частью нашей жизни. Обработка видео – одна из сфер, где возможности ИИ используют очень активно. В этом материале мы расскажем о приемах и программах, которые помогают пов…

28
Фев
2022

🐼 25 возможностей Pandas, о которых вы не знали

Новые и малоизвестные возможности Pandas, о которых полезно знать каждому специалисту по Data Science.

Сколько раз вы говорили: «было бы здорово, если бы я мог это сделать в Pandas»?

Возможно, вы это и можете! Pandas настолько огромна и глубока, что она позволяет выполнять практически любые операции с таблицами, которые вы можете себе представить. Однако подобная глубина имеет свою цену. Многие элегантные возможности, решающие редко возникающие задачи и уникальные сценарии, теряются в глубинах документации, затертые более часто используемыми функциями.

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

Автор <a href="https://www.pexels.com/@introspectivedsgn" target="_blank" rel="noopener noreferrer nofollow">Erik Mclean</a>. Источник <a href="https://www.pexels.com/photo/unrecognizable-man-in-panda-head-sitting-near-car-5199661" target="_blank" rel="noopener noreferrer nofollow">Pexels</a>
Автор Erik Mclean. Источник Pexels

1. ExcelWriter

ExcelWriter – это обобщенный класс для создания файлов Excel (с листами!) и записи в них DataFrame. Допустим, у нас есть два набора данных:

        # Загрузим два набора данных
diamonds = sns.load_dataset("diamonds")
tips = sns.load_dataset("tips")

# Пишем оба в один и тот же файл Excel
with pd.ExcelWriter("data/data.xlsx") as writer:
    diamonds.to_excel(writer, sheet_name="diamonds")
    tips.to_excel(writer, sheet_name="tips")
    

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

2. pipe

Автор <a href="https://www.pexels.com/@leah-kelley-50725" target="_blank" rel="noopener noreferrer nofollow">Leah Kelley</a>. Источник <a href="https://www.pexels.com/photo/grayscale-photo-of-man-holding-tobacco-pipe-192473/" target="_blank" rel="noopener noreferrer nofollow">Pexels</a>
Автор Leah Kelley. Источник Pexels

pipe – одна из лучших функций для проведения очистки данных в Pandas кратким и четким образом. Она позволяет объединять несколько пользовательских функций в одну операцию.

Для примера допустим, что у вас есть функции удаления дубликатов (remove_duplicates), удаления выбросов (remove_outliers) и кодирования категориальных признаков (encode_categoricals), каждая со своими аргументами. Вот как вы можете применить все эти функции одной операцией:

        df_preped = (diamonds.pipe(drop_duplicates).
                      pipe(remove_outliers, ['price', 'carat', 'depth']).
                      pipe(encode_categoricals, ['cut', 'color', 'clarity'])
            )
    

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

Больше полезной информации вы найдете на нашем телеграм-канале «Библиотека data scientist’а»

3. factorize

Эта функция – альтернатива LabelEncoder из Sklearn в Pandas:

        # Обратите внимание на [0] в конце
diamonds["cut_enc"] = pd.factorize(diamonds["cut"])[0]

>>> diamonds["cut_enc"].sample(5)

52103    2
39813    0
31843    0
10675    0
6634     0
Name: cut_enc, dtype: int64
    

В отличие от LabelEncoder‘а, factorize возвращает кортеж из двух значений: закодированный столбец и список уникальных категорий.

        codes, unique = pd.factorize(diamonds["cut"], sort=True)

>>> codes[:10]
array([0, 1, 3, 1, 3, 2, 2, 2, 4, 2], dtype=int64)

>>> unique
['Ideal', 'Premium', 'Very Good', 'Good', 'Fair']
    

4. explode – Бабах!

 Автор <a href="https://unsplash.com/@joshuas" target="_blank" rel="noopener noreferrer nofollow">Joshua Sukoff</a>. Источник <a href="https://unsplash.com/s/photos/explode" target="_blank" rel="noopener noreferrer nofollow">Unsplash</a>
Автор Joshua Sukoff. Источник Unsplash

explode – это функция с интересным именем (взрыв). Давайте сначала посмотрим пример, а потом объясним:

        data = pd.Series([1, 6, 7, [46, 56, 49], 45, [15, 10, 12]]).to_frame("dirty")

>>> data
    

В столбце dirty есть две строки, в которых хранятся не единственные значения, а списки. Такие данные часто встречаются в опросах, поскольку некоторые вопросы допускают несколько ответов.

        >>> data.explode("dirty", ignore_index=True)
    

explode распространяет данные из ячейки с массивоподобным значением на несколько строк. Установите ignore_index в True, чтобы сохранить числовой индекс по порядку.

5. squeeze

 Автор <a href="https://www.pexels.com/@cottonbro?utm_content=attributionCopyText&amp;utm_medium=referral&amp;utm_source=pexels" target="_blank" rel="noopener noreferrer nofollow">cottonbro</a>. Источник <a href="https://www.pexels.com/photo/close-up-photo-of-sausage-5875701/" target="_blank" rel="noopener noreferrer nofollow">Pexels</a>
Автор cottonbro. Источник Pexels

Еще одна функция с забавным именем – это squeeze (сжатие), используемая в очень редких, но надоедливых случаях. Один из таких случаев – когда запрос подмножества из DataFrame возвращает единственное значение. Рассмотрим следующий пример:

        subset = diamonds.loc[diamonds.index < 1, ["price"]]

>>> subset
    

Хотя результат – всего одна ячейка, он возвращается как DataFrame. Это неудобно, поскольку вам придется еще раз использовать .loc и передать как имя столбца, так и индекс, чтобы получить цену.

Но если вы знаете про squeeze, нам не придется этого делать. Эта функция позволяет убрать оси из DataFrame или Series, содержащих единственную ячейку. Например:

        >>> subset.squeeze()
326
    

Теперь мы получили скалярное значение. Можно также указать, какую ось нужно убрать:

        >>> subset.squeeze("columns")  # или "rows"

0    326
Name: price, dtype: int64
    

Обратите внимание, что squeeze работает только с DataFrame и Series, имеющими единственное значение.

6. between

 Автор <a href="https://unsplash.com/@jujudreaminx" target="_blank" rel="noopener noreferrer nofollow">Justin Dream</a>. Источник <a href="https://unsplash.com/photos/BxNyZdCbdz0" target="_blank" rel="noopener noreferrer nofollow">Pexels</a>
Автор Justin Dream. Источник Pexels

Это довольно изящная функция для бинарного индексирования числовых признаков по принадлежности диапазону:

        # Выбрать бриллианты с ценой от 3500 до 3700 долларов
diamonds[diamonds["price"]\
      .between(3500, 3700, inclusive="neither")].sample(5)
    

7. T

У всех DataFrame есть простой атрибут T, что значит «транспонирование». Возможно, вы не будете часто его использовать, но я считаю его довольно полезным при выводе DataFrame’ов после метода describe():

        >>> boston.describe().T.head(10)
    

Набор данных о недвижимости Бостона содержит 30 числовых столбцов. Если просто вызвать describe, результирующий DataFrame будет сжат по горизонтали, и сравнить статистику будет трудно. Транспонирование поменяет оси местами, и по горизонтали будут уже статистические параметры.

8. Стилизатор Pandas

А вы знали, что Pandas позволяет вам задавать стили DataFrame’ов?

У них есть атрибут style, открывающий дверь к настройкам и стилям, ограниченным только вашими знаниями HTML и CSS. Я не буду рассказывать обо всем, что вы можете сделать с помощью style, покажу лишь свои любимые функции:

        >>> diabetes.describe().T.drop("count", axis=1)\
                 .style.highlight_max(color="darkred")
    

Мы выделили ячейки, содержащие максимальные значения каждого столбца. Еще один полезный стилизатор – background_gradient, присваивающий ячейкам градиентный фоновый цвет на основе их значений:

        diabetes.describe().T.drop("count", axis=1).style.background_gradient(
    subset=["mean", "50%"], cmap="Reds"
)
    

Это особенно полезно, если вы вызываете describe для таблицы со множеством столбцов и хотите сравнить суммарную статистику. Подробности см. в документации по стилизатору.

9. Опции Pandas

Как и в Matplotlib, в Pandas есть глобальные настройки, которые можно изменять для настройки поведения по умолчанию:

        >>> dir(pd.options)
['compute', 'display', 'io', 'mode', 'plotting']
    

Эти настройки разделены на 5 модулей. Давайте посмотрим, какие настройки есть в модуле display:

        >>> dir(pd.options.display)

['chop_threshold',
 'max_columns',
 'max_colwidth',
 'max_info_columns',
 'max_info_rows',
 'max_rows',
 ...
 'precision',
 'show_dimensions',
 'unicode',
 'width']
    

В группе display много настроек, но я в основном использую max_columns и precision:

        # Убрать лимит на изображаемое количество столбцов
pd.options.display.max_columns = None

# Показывать только 5 цйфр после запятой
pd.options.display.precision = 5  # избавиться от научной нотации
    

Больше подробностей об этих замечательных настройках можно найти в документации.

10. convert_dtypes

Все мы знаем, что Pandas имеет раздражающую привычку присваивать столбцам тип данных object. Вместо того чтобы назначать типы данных вручную, вы можете использовать метод convert_dtypes, который пытается подобрать наиболее подходящий тип данных:

        sample = pd.read_csv(
    "data/station_day.csv",
    usecols=["StationId", "CO", "O3", "AQI_Bucket"],
)

>>> sample.dtypes

StationId      object
CO            float64
O3            float64
AQI_Bucket     object
dtype: object

>>> sample.convert_dtypes().dtypes

StationId      string
CO            float64
O3            float64
AQI_Bucket     string
dtype: object
    

К сожалению, метод не может разбирать даты из-за нюансов различных форматов их представления.

11. select_dtypes

Функция, которую я использую постоянно – это select_dtypes. Ее функционал очевиден из ее названия: выбор по типам данных. У нее есть параметры include и exclude, позволяющие выбрать столбцы, включая или исключая определенные типы данных. Например, можно выбрать только числовые столбцы, указав np.number:

        # Выбрать только числовые столбцы
diamonds.select_dtypes(include=np.number).head()
    

или, наоборот, исключить их:

        # Исключить числовые столбцы
diamonds.select_dtypes(exclude=np.number).head()
    

12. mask

 Автор <a href="https://www.pexels.com/@pixabay" target="_blank" rel="noopener noreferrer nofollow">Pixabay</a>. Источник <a href="https://www.pexels.com/photo/guy-fawkes-mask-and-red-flower-on-hand-38275/" target="_blank" rel="noopener noreferrer nofollow">Pexels.</a>
Автор Pixabay. Источник Pexels.

Функция mask позволяет быстро заменять значения в тех ячейках, для которых выполняется определенное условие. Предположим, что у нас есть данные опроса людей от 50 до 60:

        # Create sample data
ages = pd.Series([55, 52, 50, 66, 57, 59, 49, 60]).to_frame("ages")

ages
    

Мы будем считать возраст, выходящий за границы от 50 до 60 лет, ошибками ввода (таких ошибок две – 49 и 66 лет), которые заменим на NaN:

        ages.mask(cond=~ages["ages"].between(50, 60), other=np.nan)
    

Таким образом, mask заменяет значения ячеек, не удовлетворяющие cond, значениями other.

13. min и max по строкам и столбцам

Хотя функции min и max широко известны, они имеют еще одно применение, полезное для особых случаев. Давайте рассмотрим следующий набор данных:

        index = ["Diamonds", "Titanic", "Iris", "Heart Disease", "Loan Default"]
libraries = ["XGBoost", "CatBoost", "LightGBM", "Sklearn GB"]

df = pd.DataFrame(
    {lib: np.random.uniform(90, 100, 5) for lib in libraries}, index=index
)

>>> df
    

Этот DataFrame содержит воображаемую точность моделей, полученных в результате четырех видов градиентного бустинга на пяти различных наборах данных. Мы хотим найти лучшую точность на каждом наборе. Вот как элегантно это можно сделать с помощью max:

        >>> df.max(axis=1)

Diamonds         99.52684
Titanic          99.63650
Iris             99.10989
Heart Disease    99.31627
Loan Default     97.96728
dtype: float64
    

Если вы хотите найти максимум или минимум по каждому столбцу, достаточно изменить 1 на 0.

14. nlargest и nsmallest

Иногда вам недостаточно получить максимальное или минимальное значение столбца. Вы хотите получить N максимальных (или минимальных) значений. Для этого пригодятся функции nlargest и nsmallest. Давайте выведем 5 самых дорогих и самых дешевых бриллиантов:

        diamonds.nlargest(5, "price")
    

        diamonds.nsmallest(5, "price")
    

15. idxmax и idxmin

Когда вы вызываете max или min для столбца, pandas возвращает максимальное или минимальное значение. Однако иногда вам нужна позиция этого значения, а не оно само. Для этого используйте функции idxmax и idxmin:

        >>> diamonds.price.idxmax()
27749

>>> diamonds.carat.idxmin()
14
    

Можно также задать axis=’columns’, при этом функции будут возвращать номер индекса нужного столбца.

16. value_counts с параметром dropna=False

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

        ames_housing = pd.read_csv("data/train.csv")

>>> ames_housing["FireplaceQu"].value_counts(dropna=False, normalize=True)

NaN    0.47260
Gd     0.26027
TA     0.21438
Fa     0.02260
Ex     0.01644
Po     0.01370
Name: FireplaceQu, dtype: float64
    

В этом столбце 47% пустых значений (NaN).

17. clip

 Автор <a href="https://www.pexels.com/@ann-h-45017" target="_blank" rel="noopener noreferrer nofollow">Ann H</a>. Источник <a href="https://www.pexels.com/photo/a-lot-of-paper-clips-2448452/" target="_blank" rel="noopener noreferrer nofollow">Pexels</a>
Автор Ann H. Источник Pexels

Обнаружение и удаление выбросов часто используется в анализе данных. Функция clip позволяет очень легко найти выбросы, выходящие за пределы диапазона и заменить их предельными значениями. Давайте вернемся к примеру с возрастами людей от 50 до 60:


На этот раз мы заменим значения, выходящие за диапазон от 50 до 60, крайними значениями диапазона.

        ages.clip(50, 60)
    

Быстро и эффективно!

18. at_time и between_time

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

        index = pd.date_range("2021-08-01", periods=100, freq="H")
data = pd.DataFrame({"col": list(range(100))}, index=index)

>>> data.head()
    

Функция at_time позволяет выбирать значения по заданным дате или времени. Давайте выделим все строки, соответствующие 15.00:

        data.at_time("15:00")
    

Здорово, правда? А теперь давайте используем between_time, чтобы выделить строки в заданном интервале времени:

        from datetime import datetime

>>> data.between_time("09:45", "12:00")
    

Заметьте, что обе функции требуют DateTimeIndex, и они работают только с временем. Если вы хотите выделить строки в определенном интервале DateTime, используйте between.

19. bdate_range

bdate_range – это функция для быстрого создания индексов TimeSeries с частотой в один бизнес-день.

        series = pd.bdate_range("2021-01-01", "2021-01-31")  # A period of one month

>>> len(series)
21
    

Частота в бизнес-день часто встречается в финансовом мире. Значит, эта функция может оказаться полезной для переиндексирования существующих временных интервалов функцией reindex.

20. autocorr

Один из важнейших компонентов анализа временных последовательностей – это изучение автокорреляции переменной. Автокорреляция – это старый добрый коэффициент корреляции, но взятый по сравнению с лагом той же последовательности. Точнее, автокорреляция при lag=k вычисляется следующим образом:

1. Последовательность сдвигается k периодов времени:

        time_series = tips[["tip"]]
time_series["lag_1"] = time_series["tip"].shift(1)
time_series["lag_2"] = time_series["tip"].shift(2)
time_series["lag_3"] = time_series["tip"].shift(3)
time_series["lag_4"] = time_series["tip"].shift(4)
# time_series['lag_k'] = time_series['tip'].shift(k)

>>> time_series.head()
    

2. Рассчитывается корреляция между исходным значением и каждым лагом.

Вместо того чтобы делать все это вручную, вы можете использовать функцию Pandas autocorr:

        # Автокорреляция tip при lag_8
>>> time_series["tip"].autocorr(lag=8)
0.07475238789967077
    

О важности автокорреляции для анализа временных рядов можно прочитать в этой статье.

21. hasnans

Pandas предлагает метод для простой проверки, содержит ли Series какие-либо пустые значения – атрибут hasnans:

        series = pd.Series([2, 4, 6, "sadf", np.nan])

>>> series.hasnans
True
    

Согласно его документации, атрибут существенно ускоряет производительность. Заметьте, что он работает только для Series.

22. at и iat

Эти два метода доступа к значениям – гораздо более быстрые альтернативы loc и iloc, но с существенным недостатком: они позволяют получить или изменить только одно значение:

        # [index, label]
>>> diamonds.at[234, "cut"]
'Ideal'

# [index, index]
>>> diamonds.iat[1564, 4]
61.2

# Заменить 16541-ю строку столбца price
>>> diamonds.at[16541, "price"] = 10000
    

23. argsort

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

        tips.reset_index(inplace=True, drop=True)

sort_idx = tips["total_bill"].argsort(kind="mergesort")

# Теперь выведем значения `tips` в порядке сортировки по total_bill
tips.iloc[sort_idx].head()
    

24. Метод доступа cat

Общеизвестно, что Pandas позволяет использовать встроенные функции Python на датах и строках с помощью методов доступа вроде dt или str. Кроме того, в Pandas есть специальный тип данных category для категориальных переменных, как показано ниже:

        >>> diamonds.dtypes

carat       float64
cut        category
color      category
clarity    category
depth       float64
table       float64
price         int64
x           float64
y           float64
z           float64
cut_enc       int64
dtype: object
    

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

        >>> diamonds["cut"].cat.categories
['Ideal', 'Premium', 'Very Good', 'Good', 'Fair']
    

Есть и такие функции, как remove_categories, rename_categories и т. д.:

        diamonds["new_cuts"] = diamonds["cut"].cat.rename_categories(list("ABCDE"))

>>> diamonds["new_cuts"].cat.categories
Index(['A', 'B', 'C', 'D', 'E'], dtype='object')
    

Полный список функций, доступных через метод доступа cat, можно посмотреть здесь.

25. GroupBy.nth

Эта функция работает только для объектов GroupBy. После группировки она возвращает n-ю строку каждой группы:

        diamonds.groupby("cut").nth(5)
    

Заключение

Несмотря на то, что новые библиотеки вроде Dask и datatable потихоньку вытесняют Pandas благодаря их сверкающим новым возможностям для обработки огромных массивов данных, Pandas по-прежнему остается самым популярным средством манипуляции данными в экосистеме Data Science на Python.

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

Спасибо за внимание!

***

Материалы по теме

21
Фев
2022

🎲🐍 Моделируем игру в кости на Python с помощью метода Монте-Карло

В этой статье учимся использовать метод Монте-Карло для прогнозирования вероятностей.

Что такое моделирование методом Монте-Карло?

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

Игра в кости

В этой игре будут задействованы 2 шестигранных кубика. Для выигрыша игроку требуется выбросить одинаковое число на обоих кубиках. Шестигранный кубик имеет 6 возможных исходов (1, 2, 3, 4, 5 и 6). С двумя кубиками 36 возможных исходов (1 и 1, 1 и 2, 1 и 3 и т. д., или 6 х 6 = 36 вариантов). В этой игре у заведения больше шансов на победу (30 исходов против 6 у игроков), следовательно, у заведения значительное преимущество.

Предположим, игрок начинает с баланса в 1000$ и готов проиграть все, поэтому ставит 1$ на каждый бросок (оба кубика) и решает сделать 1000 бросков. Предположим, что заведение щедрое и выплата будет в 4 раза больше ставки, когда игрок выигрывает. Например, если игрок выиграет первый бросок, баланс увеличится на 4$, и раунд закончится с 1004$. Если чудом получится выиграть серию из 1000 бросков, то итого получится 5000$. В случае каждого неудачного раунда получится 0$.

Импорт библиотек Python

Смоделируем игру, чтобы выяснить, сделал ли игрок правильный выбор. Начнем код с импорта необходимых библиотек Python: Pyplot из Matplotlib и random. Для визуализации результатов будет использоваться библиотека Pyplot, для моделирования броска шестигранной игральной кости – random.

        # Импорт библиотек
import matplotlib.pyplot as plt
import random

    

Функция броска кубиков

Далее определим функцию, которая будет выдавать случайное целое число от 1 до 6 для обеих костей (имитация броска). Функция будет сравнивать значение обеих костей и возвращать логическое значение – одинаковы броски или нет. Позже это значение будет использоваться для определения действий в коде.

        # Создаем функцию броска кубика
def roll_dice():
    die_1 = random.randint(1, 6)
    die_2 = random.randint(1, 6)

    # Определим является ли значение на костях одинаковым
    if die_1 == die_2:
        same_num = True
    else:
        same_num = False
    return same_num

    

Входные данные и отслеживаемые переменные

Каждое моделирование методом Монте-Карло требует знание входных данных и информации, которую требуется получить. Входные данные определены, когда описывалась игра. Количество бросков за игру 1000 раз, сумма за бросок 1$. В дополнение требуется определить – сколько раз игра будет моделироваться. В качестве счетчика Монте-Карло используем переменную num_simulations. Чем больше это число, тем точнее прогнозируемая вероятность соответствует истинному значению.

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

        # Входные данные
num_simulations = 10000
max_num_rolls = 1000
bet = 1

# Отслеживаемые переменные
win_probability = []
end_balance = []

    

Настройка фигуры

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

        # Создание фигуры, для симуляции баланса
fig = plt.figure()
plt.title("Monte Carlo Dice Game [" + str(num_simulations) + "   
          simulations]")
plt.xlabel("Roll Number")
plt.ylabel("Balance [$]")
plt.xlim([0, max_num_rolls])

    

Моделирование Монте-Карло

В коде ниже 2 цикла: внешний, который перебирает заданное кол-во симуляций (10000) и вложенный, который запускает каждую игру (1000 бросков). Перед запуском каждого цикла while инициализируется:

  • баланс игрока как 1000$(в виде списка для построения графиков);
  • количество бросков и выигрышей.

Цикл while будет моделировать игру на 1000 бросков. Внутри этого цикла бросаются кости, используя логическую переменную, возвращаемую функцией roll_dice() для определения результата. Если кости одинаковые – в список баланса добавляется 4-кратная ставка и выигрыш к счету игрока. Если кости разные – вычитается ставка из списка баланса. В конце каждого броска добавляем счетчик в список num_rolls.

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

        # Цикл for запускает желаемое количество симуляций
for i in range(num_simulations):
    balance = [1000]
    num_rolls = [0]
    num_wins = 0
    # Выполняется до тех пор пока игрок не выкинет 1000 раз
    while num_rolls[-1] < max_num_rolls:
        same = roll_dice()
        # Результат если кости одинаковые
        if same:
            balance.append(balance[-1] + 4 * bet)
            num_wins += 1
        # Результат если кости разные
        else:
            balance.append(balance[-1] - bet)

        num_rolls.append(num_rolls[-1] + 1)
    # Сохраняем отслеживаемую переменную и добавляем строку к рисунку
    win_probability.append(num_wins/num_rolls[-1])
    end_balance.append(balance[-1])
    plt.plot(num_rolls, balance)

    

Получение результатов

Последний шаг – вывод осмысленных данных из отслеживаемых переменных. Отобразим фигуру (показанную ниже), созданную в цикле for. Также рассчитаем и отобразим общую вероятность выигрыша и конечный баланс, усредняя списки win_probability и end_balance.

        # Выведем график после завершения моделирования
plt.show()

# Усредненная вероятность выигрыша и конечного баланса
overall_win_probability = sum(win_probability)/len(win_probability)
overall_end_balance = sum(end_balance)/len(end_balance)
# Вывод средних значений
print("Average win probability after " + str(num_simulations) + "   
       runs: " + str(overall_win_probability))
print("Average ending balance after " + str(num_simulations) + " 
       runs: $" + str(overall_end_balance))

    
Моделирование игры в кости:
Моделирование игры в кости:

        Average win probability after 10000runs: 0.16666139999999954
Average ending balance after 10000runs: $833.307
    

Анализ результатов

Сделаем вывод из результатов. Из рисунка выше возможно определить, что игрок редко получает прибыль после 1000 бросков. Средний конечный баланс 10000 симуляций составляет 833,66$ (из-за рандомизации, возможны различия результатов). Казино остается в выигрыше даже, если выплачивает в четыре раза больше при победе игрока.

Также заметим, что вероятность выигрыша составляет 0.1667 или 1/6. Выше было отмечено, что у игрока 6 выигрышных исходов из 36 возможных. Используя эти 2 числа ожидаемо, что игрок выиграет 6 из 36 бросков, или 1/6 бросков, что соответствует прогнозу Монте-Карло.

***

Материалы по теме

Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека питониста»

17
Фев
2022

📈 Big O нотация: что это такое и почему ее обязательно нужно знать каждому программисту

Чем отличается структура данных от абстрактного типа данных? Что такое Big O нотация и как ее применять? Мы постарались ответить на эти вопросы в данной статье.

О структурах данных

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

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

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

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


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

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

***

Прежде чем приступить к изучению структур данных, необходимо понять, что это такое и как их сравнивать. Начнем с изучения различий внутри структур данных и их назначения – абстрактных типов данных. Далее расскажем про Big O нотацию – метрику для сравнения скорости операций над структурами данных и пройдемся по основным типам данных, которые хранятся внутри структуры данных.

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

Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека программиста»

Структуры данных vs абстрактные типы данных

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

Однако, если отделить задачу от способа ее выполнения, вы увидите, что данные инструменты выполняют роль «средства для копания» и являются абстрактным типом данных. То, как вы на самом деле копаете, можно назвать структурой данных. Абстрактный тип данных – это теоретическая сущность, а структура данных – это ее реализация.

Рассмотрим еще один пример. Предположим, вы хотите навестить своего друга, живущего на другом конце города. В вашем распоряжении велосипед, автомобиль и ноги. Здесь транспортное средство – это абстрактный тип данных. То, как вы передвигаетесь – это структура данных.


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

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

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

Альтернативным методом может быть аккуратное размещение одежды в комоде или шкафу. Этот метод имеет высокое время записи и низкое время чтения, поскольку потребуется больше времени, чтобы разложить одежду, но вы сможете быстрее получить доступ к любому предмету, который вы искали.


Данные примеры не далеки от стратегий сброса данных в AWS S3 по сравнению с базой данных или хранения данных в высокоструктурированной базе данных SQL по сравнению с гибкой базой данных NoSQL.

Big O нотация

Логично, что лопатой легче выкопать яму, чем молотком, но как оценить разницу в производительности? Количество секунд, которые мы потратим на то, чтобы вырыть яму – это хорошая метрика. При этом для работы с ямами разного размера нам нужен расчет времени, который учитывает объем и глубину. Есть и другие факторы, которые придется учесть. Такие, как разный размер лопат и возможности людей, которые копают. Бодибилдер с молотком может выкопать яму быстрее, чем ребенок с лопатой, но это не значит, что молоток – лучший инструмент для копания.


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

Для этого мы можем обратиться к нотации Big O, обозначаемой как O(⋅). Big O – это мера эффективности «в худшем случае», верхняя граница того, сколько времени потребуется для выполнения задачи, или сколько памяти для этого необходимо. Например, поиск элемента в несортированном списке имеет значение O(n). Для получения результата, возможно, вам придется перебрать весь список.

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

        # сложность времени O(n)
def print_num(arr: list):
    for num in arr:
        print(num)
    

Если же мы выводим каждую пару элементов в массиве, то сложность становится O(n²). Массив из 4 элементов требует 16 шагов, массив из 10 элементов – 100 шагов и так далее.

        # сложность времени O(n^2)
def print_pairs(arr: list):
    for num1 in arr:
        for num2 in arr:
            print(num1, num2)
    

Алгоритм O(n²) не является идеальным. Нам нужен алгоритм, который работает в постоянном режиме. Или O(1), где время выполнения не зависит от объема данных. Например, печать случайного значения из массива всегда будет занимать одно и то же время, независимо от размера массива.

        # время O(1)
def print_idx(arr: list, i: int):
    print(arr[i])
    

Можно количественно оценить эффективность этих функций с помощью команды %%timeit в Jupyter Notebook. Ниже видно резкое увеличение времени выполнения O(n²) print_pairs. Также видно силу функции O(1) print_idx, время выполнения которой колеблется около 0.153 мс, независимо от размера массива и от того, какой элемент запрашивается, первый или последний.


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


Однако, для решения каких задач может потребоваться алгоритм из красной зоны? Они необходимы для решения задач, где требуется знать все возможные ответы на вопрос. Одним из таких примеров алгоритма O(2ⁿ) является поиск всех подмножеств массива. Каждый элемент множества может быть либо включен, либо исключен из подмножества. Набор из четырех элементов [A,B,C,D] будет иметь 2⁴ или 16 подмножеств:

  1. [], [A], [B], [C], [D]
  2. [A,B], [A,C], [A,D], [B,C], [B,D], [C,D]
  3. [A,B,C], [A,B,D], [A,C,D], [B,C,D]
  4. [A,B,C,D]

Худшее время выполнения у алгоритма O(n!), который представляет из себя перестановки – классический пример n-факторной сложности. Чтобы найти все возможные варианты расположения [A, B, C, D] мы начинаем с одной из четырех букв в первой позиции, затем одну из оставшихся трех во второй позиции и так далее. Таким образом, будет 4 × 3 × 2 × 1, или 24 перестановки:

  1. [A,B,C,D], [A,B,D,C], [A,C,B,D], [A,C,D,B], [A,D,B,C], [A,D,C,B]
  2. [B,A,C,D], [B,A,D,C], [B,C,A,D], [B,C,D,A], [B,D,C,A], [B,D,A,C]
  3. [C,A,B,D], [C,A,D,B], [C,B,A,D], [C,B,D,A], [C,D,B,A], [C,D,A,B]
  4. [D,A,B,C], [D,A,C,B], [D,B,A,C], [D,B,C,A], [D,C,A,B], [D,C,B,A]

Время выполнения этих задач быстро увеличивается. Массив из 10 элементов имеет 1024 подмножества и 3 628 800 перестановок. Массив из 20 элементов имеет 1 048 576 подмножеств и 2 432 902 008 176 640 000 перестановок.

Если ваша задача состоит в том, чтобы найти все подмножества или перестановки введенного массива, сложно избежать времени выполнения O(2ⁿ) или O(n!). Однако, если вы выполняете эту операцию более одного раза, есть несколько архитектурных трюков, которые можно использовать для уменьшения нагрузки.

Типы данных

Перейдем к фундаментальным типам данных. Если структура данных – это набор данных, возникает вопрос: какие типы данных должны быть в наших структурах? Есть несколько типов данных, универсальных для всех языков программирования:

  • Целые числа (Integers), такие как 1, -5 и 256.

В других языках программирования (кроме Python) вы можете определить тип целого числа. Например, знаковое (+/-) или беззнаковое (только +), а также количество бит, которое может содержать целое число.

  • Числа с плавающей запятой (Float) – это числа с десятичными знаками. Например, 1.2, 0.14.

В Python к ним относятся числа, определенные с помощью научной нотации, такие как 1e5. В более низкоуровневых языках, например, C или Java, есть родственный тип double. Он обозначает дополнительную точность после запятой.

  • Заголовки (Chars) – это буквы a, b, c. Их набор – это строка, которая технически является массивом chars. Строковые представления чисел и символов, таких как 5 или ?, тоже являются символами.
  • Void – ноль, как None в Python. Указывает на отсутствие данных. Это помогает при инициализации массива, который будет заполняться. Например, функция, которая выполняет действие, но ничего не возвращает. Такая, как отправка электронного письма.

***

В следующей части материала мы приступим к изучению массивов и связанных списков.

Материалы по теме

21
Янв
2022

🍺📊 Какая связь между пивом и t-распределением? Рассказывает Кирилл Дубовиков из компании «Синимекс»

Что общего у пива Guinness и t-распределением вероятности? Об этом вы узнаете в нашей статье.

Автор: Кирилл Дубовиков, Директор по анализу данных и машинному обучению компании «Синимекс».

Нормальное распределение

Представим, что нам нужно исследовать рост людей в городе. Мы выходим на улицу и начинаем измерять рост случайных прохожих. (Некоторые из них могут вызвать полицию, но это же все ради науки!)

Нам нужно провести немного разведочного анализа данных как порядочным data scientist’ам. Но под рукой нет статистических инструментов, таких как язык R, поэтому мы просто берем и строим гистограмму из людей.

Когда под рукой нет статистических пакетов
Когда под рукой нет статистических пакетов

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

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

Функция плотности распределения Гаусса представлена ниже:

f(x∣μ,σ)=1σ2πe−(x−μ)2/2σ2

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

  • µ (математическое ожидание)
  • σ (стандартное отклонение).

Математическое ожидание µ определяет математическое ожидание случайной величины с нормальным распределением. Дисперсия σ² определяет меру разброса возможных значений.

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

  • Подразумевается, что ошибки в линейных моделях имеют нормальное распределение.
  • Гауссовские процессы предполагают, что значения функции в рамках модели имеют нормальное распределение.
  • Смешанные гауссовские модели позволяют моделировать сложные распределения, используя несколько «простых» нормальных распределений.
  • Нормальное распределение является одним из основных компонентов в вариационных автокодировщиках.

По ссылке представлено интерактивное демо Гауссова распределения:

Рис. 1. Иллюстрация распределение Гаусса
Рис. 1. Иллюстрация распределение Гаусса

t-распределение Стьюдента

Что если бы мы захотели моделировать данные по Гауссову распределению, но истинная величина дисперсии σ² была бы нам не известна? Такая проблема возникает, когда выборка маленькая и стандартное отклонение (σ) невозможно оценить с достаточной точностью.

Уильям Госсет столкнулся с этой проблемой, оценивая качество пива Guinness. Он эмпирическим образом вывел формулу для случайной переменной, имеющей t-распределение.

Для начала, допустим у нас есть ряд значений x, …, xn, которые были получены путем выборки из нормального распределения N(µ, σ²).

Мы не знаем величину истинной дисперсии, но мы можем примерно оценить ее, рассчитав выборочное среднее и дисперсию:

x¯=1n∑i=1nxi
s2=1n−1∑i=1n(xi−x¯)2

Тогда случайная переменная

t=x¯−μS/n

будет иметь t-распределение с числом степеней свободы n-1 , где n это число элементов в выборке.

Эта формула похожа на трансформацию нормального распределения в стандартное нормальное (это условное название нормального распределения, где математическое ожидание равно 0, а дисперсия – 1):

x¯−μσ/n

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

Это распределение лежит в основе научного метода, который называется t-тест. Он был впервые использован в компании Guinness для оценки качества их пива.

Уильям Госсет опубликовал результаты своего исследования под псевдонимом Стьюдент, так как в Guinness волновались о том, что конкуренты узнают о методике использования t-теста для контроля качества своей продукции.

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

Пощупать t-распределение в интерактивном режиме можно по ссылке.

Рис. 2. Иллюстрация t-распределения
Рис. 2. Иллюстрация t-распределения

Как вы можете видеть, t-распределение приближается к нормальному на больших числах степеней свободы. Это происходит, потому что выборочное среднее приближается к истинному среднему по мере того, как размер выборки приближается к бесконечности. А «толстые» хвосты t-распределения компенсируют состояние неопределенности при работе с выборкой малого размера.

Логично задать вопрос: «Чему равна плотность распределения вероятностей t-распределения? Как мы можем вывести её?» Это непростая задача с точки зрения математики, но в основе её лежит достаточно понятная мысль.

Предположим, что мы хотим узнать плотность распределения вероятностей нормальной переменной X ~ N(0, σ). Но без прямой зависимости от стандартного отклонения σ.

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

Таким образом, X представляет собой смесь двух непрерывных вероятностных распределений – нормального и Гамма. После этого мы исключаем путем интегрирования σ и получаем формулу плотности распределения вероятности для t-распределения.

Если объяснение выше оказалось недостаточно подробным и показалось похожим на инструкцию по «рисованию совы», то больше доказательств вы найдете <a href="https://probabilityandstats.wordpress.com/tag/students-t-distribution/" target="_blank" rel="noopener noreferrer nofollow">здесь</a> и <a href="https://www.statlect.com/probability-distributions/student-t-distribution" target="_blank" rel="noopener noreferrer nofollow">здесь</a> ☺.
Если объяснение выше оказалось недостаточно подробным и показалось похожим на инструкцию по «рисованию совы», то больше доказательств вы найдете здесь и здесь ☺.

Заключение

Распределения Гаусса и Стьюдента относятся к важнейшим непрерывным вероятностным распределениям в статистике и машинном обучении.

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

Спасибо, что прочитали эту статью. Надеюсь, вы открыли для себя что-то новое или освежили свои знания.

18
Янв
2022

👨‍🎨 NFT и криптопанки: пишем нейросеть для их генерации

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

Что такое NFT и криптопанки?

NFT (Non Fungible Token) – это невзаимозаменяемый, уникальный токен, которым торгуют и обмениваются на блокчейне Ethereum.

Криптопанки (CryptoPunks) – набор из 10 000 уникальных коллекционных персонажей, каждый из которых представляет собой уникальный аватар размером 24×24 пикселя в 8-битном стиле. Доказательство прав собственности хранится на блокчейне Ethereum.

Их создание началось как эксперимент, проведенный разработчиками программного обеспечения Мэттом Холлом и Джоном Аткинсоном в 2017 году. Криптопанки послужили вдохновением для стандарта ERC-721, на котором сегодня основано большинство цифровых произведений искусства и коллекционных предметов.

Это самая первая серия NFT, которая вызвала интерес у публики, и одна из наиболее активно торгуемых и «хайповых» сегодня.

Рыночная капитализация всех 10 000 CryptoPunks на текущий момент оценивается в более чем несколько миллиардов долларов США.

В этом материале мы будем использовать DCGAN (Deep Convolutional Generative Adversarial Network – Глубокая Сверточная Генеративно-Состязательная Сеть) и обучать ее на наборе данных CryptoPunks для того, чтобы попытаться сгенерировать новых «панков» на основе существующих. Вы можете скачать данные для проекта отсюда. Если вы ещё не знакомы с платформой Kaggle и тем, как ей пользоваться, рекомендуем вам ознакомиться со следующим материалом: 📊 Kaggle за 30 минут: практическое руководство для начинающих.

Почему нейросеть с таким сложным названием?

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

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

Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека data scientist’а»

Загрузка данных и препроцессинг

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

        # загрузим данные
base_dir = '/kaggle/input/cryptopunks/'
os.listdir(base_dir)
data_dir = '../input/cryptopunks/txn_history-2021-10-07.jsonl'
image_dir = "../input/cryptopunks/imgs/imgs"
image_root = "../input/cryptopunks/imgs"
df = pd.read_json(base_dir + 'txn_history-2021-10-07.jsonl', lines=True)
    

Посмотрим на первые 100 изображений, перед этим определив количество строк и столбцов:


Очистим наш датасет и оставим те столбцы, которые нам действительно нужны.

        df = df[["txn_type", "date", "eth", "punk_id", "type", "accessories"]]
    

Здесь:

eth – цена в криптовалюте Ethereum.

punk_idid криптопанка.

type – его вид (пришелец, зомби, мужчина/женщина).

accessories – особенности изображения.

Сравним цену NFT-токенов исходя из их типа («Пришелец», «Зомби» и.т.д.):


Как мы видим, наибольшую цену имеют «панки» категории Alien.

Для того чтобы наша GAN-нейросеть работала корректно, необходимо чтобы наш код имел следующую структуру:

  1. Функция для отображения картинок и загрузки данных
  2. Генератор
  3. Дискриминатор
  4. Параметры модели и функция потерь
  5. Код для старта тренировки и генерации изображений

Функции

def tensor_imshow() – функция для отображения изображений. Сюда мы прописываем общую переменную для картинок и их размерность.

def get_dataloader() – функция для загрузки данных, куда мы прописываем трансформер (созданный для изменения картинок в подходящий формат), переменную самого датасета и его загрузчика.

Структура генератора

Класс генератора создает фейковые данные с помощью обратной связи от дискриминатора (о нём ниже). Таким образом, он обучает дискриминатор обнаружению настоящих данных. Это необходимо для более точной работы модели и является неотъемлемой частью GAN. Обучение генератора требует более тесной интеграции между генератором и дискриминатором, чем обучение самого дискриминатора.

        class Generator(nn.Module):     
    def __init__() – функция инициализации размерности класса Генератор  
    def make_gen_block() – «начинка» генератора, которая преобразует случайный input в экземпляр данных
return nn.Sequential(*layers)  – возвращаем слои нейросети
    

Структура дискриминатора

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

        class Discriminator(nn.Module):     
    def __init__() – функция инициализации размерности класса Генератор  
    def make_disc_block() – «начинка» генератора
return nn.Sequential(*layers) – возвращаем слои нейросети
    

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

Параметры для модели

В первую очередь нам необходимо инициализировать размерность. По мнению Ian Goodfellow, который является одним из отцов-основателей современного Deep Learning, оптимальное значение для коэффициентов mean=0, stdev=0.2.

        def weights_init_normal(m):
    
    if isinstance(m, nn.Conv2d) or isinstance(m, nn.ConvTranspose2d):
        torch.nn.init.normal_(m.weights, 0.0, 0.02)
    
    if isinstance(m, nn.BatchNorm2d):
      torch.nn.init.normal_(m.weights, 0.0, 0.02)
              torch.nn.init.constant_(m.bias, 0)
    

Функция потерь

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

Теперь мы инициализируем критерий и параметры функции потерь, размер batch-а и лейблы:

def real_loss() – реальная потеря.

def fake_loss() – фейковая потеря. Необходима для работы генератора.

В каждой из них мы инициализируем критерий (criterion) и параметры функции потерь, размер batch-а (batch_size) и лейблы (labels).

Тренировка модели

def print_tensor_images() – функция для отображения изображений: даем тензор, получаем картинку.

def train() – функция будущей тренировки с необходимыми гиперпараметрами для генератора и дискриминатора.

И наконец, тренируем саму модель!

Вводим общепринятые числа для всех гиперпараметров.

        z_dim = 100
beta_1 = 0.5 
beta_2 = 0.999 
lr = 0.0002
n_epochs = 100
batch_size = 128
image_size = 64
    

Нам нужно максимально сбалансировать генератор и дискриминатор. Для лучшего результата добавим Adam-оптимизацию отдельно для каждого из элементов.

Инициализируем как generator, так и discriminator, после чего настроим Adam-оптимизацию с помощью g_optimizer и d_optimizer соответственно.

Начинаем тренировку! Вводим те параметры, которые указаны выше.

        n_epochs = 100
train(необходимые параметры)
    

Мы видим, что дискриминатор несколько превосходит генератор, особенно на начальном этапе, потому что его работа проще. Сбалансировать эти две модели в стандартном GAN очень сложно, но то, что мы сделали ранее, имеет значение: иначе разрыв между двумя элементами внутри модели мог быть гораздо больше.

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

        def save_model(generator,file_name):
    generator = generator.to('cuda')
    torch.save(generator.state_dict(),"cryptopunks_generator.pth")

save_model(generator, "kaggle")
    

Генерируем новых криптопанков

        generator.to(device)
generator.eval() 
sample_size=8

for i in range(8):    

    fixed_z = Generator.get_noise(n_samples=sample_size, 
                                  z_dim=z_dim, 
                                  device=device)    
    sample_image = generator(fixed_z)
    print_tensor_images(sample_image)
    

Готово! Написанная нами нейросеть теперь генерирует изображения криптопанков. Мы проделали большую работу.

***

Основная цель этой статьи – предоставить вам базовое понимание пайплайна, с помощью которого мы строим нейросеть, генерирующую различные изображения. В данном случае в качестве примера мы взяли популярную тему NFT-токенов CryptoPunks.

Пайплайн включает в себя:

  1. Обзор данных для понимания того, какая нейросеть лучше всего подойдет (в нашем случае – GAN, генеративно-состязательная)
  2. Понимание интуиции, которая стоит за выбранной нейросетью. У нас это «соревнование» генератора и дискриминатора, которые необходимо написать.
  3. Определение правильных параметров для модели, ее тренировка и сохранение.
  4. Генерация новых изображений на основе натренированной модели.

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

Бескрайнее поле Глубокого Обучения (Deep Learning) – это целое искусство, освоить которое вы сможете, комбинируя самые смелые подходы с разнообразными техниками.

Материалы по теме

25
Дек
2021

🐼 Как правильно сортировать данные в Pandas

Из этой заметки вы узнаете, как сортировать датафреймы Pandas по нескольким столбцам с помощью метода sort_values, а также по индексам методом sort_index.

Датафрейм – это двумерная структура данных, очень похожая на таблицу, которая состоит из рядов и столбцов. В Pandas мы можем сортировать фрейм данных по одному столбцу, либо по нескольким столбцам.

Мы будем работать с набором данных Kaggle, а именно со статистикой трендовых видеороликов YouTube. Скачаем и откроем файлUSvideos.csv:

        df = pd.read_csv('USvideos.csv')
df.columns
    

Датафрейм включает следующие столбцы:


Метод sort_values применяется для сортировки датафрейма и выглядит следующим образом:

        DataFrame.sort_values(by, axis=0, ascending=True, inplace=False, kind='quicksort', na_position='last', ignore_index=False, key=None)
    

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

Сортировка по одному столбцу

Чтобы отсортировать датафрейм по одному столбцу, укажем имя столбца с помощью параметра by. В качестве примера возьмем столбец likes, который выводит количество набранных лайков.

        df.sort_values(by='likes', ascending=False)
    

Здесь:

by – определяет список столбцов для сортировки.

ascending – задает порядок сортировки.

sort_values по умолчанию сортирует по возрастанию.

Чтобы задать порядок «по убыванию», следует указать ascending=False.


Сортировка по нескольким столбцам

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

Давайте отсортируем датафрейм по показателям likes и dislikes, то есть найдем видео с наибольшим числом лайков и дизлайков.

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

        df.sort_values(['likes','dislikes'], ascending=[False, False])
    

Подробнее про параметры

Параметр kind позволяет выбрать алгоритм сортировки: quicksort (Быстрая сортировка), mergesort (Сортировка слиянием) или heapsort (Пирамидальная сортировка). По умолчанию используется алгоритм quicksort.

        df.sort_values('likes', ascending=False, kind="mergesort")
    

Inplace-сортировка (на месте) по умолчанию отключена. Для включения inplace-сортировки используем параметр inplace:

        df.sort_values('likes', ascending=False, kind="mergesort", inplace=True)
    

С помощью параметра key мы можем применить любую функцию к данным еще до операции сортировки. Вместо функции также может быть lambda-функция.

        df.sort_values(by='title', key=lambda x: x.str.lower())
    

Метод sort_index сортирует датафрейм по индексам строк. Давайте еще раз взглянем на наш датафрейм:

        sdf = df.sort_values(by='likes', ascending=False)
    

Когда мы сортируем датафрейм, используя метод sort_values, Pandas учитывает столбец, ответственный за сортировку. Чтобы отсортировать датафрейм sdf по индексу строк, воспользуемся методом sort_index:

        sdf.sort_index()
    

Чтобы отсортировать датафрейм по меткам столбцов, присвоим параметру axis значение 1:

        sdf.sort_index(axis=1)
    

***

В этой заметке мы:

  • научились сортировать датафрейм по одному и нескольким значениям;
  • узнали, как работают методы sort_values() и sort_index().

Материалы по теме

18
Дек
2021

📚 ТОП-10 вышедших в 2021 году книг по Data Science для новичков и профессионалов

Чтобы стать специалистом в области анализа данных, необходимо следить за последними новинками в отрасли. В этом материале мы собрали для вас самые интересные книги по Data Science, вышедшие в 2021 году.

09
Дек
2021

Факультет Искусственного интеллекта

Получите одну из самых востребованных IT-профессий. Машинное обучение от профессиональных преподавателей.
— Читать дальше «Факультет Искусственного интеллекта»

25
Ноя
2021

🤖 Решаем задачи машинного обучения с помощью алгоритма градиентного бустинга

Градиентный бустинг (Gradient Boosting) – один из самых эффективных инструментов для решения задач машинного обучения, в особенности на соревнованиях Kaggle. Чтобы научиться правильно его применять, разберем подробнее лежащие в основе алгоритма процессы.

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

Предположим, что вы играете в гольф. Чтобы загнать мяч в лунĸу, вам необходимо замахиваться клюшкой, каждый раз исходя из предыдущего удара. То есть перед новым ударом гольфист в первую очередь смотрит на расстояние между мячом и лунĸой после предыдущего удара, так как наша основная задача – при следующем ударе уменьшить это расстояние.

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

Стоит отметить, что для задач классификации и регрессии реализация алгоритма в программировании будет различаться.

Больше полезной информации вы найдете на нашем телеграм-канале «Библиотека data scientist’а».

Параметры алгоритма

  • criterion – критерий выбора расщепления, Mean Absolute Error (MAE) или Mean Squared Error (MSE). Используется только при построении деревьев.
  • init – какой алгоритм мы будем использовать в качестве главного (именно его и улучшает техника бустинга).
  • learning_rate – скорость обучения.
  • n_estimators – число итераций в бустинге. Чем больше, тем лучше качество, однако слишком большой увеличение данного параметра может привести к ухудшению производительности и переобучению.
  • min_samples_split – минимальное число объектов, при котором происходит расщепление. С данным параметром мы можем избежать переобучение.
  • min_samples_leaf – минимальное число объектов в листе (узле). При увеличении данного параметра качество модели на обучении падает, в то время как время построения модели сокращается. Меньшие значения стоит выбирать для менее сбалансированных выборок.
  • max_depth – максимальная глубина дерева. Используется для того, чтобы исключить возможность переобучения.
  • max_features – количество признаков, учитываемых алгоритмом для построения расщепления в дереве.
  • max_leaf_nodes : Максимальное число верхних точек в дереве. При наличии данного параметра max_depth будет игнорироваться.

Реализация на языке python (библиотека sklearn)

        ### Загружаем библиотеки и данные
import pandas as pd
import numpy as np
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler,LabelEncoder
from sklearn.datasets import load_breast_cancer
from sklearn.metrics import mean_squared_error,r2_score 

import warnings
warnings.filterwarnings('ignore')
breast_cancer = load_breast_cancer()


### Обозначаем целевую переменную для нашей будущей модели
X = pd.DataFrame(breast_cancer['data'], columns=breast_cancer['feature_names'])
y = pd.Categorical.from_codes(breast_cancer['target'], breast_cancer['target_names'])

lbl = LabelEncoder() 
lbl.fit(y)

y_enc = lbl.transform(y)


### Разбираемся с признаками
scl = StandardScaler()
scl.fit(X)
X_scaled = scl.transform(X)

X_train, X_test, y_train, y_test = train_test_split(X, y_enc, test_size=0.20, random_state=42)


### Прописываем параметры для нашей модели 
params = {'n_estimators':200,
          'max_depth':12,
          'criterion':'mse',
          'learning_rate':0.03,
          'min_samples_leaf':16,
          'min_samples_split':16
          }


### Тренируем
gbr = GradientBoostingRegressor(**params)
gbr.fit(X_train,y_train)


### Вычисляем точность
train_accuracy_score=gbr.score(X_train,y_train)
print(train_accuracy_score)

test_accuracy_score=gbr.score(X_test,y_test)
print(test_accuracy_score)

### Предсказание
y_pred = gbr.predict(X_test)

### И среднеквадратичную ошибку
mse = mean_squared_error(y_test,y_pred)
print("MSE: %.2f" % mse)
print(r2_score(y_test,y_pred))
    

Результат работы кода:

        0.9854271477118486
0.8728770740774442
MSE: 0.03
0.8728770740774442
    

Базовая модель градиентного бустинга с несложной простой настройкой дает нам точность более чем в 95% на задаче регрессии.

Какие библиотеки использовать?

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

XGBoost – более регуляризованная форма градиентного бустинга. Основным преимуществом данной библиотеки является производительность и эффективная оптимизация вычислений (лучший результат с меньшей затратой ресурсов).

Вы можете установить XGBoost следующим образом:

        pip install xgboost
    

Библиотека XGBoost предоставляем нам разные классы для разных задач: XGBClassifier для классификации и XGBregressor для регрессии.

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

  • Пример использования XGBoost для классификации:
        # xgboost для классификации
from numpy import asarray
from numpy import mean
from numpy import std
from sklearn.datasets import make_classification
from xgboost import XGBClassifier
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold
# определяем датасет
X, y = make_classification(n_samples=1000, n_features=10, n_informative=5, n_redundant=5, random_state=1)
# и нашу модель вместе с кросс-валидацией
model = XGBClassifier()
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
n_scores = cross_val_score(model, X, y, scoring='accuracy', cv=cv, n_jobs=-1, error_score='raise')
print('Точность: %.3f (%.3f)' % (mean(n_scores), std(n_scores)))
# тренируем модель на всём наборе данных
model = XGBClassifier()
model.fit(X, y)
# предсказываем
row = [2.56999479, -0.13019997, 3.16075093, -4.35936352, -1.61271951, -1.39352057, -2.48924933, -1.93094078, 3.26130366, 2.05692145]
row = asarray(row).reshape((1, len(row)))
yhat = model.predict(row)
print('Предсказание (Предикт): %d' % yhat[0])
    
  • Пример использования XGBoost для регрессии:
        # xgboost для регрессии
from numpy import asarray
from numpy import mean
from numpy import std
from sklearn.datasets import make_regression
from xgboost import XGBRegressor
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedKFold
# определяем датасет
X, y = make_regression(n_samples=1000, n_features=10, n_informative=5, random_state=1)
# и нашу модель (в данном примере мы меняем метрику на MAE)
model = XGBRegressor(objective='reg:squarederror')
cv = RepeatedKFold(n_splits=10, n_repeats=3, random_state=1)
n_scores = cross_val_score(model, X, y, scoring='neg_mean_absolute_error', cv=cv, n_jobs=-1, error_score='raise')
print('MAE (Средняя Абсолютная Ошибка): %.3f (%.3f)' % (mean(n_scores), std(n_scores)))
# тренируем модель на всём наборе данных
model = XGBRegressor(objective='reg:squarederror')
model.fit(X, y)
# предсказываем
row = [2.02220122, 0.31563495, 0.82797464, -0.30620401, 0.16003707, -1.44411381, 0.87616892, -0.50446586, 0.23009474, 0.76201118]
row = asarray(row).reshape((1, len(row)))
yhat = model.predict(row)
print('Предсказание (Предикт): %.3f' % yhat[0])

    
LightGBM – библиотека от Microsoft. В ней идет добавление авто выбора объектов и фокуса на тех частях бустинга, в которых мы имеем больший градиент. Это способствует значительному ускорению в обучении модели и улучшению показателей предсказания. Основная сфера применения – соревнования с использованием табличных данных на Kaggle.

Вы можете установить LightGBM также при помощи pip:

        pip install lightgbm
    
  • LightGBM для классификации:
        # lightgbm для классификации
from numpy import mean
from numpy import std
from sklearn.datasets import make_classification
from lightgbm import LGBMClassifier
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold
# определяем датасет
X, y = make_classification(n_samples=1000, n_features=10, n_informative=5, n_redundant=5, random_state=1)
# и нашу модель
model = LGBMClassifier()
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
n_scores = cross_val_score(model, X, y, scoring='accuracy', cv=cv, n_jobs=-1, error_score='raise')
print('Точность: %.3f (%.3f)' % (mean(n_scores), std(n_scores)))
# тренируем модель на всём наборе данных
model = LGBMClassifier()
model.fit(X, y)
# предсказываем
row = [[2.56999479, -0.13019997, 3.16075093, -4.35936352, -1.61271951, -1.39352057, -2.48924933, -1.93094078, 3.26130366, 2.05692145]]
yhat = model.predict(row)
print('Предсказание (Предикт): %d' % yhat[0])
    
  • LightGBM для регрессии:
        # lightgbm для регрессии
from numpy import mean
from numpy import std
from sklearn.datasets import make_regression
from lightgbm import LGBMRegressor
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedKFold
# определяем датасет
X, y = make_regression(n_samples=1000, n_features=10, n_informative=5, random_state=1)
# и нашу модель (в данном примере мы меняем метрику на MAE)
model = LGBMRegressor()
cv = RepeatedKFold(n_splits=10, n_repeats=3, random_state=1)
n_scores = cross_val_score(model, X, y, scoring='neg_mean_absolute_error', cv=cv, n_jobs=-1, error_score='raise')
print('MAE (Средняя Абсолютная Ошибка): %.3f (%.3f)' % (mean(n_scores), std(n_scores)))
# тренируем модель на всём наборе данных
model = LGBMRegressor()
model.fit(X, y)
# предсказываем
row = [[2.02220122, 0.31563495, 0.82797464, -0.30620401, 0.16003707, -1.44411381, 0.87616892, -0.50446586, 0.23009474, 0.76201118]]
yhat = model.predict(row)
print('Предсказание (Предикт): %.3f' % yhat[0])
    
CatBoost – это библиотека градиентного бустинга, которую создали разработчики Яндекса. Здесь используются “забывчивые” (oblivious) деревья решений, при помощи которых мы растим сбалансированное дерево. Одни и те же функции используются для создания разделений (split) на каждом уровне дерева.

Более того, главным преимуществом CatBoost (помимо улучшения скорости вычислений) является поддержка категориальных входных переменных. Из-за этого библиотека получила свое название CatBoost, от “Category Gradient Boosting” (Категориальный Градиентный Бустинг).

Вы можете установить CatBoost проверенным ранее путем:

        pip install catboost
    
  • CatBoost в задаче классификации:
        # catboost для классификации
from numpy import mean
from numpy import std
from sklearn.datasets import make_classification
from catboost import CatBoostClassifier
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold
from matplotlib import pyplot
# определяем датасет
X, y = make_classification(n_samples=1000, n_features=10, n_informative=5, n_redundant=5, random_state=1)
# evaluate the model
model = CatBoostClassifier(verbose=0, n_estimators=100)
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
n_scores = cross_val_score(model, X, y, scoring='accuracy', cv=cv, n_jobs=-1, error_score='raise')
print('Точность: %.3f (%.3f)' % (mean(n_scores), std(n_scores)))
# тренируем модель на всём наборе данных
model = CatBoostClassifier(verbose=0, n_estimators=100)
model.fit(X, y)
# предсказываем
row = [[2.56999479, -0.13019997, 3.16075093, -4.35936352, -1.61271951, -1.39352057, -2.48924933, -1.93094078, 3.26130366, 2.05692145]]
yhat = model.predict(row)
print('Предсказание (Предикт): %d' % yhat[0])
    
  • CatBoost в задаче регрессии:
        # catboost для регрессии
from numpy import mean
from numpy import std
from sklearn.datasets import make_regression
from catboost import CatBoostRegressor
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedKFold
from matplotlib import pyplot
# определяем датасет
X, y = make_regression(n_samples=1000, n_features=10, n_informative=5, random_state=1)
# и нашу модель (в данном примере мы меняем метрику на MAE)
model = CatBoostRegressor(verbose=0, n_estimators=100)
cv = RepeatedKFold(n_splits=10, n_repeats=3, random_state=1)
n_scores = cross_val_score(model, X, y, scoring='neg_mean_absolute_error', cv=cv, n_jobs=-1, error_score='raise')
print('MAE (Средняя Абсолютная Ошибка): %.3f (%.3f)' % (mean(n_scores), std(n_scores)))
# тренируем модель на всём наборе данных
model = CatBoostRegressor(verbose=0, n_estimators=100)
model.fit(X, y)
# предсказываем
row = [[2.02220122, 0.31563495, 0.82797464, -0.30620401, 0.16003707, -1.44411381, 0.87616892, -0.50446586, 0.23009474, 0.76201118]]
yhat = model.predict(row)
print('Предсказание (Предикт): %.3f' % yhat[0])
    

Когда использовать?

Вы можете использовать алгоритм градиентного бустинга при следующих условиях:

  • Наличие большого количества наблюдений (ближайшее сходство) в тренировочной выборке данных.
  • Количество признаков меньше количества наблюдений в обучающих данных. Бустинг хорошо работает, когда данные содержат смесь числовых и категориальных признаков или только числовые признаки.
  • Когда необходимо рассмотреть метрики производительности модели.

Когда НЕ следует использовать XGBoost:

  • В задачах распознавания изображений и компьютерного зрения (CV – Computer Vision).
  • В обработке и понимании естественного языка (NLP – Natural Language Processing).
  • Когда число обучающих выборок значительно меньше чем число признаков (фич).

Плюсы и минусы

Плюсы

  • Алгоритм работает с любыми функциями потерь.
  • Предсказания в среднем лучше, чем у других алгоритмов.
  • Самостоятельно справляется с пропущенными данными.

Минусы

  • Алгоритм крайне чувствителен к выбросам и при их наличии будет тратить огромное количество ресурсов на эти моменты. Однако, стоит отметить, что использование Mean Absolute Error (MAE) вместо Mean Squared Error (MSE) значительно снижает влияние выбросов на вашу модель (выбор функции в параметре criterion).
  • Ваша модель будет склонна к переобучению при слишком большом количестве деревьев. Данная проблема присутствует в любом алгоритме, связанном с деревьями и справляется правильной настройкой параметра n_estimators.
  • Вычисления могут занять много времени. Поэтому, если у вас большой набор данных, всегда составляйте правильный размер выборки и не забывайте правильно настроить параметр min_samples_leaf.
***

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

19
Ноя
2021

«Чёрная пятница» в GeekBrains

На выбор есть факультеты, профессии, программы и курсы по 7 направлениям: программирование, IT-инфраструктура, аналитика, дизайн, маркетинг, менеджмент и школьникам.
— Читать дальше ««Чёрная пятница» в GeekBrains»

12
Ноя
2021

🤖 Наивный байесовский алгоритм классификации: преимущества и недостатки

Наивный байесовский классификатор (Naive Bayes classifier) – это очень популярный в машинном обучении алгоритм, который в основном используется для получения базовой точности набора данных. Изучим его преимущества и недостатки, а также реализацию на языке Python.

Что это такое?

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

Предположим, что вы видите перед собой что-то зеленое. Этот зеленый объект может быть ежиком, собакой или мячом. Естественно, вы предположите, что это будет мяч. Но почему?

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

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

Больше полезной информации вы можете найти на нашем телеграм-канале «Библиотека data scientist’а».

Теоретическая составляющая алгоритма

Теорема Байеса позволяет рассчитать апостериорную вероятность P(A | B) на основе P(A), P(B) и P(B | A).


Где:

  • P(A | B) – апостериорная вероятность (что A из B истинно)
  • P(A) – априорная вероятность (независимая вероятность A)
  • P(B | A) – вероятность данного значения признака при данном классе. (что B из A истинно)
  • P(B) – априорная вероятность при значении нашего признака. (независимая вероятность B)

Реализация на языке python

        ### Загружаем библиотеки и данные
import numpy as np
import pandas as pd
from sklearn.datasets import load_iris
from scipy.stats import norm
 
data = load_iris()
X, y, column_names = data['data'], data['target'], data['feature_names']
X = pd.DataFrame(X, columns = column_names)
 
### Разбиваем данные
from sklearn.model_selection import train_test_split
X_train, X_val, y_train, y_val = train_test_split(X, y, random_state=44)
 
means = X_train.groupby(y_train).apply(np.mean)
stds = X_train.groupby(y_train).apply(np.std)
 
### Вычисляем априорную вероятность класса
probs = X_train.groupby(y_train).apply(lambda x: len(x)) / X_train.shape[0]
 
### Вычисляем вероятность для Теоремы Байеса для каждого элемента
y_pred = []
# каждый элемент в валидационной части данных
for elem in range(X_val.shape[0]):
   p = {}
 
   # для каждого возможного класса
   for cl in np.unique(y_train):
 
       # априорная вероятность взятого ранее класса
       p[cl] = probs.iloc[cl]
 
       # для каждого столбца в датасете
       for index, param in enumerate(X_val.iloc[elem]):
 
           # умножаем вероятность того, что данное значение столбца
           # будет принадлежать распределению для выбранного класса
           p[cl] *= norm.pdf(param, means.iloc[cl, index], stds.iloc[cl, index])
  
   y_pred.append(pd.Series(p).values.argmax())
 
### Посмотрим точность нашего предсказания несколькими методами
# ручной классификатор
from sklearn.metrics import accuracy_score
accuracy1 = accuracy_score(y_val, y_pred)
 
# классификатор из библиотеки sklearn
from sklearn.naive_bayes import GaussianNB
model = GaussianNB()
model.fit(X_train, y_train)
accuracy2 = accuracy_score(y_val, model.predict(X_val))
 
print(accuracy1)
print(accuracy2)
    

Результат работы кода:

        0.9210526315789473
0.9210526315789473
    

Базовая модель с самой простой настройкой дает нам точность более чем в 90% на задаче классификации цветков ириса.

Плюсы и минусы

Плюсы

  • Алгоритм легко и быстро предсказывает класс тестового набора данных. Он также хорошо справляется с многоклассовым прогнозированием.
  • Производительность наивного байесовского классификатора лучше, чем у других простых алгоритмов, таких как логистическая регрессия. Более того, вам требуется меньше обучающих данных.
  • Он хорошо работает с категориальными признаками(по сравнению с числовыми). Для числовых признаков предполагается нормальное распределение, что может быть серьезным допущением в точности нашего алгоритма.

Минусы

  • Если переменная имеет категорию (в тестовом наборе данных), которая не наблюдалась в обучающем наборе данных, то модель присвоит 0 (нулевую) вероятность и не сможет сделать предсказание. Это часто называют нулевой частотой. Чтобы решить эту проблему, мы можем использовать технику сглаживания. Один из самых простых методов сглаживания называется оценкой Лапласа.
  • Значения спрогнозированных вероятностей, возвращенные методом predict_proba, не всегда являются достаточно точными.
  • Ограничением данного алгоритма является предположение о независимости признаков. Однако в реальных задачах полностью независимые признаки встречаются крайне редко.

В каких областях использовать?

Алгоритм наивного Байеса – это классификатор, обучение которого идет очень быстро. Следовательно, данный инструмент идеально подходит для составления прогнозов в реальном времени.

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

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

  • Система рекомендаций. В сочетании алгоритма с методами коллаборативной фильтрации (Collaborative Filtering) мы можем создать рекомендательную систему, которая использует машинное обучение и методы добычи данных для учета невидимой информации (такой, как поиск фильмов пользователем и длительность просмотра). Цель – предсказание того, понравится ли пользователю данный ресурс/продукт или нет.
  • Фильтрация спама и классификация текста. Наивный байесовский классификатор в основном используются для классификации текстов (благодаря лучшему результату в многоклассовых проблемах и правилу независимости) и имеет более высокую точность по сравнению с другими алгоритмами. В результате он широко используется в фильтрации спама (в электронной почте) и анализе настроений (к примеру, социальных сетей, для выявления положительных и отрицательных настроений клиентов).
***

Из этого руководства вы узнали о наивном байесовском алгоритме классификации, его работе, проблемах, реализации, преимуществах и недостатках.

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

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