-
Notifications
You must be signed in to change notification settings - Fork 86
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add ROC/confusion graphing methods #720
Conversation
Codecov Report
@@ Coverage Diff @@
## master #720 +/- ##
==========================================
+ Coverage 99.34% 99.35% +0.01%
==========================================
Files 148 148
Lines 5175 5299 +124
==========================================
+ Hits 5141 5265 +124
Misses 34 34
Continue to review full report at Codecov.
|
assert np.array_equal(conf_mat_expected, conf_mat) | ||
conf_mat = confusion_matrix(y_true, y_predicted, normalize_method='pred') | ||
conf_mat_expected = np.array([[2 / 3.0, np.nan, 0], [0, np.nan, 1 / 3.0], [1 / 3.0, np.nan, 2 / 3.0]]) | ||
assert np.allclose(conf_mat_expected, conf_mat, equal_nan=True) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
np.allclose
is cool! Setting equal_nan=True
means it handles nans. I'm liking this more than array_almost_equal
. I think they've deprecated that in recent versions actually, should check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me - just some clarifying questions.
evalml/pipelines/plot_utils.py
Outdated
Arguments: | ||
y_true (pd.Series or np.array): true binary labels. | ||
y_pred (pd.Series or np.array): predictions from a binary classifier. | ||
normalize_method ({'true', 'pred', 'all'}): Normalization method. Supported options are: 'true' to normalize by row, 'pred' to normalize by column, or 'all' to normalize by all values. Defaults to 'true'. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Curious about your thoughts on 'true', 'pred' and 'all'. It seems great if we're following the sklearn API but it always seemed confusing to me. IMO axis would be more clear.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd argue that using 'true' / 'pred' is more helpful because users don't have to figure out which axis corresponds to what (x-axis == true? x-axis == predicted values?); they get direct access to what they want to normalize.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unless I'm misunderstanding what you mean by axis 😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm when you put it like that it makes sense 😄
evalml/pipelines/plot_utils.py
Outdated
return sklearn_roc_curve(y_true, y_pred_proba) | ||
fpr_rates, tpr_rates, thresholds = sklearn_roc_curve(y_true, y_pred_proba) | ||
auc_score = sklearn_auc(fpr_rates, tpr_rates) | ||
return {'fpr_rates': fpr_rates, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this change - much more clear for users!
evalml/pipelines/plot_utils.py
Outdated
name='ROC (w/ AUC {:06f})'.format(roc_curve_data['auc_score']), | ||
line=dict(width=3))) | ||
data.append(_go.Scatter(x=[0, 1], y=[0, 1], | ||
name='Random Chance', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Little nitpick but I like Random Guess
over Random Chance
😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the suggestion! I actually prefer "Trivial Model" or something like that -- lmk what you think
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thats good as well!
evalml/pipelines/plot_utils.py
Outdated
labels = conf_mat.columns | ||
reversed_labels = labels[::-1] | ||
|
||
title = 'Confusion matrix {}{}'.format( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit-picking but there's an extra space between "Confusion matrix" and the rest of the title!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks wil fix
evalml/pipelines/plot_utils.py
Outdated
'' if normalize_method is None else (' normalized using method "' + normalize_method + '"')) | ||
z_data, custom_data = (conf_mat, conf_mat_normalized) if normalize_method is None else (conf_mat_normalized, conf_mat) | ||
primary_heading, secondary_heading = ('Raw', 'Normalized') if normalize_method is None else ('Normalized', 'Raw') | ||
hover_text = '<br><b>' + primary_heading + ' Count</b>: %{z}<br><b>' + secondary_heading + ' Count</b>: %{customdata:.3f} <br>' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, good idea!
evalml/pipelines/plot_utils.py
Outdated
return conf_mat | ||
|
||
|
||
def graph_confusion_matrix(y_true, y_pred, normalize_method='true', title_addition=None): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
title_addition
is a cool addition to both graphing methods! Would it be possible to add a test for them?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure! Good point :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks for adding this back in! I just had a few suggestions :)
914b8c9
to
f533a81
Compare
@angela97lin @jeremyliweishih thanks for the great comments! And I apologize for disturbing your weekends--this was work I did Friday before signing off, but in the future I'll wait until Monday 😂 I've addressed all the comments. Outstanding:
IMO, this shouldn't block the release; it's ok if this doesn't get into v0.9.0. |
f533a81
to
8514d07
Compare
I've addressed the TODOs above. This is ready to go! |
colorscale='Blues'), | ||
layout=layout) | ||
# plotly Heatmap y axis defaults to the reverse of what we want: https://community.plotly.com/t/heatmap-y-axis-is-reversed-by-default-going-against-standard-convention-for-matrices/32180 | ||
fig.update_yaxes(autorange="reversed") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@angela97lin : idk if you remember, but a couple weeks ago I mentioned I was having trouble getting the confusion matrix to plot out in the right order. Turns out that's because the y axis on plotly.Heatmap
is the reverse of the input data by default! As you can see in the link I posted in the code comment above, they did that because in the field of image processing, images are typically stored in matrices with the y axis inverted.
Long story short, this inversion fixes the problem without the need for us to invert the labels or data :) lmk if you spot anything funky with this code.
Fixes #697
Adds back in the capability to generate ROC plots and confusion matrices (removed in #615), now as standalone methods rather than relying on automl to compute the plot data during CV.
Changes
roc_curve
to return a dictgraph_roc_curve
which takes predicted vs actual and makes a graphconfusion_matrix
to optionally callnormalize_confusion_matrix
internallygraph_confusion_matrix
which takes predicted vs actual and makes a graphWhat's currently missing
Open questions
graph
orplot
here? We useplot
inSearchIterationPlot
. But we also moved the feature importance chart tograph_feature_importance
, and this aligns with that. My default is to keep that pattern.