Embeddingレイヤのための前処理

RenomのEmbeddingレイヤに必要な前処理の説明

ニューラルネットワークを用いて任意の連続関数を近似できることはよく知られています。一方、勾配を計算することが困難であるため、不連続関数を近似することは適切ではないと言われています。したがって、ニューラルネットワークは、データが構造化されている分野では、データの特徴量がしばしばカテゴリ変数を含むため、機械学習の他の方法よりも有効性が低下する可能性があります。カテゴリ変数を扱うには、通常、対応するカテゴリを示すベクトルに変換するワンホットエンコーディングを使用します。そのワンホットエンコーディングの後、入​​力の次元は大きくなることがあります。しかし、Entity embeddingは、このような状況におけるニューラルネットワークを含む機械学習のトレーニングに効果的であることが最近明らかになりました[1]。Entity embeddingは、PCAのようにカテゴリ変数の次元を減らし、ワンホットベクトルを変換します。これにより、Entity embeddingは、ニューラルネットワークを含むいくつかの機械学習法に対してカテゴリ変数を扱いやすくさせ、その性能を改善させます。

このチュートリアルでは、RenomでEmbeddingレイヤを使用するためのデータを前処理する方法を紹介します。 Renom上のカテゴリ変数は、0から始まり1ずつ増える非負の整数として定義されます。これらの変数をRenomのEmbeddingレイヤに入力する必要があります。 しかし、多くのデータによくあるように、カテゴリ変数の型は整数ではなく、文字列やdatetimeなどです。したがって、これらの変数をRenomのEmbeddingレイヤに適したカテゴリ変数に変換する必要があります。

Required Libraries

  • numpy 1.13.3
  • pandas 0.21.0
  • stats 0.1.2a
In [1]:
from __future__ import division, print_function
import numpy as np
import pandas as pd
import stats

データの読み込みと前処理

このチュートリアルで使用するデータは、「 http://archive.ics.uci.edu/ml/datasets/online+retail 」からダウンロードできます。 このデータには、2010年12月から2011年12月までのとあるオンライン小売店舗の各顧客のすべての取引が含まれています。データには、購入する取引コード、商品名とコード、日時、購入数、単価、顧客ID 、顧客の国が記録されています。

ここでは、埋め込みレイヤーを使用するニューラルネットワークを使用して、このストアで1日あたりの各顧客の支出を予測する問題について考えます。 顧客のIDと顧客の国だけでなく、年月の日付もカテゴリ変数だとみなしていることに注意してください。

In [2]:
excel = pd.ExcelFile("Online Retail.xlsx")
df = excel.parse(excel.sheet_names[0])
df.head()
Out[2]:
InvoiceNo StockCode Description Quantity InvoiceDate UnitPrice CustomerID Country
0 536365 85123A WHITE HANGING HEART T-LIGHT HOLDER 6 2010-12-01 08:26:00 2.55 17850.0 United Kingdom
1 536365 71053 WHITE METAL LANTERN 6 2010-12-01 08:26:00 3.39 17850.0 United Kingdom
2 536365 84406B CREAM CUPID HEARTS COAT HANGER 8 2010-12-01 08:26:00 2.75 17850.0 United Kingdom
3 536365 84029G KNITTED UNION FLAG HOT WATER BOTTLE 6 2010-12-01 08:26:00 3.39 17850.0 United Kingdom
4 536365 84029E RED WOOLLY HOTTIE WHITE HEART. 6 2010-12-01 08:26:00 3.39 17850.0 United Kingdom

分析に必要な列のみを選択し、「expenditure(数量*単価)」の列を計算します。

In [3]:
df = df.iloc[:, 3:8]
df = df.assign(expenditure = df.Quantity * df.UnitPrice)
df.head()
Out[3]:
Quantity InvoiceDate UnitPrice CustomerID Country expenditure
0 6 2010-12-01 08:26:00 2.55 17850.0 United Kingdom 15.30
1 6 2010-12-01 08:26:00 3.39 17850.0 United Kingdom 20.34
2 8 2010-12-01 08:26:00 2.75 17850.0 United Kingdom 22.00
3 6 2010-12-01 08:26:00 3.39 17850.0 United Kingdom 20.34
4 6 2010-12-01 08:26:00 3.39 17850.0 United Kingdom 20.34

“InvoiceDate”列の型はdatetimeなので、この列から “Day of week”、 “Year”、 “Month”、 “Day”を抽出する必要があります。 さらに、各列が0で始まるように、これらの列から1を引いています。

In [4]:
df = df.assign(dow = df.InvoiceDate.dt.dayofweek.astype(np.int),
                 year = df.InvoiceDate.dt.strftime('%Y').astype(np.int),
                 month = df.InvoiceDate.dt.strftime('%m').astype(np.int),
                 day = df.InvoiceDate.dt.strftime('%d').astype(np.int))
df = df.assign(year = df.year - np.min(df.year), month = df.month - 1,
                 day = df.day - 1)
