Compression des données en virgule flottante
Existe-t-il des méthodes de compression sans perte qui peuvent être appliquées à des données chronologiques à virgule flottante, et qui seront nettement plus performantes, par exemple, en écrivant les données sous forme binaire dans un fichier et en les exécutant via gzip?
la réduction de la précision peut être acceptable, mais elle doit se faire de façon contrôlée (c.-à-d. que je dois être capable de fixer une limite sur le nombre de chiffres qui doivent être conservés)
je suis en train de travailler avec certains de gros fichiers de données qui sont des séries de corrélation double
s, décrivant un fonction du temps (c'est à dire les valeurs sont corrélées). Je n'ai généralement pas besoin de l'entière double
précision, mais je pourrais avoir besoin de plus que float
.
Puisqu'il y a des méthodes sans perte spécialisées pour les images/audio, je me demandais si quelque chose de Spécialisé existait pour cette situation.
Précision: je cherche des outils pratiques existants plutôt qu'un document décrivant comment mettre en oeuvre quelque chose comme ça. Quelque chose de comparable à gzip en vitesse excellent.
7 réponses
Vous pouvez avoir un coup d'oeil à ces ressources:
- la Compression sans perte de prédire les Valeurs à virgule Flottante
- Papiers par Martin Burtscher: le FPC double-précision Compression à Point flottant Algorithme et sa mise en Œuvre, Compression rapide sans perte de données scientifiques à virgule flottante et Compression à haut débit de Double précision Données À Virgule Flottante
Vous pouvez également essayer Logluv-TIFF comprimé pour cela, je pensais ne pas les avoir utilisés moi-même.
Voici quelques idées si vous voulez créer votre propre algorithme simple:
- Utiliser xor de la valeur actuelle à la valeur précédente pour obtenir un ensemble de bits décrivant la différence.
- divisez cette différence en deux parties: une partie est les "bits de mantissa" et une partie est les "bits d'exposant".
- utilisez l'encodage de longueur variable (nombre différent de bits/octets par valeur), ou n'importe quelle méthode de compression que vous choisissez, pour sauver ces différences. Vous pouvez utiliser flux séparés pour mantissas et exposants, depuis mantissas ont plus de bits à compresser.
- cela peut ne pas bien fonctionner si vous alternez entre deux sources de flux de valeurs de temps différentes. Il se peut donc que vous deviez comprimer chaque source dans un flux ou un bloc distinct.
- pour perdre la précision, vous pouvez laisser tomber les bits ou octets les moins significatifs de la mantissa, tout en laissant l'exposant intact.
puisque vous déclarez que vous avez besoin d'une précision quelque part entre 'float' et' double': vous pouvez mettre à zéro n'importe quel nombre de bits les moins significatifs dans des flotteurs à simple et double précision. La norme IEEE-754 virgule flottante chiffres sont représentés en binaire à peu près comme seeefffffffff
, qui représente la valeur
signe*1.fffffff * 2^(eee).
vous pouvez mettre à zéro les bits de la fraction (f) la moins significative. Pour les flotteurs de précision simple (32 bits), il y a 23 bits de fraction dont vous pouvez mettre à zéro 22. Pour une double précision (64 bits), c'est 52 et jusqu'à 51. (Si vous mettez tous les bits à zéro, alors les valeurs spéciales NaN et +/-inf seront perdues).
surtout si les données représentent des valeurs décimales telles que 1.2345, cela aidera à la compression des données. C'est parce que 1.2345 ne peut pas être représenté exactement comme une valeur flottante binaire, mais plutôt comme 0x3ff3c083126e978d
, qui n'est pas favorable à la compression de données. Couper les 24 bits les moins significatifs donnera 0x3ff3c08312000000
, qui est encore précis à environ 9 chiffres décimaux (dans cet exemple, la différence est de 1,6 e-9).
si vous faites cela sur les données brutes, et puis enregistrez les différences entre les nombres ultérieurs, il sera encore plus facile à la compression (via gzip) si les données brutes varient lentement.
Voici un exemple en C:
#include <inttypes.h>
double double_trunc(double x, int zerobits)
{
// mask is e.g. 0xffffffffffff0000 for zerobits==16
uint64_t mask = -(1LL << zerobits);
uint64_t floatbits = (*((uint64_t*)(&x)));
floatbits &= mask;
x = * ((double*) (&floatbits));
return x;
}
et un en python / numpy:
import numpy as np
def float_trunc(a, zerobits):
"""Set the least significant <zerobits> bits to zero in a numpy float32 or float64 array.
Do this in-place. Also return the updated array.
Maximum values of 'nzero': 51 for float64; 22 for float32.
"""
at = a.dtype
assert at == np.float64 or at == np.float32 or at == np.complex128 or at == np.complex64
if at == np.float64 or at == np.complex128:
assert nzero <= 51
mask = 0xffffffffffffffff - (1 << nzero) + 1
bits = a.view(np.uint64)
bits &= mask
elif at == np.float32 or at == np.complex64:
assert nzero <= 22
mask = 0xffffffff - (1 << nzero) + 1
bits = a.view(np.uint32)
bits &= mask
return a
une technique que les gens de HDF5 utilisent est "shuffling", où vous groupez chaque octet pour N valeurs de virgule flottante ensemble. Ceci est plus susceptible de vous donner des séquences répétitives d'octets qui compresseront mieux avec gzip,par exemple.
Une deuxième méthode que j'ai trouvé qui réduit considérablement la taille de gzippée de données est d'abord convertir les données à l' float16 (demi-précision) format et retour à float32. Ce produit beaucoup de des zéros dans le flux de sortie qui peuvent réduire la taille des fichiers d'environ 40-60% après compression. Une subtilité est que la valeur maximale de float16 est assez basse, donc vous pouvez vouloir mettre vos données à l'échelle en premier, par exemple en python
import numpy as np
import math
input = np.array(...)
# format can only hold 65504 maximum, so we scale input data
log2max = int(math.log(np.nanmax(input), 2))
scale = 2**(log2max - 14)
scaled = input * (1./scale)
# do the conversion to float16
temp_float16 = np.array(scaled, dtype=np.float16)
# convert back again and rescale
output = np.array(temp_float16, dtype=np.float32) * scale
certains tests suggèrent que la différence moyenne absolue entre l'entrée et la sortie pour certaines données est d'environ 0,00019 avec un maximum de 0,00048. Ceci est en ligne avec la précision 2**11 du mantissa.
vous pouvez utiliser L'algorithme de lissage exponentiel de Holt (qui est un algorithme de compression basé sur la prédiction). Attribuez d'abord un certain poids aux données et prédisez la valeur suivante.Si les deux données sont les mêmes, il produit beaucoup de zéro dans le MSB en faisant l'opération XOR
puisque vous demandez des outils existants, peut-être pdz fera l'affaire.
méthodes possibles pouvant être utilisées pour la compression en virgule flottante:
transposer 4xN pour float et 8xN pour double + lz77
Application: Floating point de compression dans TurboTranspose
voir également d'erreur lié à la compression avec perteprédicteur (ex. Méthode de contexte fini) + encodage(ex. "compression entière").
Application: compression en virgule flottante TurboPFor!--8-->dans la mesure du possible, Convertissez tous les nombres à virgule flottante en nombres entiers (ex. 1,63 - > 163),
puis utiliser la compression entière
Application: compression entière
vous pouvez tester toutes ces méthodes, avec vos données, en utilisant le icapp outil pour linux et windows.