Evalml: One Hot Encoder: Jatuhkan satu fitur redundan secara default untuk fitur dengan dua kategori

Dibuat pada 5 Mar 2021  ·  14Komentar  ·  Sumber: alteryx/evalml

Satu pembuat enkode panas kami membuat fitur untuk setiap tingkat fitur kategoris asli:

from evalml.pipelines import OneHotEncoder
import pandas as pd
df = pd.DataFrame({"category": ["a", "b"], "number": [4,5 ]})
OneHotEncoder().fit_transform(df).to_dataframe()

image

Kolom category_a dan category_b benar-benar collinear, yang membuat satu kolom menjadi redundan. Ini bisa memiliki efek buruk pada pemasangan estimator. Saya pikir kita harus menjatuhkan satu secara default.

FYI @rpeck

enhancement

Komentar yang paling membantu

Hukum Kode Ketiga: Jangan Membuat == Perbandingan Dengan Floats

Semua 14 komentar

% kita harus membuang kolom kasus negatif.

Jika kita melakukan OHE sendiri dulu, maka sklearn semoga tidak mengembangkannya. Seperti yang dikatakan Freddy, Anda dapat menganggap ini sebagai menghasilkan dua kolom yang memiliki kolinearitas sempurna.

Ada dua masalah yang saya lihat dengan memperluas biner menjadi dua kolom daripada satu:

  1. Seperti bentuk kolinearitas fitur lainnya, ini mengacaukan banyak hal dalam interpretasi, karena efek dari satu kolom sumber asli dibagi di antara dua kolom OHE. Rollup SHAP baru Freddy mengatasi hal ini, tentu saja, tetapi hal-hal seperti Kepentingan Fitur dan plot Ketergantungan Sebagian masih akan memiliki masalah.
  2. Model pohon seperti Random Forest dan GBM secara acak mengambil sampel fitur inputnya. Kolom sumber dalam hal ini akan diambil sampelnya secara acak dua kali lebih sering dari yang seharusnya, sehingga dapat memberikan dampak yang sangat besar pada model.

@freddyaboulton T: Kerangka data di atas untuk kolom OHE menunjukkannya sebagai pelampung. Apakah ini benar?

@rpeck Ya!

@freddyaboulton Ada apa? Itu aneh. Saya belum pernah melihat apa pun selain boolean sejati, atau bilangan bulat 0/1. Saya bertanya-tanya bagaimana model pohon benar-benar menangani ini. Ini memiliki bau yang tidak enak bagi saya.

Hukum Kode Ketiga: Jangan Membuat == Perbandingan Dengan Floats

(ok, kecuali dengan Math.NaN )

Hmm, saya pikir kami melakukan ini!

Saya setuju kita harus. Saya pikir itu hanya bendera yang harus kami atur di impl.

@dsherry @freddyaboulton Sepertinya kami memiliki dukungan untuk itu melalui parameter drop tetapi hanya mempertimbangkan input pengguna dan tidak digunakan oleh impl kami sehingga masalah ini hanya melacak pengaturan default untuk drop untuk sesuatu selain Tidak Ada?

https://github.com/alteryx/evalml/blob/91775ffc26c47205adc0fb255832d828ead6e7c9/evalml/pipelines/components/transformers/encoders/onehot_encoder.py#L28

https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.OneHotEncoder.html

Kita bisa menggunakan first atau if_binary , tidak yakin apa panggilan yang tepat.

@ angela97lin Anda benar bahwa mengubah nilai default sudah cukup! Saya pikir first adalah cara untuk pergi karena kita harus menghindari fitur collinear sempurna bahkan ketika jumlah kategori > 2. Bagaimana menurut Anda @rpeck ?

Sedang membaca ini sedikit dan menemukan tautan ini: https://inmachineswetrust.com/posts/drop-first-columns/

