{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"# https://github.com/location-competition/indoor-location-competition-20\n# https://www.kaggle.com/c/indoor-location-navigation/data\n# https://www.kaggle.com/titericz/eda-loading-data-and-visualizing-paths\n# https://www.kaggle.com/npa02012/time-to-complete-trace-eda\n# https://pytorch.org/tutorials/beginner/blitz/cifar10_tutorial.html\n\n# www.reddit.com/r/MachineLearning/comments/4dzxs3/best_way_to_deal_with_time_series_data\n# https://arxiv.org/pdf/1907.03907.pdf\n# https://github.com/YuliaRubanova/latent_ode\n\nimport os\nimport glob\nimport numpy as np\nimport pandas as pd\nimport torch\nimport tqdm\nimport matplotlib\nimport time\nimport pickle\n\nsettings = {\n    'beacon_seq_len' : 50\n    ,'beacon_embed_dim' : 256\n    ,'device' : torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n    ,'floor_mapping' : {'B1' : -1, 'B2' : -2, 'B3' : -3\n                         ,'F1' : 0, 'F2' : 1, 'F3' : 2, 'F4' : 3, 'F5' : 4\n                         ,'F6' : 5, 'F7' : 6 , 'F8' : 7, 'F9' : 8, 'F10' : 9\n                         ,'1F' : 0, '2F' : 1, '3F' : 2, '4F' : 3, '5F' : 4\n                         ,'6F' : 5, '7F' : 6, '8F' : 7, '9F' : 8\n                        }\n    ,'n_floor' : 13\n    ,'max_beacon_distance' : 200\n    ,'path_train' : '../input/indoor-location-navigation/train/*/*/*'\n    ,'path_test' : '../input/indoor-location-navigation/test/*'\n    ,'path_sample' : '../input/indoor-location-navigation/sample_submission.csv'\n}","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"with open('../input/location-data/beacon_train.pkl', 'rb') as handle:\n    train = pickle.load(handle)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Delete rows with 'unusual' floors\ni = train['df'][~train['df']['floor'].isin(settings['floor_mapping'].keys())].index\ntrain['df'].drop(i, inplace=True)\n\n# Only keep initial rows of a trace\ntrain['df'] = train['df'].groupby('trace_id')\\\n                .head(settings['beacon_seq_len']).reset_index(drop=True)\n\n# Convert distance column to int\ntmp = settings['max_beacon_distance']\ntrain['df']['distance'] = np.where(train['df'][\"distance\"]>tmp, tmp, train['df'][\"distance\"])\ntrain['df']['distance'] = np.where(train['df'][\"distance\"]<0, 0, train['df'][\"distance\"])\ntrain['df']['distance'] = train['df']['distance'].astype(int) + 1\n\n# Map columns\ntrain['df']['site_id'] = train['df']['site_id'].astype('category').cat.codes + 1\ntrain['df']['UUID'] = train['df']['UUID'].astype('category').cat.codes + 1\ntrain['df']['MinorID'] = train['df']['MinorID'].astype('category').cat.codes + 1\ntrain['df']['MajorID'] = train['df']['MajorID'].astype('category').cat.codes + 1\ntrain['df']['MAC_Address'] = train['df']['MAC_Address'].astype('category').cat.codes + 1\ntrain['df'].replace({'floor' : settings['floor_mapping']}, inplace=True)\n\n# Record settings\nsettings['n_uuids'] = train['df']['UUID'].max() + 1\nsettings['n_minor_ids'] = train['df']['MinorID'].max() + 1\nsettings['n_major_ids'] = train['df']['MajorID'].max() + 1\nsettings['n_macs'] = train['df']['MAC_Address'].max() + 1\nsettings['n_sites'] = train['df']['site_id'].max() + 1\n\n# Convert to dictionary\ntrain['df'] = {k: table for k, table in train['df'].groupby(\"trace_id\")} # slower, but easier\n\ntrain['df']['5d09b22fcfb49b00085466a0']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Make validation set\nnp.random.seed(1)\nval_idx = np.random.choice(list(train['df'].keys())\n                           ,int(.2 * len(train['df'].keys())), replace=False)\nvalid = {'df' : {}}\nfor i in val_idx:\n    valid['df'][i] = train['df'][i].copy()\n    del train['df'][i]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class beacon_dataset(torch.utils.data.Dataset):\n    \n    def __init__(self, group, settings):\n        super(beacon_dataset, self).__init__()\n        self.beacon_seq_len = settings['beacon_seq_len']\n        self.n_floor = settings['n_floor']\n        self.group = group\n        self.trace_ids = list(group.keys())\n        \n    def __len__(self):\n        return(len(self.trace_ids))\n    \n    def __getitem__(self, index):\n        # Get the relevant user row\n        sample = self.group[self.trace_ids[index]]\n        \n        # Get contents as np.int64s\n        uuids = sample['UUID'].values\n        distances = sample['distance'].values\n        minor_ids = sample['MinorID'].values\n        major_ids = sample['MajorID'].values\n        macs = sample['MAC_Address'].values\n        sites = sample['site_id'].values\n        \n        # Pad if needed\n        n_pad = self.beacon_seq_len - len(uuids)\n        if n_pad > 0:\n            uuids = np.concatenate((uuids, np.full(n_pad, 0).astype(np.int64)))\n            distances = np.concatenate((distances, np.full(n_pad, 0).astype(np.int64)))\n            minor_ids = np.concatenate((minor_ids, np.full(n_pad, 0).astype(np.int64))) \n            major_ids = np.concatenate((major_ids, np.full(n_pad, 0).astype(np.int64)))\n            macs = np.concatenate((macs, np.full(n_pad, 0).astype(np.int64)))\n            sites = np.concatenate((sites, np.full(n_pad, 0).astype(np.int64)))\n        else:\n            uuids = uuids[:self.beacon_seq_len]\n            distances = distances[:self.beacon_seq_len]\n            minor_ids = minor_ids[:self.beacon_seq_len]\n            major_ids = major_ids[:self.beacon_seq_len]\n            macs = macs[:self.beacon_seq_len]\n            sites = sites[:self.beacon_seq_len]\n\n        \n        # Return\n        return({\n            'floor' : sample.iloc[0]['floor'] + 3#np.array(floor)\n            ,'uuids' : uuids\n            ,'distances' : distances\n            ,'minor_ids' : minor_ids\n            ,'major_ids' : major_ids\n            ,'macs' : macs\n            ,'sites' : sites\n        })\n    \n\ntrain_dataset = beacon_dataset(group = train['df']\n                              ,settings = settings\n                              )\ntrain_dataloader = torch.utils.data.DataLoader(train_dataset\n                                                ,batch_size = 128\n                                                ,drop_last = True\n                                                ,shuffle = True\n                                                ,num_workers = 4\n                                               )\n\nvalid_dataset = beacon_dataset(group = valid['df']\n                              ,settings = settings\n                              )\nvalid_dataloader = torch.utils.data.DataLoader(valid_dataset\n                                                ,batch_size = 10\n                                                ,num_workers = 4\n                                               )\n\nvalid_dataset.__getitem__(1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class floor_model(torch.nn.Module):\n    def __init__(self, settings):\n        super(floor_model, self).__init__()\n        self.embed_dim = settings['beacon_embed_dim']\n        self.seq_len = settings['beacon_seq_len']\n        self.device = settings['device']\n        \n        self.minor_id_embedding = torch.nn.Embedding(settings['n_minor_ids']\n                                                    ,self.embed_dim)\n        self.major_id_embedding = torch.nn.Embedding(settings['n_major_ids']\n                                                    ,self.embed_dim)\n        self.uuid_embedding = torch.nn.Embedding(settings['n_uuids']\n                                                 ,self.embed_dim)\n        self.mac_embedding = torch.nn.Embedding(settings['n_macs']\n                                                 ,self.embed_dim)\n        # Site embedding doesn't make to much sense (same site always)\n        self.site_embedding = torch.nn.Embedding(settings['n_sites']\n                                                ,self.embed_dim)\n        self.distance_embedding = torch.nn.Embedding(settings['max_beacon_distance']+2\n                                                     ,self.embed_dim)\n        self.pos_embedding = torch.nn.Embedding(self.seq_len, self.embed_dim)\n        self.multi_att = torch.nn.MultiheadAttention(embed_dim = self.embed_dim\n                                                     ,num_heads = 4\n                                                     ,dropout = 0.2)\n\n        self.lin_1 = torch.nn.Linear(self.embed_dim, self.embed_dim)\n        self.relu = torch.nn.ReLU()\n        self.lin_2 = torch.nn.Linear(self.embed_dim, 1)\n        self.dropout = torch.nn.Dropout(0.2)\n        \n        self.pred = torch.nn.Linear(self.seq_len, settings['n_floor'])\n        \n        self.tmp = True\n            \n    def forward(self, batch):        \n        # Minor id embedding\n        x = self.minor_id_embedding(batch['minor_ids'].long())\n        \n        # MAC Address embedding\n        x = x + self.mac_embedding(batch['macs'].long())\n        \n        # Site embedding\n        x = x + self.site_embedding(batch['sites'].long())\n        \n        # Major Id embedding\n        x = x + self.major_id_embedding(batch['major_ids'].long())\n        \n        # UUID embedding\n        #x = x + self.uuid_embedding(batch['uuids'].long())\n        \n        # Distance embedding\n        x = x + self.distance_embedding(batch['distances'])\n        \n        # Position embedding\n        pos_id = torch.arange(x.shape[1])[None, :].to(self.device)\n        x = x + self.pos_embedding(pos_id)\n        \n        # Permute\n        x = x.permute(1, 0, 2) # x: [bs, s_len, embed] => [s_len, bs, embed]\n        \n        # MultiHead Attention and permute back\n        attn_output, _ = self.multi_att(x, x, x)\n        x = x + attn_output\n        x = x.permute(1, 0, 2)\n        \n        # Feed forward\n        x = self.lin_1(x)\n        x = self.relu(x)\n        x = self.lin_2(x)\n        x = self.dropout(x)\n        \n        # Predict\n        x = x[:, :, -1]\n        x = self.pred(x)\n        \n        # Return\n        return(x)\n        \n\n# Setup model, optimizer and criterion\nmodel = floor_model(settings)\noptimizer = torch.optim.Adam(model.parameters(), lr=.002)\n#criterion = torch.nn.BCEWithLogitsLoss()\ncriterion = torch.nn.CrossEntropyLoss()\nall_auc = []\n\n# Move model and criteriod to device\nmodel.to(settings['device'])\ncriterion.to(settings['device'])\nall_loss = []\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for _ in range(25):\n    tbar = tqdm.tqdm(train_dataloader)\n    for batch in tbar:\n        for k in batch.keys():\n            batch[k] = batch[k].to(settings['device'])\n        optimizer.zero_grad()\n        pred = model(batch)\n        loss = criterion(pred, batch['floor'].long())\n        loss.backward()\n        optimizer.step()\n        \n        # Record metrics\n        all_loss.append(loss.item())\n\nprint(np.array(all_loss[-200:]).mean())\nmatplotlib.pyplot.plot(all_loss)\nmatplotlib.pyplot.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_acc(dataset):\n    if dataset == 'valid':\n        dl = valid_dataloader\n    else:\n        dl = train_dataloader\n    # Accuracy mesurments\n    preds = np.empty(0, dtype=np.int64)\n    labels = np.empty(0, dtype=np.int64)\n\n    model.eval()\n    for batch in dl:\n        for k in batch.keys():\n            batch[k] = batch[k].to(settings['device'])\n\n        # Get predictions\n        pred = model(batch)\n        p = pred.detach().to('cpu').numpy()\n        p = np.argmax(p, axis = 1)\n        preds = np.concatenate((preds, p))\n\n        # Label\n        l = batch['floor'].detach().to('cpu').numpy()\n        labels = np.concatenate((labels, l))\n    model.train()\n    print(dataset)\n    print(np.sum(preds == labels)/preds.shape[0])\n    print((15 * abs(preds - labels)).mean())\n    \n    \n# Get accuracy\nget_acc('valid')\nget_acc('train')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# .729","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat":4,"nbformat_minor":4}