Edited at

kddcup2012で年齢性別予測!

More than 1 year has passed since last update.


kddcup2012で年齢性別予測!

hivemallを使ってkddcup2012のデータセットで年齢と性別を予測するよー。


dataset

kddcup2012

training 1億5000万レコード

test 2000万レコード

本当はCTR予測のデータセットのため、

trainingにはimpressionとclickという項目があるがtestには入っていない。

その他共通の項目

displayurl

adid

advetiserid

queryid

keywordid

titleid

descriptionid

userid

depth : 1つのsessionで表示された広告の数(1~3)

position : その時の広告の位置(1~3)

なのでsession毎の広告毎に1レコードか??(英語..orz)

別途usersテーブルにuseridとage,genderが入っている。


Step1) データの前処理

useridや年齢性別が欠損しているデータも存在する。

CTR予測の場合は補完したりしてもよいが、今回は目的変数となるものなので欠損しているデータは省く。

また、データ量が多すぎてこのままだと一回の試行に時間がかかってしまうので、1割程度をランダムサンプリングして速い回転で試行錯誤したいと思う。

SELECT

*
FROM(
SELECT
*,
rand() as rnd
FROM(
SELECT
t.*,
u.age,
u.gender
FROM
training as t
LEFT OUTER JOIN users as u ON (t.userid=u.userid)
WHERE
t.userid <> 0
AND u.age is not NULL
AND u.gender is not NULL
AND u.gender <> 0
)
)
WHERE
rnd < 0.1 --random sampling

test用のデータも同様に作成。

次にそれらを特徴ベクトル化。

とりあえず1回目はdisplayurlだけ長いのでhash化、depthとpositionは量的変数と考えてやってみる。

useridを特徴で使ってしまうとリークしてoverfitしてしまうので使わないよう注意する。

SELECT

-- ageとgenderの組み合わせた目的変数labelを作った。
-- 単純に concat(age,"-",gender) as label でもよいか。
case
when age = 1 AND gender =1 then 0
when age = 1 AND gender =2 then 1
when age = 2 AND gender =1 then 2
when age = 2 AND gender =2 then 3
when age = 3 AND gender =1 then 4
when age = 3 AND gender =2 then 5
when age = 4 AND gender =1 then 6
when age = 4 AND gender =2 then 7
when age = 5 AND gender =1 then 8
when age = 5 AND gender =2 then 9
when age = 6 AND gender =1 then 10
when age = 6 AND gender =2 then 11
else 12
end as label,
add_bias(array_concat(
categorical_features(
array('displayurl','adid','advetiserid','queryid','keywordid','titleid','descriptionid'),
mhash(displayurl),adid,advetiserid,queryid,keywordid,titleid,descriptionid
),
quantitative_features(
array('depth','position'),
depth2,position2
)
)) as features
FROM(
SELECT
*,
-- depth,posistionを量的変数と考えてrescale
rescale(depth,1,3) as depth2,
rescale(position,1,3) as position2,
FROM
training2
) t

testデータも同様に。predict後の評価用にrowidをつけるのを忘れずに。


Step2) 学習

train_multiclass_scw2を使用。

SCW,AROW,SCW2と試したが、

SCWは途中計算でNaNが出てしまいうまく予測できず。

AROWは偏った結果となってしまった。

学習させる時にCLUSTER BY rand(1)で入力データのシャッフルを忘れずに。

SELECT

label,
feature,
argmin_kld(weight,covar) as weight
FROM (
SELECT
train_multiclass_scw2(features,label) as (label,feature,weight,covar)
FROM (
SELECT features,label
FROM training_featured
CLUSTER BY rand(1)
) t1
) t2
GROUP BY
label,feature


Step3) 予測

hivemallでは、LATERAL VIEW explode(features)で予測用データの特徴を展開。

modelとJOINして計算を行うことでpredictしている。

SELECT

rowid,
m.col0 as score,
m.col1 as label
FROM(
SELECT
rowid,
maxrow(score, label) as m
FROM(
SELECT
t.rowid,
m.label,
sum(m.weight * t.value) as score
FROM(
SELECT
rowid,
label,
extract_feature(feature) as feature,
extract_weight(feature) as value
FROM
testing_featured LATERAL VIEW explode(features) t as feature
) t
LEFT OUTER JOIN model m ON (t.feature = m.feature)
GROUP BY
t.rowid, m.label
) t1
GROUP BY rowid
) t2


Step4) 評価

年齢6パターン × 性別2パターンの合計12パターンで予測したものをaccuracy(予測データの正答率)で評価。

ちなみにRandom Predictionの結果は約9.33%だった。

SELECT

COUNT(1) / 2187606.0 --testデータのレコード数
FROM(
SELECT
p.rowid,
p.label,
t.label as actual
FROM
predicted as p
LEFT OUTER JOIN testing_featured as t ON (p.rowid=t.rowid)
)
WHERE
actual = label

結果
16.84%


試行錯誤


・impressionとclicksを特徴として使用する。

testデータにはimpressionとclicksという項目がなかったため、今回は特徴として使わなかった。

が、よく考えたらclickしたかどうかは年齢・性別にかなり関わってるのではないかと気づき、

impressionとclicksが分かるtrainingデータの中からtrainingとtestに分けて試行してみる。

impressionとclicksは量的変数なのでrescaleして行う。

