| |
| |
| |
| |
| @@ -159,6 +159,24 @@ extern "C" { |
| |
| GGML_API const char * ggml_opt_optimizer_name(enum ggml_opt_optimizer_type); |
| |
| + |
| +// PRISM_STEP10_GGML_OPT_API_BEGIN |
| +// Step 10 uses GGML only for deterministic forward/backward. AdamW state and |
| +// updates are managed by the native packed-Q1 LoRA trainer so they can be |
| +// checkpointed exactly, clipped, accumulated, and resumed mid-window. |
| +GGML_API void ggml_opt_configure_gradient_only( |
| + ggml_opt_context_t opt_ctx, |
| + int32_t opt_period); |
| +GGML_API void ggml_opt_reset_gradient_cycle(ggml_opt_context_t opt_ctx); |
| +GGML_API void ggml_opt_zero_grad_accumulators(ggml_opt_context_t opt_ctx); |
| +GGML_API struct ggml_tensor * ggml_opt_grad_from_active_graph( |
| + ggml_opt_context_t opt_ctx, |
| + struct ggml_tensor * node); |
| +GGML_API struct ggml_tensor * ggml_opt_grad_acc_from_active_graph( |
| + ggml_opt_context_t opt_ctx, |
| + struct ggml_tensor * node); |
| +// PRISM_STEP10_GGML_OPT_API_END |
| + |
| // |
| |
| GGML_API ggml_opt_result_t ggml_opt_result_init(void); |
| |
| |
| |
| |
| @@ -11,6 +11,9 @@ |
| #include <algorithm> |
| #include <cfloat> |
| #include <cmath> |
| +#include <cstdio> |
| +#include <cstdlib> |
| +#include <cstring> |
| |
| // ggml_compute_forward_dup |
| |
| @@ -1156,45 +1159,38 @@ void ggml_compute_forward_add1( |
| static void ggml_compute_forward_acc_f32( |
| const ggml_compute_params * params, |
| ggml_tensor * dst) { |
| - |
| + // PRISM_Q1_LORA_STRIDED_CPU_ACC_V1 |
| const ggml_tensor * src0 = dst->src[0]; |
| const ggml_tensor * src1 = dst->src[1]; |
| |
| + GGML_ASSERT(src0->type == GGML_TYPE_F32); |
| + GGML_ASSERT(src1->type == GGML_TYPE_F32); |
| + GGML_ASSERT(dst->type == GGML_TYPE_F32); |
| GGML_ASSERT(ggml_are_same_shape(src0, dst)); |
| GGML_ASSERT(ggml_is_contiguous(dst) && ggml_is_contiguous(src0)); |
| |
| - // view src0 and dst with these strides and data offset inbytes during acc |
| - // nb0 is implicitly element_size because src0 and dst are contiguous |
| - size_t nb1 = ((int32_t *) dst->op_params)[0]; |
| - size_t nb2 = ((int32_t *) dst->op_params)[1]; |
| - size_t nb3 = ((int32_t *) dst->op_params)[2]; |
| - size_t offset = ((int32_t *) dst->op_params)[3]; |
| - bool inplace = (bool) ((int32_t *) dst->op_params)[4]; |
| + size_t nb1 = ((int32_t *) dst->op_params)[0]; |
| + size_t nb2 = ((int32_t *) dst->op_params)[1]; |
| + size_t nb3 = ((int32_t *) dst->op_params)[2]; |
| + size_t offset = ((int32_t *) dst->op_params)[3]; |
| + bool inplace = (bool) ((int32_t *) dst->op_params)[4]; |
| |
| if (!inplace) { |
| if (params->ith == 0) { |
| - // memcpy needs to be synchronized across threads to avoid race conditions. |
| - // => do it in INIT phase |
| - memcpy( |
| - ((char *) dst->data), |
| - ((char *) src0->data), |
| - ggml_nbytes(dst)); |
| + memcpy((char *) dst->data, (char *) src0->data, ggml_nbytes(dst)); |
| } |
| ggml_barrier(params->threadpool); |
| } |
| |
| const int ith = params->ith; |
| const int nth = params->nth; |
| - |
| const int nr = ggml_nrows(src1); |
| const int nc = src1->ne[0]; |
| |
| GGML_TENSOR_LOCALS(int64_t, ne1, src1, ne) |
| GGML_TENSOR_LOCALS(size_t, nb1, src1, nb) |
| |
| - // src0 and dst as viewed during acc |
| const size_t nb0 = ggml_element_size(src0); |
| - |
| const size_t nb00 = nb0; |
| const size_t nb01 = nb1; |
| const size_t nb02 = nb2; |
| @@ -1202,34 +1198,54 @@ static void ggml_compute_forward_acc_f32( |
| |
| GGML_ASSERT(offset + (ne10 == 0 ? 0 : ne10-1)*nb0 + (ne11 == 0 ? 0 : ne11-1)*nb1 + (ne12 == 0 ? 0 : ne12-1)*nb2 + (ne13 == 0 ? 0 : ne13-1)*nb3 < ggml_nbytes(dst)); |
| GGML_ASSERT(offset + (ne10 == 0 ? 0 : ne10-1)*nb00 + (ne11 == 0 ? 0 : ne11-1)*nb01 + (ne12 == 0 ? 0 : ne12-1)*nb02 + (ne13 == 0 ? 0 : ne13-1)*nb03 < ggml_nbytes(src0)); |
| + GGML_ASSERT((ne10 == 0 ? 0 : ne10-1)*nb10 + (ne11 == 0 ? 0 : ne11-1)*nb11 + (ne12 == 0 ? 0 : ne12-1)*nb12 + (ne13 == 0 ? 0 : ne13-1)*nb13 < ggml_nbytes(src1)); |
| |
| - GGML_ASSERT(nb10 == sizeof(float)); |
| + const bool src1_dim0_contiguous = nb10 == sizeof(float); |
| + const bool prism_training = std::getenv("PRISM_Q1_LORA_TRAINING") != nullptr; |
| + if (!src1_dim0_contiguous) { |
| + GGML_ASSERT(prism_training); |
| + if (ith == 0) { |
| + static int prism_strided_acc_count = 0; |
| + if (prism_strided_acc_count < 128) { |
| + std::fprintf(stderr, |
| + "PRISM_Q1_LORA_STRIDED_CPU_ACC count=%d dst_name='%s' src1_name='%s' " |
| + "src1_op=%s src1_shape=[%lld,%lld,%lld,%lld] " |
| + "src1_strides=[%zu,%zu,%zu,%zu]\n", |
| + prism_strided_acc_count + 1, dst->name, src1->name, ggml_op_name(src1->op), |
| + (long long) src1->ne[0], (long long) src1->ne[1], |
| + (long long) src1->ne[2], (long long) src1->ne[3], |
| + src1->nb[0], src1->nb[1], src1->nb[2], src1->nb[3]); |
| + } |
| + prism_strided_acc_count++; |
| + } |
| + } |
| |
| - // rows per thread |
| const int dr = (nr + nth - 1)/nth; |
| - |
| - // row range for this thread |
| const int ir0 = dr*ith; |
| const int ir1 = MIN(ir0 + dr, nr); |
| |
| for (int ir = ir0; ir < ir1; ++ir) { |
| - // src0 and dst are viewed with shape of src1 and offset |
| - // => same indices |
| const int i3 = ir/(ne12*ne11); |
| const int i2 = (ir - i3*ne12*ne11)/ne11; |
| - const int i1 = (ir - i3*ne12*ne11 - i2*ne11); |
| + const int i1 = ir - i3*ne12*ne11 - i2*ne11; |
| + |
| + float * dst_row = (float *) ((char *) dst->data + i3*nb3 + i2*nb2 + i1*nb1 + offset); |
| + const float * src0_row = (const float *) ((const char *) src0->data + i3*nb03 + i2*nb02 + i1*nb01 + offset); |
| + const char * src1_row = (const char *) src1->data + i3*nb13 + i2*nb12 + i1*nb11; |
| |
| + if (src1_dim0_contiguous) { |
| #ifdef GGML_USE_ACCELERATE |
| - vDSP_vadd( |
| - (float *) ((char *) src0->data + i3*nb03 + i2*nb02 + i1*nb01 + offset), 1, |
| - (float *) ((char *) src1->data + i3*nb13 + i2*nb12 + i1*nb11), 1, |
| - (float *) ((char *) dst->data + i3*nb3 + i2*nb2 + i1*nb1 + offset), 1, nc); |
| + vDSP_vadd(src0_row, 1, (const float *) src1_row, 1, dst_row, 1, nc); |
| #else |
| - ggml_vec_add_f32(nc, |
| - (float *) ((char *) dst->data + i3*nb3 + i2*nb2 + i1*nb1 + offset), |
| - (float *) ((char *) src0->data + i3*nb03 + i2*nb02 + i1*nb01 + offset), |
| - (float *) ((char *) src1->data + i3*nb13 + i2*nb12 + i1*nb11)); |
| + ggml_vec_add_f32(nc, dst_row, src0_row, (const float *) src1_row); |
| #endif |
| + } else { |
| + for (int i0 = 0; i0 < nc; ++i0) { |
| + float value; |
| + memcpy(&value, src1_row + (size_t) i0*nb10, sizeof(value)); |
| + dst_row[i0] = src0_row[i0] + value; |
| + } |
| + } |
| } |
| } |
| |
| |
| |
| |
| |
| @@ -29,6 +29,7 @@ |
| #include "ggml-cuda/im2col.cuh" |
| #include "ggml-cuda/mmf.cuh" |
| #include "ggml-cuda/mmq.cuh" |
| +#include "ggml-cuda/mulmat-q1-f32.cuh" |
| #include "ggml-cuda/mmvf.cuh" |
| #include "ggml-cuda/mmvq.cuh" |
| #include "ggml-cuda/norm.cuh" |
| @@ -2542,6 +2543,32 @@ bool ggml_cuda_mul_mat_q1_hopper(ggml_backend_cuda_context & ctx, const ggml_ten |
| static void ggml_cuda_mul_mat(ggml_backend_cuda_context & ctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst) { |
| const bool split = ggml_backend_buft_is_cuda_split(src0->buffer->buft); |
| |
| + // PRISM_Q1_EXACT_TRAIN_FORWARD_V1 |
| + // GGML_PREC_F32 is the explicit selector for the |
| + // mathematically exact packed-Q1 training path. |
| + // Default precision retains the existing inference |
| + // MMQ/MMVQ implementation. |
| + const int32_t precision = |
| + ggml_get_op_params_i32(dst, 0); |
| + |
| + if ( |
| + !split |
| + && precision == GGML_PREC_F32 |
| + && src0->type == GGML_TYPE_Q1_0 |
| + && src1->type == GGML_TYPE_F32 |
| + && dst->type == GGML_TYPE_F32 |
| + && !ggml_is_transposed(src0) |
| + && !ggml_is_transposed(src1) |
| + ) { |
| + ggml_cuda_mul_mat_q1_f32_exact( |
| + ctx, |
| + src0, |
| + src1, |
| + dst); |
| + |
| + return; |
| + } |
| + |
| // If src0 is a temporary compute buffer it may have some padding that needs to be cleared for mul_mat_vec_q or mul_mat_q. |
| // But if src0 is also a view of another tensor then this cannot be done safely because it may overwrite valid tensor data. |
| // Therefore, in such cases use cuBLAS. |
| @@ -5188,7 +5215,16 @@ static bool ggml_backend_cuda_device_supports_op(ggml_backend_dev_t dev, const g |
| } |
| } break; |
| case GGML_OP_OUT_PROD: |
| - return op->type == GGML_TYPE_F32 && op->src[0]->type == GGML_TYPE_F32 && op->src[1]->type == GGML_TYPE_F32; |
| + // PRISM_Q1_EXACT_TRAIN_BACKWARD_V1 |
| + return |
| + op->type == GGML_TYPE_F32 |
| + && op->src[1]->type == GGML_TYPE_F32 |
| + && ( |
| + op->src[0]->type == GGML_TYPE_F32 |
| + || ( |
| + op->src[0]->type == GGML_TYPE_Q1_0 |
| + && !ggml_is_transposed(op->src[0]) |
| + && op->src[0]->ne[0] % QK1_0 == 0)); |
| case GGML_OP_GET_ROWS: |
| { |
| switch (op->src[0]->type) { |
| |
| |
| |
| |
| @@ -1,83 +1,565 @@ |
| #include "out-prod.cuh" |
| |
| +#include <algorithm> |
| #include <cstdint> |
| +#include <limits> |
| |
| -void ggml_cuda_out_prod(ggml_backend_cuda_context & ctx, ggml_tensor * dst) { |
| - const ggml_tensor * src0 = dst->src[0]; |
| - const ggml_tensor * src1 = dst->src[1]; |
| +// PRISM_Q1_EXACT_TRAIN_BACKWARD_V1 |
| +// |
| +// Frozen packed-Q1 MUL_MAT backward requires: |
| +// |
| +// dX = OUT_PROD(W_q1, transpose(dY)) |
| +// |
| +// This implementation computes only dX. It never creates a Q1 |
| +// weight gradient and never expands the full packed matrix. |
| + |
| +static __device__ __forceinline__ float q1_out_prod_value( |
| + const char * row, |
| + const size_t nb00, |
| + const int64_t element_index) { |
| + const int64_t block_index = |
| + element_index / QK1_0; |
| + |
| + const int quant_index = |
| + static_cast<int>( |
| + element_index % QK1_0); |
| + |
| + const block_q1_0 * block = |
| + reinterpret_cast< |
| + const block_q1_0 *>( |
| + row |
| + + block_index |
| + * nb00); |
| + |
| + const uint8_t packed = |
| + block->qs[ |
| + quant_index >> 3 |
| + ]; |
| + |
| + const int bit = |
| + ( |
| + packed |
| + >> ( |
| + quant_index |
| + & 7) |
| + ) |
| + & 1; |
| + |
| + const float scale = |
| + __half2float( |
| + block->d); |
| + |
| + return bit |
| + ? scale |
| + : -scale; |
| +} |
| + |
| + |
| +static __global__ void out_prod_q1_f32_kernel( |
| + const char * __restrict__ src0, |
| + const char * __restrict__ src1, |
| + char * __restrict__ dst, |
| + |
| + const int64_t ne0, |
| + const int64_t ne1, |
| + const int64_t ne01, |
| + const int64_t ne2, |
| + const int64_t ne3, |
| + |
| + const size_t nb00, |
| + const size_t nb01, |
| + const size_t nb02, |
| + const size_t nb03, |
| + |
| + const size_t nb10, |
| + const size_t nb11, |
| + const size_t nb12, |
| + const size_t nb13, |
| + |
| + const size_t nb0, |
| + const size_t nb1, |
| + const size_t nb2, |
| + const size_t nb3, |
| + |
| + const int64_t dps2, |
| + const int64_t dps3) { |
| + const int64_t total = |
| + ne0 |
| + * ne1 |
| + * ne2 |
| + * ne3; |
| + |
| + const int64_t grid_stride = |
| + static_cast<int64_t>( |
| + blockDim.x) |
| + * static_cast<int64_t>( |
| + gridDim.x); |
| + |
| + for ( |
| + int64_t linear = |
| + static_cast<int64_t>( |
| + blockIdx.x) |
| + * blockDim.x |
| + + threadIdx.x; |
| + |
| + linear < total; |
| + |
| + linear += grid_stride |
| + ) { |
| + int64_t remaining = linear; |
| + |
| + const int64_t i0 = |
| + remaining % ne0; |
| + remaining /= ne0; |
| + |
| + const int64_t i1 = |
| + remaining % ne1; |
| + remaining /= ne1; |
| + |
| + const int64_t i2 = |
| + remaining % ne2; |
| + remaining /= ne2; |
| + |
| + const int64_t i3 = |
| + remaining; |
| + |
| + const int64_t src0_i2 = |
| + i2 / dps2; |
| + |
| + const int64_t src0_i3 = |
| + i3 / dps3; |
| + |
| + float sum = 0.0f; |
| + |
| + for ( |
| + int64_t k = 0; |
| + k < ne01; |
| + ++k |
| + ) { |
| + const char * weight_row = |
| + src0 |
| + + src0_i3 * nb03 |
| + + src0_i2 * nb02 |
| + + k * nb01; |
| + |
| + const float weight = |
| + q1_out_prod_value( |
| + weight_row, |
| + nb00, |
| + i0); |
| + |
| + const float gradient = |
| + *reinterpret_cast< |
| + const float *>( |
| + src1 |
| + + i3 * nb13 |
| + + i2 * nb12 |
| + + k * nb11 |
| + + i1 * nb10); |
| + |
| + sum += weight * gradient; |
| + } |
| + |
| + *reinterpret_cast<float *>( |
| + dst |
| + + i3 * nb3 |
| + + i2 * nb2 |
| + + i1 * nb1 |
| + + i0 * nb0) = sum; |
| + } |
| +} |
| + |
| + |
| +static void ggml_cuda_out_prod_q1_f32( |
| + ggml_backend_cuda_context & ctx, |
| + ggml_tensor * dst) { |
| + const ggml_tensor * src0 = |
| + dst->src[0]; |
| + |
| + const ggml_tensor * src1 = |
| + dst->src[1]; |
| |
| GGML_TENSOR_BINARY_OP_LOCALS |
| |
| - GGML_ASSERT(src0->type == GGML_TYPE_F32); |
| - GGML_ASSERT(src1->type == GGML_TYPE_F32); |
| - GGML_ASSERT(dst->type == GGML_TYPE_F32); |
| + GGML_ASSERT( |
| + src0->type |
| + == GGML_TYPE_Q1_0); |
| + |
| + GGML_ASSERT( |
| + src1->type |
| + == GGML_TYPE_F32); |
| + |
| + GGML_ASSERT( |
| + dst->type |
| + == GGML_TYPE_F32); |
| + |
| + GGML_ASSERT( |
| + !ggml_is_transposed(src0)); |
| + |
| + GGML_ASSERT( |
| + ne00 |
| + % QK1_0 |
| + == 0); |
| + |
| + GGML_ASSERT( |
| + nb00 |
| + == sizeof(block_q1_0)); |
| + |
| + GGML_ASSERT( |
| + ne01 |
| + == ne11); |
| + |
| + GGML_ASSERT( |
| + ne0 |
| + == ne00); |
| + |
| + GGML_ASSERT( |
| + ne1 |
| + == ne10); |
| + |
| + GGML_ASSERT( |
| + ne2 |
| + % ne02 |
| + == 0); |
| |
| - GGML_ASSERT(ne01 == ne11); |
| - GGML_ASSERT(ne0 == ne00); |
| - GGML_ASSERT(ne1 == ne10); |
| + GGML_ASSERT( |
| + ne3 |
| + % ne03 |
| + == 0); |
| |
| - GGML_ASSERT(ne2 % src0->ne[2] == 0); |
| - GGML_ASSERT(ne3 % src0->ne[3] == 0); |
| + GGML_ASSERT( |
| + ne2 |
| + == ne12); |
| |
| - GGML_ASSERT(ne2 == src1->ne[2]); |
| - GGML_ASSERT(ne3 == src1->ne[3]); |
| + GGML_ASSERT( |
| + ne3 |
| + == ne13); |
| |
| - const float * src0_d = (const float *) src0->data; |
| - const float * src1_d = (const float *) src1->data; |
| - float * dst_d = (float *) dst->data; |
| + GGML_ASSERT( |
| + nb0 |
| + == sizeof(float)); |
| |
| - cudaStream_t stream = ctx.stream(); |
| - cublasHandle_t handle = ctx.cublas_handle(); |
| + const int64_t dps2 = |
| + ne2 / ne02; |
| + |
| + const int64_t dps3 = |
| + ne3 / ne03; |
| + |
| + const int64_t total = |
| + ne0 |
| + * ne1 |
| + * ne2 |
| + * ne3; |
| + |
| + constexpr int threads = 256; |
| + |
| + const int64_t required_blocks = |
| + ( |
| + total |
| + + threads |
| + - 1 |
| + ) |
| + / threads; |
| + |
| + const int blocks = |
| + static_cast<int>( |
| + std::min<int64_t>( |
| + required_blocks, |
| + 65535)); |
| + |
| + cudaStream_t stream = |
| + ctx.stream(); |
| + |
| + out_prod_q1_f32_kernel<<< |
| + blocks, |
| + threads, |
| + 0, |
| + stream |
| + >>>( |
| + reinterpret_cast< |
| + const char *>(src0->data), |
| + |
| + reinterpret_cast< |
| + const char *>(src1->data), |
| + |
| + reinterpret_cast< |
| + char *>(dst->data), |
| + |
| + ne0, |
| + ne1, |
| + ne01, |
| + ne2, |
| + ne3, |
| + |
| + nb00, |
| + nb01, |
| + nb02, |
| + nb03, |
| + |
| + nb10, |
| + nb11, |
| + nb12, |
| + nb13, |
| + |
| + nb0, |
| + nb1, |
| + nb2, |
| + nb3, |
| + |
| + dps2, |
| + dps3); |
| + |
| + CUDA_CHECK( |
| + cudaGetLastError()); |
| +} |
| + |
| + |
| +void ggml_cuda_out_prod( |
| + ggml_backend_cuda_context & ctx, |
| + ggml_tensor * dst) { |
| + const ggml_tensor * src0 = |
| + dst->src[0]; |
| + |
| + const ggml_tensor * src1 = |
| + dst->src[1]; |
| + |
| + if ( |
| + src0->type |
| + == GGML_TYPE_Q1_0 |
| + && src1->type |
| + == GGML_TYPE_F32 |
| + && dst->type |
| + == GGML_TYPE_F32 |
| + ) { |
| + ggml_cuda_out_prod_q1_f32( |
| + ctx, |
| + dst); |
| + |
| + return; |
| + } |
| + |
| + GGML_TENSOR_BINARY_OP_LOCALS |
| + |
| + GGML_ASSERT( |
| + src0->type |
| + == GGML_TYPE_F32); |
| + |
| + GGML_ASSERT( |
| + src1->type |
| + == GGML_TYPE_F32); |
| + |
| + GGML_ASSERT( |
| + dst->type |
| + == GGML_TYPE_F32); |
| + |
| + GGML_ASSERT( |
| + ne01 |
| + == ne11); |
| + |
| + GGML_ASSERT( |
| + ne0 |
| + == ne00); |
| + |
| + GGML_ASSERT( |
| + ne1 |
| + == ne10); |
| + |
| + GGML_ASSERT( |
| + ne2 |
| + % src0->ne[2] |
| + == 0); |
| + |
| + GGML_ASSERT( |
| + ne3 |
| + % src0->ne[3] |
| + == 0); |
| + |
| + GGML_ASSERT( |
| + ne2 |
| + == src1->ne[2]); |
| + |
| + GGML_ASSERT( |
| + ne3 |
| + == src1->ne[3]); |
| + |
| + const float * src0_d = |
| + reinterpret_cast< |
| + const float *>(src0->data); |
| + |
| + const float * src1_d = |
| + reinterpret_cast< |
| + const float *>(src1->data); |
| + |
| + float * dst_d = |
| + reinterpret_cast< |
| + float *>(dst->data); |
| + |
| + cudaStream_t stream = |
| + ctx.stream(); |
| + |
| + cublasHandle_t handle = |
| + ctx.cublas_handle(); |
| |
| const float alpha = 1.0f; |
| const float beta = 0.0f; |
| |
| - CUBLAS_CHECK(cublasSetStream(handle, stream)); |
| - |
| - const int64_t lda = nb01 / sizeof(float); |
| - const int64_t ldc = nb1 / sizeof(float); |
| - |
| - const bool src1_T = ggml_is_transposed(src1); |
| - const cublasOperation_t src1_cublas_op = src1_T ? CUBLAS_OP_N : CUBLAS_OP_T; |
| - const int64_t ldb = (src1_T ? nb10 : nb11) / sizeof(float); |
| - GGML_ASSERT( (src1_T ? nb11 : nb10) == sizeof(float)); |
| - |
| - // data strides in dimensions 2/3 |
| - const size_t s02 = nb02 / sizeof(float); |
| - const size_t s03 = nb03 / sizeof(float); |
| - const size_t s12 = nb12 / sizeof(float); |
| - const size_t s13 = nb13 / sizeof(float); |
| - const size_t s2 = nb2 / sizeof(float); |
| - const size_t s3 = nb3 / sizeof(float); |
| - |
| - // dps == dst per src0, used for group query attention |
| - const int64_t dps2 = ne2 / ne02; |
| - const int64_t dps3 = ne3 / ne03; |
| - |
| - if (dps2 == 1 && ne2 > 1) { |
| - // src0 has uniform stride s02 along dim 2; batch the inner loop with a strided GEMM |
| - GGML_ASSERT(ne2 <= std::numeric_limits<int>::max()); |
| - const int batch_count = (int) ne2; |
| - for (int64_t i3 = 0; i3 < ne3; ++i3) { |
| + CUBLAS_CHECK( |
| + cublasSetStream( |
| + handle, |
| + stream)); |
| + |
| + const int64_t lda = |
| + nb01 / sizeof(float); |
| + |
| + const int64_t ldc = |
| + nb1 / sizeof(float); |
| + |
| + const bool src1_T = |
| + ggml_is_transposed(src1); |
| + |
| + const cublasOperation_t src1_cublas_op = |
| + src1_T |
| + ? CUBLAS_OP_N |
| + : CUBLAS_OP_T; |
| + |
| + const int64_t ldb = |
| + ( |
| + src1_T |
| + ? nb10 |
| + : nb11 |
| + ) |
| + / sizeof(float); |
| + |
| + GGML_ASSERT( |
| + ( |
| + src1_T |
| + ? nb11 |
| + : nb10 |
| + ) |
| + == sizeof(float)); |
| + |
| + const size_t s02 = |
| + nb02 / sizeof(float); |
| + |
| + const size_t s03 = |
| + nb03 / sizeof(float); |
| + |
| + const size_t s12 = |
| + nb12 / sizeof(float); |
| + |
| + const size_t s13 = |
| + nb13 / sizeof(float); |
| + |
| + const size_t s2 = |
| + nb2 / sizeof(float); |
| + |
| + const size_t s3 = |
| + nb3 / sizeof(float); |
| + |
| + const int64_t dps2 = |
| + ne2 / ne02; |
| + |
| + const int64_t dps3 = |
| + ne3 / ne03; |
| + |
| + if ( |
| + dps2 == 1 |
| + && ne2 > 1 |
| + ) { |
| + GGML_ASSERT( |
| + ne2 |
| + <= std::numeric_limits<int>::max()); |
| + |
| + const int batch_count = |
| + static_cast<int>(ne2); |
| + |
| + for ( |
| + int64_t i3 = 0; |
| + i3 < ne3; |
| + ++i3 |
| + ) { |
| CUBLAS_CHECK( |
| - cublasSgemmStridedBatched(handle, CUBLAS_OP_N, src1_cublas_op, |
| - ne0, ne1, ne01, |
| - &alpha, src0_d + (i3/dps3)*s03, lda, s02, |
| - src1_d + i3 *s13, ldb, s12, |
| - &beta, dst_d + i3 *s3, ldc, s2, |
| - batch_count)); |
| + cublasSgemmStridedBatched( |
| + handle, |
| + CUBLAS_OP_N, |
| + src1_cublas_op, |
| + |
| + ne0, |
| + ne1, |
| + ne01, |
| + |
| + &alpha, |
| + |
| + src0_d |
| + + ( |
| + i3 / dps3) |
| + * s03, |
| + |
| + lda, |
| + s02, |
| + |
| + src1_d |
| + + i3 * s13, |
| + |
| + ldb, |
| + s12, |
| + |
| + &beta, |
| + |
| + dst_d |
| + + i3 * s3, |
| + |
| + ldc, |
| + s2, |
| + |
| + batch_count)); |
| } |
| } else { |
| - // Fallback: ne2 == 1 (no batching benefit) or dps2 > 1 (src0 broadcast along dim 2 |
| - // with non-uniform stride; would need cublasSgemmBatched with pointer arrays). |
| - for (int64_t i3 = 0; i3 < ne3; ++i3) { |
| - for (int64_t i2 = 0; i2 < ne2; ++i2) { |
| + for ( |
| + int64_t i3 = 0; |
| + i3 < ne3; |
| + ++i3 |
| + ) { |
| + for ( |
| + int64_t i2 = 0; |
| + i2 < ne2; |
| + ++i2 |
| + ) { |
| CUBLAS_CHECK( |
| - cublasSgemm(handle, CUBLAS_OP_N, src1_cublas_op, |
| - ne0, ne1, ne01, |
| - &alpha, src0_d + (i3/dps3)*s03 + (i2/dps2)*s02, lda, |
| - src1_d + i3 *s13 + i2 *s12, ldb, |
| - &beta, dst_d + i3 *s3 + i2 *s2, ldc)); |
| + cublasSgemm( |
| + handle, |
| + CUBLAS_OP_N, |
| + src1_cublas_op, |
| + |
| + ne0, |
| + ne1, |
| + ne01, |
| + |
| + &alpha, |
| + |
| + src0_d |
| + + ( |
| + i3 / dps3) |
| + * s03 |
| + + ( |
| + i2 / dps2) |
| + * s02, |
| + |
| + lda, |
| + |
| + src1_d |
| + + i3 * s13 |
| + + i2 * s12, |
| + |
| + ldb, |
| + |
| + &beta, |
| + |
| + dst_d |
| + + i3 * s3 |
| + + i2 * s2, |
| + |
| + ldc)); |
| } |
| } |
| } |
| |
| |
| |
| |
| @@ -1,6 +1,9 @@ |
| #include "unary.cuh" |
| #include "convert.cuh" |
| |
| +#include <cstdio> |
| +#include <cstdlib> |
| + |
| static __device__ __forceinline__ float op_abs(float x) { |
| return fabsf(x); |
| } |
| @@ -127,6 +130,34 @@ static __global__ void unary_op_kernel(const T * x, T * dst, const int k) { |
| dst[i] = (T)op((float)x[i]); |
| } |
| |
| +// PRISM_Q1_LORA_STRIDED_UNARY_KERNEL_V2 |
| +template <float (*op)(float), typename T> |
| +static __global__ void unary_op_strided_kernel( |
| + const char * x, |
| + char * dst, |
| + int64_t ne0, int64_t ne1, int64_t ne2, int64_t ne3, |
| + size_t snb0, size_t snb1, size_t snb2, size_t snb3, |
| + size_t dnb0, size_t dnb1, size_t dnb2, size_t dnb3, |
| + int64_t n) { |
| + ggml_cuda_pdl_lc(); |
| + const int64_t linear = (int64_t) blockDim.x*blockIdx.x + threadIdx.x; |
| + if (linear >= n) { |
| + return; |
| + } |
| + |
| + int64_t value = linear; |
| + const int64_t i0 = value % ne0; value /= ne0; |
| + const int64_t i1 = value % ne1; value /= ne1; |
| + const int64_t i2 = value % ne2; value /= ne2; |
| + const int64_t i3 = value % ne3; |
| + |
| + ggml_cuda_pdl_sync(); |
| + |
| + const T * xp = reinterpret_cast<const T *>(x + i0*snb0 + i1*snb1 + i2*snb2 + i3*snb3); |
| + T * dp = reinterpret_cast<T *>(dst + i0*dnb0 + i1*dnb1 + i2*dnb2 + i3*dnb3); |
| + *dp = (T) op((float) *xp); |
| +} |
| + |
| template <float (*op)(float), typename T> |
| static void unary_cuda(const T * x, T * dst, const int k, cudaStream_t stream) { |
| const int num_blocks = (k + CUDA_NEG_BLOCK_SIZE - 1) / CUDA_NEG_BLOCK_SIZE; |
| @@ -134,26 +165,62 @@ static void unary_cuda(const T * x, T * dst, const int k, cudaStream_t stream) { |
| ggml_cuda_kernel_launch(unary_op_kernel<op, T>, launch_params, x, dst, k); |
| } |
| |
| +template <float (*op)(float), typename T> |
| +static void unary_cuda_strided(const ggml_tensor * src0, ggml_tensor * dst, cudaStream_t stream) { |
| + const int64_t n = ggml_nelements(src0); |
| + const int64_t num_blocks = (n + CUDA_NEG_BLOCK_SIZE - 1) / CUDA_NEG_BLOCK_SIZE; |
| + const ggml_cuda_kernel_launch_params launch_params = ggml_cuda_kernel_launch_params((dim3) num_blocks, CUDA_NEG_BLOCK_SIZE, 0, stream); |
| + ggml_cuda_kernel_launch(unary_op_strided_kernel<op, T>, launch_params, |
| + (const char *) src0->data, (char *) dst->data, |
| + src0->ne[0], src0->ne[1], src0->ne[2], src0->ne[3], |
| + src0->nb[0], src0->nb[1], src0->nb[2], src0->nb[3], |
| + dst->nb[0], dst->nb[1], dst->nb[2], dst->nb[3], n); |
| +} |
| + |
| template <float (*op)(float)> |
| void ggml_cuda_op_unary(ggml_backend_cuda_context & ctx, ggml_tensor * dst) { |
| + // PRISM_Q1_LORA_STRIDED_UNARY_DISPATCH_V2 |
| const ggml_tensor * src0 = dst->src[0]; |
| - const void * src0_d = src0->data; |
| - void * dst_d = dst->data; |
| cudaStream_t stream = ctx.stream(); |
| |
| - GGML_ASSERT(ggml_is_contiguous(src0)); |
| - |
| GGML_ASSERT(src0->type == GGML_TYPE_F32 || src0->type == GGML_TYPE_F16); |
| GGML_ASSERT( dst->type == GGML_TYPE_F32 || dst->type == GGML_TYPE_F16); |
| GGML_ASSERT(src0->type == dst->type); |
| + GGML_ASSERT(ggml_are_same_shape(src0, dst)); |
| + |
| + const bool contiguous = ggml_is_contiguous(src0) && ggml_is_contiguous(dst); |
| + if (contiguous) { |
| + if (src0->type == GGML_TYPE_F16) { |
| + unary_cuda<op>((const half *) src0->data, (half *) dst->data, ggml_nelements(src0), stream); |
| + } else { |
| + unary_cuda<op>((const float *) src0->data, (float *) dst->data, ggml_nelements(src0), stream); |
| + } |
| + return; |
| + } |
| + |
| + const bool prism_training = std::getenv("PRISM_Q1_LORA_TRAINING") != nullptr; |
| + GGML_ASSERT(prism_training); |
| + |
| + static int prism_strided_unary_count = 0; |
| + if (prism_strided_unary_count < 128) { |
| + std::fprintf(stderr, |
| + "PRISM_Q1_LORA_STRIDED_UNARY count=%d src_name='%s' src_op=%s " |
| + "shape=[%lld,%lld,%lld,%lld] src_strides=[%zu,%zu,%zu,%zu] " |
| + "dst_strides=[%zu,%zu,%zu,%zu]\n", |
| + prism_strided_unary_count + 1, src0->name, ggml_op_name(src0->op), |
| + (long long) src0->ne[0], (long long) src0->ne[1], |
| + (long long) src0->ne[2], (long long) src0->ne[3], |
| + src0->nb[0], src0->nb[1], src0->nb[2], src0->nb[3], |
| + dst->nb[0], dst->nb[1], dst->nb[2], dst->nb[3]); |
| + } |
| + prism_strided_unary_count++; |
| |
| if (src0->type == GGML_TYPE_F16) { |
| - unary_cuda<op>((const half *)src0_d, (half *)dst_d, ggml_nelements(src0), stream); |
| + unary_cuda_strided<op, half>(src0, dst, stream); |
| } else { |
| - unary_cuda<op>((const float *)src0_d, (float *)dst_d, ggml_nelements(src0), stream); |
| + unary_cuda_strided<op, float>(src0, dst, stream); |
| } |
| } |
| - |
| void ggml_cuda_op_abs(ggml_backend_cuda_context & ctx, ggml_tensor * dst) { |
| ggml_cuda_op_unary<op_abs>(ctx, dst); |
| } |
| |
| |
| |
| |
| @@ -8,6 +8,8 @@ |
| #include <algorithm> |
| #include <cmath> |
| #include <cstdint> |
| +#include <cstdlib> |
| +#include <cstring> |
| #include <cinttypes> |
| #include <map> |
| #include <random> |
| @@ -325,8 +327,37 @@ static void ggml_opt_build(ggml_opt_context_t opt_ctx) { |
| |
| const enum ggml_opt_optimizer_type optimizer = opt_ctx->optimizer; |
| |
| - const bool accumulate = opt_ctx->build_type_alloc >= GGML_OPT_BUILD_TYPE_GRAD && |
| - !(opt_ctx->static_graphs && opt_ctx->build_type_alloc == GGML_OPT_BUILD_TYPE_OPT && opt_ctx->opt_period == 1); |
| + // PRISM_STEP10_V46_EXTERNAL_ACCUM_POLICY_BEGIN |
| + const char * prism_external_accum_env = |
| + std::getenv("PRISM_STEP10_EXTERNAL_GRAD_ACCUM"); |
| + |
| + const bool prism_external_host_accum = |
| + !opt_ctx->static_graphs |
| + && prism_external_accum_env |
| + && prism_external_accum_env[0] == '1' |
| + && prism_external_accum_env[1] == '\0'; |
| + |
| + // Dynamic Step 10 graphs expose direct parameter gradients. |
| + // Accumulation, clipping, AdamW, checkpointing, and resume remain |
| + // exclusively owned by the deterministic host trainer. |
| + const bool accumulate = |
| + !prism_external_host_accum |
| + && opt_ctx->build_type_alloc >= GGML_OPT_BUILD_TYPE_GRAD |
| + && !(opt_ctx->static_graphs |
| + && opt_ctx->build_type_alloc == GGML_OPT_BUILD_TYPE_OPT |
| + && opt_ctx->opt_period == 1); |
| + |
| + if (prism_external_host_accum) { |
| + static bool prism_external_accum_logged = false; |
| + if (!prism_external_accum_logged) { |
| + fprintf( |
| + stderr, |
| + "PRISM_STEP10_EXTERNAL_HOST_ACCUM=1 " |
| + "ggml_parameter_accumulate=0\n"); |
| + prism_external_accum_logged = true; |
| + } |
| + } |
| + // PRISM_STEP10_V46_EXTERNAL_ACCUM_POLICY_END |
| |
| const bool need_momenta = opt_ctx->build_type_alloc == GGML_OPT_BUILD_TYPE_OPT && |
| opt_ctx->optimizer == GGML_OPT_OPTIMIZER_TYPE_ADAMW; |
| @@ -631,8 +662,193 @@ struct ggml_tensor * ggml_opt_ncorrect(ggml_opt_context_t opt_ctx) { |
| } |
| |
| struct ggml_tensor * ggml_opt_grad_acc(ggml_opt_context_t opt_ctx, struct ggml_tensor * node) { |
| - return ggml_graph_get_grad_acc(opt_ctx->gb_opt, node); |
| + if (!opt_ctx || !node) { |
| + return nullptr; |
| + } |
| + struct ggml_cgraph * graph = opt_ctx->allocated_graph; |
| + if (!graph) { |
| + switch (opt_ctx->build_type) { |
| + case GGML_OPT_BUILD_TYPE_FORWARD: graph = opt_ctx->gf; break; |
| + case GGML_OPT_BUILD_TYPE_GRAD: graph = opt_ctx->gb_grad; break; |
| + case GGML_OPT_BUILD_TYPE_OPT: graph = opt_ctx->gb_opt; break; |
| + } |
| + } |
| + return graph ? ggml_graph_get_grad_acc(graph, node) : nullptr; |
| +} |
| + |
| + |
| +// PRISM_STEP10_GGML_OPT_IMPL_BEGIN |
| +// PRISM_STEP10_V451_FORCE_OPT_CONFIG_BEGIN |
| +void ggml_opt_configure_gradient_only( |
| + ggml_opt_context_t opt_ctx, |
| + int32_t opt_period) { |
| + GGML_ASSERT(opt_ctx != nullptr); |
| + GGML_ASSERT(opt_period >= 1); |
| + GGML_ASSERT( |
| + opt_ctx->build_type_alloc |
| + == GGML_OPT_BUILD_TYPE_OPT); |
| + |
| + const char * force_opt_env = |
| + std::getenv("PRISM_STEP10_FORCE_OPT_BACKWARD"); |
| + |
| + const bool force_opt = |
| + force_opt_env |
| + && force_opt_env[0] == '1' |
| + && force_opt_env[1] == '\0'; |
| + |
| + opt_ctx->build_type = |
| + force_opt |
| + ? GGML_OPT_BUILD_TYPE_OPT |
| + : GGML_OPT_BUILD_TYPE_GRAD; |
| + |
| + opt_ctx->opt_period = |
| + force_opt |
| + ? 1 |
| + : opt_period; |
| + |
| + opt_ctx->opt_i = 0; |
| + |
| + fprintf( |
| + stderr, |
| + "PRISM_STEP10_FORCE_OPT_BACKWARD=%d " |
| + "build_type_alloc=OPT opt_period=%d\n", |
| + force_opt ? 1 : 0, |
| + opt_ctx->opt_period); |
| +} |
| +// PRISM_STEP10_V451_FORCE_OPT_CONFIG_END |
| + |
| +void ggml_opt_reset_gradient_cycle(ggml_opt_context_t opt_ctx) { |
| + GGML_ASSERT(opt_ctx != nullptr); |
| + GGML_ASSERT(opt_ctx->build_type_alloc == GGML_OPT_BUILD_TYPE_OPT); |
| + opt_ctx->opt_i = 0; |
| + opt_ctx->build_type = GGML_OPT_BUILD_TYPE_GRAD; |
| +} |
| + |
| +// PRISM_STEP10_V47_LOSS_SEED_PRESERVE_BEGIN |
| +void ggml_opt_zero_grad_accumulators( |
| + ggml_opt_context_t opt_ctx) { |
| + GGML_ASSERT(opt_ctx != nullptr); |
| + GGML_ASSERT(opt_ctx->allocated_graph != nullptr); |
| + GGML_ASSERT(opt_ctx->loss != nullptr); |
| + |
| + ggml_tensor * loss_grad = |
| + ggml_graph_get_grad_acc( |
| + opt_ctx->allocated_graph, |
| + opt_ctx->loss); |
| + |
| + GGML_ASSERT( |
| + loss_grad != nullptr |
| + && "active backward graph has no loss gradient accumulator"); |
| + |
| + GGML_ASSERT( |
| + loss_grad->type == GGML_TYPE_F32 |
| + && ggml_is_scalar(loss_grad)); |
| + |
| + float seed_before = NAN; |
| + |
| + if (loss_grad->buffer) { |
| + ggml_backend_tensor_get( |
| + loss_grad, |
| + &seed_before, |
| + 0, |
| + sizeof(seed_before)); |
| + } |
| + |
| + size_t zeroed_accumulators = 0; |
| + size_t preserved_loss_entries = 0; |
| + |
| + for (ggml_tensor * tensor : opt_ctx->grad_accs) { |
| + if (!tensor || !tensor->buffer) { |
| + continue; |
| + } |
| + |
| + if (tensor == loss_grad) { |
| + ++preserved_loss_entries; |
| + continue; |
| + } |
| + |
| + std::vector<uint8_t> zeros( |
| + ggml_nbytes(tensor), |
| + 0); |
| + |
| + ggml_backend_tensor_set( |
| + tensor, |
| + zeros.data(), |
| + 0, |
| + zeros.size()); |
| + |
| + ++zeroed_accumulators; |
| + } |
| + |
| + const float loss_seed = 1.0f; |
| + |
| + ggml_backend_tensor_set( |
| + loss_grad, |
| + &loss_seed, |
| + 0, |
| + sizeof(loss_seed)); |
| + |
| + float seed_after = NAN; |
| + |
| + ggml_backend_tensor_get( |
| + loss_grad, |
| + &seed_after, |
| + 0, |
| + sizeof(seed_after)); |
| + |
| + fprintf( |
| + stderr, |
| + "PRISM_STEP10_LOSS_SEED_BEFORE=%.9g\n" |
| + "PRISM_STEP10_LOSS_SEED_AFTER=%.9g\n" |
| + "PRISM_STEP10_NONLOSS_ACCUMULATORS_ZEROED=%zu\n" |
| + "PRISM_STEP10_LOSS_ACCUMULATOR_PRESERVED=%zu\n", |
| + static_cast<double>(seed_before), |
| + static_cast<double>(seed_after), |
| + zeroed_accumulators, |
| + preserved_loss_entries); |
| + |
| + GGML_ASSERT( |
| + seed_after == 1.0f |
| + && "failed to restore loss backward seed"); |
| +} |
| +// PRISM_STEP10_V47_LOSS_SEED_PRESERVE_END |
| + |
| +static ggml_cgraph * prism_step10_active_graph( |
| + ggml_opt_context_t opt_ctx) { |
| + if (!opt_ctx) { |
| + return nullptr; |
| + } |
| + |
| + if (opt_ctx->allocated_graph) { |
| + return opt_ctx->allocated_graph; |
| + } |
| + |
| + switch (opt_ctx->build_type) { |
| + case GGML_OPT_BUILD_TYPE_FORWARD: |
| + return opt_ctx->gf; |
| + case GGML_OPT_BUILD_TYPE_GRAD: |
| + return opt_ctx->gb_grad; |
| + case GGML_OPT_BUILD_TYPE_OPT: |
| + return opt_ctx->gb_opt; |
| + } |
| + |
| + return nullptr; |
| +} |
| + |
| +struct ggml_tensor * ggml_opt_grad_from_active_graph( |
| + ggml_opt_context_t opt_ctx, |
| + struct ggml_tensor * node) { |
| + ggml_cgraph * graph = prism_step10_active_graph(opt_ctx); |
| + return graph && node ? ggml_graph_get_grad(graph, node) : nullptr; |
| +} |
| + |
| +struct ggml_tensor * ggml_opt_grad_acc_from_active_graph( |
| + ggml_opt_context_t opt_ctx, |
| + struct ggml_tensor * node) { |
| + ggml_cgraph * graph = prism_step10_active_graph(opt_ctx); |
| + return graph && node ? ggml_graph_get_grad_acc(graph, node) : nullptr; |
| } |
| +// PRISM_STEP10_GGML_OPT_IMPL_END |
| |
| // |
| |
| @@ -658,11 +874,15 @@ void ggml_opt_result_ndata(ggml_opt_result_t result, int64_t * ndata) { |
| void ggml_opt_result_loss(ggml_opt_result_t result, double * loss, double * unc) { |
| const int64_t nbatches = result->loss.size(); // Number of physical batches. |
| |
| + // PRISM_STEP10_V43_NULL_SAFE_RESULT_LOSS_BEGIN |
| if (nbatches == 0) { |
| *loss = 0.0; |
| - *unc = NAN; |
| + if (unc) { |
| + *unc = NAN; |
| + } |
| return; |
| } |
| + // PRISM_STEP10_V43_NULL_SAFE_RESULT_LOSS_END |
| |
| double sum = 0.0; |
| double sum_squared = 0.0; |
| @@ -822,6 +1042,108 @@ void ggml_opt_eval(ggml_opt_context_t opt_ctx, ggml_opt_result_t result) { |
| } |
| |
| ggml_backend_sched_graph_compute(opt_ctx->backend_sched, opt_ctx->allocated_graph_copy); |
| + |
| + // PRISM_Q1_LORA_CHAIN_AUDIT_HELPER_V1 |
| + // PRISM_Q1_LORA_ATTENTION_CHAIN_AUDIT_V1 |
| + // Retired diagnostic identities: the safe post-compute parameter-gradient |
| + // audit below supersedes the earlier view-chain debug reader. No callback |
| + // reads from dynamic graphs are reintroduced. |
| + // PRISM_Q1_LORA_SAFE_GRADIENT_AUDIT_V1 |
| + // |
| + // Dynamic llama optimizer graphs are released later in this |
| + // function. Inspect gradients here, after synchronous compute |
| + // and while gb_opt still contains its valid mapped parameter |
| + // nodes. Do not call ggml_opt_grad_acc() from the later epoch |
| + // callback for dynamic graphs. |
| + if ( |
| + getenv("PRISM_Q1_LORA_GRAD_AUDIT") != nullptr |
| + && opt_ctx->allocated_graph == opt_ctx->gb_opt |
| + && opt_ctx->gb_opt != nullptr |
| + ) { |
| + for ( |
| + int i = 0; |
| + i < opt_ctx->gb_opt->n_nodes; |
| + ++i |
| + ) { |
| + struct ggml_tensor * node = |
| + opt_ctx->gb_opt->nodes[i]; |
| + |
| + if ( |
| + node == nullptr |
| + || !(node->flags & GGML_TENSOR_FLAG_PARAM) |
| + ) { |
| + continue; |
| + } |
| + |
| + const bool is_lora_a = |
| + strstr(node->name, ".lora_a") != nullptr; |
| + |
| + const bool is_lora_b = |
| + strstr(node->name, ".lora_b") != nullptr; |
| + |
| + if (!is_lora_a && !is_lora_b) { |
| + continue; |
| + } |
| + |
| + struct ggml_tensor * grad = |
| + ggml_graph_get_grad( |
| + opt_ctx->gb_opt, |
| + node); |
| + |
| + if (grad == nullptr) { |
| + fprintf( |
| + stderr, |
| + "PRISM_Q1_LORA_OPT_GRAD " |
| + "name=%s present=0 max_abs=0\n", |
| + node->name); |
| + |
| + continue; |
| + } |
| + |
| + if (grad->type != GGML_TYPE_F32) { |
| + fprintf( |
| + stderr, |
| + "PRISM_Q1_LORA_OPT_GRAD " |
| + "name=%s present=1 " |
| + "type=%s max_abs=nan\n", |
| + node->name, |
| + ggml_type_name(grad->type)); |
| + |
| + continue; |
| + } |
| + |
| + const int64_t count = |
| + ggml_nelements(grad); |
| + |
| + std::vector<float> values( |
| + static_cast<size_t>(count)); |
| + |
| + ggml_backend_tensor_get( |
| + grad, |
| + values.data(), |
| + 0, |
| + ggml_nbytes(grad)); |
| + |
| + double maximum = 0.0; |
| + |
| + for (const float value : values) { |
| + maximum = std::max( |
| + maximum, |
| + std::abs( |
| + static_cast<double>(value))); |
| + } |
| + |
| + fprintf( |
| + stderr, |
| + "PRISM_Q1_LORA_OPT_GRAD " |
| + "name=%s present=1 " |
| + "max_abs=%.12g nelements=%" PRId64 "\n", |
| + node->name, |
| + maximum, |
| + count); |
| + } |
| + } |
| + |
| opt_ctx->iter += opt_ctx->allocated_graph == opt_ctx->gb_opt; |
| opt_ctx->opt_i = (opt_ctx->opt_i + 1) % opt_ctx->opt_period; |
| |
| |
| |
| |
| |
| @@ -143,6 +143,8 @@ static void ggml_print_backtrace_symbols(void) { |
| } |
| #elif defined(__APPLE__) |
| #include <execinfo.h> |
| + |
| + |
| static void ggml_print_backtrace_symbols(void) { |
| void * trace[100]; |
| int nptrs = backtrace(trace, sizeof(trace)/sizeof(trace[0])); |
| @@ -1999,10 +2001,30 @@ struct ggml_tensor * ggml_get_tensor(struct ggml_context * ctx, const char * nam |
| |
| // ggml_dup |
| |
| + |
| +// PRISM_Q1_LORA_TRAINING_NO_INPLACE_V2 |
| +// |
| +// GGML backward graph construction rejects arithmetic operations |
| +// whose output aliases a source tensor. |
| +// |
| +// During explicit Prism native Q1-LoRA training, standard GGML |
| +// *_inplace constructors therefore return out-of-place tensors. |
| +// |
| +// Normal inference behavior remains unchanged. |
| +static bool ggml_prism_q1_lora_training_no_inplace(void) { |
| + const char * value = |
| + getenv("PRISM_Q1_LORA_TRAINING"); |
| + |
| + return value != NULL |
| + && strcmp(value, "0") != 0; |
| +} |
| + |
| static struct ggml_tensor * ggml_dup_impl( |
| struct ggml_context * ctx, |
| struct ggml_tensor * a, |
| bool inplace) { |
| + inplace = inplace && !ggml_prism_q1_lora_training_no_inplace(); |
| + |
| struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a); |
| |
| result->op = GGML_OP_DUP; |
| @@ -2032,6 +2054,8 @@ static struct ggml_tensor * ggml_add_impl( |
| bool inplace) { |
| GGML_ASSERT(ggml_can_repeat(b, a)); |
| |
| + inplace = inplace && !ggml_prism_q1_lora_training_no_inplace(); |
| + |
| struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a); |
| |
| result->op = GGML_OP_ADD; |
| @@ -2119,6 +2143,8 @@ static struct ggml_tensor * ggml_add1_impl( |
| GGML_ASSERT(ggml_is_scalar(b)); |
| GGML_ASSERT(ggml_is_padded_1d(a)); |
| |
| + inplace = inplace && !ggml_prism_q1_lora_training_no_inplace(); |
| + |
| struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a); |
| |
| result->op = GGML_OP_ADD1; |
| @@ -2158,6 +2184,8 @@ static struct ggml_tensor * ggml_acc_impl( |
| GGML_ASSERT(a->type == GGML_TYPE_F32); |
| GGML_ASSERT(b->type == GGML_TYPE_F32); |
| |
| + inplace = inplace && !ggml_prism_q1_lora_training_no_inplace(); |
| + |
| struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a); |
| |
| int32_t params[] = { nb1, nb2, nb3, offset, inplace ? 1 : 0 }; |
| @@ -2201,6 +2229,8 @@ static struct ggml_tensor * ggml_sub_impl( |
| bool inplace) { |
| GGML_ASSERT(ggml_can_repeat(b, a)); |
| |
| + inplace = inplace && !ggml_prism_q1_lora_training_no_inplace(); |
| + |
| struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a); |
| |
| result->op = GGML_OP_SUB; |
| @@ -2233,6 +2263,8 @@ static struct ggml_tensor * ggml_mul_impl( |
| bool inplace) { |
| GGML_ASSERT(ggml_can_repeat(b, a)); |
| |
| + inplace = inplace && !ggml_prism_q1_lora_training_no_inplace(); |
| + |
| struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a); |
| |
| result->op = GGML_OP_MUL; |
| @@ -2265,6 +2297,8 @@ static struct ggml_tensor * ggml_div_impl( |
| bool inplace) { |
| GGML_ASSERT(ggml_can_repeat(b, a)); |
| |
| + inplace = inplace && !ggml_prism_q1_lora_training_no_inplace(); |
| + |
| struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a); |
| |
| result->op = GGML_OP_DIV; |
| @@ -2294,6 +2328,8 @@ static struct ggml_tensor * ggml_sqr_impl( |
| struct ggml_context * ctx, |
| struct ggml_tensor * a, |
| bool inplace) { |
| + inplace = inplace && !ggml_prism_q1_lora_training_no_inplace(); |
| + |
| struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a); |
| |
| result->op = GGML_OP_SQR; |
| @@ -2320,6 +2356,8 @@ static struct ggml_tensor * ggml_sqrt_impl( |
| struct ggml_context * ctx, |
| struct ggml_tensor * a, |
| bool inplace) { |
| + inplace = inplace && !ggml_prism_q1_lora_training_no_inplace(); |
| + |
| struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a); |
| |
| result->op = GGML_OP_SQRT; |
| @@ -2346,6 +2384,8 @@ static struct ggml_tensor * ggml_log_impl( |
| struct ggml_context * ctx, |
| struct ggml_tensor * a, |
| bool inplace) { |
| + inplace = inplace && !ggml_prism_q1_lora_training_no_inplace(); |
| + |
| struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a); |
| |
| result->op = GGML_OP_LOG; |
| @@ -2396,6 +2436,8 @@ static struct ggml_tensor * ggml_sin_impl( |
| struct ggml_context * ctx, |
| struct ggml_tensor * a, |
| bool inplace) { |
| + inplace = inplace && !ggml_prism_q1_lora_training_no_inplace(); |
| + |
| struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a); |
| |
| result->op = GGML_OP_SIN; |
| @@ -2422,6 +2464,8 @@ static struct ggml_tensor * ggml_cos_impl( |
| struct ggml_context * ctx, |
| struct ggml_tensor * a, |
| bool inplace) { |
| + inplace = inplace && !ggml_prism_q1_lora_training_no_inplace(); |
| + |
| struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a); |
| |
| result->op = GGML_OP_COS; |
| @@ -2723,6 +2767,8 @@ struct ggml_tensor * ggml_leaky_relu( |
| struct ggml_tensor * a, |
| float negative_slope, |
| bool inplace) { |
| + inplace = inplace && !ggml_prism_q1_lora_training_no_inplace(); |
| + |
| struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a); |
| |
| ggml_set_op_params(result, &negative_slope, sizeof(negative_slope)); |
| @@ -3097,6 +3143,8 @@ static struct ggml_tensor * ggml_norm_impl( |
| struct ggml_tensor * a, |
| float eps, |
| bool inplace) { |
| + inplace = inplace && !ggml_prism_q1_lora_training_no_inplace(); |
| + |
| struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a); |
| |
| ggml_set_op_params(result, &eps, sizeof(eps)); |
| @@ -3128,6 +3176,8 @@ static struct ggml_tensor * ggml_rms_norm_impl( |
| struct ggml_tensor * a, |
| float eps, |
| bool inplace) { |
| + inplace = inplace && !ggml_prism_q1_lora_training_no_inplace(); |
| + |
| struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a); |
| |
| ggml_set_op_params(result, &eps, sizeof(eps)); |
| @@ -3178,6 +3228,8 @@ static struct ggml_tensor * ggml_group_norm_impl( |
| int n_groups, |
| float eps, |
| bool inplace) { |
| + inplace = inplace && !ggml_prism_q1_lora_training_no_inplace(); |
| + |
| struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a); |
| |
| ggml_set_op_params_i32(result, 0, n_groups); |
| @@ -3212,6 +3264,8 @@ static struct ggml_tensor * ggml_l2_norm_impl( |
| struct ggml_tensor * a, |
| float eps, |
| bool inplace) { |
| + inplace = inplace && !ggml_prism_q1_lora_training_no_inplace(); |
| + |
| struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a); |
| |
| ggml_set_op_params_f32(result, 0, eps); |
| @@ -3359,7 +3413,30 @@ static struct ggml_tensor * ggml_scale_impl( |
| float s, |
| float b, |
| bool inplace) { |
| - GGML_ASSERT(ggml_is_padded_1d(a)); |
| + // PRISM_Q1_LORA_TRAINING_CONTIGUOUS_SCALE_V1 |
| + // Preserve the stock padded-1D contract outside explicit training. |
| + if (!ggml_is_padded_1d(a)) { |
| + const bool prism_training = getenv("PRISM_Q1_LORA_TRAINING") != NULL; |
| + if (prism_training && !inplace) { |
| + static int prism_scale_materialize_count = 0; |
| + if (prism_scale_materialize_count < 128) { |
| + fprintf(stderr, |
| + "PRISM_Q1_LORA_SCALE_MATERIALIZE count=%d name='%s' op=%s " |
| + "shape=[%" PRId64 ",%" PRId64 ",%" PRId64 ",%" PRId64 "] " |
| + "strides=[%zu,%zu,%zu,%zu]\n", |
| + prism_scale_materialize_count + 1, |
| + a->name, ggml_op_name(a->op), |
| + a->ne[0], a->ne[1], a->ne[2], a->ne[3], |
| + a->nb[0], a->nb[1], a->nb[2], a->nb[3]); |
| + } |
| + prism_scale_materialize_count++; |
| + a = ggml_cont(ctx, a); |
| + } else { |
| + GGML_ASSERT(ggml_is_padded_1d(a)); |
| + } |
| + } |
| + |
| + inplace = inplace && !ggml_prism_q1_lora_training_no_inplace(); |
| |
| struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a); |
| |
| @@ -3416,6 +3493,8 @@ static struct ggml_tensor * ggml_set_impl( |
| GGML_ASSERT(ggml_nelements(a) >= ggml_nelements(b)); |
| |
| // make a view of the destination |
| + inplace = inplace && !ggml_prism_q1_lora_training_no_inplace(); |
| + |
| struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a); |
| |
| GGML_ASSERT(offset < (size_t)(1 << 30)); |
| @@ -3956,6 +4035,8 @@ static struct ggml_tensor * ggml_diag_mask_inf_impl( |
| struct ggml_tensor * a, |
| int n_past, |
| bool inplace) { |
| + inplace = inplace && !ggml_prism_q1_lora_training_no_inplace(); |
| + |
| struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a); |
| |
| int32_t params[] = { n_past }; |
| @@ -3988,6 +4069,8 @@ static struct ggml_tensor * ggml_diag_mask_zero_impl( |
| struct ggml_tensor * a, |
| int n_past, |
| bool inplace) { |
| + inplace = inplace && !ggml_prism_q1_lora_training_no_inplace(); |
| + |
| struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a); |
| |
| int32_t params[] = { n_past }; |
| @@ -4037,6 +4120,8 @@ static struct ggml_tensor * ggml_soft_max_impl( |
| GGML_ASSERT(mask); |
| } |
| |
| + inplace = inplace && !ggml_prism_q1_lora_training_no_inplace(); |
| + |
| struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a); |
| |
| float params[] = { scale, max_bias }; |
| @@ -4104,6 +4189,8 @@ static struct ggml_tensor * ggml_soft_max_ext_back_impl( |
| float scale, |
| float max_bias, |
| bool inplace) { |
| + inplace = inplace && !ggml_prism_q1_lora_training_no_inplace(); |
| + |
| struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a); |
| |
| result->op = GGML_OP_SOFT_MAX_BACK; |
| @@ -4169,6 +4256,8 @@ static struct ggml_tensor * ggml_rope_impl( |
| GGML_ASSERT(c->ne[0] >= n_dims / 2); |
| } |
| |
| + inplace = inplace && !ggml_prism_q1_lora_training_no_inplace(); |
| + |
| struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a); |
| |
| int32_t params[15] = { /*n_past*/ 0, n_dims, mode, /*n_ctx*/ 0, n_ctx_orig }; |
| @@ -5235,6 +5324,8 @@ static struct ggml_tensor * ggml_fill_impl( |
| GGML_ASSERT(a->type == GGML_TYPE_F32 || a->type == GGML_TYPE_F16); |
| GGML_ASSERT(ggml_is_contiguous(a)); |
| |
| + inplace = inplace && !ggml_prism_q1_lora_training_no_inplace(); |
| + |
| struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a); |
| |
| ggml_set_op_params_f32(result, 0, c); |
| @@ -5666,6 +5757,8 @@ static struct ggml_tensor * ggml_add_rel_pos_impl( |
| GGML_ASSERT(pw->ne[0]*pw->ne[0] == a->ne[0]); |
| GGML_ASSERT(pw->ne[1]*pw->ne[2] == a->ne[1]); |
| |
| + inplace = inplace && !ggml_prism_q1_lora_training_no_inplace(); |
| + |
| struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a); |
| ggml_set_op_params_i32(result, 0, inplace ? 1 : 0); |
| |
| @@ -5836,6 +5929,8 @@ static struct ggml_tensor * ggml_unary_impl( |
| bool inplace) { |
| GGML_ASSERT(ggml_is_contiguous_rows(a)); |
| |
| + inplace = inplace && !ggml_prism_q1_lora_training_no_inplace(); |
| + |
| struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a); |
| |
| ggml_set_op_params_i32(result, 0, (int32_t) op); |
| @@ -5871,6 +5966,8 @@ static struct ggml_tensor * ggml_map_custom1_impl( |
| bool inplace) { |
| GGML_ASSERT(n_tasks == GGML_N_TASKS_MAX || n_tasks > 0); |
| |
| + inplace = inplace && !ggml_prism_q1_lora_training_no_inplace(); |
| + |
| struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a); |
| |
| struct ggml_map_custom1_op_params params = { |
| @@ -5916,6 +6013,8 @@ static struct ggml_tensor * ggml_map_custom2_impl( |
| bool inplace) { |
| GGML_ASSERT(n_tasks == GGML_N_TASKS_MAX || n_tasks > 0); |
| |
| + inplace = inplace && !ggml_prism_q1_lora_training_no_inplace(); |
| + |
| struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a); |
| |
| struct ggml_map_custom2_op_params params = { |
| @@ -5965,6 +6064,8 @@ static struct ggml_tensor * ggml_map_custom3_impl( |
| bool inplace) { |
| GGML_ASSERT(n_tasks == GGML_N_TASKS_MAX || n_tasks > 0); |
| |
| + inplace = inplace && !ggml_prism_q1_lora_training_no_inplace(); |
| + |
| struct ggml_tensor * result = inplace ? ggml_view_tensor(ctx, a) : ggml_dup_tensor(ctx, a); |
| |
| struct ggml_map_custom3_op_params params = { |
| @@ -6510,7 +6611,12 @@ static void ggml_compute_backward( |
| ggml_add_or_set(ctx, cgraph, isrc0, grad); |
| } |
| if (src1_needs_grads) { |
| - ggml_sub_or_set(ctx, cgraph, isrc1, grad); |
| + // PRISM_Q1_LORA_SUB_BACKWARD_BROADCAST_V1 |
| + struct ggml_tensor * tmp = grad; |
| + if (!ggml_are_same_shape(grad, src1)) { |
| + tmp = ggml_repeat_back(ctx, tmp, src1); |
| + } |
| + ggml_sub_or_set(ctx, cgraph, isrc1, tmp); |
| } |
| } break; |
| case GGML_OP_MUL: { |
| @@ -6530,7 +6636,12 @@ static void ggml_compute_backward( |
| ggml_add_or_set(ctx, cgraph, isrc0, ggml_div(ctx, grad, src1)); |
| } |
| if (src1_needs_grads) { |
| - ggml_sub_or_set(ctx, cgraph, isrc1, ggml_mul(ctx, grad, ggml_div(ctx, tensor, src1))); |
| + // PRISM_Q1_LORA_DIV_BACKWARD_BROADCAST_V1 |
| + struct ggml_tensor * tmp = ggml_mul(ctx, grad, ggml_div(ctx, tensor, src1)); |
| + if (!ggml_are_same_shape(tmp, src1)) { |
| + tmp = ggml_repeat_back(ctx, tmp, src1); |
| + } |
| + ggml_sub_or_set(ctx, cgraph, isrc1, tmp); |
| } |
| } break; |
| case GGML_OP_SQR: { |
| @@ -6583,6 +6694,139 @@ static void ggml_compute_backward( |
| ggml_add_or_set(ctx, cgraph, isrc0, ggml_repeat(ctx, grad, src0)); |
| } |
| } break; |
| + case GGML_OP_L2_NORM: { |
| + if (src0_needs_grads) { |
| + // PRISM_Q1_LORA_L2_NORM_BACKWARD_V3 |
| + float eps; |
| + memcpy(&eps, tensor->op_params, sizeof(float)); |
| + |
| + // y = x / ||x|| ; dx = (g - y * sum(g*y)) / ||x|| |
| + struct ggml_tensor * gy = ggml_mul(ctx, grad, tensor); |
| + struct ggml_tensor * projection = ggml_sum_rows(ctx, gy); |
| + projection = ggml_mul(ctx, tensor, projection); |
| + struct ggml_tensor * numerator = ggml_sub(ctx, grad, projection); |
| + |
| + struct ggml_tensor * norm_squared = ggml_sum_rows(ctx, ggml_sqr(ctx, src0)); |
| + struct ggml_tensor * eps_tensor = ggml_fill(ctx, norm_squared, eps); |
| + struct ggml_tensor * norm = ggml_sqrt(ctx, ggml_add(ctx, norm_squared, eps_tensor)); |
| + ggml_add_or_set(ctx, cgraph, isrc0, ggml_div(ctx, numerator, norm)); |
| + } |
| + } break; |
| + case GGML_OP_TRI: { |
| + if (src0_needs_grads) { |
| + // PRISM_Q1_LORA_TRI_BACKWARD_V1 |
| + const enum ggml_tri_type type = (enum ggml_tri_type) ggml_get_op_params_i32(tensor, 0); |
| + ggml_add_or_set(ctx, cgraph, isrc0, ggml_tri(ctx, ggml_cont(ctx, grad), type)); |
| + } |
| + } break; |
| + case GGML_OP_PAD: { |
| + if (src0_needs_grads) { |
| + // PRISM_Q1_LORA_PAD_BACKWARD_V2 |
| + GGML_ASSERT(ggml_get_op_params_i32(tensor, 8) == 0 && "circular PAD backward is not implemented"); |
| + const int64_t lp0 = ggml_get_op_params_i32(tensor, 0); |
| + const int64_t lp1 = ggml_get_op_params_i32(tensor, 2); |
| + const int64_t lp2 = ggml_get_op_params_i32(tensor, 4); |
| + const int64_t lp3 = ggml_get_op_params_i32(tensor, 6); |
| + struct ggml_tensor * grad_cont = ggml_cont(ctx, grad); |
| + const size_t offset = |
| + (size_t) lp0 * grad_cont->nb[0] + |
| + (size_t) lp1 * grad_cont->nb[1] + |
| + (size_t) lp2 * grad_cont->nb[2] + |
| + (size_t) lp3 * grad_cont->nb[3]; |
| + struct ggml_tensor * crop = ggml_view_4d(ctx, grad_cont, |
| + src0->ne[0], src0->ne[1], src0->ne[2], src0->ne[3], |
| + grad_cont->nb[1], grad_cont->nb[2], grad_cont->nb[3], offset); |
| + ggml_add_or_set(ctx, cgraph, isrc0, ggml_cont(ctx, crop)); |
| + } |
| + } break; |
| + case GGML_OP_CUMSUM: { |
| + if (src0_needs_grads) { |
| + // PRISM_Q1_LORA_CUMSUM_BACKWARD_V1 |
| + const int64_t n = grad->ne[0]; |
| + struct ggml_tensor * order = ggml_argsort(ctx, ggml_arange(ctx, 0.0f, (float) n, 1.0f), GGML_SORT_ORDER_DESC); |
| + order = ggml_repeat_4d(ctx, order, n, grad->ne[2], grad->ne[3], 1); |
| + struct ggml_tensor * transposed = ggml_cont(ctx, ggml_transpose(ctx, grad)); |
| + struct ggml_tensor * reversed = ggml_get_rows(ctx, transposed, order); |
| + reversed = ggml_cont(ctx, ggml_transpose(ctx, reversed)); |
| + struct ggml_tensor * accumulated = ggml_cumsum(ctx, reversed); |
| + transposed = ggml_cont(ctx, ggml_transpose(ctx, accumulated)); |
| + reversed = ggml_get_rows(ctx, transposed, order); |
| + reversed = ggml_cont(ctx, ggml_transpose(ctx, reversed)); |
| + ggml_add_or_set(ctx, cgraph, isrc0, reversed); |
| + } |
| + } break; |
| + case GGML_OP_SOLVE_TRI: { |
| + // PRISM_Q1_LORA_SOLVE_TRI_BACKWARD_V4 |
| + // Forward contract is A X = B with lower-triangular A. |
| + // For dB solve A^T dB = grad. Convert the upper-triangular |
| + // system into a lower-triangular one with J A^T J. |
| + const int64_t n = src0->ne[0]; |
| + struct ggml_tensor * order = ggml_argsort(ctx, ggml_arange(ctx, 0.0f, (float) n, 1.0f), GGML_SORT_ORDER_DESC); |
| + order = ggml_repeat_4d(ctx, order, n, src0->ne[2], src0->ne[3], 1); |
| + |
| + struct ggml_tensor * a_t = ggml_cont(ctx, ggml_transpose(ctx, src0)); |
| + struct ggml_tensor * a_t_reverse_rows = ggml_get_rows(ctx, a_t, order); |
| + struct ggml_tensor * a_t_reverse_rows_t = ggml_cont(ctx, ggml_transpose(ctx, a_t_reverse_rows)); |
| + struct ggml_tensor * a_star_rows = ggml_get_rows(ctx, a_t_reverse_rows_t, order); |
| + struct ggml_tensor * a_star = ggml_cont(ctx, ggml_transpose(ctx, a_star_rows)); |
| + |
| + struct ggml_tensor * grad_reversed = ggml_get_rows(ctx, ggml_cont(ctx, grad), order); |
| + struct ggml_tensor * db_reversed = ggml_solve_tri(ctx, a_star, ggml_cont(ctx, grad_reversed), true, true, false); |
| + struct ggml_tensor * db = ggml_get_rows(ctx, db_reversed, order); |
| + db = ggml_cont(ctx, db); |
| + |
| + if (src1_needs_grads) { |
| + ggml_add_or_set(ctx, cgraph, isrc1, db); |
| + } |
| + if (src0_needs_grads) { |
| + // In GGML storage convention this is dB * X^T. |
| + struct ggml_tensor * da = ggml_neg(ctx, ggml_mul_mat(ctx, tensor, db)); |
| + ggml_add_or_set(ctx, cgraph, isrc0, da); |
| + } |
| + } break; |
| + case GGML_OP_FILL: { |
| + // PRISM_Q1_LORA_FILL_BACKWARD_V1 |
| + // The output is constant with respect to its shape-source tensor. |
| + } break; |
| + case GGML_OP_DIAG: { |
| + if (src0_needs_grads) { |
| + // PRISM_Q1_LORA_DIAG_BACKWARD_V1 |
| + struct ggml_tensor * ones = ggml_fill(ctx, src0, 1.0f); |
| + struct ggml_tensor * identity = ggml_diag(ctx, ones); |
| + struct ggml_tensor * masked = ggml_mul(ctx, grad, identity); |
| + struct ggml_tensor * diagonal = ggml_sum_rows(ctx, masked); |
| + diagonal = ggml_cont(ctx, ggml_transpose(ctx, diagonal)); |
| + ggml_add_or_set(ctx, cgraph, isrc0, ggml_reshape(ctx, diagonal, src0)); |
| + } |
| + } break; |
| + case GGML_OP_CONCAT: { |
| + // PRISM_Q1_LORA_CONCAT_BACKWARD_V2_MATERIALIZED |
| + const int dim = ggml_get_op_params_i32(tensor, 0); |
| + GGML_ASSERT(dim >= 0 && dim < GGML_MAX_DIMS); |
| + struct ggml_tensor * grad_cont = ggml_cont(ctx, grad); |
| + const size_t src1_offset = (size_t) src0->ne[dim] * grad_cont->nb[dim]; |
| + |
| + struct ggml_tensor * src0_view = ggml_view_4d(ctx, grad_cont, |
| + src0->ne[0], src0->ne[1], src0->ne[2], src0->ne[3], |
| + grad_cont->nb[1], grad_cont->nb[2], grad_cont->nb[3], 0); |
| + struct ggml_tensor * src1_view = ggml_view_4d(ctx, grad_cont, |
| + src1->ne[0], src1->ne[1], src1->ne[2], src1->ne[3], |
| + grad_cont->nb[1], grad_cont->nb[2], grad_cont->nb[3], src1_offset); |
| + |
| + struct ggml_tensor * src0_grad = ggml_cont(ctx, src0_view); |
| + struct ggml_tensor * src1_grad = ggml_cont(ctx, src1_view); |
| + GGML_ASSERT(src0_grad->view_src == NULL); |
| + GGML_ASSERT(src1_grad->view_src == NULL); |
| + GGML_ASSERT(ggml_are_same_shape(src0_grad, src0)); |
| + GGML_ASSERT(ggml_are_same_shape(src1_grad, src1)); |
| + |
| + if (src0_needs_grads) { |
| + ggml_add_or_set(ctx, cgraph, isrc0, src0_grad); |
| + } |
| + if (src1_needs_grads) { |
| + ggml_add_or_set(ctx, cgraph, isrc1, src1_grad); |
| + } |
| + } break; |
| case GGML_OP_RMS_NORM: { |
| if (src0_needs_grads) { |
| float eps; |
| @@ -6760,6 +7004,76 @@ static void ggml_compute_backward( |
| // noop |
| } |
| } break; |
| + case GGML_OP_SET_ROWS: { |
| + // PRISM_Q1_LORA_SET_ROWS_BACKWARD_V1 |
| + // |
| + // Forward source ordering: |
| + // |
| + // src0 = rows inserted into the destination |
| + // src1 = row indices |
| + // src2 = original destination tensor |
| + // |
| + // Forward: |
| + // |
| + // tensor = src2 |
| + // tensor[src1] = src0 |
| + // |
| + // Therefore: |
| + // |
| + // dsrc0 = get_rows(grad, src1) |
| + // |
| + // dsrc2 = grad with the overwritten rows zeroed |
| + // |
| + // Row indices are discrete and have no gradient. |
| + |
| + if (src0_needs_grads) { |
| + struct ggml_tensor * inserted_rows_grad = |
| + ggml_get_rows( |
| + ctx, |
| + grad, |
| + src1); |
| + |
| + ggml_add_or_set( |
| + ctx, |
| + cgraph, |
| + isrc0, |
| + inserted_rows_grad); |
| + } |
| + |
| + GGML_ASSERT( |
| + !src1_needs_grads |
| + && "SET_ROWS indices are not differentiable"); |
| + |
| + if (src2_needs_grads) { |
| + // Create zeros with the exact shape of the inserted |
| + // rows. No second-order backward graph is requested, |
| + // so this dependency is only used to build values. |
| + struct ggml_tensor * zero_rows = |
| + ggml_scale( |
| + ctx, |
| + src0, |
| + 0.0f); |
| + |
| + // Do not overwrite the incoming gradient tensor. |
| + // First duplicate it, then clear rows overwritten by |
| + // the forward SET_ROWS operation. |
| + struct ggml_tensor * destination_grad = |
| + ggml_set_rows( |
| + ctx, |
| + ggml_dup( |
| + ctx, |
| + grad), |
| + zero_rows, |
| + src1); |
| + |
| + ggml_add_or_set( |
| + ctx, |
| + cgraph, |
| + isrc2, |
| + destination_grad); |
| + } |
| + } break; |
| + |
| case GGML_OP_DIAG_MASK_INF: { |
| if (src0_needs_grads) { |
| /* ggml_diag_mask_inf_impl() shouldn't be here */ |
| @@ -6869,6 +7183,40 @@ static void ggml_compute_backward( |
| ggml_add_or_set(ctx, cgraph, isrc0, ggml_silu_back(ctx, grad, src0)); |
| } |
| } break; |
| + case GGML_UNARY_OP_SIGMOID: { |
| + // PRISM_Q1_LORA_SIGMOID_BACKWARD_V1 |
| + // |
| + // y = sigmoid(x) |
| + // |
| + // dy/dx = y * (1 - y) |
| + // = sigmoid(x) * sigmoid(-x) |
| + // |
| + // `tensor` is the already-created forward |
| + // sigmoid output. |
| + if (src0_needs_grads) { |
| + struct ggml_tensor * one_minus_y = |
| + ggml_sigmoid( |
| + ctx, |
| + ggml_neg( |
| + ctx, |
| + src0)); |
| + |
| + struct ggml_tensor * local_derivative = |
| + ggml_mul( |
| + ctx, |
| + tensor, |
| + one_minus_y); |
| + |
| + ggml_add_or_set( |
| + ctx, |
| + cgraph, |
| + isrc0, |
| + ggml_mul( |
| + ctx, |
| + grad, |
| + local_derivative)); |
| + } |
| + } break; |
| case GGML_UNARY_OP_EXP: { |
| if (src0_needs_grads) { |
| ggml_add_or_set(ctx, cgraph, isrc0, ggml_mul(ctx, tensor, grad)); |
| @@ -6922,6 +7270,7 @@ static void ggml_compute_backward( |
| } //break; |
| } |
| |
| + // PRISM_Q1_LORA_BACKWARD_SHAPE_AUDIT_V1 |
| GGML_ASSERT(!src0_needs_grads || ggml_are_same_shape(src0, cgraph->grads[isrc0])); |
| GGML_ASSERT(!src1_needs_grads || ggml_are_same_shape(src1, cgraph->grads[isrc1])); |
| GGML_ASSERT(!src2_needs_grads || ggml_are_same_shape(src2, cgraph->grads[isrc2])); |
| @@ -7105,8 +7454,52 @@ void ggml_build_backward_expand( |
| } |
| |
| // inplace operations are currently not supported |
| - GGML_ASSERT(!node->view_src || node->op == GGML_OP_CPY || node->op == GGML_OP_VIEW || |
| - node->op == GGML_OP_RESHAPE || node->op == GGML_OP_PERMUTE || node->op == GGML_OP_TRANSPOSE); |
| + if ( |
| + node->view_src |
| + && node->op != GGML_OP_CPY |
| + && node->op != GGML_OP_VIEW |
| + && node->op != GGML_OP_RESHAPE |
| + && node->op != GGML_OP_PERMUTE |
| + && node->op != GGML_OP_TRANSPOSE |
| + && node->op != GGML_OP_SET_ROWS |
| + ) { |
| + // PRISM_Q1_LORA_INPLACE_DIAGNOSTIC_V2 |
| + GGML_LOG_ERROR( |
| + "PRISM_Q1_LORA_INPLACE_BLOCKER " |
| + "node='%s' op=%d " |
| + "view_src='%s' " |
| + "src0='%s' src1='%s' src2='%s' " |
| + "shape=[%lld,%lld,%lld,%lld] " |
| + "flags=%d\n", |
| + node->name, |
| + (int) node->op, |
| + |
| + node->view_src |
| + ? node->view_src->name |
| + : "<null>", |
| + |
| + node->src[0] |
| + ? node->src[0]->name |
| + : "<null>", |
| + |
| + node->src[1] |
| + ? node->src[1]->name |
| + : "<null>", |
| + |
| + node->src[2] |
| + ? node->src[2]->name |
| + : "<null>", |
| + |
| + (long long) node->ne[0], |
| + (long long) node->ne[1], |
| + (long long) node->ne[2], |
| + (long long) node->ne[3], |
| + (int) node->flags); |
| + |
| + GGML_ABORT( |
| + "native Q1-LoRA backward encountered " |
| + "a remaining in-place graph node"); |
| + } |
| |
| const size_t ihash = ggml_hash_find(&cgraph->visited_hash_set, node); |
| GGML_ASSERT(ihash != GGML_HASHSET_FULL); |
| |
| |
| |
| |
| @@ -1590,6 +1590,33 @@ extern "C" { |
| ggml_opt_epoch_callback callback_train, |
| ggml_opt_epoch_callback callback_eval); |
| |
| + |
| +// PRISM_STEP10_LLAMA_MASKED_API_BEGIN |
| +struct llama_opt_masked_stats { |
| + double loss; |
| + uint32_t supervised_tokens; |
| + uint32_t ubatches; |
| + float gradient_scale; |
| +}; |
| + |
| +// Execute one fixed-length sequence through the real model graph. labels are |
| +// already causally shifted. loss_mask selects the output rows that contribute |
| +// to cross entropy. When train=true, gradients for params are copied into the |
| +// caller-provided contiguous F32 buffer; no optimizer update is performed. |
| +LLAMA_API bool llama_opt_masked_sequence( |
| + struct llama_context * ctx, |
| + const llama_token * tokens, |
| + const llama_token * labels_sparse, |
| + const uint8_t * loss_mask, |
| + uint32_t n_tokens, |
| + bool train, |
| + struct ggml_tensor ** params, |
| + size_t n_params, |
| + float * gradients_out, |
| + size_t gradients_count, |
| + struct llama_opt_masked_stats * stats); |
| +// PRISM_STEP10_LLAMA_MASKED_API_END |
| + |
| #ifdef __cplusplus |
| } |
| #endif |
| |
| |
| |
| |
| @@ -6,6 +6,8 @@ |
| |
| #include <map> |
| #include <cassert> |
| +#include <cstdlib> |
| +#include <cstring> |
| #include <sstream> |
| #include <stdexcept> |
| |
| @@ -372,6 +374,28 @@ static void llama_adapter_lora_init_impl(llama_model & model, const char * path_ |
| ggml_tensor * tensor_b = ggml_dup_tensor(dev_ctx, w.b); |
| ggml_set_name(tensor_a, w.a->name); |
| ggml_set_name(tensor_b, w.b->name); |
| + |
| + // PRISM_Q1_LORA_TRAINABLE_ADAPTER_V2 |
| + const char * prism_training_env = |
| + std::getenv( |
| + "PRISM_Q1_LORA_TRAINING"); |
| + |
| + const bool prism_q1_lora_training = |
| + prism_training_env != nullptr |
| + && std::strcmp( |
| + prism_training_env, |
| + "0") != 0; |
| + |
| + if (prism_q1_lora_training) { |
| + ggml_set_param(tensor_a); |
| + ggml_set_param(tensor_b); |
| + |
| + LLAMA_LOG_INFO( |
| + "PRISM_Q1_LORA_PARAM " |
| + "weight=%s a_param=1 b_param=1\n", |
| + name.c_str()); |
| + } |
| + |
| adapter.ab_map[name] = llama_adapter_lora_weight(tensor_a, tensor_b); |
| } |
| |
| |
| |
| |
| |
| @@ -22,6 +22,9 @@ |
| #include <limits> |
| #include <numeric> |
| #include <stdexcept> |
| +#include <iomanip> |
| +#include <iostream> |
| +#include <cstdlib> |
| |
| // |
| // llama_context |
| @@ -197,6 +200,36 @@ llama_context::llama_context( |
| cparams.fused_gdn_ch = true; |
| cparams.auto_fgdn = true; |
| |
| + // PRISM_Q1_LORA_UNFUSED_GDN_TRAINING_V1 |
| + // |
| + // The fused GGML_OP_GATED_DELTA_NET is an inference kernel |
| + // and currently has no generic ggml_compute_backward case. |
| + // |
| + // Qwen3.5 already has a graph-composed non-fused path. |
| + // Enable it only for explicit native-LoRA SSM validation. |
| + if ( |
| + getenv("PRISM_Q1_LORA_TRAINING") != nullptr |
| + && getenv("PRISM_Q1_LORA_UNFUSED_GDN") != nullptr |
| + ) { |
| + cparams.fused_gdn_ar = false; |
| + cparams.fused_gdn_ch = false; |
| + cparams.auto_fgdn = false; |
| + |
| + LLAMA_LOG_INFO( |
| + "PRISM_Q1_LORA_UNFUSED_GDN enabled=1\n"); |
| + } |
| + |
| + // PRISM_Q1_LORA_NO_FLASH_TRAINING_V2 |
| + // Flash-attention backward is not used by the native packed-Q1 trainer. |
| + if ( |
| + getenv("PRISM_Q1_LORA_TRAINING") != nullptr && |
| + getenv("PRISM_Q1_LORA_TRAINING_NO_KV_CACHE") != nullptr |
| + ) { |
| + cparams.flash_attn = false; |
| + cparams.auto_fa = false; |
| + LLAMA_LOG_INFO("PRISM_Q1_LORA_NO_FLASH_TRAINING enabled=1\n"); |
| + } |
| + |
| // with causal attention, the batch size is limited by the context size |
| cparams.n_batch = cparams.causal_attn ? std::min(cparams.n_ctx, params.n_batch) : params.n_batch; |
| |
| @@ -3541,6 +3574,21 @@ static void llama_set_param(struct ggml_tensor * tensor, llama_opt_param_filter |
| } |
| |
| void llama_context::opt_init(struct llama_model * model, struct llama_opt_params lopt_params) { |
| + |
| + // PRISM_STEP10_UNFUSED_GDN_TRAINING_BEGIN |
| + // GGML's fused GATED_DELTA_NET node has a forward implementation but the |
| + // generic backward builder cannot differentiate it. Keep fused GDN enabled |
| + // for normal inference, but rebuild the scheduler with the decomposed graph |
| + // before creating the optimizer context. |
| + if (cparams.fused_gdn_ar || cparams.fused_gdn_ch || cparams.auto_fgdn) { |
| + cparams.fused_gdn_ar = false; |
| + cparams.fused_gdn_ch = false; |
| + cparams.auto_fgdn = false; |
| + sched_need_reserve = true; |
| + LLAMA_LOG_INFO("%s: PRISM_STEP10_TRAINING_GDN_MODE=UNFUSED\n", __func__); |
| + sched_reserve(); |
| + } |
| + // PRISM_STEP10_UNFUSED_GDN_TRAINING_END |
| GGML_ASSERT(!opt_ctx); |
| model->hparams.n_ctx_train = lopt_params.n_ctx_train > 0 ? lopt_params.n_ctx_train : n_ctx(); |
| const uint32_t n_batch = std::min(this->n_batch(), model->hparams.n_ctx_train); |
| @@ -3690,6 +3738,546 @@ void llama_context::opt_epoch_iter( |
| } |
| } |
| |
| + |
| +// PRISM_STEP10_MASKED_SEQUENCE_IMPL_BEGIN |
| +bool llama_context::opt_masked_sequence( |
| + const llama_token * tokens, |
| + const llama_token * labels_sparse, |
| + const uint8_t * loss_mask, |
| + uint32_t n_tokens, |
| + bool train, |
| + struct ggml_tensor ** params, |
| + size_t n_params, |
| + float * gradients_out, |
| + size_t gradients_count, |
| + struct llama_opt_masked_stats * stats) { |
| + if (!opt_ctx || !tokens || !labels_sparse || !loss_mask || !stats) { |
| + return false; |
| + } |
| + |
| + const uint32_t n_ctx = llama_model_n_ctx_train(&model); |
| + const uint32_t n_batch = std::min(this->n_batch(), n_ctx); |
| + const uint32_t n_ubatch = std::min(this->n_ubatch(), n_batch); |
| + if (n_tokens != n_ctx || n_ctx == 0 || n_ctx % n_batch != 0 || n_batch % n_ubatch != 0) { |
| + return false; |
| + } |
| + |
| + uint32_t supervised = 0; |
| + for (uint32_t i = 0; i < n_ctx; ++i) { |
| + supervised += loss_mask[i] != 0; |
| + } |
| + if (supervised == 0) { |
| + return false; |
| + } |
| + |
| + size_t expected_gradients = 0; |
| + for (size_t i = 0; i < n_params; ++i) { |
| + if (!params || !params[i]) { |
| + return false; |
| + } |
| + expected_gradients += ggml_nelements(params[i]); |
| + } |
| + if (train && (!gradients_out || gradients_count != expected_gradients)) { |
| + return false; |
| + } |
| + |
| +// PRISM_STEP10_V451_OPT_SNAPSHOT_BEGIN |
| + const uint32_t ubatches_total = |
| + n_ctx / n_ubatch; |
| + |
| + const char * force_opt_env = |
| + std::getenv("PRISM_STEP10_FORCE_OPT_BACKWARD"); |
| + |
| + const bool force_opt_backward = |
| + force_opt_env |
| + && force_opt_env[0] == '1' |
| + && force_opt_env[1] == '\0'; |
| + |
| + const int32_t sentinel_period = |
| + force_opt_backward |
| + ? 1 |
| + : static_cast<int32_t>( |
| + ubatches_total + 1); |
| + |
| + std::vector<std::vector<float>> |
| + opt_probe_snapshots; |
| + |
| + if (train && force_opt_backward) { |
| + opt_probe_snapshots.resize(n_params); |
| + |
| + for (size_t i = 0; i < n_params; ++i) { |
| + if (!params[i] |
| + || params[i]->type != GGML_TYPE_F32) { |
| + return false; |
| + } |
| + |
| + const size_t count = |
| + ggml_nelements(params[i]); |
| + |
| + opt_probe_snapshots[i].resize( |
| + count, |
| + 0.0f); |
| + |
| + ggml_backend_tensor_get( |
| + params[i], |
| + opt_probe_snapshots[i].data(), |
| + 0, |
| + count*sizeof(float)); |
| + } |
| + } |
| + |
| + ggml_opt_configure_gradient_only( |
| + opt_ctx, |
| + sentinel_period); |
| + // PRISM_STEP10_V451_OPT_SNAPSHOT_END |
| + |
| + ggml_opt_result_t result = ggml_opt_result_init(); |
| + llama_batch batch = llama_batch_init(n_batch, 0, 1); |
| + // PRISM_STEP10_V44_HOST_GRADIENT_CAPTURE_BEGIN |
| + std::vector<size_t> param_offsets(n_params + 1, 0); |
| + for (size_t i = 0; i < n_params; ++i) { |
| + param_offsets[i + 1] = |
| + param_offsets[i] + ggml_nelements(params[i]); |
| + } |
| + |
| + std::vector<double> direct_host_sum(expected_gradients, 0.0); |
| + std::vector<ggml_tensor *> persistent_accumulators(n_params, nullptr); |
| + bool accumulators_zeroed = false; |
| + uint32_t physical_ubatch_index = 0; |
| + // PRISM_STEP10_V44_HOST_GRADIENT_CAPTURE_END |
| + bool ok = true; |
| + |
| + memory->clear(true); |
| + |
| + for (uint32_t pos_ctx = 0; ok && pos_ctx < n_ctx; pos_ctx += n_batch) { |
| + batch.n_tokens = n_batch; |
| + for (uint32_t pos_batch = 0; pos_batch < n_batch; ++pos_batch) { |
| + batch.token [pos_batch] = tokens[pos_ctx + pos_batch]; |
| + batch.pos [pos_batch] = pos_ctx + pos_batch; |
| + batch.n_seq_id[pos_batch] = 1; |
| + batch.seq_id [pos_batch][0] = 0; |
| + batch.logits [pos_batch] = true; |
| + } |
| + |
| + if (!balloc->init(batch, model.vocab, nullptr, model.hparams.n_embd_inp(), |
| + cparams.kv_unified ? LLAMA_MAX_SEQ : cparams.n_seq_max, true)) { |
| + ok = false; |
| + break; |
| + } |
| + |
| + const uint32_t n_tokens_all = balloc->get_n_tokens(); |
| + n_queued_tokens += n_tokens_all; |
| + embd_seq.clear(); |
| + |
| + auto mctx = memory->init_batch(*balloc, cparams.n_ubatch, true); |
| + if (!mctx || mctx->get_status() != LLAMA_MEMORY_STATUS_SUCCESS) { |
| + ok = false; |
| + break; |
| + } |
| + |
| + if (output_reserve(n_tokens_all) < n_tokens_all) { |
| + ok = false; |
| + break; |
| + } |
| + |
| + uint32_t pos_batch = 0; |
| + do { |
| + const auto & ubatch = mctx->get_ubatch(); |
| + n_outputs = ubatch.n_tokens; |
| + |
| + if (!mctx->apply()) { |
| + ok = false; |
| + break; |
| + } |
| + |
| + auto * res = gf_res_prev.get(); |
| + const auto gparams = graph_params( |
| + res, ubatch, mctx.get(), ctx_type_to_graph_type(cparams.ctx_type)); |
| + res->reset(); |
| + auto * gf = model.build_graph(gparams); |
| + |
| + struct ggml_context * ctx_compute_opt = nullptr; |
| + { |
| + const size_t size_gf = ggml_graph_size(gf); |
| + const size_t size_meta = |
| + 4*size_gf*ggml_tensor_overhead() + |
| + 2*ggml_graph_overhead_custom(size_gf, true); |
| + struct ggml_init_params init_params = { |
| + /*.mem_size =*/ size_meta, |
| + /*.mem_buffer =*/ nullptr, |
| + /*.no_alloc =*/ true, |
| + }; |
| + ctx_compute_opt = ggml_init(init_params); |
| + } |
| + |
| + ggml_opt_prepare_alloc( |
| + opt_ctx, ctx_compute_opt, gf, res->get_inp_tokens(), res->get_logits()); |
| + ggml_opt_alloc(opt_ctx, train); |
| + |
| + // PRISM_STEP10_V44_ACTIVE_GRADIENT_LOOKUP_BEGIN |
| + std::vector<ggml_tensor *> direct_tensors(n_params, nullptr); |
| + |
| + if (train) { |
| + if (!accumulators_zeroed) { |
| + ggml_opt_zero_grad_accumulators(opt_ctx); |
| + accumulators_zeroed = true; |
| + } |
| + |
| + for (size_t i = 0; i < n_params; ++i) { |
| + direct_tensors[i] = |
| + ggml_opt_grad_from_active_graph(opt_ctx, params[i]); |
| + |
| + ggml_tensor * accumulator = |
| + ggml_opt_grad_acc_from_active_graph(opt_ctx, params[i]); |
| + |
| + if (accumulator) { |
| + persistent_accumulators[i] = accumulator; |
| + } |
| + |
| + if (!direct_tensors[i] && !accumulator) { |
| + std::cerr |
| + << "PRISM_STEP10_GRAD_LOOKUP_FAILED " |
| + << "param_index=" << i |
| + << " name='" << params[i]->name << "'\n"; |
| + ok = false; |
| + break; |
| + } |
| + } |
| + } |
| + // PRISM_STEP10_V44_ACTIVE_GRADIENT_LOOKUP_END |
| + if (!ok) { |
| + ggml_free(ctx_compute_opt); |
| + break; |
| + } |
| + |
| + res->set_inputs(&ubatch); |
| + struct ggml_tensor * labels = ggml_opt_labels(opt_ctx); |
| + if (!labels || labels->ne[1] != n_ubatch) { |
| + ggml_free(ctx_compute_opt); |
| + ok = false; |
| + break; |
| + } |
| + ggml_set_zero(labels); |
| + const float one = 1.0f; |
| + for (uint32_t pos_ubatch = 0; pos_ubatch < n_ubatch; ++pos_ubatch) { |
| + const uint32_t index = pos_ctx + pos_batch + pos_ubatch; |
| + if (!loss_mask[index]) { |
| + continue; |
| + } |
| + const llama_token label = labels_sparse[index]; |
| + if (label < 0 || label >= labels->ne[0]) { |
| + ggml_free(ctx_compute_opt); |
| + ok = false; |
| + break; |
| + } |
| + const size_t offset = |
| + (static_cast<size_t>(pos_ubatch)*labels->ne[0] + label)*sizeof(float); |
| + ggml_backend_tensor_set(labels, &one, offset, sizeof(float)); |
| + } |
| + if (!ok) { |
| + break; |
| + } |
| + |
| + ggml_opt_eval(opt_ctx, result); |
| + |
| + // PRISM_STEP10_V44_IMMEDIATE_DIRECT_COPY_BEGIN |
| + if (train) { |
| + for (size_t i = 0; i < n_params; ++i) { |
| + ggml_tensor * direct = direct_tensors[i]; |
| + if (!direct || direct->type != GGML_TYPE_F32) { |
| + continue; |
| + } |
| + |
| + const size_t count = ggml_nelements(params[i]); |
| + std::vector<float> values(count, 0.0f); |
| + ggml_backend_tensor_get( |
| + direct, |
| + values.data(), |
| + 0, |
| + count*sizeof(float)); |
| + |
| + size_t nonzero = 0; |
| + double max_abs = 0.0; |
| + double l2_squared = 0.0; |
| + |
| + for (size_t j = 0; j < count; ++j) { |
| + const float value = values[j]; |
| + if (!std::isfinite(value)) { |
| + ok = false; |
| + break; |
| + } |
| + |
| + direct_host_sum[param_offsets[i] + j] += |
| + static_cast<double>(value); |
| + |
| + if (value != 0.0f) { |
| + ++nonzero; |
| + } |
| + |
| + const double value_d = |
| + static_cast<double>(value); |
| + |
| + max_abs = std::max( |
| + max_abs, |
| + std::abs(value_d)); |
| + |
| + l2_squared += value_d*value_d; |
| + } |
| + |
| + std::cerr |
| + << std::setprecision(17) |
| + << "PRISM_STEP10_UBATCH_DIRECT_GRAD " |
| + << "ubatch=" << physical_ubatch_index |
| + << " param_index=" << i |
| + << " name='" << params[i]->name << "'" |
| + << " nonzero=" << nonzero |
| + << " max_abs=" << max_abs |
| + << " l2=" << std::sqrt(l2_squared) |
| + << "\n"; |
| + |
| + if (!ok) { |
| + break; |
| + } |
| + } |
| + } |
| + |
| + ++physical_ubatch_index; |
| + // PRISM_STEP10_V44_IMMEDIATE_DIRECT_COPY_END |
| + |
| +// PRISM_STEP10_V451_OPT_RESTORE_BEGIN |
| + if (train && force_opt_backward) { |
| + for (size_t i = 0; i < n_params; ++i) { |
| + const size_t count = |
| + ggml_nelements(params[i]); |
| + |
| + ggml_backend_tensor_set( |
| + params[i], |
| + opt_probe_snapshots[i].data(), |
| + 0, |
| + count*sizeof(float)); |
| + } |
| + |
| + std::cerr |
| + << "PRISM_STEP10_OPT_PARAMS_RESTORED " |
| + << "ubatch=" |
| + << physical_ubatch_index |
| + << "\n"; |
| + } |
| + // PRISM_STEP10_V451_OPT_RESTORE_END |
| + |
| + ggml_free(ctx_compute_opt); |
| + pos_batch += ubatch.n_tokens; |
| + } while (mctx->next()); |
| + } |
| + |
| + // PRISM_STEP10_V43_RESULT_ACCOUNTING_BEGIN |
| + int64_t result_ndata = 0; |
| + ggml_opt_result_ndata(result, &result_ndata); |
| + std::cerr << "PRISM_STEP10_RESULT_NDATA=" << result_ndata << "\n"; |
| + |
| + double loss = 0.0; |
| + double loss_uncertainty = NAN; |
| + |
| + if (ok && result_ndata <= 0) { |
| + std::cerr << "PRISM_STEP10_EMPTY_OPT_RESULT=1\n"; |
| + ok = false; |
| + } |
| + |
| + if (ok) { |
| + ggml_opt_result_loss(result, &loss, &loss_uncertainty); |
| + std::cerr << std::setprecision(17) |
| + << "PRISM_STEP10_RAW_LOSS=" << loss << "\n" |
| + << "PRISM_STEP10_LOSS_UNCERTAINTY=" << loss_uncertainty << "\n"; |
| + loss *= static_cast<double>(n_ctx) / static_cast<double>(supervised); |
| + } |
| + // PRISM_STEP10_V43_RESULT_ACCOUNTING_END |
| + |
| + // The backward graph used 1/sentinel_period for each ubatch. Convert the |
| + // accumulated gradient to an average over supervised output rows. |
| + const float gradient_scale = |
| + static_cast<float>(sentinel_period) / |
| + static_cast<float>(ubatches_total) * |
| + static_cast<float>(n_ctx) / |
| + static_cast<float>(supervised); |
| + |
| + // PRISM_STEP10_V44_GRADIENT_SOURCE_ARBITRATION_BEGIN |
| + if (ok && train) { |
| + std::vector<float> accumulator_values(expected_gradients, 0.0f); |
| + |
| + size_t accumulator_nonzero = 0; |
| + size_t direct_nonzero = 0; |
| + double accumulator_l2_squared = 0.0; |
| + double direct_l2_squared = 0.0; |
| + |
| + for (size_t i = 0; i < n_params; ++i) { |
| + const size_t count = ggml_nelements(params[i]); |
| + |
| + if (persistent_accumulators[i] && |
| + persistent_accumulators[i]->type == GGML_TYPE_F32) { |
| + std::vector<float> values(count, 0.0f); |
| + ggml_backend_tensor_get( |
| + persistent_accumulators[i], |
| + values.data(), |
| + 0, |
| + count*sizeof(float)); |
| + |
| + for (size_t j = 0; j < count; ++j) { |
| + const float value = values[j]; |
| + if (!std::isfinite(value)) { |
| + ok = false; |
| + break; |
| + } |
| + |
| + accumulator_values[param_offsets[i] + j] = value; |
| + |
| + const double value_d = |
| + static_cast<double>(value); |
| + |
| + if (value != 0.0f) { |
| + ++accumulator_nonzero; |
| + } |
| + |
| + accumulator_l2_squared += value_d*value_d; |
| + } |
| + } |
| + |
| + for (size_t j = 0; j < count; ++j) { |
| + const double value = |
| + direct_host_sum[param_offsets[i] + j]; |
| + |
| + if (value != 0.0) { |
| + ++direct_nonzero; |
| + } |
| + |
| + direct_l2_squared += value*value; |
| + } |
| + |
| + if (!ok) { |
| + break; |
| + } |
| + } |
| + |
| + const double accumulator_l2 = |
| + std::sqrt(accumulator_l2_squared); |
| + |
| + const double direct_l2 = |
| + std::sqrt(direct_l2_squared); |
| + |
| + const bool use_accumulator = |
| + accumulator_nonzero > 0 && |
| + std::isfinite(accumulator_l2) && |
| + accumulator_l2 > 0.0; |
| + |
| + const bool use_direct = |
| + !use_accumulator && |
| + direct_nonzero > 0 && |
| + std::isfinite(direct_l2) && |
| + direct_l2 > 0.0; |
| + |
| + const char * source_name = |
| + use_accumulator |
| + ? "ACCUMULATOR" |
| + : use_direct |
| + ? "DIRECT_HOST_SUM" |
| + : "NONE"; |
| + |
| + std::cerr |
| + << std::setprecision(17) |
| + << "PRISM_STEP10_GRADIENT_SOURCE=" |
| + << source_name << "\n" |
| + << "PRISM_STEP10_ACCUMULATOR_NONZERO=" |
| + << accumulator_nonzero << "\n" |
| + << "PRISM_STEP10_ACCUMULATOR_L2=" |
| + << accumulator_l2 << "\n" |
| + << "PRISM_STEP10_DIRECT_SUM_NONZERO=" |
| + << direct_nonzero << "\n" |
| + << "PRISM_STEP10_DIRECT_SUM_L2=" |
| + << direct_l2 << "\n"; |
| + |
| + if (!use_accumulator && !use_direct) { |
| + std::cerr |
| + << "PRISM_STEP10_ZERO_USEFUL_GRADIENT=1\n"; |
| + ok = false; |
| + } |
| + |
| + size_t output_offset = 0; |
| + |
| + for (size_t i = 0; ok && i < n_params; ++i) { |
| + const size_t count = ggml_nelements(params[i]); |
| + |
| + size_t param_nonzero = 0; |
| + double param_max_abs = 0.0; |
| + double param_l2_squared = 0.0; |
| + |
| + for (size_t j = 0; j < count; ++j) { |
| + const size_t index = param_offsets[i] + j; |
| + |
| + const double raw_value = |
| + use_accumulator |
| + ? static_cast<double>(accumulator_values[index]) |
| + : direct_host_sum[index]; |
| + |
| + const double scaled_value = |
| + raw_value*static_cast<double>(gradient_scale); |
| + |
| + if (!std::isfinite(scaled_value)) { |
| + ok = false; |
| + break; |
| + } |
| + |
| + gradients_out[output_offset + j] = |
| + static_cast<float>(scaled_value); |
| + |
| + if (scaled_value != 0.0) { |
| + ++param_nonzero; |
| + } |
| + |
| + param_max_abs = std::max( |
| + param_max_abs, |
| + std::abs(scaled_value)); |
| + |
| + param_l2_squared += |
| + scaled_value*scaled_value; |
| + } |
| + |
| + std::cerr |
| + << std::setprecision(17) |
| + << "PRISM_STEP10_PARAM_GRAD " |
| + << "param_index=" << i |
| + << " name='" << params[i]->name << "'" |
| + << " source=" << source_name |
| + << " nonzero=" << param_nonzero |
| + << " max_abs=" << param_max_abs |
| + << " l2=" << std::sqrt(param_l2_squared) |
| + << "\n"; |
| + |
| + if (param_nonzero == 0 || |
| + !std::isfinite(param_max_abs) || |
| + param_max_abs <= 0.0) { |
| + std::cerr |
| + << "PRISM_STEP10_ZERO_PARAM_GRAD " |
| + << "param_index=" << i |
| + << " name='" << params[i]->name << "'\n"; |
| + ok = false; |
| + } |
| + |
| + output_offset += count; |
| + } |
| + } |
| + // PRISM_STEP10_V44_GRADIENT_SOURCE_ARBITRATION_END |
| + |
| + stats->loss = loss; |
| + stats->supervised_tokens = supervised; |
| + stats->ubatches = ubatches_total; |
| + stats->gradient_scale = gradient_scale; |
| + |
| + ggml_opt_reset_gradient_cycle(opt_ctx); |
| + llama_batch_free(batch); |
| + ggml_opt_result_free(result); |
| + return ok && std::isfinite(loss); |
| +} |
| +// PRISM_STEP10_MASKED_SEQUENCE_IMPL_END |
| + |
| void llama_context::opt_epoch( |
| ggml_opt_dataset_t dataset, |
| ggml_opt_result_t result_train, |
| @@ -4453,6 +5041,34 @@ void llama_opt_epoch( |
| callback_eval); |
| } |
| |
| + |
| +// PRISM_STEP10_MASKED_SEQUENCE_C_API_BEGIN |
| +bool llama_opt_masked_sequence( |
| + struct llama_context * ctx, |
| + const llama_token * tokens, |
| + const llama_token * labels_sparse, |
| + const uint8_t * loss_mask, |
| + uint32_t n_tokens, |
| + bool train, |
| + struct ggml_tensor ** params, |
| + size_t n_params, |
| + float * gradients_out, |
| + size_t gradients_count, |
| + struct llama_opt_masked_stats * stats) { |
| + return ctx && ctx->opt_masked_sequence( |
| + tokens, |
| + labels_sparse, |
| + loss_mask, |
| + n_tokens, |
| + train, |
| + params, |
| + n_params, |
| + gradients_out, |
| + gradients_count, |
| + stats); |
| +} |
| +// PRISM_STEP10_MASKED_SEQUENCE_C_API_END |
| + |
| // |
| // ext |
| // |
| |
| |
| |
| |
| @@ -218,6 +218,21 @@ struct llama_context { |
| ggml_opt_epoch_callback callback_train, |
| ggml_opt_epoch_callback callback_eval); |
| |
| + |
| + // PRISM_STEP10_LLAMA_CONTEXT_API_BEGIN |
| + bool opt_masked_sequence( |
| + const llama_token * tokens, |
| + const llama_token * labels_sparse, |
| + const uint8_t * loss_mask, |
| + uint32_t n_tokens, |
| + bool train, |
| + struct ggml_tensor ** params, |
| + size_t n_params, |
| + float * gradients_out, |
| + size_t gradients_count, |
| + struct llama_opt_masked_stats * stats); |
| + // PRISM_STEP10_LLAMA_CONTEXT_API_END |
| + |
| void opt_epoch_iter( |
| ggml_opt_dataset_t dataset, |
| ggml_opt_result_t result, |
| |
| |
| |
| |
| @@ -15,6 +15,8 @@ |
| #include <cassert> |
| #include <cmath> |
| #include <cstring> |
| +#include <cstdlib> |
| +#include <cstdio> |
| #include <numeric> |
| #include <sstream> |
| #include <unordered_set> |
| @@ -692,31 +694,51 @@ void llm_graph_input_attn_cross::set_input(const llama_ubatch * ubatch) { |
| } |
| |
| void llm_graph_input_mem_hybrid::set_input(const llama_ubatch * ubatch) { |
| - mctx->get_attn()->set_input_k_idxs(inp_attn->self_k_idxs, ubatch); |
| - mctx->get_attn()->set_input_v_idxs(inp_attn->self_v_idxs, ubatch); |
| + const bool prism_no_kv = |
| + getenv("PRISM_Q1_LORA_TRAINING") != nullptr && |
| + getenv("PRISM_Q1_LORA_TRAINING_NO_KV_CACHE") != nullptr; |
| |
| - mctx->get_attn()->set_input_kq_mask(inp_attn->self_kq_mask, ubatch, cparams.causal_attn); |
| + const bool k_allocated = inp_attn->self_k_idxs && inp_attn->self_k_idxs->buffer; |
| + const bool v_allocated = inp_attn->self_v_idxs && inp_attn->self_v_idxs->buffer; |
| + const bool mask_allocated = inp_attn->self_kq_mask && inp_attn->self_kq_mask->buffer; |
| + |
| + // PRISM_Q1_LORA_HYBRID_NO_KV_INPUT_SKIP_V2 |
| + if (!prism_no_kv || k_allocated) { |
| + mctx->get_attn()->set_input_k_idxs(inp_attn->self_k_idxs, ubatch); |
| + } |
| + if (!prism_no_kv || v_allocated) { |
| + mctx->get_attn()->set_input_v_idxs(inp_attn->self_v_idxs, ubatch); |
| + } |
| + if (!prism_no_kv || mask_allocated) { |
| + mctx->get_attn()->set_input_kq_mask(inp_attn->self_kq_mask, ubatch, cparams.causal_attn); |
| + } |
| + |
| + if (prism_no_kv && (!k_allocated || !v_allocated || !mask_allocated)) { |
| + static int prism_skip_count = 0; |
| + if (prism_skip_count < 32) { |
| + fprintf(stderr, |
| + "PRISM_Q1_LORA_SKIP_NO_KV_INPUTS class=hybrid count=%d " |
| + "k_allocated=%d v_allocated=%d mask_allocated=%d\n", |
| + prism_skip_count + 1, (int) k_allocated, (int) v_allocated, (int) mask_allocated); |
| + } |
| + prism_skip_count++; |
| + } |
| |
| if (inp_attn->self_k_rot) { |
| mctx->get_attn()->set_input_k_rot(inp_attn->self_k_rot); |
| } |
| - |
| if (inp_attn->self_v_rot) { |
| mctx->get_attn()->set_input_v_rot(inp_attn->self_v_rot); |
| } |
| |
| const int64_t n_rs = mctx->get_recr()->get_n_rs(); |
| - |
| if (inp_rs->s_copy) { |
| GGML_ASSERT(ggml_backend_buffer_is_host(inp_rs->s_copy->buffer)); |
| int32_t * data = (int32_t *) inp_rs->s_copy->data; |
| - |
| - // assuming copy destinations ALWAYS happen ONLY on the cells between head and head+n |
| for (uint32_t i = 0; i < n_rs; ++i) { |
| data[i] = mctx->get_recr()->s_copy(i); |
| } |
| } |
| - |
| if (inp_rs->s_write_rows) { |
| mctx->get_recr()->set_input_s_write_rows(inp_rs->s_write_rows, inp_rs->s_write_rows_conv); |
| } |
| @@ -724,29 +746,30 @@ void llm_graph_input_mem_hybrid::set_input(const llama_ubatch * ubatch) { |
| |
| bool llm_graph_input_mem_hybrid::can_reuse(const llm_graph_params & params) { |
| const auto * mctx = static_cast<const llama_memory_hybrid_context *>(params.mctx); |
| - |
| this->mctx = mctx; |
| - |
| bool res = true; |
| |
| - res &= inp_attn->self_k_idxs->ne[0] == params.ubatch.n_tokens; |
| - //res &= inp_attn->self_v_idxs->ne[0] == params.ubatch.n_tokens; // TODO: need to move this to the unified cache and check there |
| + const bool prism_no_kv = |
| + getenv("PRISM_Q1_LORA_TRAINING") != nullptr && |
| + getenv("PRISM_Q1_LORA_TRAINING_NO_KV_CACHE") != nullptr; |
| |
| - res &= can_reuse_kq_mask(inp_attn->self_kq_mask, mctx->get_attn(), params.ubatch, params.cparams); |
| + // PRISM_Q1_LORA_HYBRID_NO_KV_REUSE_V2 |
| + if (!prism_no_kv || (inp_attn->self_k_idxs && inp_attn->self_k_idxs->buffer)) { |
| + res &= inp_attn->self_k_idxs->ne[0] == params.ubatch.n_tokens; |
| + } |
| + if (!prism_no_kv || (inp_attn->self_kq_mask && inp_attn->self_kq_mask->buffer)) { |
| + res &= can_reuse_kq_mask(inp_attn->self_kq_mask, mctx->get_attn(), params.ubatch, params.cparams); |
| + } |
| |
| res &= inp_rs->s_copy->ne[0] == mctx->get_recr()->get_n_rs(); |
| - |
| res &= inp_rs->s_copy_main->ne[0] == params.ubatch.n_seqs; |
| res &= inp_rs->s_copy_extra->ne[0] == mctx->get_recr()->get_n_rs() - params.ubatch.n_seqs; |
| - |
| if (inp_rs->s_write_rows) { |
| res &= inp_rs->s_write_rows->ne[0] == rs_n_write_rows(mctx->get_recr(), params.ubatch); |
| res &= !inp_rs->s_write_rows_conv || inp_rs->s_write_rows_conv->ne[0] == rs_n_write_rows(mctx->get_recr(), params.ubatch); |
| } |
| - |
| res &= inp_rs->head == mctx->get_recr()->get_head(); |
| res &= inp_rs->rs_z == mctx->get_recr()->get_rs_z(); |
| - |
| return res; |
| } |
| |
| @@ -754,22 +777,28 @@ bool llm_graph_input_mem_hybrid::can_reuse(const llm_graph_params & params) { |
| // Instead of creating a hybrid input, the graph can simply create 2 separate inputs. |
| // Refactoring is required in the future. |
| void llm_graph_input_mem_hybrid_k::set_input(const llama_ubatch * ubatch) { |
| - mctx->get_attn()->set_input_k_idxs(inp_attn->self_k_idxs, ubatch); |
| + const bool prism_no_kv = |
| + getenv("PRISM_Q1_LORA_TRAINING") != nullptr && |
| + getenv("PRISM_Q1_LORA_TRAINING_NO_KV_CACHE") != nullptr; |
| + const bool k_allocated = inp_attn->self_k_idxs && inp_attn->self_k_idxs->buffer; |
| + const bool mask_allocated = inp_attn->self_kq_mask && inp_attn->self_kq_mask->buffer; |
| |
| - mctx->get_attn()->set_input_kq_mask(inp_attn->self_kq_mask, ubatch, cparams.causal_attn); |
| + // PRISM_Q1_LORA_HYBRID_K_NO_KV_INPUT_SKIP_V2 |
| + if (!prism_no_kv || k_allocated) { |
| + mctx->get_attn()->set_input_k_idxs(inp_attn->self_k_idxs, ubatch); |
| + } |
| + if (!prism_no_kv || mask_allocated) { |
| + mctx->get_attn()->set_input_kq_mask(inp_attn->self_kq_mask, ubatch, cparams.causal_attn); |
| + } |
| |
| const int64_t n_rs = mctx->get_recr()->get_n_rs(); |
| - |
| if (inp_rs->s_copy) { |
| GGML_ASSERT(ggml_backend_buffer_is_host(inp_rs->s_copy->buffer)); |
| int32_t * data = (int32_t *) inp_rs->s_copy->data; |
| - |
| - // assuming copy destinations ALWAYS happen ONLY on the cells between head and head+n |
| for (uint32_t i = 0; i < n_rs; ++i) { |
| data[i] = mctx->get_recr()->s_copy(i); |
| } |
| } |
| - |
| if (inp_rs->s_write_rows) { |
| mctx->get_recr()->set_input_s_write_rows(inp_rs->s_write_rows, inp_rs->s_write_rows_conv); |
| } |
| @@ -777,28 +806,30 @@ void llm_graph_input_mem_hybrid_k::set_input(const llama_ubatch * ubatch) { |
| |
| bool llm_graph_input_mem_hybrid_k::can_reuse(const llm_graph_params & params) { |
| const auto * mctx = static_cast<const llama_memory_hybrid_context *>(params.mctx); |
| - |
| this->mctx = mctx; |
| - |
| bool res = true; |
| |
| - res &= inp_attn->self_k_idxs->ne[0] == params.ubatch.n_tokens; |
| + const bool prism_no_kv = |
| + getenv("PRISM_Q1_LORA_TRAINING") != nullptr && |
| + getenv("PRISM_Q1_LORA_TRAINING_NO_KV_CACHE") != nullptr; |
| |
| - res &= can_reuse_kq_mask(inp_attn->self_kq_mask, mctx->get_attn(), params.ubatch, params.cparams); |
| + // PRISM_Q1_LORA_HYBRID_K_NO_KV_REUSE_V1 |
| + if (!prism_no_kv || (inp_attn->self_k_idxs && inp_attn->self_k_idxs->buffer)) { |
| + res &= inp_attn->self_k_idxs->ne[0] == params.ubatch.n_tokens; |
| + } |
| + if (!prism_no_kv || (inp_attn->self_kq_mask && inp_attn->self_kq_mask->buffer)) { |
| + res &= can_reuse_kq_mask(inp_attn->self_kq_mask, mctx->get_attn(), params.ubatch, params.cparams); |
| + } |
| |
| res &= inp_rs->s_copy->ne[0] == mctx->get_recr()->get_n_rs(); |
| - |
| res &= inp_rs->s_copy_main->ne[0] == params.ubatch.n_seqs; |
| res &= inp_rs->s_copy_extra->ne[0] == mctx->get_recr()->get_n_rs() - params.ubatch.n_seqs; |
| - |
| if (inp_rs->s_write_rows) { |
| res &= inp_rs->s_write_rows->ne[0] == rs_n_write_rows(mctx->get_recr(), params.ubatch); |
| res &= !inp_rs->s_write_rows_conv || inp_rs->s_write_rows_conv->ne[0] == rs_n_write_rows(mctx->get_recr(), params.ubatch); |
| } |
| - |
| res &= inp_rs->head == mctx->get_recr()->get_head(); |
| res &= inp_rs->rs_z == mctx->get_recr()->get_rs_z(); |
| - |
| return res; |
| } |
| |
| @@ -1132,28 +1163,99 @@ ggml_tensor * llm_graph_context::build_lora_mm( |
| ggml_tensor * w, |
| ggml_tensor * cur, |
| ggml_tensor * w_s) const { |
| - ggml_tensor * res = ggml_mul_mat(ctx0, w, cur); |
| + // PRISM_Q1_LORA_REAL_LOADER_V2 |
| + // |
| + // Normal inference remains unchanged unless the explicit |
| + // training environment flag is enabled. |
| + const char * prism_training_env = |
| + std::getenv( |
| + "PRISM_Q1_LORA_TRAINING"); |
| + |
| + const bool prism_q1_lora_training = |
| + prism_training_env != nullptr |
| + && std::strcmp( |
| + prism_training_env, |
| + "0") != 0; |
| + |
| + ggml_tensor * res = |
| + ggml_mul_mat( |
| + ctx0, |
| + w, |
| + cur); |
| + |
| + // Q1 inference normally uses the fast MMQ route. |
| + // Native LoRA training requires the exact packed-Q1 × F32 |
| + // activation route so backward-X matches the linear forward. |
| + if ( |
| + prism_q1_lora_training |
| + && w->type == GGML_TYPE_Q1_0 |
| + ) { |
| + ggml_mul_mat_set_prec( |
| + res, |
| + GGML_PREC_F32); |
| + } |
| |
| for (const auto & lora : *loras) { |
| - llama_adapter_lora_weight * lw = lora.first->get_weight(w); |
| + llama_adapter_lora_weight * lw = |
| + lora.first->get_weight(w); |
| + |
| if (lw == nullptr) { |
| continue; |
| } |
| |
| - const float adapter_scale = lora.second; |
| - const float scale = lw->get_scale(lora.first->alpha, adapter_scale); |
| - |
| - ggml_tensor * ab_cur = ggml_mul_mat( |
| - ctx0, lw->b, |
| - ggml_mul_mat(ctx0, lw->a, cur) |
| - ); |
| - |
| - ab_cur = ggml_scale(ctx0, ab_cur, scale); |
| - res = ggml_add(ctx0, res, ab_cur); |
| + const float adapter_scale = |
| + lora.second; |
| + |
| + const float scale = |
| + lw->get_scale( |
| + lora.first->alpha, |
| + adapter_scale); |
| + |
| + if (prism_q1_lora_training) { |
| + LLAMA_LOG_INFO( |
| + "PRISM_Q1_LORA_GRAPH " |
| + "weight=%s exact_q1=%d " |
| + "a_param=%d b_param=%d\n", |
| + w->name, |
| + w->type == GGML_TYPE_Q1_0 ? 1 : 0, |
| + ( |
| + lw->a->flags |
| + & GGML_TENSOR_FLAG_PARAM |
| + ) ? 1 : 0, |
| + ( |
| + lw->b->flags |
| + & GGML_TENSOR_FLAG_PARAM |
| + ) ? 1 : 0); |
| + } |
| + |
| + ggml_tensor * ab_cur = |
| + ggml_mul_mat( |
| + ctx0, |
| + lw->b, |
| + ggml_mul_mat( |
| + ctx0, |
| + lw->a, |
| + cur)); |
| + |
| + ab_cur = |
| + ggml_scale( |
| + ctx0, |
| + ab_cur, |
| + scale); |
| + |
| + res = |
| + ggml_add( |
| + ctx0, |
| + res, |
| + ab_cur); |
| } |
| |
| if (w_s) { |
| - res = ggml_mul(ctx0, res, w_s); |
| + res = |
| + ggml_mul( |
| + ctx0, |
| + res, |
| + w_s); |
| } |
| |
| return res; |
| |
| |
| |
| |
| @@ -152,10 +152,10 @@ std::pair<ggml_tensor *, ggml_tensor *> llm_build_delta_net_base::build_delta_ne |
| attn = ggml_tri(ctx0, kb, GGML_TRI_TYPE_LOWER); |
| cb(attn, "attn", il); |
| |
| - ggml_tensor * identity; |
| - identity = ggml_view_1d(ctx0, attn, CS, 0); |
| - identity = ggml_fill (ctx0, identity, 1.0f); |
| - identity = ggml_diag (ctx0, identity); |
| + // PRISM_Q1_LORA_INDEPENDENT_DELTA_IDENTITY_V1 |
| + ggml_tensor * identity = ggml_new_tensor_1d(ctx0, GGML_TYPE_F32, CS); |
| + identity = ggml_fill(ctx0, identity, 1.0f); |
| + identity = ggml_diag(ctx0, identity); |
| |
| ggml_tensor * lhs = ggml_add(ctx0, attn, identity); |
| cb(lhs, "dnet_add_ch_lhs", il); |
| |
| |
| |
| |
| @@ -1,6 +1,42 @@ |
| #include "models.h" |
| #include "llama-memory-recurrent.h" |
| |
| +#include <cstdio> |
| +#include <cstdlib> |
| + |
| +// PRISM_Q1_LORA_GENERIC_SSM_CONV_V2 |
| +static ggml_tensor * prism_q1_lora_generic_ssm_conv( |
| + ggml_context * ctx, |
| + ggml_tensor * input, |
| + ggml_tensor * kernel) { |
| + GGML_ASSERT(input->type == GGML_TYPE_F32); |
| + GGML_ASSERT(kernel->type == GGML_TYPE_F32); |
| + GGML_ASSERT(input->ne[1] == kernel->ne[1]); |
| + GGML_ASSERT(input->ne[0] >= kernel->ne[0]); |
| + |
| + const int64_t kernel_size = kernel->ne[0]; |
| + const int64_t channels = input->ne[1]; |
| + const int64_t tokens = input->ne[0] - kernel_size + 1; |
| + const int64_t sequences = input->ne[2]; |
| + |
| + ggml_tensor * result = nullptr; |
| + for (int64_t tap = 0; tap < kernel_size; ++tap) { |
| + ggml_tensor * window = ggml_view_3d(ctx, input, |
| + tokens, channels, sequences, |
| + input->nb[1], input->nb[2], (size_t) tap*input->nb[0]); |
| + window = ggml_cont(ctx, ggml_transpose(ctx, window)); |
| + |
| + ggml_tensor * tap_weight = ggml_view_2d(ctx, kernel, |
| + 1, channels, kernel->nb[1], (size_t) tap*kernel->nb[0]); |
| + tap_weight = ggml_cont(ctx, ggml_transpose(ctx, tap_weight)); |
| + tap_weight = ggml_repeat(ctx, tap_weight, window); |
| + |
| + ggml_tensor * term = ggml_mul(ctx, window, tap_weight); |
| + result = result ? ggml_add(ctx, result, term) : term; |
| + } |
| + return result; |
| +} |
| + |
| void llama_model_qwen35::load_arch_hparams(llama_model_loader & ml) { |
| ml.get_key(LLM_KV_ATTENTION_LAYERNORM_RMS_EPS, hparams.f_norm_rms_eps); |
| ml.get_key_or_arr(LLM_KV_ROPE_DIMENSION_SECTIONS, hparams.rope_sections, 4, true); |
| @@ -393,9 +429,25 @@ ggml_tensor * llama_model_qwen35::graph::build_layer_attn( |
| // Attention computation |
| const float kq_scale = hparams.f_attention_scale == 0.0f ? 1.0f / sqrtf(float(n_embd_head)) : hparams.f_attention_scale; |
| |
| - cur = build_attn(inp, |
| - nullptr, nullptr, nullptr, |
| - Qcur, Kcur, Vcur, nullptr, nullptr, nullptr, kq_scale, il); |
| + if ( |
| + getenv("PRISM_Q1_LORA_TRAINING") != nullptr && |
| + getenv("PRISM_Q1_LORA_TRAINING_NO_KV_CACHE") != nullptr |
| + ) { |
| + // PRISM_Q1_LORA_NO_KV_ATTENTION_V2 |
| + static int prism_no_kv_attention_count = 0; |
| + if (prism_no_kv_attention_count < 128) { |
| + fprintf(stderr, "PRISM_Q1_LORA_NO_KV_ATTENTION enabled=1 layer=%d\n", il); |
| + } |
| + prism_no_kv_attention_count++; |
| + llm_graph_input_attn_no_cache * inp_no_cache = build_attn_inp_no_cache(); |
| + cur = build_attn(inp_no_cache, |
| + nullptr, nullptr, nullptr, |
| + Qcur, Kcur, Vcur, nullptr, nullptr, nullptr, kq_scale, il); |
| + } else { |
| + cur = build_attn(inp, |
| + nullptr, nullptr, nullptr, |
| + Qcur, Kcur, Vcur, nullptr, nullptr, nullptr, kq_scale, il); |
| + } |
| cb(cur, "attn_pregate", il); |
| |
| ggml_tensor * gate_sigmoid = ggml_sigmoid(ctx0, gate); |
| @@ -498,7 +550,20 @@ ggml_tensor * llama_model_qwen35::graph::build_layer_attn_linear( |
| cb(state, "state_predelta", il); |
| } |
| |
| - ggml_tensor * conv_output_proper = ggml_ssm_conv(ctx0, conv_input, conv_kernel); |
| + ggml_tensor * conv_output_proper; |
| + if ( |
| + getenv("PRISM_Q1_LORA_TRAINING") != nullptr && |
| + getenv("PRISM_Q1_LORA_TRAINING_GENERIC_SSM_CONV") != nullptr |
| + ) { |
| + static int prism_generic_conv_count = 0; |
| + if (prism_generic_conv_count < 128) { |
| + fprintf(stderr, "PRISM_Q1_LORA_GENERIC_SSM_CONV enabled=1 layer=%d\n", il); |
| + } |
| + prism_generic_conv_count++; |
| + conv_output_proper = prism_q1_lora_generic_ssm_conv(ctx0, conv_input, conv_kernel); |
| + } else { |
| + conv_output_proper = ggml_ssm_conv(ctx0, conv_input, conv_kernel); |
| + } |
| cb(conv_output_proper, "conv_output_raw", il); |
| |
| ggml_tensor * conv_output_silu = ggml_silu(ctx0, conv_output_proper); |
| |
| |
| |
| |
| @@ -367,3 +367,42 @@ llama_build(test-dspark-loop.cpp) |
| # file's header comment). Not wired into `ctest`: needs real multi-GB GGUFs |
| # passed on the command line; run manually on a GPU host. |
| llama_build(test-dspark-real-eval.cpp) |
| + |
| +# PRISM_Q1_LORA_OPTIMIZER_TEST_V1 |
| +llama_build(test-q1-lora-opt.cpp) |
| +# PRISM_Q1_LORA_INTERNAL_INCLUDE_V1 |
| +target_include_directories(test-q1-lora-opt PRIVATE ${PROJECT_SOURCE_DIR}/ggml/src) |
| + |
| +# PRISM_REAL_BONSAI_Q1_LORA_TARGET_V1 |
| +llama_build(test-bonsai-q1-lora-layers.cpp) |
| +target_include_directories(test-bonsai-q1-lora-layers PRIVATE ${PROJECT_SOURCE_DIR}/ggml/src) |
| + |
| +# PRISM_Q1_LORA_LOADER_BLOCK_TARGET_V2 |
| +llama_build(test-q1-lora-loader-blocks.cpp) |
| +target_include_directories( |
| + test-q1-lora-loader-blocks |
| + PRIVATE |
| + ${PROJECT_SOURCE_DIR}/src |
| + ${PROJECT_SOURCE_DIR}/ggml/src |
| +) |
| + |
| +# PRISM_Q1_LORA_COMPLETE_BACKWARD_TARGET_V1 |
| +llama_build(test-q1-lora-full-backward.cpp) |
| +target_include_directories( |
| + test-q1-lora-full-backward |
| + PRIVATE |
| + ${PROJECT_SOURCE_DIR}/src |
| + ${PROJECT_SOURCE_DIR}/ggml/src |
| +) |
| +llama_build(test-q1-lora-multitarget.cpp) |
| +llama_build(test-q1-lora-dataset.cpp) |
| +llama_build(test-q1-lora-step10.cpp) |
| +llama_build(test-q1-lora-step11.cpp) |
| +target_include_directories(test-q1-lora-multitarget PRIVATE ${PROJECT_SOURCE_DIR}/src) |
| +target_include_directories(test-q1-lora-step10 PRIVATE ${PROJECT_SOURCE_DIR}/src) |
| + |
| +# PRISM_STEP10_V481_ROUNDTRIP_TARGET |
| +add_executable(test-q1-lora-roundtrip test-q1-lora-roundtrip.cpp) |
| +target_link_libraries(test-q1-lora-roundtrip PRIVATE llama) |
| +target_include_directories(test-q1-lora-roundtrip PRIVATE ${CMAKE_SOURCE_DIR}/src) |
| +target_include_directories(test-q1-lora-step11 PRIVATE ${PROJECT_SOURCE_DIR}/src) |
| |
| |
| |
| |
| @@ -4348,6 +4348,126 @@ struct test_mul_mat_id_fusion : public test_case { |
| } |
| }; |
| |
| + |
| +// PRISM_Q1_EXACT_TRAIN_GRAPH_TEST_V1 |
| +// |
| +// This operation proves that setting GGML_PREC_F32 on a packed-Q1 |
| +// MUL_MAT selects the exact training-forward CUDA path. |
| +// |
| +// Only X is a gradient probe. The packed Q1 base is frozen. |
| + |
| +struct test_q1_exact_train_x : public test_case { |
| + const int64_t k; |
| + const int64_t m; |
| + const int64_t batch; |
| + |
| + test_q1_exact_train_x( |
| + int64_t k = 256, |
| + int64_t m = 16, |
| + int64_t batch = 2) |
| + : k(k), |
| + m(m), |
| + batch(batch) { |
| + } |
| + |
| + std::string op_desc( |
| + ggml_tensor * tensor) override { |
| + GGML_UNUSED(tensor); |
| + return "Q1_EXACT_TRAIN_X"; |
| + } |
| + |
| + std::string vars() override { |
| + return |
| + VAR_TO_STR(k) |
| + + "," |
| + + VAR_TO_STR(m) |
| + + "," |
| + + VAR_TO_STR(batch); |
| + } |
| + |
| + // PRISM_Q1_EXACT_CROSS_BACKEND_TOLERANCE_V1 |
| + // This threshold applies only to test mode's |
| + // generic cross-backend Q1 comparison. The exact |
| + // dequantized oracle is validated independently in |
| + // Step 2, and gradient mode retains max_maa_err(). |
| + double max_nmse_err() override { |
| + return 1.0000000000e-04; |
| + } |
| + |
| + double max_maa_err() override { |
| + return 2e-3; |
| + } |
| + |
| + float grad_eps() override { |
| + return 3e-2f; |
| + } |
| + |
| + bool grad_precise() override { |
| + return true; |
| + } |
| + |
| + int64_t grad_nmax() override { |
| + return 1024; |
| + } |
| + |
| + bool run_whole_graph() override { |
| + return true; |
| + } |
| + |
| + ggml_tensor * build_graph( |
| + ggml_context * ctx) override { |
| + GGML_ASSERT( |
| + k |
| + % ggml_blck_size( |
| + GGML_TYPE_Q1_0) |
| + == 0); |
| + |
| + ggml_tensor * x = |
| + ggml_new_tensor_2d( |
| + ctx, |
| + GGML_TYPE_F32, |
| + k, |
| + batch); |
| + |
| + ggml_set_name( |
| + x, |
| + "q1_exact_train.x"); |
| + |
| + ggml_set_param(x); |
| + |
| + ggml_tensor * base_weight = |
| + ggml_new_tensor_2d( |
| + ctx, |
| + GGML_TYPE_Q1_0, |
| + k, |
| + m); |
| + |
| + ggml_set_name( |
| + base_weight, |
| + "q1_exact_train.base_q1"); |
| + |
| + // Deliberately frozen: |
| + // no ggml_set_param(base_weight). |
| + |
| + ggml_tensor * output = |
| + ggml_mul_mat( |
| + ctx, |
| + base_weight, |
| + x); |
| + |
| + ggml_set_name( |
| + output, |
| + "q1_exact_train.output"); |
| + |
| + // This is the opt-in training-forward selector. |
| + ggml_mul_mat_set_prec( |
| + output, |
| + GGML_PREC_F32); |
| + |
| + return output; |
| + } |
| +}; |
| + |
| // GGML_OP_OUT_PROD |
| struct test_out_prod : public test_case { |
| const ggml_type type_a; |
| @@ -7635,6 +7755,10 @@ static const ggml_type other_types[] = { |
| // Test cases for evaluation: should try to cover edge cases while using small input sizes to keep the runtime low |
| static std::vector<std::unique_ptr<test_case>> make_test_cases_eval() { |
| std::vector<std::unique_ptr<test_case>> test_cases; |
| + |
| + // PRISM_Q1_EXACT_TRAIN_GRAPH_TEST_V1 |
| + test_cases.emplace_back( |
| + new test_q1_exact_train_x()); |
| std::default_random_engine rng(0); |
| |
| // unary ops |
|
|