Вступление
Глубокое обучение - одна из самых интересных и перспективных областей искусственного интеллекта (ИИ) и машинного обучения в настоящее время. Благодаря значительным достижениям в области технологий и алгоритмов в последние годы глубокое обучение открыло дверь в новую эру приложений искусственного интеллекта.
Во многих из этих приложений алгоритмы глубокого обучения работают наравне с экспертами-людьми, а иногда и превосходят их .
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 Разное 2824 96,3823 2 Аллея 2732 93,2423 3 Изгородь 2358 80,4778 4 Камин Qu 1422 48,5324 5 Lot Frontage 490 16,7235 6 Гараж Конд 159 5,42662 7 Garage Qual 159 5,42662 8 Отделка гаража 159 5,42662 9 Гараж Yr Blt 159 5,42662 10 Тип гаража 157 5,35836 11 Bsmt Exposure 83 2,83276 12 BsmtFin Тип 2 81 год 2,76451 13 BsmtFin Тип 1 80 2,73038 14 Bsmt Qual 80 2,73038 15 Bsmt Cond 80 2,73038 16 Mas Vnr Area 23 0,784983 17 Тип Mas Vnr 23 0,784983 18 Bsmt Half Bath 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, как и все модели машинного обучения, в основном работают с числовыми данными. Категориальные данные не имеют значения для компьютера, но имеют значение для нас. Нам необходимо преобразовать эти категориальные переменные в числовые представления, чтобы набор данных можно было использовать.
Техника, которую мы будем использовать для этого преобразования, - это One-Hot Encoding . Pandas предоставляет нам простой способ автоматического выполнения кодирования One-Hot для всех категориальных переменных в данных.
Однако перед этим мы должны убедиться, что никакая категориальная переменная в наших данных не будет случайно представлена как числовая переменная.
Проверка типов данных переменных
Когда мы читаем набор данных CSV, используя Pandas, как и мы, Pandas автоматически пытается определить тип каждой переменной в наборе данных.
Иногда Pandas может определить это неправильно - если категориальная переменная представлена числами, он может ошибочно сделать вывод, что это числовая переменная.
Давайте проверим, нет ли несоответствий типов данных в DataFrame
:
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 Lot Frontage float64 2 Площадь участка int64 3 Общая квалификация int64 4 Общая конд. int64 5 Год постройки int64 6 Год Ремод / Добавить int64
Основываясь на этой таблице и описаниях переменных из Kaggle , мы можем заметить, какие переменные ошибочно считались числовыми в Pandas.
Например, MS SubClass
был обнаружен как числовая переменная с типом
данных 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
(двухэтажные дома, построенные в 1946 году и новее) и т. Д.
На самом деле это не числовая, а категориальная переменная. Давайте снова преобразуем ее в категориальную переменную, переназначив ее как строку:
df['MS SubClass'] = df['MS SubClass'].astype(str)
Выполнение одноразового кодирования
Перед выполнением One-Hot Encoding мы хотим выбрать подмножество функций из наших данных для использования с этого момента. Мы захотим это сделать, потому что наш набор данных содержит 2930 записей и 75 функций.
Многие из этих характеристик категоричны. Поэтому, если мы сохраним все функции и выполним One-Hot Encoding, результирующее количество функций будет большим, и в результате модель может пострадать от проклятия размерности .
Давайте составим список переменных, которые мы хотим сохранить в
подмножестве, и 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]
Теперь мы можем легко выполнить One-Hot Encoding с помощью функции
Pandas get_dummies()
:
df = pd.get_dummies(df)
После однократного кодирования в наборе данных будет 67 переменных. Вот несколько первых строк с ограничениями - переменных гораздо больше:
Lot Frontage Площадь участка Общая квалификация Общая конд. Год постройки Итого Bsmt SF 1-й этаж SF 2-й этаж SF 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% используются для проверки.
Это очень просто сделать с помощью функций sample()
и drop()
train_df = df.sample(frac=0.8, random_state=9)
test_df = df.drop(train_df.index)
Теперь train_df
содержит данные для обучения, а test_df
данные для
тестирования.
Далее мы сохраним целевую переменную SalePrice
отдельно для каждого из
обучающих и тестовых наборов:
train_labels = train_df.pop('SalePrice')
test_labels = test_df.pop('SalePrice')
Мы SalePrice
потому что мы хотим его предсказать. Нет смысла
предсказывать то, что мы уже знаем и использовали в модели. Мы будем
использовать фактические значения, чтобы проверить правильность наших
прогнозов.
После этого шага train_df
будет содержать переменные-предикторы наших
обучающих данных (то есть все переменные, за исключением целевой
переменной), а train_labels
будет содержать значения целевых
переменных для train_df
. То же самое относится к test_df
и
test_labels
.
Мы выполняем эту операцию, чтобы подготовиться к следующему шагу масштабирования данных.
Обратите внимание, что
pop()
Pandas вернет указанный столбец (в нашем случае этоSalePrice
) изtrain_df
(например, train_df) с удалением этого столбца из фрейма данных.
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.
После этого наш набор данных готов к использованию для обучения и оценки модели. В следующей статье мы построим глубокую нейронную сеть.
Заключение
Предварительная обработка данных - важный этап в конвейере машинного обучения. Не отбрасывая определенные переменные, не имея дело с пропущенными значениями, кодируя категориальные значения и стандартизуя
- мы будем загружать беспорядочный (или невозможный) набор данных в модель.
Модель будет настолько хороша, насколько хороши данные, которые мы ей скармливаем, и в этой статье мы подготовили набор данных для соответствия модели.