LoginSignup
1
2

More than 5 years have passed since last update.

kddcup2012で年齢性別予測!

Last updated at Posted at 2017-06-30

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%

上がった!

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

感想

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

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2