#Day of Week : Monday=0, Tuesday=1, ..., Friday=4, Saturday=5, Sunday=6
#              But, we will additionally normalize this column later because Saturday is missing.
#Year : 2010=0, 2011=1
#Month : January=0, February=1, ..., November=10, December=11
#Date : 1st=0, 2nd=1, ..., 30th=29, 31st=30
df.head()
Out[4]:
Quantity InvoiceDate UnitPrice CustomerID Country expenditure day dow month year
0 6 2010-12-01 08:26:00 2.55 17850.0 United Kingdom 15.30 0 2 11 0
1 6 2010-12-01 08:26:00 3.39 17850.0 United Kingdom 20.34 0 2 11 0
2 8 2010-12-01 08:26:00 2.75 17850.0 United Kingdom 22.00 0 2 11 0
3 6 2010-12-01 08:26:00 3.39 17850.0 United Kingdom 20.34 0 2 11 0
4 6 2010-12-01 08:26:00 3.39 17850.0 United Kingdom 20.34 0 2 11 0

各取引を顧客ごとに日毎に集計します。 “CustomerID”にNaNがあるため、まず “CustomerID”を文字列型に変換します。

In [5]:
df["CustomerID"] = df["CustomerID"].astype(str)
df = df.groupby(["CustomerID", "year", "month", "day", "dow"], as_index=False).agg({"expenditure":"sum", "Country":lambda x: stats.mode(x)})
df.head()
Out[5]:
CustomerID year month day dow expenditure Country
0 12346.0 1 0 17 1 0.00 United Kingdom
1 12347.0 0 11 6 1 711.79 Iceland
2 12347.0 1 0 25 2 475.39 Iceland
3 12347.0 1 3 6 3 636.25 Iceland
4 12347.0 1 5 8 3 382.52 Iceland

“Country”列を非負の整数に変換します。 Pandasの “factorize”関数を使うのが便利です。 “factorize”関数は、変換されたPandas.Seriesと、国名を国の番号で索引付けしたリストを返します。 “factorize”関数を使用する前にデータフレームを”Country”でソートすると、国の数字がより直感的になります(数値が大きいほどアルファベット順が後になります)。

In [6]:
df = df.sort_values("Country")
df["Country"], list_country = pd.factorize(df["Country"])
df.head()
Out[6]:
CustomerID year month day dow expenditure Country
173 12415.0 1 0 5 3 7011.38 0
107 12388.0 1 8 24 6 825.92 0
108 12388.0 1 10 23 3 286.40 0
176 12415.0 1 2 2 3 16558.14 0
221 12424.0 1 5 29 3 1760.96 0
In [7]:
for index, name in zip(set(df["Country"]), list_country):
    print(index, name)
0 Australia
1 Austria
2 Bahrain
3 Belgium
4 Brazil
5 Canada
6 Channel Islands
7 Cyprus
8 Czech Republic
9 Denmark
10 EIRE
11 European Community
12 Finland
13 France
14 Germany
15 Greece
16 Hong Kong
17 Iceland
18 Israel
19 Italy
20 Japan
21 Lebanon
22 Lithuania
23 Malta
24 Netherlands
25 Norway
26 Poland
27 Portugal
28 RSA
29 Saudi Arabia
30 Singapore
31 Spain
32 Sweden
33 Switzerland
34 USA
35 United Arab Emirates
36 United Kingdom
37 Unspecified
In [8]:
df = df.sort_values("CustomerID")
df["CustomerID"], list_ID = pd.factorize(df["CustomerID"])
df.head()
Out[8]:
CustomerID year month day dow expenditure Country
0 0 1 0 17 1 0.00 36
4 1 1 5 8 3 382.52 17
5 1 1 7 1 1 584.91 17
7 1 1 11 6 2 224.82 17
1 1 0 11 6 1 711.79 17

このデータには土曜日の取引が欠落しているため、 “factorize”を使用して “dow”列を変換し、列が0から1ずつ増加するように変換します。

In [9]:
df = df.sort_values("dow")
df["dow"], list_dow = pd.factorize(df["dow"])
#Before : Monday=0, Tuesday=1, ..., Friday=4, Saturday=5, Sunday=6
#After : Monday=0, Tuesday=1, ..., Friday=4, Saturday=Missing, Sunday=5
for index, name in zip(set(df["dow"]), list_dow):
    print(index, name)
0 0
1 1
2 2
3 3
4 4
5 6
In [10]:
df.head()
Out[10]:
CustomerID year month day dow expenditure Country
15726 3555 1 1 27 0 158.68 36
5662 1241 1 4 15 0 1133.33 10
5654 1237 1 2 6 0 328.80 36
5652 1236 1 4 22 0 -9.68 36
5648 1235 1 0 30 0 304.44 36

最終的に、RenomにおいてEmbeddingレイヤを持つニューラルネットワークに使用するためのデータフレームを手に入れました。

参考文献

[1] Cheng Guo and Felix Berkhahn. Entity Embeddings of Categorical Variables. CoRR, 2016. https://arxiv.org/abs/1604.06737