From 3337494326eb7909bb4e581ab14ea61f4dd92758 Mon Sep 17 00:00:00 2001 From: Baptiste Bauvin <baptiste.bauvin@lis-lab.fr> Date: Mon, 13 Aug 2018 11:47:39 -0400 Subject: [PATCH] Simplified error analysis plotting --- .../ResultAnalysis.py | 283 +++++++----------- 1 file changed, 116 insertions(+), 167 deletions(-) diff --git a/multiview_platform/MonoMultiViewClassifiers/ResultAnalysis.py b/multiview_platform/MonoMultiViewClassifiers/ResultAnalysis.py index 2ad258dd..192a4faf 100644 --- a/multiview_platform/MonoMultiViewClassifiers/ResultAnalysis.py +++ b/multiview_platform/MonoMultiViewClassifiers/ResultAnalysis.py @@ -119,7 +119,7 @@ def getExampleErrorsBiclass(groud_truth, results): errorOnExamples = np.equal(classifierResult.full_labels_pred, groud_truth).astype(int) unseenExamples = np.where(groud_truth==-100)[0] errorOnExamples[unseenExamples]=-100 - exampleErrors[classifierResult.get_classifier_name()] = errorOnExamples + exampleErrors[classifierResult.get_classifier_name()] = {"errorOnExamples": errorOnExamples} return exampleErrors @@ -215,6 +215,12 @@ def plotMetricScores(trainScores, testScores, names, nbResults, metricName, file The plotted metric's name fileName : str The name of the file where the figure will be saved. + tag : str + Some text to personalize the title, must start with a whitespace. + train_STDs : np.array of floats or None + The array containing the standard deviations for the averaged scores on the training set. + test_STDs : np.array of floats or None + The array containing the standard deviations for the averaged scores on the testing set. Returns ------- @@ -228,8 +234,8 @@ def plotMetricScores(trainScores, testScores, names, nbResults, metricName, file f, ax = plt.subplots(nrows=1, ncols=1, **figKW) ax.set_title(metricName + "\n"+ tag +" scores for each classifier") - rects = ax.bar(range(nbResults), testScores, barWidth, color="0.8", yerr=test_STDs) - rect2 = ax.bar(np.arange(nbResults) + barWidth, trainScores, barWidth, color="0.5", yerr=train_STDs) + rects = ax.bar(range(nbResults), testScores, barWidth, color="0.1", yerr=test_STDs) + rect2 = ax.bar(np.arange(nbResults) + barWidth, trainScores, barWidth, color="0.8", yerr=train_STDs) autolabel(rects, ax, set=1, std=test_STDs) autolabel(rect2, ax, set=2, std=train_STDs) @@ -274,58 +280,81 @@ def publishMetricsGraphs(metricsScores, directory, databaseName, labelsNames): logging.debug("Done:\t Biclass score graph generation for " + metricName) -def publishExampleErrors(exampleErrors, directory, databaseName, labelsNames, minSize=10): - logging.debug("Start:\t Biclass Label analysis figure generation") - nbClassifiers = len(exampleErrors) - nbExamples = len(list(exampleErrors.values())[0]) - nbIter = 2 - data = np.zeros((nbExamples, nbClassifiers * nbIter)) - temp_data = np.zeros((nbExamples, nbClassifiers)) - classifiersNames = exampleErrors.keys() - for classifierIndex, (classifierName, errorOnExamples) in enumerate(exampleErrors.items()): - for iterIndex in range(nbIter): - data[:, classifierIndex * nbIter + iterIndex] = errorOnExamples - temp_data[:,classifierIndex] = errorOnExamples - - figWidth = max(nbClassifiers/2, minSize) - figHeight = max(nbExamples/20, minSize) - figKW = {"figsize":(figWidth, figHeight)} - fig, ax = plt.subplots(nrows=1, ncols=1, **figKW) - cmap = mpl.colors.ListedColormap(['black', 'red', 'green']) - bounds = [-100.5,-0.5, 0.5, 1.5] +def iterCmap(statsIter): + cmapList = ["red", "0.0"]+[str(float((i+1))/statsIter) for i in range(statsIter)] + cmap = mpl.colors.ListedColormap(cmapList) + bounds = [-100*statsIter-0.5, -0.5] + for i in range(statsIter): + bounds.append(i+0.5) + bounds.append(statsIter+0.5) norm = mpl.colors.BoundaryNorm(bounds, cmap.N) + return cmap, norm + - plt.imshow(data, interpolation='none', cmap=cmap, norm=norm, aspect='auto') +def publish2Dplot(data, classifiersNames, nbClassifiers, nbExamples, nbCopies, fileName, minSize=10, + width_denominator=2.0, height_denominator=20.0, statsIter=1): + figWidth = max(nbClassifiers / width_denominator, minSize) + figHeight = max(nbExamples / height_denominator, minSize) + figKW = {"figsize": (figWidth, figHeight)} + fig, ax = plt.subplots(nrows=1, ncols=1, **figKW) + cmap, norm = iterCmap(statsIter) + cax = plt.imshow(data, interpolation='none', cmap=cmap, norm=norm, aspect='auto') plt.title('Errors depending on the classifier') - ticks = np.arange(nbIter/2-0.5, nbClassifiers * nbIter, nbIter) + ticks = np.arange(nbCopies / 2 - 0.5, nbClassifiers * nbCopies, nbCopies) labels = classifiersNames plt.xticks(ticks, labels, rotation="vertical") - red_patch = mpatches.Patch(color='red', label='Classifier failed') - green_patch = mpatches.Patch(color='green', label='Classifier succeded') - black_patch = mpatches.Patch(color='black', label='Unseen data') - plt.legend(handles=[red_patch, green_patch, black_patch], - bbox_to_anchor=(0,1.02,1,0.2), - loc="lower left", - mode="expand", - borderaxespad=0, - ncol=3) + cbar = fig.colorbar(cax, ticks=[-100 * statsIter / 2, 0, statsIter]) + cbar.ax.set_yticklabels(['Unseen', 'Always Wrong', 'Always Right']) fig.tight_layout() - fig.savefig(directory + time.strftime("%Y_%m_%d-%H_%M_%S") + "-" + databaseName +"-"+"_vs_".join(labelsNames)+ "-error_analysis.png", bbox_inches="tight") + fig.savefig(fileName+"error_analysis_2D.png", bbox_inches="tight") plt.close() - logging.debug("Done:\t Biclass Label analysis figure generation") - logging.debug("Start:\t Biclass Error by example figure generation") - errorOnExamples = -1*np.sum(data, axis=1)/nbIter+nbClassifiers - np.savetxt(directory + "clf_errors_doubled.csv", data, delimiter=",") - np.savetxt(directory + "example_errors.csv", temp_data, delimiter=",") + +def publishErrorsBarPlot(errorOnExamples, nbClassifiers, nbExamples, fileName): fig, ax = plt.subplots() x = np.arange(nbExamples) plt.bar(x, errorOnExamples) - plt.ylim([0,nbClassifiers]) + plt.ylim([0, nbClassifiers]) plt.title("Number of classifiers that failed to classify each example") - fig.savefig(directory + time.strftime("%Y_%m_%d-%H_%M_%S") + "-" + databaseName +"-"+"_vs_".join(labelsNames)+ "-example_errors.png") + fig.savefig(fileName+"error_analysis_bar.png") plt.close() - logging.debug("Done:\t Biclass Error by example figure generation") + + +def gen_error_data(example_errors, base_file_name, nbCopies=2): + nbClassifiers = len(example_errors) + nbExamples = len(list(example_errors.values())[0]["errorOnExamples"]) + classifiersNames = example_errors.keys() + + data = np.zeros((nbExamples, nbClassifiers * nbCopies)) + temp_data = np.zeros((nbExamples, nbClassifiers)) + for classifierIndex, (classifierName, errorOnExamples) in enumerate(example_errors.items()): + for iterIndex in range(nbCopies): + data[:, classifierIndex * nbCopies + iterIndex] = errorOnExamples["errorOnExamples"] + temp_data[:, classifierIndex] = errorOnExamples["errorOnExamples"] + errorOnExamples = -1 * np.sum(data, axis=1) / nbCopies + nbClassifiers + + np.savetxt(base_file_name + "2D_plot_data.csv", data, delimiter=",") + np.savetxt(base_file_name + "bar_plot_data.csv", temp_data, delimiter=",") + + return nbClassifiers, nbExamples, nbCopies, classifiersNames, data, errorOnExamples + + +def publishExampleErrors(exampleErrors, directory, databaseName, labelsNames): + + logging.debug("Start:\t Biclass Label analysis figure generation") + + base_file_name = directory + time.strftime("%Y_%m_%d-%H_%M_%S") + "-" + databaseName + "-" + "_vs_".join( + labelsNames) + "-" + + nbClassifiers, nbExamples, nCopies, classifiersNames, data, errorOnExamples = gen_error_data(exampleErrors, + base_file_name) + + + publish2Dplot(data, classifiersNames, nbClassifiers, nbExamples, nCopies, base_file_name) + + publishErrorsBarPlot(errorOnExamples, nbClassifiers, nbExamples, base_file_name) + + logging.debug("Done:\t Biclass Label analysis figures generation") def analyzeBiclass(results, benchmarkArgumentDictionaries, statsIter, metrics): @@ -386,7 +415,7 @@ def getErrorOnLabelsMulticlass(multiclassResults, multiclassLabels): for iterIndex, iterResults in enumerate(multiclassResults): for classifierName, classifierResults in iterResults.items(): errorOnExamples = classifierResults["labels"] == multiclassLabels - multiclassResults[iterIndex][classifierName]["errorOnExample"] = errorOnExamples.astype(int) + multiclassResults[iterIndex][classifierName]["errorOnExamples"] = errorOnExamples.astype(int) logging.debug("Done:\t Getting errors on each example for each classifier") @@ -416,50 +445,18 @@ def publishMulticlassScores(multiclassResults, metrics, statsIter, direcories, d def publishMulticlassExmapleErrors(multiclassResults, directories, databaseName, minSize=10): for iterIndex, multiclassResult in enumerate(multiclassResults): directory = directories[iterIndex] - logging.debug("Start:\t Label analysis figure generation") - nbClassifiers = len(multiclassResult) - nbExamples = len(list(multiclassResult.values())[0]["errorOnExample"]) - nbIter = 2 - data = np.zeros((nbExamples, nbClassifiers * nbIter)) - temp_data = np.zeros((nbExamples, nbClassifiers)) - classifiersNames = multiclassResult.keys() - for classifierIndex, (classifierName, errorOnExamplesDict) in enumerate(multiclassResult.items()): - for iterIndex in range(nbIter): - data[:, classifierIndex * nbIter + iterIndex] = errorOnExamplesDict["errorOnExample"] - temp_data[:,classifierIndex] = errorOnExamplesDict["errorOnExample"] - figWidth = max(nbClassifiers/2, minSize) - figHeight = max(nbExamples/20, minSize) - figKW = {"figsize":(figWidth, figHeight)} - fig, ax = plt.subplots(nrows=1, ncols=1, **figKW) - cmap = mpl.colors.ListedColormap(['red', 'green']) - bounds = [-0.5, 0.5, 1.5] - norm = mpl.colors.BoundaryNorm(bounds, cmap.N) - - cax = plt.imshow(data, interpolation='none', cmap=cmap, norm=norm, aspect='auto') - plt.title('Errors depending on the classifier') - ticks = np.arange(nbIter/2-0.5, nbClassifiers * nbIter, nbIter) - labels = classifiersNames - plt.xticks(ticks, labels, rotation="vertical") - red_patch = mpatches.Patch(color='red', label='Classifier failed') - green_patch = mpatches.Patch(color='green', label='Classifier succeded') - plt.legend(handles=[red_patch, green_patch], bbox_to_anchor=(0,1.02,1,0.2), loc="lower left",mode="expand", borderaxespad=0, ncol=2) - fig.tight_layout() - fig.savefig(directory + time.strftime("%Y_%m_%d-%H_%M_%S") + "-" + databaseName +"-error_analysis.png", bbox_inches="tight") - plt.close() - logging.debug("Done:\t Label analysis figure generation") - - logging.debug("Start:\t Error by example figure generation") - errorOnExamples = -1*np.sum(data, axis=1)/nbIter+nbClassifiers - np.savetxt(directory + time.strftime("%Y_%m_%d-%H_%M_%S") + "-clf_errors_doubled.csv", data, delimiter=",") - np.savetxt(directory + time.strftime("%Y_%m_%d-%H_%M_%S") + "-example_errors.csv", temp_data, delimiter=",") - fig, ax = plt.subplots() - x = np.arange(nbExamples) - plt.bar(x, errorOnExamples) - plt.ylim([0,nbClassifiers]) - plt.title("Number of classifiers that failed to classify each example") - fig.savefig(directory + time.strftime("%Y_%m_%d-%H_%M_%S") + "-" + databaseName +"-example_errors.png") - plt.close() - logging.debug("Done:\t Error by example figure generation") + logging.debug("Start:\t Multiclass Label analysis figure generation") + + base_file_name = directory + time.strftime("%Y_%m_%d-%H_%M_%S") + "-" + databaseName +"-" + + nbClassifiers, nbExamples, nCopies, classifiersNames, data, errorOnExamples = gen_error_data(multiclassResult, + base_file_name) + + publish2Dplot(data, classifiersNames, nbClassifiers, nbExamples, nCopies, base_file_name) + + publishErrorsBarPlot(errorOnExamples, nbClassifiers, nbExamples, base_file_name) + + logging.debug("Done:\t Multiclass Label analysis figure generation") def analyzeMulticlass(results, statsIter, benchmarkArgumentDictionaries, nbExamples, nbLabels, multiclassLabels, @@ -504,6 +501,7 @@ def analyzeMulticlass(results, statsIter, benchmarkArgumentDictionaries, nbExamp publishMulticlassExmapleErrors(multiclassResults, directories, benchmarkArgumentDictionaries[0]["args"].name) return multiclassResults + def numpy_mean_and_std(scores_array): return np.mean(scores_array, axis=1), np.std(scores_array, axis=1) @@ -532,57 +530,30 @@ def publishIterBiclassMetricsScores(iterResults, directory, labelsDictionary, cl -def iterCmap(statsIter): - cmapList = ["red", "0.0"] - for i in range(statsIter): - cmapList.append(str(float((i+1))/statsIter)) - cmap = mpl.colors.ListedColormap(cmapList) - bounds = [-100*statsIter-0.5, -0.5] - for i in range(statsIter): - bounds.append(i+0.5) - bounds.append(statsIter+0.5) - norm = mpl.colors.BoundaryNorm(bounds, cmap.N) - return cmap, norm +def gen_error_dat_glob(combiResults, statsIter, base_file_name): + nbExamples = combiResults["errorOnExamples"].shape[1] + nbClassifiers = combiResults["errorOnExamples"].shape[0] + data = np.transpose(combiResults["errorOnExamples"]) + errorOnExamples = -1 * np.sum(data, axis=1) + (nbClassifiers * statsIter) + np.savetxt(base_file_name + "clf_errors.csv", data, delimiter=",") + np.savetxt(base_file_name + "example_errors.csv", errorOnExamples, delimiter=",") + return nbExamples, nbClassifiers, data, errorOnExamples def publishIterBiclassExampleErrors(iterResults, directory, labelsDictionary, classifiersDict, statsIter, minSize=10): for labelsCombination, combiResults in iterResults.items(): - currentDirectory = directory+ labelsDictionary[int(labelsCombination[0])]+"-vs-"+labelsDictionary[int(labelsCombination[1])]+"/" - reversedClassifiersDict = dict((value, key) for key, value in classifiersDict.items()) - classifiersNames = [reversedClassifiersDict[i] for i in range(len(classifiersDict))] - - logging.debug("Start:\t Global label analysis figure generation") - nbExamples = combiResults["errorOnExamples"].shape[1] - nbClassifiers = combiResults["errorOnExamples"].shape[0] - figWidth = max(nbClassifiers / 2, minSize) - figHeight = max(nbExamples / 20, minSize) - figKW = {"figsize": (figWidth, figHeight)} - fig, ax = plt.subplots(nrows=1, ncols=1, **figKW) - data = np.transpose(combiResults["errorOnExamples"]) - cmap, norm = iterCmap(statsIter) - cax = plt.imshow(data, interpolation='none', cmap=cmap, norm=norm, aspect='auto') - plt.title('Errors depending on the classifier') - ticks = np.arange(nbClassifiers) - plt.xticks(ticks, classifiersNames, rotation="vertical") - cbar = fig.colorbar(cax, ticks=[-100*statsIter/2, 0, statsIter]) - cbar.ax.set_yticklabels(['Unseen', 'Always Wrong', 'Always Right']) - fig.tight_layout() - fig.savefig(currentDirectory + time.strftime("%Y_%m_%d-%H_%M_%S") + "-error_analysis.png") - plt.close() - logging.debug("Done:\t Global label analysis figure generation") - - logging.debug("Start:\t Global error by example figure generation") - errorOnExamples = -1 * np.sum(data, axis=1) + (nbClassifiers*statsIter) - np.savetxt(currentDirectory + time.strftime("%Y%m%d-%H%M%S") + "-clf_errors.csv", data, delimiter=",") - np.savetxt(currentDirectory + time.strftime("%Y%m%d-%H%M%S") + "-example_errors.csv", errorOnExamples, delimiter=",") - fig, ax = plt.subplots() - x = np.arange(nbExamples) - plt.bar(x, errorOnExamples) - plt.ylim([0,nbClassifiers*statsIter]) - plt.title("Number of classifiers that failed to classify each example") - fig.savefig(currentDirectory + time.strftime("%Y_%m_%d-%H_%M_%S") + "-example_errors.png") - plt.close() - logging.debug("Done:\t Global error by example figure generation") + base_file_name = directory + labelsDictionary[int(labelsCombination[0])]+"-vs-"+\ + labelsDictionary[int(labelsCombination[1])]+"/" + time.strftime("%Y_%m_%d-%H_%M_%S") + "-" + classifiersNames = [classifierName for classifierName in classifiersDict.values()] + logging.debug("Start:\t Global biclass label analysis figure generation") + + nbExamples, nbClassifiers, data, errorOnExamples = gen_error_dat_glob(combiResults, statsIter, base_file_name) + + publish2Dplot(data, classifiersNames, nbClassifiers, nbExamples, 1, base_file_name, statsIter=statsIter) + + publishErrorsBarPlot(errorOnExamples, nbClassifiers*statsIter, nbExamples, base_file_name) + + logging.debug("Done:\t Global biclass label analysis figures generation") def publishIterMulticlassMetricsScores(iterMulticlassResults, classifiersNames, dataBaseName, directory, statsIter, minSize=10): @@ -600,40 +571,18 @@ def publishIterMulticlassMetricsScores(iterMulticlassResults, classifiersNames, train_STDs=trainSTDs, test_STDs=testSTDs) - def publishIterMulticlassExampleErrors(iterMulticlassResults, directory, classifiersNames, statsIter, minSize=10): - logging.debug("Start:\t Global label analysis figure generation") - nbExamples = iterMulticlassResults["errorOnExamples"].shape[1] - nbClassifiers = iterMulticlassResults["errorOnExamples"].shape[0] - figWidth = max(nbClassifiers / 2, minSize) - figHeight = max(nbExamples / 20, minSize) - figKW = {"figsize": (figWidth, figHeight)} - fig, ax = plt.subplots(nrows=1, ncols=1, **figKW) - data = np.transpose(iterMulticlassResults["errorOnExamples"]) - cax = plt.imshow(-data, interpolation='none', cmap="Greys", aspect='auto') - plt.title('Errors depending on the classifier') - ticks = np.arange(nbClassifiers) - plt.xticks(ticks, classifiersNames, rotation="vertical") - cbar = fig.colorbar(cax, ticks=[0, -statsIter]) - cbar.ax.set_yticklabels(['Always Wrong', 'Always Right']) - fig.tight_layout() - fig.savefig(directory + time.strftime("%Y%m%d-%H%M%S") + "-error_analysis.png") - plt.close() - logging.debug("Done:\t Global label analysis figure generation") + logging.debug("Start:\t Global multiclass label analysis figures generation") + base_file_name = directory + time.strftime("%Y_%m_%d-%H_%M_%S") + "-" - logging.debug("Start:\t Global error by example figure generation") - errorOnExamples = -1 * np.sum(data, axis=1) + (nbClassifiers*statsIter) - np.savetxt(directory + time.strftime("%Y_%m_%d-%H_%M_%S") + "-clf_errors.csv", data, delimiter=",") - np.savetxt(directory + time.strftime("%Y_%m_%d-%H_%M_%S") + "-example_errors.csv", errorOnExamples, delimiter=",") - fig, ax = plt.subplots() - x = np.arange(nbExamples) - plt.bar(x, errorOnExamples) - plt.ylim([0,nbClassifiers*statsIter]) - plt.title("Number of classifiers that failed to classify each example") - fig.savefig(directory + time.strftime("%Y_%m_%d-%H_%M_%S") + "-example_errors.png") - plt.close() - logging.debug("Done:\t Global error by example figure generation") + nbExamples, nbClassifiers, data, errorOnExamples = gen_error_dat_glob(iterMulticlassResults, statsIter, base_file_name) + + publish2Dplot(data, classifiersNames, nbClassifiers, nbExamples, 1, base_file_name, statsIter=statsIter) + + publishErrorsBarPlot(errorOnExamples, nbClassifiers * statsIter, nbExamples, base_file_name) + + logging.debug("Done:\t Global multiclass label analysis figures generation") def analyzebiclassIter(biclassResults, metrics, statsIter, directory, labelsDictionary, dataBaseName, nbExamples): @@ -666,7 +615,7 @@ def analyzebiclassIter(biclassResults, metrics, statsIter, directory, labelsDict iterBiclassResults[labelsComination]["metricsScores"][metric[0]]["trainScores"][classifiersDict[classifierName], iterIndex] = trainScore iterBiclassResults[labelsComination]["metricsScores"][metric[0]]["testScores"][classifiersDict[classifierName], iterIndex] = testScore for classifierName, errorOnExample in results["exampleErrors"].items(): - iterBiclassResults[labelsComination]["errorOnExamples"][classifiersDict[classifierName], :] += errorOnExample + iterBiclassResults[labelsComination]["errorOnExamples"][classifiersDict[classifierName], :] += errorOnExample["errorOnExamples"] publishIterBiclassMetricsScores(iterBiclassResults, directory, labelsDictionary, classifiersDict, dataBaseName, statsIter) publishIterBiclassExampleErrors(iterBiclassResults, directory, labelsDictionary, classifiersDict, statsIter) @@ -693,7 +642,7 @@ def analyzeIterMulticlass(multiclassResults, directory, statsIter, metrics, data np.zeros((nbClassifiers, statsIter))} iterMulticlassResults["metricsScores"][metric[0]]["trainScores"][classifierIndex, iterIndex] = classifierResults["metricsScores"][metric[0]][0] iterMulticlassResults["metricsScores"][metric[0]]["testScores"][classifierIndex, iterIndex] = classifierResults["metricsScores"][metric[0]][1] - iterMulticlassResults["errorOnExamples"][classifierIndex, :] += classifierResults["errorOnExample"] + iterMulticlassResults["errorOnExamples"][classifierIndex, :] += classifierResults["errorOnExamples"] logging.debug("Start:\t Getting mean results for multiclass classification") classifiersNames = np.array(classifiersNames) -- GitLab