0,0
рейтинг
23 февраля 2015 в 03:15

Разработка → Необычные модели Playboy, или про обнаружение выбросов в данных c помощью Scikit-learn

Мотивированный статьей пользователя BubaVV про предсказание веса модели Playboy по ее формам и росту, автор решил углубиться if you know what I mean в эту будоражащую кровь тему исследования и в тех же данных найти выбросы, то есть особо сисястые модели, выделяющиеся на фоне других своими формами, ростом или весом. А на фоне этой разминки чувства юмора заодно немного рассказать начинающим исследователям данных про обнаружение выбросов (outlier detection) и аномалий (anomaly detection) в данных с помощью реализации одноклассовой машины опорных векторов (One-class Support Vector Machine) в библиотеке Scikit-learn, написанной на языке Python.

Загрузка и первичный анализ данных


Итак, по-честному сославшись на первоисточник данных и человека, который над ними поработал, откроем CSV-файл с данными girls.csv и посмотрим, что там есть. Видим параметры 604-х девушек месяца Playboy с декабря 1953 по январь 2009: обхват груди (Bust, в см), обхват талии (Waist, в см), обхват бедер (Hips, в см), а также рост (Height, в см.) и вес (Weight, в кг).

Откроем нашу любимую среду программирования для Python (в моем случае Eclipse + PyDev) и загрузим данные с помощью библиотеки Pandas. В этой статье предполагается, что библиотеки Pandas, NumPy, SciPy, sklearn и matplotlib установлены. Если нет, пользователи Windows могут порадоваться и элементарно установить прекомпилированные библиотеки отсюда.
Ну а пользователям никсов и маков (как и автору) придется чуть-чуть помучаться, но статья не об этом.

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

import pandas
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.font_manager
from scipy import stats
from sklearn.preprocessing import scale
from sklearn import svm
from sklearn.decomposition import PCA

Создаем экземпляр girls структуры данных DataFrame модуля Pandas считыванием данных из файла girls.csv (он лежит рядом с данным py-файлом, иначе надо указывать полный путь). Параметр header говорит, что названия признаков находятся в первой строке (т.е. в нулевой, если считать, как программисты).

girls = pandas.read_csv('girls.csv', header=0)

Кстати, Pandas — отличный вариант для тех, кто привык к питону, но все еще любит быстроту парсинга данных в R. Главное, что унаследовал Pandas от R — это как раз удобную структуру данных DataFrame.
Автор знакомился с Pandas по тьюториалу Kaggle в пробном соревновании «Titanic: Machine Learning from Disaster». Для тех, кто не знаком с Kaggle, — отличный повод наконец сделать это.

Посмотрим общую статистику наших девушек:

print  girls.info()

Нам сообщат, что в нашем распоряжении 604 девушки, каждая с 7-ю признаками — Month (тип object), Year (тип int64) и еще 5-ю признаками типа int64, которые мы уже называли.
Дальше узнаем про девушек побольше:

print  girls.describe()

Эх, если бы в жизни все было так просто!
Интерпретатор нам перечислит основные статистические характеристики признаков девушек — среднее, минимальное и максимальное значения. Уже неплохо. Отсюда заключаем, что средние формы модели Playboy 89-60-88 (ожидаемо), средний рост — 168 см, вес — 52 кг.
Вот рост то, кажется, маловат. Видимо, объясняется тем, что данные исторические, с середины ХХ века, сейчас-то стандартом у моделей, кажется, считается рост 180 см.
Охват груди девушек меняется от 81 до 104 см, талия — от 46 до 89, бедра — от 61 до 99, рост — от 150 см до 188 см, вес — от 42 кг до 68 кг.
Ух ты, уже можно подозревать, что в данные вкралась ошибка. Это что за пивная бочка модель с талией 89 см? А как бедра могут быть 61 см?

Давайте посмотрим, что это за уникумы:

print girls[['Month','Year']][girls['Waist'] == 89]

