Наш один горячий кодировщик создает функцию для каждого уровня исходной категориальной функции:
from evalml.pipelines import OneHotEncoder
import pandas as pd
df = pd.DataFrame({"category": ["a", "b"], "number": [4,5 ]})
OneHotEncoder().fit_transform(df).to_dataframe()
Столбцы category_a
и category_b
полностью коллинеарны, что делает один из них излишним. Это может иметь неблагоприятные последствия для подгонки оценщика. Я думаю, мы должны отказаться от одного по умолчанию.
К вашему сведению @rpeck
💯% мы должны убрать столбец с отрицательным регистром.
Если мы сначала сделаем OHE сами, то, надеюсь, sklearn не будет их расширять. Как сказал Фредди, вы можете думать об этом как о создании двух столбцов с идеальной коллинеарностью.
Есть две проблемы, которые я вижу при расширении двоичного файла в два столбца, а не в один:
@freddyaboulton Q:
@rpeck Да!
@freddyaboulton Что за ? Это странно. Я никогда не видел ничего, кроме настоящих логических значений или целых чисел 0/1. Интересно, как на самом деле модели дерева справляются с этим. Он имеет неприятный запах для меня.
Третий закон кодекса: не делайте == сравнений с поплавками
(хорошо, если это не с Math.NaN
)
Хм, я думал, что мы делаем это!
Я согласен, что мы должны. Я думал, что это просто флаг, который мы должны были установить в базовой реализации.
@dsherry @freddyaboulton Похоже, у нас есть поддержка для этого с помощью нашего параметра drop
но он принимает во внимание только ввод данных пользователем и не используется нашей имплементацией, поэтому эта проблема просто отслеживает установку значения по умолчанию для drop
к чему-то другому, кроме None?
https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.OneHotEncoder.html
Мы могли бы выбрать first
или if_binary
, но не уверены, что это правильно.
@ angela97lin Вы правы, достаточно изменить значение по умолчанию! Я думаю, что first
— это правильный путь, поскольку мы должны избегать идеально коллинеарных функций, даже когда количество категорий> 2. Что вы думаете о @rpeck ?
Немного читал об этом и нашел эту ссылку: https://inmachineswetrust.com/posts/drop-first-columns/
Основные выводы:
Первый комментарий RE @rpeck : «Как и другие формы коллинеарности функций, это
Это имеет смысл для двоичных случаев, но в случае, когда у нас есть несколько категорий, удаление одного столбца по-прежнему будет иметь эту проблему.
Возможно, мы не должны делать это по умолчанию, но должны обновить make_pipeline
чтобы создать OHE с first
в качестве параметра, если оценщик является линейным регрессором?
Увы, я не слишком разбираюсь в базовой математике, чтобы судить об этом, поэтому я хотел бы услышать ваше мнение , @rpeck @dsherry
Пост-дискуссия с @freddyaboulton @rpeck @dsherry @chukarsten @jeremyliweishih
@angela97lin звучит как хорошее поведение RE по умолчанию. Еще один приятный момент: возможность переопределить это поведение по умолчанию с помощью параметров компонента.
@dsherry Если я правильно понимаю, поскольку мы обновляем значение по умолчанию drop
(параметр), пользователи смогут переопределить это, установив параметр компонента вручную?
Покопался, чтобы посмотреть, что необходимо для реализации этого. В частности, мне было любопытно, насколько сложно будет всегда удалять миноритарный класс в бинарном случае.
Результат копания таков:
drop
(https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.OneHotEncoder.html). Однако после опробования требуется, чтобы для каждого столбца было указано значение индекса. Следовательно, следующее, которое пытается удалить категорию, указанную в индексе 0 для столбца 0, и никаких других значений для столбцов 1 и 2 ошибок:import pandas as pd
import numpy as np
from sklearn.preprocessing import OneHotEncoder
X = pd.DataFrame({'col_1': ["a", "b", "b", "a", "b"],
'col_2': ["a", "b", "a", "c", "b"],
'col_3': ["a", "a", "a", "a", "a"]})
indices_to_drop = np.array([0, None, None])
ohe = OneHotEncoder(drop=indices_to_drop)
ohe.fit(X)
ValueError Traceback (most recent call last)
<ipython-input-4-a099fa2fc4a7> in <module>
----> 1 ohe.fit(X)
~/Desktop/venv/lib/python3.7/site-packages/sklearn/preprocessing/_encoders.py in fit(self, X, y)
417 self._fit(X, handle_unknown=self.handle_unknown,
418 force_all_finite='allow-nan')
--> 419 self.drop_idx_ = self._compute_drop_idx()
420 return self
421
~/Desktop/venv/lib/python3.7/site-packages/sklearn/preprocessing/_encoders.py in _compute_drop_idx(self)
394 ["Category: {}, Feature: {}".format(c, v)
395 for c, v in missing_drops])))
--> 396 raise ValueError(msg)
397 return np.array(drop_indices, dtype=object)
398
ValueError: The following categories were supposed to be dropped, but were not found in the training data.
Category: 0, Feature: 0
Category: 1, Feature: None
Category: 2, Feature: None
Я считаю, что это также половина того, на что указывает эта проблема: https://github.com/scikit-learn/scikit-learn/issues/16511.
Альтернативой, которую мы можем сделать, чтобы поддержать это, является ручное отслеживание того, какие столбцы и какие значения мы хотим отбросить во время подбора. Передайте данные в scikit-learn. Затем удалите столбцы, которые мы сохранили и указали, что хотим удалить. Однако для этого требуется некоторая логическая обработка для определения оригинала (признака, значения) из имени преобразованного столбца. (У нас есть эта логика в get_feature_names
но это помогает нам соединить имена столбцов, предполагая, что ничего не нужно отбрасывать...)
Все это говорит о том, что, возможно, на данный момент будет достаточно просто использовать scikit-learn if_binary
по умолчанию, и мы можем зарегистрировать отдельную проблему, чтобы всегда использовать класс меньшинства. Честно говоря, также за то, чтобы отказаться от реализации OHE в scikit-learn, учитывая, сколько нам пришлось обойти это.
Полезные ресурсы:
Документ OHE: https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.OneHotEncoder.html
Код в scikit-learn, вызывающий негибкость: https://github.com/scikit-learn/scikit-learn/blob/95119c13af77c76e150b753485c662b7c52a41a2/sklearn/preprocessing/_encoders.py#L338
Связанная проблема: https://github.com/scikit-learn/scikit-learn/issues/16511
Для использования if_binary
: scikit-learn требует, чтобы handle_unknown
равнялось error
. Это не очень хорошо сочетается с нашими параметрами top_n
, которые отбрасывают все, кроме первых N категорий, поскольку преобразуемые данные не будут знать, что делать с новыми категориями. Как отметила Бекка в https://github.com/alteryx/evalml/pull/830 , нам нужно установить для top_n
значение None, чтобы эти параметры работали.
Имея это в виду, может быть, лучше просто свернуть наш собственный импл 🤔
Самый полезный комментарий
Третий закон кодекса: не делайте == сравнений с поплавками