|
| 1 | +#include "cross_entropy_loss.hpp" |
| 2 | + |
| 3 | +#include <cstdint> |
| 4 | +#include <cmath> |
| 5 | + |
| 6 | +template <bool has_shared> |
| 7 | +static __dpct_inline__ void cross_entropy_loss_f32_kernel( |
| 8 | + const float * __restrict__ logits, |
| 9 | + const float * __restrict__ labels, |
| 10 | + float * __restrict__ row_loss, |
| 11 | + const int nclasses, |
| 12 | + const int nrows, |
| 13 | + float * __restrict__ smem, |
| 14 | + const sycl::nd_item<3> & item) { |
| 15 | + |
| 16 | + const int row = item.get_group(2); |
| 17 | + const int tid = item.get_local_id(2); |
| 18 | + |
| 19 | + logits += (int64_t) row * nclasses; |
| 20 | + labels += (int64_t) row * nclasses; |
| 21 | + |
| 22 | + float max_logit = -INFINITY; |
| 23 | + for (int i = tid; i < nclasses; i += WARP_SIZE) { |
| 24 | + const float v = logits[i]; |
| 25 | + max_logit = sycl::fmax(max_logit, v); |
| 26 | + if (has_shared) { |
| 27 | + smem[i] = v; |
| 28 | + } |
| 29 | + } |
| 30 | + max_logit = warp_reduce_max<WARP_SIZE>(max_logit); |
| 31 | + |
| 32 | + float sum_exp = 0.0f; |
| 33 | + for (int i = tid; i < nclasses; i += WARP_SIZE) { |
| 34 | + const float v = has_shared ? smem[i] : logits[i]; |
| 35 | + sum_exp += sycl::exp(v - max_logit); |
| 36 | + } |
| 37 | + sum_exp = warp_reduce_sum<WARP_SIZE>(sum_exp); |
| 38 | + const float log_sum = sycl::log(sum_exp); |
| 39 | + |
| 40 | + float loss = 0.0f; |
| 41 | + for (int i = tid; i < nclasses; i += WARP_SIZE) { |
| 42 | + const float v = has_shared ? smem[i] : logits[i]; |
| 43 | + loss += (v - max_logit - log_sum) * labels[i]; |
| 44 | + } |
| 45 | + loss = -warp_reduce_sum<WARP_SIZE>(loss) / (float) nrows; |
| 46 | + |
| 47 | + if (tid == 0) { |
| 48 | + row_loss[row] = loss; |
| 49 | + } |
| 50 | +} |
| 51 | + |
| 52 | +template <bool has_shared> |
| 53 | +static __dpct_inline__ void cross_entropy_loss_back_f32_kernel( |
| 54 | + const float * __restrict__ grad, |
| 55 | + const float * __restrict__ logits, |
| 56 | + const float * __restrict__ labels, |
| 57 | + float * __restrict__ dst, |
| 58 | + const int nclasses, |
| 59 | + const int nrows, |
| 60 | + float * __restrict__ smem, |
| 61 | + const sycl::nd_item<3> & item) { |
| 62 | + |
| 63 | + const int row = item.get_group(2); |
| 64 | + const int tid = item.get_local_id(2); |
| 65 | + |
| 66 | + logits += (int64_t) row * nclasses; |
| 67 | + labels += (int64_t) row * nclasses; |
| 68 | + dst += (int64_t) row * nclasses; |
| 69 | + |
| 70 | + float max_logit = -INFINITY; |
| 71 | + for (int i = tid; i < nclasses; i += WARP_SIZE) { |
| 72 | + const float v = logits[i]; |
| 73 | + max_logit = sycl::fmax(max_logit, v); |
| 74 | + if (has_shared) { |
| 75 | + smem[i] = v; |
| 76 | + } |
| 77 | + } |
| 78 | + max_logit = warp_reduce_max<WARP_SIZE>(max_logit); |
| 79 | + |
| 80 | + float sum_exp = 0.0f; |
| 81 | + for (int i = tid; i < nclasses; i += WARP_SIZE) { |
| 82 | + const float v = sycl::exp((has_shared ? smem[i] : logits[i]) - max_logit); |
| 83 | + sum_exp += v; |
| 84 | + if (has_shared) { |
| 85 | + smem[i] = v; |
| 86 | + } else { |
| 87 | + dst[i] = v; |
| 88 | + } |
| 89 | + } |
| 90 | + sum_exp = warp_reduce_sum<WARP_SIZE>(sum_exp); |
| 91 | + const float inv_sum = 1.0f / sum_exp; |
| 92 | + |
| 93 | + const float d_by_nrows = grad[0] / (float) nrows; |
| 94 | + for (int i = tid; i < nclasses; i += WARP_SIZE) { |
| 95 | + const float sm_num = has_shared ? smem[i] : dst[i]; |
| 96 | + dst[i] = (sm_num * inv_sum - labels[i]) * d_by_nrows; |
| 97 | + } |
| 98 | +} |
| 99 | + |
| 100 | +static void cross_entropy_reduce_rows( |
| 101 | + ggml_backend_sycl_context & ctx, |
| 102 | + const float * row_loss, |
| 103 | + float * dst, |
| 104 | + const int64_t nrows) { |
| 105 | + if (nrows == 1) { |
| 106 | + SYCL_CHECK(CHECK_TRY_ERROR( |
| 107 | + ctx.stream()->memcpy(dst, row_loss, sizeof(float)))); |
| 108 | + return; |
| 109 | + } |
| 110 | + |
| 111 | + ggml_sycl_pool_alloc<float> tmp_alloc(ctx.pool(), nrows); |
| 112 | + float * tmp = tmp_alloc.get(); |
| 113 | + SYCL_CHECK(CHECK_TRY_ERROR( |
| 114 | + ctx.stream()->memcpy(tmp, row_loss, nrows * sizeof(float)))); |
| 115 | + |
| 116 | + int64_t cur = nrows; |
| 117 | + while (cur > 1) { |
| 118 | + const int64_t out = (cur + WARP_SIZE - 1) / WARP_SIZE; |
| 119 | + const sycl::range<3> block(1, 1, WARP_SIZE); |
| 120 | + const sycl::range<3> grid(1, 1, out); |
| 121 | + ctx.stream()->parallel_for( |
| 122 | + sycl::nd_range<3>(grid * block, block), |
| 123 | + [=](sycl::nd_item<3> item) [[sycl::reqd_sub_group_size(WARP_SIZE)]] { |
| 124 | + const int row = item.get_group(2); |
| 125 | + const int tid = item.get_local_id(2); |
| 126 | + const int64_t i = (int64_t) row * WARP_SIZE + tid; |
| 127 | + float v = i < cur ? tmp[i] : 0.0f; |
| 128 | + v = warp_reduce_sum<WARP_SIZE>(v); |
| 129 | + if (tid == 0) { |
| 130 | + tmp[row] = v; |
| 131 | + } |
| 132 | + }); |
| 133 | + cur = out; |
| 134 | + } |
| 135 | + |
| 136 | + SYCL_CHECK(CHECK_TRY_ERROR( |
| 137 | + ctx.stream()->memcpy(dst, tmp, sizeof(float)))); |
| 138 | +} |
| 139 | + |
| 140 | +void ggml_sycl_cross_entropy_loss(ggml_backend_sycl_context & ctx, ggml_tensor * dst) { |
| 141 | + scope_op_debug_print scope_dbg_print(__func__, dst, /*num_src=*/2); |
| 142 | + |
| 143 | + const ggml_tensor * src0 = dst->src[0]; |
| 144 | + const ggml_tensor * src1 = dst->src[1]; |
| 145 | + |
| 146 | + GGML_ASSERT(src0->type == GGML_TYPE_F32); |
| 147 | + GGML_ASSERT(src1->type == GGML_TYPE_F32); |
| 148 | + GGML_ASSERT(dst->type == GGML_TYPE_F32); |
| 149 | + GGML_ASSERT(ggml_is_contiguous(src0)); |
| 150 | + GGML_ASSERT(ggml_is_contiguous(src1)); |
| 151 | + GGML_ASSERT(ggml_is_contiguous(dst)); |
| 152 | + GGML_ASSERT(ggml_are_same_shape(src0, src1)); |
| 153 | + GGML_ASSERT(ggml_is_scalar(dst)); |
| 154 | + |
| 155 | + SYCL_CHECK(ggml_sycl_set_device(ctx.device)); |
| 156 | + |
| 157 | + const int64_t nclasses = src0->ne[0]; |
| 158 | + const int64_t nrows = ggml_nrows(src0); |
| 159 | + |
| 160 | + const float * logits_d = (const float *) src0->data; |
| 161 | + const float * labels_d = (const float *) src1->data; |
| 162 | + float * dst_d = (float *) dst->data; |
| 163 | + |
| 164 | + ggml_sycl_pool_alloc<float> row_loss_alloc(ctx.pool(), nrows); |
| 165 | + float * row_loss = row_loss_alloc.get(); |
| 166 | + |
| 167 | + const sycl::range<3> block(1, 1, WARP_SIZE); |
| 168 | + const sycl::range<3> grid(1, 1, nrows); |
| 169 | + const size_t nbytes_shared = (size_t) nclasses * sizeof(float); |
| 170 | + const size_t smpbo = ggml_sycl_info().devices[ctx.device].smpbo; |
| 171 | + |
| 172 | + if (nbytes_shared <= smpbo) { |
| 173 | + ctx.stream()->submit([&](sycl::handler & cgh) { |
| 174 | + sycl::local_accessor<float, 1> smem(sycl::range<1>(nclasses), cgh); |
| 175 | + cgh.parallel_for( |
| 176 | + sycl::nd_range<3>(grid * block, block), |
| 177 | + [=](sycl::nd_item<3> item) [[sycl::reqd_sub_group_size(WARP_SIZE)]] { |
| 178 | + cross_entropy_loss_f32_kernel<true>( |
| 179 | + logits_d, labels_d, row_loss, |
| 180 | + (int) nclasses, (int) nrows, |
| 181 | + get_pointer(smem), item); |
| 182 | + }); |
| 183 | + }); |
| 184 | + } else { |
| 185 | + ctx.stream()->parallel_for( |
| 186 | + sycl::nd_range<3>(grid * block, block), |
| 187 | + [=](sycl::nd_item<3> item) [[sycl::reqd_sub_group_size(WARP_SIZE)]] { |
| 188 | + cross_entropy_loss_f32_kernel<false>( |
| 189 | + logits_d, labels_d, row_loss, |
| 190 | + (int) nclasses, (int) nrows, |
| 191 | + nullptr, item); |
| 192 | + }); |
| 193 | + } |
| 194 | + |
| 195 | + cross_entropy_reduce_rows(ctx, row_loss, dst_d, nrows); |
| 196 | +} |
| 197 | + |
| 198 | +void ggml_sycl_cross_entropy_loss_back(ggml_backend_sycl_context & ctx, ggml_tensor * dst) { |
| 199 | + scope_op_debug_print scope_dbg_print(__func__, dst, /*num_src=*/3); |
| 200 | + |
| 201 | + const ggml_tensor * grad = dst->src[0]; |
| 202 | + const ggml_tensor * src0f = dst->src[1]; |
| 203 | + const ggml_tensor * src1f = dst->src[2]; |
| 204 | + |
| 205 | + GGML_ASSERT(grad->type == GGML_TYPE_F32); |
| 206 | + GGML_ASSERT(src0f->type == GGML_TYPE_F32); |
| 207 | + GGML_ASSERT(src1f->type == GGML_TYPE_F32); |
| 208 | + GGML_ASSERT(dst->type == GGML_TYPE_F32); |
| 209 | + |
| 210 | + GGML_ASSERT(ggml_is_scalar(grad)); |
| 211 | + GGML_ASSERT(ggml_is_contiguous(grad)); |
| 212 | + GGML_ASSERT(ggml_is_contiguous(src0f)); |
| 213 | + GGML_ASSERT(ggml_is_contiguous(src1f)); |
| 214 | + GGML_ASSERT(ggml_is_contiguous(dst)); |
| 215 | + GGML_ASSERT(ggml_are_same_shape(src0f, src1f)); |
| 216 | + GGML_ASSERT(ggml_are_same_shape(src0f, dst)); |
| 217 | + |
| 218 | + SYCL_CHECK(ggml_sycl_set_device(ctx.device)); |
| 219 | + |
| 220 | + const int64_t nclasses = src0f->ne[0]; |
| 221 | + const int64_t nrows = ggml_nrows(src0f); |
| 222 | + |
| 223 | + const float * grad_d = (const float *) grad->data; |
| 224 | + const float * logits_d = (const float *) src0f->data; |
| 225 | + const float * labels_d = (const float *) src1f->data; |
| 226 | + float * dst_d = (float *) dst->data; |
| 227 | + |
| 228 | + const sycl::range<3> block(1, 1, WARP_SIZE); |
| 229 | + const sycl::range<3> grid(1, 1, nrows); |
| 230 | + const size_t nbytes_shared = (size_t) nclasses * sizeof(float); |
| 231 | + const size_t smpbo = ggml_sycl_info().devices[ctx.device].smpbo; |
| 232 | + |
| 233 | + if (nbytes_shared <= smpbo) { |
| 234 | + ctx.stream()->submit([&](sycl::handler & cgh) { |
| 235 | + sycl::local_accessor<float, 1> smem(sycl::range<1>(nclasses), cgh); |
| 236 | + cgh.parallel_for( |
| 237 | + sycl::nd_range<3>(grid * block, block), |
| 238 | + [=](sycl::nd_item<3> item) [[sycl::reqd_sub_group_size(WARP_SIZE)]] { |
| 239 | + cross_entropy_loss_back_f32_kernel<true>( |
| 240 | + grad_d, logits_d, labels_d, dst_d, |
| 241 | + (int) nclasses, (int) nrows, |
| 242 | + get_pointer(smem), item); |
| 243 | + }); |
| 244 | + }); |
| 245 | + } else { |
| 246 | + ctx.stream()->parallel_for( |
| 247 | + sycl::nd_range<3>(grid * block, block), |
| 248 | + [=](sycl::nd_item<3> item) [[sycl::reqd_sub_group_size(WARP_SIZE)]] { |
| 249 | + cross_entropy_loss_back_f32_kernel<false>( |
| 250 | + grad_d, logits_d, labels_d, dst_d, |
| 251 | + (int) nclasses, (int) nrows, |
| 252 | + nullptr, item); |
| 253 | + }); |
| 254 | + } |
| 255 | +} |
0 commit comments