Skip to content

Commit 516a917

Browse files
hyperpolymathclaude
andcommitted
fix(ml): make training loop Zygote-differentiable and run
- Replace setdiff-based ranking_loss with BCE loss (differentiable) - Use Zygote.@ignore for label construction - Use hcat instead of setindex! for premise vectors (no mutation) - Pre-encode tokens outside gradient graph - Rename MultiHeadAttention to EchidnaMHA (avoids Flux name collision) - Fix NeuralSolver @functor to exclude vocabulary/config from traversal - Fix TheoremGraph type annotation for SimpleWeightedDiGraph - Fix compute_metrics to use cosine similarity scoring - Fix run_training.jl to use module-qualified CUDA/Flux references - Remove unused Transformers.jl dependency - Fix empty context/hypothesis handling in encoder Training runs: Epoch 1 loss 2.20→2.00→2.14 on 53 examples (CPU mode). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 4b63238 commit 516a917

4 files changed

Lines changed: 162 additions & 77 deletions

File tree

src/julia/models/encoder.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -533,12 +533,12 @@ function encode_proof_state(state::ProofState, vocab::ProverVocabulary, encoder:
533533
goal_tokens, goal_features = encode_goal(prover_enc, state.goal)
534534
goal_ids = encode_tokens(vocab, goal_tokens)
535535

536-
# Encode context
537-
context_tokens = reduce(vcat, [tokenize_text(c) for c in state.context])
536+
# Encode context (may be empty)
537+
context_tokens = isempty(state.context) ? String[] : reduce(vcat, [tokenize_text(c) for c in state.context])
538538
context_ids = encode_tokens(vocab, context_tokens)
539539

540-
# Encode hypotheses
541-
hyp_tokens = reduce(vcat, [tokenize_text(h) for h in state.hypotheses])
540+
# Encode hypotheses (may be empty)
541+
hyp_tokens = isempty(state.hypotheses) ? String[] : reduce(vcat, [tokenize_text(h) for h in state.hypotheses])
542542
hyp_ids = encode_tokens(vocab, hyp_tokens)
543543

544544
# Combine all tokens with separator

src/julia/models/neural_solver.jl

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ Nodes: Theorems, premises, proof states
3939
Edges: Dependencies, implications, similarity
4040
"""
4141
struct TheoremGraph
42-
graph::SimpleWeightedDiGraph{Int, Float32}
42+
graph::SimpleWeightedDiGraph
4343
node_features::Matrix{Float32} # (feature_dim, num_nodes)
4444
node_types::Vector{Symbol} # :theorem, :premise, :goal
4545
node_names::Vector{String}
@@ -286,7 +286,7 @@ struct PremiseRanker
286286
gnn_encoder::GNNEncoder
287287
goal_encoder::Dense
288288
premise_encoder::Dense
289-
cross_attention::MultiHeadAttention
289+
cross_attention # Our custom EchidnaMHA (not Flux's)
290290
score_mlp::Chain
291291
end
292292

@@ -298,7 +298,7 @@ function PremiseRanker(feature_dim::Int, hidden_dim::Int, num_gnn_layers::Int=4)
298298
goal_encoder = Dense(hidden_dim, hidden_dim)
299299
premise_encoder = Dense(hidden_dim, hidden_dim)
300300

301-
cross_attention = MultiHeadAttention(hidden_dim, 8)
301+
cross_attention = EchidnaMHA(hidden_dim, 8)
302302

303303
score_mlp = Chain(
304304
Dense(hidden_dim * 2, hidden_dim, relu),
@@ -339,30 +339,30 @@ function (ranker::PremiseRanker)(g::TheoremGraph)
339339
end
340340

341341
"""
342-
MultiHeadAttention
342+
EchidnaMHA
343343
344344
Standard multi-head attention mechanism.
345345
"""
346-
struct MultiHeadAttention
346+
struct EchidnaMHA
347347
num_heads::Int
348348
head_dim::Int
349349
qkv_proj::Dense
350350
out_proj::Dense
351351
dropout::Dropout
352352
end
353353

354-
Flux.@functor MultiHeadAttention
354+
Flux.@functor EchidnaMHA
355355

356-
function MultiHeadAttention(d_model::Int, num_heads::Int; dropout::Float32=0.1f0)
356+
function EchidnaMHA(d_model::Int, num_heads::Int; dropout::Float32=0.1f0)
357357
head_dim = d_model ÷ num_heads
358358
qkv_proj = Dense(d_model, 3 * d_model)
359359
out_proj = Dense(d_model, d_model)
360360
dropout_layer = Dropout(dropout)
361361

362-
return MultiHeadAttention(num_heads, head_dim, qkv_proj, out_proj, dropout_layer)
362+
return EchidnaMHA(num_heads, head_dim, qkv_proj, out_proj, dropout_layer)
363363
end
364364

365-
function (mha::MultiHeadAttention)(Q::AbstractMatrix, K::AbstractMatrix, V::AbstractMatrix)
365+
function (mha::EchidnaMHA)(Q::AbstractMatrix, K::AbstractMatrix, V::AbstractMatrix)
366366
# Q, K, V: (d_model, seq_len)
367367
seq_len_q = size(Q, 2)
368368
seq_len_kv = size(K, 2)
@@ -411,7 +411,8 @@ struct NeuralSolver
411411
config::EchidnaConfig
412412
end
413413

414-
Flux.@functor NeuralSolver
414+
# Only traverse neural network fields, not vocabulary/config
415+
Flux.@functor NeuralSolver (text_encoder, premise_ranker,)
415416

416417
"""
417418
create_solver(vocab::ProverVocabulary; config::EchidnaConfig=get_config())
@@ -529,5 +530,5 @@ end
529530

530531
export TheoremGraph, build_theorem_graph
531532
export GCNLayer, GraphAttentionLayer, GNNEncoder
532-
export PremiseRanker, MultiHeadAttention
533+
export PremiseRanker, EchidnaMHA
533534
export NeuralSolver, create_solver, save_solver, load_solver

src/julia/run_training.jl

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,19 +40,20 @@ using .EchidnaML
4040

4141
# Configure for available hardware
4242
println("Configuring...")
43-
if CUDA.functional()
43+
using CUDA, Flux
44+
has_gpu = CUDA.functional()
45+
if has_gpu
4446
println(" GPU: $(CUDA.device())")
45-
set_config!(device=gpu)
47+
EchidnaML.set_config!(device=Flux.gpu)
4648
else
4749
println(" GPU: not available (using CPU)")
48-
set_config!(device=cpu)
50+
EchidnaML.set_config!(device=Flux.cpu)
4951
end
5052

5153
# Use smaller model dimensions for CPU training
52-
config = get_config()
53-
if config.device === cpu
54+
if !has_gpu
5455
println(" Reducing model size for CPU training")
55-
set_config!(
56+
EchidnaML.set_config!(
5657
embedding_dim=128,
5758
hidden_dim=256,
5859
num_transformer_layers=2,
@@ -61,11 +62,11 @@ if config.device === cpu
6162
)
6263
end
6364

64-
println(" Embedding dim: $(get_config().embedding_dim)")
65-
println(" Hidden dim: $(get_config().hidden_dim)")
66-
println(" Transformer layers: $(get_config().num_transformer_layers)")
67-
println(" GNN layers: $(get_config().gnn_num_layers)")
68-
println(" Batch size: $(get_config().batch_size)")
65+
println(" Embedding dim: $(EchidnaML.get_config().embedding_dim)")
66+
println(" Hidden dim: $(EchidnaML.get_config().hidden_dim)")
67+
println(" Transformer layers: $(EchidnaML.get_config().num_transformer_layers)")
68+
println(" GNN layers: $(EchidnaML.get_config().gnn_num_layers)")
69+
println(" Batch size: $(EchidnaML.get_config().batch_size)")
6970
println()
7071

7172
# Load data

0 commit comments

Comments
 (0)