結果
18.293%

ちょっと上がった。


・depthとpositionは質的変数と考えてみる。

結果
18.288%

ほとんど変わらない。


・clicksとimpressionではなくctr(clicks/impression)を特徴として使用する。

結果
18.23%


confusion_matrix

闇雲に特徴設計するのではなく、何が正しく分類できて何が誤分類が多いのか確認して特徴設計するべきである。

そのために confusion_matrixを書いてみる。

SELECT

actual,label,count(1) as cnt
FROM
predicted_data
GROUP BY
actual,label

でもよいが、12^2=144行眺めるのは微妙なので横持ちのテーブルに変換してみる。

SELECT

actual,
kv[0] as predicted_0,
kv[1] as predicted_1,
kv[2] as predicted_2,
kv[3] as predicted_3,
kv[4] as predicted_4,
kv[5] as predicted_5,
kv[6] as predicted_6,
kv[7] as predicted_7,
kv[8] as predicted_8,
kv[9] as predicted_9,
kv[10] as predicted_10,
kv[11] as predicted_11
FROM(
SELECT
actual,
to_map(label, cnt) as kv
FROM(
SELECT
actual,
label,
count(1) as cnt
FROM
predicted_data
GROUP BY
actual,label
) t2
GROUP BY
actual
) t3

actual\predicted
0
1
2
3
4
5
6
7
8
9
10
11

0
11970
3024
15377
5378
16236
9353
6579
5447
4907
3877
2094
1574

1
5662
3529
8231
5610
11602
10252
5379
5873
3991
3738
1852
1631

2
14382
5686
50906
17149
55483
24175
15326
11391
9846
7214
4532
3561

3
7676
4049
20630
21110
26339
22475
9162
9634
6206
5473
3012
2760

4
12502
9408
44532
19262
117500
57874
38691
30561
21449
14349
10273
8003

5
10186
8597
25022
18781
67380
81092
29364
34815
17163
15396
8910
7810

6
9392
7561
22290
12404
65436
40956
39037
25472
18495
12364
8440
6484

7
8220
6726
14753
11131
42794
46111
22832
33725
13887
13105
6832
6035

8
8579
5603
14869
8712
39902
26120
21543
16772
19195
9821
6545
4938

9
7441
4889
10603
7704
25930
27442
14390
17062
10170
12768
4851
4432

10
3344
2436
6617
3781
17667
11813
9684
7442
7378
4622
4921
2527

11
2275
1969
4009
3058
10683
10927
6177
7094
4482
4124
2414
3136

・・見にくい!

ということで合わせて、性別だけ・年齢だけのconfusion matrixを書いてみる。  

actual\predicted
male
female

male
775919
447114

female
480510
484063

actual\predicted
1~12
13~18
19~24
25~30
31~40
41~

1~12
24185
34596
47443
23278
16513
7151

13~18
31793
109795
128472
45513
28739
13865

19~24
40693
107597
323846
133431
68357
34996

25~30
31899
60578
195297
121066
57851
27791

31~40
26512
41888
119394
69767
51954
20766

41~
10024
17465
51090
30397
20606
12998

性別は特に女性をうまく分類できていないようだ。

年齢は特に19~24歳と判定されている率が高い。

actualを見ると、そもそも19~24歳のデータだけ多そう。  

age
test
train

1~12
153166
612219

13~18
358177
1428616

19~24
708920
2839792

25~30
494482
1980388

31~40
330281
1326127

41~
142580
570365

trainingのデータも、19~24歳だけ特に多いと気づく。

そこにしかない特定の特徴について重みが学習されないのであまりよくはないが、

19~24歳だけ少しundersamplingしてみたらどうなるだろうか?

となるわけである。

数字だけ眺めても気づきを得にくいので

pythonでグラフ化したりするとなおよし。


特徴の組み合わせ

場合によっては、2次か3次の組み合わせによる特徴量を作ってもよいかもしれない。

hivemallでは、polynomial_features関数を使うことで簡単に2次、3次の組み合わせ特徴量を作ることができる。

SELECT

label,
polynomial_features(features, 2, true, false)
FROM
training_featured

上のqueryだと全特徴量の2次の組み合わせとなる。

それだと学習や予測に時間もかかってしまうし、1回しか訓練事例に現れないような逆効果となる組み合わせが出てくる可能性もある。

なので、組み合わせることで目的変数と関係性のありそうな特徴量を考えてpolynomial_featuresする必要がある。

今回だと、、queryid,adid,clicks の組み合わせは何かしら年齢性別に関わっていそうだと考える。

clickの回数ではなく、"clickした"or"clickしなかった"の"1"or"0"に変換したclick01という変数を使って各変数を組み合わせてみる。

SELECT

label,
add_bias(array_concat(
categorical_features(
array('displayurl','advetiserid','keywordid','titleid','descriptionid'),
mhash(displayurl),advetiserid,keywordid,titleid,descriptionid
),
quantitative_features(
array('depth','position','ctr'),
depth2,position2,ctr
),
polynomial_features(
categorical_features(
array('adid','queryid','click01'),
adid,queryid,click01
),
2, true, false
)
)) as features
FROM
training_data

結果
18.76%

上がった!


これくらいが限界か、、?


感想

特徴量設計が一番難しくて、重要で、面白いことな気がする。