{"action":"created","issue":{"url":"https://api.github.com/repos/keras-team/keras/issues/9395","repository_url":"https://api.github.com/repos/keras-team/keras","labels_url":"https://api.github.com/repos/keras-team/keras/issues/9395/labels{/name}","comments_url":"https://api.github.com/repos/keras-team/keras/issues/9395/comments","events_url":"https://api.github.com/repos/keras-team/keras/issues/9395/events","html_url":"https://github.com/keras-team/keras/issues/9395","id":297397652,"number":9395,"title":"Generalized dice loss for multi-class segmentation","user":{"login":"lazyleaf","id":16797194,"avatar_url":"https://avatars3.githubusercontent.com/u/16797194?v=4","gravatar_id":"","url":"https://api.github.com/users/lazyleaf","html_url":"https://github.com/lazyleaf","followers_url":"https://api.github.com/users/lazyleaf/followers","following_url":"https://api.github.com/users/lazyleaf/following{/other_user}","gists_url":"https://api.github.com/users/lazyleaf/gists{/gist_id}","starred_url":"https://api.github.com/users/lazyleaf/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lazyleaf/subscriptions","organizations_url":"https://api.github.com/users/lazyleaf/orgs","repos_url":"https://api.github.com/users/lazyleaf/repos","events_url":"https://api.github.com/users/lazyleaf/events{/privacy}","received_events_url":"https://api.github.com/users/lazyleaf/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":9,"created_at":"2018-02-15T10:36:43Z","updated_at":"2018-04-06T14:47:51Z","closed_at":null,"author_association":"NONE","body":"Hey guys, I just implemented the generalised dice loss (multi-class version of dice loss), as described in [ref](https://arxiv.org/pdf/1707.03237.pdf) :\r\n(my targets are defined as: (batch_size, image_dim1, image_dim2, image_dim3, nb_of_classes))\r\n```\r\ndef generalized_dice_loss_w(y_true, y_pred): \r\n    # Compute weights: \"the contribution of each label is corrected by the inverse of its volume\"\r\n    Ncl = y_pred.shape[-1]\r\n    w = np.zeros((Ncl,))\r\n    for l in range(0,Ncl): w[l] = np.sum( np.asarray(y_true[:,:,:,:,l]==1,np.int8) )\r\n    w = 1/(w**2+0.00001)\r\n\r\n    # Compute gen dice coef:\r\n    numerator = y_true*y_pred\r\n    numerator = w*K.sum(numerator,(0,1,2,3))\r\n    numerator = K.sum(numerator)\r\n    \r\n    denominator = y_true+y_pred\r\n    denominator = w*K.sum(denominator,(0,1,2,3))\r\n    denominator = K.sum(denominator)\r\n    \r\n    gen_dice_coef = numerator/denominator\r\n    \r\n    return 1-2*gen_dice_coef\r\n```\r\nBut something must be wrong. I'm working with 3D images that I have to segment for 4 classes (1 background class and 3 object classes, I have a imbalanced dataset). First odd thing: while my train loss and accuracy improve during training (and converge really fast), my validation loss/accuracy are constant trough epochs (see [image](https://drive.google.com/open?id=1wp7bbGPalblL9J-8puDmcYI_1-dFCGI8)). Second, when predicting on test data, only the background class is predicted: I get a constant volume.\r\n\r\nI used the exact same data and script but with categorical cross-entropy loss and get plausible results (object classes are segmented). Which means something is wrong with my implementation. Any idea what it could be?\r\n\r\nPlus I believe it would be usefull to the keras community to have a generalised dice loss implementation, as it seems to be used in most of recent semantic segmentation tasks (at least in the medical image community).\r\n\r\nPS: it seems odd to me how the weights are defined; I get values around 10^-10. Anyone else has tried to implement this? I also tested my function without the weights but get same problems."},"comment":{"url":"https://api.github.com/repos/keras-team/keras/issues/comments/379276452","html_url":"https://github.com/keras-team/keras/issues/9395#issuecomment-379276452","issue_url":"https://api.github.com/repos/keras-team/keras/issues/9395","id":379276452,"user":{"login":"gattia","id":10676083,"avatar_url":"https://avatars3.githubusercontent.com/u/10676083?v=4","gravatar_id":"","url":"https://api.github.com/users/gattia","html_url":"https://github.com/gattia","followers_url":"https://api.github.com/users/gattia/followers","following_url":"https://api.github.com/users/gattia/following{/other_user}","gists_url":"https://api.github.com/users/gattia/gists{/gist_id}","starred_url":"https://api.github.com/users/gattia/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gattia/subscriptions","organizations_url":"https://api.github.com/users/gattia/orgs","repos_url":"https://api.github.com/users/gattia/repos","events_url":"https://api.github.com/users/gattia/events{/privacy}","received_events_url":"https://api.github.com/users/gattia/received_events","type":"User","site_admin":false},"created_at":"2018-04-06T14:47:51Z","updated_at":"2018-04-06T14:47:51Z","author_association":"NONE","body":"@lazyleaf, I just stumbled upon this. I am doing 3D segmentation on multiclass. I will definitely try out the proposed method and see how it works. However, I also have another solution that has worked for me in the past: \r\n\r\n```\r\ndef dice_coef(y_true, y_pred):\r\n    y_true_f = K.flatten(y_true)\r\n    y_pred_f = K.flatten(y_pred)\r\n    intersection = K.sum(y_true_f * y_pred_f)\r\n    return (2. * intersection + smooth) / (K.sum(y_true_f) + K.sum(y_pred_f) + smooth)\r\n\r\ndef dice_coef_multilabel(y_true, y_pred, numLabels=5):\r\n    dice=0\r\n    for index in range(numLabels):\r\n        dice -= dice_coef(y_true[:,index,:,:,:], y_pred[:,index,:,:,:])\r\n    return dice\r\n```\r\n\r\nThis simply calculates the dice score for each individual label, and then sums them together, and includes the background. The best dice score you will ever get is equal to `numLables*-1.0`. When monitoring I always keep in mind that the dice for the background is almost always near 1.0."}}
{"actor":{"display_login":"gattia"}}