{"cells": [{"metadata": {}, "source": ["Spent a bit of time testing out a GRU model, but ran out of time... just posting what I have so far, will run, but not on Kaggle."], "cell_type": "markdown"}, {"metadata": {"_cell_guid": "2f31f0c2-f954-4094-89b2-78fe420b0373", "_uuid": "cded261fe3b91137c29627ac8a2814d3fa0d0c25"}, "source": ["# This Python 3 environment comes with many helpful analytics libraries installed\n", "# It is defined by the kaggle/python docker image: https://github.com/kaggle/docker-python\n", "# For example, here's several helpful packages to load in \n", "\n", "import numpy as np # linear algebra\n", "import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n", "\n", "import pandas as pd\n", "import numpy as np\n", "from torch.utils.data import Dataset, DataLoader #doesn't work on kaggle\n", "import torch\n", "import torch.nn as nn\n", "from torch.autograd import Variable\n", "import time, math\n", "import random\n", "\n", "# Input data files are available in the \"../input/\" directory.\n", "# For example, running this (by clicking run or pressing Shift+Enter) will list the files in the input directory\n", "\n", "from subprocess import check_output\n", "print(check_output([\"ls\", \"../input\"]).decode(\"utf8\"))\n", "\n", "# Any results you write to the current directory are saved as output."], "cell_type": "code", "execution_count": null, "outputs": []}, {"metadata": {}, "source": ["# DataLoader"], "cell_type": "markdown"}, {"metadata": {}, "source": ["one_year = pd.date_range(pd.to_datetime('2016-08-15'),pd.to_datetime('2017-08-15'))\n", "cols = ['date','unit_sales','onpromotion']\n", "df_to_copy = pd.DataFrame(index=one_year)\n", "df_to_copy['day'] = df_to_copy.index.day\n", "df_to_copy['month'] = df_to_copy.index.month\n", "\n", "class GroceryDataset(Dataset):\n", "    \n", "    #Create a fixed one-year length time series of sales data\n", "    def groups_to_series(self, x):\n", "        global df_to_copy\n", "        df = df_to_copy.copy()\n", "        df = df.merge(x[cols], right_on='date', left_index=True, how='left')\n", "        df = df.fillna(0)\n", "        df = df.as_matrix()\n", "        return df\n", "    def __init__(self):\n", "        \n", "        \n", "        #load different data\n", "        self.train = pd.read_csv('data/grocery kaggle comp/train 2.csv')\n", "        self.train.date = pd.to_datetime(self.train.date)\n", "        self.train = self.train[self.train.date>pd.to_datetime('2016-08-14')] #I used one year of data for training\n", "        self.train = self.train.drop('id',axis=1)\n", "        self.train.onpromotion = self.train.onpromotion.astype(int)\n", "        self.train.unit_sales = self.train.unit_sales.apply(abs)\n", "        \n", "        #item, store combinations\n", "        self.group = list(self.train.groupby(['item_nbr','store_nbr']).groups.keys())\n", "        \n", "\n", "        \n", "        self.item_dataset = pd.read_csv('data/grocery kaggle comp/items.csv').set_index('item_nbr')\n", "        self.store_dataset = pd.read_csv('data/grocery kaggle comp/stores.csv').set_index('store_nbr')\n", "        self.len = len(self.group)\n", "        \n", "        self.item_set = list(set(self.train['item_nbr']))\n", "        self.store_set = list(set(self.train['store_nbr']))\n", "        self.fam_set = list(set(self.item_dataset['family']))\n", "        self.cluster_set = list(set(self.store_dataset['cluster']))\n", "        \n", "        self.item_set_len = len(self.item_set)\n", "        self.store_set_len = len(self.store_set)\n", "        self.fam_set_len = len(self.fam_set)\n", "        self.cluster_set_len = len(self.cluster_set)\n", "\n", "        #onvert values to categories\n", "        self.item_to_ix = {var: i for i, var in enumerate(self.item_set)}\n", "        self.store_to_ix = {var: i for i, var in enumerate(self.store_set)}\n", "        self.fam_to_ix = {var: i for i, var in enumerate(self.fam_set)}\n", "        self.cluster_to_ix = {var: i for i, var in enumerate(self.cluster_set)}\n", "    \n", "        \n", "    def __getitem__(self, ind):\n", "                \n", "        #for a particular item and store, create the one years data series\n", "        item, store = self.group[ind]\n", "        \n", "        data = self.train[(self.train.store_nbr==store)&(self.train.item_nbr==item)].groupby(['store_nbr','item_nbr']).apply(self.groups_to_series)\n", "\n", "        \n", "        fam = self.item_dataset.loc[item].family\n", "        cluster = self.store_dataset.loc[store].cluster\n", "        \n", "        #categorize other data\n", "        item_tensor = torch.LongTensor([[self.item_to_ix[item]]])\n", "        \n", "        store_tensor = torch.LongTensor([[self.store_to_ix[store]]])\n", "        \n", "        fam_tensor = torch.LongTensor([[self.fam_to_ix[fam]]])\n", "        \n", "        cluster_tensor = torch.LongTensor([[self.cluster_to_ix[cluster]]])\n", "                                \n", "        unit_sales = torch.from_numpy(np.array([x[3:] for x in data.iloc[0][:-1]]).astype(float)).type(torch.FloatTensor)\n", "        \n", "        day = torch.from_numpy(np.array([x[0] for x in data.iloc[0][:-1]])).type(torch.LongTensor)\n", "        \n", "        month = torch.from_numpy(np.array([x[1] for x in data.iloc[0][:-1]])).type(torch.LongTensor)\n", "        \n", "        y = torch.from_numpy(np.array([x[3] for x in data.iloc[0][1:]])).type(torch.FloatTensor)\n", "        \n", "        return item_tensor, store_tensor,fam_tensor, cluster_tensor, unit_sales, day, month, y\n", "    def __len__(self):\n", "        return self.len"], "cell_type": "code", "execution_count": null, "outputs": []}, {"metadata": {}, "source": ["dataset = GroceryDataset()\n", "train_loader = DataLoader(dataset= dataset, batch_size=32, shuffle=False, num_workers=0)"], "cell_type": "code", "execution_count": null, "outputs": []}, {"metadata": {}, "source": ["# GRU Model"], "cell_type": "markdown"}, {"metadata": {}, "source": ["class RNN(nn.Module):\n", "    def __init__(self, bs,input_size, item_set_size ,store_set_size,fam_set_size,\n", "                 cluster_set_size, hidden_size, output_size, n_layers=1):\n", "        super(RNN, self).__init__()\n", "        self.input_size = input_size\n", "        self.hidden_size = hidden_size\n", "        self.output_size = output_size\n", "        self.n_layers = n_layers\n", "        self.batch_size = bs\n", "        \n", "        self.item_encoder = nn.Embedding(item_set_size, 40)\n", "        self.store_encoder = nn.Embedding(store_set_size, 5)\n", "        self.fam_encoder = nn.Embedding(fam_set_size, 5)\n", "        self.cluster_encoder = nn.Embedding(cluster_set_size, 5)  \n", "        self.day_encoder = nn.Embedding(32, 10)\n", "        self.month_encoder = nn.Embedding(13, 3)\n", "        \n", "        self.gru = nn.GRU(input_size+40+5+5+5+10+3, hidden_size, n_layers, batch_first=True, dropout=0.2)\n", "        self.regressor = nn.Linear(hidden_size, output_size)\n", "    \n", "    def forward(self, input, item_tensor, store_tensor,fam_tensor, cluster_tensor, day, month, hidden):\n", "\n", "        embedding = torch.cat((self.item_encoder(item_tensor.squeeze()), \n", "                                self.day_encoder(day),\n", "                                self.month_encoder(month),\n", "                                self.store_encoder(store_tensor.squeeze()), self.fam_encoder(fam_tensor.squeeze()),\n", "                                self.cluster_encoder(cluster_tensor.squeeze())),1)\n", "        \n", "        \n", "        input = torch.cat((input, embedding),1).unsqueeze(1)\n", "\n", "        output, hidden = self.gru(input,hidden)\n", "        output = self.regressor(output)\n", "        return output, hidden\n", "\n", "    def init_hidden(self):\n", "        return Variable(torch.zeros(self.n_layers,self.batch_size,self.hidden_size))"], "cell_type": "code", "execution_count": null, "outputs": []}, {"metadata": {}, "source": ["n_epochs = 100\n", "hidden_size = 64\n", "output_size = 1\n", "n_layers = 2\n", "lr = 0.005\n", "batch_size=32\n", "\n", "decoder = RNN(batch_size,2, dataset.item_set_len, dataset.store_set_len, dataset.fam_set_len,dataset.cluster_set_len, hidden_size , output_size, n_layers)\n", "decoder_optimizer = torch.optim.Adam(decoder.parameters(), lr=lr)\n", "criterion = nn.MSELoss() #Not using proper loss function"], "cell_type": "code", "execution_count": null, "outputs": []}, {"metadata": {}, "source": ["# Train"], "cell_type": "markdown"}, {"metadata": {"collapsed": true}, "source": ["loss_avg = 0\n", "\n", "\n", "def time_since(since):\n", "    s = time.time() - since\n", "    m = math.floor(s / 60)\n", "    s -= m * 60\n", "    return '%dm %ds' % (m, s)\n", "\n", "start = time.time()\n", "for i, data in enumerate(train_loader, 0):\n", "    \n", "    item_tensor, store_tensor,fam_tensor, cluster_tensor, unit_sales, day, month, y = data\n", "    item = Variable(item_tensor, requires_grad=False)\n", "    store = Variable(store_tensor,requires_grad=False)\n", "    fam = Variable(fam_tensor,requires_grad=False)\n", "    cluster = Variable(cluster_tensor,requires_grad=False) \n", "    d = Variable(day,requires_grad=False) #day of week\n", "    m = Variable(month,requires_grad=False) # month of year\n", "    row = Variable(unit_sales,requires_grad=False) #sales for that day, and if on promo\n", "    \n", "    y = Variable(y,requires_grad=False)\n", "    \n", "    hidden = decoder.init_hidden()\n", "    decoder.zero_grad()\n", "    \n", "    outputs = Variable(torch.zeros((len(row[0]), row.size()[0]))) #series to collect ouputs\n", "    loss = 0\n", "    force = random.random() < 0.5\n", "    \n", "    #teacher-forcing or not\n", "    if force:\n", "        for c in range(len(row[0])):\n", "            outputs[c], hidden = decoder(row[:,c],item, store,fam , cluster, d[:,c],m[:,c] , hidden)\n", "    else:\n", "         for c in range(len(row[0])):\n", "            if c>0:\n", "                outputs[c], hidden = decoder(torch.cat((outputs[c-1].unsqueeze(1),row[:,c,1].unsqueeze(1)),1),item, store,fam , cluster, d[:,c],m[:,c] , hidden)   \n", "            else:\n", "                outputs[c], hidden = decoder(row[:,c],item, store,fam , cluster, d[:,c],m[:,c] , hidden)\n", "\n", "    loss += criterion(outputs[15:].view(32,-1),y[:,15:]) #hard coded batch-size... only eval after 15 time steps\n", "    loss.backward()\n", "    decoder_optimizer.step()\n", "    \n", "\n", "    if i % 2 == 0:\n", "        print('[%s (%d %d%%) %.4f]' % (time_since(start), i, i / 10, loss.data[0]/y.size()[1]))\n", "        #print(evaluate('Wh', 100), '\\n')\n"], "cell_type": "code", "execution_count": null, "outputs": []}], "metadata": {"kernelspec": {"name": "python3", "language": "python", "display_name": "Python 3"}, "language_info": {"name": "python", "nbconvert_exporter": "python", "version": "3.6.4", "pygments_lexer": "ipython3", "mimetype": "text/x-python", "codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py"}}, "nbformat_minor": 1, "nbformat": 4}