Last active
July 10, 2025 18:46
-
-
Save darcyabjones/ace0934ae19c56ed4babc50167f282b0 to your computer and use it in GitHub Desktop.
A quick introduction to learning to rank models.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| "cells": [ | |
| { | |
| "cell_type": "code", | |
| "execution_count": 1, | |
| "id": "sensitive-recognition", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "import pandas as pd\n", | |
| "import numpy as np\n", | |
| "import seaborn as sns\n", | |
| "from matplotlib import pyplot as plt\n", | |
| "from statistics import mean" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "precise-colleague", | |
| "metadata": {}, | |
| "source": [ | |
| "# A brief intro to gradient descent and neural networks\n", | |
| "\n", | |
| "Feel free to skip." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 2, | |
| "id": "perceived-community", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "def log_loss(true, pred):\n", | |
| " return ((true * np.log(pred)) + ((1 - true) * np.log(1 - pred))).sum() / -true.shape[0]\n", | |
| "\n", | |
| "def sigmoid(pred):\n", | |
| " return 1 / (1 + np.exp(-pred))" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 3, | |
| "id": "great-shopper", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "image/png": "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\n", | |
| "text/plain": [ | |
| "<Figure size 432x288 with 1 Axes>" | |
| ] | |
| }, | |
| "metadata": { | |
| "needs_background": "light" | |
| }, | |
| "output_type": "display_data" | |
| } | |
| ], | |
| "source": [ | |
| "linspace = np.linspace(-5, 5, 100)\n", | |
| "ax = sns.scatterplot(y=sigmoid(linspace), x=linspace) \n", | |
| "ax.set_ylabel(\"f(x)\")\n", | |
| "ax.set_xlabel(\"x\");" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 4, | |
| "id": "distinct-ethiopia", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXgAAAEGCAYAAABvtY4XAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8QVMy6AAAACXBIWXMAAAsTAAALEwEAmpwYAAAZu0lEQVR4nO3de5ScdX3H8fd3N5tsLpsLySZgcFkCAUQuga4RESwQxUg5Qr3grbEH44m0RwiltlTraavSc6QXjgT00AgUsUVAuShKUQ5RgpVbgiHcysWQxEAgN0iWTTa72fn2j5lZJ5PZ3dnd5/c88zzzeZ2Ts5eZnef3kPDJL5/nN7/H3B0REcmehqQHICIiYSjgRUQySgEvIpJRCngRkYxSwIuIZNSYpAdQasaMGd7e3p70MEREUmP16tXb3L210mM1FfDt7e2sWrUq6WGIiKSGmW0Y6DFVNCIiGaWAFxHJKAW8iEhGKeBFRDJKAS8iklFBV9GY2XqgE+gD9rl7R8jjiYikSS7nrN/exeu7upk1uZn26RNpaLDIXj+OZZJnuvu2GI4jIpIauZxz3zOvcdnta+juzdHc1MBVF8xj4TsPjizkVdGIiCRg/fau/nAH6O7Ncdnta1i/vSuyY4QOeAd+YWarzWxJ4GOJiKTG67u6+8O9qLs3x5bO7siOEbqiea+7v2pmM4H7zez/3H1l6RMKwb8EoK2tLfBwRERqw6zJzTQ3NewX8s1NDcxsaY7sGEFn8O7+auHjFuAuYH6F5yx39w5372htrbidgohI5rRNm8AV5x9Hc1M+hpubGrji/ONomzYhsmMEm8Gb2USgwd07C5+fDXw91PFERNJk4xu7uWbFiyw+bQ5m4A7XrHiRk9umMad1UiTHCFnRzALuMrPicW5x9/sCHk9EJDVe39XNhu17+PYvX9rv+1s6u2s/4N19HXBiqNcXEUmz1HfwIiJSWao7eBERGVjaO3gRERlAHB28KhoRkQTMbGnur2eK1MGLiKRcLue8vP0tli6Yu18Hf9UF82ifPjGy46iiERGJ2frtXXzxlt8ybcLY/g6+weDYQ1pSt5ukiIiUKO5Ds3ln934d/KlHTKd9RjT9O6iiERGJXXENfKmo+3dQwIuIxC6ONfCgikZEJHZxrIEHBbyISOziWAMPqmhERGKnDl5EJKPUwYuIZJQ6eBGRjFIHLyKSUXHsQwMKeBGRWMW1Dw2oohERiVVc+9CAAl5EJFZx7UMDqmhERGIV1xp4UMCLiMQqrjXwoIpGRCRWca2BBwW8iEis4loDD6poRERipQ5eRCSj1MGLiGSUOngRkYxSBy8iklFx7UMDCngRkdjEuQ8NqKIREYlNnPvQgAJeRCQ2ce5DAzFUNGbWaGa/NbOfhj6WiEgti3MNPMTTwS8FnovhOCIiNS3ONfAQuKIxs0OBPwH+Gbgs5LFERGpdnGvgIXwH/y3gb4GWgZ5gZkuAJQBtbW2BhyMikpw418BDwIrGzM4Ftrj76sGe5+7L3b3D3TtaW1tDDUdEJHFxroGHsB38e4EPm9l64FbgLDP7r4DHExGpWXGvgYeAFY27fxn4MoCZnQF8yd3/LNTxRERqWdxr4EHr4EVEYhH3GniIaasCd/+Vu58bx7FERGpR3P07aC8aEZHgkujfQRWNiEhwSfTvoIAXEQkuif4dVNGIiAQ3UP/eOilc/w4KeBGR4BobOKB/X7pgLo2BE1gVjYhIYJt3dnPzwxv224Pm5oc3cFLb1KAVjQJeRCSwmS3NvLG7Z7/+PfQSSVBFIyISVFJLJEEzeBGRoJJaIgkKeBGRoJJaIgmqaEREgkpii4IiBbyISCBJ9u+gikZEJJgk+3dQwIuIBJNk/w6qaEREgkmyfwcFvIhIEEn376CKRkQkiKT7d1DAi4gEkXT/DqpoRESCSLp/BwW8iEjkaqF/B1U0IiKRq4X+HRTwIiKRq4X+HVTRiIhErhb6d1DAi4hEqlb6d1BFIyISqVrp30EBLyISqVrp30EVjYhIpGqlfwcFvIhIZGqpfwdVNCIikaml/h0U8CIikcjlnPXbu2qmf4eAFY2ZNZvZY2b2pJk9Y2ZfC3UsEZEk5XLOfc+8xpO/f7Nm+ncI28HvBc5y9xOBecBCMzsl4PFERBKxfnsXl92+httXbeKSs2qjf4eAFY27O/BW4cumwi8PdTwRkaSULo38/iMb+vv304+cwbvaD0qkf4fAq2jMrNHM1gBbgPvd/dGQxxMRSULp0shi/379Q+tobRmXWLhD4IB39z53nwccCsw3s+PKn2NmS8xslZmt2rp1a8jhiIhErtaWRpaKZRWNu79pZr8CFgJPlz22HFgO0NHRoQpHRFKl1pZGlgoW8GbWCvQWwn088H7gylDHExFJQi1tTVAu5Az+EOB7ZtZIvgq63d1/GvB4IiKxyuWcCWPH0NzUQHdvrv/7SS6NLBVyFc1a4KRQry8ikqTi2vcr73uOS86ay7IVL9Ldm6uZ/h30TlYRkREprn3v7s31L41sbIAFx8zk+NlTE+/fQQEvIjIixe4d2K9/P/WI6TUR7qDdJEVEhq20ey9VK917kQJeRGQYit37Jbc+UVPbElSiikZEZBjS0L0XKeBFRKpUuiUw1G73XlRVRWNmE82sofD5UWb2YTNrCjs0EZHaUatbAg+m2g5+JdBsZrOBB4ALgZtCDUpEpNbU6pbAg6m2ojF3321mi4Fr3P1fzOy3IQcmIlJLanVL4MFUHfBm9h7gM8DiYf6siEiqlW9JUOzem5sa+MhJs2sy3KH6iuZS4MvAXe7+jJnNAX4ZbFQiIjUiTcsiy1U1C3f3B4EHAQoXW7e5+yUhByYiUgvStCyyXLWraG4xs8lmNhF4FnjezP4m7NBERJJXaUuCZQ+8xJ7evpoOd6i+ojnW3XcB5wP3Am3AolCDEhGpBWnZkmAg1QZ8U2Hd+/nAj929F91AW0QyLM3de1G1K2H+A1gPPAmsNLPDgF2hBiUikrQ0d+9F1V5kXQYsK/nWBjM7M8yQRESSU9yO4IXXO1OzJcFAqr3IOsXMrjKzVYVf/w7U/r9PRESGoVjLnLPsIZ5+dVdqu/eiajv4G4FO4ILCr13Af4YalIhIEkprmTtWp2dLgoFU28Ef4e4fLfn6a2a2JsB4REQSU74ksti9nzB7MnNntdA+fWJq6hmofga/x8xOK35hZu8F9oQZkohI/Cotidy8s5sbfr2OubNamNM6KVXhDtUH/EXAt81svZmtB64FvhBsVCIiMcrCkshKql1F8yRwoplNLny9y8wuBdYGHJuISCyysCSykmHtCFl4N2vRZcC3Ih2NiEgCKm1HAOlaElnJaG66nd6zFhEhX82s2/oWDWapXxJZyWgCXlsViEhqla55v/S2NSxdkJ3uvWjQisbMOqkc5AaMDzIiEZEYlPbum3d2c/PDG1jyvjmc9PapHDZ9YuqWRFYyaMC7e0tcAxERCa24DcHru7rZ09vX37tDvntf9sBL3Lrk3cxpnZTgKKOj2+6JSF0oVjLFWfvSBUf234KvKAu9e6nRdPAiIqlRWskA3L5qUyZ791KawYtIXShdCgn09+7fu3A+jjOzpTkTvXupYAFvZm8HbgYOBnLAcne/OtTxREQqKfbuxaWQpSH/xu4eWlvGZaZzLxdyBr8P+Gt3f8LMWoDVZna/uz8b8JgiIv1Ke/dpE8aydMFcrn7gRbp7c5msZMoFC3h33wxsLnzeaWbPAbPJ37RbRCS4elgKOZhYLrKaWTtwEvBohceWFG8ksnXr1jiGIyIZVnx36sO/28b67V0Vl0KOH9uYyt0hhyv4RVYzmwTcAVxatpcNAO6+HFgO0NHRoXfHisiI1eNSyMEEncGbWRP5cP9vd78z5LFEROpxKeRgQq6iMeAG4Dl3vyrUcUSkvg317tSsL4UcTMiK5r3AIuCpktv7fcXd7w14TBGpI9VUMllfCjmYkKtofo22FBaRgAaqZOppKeRg9E5WEUmtenx36nAo4EUkVUo79+JNslXJVKaAF5HUKO/cD5s+nivOP46v3v20KpkKFPAikhrlnfuG7Xu4ZsWL3LbkFPb09tV9JVNOAS8iNau0jpk1ufmAzh3yIb+nt49T5sxIaJS1SwEvIjWpvI5pbmrgu4s66vqdqcOlG36ISE0qr2O6e3N89cdPceVHT6jbd6YOl2bwIlITqq1jZk9t5t5LTmdLZ7c69yEo4EUkccOpYw6amF8CqWWQQ1NFIyKJUx0ThmbwIhI71THxUMCLSKxUx8RHFY2IxEp1THw0gxeRoFTHJEcBLyLBqI5JlioaEQlGdUyyNIMXkUiUVzHt0yeqjkmYAl5ERq1SFXPVBfM4elaL6pgEqaIRkVGrVMVcdvsaGhvgqgvmqY5JiGbwIjIs1VYx3b05XtvVzcJ3HswxqmMSoYAXkaoNt4qZ2dJMQ4OpjkmIKhoRqZqqmHTRDF5EDlCphmloMFUxKaOAF5H9DFTDLHznwcya3KwqJkVU0YjIfgaqYdZv76J9+kRVMSmiGbxIHRvOipgtnd3MaZ2kKiZFFPAidWokK2IAVTEpoopGpE5pRUz2aQYvknFaEVO/FPAiGaYVMfUtWEVjZjea2RYzezrUMUQkL5dz1m19i4d/t411W98il3NAK2LqXcgZ/E3AtcDNAY8hUvcGm6VrRUx9CzaDd/eVwI5Qry8ieYPN0os1TKlKK2JOmTODOa2TFO4Zo1U0IikwUAUDDDpLVw1T3xK/yGpmS4AlAG1tbQmPRqT2DFbBNDTYkBdLVcPUr8Rn8O6+3N073L2jtbU16eGIJGYkF0qBIWfpqmHqV+IzeBEZ3YVSzdJlICGXSf4AeBg42sw2mdniUMcSSYPBevTRXCgFzdKlsmAzeHf/VKjXFkmboXr0wWbp89unc9UF8w74WV0olaGoohGJwUAz9GMuOZ05rZN0oVSCSPwiq0iWDFTDDDZDB10olTA0gxeJyEj3fQE0S5cgNIMXGYaRXiit5g1HmqVL1DSDF6nSaC6Uat8XSYJm8CIlRjpDB7Tvi9QcBbxIQXGGfs6yh/jUdx/lnGUPcd8zr0V2oVQkbqpopK4MdHcjGN1SRtCFUqk9CnjJnIFCfLQdenGGPtgbjnQnJKklCnjJlMFCXDN0qTfq4CV1RnohNIoOXRdKJU00g5dUGU3Nohm61BvN4KUmjXRv9MGWKmqGLvVGM3hJxGCrWUazN/pgF0I1Q5d6o4CX2A1Vswx2MXS0NYtWuUg9UUUjwYy0ZhntTaRVs4jkaQYvozKSNedD1SzaG10kGprBS1UqzcYHe2v/aG5Bp73RRaKhGbz0G+5s/OhZLQN25aO5BZ1m6SLRUMDXoUpBDgz7HaDf+czJI1pzXk2A62KoyOgp4DNsOEE+ktn4xHFjBgzxofZtUYCLhKeAz4jyMG+bNoFfPPd61UE+ktn4rJZxWnMuUsMU8ClUTZgvX9QxrCAfyWy87aCJtB00UWvORWqUAr7GjTTMV23YMawgH81sXCEuUpsU8AmqFN4b39gdSZjnnGEFuWbjItmjgI9RaaAfMqWZZzd39gftYdPHc/FZc/nq3U9HEub3PPkKV370BC6/Y+2wglwhLpIdCvhRKg/tvhz9FyZLZ+TlgX7JgiNZvnJdfyife8Ls/nCH0Yf55QvfwdnvmMXxs6coyEXqlAJ+GIphvr1rL2MbG+jpy/Hqm91cfsdapk0Yy2ffcxhXP/BixRl5eaDnnP1C2owgYa4gF6lfCvgylUK8NMyvuv95PtHRxrIVL7L4tDnc8Ot8aH/k5EP7wx0OnJGXBzpU7sgV5iISlboK+NLwHt/USNfePnr6+pg8romeXI7xTY288PpbXP3AC3yio43bVm08IMwXnzaHZSvyQV466y6fgVeakZcG+B2rN7F0wdz+vxTuefIVrjj/uP06eIW5iIxGpgI+l3M27uhi5+5eenM5evtyNJiBwZgGY/32PSx74AU+d+rh7O7t49bHN+4X5N37+li+8g8hXinMy4O7NLQHm5GXB/obu3uYO2sSP7v4dLa+lQ/vtmkTOLltmsJcRCIRNODNbCFwNdAIXO/u34z6GD09fby8o5PefbBhx2669vbSNKaRzj29+z2vtaWZv7/rKRafNoftu3sqBvnnT58zZJgXP3b35rhj9SYuOWsuy1a8OOSMvFKgFwP8iJl/CG+FuYhEJVjAm1kj8G3gA8Am4HEz+4m7PxvVMXp6+nhs4za6uvsY1zSG51/v5KiZLbywpfOA527r6ukP7WIfXinIS0O8+LE0zG9btbE/1Dfv7Oa2VRtZvqiDpkbjkCnNnH3swUPOyEsDXUQklJAz+PnAS+6+DsDMbgXOAyIL+LWv7qTRGunt6+PNPd3kHLr27qNw46ADFEO70SoHeXmIVwrzb5x3POPHNnDbklPY3dN3wP1EAc3IRaQmhAz42cDvS77eBLy7/ElmtgRYAtDW1jasA7y2q5tcztnT08eEcWNoNJjYnP9Y7u41r/SH9udOPZylC+Zy6+MHBvn3H9nAxzsO5chZk1j2iZPoyeUGDXMRkVpl7gNMd0f7wmYfBz7o7p8vfL0ImO/uFw/0Mx0dHb5q1aqqj7F6/Q56+nJs7+rh1Td209zUSHNTQ8UOvqsnf1H13BNmM6W5kZMOm0YuB719fbQUVtGMbWxQkItIqpjZanfvqPRYyBn8JuDtJV8fCrwa5QGOf9sUHtu4jaYGaJ3cTOeeXt7c3cvcg5t529RmcjnvX0UDcMKhU+jZl6PtoIkcPkMBLiLZFjLgHwfmmtnhwCvAJ4FPR3mAsWMbmd82g5d3dNLd4+zN5eju6WNvb47pU8fpfp0iUteCBby77zOzLwI/J79M8kZ3fybq44wd28jRB0+N+mVFRFIv6Dp4d78XuDfkMUREpLKGpAcgIiJhKOBFRDJKAS8iklEKeBGRjAr2RqeRMLOtwIZh/tgMYFuA4dSyejxn0HnXm3o875Gc82Hu3lrpgZoK+JEws1UDvYsrq+rxnEHnnfQ44laP5x31OauiERHJKAW8iEhGZSHglyc9gATU4zmDzrve1ON5R3rOqe/gRUSksizM4EVEpAIFvIhIRqUi4M1soZk9b2YvmdnfVXjczGxZ4fG1ZnZyEuOMWhXn/ZnC+a41s9+Y2YlJjDNqQ513yfPeZWZ9ZvaxOMcXQjXnbGZnmNkaM3vGzB6Me4whVPFnfIqZ3WNmTxbO+8IkxhklM7vRzLaY2dMDPB5dnrl7Tf8iv9Xw74A5wFjgSeDYsuecA/wP+Vt7nAI8mvS4YzrvU4Fphc8/VC/nXfK8FeR3K/1Y0uOO4fd6Kvn7GbcVvp6Z9LhjOu+vAFcWPm8FdgBjkx77KM/7fcDJwNMDPB5ZnqVhBt9/82537wGKN+8udR5ws+c9Akw1s0PiHmjEhjxvd/+Nu79R+PIR8nfNSrtqfr8BLgbuALbEObhAqjnnTwN3uvtGAHevl/N2oMXMDJhEPuD3xTvMaLn7SvLnMZDI8iwNAV/p5t2zR/CctBnuOS0m/7d+2g153mY2G/hT4LoYxxVSNb/XRwHTzOxXZrbazD4b2+jCqea8rwXeQf52n08BS909F8/wEhNZngW94UdEKt1zr3xtZzXPSZuqz8nMziQf8KcFHVE8qjnvbwGXu3tffmKXetWc8xjgj4AFwHjgYTN7xN1fCD24gKo57w8Ca4CzgCOA+83sIXffFXhsSYosz9IQ8NXcvDv4Db4TUNU5mdkJwPXAh9x9e0xjC6ma8+4Abi2E+wzgHDPb5+53xzLC6FX7Z3ybu3cBXWa2EjgRSHPAV3PeFwLf9Hw5/ZKZvQwcAzwWzxATEVmepaGi6b95t5mNJX/z7p+UPecnwGcLV59PAXa6++a4BxqxIc/bzNqAO4FFKZ/JlRryvN39cHdvd/d24EfAX6Y43KG6P+M/Bk43szFmNgF4N/BczOOMWjXnvZH8v1ows1nA0cC6WEcZv8jyrOZn8D7AzbvN7KLC49eRX0lxDvASsJv83/qpVuV5/wMwHfhOYTa7z1O++16V550p1Zyzuz9nZvcBa4EccL27V1xmlxZV/l5/A7jJzJ4iX11c7u6p3kLYzH4AnAHMMLNNwD8CTRB9nmmrAhGRjEpDRSMiIiOggBcRySgFvIhIRingRUQySgEvIpJRCnjJhMKukmvM7Gkz+2FhrfhIX+um4g6VZna9mR07yHPPMLNTS76+KCPbCEgGKOAlK/a4+zx3Pw7oAS4qfdDMGkfyou7+eXd/dpCnnEF+V8/i869z95tHciyRqCngJYseAo4szK5/aWa3AE+ZWaOZ/auZPV7YZ/sL0L//9rVm9qyZ/QyYWXyhwuZeHYXPF5rZE4W9yR8ws3byf5H8VeFfD6eb2T+Z2ZcKz59nZo8UjnWXmU0rec0rzewxM3vBzE6P9z+P1IuafyeryHCY2Rjye+PfV/jWfOA4d3/ZzJaQf9v3u8xsHPC/ZvYL4CTyb4E/HphFft/1G8tetxX4LvC+wmsd5O47zOw64C13/7fC8xaU/NjNwMXu/qCZfZ38OxYvLTw2xt3nm9k5he+/P+L/FCIKeMmM8Wa2pvD5Q8AN5KuTx9z95cL3zwZOsD/cAWoKMJf8DRh+4O59wKtmtqLC658CrCy+lrsPtp83ZjYFmOruxTsvfQ/4YclT7ix8XA20V3WGIsOkgJes2OPu80q/Udifp6v0W+Rn1D8ve945DL0dq1XxnOHYW/jYh/4/lEDUwUs9+TnwF2bWBGBmR5nZRGAl8MlCR38IcGaFn30Y+GMzO7zwswcVvt8JtJQ/2d13Am+U9OuLgEzcR1XSQzMHqSfXk69DnrD89H4rcD5wF/kbSjxFfn/1A4LY3bcWOvw7zayB/K0CPwDcA/zIzM4jfxvBUn8OXFdYsrmODOxyKumi3SRFRDJKFY2ISEYp4EVEMkoBLyKSUQp4EZGMUsCLiGSUAl5EJKMU8CIiGfX/T4eBsXN3IcUAAAAASUVORK5CYII=\n", | |
| "text/plain": [ | |
| "<Figure size 432x288 with 1 Axes>" | |
| ] | |
| }, | |
| "metadata": { | |
| "needs_background": "light" | |
| }, | |
| "output_type": "display_data" | |
| } | |
| ], | |
| "source": [ | |
| "linspace = sigmoid(np.linspace(-5, 5, 100))\n", | |
| "true_class = 0.0\n", | |
| "losses = np.apply_along_axis(lambda x: log_loss(np.array([true_class]), x), 0, linspace.reshape(1, -1))\n", | |
| "ax = sns.scatterplot(y=losses, x=linspace)\n", | |
| "ax.set_xlabel(\"Prediction\")\n", | |
| "ax.set_ylabel(\"Loss\");" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 5, | |
| "id": "protected-blank", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "image/png": "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\n", | |
| "text/plain": [ | |
| "<Figure size 432x288 with 1 Axes>" | |
| ] | |
| }, | |
| "metadata": { | |
| "needs_background": "light" | |
| }, | |
| "output_type": "display_data" | |
| } | |
| ], | |
| "source": [ | |
| "coef = np.linspace(0, 10, 100)\n", | |
| "loss = (coef - 5) ** 2\n", | |
| "\n", | |
| "line = lambda x: 2 * (x - 5)\n", | |
| "ax = sns.scatterplot(x=coef, y=loss)\n", | |
| "plt.plot([5, 10], line(np.array([5, 10])), color=\"red\", label=\"derivative\")\n", | |
| "plt.legend()\n", | |
| "ax.set_xlabel(\"Coefficient\")\n", | |
| "ax.set_ylabel(\"Loss\");" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 6, | |
| "id": "advance-shannon", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array([[ 0.8217988 , 0.91751578],\n", | |
| " [-0.42918603, -0.56585462],\n", | |
| " [-0.74825694, 2.65235002],\n", | |
| " [-0.70705092, -0.6665801 ],\n", | |
| " [ 0.85580474, 0.89543249]])" | |
| ] | |
| }, | |
| "execution_count": 6, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "X = np.random.normal(size=200).reshape(-1, 2)\n", | |
| "X[:5]" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 7, | |
| "id": "thick-ocean", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array([1., 0., 1., 0., 1.])" | |
| ] | |
| }, | |
| "execution_count": 7, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "y = np.round(sigmoid((2 * X[:, 0] + 3 * X[:, 1])))\n", | |
| "y[:5]" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 8, | |
| "id": "advanced-bread", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "initial weights [0.08587948 0.0992165 ] \n", | |
| "\n", | |
| "loss: 0.6327634538180974\n", | |
| "weights: [0.10381509 0.12992812]\n", | |
| "loss: 0.6208585100088778\n", | |
| "weights: [0.12138765 0.15986252]\n", | |
| "loss: 0.6095124985129491\n", | |
| "weights: [0.1386076 0.18904531]\n", | |
| "loss: 0.5986938306967757\n", | |
| "weights: [0.15548549 0.21750214]\n", | |
| "loss: 0.5883723485148004\n", | |
| "weights: [0.1720319 0.24525852]\n", | |
| "loss: 0.5785193747199389\n", | |
| "weights: [0.18825734 0.27233962]\n", | |
| "loss: 0.5691077334359955\n", | |
| "weights: [0.20417221 0.29877017]\n", | |
| "loss: 0.5601117464238963\n", | |
| "weights: [0.21978674 0.32457433]\n", | |
| "loss: 0.551507209956081\n", | |
| "weights: [0.23511096 0.34977561]\n", | |
| "loss: 0.5432713566872078\n", | |
| "weights: [0.25015464 0.37439684]\n" | |
| ] | |
| } | |
| ], | |
| "source": [ | |
| "learning_rate = 0.1\n", | |
| "nepoch = 10\n", | |
| "\n", | |
| "w = np.random.normal(scale=0.1, size=2).reshape(1, 2)\n", | |
| "print(\"initial weights\", w[0], \"\\n\")\n", | |
| "for i in range(nepoch):\n", | |
| " preds = sigmoid(w.dot(X.T))[0]\n", | |
| " gradient = np.sum((y - preds).reshape(-1, 1) * X, axis=0).reshape(1, 2) / X.shape[0]\n", | |
| " w += learning_rate * gradient\n", | |
| " #print(gradient)\n", | |
| "\n", | |
| " preds = sigmoid(w.dot(X.T))[0]\n", | |
| " loss = log_loss(y, preds)\n", | |
| "\n", | |
| " print(\"loss:\", loss)\n", | |
| " print(\"weights:\", w[0])" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "statutory-rotation", | |
| "metadata": { | |
| "slideshow": { | |
| "slide_type": "slide" | |
| } | |
| }, | |
| "source": [ | |
| "# Learning to rank\n", | |
| "\n", | |
| "- A supervised learning task\n", | |
| "- Attempts to optimally order a list (or lists) of items.\n", | |
| "- Usually used for information retrieval: search engines, recommenders.\n", | |
| "\n", | |
| "There are currently 3 general kinds of methods for this task.\n", | |
| "\n", | |
| "- Point-wise\n", | |
| " - Basically just regression or classification\n", | |
| " - Considers each list item independently of others\n", | |
| " - Rankprop (MSE regression with adjustments)\n", | |
| " - https://proceedings.neurips.cc/paper/1995/hash/36a16a2505369e0c922b6ea7a23a56d2-Abstract.html\n", | |
| " - P-rank (ordinal regression)\n", | |
| " - https://papers.nips.cc/paper/2001/file/5531a5834816222280f20d1ef9e95f69-Paper.pdf\n", | |
| " - mcrank (multiple classification/ordinal classification)\n", | |
| " - http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.992.5789&rep=rep1&type=pdf\n", | |
| "\n", | |
| "- Pair-wise\n", | |
| " - Consider pairs of items and decide which should be higher\n", | |
| " - RankingSVM/RankSVM/SVMrank\n", | |
| " - https://www.hindawi.com/journals/cin/2017/4629534/\n", | |
| " - https://www.cs.cornell.edu/people/tj/svm_light/svm_rank.html\n", | |
| " - https://doi.org/10.1007/978-3-642-01307-2_39\n", | |
| " - RankBoost\n", | |
| " - https://www.jmlr.org/papers/volume4/freund03a/freund03a.pdf\n", | |
| " - Ranknet\n", | |
| " - https://www.microsoft.com/en-us/research/wp-content/uploads/2005/08/icml_ranking.pdf\n", | |
| " - LambdaRank/LambdaMART\n", | |
| " - https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/lambdarank.pdf\n", | |
| "\n", | |
| "- List-wise\n", | |
| " - Optimise the ordering of whole lists of items at a time\n", | |
| " - Requires numerous lists of items\n", | |
| " - Often used for information retrieval (match relevance) or product display order (likelihood of sale)\n", | |
| " - e.g. LambdaRank/LambdaMART\n", | |
| " - softRank\n", | |
| " - listRank\n", | |
| " - listMLE\n", | |
| " \n", | |
| " \n", | |
| " \n", | |
| "Recommended resource from Guido Zuccon\n", | |
| "- Hang Li, \"Learning to Rank for Information Retrieval and Natural Language Processing\" https://www.morganclaypool.com/doi/abs/10.2200/S00607ED2V01Y201410HLT026\n", | |
| "\n", | |
| " \n", | |
| "\n", | |
| "LTR is closely related to (and is incorporated in) some recommender system problems, but the literature doesn't seem to intersect much.\n", | |
| "Lots more information available on recommenders so I'd add it as a search term if you're trying to learn this stuff.\n", | |
| "\n", | |
| "Collaborative filtering might form part of your initial selection of documents to present.\n", | |
| "Ranking and reranking in recommenders often don't use the term \"learning to rank\" so look around.\n", | |
| "\n", | |
| "- https://arxiv.org/ftp/arxiv/papers/1205/1205.2618.pdf\n", | |
| "- https://arxiv.org/pdf/1812.04109.pdf\n", | |
| "- https://arxiv.org/pdf/1904.06813.pdf\n", | |
| "- https://arxiv.org/abs/1804.05936" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "french-macro", | |
| "metadata": { | |
| "slideshow": { | |
| "slide_type": "slide" | |
| } | |
| }, | |
| "source": [ | |
| "## Data and labels\n", | |
| "\n", | |
| "Selection of features usually some indicator of relevance to a query term.\n", | |
| "\n", | |
| "- Distance/Similarity of word/sentence embeddings\n", | |
| "- PageRank\n", | |
| "- Clicks\n", | |
| "- User history\n", | |
| "\n", | |
| "Could also just be general features used for regression or classification and a query term as categorical factor.\n", | |
| "\n", | |
| "> A lot of feature engineering has historically been done in search engines (so you can imagine how happy people are to be moving to DL).\n", | |
| "> Complex indexing time features are often computed.\n", | |
| "> Complex query time features are more challenging, as they add to the query latency.\n", | |
| "> So often a cascade of rankers is employed (e.g. Twitter uses 3 or 4 depths of cascades) with the complex query-time features being computed only at later stages.\n", | |
| ">\n", | |
| "> \\- Guido Zuccon\n", | |
| "\n", | |
| "Labels used are relevance or priority scores for each item in your training/test datasets (e.g. 0, 1, 2, 3, 4).\n", | |
| "\n", | |
| "Can also be pairwise decisions on whether one should be higher than the other (like a binary classifier for each pair)." | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "informed-window", | |
| "metadata": {}, | |
| "source": [ | |
| "## Why do I care about this stuff?\n", | |
| "\n", | |
| "\n", | |
| "#### Protein functional annotation\n", | |
| "\n", | |
| "Came about LTR when I was looking at new gene ontology term prediction methods and came across [GOLabeler](https://academic.oup.com/bioinformatics/article/34/14/2465/4924212).\n", | |
| "GoLabeler takes a protein sequence, performs several analyses and uses LTR to order the resulting GO terms by relevance (i.e. a multi-label problem).\n", | |
| "\n", | |
| "#### Disease resistance scoring\n", | |
| "\n", | |
| "Quantifying (phenotypically) complex traits and combinations of traits can be difficult.\n", | |
| "Scoring methods have been used for years, and researchers are hesitant/unable to move into more automated areas.\n", | |
| "\n", | |
| "Below is a typical scoring key used for a wheat necrotrophic pathogen (Ptr/Yellow spot)...\n", | |
| "\n", | |
| "\n", | |
| "\n", | |
| "> Dinglasan, E., Godwin, I.D., Mortlock, M.Y. et al. Resistance to yellow spot in wheat grown under accelerated growth > conditions. Euphytica 209, 693–707 (2016). https://doi.org/10.1007/s10681-016-1660-z\n", | |
| "\n", | |
| "\n", | |
| "It's difficult to assign a distinct quantitative score of disease severity (or even an ordinal one), but it's much easier to say that one leaf is more severely diseased than another.\n", | |
| "\n", | |
| "#### Protein candidate ranking.\n", | |
| "\n", | |
| "There is a class of proteins called \"effectors\" that we're interested in.\n", | |
| "Effectors share little sequence similarity but have some common characteristics related to size, charge, motifs etc.\n", | |
| "But not all indicators of these characteristics are perfect (e.g signal peptide prediction), and not all effectors have these characteristics (large size).\n", | |
| "\n", | |
| "The idea is to use LTR to learn a static sorting function, so that things with more of the indicator characteristics are nearer the top of the list in a big spreadsheet but we're not \"throwing out\" good candidates (jut a bit further down the list).\n", | |
| "\n", | |
| "Wrote a pipeline called [predector](https://github.com/ccdmb/predector) based on this idea, and it's super useful.\n", | |
| "\n", | |
| "\n", | |
| "#### A few other interesting cases\n", | |
| "\n", | |
| "- Drug selection for individual patients based on genomic information\n", | |
| " - https://ieeexplore.ieee.org/abstract/document/8395000?casa_token=U1XwW3DNc34AAAAA:gLBTlk1uCfJQZpxTi_emFrCGHB23WiFNL2engdCvI8yrQMTFao3rslhgvs3kLFfZ48k113I9WF7u\n", | |
| "- Ranking individual treatment effects (Uplift modelling)\n", | |
| " - https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=9311871&tag=1\n", | |
| "- Diseases name normalisation/standardisation\n", | |
| " - https://academic.oup.com/bioinformatics/article/29/22/2909/312804?login=true\n", | |
| "\n", | |
| "\n", | |
| "---\n", | |
| "\n", | |
| "\n", | |
| "Adjusting the objective of the modelling rather than changing requirements of researchers/input:\n", | |
| "\n", | |
| "- lower barrier to uptake by researchers (easier data generation, results closer to what they expect)\n", | |
| "- viable intermediate tools until traits can be broken down into separate components\n", | |
| "- potential for interactive reinforcement/labelling" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "welsh-ecology", | |
| "metadata": { | |
| "slideshow": { | |
| "slide_type": "slide" | |
| } | |
| }, | |
| "source": [ | |
| "## Metrics\n", | |
| "\n", | |
| "Specialised evaluation metrics are all list-wise.\n", | |
| "Point- and pair-wise algorithms use a different loss function during training.\n", | |
| "\n", | |
| "Binary relevance (0 or 1):\n", | |
| "- Mean average precision (MAP) - Like an AUC for ranking.\n", | |
| "- Mean reciprocal rank (MRR) - Position of first relevant item\n", | |
| "- accuracy@k (or precision, recall etc)\n", | |
| "\n", | |
| "Multi-level relevance (0, 1, 2, ...):\n", | |
| "- Normalised DCG (NDCG)\n", | |
| "- Expected reciprocal rank (ERR) https://dl.acm.org/doi/pdf/10.1145/1645953.1646033" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 9, | |
| "id": "correct-variation", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "0.6111111111111112" | |
| ] | |
| }, | |
| "execution_count": 9, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "def mrr(true, pred):\n", | |
| " \"\"\" Mean reciprocal rank\n", | |
| " \n", | |
| " all you care about is the top hit.\n", | |
| " \n", | |
| " true: array of 0 or 1 indicating relevance\n", | |
| " pred: scoring function producing sorted list in descending order.\n", | |
| " \"\"\"\n", | |
| " \n", | |
| " import numpy as np\n", | |
| " from statistics import mean\n", | |
| " \n", | |
| " reciprocal_ranks = []\n", | |
| " \n", | |
| " for i in range(true.shape[0]):\n", | |
| " order = np.argsort(pred[i])[::-1]\n", | |
| " ranked = true[i][order]\n", | |
| "\n", | |
| " # Find the position of the first element ranked as relevant\n", | |
| " list_rank = np.argmax(ranked > 0) + 1 # Correct for zero indexing.\n", | |
| "\n", | |
| " reciprocal_ranks.append(1 / list_rank)\n", | |
| "\n", | |
| " return mean(reciprocal_ranks) # Mean of list scores\n", | |
| "\n", | |
| "p = np.array([\n", | |
| " [3, 2, 1],\n", | |
| " [3, 2, 1],\n", | |
| " [3, 2, 1]\n", | |
| "])\n", | |
| "\n", | |
| "t = np.array([\n", | |
| " [0, 0, 1],\n", | |
| " [0, 1, 1],\n", | |
| " [1, 1, 0]\n", | |
| "])\n", | |
| "\n", | |
| "mrr(t, p)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 10, | |
| "id": "designed-blowing", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array([0.33333333, 0.58333333, 1. ])" | |
| ] | |
| }, | |
| "execution_count": 10, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "def average_precision(true, pred):\n", | |
| " import numpy as np\n", | |
| " \n", | |
| " average_precisions = []\n", | |
| " \n", | |
| " for i in range(true.shape[0]):\n", | |
| " order = np.argsort(pred[i])[::-1]\n", | |
| " ranked = true[i][order]\n", | |
| " \n", | |
| " running_nrelevant = np.cumsum(ranked)\n", | |
| " running_rank = np.arange(1, ranked.shape[0] + 1)\n", | |
| " running_precision = running_nrelevant[ranked > 0] / running_rank[ranked > 0]\n", | |
| "\n", | |
| " average_precisions.append(running_precision.mean())\n", | |
| "\n", | |
| " return np.array(average_precisions)\n", | |
| "\n", | |
| "p = np.array([\n", | |
| " [3, 2, 1],\n", | |
| " [3, 2, 1],\n", | |
| " [3, 2, 1]\n", | |
| "])\n", | |
| "\n", | |
| "t = np.array([\n", | |
| " [0, 0, 1],\n", | |
| " [0, 1, 1],\n", | |
| " [1, 1, 0]\n", | |
| "])\n", | |
| "\n", | |
| "average_precision(t, p)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 11, | |
| "id": "coastal-fluid", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "0.6388888888888888" | |
| ] | |
| }, | |
| "execution_count": 11, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "def map_(true, pred):\n", | |
| " return average_precision(true, pred).mean()\n", | |
| "\n", | |
| "map_(t, p)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "global-requirement", | |
| "metadata": {}, | |
| "source": [ | |
| "### NDCG\n", | |
| "\n", | |
| "Applies a \"discount\" to scores based on sorted list index.\n", | |
| "There are multiple versions, and different implementations may yield different results.\n", | |
| "\n", | |
| "$ DCG@N = \\sum_{i=1}^{N} \\frac{y_i}{log(1+i)} $\n", | |
| "\n", | |
| "The one people actually use:\n", | |
| "\n", | |
| "$ DCG@N = \\sum_{i=1}^{N} \\frac{2^{y_i} - 1}{log(1+i)} $\n", | |
| "\n", | |
| "> The version with $2^{y{_i}}$ is often preferred as has a smoother gain curve for the different grades of relevance - Guido Zuccon\n", | |
| "\n", | |
| "\n", | |
| "To find the normalised DCG (NDCG) you divide the DCG by the ideal DCG.\n", | |
| "Ideal DCG is obtained by a perfect predicted ordering wrt your relevance labels.\n", | |
| "\n", | |
| "Great overview and analysis of NDCG in:\n", | |
| "\n", | |
| "Clémençon S., Lugosi G., Vayatis N. (2005) Ranking and Scoring Using Empirical Risk Minimization. In: Auer P., Meir R. (eds) Learning Theory. COLT 2005. Lecture Notes in Computer Science, vol 3559. Springer, Berlin, Heidelberg. https://doi.org/10.1007/11503415_1" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 12, | |
| "id": "hollywood-flood", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array([0.5 , 1.13092975, 1.63092975])" | |
| ] | |
| }, | |
| "execution_count": 12, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "def discounted_cumulative_gain(true, pred, at=None, original=False):\n", | |
| " import numpy as np\n", | |
| "\n", | |
| " dcgs = []\n", | |
| "\n", | |
| " for i in range(true.shape[0]):\n", | |
| " order = np.argsort(pred[i])[::-1]\n", | |
| " ranked = true[i][order]\n", | |
| "\n", | |
| " if at is not None:\n", | |
| " # Only evaluating in top k\n", | |
| " ranked = ranked[:at]\n", | |
| "\n", | |
| " if original:\n", | |
| " # Ranked score divided by rank of prediction\n", | |
| " # Note log2 means values with higher ranks (i.e. less relevant)\n", | |
| " # has weaker effect on score.\n", | |
| " ratios = ranked / np.log2(np.arange(1, ranked.shape[0] + 1) + 1)\n", | |
| " else:\n", | |
| " # Alternative ratio to make emphasis on most relevant stronger.\n", | |
| " ratios = ((2 ** ranked) - 1) / np.log2(np.arange(1, ranked.shape[0] + 1) + 1)\n", | |
| " dcgs.append(ratios.sum())\n", | |
| "\n", | |
| " return np.array(dcgs)\n", | |
| "\n", | |
| "p = np.array([\n", | |
| " [3, 2, 1],\n", | |
| " [3, 2, 1],\n", | |
| " [3, 2, 1]\n", | |
| "])\n", | |
| "\n", | |
| "t = np.array([\n", | |
| " [0, 0, 1],\n", | |
| " [0, 1, 1],\n", | |
| " [1, 1, 0]\n", | |
| "])\n", | |
| "\n", | |
| "discounted_cumulative_gain(t, p)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 13, | |
| "id": "sustained-player", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array([1. , 1.63092975, 1.63092975])" | |
| ] | |
| }, | |
| "execution_count": 13, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "discounted_cumulative_gain(t, t)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 14, | |
| "id": "cardiovascular-invite", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array([0.5 , 0.6934264, 1. ])" | |
| ] | |
| }, | |
| "execution_count": 14, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "discounted_cumulative_gain(t, p) / discounted_cumulative_gain(t, t)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 15, | |
| "id": "flexible-laugh", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "0.7311421345390903" | |
| ] | |
| }, | |
| "execution_count": 15, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "def normalised_dcg(true, pred, at=None, original=False):\n", | |
| " # Actual dcg\n", | |
| " dcgs = discounted_cumulative_gain(true, pred, at, original=original)\n", | |
| "\n", | |
| " # Maximum possible dcg\n", | |
| " ideal_dcgs = discounted_cumulative_gain(true, true, at, original=original)\n", | |
| "\n", | |
| " # Handle edge case where ideal dcg is zero\n", | |
| " # This could happen if you have no relevant items in your list.\n", | |
| " dcgs[ideal_dcgs == 0.] = 0\n", | |
| " ideal_dcgs[ideal_dcgs == 0.] = 1\n", | |
| "\n", | |
| " return (dcgs / ideal_dcgs).mean()\n", | |
| "\n", | |
| "normalised_dcg(t, p)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 16, | |
| "id": "victorian-wallet", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "MRR 0.625\n", | |
| "MAP [0.8055555555555555, 0.325]\n", | |
| "MAP 0.5652777777777778\n", | |
| "NDCG [0.90602544 0.50126584]\n", | |
| "NDCG 0.7036456354382847\n" | |
| ] | |
| } | |
| ], | |
| "source": [ | |
| "p = np.array([\n", | |
| " np.arange(5)[::-1],\n", | |
| " np.arange(5)[::-1]\n", | |
| "])\n", | |
| "\n", | |
| "t = np.array([\n", | |
| " [1, 0, 1, 1, 0],\n", | |
| " [0, 0, 0, 1, 1]\n", | |
| "])\n", | |
| "\n", | |
| "print(\"MRR\", mrr(t, p))\n", | |
| "\n", | |
| "print(\"MAP\", [map_(ti.reshape(1, -1), pi.reshape(1, -1)) for ti, pi in zip(t, p)])\n", | |
| "print(\"MAP\", map_(t, p))\n", | |
| "\n", | |
| "print(\"NDCG\", discounted_cumulative_gain(t, p) / discounted_cumulative_gain(t, t))\n", | |
| "print(\"NDCG\", normalised_dcg(t, p))" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 17, | |
| "id": "according-banks", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array([0.94881075, 0.59089746, 0.66534971, 0.88545988])" | |
| ] | |
| }, | |
| "execution_count": 17, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "# A multi-level example\n", | |
| "\n", | |
| "true = np.array([\n", | |
| " [3, 2, 3, 0, 1, 2],\n", | |
| " [0, 1, 2, 2, 3, 3],\n", | |
| " [0, 3, 0, 3, 0, 3],\n", | |
| " [3, 0, 3, 0, 3, 0],\n", | |
| "])\n", | |
| "\n", | |
| "pred = np.array([\n", | |
| " [5, 4, 3, 2, 1, 0],\n", | |
| " [5, 4, 3, 2, 1, 0],\n", | |
| " [5, 4, 3, 2, 1, 0],\n", | |
| " [5, 4, 3, 2, 1, 0],\n", | |
| "])\n", | |
| "\n", | |
| "discounted_cumulative_gain(true, pred) / discounted_cumulative_gain(true, true)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "inside-harmony", | |
| "metadata": {}, | |
| "source": [ | |
| "Sci-kit learn has an implementation of ndcg but it only uses the original score function (rather than the in-practise standard $2^i - 1$)." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 18, | |
| "id": "searching-excellence", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "0.7931987348917845" | |
| ] | |
| }, | |
| "execution_count": 18, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "normalised_dcg(true, pred, original=True)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 19, | |
| "id": "floral-nitrogen", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "0.7931987348917845" | |
| ] | |
| }, | |
| "execution_count": 19, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "from sklearn.metrics import ndcg_score\n", | |
| "ndcg_score(true, pred)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "adolescent-trader", | |
| "metadata": {}, | |
| "source": [ | |
| "Note that using priorities `1, 2, ...` instead of `0, 1, ...` will give different results.\n", | |
| "\n", | |
| "\"Correct\" usage is to start at 0 for irrelevant." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 20, | |
| "id": "abroad-fluid", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "0.7726294517134219" | |
| ] | |
| }, | |
| "execution_count": 20, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "normalised_dcg(true, pred)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 21, | |
| "id": "northern-george", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "0.7955308580375091" | |
| ] | |
| }, | |
| "execution_count": 21, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "normalised_dcg(true + 1, pred)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "inner-confidence", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "combined-equality", | |
| "metadata": {}, | |
| "source": [ | |
| "NDCG can be a bit difficult to interpret, and varies with length of list and labels used.\n", | |
| "The worst possible order will still have NDCG > 0." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 22, | |
| "id": "loving-cliff", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array([0.59089746, 0.59089746, 0.55080959, 0.55080959])" | |
| ] | |
| }, | |
| "execution_count": 22, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "discounted_cumulative_gain(true, (true.max() - true)) / discounted_cumulative_gain(true, true)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "specialized-leave", | |
| "metadata": {}, | |
| "source": [ | |
| "And the average NDCG given by randomly ordered lists can be quite high." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 23, | |
| "id": "optimum-jackson", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "from numpy.random import default_rng" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 24, | |
| "id": "detailed-albert", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "rng = default_rng()" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 25, | |
| "id": "parental-trinity", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array([[2, 3, 0, 4, 5, 1],\n", | |
| " [4, 5, 1, 3, 2, 0],\n", | |
| " [2, 5, 1, 0, 4, 3],\n", | |
| " [0, 3, 5, 4, 2, 1]])" | |
| ] | |
| }, | |
| "execution_count": 25, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "pred2 = np.array([\n", | |
| " rng.choice(np.arange(6), 6, replace=False),\n", | |
| " rng.choice(np.arange(6), 6, replace=False),\n", | |
| " rng.choice(np.arange(6), 6, replace=False),\n", | |
| " rng.choice(np.arange(6), 6, replace=False)\n", | |
| "])\n", | |
| "pred2" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 26, | |
| "id": "unnecessary-beauty", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array([0.62819453, 0.62819453, 0.87107854, 0.83854653])" | |
| ] | |
| }, | |
| "execution_count": 26, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "discounted_cumulative_gain(true, pred2) / discounted_cumulative_gain(true, true)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "geological-burke", | |
| "metadata": {}, | |
| "source": [ | |
| "Rather than thinking about \"how much better than random we are are doing\", it might be better to think of \"how far away from the optimal solution are we?\".\n", | |
| "\n", | |
| "\n", | |
| "#### Expected reciprocal rank (ERR)\n", | |
| "\n", | |
| "People don't seem to use this so much.\n", | |
| "\n", | |
| "$$\n", | |
| "ERR = \\sum_{r=1}^{N} \\frac{1}{r}R_r\\prod_{i=1}^{r-1}(1-R_i)\n", | |
| "$$\n", | |
| "\n", | |
| "if $y_m$ is the maximum label value,\n", | |
| "\n", | |
| "$$\n", | |
| "R_i = \\frac{2^{y_i}-1}{2^{y_m}}\n", | |
| "$$" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "incomplete-clinton", | |
| "metadata": {}, | |
| "source": [ | |
| "## Pointwise ranking - MCrank\n", | |
| "\n", | |
| "Learning to rank as \"multiple ordinal classification\" with boosted trees.\n", | |
| "\n", | |
| "http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.992.5789&rep=rep1&type=pdf\n", | |
| "\n", | |
| "- Show that there's a relationship between DCG and multiple classification.\n", | |
| "- Two multiple classification approaches (and a regression based solution) were quite successful compared to LambdaRank.\n", | |
| "\n", | |
| "Expected rank:\n", | |
| "\n", | |
| "1. Train a multi-class predictor (one class for each rank level: 0, 1, 2, ...).\n", | |
| "2. Multiply the classification probabilities with some monotonically increasing function of the rank levels.\n", | |
| "\n", | |
| "$$\n", | |
| "S_i = \\sum_{i=0}^{K - 1}\\bar{P}(i = k)f(k)\n", | |
| "$$\n", | |
| "\n", | |
| "3. Sort by $S_i$\n", | |
| "\n", | |
| "\n", | |
| "I'll define some example data that i'll use for all of the examples." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 27, | |
| "id": "massive-astronomy", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array([[ 0.82418808, 0.479966 ],\n", | |
| " [ 1.17346801, 0.90904807],\n", | |
| " [-0.57172145, -0.10949727],\n", | |
| " [ 0.01902826, -0.94376106],\n", | |
| " [ 0.64057315, -0.78644317]])" | |
| ] | |
| }, | |
| "execution_count": 27, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "np.random.seed(666)\n", | |
| "\n", | |
| "X = np.random.normal(size=20000).reshape(-1, 2)\n", | |
| "X[:5]" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 28, | |
| "id": "robust-pollution", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array([0., 1., 2., 3.])" | |
| ] | |
| }, | |
| "execution_count": 28, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "def quantise(arr, maxpriority=3):\n", | |
| " return (maxpriority * (arr - arr.min()) / (arr.max() - arr.min())).round()\n", | |
| "\n", | |
| "# This just makes sure all have all relevance levels\n", | |
| "y = np.zeros(10000)\n", | |
| "i = 0\n", | |
| "for j in range(100, 10001, 100):\n", | |
| " y[i:j] = quantise((5 * X[i:j, 0] + 1.5 * X[i:j, 1]))\n", | |
| " i = j\n", | |
| "\n", | |
| "np.unique(y)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 29, | |
| "id": "invisible-complement", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array([[0., 0., 1., 0.],\n", | |
| " [0., 0., 1., 0.],\n", | |
| " [0., 1., 0., 0.],\n", | |
| " [0., 1., 0., 0.]])" | |
| ] | |
| }, | |
| "execution_count": 29, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "from sklearn.preprocessing import OneHotEncoder\n", | |
| "from sklearn.ensemble import RandomForestClassifier\n", | |
| "\n", | |
| "\n", | |
| "y_ohe = OneHotEncoder(drop=None, sparse=False).fit_transform(y.reshape(-1, 1))\n", | |
| "y_ohe[:4,]" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 30, | |
| "id": "operating-cattle", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array([[0., 0., 1., 0.],\n", | |
| " [0., 0., 1., 0.],\n", | |
| " [0., 1., 0., 0.],\n", | |
| " [0., 1., 0., 0.],\n", | |
| " [0., 0., 1., 0.]])" | |
| ] | |
| }, | |
| "execution_count": 30, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "model = RandomForestClassifier(max_depth=4)\n", | |
| "model.fit(X, y_ohe)\n", | |
| "model.predict(X)[:5]" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 31, | |
| "id": "seven-official", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array([1.97749313, 2.0729215 , 1.06639982, ..., 1.61335939, 1.00231679,\n", | |
| " 1.8529369 ])" | |
| ] | |
| }, | |
| "execution_count": 31, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "proba = model.predict_proba(X)\n", | |
| "preds = np.array([p[:, 1] for p in proba]).T\n", | |
| "preds = np.sum(preds * np.arange(4), axis=1)\n", | |
| "preds" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 32, | |
| "id": "cheap-pierre", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "[(3.0, 2.677313467063255),\n", | |
| " (3.0, 2.664500193638078),\n", | |
| " (3.0, 2.662675469878174),\n", | |
| " (3.0, 2.6614690082257124),\n", | |
| " (3.0, 2.656291430113934),\n", | |
| " (3.0, 2.6503448633931153),\n", | |
| " (3.0, 2.6503136455515315),\n", | |
| " (3.0, 2.648891314606223),\n", | |
| " (3.0, 2.648891314606223),\n", | |
| " (3.0, 2.648891314606223)]" | |
| ] | |
| }, | |
| "execution_count": 32, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "sorted(zip(y, preds), key=lambda t: t[1], reverse=True)[:10]" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 33, | |
| "id": "damaged-painting", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "[(0.0, 0.3397197218572301),\n", | |
| " (0.0, 0.335251017634349),\n", | |
| " (0.0, 0.33390507925899887),\n", | |
| " (0.0, 0.33352850084104024),\n", | |
| " (0.0, 0.33042760238756513),\n", | |
| " (0.0, 0.3258391060681249),\n", | |
| " (0.0, 0.32583766825874333),\n", | |
| " (0.0, 0.3244991526954715),\n", | |
| " (0.0, 0.3191379092655178),\n", | |
| " (0.0, 0.3114470766832209)]" | |
| ] | |
| }, | |
| "execution_count": 33, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "sorted(zip(y, preds), key=lambda t: t[1], reverse=True)[-10:]" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 34, | |
| "id": "heated-decision", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "0.9940772372662219" | |
| ] | |
| }, | |
| "execution_count": 34, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "normalised_dcg(y.reshape(1, -1), preds.reshape(1, -1))" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "boxed-paradise", | |
| "metadata": {}, | |
| "source": [ | |
| "Ordinal classification:\n", | |
| "\n", | |
| "1. Train a classifier to predict cumulative probabilities i.e. $\\bar{P}(y_i \\le k)$\n", | |
| "2. Then find $\\bar{P}(y_i=k) = \\bar{P}(y_i \\le k) − \\bar{P}(y_i \\le k−1)$\n", | |
| "3. Calculate scores as before." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 35, | |
| "id": "naval-builder", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array([[0., 0., 1., 1.],\n", | |
| " [0., 0., 1., 1.],\n", | |
| " [0., 1., 1., 1.],\n", | |
| " ...,\n", | |
| " [0., 0., 1., 1.],\n", | |
| " [0., 1., 1., 1.],\n", | |
| " [0., 0., 1., 1.]])" | |
| ] | |
| }, | |
| "execution_count": 35, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "y_ohe2 = np.zeros((10000, 4))\n", | |
| "y_ohe2[:, 0] = (y <= 0).astype(int)\n", | |
| "y_ohe2[:, 1] = (y <= 1).astype(int)\n", | |
| "y_ohe2[:, 2] = (y <= 2).astype(int)\n", | |
| "y_ohe2[:, 3] = (y <= 3).astype(int)\n", | |
| "y_ohe2" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 36, | |
| "id": "effective-capacity", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array([[0., 0., 1., 1.],\n", | |
| " [0., 0., 1., 1.],\n", | |
| " [0., 1., 1., 1.],\n", | |
| " [0., 1., 1., 1.],\n", | |
| " [0., 0., 1., 1.]])" | |
| ] | |
| }, | |
| "execution_count": 36, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "model = RandomForestClassifier(max_depth=4)\n", | |
| "model.fit(X, y_ohe2)\n", | |
| "model.predict(X)[:5]" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 37, | |
| "id": "about-vision", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array([[0.00156794, 0.08226345, 0.93272601, 1. ],\n", | |
| " [0.0016215 , 0.05687561, 0.84154191, 1. ],\n", | |
| " [0.04926611, 0.87319555, 0.99565484, 1. ],\n", | |
| " ...,\n", | |
| " [0.01627381, 0.4333639 , 0.98515771, 1. ],\n", | |
| " [0.11859363, 0.90130716, 0.99911182, 1. ],\n", | |
| " [0.01299227, 0.17832554, 0.96272132, 1. ]])" | |
| ] | |
| }, | |
| "execution_count": 37, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "proba = model.predict_proba(X)\n", | |
| "proba = np.array([p[:, -1] for p in proba]).T\n", | |
| "proba" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 38, | |
| "id": "bound-replica", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array([1.9834426 , 2.09996098, 1.0818835 , 1.19870506, 1.91509632])" | |
| ] | |
| }, | |
| "execution_count": 38, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "preds = np.zeros_like(proba)\n", | |
| "preds[:, 3] = proba[:, 3] - proba[:, 2]\n", | |
| "preds[:, 2] = proba[:, 2] - proba[:, 1]\n", | |
| "preds[:, 1] = proba[:, 1] - proba[:, 0]\n", | |
| "preds[:, 0] = proba[:, 0]\n", | |
| "\n", | |
| "preds = np.sum(preds * np.arange(4), axis=1)\n", | |
| "preds[:5]" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 39, | |
| "id": "blond-headquarters", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "[(3.0, 2.659835580785948),\n", | |
| " (3.0, 2.618482471782334),\n", | |
| " (3.0, 2.6177394743764864),\n", | |
| " (3.0, 2.6154220385465967),\n", | |
| " (3.0, 2.6091923047850396),\n", | |
| " (3.0, 2.6071536935013717),\n", | |
| " (3.0, 2.605864341426174),\n", | |
| " (3.0, 2.6048851218042937),\n", | |
| " (3.0, 2.603226828313331),\n", | |
| " (3.0, 2.603226828313331)]" | |
| ] | |
| }, | |
| "execution_count": 39, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "sorted(zip(y, preds), key=lambda t: t[1], reverse=True)[:10]" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 40, | |
| "id": "fabulous-expert", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "[(0.0, 0.38541689664503054),\n", | |
| " (0.0, 0.3794059606745688),\n", | |
| " (0.0, 0.3778614518262179),\n", | |
| " (0.0, 0.3766454173898445),\n", | |
| " (0.0, 0.37562909145854884),\n", | |
| " (0.0, 0.37473664556868047),\n", | |
| " (0.0, 0.3728656881162348),\n", | |
| " (0.0, 0.3674908811208747),\n", | |
| " (0.0, 0.3643410653153032),\n", | |
| " (0.0, 0.3609506085625409)]" | |
| ] | |
| }, | |
| "execution_count": 40, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "sorted(zip(y, preds), key=lambda t: t[1], reverse=True)[-10:]" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 41, | |
| "id": "swiss-photograph", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "0.9937158387167305" | |
| ] | |
| }, | |
| "execution_count": 41, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "normalised_dcg(y.reshape(1, -1), preds.reshape(1, -1))" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "compatible-robert", | |
| "metadata": {}, | |
| "source": [ | |
| "## Pairwise approaches\n", | |
| "\n", | |
| "Essentially train a binary classifier on differences of pairs of items.\n", | |
| "\n", | |
| "Earlier pairwise ranking SVMs work with a transformation of pairs.\n", | |
| "\n", | |
| "$$\n", | |
| "X_{pairij} = X_i - X_j \\\\\n", | |
| "y_{pairij} = \\left\\{\\begin{array}{lr}\n", | |
| " -1, & \\text{if } y_i \\lt y_j \\\\\n", | |
| " 0, & \\text{if } y_i = y_j \\\\\n", | |
| " 1, & \\text{if } y_i \\gt y_j\n", | |
| " \\end{array}\\right\\}\n", | |
| "$$\n", | |
| "\n", | |
| "Since each pair appears twice you only really need one combination.\n", | |
| "- Good overview of earlier SVM approaches: https://doi.org/10.1007/978-3-642-01307-2_39" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 42, | |
| "id": "efficient-language", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "# Our larger dataset is a bit too big to do all combinations, so take a subset.\n", | |
| "X_sub = X[:1000]\n", | |
| "y_sub = y[:1000]" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 43, | |
| "id": "explicit-ireland", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "from itertools import combinations\n", | |
| "\n", | |
| "X_pairs = []\n", | |
| "y_pairs = []\n", | |
| "\n", | |
| "for i, j in combinations(range(X_sub.shape[0]), 2):\n", | |
| " if y_sub[i] > y_sub[j]:\n", | |
| " y_pairij = 1\n", | |
| " elif y_sub[i] < y_sub[j]:\n", | |
| " y_pairij = 0\n", | |
| " else:\n", | |
| " y_pairij = 0.5\n", | |
| "\n", | |
| " X_pairij = X_sub[i] - X_sub[j]\n", | |
| "\n", | |
| " X_pairs.append(X_pairij)\n", | |
| " y_pairs.append(y_pairij)\n", | |
| "\n", | |
| "X_pairs = np.array(X_pairs)\n", | |
| "y_pairs = np.array(y_pairs)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 44, | |
| "id": "nearby-cleaner", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array([[-0.34927993, -0.42908207],\n", | |
| " [ 1.39590954, 0.58946327],\n", | |
| " [ 0.80515982, 1.42372707],\n", | |
| " ...,\n", | |
| " [-0.8909495 , -0.06959686],\n", | |
| " [ 1.33606144, -0.54053795],\n", | |
| " [ 2.22701094, -0.47094109]])" | |
| ] | |
| }, | |
| "execution_count": 44, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "X_pairs" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 45, | |
| "id": "speaking-opposition", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array([0.5, 1. , 1. , ..., 0.5, 1. , 1. ])" | |
| ] | |
| }, | |
| "execution_count": 45, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "y_pairs" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 46, | |
| "id": "answering-roulette", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "def log_loss(true, pred):\n", | |
| " return ((true * np.log(pred)) + ((1 - true) * np.log(1 - pred))).sum() / -true.shape[0]\n", | |
| "\n", | |
| "def sigmoid(pred):\n", | |
| " return 1 / (1 + np.exp(-pred))" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "developing-rebound", | |
| "metadata": {}, | |
| "source": [ | |
| "train a quicky gradient descent model." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 47, | |
| "id": "frank-marble", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "loss: 0.4487852673800323\n", | |
| "weights: [1.19453351 0.38415072]\n" | |
| ] | |
| } | |
| ], | |
| "source": [ | |
| "learning_rate = 0.1\n", | |
| "nepoch = 100\n", | |
| "\n", | |
| "w = np.random.normal(scale=0.01, size=2).reshape(1, 2)\n", | |
| "for i in range(nepoch):\n", | |
| " preds = sigmoid(w.dot(X_pairs.T))[0]\n", | |
| " gradient = np.sum((y_pairs - preds).reshape(-1, 1) * X_pairs, axis=0).reshape(1, 2) / X_pairs.shape[0]\n", | |
| " w += learning_rate * gradient\n", | |
| "\n", | |
| " preds = sigmoid(w.dot(X_pairs.T))[0]\n", | |
| " loss = log_loss(y_pairs, preds)\n", | |
| "\n", | |
| "print(\"loss:\", loss)\n", | |
| "print(\"weights:\", w[0])" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "manufactured-seller", | |
| "metadata": {}, | |
| "source": [ | |
| "We can now use these weights to perform pairwise order comparisons using your favourite sorting algorithm." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 48, | |
| "id": "distant-updating", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "class Obs(object):\n", | |
| "\n", | |
| " def __init__(self, arr, weights):\n", | |
| " self.arr = arr\n", | |
| " self.weights = weights\n", | |
| " return\n", | |
| "\n", | |
| " def __repr__(self):\n", | |
| " arr = [round(x, ndigits=2) for x in self.arr]\n", | |
| " weights = [round(x, ndigits=2) for x in self.weights]\n", | |
| " return f\"Obs(arr={arr}, weights={weights})\"\n", | |
| "\n", | |
| " def predict(self, other):\n", | |
| " return sigmoid(self.weights.reshape(1, 2).dot(self.arr.reshape(2, 1) - other.arr.reshape(2, 1)))[0, 0]\n", | |
| "\n", | |
| " def __gt__(self, other):\n", | |
| " return self.predict(other) > 0.5\n", | |
| "\n", | |
| " def __lt__(self, other):\n", | |
| " return self.predict(other) < 0.5\n", | |
| "\n", | |
| " def __eq__(self, other):\n", | |
| " return self.arr == other.arr" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "continent-communications", | |
| "metadata": {}, | |
| "source": [ | |
| "Pythons sorting method will access the `__gt__` method to decide the order, which calls our predict function." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 49, | |
| "id": "raising-fighter", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "[Obs(arr=[-3.8, -1.3], weights=[1.19, 0.38]),\n", | |
| " Obs(arr=[-4.11, -0.11], weights=[1.19, 0.38]),\n", | |
| " Obs(arr=[-3.36, -1.32], weights=[1.19, 0.38]),\n", | |
| " Obs(arr=[-3.1, -2.05], weights=[1.19, 0.38]),\n", | |
| " Obs(arr=[-3.61, -0.45], weights=[1.19, 0.38]),\n", | |
| " Obs(arr=[-3.63, -0.34], weights=[1.19, 0.38]),\n", | |
| " Obs(arr=[-3.29, -1.28], weights=[1.19, 0.38]),\n", | |
| " Obs(arr=[-3.9, 0.81], weights=[1.19, 0.38]),\n", | |
| " Obs(arr=[-2.61, -2.25], weights=[1.19, 0.38]),\n", | |
| " Obs(arr=[-3.15, -0.48], weights=[1.19, 0.38])]" | |
| ] | |
| }, | |
| "execution_count": 49, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "observations = [Obs(xi, w[0]) for xi in X]\n", | |
| "sorted(observations)[:10]" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "modern-console", | |
| "metadata": {}, | |
| "source": [ | |
| "I'll combine it with the ranks to check if it seems reasonable." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 50, | |
| "id": "presidential-budapest", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "[(3.0, Obs(arr=[4.03, 0.57], weights=[1.19, 0.38])),\n", | |
| " (3.0, Obs(arr=[3.32, 0.27], weights=[1.19, 0.38])),\n", | |
| " (3.0, Obs(arr=[3.06, 0.92], weights=[1.19, 0.38])),\n", | |
| " (3.0, Obs(arr=[3.31, 0.09], weights=[1.19, 0.38])),\n", | |
| " (3.0, Obs(arr=[2.68, 2.0], weights=[1.19, 0.38])),\n", | |
| " (3.0, Obs(arr=[2.77, 1.53], weights=[1.19, 0.38])),\n", | |
| " (3.0, Obs(arr=[3.14, 0.29], weights=[1.19, 0.38])),\n", | |
| " (3.0, Obs(arr=[2.93, 0.67], weights=[1.19, 0.38])),\n", | |
| " (3.0, Obs(arr=[2.87, 0.82], weights=[1.19, 0.38])),\n", | |
| " (3.0, Obs(arr=[2.67, 1.31], weights=[1.19, 0.38]))]" | |
| ] | |
| }, | |
| "execution_count": 50, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "observations = sorted(zip(y, observations), key=lambda t: t[1], reverse=True)\n", | |
| "observations[:10]" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "hundred-sacrifice", | |
| "metadata": {}, | |
| "source": [ | |
| "Remember that sorting like this will usually be $O = n\\times log_2(n)$. A complex function might take a long time to evaluate.\n", | |
| "\n", | |
| "For a strictly linear function, you can use the model directly on the features (rather than the space of differences)." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 51, | |
| "id": "imposed-virtue", | |
| "metadata": { | |
| "scrolled": false | |
| }, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "[(3.0, 5.026986168572513),\n", | |
| " (3.0, 4.064973208581116),\n", | |
| " (3.0, 4.004872670382963),\n", | |
| " (3.0, 3.9923592281037013),\n", | |
| " (3.0, 3.9668530791630476),\n", | |
| " (3.0, 3.899361741345462),\n", | |
| " (3.0, 3.864273210501915),\n", | |
| " (3.0, 3.7553321255701824),\n", | |
| " (3.0, 3.7361629846308007),\n", | |
| " (3.0, 3.68819389908362)]" | |
| ] | |
| }, | |
| "execution_count": 51, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "sorted(zip(y, w.dot(X.T)[0]), key=lambda t: t[1], reverse=True)[:10]" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "imperial-hammer", | |
| "metadata": {}, | |
| "source": [ | |
| "### RankNet\n", | |
| "\n", | |
| "https://www.microsoft.com/en-us/research/wp-content/uploads/2005/08/icml_ranking.pdf\n", | |
| "\n", | |
| "Basically it's a siamese network with logistic activation and cross entropy loss.\n", | |
| "More or less as above, but moving where we take the difference so that we speed up evaluation time.\n", | |
| "RankBoost did the same thing but with a fancy schmancy loss function.\n", | |
| "\n", | |
| "They also provide a more thorough analysis of why mapping to a probability of $x_i > x_j$ is useful and consistent." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 52, | |
| "id": "precise-holder", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "from itertools import combinations\n", | |
| "\n", | |
| "X_pairs = []\n", | |
| "y_pairs = []\n", | |
| "\n", | |
| "for i, j in combinations(range(X_sub.shape[0]), 2):\n", | |
| " if y_sub[i] > y_sub[j]:\n", | |
| " y_pairij = 1\n", | |
| " else:\n", | |
| " continue\n", | |
| " #elif y[i] < y[j]:\n", | |
| " # y_pairij = 0\n", | |
| " #else:\n", | |
| " # y_pairij = 0.5\n", | |
| "\n", | |
| " X_pairs.append((X_sub[i], X_sub[j]))\n", | |
| " y_pairs.append(y_pairij)\n", | |
| "\n", | |
| "X_pairs = np.array(X_pairs)\n", | |
| "y_pairs = np.array(y_pairs)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 53, | |
| "id": "educational-organization", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array([[0.82418808, 0.479966 ],\n", | |
| " [0.82418808, 0.479966 ],\n", | |
| " [0.82418808, 0.479966 ],\n", | |
| " [0.82418808, 0.479966 ],\n", | |
| " [0.82418808, 0.479966 ],\n", | |
| " [0.82418808, 0.479966 ],\n", | |
| " [0.82418808, 0.479966 ],\n", | |
| " [0.82418808, 0.479966 ],\n", | |
| " [0.82418808, 0.479966 ],\n", | |
| " [0.82418808, 0.479966 ]])" | |
| ] | |
| }, | |
| "execution_count": 53, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "X_pairs[:10, 0, :]" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 54, | |
| "id": "still-encyclopedia", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array([[-0.57172145, -0.10949727],\n", | |
| " [ 0.01902826, -0.94376106],\n", | |
| " [-0.29873262, -0.46058737],\n", | |
| " [-1.08879299, -0.57577075],\n", | |
| " [-1.68290077, 0.22918525],\n", | |
| " [-1.75662522, 0.84463262],\n", | |
| " [-0.6564723 , -0.2015057 ],\n", | |
| " [-0.70061583, 0.68713795],\n", | |
| " [-0.02607576, -0.82975832],\n", | |
| " [-0.61130127, -0.8217515 ]])" | |
| ] | |
| }, | |
| "execution_count": 54, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "X_pairs[:10, 1, :]" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 55, | |
| "id": "respiratory-replica", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "import tensorflow as tf\n", | |
| "from tensorflow.keras import Sequential\n", | |
| "from tensorflow.keras.layers import Dense\n", | |
| "\n", | |
| "from tensorflow.keras.activations import sigmoid as tfsigmoid\n", | |
| "from tensorflow.keras.losses import BinaryCrossentropy\n", | |
| "from tensorflow.keras.optimizers import Adam" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 56, | |
| "id": "minus-compatibility", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "tf.Tensor(0.11192029, shape=(), dtype=float32)\n" | |
| ] | |
| } | |
| ], | |
| "source": [ | |
| "optimizer = Adam(learning_rate=0.1)\n", | |
| "loss_fn = BinaryCrossentropy(from_logits=True)\n", | |
| "\n", | |
| "model = Dense(1, activation=\"linear\", use_bias=False)\n", | |
| "\n", | |
| "n_epoch = 100\n", | |
| "for _ in range(n_epoch):\n", | |
| " with tf.GradientTape() as tape:\n", | |
| " s1 = model(X_pairs[:, 0, :])\n", | |
| " s2 = model(X_pairs[:, 1, :])\n", | |
| " logits = tfsigmoid(s1 - s2)\n", | |
| " loss_value = loss_fn(y_pairs.reshape(-1, 1), logits)\n", | |
| "\n", | |
| " grads = tape.gradient(loss_value, model.trainable_variables)\n", | |
| " optimizer.apply_gradients(zip(grads, model.trainable_variables))\n", | |
| "\n", | |
| "print(loss_value)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 57, | |
| "id": "religious-blond", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "[array([[3.30593 ],\n", | |
| " [1.098215]], dtype=float32)]" | |
| ] | |
| }, | |
| "execution_count": 57, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "model.get_weights()" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 58, | |
| "id": "random-therapist", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "[(3.0, 13.932344),\n", | |
| " (3.0, 11.259557),\n", | |
| " (3.0, 11.116082),\n", | |
| " (3.0, 11.052165),\n", | |
| " (3.0, 11.048449),\n", | |
| " (3.0, 10.845408),\n", | |
| " (3.0, 10.704596),\n", | |
| " (3.0, 10.416609),\n", | |
| " (3.0, 10.368637),\n", | |
| " (3.0, 10.253225)]" | |
| ] | |
| }, | |
| "execution_count": 58, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "y_pred = model(X).numpy()\n", | |
| "\n", | |
| "sorted(zip(y, y_pred[:, 0]), key=lambda t: t[1], reverse=True)[:10]" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "executed-disney", | |
| "metadata": {}, | |
| "source": [ | |
| "There are newer pairwise ranking methods that's popular in the recommender system world using the WARP loss:\n", | |
| "\n", | |
| "- http://www.gabormelli.com/RKB/Weighted_Approximately_Ranked_Pairwise_(WARP)_Ranking_Loss\n", | |
| "- https://www.hongliangjie.com/2012/08/24/weighted-approximately-ranked-pairwise-loss-warp/\n", | |
| "\n", | |
| "I haven't looked at it yet, but apparently it's quite good.\n", | |
| "\n", | |
| "Triplet losses might also be interesting for people interested in metric or embedding learning.\n", | |
| "\n", | |
| "- Very successful for learning semantic representations of data.\n", | |
| "- E.g. dense embeddings of very sparse, multi-class problems.\n", | |
| "- https://arxiv.org/abs/1412.6622\n", | |
| "\n", | |
| "\n", | |
| "Define an anchor term $a$ which is your item of interest, then sample a positive $p$ and negative $n$ example from the data.\n", | |
| "$p$ should share a class annotation with $a$ and $n$ should not share any annotations.\n", | |
| "\n", | |
| "The idea is to learn a distance function $f$ that maximises the distance between $a$ and $n$, and minimises distance between $a$ and $p$.\n", | |
| "\n", | |
| "$$\n", | |
| "Loss = max(f(a, p) − f(a, n) + \\text{margin}, 0)\n", | |
| "$$\n", | |
| "\n", | |
| "Similar in spirit to SVM classification (pushing two opposing classses together).\n", | |
| "\n", | |
| "\n", | |
| "## Listwise ranking\n", | |
| "\n", | |
| "In many applications you're really only interested in the top few hits (e.g. search engines).\n", | |
| "Pairwise approaches give equal importance for all pairs, but we want to emphasise the relevant items during training.\n", | |
| "\n", | |
| "Next step was to optimise list-wise ranking, loss function is NDCG (or similar) of the whole list.\n", | |
| "Means you require multiple lists, e.g. many query terms and a ranked list of results.\n", | |
| "\n", | |
| "So the idea is to minimise some function:\n", | |
| "\n", | |
| "$$\n", | |
| "\\frac{1}{m} \\sum_{i=1}^{m}loss(sort(f(x_1^i), ...), y^i)\n", | |
| "$$\n", | |
| "\n", | |
| "\n", | |
| "### LambdaRank\n", | |
| "\n", | |
| "Extended RankNet by optimising directly for NDCG (or any other list-wise metric).\n", | |
| "\n", | |
| "- https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/lambdarank.pdf\n", | |
| "- https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/SoftRankWsdm08Submitted.pdf\n", | |
| "- https://www.cs.cmu.edu/~pinard/Papers/sigirfp092-donmez.pdf\n", | |
| "\n", | |
| "\n", | |
| "- list-ranking statistics are highly discontinuous/non-smooth/non-differentiable.\n", | |
| "- Key observation here is that you don't need the results of the loss function, just the gradient.\n", | |
| "- So you can add your own slope to the gradient for update and use that instead.\n", | |
| "\n", | |
| "\n", | |
| "From RankNet given our loss function $C$ and weights $w$, predictions $s_i = f(x_i)$ and $S_{ij} \\in \\{-1, 0, 1\\}$.\n", | |
| "\n", | |
| "You can factor out the gradient given by RankNet to be:\n", | |
| "\n", | |
| "$$\n", | |
| "\\frac{\\partial C}{\\partial w_k} = \\lambda_{ij} \\left(\\frac{\\partial s_i}{\\partial w_k} - \\frac{\\partial s_j}{\\partial w_k}\\right)\n", | |
| "$$\n", | |
| "\n", | |
| "where\n", | |
| "\n", | |
| "$$\n", | |
| "\\lambda_{ij} = \\frac{\\partial C(s_i - s_j)}{\\partial s_i} = \\sigma \\left(\\frac{1}{2}(1 - S_{ij}) - \\frac{1}{1 + e^{\\sigma(s_i - s_j)}}\\right)\n", | |
| "$$\n", | |
| "\n", | |
| "\n", | |
| "In lambdaRank:\n", | |
| "1. predict the labels for each sample\n", | |
| "2. for each pair i, j where $y_i > y_j$ (i.e. $S_{ij} = 1$) you calculate the effect that switching their predicted place in the sorted list has on NDCG@N ($\\Delta \\text{NDCG}_{ij}$).\n", | |
| "3. You replace the derivative of the loss w.r.t. $s_i$ ($\\lambda_{ij}$) with the following:\n", | |
| "\n", | |
| "\n", | |
| "$$\n", | |
| "\\lambda_{ij} = \\frac{-\\sigma}{1 + e^{\\sigma(s_i - s_j)}} \\left|\\Delta \\text{NDCG}_{ij}\\right|\n", | |
| "$$\n", | |
| "\n", | |
| "\n", | |
| "\n", | |
| "### LambdaMART\n", | |
| "\n", | |
| "Does basically the same thing as lambdaRank, except the $\\lambda_{ij}$ is used when calculating the gradients for adding new boosting trees instead of gradient descent.\n", | |
| "\n", | |
| "Highly recommend \"From RankNet to LamdaRank to LambdaMART: an overview\" here: https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/MSR-TR-2010-82.pdf\n", | |
| "\n", | |
| "Quite a clear description of the conceptual process from the Microsoft people that did this work. And reasonably complete equations and descriptions.\n", | |
| "\n", | |
| "\n", | |
| "XGBoost has a good but poorly documented implementation of LamdaMART.\n", | |
| "\n", | |
| "LightGBM seems to have \"lambdarank\" and another model XENDCG (https://arxiv.org/abs/1911.09798; related to listNet)." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 59, | |
| "id": "champion-jewel", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "import xgboost as xgb" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 60, | |
| "id": "greek-sherman", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "from sklearn.datasets import dump_svmlight_file\n", | |
| "dump_svmlight_file(X[:9000], y[:9000], \"train.svmlight\")\n", | |
| "\n", | |
| "with open(\"train.svmlight.group\", \"w\") as handle:\n", | |
| " print(\"\\n\".join(str(100) for _ in range(90)), file=handle)\n", | |
| " \n", | |
| "dump_svmlight_file(X[9000:], y[9000:], \"test.svmlight\")\n", | |
| "\n", | |
| "with open(\"test.svmlight.group\", \"w\") as handle:\n", | |
| " print(\"\\n\".join(str(100) for _ in range(10)), file=handle)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 61, | |
| "id": "statutory-malaysia", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "[02:53:25] 9000x2 matrix with 18000 entries loaded from train.svmlight\n", | |
| "[02:53:25] 90 groups are loaded from train.svmlight.group\n", | |
| "[02:53:25] 1000x2 matrix with 2000 entries loaded from test.svmlight\n", | |
| "[02:53:25] 10 groups are loaded from test.svmlight.group\n" | |
| ] | |
| } | |
| ], | |
| "source": [ | |
| "train = xgb.DMatrix(\"train.svmlight\")\n", | |
| "test = xgb.DMatrix(\"test.svmlight\")" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 62, | |
| "id": "stone-organ", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "params = {\n", | |
| " 'verbosity': 0,\n", | |
| " 'max_depth': 4,\n", | |
| " 'objective': 'rank:ndcg', # can also do rank:pairwise or rank:map\n", | |
| " 'eval_metric': [\n", | |
| " 'ndcg',\n", | |
| " 'ndcg@10',\n", | |
| " 'map',\n", | |
| " 'map@10',\n", | |
| " ]\n", | |
| "}" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 63, | |
| "id": "circular-personal", | |
| "metadata": { | |
| "scrolled": true | |
| }, | |
| "outputs": [ | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "[0]\ttest-ndcg:0.905414\ttest-ndcg@10:0.681215\ttest-map:0.987547\ttest-map@10:0.106358\n", | |
| "[1]\ttest-ndcg:0.930646\ttest-ndcg@10:0.779804\ttest-map:0.98844\ttest-map@10:0.106358\n", | |
| "[2]\ttest-ndcg:0.931475\ttest-ndcg@10:0.781137\ttest-map:0.988497\ttest-map@10:0.106358\n", | |
| "[3]\ttest-ndcg:0.960732\ttest-ndcg@10:0.903813\ttest-map:0.988633\ttest-map@10:0.106358\n", | |
| "[4]\ttest-ndcg:0.960481\ttest-ndcg@10:0.910845\ttest-map:0.988403\ttest-map@10:0.106358\n", | |
| "[5]\ttest-ndcg:0.960985\ttest-ndcg@10:0.911291\ttest-map:0.988541\ttest-map@10:0.106358\n", | |
| "[6]\ttest-ndcg:0.964712\ttest-ndcg@10:0.920337\ttest-map:0.988581\ttest-map@10:0.106358\n", | |
| "[7]\ttest-ndcg:0.965182\ttest-ndcg@10:0.920337\ttest-map:0.988431\ttest-map@10:0.106358\n", | |
| "[8]\ttest-ndcg:0.97069\ttest-ndcg@10:0.936699\ttest-map:0.988225\ttest-map@10:0.106358\n", | |
| "[9]\ttest-ndcg:0.971002\ttest-ndcg@10:0.937074\ttest-map:0.988226\ttest-map@10:0.106358\n", | |
| "[10]\ttest-ndcg:0.97941\ttest-ndcg@10:0.955805\ttest-map:0.988309\ttest-map@10:0.106358\n", | |
| "[11]\ttest-ndcg:0.984046\ttest-ndcg@10:0.966782\ttest-map:0.988217\ttest-map@10:0.106358\n", | |
| "[12]\ttest-ndcg:0.984086\ttest-ndcg@10:0.966782\ttest-map:0.988282\ttest-map@10:0.106358\n", | |
| "[13]\ttest-ndcg:0.984272\ttest-ndcg@10:0.966953\ttest-map:0.988383\ttest-map@10:0.106358\n", | |
| "[14]\ttest-ndcg:0.984449\ttest-ndcg@10:0.967277\ttest-map:0.98833\ttest-map@10:0.106358\n", | |
| "[15]\ttest-ndcg:0.984635\ttest-ndcg@10:0.967482\ttest-map:0.988316\ttest-map@10:0.106358\n", | |
| "[16]\ttest-ndcg:0.984704\ttest-ndcg@10:0.967482\ttest-map:0.988686\ttest-map@10:0.106358\n", | |
| "[17]\ttest-ndcg:0.984704\ttest-ndcg@10:0.967405\ttest-map:0.988562\ttest-map@10:0.106358\n", | |
| "[18]\ttest-ndcg:0.984779\ttest-ndcg@10:0.967106\ttest-map:0.988665\ttest-map@10:0.106358\n", | |
| "[19]\ttest-ndcg:0.984641\ttest-ndcg@10:0.966782\ttest-map:0.988944\ttest-map@10:0.106358\n" | |
| ] | |
| } | |
| ], | |
| "source": [ | |
| "model = xgb.train(params, train, evals=[(test, \"test\")], num_boost_round=20, verbose_eval=True)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 64, | |
| "id": "spoken-space", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "ytest = y[9000:].reshape(-1, 100)\n", | |
| "preds = model.predict(test).reshape(-1, 100)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 65, | |
| "id": "verbal-amber", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "0.9889175984677776" | |
| ] | |
| }, | |
| "execution_count": 65, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "normalised_dcg(ytest, preds)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 66, | |
| "id": "scenic-facility", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "0.9943802083769608" | |
| ] | |
| }, | |
| "execution_count": 66, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "normalised_dcg(ytest, preds, original=True)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 67, | |
| "id": "comic-vegetarian", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "0.9913811457214813" | |
| ] | |
| }, | |
| "execution_count": 67, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "ndcg_score(ytest, preds)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "continued-parade", | |
| "metadata": {}, | |
| "source": [ | |
| "None of the NDCG scores are the same :S" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "atomic-draft", | |
| "metadata": {}, | |
| "source": [ | |
| "### Beyond LambdaRank\n", | |
| "\n", | |
| "Soon after lambdaRank people started looking for \"smooth\" (i.e. differentiable) loss functions approximating ranking/IR metrics.\n", | |
| "\n", | |
| "- softRank\n", | |
| " - https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/SoftRankWsdm08Submitted.pdf\n", | |
| "- listNet\n", | |
| " - https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/tr-2007-40.pdf\n", | |
| " - Attempts to define a probability for a particular list permutation (over all possible permutations). Like a long chain of conditional probabilities.\n", | |
| " - Heuristic approximation \"top 1\". Sum of permutation probabilities for permutations where the current observation is highest ranked.\n", | |
| "- listMLE\n", | |
| " - https://dl.acm.org/doi/abs/10.1145/1390156.1390306\n", | |
| " - Uses a likelihood loss, which they are a bit more formal about defining that listNet was.\n", | |
| "- Bayesian personalised ranking (BPR)\n", | |
| " - Popular in recommenders\n", | |
| " - https://arxiv.org/ftp/arxiv/papers/1205/1205.2618.pdf\n", | |
| "\n", | |
| "\n", | |
| "### ListMLE\n", | |
| " \n", | |
| "listMLE likelihood loss function where $g$ is your function resulting in ranking scores.\n", | |
| "\n", | |
| "$$\n", | |
| "l(g(x), y) = -log P(y | x; g) \\\\\n", | |
| "P(y | x; g) = \\prod_{i=1}^{n} \\frac{exp(g(x_{y(i)}))}{\\sum_{k=i}^{n}exp(g(x_{y(k)}))}\n", | |
| "$$\n", | |
| "\n", | |
| "Where $y(i)$ is the index of the object ranked at position $i$.\n", | |
| "So $x_{y(i)}$ retrieves that document.\n", | |
| "\n", | |
| "\n" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 68, | |
| "id": "postal-syndicate", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "@tf.function\n", | |
| "def list_mle_loss(true, pred):\n", | |
| " order = tf.argsort(true, direction=\"DESCENDING\")\n", | |
| " pred_sort = tf.gather(pred, order)\n", | |
| "\n", | |
| " pred_sort -= tf.reduce_max(pred_sort, axis=-1)\n", | |
| " sums = tf.cumsum(tf.exp(pred_sort), axis=-1, reverse=True)\n", | |
| " sums = -1 * (pred_sort - tf.math.log(sums))\n", | |
| " return tf.reduce_sum(sums, keepdims=True, axis=-1)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 69, | |
| "id": "killing-bhutan", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array([1. , 0.98989899, 0.97979798, 0.96969697, 0.95959596,\n", | |
| " 0.94949495, 0.93939394, 0.92929293, 0.91919192, 0.90909091,\n", | |
| " 0.8989899 , 0.88888889, 0.87878788, 0.86868687, 0.85858586,\n", | |
| " 0.84848485, 0.83838384, 0.82828283, 0.81818182, 0.80808081,\n", | |
| " 0.7979798 , 0.78787879, 0.77777778, 0.76767677, 0.75757576,\n", | |
| " 0.74747475, 0.73737374, 0.72727273, 0.71717172, 0.70707071,\n", | |
| " 0.6969697 , 0.68686869, 0.67676768, 0.66666667, 0.65656566,\n", | |
| " 0.64646465, 0.63636364, 0.62626263, 0.61616162, 0.60606061,\n", | |
| " 0.5959596 , 0.58585859, 0.57575758, 0.56565657, 0.55555556,\n", | |
| " 0.54545455, 0.53535354, 0.52525253, 0.51515152, 0.50505051,\n", | |
| " 0.49494949, 0.48484848, 0.47474747, 0.46464646, 0.45454545,\n", | |
| " 0.44444444, 0.43434343, 0.42424242, 0.41414141, 0.4040404 ,\n", | |
| " 0.39393939, 0.38383838, 0.37373737, 0.36363636, 0.35353535,\n", | |
| " 0.34343434, 0.33333333, 0.32323232, 0.31313131, 0.3030303 ,\n", | |
| " 0.29292929, 0.28282828, 0.27272727, 0.26262626, 0.25252525,\n", | |
| " 0.24242424, 0.23232323, 0.22222222, 0.21212121, 0.2020202 ,\n", | |
| " 0.19191919, 0.18181818, 0.17171717, 0.16161616, 0.15151515,\n", | |
| " 0.14141414, 0.13131313, 0.12121212, 0.11111111, 0.1010101 ,\n", | |
| " 0.09090909, 0.08080808, 0.07070707, 0.06060606, 0.05050505,\n", | |
| " 0.04040404, 0.03030303, 0.02020202, 0.01010101, 0. ])" | |
| ] | |
| }, | |
| "execution_count": 69, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "ysorted = np.arange(99, -1, step=-1).astype(float)\n", | |
| "ypred = np.linspace(1, 0, 100)\n", | |
| "ypred" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 70, | |
| "id": "annoying-ministry", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array([340.17004064])" | |
| ] | |
| }, | |
| "execution_count": 70, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "list_mle_loss(ysorted, ypred).numpy()" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 71, | |
| "id": "analyzed-conservative", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array([390.17004064])" | |
| ] | |
| }, | |
| "execution_count": 71, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "list_mle_loss(ysorted, ypred[::-1]).numpy()" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 72, | |
| "id": "minus-aspect", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array([0. , 0.98989899, 0.97979798, 0.96969697, 0.95959596,\n", | |
| " 0.94949495, 0.93939394, 0.92929293, 0.91919192, 0.90909091,\n", | |
| " 0.8989899 , 0.88888889, 0.87878788, 0.86868687, 0.85858586,\n", | |
| " 0.84848485, 0.83838384, 0.82828283, 0.81818182, 0.80808081,\n", | |
| " 0.7979798 , 0.78787879, 0.77777778, 0.76767677, 0.75757576,\n", | |
| " 0.74747475, 0.73737374, 0.72727273, 0.71717172, 0.70707071,\n", | |
| " 0.6969697 , 0.68686869, 0.67676768, 0.66666667, 0.65656566,\n", | |
| " 0.64646465, 0.63636364, 0.62626263, 0.61616162, 0.60606061,\n", | |
| " 0.5959596 , 0.58585859, 0.57575758, 0.56565657, 0.55555556,\n", | |
| " 0.54545455, 0.53535354, 0.52525253, 0.51515152, 0.50505051,\n", | |
| " 0.49494949, 0.48484848, 0.47474747, 0.46464646, 0.45454545,\n", | |
| " 0.44444444, 0.43434343, 0.42424242, 0.41414141, 0.4040404 ,\n", | |
| " 0.39393939, 0.38383838, 0.37373737, 0.36363636, 0.35353535,\n", | |
| " 0.34343434, 0.33333333, 0.32323232, 0.31313131, 0.3030303 ,\n", | |
| " 0.29292929, 0.28282828, 0.27272727, 0.26262626, 0.25252525,\n", | |
| " 0.24242424, 0.23232323, 0.22222222, 0.21212121, 0.2020202 ,\n", | |
| " 0.19191919, 0.18181818, 0.17171717, 0.16161616, 0.15151515,\n", | |
| " 0.14141414, 0.13131313, 0.12121212, 0.11111111, 0.1010101 ,\n", | |
| " 0.09090909, 0.08080808, 0.07070707, 0.06060606, 0.05050505,\n", | |
| " 0.04040404, 0.03030303, 0.02020202, 0.01010101, 1. ])" | |
| ] | |
| }, | |
| "execution_count": 72, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "ypred_mut = ypred.copy()\n", | |
| "ypred_mut[0] = ypred[-1]\n", | |
| "ypred_mut[-1] = ypred[0]\n", | |
| "ypred_mut" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 73, | |
| "id": "italian-victorian", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array([346.93373969])" | |
| ] | |
| }, | |
| "execution_count": 73, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "list_mle_loss(ysorted, ypred_mut).numpy()" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 74, | |
| "id": "declared-johns", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array([0.98989899, 1. , 0.97979798, 0.96969697, 0.95959596,\n", | |
| " 0.94949495, 0.93939394, 0.92929293, 0.91919192, 0.90909091,\n", | |
| " 0.8989899 , 0.88888889, 0.87878788, 0.86868687, 0.85858586,\n", | |
| " 0.84848485, 0.83838384, 0.82828283, 0.81818182, 0.80808081,\n", | |
| " 0.7979798 , 0.78787879, 0.77777778, 0.76767677, 0.75757576,\n", | |
| " 0.74747475, 0.73737374, 0.72727273, 0.71717172, 0.70707071,\n", | |
| " 0.6969697 , 0.68686869, 0.67676768, 0.66666667, 0.65656566,\n", | |
| " 0.64646465, 0.63636364, 0.62626263, 0.61616162, 0.60606061,\n", | |
| " 0.5959596 , 0.58585859, 0.57575758, 0.56565657, 0.55555556,\n", | |
| " 0.54545455, 0.53535354, 0.52525253, 0.51515152, 0.50505051,\n", | |
| " 0.49494949, 0.48484848, 0.47474747, 0.46464646, 0.45454545,\n", | |
| " 0.44444444, 0.43434343, 0.42424242, 0.41414141, 0.4040404 ,\n", | |
| " 0.39393939, 0.38383838, 0.37373737, 0.36363636, 0.35353535,\n", | |
| " 0.34343434, 0.33333333, 0.32323232, 0.31313131, 0.3030303 ,\n", | |
| " 0.29292929, 0.28282828, 0.27272727, 0.26262626, 0.25252525,\n", | |
| " 0.24242424, 0.23232323, 0.22222222, 0.21212121, 0.2020202 ,\n", | |
| " 0.19191919, 0.18181818, 0.17171717, 0.16161616, 0.15151515,\n", | |
| " 0.14141414, 0.13131313, 0.12121212, 0.11111111, 0.1010101 ,\n", | |
| " 0.09090909, 0.08080808, 0.07070707, 0.06060606, 0.05050505,\n", | |
| " 0.04040404, 0.03030303, 0.02020202, 0.01010101, 0. ])" | |
| ] | |
| }, | |
| "execution_count": 74, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "ypred_mut = ypred.copy()\n", | |
| "ypred_mut[0] = ypred[1]\n", | |
| "ypred_mut[1] = ypred[0]\n", | |
| "ypred_mut" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 75, | |
| "id": "alpha-traveler", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array([340.17020204])" | |
| ] | |
| }, | |
| "execution_count": 75, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "list_mle_loss(ysorted, ypred_mut).numpy()" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 76, | |
| "id": "cooked-swing", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array([1. , 0.98989899, 0.97979798, 0.96969697, 0.95959596,\n", | |
| " 0.94949495, 0.93939394, 0.92929293, 0.91919192, 0.90909091,\n", | |
| " 0.8989899 , 0.88888889, 0.87878788, 0.86868687, 0.85858586,\n", | |
| " 0.84848485, 0.83838384, 0.82828283, 0.81818182, 0.80808081,\n", | |
| " 0.7979798 , 0.78787879, 0.77777778, 0.76767677, 0.75757576,\n", | |
| " 0.74747475, 0.73737374, 0.72727273, 0.71717172, 0.70707071,\n", | |
| " 0.6969697 , 0.68686869, 0.67676768, 0.66666667, 0.65656566,\n", | |
| " 0.64646465, 0.63636364, 0.62626263, 0.61616162, 0.60606061,\n", | |
| " 0.5959596 , 0.58585859, 0.57575758, 0.56565657, 0.55555556,\n", | |
| " 0.54545455, 0.53535354, 0.52525253, 0.51515152, 0.50505051,\n", | |
| " 0.49494949, 0.48484848, 0.47474747, 0.46464646, 0.45454545,\n", | |
| " 0.44444444, 0.43434343, 0.42424242, 0.41414141, 0.4040404 ,\n", | |
| " 0.39393939, 0.38383838, 0.37373737, 0.36363636, 0.35353535,\n", | |
| " 0.34343434, 0.33333333, 0.32323232, 0.31313131, 0.3030303 ,\n", | |
| " 0.29292929, 0.28282828, 0.27272727, 0.26262626, 0.25252525,\n", | |
| " 0.24242424, 0.23232323, 0.22222222, 0.21212121, 0.2020202 ,\n", | |
| " 0.19191919, 0.18181818, 0.17171717, 0.16161616, 0.15151515,\n", | |
| " 0.14141414, 0.13131313, 0.12121212, 0.11111111, 0.1010101 ,\n", | |
| " 0.09090909, 0.08080808, 0.07070707, 0.06060606, 0.05050505,\n", | |
| " 0.04040404, 0.03030303, 0.02020202, 0. , 0. ])" | |
| ] | |
| }, | |
| "execution_count": 76, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "ypred_mut = ypred.copy()\n", | |
| "ypred_mut[-1] = ypred[-1]\n", | |
| "ypred_mut[-2] = ypred[-1]\n", | |
| "ypred_mut" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 77, | |
| "id": "outer-article", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array([340.14205151])" | |
| ] | |
| }, | |
| "execution_count": 77, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "list_mle_loss(ysorted, ypred_mut).numpy()" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "psychological-optics", | |
| "metadata": {}, | |
| "source": [ | |
| "Lets try training a model!" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 78, | |
| "id": "civil-belgium", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "Xsplit = list(np.split(X[:9000], np.arange(100, 9000, 100)))\n", | |
| "ysplit = list(np.split(y[:9000], np.arange(100, 9000, 100)))" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 79, | |
| "id": "bulgarian-argentina", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "tf.keras.backend.clear_session()" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 80, | |
| "id": "outer-throat", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "0 [370.86136] 0.7423732225166043\n", | |
| "10 [354.9018] 0.9205463108498243\n", | |
| "20 [346.13977] 1.0\n", | |
| "30 [346.25073] 0.9977990446115134\n", | |
| "40 [341.0366] 0.9972605686529491\n", | |
| "50 [337.79468] 1.0\n", | |
| "60 [341.48578] 0.9999826230597305\n", | |
| "70 [339.2522] 0.9999943836284894\n", | |
| "80 [339.8788] 0.999715882466915\n" | |
| ] | |
| } | |
| ], | |
| "source": [ | |
| "optimizer = Adam(learning_rate=0.1)\n", | |
| "loss_fn = list_mle_loss\n", | |
| "\n", | |
| "model = Dense(1, activation=\"linear\", use_bias=False)\n", | |
| "\n", | |
| "for i, (Xi, yi) in enumerate(zip(Xsplit, ysplit)):\n", | |
| " with tf.GradientTape() as tape:\n", | |
| " pred = model(Xi)\n", | |
| " logits = tfsigmoid(pred)\n", | |
| " loss_value = loss_fn(yi, tf.reshape(logits, -1))\n", | |
| "\n", | |
| " grads = tape.gradient(loss_value, model.trainable_variables)\n", | |
| " optimizer.apply_gradients(zip(grads, model.trainable_variables))\n", | |
| " #print(sorted(zip(pred.numpy().reshape(-1), yi), key=lambda t: t[0]))\n", | |
| "\n", | |
| " if (i % 10) == 0:\n", | |
| " print(i, loss_value.numpy(), normalised_dcg(yi.reshape(1, -1), pred.numpy().reshape(1, -1)))" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 81, | |
| "id": "congressional-enough", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "Xsplit = list(np.split(X[9000:], np.arange(100, 1000, 100)))\n", | |
| "ysplit = list(np.split(y[9000:], np.arange(100, 1000, 100)))" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 82, | |
| "id": "pending-corpus", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "[1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]" | |
| ] | |
| }, | |
| "execution_count": 82, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "preds = []\n", | |
| "ndcgs = []\n", | |
| "for Xi, yi in zip(Xsplit, ysplit):\n", | |
| " p = model(Xi)\n", | |
| " preds.append(p.numpy().flatten())\n", | |
| " ndcgs.append(normalised_dcg(yi.reshape(1, -1), p.numpy().reshape(1, -1)))\n", | |
| "\n", | |
| "preds = pd.DataFrame({\"true\": ysplit[0], \"preds\": preds[0]})\n", | |
| "\n", | |
| "ndcgs" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 83, | |
| "id": "consolidated-surfing", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/html": [ | |
| "<div>\n", | |
| "<style scoped>\n", | |
| " .dataframe tbody tr th:only-of-type {\n", | |
| " vertical-align: middle;\n", | |
| " }\n", | |
| "\n", | |
| " .dataframe tbody tr th {\n", | |
| " vertical-align: top;\n", | |
| " }\n", | |
| "\n", | |
| " .dataframe thead th {\n", | |
| " text-align: right;\n", | |
| " }\n", | |
| "</style>\n", | |
| "<table border=\"1\" class=\"dataframe\">\n", | |
| " <thead>\n", | |
| " <tr style=\"text-align: right;\">\n", | |
| " <th></th>\n", | |
| " <th>true</th>\n", | |
| " <th>preds</th>\n", | |
| " </tr>\n", | |
| " </thead>\n", | |
| " <tbody>\n", | |
| " <tr>\n", | |
| " <th>30</th>\n", | |
| " <td>0.0</td>\n", | |
| " <td>-8.204016</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>87</th>\n", | |
| " <td>0.0</td>\n", | |
| " <td>-7.504719</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>0</th>\n", | |
| " <td>0.0</td>\n", | |
| " <td>-7.269742</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>88</th>\n", | |
| " <td>0.0</td>\n", | |
| " <td>-6.528606</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>82</th>\n", | |
| " <td>0.0</td>\n", | |
| " <td>-6.395883</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>...</th>\n", | |
| " <td>...</td>\n", | |
| " <td>...</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>89</th>\n", | |
| " <td>2.0</td>\n", | |
| " <td>6.246413</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>33</th>\n", | |
| " <td>2.0</td>\n", | |
| " <td>6.354847</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>16</th>\n", | |
| " <td>2.0</td>\n", | |
| " <td>7.383247</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>29</th>\n", | |
| " <td>2.0</td>\n", | |
| " <td>7.794843</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>99</th>\n", | |
| " <td>3.0</td>\n", | |
| " <td>11.692219</td>\n", | |
| " </tr>\n", | |
| " </tbody>\n", | |
| "</table>\n", | |
| "<p>100 rows × 2 columns</p>\n", | |
| "</div>" | |
| ], | |
| "text/plain": [ | |
| " true preds\n", | |
| "30 0.0 -8.204016\n", | |
| "87 0.0 -7.504719\n", | |
| "0 0.0 -7.269742\n", | |
| "88 0.0 -6.528606\n", | |
| "82 0.0 -6.395883\n", | |
| ".. ... ...\n", | |
| "89 2.0 6.246413\n", | |
| "33 2.0 6.354847\n", | |
| "16 2.0 7.383247\n", | |
| "29 2.0 7.794843\n", | |
| "99 3.0 11.692219\n", | |
| "\n", | |
| "[100 rows x 2 columns]" | |
| ] | |
| }, | |
| "execution_count": 83, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "preds.sort_values(\"preds\")" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 84, | |
| "id": "micro-kitchen", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "[array([[3.8863764],\n", | |
| " [1.0636641]], dtype=float32)]" | |
| ] | |
| }, | |
| "execution_count": 84, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "model.get_weights()" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "rotary-baseline", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "necessary-manitoba", | |
| "metadata": {}, | |
| "source": [ | |
| "Some libraries for doing LTR.\n", | |
| "\n", | |
| "- tf-ranking https://github.com/tensorflow/ranking\n", | |
| "- allrank (pytorch) https://github.com/allegro/allRank/tree/master/allrank\n", | |
| "- lightGBM https://lightgbm.readthedocs.io/en/latest/index.html\n", | |
| "- XGBoost https://xgboost.readthedocs.io/en/latest/ (doesn't work with the sk-learn estimator API).\n", | |
| "- SVMRank https://www.cs.cornell.edu/people/tj/svm_light/svm_rank.html https://www.cs.cornell.edu/people/tj/svm_light/svm_proprank.html\n", | |
| "- https://github.com/hpclab/quickrank\n", | |
| "- A lot of options dedicated to search engine stuff e.g. https://elasticsearch-learning-to-rank.readthedocs.io/en/latest/\n", | |
| "\n", | |
| "\n", | |
| "There might be more complete libraries out there for recommenders that incorporate some LTR features. \n", | |
| "\n", | |
| "- https://github.com/maciejkula/spotlight\n", | |
| "\n", | |
| "\n", | |
| "\n", | |
| "Most of the current focus is around correcting for biased feedback (e.g. clicks) and online learning.\n", | |
| "\n", | |
| "Position bias means higher ranked items more likely to be clicked.\n", | |
| "Selection bias means that some relevant documents might never reach the stage of ranking (i.e. after the initial document retrieval stage).\n", | |
| "\n", | |
| "- Good overview of biased sampling and ML overall https://dl.acm.org/doi/pdf/10.1145/1015330.1015425\n", | |
| "\n", | |
| "- Click models. Attempt rank to maximise number of clicks. Refers to \"click bandits\" or \"multi-armed bandit learners\" which is fun. http://proceedings.mlr.press/v70/zoghi17a/zoghi17a.pdf \n", | |
| "- Inverse propensity scores https://arxiv.org/abs/1608.04468\n", | |
| "- Heckman 2 stage & predict ranking as counterfactual i.e. predict probability that the document have been clicked had it been presented to user (https://dl.acm.org/doi/abs/10.1145/3366423.3380255\n", | |
| "- Online learning and bias correction https://dl.acm.org/doi/pdf/10.1145/3269206.3271686\n" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "velvet-halifax", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "virgin-aberdeen", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "announced-shopping", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "united-programming", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "functioning-parliament", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "fifth-value", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "younger-marshall", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "applied-bosnia", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "native-chosen", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "criminal-liberia", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "hidden-noise", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "persistent-fisher", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "cutting-entity", | |
| "metadata": {}, | |
| "source": [ | |
| "### I had this half baked idea about some relationship between pairwise learning approaches and matched samples." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 85, | |
| "id": "technical-findings", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "rng = default_rng()" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 86, | |
| "id": "banned-seattle", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array([[0, 0, 1, ..., 1, 0, 0],\n", | |
| " [0, 1, 0, ..., 0, 1, 0],\n", | |
| " [0, 1, 0, ..., 0, 0, 0],\n", | |
| " ...,\n", | |
| " [0, 1, 0, ..., 0, 1, 0],\n", | |
| " [0, 0, 1, ..., 0, 0, 1],\n", | |
| " [1, 0, 1, ..., 1, 1, 0]])" | |
| ] | |
| }, | |
| "execution_count": 86, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "nloci = 1000\n", | |
| "nsamples = 101\n", | |
| "freqs_pop1 = rng.uniform(size=nloci)\n", | |
| "\n", | |
| "pop1 = np.apply_along_axis(\n", | |
| " lambda p: rng.choice([1, 0], size=nsamples, p=[p[0], 1 - p[0]]),\n", | |
| " 0,\n", | |
| " freqs_pop1.reshape(1, -1)\n", | |
| ")\n", | |
| "\n", | |
| "pop1" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 87, | |
| "id": "vocational-diversity", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array([0.35643564, 0.36633663, 0.34653465, 0.92079208, 0.72277228])" | |
| ] | |
| }, | |
| "execution_count": 87, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "pop1.mean(axis=0)[:5]" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 88, | |
| "id": "small-beaver", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array([0.42173889, 0.28234691, 0.32596702, 0.87615543, 0.7065007 ])" | |
| ] | |
| }, | |
| "execution_count": 88, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "freqs_pop1[:5]" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 89, | |
| "id": "beginning-occupation", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array([0.8, 0.7, 0.2, 0.3, 0.7])" | |
| ] | |
| }, | |
| "execution_count": 89, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "nsamples2 = int(nsamples / 10)\n", | |
| "freqs_pop2 = rng.uniform(size=nloci)\n", | |
| "pop2 = np.apply_along_axis(\n", | |
| " lambda p: rng.choice([1, 0], size=nsamples2, p=[p[0], 1 - p[0]]),\n", | |
| " 0,\n", | |
| " freqs_pop2.reshape(1, -1)\n", | |
| ")\n", | |
| "\n", | |
| "pop2.mean(axis=0)[:5]" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 90, | |
| "id": "given-consent", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array([0.71669173, 0.55885922, 0.16555695, 0.11097242, 0.61347501])" | |
| ] | |
| }, | |
| "execution_count": 90, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "freqs_pop2[:5]" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 91, | |
| "id": "through-welding", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "(111, 1000)" | |
| ] | |
| }, | |
| "execution_count": 91, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "X = np.concatenate([pop1, pop2], axis=0)\n", | |
| "X.shape" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 92, | |
| "id": "ranking-antigua", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array([616, 942, 359, 399, 389, 399, 706, 424, 476, 279])" | |
| ] | |
| }, | |
| "execution_count": 92, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "freq_diff = np.abs(freqs_pop1 - freqs_pop2)\n", | |
| "freq_diff /= freq_diff.sum()\n", | |
| "\n", | |
| "n_true_loci = 10\n", | |
| "true_effect_loci = rng.choice(np.arange(nloci), size=n_true_loci, p=freq_diff)\n", | |
| "true_effect_loci" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 93, | |
| "id": "enabling-soviet", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array([0.00219396, 0.00155591, 0.00167062, 0.00108379, 0.00179073,\n", | |
| " 0.00108379, 0.00060603, 0.00144228, 0.00250293, 0.00146181])" | |
| ] | |
| }, | |
| "execution_count": 93, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "freq_diff[true_effect_loci]" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 94, | |
| "id": "sophisticated-groove", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array([ 1.58211257, -3.31679662, -1.65575129, -0.29111095, 2.48020977,\n", | |
| " -0.29111095, 0.615461 , -4.98735786, 1.59195686, -0.21220992])" | |
| ] | |
| }, | |
| "execution_count": 94, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "true_effects = np.zeros(nloci)\n", | |
| "true_effects[true_effect_loci] = rng.normal(scale=2, size=n_true_loci)\n", | |
| "true_effects[true_effect_loci]" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 95, | |
| "id": "removable-myanmar", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array([-2.89003618, -7.37392783, 0.53139135, -2.64806911, 1.31172872,\n", | |
| " -6.03047451, -5.73292675, -5.54473542, 1.21794124, -6.08810311,\n", | |
| " -5.06613556, -2.17749576, 1.153398 , 0.58935916, -5.1086086 ,\n", | |
| " -4.13321636, -6.97651621, 1.02283966, -2.35137949, -4.94724517,\n", | |
| " -4.86981345, -2.43990861, -2.51994556, -9.27638919, -2.42012765,\n", | |
| " -4.35831236, -4.76393238, -4.85760981, -1.2375028 , -5.5706874 ,\n", | |
| " -1.95124271, -8.19967383, -5.31216132, -0.15281248, -4.90213372,\n", | |
| " -3.32718025, -3.83720197, -2.90110106, -2.24997491, -6.53388119,\n", | |
| " -4.86865215, 1.56853564, -4.84568096, -5.24571228, -4.34553181,\n", | |
| " 1.19739964, -8.96300407, -6.07413718, -5.66936917, -9.10162307,\n", | |
| " -7.626954 , -4.96612254, -9.0948901 , -4.24148847, -5.0913764 ,\n", | |
| " -4.52476997, -4.91708561, -5.7047012 , -5.18291892, -5.6231925 ,\n", | |
| " -3.74753687, -2.38644182, -5.1069895 , -4.54746666, 0.32610541,\n", | |
| " -7.53173038, -2.78363444, -6.16385426, -2.34379364, -4.144581 ,\n", | |
| " -2.5137671 , 1.75003848, -7.39191795, -2.31144608, 0.2806683 ,\n", | |
| " -6.84167982, -6.21334354, -7.54033069, -8.86755271, -1.33341253,\n", | |
| " 2.13838086, -4.36326148, -2.64128861, -5.25058532, -5.1479919 ,\n", | |
| " -3.89250774, -4.48815988, 1.14517427, -1.76937394, -7.3322457 ,\n", | |
| " -8.07716692, -2.12339758, -4.16387593, 2.66831327, -7.99565036,\n", | |
| " -6.44008276, -6.67012985, -2.36474471, -3.83451463, 0.25120136,\n", | |
| " -1.66781202, 0.64495563, 0.27051483, -4.05533118, -4.14705515,\n", | |
| " -3.96143322, -0.24173033, -3.7781134 , 1.48863394, 0.82347723,\n", | |
| " 0.71743525])" | |
| ] | |
| }, | |
| "execution_count": 95, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "y = true_effects.dot(X.T)\n", | |
| "#y[:nsamples] += 2\n", | |
| "y += rng.normal(scale=0.5, size=len(y))\n", | |
| "y" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 96, | |
| "id": "critical-coating", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "array([-2.89003618, -7.37392783, 0.53139135, -2.64806911, 1.31172872,\n", | |
| " -6.03047451, -5.73292675, -5.54473542, 1.21794124, -6.08810311,\n", | |
| " -5.06613556, -2.17749576, 1.153398 , 0.58935916, -5.1086086 ,\n", | |
| " -4.13321636, -6.97651621, 1.02283966, -2.35137949, -4.94724517,\n", | |
| " -4.86981345, -2.43990861, -2.51994556, -9.27638919, -2.42012765,\n", | |
| " -4.35831236, -4.76393238, -4.85760981, -1.2375028 , -5.5706874 ,\n", | |
| " -1.95124271, -8.19967383, -5.31216132, -0.15281248, -4.90213372,\n", | |
| " -3.32718025, -3.83720197, -2.90110106, -2.24997491, -6.53388119,\n", | |
| " -4.86865215, 1.56853564, -4.84568096, -5.24571228, -4.34553181,\n", | |
| " 1.19739964, -8.96300407, -6.07413718, -5.66936917, -9.10162307,\n", | |
| " -7.626954 , -4.96612254, -9.0948901 , -4.24148847, -5.0913764 ,\n", | |
| " -4.52476997, -4.91708561, -5.7047012 , -5.18291892, -5.6231925 ,\n", | |
| " -3.74753687, -2.38644182, -5.1069895 , -4.54746666, 0.32610541,\n", | |
| " -7.53173038, -2.78363444, -6.16385426, -2.34379364, -4.144581 ,\n", | |
| " -2.5137671 , 1.75003848, -7.39191795, -2.31144608, 0.2806683 ,\n", | |
| " -6.84167982, -6.21334354, -7.54033069, -8.86755271, -1.33341253,\n", | |
| " 2.13838086, -4.36326148, -2.64128861, -5.25058532, -5.1479919 ,\n", | |
| " -3.89250774, -4.48815988, 1.14517427, -1.76937394, -7.3322457 ,\n", | |
| " -8.07716692, -2.12339758, -4.16387593, 2.66831327, -7.99565036,\n", | |
| " -6.44008276, -6.67012985, -2.36474471, -3.83451463, 0.25120136,\n", | |
| " -1.66781202, 0.64495563, 0.27051483, -4.05533118, -4.14705515,\n", | |
| " -3.96143322, -0.24173033, -3.7781134 , 1.48863394, 0.82347723,\n", | |
| " 0.71743525])" | |
| ] | |
| }, | |
| "execution_count": 96, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "y" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 97, | |
| "id": "rental-museum", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "from scipy.spatial.distance import pdist, squareform" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 98, | |
| "id": "initial-generic", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "import seaborn as sns" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 99, | |
| "id": "related-burner", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "<seaborn.matrix.ClusterGrid at 0x7f8b4c0b3a50>" | |
| ] | |
| }, | |
| "execution_count": 99, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| }, | |
| { | |
| "data": { | |
| "image/png": "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\n", | |
| "text/plain": [ | |
| "<Figure size 720x720 with 4 Axes>" | |
| ] | |
| }, | |
| "metadata": { | |
| "needs_background": "light" | |
| }, | |
| "output_type": "display_data" | |
| } | |
| ], | |
| "source": [ | |
| "sns.clustermap(squareform(pdist(X)))" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 100, | |
| "id": "passing-joseph", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "import pandas as pd\n", | |
| "from sklearn.linear_model import LinearRegression, Lasso" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 101, | |
| "id": "subject-mortality", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "Lasso(alpha=0.1)" | |
| ] | |
| }, | |
| "execution_count": 101, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "model = Lasso(alpha=0.1)\n", | |
| "model.fit(X, y)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 102, | |
| "id": "durable-three", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/html": [ | |
| "<div>\n", | |
| "<style scoped>\n", | |
| " .dataframe tbody tr th:only-of-type {\n", | |
| " vertical-align: middle;\n", | |
| " }\n", | |
| "\n", | |
| " .dataframe tbody tr th {\n", | |
| " vertical-align: top;\n", | |
| " }\n", | |
| "\n", | |
| " .dataframe thead th {\n", | |
| " text-align: right;\n", | |
| " }\n", | |
| "</style>\n", | |
| "<table border=\"1\" class=\"dataframe\">\n", | |
| " <thead>\n", | |
| " <tr style=\"text-align: right;\">\n", | |
| " <th></th>\n", | |
| " <th>y</th>\n", | |
| " <th>pred</th>\n", | |
| " <th>pop</th>\n", | |
| " </tr>\n", | |
| " </thead>\n", | |
| " <tbody>\n", | |
| " <tr>\n", | |
| " <th>0</th>\n", | |
| " <td>-2.890036</td>\n", | |
| " <td>-2.483981</td>\n", | |
| " <td>1</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>1</th>\n", | |
| " <td>-7.373928</td>\n", | |
| " <td>-5.123978</td>\n", | |
| " <td>1</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>2</th>\n", | |
| " <td>0.531391</td>\n", | |
| " <td>-0.083482</td>\n", | |
| " <td>1</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>3</th>\n", | |
| " <td>-2.648069</td>\n", | |
| " <td>-2.437965</td>\n", | |
| " <td>1</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>4</th>\n", | |
| " <td>1.311729</td>\n", | |
| " <td>0.481124</td>\n", | |
| " <td>1</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>...</th>\n", | |
| " <td>...</td>\n", | |
| " <td>...</td>\n", | |
| " <td>...</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>106</th>\n", | |
| " <td>-0.241730</td>\n", | |
| " <td>-0.149222</td>\n", | |
| " <td>2</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>107</th>\n", | |
| " <td>-3.778113</td>\n", | |
| " <td>-4.339746</td>\n", | |
| " <td>2</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>108</th>\n", | |
| " <td>1.488634</td>\n", | |
| " <td>0.296920</td>\n", | |
| " <td>2</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>109</th>\n", | |
| " <td>0.823477</td>\n", | |
| " <td>0.342125</td>\n", | |
| " <td>2</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>110</th>\n", | |
| " <td>0.717435</td>\n", | |
| " <td>0.309029</td>\n", | |
| " <td>2</td>\n", | |
| " </tr>\n", | |
| " </tbody>\n", | |
| "</table>\n", | |
| "<p>111 rows × 3 columns</p>\n", | |
| "</div>" | |
| ], | |
| "text/plain": [ | |
| " y pred pop\n", | |
| "0 -2.890036 -2.483981 1\n", | |
| "1 -7.373928 -5.123978 1\n", | |
| "2 0.531391 -0.083482 1\n", | |
| "3 -2.648069 -2.437965 1\n", | |
| "4 1.311729 0.481124 1\n", | |
| ".. ... ... ...\n", | |
| "106 -0.241730 -0.149222 2\n", | |
| "107 -3.778113 -4.339746 2\n", | |
| "108 1.488634 0.296920 2\n", | |
| "109 0.823477 0.342125 2\n", | |
| "110 0.717435 0.309029 2\n", | |
| "\n", | |
| "[111 rows x 3 columns]" | |
| ] | |
| }, | |
| "execution_count": 102, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "preds = pd.DataFrame({\"y\": y, \"pred\": model.predict(X), \"pop\": 1})\n", | |
| "preds.loc[nsamples:, \"pop\"] = 2\n", | |
| "preds" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 103, | |
| "id": "latest-thanks", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "pop\n", | |
| "1 0.792984\n", | |
| "2 0.308206\n", | |
| "dtype: float64" | |
| ] | |
| }, | |
| "execution_count": 103, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "preds.groupby(\"pop\").apply(lambda p: ((p[\"y\"] - p[\"pred\"]) ** 2).mean())" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 104, | |
| "id": "higher-married", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/html": [ | |
| "<div>\n", | |
| "<style scoped>\n", | |
| " .dataframe tbody tr th:only-of-type {\n", | |
| " vertical-align: middle;\n", | |
| " }\n", | |
| "\n", | |
| " .dataframe tbody tr th {\n", | |
| " vertical-align: top;\n", | |
| " }\n", | |
| "\n", | |
| " .dataframe thead th {\n", | |
| " text-align: right;\n", | |
| " }\n", | |
| "</style>\n", | |
| "<table border=\"1\" class=\"dataframe\">\n", | |
| " <thead>\n", | |
| " <tr style=\"text-align: right;\">\n", | |
| " <th></th>\n", | |
| " <th>true</th>\n", | |
| " <th>pred</th>\n", | |
| " <th>freq1</th>\n", | |
| " <th>freq2</th>\n", | |
| " </tr>\n", | |
| " </thead>\n", | |
| " <tbody>\n", | |
| " <tr>\n", | |
| " <th>424</th>\n", | |
| " <td>-4.987358</td>\n", | |
| " <td>-4.636665</td>\n", | |
| " <td>0.782155</td>\n", | |
| " <td>0.314272</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>942</th>\n", | |
| " <td>-3.316797</td>\n", | |
| " <td>-2.358242</td>\n", | |
| " <td>0.294759</td>\n", | |
| " <td>0.799501</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>359</th>\n", | |
| " <td>-1.655751</td>\n", | |
| " <td>-0.447206</td>\n", | |
| " <td>0.902841</td>\n", | |
| " <td>0.360887</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>399</th>\n", | |
| " <td>-0.291111</td>\n", | |
| " <td>0.000000</td>\n", | |
| " <td>0.528208</td>\n", | |
| " <td>0.879795</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>279</th>\n", | |
| " <td>-0.212210</td>\n", | |
| " <td>-0.000000</td>\n", | |
| " <td>0.437959</td>\n", | |
| " <td>0.912175</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>706</th>\n", | |
| " <td>0.615461</td>\n", | |
| " <td>0.000000</td>\n", | |
| " <td>0.589905</td>\n", | |
| " <td>0.786503</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>616</th>\n", | |
| " <td>1.582113</td>\n", | |
| " <td>0.526349</td>\n", | |
| " <td>0.082899</td>\n", | |
| " <td>0.794628</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>476</th>\n", | |
| " <td>1.591957</td>\n", | |
| " <td>0.008964</td>\n", | |
| " <td>0.873217</td>\n", | |
| " <td>0.061256</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>389</th>\n", | |
| " <td>2.480210</td>\n", | |
| " <td>1.641444</td>\n", | |
| " <td>0.239744</td>\n", | |
| " <td>0.820666</td>\n", | |
| " </tr>\n", | |
| " </tbody>\n", | |
| "</table>\n", | |
| "</div>" | |
| ], | |
| "text/plain": [ | |
| " true pred freq1 freq2\n", | |
| "424 -4.987358 -4.636665 0.782155 0.314272\n", | |
| "942 -3.316797 -2.358242 0.294759 0.799501\n", | |
| "359 -1.655751 -0.447206 0.902841 0.360887\n", | |
| "399 -0.291111 0.000000 0.528208 0.879795\n", | |
| "279 -0.212210 -0.000000 0.437959 0.912175\n", | |
| "706 0.615461 0.000000 0.589905 0.786503\n", | |
| "616 1.582113 0.526349 0.082899 0.794628\n", | |
| "476 1.591957 0.008964 0.873217 0.061256\n", | |
| "389 2.480210 1.641444 0.239744 0.820666" | |
| ] | |
| }, | |
| "execution_count": 104, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "effects = pd.DataFrame({\"true\": true_effects, \"pred\": model.coef_, \"freq1\": freqs_pop1, \"freq2\": freqs_pop2})\n", | |
| "effects[effects[\"true\"] != 0].sort_values(\"true\")" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 105, | |
| "id": "sought-leader", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/html": [ | |
| "<div>\n", | |
| "<style scoped>\n", | |
| " .dataframe tbody tr th:only-of-type {\n", | |
| " vertical-align: middle;\n", | |
| " }\n", | |
| "\n", | |
| " .dataframe tbody tr th {\n", | |
| " vertical-align: top;\n", | |
| " }\n", | |
| "\n", | |
| " .dataframe thead th {\n", | |
| " text-align: right;\n", | |
| " }\n", | |
| "</style>\n", | |
| "<table border=\"1\" class=\"dataframe\">\n", | |
| " <thead>\n", | |
| " <tr style=\"text-align: right;\">\n", | |
| " <th></th>\n", | |
| " <th>true</th>\n", | |
| " <th>pred</th>\n", | |
| " <th>freq1</th>\n", | |
| " <th>freq2</th>\n", | |
| " </tr>\n", | |
| " </thead>\n", | |
| " <tbody>\n", | |
| " <tr>\n", | |
| " <th>424</th>\n", | |
| " <td>-4.987358</td>\n", | |
| " <td>-4.636665</td>\n", | |
| " <td>0.782155</td>\n", | |
| " <td>0.314272</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>942</th>\n", | |
| " <td>-3.316797</td>\n", | |
| " <td>-2.358242</td>\n", | |
| " <td>0.294759</td>\n", | |
| " <td>0.799501</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>359</th>\n", | |
| " <td>-1.655751</td>\n", | |
| " <td>-0.447206</td>\n", | |
| " <td>0.902841</td>\n", | |
| " <td>0.360887</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>984</th>\n", | |
| " <td>0.000000</td>\n", | |
| " <td>-0.131787</td>\n", | |
| " <td>0.474286</td>\n", | |
| " <td>0.452758</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>529</th>\n", | |
| " <td>0.000000</td>\n", | |
| " <td>-0.059976</td>\n", | |
| " <td>0.556470</td>\n", | |
| " <td>0.096112</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>847</th>\n", | |
| " <td>0.000000</td>\n", | |
| " <td>-0.058744</td>\n", | |
| " <td>0.647811</td>\n", | |
| " <td>0.407043</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>533</th>\n", | |
| " <td>0.000000</td>\n", | |
| " <td>-0.045375</td>\n", | |
| " <td>0.616533</td>\n", | |
| " <td>0.053800</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>664</th>\n", | |
| " <td>0.000000</td>\n", | |
| " <td>0.000000</td>\n", | |
| " <td>0.734732</td>\n", | |
| " <td>0.037101</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>663</th>\n", | |
| " <td>0.000000</td>\n", | |
| " <td>-0.000000</td>\n", | |
| " <td>0.468895</td>\n", | |
| " <td>0.420180</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>662</th>\n", | |
| " <td>0.000000</td>\n", | |
| " <td>0.000000</td>\n", | |
| " <td>0.285418</td>\n", | |
| " <td>0.819648</td>\n", | |
| " </tr>\n", | |
| " </tbody>\n", | |
| "</table>\n", | |
| "</div>" | |
| ], | |
| "text/plain": [ | |
| " true pred freq1 freq2\n", | |
| "424 -4.987358 -4.636665 0.782155 0.314272\n", | |
| "942 -3.316797 -2.358242 0.294759 0.799501\n", | |
| "359 -1.655751 -0.447206 0.902841 0.360887\n", | |
| "984 0.000000 -0.131787 0.474286 0.452758\n", | |
| "529 0.000000 -0.059976 0.556470 0.096112\n", | |
| "847 0.000000 -0.058744 0.647811 0.407043\n", | |
| "533 0.000000 -0.045375 0.616533 0.053800\n", | |
| "664 0.000000 0.000000 0.734732 0.037101\n", | |
| "663 0.000000 -0.000000 0.468895 0.420180\n", | |
| "662 0.000000 0.000000 0.285418 0.819648" | |
| ] | |
| }, | |
| "execution_count": 105, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "effects.sort_values(\"pred\").head(10)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 106, | |
| "id": "differential-miller", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/html": [ | |
| "<div>\n", | |
| "<style scoped>\n", | |
| " .dataframe tbody tr th:only-of-type {\n", | |
| " vertical-align: middle;\n", | |
| " }\n", | |
| "\n", | |
| " .dataframe tbody tr th {\n", | |
| " vertical-align: top;\n", | |
| " }\n", | |
| "\n", | |
| " .dataframe thead th {\n", | |
| " text-align: right;\n", | |
| " }\n", | |
| "</style>\n", | |
| "<table border=\"1\" class=\"dataframe\">\n", | |
| " <thead>\n", | |
| " <tr style=\"text-align: right;\">\n", | |
| " <th></th>\n", | |
| " <th>true</th>\n", | |
| " <th>pred</th>\n", | |
| " <th>freq1</th>\n", | |
| " <th>freq2</th>\n", | |
| " </tr>\n", | |
| " </thead>\n", | |
| " <tbody>\n", | |
| " <tr>\n", | |
| " <th>337</th>\n", | |
| " <td>0.000000</td>\n", | |
| " <td>0.000000</td>\n", | |
| " <td>0.572401</td>\n", | |
| " <td>0.765802</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>476</th>\n", | |
| " <td>1.591957</td>\n", | |
| " <td>0.008964</td>\n", | |
| " <td>0.873217</td>\n", | |
| " <td>0.061256</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>925</th>\n", | |
| " <td>0.000000</td>\n", | |
| " <td>0.021720</td>\n", | |
| " <td>0.346882</td>\n", | |
| " <td>0.865417</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>744</th>\n", | |
| " <td>0.000000</td>\n", | |
| " <td>0.033096</td>\n", | |
| " <td>0.587489</td>\n", | |
| " <td>0.797726</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>862</th>\n", | |
| " <td>0.000000</td>\n", | |
| " <td>0.045205</td>\n", | |
| " <td>0.328772</td>\n", | |
| " <td>0.678966</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>755</th>\n", | |
| " <td>0.000000</td>\n", | |
| " <td>0.061040</td>\n", | |
| " <td>0.641671</td>\n", | |
| " <td>0.571747</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>85</th>\n", | |
| " <td>0.000000</td>\n", | |
| " <td>0.074928</td>\n", | |
| " <td>0.636585</td>\n", | |
| " <td>0.817731</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>111</th>\n", | |
| " <td>0.000000</td>\n", | |
| " <td>0.162889</td>\n", | |
| " <td>0.515959</td>\n", | |
| " <td>0.974238</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>616</th>\n", | |
| " <td>1.582113</td>\n", | |
| " <td>0.526349</td>\n", | |
| " <td>0.082899</td>\n", | |
| " <td>0.794628</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>389</th>\n", | |
| " <td>2.480210</td>\n", | |
| " <td>1.641444</td>\n", | |
| " <td>0.239744</td>\n", | |
| " <td>0.820666</td>\n", | |
| " </tr>\n", | |
| " </tbody>\n", | |
| "</table>\n", | |
| "</div>" | |
| ], | |
| "text/plain": [ | |
| " true pred freq1 freq2\n", | |
| "337 0.000000 0.000000 0.572401 0.765802\n", | |
| "476 1.591957 0.008964 0.873217 0.061256\n", | |
| "925 0.000000 0.021720 0.346882 0.865417\n", | |
| "744 0.000000 0.033096 0.587489 0.797726\n", | |
| "862 0.000000 0.045205 0.328772 0.678966\n", | |
| "755 0.000000 0.061040 0.641671 0.571747\n", | |
| "85 0.000000 0.074928 0.636585 0.817731\n", | |
| "111 0.000000 0.162889 0.515959 0.974238\n", | |
| "616 1.582113 0.526349 0.082899 0.794628\n", | |
| "389 2.480210 1.641444 0.239744 0.820666" | |
| ] | |
| }, | |
| "execution_count": 106, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "effects.sort_values(\"pred\").tail(10)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "wound-converter", | |
| "metadata": {}, | |
| "source": [ | |
| "RelieF" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 107, | |
| "id": "known-lotus", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "dists = squareform(pdist(X))" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 108, | |
| "id": "divine-capacity", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "nneighbors = 5\n", | |
| "weights = np.zeros(nloci)\n", | |
| "\n", | |
| "k = 1\n", | |
| "n = nsamples + nsamples2\n", | |
| "std = np.std(y)\n", | |
| "for i in range(n):\n", | |
| " closest_samples = np.argsort(dists[i])\n", | |
| "\n", | |
| " nhits = 0\n", | |
| " nmiss = 0\n", | |
| " for j in closest_samples:\n", | |
| " if i == j:\n", | |
| " continue\n", | |
| " elif (nhits > k) and (nmiss > k):\n", | |
| " break\n", | |
| "\n", | |
| " xij = np.abs(X[i,] - X[j,])\n", | |
| " yij = y[i] - y[j]\n", | |
| "\n", | |
| " if abs(yij) < std:\n", | |
| " # Its a hit\n", | |
| " if nhits > k:\n", | |
| " continue\n", | |
| " weights -= xij / (n * k)\n", | |
| " nhits += 1\n", | |
| " else:\n", | |
| " if nmiss > k:\n", | |
| " continue\n", | |
| " weights += xij / (n * k)\n", | |
| " nmiss += 1" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 109, | |
| "id": "downtown-sellers", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/html": [ | |
| "<div>\n", | |
| "<style scoped>\n", | |
| " .dataframe tbody tr th:only-of-type {\n", | |
| " vertical-align: middle;\n", | |
| " }\n", | |
| "\n", | |
| " .dataframe tbody tr th {\n", | |
| " vertical-align: top;\n", | |
| " }\n", | |
| "\n", | |
| " .dataframe thead th {\n", | |
| " text-align: right;\n", | |
| " }\n", | |
| "</style>\n", | |
| "<table border=\"1\" class=\"dataframe\">\n", | |
| " <thead>\n", | |
| " <tr style=\"text-align: right;\">\n", | |
| " <th></th>\n", | |
| " <th>true</th>\n", | |
| " <th>pred</th>\n", | |
| " <th>freq1</th>\n", | |
| " <th>freq2</th>\n", | |
| " <th>relief</th>\n", | |
| " </tr>\n", | |
| " </thead>\n", | |
| " <tbody>\n", | |
| " <tr>\n", | |
| " <th>424</th>\n", | |
| " <td>-4.987358</td>\n", | |
| " <td>-4.636665</td>\n", | |
| " <td>0.782155</td>\n", | |
| " <td>0.314272</td>\n", | |
| " <td>0.963964</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>942</th>\n", | |
| " <td>-3.316797</td>\n", | |
| " <td>-2.358242</td>\n", | |
| " <td>0.294759</td>\n", | |
| " <td>0.799501</td>\n", | |
| " <td>0.360360</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>382</th>\n", | |
| " <td>0.000000</td>\n", | |
| " <td>-0.000000</td>\n", | |
| " <td>0.434281</td>\n", | |
| " <td>0.122560</td>\n", | |
| " <td>0.351351</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>681</th>\n", | |
| " <td>0.000000</td>\n", | |
| " <td>0.000000</td>\n", | |
| " <td>0.338203</td>\n", | |
| " <td>0.177349</td>\n", | |
| " <td>0.333333</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>130</th>\n", | |
| " <td>0.000000</td>\n", | |
| " <td>0.000000</td>\n", | |
| " <td>0.492619</td>\n", | |
| " <td>0.430562</td>\n", | |
| " <td>0.279279</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>...</th>\n", | |
| " <td>...</td>\n", | |
| " <td>...</td>\n", | |
| " <td>...</td>\n", | |
| " <td>...</td>\n", | |
| " <td>...</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>126</th>\n", | |
| " <td>0.000000</td>\n", | |
| " <td>-0.000000</td>\n", | |
| " <td>0.348554</td>\n", | |
| " <td>0.303019</td>\n", | |
| " <td>-0.261261</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>163</th>\n", | |
| " <td>0.000000</td>\n", | |
| " <td>0.000000</td>\n", | |
| " <td>0.333717</td>\n", | |
| " <td>0.824376</td>\n", | |
| " <td>-0.261261</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>695</th>\n", | |
| " <td>0.000000</td>\n", | |
| " <td>0.000000</td>\n", | |
| " <td>0.698863</td>\n", | |
| " <td>0.816582</td>\n", | |
| " <td>-0.279279</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>298</th>\n", | |
| " <td>0.000000</td>\n", | |
| " <td>-0.000000</td>\n", | |
| " <td>0.500859</td>\n", | |
| " <td>0.073400</td>\n", | |
| " <td>-0.279279</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>762</th>\n", | |
| " <td>0.000000</td>\n", | |
| " <td>-0.000000</td>\n", | |
| " <td>0.490440</td>\n", | |
| " <td>0.624082</td>\n", | |
| " <td>-0.297297</td>\n", | |
| " </tr>\n", | |
| " </tbody>\n", | |
| "</table>\n", | |
| "<p>1000 rows × 5 columns</p>\n", | |
| "</div>" | |
| ], | |
| "text/plain": [ | |
| " true pred freq1 freq2 relief\n", | |
| "424 -4.987358 -4.636665 0.782155 0.314272 0.963964\n", | |
| "942 -3.316797 -2.358242 0.294759 0.799501 0.360360\n", | |
| "382 0.000000 -0.000000 0.434281 0.122560 0.351351\n", | |
| "681 0.000000 0.000000 0.338203 0.177349 0.333333\n", | |
| "130 0.000000 0.000000 0.492619 0.430562 0.279279\n", | |
| ".. ... ... ... ... ...\n", | |
| "126 0.000000 -0.000000 0.348554 0.303019 -0.261261\n", | |
| "163 0.000000 0.000000 0.333717 0.824376 -0.261261\n", | |
| "695 0.000000 0.000000 0.698863 0.816582 -0.279279\n", | |
| "298 0.000000 -0.000000 0.500859 0.073400 -0.279279\n", | |
| "762 0.000000 -0.000000 0.490440 0.624082 -0.297297\n", | |
| "\n", | |
| "[1000 rows x 5 columns]" | |
| ] | |
| }, | |
| "execution_count": 109, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "effects = pd.DataFrame({\"true\": true_effects, \"pred\": model.coef_, \"freq1\": freqs_pop1, \"freq2\": freqs_pop2, \"relief\": weights})\n", | |
| "effects.sort_values(\"relief\", ascending=False)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 110, | |
| "id": "european-contractor", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/html": [ | |
| "<div>\n", | |
| "<style scoped>\n", | |
| " .dataframe tbody tr th:only-of-type {\n", | |
| " vertical-align: middle;\n", | |
| " }\n", | |
| "\n", | |
| " .dataframe tbody tr th {\n", | |
| " vertical-align: top;\n", | |
| " }\n", | |
| "\n", | |
| " .dataframe thead th {\n", | |
| " text-align: right;\n", | |
| " }\n", | |
| "</style>\n", | |
| "<table border=\"1\" class=\"dataframe\">\n", | |
| " <thead>\n", | |
| " <tr style=\"text-align: right;\">\n", | |
| " <th></th>\n", | |
| " <th>true</th>\n", | |
| " <th>pred</th>\n", | |
| " <th>freq1</th>\n", | |
| " <th>freq2</th>\n", | |
| " <th>relief</th>\n", | |
| " </tr>\n", | |
| " </thead>\n", | |
| " <tbody>\n", | |
| " <tr>\n", | |
| " <th>279</th>\n", | |
| " <td>-0.212210</td>\n", | |
| " <td>-0.000000</td>\n", | |
| " <td>0.437959</td>\n", | |
| " <td>0.912175</td>\n", | |
| " <td>0.027027</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>359</th>\n", | |
| " <td>-1.655751</td>\n", | |
| " <td>-0.447206</td>\n", | |
| " <td>0.902841</td>\n", | |
| " <td>0.360887</td>\n", | |
| " <td>0.054054</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>389</th>\n", | |
| " <td>2.480210</td>\n", | |
| " <td>1.641444</td>\n", | |
| " <td>0.239744</td>\n", | |
| " <td>0.820666</td>\n", | |
| " <td>0.090090</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>399</th>\n", | |
| " <td>-0.291111</td>\n", | |
| " <td>0.000000</td>\n", | |
| " <td>0.528208</td>\n", | |
| " <td>0.879795</td>\n", | |
| " <td>0.036036</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>424</th>\n", | |
| " <td>-4.987358</td>\n", | |
| " <td>-4.636665</td>\n", | |
| " <td>0.782155</td>\n", | |
| " <td>0.314272</td>\n", | |
| " <td>0.963964</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>476</th>\n", | |
| " <td>1.591957</td>\n", | |
| " <td>0.008964</td>\n", | |
| " <td>0.873217</td>\n", | |
| " <td>0.061256</td>\n", | |
| " <td>-0.135135</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>616</th>\n", | |
| " <td>1.582113</td>\n", | |
| " <td>0.526349</td>\n", | |
| " <td>0.082899</td>\n", | |
| " <td>0.794628</td>\n", | |
| " <td>0.045045</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>706</th>\n", | |
| " <td>0.615461</td>\n", | |
| " <td>0.000000</td>\n", | |
| " <td>0.589905</td>\n", | |
| " <td>0.786503</td>\n", | |
| " <td>0.198198</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>942</th>\n", | |
| " <td>-3.316797</td>\n", | |
| " <td>-2.358242</td>\n", | |
| " <td>0.294759</td>\n", | |
| " <td>0.799501</td>\n", | |
| " <td>0.360360</td>\n", | |
| " </tr>\n", | |
| " </tbody>\n", | |
| "</table>\n", | |
| "</div>" | |
| ], | |
| "text/plain": [ | |
| " true pred freq1 freq2 relief\n", | |
| "279 -0.212210 -0.000000 0.437959 0.912175 0.027027\n", | |
| "359 -1.655751 -0.447206 0.902841 0.360887 0.054054\n", | |
| "389 2.480210 1.641444 0.239744 0.820666 0.090090\n", | |
| "399 -0.291111 0.000000 0.528208 0.879795 0.036036\n", | |
| "424 -4.987358 -4.636665 0.782155 0.314272 0.963964\n", | |
| "476 1.591957 0.008964 0.873217 0.061256 -0.135135\n", | |
| "616 1.582113 0.526349 0.082899 0.794628 0.045045\n", | |
| "706 0.615461 0.000000 0.589905 0.786503 0.198198\n", | |
| "942 -3.316797 -2.358242 0.294759 0.799501 0.360360" | |
| ] | |
| }, | |
| "execution_count": 110, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "effects[effects[\"true\"] != 0]" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 111, | |
| "id": "raised-charter", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "count 1.000000e+03\n", | |
| "mean 4.630631e-03\n", | |
| "std 1.004398e-01\n", | |
| "min -2.972973e-01\n", | |
| "25% -5.405405e-02\n", | |
| "50% 3.469447e-18\n", | |
| "75% 6.306306e-02\n", | |
| "max 9.639640e-01\n", | |
| "Name: relief, dtype: float64" | |
| ] | |
| }, | |
| "execution_count": 111, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "effects[\"relief\"].describe()" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "id": "industrial-christmas", | |
| "metadata": {}, | |
| "source": [ | |
| "[SKrebate](https://epistasislab.github.io/scikit-rebate/using/) has more algorithms but they are really SLOW.\n", | |
| "It's python loops all the way down.\n", | |
| "\n", | |
| "Plenty of other implementations in R." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 112, | |
| "id": "solved-second", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "sim = X.dot(X.T).astype(float)\n", | |
| "sim -= sim.min()\n", | |
| "sim /= 50 #np.percentile(sim, 25)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 113, | |
| "id": "twelve-scheduling", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "<AxesSubplot:>" | |
| ] | |
| }, | |
| "execution_count": 113, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| }, | |
| { | |
| "data": { | |
| "image/png": "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\n", | |
| "text/plain": [ | |
| "<Figure size 432x288 with 2 Axes>" | |
| ] | |
| }, | |
| "metadata": { | |
| "needs_background": "light" | |
| }, | |
| "output_type": "display_data" | |
| } | |
| ], | |
| "source": [ | |
| "sns.heatmap(sim)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 114, | |
| "id": "regular-remove", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "from itertools import combinations\n", | |
| "\n", | |
| "X_pairs = []\n", | |
| "y_pairs = []\n", | |
| "pair_sim = []\n", | |
| "\n", | |
| "for i, j in combinations(range(X.shape[0]), 2):\n", | |
| " if i == j:\n", | |
| " continue\n", | |
| "\n", | |
| " if y[i] > y[j]:\n", | |
| " y_pairij = 1\n", | |
| " elif np.abs(y[i] - y[j]) < np.std(y):\n", | |
| " y_pairij = 0.5\n", | |
| " else:\n", | |
| " y_pairij = 0\n", | |
| "\n", | |
| " X_pairs.append((X[i], X[j]))\n", | |
| " y_pairs.append(y_pairij)\n", | |
| " pair_sim.append(sim[i, j])\n", | |
| " \n", | |
| "X_pairs = np.array(X_pairs)\n", | |
| "y_pairs = np.array(y_pairs)\n", | |
| "pair_sim = np.array(pair_sim)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 115, | |
| "id": "elect-terrorist", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "tf.keras.backend.clear_session()" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 116, | |
| "id": "drawn-sapphire", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "tf.Tensor(5.658346, shape=(), dtype=float32)\n" | |
| ] | |
| } | |
| ], | |
| "source": [ | |
| "optimizer = Adam(learning_rate=1)\n", | |
| "loss_fn = BinaryCrossentropy(from_logits=True)\n", | |
| "\n", | |
| "model = Sequential([Dense(1, activation=\"linear\", use_bias=False, kernel_regularizer=tf.keras.regularizers.l1(1))])\n", | |
| "\n", | |
| "n_epoch = 100\n", | |
| "for _ in range(n_epoch):\n", | |
| " with tf.GradientTape() as tape:\n", | |
| " s1 = model(np.abs(X_pairs[:, 0, :] - X_pairs[:, 1, :]))\n", | |
| " #s2 = model(X_pairs[:, 1, :])\n", | |
| " pred = tfsigmoid(s1)\n", | |
| " loss_value = loss_fn(y_pairs.reshape(-1, 1), pred, sample_weight=pair_sim)\n", | |
| "\n", | |
| " grads = tape.gradient(loss_value, model.trainable_variables)\n", | |
| " optimizer.apply_gradients(zip(grads, model.trainable_variables))\n", | |
| "\n", | |
| "print(loss_value)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 117, | |
| "id": "declared-recycling", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/html": [ | |
| "<div>\n", | |
| "<style scoped>\n", | |
| " .dataframe tbody tr th:only-of-type {\n", | |
| " vertical-align: middle;\n", | |
| " }\n", | |
| "\n", | |
| " .dataframe tbody tr th {\n", | |
| " vertical-align: top;\n", | |
| " }\n", | |
| "\n", | |
| " .dataframe thead th {\n", | |
| " text-align: right;\n", | |
| " }\n", | |
| "</style>\n", | |
| "<table border=\"1\" class=\"dataframe\">\n", | |
| " <thead>\n", | |
| " <tr style=\"text-align: right;\">\n", | |
| " <th></th>\n", | |
| " <th>true</th>\n", | |
| " <th>freq1</th>\n", | |
| " <th>freq2</th>\n", | |
| " <th>relief</th>\n", | |
| " <th>nn</th>\n", | |
| " </tr>\n", | |
| " </thead>\n", | |
| " <tbody>\n", | |
| " <tr>\n", | |
| " <th>869</th>\n", | |
| " <td>0.0</td>\n", | |
| " <td>0.996953</td>\n", | |
| " <td>0.807186</td>\n", | |
| " <td>0.009009</td>\n", | |
| " <td>7.943168</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>484</th>\n", | |
| " <td>0.0</td>\n", | |
| " <td>0.036648</td>\n", | |
| " <td>0.687834</td>\n", | |
| " <td>-0.072072</td>\n", | |
| " <td>4.447771</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>793</th>\n", | |
| " <td>0.0</td>\n", | |
| " <td>0.998612</td>\n", | |
| " <td>0.596046</td>\n", | |
| " <td>0.036036</td>\n", | |
| " <td>3.997329</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>894</th>\n", | |
| " <td>0.0</td>\n", | |
| " <td>0.097156</td>\n", | |
| " <td>0.121705</td>\n", | |
| " <td>0.063063</td>\n", | |
| " <td>3.377757</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>561</th>\n", | |
| " <td>0.0</td>\n", | |
| " <td>0.003345</td>\n", | |
| " <td>0.196184</td>\n", | |
| " <td>0.000000</td>\n", | |
| " <td>3.357172</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>...</th>\n", | |
| " <td>...</td>\n", | |
| " <td>...</td>\n", | |
| " <td>...</td>\n", | |
| " <td>...</td>\n", | |
| " <td>...</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>530</th>\n", | |
| " <td>0.0</td>\n", | |
| " <td>0.959838</td>\n", | |
| " <td>0.461836</td>\n", | |
| " <td>0.063063</td>\n", | |
| " <td>-3.173112</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>675</th>\n", | |
| " <td>0.0</td>\n", | |
| " <td>0.994988</td>\n", | |
| " <td>0.836241</td>\n", | |
| " <td>0.018018</td>\n", | |
| " <td>-3.580165</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>625</th>\n", | |
| " <td>0.0</td>\n", | |
| " <td>0.997831</td>\n", | |
| " <td>0.423959</td>\n", | |
| " <td>0.045045</td>\n", | |
| " <td>-3.708804</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>742</th>\n", | |
| " <td>0.0</td>\n", | |
| " <td>0.994504</td>\n", | |
| " <td>0.051681</td>\n", | |
| " <td>0.000000</td>\n", | |
| " <td>-4.116137</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>779</th>\n", | |
| " <td>0.0</td>\n", | |
| " <td>0.031603</td>\n", | |
| " <td>0.928924</td>\n", | |
| " <td>0.009009</td>\n", | |
| " <td>-4.695507</td>\n", | |
| " </tr>\n", | |
| " </tbody>\n", | |
| "</table>\n", | |
| "<p>1000 rows × 5 columns</p>\n", | |
| "</div>" | |
| ], | |
| "text/plain": [ | |
| " true freq1 freq2 relief nn\n", | |
| "869 0.0 0.996953 0.807186 0.009009 7.943168\n", | |
| "484 0.0 0.036648 0.687834 -0.072072 4.447771\n", | |
| "793 0.0 0.998612 0.596046 0.036036 3.997329\n", | |
| "894 0.0 0.097156 0.121705 0.063063 3.377757\n", | |
| "561 0.0 0.003345 0.196184 0.000000 3.357172\n", | |
| ".. ... ... ... ... ...\n", | |
| "530 0.0 0.959838 0.461836 0.063063 -3.173112\n", | |
| "675 0.0 0.994988 0.836241 0.018018 -3.580165\n", | |
| "625 0.0 0.997831 0.423959 0.045045 -3.708804\n", | |
| "742 0.0 0.994504 0.051681 0.000000 -4.116137\n", | |
| "779 0.0 0.031603 0.928924 0.009009 -4.695507\n", | |
| "\n", | |
| "[1000 rows x 5 columns]" | |
| ] | |
| }, | |
| "execution_count": 117, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "effects = pd.DataFrame({\"true\": true_effects, \"freq1\": freqs_pop1, \"freq2\": freqs_pop2, \"relief\": weights, \"nn\": model.get_weights()[0][:, 0]})\n", | |
| "effects.sort_values(\"nn\", ascending=False)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "vital-exchange", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 118, | |
| "id": "north-winning", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/html": [ | |
| "<div>\n", | |
| "<style scoped>\n", | |
| " .dataframe tbody tr th:only-of-type {\n", | |
| " vertical-align: middle;\n", | |
| " }\n", | |
| "\n", | |
| " .dataframe tbody tr th {\n", | |
| " vertical-align: top;\n", | |
| " }\n", | |
| "\n", | |
| " .dataframe thead th {\n", | |
| " text-align: right;\n", | |
| " }\n", | |
| "</style>\n", | |
| "<table border=\"1\" class=\"dataframe\">\n", | |
| " <thead>\n", | |
| " <tr style=\"text-align: right;\">\n", | |
| " <th></th>\n", | |
| " <th>true</th>\n", | |
| " <th>freq1</th>\n", | |
| " <th>freq2</th>\n", | |
| " <th>relief</th>\n", | |
| " <th>nn</th>\n", | |
| " </tr>\n", | |
| " </thead>\n", | |
| " <tbody>\n", | |
| " <tr>\n", | |
| " <th>279</th>\n", | |
| " <td>-0.212210</td>\n", | |
| " <td>0.437959</td>\n", | |
| " <td>0.912175</td>\n", | |
| " <td>0.027027</td>\n", | |
| " <td>-0.100957</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>359</th>\n", | |
| " <td>-1.655751</td>\n", | |
| " <td>0.902841</td>\n", | |
| " <td>0.360887</td>\n", | |
| " <td>0.054054</td>\n", | |
| " <td>-2.756028</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>389</th>\n", | |
| " <td>2.480210</td>\n", | |
| " <td>0.239744</td>\n", | |
| " <td>0.820666</td>\n", | |
| " <td>0.090090</td>\n", | |
| " <td>-0.689894</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>399</th>\n", | |
| " <td>-0.291111</td>\n", | |
| " <td>0.528208</td>\n", | |
| " <td>0.879795</td>\n", | |
| " <td>0.036036</td>\n", | |
| " <td>-0.063009</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>424</th>\n", | |
| " <td>-4.987358</td>\n", | |
| " <td>0.782155</td>\n", | |
| " <td>0.314272</td>\n", | |
| " <td>0.963964</td>\n", | |
| " <td>-2.341457</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>476</th>\n", | |
| " <td>1.591957</td>\n", | |
| " <td>0.873217</td>\n", | |
| " <td>0.061256</td>\n", | |
| " <td>-0.135135</td>\n", | |
| " <td>0.066689</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>616</th>\n", | |
| " <td>1.582113</td>\n", | |
| " <td>0.082899</td>\n", | |
| " <td>0.794628</td>\n", | |
| " <td>0.045045</td>\n", | |
| " <td>-2.427979</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>706</th>\n", | |
| " <td>0.615461</td>\n", | |
| " <td>0.589905</td>\n", | |
| " <td>0.786503</td>\n", | |
| " <td>0.198198</td>\n", | |
| " <td>-0.165121</td>\n", | |
| " </tr>\n", | |
| " <tr>\n", | |
| " <th>942</th>\n", | |
| " <td>-3.316797</td>\n", | |
| " <td>0.294759</td>\n", | |
| " <td>0.799501</td>\n", | |
| " <td>0.360360</td>\n", | |
| " <td>1.856650</td>\n", | |
| " </tr>\n", | |
| " </tbody>\n", | |
| "</table>\n", | |
| "</div>" | |
| ], | |
| "text/plain": [ | |
| " true freq1 freq2 relief nn\n", | |
| "279 -0.212210 0.437959 0.912175 0.027027 -0.100957\n", | |
| "359 -1.655751 0.902841 0.360887 0.054054 -2.756028\n", | |
| "389 2.480210 0.239744 0.820666 0.090090 -0.689894\n", | |
| "399 -0.291111 0.528208 0.879795 0.036036 -0.063009\n", | |
| "424 -4.987358 0.782155 0.314272 0.963964 -2.341457\n", | |
| "476 1.591957 0.873217 0.061256 -0.135135 0.066689\n", | |
| "616 1.582113 0.082899 0.794628 0.045045 -2.427979\n", | |
| "706 0.615461 0.589905 0.786503 0.198198 -0.165121\n", | |
| "942 -3.316797 0.294759 0.799501 0.360360 1.856650" | |
| ] | |
| }, | |
| "execution_count": 118, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "effects[effects[\"true\"] != 0]" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 119, | |
| "id": "extra-composite", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "count 1000.000000\n", | |
| "mean -0.027586\n", | |
| "std 0.865034\n", | |
| "min -4.695507\n", | |
| "25% -0.400552\n", | |
| "50% -0.027578\n", | |
| "75% 0.361622\n", | |
| "max 7.943168\n", | |
| "Name: nn, dtype: float64" | |
| ] | |
| }, | |
| "execution_count": 119, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "effects[\"nn\"].describe()" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 120, | |
| "id": "simple-transition", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "<AxesSubplot:xlabel='nn', ylabel='Density'>" | |
| ] | |
| }, | |
| "execution_count": 120, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| }, | |
| { | |
| "data": { | |
| "image/png": "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\n", | |
| "text/plain": [ | |
| "<Figure size 432x288 with 1 Axes>" | |
| ] | |
| }, | |
| "metadata": { | |
| "needs_background": "light" | |
| }, | |
| "output_type": "display_data" | |
| } | |
| ], | |
| "source": [ | |
| "sns.kdeplot(effects[\"nn\"])" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "bottom-structure", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [] | |
| } | |
| ], | |
| "metadata": { | |
| "kernelspec": { | |
| "display_name": "Python [conda env:.conda-condaenv]", | |
| "language": "python", | |
| "name": "conda-env-.conda-condaenv-py" | |
| }, | |
| "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.7" | |
| } | |
| }, | |
| "nbformat": 4, | |
| "nbformat_minor": 5 | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment