dpv007 commited on
Commit
769f59f
·
verified ·
1 Parent(s): 887f8c4

Upload folder using huggingface_hub

Browse files
Files changed (5) hide show
  1. Dockerfile +4 -4
  2. config.yaml +28 -0
  3. model.py +116 -0
  4. neural_engine/src/main.rs +32 -30
  5. rl_train.py +7 -1
Dockerfile CHANGED
@@ -36,10 +36,10 @@ COPY --chown=user . $HOME/app
36
  RUN cd neural_engine && cargo build --release
37
 
38
  # Download all model weights from our model repository directly into the root folder
39
- RUN curl -L -o ./model.onnx "https://huggingface.co/dpv007/Neurex-Weights/resolve/main/model.onnx?v=4" || true
40
- RUN curl -L -o ./model.onnx.data "https://huggingface.co/dpv007/Neurex-Weights/resolve/main/model.onnx.data?v=4" || true
41
- RUN curl -L -o ./model_int8.onnx "https://huggingface.co/dpv007/Neurex-Weights/resolve/main/model_int8.onnx?v=4" || true
42
- RUN curl -L -o ./model_int8.onnx.data "https://huggingface.co/dpv007/Neurex-Weights/resolve/main/model_int8.onnx.data?v=4" || true
43
 
44
  # Prepare the Neurex_Engine folder
45
  RUN mkdir -p Neurex_Engine && \
 
36
  RUN cd neural_engine && cargo build --release
37
 
38
  # Download all model weights from our model repository directly into the root folder
39
+ RUN curl -L -o ./model.onnx "https://huggingface.co/dpv007/Neurex-Weights/resolve/main/model.onnx?v=5" || true
40
+ RUN curl -L -o ./model.onnx.data "https://huggingface.co/dpv007/Neurex-Weights/resolve/main/model.onnx.data?v=5" || true
41
+ RUN curl -L -o ./model_int8.onnx "https://huggingface.co/dpv007/Neurex-Weights/resolve/main/model_int8.onnx?v=5" || true
42
+ RUN curl -L -o ./model_int8.onnx.data "https://huggingface.co/dpv007/Neurex-Weights/resolve/main/model_int8.onnx.data?v=5" || true
43
 
44
  # Prepare the Neurex_Engine folder
45
  RUN mkdir -p Neurex_Engine && \