Это девушки месяца Playboy в декабре 1998-го и январе 2005-го соответственно. Несложно их отыскать здесь. Это тройняшки Николь, Эрика и Жаклин с неговорящей фамилей Дам (Dahm) — все три «под одним аккаунтом» и Дэстини Дэвис (Destiny Davis). Легко заметить, что талии тройняшек — 25 дюймов (64 см), а не 89, а бедра нашей Дэстини — 86 см, а никак не 61.

Для красоты можно еще построить и гистограммы распределения параметров девушек (для разнообразия они сделаны в R).




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

Предобработка данных


Для обучения модели оставим только численные параметры, кроме года. Запишем их в массив NumPy girl_params, попутно преобразовав к типу float64. Шкалируем данные так, чтобы все признаки лежали в диапазоне от -1 до 1. Это важно для многих алгоритмов маишинного обучения. Не вдаваясь в детали, шкалированием мы избегаем того, что признак получает больший вес только из-за того, что у него больший диапазон изменения. Например, если по признакам «Возраст» и «Доход» считать Евклидово расстояние между людьми, то у дохода вклад в метрику будет намного выше только из-за того, что он измеряется, например, в тысячах, а возраст в десятках.

girl_params = np.array(girls.values[:,2:], dtype="float64")
girl_params = scale(girl_params) 

Далее выделяем 2 главных компонента в данных, чтоб их можно было отобразить. Тут нам пригодилась библиотека Scikit-learn Principal Component Analysis (PCA). Также нам не помешает сохранить число наших девушек. Кроме того, мы скажем, что ищем 1% выбросов в данных, то есть ограничимся 6-7 «странными» девушками. (Переменные в Питоне, записанные в верхнем регистре, символизируют константы и обычно записываются в начале файла после подключения модулей).

X = PCA(n_components=2).fit_transform(girl_params)
girls_num = X.shape[0]
OUTLIER_FRACTION = 0.01

Обучение модели


Для обнаружения «выбросов» в данных используем одноклассовую модель машины опорных векторов. Теоретическую работу над этой вариацией SVM начал Алексей Яковлевич Червоненкис. Как заявляет «Яндекс», сейчас разработка методов решения этой задачи занимает первое место в развитии теории машинного обучения.
Не буду здесь рассказывать, что такое SVM и ядра, про это и так много написано, например на Хабре (попроще) и на machinelearning.ru (посложнее). Отмечу только, что One-class SVM позовляет, как это следует из названия, отличать объекты одного класса. Обнаружение аномалий в данных — всего лишь скромное приложение этой идеи. Сейчас, в эпоху глубинного обучения, с помощью алгоритмов одноклассовой классификации пытаются научить компьютер «создавать представление» предмета, как, например, ребенок отличает собаку от всех остальных предметов.

Но вернемся к Scikit-реализации One-class SVM, которая неплохо документирована на сайте Scikit-learn.
Создаем экземпляр классификатора с гауссовым ядром и «скармливаем» ему данные.

clf = svm.OneClassSVM(kernel="rbf")
clf.fit(X)


Поиск выбросов


Создаем массив dist_to_border, который хранит расстояния от объектов обучающей выборки X до построенной разделяющей поверхности, а затем, после того, как мы выбрали порог, создаем массив индикаторов (True или False) того, что объект является представителем данного класса, а не выбросом. При этом расстояние положительно, если объект лежит «внутри» области, ограниченной построенной разделяющей поверхностью (т.е. является представителем класса), и отрицательно в противном случае. Порог определяется статистически, как такое расстояние до разделяющей поверхности, что у OUTLIER_FRACTION (в нашем случае у одного) процента выборки оно больше (т.е в нашем случае, threshold — это 1%-перцентиль массива расстояний до разделяющей поверхности).

dist_to_border = clf.decision_function(X).ravel()
threshold = stats.scoreatpercentile(dist_to_border,
            100 * OUTLIER_FRACTION)
is_inlier = dist_to_border > threshold


Отображение и трактовка результатов


Наконец, визуализируем то что получилось. На этом моменте я не буду останавливаться, разобраться с matplotlib желающие могут самостоятельно. Это переработанный код из примера Scikit-learn «Outlier detection with several methods».

