diff --git a/profile_gpt2.cu b/profile_gpt2.cu index 29fbdb9..f5efed8 100644 --- a/profile_gpt2.cu +++ b/profile_gpt2.cu @@ -61,7 +61,7 @@ int main(int argc, char *argv[]) { // do a training step gpt2_forward(&model, x, B, T); gpt2_zero_grad(&model); - gpt2_backward_and_reduce(&model, x, y, 1, true); + gpt2_backward_and_reduce(&model, x, y, 1, 0); float grad_norm = gpt2_calculate_grad_norm(&model, &multi_gpu_config); float grad_scale = (grad_norm > 1.0f) ? 1.0f / grad_norm : 1.0f; gpt2_update(&model, 1e-4f, 0.9f, 0.999f, 1e-8f, 0.0f, grad_scale, 1, &multi_gpu_config); diff --git a/test_gpt2.cu b/test_gpt2.cu index 800df69..d7464bf 100644 --- a/test_gpt2.cu +++ b/test_gpt2.cu @@ -218,7 +218,7 @@ int main(int argc, char *argv[]) { clock_gettime(CLOCK_MONOTONIC, &start); gpt2_forward(&model, x, B, T); gpt2_zero_grad(&model); - gpt2_backward_and_reduce(&model, x, y, 1, true); + gpt2_backward_and_reduce(&model, x, y, 1, 0); clock_gettime(CLOCK_MONOTONIC, &end); double time_elapsed_s = (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9; @@ -336,7 +336,7 @@ int main(int argc, char *argv[]) { dataloader_next_batch(&loader); gpt2_forward(&model, loader.inputs, B, T); gpt2_zero_grad(&model); - gpt2_backward_and_reduce(&model, loader.inputs, loader.targets, 1, true); + gpt2_backward_and_reduce(&model, loader.inputs, loader.targets, 1, 0); gpt2_update(&model, 1e-4f, 0.9f, 0.95f, 1e-8f, 0.0f, 1.0f, step+11, &multi_gpu_config); losses[step] = model.mean_loss; tokens[step] = loader.inputs[0]; @@ -351,7 +351,7 @@ int main(int argc, char *argv[]) { dataloader_next_batch(&loader); gpt2_forward(&model, loader.inputs, B, T); gpt2_zero_grad(&model); - gpt2_backward_and_reduce(&model, loader.inputs, loader.targets, 1, true); + gpt2_backward_and_reduce(&model, loader.inputs, loader.targets, 1, 0); gpt2_update(&model, 1e-4f, 0.9f, 0.95f, 1e-8f, 0.0f, 1.0f, step+11, &multi_gpu_config); if(loader.inputs[0] != tokens[step]) { diff --git a/train_gpt2.cu b/train_gpt2.cu index b89a27d..246d413 100644 --- a/train_gpt2.cu +++ b/train_gpt2.cu @@ -740,10 +740,11 @@ float gpt2_validate(GPT2 *model, const int* inputs, const int* targets, size_t B return mean_loss; } - void gpt2_zero_grad(GPT2 *model) { NVTX_RANGE_FN(); - // the losses accumulate over the duration of gradient accumulation micro steps, also reset here + // there are currently two state vars during the gradient accumulation inner loop: + // 1) the losses accumulate += into acts.losses, reset here + // 2) the gradients accumulate += into grads_memory, reset here cudaCheck(cudaMemset(model->acts.losses, 0, model->batch_size * model->seq_len * sizeof(float))); if (model->grads_memory != NULL) { cudaCheck(cudaMemset(model->grads_memory, 0, model->num_parameters * sizeof(floatX))); @@ -751,8 +752,14 @@ void gpt2_zero_grad(GPT2 *model) { cudaCheck(cudaDeviceSynchronize()); } -void gpt2_backward_and_reduce(GPT2 *model, int* inputs, const int* targets, int grad_accum_steps, bool last_step) { +void gpt2_backward_and_reduce(GPT2 *model, int* inputs, const int* targets, int grad_accum_steps, int micro_step) { NVTX_RANGE_FN(); + bool last_step = micro_step == grad_accum_steps - 1; + + // on the first micro-step zero the gradients, as we're about to += accumulate into them + if (micro_step == 0) { + gpt2_zero_grad(model); + } // lazily allocate the memory for gradients of the weights and activations, if needed if (model->grads_memory == NULL) { @@ -760,8 +767,6 @@ void gpt2_backward_and_reduce(GPT2 *model, int* inputs, const int* targets, int // allocate buffers for weight gradients printf0("allocating %d MiB for parameter gradients\n", (int)round(model->num_parameters * sizeof(floatX) / (1024 * 1024))); model->grads_memory = malloc_and_point_parameters(&model->grads, model->param_elements, model->param_sizeof); - // init gradients of parameters and activations to zero - gpt2_zero_grad(model); // initialise cpu scratch buffers for encoder backward size_t num_c_groups = CEIL_DIV(model->config.channels, (WARP_SIZE * x128::size)); assert((size_t)(model->batch_size * model->seq_len) * num_c_groups < (1ULL<<31ULL)); // todo - maybe an issue for llama3-400B(?) @@ -1795,7 +1800,7 @@ int main(int argc, char *argv[]) { // forward pass. note that we pass in grad_accum_steps, which scales down the loss gpt2_forward(&model, train_loader.inputs, B, T); // backward pass. all model params accumulate gradients with += inside this inner loop - gpt2_backward_and_reduce(&model, train_loader.inputs, train_loader.targets, grad_accum_steps, micro_step == grad_accum_steps - 1); + gpt2_backward_and_reduce(&model, train_loader.inputs, train_loader.targets, grad_accum_steps, micro_step); } float zloss = (float)(update_detector(&loss_outlier_detector, (double)model.mean_loss)); // loss z-score // fetch the next learning rate @@ -1814,8 +1819,6 @@ int main(int argc, char *argv[]) { float grad_scale = (grad_norm > grad_clip) ? grad_clip / grad_norm : 1.0f; gpt2_update(&model, step_learning_rate, 0.9f, 0.95f, 1e-8f, weight_decay, grad_scale, step+1, &multi_gpu_config); } - // zero out the gradients for the next iteration - gpt2_zero_grad(&model); cudaCheck(cudaEventRecord(end)); cudaCheck(cudaEventSynchronize(end)); // wait for the end event to finish to get correct timings // --------------- TRAINING SECTION END ------------------- diff --git a/train_gpt2.py b/train_gpt2.py index e5974f7..b9dee87 100644 --- a/train_gpt2.py +++ b/train_gpt2.py @@ -698,10 +698,6 @@ if __name__ == "__main__": write_state(model, x, y, logits, loss, f"gpt2_{model_size_str}_debug_state.bin") # reset the train_loader for the optimization below train_loader.reset() - # clear the grads here explicitly because otherwise we'd have a duplicate grad accumulation - # since in the training loop we do a backward() and then zero_grad() at the end of the loop - # this would cause an incorrect first training step - model.zero_grad() # ------------------------------------------------------------------------- # main training loop @@ -794,6 +790,7 @@ if __name__ == "__main__": # --------------- TRAINING SECTION BEGIN ----------------- model.train() + optimizer.zero_grad(set_to_none=True) # if we are trying to overfit a single batch, we reset the loader here if args.overfit_single_batch: train_loader.reset() @@ -830,7 +827,6 @@ if __name__ == "__main__": param_group['lr'] = lr # step the optimizer optimizer.step() - optimizer.zero_grad(set_to_none=True) # --------------- TRAINING SECTION END ------------------- # everything that follows now is just diagnostics, prints, logging, etc.