Рубрики
Без рубрики

Глубокое обучение в Keras – Предварительная обработка данных

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

Автор оригинала: Ammar Alyousfi.

Вступление

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

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

Python стал основным языком машинного обучения, и многие из самых популярных и мощных библиотек глубокого обучения и фреймворков , таких как TensorFlow , Keras и PyTorch , построены на Python.

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

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

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

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

Работа с пропущенными значениями

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

missing_values = pd.DataFrame({
    'Column': df.columns.values,
    '# of missing values': df.isna().sum().values,
    '% of missing values': 100 * df.isna().sum().values / len(df),
})

missing_values = missing_values[missing_values['# of missing values'] > 0]
print(missing_values.sort_values(by='# of missing values', 
                                 ascending=False
                                ).reset_index(drop=True))

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

Колонка # пропущенных значений % пропущенных значений
0 Контроль качества бассейна 2917 99.5563
1 Функция Misc 2824 96.3823
2 Аллея 2732 93.2423
3 Забор 2358 80.4778
4 Качество камина 1422 48.5324
5 Фасад участка 490 16.7235
6 Гараж Кондоминиум 159 5.42662
7 Гаражное качество 159 5.42662
8 Отделка гаража 159 5.42662
9 Гараж Yr Blt 159 5.42662
10 Гаражный Тип 157 5.35836
11 Экспозиция Bsmt 83 2.83276
12 Плавник Bsmt Тип 2 81 2.76451
13 Плавник Bsmt Тип 1 80 2.73038
14 Лучшее Качество 80 2.73038
15 Bsmt Cond 80 2.73038
16 Область Mas Vnr 23 0.784983
17 Тип Mas Vnr 23 0.784983
18 Bsmt Половинная Ванна 2 0.0682594
19 Полная Ванна Bsmt 2 0.0682594
20 Итого Bsmt SF 1 0.0341297

Поскольку переменные Pool QC , Misc Feature , Alley , Fence и Fireplace Qu содержат высокий процент пропущенных значений , как показано в таблице, мы просто удалим их, так как они, вероятно, не сильно повлияют на результаты:

df.drop(['Pool QC', 'Misc Feature', 'Alley', 'Fence', 'Fireplace Qu'], 
        axis=1, inplace=True)

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

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

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

cols_with_missing_values = df.columns[df.isna().sum() > 0]
for col in cols_with_missing_values:
    print(col)
    print(df[col].unique()[:5])
    print('*'*30)

И мы получаем следующие результаты:

Lot Frontage
[141.  80.  81.  93.  74.]
******************************
Mas Vnr Type
['Stone' 'None' 'BrkFace' nan 'BrkCmn']
******************************
...

Давайте заменим значения пропущенных числовых значений на средние:

num_with_missing = ['Lot Frontage', 'Mas Vnr Area', 'BsmtFin SF 1', 'BsmtFin SF 2', 
                    'Bsmt Unf SF', 'Total Bsmt SF', 'Bsmt Full Bath', 'Bsmt Half Bath', 
                    'Garage Yr Blt', 'Garage Cars', 'Garage Area']

for n_col in num_with_missing:
    df[n_col] = df[n_col].fillna(df[n_col].mean())

Здесь мы просто помещаем их все в список и присваиваем им новые значения. Далее заменим пропущенные значения категориальных переменных:

cat_with_missing = [x for x in cols_with_missing_values if x not in num_with_missing]

for c_col in cat_with_missing:
    df[c_col] = df[c_col].fillna(df[c_col].mode().to_numpy()[0])

После этого шага в нашем наборе данных не будет отсутствующих значений.

Однократное кодирование категориальных переменных

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

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

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

Проверка Переменных Типов данных

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

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

Давайте проверим, есть ли какие-либо несоответствия типов данных в фрейме данных :

data_types = pd.DataFrame({
    'Column': df.select_dtypes(exclude='object').columns.values,
    'Data type': df.select_dtypes(exclude='object').dtypes.values
})

print(data_types)
Колонка Тип данных
0 Подкласс MS int64
1 Фасад участка float64
2 Площадь Участка int64
3 Общее Качество int64
4 Общее Состояние int64
5 Год Постройки int64
6 Год Remod/Add int64

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

