01.4.1 高校生向け
One-Hot Encoding(高校生向け)
こんな場面を想像してみよう #
クラスの 5 人について、「部活」と「1 日の勉強時間」から「定期テストの点数」を予想するモデルを作りたいとします。
| 名前 | 部活 | 勉強時間(時間) | 点数 |
|---|---|---|---|
| A | サッカー | 1.0 | 62 |
| B | 吹奏楽 | 2.0 | 75 |
| C | 美術 | 1.5 | 70 |
| D | サッカー | 2.5 | 80 |
| E | 吹奏楽 | 0.5 | 58 |
勉強時間は数なので、そのまま直線の式 \(y = ax + b\) に入れられます。ところが「部活」は言葉なので、何とかして数に直さなければなりません。
一番手軽なのは「サッカー=1、吹奏楽=2、美術=3」と番号を振る方法です。でも、こう直してから直線の式に入れると、コンピュータは **「美術はサッカーの 3 倍」「吹奏楽はサッカーと美術のちょうど中間」**という関係があるものとして計算します。サッカー・吹奏楽・美術という部活の名前に、そんな大小も順序もありません。
そこで One-Hot Encoding です。「部活」の 1 列を、カテゴリの数だけの Yes/No の列に分けます。
| 名前 | 部活=サッカー | 部活=吹奏楽 | 部活=美術 |
|---|---|---|---|
| A | 1 | 0 | 0 |
| B | 0 | 1 | 0 |
| C | 0 | 0 | 1 |
| D | 1 | 0 | 0 |
| E | 0 | 1 | 0 |
変換前は「言葉が 1 列」。変換後は「0 と 1 だけの 3 列」で、どの行も 1 がちょうど 1 つだけ点灯しています。
仕組み:カテゴリごとに Yes/No の列を立てる #
まず、カテゴリの種類を数えます。部活の列に出てくる言葉は「サッカー・吹奏楽・美術」の 3 種類で、これが新しい列の本数になります。次に、種類ごとに列を 1 本ずつ作ります。「部活=サッカー?」「部活=吹奏楽?」「部活=美術?」という 3 つの質問です。そのうえで各行を見て、当てはまる列だけを 1、ほかを 0 にします。A さんはサッカーなので (1, 0, 0)、B さんは (0, 1, 0) となり、表は横に広がって言葉の列は消えます。最後に数の列と並べてモデルに渡します。直線の式でいえば \(y = a_1 \times(\text{サッカー}) + a_2 \times(\text{吹奏楽}) + a_3 \times(\text{美術}) + a_4 \times(\text{勉強時間}) + b\) の形になり、\(a_1\) は「サッカー部であることが点数を何点動かすか」と読めます。
次の短い Python は、5 人の部活のリストを One-Hot に直して 1 行ずつ表示します。ライブラリを使わず、「種類を数える」「当てはまれば 1」の 2 手順だけで書けることを確かめるコードです。
| |
番号を振る方法では「サッカーと美術の距離は 2、サッカーと吹奏楽の距離は 1」と差がつきましたが、One-Hot ではどの 2 つの部活も同じだけ離れています。「ちがう」という事実だけを数にして、「どれだけちがう」は持ち込まない。これが One-Hot の設計です。
気をつけたいこと #
最初の注意点は、カテゴリの種類が多いと One-Hot の列が爆発することです。都道府県なら 47 列、市区町村なら 1,700 列以上になり、列が増えるほど計算が重くなるので、種類が多いときは「出現頻度に置き換える」など別の数値化を考えます。二つ目は、順序があるカテゴリには向かないことがある点です。「小・中・大」のように本当に順序がある言葉は、1・2・3 と振ったほうが情報を活かせることもあり、One-Hot は順序をわざと捨てる変換だと覚えておきます。三つ目は、学習時になかったカテゴリが来ると困ることです。「書道部」が新しく現れたとき対応する列がないので、実務では「未知のカテゴリは全部 0 にする」などの決まりを先に作っておきます。
やってみよう #
最初の課題は、自分のクラスの 10 人について「好きな教科」を聞き、紙の表で One-Hot Encoding してみることです。列の本数が教科の種類と同じになること、各行の 1 の個数がちょうど 1 になることを確かめましょう。二つ目は、身の回りの「言葉で表されるデータ」を 5 つ探し(血液型・曜日・天気など)、それぞれ One-Hot にすると何列になるか数えてみることです。血液型は 4 列、曜日は 7 列になりますが、その中に順序のあるものはどれか考えてみてください。三つ目は、「曜日」を月=1〜日=7 と番号で表したとき、どんなおかしな結論が出そうか考える課題です。日曜と月曜は隣なのに距離が 6 になる、というところがヒントです。
つぎに読む #
scikit-learn の OneHotEncoder と ColumnTransformer を使った実装、列が増えすぎたときの対策は 標準版の One-Hot Encoding で扱っています。逆に「数を言葉(区間)に直す」前処理は 数値データのビニング(高校生向け) へ進んでください。数の列の単位をそろえる前処理は StandardScaler(高校生向け) で説明しており、One-Hot した 0/1 の列とは別の扱いになる理由も見えてきます。