К предобработке данных для машинного обучения относят : Кодирование категориальных переменных , разделение данных на обучающую и тестовую части, нормализацию или стандартизацию.
Для иллюстрации методов, выполняющих такую предобработку создадим учебный датафрейм с различными категориальными показателями : "Категория товара", в дальнейшем ее можно использовать как метку класса, "Размер", "Бренд", "Цвет" и "Пол". В дальнейшем добавим одну числовую характеристику - "Цена".
df = pd.DataFrame({
'Сategory':np.random.choice(['Cat 1', 'Cat 2', 'Cat 3'],10),
'Size': np.random.choice(['XS', 'S', 'M', 'L', 'XL', 'XXL'], 10),
'Brand': np.random.choice(['Nike', 'Puma', 'Adidas', 'Le Coq','Reebok'], 10),
'Color': np.random.choice(['Blue', 'Yellow', 'Red'],10),
'Gender': np.random.choice(['F', 'M'],10)
})
df
Сategory | Size | Brand | Color | Gender | |
---|---|---|---|---|---|
0 | Cat 1 | XXL | Adidas | Red | F |
1 | Cat 2 | XL | Adidas | Yellow | M |
2 | Cat 3 | M | Puma | Red | F |
3 | Cat 2 | S | Le Coq | Red | M |
4 | Cat 1 | S | Puma | Yellow | F |
5 | Cat 1 | M | Nike | Yellow | M |
6 | Cat 3 | XXL | Reebok | Blue | F |
7 | Cat 2 | XXL | Puma | Red | F |
8 | Cat 1 | XXL | Le Coq | Yellow | M |
9 | Cat 1 | L | Nike | Yellow | F |
Кодирование категориальных переменных
Когда речь идет о категориальных данных, мы должны дополнительно
проводить различие между именными и порядковыми признаками.
Порядковые признаки - это категориальные значения, которые
допускают сортировку или упорядочение. Например, размер футболки
был бы порядковым признаком, потому что мы можем определить порядок
XXL-XL > L > М>S. Напротив, именные признаки не подразумевают какого-либо
порядка и в контексте примера с футболками мы могли бы считать именным
признаком цвет футболки, т.к. обычно не имеет смысла говорить что-то вроде
того, что красный больше синего.
проводить различие между именными и порядковыми признаками.
Порядковые признаки - это категориальные значения, которые
допускают сортировку или упорядочение. Например, размер футболки
был бы порядковым признаком, потому что мы можем определить порядок
XXL-XL > L > М>S. Напротив, именные признаки не подразумевают какого-либо
порядка и в контексте примера с футболками мы могли бы считать именным
признаком цвет футболки, т.к. обычно не имеет смысла говорить что-то вроде
того, что красный больше синего.
Чтобы обеспечить корректную интерпретацию порядковых признаков алгоритмами
обучения, нам нужно преобразовать строковые значения в целые числа. Сделаем это с помощью словаря size_mapping.
size_mapping = {'XS':1, 'S':2, 'M':3, 'L':4, 'XL':5, 'XXL':6}
df['Size [Ordinal Encoded]'] = df['Size'].map(size_mapping)
df
Сategory | Size | Brand | Color | Gender | Size [Ordinal Encoded] | |
---|---|---|---|---|---|---|
0 | Cat 1 | XXL | Adidas | Red | F | 6 |
1 | Cat 2 | XL | Adidas | Yellow | M | 5 |
2 | Cat 3 | M | Puma | Red | F | 3 |
3 | Cat 2 | S | Le Coq | Red | M | 2 |
4 | Cat 1 | S | Puma | Yellow | F | 2 |
5 | Cat 1 | M | Nike | Yellow | M | 3 |
6 | Cat 3 | XXL | Reebok | Blue | F | 6 |
7 | Cat 2 | XXL | Puma | Red | F | 6 |
8 | Cat 1 | XXL | Le Coq | Yellow | M | 6 |
9 | Cat 1 | L | Nike | Yellow | F | 4 |
Если на более поздней стадии целочисленные значения необходимо
трансформировать в первоначальное строковое представление, тогда мы
можем определить словарь обратного отображения inv_size mapping =
{ v: k for k, v in size_mapping.items () } . Затем он может применяться
при вызове метода map библиотеки pandas на трансформированном столбце
признака подобно использованному ранее словарю size_mapping:
inv_size_mapping = {v: k for k, v in size_mapping.items ()}
df['Size 0']=df['Size [Ordinal Encoded]'].map(inv_size_mapping)
df
Сategory | Size | Brand | Color | Gender | Size [Ordinal Encoded] | Size 0 | |
---|---|---|---|---|---|---|---|
0 | Cat 2 | XXL | Adidas | Red | F | 6 | XXL |
1 | Cat 3 | M | Reebok | Red | F | 3 | M |
2 | Cat 3 | XXL | Nike | Red | M | 6 | XXL |
3 | Cat 2 | XS | Nike | Blue | F | 1 | XS |
4 | Cat 3 | XL | Le Coq | Yellow | F | 5 | XL |
5 | Cat 2 | XL | Nike | Blue | F | 5 | XL |
6 | Cat 3 | XXL | Nike | Yellow | F | 6 | XXL |
7 | Cat 1 | S | Reebok | Yellow | F | 2 | S |
8 | Cat 2 | M | Le Coq | Yellow | F | 3 | M |
9 | Cat 1 | L | Le Coq | Red | M | 4 | L |
Для перевода метки классов (категория) в целочисленные значения используем класс LabelEncoder из библиотеке scikitleam.
from sklearn.preprocessing import LabelEncoder
class_le = LabelEncoder ()
df['Cat num']=class_le.fit_transform(df['Сategory'].values)
df
Сategory | Size | Brand | Color | Gender | Size [Ordinal Encoded] | Size 0 | Cat num | |
---|---|---|---|---|---|---|---|---|
0 | Cat 2 | XXL | Adidas | Red | F | 6 | XXL | 1 |
1 | Cat 3 | M | Reebok | Red | F | 3 | M | 2 |
2 | Cat 3 | XXL | Nike | Red | M | 6 | XXL | 2 |
3 | Cat 2 | XS | Nike | Blue | F | 1 | XS | 1 |
4 | Cat 3 | XL | Le Coq | Yellow | F | 5 | XL | 2 |
5 | Cat 2 | XL | Nike | Blue | F | 5 | XL | 1 |
6 | Cat 3 | XXL | Nike | Yellow | F | 6 | XXL | 2 |
7 | Cat 1 | S | Reebok | Yellow | F | 2 | S | 0 |
8 | Cat 2 | M | Le Coq | Yellow | F | 3 | M | 1 |
9 | Cat 1 | L | Le Coq | Red | M | 4 | L | 0 |
А с помощью метода inverse transform целочисленные метки классов можно трансформировать обратно в первоначальное строковое представление:
df['Cat 0']=class_le.inverse_transform(df['Cat num'])
df
Сategory | Size | Brand | Color | Gender | Size [Ordinal Encoded] | Size 0 | Cat num | Cat 0 | |
---|---|---|---|---|---|---|---|---|---|
0 | Cat 2 | XXL | Adidas | Red | F | 6 | XXL | 1 | Cat 2 |
1 | Cat 3 | M | Reebok | Red | F | 3 | M | 2 | Cat 3 |
2 | Cat 3 | XXL | Nike | Red | M | 6 | XXL | 2 | Cat 3 |
3 | Cat 2 | XS | Nike | Blue | F | 1 | XS | 1 | Cat 2 |
4 | Cat 3 | XL | Le Coq | Yellow | F | 5 | XL | 2 | Cat 3 |
5 | Cat 2 | XL | Nike | Blue | F | 5 | XL | 1 | Cat 2 |
6 | Cat 3 | XXL | Nike | Yellow | F | 6 | XXL | 2 | Cat 3 |
7 | Cat 1 | S | Reebok | Yellow | F | 2 | S | 0 | Cat 1 |
8 | Cat 2 | M | Le Coq | Yellow | F | 3 | M | 1 | Cat 2 |
9 | Cat 1 | L | Le Coq | Red | M | 4 | L | 0 | Cat 1 |
Также с помощью метода LabelEncoder кодируем пол
df['Gender Enc']=class_le.fit_transform(df['Gender'].values)
df
Сategory | Size | Brand | Color | Gender | Size [Ordinal Encoded] | Size 0 | Cat num | Cat 0 | Gender Enc | |
---|---|---|---|---|---|---|---|---|---|---|
0 | Cat 2 | XXL | Adidas | Red | F | 6 | XXL | 1 | Cat 2 | 0 |
1 | Cat 3 | M | Reebok | Red | F | 3 | M | 2 | Cat 3 | 0 |
2 | Cat 3 | XXL | Nike | Red | M | 6 | XXL | 2 | Cat 3 | 1 |
3 | Cat 2 | XS | Nike | Blue | F | 1 | XS | 1 | Cat 2 | 0 |
4 | Cat 3 | XL | Le Coq | Yellow | F | 5 | XL | 2 | Cat 3 | 0 |
5 | Cat 2 | XL | Nike | Blue | F | 5 | XL | 1 | Cat 2 | 0 |
6 | Cat 3 | XXL | Nike | Yellow | F | 6 | XXL | 2 | Cat 3 | 0 |
7 | Cat 1 | S | Reebok | Yellow | F | 2 | S | 0 | Cat 1 | 0 |
8 | Cat 2 | M | Le Coq | Yellow | F | 3 | M | 1 | Cat 2 | 0 |
9 | Cat 1 | L | Le Coq | Red | M | 4 | L | 0 | Cat 1 | 1 |
Одним из наиболее используемых методов работы с категориальными признаками является подход называемым One Hot Encoding. Основная стратегия состоит в том, чтобы преобразовать значение каждой категории в новую колонку и присваивает значение 1 или 0 (True / False) значение для столбца.
Есть несколько способов использовать этот метод. Мы представим, как сделать это с Pands, используя функцию get_dammies. Эта функция создает фиктивные переменные со значениями 0 или 1. Сделаем такое кодирование для цвета. Созданы три дополнительных столбца, по одному для каждого уникальных значений цвета: color_blue, color_red и color_yellow. В зависимости от значения цвета, только один из трех фиктивных колонок имеет значение 1.
df['Color_cat'] = df.Color
df = pd.get_dummies(df, columns=["Color_cat"], prefix=["Color"])
df
Сategory | Size | Brand | Color | Gender | Color_Blue | Color_Red | Color_Yellow | |
---|---|---|---|---|---|---|---|---|
0 | Cat 3 | L | Nike | Blue | F | 1 | 0 | 0 |
1 | Cat 1 | L | Nike | Yellow | M | 0 | 0 | 1 |
2 | Cat 2 | XS | Adidas | Red | M | 0 | 1 | 0 |
3 | Cat 1 | M | Le Coq | Red | M | 0 | 1 | 0 |
4 | Cat 2 | S | Le Coq | Blue | F | 1 | 0 | 0 |
5 | Cat 1 | XXL | Puma | Red | F | 0 | 1 | 0 |
6 | Cat 2 | M | Adidas | Red | M | 0 | 1 | 0 |
7 | Cat 3 | M | Adidas | Blue | M | 1 | 0 | 0 |
8 | Cat 1 | S | Reebok | Blue | M | 1 | 0 | 0 |
9 | Cat 2 | XS | Nike | Yellow | M | 0 | 0 | 1 |
Поскольку «get_dummies» заменит оригинальный столбец фиктивными столбцами, мы дублируем колонку цвет, чтобы сохранить исходные значения. Задаем prefix = [« Color»] , чтобы определить имена фиктивных колонок с префиксом «Color». Без этого аргумента фиктивные колонки будут названы значениями цвета, т.е. имена будут «Blue», «Red» и «Yellow».
Полезные замечания экспертов :
При использовании такого кодирования мы должны помнить, что оно привносит мультиколлинеарность, которая может стать проблемой для определенных методов (например, методов, требующих обращения матриц). Если признаки сильно взаимосвязаны, тогда обращать матрицы будет трудно в вычислительном плане, что может привести к получению численно неустойчивых оценок. Чтобы сократить взаимосвязь между переменными, мы можем просто удалить один столбец признака из
закодированного таким кодом массива. При этом мы не теряем какую-то важную информацию. Скажем, после удаления столбца color_blue информация признака
все равно сохраняется, т.к. из наблюдения color_green=0 и color_ red=0 вытекает, что цветом должен быть blue.
Если мы применяем функцию get dummies, то можем удалить первый
столбец, указав True для параметра drop_first, как показано в
следующем коде:
df['Color_cat'] = df.Color
df = pd.get_dummies(df, columns=["Color_cat"], prefix=["Color"],drop_first=True)
df
Сategory | Size | Brand | Color | Gender | Color_Red | Color_Yellow | |
---|---|---|---|---|---|---|---|
0 | Cat 3 | XXL | Puma | Red | M | 1 | 0 |
1 | Cat 2 | XL | Puma | Blue | M | 0 | 0 |
2 | Cat 2 | XXL | Le Coq | Yellow | M | 0 | 1 |
3 | Cat 3 | XS | Nike | Red | M | 1 | 0 |
4 | Cat 1 | L | Le Coq | Yellow | F | 0 | 1 |
5 | Cat 2 | XL | Puma | Yellow | F | 0 | 1 |
6 | Cat 3 | M | Puma | Red | F | 1 | 0 |
7 | Cat 3 | XXL | Le Coq | Blue | M | 0 | 0 |
8 | Cat 1 | XS | Le Coq | Blue | M | 0 | 0 |
9 | Cat 1 | L | Adidas | Blue | M | 0 | 0 |
Для кодирования числовых показателей можно использовать пороговую кодировку.
Добавим в наши данные цену и введем новый параметр категории цены
df = pd.DataFrame({
'Сategory':np.random.choice(['Cat 1', 'Cat 2', 'Cat 3'],10),
'Size': np.random.choice(['XS', 'S', 'M', 'L', 'XL', 'XXL'], 10),
'Brand': np.random.choice(['Nike', 'Puma', 'Adidas', 'Le Coq','Reebok'], 10),
'Color': np.random.choice(['Blue', 'Yellow', 'Red'],10),
'Gender': np.random.choice(['F', 'M'],10),
'Price': np.random.choice(range(50,200),10)
})
df
Сategory | Size | Brand | Color | Gender | Price | |
---|---|---|---|---|---|---|
0 | Cat 2 | XS | Nike | Yellow | M | 80 |
1 | Cat 1 | L | Adidas | Red | F | 178 |
2 | Cat 2 | L | Puma | Yellow | M | 83 |
3 | Cat 1 | M | Puma | Blue | F | 117 |
4 | Cat 1 | S | Nike | Yellow | F | 169 |
5 | Cat 1 | S | Nike | Red | M | 165 |
6 | Cat 3 | L | Le Coq | Red | M | 84 |
7 | Cat 2 | XXL | Nike | Blue | F | 117 |
8 | Cat 2 | S | Nike | Yellow | F | 96 |
9 | Cat 1 | L | Adidas | Yellow | M | 127 |
df['Price1'] = df['Price'].apply(lambda x : 1 if x < 100 else 0)
df['Price2'] = df['Price'].apply(lambda x : 1 if (x >= 100) & (x < 150) else 0)
df['Price3'] = df['Price'].apply(lambda x : 1 if x >= 150 else 0)
df
Сategory | Size | Brand | Color | Gender | Price | Price1 | Price2 | Price3 | |
---|---|---|---|---|---|---|---|---|---|
0 | Cat 2 | XL | Puma | Red | F | 160 | 0 | 0 | 1 |
1 | Cat 1 | S | Nike | Yellow | M | 86 | 1 | 0 | 0 |
2 | Cat 2 | L | Puma | Blue | F | 180 | 0 | 0 | 1 |
3 | Cat 3 | M | Adidas | Blue | F | 194 | 0 | 0 | 1 |
4 | Cat 1 | XL | Reebok | Blue | M | 164 | 0 | 0 | 1 |
5 | Cat 2 | M | Nike | Blue | M | 134 | 0 | 1 | 0 |
6 | Cat 2 | M | Puma | Red | F | 191 | 0 | 0 | 1 |
7 | Cat 3 | XS | Puma | Red | F | 134 | 0 | 1 | 0 |
8 | Cat 3 | L | Nike | Blue | M | 184 | 0 | 0 | 1 |
9 | Cat 2 | S | Adidas | Blue | F | 168 | 0 | 0 | 1 |
Разбиение набора данных на отдельные обучающий и тестовые наборы
Хороший способ случайного разбиения набора данных на отдельные обучающий и тестовый поднаборы предусматривает применение функции train_test_split из подмодуля model_selection библиотеки scikit-learn.
from sklearn.model_selection import train_test_split
X,y= df.iloc[:,1:].values,df.iloc[:,0].values
X_train, X_test, y_train, y_test = train_test_split(X, y,test_size=0.3,random_state=0,stratify=y)
print(y_train)
print(y_test)
['Cat 2' 'Cat 2' 'Cat 2' 'Cat 3' 'Cat 3' 'Cat 1' 'Cat 2'] ['Cat 1' 'Cat 2' 'Cat 3']
Сначала мы присваиваем переменной Х представление в виде массива
NumPy столбцов признаков, а переменной у - метки классов из первого столбца. Затем мы используем функцию train_ test_split для случайного разделения Х и у на обучающий и испытательный наборы данных. Установкой test_size=0.3 мы присваиваем 30% образцов данных Х test и у_ test, а оставшиеся 70% образцов - Х_train и у_train. Предоставление массива меток классов у как аргумента для stratify гарантирует, что и обучающий, и испытательный набор данных имеют такие же доли классов, как у исходного набора данных.
Полезные замечания экспертов :
На практике самыми часто применяемыми разделениями являются 60:40, 70:30 и 80:20 в зависимости от размера первоначального набора данных. Однако для крупных наборов данных разделение 90:10 или 99:1 на обучающий и испытательный наборы также оказываются допустимым. Если набор данных содержит свыше 100 000 обучающих образцов, то может оказаться неплохим решение удержать только 10000 образцов для испытаний, чтобы получить хорошую оценку эффективности обобщения.
Приведение признаков к одному масштабу
Полезные замечания экспертов :
Масштабирование признаков - критически важный шаг в конвейере предварительной обработки, о котором можно легко забыть. Деревья принятия решений и случайные леса представляют собой два из очень немногих алгоритмов МО, где не нужно беспокоиться о масштабировании признаков. Такие алгоритмы инвариантны к масштабу. Тем не менее, большинство алгоритмов МО и оптимизации работают гораздо лучше, когда признаки имеют один и тот же масштаб.
Масштабирование данных в Scikit-Learn может принимать несколько форм, мы введем два из них для показателя Price.
X_train[:,4],X_test[:,4]
(array([180, 168, 134, 194, 184, 164, 191], dtype=object), array([86, 160, 134], dtype=object))
Стандартизация: данные масштабируются, чтобы иметь нулевую среднюю и единицу (то есть, одну) дисперсию.
from sklearn.preprocessing import StandardScaler
ss = StandardScaler()
ss.fit(X_train[:,4].reshape(-1, 1))
df_train_ss = ss.transform(X_train[:,4].reshape(-1, 1))
df_test_ss = ss.transform(X_test[:,4].reshape(-1, 1))
df_train_ss,df_test_ss
(array([[ 0.33617681], [-0.29135323], [-2.06935501], [ 1.06829519], [ 0.54535349], [-0.50052991], [ 0.91141268]]), array([[-4.57947517], [-0.70970659], [-2.06935501]]))
Нормализация: данные масштабируются, чтобы охватить определенный диапазон, такой как [0,1].
from sklearn.preprocessing import MinMaxScaler
mms = MinMaxScaler()
mms.fit(X_train[:,4].reshape(-1, 1))
df_train_mms = mms.transform(X_train[:,4].reshape(-1, 1))
df_test_mms = mms.transform(X_test[:,4].reshape(-1, 1))
df_train_mms,df_test_mms
(array([[0.76666667], [0.56666667], [0. ], [1. ], [0.83333333], [0.5 ], [0.95 ]]), array([[-0.8 ], [ 0.43333333], [ 0. ]]))
Полезные замечания экспертов :
Несмотря на то что нормализация посредством масштабирования по
минимаксу является ходовым приемом, который полезен, когда необходимы
значения в ограниченном интервале, стандартизация может быть более
практичной для многих алгоритмов МО, особенно для алгоритмов оптимизации
наподобие градиентного спуска. Причина в том, что многие линейные
модели, такие как логистическая регрессия и SVM, инициализируют веса нулями или небольшими случайными значениями, близкими к нулю. С помощью стандартизации мы центрируем столбцы признаков относительно среднего значения 0 со стандартным отклонением 1, так что столбцы признаков имеют те же параметры, как нормальное распределение (нулевое среднее и единичная дисперсия), облегчая выяснение
весов. Кроме того, стандартизация сохраняет полезную информацию о выбросах и делает алгоритм менее чувствительным к ним в отличие от масштабирования по минимаксу, которое приводит данные к ограниченному диапазону значений.
Комментариев нет:
Отправить комментарий