diff --git a/avae/config.py b/avae/config.py index 4aad47dc..32cf7a55 100644 --- a/avae/config.py +++ b/avae/config.py @@ -328,6 +328,8 @@ def load_config_params( # if no config file is provided, start from default and update with command line arguments params = {} + command_line_params = set() + # check for command line input values and overwrite config file values # we're using sys args because click has populated defaults from confing if sys_args is not None: @@ -337,6 +339,7 @@ def load_config_params( continue name = arg[2:].split("=")[0] if name in local_args.keys(): + command_line_params.add(name) # overwrite config file value with command line argument value # but only if its on system args (click has defaults) if name in params.keys(): @@ -372,7 +375,11 @@ def load_config_params( if type(val) == pathlib.Path: # turn relative paths to absolute params[key] = str(val.absolute()) - if 'vis' in key and params['vis_all'] is not None: + if ( + 'vis' in key + and params['vis_all'] is not None + and key not in command_line_params + ): # set visualisation to vis_all if it is not set, except for vis_z_n_int and vis_pose_class if key in [ 'vis_all', @@ -385,7 +392,11 @@ def load_config_params( logging.warning( f"Visualisation parameter 'vis_all' is overriding {key} to {params['vis_all']}" ) - if 'freq' in key and params['freq_all'] is not None: + if ( + 'freq' in key + and params['freq_all'] is not None + and key not in command_line_params + ): # set frequency to freq_all if it is not set if key in ['freq_all']: continue diff --git a/avae/data.py b/avae/data.py index 701b84b5..63217036 100644 --- a/avae/data.py +++ b/avae/data.py @@ -140,7 +140,7 @@ def load_data( logging.info("\n") logging.info("############################################### DATA") - logging.info(f"Loading data...") + logging.info("Loading data...\n") # read the class list, if not provided all classes in the dataset will be used as default if classes is not None: diff --git a/avae/utils.py b/avae/utils.py index b8689726..184262c3 100644 --- a/avae/utils.py +++ b/avae/utils.py @@ -98,10 +98,16 @@ def save_imshow_png( if not os.path.exists("plots"): os.mkdir("plots") - fig, _ = plt.subplots(figsize=(10, 10)) - plt.imshow(array, cmap=cmap, vmin=min, vmax=max) # channels last - - plt.savefig("plots/" + fname) + height, width = array.shape[:2] + if width >= height: + image_figure_size = (10, 10 * height / width) + else: + image_figure_size = (10 * width / height, 10) + fig, ax = plt.subplots(figsize=image_figure_size) + ax.imshow(array, cmap=cmap, vmin=min, vmax=max) # channels last + ax.axis("off") + fig.subplots_adjust(left=0, right=1, bottom=0, top=1) + fig.savefig("plots/" + fname, bbox_inches="tight", pad_inches=0) if writer: writer.add_figure(figname, fig, epoch) diff --git a/avae/vis.py b/avae/vis.py index 92fd2a09..b4083d89 100644 --- a/avae/vis.py +++ b/avae/vis.py @@ -7,7 +7,6 @@ import warnings import altair -import matplotlib.gridspec as gridspec import matplotlib.pyplot as plt import numpy as np import numpy.typing as npt @@ -18,6 +17,7 @@ import sklearn.metrics.pairwise import torch import torchvision +from mpl_toolkits.axes_grid1 import make_axes_locatable from . import utils_learning from .utils import ( @@ -31,6 +31,18 @@ ) +def _class_plot_scale( + num_classes: int, fig_size: int | None = None +) -> tuple[float, int, int]: + """Return consistent figure and font sizes for class-based plots.""" + font_size = max(8, int(num_classes / 3) + 3) + return ( + fig_size if fig_size is not None else max(6, int(num_classes) / 2), + font_size, + max(font_size + 4, int(num_classes / 2) + 8), + ) + + def _encoder(i: PIL.Image) -> str: """Encode PIL Image as base64 buffer. Parameters @@ -248,6 +260,7 @@ def latent_embed_plot_tsne( ) n_classes = len(classes) + fig_size, font_size, title_font_size = _class_plot_scale(n_classes) if n_classes < 3: # If the number of classes are not moe than 3 the size of the figure would be too @@ -259,6 +272,7 @@ def latent_embed_plot_tsne( fig, ax = plt.subplots( figsize=(int(n_classes / 2) + 4, int(n_classes / 2) + 2) ) + marker_size = max(marker_size, int(marker_size * max(1, n_classes / 5))) # When the number of classes is less than 3 the image becomes two small colours = colour_per_class(classes) @@ -276,12 +290,16 @@ def latent_embed_plot_tsne( label=mol[:4], facecolor=color, edgecolor=color, - alpha=0.2, + alpha=0.5, ) - ax.legend(bbox_to_anchor=(1.05, 1), loc="upper left", fontsize=16) - plt.xlabel("TSNE-1") - plt.ylabel("TSNE-2") + ax.legend( + bbox_to_anchor=(1.05, 1), + loc="upper left", + fontsize=font_size, + ) + ax.set_xlabel("TSNE-1", fontsize=font_size) + ax.set_ylabel("TSNE-2", fontsize=font_size) if xs.shape[-1] == 1: @@ -298,16 +316,21 @@ def latent_embed_plot_tsne( label=mol[:4], linewidth=l_w, ) - plt.legend( - prop={"size": 10}, + ax.legend( bbox_to_anchor=(1.05, 1), loc="upper left", - fontsize=16, + fontsize=font_size, ) - plt.xlabel("dim 1") - plt.ylabel("freq") + ax.set_xlabel("dim 1", fontsize=font_size) + ax.set_ylabel("freq", fontsize=font_size) - plt.tight_layout() + ax.set_title( + f"t-SNE Embedding at epoch: {epoch + 1}", + fontsize=title_font_size, + fontweight="normal", + ) + ax.tick_params(axis="both", labelsize=font_size) + fig.tight_layout() if not display: if not os.path.exists("plots"): @@ -507,11 +530,14 @@ def confidence_plot(x, y, s, suffix=None, vis_format="png"): ) cmap = plt.get_cmap("jet") cols = [cmap(i) for i in np.linspace(0, 1, len(x[0]))] - rows = len(np.unique(y)) // 2 - if len(np.unique(y)) % 2 != 0: + classes = np.unique(y) + _, font_size, title_font_size = _class_plot_scale(len(classes)) + rows = len(classes) // 2 + if len(classes) % 2 != 0: rows += 1 - fig, ax = plt.subplots(len(np.unique(y)), sharex=True, sharey=True) - for c, cl in enumerate(np.unique(y)): + fig, ax = plt.subplots(len(classes), sharex=True, sharey=True) + ax = np.atleast_1d(ax) + for c, cl in enumerate(classes): mu_cl = np.take(x, np.where(np.array(y) == cl)[0], axis=0) var_cl = np.take(s, np.where(np.array(y) == cl)[0], axis=0) std_cl = np.exp(0.5 * var_cl) @@ -532,7 +558,7 @@ def confidence_plot(x, y, s, suffix=None, vis_format="png"): color=cols[i], label="lat" + str(i + 1), ) - ax[c].set_title(cl) + ax[c].set_title(cl, fontsize=title_font_size, fontweight="normal") name = f"plots/confidence.{vis_format}" if suffix is not None: name = name[:-4] + "_" + suffix + name[-4:] @@ -540,6 +566,7 @@ def confidence_plot(x, y, s, suffix=None, vis_format="png"): leg = fig.legend( handles, labels, bbox_to_anchor=(1.06, 0.9) ) # , loc="upper left") + plt.tight_layout() fig.savefig(name, bbox_extra_artists=(leg,), bbox_inches="tight") plt.close() @@ -582,6 +609,9 @@ def accuracy_plot( classes_list = np.unique(np.concatenate((y_train, ypred_train))) + fig_size, font_size, title_font_size = _class_plot_scale(len(classes_list)) + value_font_size = min(font_size, 10) + # Compute confusion matrix cm = sklearn.metrics.confusion_matrix( y_train, ypred_train, labels=classes_list @@ -621,32 +651,35 @@ def accuracy_plot( confusion_matrix=cmn, display_labels=classes_list ) - with plt.rc_context( - {"font.weight": "bold", "font.size": int(len(classes_list) / 3) + 3} - ): - fig, ax = plt.subplots( - figsize=(int(len(classes_list)) / 2, int(len(classes_list)) / 2) - ) + with plt.rc_context({"font.weight": "bold", "font.size": font_size}): + fig, ax = plt.subplots(figsize=(fig_size, fig_size)) disp.plot( cmap=plt.cm.Blues, ax=ax, xticks_rotation=90, values_format="d", + colorbar=False, ) + divider = make_axes_locatable(ax) + colorbar_ax = divider.append_axes("right", size="5%", pad=0.1) + fig.colorbar(disp.im_, cax=colorbar_ax) + colorbar_ax.tick_params(labelsize=font_size) + plt.setp(disp.text_.ravel(), fontsize=value_font_size) - plt.tight_layout() - plt.title( + ax.set_title( "Average accuracy at epoch {}: {:.3f}%".format( epoch + 1, np.mean(avg_accuracy[supported_train_classes]) * 100, ), - fontsize=10, + fontsize=title_font_size, + fontweight="normal", ) if not os.path.exists("plots"): os.mkdir("plots") + fig.tight_layout() plt.savefig(f"plots/confusion_train{mode}.{vis_format}", dpi=300) # Save confusion matrix DataFrame to CSV @@ -660,9 +693,7 @@ def accuracy_plot( plt.close() - fig, ax = plt.subplots( - figsize=(int(len(classes_list)) / 2, int(len(classes_list)) / 2) - ) + fig, ax = plt.subplots(figsize=(fig_size, fig_size)) dispn.plot( cmap=plt.cm.Blues, @@ -670,19 +701,26 @@ def accuracy_plot( xticks_rotation=90, values_format=".0f", im_kw={"vmin": 0, "vmax": 100}, + colorbar=False, ) + divider = make_axes_locatable(ax) + colorbar_ax = divider.append_axes("right", size="5%", pad=0.1) + fig.colorbar(dispn.im_, cax=colorbar_ax) + colorbar_ax.tick_params(labelsize=font_size) + plt.setp(dispn.text_.ravel(), fontsize=value_font_size) - plt.tight_layout() - plt.title( + ax.set_title( "Average accuracy at epoch {}: {:.3f}%".format( epoch + 1, np.mean(avg_accuracy[supported_train_classes]) * 100, ), - fontsize=12, + fontsize=title_font_size, + fontweight="normal", ) - plt.xlabel("Predicted label (%)") - plt.ylabel("True label (%)") + ax.set_xlabel("Predicted label (%)") + ax.set_ylabel("True label (%)") + fig.tight_layout() plt.savefig(f"plots/confusion_train{mode}_norm.{vis_format}", dpi=300) if writer: @@ -759,35 +797,35 @@ def accuracy_plot( with plt.rc_context( { "font.weight": "bold", - "font.size": int(len(ordered_class_eval) / 3) + 3, + "font.size": font_size, } ): - fig, ax = plt.subplots( - figsize=( - int(len(ordered_class_eval)) / 2, - int(len(ordered_class_eval)) / 2, - ) - ) + fig, ax = plt.subplots(figsize=(fig_size, fig_size)) disp_eval.plot( cmap=plt.cm.Blues, ax=ax, xticks_rotation=90, values_format="d", + colorbar=False, ) - plt.tight_layout() - plt.title( + divider = make_axes_locatable(ax) + colorbar_ax = divider.append_axes("right", size="5%", pad=0.1) + fig.colorbar(disp_eval.im_, cax=colorbar_ax) + colorbar_ax.tick_params(labelsize=font_size) + plt.setp(disp_eval.text_.ravel(), fontsize=value_font_size) + ax.set_title( "Average accuracy at epoch {}: {:.1f}%".format( epoch + 1, np.mean(avg_accuracy_eval[supported_eval_classes]) * 100, ), - fontsize=12, + fontsize=title_font_size, + fontweight="normal", ) + fig.tight_layout() plt.savefig(figure_name + f".{vis_format}", dpi=300) plt.close() - fig, ax = plt.subplots( - figsize=(int(len(classes_list)) / 2, int(len(classes_list)) / 2) - ) + fig, ax = plt.subplots(figsize=(fig_size, fig_size)) dispn_eval.plot( cmap=plt.cm.Blues, @@ -795,18 +833,25 @@ def accuracy_plot( xticks_rotation=90, values_format=".0f", im_kw={"vmin": 0, "vmax": 100}, + colorbar=False, ) + divider = make_axes_locatable(ax) + colorbar_ax = divider.append_axes("right", size="5%", pad=0.1) + fig.colorbar(dispn_eval.im_, cax=colorbar_ax) + colorbar_ax.tick_params(labelsize=font_size) + plt.setp(dispn_eval.text_.ravel(), fontsize=value_font_size) - plt.tight_layout() - plt.title( + ax.set_title( "Average accuracy at epoch {}: {:.1f}% ".format( epoch + 1, np.mean(avg_accuracy_eval[supported_eval_classes]) * 100, ), - fontsize=10, + fontsize=title_font_size, + fontweight="normal", ) - plt.xlabel("Predicted label (%)") - plt.ylabel("True label (%)") + ax.set_xlabel("Predicted label (%)") + ax.set_ylabel("True label (%)") + fig.tight_layout() plt.savefig(f"{figure_name}_norm.{vis_format}", dpi=300) plt.close() @@ -848,6 +893,7 @@ def f1_plot( logging.info("Visualising F1 scores ...\n") classes_list = np.unique(np.concatenate((y_train, ypred_train))) classes_list_eval = np.unique(np.concatenate((y_val, ypred_val))) + fig_size, font_size, title_font_size = _class_plot_scale(len(classes_list)) if np.setdiff1d(classes_list_eval, classes_list).size > 0: logging.info( @@ -906,21 +952,34 @@ def f1_plot( with plt.rc_context( { "font.weight": "bold", - "font.size": int(len(classes_list) / 3) + 3, + "font.size": font_size, } ): - fig, ax = plt.subplots( - figsize=( - int(len(classes_list)) / 2, - int(len(classes_list)) / 2, - ) + fig, ax = plt.subplots(figsize=(fig_size, fig_size)) + line_width = max(1.5, font_size / 6) + plt.plot( + classes_list, + train_f1_score, + label="train", + marker="o", + linewidth=line_width, + ) + plt.plot( + classes_list, + valid_f1_score, + label=label, + marker="o", + linewidth=line_width, ) - plt.plot(classes_list, train_f1_score, label="train", marker="o") - plt.plot(classes_list, valid_f1_score, label=label, marker="o") plt.xticks(rotation=45) plt.legend(loc="lower left") - plt.title("F1 Score at epoch {}".format(epoch + 1)) + plt.title( + "F1 Score at epoch {}".format(epoch + 1), + fontsize=title_font_size, + fontweight="normal", + ) plt.ylabel("F1 Score") + plt.tight_layout() plt.savefig(f"plots/f1{mode}.{vis_format}", dpi=150) if writer: @@ -978,16 +1037,19 @@ def loss_plot( "VAL KL divergence loss x BETA", "VAL Affinity loss x GAMMA", ] + title_font_size = 16 + label_font_size = 16 + tick_font_size = 14 - plt.clf() - plt.ticklabel_format(useOffset=False) + fig, ax = plt.subplots(figsize=(10, 7)) + ax.ticklabel_format(useOffset=False) train_loss[-2] = train_loss[-2] * beta train_loss[-1] = train_loss[-1] * gamma for i, loss in enumerate(train_loss): s = "-" - plt.plot( + ax.plot( range(1, epochs + 1), loss, c=cols[i], linestyle=s, label=labs[i] ) if val_loss is not None: @@ -995,7 +1057,7 @@ def loss_plot( val_loss[-1] = val_loss[-1] * gamma for i, loss in enumerate(val_loss): s = "--" - plt.plot( + ax.plot( range(1, epochs + 1), loss, c=cols[i], @@ -1012,28 +1074,30 @@ def loss_plot( "Exiting.\n", ) return - plt.title( + ax.set_title( "bs: %d, d: %d, ch: %d, lat: %d, lr: %.3f, beta: %.1f, " - "gamma: %.1f" % (p[0], p[1], p[2], p[3], p[4], p[5], p[6]) + "gamma: %.1f" % (p[0], p[1], p[2], p[3], p[4], p[5], p[6]), + fontsize=title_font_size, + fontweight="normal", ) else: - plt.title("Loss") + ax.set_title("Loss", fontsize=title_font_size, fontweight="normal") - plt.yscale("log") - plt.ylabel("Loss", fontsize=16) - plt.xlabel("Epochs", fontsize=16) - plt.xticks(fontsize=16) - plt.yticks(fontsize=16) - plt.legend() + ax.set_yscale("log") + ax.set_ylabel("Loss", fontsize=label_font_size) + ax.set_xlabel("Epochs", fontsize=label_font_size) + ax.tick_params(axis="both", labelsize=tick_font_size) + ax.legend(fontsize=tick_font_size) - plt.tight_layout() + fig.tight_layout() if not os.path.exists("plots"): os.mkdir("plots") - plt.savefig(f"plots/loss.{vis_format}", dpi=300) - plt.close() + fig.savefig(f"plots/loss.{vis_format}", dpi=300) + plt.close(fig) # plotting only the total loss as it sometimes is a few order of magnitude higher than KLD and affinity losses - plt.plot( + fig, ax = plt.subplots(figsize=(10, 7)) + ax.plot( range(1, epochs + 1), train_loss[0], c=cols[0], @@ -1041,22 +1105,21 @@ def loss_plot( label=labs[0], ) if val_loss is not None: - plt.plot( + ax.plot( range(1, epochs + 1), val_loss[0], c=cols[0], linestyle="--", label=vlabs[0], ) - plt.yscale("log") - plt.ylabel("Loss", fontsize=16) - plt.xlabel("Epochs", fontsize=16) - plt.xticks(fontsize=16) - plt.yticks(fontsize=16) - plt.legend() - plt.tight_layout() - plt.savefig(f"plots/loss_total.{vis_format}", dpi=300) - plt.close() + ax.set_yscale("log") + ax.set_ylabel("Loss", fontsize=label_font_size) + ax.set_xlabel("Epochs", fontsize=label_font_size) + ax.tick_params(axis="both", labelsize=tick_font_size) + ax.legend(fontsize=tick_font_size) + fig.tight_layout() + fig.savefig(f"plots/loss_total.{vis_format}", dpi=300) + plt.close(fig) def recon_plot( @@ -1783,21 +1846,16 @@ def plot_affinity_matrix( ) logging.info("Visualising affinity matrix ...\n") - if fig_size is None: - with plt.rc_context( - {"font.weight": "bold", "font.size": int(len(all_classes) / 3) + 3} - ): - fig, ax = plt.subplots( - figsize=(int(len(all_classes)) / 2, int(len(all_classes)) / 2) - ) - else: - fig, ax = plt.subplots(figsize=(fig_size, fig_size)) - # Create the figure and gridspec - gs = gridspec.GridSpec(1, 2, width_ratios=[9, 0.4]) + resolved_fig_size, font_size, title_font_size = _class_plot_scale( + len(all_classes), fig_size + ) - # Plot the data on the left grid - ax = plt.subplot(gs[0]) - ax.set_title("Affinity Matrix", fontsize=16) + fig, ax = plt.subplots(figsize=(resolved_fig_size, resolved_fig_size)) + ax.set_title( + "Affinity Matrix", + fontsize=title_font_size, + fontweight="normal", + ) im = ax.imshow(lookup, vmin=-1, vmax=1, cmap=plt.get_cmap("RdBu")) @@ -1806,21 +1864,19 @@ def plot_affinity_matrix( ax.set_yticks(np.arange(0, len(all_classes))) ax.set_yticklabels(all_classes) - # Colour the label for selected classes + # Colour classes absent from the selected dataset. for i, c in enumerate(all_classes): - if c in selected_classes: + if c not in selected_classes: ax.get_xticklabels()[i].set_color("red") ax.get_yticklabels()[i].set_color("red") - ax.tick_params(axis="x", rotation=90, labelsize=16) - ax.tick_params(axis="y", labelsize=16) - - # Create an empty plot on the right grid - ax2 = plt.subplot(gs[1]) + ax.tick_params(axis="x", rotation=90, labelsize=font_size) + ax.tick_params(axis="y", labelsize=font_size) - plt.colorbar(im, cax=ax2) - # Set the height of the color bar to match the height of the plot - # cb.ax.set_position([0.96, 0.01, 0.01, 0.01]) + divider = make_axes_locatable(ax) + colorbar_ax = divider.append_axes("right", size="5%", pad=0.1) + fig.colorbar(im, cax=colorbar_ax) + colorbar_ax.tick_params(labelsize=font_size) fig.tight_layout() if not os.path.exists("plots"): os.mkdir("plots") @@ -1846,17 +1902,22 @@ def plot_classes_distribution( ) logging.info("Visualising classes distribution " + category + "...\n") - fig, ax = plt.subplots(figsize=(9, 9)) labels, counts = np.unique(data, return_counts=True) + fig_size, font_size, title_font_size = _class_plot_scale(len(labels)) + fig, ax = plt.subplots(figsize=(fig_size, fig_size)) ticks = range(len(counts)) - plt.bar(ticks, counts, align="center", color="blue", alpha=0.5) - plt.xticks(ticks, labels) - plt.title("Classes Distribution", fontsize=16) - plt.xlabel("Class", fontsize=16) - plt.ylabel("Number of Entries", fontsize=16) - plt.xticks(fontsize=16, rotation=90) - plt.yticks(fontsize=16) - plt.tight_layout() + ax.bar(ticks, counts, align="center", color="blue", alpha=0.5) + ax.set_xticks(ticks, labels) + ax.set_title( + "Classes Distribution", + fontsize=title_font_size, + fontweight="normal", + ) + ax.set_xlabel("Class", fontsize=font_size) + ax.set_ylabel("Number of Entries", fontsize=font_size) + ax.tick_params(axis="x", labelsize=font_size, rotation=90) + ax.tick_params(axis="y", labelsize=font_size) + fig.tight_layout() if not os.path.exists("plots"): os.mkdir("plots") plt.savefig(f"plots/classes_distribution_{category}.{vis_format}", dpi=300) @@ -1879,16 +1940,16 @@ def plot_cyc_variable( "################################################################", ) logging.info(f"Visualising {variable_name} ...\n") - plt.plot(array, linewidth=3) - plt.ylabel(rf"$\{variable_name}$", fontsize=16) - plt.xlabel("Epochs", fontsize=16) - plt.xticks(fontsize=16) - plt.yticks(fontsize=16) - plt.tight_layout() + fig, ax = plt.subplots(figsize=(10, 7)) + ax.plot(array, linewidth=3) + ax.set_ylabel(rf"$\{variable_name}$", fontsize=16) + ax.set_xlabel("Epochs", fontsize=16) + ax.tick_params(axis="both", labelsize=14) + fig.tight_layout() if not os.path.exists("plots"): os.mkdir("plots") - plt.savefig(f"plots/{variable_name}_array.{vis_format}", dpi=300) - plt.close() + fig.savefig(f"plots/hyperaparam_{variable_name}.{vis_format}", dpi=300) + plt.close(fig) def latent_space_similarity_plot( @@ -1944,6 +2005,9 @@ def latent_space_similarity_plot( unique_classes = classes_order num_classes = len(unique_classes) + resolved_fig_size, font_size, title_font_size = _class_plot_scale( + num_classes, fig_size + ) cosine_sim = latent_space_similarity_mat( latent_space, class_labels, @@ -1957,32 +2021,46 @@ def latent_space_similarity_plot( with plt.rc_context( { "font.weight": "bold", - "font.size": int(len(unique_classes) / 3) + 3, + "font.size": font_size, } ): fig, ax = plt.subplots( - figsize=( - int(len(unique_classes)) / 2, - int(len(unique_classes)) / 2, - ) + figsize=(resolved_fig_size, resolved_fig_size) ) else: - fig, ax = plt.subplots(figsize=(fig_size, fig_size)) - fig.tight_layout(pad=3) - plt.imshow(cosine_sim, cmap="RdBu", vmin=-1, vmax=1) - plt.colorbar(label="Average Cosine Similarity") - plt.xticks(ticks=np.arange(num_classes), labels=unique_classes) - plt.yticks(ticks=np.arange(num_classes), labels=unique_classes) - plt.title(f"Average Cosine Similarity Matrix at epoch :{epoch}") - plt.xlabel("Class Labels") - plt.ylabel("Class Labels") + fig, ax = plt.subplots(figsize=(resolved_fig_size, resolved_fig_size)) + im = ax.imshow(cosine_sim, cmap="RdBu", vmin=-1, vmax=1) + divider = make_axes_locatable(ax) + colorbar_ax = divider.append_axes("right", size="5%", pad=0.1) + fig.colorbar(im, cax=colorbar_ax) + colorbar_ax.tick_params(labelsize=font_size) + ax.set_xticks(np.arange(num_classes), labels=unique_classes) + ax.set_yticks(np.arange(num_classes), labels=unique_classes) + ax.set_title( + f"Average Cosine Similarity Matrix at epoch: {epoch + 1}", + fontsize=title_font_size, + fontweight="normal", + ) + ax.set_xlabel("Class Labels", fontsize=font_size) + ax.set_ylabel("Class Labels", fontsize=font_size) ax.tick_params(axis="x", rotation=90, labelsize=font_size) ax.tick_params(axis="y", labelsize=font_size) + classes_in_data = set(np.asarray(class_labels).astype(str)) + for class_name, x_label, y_label in zip( + unique_classes, ax.get_xticklabels(), ax.get_yticklabels() + ): + if str(class_name) not in classes_in_data: + x_label.set_color("red") + y_label.set_color("red") + fig.tight_layout() if not display: if not os.path.exists("plots"): os.mkdir("plots") - plt.savefig(f"plots/similarity_mean{mode}.{vis_format}", dpi=dpi) + plt.savefig( + f"plots/similarity_mean{mode}.{vis_format}", + dpi=dpi, + ) plt.close() else: plt.show() diff --git a/tests/test_config.py b/tests/test_config.py index 44d6af95..de228d03 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -121,6 +121,13 @@ def test_validate_config(self): data_config_only = load_config_params(config_file=self.config) self.assertEqual(data_config_only.epochs, 1000) + data_cli_frequency = load_config_params( + config_file=self.config, + local_args={"freq_all": 5, "freq_acc": 3}, + ) + self.assertEqual(data_cli_frequency.freq_acc, 3) + self.assertEqual(data_cli_frequency.freq_rec, 5) + data_local_data_only = load_config_params(local_args=self.data_local) self.assertEqual(data_local_data_only.epochs, 150) self.assertEqual( diff --git a/tests/test_train_eval_pipeline.py b/tests/test_train_eval_pipeline.py index 95642edc..bf5b2646 100644 --- a/tests/test_train_eval_pipeline.py +++ b/tests/test_train_eval_pipeline.py @@ -23,7 +23,6 @@ def pipeline_params(datapath): "no_val_drop": True, "affinity": os.path.join(datapath, "affinity_fsc_10.csv"), "classes": os.path.join(datapath, "classes.csv"), - "vis_dynamic": True, "epochs": 1, "channels": 3, "depth": 4,