{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Семинар 13. Понижение размерности данных (отбор признаков)\n", "\n", "![](http://i.imgur.com/9HiVD0m.png)\n", "\n", "\n", "Основная цель: каким-либо образом \"уменьшить\" число признаков.\n", "\n", "Почему это может быть важно:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Проклятие размерности\n", "\n", "Рассмотрим пример (аналогично пончикам с лекции): пусть есть треугольники, круги и квадратики. Будем считать что признаки принимают значения только из какого-то интервала. Если есть всего один признак, то объекты лежат \"примерно\" рядом. В случае двух признаков они находятся дальше друг от друга. При увеличении размерности объекты будут все дальше друг от друга. То есть чем больше пространство, тем объекты становятся более \"непохожими\" (лежат далеко друг от друга). Получается, нужно много данных, чтобы алгоритм мог с ними работать (что порой затруднительно).\n", "\n", "![](http://nikhilbuduma.com/img/dimension_sparsity.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Шумовые и просто плохие признаки\n", "\n", "Присутствие шума в признаках может ухудшать модель. Будем решать задачу классификации." ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import numpy as np\n", "import pandas as pd\n", "import pylab as plt\n", "\n", "%precision 3\n", "%matplotlib inline" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": true }, "outputs": [], "source": [ "import sklearn.datasets\n", "from sklearn.cross_validation import train_test_split" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Рассмотрим некоторую случайную выборку, где все признаки \"хорошие\". Разделим ее на обучающую и тестовую:" ] }, { "cell_type": "code", "execution_count": 94, "metadata": { "collapsed": true }, "outputs": [], "source": [ "X, y = sklearn.datasets.make_classification(n_redundant=0, random_state=42)\n", "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5, random_state=42)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "В качестве модели будем использовать логистическую регрессию. Для оценки качества воспользуемся метрикой *Accuracy*." ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": true }, "outputs": [], "source": [ "from sklearn.linear_model import LogisticRegressionCV" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": true }, "outputs": [], "source": [ "from sklearn.metrics import accuracy_score" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Обучим нашу модель и посчитаем ее качество:" ] }, { "cell_type": "code", "execution_count": 97, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "0.940" ] }, "execution_count": 97, "metadata": {}, "output_type": "execute_result" } ], "source": [ "lr = LogisticRegressionCV(Cs=[0.01, 0.1, 1.0], random_state=42)\n", "lr.fit(X_train, y_train)\n", "accuracy_score(y_test, lr.predict(X_test))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Теперь к обучающим и тестовым данным добавим признак, не несущий никакой информации, а являющийся равномерным шумом на интервале от 0 до 1." ] }, { "cell_type": "code", "execution_count": 98, "metadata": { "collapsed": false }, "outputs": [], "source": [ "X_train_noise = np.concatenate([X_train, np.random.uniform(size=(X_train.shape[0], 1))], axis=1)\n", "X_test_noise = np.concatenate([X_test, np.random.uniform(size=(X_test.shape[0], 1))], axis=1)" ] }, { "cell_type": "code", "execution_count": 99, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "0.920" ] }, "execution_count": 99, "metadata": {}, "output_type": "execute_result" } ], "source": [ "lr = LogisticRegressionCV(Cs=[0.01, 0.1, 1.0], random_state=42)\n", "lr.fit(X_train_noise, y_train)\n", "accuracy_score(y_test, lr.predict(X_test_noise))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Можно заметить, что качество стало чуть хуже.\n", "\n", "Аналогично и для \"плохих\" признаков (в данном случае — коррелирующих). Добавим к исходным данным первый признак умноженый на 2." ] }, { "cell_type": "code", "execution_count": 100, "metadata": { "collapsed": false }, "outputs": [], "source": [ "X_train_corr = np.concatenate([X_train, (X_train[:,0] * 2)[:, np.newaxis]], axis=1)\n", "X_test_corr = np.concatenate([X_test, (X_test[:, 0] * 2)[:, np.newaxis]], axis=1)" ] }, { "cell_type": "code", "execution_count": 101, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "0.920" ] }, "execution_count": 101, "metadata": {}, "output_type": "execute_result" } ], "source": [ "lr = LogisticRegressionCV(Cs=[0.01, 0.1, 1.0], random_state=42)\n", "lr.fit(X_train_corr, y_train)\n", "accuracy_score(y_test, lr.predict(X_test_corr))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Качество снова стало чуть ниже.\n", "\n", "К сожалению, сходу не всегда понятно какие именно из признаков являются шумовыми." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Переобучение\n", "\n", "Чем больше признаков, тем сложнее становится модель (например, в случае линейных — нужно находить больше коэффициентов). Таким образом, если признаков очень много, а данных не совсем — можно столкнуться с переобучением." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Какие еще проблемы возникают при большом числе признаков?*" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Отбор признаков" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " - Одномерные методы — оценивают важность признаков по отдельности\n", "\n", "Например, использую оценку дисперсии признака:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": true }, "outputs": [], "source": [ "from sklearn.feature_selection import VarianceThreshold\n", "from sklearn.feature_selection import SelectKBest\n", "from sklearn.feature_selection import f_classif" ] }, { "cell_type": "code", "execution_count": 41, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([ 0.139, 0.222, 0.25 ])" ] }, "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], "source": [ "X = [[0, 0, 1], \n", " [0, 1, 0], \n", " [1, 0, 0], \n", " [0, 1, 1], \n", " [0, 1, 0], \n", " [0, 1, 1]]\n", "sel = VarianceThreshold(0.2)\n", "sel.fit(X)\n", "X_new = sel.fit_transform(X)\n", "sel.variances_" ] }, { "cell_type": "code", "execution_count": 42, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([[0, 1],\n", " [1, 0],\n", " [0, 0],\n", " [1, 1],\n", " [1, 0],\n", " [1, 1]])" ] }, "execution_count": 42, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sel = VarianceThreshold(0.2)\n", "X_new = sel.fit_transform(X)\n", "X_new" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Теперь попробуем поработать с реальными данными." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Рассмотрим датасет Титаник:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/html": [ "
\n", " | PassengerId | \n", "Survived | \n", "Pclass | \n", "Name | \n", "Sex | \n", "Age | \n", "SibSp | \n", "Parch | \n", "Ticket | \n", "Fare | \n", "Cabin | \n", "Embarked | \n", "
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | \n", "1 | \n", "0 | \n", "3 | \n", "Braund, Mr. Owen Harris | \n", "male | \n", "22 | \n", "1 | \n", "0 | \n", "A/5 21171 | \n", "7.2500 | \n", "NaN | \n", "S | \n", "
1 | \n", "2 | \n", "1 | \n", "1 | \n", "Cumings, Mrs. John Bradley (Florence Briggs Th... | \n", "female | \n", "38 | \n", "1 | \n", "0 | \n", "PC 17599 | \n", "71.2833 | \n", "C85 | \n", "C | \n", "
2 | \n", "3 | \n", "1 | \n", "3 | \n", "Heikkinen, Miss. Laina | \n", "female | \n", "26 | \n", "0 | \n", "0 | \n", "STON/O2. 3101282 | \n", "7.9250 | \n", "NaN | \n", "S | \n", "
3 | \n", "4 | \n", "1 | \n", "1 | \n", "Futrelle, Mrs. Jacques Heath (Lily May Peel) | \n", "female | \n", "35 | \n", "1 | \n", "0 | \n", "113803 | \n", "53.1000 | \n", "C123 | \n", "S | \n", "
4 | \n", "5 | \n", "0 | \n", "3 | \n", "Allen, Mr. William Henry | \n", "male | \n", "35 | \n", "0 | \n", "0 | \n", "373450 | \n", "8.0500 | \n", "NaN | \n", "S | \n", "
\n", " | Pclass | \n", "Age | \n", "SibSp | \n", "Parch | \n", "Fare | \n", "Sex | \n", "
---|---|---|---|---|---|---|
0 | \n", "3 | \n", "22 | \n", "1 | \n", "0 | \n", "7.2500 | \n", "1 | \n", "
1 | \n", "1 | \n", "38 | \n", "1 | \n", "0 | \n", "71.2833 | \n", "0 | \n", "
2 | \n", "3 | \n", "26 | \n", "0 | \n", "0 | \n", "7.9250 | \n", "0 | \n", "
3 | \n", "1 | \n", "35 | \n", "1 | \n", "0 | \n", "53.1000 | \n", "0 | \n", "
4 | \n", "3 | \n", "35 | \n", "0 | \n", "0 | \n", "8.0500 | \n", "1 | \n", "