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.
This commit is contained in:
parent
e8e2b7e03f
commit
5b67a6cfbf
5 changed files with 14 additions and 31 deletions
|
@ -295,7 +295,7 @@ class Model:
|
||||||
|
|
||||||
if self.ftype != gguf.LlamaFileType.ALL_F32 and extra_f16 and not extra_f32:
|
if self.ftype != gguf.LlamaFileType.ALL_F32 and extra_f16 and not extra_f32:
|
||||||
if self.ftype == gguf.LlamaFileType.MOSTLY_BF16:
|
if self.ftype == gguf.LlamaFileType.MOSTLY_BF16:
|
||||||
data = gguf.truncate_bf16(data) if old_dtype == torch.bfloat16 else gguf.quantize_bf16(data)
|
data = gguf.quantize_bf16(data)
|
||||||
assert data.dtype == np.uint16
|
assert data.dtype == np.uint16
|
||||||
data_qtype = gguf.GGMLQuantizationType.BF16
|
data_qtype = gguf.GGMLQuantizationType.BF16
|
||||||
|
|
||||||
|
|
|
@ -80,8 +80,9 @@ static inline float ggml_compute_bf16_to_fp32(ggml_bf16_t h) {
|
||||||
/**
|
/**
|
||||||
* Converts float32 to brain16.
|
* Converts float32 to brain16.
|
||||||
*
|
*
|
||||||
* This function is binary identical to AMD Zen4 VCVTNEPS2BF16.
|
* This is binary identical with Google Brain float conversion.
|
||||||
* Subnormals shall be flushed to zero, and NANs will be quiet.
|
* Floats shall round to nearest even, and NANs shall be quiet.
|
||||||
|
* Subnormals aren't flushed to zero, except perhaps when used.
|
||||||
* This code should vectorize nicely if using modern compilers.
|
* This code should vectorize nicely if using modern compilers.
|
||||||
*/
|
*/
|
||||||
static inline ggml_bf16_t ggml_compute_fp32_to_bf16(float s) {
|
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) {
|
||||||
h.bits = (u.i >> 16) | 64; /* force to quiet */
|
h.bits = (u.i >> 16) | 64; /* force to quiet */
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
if (!(u.i & 0x7f800000)) { /* subnormal */
|
|
||||||
h.bits = (u.i & 0x80000000) >> 16; /* flush to zero */
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
h.bits = (u.i + (0x7fff + ((u.i >> 16) & 1))) >> 16;
|
h.bits = (u.i + (0x7fff + ((u.i >> 16) & 1))) >> 16;
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
11
ggml.c
11
ggml.c
|
@ -411,9 +411,16 @@ void ggml_bf16_to_fp32_row(const ggml_bf16_t * x, float * y, int64_t n) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ggml_fp32_to_bf16_row_reference(const float * x, ggml_bf16_t * y, int64_t n) {
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
y[i] = ggml_compute_fp32_to_bf16(x[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ggml_fp32_to_bf16_row(const float * x, ggml_bf16_t * y, int64_t n) {
|
void ggml_fp32_to_bf16_row(const float * x, ggml_bf16_t * y, int64_t n) {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
#if defined(__AVX512BF16__)
|
#if defined(__AVX512BF16__)
|
||||||
|
// subnormals are flushed to zero on this platform
|
||||||
for (; i + 32 <= n; i += 32) {
|
for (; i + 32 <= n; i += 32) {
|
||||||
_mm512_storeu_si512(
|
_mm512_storeu_si512(
|
||||||
(__m512i *)(y + i),
|
(__m512i *)(y + i),
|
||||||
|
@ -904,7 +911,7 @@ static const ggml_type_traits_t type_traits[GGML_TYPE_COUNT] = {
|
||||||
.is_quantized = false,
|
.is_quantized = false,
|
||||||
.to_float = (ggml_to_float_t) ggml_bf16_to_fp32_row,
|
.to_float = (ggml_to_float_t) ggml_bf16_to_fp32_row,
|
||||||
.from_float = (ggml_from_float_t) ggml_fp32_to_bf16_row,
|
.from_float = (ggml_from_float_t) ggml_fp32_to_bf16_row,
|
||||||
.from_float_reference = (ggml_from_float_t) ggml_fp32_to_bf16_row,
|
.from_float_reference = (ggml_from_float_t) ggml_fp32_to_bf16_row_reference,
|
||||||
.vec_dot = (ggml_vec_dot_t) ggml_vec_dot_bf16,
|
.vec_dot = (ggml_vec_dot_t) ggml_vec_dot_bf16,
|
||||||
.vec_dot_type = GGML_TYPE_BF16,
|
.vec_dot_type = GGML_TYPE_BF16,
|
||||||
.nrows = 1,
|
.nrows = 1,
|
||||||
|
@ -21334,7 +21341,7 @@ size_t ggml_quantize_chunk(
|
||||||
case GGML_TYPE_BF16:
|
case GGML_TYPE_BF16:
|
||||||
{
|
{
|
||||||
size_t elemsize = sizeof(ggml_bf16_t);
|
size_t elemsize = sizeof(ggml_bf16_t);
|
||||||
ggml_fp32_to_bf16_row(src + start, (ggml_bf16_t *)dst + start, n);
|
ggml_fp32_to_bf16_row_reference(src + start, (ggml_bf16_t *)dst + start, n);
|
||||||
result = n * elemsize;
|
result = n * elemsize;
|
||||||
} break;
|
} break;
|
||||||
case GGML_TYPE_F32:
|
case GGML_TYPE_F32:
|
||||||
|
|
1
ggml.h
1
ggml.h
|
@ -339,6 +339,7 @@ extern "C" {
|
||||||
GGML_API ggml_bf16_t ggml_fp32_to_bf16(float);
|
GGML_API ggml_bf16_t ggml_fp32_to_bf16(float);
|
||||||
GGML_API float ggml_bf16_to_fp32(ggml_bf16_t); // consider just doing << 16
|
GGML_API float ggml_bf16_to_fp32(ggml_bf16_t); // consider just doing << 16
|
||||||
GGML_API void ggml_bf16_to_fp32_row(const ggml_bf16_t *, float *, int64_t);
|
GGML_API void ggml_bf16_to_fp32_row(const ggml_bf16_t *, float *, int64_t);
|
||||||
|
GGML_API void ggml_fp32_to_bf16_row_reference(const float *, ggml_bf16_t *, int64_t);
|
||||||
GGML_API void ggml_fp32_to_bf16_row(const float *, ggml_bf16_t *, int64_t);
|
GGML_API void ggml_fp32_to_bf16_row(const float *, ggml_bf16_t *, int64_t);
|
||||||
|
|
||||||
struct ggml_object;
|
struct ggml_object;
|
||||||
|
|
|
@ -28,19 +28,11 @@ def __compute_fp32_to_bf16(n: np.ndarray) -> np.ndarray:
|
||||||
n = n.astype(np.float32, copy=False).view(np.uint32)
|
n = n.astype(np.float32, copy=False).view(np.uint32)
|
||||||
# force nan to quiet
|
# force nan to quiet
|
||||||
n = np.where((n & 0x7fffffff) > 0x7f800000, (n & np.uint32(0xffff0000)) | np.uint32(64 << 16), n)
|
n = np.where((n & 0x7fffffff) > 0x7f800000, (n & np.uint32(0xffff0000)) | np.uint32(64 << 16), n)
|
||||||
# flush subnormals to zero
|
|
||||||
n = np.where((n & 0x7f800000) == 0, n & np.uint32(0x80000000), n)
|
|
||||||
# round to nearest even
|
# round to nearest even
|
||||||
n = (np.uint64(n) + (0x7fff + ((n >> 16) & 1))) >> 16
|
n = (np.uint64(n) + (0x7fff + ((n >> 16) & 1))) >> 16
|
||||||
return n.astype(np.uint16)
|
return n.astype(np.uint16)
|
||||||
|
|
||||||
|
|
||||||
# for fp32 values that are just extended bf16
|
|
||||||
def __truncate_fp32_to_bf16(n: np.ndarray) -> np.ndarray:
|
|
||||||
n = n.astype(np.float32, copy=False).view(np.uint32) >> 16
|
|
||||||
return n.astype(np.uint16)
|
|
||||||
|
|
||||||
|
|
||||||
# This is faster than np.vectorize and np.apply_along_axis because it works on more than one row at a time
|
# This is faster than np.vectorize and np.apply_along_axis because it works on more than one row at a time
|
||||||
def __apply_over_grouped_rows(func: Callable[[np.ndarray], np.ndarray], arr: np.ndarray, otype: DTypeLike, oshape: tuple[int, ...]) -> np.ndarray:
|
def __apply_over_grouped_rows(func: Callable[[np.ndarray], np.ndarray], arr: np.ndarray, otype: DTypeLike, oshape: tuple[int, ...]) -> np.ndarray:
|
||||||
rows = arr.reshape((-1, arr.shape[-1]))
|
rows = arr.reshape((-1, arr.shape[-1]))
|
||||||
|
@ -68,20 +60,6 @@ def quantize_bf16(n: np.ndarray):
|
||||||
return __quantize_bf16_array(n)
|
return __quantize_bf16_array(n)
|
||||||
|
|
||||||
|
|
||||||
def __truncate_bf16_array(n: np.ndarray) -> np.ndarray:
|
|
||||||
return __apply_over_grouped_rows(__truncate_fp32_to_bf16, arr=n, otype=np.uint16, oshape=n.shape)
|
|
||||||
|
|
||||||
|
|
||||||
__truncate_bf16_lazy = LazyNumpyTensor._wrap_fn(__truncate_bf16_array, meta_noop=np.uint16)
|
|
||||||
|
|
||||||
|
|
||||||
def truncate_bf16(n: np.ndarray):
|
|
||||||
if type(n) is LazyNumpyTensor:
|
|
||||||
return __truncate_bf16_lazy(n)
|
|
||||||
else:
|
|
||||||
return __truncate_bf16_array(n)
|
|
||||||
|
|
||||||
|
|
||||||
__q8_block_size, __q8_type_size = GGML_QUANT_SIZES[GGMLQuantizationType.Q8_0]
|
__q8_block_size, __q8_type_size = GGML_QUANT_SIZES[GGMLQuantizationType.Q8_0]
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue