Skip to content

Commit b72c20b

Browse files
CISCcompilade
andauthored
Fix conversion of unnormalized BF16->BF16 weights (#7843)
* add truncate_bf16 * truncate intermediate fp32 if converting bf16 to bf16 * fix masking in __compute_fp32_to_bf16 * np.int16 no longer used * missing cast and additional numpy 2.x fix * ggml-impl : do not flush bf16 subnormals to zero * ggml : add reference fp32 to bf16 conversion The fast version is no longer equivalent for all platforms because of the handling of subnormal values. * gguf-py : remove flush to zero for bf16 subnormals * gguf-py : remove float32 truncation to bf16 Rounding achieves the same thing in the cases where this was used. * missed prototype update in merge * merge cleanup --------- Co-authored-by: Francis Couture-Harpin <[email protected]>
1 parent e09a800 commit b72c20b

File tree

5 files changed

+20
-17
lines changed

5 files changed

+20
-17
lines changed

convert_hf_to_gguf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ def prepare_tensors(self):
316316
if self.ftype != gguf.LlamaFileType.ALL_F32 and extra_f16 and not extra_f32:
317317
if self.ftype == gguf.LlamaFileType.MOSTLY_BF16:
318318
data = gguf.quantize_bf16(data)
319-
assert data.dtype == np.int16
319+
assert data.dtype == np.uint16
320320
data_qtype = gguf.GGMLQuantizationType.BF16
321321

322322
elif self.ftype == gguf.LlamaFileType.MOSTLY_Q8_0 and gguf.can_quantize_to_q8_0(data):

ggml/include/ggml.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,7 @@ extern "C" {
349349
GGML_API ggml_bf16_t ggml_fp32_to_bf16(float);
350350
GGML_API float ggml_bf16_to_fp32(ggml_bf16_t); // consider just doing << 16
351351
GGML_API void ggml_bf16_to_fp32_row(const ggml_bf16_t *, float *, int64_t);
352+
GGML_API void ggml_fp32_to_bf16_row_ref(const float *, ggml_bf16_t *, int64_t);
352353
GGML_API void ggml_fp32_to_bf16_row(const float *, ggml_bf16_t *, int64_t);
353354

354355
struct ggml_object;

ggml/src/ggml-impl.h

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,9 @@ static inline float ggml_compute_bf16_to_fp32(ggml_bf16_t h) {
8080
/**
8181
* Converts float32 to brain16.
8282
*
83-
* This function is binary identical to AMD Zen4 VCVTNEPS2BF16.
84-
* Subnormals shall be flushed to zero, and NANs will be quiet.
83+
* This is binary identical with Google Brain float conversion.
84+
* Floats shall round to nearest even, and NANs shall be quiet.
85+
* Subnormals aren't flushed to zero, except perhaps when used.
8586
* This code should vectorize nicely if using modern compilers.
8687
*/
8788
static inline ggml_bf16_t ggml_compute_fp32_to_bf16(float s) {
@@ -95,10 +96,6 @@ static inline ggml_bf16_t ggml_compute_fp32_to_bf16(float s) {
9596
h.bits = (u.i >> 16) | 64; /* force to quiet */
9697
return h;
9798
}
98-
if (!(u.i & 0x7f800000)) { /* subnormal */
99-
h.bits = (u.i & 0x80000000) >> 16; /* flush to zero */
100-
return h;
101-
}
10299
h.bits = (u.i + (0x7fff + ((u.i >> 16) & 1))) >> 16;
103100
return h;
104101
}

ggml/src/ggml.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -480,9 +480,16 @@ void ggml_bf16_to_fp32_row(const ggml_bf16_t * x, float * y, int64_t n) {
480480
}
481481
}
482482

483+
void ggml_fp32_to_bf16_row_ref(const float * x, ggml_bf16_t * y, int64_t n) {
484+
for (int i = 0; i < n; i++) {
485+
y[i] = ggml_compute_fp32_to_bf16(x[i]);
486+
}
487+
}
488+
483489
void ggml_fp32_to_bf16_row(const float * x, ggml_bf16_t * y, int64_t n) {
484490
int i = 0;
485491
#if defined(__AVX512BF16__)
492+
// subnormals are flushed to zero on this platform
486493
for (; i + 32 <= n; i += 32) {
487494
_mm512_storeu_si512(
488495
(__m512i *)(y + i),
@@ -962,7 +969,7 @@ static const ggml_type_traits_t type_traits[GGML_TYPE_COUNT] = {
962969
.is_quantized = false,
963970
.to_float = (ggml_to_float_t) ggml_bf16_to_fp32_row,
964971
.from_float = (ggml_from_float_t) ggml_fp32_to_bf16_row,
965-
.from_float_ref = (ggml_from_float_t) ggml_fp32_to_bf16_row,
972+
.from_float_ref = (ggml_from_float_t) ggml_fp32_to_bf16_row_ref,
966973
.vec_dot = (ggml_vec_dot_t) ggml_vec_dot_bf16,
967974
.vec_dot_type = GGML_TYPE_BF16,
968975
.nrows = 1,
@@ -20650,7 +20657,7 @@ size_t ggml_quantize_chunk(
2065020657
case GGML_TYPE_BF16:
2065120658
{
2065220659
size_t elemsize = sizeof(ggml_bf16_t);
20653-
ggml_fp32_to_bf16_row(src + start, (ggml_bf16_t *)dst + start, n);
20660+
ggml_fp32_to_bf16_row_ref(src + start, (ggml_bf16_t *)dst + start, n);
2065420661
result = n * elemsize;
2065520662
} break;
2065620663
case GGML_TYPE_F32:

gguf-py/gguf/quants.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,12 @@ def quant_shape_from_byte_shape(shape: Sequence[int], quant_type: GGMLQuantizati
2525

2626
# same as ggml_compute_fp32_to_bf16 in ggml-impl.h
2727
def __compute_fp32_to_bf16(n: np.ndarray) -> np.ndarray:
28-
n = n.astype(np.float32, copy=False).view(np.int32)
28+
n = n.astype(np.float32, copy=False).view(np.uint32)
2929
# force nan to quiet
30-
n = np.where((n & 0x7fffffff) > 0x7f800000, (n & 0xffff0000) | (64 << 16), n)
31-
# flush subnormals to zero
32-
n = np.where((n & 0x7f800000) == 0, n & 0x80000000, n)
30+
n = np.where((n & 0x7fffffff) > 0x7f800000, (n & np.uint32(0xffff0000)) | np.uint32(64 << 16), n)
3331
# round to nearest even
34-
n = (n + (0x7fff + ((n >> 16) & 1))) >> 16
35-
return n.astype(np.int16)
32+
n = (np.uint64(n) + (0x7fff + ((n >> 16) & 1))) >> 16
33+
return n.astype(np.uint16)
3634

3735

3836
# This is faster than np.vectorize and np.apply_along_axis because it works on more than one row at a time
@@ -49,10 +47,10 @@ def __apply_over_grouped_rows(func: Callable[[np.ndarray], np.ndarray], arr: np.
4947

5048

5149
def __quantize_bf16_array(n: np.ndarray) -> np.ndarray:
52-
return __apply_over_grouped_rows(__compute_fp32_to_bf16, arr=n, otype=np.int16, oshape=n.shape)
50+
return __apply_over_grouped_rows(__compute_fp32_to_bf16, arr=n, otype=np.uint16, oshape=n.shape)
5351

5452

55-
__quantize_bf16_lazy = LazyNumpyTensor._wrap_fn(__quantize_bf16_array, meta_noop=np.int16)
53+
__quantize_bf16_lazy = LazyNumpyTensor._wrap_fn(__quantize_bf16_array, meta_noop=np.uint16)
5654

5755

5856
def quantize_bf16(n: np.ndarray):

0 commit comments

Comments
 (0)