В этом проекте я буду анализировать данные переписи из репозитория машинного обучения UCI, которые я получил отсюда, и классифицировать данные на основе дохода гражданина. Эти данные были взяты из базы данных Бюро переписи населения США.
Я использую файл adult.data, который содержит следующие столбцы:
- возраст: непрерывный.
- рабочий класс: частный, Self-emp-not-inc, Self-emp-inc, Federal-gov, Local-gov, State-gov, без оплаты, никогда не работал.
- fnlwgt: непрерывный.
- образование: бакалавриат, некоторый колледж, 11-й, высшая степень, проф-школа, ассоц-акдм, ассоц-вок, 9-й, 7-8-й, 12-й, магистр, 1-4-й, 10-й, докторантура, 5-й-6-й, дошкольное.
- номер образования: непрерывное.
- семейное положение: Женат-гражданский-супруг, Разведен, Никогда не состоял в браке, Раздельно, Вдовец, Женат-супруг-отсутствует, Женат-AF-супруга.
- профессия: Техподдержка, Ремесло-ремонт, Прочее-обслуживание, Продажи, Исполнитель-менеджер, Проф-специальность, Обработчики-уборщики, Машино-осмотр, Адм-канцелярия, Фермерство-рыболовство, Транспорт-переезд, Приват- серв, Защитно-серв, Вооруженные Силы.
- Отношения: Жена, Собственный ребенок, Муж, Вне семьи, Другой родственник, Неженатый.
- раса: Белый, Азиатско-Пакско-Айлендерианец, Амерско-Индейско-Эскимосский, Другой, Черный.
- пол: Женский, Мужской.
- прирост капитала: непрерывный.
- убыток капитала: непрерывный.
- часов в неделю: непрерывно.
- родная страна: США, Камбоджа, Англия, Пуэрто-Рико, Канада, Германия, отдаленные районы США (Гуам-USVI-и т. д.), Индия, Япония, Греция, Юг, Китай, Куба, Иран, Гондурас, Филиппины, Италия , Польша, Ямайка, Вьетнам, Мексика, Португалия, Ирландия, Франция, Доминиканская Республика, Лаос, Эквадор, Тайвань, Гаити, Колумбия, Венгрия, Гватемала, Никарагуа, Шотландия, Таиланд, Югославия, Сальвадор, Тринадад и Тобаго, Перу, Гонконг , Голландия-Нидерланды.
- доход: ›50K, ‹=50K.
В этом проекте я буду использовать классификатор XGBoost. Шаги, которые я сделаю:
- Исследовательский анализ данных
- Построение модели
- Обучение модели
- Улучшение модели
Исследовательский анализ данных
Я загружаю данные из источника и помещаю их в переменную df
.
import requests import io col = ['age','workclass','fnlwgt','education','education-num','marital-status','occupation','relationship','race','sex','capital-gain','capital-loss','hours-per-week','native-country','income'] s = requests.get('https://archive.ics.uci.edu/ml/machine-learning-databases/adult/adult.data', verify=False) df = pd.read_csv(io.StringIO(s.content.decode('utf-8')),delimiter=",",names=col,skipinitialspace=True)
Обзор данных показан ниже.
Давайте получим больше информации о данных, используя df.info()
.
RangeIndex: 32561 entries, 0 to 32560 Data columns (total 15 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 age 32561 non-null int64 1 workclass 32561 non-null object 2 fnlwgt 32561 non-null int64 3 education 32561 non-null object 4 education-num 32561 non-null int64 5 marital-status 32561 non-null object 6 occupation 32561 non-null object 7 relationship 32561 non-null object 8 race 32561 non-null object 9 sex 32561 non-null object 10 capital-gain 32561 non-null int64 11 capital-loss 32561 non-null int64 12 hours-per-week 32561 non-null int64 13 native-country 32561 non-null object 14 income 32561 non-null object dtypes: int64(6), object(9) memory usage: 3.7+ MB
Данные содержат 32561 запись. Все записи не являются нулевыми во всех столбцах. Есть 14 столбцов (признаков), 6 столбцов числовых с типом целых чисел, остальные (категориальные) с типом объекта.
Давайте посмотрим описательную статистику числовых столбцов, используя df.describe()
.
Для категориальных столбцов количество категорий каждого столбца может быть показано с помощью df.unique()
, который возвращает
age 73 workclass 9 fnlwgt 21648 education 16 education-num 16 marital-status 7 occupation 15 relationship 6 race 5 sex 2 capital-gain 119 capital-loss 92 hours-per-week 94 native-country 42 income 2 dtype: int64
Используя приведенный ниже код, мы можем увидеть сравнение между двумя категориями доходов.
print('These are the income categories: %s' % (pd.unique(df['income']))) for i in pd.unique(df['income']): print('%.2f%% people have income %s' %(df['income'][df['income']==i].count()/df['income'].count()*100,i))
Выходы
These are the income categories: ['<=50K' '>50K'] 75.92% people have income <=50K 24.08% people have income >50K
Давайте посмотрим на распределение всего числового столбца, используя приведенный ниже код.
df[df.select_dtypes(include=['int64', 'float64']).columns].hist(figsize=(15,10)) plt.show()
Все распределения числовых признаков выглядят совершенно иначе. Они также имеют различный диапазон, поэтому лучше масштабировать их перед созданием модели.
Давайте посмотрим на детали столбца education-num
и столбца education
, поскольку оба имеют одинаковое имя, используя print(df[['education-num'],['education']])
, что приводит к выводу ниже.
education-num education 0 13 Bachelors 1 13 Bachelors 2 9 HS-grad 3 7 11th 4 13 Bachelors ... ... ... 32556 12 Assoc-acdm 32557 9 HS-grad 32558 9 HS-grad 32559 9 HS-grad 32560 9 HS-grad
Число образования — это количество лет, потраченных на чье-то образование. Люди с одинаковым образованием также имеют одинаковый номер образования. Так что функция образования избыточна. Некоторые символы в имени столбца изменены. Столбец fnlwgt также удален, поскольку он не имеет отношения к переписываемому лицу. Я удаляю эти столбцы, используя этот код
df=df.drop(['education'], axis=1) df=df.drop(['fnlwgt'], axis=1)
Поскольку у нас есть 8 нечисловых признаков, мы должны преобразовать их в числовые.
df=pd.get_dummies(df) df.rename(columns = {'income_<=50K':'income_less=50K', 'income_>50K':'income_greater50K'}, inplace = True)
Здесь я использую манекены для создания дополнительных функций, которые являются вариациями записей нечисловых функций. Эти дополнительные функции состоят только из значений 1 и 0. Теперь у нас есть 92 функции.
Построение модели
Столбец дохода, который имеет только два значения, теперь преобразуется в два столбца, каждый из которых имеет одинаковое значение, представляющее столбец дохода. Мы можем удалить один из них, используя df=df.drop(“income_less=50K”,axis=1)
.
Затем столбцы функций загружаются в x, а целевой столбец «доход_›50K» загружается в y. Переменная x также стандартизирована и масштабирована.
from sklearn.preprocessing import StandardScaler scaler=StandardScaler() x=df.iloc[:,:-1] y=df.iloc[:,-1] x=scaler.fit_transform(x)
Затем функции были разделены на данные поезда, тестовые данные и данные проверки.
from sklearn.model_selection import train_test_split x_train_a, x_test, y_train_a, y_test=train_test_split(x,y,random_state=0) x_train, x_val, y_train, y_val=train_test_split(x_train_a, y_train_a, random_state=0)
Следующим шагом является построение модели. Я выбираю метод XGBoost, потому что он относительно быстрый и имеет хорошую производительность, особенно при обработке несбалансированных данных для двоичной классификации. Он также часто используется в ML-сообществе. Я использую LogLoss в качестве метрики оценки, потому что она обычно используется при оценке прогнозируемых вероятностей.
from xgboost import XGBClassifier xgb=XGBClassifier(n_estimators=500, learning_rate=.05, use_label_encoder=False, eval_metric='logloss', early_stopping_rounds=5, n_jobs=-1)
Теперь давайте подгоним модель к данным поезда.
xgb.fit(x_train, y_train, eval_set=[(x_val, y_val)], verbose=False)
Далее давайте проверим точность модели, подгоняя модель к тестовым данным.
from sklearn.metrics import accuracy_score ypredtest = xgb.predict(x_test) predtestscore = accuracy_score(ypredtest, y_test)
Тест точности возвращает результат со значением 0,87. Здесь мы знаем, что метод XGBoost, имеющий оценку 0,87. Судя по баллам, эта модель работает достаточно хорошо.
Улучшение модели
В этой части я попытаюсь улучшить модель, уменьшив размерность данных. Уменьшая размерность данных, мы можем избавиться от ненужных функций. Я попытаюсь использовать методы анализа основных компонентов и анализа множественных соответствий.
Во-первых, я использую метод PCA (анализ основных компонентов), чтобы уменьшить количество функций. Я применил его к фрейму данных, который превратился во все числовые данные. Метод PCA подходит для числовых и двоичных данных. Но многие функции в преобразованном фрейме данных, которые у меня есть, представляют его исходные категориальные функции, поэтому их нельзя удалить/уменьшить. Я попробовал этот метод, чтобы уменьшить размер данных до 60.
from sklearn.decomposition import PCA pca = PCA(n_components = 60) x_trainp = pca.fit_transform(x_train_a) x_testp = pca.transform(x_test) x_train, x_val, y_train, y_val=train_test_split(x_trainp, y_train_a, random_state=0) xgb.fit(x_train, y_train, eval_set=[(x_val, y_val)], verbose=False) ypredtest = xgb.predict(x_testp) predtestscore = accuracy_score(ypredtest, y_test) print("Test score:", np.round(predtestscore,2))
После уменьшения характеристик я получил оценку 0,85. Таким образом, модель не подходит лучше, чем исходные данные, потому что она набирает меньше баллов.
Другим вариантом уменьшения размерности данных (уменьшения признака) является MCA (анализ множественных соответствий), поскольку его можно использовать и для категориальных данных.
!pip install prince import prince mca = prince.MCA(n_components=2, n_iter=3, copy=True, check_input=True, engine='auto', random_state=42) DF_mca = mca.fit(DF) ax = DF_mca.plot_coordinates(X=DF, ax=None, figsize=(8, 10), show_row_points=False, row_points_size=0, show_row_labels=False, show_column_points=True, column_points_size=30, show_column_labels=True, legend_n_cols=1 ).legend(loc='center left', bbox_to_anchor=(1, 0.5))
График MCA показывает, что прирост капитала, убыток капитала и жители Нидерландов относительно не связаны с доходом. Таким образом, функция прироста капитала и потери капитала может быть удалена.
df=DF df=pd.get_dummies(df) df. rename(columns = {'income_<=50K':'income_less=50K', 'income_>50K':'income_greater50K'}, inplace = True) df=df.drop("income_less=50K",axis=1) df=df.drop("capital-loss",axis=1) df=df.drop("capital-gain",axis=1) from sklearn.preprocessing import StandardScaler scaler=StandardScaler() x=df.iloc[:,:-1] y=df.iloc[:,-1] x=scaler.fit_transform(x) x_train_a, x_test, y_train_a, y_test=train_test_split(x,y,random_state=0) x_train, x_val, y_train, y_val=train_test_split(x_train_a, y_train_a, random_state=0) xgb.fit(x_train, y_train, eval_set=[(x_val, y_val)], verbose=False) ypredtest = xgb.predict(x_test) predtestscore = accuracy_score(ypredtest, y_test) print("Test score:", np.round(predtestscore,2))
Здесь мы получили 0,84 в качестве оценки модели. По результатам теста видно, что эта модель достаточно хороша, но не лучше оригинала. Сокращение функций иногда не приводит нас к лучшей модели.
В заключение мы провели классификацию данных переписи населения с использованием метода XGBoost. Метод XGBoost имеет оценку 0,87, что означает, что эта модель работает достаточно хорошо. Мы попытались улучшить модель, уменьшив характеристики данных, но это не дает лучшего результата.
Полный код можно посмотреть здесь.
Источник: