延滞する貸出を見分ける質問
決定木とは、「はい」か「いいえ」で答える質問をつなげて、答えを決める木の形のモデルです。作るときのいちばん大事な仕事は、どの質問から始めるかを選ぶことです。
大学の図書館は、返却が遅れそうな貸出を、前もって見分けたいと考えています。10件の記録を使い、質問を選ぶ方法を学びます。
このレッスンのゴール
メモ
エントロピーと情報利得を計算して、いちばんよい質問を選べる。
キーワード
| ことば | いみ | れい |
|---|---|---|
| 決定木 | 質問をつなげて答えを決める、木の形のモデル | |
| 特徴量 | 質問に使う、記録の項目 | 本の種類 |
| エントロピー | グループの答えの混ざり具合を表す数 | |
| 情報利得 | 質問で分けたあとに、混ざり具合が減った量 | |
| 葉 | 答えを出す、木の端のグループ |
学生が入力するセルは、このプレビューでは空白です。
決定木は質問でデータを分ける
決定木は、質問を1つずつ重ねて、記録を小さなグループに分けます。グループの中が同じ答えだけになれば、そこで止まります。
次の表は、図書館の10件の貸出記録です。「遅れた」が答えで、ほかの3列が質問に使う特徴量です。
例: 10件の貸出記録
| 本 | 期間 | 時期 | 遅れた |
|---|---|---|---|
| 参考書 | 長い | 試験期間 | はい |
| 参考書 | 短い | ふつう | いいえ |
| 小説 | 長い | ふつう | いいえ |
| 参考書 | 短い | 試験期間 | はい |
| 小説 | 短い | 試験期間 | はい |
| 小説 | 短い | ふつう | いいえ |
| 参考書 | 長い | 試験期間 | いいえ |
| 参考書 | 短い | 試験期間 | はい |
| 小説 | 長い | 試験期間 | はい |
| 小説 | 短い | ふつう | いいえ |
学生が入力するセルは、このプレビューでは空白です。
エントロピーは答えの混ざり具合
グループに「はい」と「いいえ」がどれだけ混ざっているかを、エントロピーで表します。半分ずつなら 1.0 で、いちばん混ざっています。全部同じ答えなら 0 です。
10件は、遅れた5件と遅れなかった5件なので、エントロピーは 1.0 です。
エントロピーを計算する
import math
def entropy(yes, no):
total = yes + no
result = 0.0
for count in [yes, no]:
if count > 0:
p = count / total
result = result - p * math.log2(p)
return round(result, 3)
print(entropy(5, 5))
print(entropy(5, 1))
print(entropy(0, 4))
# entropy(3, 3) と entropy(1, 5) も表示してください。
# 混ざり具合が大きいのはどちらですか。
例: 混ざり具合の目安
| グループ | 混ざり方 | エントロピー |
|---|---|---|
| 2件中1件がはい | 半々 | 1.00 |
| 3件中2件がはい | 少し偏る | 0.92 |
| 6件中5件がはい | かなり偏る | 0.65 |
| 5件中5件がはい | 全部同じ | 0.00 |
学生が入力するセルは、このプレビューでは空白です。
ラボの365日の混ざり具合
ラボの hires.csv では、365日のうち131日が busy(300台以上)です。busy と quiet が、どれだけ混ざっているでしょう。
次のコマンドは、p = 131 ÷ 365 から、そのエントロピーを求めます。答えは 0.94 で、かなり混ざっています。
365日のエントロピー
docker exec jupyter python3 -c 'import math; p = 131 / 365; print(round(-p * math.log2(p) - (1 - p) * math.log2(1 - p), 2))'ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
情報利得が大きい質問を選ぶ
質問で分けると、グループが2つ以上に分かれます。情報利得は、分ける前のエントロピーから、分けたあとの平均を引いた値です。平均には、件数に応じた重みをつけます。
情報利得がいちばん大きい質問が、いちばん答えをそろえる質問です。
例: 3つの質問の情報利得
| 質問 | 分けたグループ | 分けたあと | 情報利得 |
|---|---|---|---|
| 時期は試験期間か | 6件(はい5)と4件(はい0) | 0.39 | 0.61 |
| 本の種類は参考書か | 5件(はい3)と5件(はい2) | 0.97 | 0.03 |
| 期間は長いか | 4件(はい2)と6件(はい3) | 1.00 | 0.00 |
学生が入力するセルは、このプレビューでは空白です。
よくある間違い
注意
木を深くして、学習データを全部当てられるほどよいと考えがちです。実際は、深すぎる木は偶然の細かい違いまで覚えてしまい、新しいデータで外れます。次のレッスンのテストデータで確かめます。
ノートブックに入力: 日の分け方
import csv
import math
def entropy(busy, total):
result = 0.0
for count in [busy, total - busy]:
if count > 0:
result = result - count / total * math.log2(count / total)
return result
days = []
for row in csv.DictReader(open("hires.csv")):
days.append([row["weekday"] in ["Sat", "Sun"], row["weather"] == "rainy",
float(row["temp_c"]) >= 12, int(row["hires"]) >= 300])
names = ["weekend", "rainy", "warm"]
for n in range(3):
count = [0, 0]
busy = [0, 0]
for day in days:
side = 0 if day[n] else 1
count[side] = count[side] + 1
busy[side] = busy[side] + day[3]
after = 0.0
for side in range(2):
after = after + count[side] / len(days) * entropy(busy[side], count[side])
print(names[n], round(entropy(busy[0] + busy[1], len(days)) - after, 3))