thrust_rl/train/ppo/
stats.rs1use std::ops::AddAssign;
7
8#[derive(Debug, Clone, Default)]
13pub struct TrainingStats {
14 pub policy_loss: f64,
16
17 pub value_loss: f64,
19
20 pub entropy: f64,
22
23 pub total_loss: f64,
25
26 pub aux_loss: f64,
28
29 pub clip_fraction: f64,
31
32 pub approx_kl: f64,
34
35 pub explained_var: f64,
37
38 pub num_updates: usize,
40}
41
42impl TrainingStats {
43 pub fn zeros() -> Self {
45 Self::default()
46 }
47
48 pub fn new(
53 policy_loss: f64,
54 value_loss: f64,
55 entropy: f64,
56 total_loss: f64,
57 clip_fraction: f64,
58 approx_kl: f64,
59 explained_var: f64,
60 ) -> Self {
61 Self {
62 policy_loss,
63 value_loss,
64 entropy,
65 total_loss,
66 aux_loss: 0.0,
67 clip_fraction,
68 approx_kl,
69 explained_var,
70 num_updates: 1,
71 }
72 }
73
74 pub fn with_aux_loss(mut self, aux_loss: f64) -> Self {
76 self.aux_loss = aux_loss;
77 self
78 }
79
80 pub fn add(&mut self, other: &TrainingStats) {
82 self.policy_loss += other.policy_loss;
83 self.value_loss += other.value_loss;
84 self.entropy += other.entropy;
85 self.total_loss += other.total_loss;
86 self.aux_loss += other.aux_loss;
87 self.clip_fraction += other.clip_fraction;
88 self.approx_kl += other.approx_kl;
89 self.explained_var += other.explained_var;
90 self.num_updates += other.num_updates;
91 }
92
93 pub fn average(&self) -> Self {
95 let scale = self.num_updates as f64;
96 if scale == 0.0 {
97 return Self::zeros();
98 }
99
100 Self {
101 policy_loss: self.policy_loss / scale,
102 value_loss: self.value_loss / scale,
103 entropy: self.entropy / scale,
104 total_loss: self.total_loss / scale,
105 aux_loss: self.aux_loss / scale,
106 clip_fraction: self.clip_fraction / scale,
107 approx_kl: self.approx_kl / scale,
108 explained_var: self.explained_var / scale,
109 num_updates: 1,
110 }
111 }
112}
113
114impl AddAssign<&TrainingStats> for TrainingStats {
115 fn add_assign(&mut self, other: &TrainingStats) {
116 self.add(other);
117 }
118}
119
120#[derive(Debug, Clone)]
124pub struct AggregatedStats {
125 pub current: TrainingStats,
127
128 pub running_avg: TrainingStats,
130
131 pub best_policy_loss: f64,
133
134 pub best_value_loss: f64,
136
137 pub total_steps: usize,
139
140 pub learning_rate: f64,
142}
143
144impl AggregatedStats {
145 pub fn new(learning_rate: f64) -> Self {
147 Self {
148 current: TrainingStats::zeros(),
149 running_avg: TrainingStats::zeros(),
150 best_policy_loss: f64::INFINITY,
151 best_value_loss: f64::INFINITY,
152 total_steps: 0,
153 learning_rate,
154 }
155 }
156
157 pub fn update(&mut self, stats: TrainingStats) {
159 self.current = stats.clone();
160 self.total_steps += 1;
161
162 let alpha = 0.1;
164 self.running_avg.policy_loss =
165 alpha * stats.policy_loss + (1.0 - alpha) * self.running_avg.policy_loss;
166 self.running_avg.value_loss =
167 alpha * stats.value_loss + (1.0 - alpha) * self.running_avg.value_loss;
168 self.running_avg.entropy = alpha * stats.entropy + (1.0 - alpha) * self.running_avg.entropy;
169 self.running_avg.total_loss =
170 alpha * stats.total_loss + (1.0 - alpha) * self.running_avg.total_loss;
171 self.running_avg.clip_fraction =
172 alpha * stats.clip_fraction + (1.0 - alpha) * self.running_avg.clip_fraction;
173 self.running_avg.approx_kl =
174 alpha * stats.approx_kl + (1.0 - alpha) * self.running_avg.approx_kl;
175 self.running_avg.explained_var =
176 alpha * stats.explained_var + (1.0 - alpha) * self.running_avg.explained_var;
177
178 if stats.policy_loss < self.best_policy_loss {
180 self.best_policy_loss = stats.policy_loss;
181 }
182 if stats.value_loss < self.best_value_loss {
183 self.best_value_loss = stats.value_loss;
184 }
185 }
186}