|
| 1 | +import os |
| 2 | +import pandas as pd |
| 3 | +import plotly.graph_objects as go |
| 4 | +from plotly.subplots import make_subplots |
| 5 | +import math |
| 6 | +import numpy as np |
| 7 | + |
| 8 | +# Function to read all CSV files in the current directory |
| 9 | +def read_csv_files(): |
| 10 | + csv_files = [file for file in os.listdir('.') if file.endswith('.csv')] |
| 11 | + data = {file: pd.read_csv(file) for file in csv_files} |
| 12 | + for file in data: |
| 13 | + df = data[file] |
| 14 | + df['Step'] = df.index # Add step column for easy plotting |
| 15 | + latest_file = max(csv_files, key=os.path.getmtime) |
| 16 | + return data, data[latest_file] |
| 17 | + |
| 18 | +# Function to plot a metric |
| 19 | +def plot_metric(fig, data, metric_name, row, col, ylabel=None, transform=None): |
| 20 | + for file, df in data.items(): |
| 21 | + y = df[metric_name][1:] if transform is None else transform(df) |
| 22 | + fig.add_trace( |
| 23 | + go.Scatter(x=df['Step'][1:], y=y, name=metric_name, mode='lines'), |
| 24 | + row=row, col=col |
| 25 | + ) |
| 26 | + fig.update_xaxes(title_text='Step', row=row, col=col) |
| 27 | + fig.update_yaxes(title_text=ylabel if ylabel else metric_name, row=row, col=col) |
| 28 | + fig.update_layout(title_text=metric_name, showlegend=True) |
| 29 | + |
| 30 | +# Read CSV files |
| 31 | +data, last_df = read_csv_files() |
| 32 | + |
| 33 | +# Define metrics to plot |
| 34 | +metrics = [ |
| 35 | + ("Count episodes", None), |
| 36 | + ("Old episodes, %", lambda df: df["Count episodes with old"][1:] / df["Count episodes"][1:] * 100), |
| 37 | + ("Old ticks, %", lambda df: df["Count ticks with old"][1:] / (df["Count ticks with current"][1:] + df["Count ticks with old"][1:]) * 100), |
| 38 | + ("Average freeze time", None), |
| 39 | + ("Average ball hits", lambda df: df["Cumulative ball hits"][1:] / (df["Count episodes"][1:] / 2)), |
| 40 | + ("Average ball absolute velocity", None), |
| 41 | + ("Average ball velocity(x)", None), |
| 42 | + ("Average first bot reward", None), |
| 43 | + ("Average second bot reward", None), |
| 44 | + ("Average bots reward difference", lambda df: df["Average second bot reward"][1:] - df["Average first bot reward"][1:]), |
| 45 | + ("Highest reward per tick", None), |
| 46 | + ("Average current-old score difference", lambda df: (df["Current bot cumulative score"][1:] - df["Old bot cumulative score"][1:]) / (df["Count episodes"][1:] / 2)), |
| 47 | + ("Actor loss", None), |
| 48 | + ("Critic loss", None), |
| 49 | + ("Critic Mean Absolute Error", None), |
| 50 | + ("Critic Correlation Coefficient", None), |
| 51 | + ("Training loss", None), |
| 52 | + ("Policy loss", lambda df: df["Critic loss"][1:] * 0.5 - df["Actor loss"][1:]), |
| 53 | + ("Entropy loss", lambda df: df["Entropy"][1:] * df["Entropy coefficient"][1:]), |
| 54 | + (["Entropy", "Minimal Entropy", "Maximal Entropy"], None), |
| 55 | + (["Angle entropy", "Minimal Angle entropy", "Maximal Angle entropy"], None), |
| 56 | + (["Hook entropy", "Minimal Hook entropy", "Maximal Hook entropy"], None), |
| 57 | + (["Hammer entropy", "Minimal Hammer entropy", "Maximal Hammer entropy"], None), |
| 58 | + (["Direction entropy", "Minimal Direction entropy", "Maximal Direction entropy"], None), |
| 59 | + ("Entropy coefficient", None), |
| 60 | + ("Actor grad norm", None), |
| 61 | + ("Critic grad norm", None), |
| 62 | + ("Actor weight norm", None), |
| 63 | + ("Critic weight norm", None), |
| 64 | + ("Actor activation mean", None), |
| 65 | + ("Actor activation std", None), |
| 66 | + ("Learning rate", None) |
| 67 | +] |
| 68 | + |
| 69 | +# Number of time-related plots |
| 70 | +num_add_plots = 5 # Time to process and Decide profiling |
| 71 | + |
| 72 | +# Total number of plots |
| 73 | +total_plots = len(metrics) + num_add_plots |
| 74 | + |
| 75 | +# Calculate nrows and ncols dynamically |
| 76 | +ncols = 3 # Fixed number of columns |
| 77 | +nrows = math.ceil(total_plots / ncols) # Calculate rows based on total plots and columns |
| 78 | + |
| 79 | +# Create subplots |
| 80 | +fig = make_subplots(rows=nrows, cols=ncols, subplot_titles=[metric[0] if not isinstance(metric[0], list) else metric[0][0] for metric in metrics] + |
| 81 | + ['Average First/Second Bot Scores', 'Average bots score', 'Ticks per second', 'Time to process', 'Decide profiling']) |
| 82 | + |
| 83 | +# Plot metrics |
| 84 | +for i, (metric_name, transform) in enumerate(metrics): |
| 85 | + row = (i // ncols) + 1 |
| 86 | + col = (i % ncols) + 1 |
| 87 | + try: |
| 88 | + if not isinstance(metric_name, list): |
| 89 | + plot_metric(fig, data, metric_name, row, col, transform=transform) |
| 90 | + else: |
| 91 | + for metric in metric_name: |
| 92 | + plot_metric(fig, data, metric, row, col, transform=transform) |
| 93 | + except Exception as e: |
| 94 | + print("Unable to plot metric:", metric_name, "error:", e) |
| 95 | + |
| 96 | +# Plot bot scores (calculated dynamically) |
| 97 | +row = (len(metrics) // ncols) + 1 |
| 98 | +col = (len(metrics) % ncols) + 1 |
| 99 | +for file, df in data.items(): |
| 100 | + fig.add_trace( |
| 101 | + go.Scatter(x=df['Step'], y=df['First bot cumulative score'] / (df['Count episodes'] / 2), |
| 102 | + name=f'First bot {file[:-4]}', mode='lines'), |
| 103 | + row=row, col=col |
| 104 | + ) |
| 105 | + fig.add_trace( |
| 106 | + go.Scatter(x=df['Step'], y=df['Second bot cumulative score'] / (df['Count episodes'] / 2), |
| 107 | + name=f'Second bot {file[:-4]}', mode='lines'), |
| 108 | + row=row, col=col |
| 109 | + ) |
| 110 | +fig.update_xaxes(title_text='Step', row=row, col=col) |
| 111 | +fig.update_yaxes(title_text='Average Score', row=row, col=col) |
| 112 | +fig.update_layout(title_text='Average Bot Scores', showlegend=True) |
| 113 | + |
| 114 | +def ema_tb(y, smoothing=0.9): |
| 115 | + y = np.asarray(y, dtype=float) |
| 116 | + alpha = 1 - smoothing |
| 117 | + s = np.zeros_like(y) |
| 118 | + s[0] = y[0] |
| 119 | + for i in range(1, len(y)): |
| 120 | + s[i] = alpha * y[i] + (1 - alpha) * s[i-1] |
| 121 | + return s |
| 122 | + |
| 123 | +# Plot bot average score |
| 124 | +row = ((len(metrics) + 1) // ncols) + 1 |
| 125 | +col = ((len(metrics) + 1) % ncols) + 1 |
| 126 | +for file, df in data.items(): |
| 127 | + first_bot_avg_score = df['First bot cumulative score'] / (df['Count episodes'] / 2) |
| 128 | + second_bot_avg_score = df['Second bot cumulative score'] / (df['Count episodes'] / 2) |
| 129 | + fig.add_trace( |
| 130 | + go.Scatter(x=df['Step'], y=(first_bot_avg_score + second_bot_avg_score) / 2, |
| 131 | + name=f'Average score {file[:-4]}', mode='lines'), |
| 132 | + row=row, col=col |
| 133 | + ) |
| 134 | + fig.add_trace( |
| 135 | + go.Scatter(x=df['Step'], y=ema_tb((first_bot_avg_score + second_bot_avg_score) / 2), |
| 136 | + name=f'Average score smoothed {file[:-4]}', mode='lines'), |
| 137 | + row=row, col=col |
| 138 | + ) |
| 139 | +fig.update_xaxes(title_text='Step', row=row, col=col) |
| 140 | +fig.update_yaxes(title_text='Average Score', row=row, col=col) |
| 141 | +fig.update_layout(title_text='Average Bots Score', showlegend=True) |
| 142 | + |
| 143 | +# Plot Ticks per second |
| 144 | +row = ((len(metrics) + 2) // ncols) + 1 |
| 145 | +col = ((len(metrics) + 2) % ncols) + 1 |
| 146 | +for file, df in data.items(): |
| 147 | + fig.add_trace( |
| 148 | + go.Scatter(x=df['Step'], y=df['TPS'], name=f'Real {file[:-4]}', mode='lines'), |
| 149 | + row=row, col=col |
| 150 | + ) |
| 151 | + fig.add_trace( |
| 152 | + go.Scatter(x=df['Step'], y=1000 / (df['Time to decide'] + df['Time to tick'] + df['Time rest']), |
| 153 | + name=f'Expected {file[:-4]}', mode='lines'), |
| 154 | + row=row, col=col |
| 155 | + ) |
| 156 | +fig.update_xaxes(title_text='Step', row=row, col=col) |
| 157 | +fig.update_yaxes(title_text='TPS', row=row, col=col) |
| 158 | +fig.update_layout(title_text='Ticks per second', showlegend=True) |
| 159 | + |
| 160 | +# Plot time-related metrics |
| 161 | +row = ((len(metrics) + 3) // ncols) + 1 |
| 162 | +col = ((len(metrics) + 3) % ncols) + 1 |
| 163 | +fig.add_trace( |
| 164 | + go.Scatter(x=last_df['Step'], y=last_df['Time to decide'], name='Time to decide', mode='lines'), |
| 165 | + row=row, col=col |
| 166 | +) |
| 167 | +fig.add_trace( |
| 168 | + go.Scatter(x=last_df['Step'], y=last_df['Time to tick'], name='Time to tick', mode='lines', line=dict(color='green')), |
| 169 | + row=row, col=col |
| 170 | +) |
| 171 | +fig.add_trace( |
| 172 | + go.Scatter(x=last_df['Step'], y=last_df['Time rest'], name='Time rest', mode='lines', line=dict(color='yellow')), |
| 173 | + row=row, col=col |
| 174 | +) |
| 175 | +fig.update_xaxes(title_text='Step', row=row, col=col) |
| 176 | +fig.update_yaxes(title_text='Time taken, ms', row=row, col=col) |
| 177 | +fig.update_layout(title_text='Time to process', showlegend=True) |
| 178 | + |
| 179 | +# Plot Decide profiling |
| 180 | +row = ((len(metrics) + 4) // ncols) + 1 |
| 181 | +col = ((len(metrics) + 4) % ncols) + 1 |
| 182 | +fig.add_trace( |
| 183 | + go.Scatter(x=last_df['Step'], y=last_df['Time pre forward'], name='Time pre forward', mode='lines'), |
| 184 | + row=row, col=col |
| 185 | +) |
| 186 | +fig.add_trace( |
| 187 | + go.Scatter(x=last_df['Step'], y=last_df['Time forward'], name='Time forward', mode='lines', line=dict(color='green')), |
| 188 | + row=row, col=col |
| 189 | +) |
| 190 | +fig.add_trace( |
| 191 | + go.Scatter(x=last_df['Step'], y=last_df['Time normal'], name='Time normal', mode='lines', line=dict(color='yellow')), |
| 192 | + row=row, col=col |
| 193 | +) |
| 194 | +fig.add_trace( |
| 195 | + go.Scatter(x=last_df['Step'], y=last_df['Time to cpu'], name='Time to cpu', mode='lines', line=dict(color='black')), |
| 196 | + row=row, col=col |
| 197 | +) |
| 198 | +fig.add_trace( |
| 199 | + go.Scatter(x=last_df['Step'], y=last_df['Time process last'], name='Time process last', mode='lines', line=dict(color='orange')), |
| 200 | + row=row, col=col |
| 201 | +) |
| 202 | +fig.update_xaxes(title_text='Step', row=row, col=col) |
| 203 | +fig.update_yaxes(title_text='Time taken, ms', row=row, col=col) |
| 204 | +fig.update_layout(title_text='Decide profiling', showlegend=True) |
| 205 | + |
| 206 | +# Hide unused subplots |
| 207 | +for i in range(total_plots, nrows * ncols): |
| 208 | + fig.update_xaxes(visible=False, row=(i // ncols) + 1, col=(i % ncols) + 1) |
| 209 | + fig.update_yaxes(visible=False, row=(i // ncols) + 1, col=(i % ncols) + 1) |
| 210 | + |
| 211 | +# Adjust layout for full screen |
| 212 | +fig.update_layout( |
| 213 | + autosize=True, # Automatically resize to fit the screen |
| 214 | + margin=dict(l=0, r=0, t=40, b=0), # Remove margins |
| 215 | + height=300 * nrows, # Let Plotly handle height |
| 216 | + width=None, # Let Plotly handle width |
| 217 | + title_text="Training Metrics", |
| 218 | + showlegend=True |
| 219 | +) |
| 220 | + |
| 221 | +# Save and show plot |
| 222 | +fig.write_html("data.html", auto_open=True) |
0 commit comments