Codage entropique à longueur variable
Transcription
Codage entropique à longueur variable
Codage entropique à longueur variable Olivier RIOUL ENST/COMELEC [email protected] 1 Description d’un système de codage à longueur variable On se donne une source discrète (données, fichier, . . . ) dont chaque symbole x prend une parmi M valeurs possibles {x1 , x2 , . . . , xM }. On appelle cela une source M -aire X. Une distribution de probabilité p(x) caractérise les statistiques de cette source, on la suppose connue (ou estimée) sous la forme {p1 , p2 , . . . , pM }, où pi est la probabilité d’occurrence du symbole xi . Le schéma de codage est illustré par la figure suivante : Code Source X - C - Codeur - Décodeur - X Le codeur code chaque symbole de source xi par un mot de code ci . Le code est, par définition, l’ensemble des mots de codes C = {c1 , . . . , cM }. Un code à longueur variable (VLC : Variable-Length Code) est tel que les différents mots de code n’ont pas nécessairement la même longueur, en bits. On note li la longueur en bits du mot de code ci . La distribution des longueurs du code est donc {l1 , l2 , . . . , lM }. Voici un exemple de code VLC pour M = 4 : xi 0 1 2 3 pi li 1/2 1 1/4 2 1/8 3 1/8 3 1 ci 0 10 110 111 Le décodeur reconstruit les symboles de source à partir de la séquence binaire des mots de codes. Le taux de codage (coding rate) R est le nombre moyen de bits codés par symbole de source, c’est à dire R= M X pi li . i=1 On peut interpréter, si on veut, R comme une moyenne de la longueur L d’un mot de code (selon la loi de probabilité de X) : R = E (L). Pour l’exemple ci-dessus, on a R= 1 2 3 3 + + + = 1.75 bits/symbole. 2 4 8 8 Quoi qu’il en soit, un code est donc d’autant plus efficace en compression que R est petit. Trouver le « meilleur » code, « optimiser » le système, c’est donc déterminer le (ou les) code(s) qui rendent R minimal pour une source donnée, et donc pour une distribution de probabilité {p1 , p2 , . . . , pM } fixée. Noter que les performances des codes ne dépendent que des distributions de longueurs. 2 Codes uniquement décodables Le but du codage de source sans pertes est de comprimer les données de telle façon que l’on puisse reconstruire parfaitement (sans pertes, sans erreur) la source au destinaaire. Pour cela, il faut que le décodage ait lieu sans ambiguïté, c’est à dire qu’une séquence codée donnée doit être interprétable de façon unique comme une succession (concaténation) de mots de codes déterminés. Un code permettant un tel décodage (sans ambiguïté) est qualifié d’uniquement décodable (u.d.). Formellement, cela signifie que si on a deux séquences de mots de code concaténés identiques : c1 c2 · · · ck = c01 c02 · · · c0l où les ci et c0j appartiennent au code C, alors les mots de code sont un à un identiques : k = l et ci = c0i , i = 1, . . . , k. Voici quatre exemples de codes : lesquels sont u.d. ? 2 xi 0 1 2 3 code 1 code 2 code 3 code 4 0 0 10 0 0 10 00 10 1 100 11 110 1 101 110 111 Le premier code est très performant (R = 1 !) mais évidemment inutilisable, car ambigu : pour être u.d., le code doit être « inversible », c’est à dire que les mots de code doivent être distincts. Ceci montre qu’il y a forcément une limite inférieure sur le taux R : dans cet exemple, la valeur R = 1 n’est pas atteignable. Le deuxième code est bien « inversible » mais pas u.d. : par exemple, la séquence codée C = 101010 . . . peut se décoder X = 130 . . . ou X = 301 . . .. Un tel code est donc également inutilisable. Le troisième code est plus intéressant : malgré les apparences, il est u.d. ! En effet, on peut imaginer un décodeur qui examine la parité du nombre de zéros qui suit la première séquence « 11 » ; ce n’est qu’après avoir re-lu un « 1 » qu’il peut correctement découper la séquence codée et fournir les symboles de source. Cet exemple montre que certains codes u.d. peuvent nécessiter une implantation complexe du décodeur, qui doit lire la séquence codée binaire suffisamment loin à l’avance pour décoder un symbole de source. 3 Codes instantanés et condition du préfixe Le quatrième code donné ci-dessus est, par contre, très simple à décoder ; De tels codes sont appelés codes instantanés, car le décodeur n’a besoin de lire que les li premiers bits d’une séquence codée pour pouvoir l’interpréter « instantanément » et de manière unique comme étant le mot de code ci , représentant le symbole xi . Un code instantané est caractérisé par la condition du préfixe : Aucun mot de code n’est le préfixe d’un autre mot de code (c’est à dire aucun ci ne débute un cj , j 6= i). Preuve: Il est facile de démontrer que la condition du préfixe caractérise bien un code instantané : cette condition est d’abord clairement suffisante, sinon un ci débuterait un autre cj et il y aurait ambiguïté lorsque le décodeur lit ci , de sorte qu’il ne pourrait pas conclure instantanément. Réciproquement, avec la condition du préfixe le décodage est clairement instantané, car dès qu’un mot de code ci est lu, il peut être décodé sans ambiguïté. Un code instantané est aussi appelé code à préfixe (prefix code) dans la littérature. Pour résumer le vocabulaire vu jusqu’ici on a les ensembles 3 emboîtés suivants : {codes VLC} ⊃ {codes inversibles} ⊃ {codes u.d.} ⊃ {codes instantanés}. 4 Inégalité de Kraft-McMillan Pour trouver le meilleur code pour une source donnée, il faut minimiser le taux R sous la contrainte que le code soit u.d. Afin de réaliser cette optimisation, on caractérise d’abord le fait qu’un code soit u.d. sur la distribution des longueurs : [McMillan, 1956] Tout code u.d. vérifie l’inégalité : M X 2−li 6 1 i=1 appelée inégalité de Kraft-McMillan. Preuve: Pour un code u.d., toute séquence de l bits peut se décomposer d’au plus une façon comme concaténation de mots de codes ci1 ci2 · · · cik où li1 + li2 + · · · + lik = l. Cela vient directement de la définition d’un code u.d. Le nombre total Nl (k) de concaténations possibles de k mots de codes donnant une séquence codée de longueur totale l bits ne peut donc pas dépasser le nombre total de séquences de l bits, qui est 2l . (Sinon, il y aurait forcément ambiguïté.) On a donc l’inégalité : Nl (k) 6 2l . Mais par ailleurs, si on développe la puissance kième (produit de k facteurs identiques) M X X xli1 +li2 +···+lik ( xli )k = i=1 i1 ,i2 ,...,ik et si on regroupe les termes de même puissance l = li1 + li2 + · · · + lik on trouve précisément Nl (k) puissances xl pour chaque l. Ainsi : M X X ( xli )k = Nl (k)xl i=1 l La somme au second membre va de l = klmin à l = klmax où lmin et lmax désignent les longueurs minimale et maximale d’un mot de code. On conclut facilement en faisant x = 1/2 et k → ∞ : on voit par exemple que M X p 2−li 6 k k(lmax − lmin ) + 1 → 1 i=1 quand k → ∞. 4 5 Algorithme de Kraft Pour l’instant l’inégalité de Kraft-McMillan n’est qu’une condition nécessaire pour qu’un code soit u.d. Mais il y a une réciproque : [Kraft, 1949] Si l’inégalité de Kraft-McMillan est vérifiée, alors il existe un code u.d., et même instantané, qui admette {l1 , l2 , . . . , lM } comme distribution de longueurs. Voici une preuve dont l’intérêt est de fournir un algorithme simple qui donne un code instantané {c1 , . . . , cM } à partir d’une distribution de longueurs {l1 , l2 , . . . , lM } vérifiant l’inégalité de Kraft-McMillan. (Il y a d’autres preuves classiques, qu’on trouve dans la littérature, qui utilisent la notion d’arbre binaire.) Preuve: On se donne une distribution de longueurs l1 6 l2 6 . . . 6 lM vérifiant l’inégalité de Kraft-McMillan1 . A chaque mot de code ci (à trouver) on associe le nombre c̄i = 0, ci ∈ [0, 1[ dont les décimales de l’écriture en base 2 est formée des bits de ci . On note Ii l’intervalle Ii = [c̄i ; c¯i + 2−li [. Par exemple, ci = 010 donne c̄i = 0, 010 = 14 . et Ii = [0, 010; 0, 011[= [ 41 ; 38 [ est l’ensemble des nombres de [0; 1[ dont les décimales en base 2 commencent par ci . Clairement, ci détermine l’intervalle Ii , et réciproquement : il suffit de tronquer le développement binaire de la limite gauche de l’intervalle (de longueur Li ) à log2 L1i bits pour retrouver ci . On peut alors traduire le fait qu’un code soit instantané sur les intervalles : Aucun ci ne débute un cj si et seulement si c̄j 6∈ Ii . Autrement dit le code est instantané si et seulement si les Ii sont des intervalles disjoints. L’inégalité de Kraft-McMillan revient précisément à dire que la somme des longueurs des Ii est 6 1. S’ils sont disjoints, on peut donc les mettre bout-à-bout en restant dans le segment [0, 1[. On en déduit facilement un algorithme de construction du code instantané : On met bout-à-bout les intervalles Ii , classés par longueurs décroissantes (li croissantes) dans le segment [0, 1[ en partant de la gauche : I1 I2 I3 ··· - 0.0 0.10 1 0.110 0.111 On peut toujours se ramener à un tel ordre croissant, quitte à permuter les symboles dans l’alphabet de la source. 5 On commence donc par c̄1 = 0.0 . . . 0, et on pose c̄i+1 = extrémité droite de Ii à chaque étape. Cela revient, pour chaque étape, à calculer ci + 1 (addition binaire) puis à compléter avec des zéros si nécessaire pour obtenir ci+1 de longueur li+1 > li . Comme les intervalles sont disjoints, le code est bien instantané. Un exemple vaut mieux qu’un grand discours : li 1 2 3 5 5 5 6 6 code 0 10 110 11100 11101 11110 111110 111111 Noter qu’avec cet algorithme on détecte automatiquement si si les li ne vérifient pas l’inégalité de Kraft-McMillan : on « dépasse » alors l’extrémité droite du segment [0, 1[, et on ne peut plus continuer. Par exemple : li 1 2 3 4 5 5 6 code 0 10 110 1110 11110 11111 Erreur 1 1 1 1 1 1 1 + + + + + + >1 2 4 8 16 32 32 64 Une conséquence importante de la caractérisation par l’inégalité de KraftMcMillan est que tout code u.d. peut être remplacé par un code instantané de même distribution de longueurs et donc de même taux. On peut donc limiter la recherche du meilleur code à l’ensemble des codes instantanés. C’est un soulagement : du coup, on pourra toujours choisir un code optimal pour lequel le décodage est instantané, donc très simple à implanter. 6 6 Recherche du code Optimal D’après ce qui précède, pour trouver le meilleur code pour une source donnée, il suffit de minimiser le taux R sur la distribution de longueurs uniquement, avec la contrainte donnée par l’inégalité de Kraft-McMillan : X X min{R = pi li | 2−li 6 1} i i On reconnait un problème classique qui se résout par la méthode des multiplicateurs de Lagrange. Appliquons (brutalement) la méthode du Lagrangien ; celui-ci s’écrit X X L= pi li − λ 2−li i i où λ est le multiplicateur de Lagrange. On doit annuler les dérivées ∂L = pi − λ0 2−li = 0 ∂li où λ0 est toujours constant (indépendant de i). Ainsi l’optimum est atteint les pi et 2−li sont proportionnels, pour une contrainte saturée P −llorsque i = 1. La constante de proportionnalité vaut forcément λ0 = 1 ; on i2 trouve donc que R est minimisé lorsque li = log2 1 , pi auquel cas on découvre avec stupéfaction que le taux minimal est l’entropie de la source : M X 1 H= pi log2 pi i=1 Seulement voilà, c’était trop beau : ce qu’on vient de faire est faux, puisque ce résultat ne donne pas, en général, des longueurs li entières ! Il fallait tenir compte de cette contrainte supplémentaire, et la méthode du Lagrangien était donc inadaptée. 7 Codes de Fano-Shannon On peut quand même exploiter ce qu’on vient de faire ; d’abord, l’entropie est atteinte dans le cas exceptionnel où les pi sont des puissances négatives de 2 (car alors li = log2 p1i est bien entier). Par exemple, le code 7 pi li 1/2 1 1/4 2 1/8 3 1/8 3 ci 0 10 110 111 est « optimalement optimal », puisqu’ici R = H. En général, un code, même optimal, n’atteint pas l’entropie à cause de la contrainte supplémentaire des longueurs entières ; son taux est forcément plus grand. Cela montre que le taux d’un code u.d. est toujours limité inférieurement par l’entropie : R>H . On comprend maintenant pourquoi la technique du codage sans pertes est aussi appelée codage entropique : on cherche à s’approcher l’entropie qui représente une borne inférieure sur le taux. Sans chercher à optimiser rigoureusement, peut-on trouver des codes dont les taux ne soient pas trop éloignés de l’entropie ? Il faut pour cela obtenir les longueurs entières ; une façon de faire est de prendre2 li = dlog2 1 e pi On vérifie immédiatement qu’avec ce choix, l’inégalité de Kraft-McMillan est vérifiée (puisque qu’on a pris soin d’arrondir vers le haut, de sorte que 2−li 6 pi ). On obtient la famille des codes de Fano-Shannon (1948) (qui vérifient bien l’inégalité de Kraft-McMillan) et pour lesquels on trouve H 6 R 6 H + 1. Preuve: Il suffit de prouver la deuxième inégalité : X X X 1 1 R= pi dlog2 e 6 pi log2 +1 =H + pi = H + 1. p p i i i i i d’où l’encadrement annoncé. On est donc au pire à un bit près de l’entropie. On verra ci-dessous qu’on ne peut pas améliorer cette distance à l’entropie pour des codes « scalaires ». En fait, on a présenté ici ces codes de Fano-Shannon que par souci pédagogique ; leur intérêt est surtout historique, depuis que Huffman a proposé un algorithme pour trouver les codes VLC optimaux, qui fait l’objet de la section suivante. 2 dxe désigne le plus petit entier > x. On a x 6 dxe 6 x + 1. 8 8 Codes de Huffman Comme on s’en doute par leur définition, on constate en pratique que les codes de Fano-Shannon sont rarement optimaux. Pour trouver les meilleurs codes il faut se résoudre à résoudre (sic) complètement le problème de la minimisation de R, en tenant compte du fait que les longueurs li doivent être entières. Une résolution complète du problème de recherche du meilleur code est donnée par algorithme itératif sur M appelé algorithme de Huffman (1952). On obtient alors un code de Huffman dont le taux R est minimal pour une source donnée (par les pi ). Comprendre cet algorithme demande un peu de travail. D’abord quelques préliminaires. 8.1 Préliminaires Considérons un code VLC optimal pour une source de distribution de probabilité3 p1 > p2 > · · · > pM . Que peut-on en dire a priori sur ce code ? D’abord, on aura nécessairement un ordre croissant de longueurs : l1 6 l2 6 · · · 6 lM Preuve: En effet, si ce n’est pas le cas, disons pi > pj et li > lj , on a pi li + pj lj > pi lj + pj li et on trouverait un meilleur code en échangeant ci et cj . Ainsi les symboles de source les plus probables doivent être codés par les mots de code les plus courts ; c’est intuitivement évident, ce principe est connu depuis Morse4 . Autre chose intuitivement évidente (déjà utilisée ci-dessus) : L’inégalité de Kraft McMillan est nécessairement une égalité pour un code optimal. Preuve: Sinon, on aurait X 2−li < 1. i Mais cette somme est un multiple entier de 2−lM car lM est la plus grande longueur. On a donc K2−lM < 1, K < 2lM , donc K 6 2lM − 1 puisque K est 3 On peut toujours se ramener à un tel ordre décroissant, quitte à permuter les symboles dans l’alphabet de la source. 4 Le code Morse, vous connaissez ? : · − − − · · − − · · · la lettre la plus courante (le « e ») est codée par le mot le plus court (« · »)). 9 entier. Autrement dit X 2−li 6 1 − 2−lM . i En conséquence, si on remplace lM par lM − 1, l’inégalité de Kraft-McMillan reste satisfaite. Ainsi on trouverait de cette façon un code u.d. meilleur. On déduit du raisonnement de la question précédente qu’à l’optimum, K est un entier pair, donc lM −1 = lM . Avec l’algorithme de Kraft ci-dessus, on s’aperçoit donc qu’on peut toujours se ramener au cas où les deux mots de codes cM −1 et cM ne diffèrent que par le dernier bit. 8.2 Réduction de Huffman Le principe de cette « réduction » est le suivant : On considère une source M -aire de distribution de probabilité (par ordre décroissant) p1 > p2 > · · · > pM . La réduction de Huffman consiste à considérer la source (M − 1)-aire, dite « réduite », de distribution de probabilité p1 , p2 , · · · , pM −2 , p0M −1 = pM −1 + pM . Autrement dit, on « combine » les deux symboles les moins probables. Noter qu’après cette opération, la dernière probabilité p0M −1 n’est plus forcément à sa place dans l’ordre décroissant. Notons CM = {c1 , . . . , cM −1 , cM } le code optimal cherché (« à l’ordre M »). D’après ci-dessus, on peut supposer que cM −1 et cM ne diffèrent que par le dernier bit ; écrivons donc cM −1 = [c0M −1 0] et cM = [c0M −1 1]. En termes 0 de longueurs cela donne lM = lM −1 = lM −1 + 1. En comparant les taux de codage de la source initiale et de la source réduite après réduction de Huffman, on peut montrer que : Le code CM −1 = {c1 , . . . , cM −2 , c0M −1 } est optimal pour la source réduite. 10 Preuve: En effet, son taux est RM −1 = = M −2 X i=1 M −2 X 0 pi li + p0M −1 lM −1 pi li + (pM −1 + pM )(l{M −1 ou M } − 1) i=1 = RM − (pM −1 + pM ) où RM est le taux du code optimal CM . Le terme pM −1 + pM est constant (on optimise pour une source donnée !), donc RM −1 est bien minimal puisque RM l’est. La réduction de Huffman permet donc de réduire le problème de la recherche du code optimal à l’ordre M à celui à l’ordre M − 1. En continuant ainsi, par réductions de Huffman successives, on arrive à M = 2 ; mais pour M = 2, le code {0,1} est (trivialement) optimal (pour toute source binaire). Attention tout de même : pour appliquer les réductions de Huffman successives, il faut prendre soin de réordonner à chaque étape les probabilités après chaque réduction de Huffman, car chaque réduction nécessite de connaitre les deux symboles les moins probables. 8.3 Remonter le tout Il reste à remonter l’itération pour construire de proche en proche des codes optimaux de plus en plus grands. Il suffit, à chaque étape, de construire le code optimal {c1 , . . . , cM −1 , cM } à partir de {c1 , . . . , cM −2 , c0M −1 }. C’est facile, car d’après ci-dessus, on doit simplement poser : cM −1 = [c0M −1 0] et cM = [c0M −1 1]. Bien entendu, à chaque étape, il faut prendre en compte les réarrangements des probabilités qui ont été faits lors de la descente par réductions successives. 8.4 Exemple Descente. La colonne de gauche donne la distribution de probabilité initiale (M = 8). Chaque réduction crée une nouvelle colonne à droite (après réarrangement par ordre décroissant) : 11 0.25 0.25 0.2 0.14 0.1 0.04 0.01 0.01 0.25 0.25 0.2 0.14 0.1 0.04 0.02 0.25 0.25 0.2 0.14 0.1 0.06 0.25 0.25 0.2 0.16 0.14 0.3 0.45 0.55 0.25 0.3 0.45 0.25 0.25 0.2 Remontée. De la droite vers la gauche, on construit de proche en proche le code optimal en tenant compte des permutations faites (on peut utiliser le même tableau si on le fait à la main) : 01 10 11 001 0000 00010 000110 000111 01 10 11 001 0000 00010 00011 01 10 11 001 0000 0001 01 10 11 000 001 00 1 0 01 00 1 10 01 11 Le taux de codage obtenu est R = 2, 54 à comparer avec l’entropie H = 2.51231997733309 . . .. On est à près d’1% de l’entropie, ce qui arrive souvent lorsque la source n’est pas trop dissymétrique. Noter qu’on pourrait tout aussi bien travailler sur les longueurs des mots de code plutot que sur ces mots de code eux-mêmes, et terminer par l’algorithme de Kraft qui donne le code : 00 01 10 110 1110 11110 111110 111111 2 2 2 3 4 5 6 6 2 2 2 3 4 5 5 2 2 2 3 4 4 2 2 2 3 3 2 1 1 2 2 1 2 2 2 Bien sûr on obtient un autre code, et pourrait d’ailleurs trouver d’autres variantes (par exemple en échangeant cM −1 et cM à certaines étapes (ou à toutes), en échangeant 0 et 1 sur une ou plusieurs colonne(s) de la liste des 12 mots de code obtenus. . . , ou encore en permutant des mots de code de même longueur). Par contre, dans cet exemple, la distribution de longueurs optimale est unique. Ce n’est pas toujours le cas, d’ailleurs ; dès qu’il y a deux ex-aequo dans une étape de ré-arrangement des probabilités, il vient un certain arbitraire dans l’ordre des probabilités dans la phase descendante, qui peut conduire a plusieurs possibilités pour la distribution de longueurs optimale obtenue à la fin. Exercice : le vérifier sur des exemples. . . 9 Nécessité d’un codage vectoriel On a vu que le taux de codage d’un code de Fano-Shannon, et donc a fortiori du meilleur code (de Huffman) vérifie l’inégalité H 6 R 6 H + 1. Mais peut-on améliorer cet encadrement ? Autrement dit, peut-on trouver une famille de codes pour lesquels, quelque soit la distribution de probabilité de la source, le taux ne s’éloigne pas de l’entropie plus qu’une quantité < 1 ? La réponse est non, comme le montre l’exemple suivant d’un source binaire (M = 2) : xi 1 0 pi ε 1−ε li 1 1 ci 1 0 Ce code (qui consiste à ne pas coder du tout) est optimal : on ne peut évidemment pas faire mieux (pas de longueur nulle pour une code u.d. !) : R = 1. Mais l’entropie vaut5 H = H2 (ε) = ε log2 1 1 + (1 − ε) log2 →0 ε 1−ε quand ε → 0. Ainsi il y a des sources pour lesquelles le taux optimal R approche H + 1 d’aussi près qu’on veut. Cet exemple d’une source binaire d’entropie faible est caractéristique : on ne peut pas en général « bien coder » (en fait on ne peut pas comprimer du tout) ce type de source à cause de la limitation imposée dès le départ dans notre présentation des codes VLC : à savoir qu’on code la source symbole par symbole (c’est du codage « scalaire »). Il est donc nécessaire, dans de pareils cas, de passer en codage vectoriel, c’est à dire de prendre en compte, lors du codage, plusieurs symboles successifs en meme temps. En tout état de cause, une telle approche prend en 5 Quand ε est très petit (devant 1), on dit que la source X est dissymétrique. H2 (ε) est la fonction d’entropie binaire, faible pour une source dissymétrique. 13 compte les corrélations éventuelles des symboles de source là où le codage scalaire est impuissant à le faire ; le codage vectoriel doit donc conduire à une performance améliorée. Pour prendre en compte plusieurs symboles successifs en même temps, on peut tout simplement coder la source par blocs de n symboles. C’est du codage vectoriel (en dimension n). Cela revient à remplacer la source initiale par une « super-source » dont les symboles sont des vecteurs de n symboles successifs de la source initiale. Cette « super-source » s’appelle « extension d’ordre n » de la source initiale6 . 10 Théorème de Shannon Examinons de plus près l’intérêt de coder vectoriellement (disons en dimension n) une source X donnée. Appliquons donc un code VLC optimal (de Huffman) à l’extension d’ordre n de la source. En fait, un code de FanoShannon fait l’affaire pour ce qui nous occupe ici. D’après ci-dessus, on a l’encadrement : Hn 6 Rn 6 Hn + 1 où Rn et Hn sont respectivement le taux de codage et l’entropie de la source étendue à l’ordre n. Ces deux quantités s’expriment en bits codés par symbole de source ; or un symbole de source est ici un « super-symbole », c’est à dire un vecteur de n symboles de source initiale X. Par conséquent, on revient aux mêmes unités (bits codés par symbole de source X) en divisant par n ; le taux de codage, exprimé en bits par symbole de source X, vaut Rn R= n alors que l’entropie H= Hn n représente l’entropie d’ordre n de la source (en bits par symbole de source X). L’encadrement ci-dessus devient : H6R6H+ 6 1 . n Le cas scalaire est naturellement un cas particulier du cas vectoriel : il suffit de poser n = 1. 14 On voit bien que la situation du cas scalaire (n = 1) s’est améliorée, à cause de la présence du n1 : plus on monte en dimension, plus on s’approche de l’entropie. On s’approche plus de l’entropie, mais de quelle entropie ? Pour le savoir, évaluons H dans le cas simple d’une source « sans mémoire », c’est à dire une source X dont les symboles sont indépendants dans le temps (et identiquement distribués : suite « i.i.d. »). L’entropie d’une source sans mémoire à l’ordre n est indépendante de n, et vaut H=H = M X i=1 pi log2 1 . pi Preuve: Soit X = (X1 , X2 , . . . , Xn ) un vecteur de source, et p(x1 , . . . , xn ) la probabilité asoociée. Alors par définition H= 1 X 1 p(x1 , . . . , xn ) log2 n x ,...,x p(x1 , . . . , xn ) 1 n Par commodité on peut introduire une espérance : H= 1 1 E log2 n p(X1 , . . . , Xn ) Or la source est sans mémoire : ses symboles sont indépendants, ce qui s’écrit : p(x1 , . . . , xn ) = p(x1 ) · · · p(xn ) de sorte que n Y 1 1 H = E log2 n p(Xk ) k=1 n 1X 1 = E log2 n k=1 p(Xk ) n = 1X H n k=1 nH n = H. = D’où le résultat. 15 Ainsi, pour une source sans mémoire, en prenant une dimension n assez grande, l’encadrement H 6 R 6 H + n1 montre qu’on s’approche aussi près de l’entropie que l’on veut. La situation est encore meilleure dans le cas d’un source avec mémoire ; En effet dans le cas général on montre que : Pour une source quelconque, l’entropie H d’ordre n vérifie l’inégalité : H6H Preuve: Considérons la différence H − H, interprétée comme la différence de l’entropie avec et sans mémoire. Avec les mêmes notations que ci-dessous, H−H = = n Y 1 1 1 1 E log2 − E log2 n p(X1 , . . . , Xn ) n p(Xk ) k=1 p(X1 ) · · · p(Xn ) 1 E log2 n p(X1 , . . . , Xn ) Or une inégalité de concavité bien connue7 affirme que la moyenne de logarithmes est 6 au logarithme de la moyenne : H−H 6 p(X1 ) · · · p(Xn ) 1 log2 E n p(X1 , . . . , Xn ) En explicitant l’espérance au second membre, il vient E X p(X1 ) · · · p(Xn ) p(x1 ) · · · p(xn ) = p(x1 , . . . , xn ) p(X1 , . . . , Xn ) p(x1 , . . . , xn ) x1 ,...,xn X = p(x1 ) · · · p(xn ) = = x1 ,...,xn n X Y p(xk ) k=1 xk n Y 1 k=1 = 1, d’où le résultat : H−H 6 1 log2 1 = 0. n 7 Mais on peut ne pas la connaître : utiliser alors à la place l’inégalité log(x) 6 x − 1 qui donne le même résultat. 16 En fait, sous des hypothèses assez faibles (stationnarité de la source) on montre que l’entropie d’ordre n décroît, lorsque n → ∞, vers une valeur limite appelée « entropie » (d’ordre ∞) de la source. Ainsi, en présence de « mémoire », en particulier lorsque les symboles de X sont corrélés en temps, l’entropie de source décroit lorsque n augmente. A la limite, l’encadrement sur le taux montre qu’on s’approche d’aussi près qu’on veut de l’entropie (d’ordre infini) : il suffit de coder en très grande dimension. On vient de démontrer le Théorème de Shannon (1948).8 Par codage vectoriel en dimension sufisemment élevée, le taux de codage peut être rendu aussi proche de l’entropie de la source que l’on veut. L’entropie apparait donc non seulement comme une « limite » inférieure sur le taux de codage mais aussi comme une limite « théoriquement atteignable ». Lorsque la source est « avec mémoire » on a H < H ; on en conclut que le codage vectoriel permet de réduire le taux en prenant en compte les corrélations des symboles de source. Ceci dit, l’intérêt du codage vectoriel ne se limite pas à la prise en compte des corrélations des symboles de source, puisqu’on a vu que même pour une source sans mémoire, au augmente les performances (en s’approchant de H) lorsque n croît (grâce au terme n1 ). Ainsi le codage vectoriel permet de réduire le taux même pour une source sans mémoire. Le théorème de Shannon semble (à première vue) clore définitivement la question du codage sans pertes en proposant de réaliser un codage vectoriel en grande dimension pour toute source. Du point de vue des performances, c’est optimal : on s’approche d’aussi près qu’on veut de l’entropie, et de tout façon on ne peut pas decendre en dessous. Cependant il y a un hic : Pour coder une source M -aire en dimension n, il faut appliquer un code VLC à la source étendue pour laquelle on a M n symboles possibles. Le code doit donc être de taille M n , qui augmente exponentiellement avec n. Par exemple, si on veut garantir un taux s’approchant de l’entropie à moins d’un centième de bit pour une source binaire, il faut un code de taille 2100 = 1267650600228229401496703205376 8 C’est le « premier » théorème de Shannon pour le codage de source sans pertes ; il existe d’autres théorèmes de Shannon pour le codage de source avec pertes, le codage de canal, et le codage de source et de canal combinés. 17 Inutile de dire que c’est prohibitif : il faudrait stocker la table du code au codeur et au décodeur, ou transmettre cette table du codeur au décodeur, ce qui nécessite un « overhead » (données annexes) qui prend une part non négligeable du débit global. En résumé, le codage vectoriel en grande dimension est d’un grand intérêt. . . théorique. Mais en pratique, pour des questions de complexité et de mémoire, on est amené à imaginer d’autres solutions pour prendre en compte des symboles de source successifs. Une de ces solutions est le codage par plages, objet de la section qui suit. 11 Codage par plages d’une source binaire Revenons, pour fixer les idées, à la source binaire dissymétrique (d’entropie faible, ε petit) : xi 1 0 pi ε 1−ε Un codage vectoriel d’une telle source est possible, mais peut conduire, comme on l’a vu, à une taille de code trop grande. En pratique, on peut utiliser alors des techniques de codage par plage (RLC : Run-Length Coding) pour améliorer les performances sans trop monter en complexité. Nous allons tenter de comprendre l’idée sur la source binaire dissymétrique donnée en exemple. 11.1 Source équivalente des plages Tout d’abord, la source X est transformée en une autre source L dont les symboles sont des entiers l > 0 représentant les longueurs des plages successives de zéros (entre deux occurences de « 1 ») de la source initiale X. Par exemple, la réalisation d’une séquence de symboles x de la source X : 0001010000000110000010001 . . . est représentée par la séquence les longueurs l : 3, 1, 7, 0, 5, 3, . . . . Ainsi une plage de longueur l correspond à la séquence binaire de source X = 00 · · · 01 de (l + 1) bits. La distribution de probabilité p(l) de L est donc 18 donnée par9 p(l) = (1 − ε)l ε. En les mettant bout à bout ces séquences de (l + 1) bits, on peut clairement reconstruire la séquence des symboles de source initiale x0 , x1 , . . . à partir de la représentation en longueurs de plages : l1 , l2 , . . .. Ainsi la source est plages est « équivalente » à la source binaire initiale. L’entropie de L vaut : H(L) = H2 (ε) ε Preuve: On peut le vérifier péniblement par le calcul en développant H(L) = P 1 l>0 p(l) log2 p(l) (Exercice : le faire !). On peut aussi procéder comme suit : Puisqu’équivalentes, les deux sources X et L ont même entropie, à condition bien sûr que ces entropies soient exprimées dans la même unité, par exemple en bits par bit de source X L’entropie de X vaut H2 (ε). Or le nombre moyen de bits de source par plage est10 E (L + 1) = X (l + 1)(1 − ε)l ε = l>0 1 ε On en déduit immédiatement, sans calcul, la formule donnant l’entropie de L, en bits par plage. 11.2 Le codage des plages Le principe du codage par plages est évidemment de coder la source équivalente des plages de façon à tenir compte des séquences de zéros consécutifs. Mais comme le nombre des valeurs possibles des longueurs L = l est infini, on ne peut pas appliquer directement un codage VLC (par exemple par l’algorithme de Huffman) sur la source L. Pour résoudre ce problème, on procèdera ici en découpant en tranches les valeurs des longueurs de plage, et on codera chaque tranche à l’aide d’un code de Huffman de taille N . C’est compliqué à décrire en détail, mais le principe est simple. Voici le détail : 9 On suppose ici que lesP bits de source X sont indépendants (« source sans mémoire »). +∞ On vérifiera qu’on a bien l=0 p(l) = 1. 10 Rappelons la formule X x lxl = . (1 − x)2 l>0 19 On code tout d’abord les N − 1 premières valeurs des longueurs de plage : l = 0, 1, . . . , N − 2 ainsi que l’événement L > N − 1, à l’aide de l’algorithme de Huffman. Le code de Huffman obtenu est noté H1 ; il possède donc N mots de code. Le mot de code associé à l’événement L > N − 1 est noté Pf 1 et servira de préfixe au codage des longueurs > N − 1. On code également la deuxième tranche, constituée des N − 1 valeurs suivantes des longueurs de plage : l = N − 1, N, . . . , 2N − 3 ainsi que l’événement L > 2N − 2, en mettant en oeuvre l’algorithme de Huffman11 . Le code de Huffman obtenu est noté H2 , il est aussi de taille N . Le mot de code associé à l’événement L > 2N − 2 est noté Pf 2 et servira de préfixe au codage des longueurs > 2N − 2. On procède de même pour toutes les tranches suivantes. On obtient donc une suite de codes de Huffman de taille N : H1 , H2 , H3 , . . ., et une suite de préfixes Pf 1 , Pf 2 , Pf 3 , . . . correspondants. Finalement la source L est codée comme suit : Si L = l appartient à la kième tranche, il est codé par c̄l = Pf 1 Pf 2 . . . Pf k−1 cl où cl est le mot de code du code Hk correspondant à L = l. Par exemple, si N = 4, L = 6, Pf 1 = Pf 2 = 10, H3 = {0, 10, 110, 111} où le mot de code correspondant à L = 6 est c6 = 110, alors L = 6 sera codé par c̄6 = 1010110. Tout a été fait pour permettre de réaliser un algorithme naturel de décodage instantané pour retrouver la source L à partir de sa représentation codée : le décodeur recherche d’abord les préfixes éventuels Pf 1 Pf 2 . . . Pf k−1 au début de la séquence à décoder, ce qui lui permet de repérer la tranche k ; puis il décode un mot de code du code de Huffman Hk . En fait on a la simplification suivante : 11 A partir de laP deuxième tranche, l’algorithme de Huffman est mis en oeuvre bien que la normalisation i pi = 1 des probabilités n’est pas satisfaite, ce qui ne pose aucun problème (l’algorithme n’utilise pas cette normalisation de toute façon). 20 Les codes de Huffman H1 , H2 , . . . sont toujours identiques, et par conséquent les préfixes Pf 1 = Pf 2 = Pf 3 = . . . sont également identiques. Preuve: Les probabilités en entrée de l’algorithme de Huffman qui fournit Hk sont, au facteur multiplicatif (1 − ε)(N −1)(k−1) près, les mêmes que pour le code H1 ; et ce facteur n’affecte pas le résultat de l’algorithme. 11.3 Performances et exemples Grâce à la simplication ci-dessus on peut déterminer une formule simple pour le taux de codage : Le taux de codage global du système complet (en bits codés par bit de source) vaut R= εRH 1 − (1 − ε)N −1 où RH est le taux de codage de chacun des codes de Huffman H1 , H2 , H3 , . . .. Preuve: Le taux de codage global du système complet est le nombre moyen de bits codés par bit de source, c’est à dire : R= +∞ X p(l)|c̄l | l=0 où |c̄l | désigne la longueur (en bits) du mot de code c̄l (représentation codée de la longueur L = l). Pour calculer R, il faut tenir compte des longueurs des préfixes éventuels. Exprimons d’abord R en bits par plage ; on a, en découpant en tranches (avec des codes de Huffman et des préfixes identiques pour chaque tranche) : R = p(0)|c0 | + · · · + p(N − 2)|cN −2 | + Prob{L > N − 1}|Pf 1 | + p(N − 1)|c0 | + · · · + p(2N − 3)|cN −2 | + Prob{L > 2N − 2}|Pf 1 | + ··· = RH + (1 − ε)N −1 RH + (1 − ε)2(N −1) RH + · · · Ici on a tenu compte du facteur mulitplicatif (1 − ε)N −1 pour les probabilités d’une tranche à la suivante. On a donc RH R= bits/plage 1 − (1 − ε)N −1 εRH = bits/bit 1 − (1 − ε)N −1 21 d’où la formule. Evidemment ce résultat dépend du choix de la longueur N des tranches. On peut d’ailleurs vérifier le résultat dans le cas extrême où N → ∞ ; le code de Huffman donne alors un taux s’approchant de l’entropie d’après le théorème de Shannon (appliqué à la source équivalente des longueurs) : RH → H(L) = H2 (ε) ε Puisque (1 − ε)N −1 → 0, on aboutit à R → H2 (ε) = H La limite est l’entropie de la source X, et donc la méthode de codage par plages exposée ici est asymptotiquement optimale. En pratique on détermine la longueur N des tranches par un compromis entre taille du code de Huffman (elle ne doit pas être trop grande) et valeur du taux R donnée par l’expression ci-dessus. Donnons un exemple précis pour ε = 0.1 et N = 4. On applique l’algorithme de Huffman pour déterminer le taux RH : (1 − ε)3 = 0.729 0.729 0.729 1 1 1 ε = 0.1 0.171 0.271 2 2 1 longueurs : ε(1 − ε) = 0.09 0.1 3 2 ε(1 − ε)2 = 0.081 3 d’où RH = 1.442 et R = 0.5321 . . . bits/bit obtenu par codage de Huffman des plages. On peut comparer ce résultat à un codage vectoriel en dimension n = 2 (pour une même taille de code de Huffman) : (1 − ε)2 = 0.81 0.81 0.81 1 1 1 ε(1 − ε) = 0.09 0.1 0.19 2 2 1 longueurs : ε(1 − ε) = 0.09 0.09 3 2 2 ε = 0.01 3 ce qui donne R = 1.29 = 0.645 bits/bit. Pour une même complexité, le codage 2 par plages se révèle meilleur que le codage vectoriel. Ce serait encore plus flagrant pour de plus grandes valeurs de N (Exercice : le faire !). 22 12 Codage arithmétique D’autres systèmes de codage de source sans pertes ont été proposés pour prendre en compte les dépendances temporelles (d’un symbole à l’autre) de la source (avec mémoire). Ces sytèmes de codage permettent de coder une source quelconque sans connaitre a priori ses statistiques (c’est ce qu’on appelle du codage « universel »), mais sont plus complexes à mettre en oeuvre. Les plus connus sont les systèmes de codage de Lempel-Ziv (1976) et de codage arithmétique (1982). Dans cette section, on va présenter les idées principales du codage arithmétique, qui est une des techniques de compression sans pertes les plus récentes, et probablement la plus importante dans les applications et les normes actuelles. Le codage arithmétique est une extension itérative d’une technique de codage connue depuis les années 50, appelée codage d’Elias (ou de ShannonFano-Elias). Cette dernière technique a été simplement introduite comme moyen de construction d’un code VLC « scalaire », où on code chaque symbole de source l’un après l’autre. Décrivons d’abord le codage d’Elias : 12.1 Codes d’Elias La distribution de probabilité des symboles de source vérifie la relation : M X pi = 1. i=1 On peut donc construire une partition (un « découpage ») du segment [0, 1[ (de longueur 1) en intervalles contigus12 I1 , . . . , IM , où chaque Ii est de longueur pi . Dans la suite, chaque point du segment [0, 1[ est un nombre qui sera représenté par son développement binaire (en base 2), par exemple : 0.110100100 . . .. I1 I2 I3 I4 ··· IM - 0 1 12 On choisira ces intervalles semi-ouverts (fermé à gauche, ouvert à droite) ; ainsi la définition précise est : i−1 i X X Ii = pi ; pi . j=1 23 j=1 L’idée est de coder un symbole de source xi par un point quelconque du segment de l’intervalle correspondant Ii : c̄i ∈ Ii . On utilise ici la même notation que lors de la description de l’algorithme de Kraft ci-dessus : le mot de code ci correspondant au symbole xi sera tel que c̄i = 0, ci ∈ [0, 1[ : les décimales de l’écriture en base 2 de c̄i est formée des bits de ci . Noter que même en supposant c̄i déterminé dans Ii , il reste une indétermination sur ci : ses premiers bits sont déterminés par le développement de c̄i en base deux, mais sa longueur li , c’est à dire le nombre de bits de précision (après la virgule) qu’on choisit pour le nombre c̄i , reste à choisir. Afin de pouvoir décoder, il est nécessaire que la précision sur c̄i soit suffisamment grande pour caractériser dans quel intervalle Ii il se trouve. Or, si c̄i est connu avec une précision de li bits, il peut prendre n’importe quelle valeur dans l’intervalle [c̄i ; c¯i + 2−li [ puisque cet intervalle est l’ensemble des nombres dont les li premières décimales en base 2 coincident avec ci . Il est donc nécessaire de choisir la précision li de sorte que [c̄i ; c¯i + 2−li [⊂ Ii . Ainsi on pourra toujours déterminer un nombre c̄i ∈ Ii convenable, pourvu que Ii contienne un intervalle du type [n2−li , (n + 1)2−li [, où n est entier (auquel cas n = ci en base 2). Il est facile de voir que ce sera toujours possible si 2 · 2−li 6 l(Ii ) = pi . On ne peut pas, en général, choisir un li plus petit car Ii pourrait être alors « à cheval » sur deux intervalles consécutifs du type [(n − 1)2−li , n2−li [ et [n2−li , (n + 1)2−li [ : MAUVAIS (n−1)2−li n2−li (n+1)2−li Ii - (n−1)2−li n2−li (n+1)2−li BON 24 On pourra donc prendre pour li le plus petit entier vérifiant l’inégalité ci-dessus, c’est à dire13 : 1 li = dlog2 e + 1 pi Remarquons que, d’après l’analyse faite ci-dessus pour l’algorithme de Kraft, on obtient un code instantané, car les intervalles [c̄i ; c¯i + 2−li [ sont tous disjoints. De plus, par un calcul analogue au cas des codes de Fano-Shannon traité ci-dessus, on en déduit l’encadrement sur le taux de codage : H 6 R 6 H + 2. Cet encadrement est moins bon que celui obtenu pour les codes de FanoShannon, ce qui explique pourquoi le codage d’Elias est rapidement tombé dans l’oubli ; il n’a été ré-exhumé qu’à la lumière de la technique itérative du codage arithmétique, qu’on va maintenant décire. 12.2 Itérations du codage arithmétique L’idée du codage arithmétique est d’itérer la procédure d’Elias au fur et à mesure du temps, afin coder plusieurs symboles de source successifs. On prendra ainsi en compte les corrélations éventuelles des symboles de la source (avec mémoire). Commencons par le codage de deux symboles consécutifs14 (x1 , x2 ). La distribution de probabilité conjointe de ce vecteur s’écrit : p(x1 , x2 ) = p(x1 )p(x2 |x1 ) où p(x2 |x1 ) est la probabilité conditionnelle de x2 sachant x1 . On commence par appliquer la procédure d’Elias pour le symbole x1 ; on obtient une représentation codée dans un intervalle I1 de longueur p(x1 ). Pour tenir compte de x2 , on considère non pas les probabilités p(x2 ), mais les probabilités conditionnelles p(x2 |x1 ) qui vérifient la relation : X p(x2 |x1 ) = 1. x2 On recommence alors la procédure d’Elias, non plus sur le segment [0, 1[, mais sur l’intervalle I1 qu’on re-partitionne en intervalles I2 plus fins, de longueurs proportionnelles aux p(x2 |x1 ) : 13 dxe désigne le plus petit entier > x. A partir de maintenant les indices désignent le temps : x1 , x2 , . . . sont les symboles de source observés aux instants t = 1, 2, . . .. 14 25 ··· I1 - 0 PP PP ! !! ! ! !! !! !! 1 PP PP P ··· I2 PP P P P - Puisque l(I1 ) = p(x1 ), ces nouveaux intervalles I2 sont en fait de longueur l(I2 ) = p(x1 )p(x2 |x1 ) = p(x1 , x2 ). Arrivé jusque là, rien n’empêche de continuer et de coder le symbole suivant x3 , en re-découpant l’intervalle I2 en sous-intervalles I3 de longueurs proportionnelles à p(x3 |x1 , x2 ). Ces intervalles seront en fait de longueur p(x1 , x2 )p(x3 |x1 , x2 ) = p(x1 , x2 , x3 ) et ainsi de suite. Après n itérations de l’algorithme d’Elias on obtient un intervalle de longueur p(x1 , x2 , . . . , xn ) = p(x1 )p(x2 |x1 )p(x3 |x1 , x2 ) · · · p(xn |x1 , . . . , xn−1 ) qui prend bien en compte la dépendance temporelle des symboles de source. 12.3 Points techniques et performances Le résultat du codage est donc formée des décimales du nombre c̄ ∈ In , avec une précision suffisante pour qu’il appartienne à un intervalle In unique. Ainsi, en théorie, il suffit de disposer d’une implantation d’une arithmétique à précision infinie (ou suffisamment grande). Ceci explique le terme « codage arithmétique ». Le décodage procède dans le même sens que le codage : on regarde d’abord dans quel intervalle I1 se trouve c̄, on en déduit x1 , puis on regarde à l’intérieur de I1 dans quel intervalle I2 se trouve c̄, on en déduit x2 , et ainsi de suite. On décode ainsi « au fil de l’eau » x1 , x2 , . . .. En pratique, il est hors de question de demander une implantation d’une artihmétique à précision infinie. C’est la raison pour laquelle il est nécessaire de procéder à des « remises à l’échelle » subtiles chaque fois que la précisionmachine va être atteinte, de façon à éviter les problèmes d’underflow. Ces remises à l’échelle sont re-parcourues lors du décodage. Je passe ici sous silence les nombreux détails d’implantation qui sont très techniques. 26 Il est facile d’évaluer les performances de ce codage : Le mot de code d’Elias au bout de n étapes aura une longueur (longueur de précision de c̄) : l = dlog2 1 e+1 p(x1 , x2 , . . . , xn ) et le taux de codage Rn , en bits par vecteur (x1 , . . . , xn ), vérifie donc l’encadrement : Hn 6 Rn 6 Hn + 2, d’où, en se ramenant à des bits par symbole de source : H6R6H+ 2 n où H est l’entropie d’ordre n de la source. On retrouve une situation déjà rencontrée lors de l’analyse du codage vectoriel en dimension n, qui fournit le théorème de Shannon : le taux de codage s’approche d’aussi près qu’on veut de l’entropie. Le codage arithmétique est donc optimal dans le cas général des sources avec mémoire. 12.4 Adaptativité et universalité L’intérêt du codage artihmétique n’est pas tant de fournir un algorithme de codage optimal mais il permet surtout un codage adaptatif : plutôt que de supposer connu une fois pour toutes la distribution de probabilité conjointe de la source p(x1 , . . . , xn ) (par modélisation et/ou estimation sur toute la source), il est possible d’estimer au fur et à mesure les probabilités conditionnelles p(xn |x1 , . . . , xn−1 ) dont on a besoin pour l’étape n. Ceci permet une plus grande flexibilité pour coder n’importe quel type de source, en s’adaptant à des variations éventuelles de statistiques. Bien entendu, cette estimation est d’abord grossière ; elle s’affine au fur et à mesure du codage des données x1 , x2 , . . .. On peut même aller plus loin : plutôt que de transmettre en données annexes (overhead) ces estimations de probabilité du codeur au décodeur (qui en a également besoin), on peut envisager de répeter au décodage (indépendamment du codeur) l’estimation voulue sur les données au fur et à mesure de leur décodage. On obtient alors un algorithme de décodage universel qui est capable de coder (et de décoder sans information supplémentaire) tout type de source sans connaissance a priori de ses statistiques. 27 Références [1] Robert Mc Eliece, The Theory of Information and Coding, Addison Wesley, 1977. [2] Thomas Cover, Joy Thomas, Elements of Information Theory, J. Wiley & sons, 1991. [3] Allan Gersho, Robert Gray, Vector Quantization and Signal Compression, Kluwer Academic, 1992. [4] Nicolas Moreau, Techniques de Compression des Signaux, Masson CNETENST, 1994. 28