{
  "id": 20556,
  "title": "MAP5 function or eval_metric",
  "url": "/competitions/expedia-hotel-recommendations/discussion/20556",
  "author_name": "",
  "post_date": "2016-04-29T21:55:59.840Z",
  "votes": 1,
  "comment_count": 25,
  "views": 6318,
  "content": "<p>Hi,  I've done ncdg5 before via a custom function in xgboost.  I also see that there is an &quot;map&quot; and &quot;map@n&quot; eval_metric in xgboost, but simply it doesn't work for me. R crashes</p>\n\n<p>I don't know if these is crossing the competition rules, but is there a hint on these?</p>\n\n<p>Thanks</p>",
  "messages": [
    {
      "id": "117628",
      "postDate": "04/29/2016 21:55:59",
      "content": "<p>Hi,  I've done ncdg5 before via a custom function in xgboost.  I also see that there is an &quot;map&quot; and &quot;map@n&quot; eval_metric in xgboost, but simply it doesn't work for me. R crashes</p>\n\n<p>I don't know if these is crossing the competition rules, but is there a hint on these?</p>\n\n<p>Thanks</p>",
      "rawMarkdown": "Hi,  I've done ncdg5 before via a custom function in xgboost.  I also see that there is an \"map\" and \"map@n\" eval_metric in xgboost, but simply it doesn't work for me. R crashes\r\n\r\nI don't know if these is crossing the competition rules, but is there a hint on these?\r\n\r\nThanks",
      "votes": null
    },
    {
      "id": "117630",
      "postDate": "04/29/2016 22:01:27",
      "content": "<p>My understanding is that the ndcg@n and map@n eval functions built into xgboost only work when using &quot;rank:pairwise&quot; for your objective. If you want to use a different objective then you need to define a custom map@5 eval function for it to work.</p>\n\n<p>For reference, see this <a href=\"https://github.com/dmlc/xgboost/issues/1143\">issue</a> on xgboost's GitHub.</p>",
      "rawMarkdown": "My understanding is that the ndcg@n and map@n eval functions built into xgboost only work when using \"rank:pairwise\" for your objective. If you want to use a different objective then you need to define a custom map@5 eval function for it to work.\r\n\r\nFor reference, see this [issue][1] on xgboost's GitHub.\r\n\r\n\r\n  [1]: https://github.com/dmlc/xgboost/issues/1143",
      "votes": null
    },
    {
      "id": "117705",
      "postDate": "04/30/2016 13:01:40",
      "content": "<p>[quote=Branden Murray;117630]</p>\n\n<p>For reference, see this <a href=\"https://github.com/dmlc/xgboost/issues/1143\">issue</a> on xgboost's GitHub.</p>\n\n<p>[/quote]</p>\n\n<p>That's my issue ;). What I ended up doing is optimising for mlogloss instead, but if someone smarter than me can implement map@5 as a custom metric in xgboost that would be very useful!</p>\n\n<p>I think xgboost might not be the way to go in this competition, as it takes so long to train (as it is 100-class problem, it trains 100 trees per boosting rounds) and this means it was taking me 12 hours on dual xeons to converge.</p>",
      "rawMarkdown": "[quote=Branden Murray;117630]\r\n\r\nFor reference, see this [issue][1] on xgboost's GitHub.\r\n\r\n\r\n  [1]: https://github.com/dmlc/xgboost/issues/1143\r\n\r\n[/quote]\r\n\r\nThat's my issue ;). What I ended up doing is optimising for mlogloss instead, but if someone smarter than me can implement map@5 as a custom metric in xgboost that would be very useful!\r\n\r\nI think xgboost might not be the way to go in this competition, as it takes so long to train (as it is 100-class problem, it trains 100 trees per boosting rounds) and this means it was taking me 12 hours on dual xeons to converge.",
      "votes": null
    },
    {
      "id": "117749",
      "postDate": "04/30/2016 17:20:28",
      "content": "<p>In R, </p>\n\n<pre><code>library(Metrics)\nmap5 &lt;- function(preds, dtrain) {\n  labels &lt;- as.list(getinfo(dtrain,&quot;label&quot;))\n  num.class = 100\n  pred &lt;- matrix(preds, nrow = num.class)\n  top &lt;- t(apply(pred, 2, function(y) order(y)[num.class:(num.class-4)]-1))\n  top &lt;- split(top, 1:NROW(top))\n\n  map &lt;- mapk(5, labels, top)\n  return(list(metric = &quot;map5&quot;, value = map))\n}\n</code></pre>\n\n<p>Then set <code>eval_metric=map5</code> in your params.</p>\n\n<p><em>[edit] Realized the <code>mapk</code> function takes in lists, not vectors. This now gives the same output as SK's function below.</em></p>",
      "rawMarkdown": "In R, \r\n\r\n    library(Metrics)\r\n    map5 <- function(preds, dtrain) {\r\n      labels <- as.list(getinfo(dtrain,\"label\"))\r\n      num.class = 100\r\n      pred <- matrix(preds, nrow = num.class)\r\n      top <- t(apply(pred, 2, function(y) order(y)[num.class:(num.class-4)]-1))\r\n      top <- split(top, 1:NROW(top))\r\n      \r\n      map <- mapk(5, labels, top)\r\n      return(list(metric = \"map5\", value = map))\r\n    }\r\n\r\nThen set `eval_metric=map5` in your params.\r\n\r\n*[edit] Realized the `mapk` function takes in lists, not vectors. This now gives the same output as SK's function below.*",
      "votes": null
    },
    {
      "id": "117762",
      "postDate": "04/30/2016 19:07:02",
      "content": "<p>I believe in the classification case (and as long as you make sure to return 5 unique predictions per example) the metric degenerates to a simple:</p>\n\n<pre><code>map5 = function(preds, dtrain) {\n  labels = getinfo(dtrain, 'label')\n  preds = t(matrix(preds, ncol = length(labels)))\n  preds = t(apply(preds, 1, order, decreasing = T))[, 1:5] - 1\n  succ = (preds == labels)\n  w = 1 / (1:5)\n  map5 = mean(succ %*% w)\n  return (list(metric = 'map5', value = map5))\n}\n</code></pre>\n\n<p>which can probably be optimized a bit more.</p>",
      "rawMarkdown": "I believe in the classification case (and as long as you make sure to return 5 unique predictions per example) the metric degenerates to a simple:\r\n\r\n    map5 = function(preds, dtrain) {\r\n      labels = getinfo(dtrain, 'label')\r\n      preds = t(matrix(preds, ncol = length(labels)))\r\n      preds = t(apply(preds, 1, order, decreasing = T))[, 1:5] - 1\r\n      succ = (preds == labels)\r\n      w = 1 / (1:5)\r\n      map5 = mean(succ %*% w)\r\n      return (list(metric = 'map5', value = map5))\r\n    }\r\n\r\nwhich can probably be optimized a bit more.",
      "votes": null
    },
    {
      "id": "117781",
      "postDate": "04/30/2016 21:31:58",
      "content": "<p>[quote=Branden Murray;117749]</p>\n\n<p>In R, </p>\n\n<pre><code>library(Metrics)\nmap5 &lt;- function(preds, dtrain) {\n  labels &lt;- getinfo(dtrain,&quot;label&quot;)\n  num.class = 100\n  pred &lt;- matrix(preds, nrow = num.class)\n  top &lt;- t(apply(pred, 2, function(y) order(y)[num.class:(num.class-4)]-1))\n\n  map &lt;- mapk(5, labels, top)\n  return(list(metric = &quot;map5&quot;, value = map))\n}\n</code></pre>\n\n<p>Then set <code>eval_metric=map5</code> in your params.</p>\n\n<p>[/quote]</p>\n\n<p>Thanks, I'm sure this will be useful for a lot of people. I'm a python guy myself, so I still need to find another way :)</p>",
      "rawMarkdown": "[quote=Branden Murray;117749]\r\n\r\nIn R, \r\n\r\n    library(Metrics)\r\n    map5 <- function(preds, dtrain) {\r\n      labels <- getinfo(dtrain,\"label\")\r\n      num.class = 100\r\n      pred <- matrix(preds, nrow = num.class)\r\n      top <- t(apply(pred, 2, function(y) order(y)[num.class:(num.class-4)]-1))\r\n      \r\n      map <- mapk(5, labels, top)\r\n      return(list(metric = \"map5\", value = map))\r\n    }\r\n\r\nThen set `eval_metric=map5` in your params.\r\n\r\n[/quote]\r\n\r\nThanks, I'm sure this will be useful for a lot of people. I'm a python guy myself, so I still need to find another way :)",
      "votes": null
    },
    {
      "id": "117815",
      "postDate": "05/01/2016 03:15:33",
      "content": "<p>@anokas, something like this should work here:</p>\n\n<pre><code>def map5eval(preds, dtrain):\n    actual = dtrain.get_label()\n    predicted = preds.argsort(axis=1)[:,-np.arange(1,6)]\n    metric = 0.\n    for i in range(5):\n        metric += np.sum(actual==predicted[:,i])/(i+1)\n    metric /= actual.shape[0]\n    return 'MAP@5', metric\n</code></pre>\n\n<p>Then pass to <code>xgb.train</code> as <code>feval = map5eval</code>.</p>\n\n<p><em>Edit: reverse the sign for use in early stopping.</em></p>\n\n<p><em>Edit 2: fixed an error in array slicing. Thanks, @HN Musac.</em></p>",
      "rawMarkdown": "anokas, something like this should work here:\r\n\r\n    def map5eval(preds, dtrain):\r\n        actual = dtrain.get_label()\r\n        predicted = preds.argsort(axis=1)[:,-np.arange(1,6)]\r\n        metric = 0.\r\n        for i in range(5):\r\n            metric += np.sum(actual==predicted[:,i])/(i+1)\r\n        metric /= actual.shape[0]\r\n        return 'MAP@5', metric\r\nThen pass to `xgb.train` as `feval = map5eval`.\r\n\r\n*Edit: reverse the sign for use in early stopping.*\r\n\r\n*Edit 2: fixed an error in array slicing. Thanks, @HN Musac.*",
      "votes": null
    },
    {
      "id": "118038",
      "postDate": "05/02/2016 17:26:03",
      "content": "<p>Has anybody tried using any of these with xgboost yet? What is the difference between your local CV score and your LB score?  On a random subset of 100,000 training rows I got 3-fold CV scores of ~0.52 (s.d. = 0.002) using my map5 function, but LB score was ~0.42. Using SK's function on the same subset I was getting CV scores of ~0.58.</p>",
      "rawMarkdown": "Has anybody tried using any of these with xgboost yet? What is the difference between your local CV score and your LB score?  On a random subset of 100,000 training rows I got 3-fold CV scores of ~0.52 (s.d. = 0.002) using my map5 function, but LB score was ~0.42. Using SK's function on the same subset I was getting CV scores of ~0.58.",
      "votes": null
    },
    {
      "id": "118103",
      "postDate": "05/02/2016 21:22:41",
      "content": "<p>[quote=dune_dweller;117815]</p>\n\n<pre><code>def map5eval(preds, dtrain):\n    actual = dtrain.get_label()\n    predicted = preds.argsort(axis=1)[:,-np.arange(5)]\n    metric = 0.\n    for i in range(5):\n        metric += np.sum(actual==predicted[:,i])/(i+1)\n    metric /= actual.shape[0]\n    return 'MAP@5', metric\n</code></pre>\n\n<p>[/quote]</p>\n\n<p>Hello Dune, i am a bit confused about the format of preds. Does preds contain the probabilities or the classes labels?</p>\n\n<p>-np.arange(5) gives [0, -1, -2, -3, -4], , which slices the first, the last, 2nd to last, 3rd to last and 4th to last columns, correct?</p>",
      "rawMarkdown": "[quote=dune_dweller;117815]\r\n\r\n    def map5eval(preds, dtrain):\r\n        actual = dtrain.get_label()\r\n        predicted = preds.argsort(axis=1)[:,-np.arange(5)]\r\n        metric = 0.\r\n        for i in range(5):\r\n            metric += np.sum(actual==predicted[:,i])/(i+1)\r\n        metric /= actual.shape[0]\r\n        return 'MAP@5', metric\r\n\r\n[/quote]\r\n\r\nHello Dune, i am a bit confused about the format of preds. Does preds contain the probabilities or the classes labels?\r\n\r\n-np.arange(5) gives [0, -1, -2, -3, -4], , which slices the first, the last, 2nd to last, 3rd to last and 4th to last columns, correct?",
      "votes": null
    },
    {
      "id": "118249",
      "postDate": "05/03/2016 02:45:55",
      "content": "<p><code>preds.</code><a href=\"http://docs.scipy.org/doc/numpy-1.10.0/reference/generated/numpy.argsort.html\"><code>argsort</code></a><code>(axis=1)</code> returns an array of indices into each row from smallest element to largest. Although you made me realize that I'm slicing it wrong: need to replace <code>-np.arange(5)</code> with <code>-np.arange(1,6)</code>, so that it takes the last element, then the second-last and so on. This way the most probable hotel cluster goes first.</p>\n\n<p>Lol, this just made my NN model go from 0.17 to 0.27 in cv. Maybe it's not as hopeless as I thought =). I'll edit the original post to include this correction, thanks!</p>",
      "rawMarkdown": "`preds.`[`argsort`](http://docs.scipy.org/doc/numpy-1.10.0/reference/generated/numpy.argsort.html)`(axis=1)` returns an array of indices into each row from smallest element to largest. Although you made me realize that I'm slicing it wrong: need to replace `-np.arange(5)` with `-np.arange(1,6)`, so that it takes the last element, then the second-last and so on. This way the most probable hotel cluster goes first.\r\n\r\nLol, this just made my NN model go from 0.17 to 0.27 in cv. Maybe it's not as hopeless as I thought =). I'll edit the original post to include this correction, thanks!",
      "votes": null
    },
    {
      "id": "118493",
      "postDate": "05/03/2016 23:41:51",
      "content": "<p>Another option is to use MAPK from Ben Hammers <a href=\"https://github.com/benhamner/Metrics\">ml_metrics</a> . He has both R and Python implementations.</p>",
      "rawMarkdown": "Another option is to use MAPK from Ben Hammers [ml_metrics](https://github.com/benhamner/Metrics) . He has both R and Python implementations.",
      "votes": null
    },
    {
      "id": "118578",
      "postDate": "05/04/2016 11:02:17",
      "content": "<p>Is there a way to compare the score on the leaderboard with the score of this map5 function? \nI got something like .24 in cross-validation, which does not seem much compared to the &quot;naive&quot; method of just going for the most popular hotels per destination.\nHowever, when I submit the results I got .04 - apparently there is still a bug in how I interpret the result matrix from the predict function. </p>",
      "rawMarkdown": "Is there a way to compare the score on the leaderboard with the score of this map5 function? \r\nI got something like .24 in cross-validation, which does not seem much compared to the \"naive\" method of just going for the most popular hotels per destination.\r\nHowever, when I submit the results I got .04 - apparently there is still a bug in how I interpret the result matrix from the predict function.",
      "votes": null
    },
    {
      "id": "118583",
      "postDate": "05/04/2016 11:24:55",
      "content": "<p>[quote=Branden Murray;118038]</p>\n\n<p>Has anybody tried using any of these with xgboost yet? What is the difference between your local CV score and your LB score?  On a random subset of 100,000 training rows I got 3-fold CV scores of ~0.52 (s.d. = 0.002) using my map5 function, but LB score was ~0.42. Using SK's function on the same subset I was getting CV scores of ~0.58.</p>\n\n<p>[/quote]</p>\n\n<p>Did you generate a time-based validation set? I found that with a small sample, ordering by time (which is what the LB uses) results in much worse performance than testing on a random subset. In a particular case I got validation score of 0.6 when test on a random subset of the train, and 0.37xx when the test set was generated by time.</p>",
      "rawMarkdown": "[quote=Branden Murray;118038]\r\n\r\nHas anybody tried using any of these with xgboost yet? What is the difference between your local CV score and your LB score?  On a random subset of 100,000 training rows I got 3-fold CV scores of ~0.52 (s.d. = 0.002) using my map5 function, but LB score was ~0.42. Using SK's function on the same subset I was getting CV scores of ~0.58.\r\n\r\n[/quote]\r\n\r\nDid you generate a time-based validation set? I found that with a small sample, ordering by time (which is what the LB uses) results in much worse performance than testing on a random subset. In a particular case I got validation score of 0.6 when test on a random subset of the train, and 0.37xx when the test set was generated by time.",
      "votes": null
    },
    {
      "id": "118606",
      "postDate": "05/04/2016 13:37:39",
      "content": "<p>I trained a neural net model on the full train.csv by reading it in chunks. First I used each chunk to evaluate performance and then to train the model. By the end of training local score was around 0.29-0.30. And leaderboard score was about 0.277. This wasn't a time-based split, but not a small sample either :).</p>",
      "rawMarkdown": "I trained a neural net model on the full train.csv by reading it in chunks. First I used each chunk to evaluate performance and then to train the model. By the end of training local score was around 0.29-0.30. And leaderboard score was about 0.277. This wasn't a time-based split, but not a small sample either :).",
      "votes": null
    },
    {
      "id": "118642",
      "postDate": "05/04/2016 15:26:02",
      "content": "<p>Reply to self - fixed the submission issue, and difference between my CV score and LB score was .02, in other words very small. Alas, model is still not performing better than the &quot;most popular&quot;.</p>\n\n<p>[quote=mafux777;118578]</p>\n\n<p>Is there a way to compare the score on the leaderboard with the score of this map5 function? \nI got something like .24 in cross-validation, which does not seem much compared to the &quot;naive&quot; method of just going for the most popular hotels per destination.\nHowever, when I submit the results I got .04 - apparently there is still a bug in how I interpret the result matrix from the predict function. </p>\n\n<p>[/quote]</p>",
      "rawMarkdown": "Reply to self - fixed the submission issue, and difference between my CV score and LB score was .02, in other words very small. Alas, model is still not performing better than the \"most popular\".\r\n\r\n[quote=mafux777;118578]\r\n\r\nIs there a way to compare the score on the leaderboard with the score of this map5 function? \r\nI got something like .24 in cross-validation, which does not seem much compared to the \"naive\" method of just going for the most popular hotels per destination.\r\nHowever, when I submit the results I got .04 - apparently there is still a bug in how I interpret the result matrix from the predict function. \r\n\r\n[/quote]",
      "votes": null
    },
    {
      "id": "120733",
      "postDate": "05/20/2016 06:19:59",
      "content": "<p>[quote=Branden Murray;117749]</p>\n\n<p>In R, </p>\n\n<pre><code>library(Metrics)\nmap5 &lt;- function(preds, dtrain) {\n  labels &lt;- as.list(getinfo(dtrain,&quot;label&quot;))\n  num.class = 100\n  pred &lt;- matrix(preds, nrow = num.class)\n  top &lt;- t(apply(pred, 2, function(y) order(y)[num.class:(num.class-4)]-1))\n  top &lt;- split(top, 1:NROW(top))\n\n  map &lt;- mapk(5, labels, top)\n  return(list(metric = &quot;map5&quot;, value = map))\n}\n</code></pre>\n\n<p>Then set <code>eval_metric=map5</code> in your params.</p>\n\n<p><em>[edit] Realized the <code>mapk</code> function takes in lists, not vectors. This now gives the same output as SK's function below.</em></p>\n\n<p>[/quote]</p>\n\n<p>What does this code return? I am getting just a large numeric list, i thought multi:softprob was supposed to return a probability matrix for the classes?!</p>",
      "rawMarkdown": "[quote=Branden Murray;117749]\r\n\r\nIn R, \r\n\r\n    library(Metrics)\r\n    map5 <- function(preds, dtrain) {\r\n      labels <- as.list(getinfo(dtrain,\"label\"))\r\n      num.class = 100\r\n      pred <- matrix(preds, nrow = num.class)\r\n      top <- t(apply(pred, 2, function(y) order(y)[num.class:(num.class-4)]-1))\r\n      top <- split(top, 1:NROW(top))\r\n      \r\n      map <- mapk(5, labels, top)\r\n      return(list(metric = \"map5\", value = map))\r\n    }\r\n\r\nThen set `eval_metric=map5` in your params.\r\n\r\n*[edit] Realized the `mapk` function takes in lists, not vectors. This now gives the same output as SK's function below.*\r\n\r\n[/quote]\r\n\r\nWhat does this code return? I am getting just a large numeric list, i thought multi:softprob was supposed to return a probability matrix for the classes?!",
      "votes": null
    },
    {
      "id": "120734",
      "postDate": "05/20/2016 06:26:38",
      "content": "<p>That's just an eval_function for xgboost, nothing to do with your issue. For multi-class problems xgboost returns a vector and you need to transform that vector into a matrix. You can do this with code below:</p>\n\n<pre><code>testPreds &lt;- predict(xgb_model, dtest)\ntestPreds &lt;- t(matrix(testPreds, nrow=100))\n</code></pre>\n\n<p>The columns will be the classes in order, from 0 to 99.</p>",
      "rawMarkdown": "That's just an eval_function for xgboost, nothing to do with your issue. For multi-class problems xgboost returns a vector and you need to transform that vector into a matrix. You can do this with code below:\r\n\r\n    testPreds <- predict(xgb_model, dtest)\r\n    testPreds <- t(matrix(testPreds, nrow=100))\r\n\r\nThe columns will be the classes in order, from 0 to 99.",
      "votes": null
    },
    {
      "id": "120735",
      "postDate": "05/20/2016 06:42:02",
      "content": "<p>[quote=Branden Murray;120734]</p>\n\n<p>That's just an eval_function for xgboost, nothing to do with your issue. For multi-class problems xgboost returns a vector and you need to transform that vector into a matrix. You can do this with code below:</p>\n\n<pre><code>testPreds &lt;- predict(xgb_model, dtest)\ntestPreds &lt;- t(matrix(testPreds, nrow=100))\n</code></pre>\n\n<p>The columns will be the classes in order, from 0 to 99.</p>\n\n<p>[/quote]</p>\n\n<p>ohh! Just figured\nThanks for clearing the doubt!!  :)</p>",
      "rawMarkdown": "[quote=Branden Murray;120734]\r\n\r\nThat's just an eval_function for xgboost, nothing to do with your issue. For multi-class problems xgboost returns a vector and you need to transform that vector into a matrix. You can do this with code below:\r\n\r\n    testPreds <- predict(xgb_model, dtest)\r\n    testPreds <- t(matrix(testPreds, nrow=100))\r\n\r\nThe columns will be the classes in order, from 0 to 99.\r\n\r\n[/quote]\r\n\r\nohh! Just figured\r\nThanks for clearing the doubt!!  :)",
      "votes": null
    },
    {
      "id": "120974",
      "postDate": "05/22/2016 09:49:49",
      "content": "<p>do you get &quot;xgboost.core.XGBoostError: label size predict size not match&quot; error after using <a href=\"https://github.com/benhamner/Metrics\">ml_metrics</a> ? mapk5 function is i copied from that link and changed the name, and i run a cv validation, code is below, has anyone used that metric?:</p>\n\n<pre><code>\ndef apk(actual, predicted, k=5):\n    if len(predicted)&gt;k:\n        predicted = predicted[:k]\n\n    score = 0.0\n    num_hits = 0.0\n\n    for i,p in enumerate(predicted):\n        if p in actual and p not in predicted[:i]:\n            num_hits += 1.0\n            score += num_hits / (i+1.0)\n\n    if not actual:\n        return 0.0\n\n    return score / min(len(actual), k)\n\ndef mapk5(actual, predicted, k=5):\n    return np.mean([apk(a,p,k) for a,p in zip(actual, predicted)])\n\nparams = {}\nparams[&quot;objective&quot;] = &quot;multi:softprob&quot;\nparams[&quot;booster&quot;] = &quot;gbtree&quot;\nparams[&quot;num_class&quot;] = 100\nparams[&quot;eta&quot;] = 0.01\nparams[&quot;subsample&quot;] = 0.75\nparams[&quot;colsample_bytree&quot;] = 0.75\nparams[&quot;max_depth&quot;] = 6\nparams[&quot;min_child_weight&quot;] = 3\nparams[&quot;silent&quot;] = 1\nxgtrain = xgb.DMatrix(X, y)\nprint xgb.cv(params, xgtrain, 1000, nfold=5, metrics=['mapk5'], early_stopping_rounds=50)\n</code></pre>",
      "rawMarkdown": "do you get \"xgboost.core.XGBoostError: label size predict size not match\" error after using [ml_metrics][1] ? mapk5 function is i copied from that link and changed the name, and i run a cv validation, code is below, has anyone used that metric?:\r\n<pre><code>\r\ndef apk(actual, predicted, k=5):\r\n    if len(predicted)>k:\r\n        predicted = predicted[:k]\r\n\r\n    score = 0.0\r\n    num_hits = 0.0\r\n\r\n    for i,p in enumerate(predicted):\r\n        if p in actual and p not in predicted[:i]:\r\n            num_hits += 1.0\r\n            score += num_hits / (i+1.0)\r\n\r\n    if not actual:\r\n        return 0.0\r\n\r\n    return score / min(len(actual), k)\r\n\r\ndef mapk5(actual, predicted, k=5):\r\n    return np.mean([apk(a,p,k) for a,p in zip(actual, predicted)])\r\n\r\nparams = {}\r\nparams[\"objective\"] = \"multi:softprob\"\r\nparams[\"booster\"] = \"gbtree\"\r\nparams[\"num_class\"] = 100\r\nparams[\"eta\"] = 0.01\r\nparams[\"subsample\"] = 0.75\r\nparams[\"colsample_bytree\"] = 0.75\r\nparams[\"max_depth\"] = 6\r\nparams[\"min_child_weight\"] = 3\r\nparams[\"silent\"] = 1\r\nxgtrain = xgb.DMatrix(X, y)\r\nprint xgb.cv(params, xgtrain, 1000, nfold=5, metrics=['mapk5'], early_stopping_rounds=50)\r\n</code></pre>\r\n\r\n  [1]: https://github.com/benhamner/Metrics",
      "votes": null
    },
    {
      "id": "120977",
      "postDate": "05/22/2016 11:11:00",
      "content": "<p>sorry for confusion, i've fixed that problem by passing feval= mapk5 instead of metrics param.</p>",
      "rawMarkdown": "sorry for confusion, i've fixed that problem by passing feval= mapk5 instead of metrics param.",
      "votes": null
    },
    {
      "id": "121034",
      "postDate": "05/23/2016 04:37:41",
      "content": "<p>@dune_dweller , which map@5 cv score do you get? i used your eval function and only get about 0.2, looks a bit lower. Maybe there're problems in my code.</p>",
      "rawMarkdown": "dune_dweller , which map@5 cv score do you get? i used your eval function and only get about 0.2, looks a bit lower. Maybe there're problems in my code.",
      "votes": null
    },
    {
      "id": "122237",
      "postDate": "06/02/2016 08:41:46",
      "content": "<p>@masterLiu, your code seems way too complicated to me.  </p>\n\n<p>A slightly simplified version of @dune_dweller map5 function runs quite fast:</p>\n\n<pre><code>def map5eval(preds, dtrain, k=5):\n    actual = dtrain.get_label()\n    predicted = (-preds).argsort(axis=1)[:,:k]\n    metric = 0.\n    for i in range(5):\n        metric += np.sum(actual==predicted[:,i])/(i+1)\n    metric /= actual.shape[0]\n    return 'MAP@5', metric\n</code></pre>\n\n<p>Also, do not forget to pass maximize=True as a parameter to train.</p>",
      "rawMarkdown": "masterLiu, your code seems way too complicated to me.  \r\n\r\nA slightly simplified version of @dune_dweller map5 function runs quite fast:\r\n\r\n    def map5eval(preds, dtrain, k=5):\r\n        actual = dtrain.get_label()\r\n        predicted = (-preds).argsort(axis=1)[:,:k]\r\n        metric = 0.\r\n        for i in range(5):\r\n            metric += np.sum(actual==predicted[:,i])/(i+1)\r\n        metric /= actual.shape[0]\r\n        return 'MAP@5', metric\r\n\r\nAlso, do not forget to pass maximize=True as a parameter to train.",
      "votes": null
    },
    {
      "id": "122558",
      "postDate": "06/05/2016 04:00:32",
      "content": "<p>@Branden Murray I face the same problem with you. I use @CPMP evaluation metric and I could get very high validation performance. Is it caused by leak information or there's something wrong with the evaluation metric? I used 100000 rows with 33% as validation set. </p>",
      "rawMarkdown": "Branden Murray I face the same problem with you. I use @CPMP evaluation metric and I could get very high validation performance. Is it caused by leak information or there's something wrong with the evaluation metric? I used 100000 rows with 33% as validation set.",
      "votes": null
    },
    {
      "id": "122568",
      "postDate": "06/05/2016 07:46:53",
      "content": "<p>@Feng, If I recall correctly, my problems were the result of a leak from the way I was doing feature engineering and splitting my data. I stopped using map5 as my eval metric in xgboost because xgboost seemed to run faster using 'mlogloss' (I think just because mlogloss is built in and therefore calculates much faster)</p>",
      "rawMarkdown": "Feng, If I recall correctly, my problems were the result of a leak from the way I was doing feature engineering and splitting my data. I stopped using map5 as my eval metric in xgboost because xgboost seemed to run faster using 'mlogloss' (I think just because mlogloss is built in and therefore calculates much faster)",
      "votes": null
    },
    {
      "id": "122607",
      "postDate": "06/05/2016 18:36:35",
      "content": "<p>@Branden Murray Thank you. But for mlogloss, as your experience. What's the score corresponding to like map@5=0.3  or 0.35 . And when you change to mlogloss, does your code speed up significantly? Thank you.</p>",
      "rawMarkdown": "Branden Murray Thank you. But for mlogloss, as your experience. What's the score corresponding to like map@5=0.3  or 0.35 . And when you change to mlogloss, does your code speed up significantly? Thank you.",
      "votes": null
    },
    {
      "id": "123000",
      "postDate": "06/09/2016 04:13:28",
      "content": "<p>has anyone come across this error &quot;IndexError: too many indices for array&quot; on line 3 while executing the code by @CPMP. </p>",
      "rawMarkdown": "has anyone come across this error \"IndexError: too many indices for array\" on line 3 while executing the code by @CPMP.",
      "votes": null
    }
  ],
  "comments": [
    {
      "id": 117630,
      "author_name": "brandenkmurray",
      "author_url": "",
      "post_date": "04/29/2016 22:01:27",
      "content": "<p>My understanding is that the ndcg@n and map@n eval functions built into xgboost only work when using &quot;rank:pairwise&quot; for your objective. If you want to use a different objective then you need to define a custom map@5 eval function for it to work.</p>\n\n<p>For reference, see this <a href=\"https://github.com/dmlc/xgboost/issues/1143\">issue</a> on xgboost's GitHub.</p>",
      "votes": null,
      "replies": []
    },
    {
      "id": 117705,
      "author_name": "anokas",
      "author_url": "",
      "post_date": "04/30/2016 13:01:40",
      "content": "<p>[quote=Branden Murray;117630]</p>\n\n<p>For reference, see this <a href=\"https://github.com/dmlc/xgboost/issues/1143\">issue</a> on xgboost's GitHub.</p>\n\n<p>[/quote]</p>\n\n<p>That's my issue ;). What I ended up doing is optimising for mlogloss instead, but if someone smarter than me can implement map@5 as a custom metric in xgboost that would be very useful!</p>\n\n<p>I think xgboost might not be the way to go in this competition, as it takes so long to train (as it is 100-class problem, it trains 100 trees per boosting rounds) and this means it was taking me 12 hours on dual xeons to converge.</p>",
      "votes": null,
      "replies": []
    },
    {
      "id": 117749,
      "author_name": "brandenkmurray",
      "author_url": "",
      "post_date": "04/30/2016 17:20:28",
      "content": "<p>In R, </p>\n\n<pre><code>library(Metrics)\nmap5 &lt;- function(preds, dtrain) {\n  labels &lt;- as.list(getinfo(dtrain,&quot;label&quot;))\n  num.class = 100\n  pred &lt;- matrix(preds, nrow = num.class)\n  top &lt;- t(apply(pred, 2, function(y) order(y)[num.class:(num.class-4)]-1))\n  top &lt;- split(top, 1:NROW(top))\n\n  map &lt;- mapk(5, labels, top)\n  return(list(metric = &quot;map5&quot;, value = map))\n}\n</code></pre>\n\n<p>Then set <code>eval_metric=map5</code> in your params.</p>\n\n<p><em>[edit] Realized the <code>mapk</code> function takes in lists, not vectors. This now gives the same output as SK's function below.</em></p>",
      "votes": null,
      "replies": []
    },
    {
      "id": 117762,
      "author_name": "optimism",
      "author_url": "",
      "post_date": "04/30/2016 19:07:02",
      "content": "<p>I believe in the classification case (and as long as you make sure to return 5 unique predictions per example) the metric degenerates to a simple:</p>\n\n<pre><code>map5 = function(preds, dtrain) {\n  labels = getinfo(dtrain, 'label')\n  preds = t(matrix(preds, ncol = length(labels)))\n  preds = t(apply(preds, 1, order, decreasing = T))[, 1:5] - 1\n  succ = (preds == labels)\n  w = 1 / (1:5)\n  map5 = mean(succ %*% w)\n  return (list(metric = 'map5', value = map5))\n}\n</code></pre>\n\n<p>which can probably be optimized a bit more.</p>",
      "votes": null,
      "replies": []
    },
    {
      "id": 117781,
      "author_name": "anokas",
      "author_url": "",
      "post_date": "04/30/2016 21:31:58",
      "content": "<p>[quote=Branden Murray;117749]</p>\n\n<p>In R, </p>\n\n<pre><code>library(Metrics)\nmap5 &lt;- function(preds, dtrain) {\n  labels &lt;- getinfo(dtrain,&quot;label&quot;)\n  num.class = 100\n  pred &lt;- matrix(preds, nrow = num.class)\n  top &lt;- t(apply(pred, 2, function(y) order(y)[num.class:(num.class-4)]-1))\n\n  map &lt;- mapk(5, labels, top)\n  return(list(metric = &quot;map5&quot;, value = map))\n}\n</code></pre>\n\n<p>Then set <code>eval_metric=map5</code> in your params.</p>\n\n<p>[/quote]</p>\n\n<p>Thanks, I'm sure this will be useful for a lot of people. I'm a python guy myself, so I still need to find another way :)</p>",
      "votes": null,
      "replies": []
    },
    {
      "id": 117815,
      "author_name": "dvasyukova",
      "author_url": "",
      "post_date": "05/01/2016 03:15:33",
      "content": "<p>@anokas, something like this should work here:</p>\n\n<pre><code>def map5eval(preds, dtrain):\n    actual = dtrain.get_label()\n    predicted = preds.argsort(axis=1)[:,-np.arange(1,6)]\n    metric = 0.\n    for i in range(5):\n        metric += np.sum(actual==predicted[:,i])/(i+1)\n    metric /= actual.shape[0]\n    return 'MAP@5', metric\n</code></pre>\n\n<p>Then pass to <code>xgb.train</code> as <code>feval = map5eval</code>.</p>\n\n<p><em>Edit: reverse the sign for use in early stopping.</em></p>\n\n<p><em>Edit 2: fixed an error in array slicing. Thanks, @HN Musac.</em></p>",
      "votes": null,
      "replies": []
    },
    {
      "id": 118038,
      "author_name": "brandenkmurray",
      "author_url": "",
      "post_date": "05/02/2016 17:26:03",
      "content": "<p>Has anybody tried using any of these with xgboost yet? What is the difference between your local CV score and your LB score?  On a random subset of 100,000 training rows I got 3-fold CV scores of ~0.52 (s.d. = 0.002) using my map5 function, but LB score was ~0.42. Using SK's function on the same subset I was getting CV scores of ~0.58.</p>",
      "votes": null,
      "replies": []
    },
    {
      "id": 118103,
      "author_name": "hitoshinagano",
      "author_url": "",
      "post_date": "05/02/2016 21:22:41",
      "content": "<p>[quote=dune_dweller;117815]</p>\n\n<pre><code>def map5eval(preds, dtrain):\n    actual = dtrain.get_label()\n    predicted = preds.argsort(axis=1)[:,-np.arange(5)]\n    metric = 0.\n    for i in range(5):\n        metric += np.sum(actual==predicted[:,i])/(i+1)\n    metric /= actual.shape[0]\n    return 'MAP@5', metric\n</code></pre>\n\n<p>[/quote]</p>\n\n<p>Hello Dune, i am a bit confused about the format of preds. Does preds contain the probabilities or the classes labels?</p>\n\n<p>-np.arange(5) gives [0, -1, -2, -3, -4], , which slices the first, the last, 2nd to last, 3rd to last and 4th to last columns, correct?</p>",
      "votes": null,
      "replies": []
    },
    {
      "id": 118249,
      "author_name": "dvasyukova",
      "author_url": "",
      "post_date": "05/03/2016 02:45:55",
      "content": "<p><code>preds.</code><a href=\"http://docs.scipy.org/doc/numpy-1.10.0/reference/generated/numpy.argsort.html\"><code>argsort</code></a><code>(axis=1)</code> returns an array of indices into each row from smallest element to largest. Although you made me realize that I'm slicing it wrong: need to replace <code>-np.arange(5)</code> with <code>-np.arange(1,6)</code>, so that it takes the last element, then the second-last and so on. This way the most probable hotel cluster goes first.</p>\n\n<p>Lol, this just made my NN model go from 0.17 to 0.27 in cv. Maybe it's not as hopeless as I thought =). I'll edit the original post to include this correction, thanks!</p>",
      "votes": null,
      "replies": []
    },
    {
      "id": 118493,
      "author_name": "kevinmcisaac",
      "author_url": "",
      "post_date": "05/03/2016 23:41:51",
      "content": "<p>Another option is to use MAPK from Ben Hammers <a href=\"https://github.com/benhamner/Metrics\">ml_metrics</a> . He has both R and Python implementations.</p>",
      "votes": null,
      "replies": []
    },
    {
      "id": 118578,
      "author_name": "mafux777",
      "author_url": "",
      "post_date": "05/04/2016 11:02:17",
      "content": "<p>Is there a way to compare the score on the leaderboard with the score of this map5 function? \nI got something like .24 in cross-validation, which does not seem much compared to the &quot;naive&quot; method of just going for the most popular hotels per destination.\nHowever, when I submit the results I got .04 - apparently there is still a bug in how I interpret the result matrix from the predict function. </p>",
      "votes": null,
      "replies": []
    },
    {
      "id": 118583,
      "author_name": "qwang88",
      "author_url": "",
      "post_date": "05/04/2016 11:24:55",
      "content": "<p>[quote=Branden Murray;118038]</p>\n\n<p>Has anybody tried using any of these with xgboost yet? What is the difference between your local CV score and your LB score?  On a random subset of 100,000 training rows I got 3-fold CV scores of ~0.52 (s.d. = 0.002) using my map5 function, but LB score was ~0.42. Using SK's function on the same subset I was getting CV scores of ~0.58.</p>\n\n<p>[/quote]</p>\n\n<p>Did you generate a time-based validation set? I found that with a small sample, ordering by time (which is what the LB uses) results in much worse performance than testing on a random subset. In a particular case I got validation score of 0.6 when test on a random subset of the train, and 0.37xx when the test set was generated by time.</p>",
      "votes": null,
      "replies": []
    },
    {
      "id": 118606,
      "author_name": "dvasyukova",
      "author_url": "",
      "post_date": "05/04/2016 13:37:39",
      "content": "<p>I trained a neural net model on the full train.csv by reading it in chunks. First I used each chunk to evaluate performance and then to train the model. By the end of training local score was around 0.29-0.30. And leaderboard score was about 0.277. This wasn't a time-based split, but not a small sample either :).</p>",
      "votes": null,
      "replies": []
    },
    {
      "id": 118642,
      "author_name": "mafux777",
      "author_url": "",
      "post_date": "05/04/2016 15:26:02",
      "content": "<p>Reply to self - fixed the submission issue, and difference between my CV score and LB score was .02, in other words very small. Alas, model is still not performing better than the &quot;most popular&quot;.</p>\n\n<p>[quote=mafux777;118578]</p>\n\n<p>Is there a way to compare the score on the leaderboard with the score of this map5 function? \nI got something like .24 in cross-validation, which does not seem much compared to the &quot;naive&quot; method of just going for the most popular hotels per destination.\nHowever, when I submit the results I got .04 - apparently there is still a bug in how I interpret the result matrix from the predict function. </p>\n\n<p>[/quote]</p>",
      "votes": null,
      "replies": []
    },
    {
      "id": 120733,
      "author_name": "abhinavs01858",
      "author_url": "",
      "post_date": "05/20/2016 06:19:59",
      "content": "<p>[quote=Branden Murray;117749]</p>\n\n<p>In R, </p>\n\n<pre><code>library(Metrics)\nmap5 &lt;- function(preds, dtrain) {\n  labels &lt;- as.list(getinfo(dtrain,&quot;label&quot;))\n  num.class = 100\n  pred &lt;- matrix(preds, nrow = num.class)\n  top &lt;- t(apply(pred, 2, function(y) order(y)[num.class:(num.class-4)]-1))\n  top &lt;- split(top, 1:NROW(top))\n\n  map &lt;- mapk(5, labels, top)\n  return(list(metric = &quot;map5&quot;, value = map))\n}\n</code></pre>\n\n<p>Then set <code>eval_metric=map5</code> in your params.</p>\n\n<p><em>[edit] Realized the <code>mapk</code> function takes in lists, not vectors. This now gives the same output as SK's function below.</em></p>\n\n<p>[/quote]</p>\n\n<p>What does this code return? I am getting just a large numeric list, i thought multi:softprob was supposed to return a probability matrix for the classes?!</p>",
      "votes": null,
      "replies": []
    },
    {
      "id": 120734,
      "author_name": "brandenkmurray",
      "author_url": "",
      "post_date": "05/20/2016 06:26:38",
      "content": "<p>That's just an eval_function for xgboost, nothing to do with your issue. For multi-class problems xgboost returns a vector and you need to transform that vector into a matrix. You can do this with code below:</p>\n\n<pre><code>testPreds &lt;- predict(xgb_model, dtest)\ntestPreds &lt;- t(matrix(testPreds, nrow=100))\n</code></pre>\n\n<p>The columns will be the classes in order, from 0 to 99.</p>",
      "votes": null,
      "replies": []
    },
    {
      "id": 120735,
      "author_name": "abhinavs01858",
      "author_url": "",
      "post_date": "05/20/2016 06:42:02",
      "content": "<p>[quote=Branden Murray;120734]</p>\n\n<p>That's just an eval_function for xgboost, nothing to do with your issue. For multi-class problems xgboost returns a vector and you need to transform that vector into a matrix. You can do this with code below:</p>\n\n<pre><code>testPreds &lt;- predict(xgb_model, dtest)\ntestPreds &lt;- t(matrix(testPreds, nrow=100))\n</code></pre>\n\n<p>The columns will be the classes in order, from 0 to 99.</p>\n\n<p>[/quote]</p>\n\n<p>ohh! Just figured\nThanks for clearing the doubt!!  :)</p>",
      "votes": null,
      "replies": []
    },
    {
      "id": 120974,
      "author_name": "masterliu",
      "author_url": "",
      "post_date": "05/22/2016 09:49:49",
      "content": "<p>do you get &quot;xgboost.core.XGBoostError: label size predict size not match&quot; error after using <a href=\"https://github.com/benhamner/Metrics\">ml_metrics</a> ? mapk5 function is i copied from that link and changed the name, and i run a cv validation, code is below, has anyone used that metric?:</p>\n\n<pre><code>\ndef apk(actual, predicted, k=5):\n    if len(predicted)&gt;k:\n        predicted = predicted[:k]\n\n    score = 0.0\n    num_hits = 0.0\n\n    for i,p in enumerate(predicted):\n        if p in actual and p not in predicted[:i]:\n            num_hits += 1.0\n            score += num_hits / (i+1.0)\n\n    if not actual:\n        return 0.0\n\n    return score / min(len(actual), k)\n\ndef mapk5(actual, predicted, k=5):\n    return np.mean([apk(a,p,k) for a,p in zip(actual, predicted)])\n\nparams = {}\nparams[&quot;objective&quot;] = &quot;multi:softprob&quot;\nparams[&quot;booster&quot;] = &quot;gbtree&quot;\nparams[&quot;num_class&quot;] = 100\nparams[&quot;eta&quot;] = 0.01\nparams[&quot;subsample&quot;] = 0.75\nparams[&quot;colsample_bytree&quot;] = 0.75\nparams[&quot;max_depth&quot;] = 6\nparams[&quot;min_child_weight&quot;] = 3\nparams[&quot;silent&quot;] = 1\nxgtrain = xgb.DMatrix(X, y)\nprint xgb.cv(params, xgtrain, 1000, nfold=5, metrics=['mapk5'], early_stopping_rounds=50)\n</code></pre>",
      "votes": null,
      "replies": []
    },
    {
      "id": 120977,
      "author_name": "masterliu",
      "author_url": "",
      "post_date": "05/22/2016 11:11:00",
      "content": "<p>sorry for confusion, i've fixed that problem by passing feval= mapk5 instead of metrics param.</p>",
      "votes": null,
      "replies": []
    },
    {
      "id": 121034,
      "author_name": "masterliu",
      "author_url": "",
      "post_date": "05/23/2016 04:37:41",
      "content": "<p>@dune_dweller , which map@5 cv score do you get? i used your eval function and only get about 0.2, looks a bit lower. Maybe there're problems in my code.</p>",
      "votes": null,
      "replies": []
    },
    {
      "id": 122237,
      "author_name": "cpmpml",
      "author_url": "",
      "post_date": "06/02/2016 08:41:46",
      "content": "<p>@masterLiu, your code seems way too complicated to me.  </p>\n\n<p>A slightly simplified version of @dune_dweller map5 function runs quite fast:</p>\n\n<pre><code>def map5eval(preds, dtrain, k=5):\n    actual = dtrain.get_label()\n    predicted = (-preds).argsort(axis=1)[:,:k]\n    metric = 0.\n    for i in range(5):\n        metric += np.sum(actual==predicted[:,i])/(i+1)\n    metric /= actual.shape[0]\n    return 'MAP@5', metric\n</code></pre>\n\n<p>Also, do not forget to pass maximize=True as a parameter to train.</p>",
      "votes": null,
      "replies": []
    },
    {
      "id": 122558,
      "author_name": "beedata",
      "author_url": "",
      "post_date": "06/05/2016 04:00:32",
      "content": "<p>@Branden Murray I face the same problem with you. I use @CPMP evaluation metric and I could get very high validation performance. Is it caused by leak information or there's something wrong with the evaluation metric? I used 100000 rows with 33% as validation set. </p>",
      "votes": null,
      "replies": []
    },
    {
      "id": 122568,
      "author_name": "brandenkmurray",
      "author_url": "",
      "post_date": "06/05/2016 07:46:53",
      "content": "<p>@Feng, If I recall correctly, my problems were the result of a leak from the way I was doing feature engineering and splitting my data. I stopped using map5 as my eval metric in xgboost because xgboost seemed to run faster using 'mlogloss' (I think just because mlogloss is built in and therefore calculates much faster)</p>",
      "votes": null,
      "replies": []
    },
    {
      "id": 122607,
      "author_name": "beedata",
      "author_url": "",
      "post_date": "06/05/2016 18:36:35",
      "content": "<p>@Branden Murray Thank you. But for mlogloss, as your experience. What's the score corresponding to like map@5=0.3  or 0.35 . And when you change to mlogloss, does your code speed up significantly? Thank you.</p>",
      "votes": null,
      "replies": []
    },
    {
      "id": 123000,
      "author_name": "dilipkomar",
      "author_url": "",
      "post_date": "06/09/2016 04:13:28",
      "content": "<p>has anyone come across this error &quot;IndexError: too many indices for array&quot; on line 3 while executing the code by @CPMP. </p>",
      "votes": null,
      "replies": []
    }
  ],
  "raw_markdown_by_id": {
    "117628": "Hi,  I've done ncdg5 before via a custom function in xgboost.  I also see that there is an \"map\" and \"map@n\" eval_metric in xgboost, but simply it doesn't work for me. R crashes\r\n\r\nI don't know if these is crossing the competition rules, but is there a hint on these?\r\n\r\nThanks",
    "117630": "My understanding is that the ndcg@n and map@n eval functions built into xgboost only work when using \"rank:pairwise\" for your objective. If you want to use a different objective then you need to define a custom map@5 eval function for it to work.\r\n\r\nFor reference, see this [issue][1] on xgboost's GitHub.\r\n\r\n\r\n  [1]: https://github.com/dmlc/xgboost/issues/1143",
    "117705": "[quote=Branden Murray;117630]\r\n\r\nFor reference, see this [issue][1] on xgboost's GitHub.\r\n\r\n\r\n  [1]: https://github.com/dmlc/xgboost/issues/1143\r\n\r\n[/quote]\r\n\r\nThat's my issue ;). What I ended up doing is optimising for mlogloss instead, but if someone smarter than me can implement map@5 as a custom metric in xgboost that would be very useful!\r\n\r\nI think xgboost might not be the way to go in this competition, as it takes so long to train (as it is 100-class problem, it trains 100 trees per boosting rounds) and this means it was taking me 12 hours on dual xeons to converge.",
    "117749": "In R, \r\n\r\n    library(Metrics)\r\n    map5 <- function(preds, dtrain) {\r\n      labels <- as.list(getinfo(dtrain,\"label\"))\r\n      num.class = 100\r\n      pred <- matrix(preds, nrow = num.class)\r\n      top <- t(apply(pred, 2, function(y) order(y)[num.class:(num.class-4)]-1))\r\n      top <- split(top, 1:NROW(top))\r\n      \r\n      map <- mapk(5, labels, top)\r\n      return(list(metric = \"map5\", value = map))\r\n    }\r\n\r\nThen set `eval_metric=map5` in your params.\r\n\r\n*[edit] Realized the `mapk` function takes in lists, not vectors. This now gives the same output as SK's function below.*",
    "117762": "I believe in the classification case (and as long as you make sure to return 5 unique predictions per example) the metric degenerates to a simple:\r\n\r\n    map5 = function(preds, dtrain) {\r\n      labels = getinfo(dtrain, 'label')\r\n      preds = t(matrix(preds, ncol = length(labels)))\r\n      preds = t(apply(preds, 1, order, decreasing = T))[, 1:5] - 1\r\n      succ = (preds == labels)\r\n      w = 1 / (1:5)\r\n      map5 = mean(succ %*% w)\r\n      return (list(metric = 'map5', value = map5))\r\n    }\r\n\r\nwhich can probably be optimized a bit more.",
    "117781": "[quote=Branden Murray;117749]\r\n\r\nIn R, \r\n\r\n    library(Metrics)\r\n    map5 <- function(preds, dtrain) {\r\n      labels <- getinfo(dtrain,\"label\")\r\n      num.class = 100\r\n      pred <- matrix(preds, nrow = num.class)\r\n      top <- t(apply(pred, 2, function(y) order(y)[num.class:(num.class-4)]-1))\r\n      \r\n      map <- mapk(5, labels, top)\r\n      return(list(metric = \"map5\", value = map))\r\n    }\r\n\r\nThen set `eval_metric=map5` in your params.\r\n\r\n[/quote]\r\n\r\nThanks, I'm sure this will be useful for a lot of people. I'm a python guy myself, so I still need to find another way :)",
    "117815": "anokas, something like this should work here:\r\n\r\n    def map5eval(preds, dtrain):\r\n        actual = dtrain.get_label()\r\n        predicted = preds.argsort(axis=1)[:,-np.arange(1,6)]\r\n        metric = 0.\r\n        for i in range(5):\r\n            metric += np.sum(actual==predicted[:,i])/(i+1)\r\n        metric /= actual.shape[0]\r\n        return 'MAP@5', metric\r\nThen pass to `xgb.train` as `feval = map5eval`.\r\n\r\n*Edit: reverse the sign for use in early stopping.*\r\n\r\n*Edit 2: fixed an error in array slicing. Thanks, @HN Musac.*",
    "118038": "Has anybody tried using any of these with xgboost yet? What is the difference between your local CV score and your LB score?  On a random subset of 100,000 training rows I got 3-fold CV scores of ~0.52 (s.d. = 0.002) using my map5 function, but LB score was ~0.42. Using SK's function on the same subset I was getting CV scores of ~0.58.",
    "118103": "[quote=dune_dweller;117815]\r\n\r\n    def map5eval(preds, dtrain):\r\n        actual = dtrain.get_label()\r\n        predicted = preds.argsort(axis=1)[:,-np.arange(5)]\r\n        metric = 0.\r\n        for i in range(5):\r\n            metric += np.sum(actual==predicted[:,i])/(i+1)\r\n        metric /= actual.shape[0]\r\n        return 'MAP@5', metric\r\n\r\n[/quote]\r\n\r\nHello Dune, i am a bit confused about the format of preds. Does preds contain the probabilities or the classes labels?\r\n\r\n-np.arange(5) gives [0, -1, -2, -3, -4], , which slices the first, the last, 2nd to last, 3rd to last and 4th to last columns, correct?",
    "118249": "`preds.`[`argsort`](http://docs.scipy.org/doc/numpy-1.10.0/reference/generated/numpy.argsort.html)`(axis=1)` returns an array of indices into each row from smallest element to largest. Although you made me realize that I'm slicing it wrong: need to replace `-np.arange(5)` with `-np.arange(1,6)`, so that it takes the last element, then the second-last and so on. This way the most probable hotel cluster goes first.\r\n\r\nLol, this just made my NN model go from 0.17 to 0.27 in cv. Maybe it's not as hopeless as I thought =). I'll edit the original post to include this correction, thanks!",
    "118493": "Another option is to use MAPK from Ben Hammers [ml_metrics](https://github.com/benhamner/Metrics) . He has both R and Python implementations.",
    "118578": "Is there a way to compare the score on the leaderboard with the score of this map5 function? \r\nI got something like .24 in cross-validation, which does not seem much compared to the \"naive\" method of just going for the most popular hotels per destination.\r\nHowever, when I submit the results I got .04 - apparently there is still a bug in how I interpret the result matrix from the predict function.",
    "118583": "[quote=Branden Murray;118038]\r\n\r\nHas anybody tried using any of these with xgboost yet? What is the difference between your local CV score and your LB score?  On a random subset of 100,000 training rows I got 3-fold CV scores of ~0.52 (s.d. = 0.002) using my map5 function, but LB score was ~0.42. Using SK's function on the same subset I was getting CV scores of ~0.58.\r\n\r\n[/quote]\r\n\r\nDid you generate a time-based validation set? I found that with a small sample, ordering by time (which is what the LB uses) results in much worse performance than testing on a random subset. In a particular case I got validation score of 0.6 when test on a random subset of the train, and 0.37xx when the test set was generated by time.",
    "118606": "I trained a neural net model on the full train.csv by reading it in chunks. First I used each chunk to evaluate performance and then to train the model. By the end of training local score was around 0.29-0.30. And leaderboard score was about 0.277. This wasn't a time-based split, but not a small sample either :).",
    "118642": "Reply to self - fixed the submission issue, and difference between my CV score and LB score was .02, in other words very small. Alas, model is still not performing better than the \"most popular\".\r\n\r\n[quote=mafux777;118578]\r\n\r\nIs there a way to compare the score on the leaderboard with the score of this map5 function? \r\nI got something like .24 in cross-validation, which does not seem much compared to the \"naive\" method of just going for the most popular hotels per destination.\r\nHowever, when I submit the results I got .04 - apparently there is still a bug in how I interpret the result matrix from the predict function. \r\n\r\n[/quote]",
    "120733": "[quote=Branden Murray;117749]\r\n\r\nIn R, \r\n\r\n    library(Metrics)\r\n    map5 <- function(preds, dtrain) {\r\n      labels <- as.list(getinfo(dtrain,\"label\"))\r\n      num.class = 100\r\n      pred <- matrix(preds, nrow = num.class)\r\n      top <- t(apply(pred, 2, function(y) order(y)[num.class:(num.class-4)]-1))\r\n      top <- split(top, 1:NROW(top))\r\n      \r\n      map <- mapk(5, labels, top)\r\n      return(list(metric = \"map5\", value = map))\r\n    }\r\n\r\nThen set `eval_metric=map5` in your params.\r\n\r\n*[edit] Realized the `mapk` function takes in lists, not vectors. This now gives the same output as SK's function below.*\r\n\r\n[/quote]\r\n\r\nWhat does this code return? I am getting just a large numeric list, i thought multi:softprob was supposed to return a probability matrix for the classes?!",
    "120734": "That's just an eval_function for xgboost, nothing to do with your issue. For multi-class problems xgboost returns a vector and you need to transform that vector into a matrix. You can do this with code below:\r\n\r\n    testPreds <- predict(xgb_model, dtest)\r\n    testPreds <- t(matrix(testPreds, nrow=100))\r\n\r\nThe columns will be the classes in order, from 0 to 99.",
    "120735": "[quote=Branden Murray;120734]\r\n\r\nThat's just an eval_function for xgboost, nothing to do with your issue. For multi-class problems xgboost returns a vector and you need to transform that vector into a matrix. You can do this with code below:\r\n\r\n    testPreds <- predict(xgb_model, dtest)\r\n    testPreds <- t(matrix(testPreds, nrow=100))\r\n\r\nThe columns will be the classes in order, from 0 to 99.\r\n\r\n[/quote]\r\n\r\nohh! Just figured\r\nThanks for clearing the doubt!!  :)",
    "120974": "do you get \"xgboost.core.XGBoostError: label size predict size not match\" error after using [ml_metrics][1] ? mapk5 function is i copied from that link and changed the name, and i run a cv validation, code is below, has anyone used that metric?:\r\n<pre><code>\r\ndef apk(actual, predicted, k=5):\r\n    if len(predicted)>k:\r\n        predicted = predicted[:k]\r\n\r\n    score = 0.0\r\n    num_hits = 0.0\r\n\r\n    for i,p in enumerate(predicted):\r\n        if p in actual and p not in predicted[:i]:\r\n            num_hits += 1.0\r\n            score += num_hits / (i+1.0)\r\n\r\n    if not actual:\r\n        return 0.0\r\n\r\n    return score / min(len(actual), k)\r\n\r\ndef mapk5(actual, predicted, k=5):\r\n    return np.mean([apk(a,p,k) for a,p in zip(actual, predicted)])\r\n\r\nparams = {}\r\nparams[\"objective\"] = \"multi:softprob\"\r\nparams[\"booster\"] = \"gbtree\"\r\nparams[\"num_class\"] = 100\r\nparams[\"eta\"] = 0.01\r\nparams[\"subsample\"] = 0.75\r\nparams[\"colsample_bytree\"] = 0.75\r\nparams[\"max_depth\"] = 6\r\nparams[\"min_child_weight\"] = 3\r\nparams[\"silent\"] = 1\r\nxgtrain = xgb.DMatrix(X, y)\r\nprint xgb.cv(params, xgtrain, 1000, nfold=5, metrics=['mapk5'], early_stopping_rounds=50)\r\n</code></pre>\r\n\r\n  [1]: https://github.com/benhamner/Metrics",
    "120977": "sorry for confusion, i've fixed that problem by passing feval= mapk5 instead of metrics param.",
    "121034": "dune_dweller , which map@5 cv score do you get? i used your eval function and only get about 0.2, looks a bit lower. Maybe there're problems in my code.",
    "122237": "masterLiu, your code seems way too complicated to me.  \r\n\r\nA slightly simplified version of @dune_dweller map5 function runs quite fast:\r\n\r\n    def map5eval(preds, dtrain, k=5):\r\n        actual = dtrain.get_label()\r\n        predicted = (-preds).argsort(axis=1)[:,:k]\r\n        metric = 0.\r\n        for i in range(5):\r\n            metric += np.sum(actual==predicted[:,i])/(i+1)\r\n        metric /= actual.shape[0]\r\n        return 'MAP@5', metric\r\n\r\nAlso, do not forget to pass maximize=True as a parameter to train.",
    "122558": "Branden Murray I face the same problem with you. I use @CPMP evaluation metric and I could get very high validation performance. Is it caused by leak information or there's something wrong with the evaluation metric? I used 100000 rows with 33% as validation set.",
    "122568": "Feng, If I recall correctly, my problems were the result of a leak from the way I was doing feature engineering and splitting my data. I stopped using map5 as my eval metric in xgboost because xgboost seemed to run faster using 'mlogloss' (I think just because mlogloss is built in and therefore calculates much faster)",
    "122607": "Branden Murray Thank you. But for mlogloss, as your experience. What's the score corresponding to like map@5=0.3  or 0.35 . And when you change to mlogloss, does your code speed up significantly? Thank you.",
    "123000": "has anyone come across this error \"IndexError: too many indices for array\" on line 3 while executing the code by @CPMP."
  },
  "source": "meta"
}