entrenar/finetune/instruct_pipeline/
mod.rs1mod accessors;
20mod backward;
21mod constructors;
22mod cuda_forward;
23mod cuda_init;
24mod generate;
25mod training;
26mod wgpu;
27
28#[cfg(all(test, feature = "cuda"))]
29mod eval_sync_probe;
30#[cfg(all(test, feature = "cuda"))]
31mod parity_probe;
32#[cfg(test)]
33mod tests;
34#[cfg(test)]
35mod tests_cov3;
36#[cfg(test)]
37mod tests_cov3b;
38
39use crate::lora::LoRALayer;
40use crate::optim::{clip_grad_norm_refs, AdamW, Optimizer};
41use crate::tokenizer::HfTokenizer;
42use crate::train::transformer_trainer::step_profiler::StepProfiler;
43use crate::transformer::{Transformer, TransformerConfig};
44use crate::Tensor;
45use std::path::{Path, PathBuf};
46
47#[cfg(feature = "cuda")]
48use crate::autograd::cuda_training::CudaTrainer;
49#[cfg(feature = "cuda")]
50use crate::gpu::guard::VramGuard;
51#[cfg(feature = "cuda")]
52use crate::transformer::{
53 CudaBlock, CudaBlockScratch, CudaLoraGradWorkspace, GpuLoraOptimizerState,
54};
55#[cfg(feature = "cuda")]
56use trueno_gpu::driver::GpuBuffer;
57
58#[derive(Debug, Clone)]
60pub struct InstructConfig {
61 pub lora_rank: usize,
63 pub lora_alpha: f32,
65 pub learning_rate: f32,
67 pub epochs: usize,
69 pub max_seq_len: usize,
71 pub gradient_clip_norm: Option<f32>,
73 pub quantize_nf4: bool,
78}
79
80impl Default for InstructConfig {
81 fn default() -> Self {
82 Self {
83 lora_rank: 16,
84 lora_alpha: 32.0,
85 learning_rate: 2e-4,
86 epochs: 3,
87 max_seq_len: 512,
88 gradient_clip_norm: Some(1.0),
89 quantize_nf4: false,
90 }
91 }
92}
93
94#[derive(Debug, Clone)]
96pub struct InstructStepResult {
97 pub loss: f32,
99 pub num_response_tokens: usize,
101 pub perplexity: f32,
103}
104
105#[derive(Debug, Clone)]
107pub struct InstructBatchResult {
108 pub avg_loss: f32,
110 pub total_response_tokens: usize,
112 pub perplexity: f32,
114 pub grad_norm: f32,
116}
117
118#[cfg(feature = "cuda")]
127pub(super) struct InstructGpuTrainingState {
128 layer_inputs: Vec<GpuBuffer<f32>>,
130 final_norm_weight: GpuBuffer<f32>,
132 blocks_output: GpuBuffer<f32>,
134 grad_buf_a: GpuBuffer<f32>,
136 grad_buf_b: GpuBuffer<f32>,
138 grad_final_norm_weight: GpuBuffer<f32>,
140 embed_transposed: GpuBuffer<f32>, embed_original: GpuBuffer<f32>, logits_buf: GpuBuffer<f32>,
144 grad_hidden_buf: GpuBuffer<f32>,
146 output_scratch: GpuBuffer<f32>,
148 grad_upload_buf: GpuBuffer<f32>,
150 fwd_scratch_a: GpuBuffer<f32>,
152 fwd_scratch_b: GpuBuffer<f32>,
154 lm_head_hidden_buf: GpuBuffer<f32>,
156 forward_graph_exec: Option<trueno_gpu::driver::CudaGraphExec>,
158 graph_cached_seq_len: usize,
159 backward_graph_state: Option<super::backward_graph::BackwardGraphState>,
161 cublas_workspace: Option<GpuBuffer<f32>>,
163 profiler_layer_fwd_us: Vec<u64>,
165 profiler_layer_bwd_us: Vec<u64>,
167 profiler_layer_start: Option<std::time::Instant>,
169 profiler_op_us: [u64; 16],
172 profiler_op_start: Option<std::time::Instant>,
174}
175
176pub struct InstructPipeline {
177 pub model: Transformer,
179 pub lora_layers: Vec<LoRALayer>,
181 pub config: InstructConfig,
183 optimizer: AdamW,
185 tokenizer: Option<HfTokenizer>,
187 model_dir: Option<PathBuf>,
189 pub profiler: StepProfiler,
192 #[cfg(feature = "cuda")]
194 cuda_trainer: Option<CudaTrainer>,
195 #[cfg(feature = "cuda")]
197 cuda_blocks: Option<Vec<CudaBlock>>,
198 #[cfg(feature = "cuda")]
200 shared_scratch: Option<CudaBlockScratch>,
201 #[cfg(feature = "cuda")]
203 #[allow(dead_code)]
204 cuda_nan_count: usize,
205 #[cfg(feature = "cuda")]
207 gpu_training: Option<InstructGpuTrainingState>,
208 #[cfg(feature = "cuda")]
210 cuda_lora_grad_workspace: Option<CudaLoraGradWorkspace>,
211 #[cfg(feature = "cuda")]
213 lora_fused_clip: Option<crate::autograd::cuda_optim::FusedClipState>,
214 #[cfg(feature = "cuda")]
216 cuda_lora_optimizer_states: Option<Vec<GpuLoraOptimizerState>>,
217 #[cfg(feature = "cuda")]
219 nf4_lora_step: u32,
220 #[cfg(feature = "cuda")]
222 #[allow(dead_code)]
223 vram_guard: Option<VramGuard>,
224 #[cfg(feature = "gpu")]
226 wgpu_training: Option<WgpuTrainingState>,
227}
228
229#[cfg(feature = "gpu")]
231struct WgpuTrainingState {
232 fwd: trueno::backends::gpu::WgslForwardPass,
234 cross_entropy: crate::autograd::wgpu_cross_entropy::WgslCrossEntropy,
235 trainer: crate::autograd::wgpu_training::WgpuTrainer,
236 logits_buf: trueno::backends::gpu::wgpu::Buffer,
238 labels_buf: trueno::backends::gpu::wgpu::Buffer,
239 losses_buf: trueno::backends::gpu::wgpu::Buffer,
240 logsumexp_buf: trueno::backends::gpu::wgpu::Buffer,
241 lm_head_gpu: trueno::backends::gpu::wgpu::Buffer,
243 lm_head_t_gpu: trueno::backends::gpu::wgpu::Buffer,
244 num_layers: usize,
246 hidden_dim: usize,
247 vocab_size: usize,
248}
249
250#[derive(Debug, Clone)]
252pub struct GenerateConfig {
253 pub max_new_tokens: usize,
255 pub temperature: f32,
257 pub top_k: usize,
259 pub stop_tokens: Vec<u32>,
261}
262
263fn sample_token(logits: &[f32], temperature: f32, top_k: usize) -> u32 {
265 if temperature <= 0.0 || top_k == 1 {
266 return logits
268 .iter()
269 .enumerate()
270 .max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
271 .map_or(0, |(idx, _)| idx as u32);
272 }
273
274 let scaled: Vec<f32> = logits.iter().map(|&l| l / temperature).collect();
276
277 let mut indices_and_logits: Vec<(usize, f32)> = scaled.iter().copied().enumerate().collect();
279 indices_and_logits
280 .sort_unstable_by(|(_, a), (_, b)| b.partial_cmp(a).unwrap_or(std::cmp::Ordering::Equal));
281
282 let k = if top_k > 0 && top_k < indices_and_logits.len() {
283 top_k
284 } else {
285 indices_and_logits.len()
286 };
287 let top = &indices_and_logits[..k];
288
289 let max_logit = top[0].1;
291 let exps: Vec<f32> = top.iter().map(|(_, l)| (l - max_logit).exp()).collect();
292 let sum: f32 = exps.iter().sum();
293 let probs: Vec<f32> = exps.iter().map(|e| e / sum).collect();
294
295 let r: f32 = simple_random();
297 let mut cumulative = 0.0;
298 for (i, &p) in probs.iter().enumerate() {
299 cumulative += p;
300 if r < cumulative {
301 return top[i].0 as u32;
302 }
303 }
304
305 top[0].0 as u32
307}
308
309fn simple_random() -> f32 {
312 use std::cell::Cell;
313 thread_local! {
314 static STATE: Cell<u64> = Cell::new(
315 std::time::SystemTime::now()
316 .duration_since(std::time::UNIX_EPOCH)
317 .map(|d| d.as_nanos() as u64)
318 .unwrap_or(42)
319 );
320 }
321 STATE.with(|s| {
322 let mut x = s.get();
324 x ^= x << 13;
325 x ^= x >> 7;
326 x ^= x << 17;
327 s.set(x);
328 (x >> 40) as f32 / (1u64 << 24) as f32
329 })
330}