Skip to content

Commit 55edb2d

Browse files
authored
[SYCL] support OP cross_entropy_loss, cross_entropy_loss_back (ggml-org#25236)
* support OP cross_entropy_loss, cross_entropy_loss_back * correct format issue
1 parent d209086 commit 55edb2d

5 files changed

Lines changed: 278 additions & 6 deletions

File tree

docs/ops.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ Legend:
3535
| COS |||||||| 🟡 ||||
3636
| COUNT_EQUAL ||||||||||||
3737
| CPY || 🟡 | 🟡 | 🟡 | 🟡 | 🟡 | 🟡 | 🟡 | 🟡 |||
38-
| CROSS_ENTROPY_LOSS ||||||| |||||
39-
| CROSS_ENTROPY_LOSS_BACK ||||||| |||||
38+
| CROSS_ENTROPY_LOSS ||||||| |||||
39+
| CROSS_ENTROPY_LOSS_BACK ||||||| |||||
4040
| CUMSUM ||||||||||||
4141
| DIAG ||||||||||||
4242
| DIAG_MASK_INF |||||| 🟡 ||||||

docs/ops/SYCL.csv

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,7 @@
407407
"SYCL0","GET_ROWS","type=i32,n=256,m=5,r=4,be1=7,be2=1,v=0","support","1","yes","SYCL"
408408
"SYCL0","GET_ROWS","type=i32,n=256,m=5,r=4,be1=7,be2=1,v=1","support","1","yes","SYCL"
409409
"SYCL0","GET_ROWS_BACK","type=f32,n=1,m=8,r=2,b=1,v=0","support","0","no","SYCL"
410+
"SYCL0","GET_ROWS_BACK","type=f32,n=1,m=70000,r=4,b=1,v=0","support","0","no","SYCL"
410411
"SYCL0","GET_ROWS_BACK","type=f32,n=256,m=5,r=4,b=1,v=0","support","0","no","SYCL"
411412
"SYCL0","GET_ROWS_BACK","type=f32,n=256,m=5,r=4,b=1,v=1","support","0","no","SYCL"
412413
"SYCL0","GET_ROWS_BACK","type=f16,n=256,m=5,r=4,b=1,v=0","support","0","no","SYCL"
@@ -16747,10 +16748,10 @@ zjy 2
1674716748
"SYCL0","FLASH_ATTN_EXT","hsk=128,hsv=64,nh=4,nr23=[1,1],kv=128,nb=2,mask=1,sinks=0,max_bias=0.000000,logit_softcap=0.000000,prec=f32,type_K=q1_0,type_V=q4_0,permute=[0,1,2,3]","support","0","no","SYCL"
1674816749
"SYCL0","FLASH_ATTN_EXT","hsk=64,hsv=128,nh=4,nr23=[1,1],kv=128,nb=2,mask=1,sinks=0,max_bias=0.000000,logit_softcap=0.000000,prec=f32,type_K=q4_0,type_V=q1_0,permute=[0,1,2,3]","support","0","no","SYCL"
1674916750
"SYCL0","FLASH_ATTN_EXT","hsk=128,hsv=64,nh=4,nr23=[1,1],kv=64,nb=2,mask=1,sinks=0,max_bias=0.000000,logit_softcap=0.000000,prec=f32,type_K=q1_0,type_V=f16,permute=[0,1,2,3]","support","0","no","SYCL"
16750-
"SYCL0","CROSS_ENTROPY_LOSS","type=f32,ne=[10,5,4,3]","support","0","no","SYCL"
16751-
"SYCL0","CROSS_ENTROPY_LOSS","type=f32,ne=[30000,1,1,1]","support","0","no","SYCL"
16752-
"SYCL0","CROSS_ENTROPY_LOSS_BACK","type=f32,ne=[10,5,4,3]","support","0","no","SYCL"
16753-
"SYCL0","CROSS_ENTROPY_LOSS_BACK","type=f32,ne=[30000,1,1,1]","support","0","no","SYCL"
16751+
"SYCL0","CROSS_ENTROPY_LOSS","type=f32,ne=[10,5,4,3]","support","1","yes","SYCL"
16752+
"SYCL0","CROSS_ENTROPY_LOSS","type=f32,ne=[30000,1,1,1]","support","1","yes","SYCL"
16753+
"SYCL0","CROSS_ENTROPY_LOSS_BACK","type=f32,ne=[10,5,4,3]","support","1","yes","SYCL"
16754+
"SYCL0","CROSS_ENTROPY_LOSS_BACK","type=f32,ne=[30000,1,1,1]","support","1","yes","SYCL"
1675416755
"SYCL0","OPT_STEP_ADAMW","type=f32,ne=[10,5,4,3]","support","0","no","SYCL"
1675516756
"SYCL0","OPT_STEP_SGD","type=f32,ne=[10,5,4,3]","support","0","no","SYCL"
1675616757
"SYCL0","GATED_DELTA_NET","type=f32,head_count=32,head_size=128,n_seq_tokens=1,n_seqs=1,v_repeat=1,permuted=0,kda=0,K=1","support","1","yes","SYCL"
Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
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+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#pragma once
2+
3+
#include "common.hpp"
4+
5+
void ggml_sycl_cross_entropy_loss(ggml_backend_sycl_context & ctx, ggml_tensor * dst);
6+
7+
void ggml_sycl_cross_entropy_loss_back(ggml_backend_sycl_context & ctx, ggml_tensor * dst);

ggml/src/ggml-sycl/ggml-sycl.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
#include "ggml-sycl/solve_tri.hpp"
7575
#include "ggml-sycl/gated_delta_net.hpp"
7676
#include "ggml-sycl/pool.hpp"
77+
#include "ggml-sycl/cross_entropy_loss.hpp"
7778

7879
#define MEM_SIZE_2M 0x00200000
7980
#define MEM_SIZE_1G 0x40000000
@@ -5078,6 +5079,12 @@ static bool ggml_sycl_compute_forward(ggml_backend_sycl_context & ctx, struct gg
50785079
case GGML_OP_SOFT_MAX_BACK:
50795080
ggml_sycl_op_soft_max_back(ctx, dst);
50805081
break;
5082+
case GGML_OP_CROSS_ENTROPY_LOSS:
5083+
ggml_sycl_cross_entropy_loss(ctx, dst);
5084+
break;
5085+
case GGML_OP_CROSS_ENTROPY_LOSS_BACK:
5086+
ggml_sycl_cross_entropy_loss_back(ctx, dst);
5087+
break;
50815088
case GGML_OP_ROPE:
50825089
ggml_sycl_rope(ctx, dst);
50835090
break;
@@ -5892,6 +5899,8 @@ static bool do_ggml_backend_sycl_device_supports_op(ggml_backend_dev_t dev, cons
58925899
case GGML_OP_FILL:
58935900
case GGML_OP_CUMSUM:
58945901
case GGML_OP_DIAG:
5902+
case GGML_OP_CROSS_ENTROPY_LOSS:
5903+
case GGML_OP_CROSS_ENTROPY_LOSS_BACK:
58955904
return true;
58965905
case GGML_OP_SOLVE_TRI:
58975906
return op->src[0]->ne[0] <= SYCL_SOLVE_TRI_MAX_N && op->src[1]->ne[0] <= SYCL_SOLVE_TRI_MAX_K;

0 commit comments

Comments
 (0)