2727
2828using Pkg
2929Pkg. activate (joinpath (@__DIR__ ))
30+ using Dates
3031
3132println (" ╔═══════════════════════════════════════════════════════════╗" )
3233println (" ║ ECHIDNA Neural Solver — Training Pipeline ║" )
@@ -143,7 +144,9 @@ println("Training ($(training_config.num_epochs) epochs)...")
143144println (" ═══════════════════════════════════════════════════════════" )
144145
145146mkpath (save_dir)
147+ t_start = time ()
146148metrics = train_solver! (solver, train_data, val_data; config= training_config)
149+ duration_seconds = time () - t_start
147150
148151# Save final model
149152println ()
@@ -154,14 +157,93 @@ println("═══════════════════════
154157final_path = joinpath (save_dir, " final_model" )
155158save_solver (solver, final_path)
156159
160+ # Deploy alias: gnn_endpoint.jl loads gnn_ranker → best_model → final_model.
161+ best_dir = joinpath (save_dir, " best_model" )
162+ ranker_dir = joinpath (save_dir, " gnn_ranker" )
163+ if isdir (best_dir)
164+ rm (ranker_dir; recursive= true , force= true )
165+ cp (best_dir, ranker_dir)
166+ println (" Published best_model → $ranker_dir " )
167+ end
168+
157169# Save vocabulary separately for the API server
158170BSON. @save joinpath (save_dir, " vocabulary.bson" ) vocab
159171
172+ # Flat canonical artefacts expected by gnn_endpoint.jl and the spec.
173+ # premise_selector.bson = the full NeuralSolver weights (renamed for clarity).
174+ # tactic_predictor.bson = same weights (tactic side shares the text_encoder).
175+ # vocab.json = human-readable vocabulary for inspection and version checks.
176+ import JSON3 as _JSON3
177+ weights = Flux. state (solver)
178+ BSON. bson (joinpath (save_dir, " premise_selector.bson" ), weights= weights)
179+ BSON. bson (joinpath (save_dir, " tactic_predictor.bson" ), weights= weights)
180+
181+ # Build a compact vocab.json: token→id map + metadata.
182+ tactic_classes = sort (unique ([
183+ ex. proof_state. goal[1 : min (8 ,length (ex. proof_state. goal))]
184+ for ex in vcat (train_data. examples, val_data. examples)
185+ ]))
186+ vocab_json = Dict (
187+ " vocab_size" => vocab. vocab_size,
188+ " tactic_classes" => length (tactic_classes),
189+ " token_to_id" => vocab. token_to_id,
190+ )
191+ open (joinpath (save_dir, " vocab.json" ), " w" ) do io
192+ _JSON3. write (io, vocab_json)
193+ end
194+ println (" Saved vocab.json ($(vocab. vocab_size) tokens, $(length (tactic_classes)) tactic classes)" )
195+
196+ # Compute final MRR / top-k on validation split.
197+ val_metrics = compute_metrics (solver, val_data; k= 10 )
198+ val_top1 = compute_metrics (solver, val_data; k= 1 ). precision
199+ val_top5 = compute_metrics (solver, val_data; k= 5 ). precision
200+ println (" Validation MRR=$(round (val_metrics. mrr, digits= 4 )) top1=$(round (val_top1, digits= 4 )) top5=$(round (val_top5, digits= 4 )) top10=$(round (val_metrics. precision, digits= 4 )) " )
201+
202+ # Append a single JSONL row to training_data/metrics_baseline.jsonl.
203+ git_sha = try ; strip (read (` git -C $(joinpath (@__DIR__ , " .." , " .." )) rev-parse --short HEAD` , String)); catch ; " unknown" ; end
204+ metrics_row = Dict {String,Any} (
205+ " timestamp" => string (Dates. now ()),
206+ " git_sha" => git_sha,
207+ " mrr" => round (Float64 (val_metrics. mrr), digits= 6 ),
208+ " top1" => round (Float64 (val_top1), digits= 6 ),
209+ " top5" => round (Float64 (val_top5), digits= 6 ),
210+ " top10" => round (Float64 (val_metrics. precision), digits= 6 ),
211+ " epochs" => training_config. num_epochs,
212+ " max_proof_states" => max_proof_states,
213+ " duration_seconds" => round (duration_seconds, digits= 1 ),
214+ " device" => has_gpu ? " gpu" : " cpu" ,
215+ )
216+ metrics_baseline_path = joinpath (data_dir, " metrics_baseline.jsonl" )
217+ open (metrics_baseline_path, " a" ) do io
218+ println (io, _JSON3. write (metrics_row))
219+ end
220+ println (" Appended metrics row to $metrics_baseline_path " )
221+
222+ # Rewrite models/model_metadata.txt with real values.
223+ metadata_path = joinpath (@__DIR__ , " .." , " .." , " models" , " model_metadata.txt" )
224+ open (metadata_path, " w" ) do io
225+ println (io, " # ECHIDNA Neural Models v2.0" )
226+ println (io, " # Trained: $(Dates. now ()) " )
227+ println (io, " # Git SHA: $git_sha " )
228+ println (io, " # Device: $(has_gpu ? " GPU" : " CPU" ) " )
229+ println (io, " # Premise Selector: vocabulary-based ($(vocab. vocab_size) words)" )
230+ println (io, " # Tactic Predictor: neural text encoder ($(length (tactic_classes)) classes)" )
231+ println (io, " # MRR: $(round (Float64 (val_metrics. mrr), digits= 4 )) " )
232+ println (io, " # Top-1 Precision: $(round (Float64 (val_top1), digits= 4 )) " )
233+ println (io, " # Top-5 Precision: $(round (Float64 (val_top5), digits= 4 )) " )
234+ println (io, " # Epochs: $(training_config. num_epochs) " )
235+ println (io, " # Max Proof States: $max_proof_states " )
236+ println (io, " # Training Examples: $(length (train_data. examples)) " )
237+ println (io, " # Validation Examples: $(length (val_data. examples)) " )
238+ end
239+ println (" Updated $metadata_path " )
240+
160241println ()
161242println (" ╔═══════════════════════════════════════════════════════════╗" )
162243println (" ║ Training Complete! ║" )
163244println (" ╚═══════════════════════════════════════════════════════════╝" )
164245println ()
165246println (" Model saved to: $save_dir " )
247+ println (" MRR: $(round (Float64 (val_metrics. mrr), digits= 4 )) " )
166248println (" To start the API server:" )
167249println (" julia --project=src/julia src/julia/run_server.jl $save_dir " )
0 commit comments