config.yaml ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ data:
2
+ url: "https://database.lichess.org/standard/lichess_db_standard_rated_2024-04.pgn.zst"
3
+ chunk_file: "dataset/stream_chunk.pgn"
4
+ games_per_chunk: 5000
5
+ max_length: 120
6
+ val_split: 0.1
7
+
8
+ model:
9
+ d_model: 512
10
+ nhead: 8
11
+ num_layers: 6
12
+
13
+ training:
14
+ epochs: 10
15
+ chunk_size: 50000
16
+ epochs_per_chunk: 2
17
+ batch_size: 32
18
+ max_lr: 0.0005
19
+ min_lr: 0.00001
20
+ warmup_steps: 50000 # 10% of max_steps
21
+ max_steps: 500000
22
+ val_every_n_steps: 100 # Checkpoint frequently so you never lose progress
23
+ patience: 50
24
+ scheduler_t0: 100000 # T0 aligned with max_steps
25
+ grad_accum_steps: 2
26
+ log_file: "dataset/fast_training_log.txt"
27
+ weights_path: "weights/chess_fast_best.pth"
28
+ latest_weights_path: "weights/chess_fast_latest.pth"
model.py ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+ import torch.nn.functional as F
4
+
5
+ class RotaryEmbedding(nn.Module):
6
+ def __init__(self, dim, max_seq_len=512):
7
+ super().__init__()
8
+ self.dim = dim
9
+ inv_freq = 1.0 / (10000 ** (torch.arange(0, dim, 2).float() / dim))
10
+ self.register_buffer("inv_freq", inv_freq, persistent=False)
11
+ t = torch.arange(max_seq_len, dtype=torch.float)
12
+ freqs = torch.outer(t, self.inv_freq)
13
+ emb = torch.cat((freqs, freqs), dim=-1)
14
+ self.register_buffer("cos_cached", emb.cos(), persistent=False)
15
+ self.register_buffer("sin_cached", emb.sin(), persistent=False)
16
+
17
+ def _rotate_half(self, x):
18
+ return torch.cat((-x[..., self.dim // 2:], x[..., :self.dim // 2]), dim=-1)
19
+
20
+ def forward(self, x, seq_len, offset=0):
21
+ cos = self.cos_cached[offset:offset+seq_len, :].unsqueeze(0).unsqueeze(0)
22
+ sin = self.sin_cached[offset:offset+seq_len, :].unsqueeze(0).unsqueeze(0)
23
+ return (x * cos) + (self._rotate_half(x) * sin)
24
+
25
+ class SwiGLU(nn.Module):
26
+ def __init__(self, d_model, d_ff):
27
+ super().__init__()
28
+ self.w1 = nn.Linear(d_model, d_ff, bias=False)
29
+ self.w2 = nn.Linear(d_model, d_ff, bias=False)
30
+ self.w3 = nn.Linear(d_ff, d_model, bias=False)
31
+
32
+ def forward(self, x):
33
+ return self.w3(F.silu(self.w1(x)) * self.w2(x))
34
+
35
+ class OptimizedAttention(nn.Module):
36
+ def __init__(self, d_model, nhead, rope, num_kv_heads=2):
37
+ super().__init__()
38
+ self.nhead, self.head_dim = nhead, d_model // nhead
39
+ self.num_kv_heads = num_kv_heads
40
+ self.num_queries_per_kv = nhead // num_kv_heads
41
+ self.rope = rope
42
+
43
+ self.q_proj = nn.Linear(d_model, d_model, bias=False)
44
+ self.k_proj = nn.Linear(d_model, num_kv_heads * self.head_dim, bias=False)
45
+ self.v_proj = nn.Linear(d_model, num_kv_heads * self.head_dim, bias=False)
46
+ self.out_proj = nn.Linear(d_model, d_model, bias=False)
47
+
48
+ def forward(self, x, past_key_value=None):
49
+ B, S, C = x.shape
50
+ q = self.q_proj(x).view(B, S, self.nhead, self.head_dim).transpose(1, 2)
51
+ k = self.k_proj(x).view(B, S, self.num_kv_heads, self.head_dim).transpose(1, 2)
52
+ v = self.v_proj(x).view(B, S, self.num_kv_heads, self.head_dim).transpose(1, 2)
53
+
54
+ offset = 0 if past_key_value is None else past_key_value[0].size(2)
55
+ q = self.rope(q, S, offset)
56
+ k = self.rope(k, S, offset)
57
+
58
+ if past_key_value is not None:
59
+ past_k, past_v = past_key_value
60
+ k = torch.cat([past_k, k], dim=2)
61
+ v = torch.cat([past_v, v], dim=2)
62
+
63
+ present_key_value = (k, v)
64
+
65
+ if self.num_queries_per_kv > 1:
66
+ k = torch.repeat_interleave(k, repeats=self.num_queries_per_kv, dim=1)
67
+ v = torch.repeat_interleave(v, repeats=self.num_queries_per_kv, dim=1)
68
+
69
+ attn_out = F.scaled_dot_product_attention(q, k, v, is_causal=True)
70
+ out = self.out_proj(attn_out.transpose(1, 2).contiguous().view(B, S, C))
71
+ return out, present_key_value
72
+
73
+ class EfficientDecoderLayer(nn.Module):
74
+ def __init__(self, d_model, nhead, d_ff, rope, num_kv_heads=2):
75
+ super().__init__()
76
+ self.attn = OptimizedAttention(d_model, nhead, rope, num_kv_heads)
77
+ self.ffn = SwiGLU(d_model, d_ff)
78
+ self.ln1, self.ln2 = nn.LayerNorm(d_model), nn.LayerNorm(d_model)
79
+
80
+ def forward(self, x, past_key_value=None):
81
+ attn_out, present_key_value = self.attn(self.ln1(x), past_key_value)
82
+ x = x + attn_out
83
+ return x + self.ffn(self.ln2(x)), present_key_value
84
+
85
+ class ChessTransformer(nn.Module):
86
+ def __init__(self, vocab_size, d_model=512, nhead=8, num_layers=6, max_length=120, num_kv_heads=2):
87
+ super().__init__()
88
+ self.token_embedding = nn.Embedding(vocab_size, d_model)
89
+ d_ff = int(2 * (d_model * 4) / 3)
90
+ self.rope = RotaryEmbedding(dim=d_model // nhead, max_seq_len=max_length)
91
+ self.layers = nn.ModuleList([EfficientDecoderLayer(d_model, nhead, d_ff, self.rope, num_kv_heads) for _ in range(num_layers)])
92
+ self.ln_final = nn.LayerNorm(d_model)
93
+ self.fc_out = nn.Linear(d_model, vocab_size, bias=False)
94
+ self.value_head = nn.Sequential(
95
+ nn.Linear(d_model, d_model // 2),
96
+ nn.SiLU(),
97
+ nn.Linear(d_model // 2, 1),
98
+ nn.Tanh()
99
+ )
100
+
101
+ def forward(self, src, past_key_values=None, use_cache=False):
102
+ x = self.token_embedding(src)
103
+ present_key_values = []
104
+ for i, layer in enumerate(self.layers):
105
+ past_kv = past_key_values[i] if past_key_values is not None else None
106
+ x, present_kv = layer(x, past_kv)
107
+ if use_cache:
108
+ present_key_values.append(present_kv)
109
+
110
+ hidden = self.ln_final(x)
111
+ policy_logits = self.fc_out(hidden)
112
+ value = self.value_head(hidden).squeeze(-1)
113
+
114
+ if use_cache:
115
+ return policy_logits, value, tuple(present_key_values)
116
+ return policy_logits, value
neural_engine/src/main.rs CHANGED
@@ -290,18 +290,18 @@ fn evaluate_onnx(
290
  let cache = if let Some(c) = root_cache { c } else { &empty_cache };
291
  let past_seq_len = cache.seq_len;
292
 
293
- let pk_0 = ort::value::Tensor::from_array((vec![1, 8, past_seq_len, 64], cache.layers[0].0.clone())).unwrap();
294
- let pv_0 = ort::value::Tensor::from_array((vec![1, 8, past_seq_len, 64], cache.layers[0].1.clone())).unwrap();
295
- let pk_1 = ort::value::Tensor::from_array((vec![1, 8, past_seq_len, 64], cache.layers[1].0.clone())).unwrap();
296
- let pv_1 = ort::value::Tensor::from_array((vec![1, 8, past_seq_len, 64], cache.layers[1].1.clone())).unwrap();
297
- let pk_2 = ort::value::Tensor::from_array((vec![1, 8, past_seq_len, 64], cache.layers[2].0.clone())).unwrap();
298
- let pv_2 = ort::value::Tensor::from_array((vec![1, 8, past_seq_len, 64], cache.layers[2].1.clone())).unwrap();
299
- let pk_3 = ort::value::Tensor::from_array((vec![1, 8, past_seq_len, 64], cache.layers[3].0.clone())).unwrap();
300
- let pv_3 = ort::value::Tensor::from_array((vec![1, 8, past_seq_len, 64], cache.layers[3].1.clone())).unwrap();
301
- let pk_4 = ort::value::Tensor::from_array((vec![1, 8, past_seq_len, 64], cache.layers[4].0.clone())).unwrap();
302
- let pv_4 = ort::value::Tensor::from_array((vec![1, 8, past_seq_len, 64], cache.layers[4].1.clone())).unwrap();
303
- let pk_5 = ort::value::Tensor::from_array((vec![1, 8, past_seq_len, 64], cache.layers[5].0.clone())).unwrap();
304
- let pv_5 = ort::value::Tensor::from_array((vec![1, 8, past_seq_len, 64], cache.layers[5].1.clone())).unwrap();
305
 
306
  let outputs = session.run(ort::inputs![
307
  "input_ids" => input_value,
@@ -356,18 +356,18 @@ fn compute_root_kv_cache(
356
  let seq_len = seq.len();
357
  let input_value = ort::value::Tensor::from_array((vec![1, seq_len], seq)).unwrap();
358
 
359
- let pk_0 = ort::value::Tensor::from_array(ndarray::Array4::<f32>::zeros((1, 8, 0, 64))).unwrap();
360
- let pv_0 = ort::value::Tensor::from_array(ndarray::Array4::<f32>::zeros((1, 8, 0, 64))).unwrap();
361
- let pk_1 = ort::value::Tensor::from_array(ndarray::Array4::<f32>::zeros((1, 8, 0, 64))).unwrap();
362
- let pv_1 = ort::value::Tensor::from_array(ndarray::Array4::<f32>::zeros((1, 8, 0, 64))).unwrap();
363
- let pk_2 = ort::value::Tensor::from_array(ndarray::Array4::<f32>::zeros((1, 8, 0, 64))).unwrap();
364
- let pv_2 = ort::value::Tensor::from_array(ndarray::Array4::<f32>::zeros((1, 8, 0, 64))).unwrap();
365
- let pk_3 = ort::value::Tensor::from_array(ndarray::Array4::<f32>::zeros((1, 8, 0, 64))).unwrap();
366
- let pv_3 = ort::value::Tensor::from_array(ndarray::Array4::<f32>::zeros((1, 8, 0, 64))).unwrap();
367
- let pk_4 = ort::value::Tensor::from_array(ndarray::Array4::<f32>::zeros((1, 8, 0, 64))).unwrap();
368
- let pv_4 = ort::value::Tensor::from_array(ndarray::Array4::<f32>::zeros((1, 8, 0, 64))).unwrap();
369
- let pk_5 = ort::value::Tensor::from_array(ndarray::Array4::<f32>::zeros((1, 8, 0, 64))).unwrap();
370
- let pv_5 = ort::value::Tensor::from_array(ndarray::Array4::<f32>::zeros((1, 8, 0, 64))).unwrap();
371
 
372
  let outputs = session.run(ort::inputs![
373
  "input_ids" => input_value,
@@ -694,12 +694,14 @@ fn main() {
694
  // Time Advantage Scaling
695
  if time_left > 0 && opp_time > 0 {
696
  let ratio = time_left as f64 / opp_time as f64;
697
- if ratio > 1.5 {
698
- // We have >50% more time than opponent, spend 30% more time to press the advantage
699
- movetime_ms = (movetime_ms as f64 * 1.3) as u64;
700
- } else if ratio < 0.6 {
701
- // We are behind on time, play faster to catch up
702
- movetime_ms = (movetime_ms as f64 * 0.85) as u64;
 
 
703
  }
704
  }
705
 
 
290
  let cache = if let Some(c) = root_cache { c } else { &empty_cache };
291
  let past_seq_len = cache.seq_len;
292
 
293
+ let pk_0 = ort::value::Tensor::from_array((vec![1, 2, past_seq_len, 64], cache.layers[0].0.clone())).unwrap();
294
+ let pv_0 = ort::value::Tensor::from_array((vec![1, 2, past_seq_len, 64], cache.layers[0].1.clone())).unwrap();
295
+ let pk_1 = ort::value::Tensor::from_array((vec![1, 2, past_seq_len, 64], cache.layers[1].0.clone())).unwrap();
296
+ let pv_1 = ort::value::Tensor::from_array((vec![1, 2, past_seq_len, 64], cache.layers[1].1.clone())).unwrap();
297
+ let pk_2 = ort::value::Tensor::from_array((vec![1, 2, past_seq_len, 64], cache.layers[2].0.clone())).unwrap();
298
+ let pv_2 = ort::value::Tensor::from_array((vec![1, 2, past_seq_len, 64], cache.layers[2].1.clone())).unwrap();
299
+ let pk_3 = ort::value::Tensor::from_array((vec![1, 2, past_seq_len, 64], cache.layers[3].0.clone())).unwrap();
300
+ let pv_3 = ort::value::Tensor::from_array((vec![1, 2, past_seq_len, 64], cache.layers[3].1.clone())).unwrap();
301
+ let pk_4 = ort::value::Tensor::from_array((vec![1, 2, past_seq_len, 64], cache.layers[4].0.clone())).unwrap();
302
+ let pv_4 = ort::value::Tensor::from_array((vec![1, 2, past_seq_len, 64], cache.layers[4].1.clone())).unwrap();
303
+ let pk_5 = ort::value::Tensor::from_array((vec![1, 2, past_seq_len, 64], cache.layers[5].0.clone())).unwrap();
304
+ let pv_5 = ort::value::Tensor::from_array((vec![1, 2, past_seq_len, 64], cache.layers[5].1.clone())).unwrap();
305
 
306
  let outputs = session.run(ort::inputs![
307
  "input_ids" => input_value,
 
356
  let seq_len = seq.len();
357
  let input_value = ort::value::Tensor::from_array((vec![1, seq_len], seq)).unwrap();
358
 
359
+ let pk_0 = ort::value::Tensor::from_array(ndarray::Array4::<f32>::zeros((1, 2, 0, 64))).unwrap();
360
+ let pv_0 = ort::value::Tensor::from_array(ndarray::Array4::<f32>::zeros((1, 2, 0, 64))).unwrap();
361
+ let pk_1 = ort::value::Tensor::from_array(ndarray::Array4::<f32>::zeros((1, 2, 0, 64))).unwrap();
362
+ let pv_1 = ort::value::Tensor::from_array(ndarray::Array4::<f32>::zeros((1, 2, 0, 64))).unwrap();
363
+ let pk_2 = ort::value::Tensor::from_array(ndarray::Array4::<f32>::zeros((1, 2, 0, 64))).unwrap();
364
+ let pv_2 = ort::value::Tensor::from_array(ndarray::Array4::<f32>::zeros((1, 2, 0, 64))).unwrap();
365
+ let pk_3 = ort::value::Tensor::from_array(ndarray::Array4::<f32>::zeros((1, 2, 0, 64))).unwrap();
366
+ let pv_3 = ort::value::Tensor::from_array(ndarray::Array4::<f32>::zeros((1, 2, 0, 64))).unwrap();
367
+ let pk_4 = ort::value::Tensor::from_array(ndarray::Array4::<f32>::zeros((1, 2, 0, 64))).unwrap();
368
+ let pv_4 = ort::value::Tensor::from_array(ndarray::Array4::<f32>::zeros((1, 2, 0, 64))).unwrap();
369
+ let pk_5 = ort::value::Tensor::from_array(ndarray::Array4::<f32>::zeros((1, 2, 0, 64))).unwrap();
370
+ let pv_5 = ort::value::Tensor::from_array(ndarray::Array4::<f32>::zeros((1, 2, 0, 64))).unwrap();
371
 
372
  let outputs = session.run(ort::inputs![
373
  "input_ids" => input_value,
 
694
  // Time Advantage Scaling
695
  if time_left > 0 && opp_time > 0 {
696
  let ratio = time_left as f64 / opp_time as f64;
697
+ if ratio > 2.0 {
698
+ movetime_ms = (movetime_ms as f64 * 1.5) as u64;
699
+ } else if ratio > 1.2 {
700
+ movetime_ms = (movetime_ms as f64 * 1.25) as u64;
701
+ } else if ratio < 0.8 {
702
+ movetime_ms = (movetime_ms as f64 * 0.9) as u64;
703
+ } else if ratio < 0.5 {
704
+ movetime_ms = (movetime_ms as f64 * 0.75) as u64;
705
  }
706
  }
707
 
rl_train.py CHANGED
@@ -284,7 +284,13 @@ def learner_worker():
284
  log_prob = F.log_softmax(p_logits, dim=-1)
285
  action_log_probs = log_prob[torch.arange(batch_size), a_batch]
286
 
287
- policy_loss = -(action_log_probs * adv_batch).mean()
 
 
 
 
 
 
288
  value_loss = F.mse_loss(v_pred, sf_val_batch)
289
 
290
  loss = policy_loss + 0.5 * value_loss
 
284
  log_prob = F.log_softmax(p_logits, dim=-1)
285
  action_log_probs = log_prob[torch.arange(batch_size), a_batch]
286
 
287
+ # CRITICAL FIX: Only train on positive advantages (Advantage-Weighted Behavioral Cloning).
288
+ # If we allow negative advantages, the optimizer pushes log_prob to negative infinity,
289
+ # causing the policy_loss to explode into massive negative numbers (e.g. -59.7) and
290
+ # completely destroying the Neural Network's weights (including the Value Head).
291
+ positive_adv = torch.clamp(adv_batch, min=0.0)
292
+ policy_loss = -(action_log_probs * positive_adv).mean()
293
+
294
  value_loss = F.mse_loss(v_pred, sf_val_batch)
295
 
296
  loss = policy_loss + 0.5 * value_loss