Takeaways utama:

  • Menjatuhkan kolom hanya diperlukan saat membuat model OLS tanpa regularisasi (saya yakin regresi linier termasuk dalam kategori ini: https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LinearRegression.html, https:// scikit-learn.org/stable/modules/linear_model.html#ordinary-least-squares)
  • Menjatuhkan kolom yang disandikan satu-panas mengubah parameter dan prediksi model regresi linier, yang memengaruhi model yang ditampilkan. Namun, sulit bagi saya untuk menentukan apakah ini menjadi lebih baik atau tidak.

Komentar pertama RE @rpeck : "Seperti bentuk kolinearitas fitur lainnya, ini mengacaukan banyak hal dalam interpretasi, karena efek dari satu kolom sumber asli dibagi di antara dua kolom OHE. Rollup SHAP baru Freddy mengatasi hal ini, jelas, tetapi hal-hal seperti Kepentingan Fitur dan plot Ketergantungan Sebagian masih akan bermasalah."

Ini masuk akal untuk kasus biner, tetapi dalam kasus di mana kami memiliki beberapa kategori, menjatuhkan satu col akan tetap memiliki masalah ini.

Mungkin kita seharusnya tidak melakukan ini secara default, tetapi harus memperbarui make_pipeline untuk membuat OHE dengan first sebagai parameter jika penaksirnya adalah regresi linier?

Sayangnya, saya tidak memiliki pemahaman yang kuat tentang matematika yang mendasari untuk membuat penilaian, jadi saya ingin mendengar pendapat Anda, @freddyaboulton @rpeck @dsherry

Pasca-diskusi dengan @freddyaboulton @rpeck @dsherry @chukarsten @jeremyliweishih

  • Kami hanya akan melakukan ini untuk kasus biner.
  • "Nice-to-have" yang digunakan, dalam kasus biner, adalah kelas minoritas, tetapi sebaliknya hanya memilih salah satu dari dua kategori sudah cukup.

@ angela97lin terdengar baik perilaku default RE. Hal lain yang bagus untuk dimiliki: kemampuan untuk mengganti perilaku default itu melalui params komponen

@dsherry Jika saya mengerti dengan benar, karena kami memperbarui nilai default drop (parameter), pengguna akan memiliki kemampuan untuk menimpa ini dengan mengatur parameter komponen secara manual?

Gali sekitar untuk melihat apa yang diperlukan untuk mengimplementasikan ini. Secara khusus, saya ingin tahu betapa sulitnya untuk selalu menghapus kelas minoritas dalam kasus biner.

Hasil dari penggalian tersebut adalah:

  • Dengan scikit-learn, cukup sulit untuk memilih kategori mana yang akan dihapus. Dari dokumentasi, ini tampaknya layak melalui opsi larik untuk parameter drop (https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.OneHotEncoder.html). Namun, setelah mencobanya, diperlukan nilai indeks yang ditentukan untuk setiap kolom. Oleh karena itu, berikut ini, yang mencoba menghapus kategori yang ditentukan pada indeks 0 untuk kolom 0 dan tidak ada nilai lain untuk kesalahan kolom 1 dan 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

Saya percaya ini juga setengah dari apa yang ditunjukkan oleh masalah ini: https://github.com/scikit-learn/scikit-learn/issues/16511

Alternatif yang dapat kita lakukan untuk mendukung ini adalah secara manual melacak kolom mana dan nilai apa yang ingin kita jatuhkan selama pemasangan. Berikan data ke scikit-learn. Kemudian, pangkas kolom yang kami simpan dan tentukan yang ingin kami jatuhkan. Namun, ini memerlukan beberapa penanganan logika untuk menentukan yang asli (fitur, nilai) dari nama kolom yang diubah. (Kami memiliki logika ini di get_feature_names tetapi itu membantu kami menghubungkan nama kolom dengan asumsi tidak ada yang harus dijatuhkan ...)

Semua ini untuk mengatakan, mungkin hanya menggunakan default scikit-learn if_binary sudah cukup untuk saat ini, dan kita dapat mengajukan masalah terpisah untuk selalu menggunakan kelas minoritas. Jujur juga mendukung pindah dari implementasi OHE scikit-learn mengingat betapa kami harus mengatasinya.

Sumber daya yang berguna:
Dokumen OHE: https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.OneHotEncoder.html
Kode dalam scikit-learn menyebabkan ketidakfleksibelan: https://github.com/scikit-learn/scikit-learn/blob/95119c13af77c76e150b753485c662b7c52a41a2/sklearn/preprocessing/_encoders.py#L338
Masalah terkait: https://github.com/scikit-learn/scikit-learn/issues/16511


Untuk menggunakan if_binary : scikit-learn mensyaratkan bahwa handle_unknown adalah error . Ini tidak cocok dengan parameter top_n , yang menghapus semuanya kecuali kategori N teratas karena data yang akan diubah tidak akan tahu apa yang harus dilakukan dengan kategori baru. Seperti yang dicatat Becca di https://github.com/alteryx/evalml/pull/830 , kita harus menyetel top_n ke None agar parameter ini berfungsi.

Dengan pemikiran ini, mungkin yang terbaik adalah menggulung impl kita sendiri

Apakah halaman ini membantu?
0 / 5 - 0 peringkat