xx, yy = np.meshgrid(np.linspace(-7, 7, 500), np.linspace(-7, 7, 500))
n_inliers = int((1. - OUTLIER_FRACTION) * girls_num)
n_outliers = int(OUTLIER_FRACTION * girls_num)
Z = clf.decision_function(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
plt.title("Outlier detection")
plt.contourf(xx, yy, Z, levels=np.linspace(Z.min(), threshold, 7),
                         cmap=plt.cm.Blues_r)
a = plt.contour(xx, yy, Z, levels=[threshold],
                            linewidths=2, colors='red')
plt.contourf(xx, yy, Z, levels=[threshold, Z.max()],
                         colors='orange')
b = plt.scatter(X[is_inlier == 0, 0], X[is_inlier == 0, 1], c='white')
c = plt.scatter(X[is_inlier == 1, 0], X[is_inlier == 1, 1], c='black')
plt.axis('tight')
plt.legend([a.collections[0], b, c],
           ['learned decision function', 'outliers', 'inliers'],
           prop=matplotlib.font_manager.FontProperties(size=11))
plt.xlim((-7, 7))
plt.ylim((-7, 7))
plt.show()


Получаем такую картинку:


Видны 7 «выбросов». Чтобы понять, что за девушки таятся под этим нелицеприятным «выбросы», посмотрим их в исходных данных.

print girls[is_inlier == 0]

         Month  Year  Bust  Waist  Hips  Height  Weight
54   September  1962    91     46    86     152      45
67     October  1963    94     66    94     183      68
79     October  1964   104     64    97     168      66
173  September  1972    98     64    99     185      64
483   December  1998    86     89    86     173      52
507   December  2000    86     66    91     188      61
535      April  2003    86     61    69     173      54

А теперь самая занимательная часть — трактовка полученных выбросов.
Замечаем, что экспонатов в нашей кунсткамере всего 7 (мы так удачно задали порог OUTLIER_FRACTION), поэтому можно пройтись по каждому из них.

  1. Мики Уинтерс. Сентябрь, 1962. 91-46-86, рост 152, вес 45.

    Талия 46 — это, конечно, круто! Как у них при этом грудь 91?
  2. Кристин Уильямс. Октябрь, 1963. 94-66-94, рост 183, вес 68.

    Не маленькая девушка для тех лет. Это тебе не Мики Уинтерс.
  3. Розмари Хилкрест. Октябрь, 1964. 104-64-97, рост 168, вес 66.

    Воу-воу, палехче! Внушительная дама.
  4. Сюзан Миллер. Сентябрь, 1972. 98-64-99, рост 185, вес 64.

  5. Милашки-тройняшки Дам. 86-89 (реально 64)-86, рост 173, вес 52.

    Пример ошибки в данных. Не очень понятно, как им все на троих мерили.
  6. Кара Мишель. Декабрь, 2000. 86-66-91, рост 188, вес 61.

    Ростом 188 — выше автора этой статьи. Явный «выброс» для таких «исторических» данных.
  7. Кармелла де Сезаре. Апрель, 2003. 86-61-69, рост 173, вес 54.

    Пожалуй, из-за бедер.


Примечательно, что дама с охватом бедер в 61 см, которую мы подозревали в сильном отличии от прочих девушек, по остальным параметрам вполне в норме, и SVM-ом не была определена как «выброс».

Заключение


Напоследок отмечу важность первичного анализа данных, «просто глазами» и, конечно, отмечу, что обнаружение аномалий в данных применяется и в более серьезных задачах — в кредитном скоринге, чтоб распознать неблагонадежных клиентов, в системах безопасности для обнаружения потенциальных «узких мест», при анализе банковских транзакций для поиска злоумышленников и не только. Заинтересованный читатель найдет и множество других алгоритмов обнаружения аномалий и выбросов в данных и их применений.
Юрий Кашницкий @yorko
карма
39,2
рейтинг 0,0
Data Scientist
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

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

  • +3
    Заинтересованный читатель найдет и множество других алгоритмов обнаружения аномалий...
    Но я так и не нашёл ответа в статье — как по-научному называется заинтересованный человек, ищущий аномалии в выборке данных? Хотя статья — об этом.
    • 0
      Такого человека можно просто назвать исследователем данных. А самый простой способ и наверно часто применяющийся — предположить, что распределение данных нормальное и поставить порог на отклонение от среднего — обозвать выбросом, например, все что лежит вне шара радиуса 2.5 std от центра. Также можно поставить порог на вероятность появление такого редкого объекта — про это Andrew Ng рассказывает в его известном курсе машинного обучения на Coursera. Примерно то же делает метод covariance estimator, который на Scikit-learn сравнивается с One-class SVM. Разновидности covariance estimator рассматриваются в еще одном примере Scikit-learn. И как всегда Википедия подскажет еще немало методов — адапатации нейронных сетей, ансамблей, kNN, ассоциативных правил и алгоритмов кластеризации под эту задачу.
    • +1
      Предлагаю размять мозг и подумать, что еще интересного можно вытащить из этого сета. До меня «первопроходец» BubaVV строил регрессию вида lm(Weight ~ Bust + Waist + Hips + Height), но так же можно предсказывать и рост от других параметров и не только линейной регрессией.
      Можно добавлять признаки (см. введение в Feature Engineering на Хабре), например индекс массы тела MI = 10^4 * Weight / Height^2. Поможет ли, подскажет кросс-валидация моделей.
      По картинке видно, что кластеры не выделяются, а жаль.
      Можно еще ассоциативные правила строить вида «Bust > 90 and Height > 170 => Weight > 62». У деревьев решений на выходе примерно то же.
      Что-нибудь еще?
      • –2
        Вижу два направления развития.
        1. Добавить много-много параметров моделей и выбрать из них самые информативные. Выбирать можно генетическим алгоритмом, он тут бодро справляется
        2. Добавить немного параметров и пробовать строить зависимость очень нелинейного вида. Например, из зависимость вида W=Bust*Height, W=Bust+Height и W=Bust+(Height*Hips) выбрать самую правильную. Зависимости генерировать в виде «программы» для маленькой стек-машины
        • 0
          1. Добавить — в смысле породить из имеющихся? Тут можно тогда feature importance считать, например, с помощью прироста информации.
          2. Вручную перебирать или автоматически генерить такие комбинации признаков — вряд ли перспективно, хотя точно не знаю. Просто SVM и нейронные сети как раз это и делают — строят очень сложную нелинейную функцию от входных признаков. А нужная комбинация найдется в процессе оптимизации.
          • –1
            1. Нет, именно найти новые. Длина волос например, или длина ног, или еще что-то в таком виде.
            2. В науке местами штука очень востребованная. Нейросеть функцию сделает и реализует, но в ряде случаев нужно именно уравнение обычного вида.
      • 0
        Статья просто от души! Респект!)
    • 0
      А вот оригинальная статья, в которой, отмечается, что пока обычные американцы и американки толстели с 1950-х годов по 2000-ые, модели Playboy, наоборот, худели. Но и, к сожалению, заодно и худели грудью. Или это результат борьбы с некачественными имплантантами…
    • 0
      девиантофил? )
  • +38
    Зашел только для того, чтобы посмотреть фото.
  • +2
    Пользователям любой ОС можно не мучаться с установкой библиотек и воспользоваться anaconda
    • 0
      Точно, спасибо! Слышал, но давно хотел проверить.
  • +14
    Я зашел, чтобы увидеть сиськи. Вы меня разочаровали, автор. :(
    • +11
      Может вам сайт подсказать?
    • +1
      Для желающих отвлечься от проганья и раскрыть для себя тему сисек в статье приведены ссылки на моделей на playboy.com.
  • +4
    Теперь ждем статью-туториал про анализ видео и алгоритмы рекомендаций!
    • 0
      Самое гнусное в оном жанре — когда модель пырится смотрит прямо в камеру. Как учебная задача для распознавания лиц — самое оно, как мне кажется
      • +1
        Но сначала придется разметить тестовую базу. Самая неприятная часть в любой задаче машинного обучения!
        • –1
          Взаимное расположение овала лица и глаз. Я абсолютно не в теме, но думал что оно сразу заведется в хоть каком-то виде. А тестовую выборку придется спарсить с какого-нибудь сайта
          • 0
            Похоже на то, что вы действительно не в теме.

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

            Но пока вы не измерили точность его работы, это — пффф — никому не интересный кода кусок, вроде сносно работающий на трех конкретных фоточках. А чтобы узнать точность, нужно каким-то более достоверным способом (часто — просто вручную), разметить достаточно большую и разнообразную тестовую базу распознаваемых объектов. А затем сравнивая достоверную разметку с ответом алгоритма судить о точности последнего.
            • 0
              Все верно, только тут стоит отметить, что мы живем уже в мире таких объемов данных, пометить которые бывает нереально или очень дорого. Поэтому современные алгоритмы Semi-supervised learning и deep learning адаптируются для работы с частично или слабо помеченными данными.
              • +4
                Все верно, только я вот пытался пошутить про разметку порно, но юмора что-то никто не оценил ;)
                • 0
                  Ниче так предложение было бы для Amazon Mechanical Turk! сразу желающие нашлись бы, и не только домохозяйки
                • 0
                  кстати, судя по ответу одного из разработчиков Яндекса, их асессоры отчасти этим и занимаются.
  • +49
    Спасибо, почитал.
  • +9
    Если нет, пользователи Windows могут порадоваться и элементарно установить прекомпилированные библиотеки отсюда.
    Ну а пользователям никсов и маков (как и автору) придется чуть-чуть помучаться, но статья не об этом.

    Вы всё перепутали.
  • 0
    Всегда считал, что главное — не конкретные размеры, а их соотношения с другими чертами, наличие некоторой симметрии.

    Другими словами, если в музыке, все строится вокруг резонансов, отсюда и идет в конечном счете размер октавы и понятие «тон, полутон», и уже существуют программные «композиторы»,
    То было бы интересно увидеть программный алгоритм определения «симпатичный/не симпатичный», возможно даже генерация лиц.
    • +5
      18+
      Точно надо?
      image

      • 0
        Я вам про алгоритмы и духовность, а вы…

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

        Блин, это же идея для стартапа в каком-нить фейсбуке/vk. И возможность отправить скриншот подруге.
        • 0
          Вы только при расчете идеального лица не забудьте про uncanny valley, а то очень забавный эффект может получиться.
  • 0
    Распределения странные. Например, рост. Видны провалы. на 160++ и на 170++. Почему? Дюймы перевели в см и уже потом сантиметры разбили на интервалы для гистограммы. Пожалуй, плохая идея была.
    • +1
      По сути преобразование из дюймов в сантиметры — линейное, и ничего не должно было кардинально поменяться. Где было 25 дюймов, стало 64 см, если правильно округлять (так то 63.5, и если над данными работают разные люди, то кто-то мог взять и «округлить» до 63).
      Причина «провалов» таится, во-первых, в округлении, а во-вторых, в слишком подробных гистограммах.
      Если посмотреть на изначальные данные в дюймах, то получится вот что:

      table(height$V1)
      

       59   60 60.5   61   62 62.5   63 63.5   64 64.5   65 65.5   66 66.5   67 67.5   68 68.5   69 69.5   70 
         2    4    1    5   29    4   38    1   54   13   85   20   86    5   99   10   78   19   31    5   18 
      70.5   71   72   73   74 
         1    9    1    1    1 
      

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

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

      hist(height$V1, breaks= 20, col="Green", xlab = "Height in inches", main = "Height")
      > hist(height$V1, breaks= 100, col="Red", xlab = "Height in inches", main = "Height")
      

  • +10
    Простите, не удержался.
    • +4
      Как и должно быть — три модели Playboy эквивалентны одной Инфузории Туфелька.
  • 0
    Жду обзор, по девушкам из интимного жанра)
  • 0
    Очень заинтересовали тройняшки, глянул фото, оказались все силиконовые, облом :(

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