awarebayes/RecNN

View on GitHub
examples/2. REINFORCE TopK Off Policy Correction/2. Reinforce Off Policy Correction.ipynb

Summary

Maintainability
Test Coverage
{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Reinforce Off Policy Correction\n",
    "\n",
    "### (Not TopK!)\n",
    "\n",
    "The following code contains an implementation of the REINFORCE algorithm, **without LSTM state encoder, and Noise Contrastive Estimation**. Look for these in other notebooks.\n",
    "\n",
    "Also, I am not google staff, and unlike the paper authors, I cannot have online feedback concerning the recommendations.\n",
    "\n",
    "**I use actor-critic for reward assigning.** In a real-world scenario that would be done through interactive user feedback, but here I use a neural network (critic) that aims to emulate it."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "import torch.nn as nn\n",
    "from torch.utils.tensorboard import SummaryWriter\n",
    "\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "from tqdm.auto import tqdm\n",
    "from time import gmtime, strftime\n",
    "\n",
    "from IPython.display import clear_output\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "\n",
    "\n",
    "# == recnn ==\n",
    "import sys\n",
    "sys.path.append(\"../../\")\n",
    "import recnn\n",
    "\n",
    "cuda = torch.device('cuda')\n",
    "\n",
    "# ---\n",
    "frame_size = 10\n",
    "batch_size = 10\n",
    "n_epochs   = 100\n",
    "plot_every = 30\n",
    "num_items    = 5000 # n items to recommend. Can be adjusted for your vram \n",
    "# --- \n",
    "\n",
    "tqdm.pandas()\n",
    "\n",
    "\n",
    "from jupyterthemes import jtplot\n",
    "jtplot.style(theme='grade3')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "action space is reduced to 26744 - 21744 = 5000\n"
     ]
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "631eddca320948d69cf69c91ad3446ba",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "HBox(children=(IntProgress(value=0, max=18946308), HTML(value='')))"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n"
     ]
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "06fa542ba48b4b85bab91eba28dffa75",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "HBox(children=(IntProgress(value=0, max=18946308), HTML(value='')))"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n"
     ]
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "d42794e7a502443c8e0940dd317a6cc5",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "HBox(children=(IntProgress(value=0, max=138493), HTML(value='')))"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n"
     ]
    }
   ],
   "source": [
    "def embed_batch(batch, item_embeddings_tensor, *args, **kwargs):\n",
    "    return recnn.data.batch_contstate_discaction(batch, item_embeddings_tensor,\n",
    "                                                 frame_size=frame_size, num_items=num_items)\n",
    "\n",
    "    \n",
    "def prepare_dataset(args_mut, kwargs):\n",
    "    kwargs.set('reduce_items_to', num_items) # set kwargs for your functions here!\n",
    "    pipeline = [recnn.data.truncate_dataset, recnn.data.prepare_dataset]\n",
    "    recnn.data.build_data_pipeline(pipeline, kwargs, args_mut)\n",
    "    \n",
    "\n",
    "\n",
    "# embeddgings: https://drive.google.com/open?id=1EQ_zXBR3DKpmJR3jBgLvt-xoOvArGMsL\n",
    "dirs = recnn.data.env.DataPath(\n",
    "    base=\"../../data/\",\n",
    "    embeddings=\"embeddings/ml20_pca128.pkl\",\n",
    "    ratings=\"ml-20m/ratings.csv\",\n",
    "    # IMPORTANT! I am using a different name for cache\n",
    "    # If you change your pipeline, change the name as well!\n",
    "    # Different pipelines must have different names!\n",
    "    cache=\"cache/frame_env_truncated.pkl\", \n",
    "    use_cache=True\n",
    ")\n",
    "\n",
    "env = recnn.data.env.FrameEnv(\n",
    "    dirs, frame_size,\n",
    "    batch_size,\n",
    "    embed_batch=embed_batch,\n",
    "    prepare_dataset=prepare_dataset,\n",
    "    num_workers=0\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Beta(nn.Module):\n",
    "    def __init__(self):\n",
    "        super(Beta, self).__init__()\n",
    "        self.net = nn.Sequential(\n",
    "            nn.Linear(1290, num_items),\n",
    "            nn.Softmax()\n",
    "        )\n",
    "        self.optim = optim.RAdam(self.net.parameters(), lr=1e-5, weight_decay=1e-5)\n",
    "        self.criterion = nn.CrossEntropyLoss()\n",
    "        \n",
    "    def forward(self, state, action):\n",
    "        \n",
    "        predicted_action = self.net(state)\n",
    "        \n",
    "        loss = self.criterion(predicted_action, action.argmax(1))\n",
    "        \n",
    "        self.optim.zero_grad()\n",
    "        loss.backward()\n",
    "        self.optim.step()\n",
    "        \n",
    "        return predicted_action.detach()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "beta_net   = Beta().to(cuda)\n",
    "value_net  = recnn.nn.Critic(1290, num_items, 2048, 54e-2).to(cuda)\n",
    "policy_net = recnn.nn.DiscreteActor(1290, num_items, 2048).to(cuda)\n",
    "# as miracle24 has suggested https://github.com/awarebayes/RecNN/issues/7\n",
    "# you can enable this to be more like the paper authors meant it to\n",
    "policy_net.action_source = {'pi': 'beta', 'beta': 'beta'}\n",
    "\n",
    "reinforce = recnn.nn.Reinforce(policy_net, value_net)\n",
    "reinforce = reinforce.to(cuda)\n",
    "\n",
    "reinforce.writer = SummaryWriter(log_dir='.../../runs/ReinforceOffPolicy{}/'.format(strftime(\"%H_%M\", gmtime())))\n",
    "plotter = recnn.utils.Plotter(reinforce.loss_layout, [['value', 'policy']],)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "from recnn.nn import ChooseREINFORCE\n",
    "\n",
    "def select_action_corr(state, action,  writer, step, **kwargs):\n",
    "    # note here I provide beta_net forward in the arguments\n",
    "    return reinforce.nets['policy_net']._select_action_with_correction(state, beta_net.forward, action,\n",
    "                                                                       writer, step)\n",
    "\n",
    "reinforce.nets['policy_net'].select_action = select_action_corr\n",
    "reinforce.params['reinforce'] = ChooseREINFORCE(ChooseREINFORCE.reinforce_with_correction)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "step 1050\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 1152x432 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "ename": "KeyboardInterrupt",
     "evalue": "",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mKeyboardInterrupt\u001b[0m                         Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-6-7eeb9e0ca892>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mepoch\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn_epochs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      2\u001b[0m     \u001b[0;32mfor\u001b[0m \u001b[0mbatch\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mtqdm\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0menv\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrain_dataloader\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m         \u001b[0mloss\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mreinforce\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mupdate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mbatch\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      4\u001b[0m         \u001b[0mreinforce\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstep\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      5\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0mloss\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/Documents/RecNN/recnn/nn/algo.py\u001b[0m in \u001b[0;36mupdate\u001b[0;34m(self, batch, learn)\u001b[0m\n\u001b[1;32m     48\u001b[0m         return self.algorithm(batch, self.params, self.nets, self.optimizers,\n\u001b[1;32m     49\u001b[0m                               \u001b[0mdevice\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdevice\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdebug\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdebug\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mwriter\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mwriter\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 50\u001b[0;31m                               learn=learn, step=self._step)\n\u001b[0m\u001b[1;32m     51\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     52\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0mto\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdevice\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/Documents/RecNN/recnn/nn/update/reinforce.py\u001b[0m in \u001b[0;36mreinforce_update\u001b[0;34m(batch, params, nets, optimizer, device, debug, writer, learn, step)\u001b[0m\n\u001b[1;32m     70\u001b[0m     \u001b[0mlearn\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mTrue\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     71\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 72\u001b[0;31m     \u001b[0mstate\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0maction\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mreward\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnext_state\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdone\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_base_batch\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mbatch\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     73\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     74\u001b[0m     predicted_probs = nets['policy_net'].select_action(state=state, action=action, K=params['K'],\n",
      "\u001b[0;32m~/Documents/RecNN/recnn/data/utils.py\u001b[0m in \u001b[0;36mget_base_batch\u001b[0;34m(batch, device, done)\u001b[0m\n\u001b[1;32m    220\u001b[0m     \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    221\u001b[0m         \u001b[0mbatch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtorch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mzeros_like\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mbatch\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'reward'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 222\u001b[0;31m     \u001b[0;32mreturn\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mto\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdevice\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mi\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mb\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    223\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    224\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/Documents/RecNN/recnn/data/utils.py\u001b[0m in \u001b[0;36m<listcomp>\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m    220\u001b[0m     \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    221\u001b[0m         \u001b[0mbatch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtorch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mzeros_like\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mbatch\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'reward'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 222\u001b[0;31m     \u001b[0;32mreturn\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mto\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdevice\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mi\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mb\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    223\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    224\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mKeyboardInterrupt\u001b[0m: "
     ]
    }
   ],
   "source": [
    "for epoch in range(n_epochs):\n",
    "    for batch in tqdm(env.train_dataloader):\n",
    "        loss = reinforce.update(batch)\n",
    "        reinforce.step()\n",
    "        if loss:\n",
    "            plotter.log_losses(loss)\n",
    "        if reinforce._step % plot_every == 0:\n",
    "            clear_output(True)\n",
    "            print('step', reinforce._step)\n",
    "            plotter.plot_loss()\n",
    "        if reinforce._step > 1000:\n",
    "            pass\n",
    "            # assert False"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}