{
 "cells": [
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "---\n",
    "execute:\n",
    "  enabled: false\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<!-- d2l:prose id=ctr-md-1 fw=all -->\n",
    "\n",
    "# Feature-Rich Recommender Systems\n",
    "\n",
    "Interaction data is the most basic indication of users' preferences and interests. It plays a critical role in former introduced models. Yet, interaction data is usually extremely sparse and can be noisy at times. To address this issue, we can integrate side information such as features of items, profiles of users, and even in which context that the interaction occurred into the recommendation model. Utilizing these features are helpful in making recommendations in that these features can be an effective predictor of users interests especially when interaction data is lacking. As such, it is essential for recommendation models also have the capability to deal with those features and give the model some content/context awareness. To demonstrate this type of recommendation models, we introduce another task on click-through rate (CTR) for online advertisement recommendations [@McMahan.Holt.Sculley.ea.2013] and present an anonymous advertising dataset. Targeted advertisement services have attracted widespread attention and are often framed as recommendation engines. Recommending advertisements that match users' personal taste and interest is important for click-through rate improvement.\n",
    "\n",
    "\n",
    "Digital marketers use online advertising to display advertisements to customers. Click-through rate is a metric that measures the number of clicks advertisers receive on their ads per number of impressions and it is expressed as a percentage calculated with the formula: \n",
    "\n",
    "$$ \\textrm{CTR} = \\frac{\\#\\textrm{Clicks}} {\\#\\textrm{Impressions}} \\times 100 \\% .$$\n",
    "\n",
    "Click-through rate is an important signal that indicates the effectiveness of prediction algorithms. Click-through rate prediction is a task of predicting the likelihood that something on a website will be clicked. Models on CTR prediction can not only be employed in targeted advertising systems but also in general item (e.g., movies, news, products) recommender systems, email campaigns, and even search engines. It is also closely related to user satisfaction, conversion rate, and can be helpful in setting campaign goals as it can help advertisers to set realistic expectations."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "id": "ctr-feature-rich-recommender-systems"
   },
   "source": [
    "from collections import defaultdict\n",
    "from d2l import torch as d2l\n",
    "import torch\n",
    "import os"
   ],
   "execution_count": 1,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<!-- d2l:prose id=ctr-md-2 fw=all -->\n",
    "\n",
    "## An Online Advertising Dataset\n",
    "\n",
    "With the considerable advancements of Internet and mobile technology, online advertising has become an important income resource and generates vast majority of revenue in the Internet industry. It is important to display relevant advertisements or advertisements that pique users' interests so that casual visitors can be converted into paying customers. The dataset we introduced is an online advertising dataset. It consists of 34 fields, with the first column representing the target variable that indicates if an ad was clicked (1) or not (0). All the other columns are categorical features. The columns might represent the advertisement id, site or application id, device id, time, user profiles and so on. The real semantics of the features are undisclosed due to anonymization and privacy concern.\n",
    "\n",
    "The following code downloads the dataset from our server and saves it into the local data folder."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "id": "ctr-an-online-advertising-dataset"
   },
   "source": [
    "\n",
    "d2l.DATA_HUB['ctr'] = (d2l.DATA_URL + 'ctr.zip',\n",
    "                       'e18327c48c8e8e5c23da714dd614e390d369843f')\n",
    "\n",
    "data_dir = d2l.download_extract('ctr')"
   ],
   "execution_count": 2,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<!-- d2l:prose id=ctr-md-3 fw=all -->\n",
    "\n",
    "There are a training set and a test set, consisting of 15000 and 3000 samples/lines, respectively.\n",
    "\n",
    "## Dataset Wrapper\n",
    "\n",
    "For the convenience of data loading, we implement a `CTRDataset` which loads the advertising dataset from the CSV file and can be used by a standard data loader."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "id": "ctr-dataset-wrapper-1"
   },
   "source": [
    "\n",
    "class CTRDataset(torch.utils.data.Dataset):\n",
    "    def __init__(self, data_path, feat_mapper=None, defaults=None,\n",
    "                 min_threshold=4, num_feat=34):\n",
    "        self.NUM_FEATS, self.count, self.data = num_feat, 0, {}\n",
    "        feat_cnts = defaultdict(lambda: defaultdict(int))\n",
    "        self.feat_mapper, self.defaults = feat_mapper, defaults\n",
    "        self.field_dims = torch.zeros(self.NUM_FEATS, dtype=torch.long)\n",
    "        with open(data_path) as f:\n",
    "            for line in f:\n",
    "                instance = {}\n",
    "                values = line.rstrip('\\n').split('\\t')\n",
    "                if len(values) != self.NUM_FEATS + 1:\n",
    "                    continue\n",
    "                instance['y'] = [float(values[0])]\n",
    "                for i in range(1, self.NUM_FEATS + 1):\n",
    "                    feat_cnts[i][values[i]] += 1\n",
    "                    instance.setdefault('x', []).append(values[i])\n",
    "                self.data[self.count] = instance\n",
    "                self.count = self.count + 1\n",
    "        if self.feat_mapper is None and self.defaults is None:\n",
    "            feat_mapper = {i: {feat for feat, c in cnt.items() if c >=\n",
    "                               min_threshold} for i, cnt in feat_cnts.items()}\n",
    "            self.feat_mapper = {i: {feat_v: idx for idx, feat_v in enumerate(sorted(feat_values))}\n",
    "                                for i, feat_values in feat_mapper.items()}\n",
    "            self.defaults = {i: len(feat_values) for i, feat_values in feat_mapper.items()}\n",
    "        for i, fm in self.feat_mapper.items():\n",
    "            self.field_dims[i - 1] = len(fm) + 1\n",
    "        self.offsets = torch.tensor(\n",
    "            (0, *torch.cumsum(self.field_dims, dim=0).numpy()[:-1]))\n",
    "\n",
    "    def __len__(self):\n",
    "        return self.count\n",
    "\n",
    "    def __getitem__(self, idx):\n",
    "        feat = torch.tensor([self.feat_mapper[i + 1].get(v, self.defaults[i + 1])\n",
    "                             for i, v in enumerate(self.data[idx]['x'])])\n",
    "        return feat + self.offsets, torch.tensor(self.data[idx]['y'])"
   ],
   "execution_count": 3,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<!-- d2l:prose id=ctr-md-4 fw=all -->\n",
    "\n",
    "The following example loads the training data and print out the first record."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "id": "ctr-dataset-wrapper-2"
   },
   "source": [
    "train_data = CTRDataset(os.path.join(data_dir, 'train.csv'))\n",
    "train_data[0]"
   ],
   "execution_count": 4,
   "outputs": [
    {
     "output_type": "execute_result",
     "data": {
      "text/plain": "(tensor([ 143,  144,  227,  231,  957, 1250, 1471, 1566, 1624, 1736, 2008, 2061,\n         2225, 2304, 2305, 2360, 2745, 2746, 2747, 2748, 2892, 2988, 3165, 3182,\n         3194, 3195, 3279, 3651, 3687, 3708, 3722, 3751, 3786, 3801]),\n tensor([1.]))"
     },
     "metadata": {},
     "execution_count": 4
    }
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<!-- d2l:prose id=ctr-md-5 fw=all -->\n",
    "\n",
    "As can be seen, all the 34 fields are categorical features. Each value represents the one-hot index of the corresponding entry. The label $0$ means that it is not clicked. This `CTRDataset` can also be used to load other datasets such as the Criteo display advertising challenge [dataset](https://labs.criteo.com/2014/02/kaggle-display-advertising-challenge-dataset/) and the Avazu click-through rate prediction [dataset](https://www.kaggle.com/c/avazu-ctr-prediction).  \n",
    "\n",
    "## Summary \n",
    "* Click-through rate is an important metric that is used to measure the effectiveness of advertising systems and recommender systems.\n",
    "* Click-through rate prediction is usually converted to a binary classification problem. The target is to predict whether an ad/item will be clicked or not based on given features.\n",
    "\n",
    "## Exercises\n",
    "\n",
    "* Can you load the Criteo and Avazu dataset with the provided `CTRDataset`. It is worth noting that the Criteo dataset consisting of real-valued features so you may have to revise the code a bit."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<!-- d2l:prose id=ctr-md-6 fw=pytorch -->\n",
    "\n",
    "[Discussions](https://d2l.discourse.group/t/405)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "name": "d2l-pytorch",
   "display_name": "d2l (PyTorch)",
   "language": "python"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