Например, подкласс MS был обнаружен как числовая переменная с типом данных int64 . Однако, основываясь на описании этой переменной, она определяет тип продаваемой единицы.

Если мы посмотрим на уникальные значения этой переменной:

df['MS SubClass'].unique().tolist()

Мы получаем этот вывод:

[20, 60, 120, 50, 85, 160, 80, 30, 90, 190, 45, 70, 75, 40, 180, 150]

Эта переменная представляет различные типы единиц измерения в виде чисел, таких как 20 (одноэтажные дома, построенные в 1946 году и более поздние), 60 (2-этажные дома, построенные в 1946 году и более поздние) и т. Д.

На самом деле это не числовая переменная, а категориальная. Давайте преобразуем его обратно в категориальную переменную, назначив ее в виде строки:

df['MS SubClass'] = df['MS SubClass'].astype(str)

Выполнение Однократного Горячего кодирования

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

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

Давайте составим список переменных, которые мы хотим сохранить в подмножестве, и обрезаем DataFrame , чтобы мы использовали только их:

selected_vars = ['MS SubClass', 'MS Zoning', 'Lot Frontage', 'Lot Area',
                 'Neighborhood', 'Overall Qual', 'Overall Cond',
                 'Year Built', 'Total Bsmt SF', '1st Flr SF', '2nd Flr SF',
                 'Gr Liv Area', 'Full Bath', 'Half Bath', 'Bedroom AbvGr', 
                 'Kitchen AbvGr', 'TotRms AbvGrd', 'Garage Area', 
                 'Pool Area', 'SalePrice']

df = df[selected_vars]

Теперь мы можем легко выполнить одно горячее кодирование с помощью функции Pandas ‘ get_dummies() :

df = pd.get_dummies(df)

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

Фасад участка Площадь Участка Общее Качество Общее Состояние Год Постройки Итого Bsmt SF 1-й Флр СФ 2-й Флр СФ Gr Liv Area
0 141 31770 6 5 1960 1080 1656 0 1656
1 80 11622 5 6 1961 882 896 0 896
2 81 14267 6 6 1958 1329 1329 0 1329

Разделение данных на Обучающие и Тестовые наборы

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

Мы разделим данные случайным образом так, чтобы обучающий набор содержал 80% данных, а тестовый-20%. Как правило, обучающий набор обычно содержит где-то между 70-80% данных, в то время как 20-30% используется для проверки.

Это делается очень просто с помощью функций Pandas sample() и drop() :

train_df = df.sample(frac=0.8, random_state=9)
test_df = df.drop(train_df.index)

Теперь train_df содержит наши обучающие данные и test_df содержит наши тестовые данные.

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

train_labels = train_df.pop('SalePrice')
test_labels = test_df.pop('SalePrice')

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

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

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

Обратите внимание, что функция Pandas’ pop() вернет указанный столбец (в нашем случае это Sale Price ) из фрейма данных (например, train_df ) с удалением этого столбца из фрейма данных.

В конце этого шага приведем количество записей (строк) и объектов (столбцов) для каждого из train_df и test_df :

Набор Количество записей Количество функций
`train_df` 2344 67
`test_df` 586 67

Кроме того, train_labels имеет 2344 метки для 2344 записей train_df и test_labels имеет 586 меток для 586 записей в test_df .

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

Масштабирование данных: Стандартизация

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

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

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

Если вы хотите прочитать о вычислении среднего, Медианы и режима в Python или Вычислении дисперсии и стандартного отклонения в Python , мы вас накроем!

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

Давайте теперь проведем стандартизацию:

predictor_vars = train_df.columns

for col in predictor_vars:
    # Calculating variable mean and std from training data
    col_mean = train_df[col].mean()
    col_std = train_df[col].std()
    if col_std == 0:
        col_std = 1e-20
    train_df[col] = (train_df[col] - col_mean) / col_std
    test_df[col] = (test_df[col] - col_mean) / col_std    

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

Затем для каждой предикторной переменной мы вычисляем среднее и стандартное отклонение, используя обучающие данные ( train_df ), вычитаем вычисленное среднее и делим на вычисленное стандартное отклонение.

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

Это дает нам масштабированные и стандартизированные данные в диапазоне от -1 до 1.

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

Вывод

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

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