FP16 (Half Precision): In FP16 wird eine Gleitkommazahl mit 16 Bit dargestellt. Es besteht aus 1 Vorzeichenbit, 5 Bits für den Exponenten und 10 Bits für den Bruch (Mantisse). Dieses Format bietet eine höhere Präzision für die Darstellung von Bruchwerten innerhalb seines Bereichs.
BF16 (BFloat16): BF16 verwendet ebenfalls 16 Bit, jedoch mit einer anderen Verteilung. Es verfügt über 1 Vorzeichenbit, 8 Bits für den Exponenten und 7 Bits für die Mantisse. Dieses Format opfert eine gewisse Präzision im Bruchteil, um einen größeren Bereich von Exponenten zu berücksichtigen.
FP16 hat einen kleineren Bereich, aber eine höhere Präzision innerhalb dieses Bereichs aufgrund seiner 10-Bit-Mantisse.
BF16 hat einen größeren Bereich, aber eine geringere Präzision für Bruchwerte aufgrund seines 8-Bit-Exponenten und seiner 7-Bit-Mantisse.
Veranschaulichen wir anhand von Beispielen die Unterschiede zwischen FP16 und BF16 anhand von drei Beispielfällen. TensorFlow wird verwendet, um die Tests und den unten geteilten Code zu erstellen:
Originalwert: 0,0001 – Beide Methoden können darstellen
FP16: 0,00010001659393 (Binär: 0|00001|1010001110, Hex: 068E) – 10 Mantissen und 5 Exponenten
BF16: 0.00010013580322 (Binär: 0|01110001|1010010, Hex: 38D2) – 7 Mantisse und 8 Exponenten
Wie Sie sehen können, haben sie unterschiedliche Exponenten und Mantissen und können daher unterschiedlich dargestellt werden. Aber wir können sehen, dass FP16 es genauer und näher dargestellt hat.
Ursprünglicher Wert: 1e-08 (0,00000001)
FP16: 0,00000000000000 (Binär: 0|00000|0000000000, Hex: 0000)
BF16: 0,00000001001172 ( Binär: 0|01100100| 0101100, Hex: 322C)
Das ist ein sehr interessanter Fall. FP16 schlägt fehl und ergibt das Ergebnis 0, aber BF16 kann es mit einer speziellen Formatierung darstellen.
Ursprünglicher Wert: 100000,00001
FP16: inf (Binär: 0|11111|0000000000, Hex: 7C00)
BF16: 99840,00000000000000 (Binär: 0|10001111|1 000011, Hex: 47C3 )
Im obigen Fall schlägt FP16 fehl, da alle Exponentenbits voll werden und nicht ausreichen, um den Wert darzustellen. Allerdings funktioniert BF16
FP16 wird häufig für Deep-Learning-Training und Inferenz verwendet, insbesondere für Aufgaben, die eine hohe Präzision bei der Darstellung kleiner Bruchwerte innerhalb eines begrenzten Bereichs erfordern.
BF16 erfreut sich zunehmender Beliebtheit in Hardwarearchitekturen, die für maschinelle Lernaufgaben entwickelt wurden und von einem größeren Bereich darstellbarer Werte profitieren, selbst auf Kosten einer gewissen Präzision im Bruchteil. Dies ist besonders nützlich, wenn es um große Gradienten geht oder wenn die numerische Stabilität über einen weiten Bereich wichtiger ist als die Präzision kleiner Werte.
FP16 bietet eine höhere Präzision für Bruchwerte innerhalb eines kleineren Bereichs und eignet sich daher für Aufgaben, die eine genaue Darstellung kleiner Zahlen erfordern. BF16 hingegen bietet einen größeren Bereich auf Kosten einer gewissen Präzision, was es vorteilhaft für Aufgaben macht, die ein breiteres Wertespektrum umfassen oder bei denen numerische Stabilität über einen weiten Bereich von entscheidender Bedeutung ist. Die Wahl zwischen FP16 und BF16 hängt von den spezifischen Anforderungen der jeweiligen maschinellen Lernaufgabe ab.
Aus allen oben genannten Gründen erfordern FP16 und BF16 beim Stable Diffusion XL (SDXL)-Training leicht unterschiedliche Lernraten und ich finde, dass BF16 besser funktioniert.
Der Code, der zum Generieren der obigen Beispiele verwendet wird
import tensorflow as tf import struct def float_to_binary(f): return ''.join(f'{b:08b}' for b in struct.pack('>f', f)) def display_fp16(value): fp16 = tf.cast(tf.constant(value, dtype=tf.float32), tf.float16) fp32 = tf.cast(fp16, tf.float32) binary = format(int.from_bytes(fp16.numpy().tobytes(), 'big'), '016b') sign = binary[0] exponent = binary[1:6] fraction = binary[6:] return f"FP16: {fp32.numpy():14.14f} (Binary: {sign}|{exponent}|{fraction}, Hex: {fp16.numpy().view('uint16'):04X})" def display_bf16(value): bf16 = tf.cast(tf.constant(value, dtype=tf.float32), tf.bfloat16) bf32 = tf.cast(bf16, tf.float32) binary = format(int.from_bytes(bf16.numpy().tobytes(), 'big'), '016b') sign = binary[0] exponent = binary[1:9] fraction = binary[9:] return f"BF16: {bf32.numpy():14.14f} (Binary: {sign}|{exponent}|{fraction}, Hex: {bf16.numpy().view('uint16'):04X})" values = [0.0001, 0.00000001, 100000.00001] for value in values: print(f"\nOriginal value: {value}") print(display_fp16(value)) print(display_bf16(value))
Haftungsausschluss: Alle bereitgestellten Ressourcen stammen teilweise aus dem Internet. Wenn eine Verletzung Ihres Urheberrechts oder anderer Rechte und Interessen vorliegt, erläutern Sie bitte die detaillierten Gründe und legen Sie einen Nachweis des Urheberrechts oder Ihrer Rechte und Interessen vor und senden Sie ihn dann an die E-Mail-Adresse: [email protected] Wir werden die Angelegenheit so schnell wie möglich für Sie erledigen.
Copyright© 2022 湘ICP备2022001581号-3