004 《信息论:信源编码基本原理深度解析》
🌟🌟🌟本文案由Gemini 2.5 Flash Preview 04-17创作,用来辅助学习知识。🌟🌟🌟
书籍大纲
▮▮▮▮ 1. chapter 1: 章节标题1
▮▮▮▮▮▮▮ 1.1 小节标题1
▮▮▮▮▮▮▮ 1.2 小节标题2
▮▮▮▮▮▮▮ 1.3 小节标题3
▮▮▮▮▮▮▮ 1.4 小节标题4
▮▮▮▮▮▮▮▮▮▮▮ 1.4.1 子小节 4-1
▮▮▮▮▮▮▮▮▮▮▮ 1.4.2 子小节 4-2
▮▮▮▮▮▮▮▮▮▮▮ 1.4.3 子小节 4-3
▮▮▮▮ 2. chapter 2: 章节标题2
▮▮▮▮▮▮▮ 2.1 小节标题1
▮▮▮▮▮▮▮ 2.2 小节标题2
▮▮▮▮▮▮▮ 2.3 小节标题3
▮▮▮▮▮▮▮ 2.4 小节标题4
▮▮▮▮ 1. chapter 1: 引言:信息、信息论与信源编码 (Introduction: Information, Information Theory, and Source Coding)
▮▮▮▮▮▮▮ 1.1 什么是信息? (What is Information?)
▮▮▮▮▮▮▮ 1.2 信息论的起源与发展 (Origin and Development of Information Theory)
▮▮▮▮▮▮▮ 1.3 信源编码的任务与重要性 (Task and Importance of Source Coding)
▮▮▮▮▮▮▮ 1.4 本书结构与阅读指南 (Book Structure and Reading Guide)
▮▮▮▮ 2. chapter 2: 信息的基本度量:熵 (Basic Measure of Information: Entropy)
▮▮▮▮▮▮▮ 2.1 概率论基础回顾 (Review of Probability Theory Basics)
▮▮▮▮▮▮▮ 2.2 自信息 (Self-Information)
▮▮▮▮▮▮▮ 2.3 离散无记忆信源的熵 (Entropy of Discrete Memoryless Sources)
▮▮▮▮▮▮▮ 2.4 熵的性质与意义 (Properties and Significance of Entropy)
▮▮▮▮▮▮▮ 2.5 联合熵与条件熵 (Joint Entropy and Conditional Entropy)
▮▮▮▮▮▮▮ 2.6 互信息 (Mutual Information)
▮▮▮▮ 3. chapter 3: 无损信源编码的理论基础 (Theoretical Foundation of Lossless Source Coding)
▮▮▮▮▮▮▮ 3.1 信源模型 (Source Models)
▮▮▮▮▮▮▮ 3.2 编码与译码 (Encoding and Decoding)
▮▮▮▮▮▮▮ 3.3 码的分类:唯一可译码与前缀码 (Code Classification: Uniquely Decodable Codes and Prefix Codes)
▮▮▮▮▮▮▮ 3.4 克拉夫特不等式 (Kraft's Inequality)
▮▮▮▮▮▮▮ 3.5 无失真信源编码定理 (Lossless Source Coding Theorem)
▮▮▮▮▮▮▮ 3.6 码字长度与平均码长 (Codeword Length and Average Codeword Length)
▮▮▮▮ 4. chapter 4: 经典无损编码算法 (Classic Lossless Coding Algorithms)
▮▮▮▮▮▮▮ 4.1 变长编码的基本思想 (Basic Idea of Variable-Length Coding)
▮▮▮▮▮▮▮ 4.2 霍夫曼编码 (Huffman Coding)
▮▮▮▮▮▮▮▮▮▮▮ 4.2.1 霍夫曼编码原理与构造 (Principle and Construction of Huffman Coding)
▮▮▮▮▮▮▮▮▮▮▮ 4.2.2 扩展霍夫曼编码 (Extended Huffman Coding)
▮▮▮▮▮▮▮▮▮▮▮ 4.2.3 自适应霍夫曼编码 (Adaptive Huffman Coding)
▮▮▮▮▮▮▮ 4.3 算术编码 (Arithmetic Coding)
▮▮▮▮▮▮▮▮▮▮▮ 4.3.1 算术编码原理 (Principle of Arithmetic Coding)
▮▮▮▮▮▮▮▮▮▮▮ 4.3.2 算术编码的实现细节 (Implementation Details of Arithmetic Coding)
▮▮▮▮▮▮▮ 4.4 游程编码 (Run-Length Encoding, RLE)
▮▮▮▮ 5. chapter 5: 基于字典的无损编码算法 (Dictionary-Based Lossless Coding Algorithms)
▮▮▮▮▮▮▮ 5.1 字典编码的基本思想 (Basic Idea of Dictionary Coding)
▮▮▮▮▮▮▮ 5.2 LZ77算法 (LZ77 Algorithm)
▮▮▮▮▮▮▮ 5.3 LZ78算法 (LZ78 Algorithm)
▮▮▮▮▮▮▮ 5.4 LZW算法 (LZW Algorithm)
▮▮▮▮▮▮▮ 5.5 LZ系列算法的比较与应用 (Comparison and Applications of LZ Family Algorithms)
▮▮▮▮ 6. chapter 6: 有失真信源编码引论 (Introduction to Lossy Source Coding)
▮▮▮▮▮▮▮ 6.1 为何需要有失真编码? (Why Lossy Coding is Needed?)
▮▮▮▮▮▮▮ 6.2 失真度量 (Distortion Measures)
▮▮▮▮▮▮▮ 6.3 率失真理论 (Rate-Distortion Theory)
▮▮▮▮▮▮▮ 6.4 率失真函数 (Rate-Distortion Function)
▮▮▮▮▮▮▮ 6.5 有失真编码的理论极限 (Theoretical Limits of Lossy Coding)
▮▮▮▮ 7. chapter 7: 有失真编码技术:量化 (Lossy Coding Technique: Quantization)
▮▮▮▮▮▮▮ 7.1 量化的基本概念 (Basic Concepts of Quantization)
▮▮▮▮▮▮▮ 7.2 标量量化 (Scalar Quantization)
▮▮▮▮▮▮▮▮▮▮▮ 7.2.1 均匀量化器 (Uniform Quantizer)
▮▮▮▮▮▮▮▮▮▮▮ 7.2.2 非均匀量化器 (Non-uniform Quantizer)
▮▮▮▮▮▮▮▮▮▮▮ 7.2.3 Lloyd-Max量化器 (Lloyd-Max Quantizer)
▮▮▮▮▮▮▮ 7.3 矢量量化 (Vector Quantization, VQ)
▮▮▮▮▮▮▮▮▮▮▮ 7.3.1 矢量量化原理 (Principle of Vector Quantization)
▮▮▮▮▮▮▮▮▮▮▮ 7.3.2 LBG算法 (LBG Algorithm)
▮▮▮▮ 8. chapter 8: 其他有失真编码技术 (Other Lossy Coding Techniques)
▮▮▮▮▮▮▮ 8.1 预测编码 (Predictive Coding)
▮▮▮▮▮▮▮▮▮▮▮ 8.1.1 DPCM (Differential Pulse Code Modulation)
▮▮▮▮▮▮▮▮▮▮▮ 8.1.2 ADPCM (Adaptive Differential Pulse Code Modulation)
▮▮▮▮▮▮▮ 8.2 变换编码概述 (Overview of Transform Coding)
▮▮▮▮▮▮▮▮▮▮▮ 8.2.1 离散余弦变换 (Discrete Cosine Transform, DCT) 简介
▮▮▮▮▮▮▮▮▮▮▮ 8.2.2 小波变换 (Wavelet Transform) 简介
▮▮▮▮ 9. chapter 9: 信源编码算法的比较与选择 (Comparison and Selection of Source Coding Algorithms)
▮▮▮▮▮▮▮ 9.1 无损算法性能比较 (Performance Comparison of Lossless Algorithms)
▮▮▮▮▮▮▮ 9.2 有损算法性能比较 (Performance Comparison of Lossy Algorithms)
▮▮▮▮▮▮▮ 9.3 复杂度与实现考虑 (Complexity and Implementation Considerations)
▮▮▮▮▮▮▮ 9.4 如何根据应用选择合适的算法 (How to Choose the Right Algorithm Based on Application)
▮▮▮▮ 10. chapter 10: 进阶主题与应用案例 (Advanced Topics and Application Cases)
▮▮▮▮▮▮▮ 10.1 通用编码 (Universal Coding)
▮▮▮▮▮▮▮ 10.2 带边信息的信源编码 (Source Coding with Side Information)
▮▮▮▮▮▮▮ 10.3 在图像、音频、视频压缩中的应用 (Applications in Image, Audio, and Video Compression)
▮▮▮▮▮▮▮ 10.4 信源编码的未来发展方向 (Future Directions of Source Coding)
▮▮▮▮▮▮▮ A.1 常用数学公式 (Common Mathematical Formulas)
▮▮▮▮▮▮▮ A.2 参考文献 (References)
1. chapter 1: 引言:信息、信息论与信源编码 (Introduction: Information, Information Theory, and Source Coding)
欢迎来到信息论的世界!这是一门既古老又年轻的学科,它深刻地改变了我们理解和处理信息的方式。在本章中,我们将首先探讨“信息”这一基本概念,了解信息论是如何诞生的,以及信源编码在信息论乃至整个信息科学领域扮演着怎样的角色。本章旨在为您构建一个清晰的知识框架,为后续深入学习信源编码的原理和技术打下坚实的基础。
1.1 什么是信息? (What is Information?)
信息,这个词在日常生活中无处不在,但要给它一个精确的定义却并非易事。从哲学到物理学,从生物学到社会学,不同的学科对信息有不同的理解。在信息论的语境下,信息通常与“不确定性”的消除或减少紧密相关。
想象一下,你正在等待一个朋友的消息。如果朋友告诉你“我今天会来”,这相对于“我今天可能来,也可能不来”提供了更多的信息,因为它减少了你对朋友是否会来的不确定性。如果朋友进一步告诉你“我今天下午三点会到你家”,这又提供了更多信息,进一步减少了不确定性。
因此,在信息论中,信息不是指消息的语义内容(比如消息是好消息还是坏消息),而是指消息所包含的、能够消除接收者不确定性的量度。一个事件发生的概率越低,当它发生时,所携带的信息量就越大。例如,预测明天太阳会升起几乎不包含信息,因为它是一个确定性事件;而预测明天某个特定股票会涨停则包含大量信息,因为这是一个低概率事件。
信息论的创始人克劳德·香农(Claude Shannon)在1948年发表的划时代论文《通信的数学理论》(A Mathematical Theory of Communication)中,为信息提供了一个可量化的定义,即自信息(Self-Information)。我们将在后续章节详细探讨这一概念。
总结来说,在信息论的视角下:
⚝ 信息是用来消除不确定性的东西。
⚝ 信息量与事件发生的概率负相关:概率越低,信息量越大。
⚝ 信息是可以被量化度量的。
1.2 信息论的起源与发展 (Origin and Development of Information Theory)
信息论的诞生并非偶然,它是第二次世界大战期间通信技术飞速发展背景下的产物。当时,如何高效、可靠地传输信息成为一个亟待解决的问题。许多科学家和工程师都在探索通信系统的基本原理和性能极限。
克劳德·香农(Claude Shannon)是公认的信息论之父。他在贝尔实验室(Bell Labs)工作期间,系统地研究了通信系统的各个组成部分,包括信源(Source)、编码器(Encoder)、信道(Channel)、译码器(Decoder)和信宿(Destination)。他的核心贡献在于:
① 提出了信息的量化度量——熵(Entropy)。
② 建立了通信系统的数学模型。
③ 证明了著名的信道编码定理(Channel Coding Theorem),指出了在存在噪声的信道中进行可靠通信的速率极限——信道容量(Channel Capacity)。
④ 提出了信源编码定理(Source Coding Theorem),指出了无损压缩信源的理论极限——信源的熵。
香农的理论为通信系统的设计提供了理论指导和性能基准,开创了一个全新的研究领域。
信息论诞生后,迅速在通信、计算机科学、数据存储、信号处理等领域得到了广泛应用和发展。其基本思想和方法也被引入到物理学(如统计力学)、生物学(如基因组信息)、经济学等众多学科。
信息论的发展历程可以大致分为几个阶段:
⚝ 早期(1940s-1960s): 理论基础的建立,香农定理的提出,线性分组码、卷积码等信道编码技术的发展。
⚝ 中期(1970s-1990s): 纠错码理论的深入研究,如BCH码、Reed-Solomon码;无损压缩算法的实用化,如LZ系列算法、霍夫曼编码的改进;有损压缩理论和技术的发展,如率失真理论、变换编码、矢量量化。
⚝ 近期(2000s至今): 低密度奇偶校验码(LDPC)和Turbo码等逼近香农极限的信道编码技术的突破;信息论在网络、安全、机器学习、大数据等新兴领域的交叉应用;对信息论基本概念(如信息、熵)在更广泛语境下的重新审视。
信息论不仅提供了解决实际问题的工具,更重要的是,它提供了一种全新的视角来理解信息、通信和知识的本质。
1.3 信源编码的任务与重要性 (Task and Importance of Source Coding)
在一个典型的通信系统中,信息从信源产生,经过编码、信道传输、译码,最终到达信宿。信源编码(Source Coding),也称为数据压缩(Data Compression),是通信系统中的一个关键环节。
信源编码的主要任务是:
① 去除信源中的冗余信息(Redundancy Reduction)。 自然语言、图像、音频、视频等信源往往包含大量的冗余。例如,文本中某些字母或词语出现的频率远高于其他;图像中相邻像素往往颜色相似;视频中连续帧之间变化不大。信源编码的目标就是利用这些统计特性,用更少的比特(bit)来表示原始信息,从而实现压缩。
② 提高信息传输或存储的效率。 通过压缩,可以用更低的带宽传输相同的信息量,或者在相同的存储介质上存储更多的信息。
根据是否允许失真,信源编码可以分为两大类:
⚝ 无损信源编码(Lossless Source Coding): 编码和译码过程不会丢失任何原始信息,译码后的数据与原始数据完全一致。适用于对数据精度要求极高的场景,如文本文件、程序代码、医学影像等。
⚝ 有失真信源编码(Lossy Source Coding): 允许在编码过程中丢失一部分对人类感知不重要或不敏感的信息,以实现更高的压缩率。适用于对保真度要求相对宽松、但对压缩率要求很高的场景,如图像(JPEG)、音频(MP3)、视频(MPEG)等。
信源编码的重要性体现在以下几个方面:
⚝ 节省存储空间: 压缩后的数据占用更小的存储空间,降低存储成本。
⚝ 提高传输效率: 减少需要传输的数据量,降低带宽需求,加快传输速度,尤其在网络带宽有限或传输成本高昂的情况下至关重要。
⚝ 降低处理负担: 处理压缩后的数据通常比处理原始数据更快。
⚝ 是现代多媒体技术的基础: 图像、音频、视频的数字化、存储和传输都严重依赖于高效的有失真信源编码技术。
信源编码的理论基础是香农的信源编码定理,它告诉我们,对于一个给定的信源,其无损压缩的理论极限是信源的熵。熵是信源不确定性的度量,也是其平均信息量的度量。任何无损编码方法都无法将信源压缩到低于其熵的平均码长。对于有失真编码,率失真理论(Rate-Distortion Theory)则给出了在允许一定失真度的情况下,信源可以被压缩到的理论极限速率。
本书将深入探讨无损和有失真信源编码的基本原理、经典算法以及它们的应用。
1.4 本书结构与阅读指南 (Book Structure and Reading Guide)
本书旨在为读者提供一个系统、全面且深入的信源编码知识体系,无论您是初学者、有一定基础的学习者还是希望深入研究的专家,都能从中获益。
本书的结构如下:
⚝ 第1章:引言 介绍信息、信息论的基本概念,信源编码的任务和重要性,以及本书的结构。
⚝ 第2章:信息的基本度量:熵 深入讲解信息论中最核心的概念——熵,以及相关的联合熵、条件熵和互信息。这是理解信源编码理论极限的基础。
⚝ 第3章:无损信源编码的理论基础 介绍信源模型、编码与译码的基本概念,唯一可译码和前缀码,克拉夫特不等式,以及无失真信源编码定理。
⚝ 第4章:经典无损编码算法 详细讲解霍夫曼编码、算术编码和游程编码等基于概率统计的无损编码方法。
⚝ 第5章:基于字典的无损编码算法 介绍LZ77、LZ78和LZW等基于字典匹配的无损编码方法。
⚝ 第6章:有失真信源编码引论 解释为何需要有失真编码,介绍失真度量和率失真理论的基本概念。
⚝ 第7章:有失真编码技术:量化 详细讲解标量量化和矢量量化这两种基本的有失真编码技术。
⚝ 第8章:其他有失真编码技术 概述预测编码和变换编码等重要的有失真编码方法。
⚝ 第9章:信源编码算法的比较与选择 对前面介绍的各种无损和有失真算法进行比较,并讨论如何根据实际应用需求选择合适的算法。
⚝ 第10章:进阶主题与应用案例 介绍通用编码、带边信息的信源编码等进阶概念,并结合图像、音频、视频压缩等实际应用案例,展望信源编码的未来发展。
⚝ 附录: 包含常用数学公式和参考文献列表。
阅读指南:
⚝ 初学者(Beginners): 建议按章节顺序阅读。重点理解第1、2、3章的基本概念和理论基础,以及第4、5章的经典无损算法。第6、7、8章可以先了解基本思想和主要技术,不必深究所有数学细节。第9、10章可以作为了解应用和未来方向的参考。
⚝ 中级学习者(Intermediate): 建议按章节顺序深入阅读。重点掌握第2、3章的理论推导,理解第4、5章算法的实现细节和性能分析。对第6、7、8章的有失真编码理论和技术进行深入学习,理解率失真理论和量化、预测、变换编码的原理。第9、10章可以结合实际问题进行思考。
⚝ 专家(Experts): 本书可以作为系统回顾和查阅特定算法细节的参考。可以重点关注第2、3、6章的理论深度,以及第9、10章的算法比较、进阶主题和最新应用。书中的参考文献可以作为进一步深入研究的起点。
无论您的基础如何,都建议在学习过程中结合实际例子和练习,动手实践编码和译码过程,这将有助于加深理解。信息论和信源编码是一个充满魅力和挑战的领域,希望本书能激发您对它的兴趣,并助您在这个领域取得进步。
2. chapter 2: 信息的基本度量:熵 (Basic Measure of Information: Entropy)
欢迎来到信息论的核心领域!在上一章中,我们对信息、信息论以及信源编码的任务有了初步的认识。现在,是时候深入探讨如何度量(measure)信息了。信息论之所以强大,很大程度上在于它为“信息”这个看似抽象的概念提供了一个严格的数学度量标准,这个标准就是熵(Entropy)。熵不仅是信息论中最基本的概念之一,也是理解信源编码理论基石。本章将带你一步步理解熵的定义、性质及其在信息论中的重要意义。
2.1 概率论基础回顾 (Review of Probability Theory Basics)
在深入探讨熵之前,我们需要回顾一些基本的概率论概念,因为信息论的许多概念都建立在概率论的基础上。
⚝ 随机变量 (Random Variable):一个随机变量是一个函数,它将随机实验的每个可能结果映射到一个实数。例如,抛掷一枚硬币的结果(正面或反面)可以映射为数字(1或0)。
⚝ 样本空间 (Sample Space):一个随机实验所有可能结果的集合。
⚝ 事件 (Event):样本空间的一个子集。
⚝ 概率 (Probability):衡量某个事件发生的可能性大小的数值,通常介于0和1之间。对于离散随机变量,我们关注其取某个特定值的概率。
⚝ 概率质量函数 (Probability Mass Function, PMF):对于离散随机变量 \(X\),其概率质量函数 \(P(x)\) 定义为 \(P(x) = P(X=x)\),即随机变量 \(X\) 取值为 \(x\) 的概率。所有可能取值的概率之和必须等于1,即 \(\sum_x P(x) = 1\)。
⚝ 期望值 (Expected Value):对于离散随机变量 \(X\),其期望值 \(E[X]\) 是其所有可能取值与其对应概率的乘积之和。
\[ E[X] = \sum_x x P(x) \]
期望值代表了随机变量的平均取值。
理解这些基本概念对于后续理解熵至关重要,因为熵的定义直接依赖于随机变量的概率分布。
2.2 自信息 (Self-Information)
想象一下,你正在接收来自某个信源(source)的信息。如果信源发出的某个符号(symbol)是你完全预料到的(例如,你知道明天太阳会升起),那么这个符号带给你的信息量就非常少。反之,如果信源发出的符号是你完全没有预料到的(例如,你收到一条消息说你中了大奖),那么这个符号带给你的信息量就非常大。这直观地说明,信息量的大小与事件发生的概率(probability)有关:概率越低,信息量越大;概率越高,信息量越小。
基于这种直觉,信息论引入了自信息(Self-Information)的概念,用于度量一个特定事件发生所带来的信息量。对于一个离散随机变量 \(X\) 的某个特定取值 \(x\),其自信息 \(I(x)\) 定义为:
\[ I(x) = \log_b \left( \frac{1}{P(x)} \right) = -\log_b P(x) \]
其中 \(P(x)\) 是事件 \(X=x\) 发生的概率,\(b\) 是对数的底数。
① 对数底数 \(b\) 的选择决定了信息量的单位:
▮▮▮▮ⓑ 如果 \(b=2\),单位是比特 (bit)。这是信息论中最常用的单位。
▮▮▮▮ⓒ 如果 \(b=e\),单位是纳特 (nat)。
▮▮▮▮ⓓ 如果 \(b=10\),单位是迪特 (dit) 或 哈特莱 (Hartley)。
在本书中,除非特别说明,我们都将使用 \(b=2\),信息量的单位是比特。因此,自信息通常表示为:
\[ I(x) = -\log_2 P(x) \]
② 自信息的性质:
⚝ 非负性:由于 \(0 \le P(x) \le 1\),所以 \(\log_2 P(x) \le 0\),因此 \(I(x) = -\log_2 P(x) \ge 0\)。信息量总是非负的。
⚝ 概率与信息量的关系:概率 \(P(x)\) 越小,\(-\log_2 P(x)\) 越大,信息量越大。当 \(P(x) = 1\) 时,\(I(x) = -\log_2(1) = 0\),确定事件的信息量为零。当 \(P(x) \to 0\) 时,\(I(x) \to \infty\),极不可能事件的信息量趋于无穷大。
⚝ 可加性:如果两个独立事件 \(x_1\) 和 \(x_2\) 同时发生,其联合概率为 \(P(x_1, x_2) = P(x_1)P(x_2)\)。它们带来的总信息量是 \(I(x_1, x_2) = -\log_2(P(x_1)P(x_2)) = -\log_2 P(x_1) - \log_2 P(x_2) = I(x_1) + I(x_2)\)。独立事件的信息量是可加的。
自信息度量的是单个事件发生所带来的信息量。然而,我们通常更关心一个信源平均而言能够产生多少信息。这就引出了熵的概念。
2.3 离散无记忆信源的熵 (Entropy of Discrete Memoryless Sources)
一个离散无记忆信源 (Discrete Memoryless Source, DMS) 是指信源发出的每一个符号都是从一个有限的字母表(alphabet)中独立同分布(independent and identically distributed, i.i.d.)地产生的。也就是说,每个符号的产生概率只取决于其自身的概率分布,与之前或之后产生的符号无关。
对于这样一个离散无记忆信源,其输出是一个随机变量 \(X\),取值于字母表 \(\mathcal{X} = \{x_1, x_2, \dots, x_n\}\),对应的概率分布为 \(P = \{p_1, p_2, \dots, p_n\}\),其中 \(p_i = P(X=x_i)\)。
熵 (Entropy),通常用 \(H(X)\) 或 \(H(P)\) 表示,是信源 \(X\) 的平均自信息量。它度量了信源的不确定性(uncertainty)或信息量(information content)的平均值。熵的定义如下:
\[ H(X) = E[I(X)] = \sum_{i=1}^n P(x_i) I(x_i) = \sum_{i=1}^n p_i (-\log_2 p_i) = -\sum_{i=1}^n p_i \log_2 p_i \]
其中,按照惯例,如果 \(p_i = 0\),则 \(p_i \log_2 p_i = 0\)。这是因为 \(\lim_{p \to 0^+} p \log_2 p = 0\)。
熵的单位通常是比特/符号 (bits/symbol)。
例子 💡:考虑一个二元信源(Binary Source),字母表为 \(\{0, 1\}\)。
① 如果 \(P(X=0) = 0.5\),\(P(X=1) = 0.5\) (公平硬币),则熵为:
\[ H(X) = -0.5 \log_2(0.5) - 0.5 \log_2(0.5) = -0.5(-1) - 0.5(-1) = 0.5 + 0.5 = 1 \text{ 比特/符号} \]
这是二元信源的最大熵,表示每个符号提供1比特的信息。
② 如果 \(P(X=0) = 1\),\(P(X=1) = 0\) (确定事件),则熵为:
\[ H(X) = -1 \log_2(1) - 0 \log_2(0) = -1(0) - 0 = 0 \text{ 比特/符号} \]
确定事件的熵为零,因为没有不确定性。
③ 如果 \(P(X=0) = 0.9\),\(P(X=1) = 0.1\),则熵为:
\[ H(X) = -0.9 \log_2(0.9) - 0.1 \log_2(0.1) \approx -0.9(-0.152) - 0.1(-3.322) \approx 0.137 + 0.332 = 0.469 \text{ 比特/符号} \]
这介于0和1之间,反映了信源的不确定性。
从这些例子可以看出,熵的大小反映了信源输出的随机性或不确定性程度。不确定性越大,熵越高;不确定性越小,熵越低。
2.4 熵的性质与意义 (Properties and Significance of Entropy)
熵作为信息的基本度量,具有许多重要的性质:
① 非负性 (Non-negativity):\(H(X) \ge 0\)。熵总是非负的,因为概率 \(p_i \in [0, 1]\),\(\log_2 p_i \le 0\),所以 \(-p_i \log_2 p_i \ge 0\)。
② 确定事件的熵为零 (Entropy of a Deterministic Event is Zero):如果信源的输出是确定的(即某个 \(p_k = 1\),其余 \(p_i = 0\)),则 \(H(X) = 0\)。这符合直觉,因为没有不确定性。
③ 最大熵 (Maximum Entropy):对于一个具有 \(n\) 个可能输出符号的离散信源,当且仅当所有符号的概率相等时(即 \(p_i = 1/n\) 对于所有 \(i\)),熵达到最大值。
\[ H(X) \le \log_2 n \]
最大熵对应于信源输出最不确定的情况(均匀分布)。
④ 凹函数性质 (Concavity):熵函数 \(H(P)\) 是关于概率分布 \(P\) 的一个凹函数 (concave function)。这意味着对于两个概率分布 \(P_1\) 和 \(P_2\),以及 \(0 \le \lambda \le 1\),有 \(H(\lambda P_1 + (1-\lambda) P_2) \ge \lambda H(P_1) + (1-\lambda) H(P_2)\)。这个性质在信息论的理论推导中非常有用。
⑤ 熵与信源编码 (Entropy and Source Coding):熵 \(H(X)\) 具有极其重要的实际意义:它代表了对离散无记忆信源进行无损压缩(lossless compression)的理论极限。根据无失真信源编码定理 (Lossless Source Coding Theorem)(也称为香农第一定理,Shannon's First Theorem),对于一个熵为 \(H(X)\) 的离散无记忆信源,任何无损编码方案的平均码长(average codeword length)不可能小于 \(H(X)\)。换句话说,\(H(X)\) 是表示信源输出符号所需的平均最小比特数。
\[ L_{avg} \ge H(X) \]
其中 \(L_{avg}\) 是每个符号的平均码长。存在一些编码方法(如霍夫曼编码和算术编码)可以使平均码长任意接近熵。
因此,熵不仅度量了信息的不确定性,更给出了无损数据压缩的理论下界,为信源编码算法的设计提供了理论指导和性能基准。
2.5 联合熵与条件熵 (Joint Entropy and Conditional Entropy)
在实际应用中,我们经常需要考虑多个随机变量之间的关系。例如,在处理序列数据时,当前符号的出现可能与前一个符号有关。为了度量多个变量的联合不确定性以及在已知某些变量的情况下其他变量的不确定性,我们引入联合熵(Joint Entropy)和条件熵(Conditional Entropy)。
① 联合熵 (Joint Entropy):对于两个离散随机变量 \(X\) 和 \(Y\),其联合熵 \(H(X, Y)\) 度量了这对随机变量的联合不确定性。它定义为联合概率分布 \(P(x, y)\) 的平均自信息量:
\[ H(X, Y) = -\sum_x \sum_y P(x, y) \log_2 P(x, y) \]
其中 \(P(x, y) = P(X=x, Y=y)\) 是 \(X\) 取值为 \(x\) 且 \(Y\) 取值为 \(y\) 的联合概率。
② 条件熵 (Conditional Entropy):条件熵 \(H(Y|X)\) 度量了在已知随机变量 \(X\) 的值后,随机变量 \(Y\) 的剩余不确定性。它定义为在给定 \(X=x\) 的条件下 \(Y\) 的条件熵 \(H(Y|X=x)\) 关于 \(X\) 的所有可能取值的平均值:
\[ H(Y|X) = \sum_x P(x) H(Y|X=x) \]
其中 \(H(Y|X=x) = -\sum_y P(y|x) \log_2 P(y|x)\),\(P(y|x) = P(Y=y|X=x)\) 是在给定 \(X=x\) 的条件下 \(Y\) 取值为 \(y\) 的条件概率。
将 \(H(Y|X=x)\) 的定义代入,得到条件熵的另一种计算公式:
\[ H(Y|X) = \sum_x P(x) \left( -\sum_y P(y|x) \log_2 P(y|x) \right) = -\sum_x \sum_y P(x) P(y|x) \log_2 P(y|x) \]
利用条件概率的定义 \(P(x, y) = P(x) P(y|x)\),上式可以写为:
\[ H(Y|X) = -\sum_x \sum_y P(x, y) \log_2 P(y|x) \]
③ 联合熵、条件熵与边缘熵的关系 (Relationship between Joint, Conditional, and Marginal Entropy):
联合熵、条件熵和边缘熵(即 \(H(X)\) 和 \(H(Y)\))之间存在一个重要的关系式,称为链式法则 (Chain Rule):
\[ H(X, Y) = H(X) + H(Y|X) \]
这个公式可以理解为:度量 \(X\) 和 \(Y\) 的总不确定性(联合熵),等于先度量 \(X\) 的不确定性(边缘熵),再加上在已知 \(X\) 的情况下 \(Y\) 的剩余不确定性(条件熵)。
由于对称性,链式法则也可以写为:
\[ H(X, Y) = H(Y) + H(X|Y) \]
从这两个关系式可以推导出:
\[ H(Y|X) = H(X, Y) - H(X) \]
\[ H(X|Y) = H(X, Y) - H(Y) \]
④ 条件熵的性质 (Properties of Conditional Entropy):
⚝ \(H(Y|X) \ge 0\)。条件熵是非负的。
⚝ \(H(Y|X) \le H(Y)\)。在已知 \(X\) 的情况下,\(Y\) 的不确定性不会增加,通常会减少或保持不变。等号成立当且仅当 \(X\) 和 \(Y\) 是独立的(independent),此时 \(P(y|x) = P(y)\),\(H(Y|X) = H(Y)\)。
联合熵和条件熵的概念对于分析相关信源(如具有记忆的信源)以及理解信息在不同变量之间的流动非常重要。
2.6 互信息 (Mutual Information)
在信息论中,我们不仅关心单个变量的不确定性,还关心两个变量之间共享的信息量(shared information),或者说,了解一个变量能够减少关于另一个变量多少不确定性。这个概念由互信息(Mutual Information)来度量。
对于两个离散随机变量 \(X\) 和 \(Y\),它们之间的互信息 \(I(X; Y)\) 定义为:
\[ I(X; Y) = \sum_x \sum_y P(x, y) \log_2 \left( \frac{P(x, y)}{P(x) P(y)} \right) \]
其中 \(P(x, y)\) 是联合概率,\(P(x)\) 和 \(P(y)\) 是边缘概率。
互信息也可以用熵、联合熵和条件熵来表示:
\[ I(X; Y) = H(X) - H(X|Y) \]
\[ I(X; Y) = H(Y) - H(Y|X) \]
这两个公式非常直观:了解 \(Y\) 带来的关于 \(X\) 的信息量,等于 \(X\) 原本的不确定性(熵 \(H(X)\))减去在已知 \(Y\) 后 \(X\) 剩余的不确定性(条件熵 \(H(X|Y)\))。同理,了解 \(X\) 带来的关于 \(Y\) 的信息量,等于 \(Y\) 原本的不确定性(熵 \(H(Y)\))减去在已知 \(X\) 后 \(Y\) 剩余的不确定性(条件熵 \(H(Y|X)\))。
由于 \(H(X, Y) = H(X) + H(Y|X) = H(Y) + H(X|Y)\),我们还可以得到互信息与联合熵的关系:
\[ I(X; Y) = H(X) + H(Y) - H(X, Y) \]
这个公式表明,互信息等于 \(X\) 和 \(Y\) 各自的熵之和减去它们的联合熵。
互信息的性质:
① 非负性 (Non-negativity):\(I(X; Y) \ge 0\)。互信息总是非负的。当且仅当 \(X\) 和 \(Y\) 相互独立时,\(I(X; Y) = 0\),因为独立时 \(P(x, y) = P(x) P(y)\),\(\log_2(P(x, y) / (P(x) P(y))) = \log_2(1) = 0\)。独立意味着它们之间没有共享信息。
② 对称性 (Symmetry):\(I(X; Y) = I(Y; X)\)。\(X\) 提供给 \(Y\) 的信息量等于 \(Y\) 提供给 \(X\) 的信息量。
③ 与熵的关系 (Relationship with Entropy):\(I(X; X) = H(X)\)。一个变量与自身的互信息就是它本身的熵,因为了解自身可以完全消除自身的不确定性。
互信息是衡量两个随机变量之间统计依赖性(statistical dependence)强弱的重要指标。在通信系统中,互信息可以用来度量通过一个信道(channel)传输信息时,输入和输出之间的信息传输效率。在机器学习和数据科学中,互信息常用于特征选择,衡量特征与目标变量之间的相关性。
本章我们深入探讨了信息论中最基本的度量单位——熵,以及相关的联合熵、条件熵和互信息。理解这些概念是掌握信源编码乃至整个信息论的关键。熵为我们提供了衡量信息量和不确定性的数学工具,并指明了无损压缩的理论极限。在接下来的章节中,我们将利用这些理论基础,学习各种具体的信源编码算法。
1. chapter 1: 引言:信息、信息论与信源编码 (Introduction: Information, Information Theory, and Source Coding)
欢迎来到信息论的世界!📚 在这个充满数据和通信的时代,理解信息本身的本质以及如何高效、可靠地处理信息变得前所未有的重要。本章将作为您探索信息论,特别是信源编码基本原理的起点。我们将从最基础的问题——“什么是信息?”开始,追溯信息论这门学科的诞生与发展,明确信源编码在整个信息处理链条中的位置与作用,并为您提供一份本书的阅读指南,帮助您更好地规划学习路径。
1.1 什么是信息? (What is Information?)
在日常生活中,“信息”这个词无处不在。新闻报道是信息,朋友的短信是信息,甚至红绿灯的颜色也是信息。但从科学,特别是信息论的角度来看,信息有着更为精确的定义。
信息论的创始人克劳德·香农(Claude Shannon)并没有直接定义“信息”是什么,而是关注信息的度量。在他看来,信息是用来消除不确定性的东西。一个事件发生的可能性越小,当它真正发生时,所带来的信息量就越大。反之,一个必然发生的事件,其发生本身不包含任何信息。
考虑以下几个例子:
⚝ “太阳从东方升起。”—— 这句话几乎不包含信息,因为这是一个确定无疑的事件。
⚝ “今天天气晴朗。”—— 这句话包含一定信息,因为它排除了阴天、雨天等其他可能性。
⚝ “某地发生了百年不遇的地震。”—— 这句话包含大量信息,因为地震,特别是百年不遇的地震,是一个发生概率很低的事件。
从这个角度看,信息量与事件发生的概率紧密相关。概率越低,信息量越高。这种对信息的理解是信息论,特别是信源编码和信道编码理论的基石。信息论关注的是信息的统计特性,而非其语义或价值。也就是说,信息论处理的是符号串的概率结构,而不是这些符号串所代表的具体含义。
在信息论中,我们通常将信息视为从某个信源(Source)发出的消息(Message)。信源可以是一个人说话、一台摄像机拍摄图像、一个传感器测量温度等。这些消息由一系列符号(Symbols)组成,这些符号来自于一个有限或无限的字母表(Alphabet)。信源编码的任务就是以一种高效的方式表示这些符号序列。
1.2 信息论的起源与发展 (Origin and Development of Information Theory)
信息论的诞生通常被认为始于1948年,克劳德·香农(Claude Shannon)发表了划时代的论文《通信的数学理论》(A Mathematical Theory of Communication)。这篇论文为通信系统建立了一个统一的数学框架,并引入了熵(Entropy)、信道容量(Channel Capacity)等核心概念。
在香农之前,通信工程师们主要依靠直觉和经验来设计系统。香农的工作则提供了一套严谨的数学工具,使得人们能够定量地分析和设计通信系统,回答了诸如“信息传输的极限是多少?”、“如何以最有效的方式压缩数据?”等根本性问题。
香农的理论将通信过程抽象为:
信源(Source) → 信源编码器(Source Encoder) → 信道(Channel) → 信道译码器(Channel Decoder) → 信宿(Sink)
其中:
⚝ 信源(Source)产生需要传输的信息。
⚝ 信源编码器(Source Encoder)将信源输出的消息转换为适合传输的二进制或其他形式的码字(Codewords),目标是压缩数据,去除冗余。
⚝ 信道(Channel)是信息传输的媒介,可能会引入噪声(Noise)导致错误。
⚝ 信道译码器(Channel Decoder)尝试从受噪声污染的接收信号中恢复原始码字。
⚝ 信宿(Sink)接收恢复出的信息。
香农的理论分为两个主要部分:
① 信源编码理论(Source Coding Theory):研究如何在给定允许的失真度下,用最少的比特(Bits)表示信源输出的信息。其核心是数据压缩(Data Compression)。
② 信道编码理论(Channel Coding Theory):研究如何在有噪声的信道中可靠地传输信息。其核心是差错控制(Error Control)。
本书主要聚焦于信源编码的基本原理。
自香农的奠基性工作以来,信息论得到了飞速发展,并深刻影响了通信、计算机科学、数据存储、机器学习、统计物理学、生物学等众多领域。新的编码算法不断涌现,理论研究也在不断深入,以应对日益增长的数据量和多样化的应用需求。
1.3 信源编码的任务与重要性 (Task and Importance of Source Coding)
信源编码,也称为数据压缩(Data Compression),其核心任务是减少表示信源输出所需的比特数量,同时尽量保持信息的完整性(无损压缩)或在可接受的失真范围内(有损压缩)。
为什么信源编码如此重要?主要原因在于:
① 资源有限性(Resource Limitation):
▮▮▮▮ⓑ 存储空间(Storage Space):计算机硬盘、手机内存、云存储等存储介质的容量是有限的。对数据进行压缩可以存储更多的信息。
▮▮▮▮ⓒ 传输带宽(Transmission Bandwidth):通信信道的传输速率是有限的。压缩数据可以使得在单位时间内传输更多的信息,提高传输效率。例如,互联网上的图片、音频、视频文件几乎都经过了压缩。
② 提高效率(Improving Efficiency):
▮▮▮▮ⓑ 减少传输时间:数据量小,传输所需时间就短。
▮▮▮▮ⓒ 降低成本:无论是存储还是传输,数据量越小,通常成本越低。
信源编码可以分为两大类:
① 无损信源编码(Lossless Source Coding):编码和译码过程不会丢失任何信息。原始数据可以从压缩后的数据中完全恢复。适用于对数据精度要求极高的场景,如文本文件、程序代码、某些医学影像等。
② 有失真信源编码(Lossy Source Coding):编码过程会丢失一部分信息,因此从压缩后的数据中无法完全恢复原始数据。但这种信息丢失通常是人或机器感知不敏感的部分,以达到更高的压缩率。适用于对数据精度要求相对较低,但对压缩率要求很高的场景,如图像(JPEG)、音频(MP3)、视频(H.26x, MPEG)等。
信源编码的挑战在于如何在压缩率和信息损失(对于有损压缩)或计算复杂度之间找到最佳平衡。理想的信源编码器应该能够逼近信源的熵(对于无损压缩的理论极限)或率失真函数(对于有损压缩的理论极限)。
1.4 本书结构与阅读指南 (Book Structure and Reading Guide)
本书旨在系统地介绍信息论中信源编码的基本原理、经典算法及其应用。全书共分为10章及附录,结构如下:
⚝ 第1章:引言 - 介绍信息、信息论的基本概念,信源编码的任务与重要性,以及本书结构。
⚝ 第2章:信息的基本度量:熵 - 深入讲解信息论中最核心的概念——熵,以及联合熵、条件熵、互信息等相关概念,为后续章节奠定数学基础。
⚝ 第3章:无损信源编码的理论基础 - 介绍信源模型、编码与译码的基本概念,克拉夫特不等式,以及无失真信源编码定理,揭示无损压缩的理论极限。
⚝ 第4章:经典无损编码算法 - 详细讲解霍夫曼编码、算术编码、游程编码等基于概率统计的经典无损压缩算法。
⚝ 第5章:基于字典的无损编码算法 - 介绍LZ77、LZ78、LZW等基于字典匹配的无损压缩算法。
⚝ 第6章:有失真信源编码引论 - 引入有失真编码的概念,讨论失真度量和率失真理论,揭示有损压缩的理论极限。
⚝ 第7章:有失真编码技术:量化 - 详细讲解有失真编码中最基本的也是最重要的技术——量化,包括标量量化和矢量量化。
⚝ 第8章:其他有失真编码技术 - 介绍预测编码和变换编码等其他重要的有失真编码技术。
⚝ 第9章:信源编码算法的比较与选择 - 对前面介绍的各种无损和有损算法进行比较,讨论它们的优缺点、复杂度以及如何在实际应用中进行选择。
⚝ 第10章:进阶主题与应用案例 - 介绍通用编码、带边信息的信源编码等进阶概念,并结合图像、音频、视频压缩等实际应用案例,展示信源编码的强大威力。
⚝ 附录 - 提供常用数学公式和参考文献列表。
阅读指南:
本书力求内容全面且深入,适合不同层次的读者。
⚝ 对于初学者(Beginners):建议按照章节顺序阅读。重点理解第1、2、3章的基本概念和理论基础。第4、5章的经典算法是理解后续复杂算法的基础。第6、7章引入有失真概念,是理解多媒体压缩的关键。遇到复杂的数学推导或算法细节时,可以先把握核心思想,后续再深入研究。
⚝ 对于中级读者(Intermediate):在掌握基本概念的基础上,可以深入研究各章节的算法细节和理论推导。尝试理解算法的优化和变种。第6、7、8章的有失真编码内容将帮助您理解现代多媒体压缩标准。
⚝ 对于专家(Experts):本书可以作为回顾经典理论和算法的参考。第3章的理论极限、第6章的率失真理论以及第10章的进阶主题可能会引起您的兴趣。您可以重点关注算法的理论性能分析、复杂度分析以及在特定领域的最新应用。
无论您是哪个层次的读者,都鼓励您在学习过程中积极思考,尝试用所学知识分析实际问题。信息论和信源编码是一个既有深厚理论基础又与实际应用紧密相关的领域。希望本书能帮助您构建扎实的知识体系,并激发您进一步探索的兴趣。
2. chapter 2: 信息的基本度量:熵 (Basic Measure of Information: Entropy)
欢迎来到本书的第二章!在上一章中,我们对信息、信息论以及信源编码的任务有了初步的认识。我们了解到,信源编码的核心目标是以尽可能少的比特(bit)来表示信源输出的信息,同时尽量减少失真(distortion)。要实现这一目标,首先需要解决一个根本性的问题:如何度量信息?或者说,一个事件或一个信源输出到底包含了多少信息?本章将深入探讨信息论中最基本、最重要的信息度量单位——熵(Entropy),以及相关的概念如自信息(Self-Information)、联合熵(Joint Entropy)、条件熵(Conditional Entropy)和互信息(Mutual Information)。这些概念不仅是理解信源编码理论的基石,也是整个信息论大厦的支柱。
2.1 概率论基础回顾 (Review of Probability Theory Basics)
信息论与概率论(Probability Theory)密不可分。信息的度量是基于事件发生的概率。因此,在深入学习熵之前,我们有必要快速回顾一些基本的概率论概念。
⚝ 随机变量 (Random Variable):一个随机变量是将随机试验的结果映射到实数的一个函数。例如,抛掷一枚硬币,结果可以是正面或反面,我们可以定义一个随机变量 \(X\),正面对应 \(X=1\),反面对应 \(X=0\)。
⚝ 概率分布 (Probability Distribution):描述随机变量取不同值的概率。对于离散随机变量(Discrete Random Variable),我们通常使用概率质量函数(Probability Mass Function, PMF),记为 \(P(X=x)\) 或 \(p(x)\),表示随机变量 \(X\) 取特定值 \(x\) 的概率。所有可能取值的概率之和必须等于1,即 \(\sum_x p(x) = 1\)。
⚝ 联合概率 (Joint Probability):对于两个或多个随机变量,联合概率描述它们同时取特定值的概率。例如,对于随机变量 \(X\) 和 \(Y\),联合概率质量函数记为 \(P(X=x, Y=y)\) 或 \(p(x, y)\)。
⚝ 边缘概率 (Marginal Probability):由联合概率计算出的单个随机变量的概率分布。例如,\(p(x) = \sum_y p(x, y)\)。
⚝ 条件概率 (Conditional Probability):在已知一个事件发生的情况下,另一个事件发生的概率。例如,在已知 \(Y=y\) 的条件下,\(X=x\) 的条件概率记为 \(P(X=x | Y=y)\) 或 \(p(x | y)\)。根据定义,\(p(x | y) = \frac{p(x, y)}{p(y)}\),前提是 \(p(y) > 0\)。
⚝ 独立性 (Independence):如果两个随机变量 \(X\) 和 \(Y\) 的联合概率等于它们边缘概率的乘积,即 \(p(x, y) = p(x)p(y)\) 对于所有可能的 \(x, y\) 都成立,则称 \(X\) 和 \(Y\) 是独立的。独立性意味着一个变量的取值不影响另一个变量的概率分布。
⚝ 期望 (Expected Value):离散随机变量的期望是其所有可能取值与其对应概率的乘积之和,表示随机变量的平均值。对于随机变量 \(X\) 及其概率质量函数 \(p(x)\),期望记为 \(E[X]\),计算公式为 \(E[X] = \sum_x x p(x)\)。
这些概率论基础将贯穿本章及后续章节,是理解信息度量和信源编码算法的基础。
2.2 自信息 (Self-Information)
想象一下,你正在等待一个事件的发生。如果这个事件发生的概率很高,比如“太阳明天会从东方升起”,那么它的发生并不会给你带来太多“信息”或“惊喜”。但如果是一个概率很低的事件,比如“你中了彩票头奖”,那么它的发生会让你感到非常“惊喜”,并且你认为这个事件包含了大量“信息”。这直观地说明了信息的量与事件发生的概率有关:概率越低,信息量越大。
基于这种直觉,信息论的创始人克劳德·香农(Claude Shannon)定义了事件的自信息(Self-Information),也称为信息量(Information Content)。
定义: 对于一个离散随机变量 \(X\) 的一个特定取值 \(x\),其自信息 \(I(x)\) 定义为:
\[ I(x) = \log \frac{1}{p(x)} = -\log p(x) \]
其中 \(p(x) = P(X=x)\) 是事件 \(X=x\) 发生的概率。
这里对数(log)的底数决定了信息量的单位。
⚝ 如果底数是 2,单位是比特(bit)。这是信息论中最常用的单位。
⚝ 如果底数是 \(e\)(自然对数的底),单位是奈特(nat)。
⚝ 如果底数是 10,单位是哈特莱(hartley)或迪特(dit)。
在本书中,除非特别说明,我们默认对数的底数为 2,信息量的单位为比特(bit)。
自信息的性质:
① 非负性:由于 \(0 \le p(x) \le 1\),所以 \(\frac{1}{p(x)} \ge 1\),因此 \(I(x) = \log \frac{1}{p(x)} \ge 0\)。信息量不会是负数。
② 概率与信息量的关系:概率越低(\(p(x)\) 越接近 0),信息量越高(\(I(x)\) 越大)。当 \(p(x) = 1\) 时,事件必然发生,信息量为 \(-\log_2(1) = 0\),符合直觉。当 \(p(x) \to 0\) 时,\(I(x) \to \infty\),表示极不可能事件的发生包含极大的信息量。
③ 独立事件的信息量:如果两个事件 \(A\) 和 \(B\) 是独立的,它们同时发生的概率是 \(P(A \text{ and } B) = P(A)P(B)\)。那么同时发生的信息量是 \(I(A \text{ and } B) = -\log(P(A)P(B)) = -\log P(A) - \log P(B) = I(A) + I(B)\)。独立事件的信息量是它们各自信息量的简单相加,这符合我们对信息“可叠加”的直觉。
例子: 考虑一个抛硬币的例子。
⚝ 如果硬币是公平的,正面(H)和反面(T)的概率都是 0.5。
▮▮▮▮⚝ 出现正面的自信息是 \(I(H) = -\log_2(0.5) = -\log_2(1/2) = -(-1) = 1\) 比特。
▮▮▮▮⚝ 出现反面的自信息是 \(I(T) = -\log_2(0.5) = 1\) 比特。
这表示抛一次公平硬币的结果包含 1 比特的信息。
⚝ 如果硬币是不公平的,比如出现正面的概率是 0.9,出现反面的概率是 0.1。
▮▮▮▮⚝ 出现正面的自信息是 \(I(H) = -\log_2(0.9) \approx 0.152\) 比特。
▮▮▮▮⚝ 出现反面的自信息是 \(I(T) = -\log_2(0.1) \approx 3.322\) 比特。
可以看到,概率较低的反面事件包含了更多的信息。
自信息度量的是单个事件发生所带来的信息量。然而,在信息论和信源编码中,我们更关心的是信源的平均信息量,这引出了熵的概念。
2.3 离散无记忆信源的熵 (Entropy of Discrete Memoryless Sources)
信源(Source)是产生消息的实体。离散信源(Discrete Source)产生的消息是有限或可数个符号(symbol)组成的序列。离散无记忆信源(Discrete Memoryless Source, DMS)是指信源在不同时刻产生的符号是相互独立的,且每个符号的概率分布是固定的。
对于一个离散无记忆信源 \(S\),其输出符号集为 \(\mathcal{X} = \{x_1, x_2, \dots, x_n\}\),每个符号 \(x_i\) 出现的概率为 \(p(x_i)\),其中 \(\sum_{i=1}^n p(x_i) = 1\)。
熵(Entropy)是描述离散随机变量不确定性(uncertainty)的度量,也可以理解为描述信源输出的平均信息量。它是信源中每个符号的自信息的期望值(Expected Value)。
定义: 离散无记忆信源 \(S\) 的熵 \(H(X)\) 定义为:
\[ H(X) = E[I(X)] = \sum_{x \in \mathcal{X}} p(x) I(x) = \sum_{x \in \mathcal{X}} p(x) (-\log p(x)) \]
通常写为:
\[ H(X) = -\sum_{x \in \mathcal{X}} p(x) \log p(x) \]
其中,当 \(p(x) = 0\) 时,\(p(x) \log p(x)\) 项被定义为 0(因为 \(\lim_{p \to 0^+} p \log p = 0\))。
熵的单位取决于对数的底数,通常使用比特(bit),对应底数为 2。
熵的意义:
① 平均不确定性:熵度量了信源输出的平均不确定性。不确定性越高,熵越大。
② 平均信息量:熵度量了信源输出的每个符号平均携带的信息量。
③ 编码下界:根据香农的无失真信源编码定理(Shannon's Lossless Source Coding Theorem),熵 \(H(X)\) 给出了对该信源进行无失真编码时,每个符号平均所需的最小比特数。任何无失真编码方案的平均码长(Average Codeword Length)都不可能小于信源的熵。
例子:
⚝ 公平硬币信源: 符号集 \(\{H, T\}\),\(p(H) = 0.5, p(T) = 0.5\)。
\[ H(X) = -0.5 \log_2(0.5) - 0.5 \log_2(0.5) = -0.5(-1) - 0.5(-1) = 0.5 + 0.5 = 1 \text{ 比特} \]
一个公平硬币信源的熵是 1 比特,这意味着平均每个抛掷结果携带 1 比特的信息,并且理论上可以用平均 1 比特来表示。
⚝ 不公平硬币信源: 符号集 \(\{H, T\}\),\(p(H) = 0.9, p(T) = 0.1\)。
\[ H(X) = -0.9 \log_2(0.9) - 0.1 \log_2(0.1) \approx -0.9(-0.152) - 0.1(-3.322) \approx 0.137 + 0.332 = 0.469 \text{ 比特} \]
不公平硬币信源的熵小于公平硬币信源的熵。这是因为不确定性降低了(出现正面的可能性很高),平均信息量也随之降低。
⚝ 确定性信源: 符号集 \(\{A, B\}\),\(p(A) = 1, p(B) = 0\)。
\[ H(X) = -1 \log_2(1) - 0 \log_2(0) = -1 \cdot 0 - 0 = 0 \text{ 比特} \]
一个确定性信源的熵是 0。因为其输出是确定的,没有任何不确定性,也不包含新的信息。
这些例子直观地展示了熵如何度量不确定性和平均信息量。熵是信源编码效率的理论极限。
2.4 熵的性质与意义 (Properties and Significance of Entropy)
熵作为信息论的核心概念,具有许多重要的性质,这些性质进一步揭示了其作为信息度量的意义。
① 非负性 (Non-negativity):\(H(X) \ge 0\)。熵不可能为负数,因为概率 \(p(x) \in [0, 1]\),\(\log p(x) \le 0\),所以 \(-p(x) \log p(x) \ge 0\)。
② 确定性事件的熵为零 (Entropy of Deterministic Events is Zero):如果信源输出是确定的,即某个符号的概率为 1,其他符号概率为 0,则熵为 0。这与直觉相符,确定性事件不包含信息。
③ 最大熵 (Maximum Entropy):对于具有 \(n\) 个可能输出符号的离散信源,当且仅当所有符号的概率相等时(即服从均匀分布,\(p(x_i) = 1/n\) 对于所有 \(i\)),熵达到最大值。
\[ H(X) \le \log_2 n \]
最大熵对应于最大的不确定性。例如,对于一个有 8 个等概率符号的信源,最大熵为 \(\log_2 8 = 3\) 比特。这意味着平均每个符号最多包含 3 比特的信息。
④ 凹函数性质 (Concavity):熵函数 \(H(p_1, p_2, \dots, p_n)\) 是关于概率分布 \((p_1, p_2, \dots, p_n)\) 的一个凹函数(concave function)。这意味着通过平均化概率分布(使其更接近均匀分布),可以增加熵。
⑤ 熵与编码效率 (Entropy and Coding Efficiency):如前所述,熵 \(H(X)\) 是对离散无记忆信源进行无失真编码的平均码长的理论下界。任何无失真编码方案的平均码长 \(L\) 都满足 \(L \ge H(X)\)。优秀的无损编码算法(如霍夫曼编码、算术编码)的目标就是使平均码长尽可能接近熵。熵因此成为衡量编码效率的基准。
⑥ 熵的链式法则 (Chain Rule for Entropy):对于联合随机变量 \((X, Y)\),联合熵 \(H(X, Y)\) 可以分解为 \(H(X, Y) = H(X) + H(Y|X)\),其中 \(H(Y|X)\) 是给定 \(X\) 后 \(Y\) 的条件熵。这个性质在分析具有记忆的信源时非常有用。
熵是信息论中对“信息量”进行量化定义的基石。它不仅提供了衡量信源不确定性和平均信息量的数学工具,更重要的是,它为无失真数据压缩设定了理论上的极限。理解熵的性质对于设计和评估信源编码算法至关重要。
2.5 联合熵与条件熵 (Joint Entropy and Conditional Entropy)
在现实世界中,信息源的输出往往不是孤立的,而是相互关联的。例如,文本中的下一个单词通常与前一个单词有关。为了描述多个随机变量的整体不确定性以及在已知某些变量取值的情况下其他变量的不确定性,我们需要引入联合熵和条件熵的概念。
联合熵 (Joint Entropy)
联合熵度量了两个或多个随机变量组成的联合系统的总不确定性或平均信息量。
定义: 对于两个离散随机变量 \(X\) 和 \(Y\),其联合概率质量函数为 \(p(x, y)\),它们的联合熵 \(H(X, Y)\) 定义为:
\[ H(X, Y) = -\sum_{x \in \mathcal{X}} \sum_{y \in \mathcal{Y}} p(x, y) \log p(x, y) \]
其中 \(\mathcal{X}\) 和 \(\mathcal{Y}\) 分别是 \(X\) 和 \(Y\) 的可能取值集合。
联合熵 \(H(X, Y)\) 度量了同时观察 \(X\) 和 \(Y\) 的结果所带来的平均信息量。
条件熵 (Conditional Entropy)
条件熵度量了在已知一个或多个随机变量的取值后,另一个随机变量的剩余不确定性或平均信息量。
定义: 对于两个离散随机变量 \(X\) 和 \(Y\),在已知 \(X\) 的取值后,\(Y\) 的条件熵 \(H(Y|X)\) 定义为 \(Y\) 在给定 \(X=x\) 时的条件熵 \(H(Y|X=x)\) 关于 \(X\) 的所有可能取值的期望:
\[ H(Y|X) = \sum_{x \in \mathcal{X}} p(x) H(Y|X=x) \]
其中,\(H(Y|X=x) = -\sum_{y \in \mathcal{Y}} p(y|x) \log p(y|x)\) 是在给定 \(X=x\) 的条件下 \(Y\) 的熵,\(p(y|x) = P(Y=y|X=x)\) 是条件概率。
将 \(H(Y|X=x)\) 的定义代入,得到条件熵的另一种计算公式:
\[ H(Y|X) = -\sum_{x \in \mathcal{X}} \sum_{y \in \mathcal{Y}} p(x, y) \log p(y|x) \]
利用条件概率的定义 \(p(y|x) = \frac{p(x, y)}{p(x)}\),可以推导出熵的链式法则:
\[ H(X, Y) = H(X) + H(Y|X) \]
这个公式非常重要。它表明同时观察 \(X\) 和 \(Y\) 的总不确定性等于先观察 \(X\) 的不确定性,加上在已知 \(X\) 的情况下观察 \(Y\) 的剩余不确定性。
类似地,也有 \(H(X, Y) = H(Y) + H(X|Y)\)。
性质:
① \(H(X, Y) \ge 0\),\(H(Y|X) \ge 0\)。
② \(H(X, Y) \le H(X) + H(Y)\)。当且仅当 \(X\) 和 \(Y\) 相互独立时,等号成立。独立性意味着知道 \(X\) 的值对 \(Y\) 的不确定性没有影响,即 \(H(Y|X) = H(Y)\),此时 \(H(X, Y) = H(X) + H(Y)\)。
③ \(H(Y|X) \le H(Y)\)。知道 \(X\) 的值通常会减少 \(Y\) 的不确定性,因此条件熵不会大于边缘熵。当且仅当 \(X\) 和 \(Y\) 相互独立时,等号成立。
联合熵和条件熵的概念对于分析具有记忆的信源(Memory Source)至关重要。例如,对于一阶马尔可夫信源(First-order Markov Source),当前符号的概率分布依赖于前一个符号。其平均信息量(即每个符号的平均熵)可以通过条件熵来计算。
2.6 互信息 (Mutual Information)
互信息(Mutual Information)度量了两个随机变量之间相互依赖的程度,或者说,一个随机变量包含的关于另一个随机变量的信息量。它是信息论中用于衡量通信信道容量(Channel Capacity)的关键概念,尽管信道容量不是本章的重点,但互信息本身也是描述变量间关联性的重要工具。
定义: 对于两个离散随机变量 \(X\) 和 \(Y\),它们的互信息 \(I(X; Y)\) 定义为 \(X\) 的熵减去在已知 \(Y\) 的条件下 \(X\) 的条件熵:
\[ I(X; Y) = H(X) - H(X|Y) \]
同样,由于互信息是关于 \(X\) 和 \(Y\) 对称的,也可以定义为 \(Y\) 的熵减去在已知 \(X\) 的条件下 \(Y\) 的条件熵:
\[ I(X; Y) = H(Y) - H(Y|X) \]
这两种定义是等价的。
利用熵的链式法则 \(H(X, Y) = H(X) + H(Y|X) = H(Y) + H(X|Y)\),我们可以推导出互信息的其他表达形式:
\[ I(X; Y) = H(X) + H(Y) - H(X, Y) \]
这个公式表明,互信息等于 \(X\) 和 \(Y\) 各自的熵之和减去它们的联合熵。这可以理解为 \(X\) 和 \(Y\) 的总不确定性(\(H(X)+H(Y)\))减去它们共同的不确定性(\(H(X,Y)\)),剩下的就是它们之间共享的信息量。
互信息也可以用联合概率和边缘概率来表示:
\[ I(X; Y) = \sum_{x \in \mathcal{X}} \sum_{y \in \mathcal{Y}} p(x, y) \log \frac{p(x, y)}{p(x)p(y)} \]
这个公式中的 \(\log \frac{p(x, y)}{p(x)p(y)}\) 项被称为点互信息(Pointwise Mutual Information),它度量了特定事件对 \((X=x, Y=y)\) 发生的概率与它们独立发生时的概率之比的对数。互信息是点互信息的期望值。
互信息的性质:
① 非负性 (Non-negativity):\(I(X; Y) \ge 0\)。互信息总是非负的。当且仅当 \(X\) 和 \(Y\) 相互独立时,\(I(X; Y) = 0\)。独立意味着一个变量不包含关于另一个变量的任何信息。
② 对称性 (Symmetry):\(I(X; Y) = I(Y; X)\)。\(X\) 包含的关于 \(Y\) 的信息量等于 \(Y\) 包含的关于 \(X\) 的信息量。
③ 与熵和条件熵的关系:互信息可以看作是由于知道另一个变量而减少的不确定性:\(I(X; Y) = H(X) - H(X|Y) = H(Y) - H(Y|X)\)。
④ 与联合熵的关系:\(I(X; Y) = H(X) + H(Y) - H(X, Y)\)。
互信息在信息论中有着广泛的应用,特别是在信道编码(Channel Coding)中,它用于定义信道容量,即通过该信道可靠传输信息的最大速率。在信源编码中,互信息可以用来分析不同部分数据之间的依赖性,从而指导更有效的压缩策略,尤其是在处理具有记忆或相关性的信源时。
本章我们深入探讨了信息论中度量信息和不确定性的基本概念:自信息、熵、联合熵、条件熵和互信息。熵作为信源的平均信息量,为无失真信源编码设定了理论极限。联合熵和条件熵帮助我们理解多变量系统的整体和条件不确定性,而互信息则量化了变量之间的相互依赖性。掌握这些概念是理解后续信源编码算法原理和性能分析的关键。在下一章中,我们将基于这些理论基础,探讨无损信源编码的更深层原理和定理。
3. chapter 3: 无损信源编码的理论基础 (Theoretical Foundation of Lossless Source Coding)
同学们,欢迎来到信息论中至关重要的一章:无损信源编码的理论基础。在上一章,我们学习了如何度量信息,引入了熵(Entropy)这一核心概念,它为我们衡量信源的不确定性以及压缩的极限提供了理论工具。现在,我们将深入探讨如何利用这些理论,设计出能够高效、无损地表示信源输出的编码方法。无损信源编码(Lossless Source Coding)的目标是在不丢失任何信息的前提下,尽可能地减少表示信源所需的平均比特数。本章将构建起理解各种无损编码算法所需的理论框架。
3.1 信源模型 (Source Models)
在进行信源编码之前,我们首先需要对信息源进行建模。一个信源模型(Source Model)是对信息产生过程的数学描述,它帮助我们理解信源的统计特性,从而设计出更有效的编码方案。不同的信源具有不同的特性,例如,有些信源产生的符号是相互独立的,而有些则存在依赖关系。
① 离散无记忆信源 (Discrete Memoryless Source, DMS)
这是最简单也是最基础的信源模型。一个离散无记忆信源由一个有限的符号集(Alphabet)\( \mathcal{X} = \{x_1, x_2, \dots, x_M\} \) 和每个符号对应的概率分布 \( P = \{p(x_1), p(x_2), \dots, p(x_M)\} \) 组成,其中 \( p(x_i) = P(X=x_i) \)。“离散”意味着符号集是有限或可数的,“无记忆”意味着信源在不同时刻产生的符号是相互独立的。
例如,抛掷一个不均匀的硬币,结果是正面(H)或反面(T),概率分别为 \( p(H) \) 和 \( p(T) \)。每次抛掷的结果都独立于之前的抛掷,这就是一个二元离散无记忆信源。
② 离散平稳信源 (Discrete Stationary Source, DSS)
离散平稳信源产生的符号序列的统计特性不随时间变化。也就是说,任意时刻 \( t \) 产生的符号 \( X_t \) 的概率分布与 \( t \) 无关,且任意长度为 \( k \) 的符号序列 \( (X_t, X_{t+1}, \dots, X_{t+k-1}) \) 的联合概率分布也与 \( t \) 无关。离散无记忆信源是离散平稳信源的一个特例。
③ 离散马尔可夫信源 (Discrete Markov Source)
离散马尔可夫信源是一种具有有限记忆的平稳信源。在一个 \( k \) 阶马尔可夫信源中,当前时刻产生的符号 \( X_t \) 的概率仅依赖于前 \( k \) 个符号 \( X_{t-1}, X_{t-2}, \dots, X_{t-k} \)。当 \( k=0 \) 时,马尔可夫信源就退化为无记忆信源。
例如,在英文文本中,字母出现的概率往往依赖于前一个或几个字母(比如字母 'q' 后面很可能跟着 'u')。这可以用一个马尔可夫模型来描述。
在本书关于无损信源编码基础理论的讨论中,我们将主要聚焦于离散无记忆信源(DMS),因为它是理解熵、编码定理以及基本编码算法的基石。对于具有记忆的信源,通常可以通过将其扩展为无记忆信源(例如,将连续的符号序列视为新的“状态”或“符号”)或使用更复杂的编码技术来处理。
3.2 编码与译码 (Encoding and Decoding)
信源编码(Source Coding)是将信源输出的符号或符号序列转换为码字(Codeword)序列的过程。码字通常是由二进制符号(0和1)组成的有限长序列。与此相对,译码(Decoding)是将接收到的码字序列恢复成原始信源符号或符号序列的过程。
① 编码器 (Encoder)
编码器是一个函数或算法,它接收信源输出的符号或符号序列作为输入,并产生一个对应的码字作为输出。对于无损编码,编码器必须是可逆的,即不同的信源输出序列必须映射到不同的码字序列,以便在译码端能够完全恢复原始信息。
一个简单的编码器可以是一个码本(Codebook),它是一个表格,列出了信源符号集中的每个符号及其对应的码字。
② 译码器 (Decoder)
译码器是编码器的逆过程。它接收编码器产生的码字序列作为输入,并尝试恢复原始的信源符号序列。对于无损编码,理想的译码器应该能够完美地恢复原始信息,没有任何损失。
③ 编码的目标
无损信源编码的主要目标是在保证无失真恢复的前提下,最小化表示信源所需的平均码长(Average Codeword Length)。平均码长越短,意味着压缩效率越高。
考虑一个离散无记忆信源 \( \mathcal{X} = \{x_1, x_2, \dots, x_M\} \) 及其概率分布 \( P = \{p_1, p_2, \dots, p_M\} \),其中 \( p_i = p(x_i) \)。一个针对单个符号的编码(Symbol Code)是将每个符号 \( x_i \) 映射到一个二进制码字 \( c_i \)。如果 \( l_i \) 是码字 \( c_i \) 的长度,那么该编码的平均码长定义为:
\[ \bar{L} = \sum_{i=1}^{M} p_i l_i \]
我们的目标就是找到一个合适的编码,使得 \( \bar{L} \) 尽可能小。
3.3 码的分类:唯一可译码与前缀码 (Code Classification: Uniquely Decodable Codes and Prefix Codes)
在设计编码时,仅仅将每个符号映射到一个码字是不够的,我们还需要考虑如何将连续的码字序列正确地解析回原始的符号序列。这就引出了码的可译性问题。
① 码 (Code)
一个码 \( C \) 是一个从信源符号集 \( \mathcal{X} \) 到有限长度二进制串集合 \( \{0, 1\}^* \) 的映射 \( C: \mathcal{X} \to \{0, 1\}^* \)。对于每个符号 \( x_i \in \mathcal{X} \),其对应的码字为 \( c_i = C(x_i) \),长度为 \( l_i = |c_i| \)。
② 码的扩展 (Extension of a Code)
对于一个符号序列 \( s = x_{i_1} x_{i_2} \dots x_{i_n} \),其对应的码字序列(或称为扩展码字)是简单地将每个符号的码字连接起来:\( C(s) = C(x_{i_1}) C(x_{i_2}) \dots C(x_{i_n}) = c_{i_1} c_{i_2} \dots c_{i_n} \)。
③ 唯一可译码 (Uniquely Decodable Code, UD Code)
如果对于任意两个不同的有限长信源符号序列 \( s_1 \) 和 \( s_2 \),它们的扩展码字 \( C(s_1) \) 和 \( C(s_2) \) 总是不同的,那么称该码 \( C \) 是唯一可译码。
换句话说,任何由该码的码字组成的有限长二进制串,如果它是某个信源符号序列的扩展码字,那么这个信源符号序列是唯一的。
⚝ 例子:
▮▮▮▮⚝ 码 A: \( x_1 \to 0, x_2 \to 01, x_3 \to 1 \)
▮▮▮▮⚝ 考虑码字序列 \( 01 \)。它可以是 \( C(x_1)C(x_3) \) 对应 \( x_1 x_3 \),也可以是 \( C(x_2) \) 对应 \( x_2 \)。因此,码 A 不是唯一可译码。
▮▮▮▮⚝ 码 B: \( x_1 \to 0, x_2 \to 10, x_3 \to 110 \)
▮▮▮▮⚝ 考虑码字序列 \( 010110 \)。它只能被解析为 \( 0 | 10 | 110 \),对应符号序列 \( x_1 x_2 x_3 \)。这个码是唯一可译码。
④ 前缀码 (Prefix Code) 或 瞬时码 (Instantaneous Code)
如果码集中没有任何一个码字是另一个码字的前缀(Prefix),那么称该码为前缀码。
⚝ 例子:
▮▮▮▮⚝ 码 C: \( x_1 \to 0, x_2 \to 10, x_3 \to 11 \)
▮▮▮▮⚝ 码字 \( 0 \) 不是 \( 10 \) 或 \( 11 \) 的前缀。码字 \( 10 \) 不是 \( 0 \) 或 \( 11 \) 的前缀。码字 \( 11 \) 不是 \( 0 \) 或 \( 10 \) 的前缀。因此,码 C 是前缀码。
▮▮▮▮⚝ 码 D: \( x_1 \to 0, x_2 \to 01, x_3 \to 1 \) (同码 A)
▮▮▮▮⚝ 码字 \( 0 \) 是码字 \( 01 \) 的前缀。因此,码 D 不是前缀码。
⑤ 前缀码与唯一可译码的关系
一个重要的结论是:所有的前缀码都是唯一可译码。
这是因为在前缀码中,当我们在接收到的二进制串中识别出一个码字时,我们立即知道它对应一个符号,并且这个码字不可能再是后续某个更长码字的前缀。因此,译码过程可以立即进行,无需向前看(Lookahead),所以前缀码也称为瞬时码。
反之不成立,存在唯一可译码但不是前缀码的情况(如上面的码 B)。然而,对于任何一个唯一可译码,都存在一个前缀码,其码字长度集合与该唯一可译码的码字长度集合完全相同。这意味着在考虑码字长度集合时,研究前缀码的性质就足够了。
3.4 克拉夫特不等式 (Kraft's Inequality)
克拉夫特不等式(Kraft's Inequality)给出了构造一个具有给定码字长度集合的二元前缀码(或唯一可译码)的充要条件。
① 克拉夫特不等式的内容
对于一个包含 \( M \) 个码字的二元码 \( C = \{c_1, c_2, \dots, c_M\} \),其码字长度分别为 \( l_1, l_2, \dots, l_M \)。
⚝ 如果码 \( C \) 是一个前缀码,则其码字长度必须满足:
\[ \sum_{i=1}^{M} 2^{-l_i} \le 1 \]
⚝ 反之,如果一组正整数 \( l_1, l_2, \dots, l_M \) 满足上述不等式,则存在一个二元前缀码,其码字长度恰好是 \( l_1, l_2, \dots, l_M \)。
② 对于唯一可译码
克拉夫特不等式对于唯一可译码同样成立:
⚝ 如果码 \( C \) 是一个唯一可译码,则其码字长度必须满足:
\[ \sum_{i=1}^{M} 2^{-l_i} \le 1 \]
⚝ 反之,如果一组正整数 \( l_1, l_2, \dots, l_M \) 满足上述不等式,则存在一个二元唯一可译码,其码字长度恰好是 \( l_1, l_2, \dots, l_M \)。
③ 意义
克拉夫特不等式建立了码字长度与码的可译性之间的基本关系。它告诉我们,码字长度不能任意选择。如果码字太短,就无法构造出唯一可译码。不等式中的求和项 \( 2^{-l_i} \) 可以理解为码字 \( c_i \) 在所有可能的无限长二进制序列中所“占据”的比例。对于前缀码,这些“占据”的比例是互不重叠的,因此总和不能超过 1。
④ 等号成立的情况
如果 \( \sum_{i=1}^{M} 2^{-l_i} = 1 \),则称该前缀码是完备的(Complete)或紧致的(Compact)。这意味着所有可能的二进制串都被用尽了,没有“浪费”的码空间。对于完备前缀码,任何由码字组成的序列都不能再添加任何比特而仍然是某个符号序列的扩展码字。
3.5 无失真信源编码定理 (Lossless Source Coding Theorem)
无失真信源编码定理,也称为香农第一定理(Shannon's First Theorem),是信息论中最核心的定理之一。它为无损压缩设定了理论极限。
① 定理内容 (对于离散无记忆信源 DMS)
对于一个具有符号集 \( \mathcal{X} \) 和概率分布 \( P \) 的离散无记忆信源,其熵为 \( H(X) \) 比特/符号。
⚝ 对于任何无失真编码,其平均码长 \( \bar{L} \) 必须满足:
\[ \bar{L} \ge H(X) \]
⚝ 换句话说,不可能构造一个无失真码,其平均码长小于信源的熵。熵是无损压缩的理论下界。
② 定理的另一部分 (可达性)
⚝ 对于任意小的正数 \( \epsilon > 0 \),存在一个无失真编码方法(通常是对足够长的信源输出序列进行分组编码),使得平均码长 \( \bar{L} \) 满足:
\[ \bar{L} < H(X) + \epsilon \]
这意味着,虽然不能达到小于 \( H(X) \) 的平均码长,但我们可以通过对足够长的信源输出序列进行编码,使得平均码长任意接近 \( H(X) \)。
③ 意义与解释
这个定理告诉我们,信源的熵 \( H(X) \) 是其内在的不确定性度量,也是进行无损压缩所能达到的最高效率的标志。任何试图将平均码长压缩到低于熵的编码方法都必然是有损的(即无法完全恢复原始信息)。
定理的可达性部分表明,通过对长序列进行编码(即块编码,Block Coding),我们可以设计出接近理论极限的编码方案。这通常是通过利用信源的统计特性,特别是高概率序列和低概率序列的出现频率差异来实现的。高概率的序列分配短码字,低概率的序列分配长码字,从而使得平均码长接近熵。
④ 与典型序列 (Typical Sequences) 的联系
香农第一定理的证明通常依赖于渐近等分性质(Asymptotic Equipartition Property, AEP)和典型序列的概念。对于一个长为 \( N \) 的信源输出序列,绝大多数可能的序列出现的概率非常低,而只有一小部分“典型序列”出现的总概率接近于 1。典型序列的数量大约是 \( 2^{N \cdot H(X)} \)。因此,我们只需要为这些典型序列分配码字,就可以以极高的概率实现无损压缩,并且每个典型序列的码长大约是 \( N \cdot H(X) \),从而平均到每个符号的码长接近 \( H(X) \)。
3.6 码字长度与平均码长 (Codeword Length and Average Codeword Length)
在无损信源编码中,我们为信源符号(或符号序列)分配二进制码字。每个码字都有一个特定的长度。
① 码字长度 (Codeword Length)
对于信源符号 \( x_i \),其对应的码字 \( c_i \) 的长度记为 \( l_i \)。这些长度是正整数。
② 平均码长 (Average Codeword Length)
对于一个离散无记忆信源 \( \mathcal{X} = \{x_1, x_2, \dots, x_M\} \) 及其概率分布 \( P = \{p_1, p_2, \dots, p_M\} \),以及一个为每个符号 \( x_i \) 分配了码字 \( c_i \) 且长度为 \( l_i \) 的码 \( C \),该码的平均码长定义为:
\[ \bar{L} = \sum_{i=1}^{M} p_i l_i \]
平均码长是衡量编码效率的关键指标。它表示平均每个信源符号被编码后所需的二进制比特数。
③ 最小平均码长
根据无失真信源编码定理,对于任何无失真码,其平均码长 \( \bar{L} \) 必须大于或等于信源的熵 \( H(X) \)。
\[ \bar{L} \ge H(X) \]
我们的目标是设计一个无失真码,使得 \( \bar{L} \) 尽可能接近 \( H(X) \)。
④ 如何选择码字长度?
为了使平均码长最小,我们应该为概率高的符号分配短码字,为概率低的符号分配长码字。这符合我们的直观理解:经常出现的符号应该用更少的比特表示。
理想情况下,我们希望码字长度 \( l_i \) 能够满足 \( l_i \approx -\log_2 p_i \)。这是因为如果 \( l_i = -\log_2 p_i \),那么平均码长将是:
\[ \bar{L} = \sum_{i=1}^{M} p_i (-\log_2 p_i) = H(X) \]
然而,码字长度 \( l_i \) 必须是整数。因此,我们通常只能选择接近 \( -\log_2 p_i \) 的整数长度。
具体来说,为了满足克拉夫特不等式并使平均码长最小,最优的码字长度 \( l_i^* \) 应该满足:
\[ -\log_2 p_i \le l_i^* < -\log_2 p_i + 1 \]
即 \( l_i^* = \lceil -\log_2 p_i \rceil \)。
如果按照这个原则选择码字长度,并能构造出前缀码,那么平均码长 \( \bar{L} \) 将满足:
\[ H(X) \le \bar{L} < H(X) + 1 \]
这意味着对于单个符号编码,平均码长与熵的差距最多为 1 比特。通过对符号序列进行分组编码,可以将这个差距平均到每个符号上,使其任意小,从而逼近熵的极限。
本章为我们理解无损信源编码奠定了坚实的理论基础。我们了解了信源模型,编码与译码的基本概念,区分了唯一可译码和前缀码,学习了约束码字长度的克拉夫特不等式,以及最重要的——指明了无损压缩极限的无失真信源编码定理。在接下来的章节中,我们将学习如何利用这些理论,设计和实现具体的无损编码算法,如霍夫曼编码和算术编码,它们正是为了逼近熵极限而设计的。
4. chapter 4: 经典无损编码算法 (Classic Lossless Coding Algorithms)
在第三章中,我们探讨了无失真信源编码的理论基础,包括熵作为信息的基本度量、克拉夫特不等式 (Kraft's Inequality) 以及无失真信源编码定理 (Lossless Source Coding Theorem)。我们了解到,对于一个离散无记忆信源 (Discrete Memoryless Source, DMS),其平均码长 (Average Codeword Length) 的下界是信源的熵 (Entropy)。本章将深入介绍几种经典且广泛应用的无损信源编码算法,它们旨在逼近或达到这一理论极限,实现对信源的有效压缩。我们将重点讲解变长编码 (Variable-Length Coding) 的基本思想,以及霍夫曼编码 (Huffman Coding)、算术编码 (Arithmetic Coding) 和游程编码 (Run-Length Encoding, RLE) 等具体算法的原理、构造和应用。
4.1 变长编码的基本思想 (Basic Idea of Variable-Length Coding)
在固定长度编码 (Fixed-Length Coding) 中,信源字母表 (Source Alphabet) 中的每个符号 (Symbol) 都被赋予一个长度相同的码字 (Codeword)。例如,如果信源有 4 个符号,我们可以使用 2 比特 (bit) 的固定长度码字来表示它们:00, 01, 10, 11。这种方法简单直观,但对于符号出现概率 (Probability) 不等的信源来说,效率不高。
变长编码的核心思想是:为出现概率高的符号分配较短的码字,为出现概率低的符号分配较长的码字。这样,在编码一个长序列时,由于高概率符号出现的次数更多,整体的平均码长就会降低,从而实现数据压缩。
考虑一个简单的例子:一个信源产生符号 A, B, C, D,它们的概率分别为 \(P(A)=0.5\),\(P(B)=0.25\),\(P(C)=0.125\),\(P(D)=0.125\)。
如果使用固定长度编码,需要 2 比特/符号。
如果使用变长编码,我们可以尝试:
A -> 0 (1 bit)
B -> 10 (2 bits)
C -> 110 (3 bits)
D -> 111 (3 bits)
这种变长编码方案的平均码长是:
\(L_{avg} = 0.5 \times 1 + 0.25 \times 2 + 0.125 \times 3 + 0.125 \times 3\)
\(L_{avg} = 0.5 + 0.5 + 0.375 + 0.375 = 1.75\) 比特/符号。
该信源的熵为:
\(H = - (0.5 \log_2 0.5 + 0.25 \log_2 0.25 + 0.125 \log_2 0.125 + 0.125 \log_2 0.125)\)
\(H = - (0.5 \times (-1) + 0.25 \times (-2) + 0.125 \times (-3) + 0.125 \times (-3))\)
\(H = - (-0.5 - 0.5 - 0.375 - 0.375) = 1.75\) 比特/符号。
在这个例子中,变长编码的平均码长恰好等于信源的熵,达到了理论最优。这并非巧合,而是因为符号的概率恰好是 \(2^{-n}\) 的形式。对于一般情况,变长编码的目标就是找到一种编码方案,使得平均码长尽可能接近信源的熵。
然而,变长编码引入了一个新的问题:如何确保码字序列是唯一可译的 (Uniquely Decodable)?例如,如果 A 编码为 0,AB 编码为 01,那么码字序列 01 既可以译为 AB,也可以译为 B (如果 B 编码为 1)。为了避免歧义,我们通常要求码字集合是前缀码 (Prefix Code),即任何一个码字都不是其他任何码字的前缀 (Prefix)。前缀码的一个重要性质是它们是瞬时码 (Instantaneous Code),即在接收到完整的码字后,无需查看后续的码字即可确定该符号。前缀码可以使用码树 (Code Tree) 来表示,每个信源符号对应码树的一个叶子节点 (Leaf Node),从根节点 (Root Node) 到叶子节点的路径上的分支标签 (通常是 0 或 1) 构成了该符号的码字。
变长编码算法的核心任务就是根据信源符号的概率分布,构造一个最优的或接近最优的前缀码,使得平均码长最小化。
4.2 霍夫曼编码 (Huffman Coding)
霍夫曼编码是一种经典的、广泛使用的变长编码算法,由 David A. Huffman 于 1952 年提出。它是一种贪婪算法 (Greedy Algorithm),能够为已知概率的离散无记忆信源构造出最优的前缀码,即平均码长最小。
4.2.1 霍夫曼编码原理与构造 (Principle and Construction of Huffman Coding)
霍夫曼编码的原理是基于信源符号的概率,通过迭代合并概率最小的符号来构建码树。概率越高的符号,其对应的叶子节点在码树中的深度越浅,码字长度越短。
构造霍夫曼码的步骤如下:
① 将所有信源符号视为独立的节点,每个节点包含符号及其概率。
② 将这些节点按概率大小升序排列。
③ 选择概率最小的两个节点,将它们合并成一个新的父节点。新节点的概率是其两个子节点概率之和。这两个子节点分别作为新节点的左孩子和右孩子(顺序可以约定,例如左孩子代表 0,右孩子代表 1)。
④ 将新节点插入到剩余节点列表中,并保持列表按概率升序排列。
⑤ 重复步骤 ③ 和 ④,直到列表中只剩下一个节点,这个节点就是码树的根节点。
⑥ 从根节点出发,为到达每个叶子节点的路径分配二进制位(例如,左分支为 0,右分支为 1),路径上的二进制位序列即为对应符号的霍夫曼码字。
示例:
考虑信源符号集 \(\mathcal{X} = \{A, B, C, D, E\}\),概率分布为 \(P(A)=0.3, P(B)=0.25, P(C)=0.2, P(D)=0.15, P(E)=0.1\)。
构造过程:
- 初始节点列表(按概率升序):E(0.1), D(0.15), C(0.2), B(0.25), A(0.3)
- 合并 E 和 D:新节点 ED,概率 0.1 + 0.15 = 0.25。子节点 E(0.1), D(0.15)。
列表:C(0.2), ED(0.25), B(0.25), A(0.3) (注意 ED 和 B 概率相同,顺序不影响最终码长,但影响具体码字) - 合并 C 和 ED:新节点 CED,概率 0.2 + 0.25 = 0.45。子节点 C(0.2), ED(0.25)。
列表:B(0.25), A(0.3), CED(0.45) - 合并 B 和 A:新节点 BA,概率 0.25 + 0.3 = 0.55。子节点 B(0.25), A(0.3)。
列表:CED(0.45), BA(0.55) - 合并 CED 和 BA:新节点 CEDBA,概率 0.45 + 0.55 = 1.0。子节点 CED(0.45), BA(0.55)。
列表:CEDBA(1.0) - 根节点。
码树构建(示例分支分配:左0右1):
1
CEDBA (1.0)
2
/ CED (0.45) BA (0.55)
3
/ \ / C(0.2) ED(0.25) B(0.25) A(0.3)
4
/ E(0.1) D(0.15)
得到码字:
A: 11 (长度 2)
B: 10 (长度 2)
C: 00 (长度 2)
D: 011 (长度 3)
E: 010 (长度 3)
平均码长:
\(L_{avg} = 0.3 \times 2 + 0.25 \times 2 + 0.2 \times 2 + 0.15 \times 3 + 0.1 \times 3\)
\(L_{avg} = 0.6 + 0.5 + 0.4 + 0.45 + 0.3 = 2.25\) 比特/符号。
该信源的熵为:
\(H = - (0.3 \log_2 0.3 + 0.25 \log_2 0.25 + 0.2 \log_2 0.2 + 0.15 \log_2 0.15 + 0.1 \log_2 0.1)\)
\(H \approx - (-0.521 - 0.5 - 0.464 - 0.410 - 0.332) \approx 2.227\) 比特/符号。
平均码长 2.25 比特/符号非常接近熵的理论下界 2.227 比特/符号。霍夫曼编码的平均码长 \(L_{avg}\) 满足 \(H \le L_{avg} < H + 1\)。
霍夫曼编码的优点是简单、易于实现,并且对于已知概率的 DMS 是最优的。缺点是需要预先知道信源的概率分布,并且对于符号数量非常大的信源,构建码树和查找码字会占用较多内存和计算资源。
4.2.2 扩展霍夫曼编码 (Extended Huffman Coding)
扩展霍夫曼编码的思想是对信源符号的序列(块)进行编码,而不是对单个符号进行编码。例如,将信源的两个连续符号作为一个新的“扩展符号”,构成一个新的扩展信源。如果原始信源字母表大小为 \(|\mathcal{X}|\),则由两个符号组成的扩展信源字母表大小为 \(|\mathcal{X}|^2\)。
对扩展信源进行霍夫曼编码,可以进一步提高压缩效率。根据无失真信源编码定理,对长度为 \(n\) 的信源序列进行编码,平均码长可以逼近 \(H/n\)。这意味着对扩展信源(相当于 \(n=2\) 的情况)进行编码,每个扩展符号的平均码长可以逼近 \(2H\),从而每个原始符号的平均码长逼近 \(H\)。随着扩展长度 \(n\) 的增加,平均码长可以无限接近信源的熵。
示例:
考虑一个二元信源,\(P(0)=0.9, P(1)=0.1\)。
熵 \(H = -(0.9 \log_2 0.9 + 0.1 \log_2 0.1) \approx 0.469\) 比特/符号。
对单个符号进行霍夫曼编码:0 -> 0 (1 bit), 1 -> 1 (1 bit)。平均码长 \(0.9 \times 1 + 0.1 \times 1 = 1\) 比特/符号。效率较低。
考虑扩展信源,由两个符号组成:00, 01, 10, 11。
概率:\(P(00) = 0.9 \times 0.9 = 0.81\)
\(P(01) = 0.9 \times 0.1 = 0.09\)
\(P(10) = 0.1 \times 0.9 = 0.09\)
\(P(11) = 0.1 \times 0.1 = 0.01\)
对扩展信源进行霍夫曼编码:
节点列表:11(0.01), 01(0.09), 10(0.09), 00(0.81)
合并 11 和 01:新节点 1101,概率 0.01+0.09=0.1。
列表:10(0.09), 1101(0.1), 00(0.81)
合并 10 和 1101:新节点 101101,概率 0.09+0.1=0.19。
列表:101101(0.19), 00(0.81)
合并 101101 和 00:根节点。
码字(示例):
00: 1 (长度 1)
10: 00 (长度 2)
01: 010 (长度 3)
11: 011 (长度 3)
对扩展符号的平均码长:
\(L_{avg, ext} = 0.81 \times 1 + 0.09 \times 2 + 0.09 \times 3 + 0.01 \times 3\)
\(L_{avg, ext} = 0.81 + 0.18 + 0.27 + 0.03 = 1.29\) 比特/扩展符号。
每个原始符号的平均码长:\(L_{avg} = L_{avg, ext} / 2 = 1.29 / 2 = 0.645\) 比特/符号。
这比对单个符号编码的 1 比特/符号效率高得多,更接近熵的理论值 0.469 比特/符号。
扩展霍夫曼编码的缺点是,随着扩展长度 \(n\) 的增加,扩展信源的字母表大小呈指数增长 (\(|\mathcal{X}|^n\)),导致码树的构造和存储变得非常复杂和巨大。因此,实际应用中扩展长度 \(n\) 通常不会很大。
4.2.3 自适应霍夫曼编码 (Adaptive Huffman Coding)
前面讨论的霍夫曼编码需要预先知道信源的概率分布。在许多实际应用中,信源的概率分布是未知或随时间变化的。自适应霍夫曼编码 (Adaptive Huffman Coding) 解决了这个问题,它在编码过程中动态地构建和更新霍夫曼码树。
自适应霍夫曼编码的基本思想是:
① 编码器 (Encoder) 和译码器 (Decoder) 都从一个初始的、包含所有可能符号的空码树开始。
② 逐个读取输入符号。
③ 对于每个符号,编码器根据当前码树生成码字并输出。
④ 编码器和译码器都根据刚刚处理的符号更新码树。更新规则基于符号出现的频率,频率高的节点在树中向上移动,保持霍夫曼树的属性。
更新码树的过程通常涉及维护一个按频率排序的节点列表,并使用一种称为“兄弟属性”(Sibling Property) 的规则来确保树的结构始终可以转换为霍夫曼树。当一个符号出现时,其频率增加,可能需要调整其在树中的位置以及其父节点、祖父节点等的频率。
自适应霍夫曼编码的优点是无需预知信源概率,适用于概率分布未知或变化的场景。缺点是编码和译码过程比静态霍夫曼编码更复杂,需要维护和更新码树,且初始阶段(频率信息不足时)的压缩效率可能不高。
4.3 算术编码 (Arithmetic Coding)
算术编码 (Arithmetic Coding) 是一种比霍夫曼编码更先进的无损编码方法。它不是将每个信源符号映射到一个独立的码字,而是将整个输入序列映射到 \([0, 1)\) 区间内的一个浮点数。这个浮点数可以表示为一个二进制小数,其精度越高,表示的序列越长。
4.3.1 算术编码原理 (Principle of Arithmetic Coding)
算术编码的原理基于概率区间划分。考虑一个输入序列,例如 "CAB"。假设信源字母表为 \(\{A, B, C\}\),概率分别为 \(P(A)=0.2, P(B)=0.3, P(C)=0.5\)。
① 初始时,表示整个序列的区间是 \([0, 1)\)。
② 编码第一个符号 'C':根据符号的概率,将当前区间 \([0, 1)\) 划分为与符号概率对应的子区间。
⚝ A 对应区间 \([0, 0.2)\)
⚝ B 对应区间 \([0.2, 0.2+0.3) = [0.2, 0.5)\)
⚝ C 对应区间 \([0.5, 0.5+0.5) = [0.5, 1.0)\)
由于第一个符号是 'C',当前区间更新为 \([0.5, 1.0)\)。
③ 编码第二个符号 'A':在当前区间 \([0.5, 1.0)\) 内,再次根据符号的概率划分新的子区间。新区间的长度是原区间长度乘以符号概率。
原区间长度 \(L = 1.0 - 0.5 = 0.5\)。
⚝ A 对应区间:\([0.5 + 0.5 \times 0, 0.5 + 0.5 \times 0.2) = [0.5, 0.6)\)
⚝ B 对应区间:\([0.5 + 0.5 \times 0.2, 0.5 + 0.5 \times (0.2+0.3)) = [0.6, 0.75)\)
⚝ C 对应区间:\([0.5 + 0.5 \times 0.5, 0.5 + 0.5 \times (0.5+0.5)) = [0.75, 1.0)\)
由于第二个符号是 'A',当前区间更新为 \([0.5, 0.6)\)。
④ 编码第三个符号 'B':在当前区间 \([0.5, 0.6)\) 内,再次根据符号的概率划分新的子区间。
原区间长度 \(L = 0.6 - 0.5 = 0.1\)。
⚝ A 对应区间:\([0.5 + 0.1 \times 0, 0.5 + 0.1 \times 0.2) = [0.5, 0.52)\)
⚝ B 对应区间:\([0.5 + 0.1 \times 0.2, 0.5 + 0.1 \times (0.2+0.3)) = [0.52, 0.55)\)
⚝ C 对应区间:\([0.5 + 0.1 \times 0.5, 0.5 + 0.1 \times (0.5+0.5)) = [0.55, 0.6)\)
由于第三个符号是 'B',当前区间更新为 \([0.52, 0.55)\)。
编码完成后,整个序列 "CAB" 对应于区间 \([0.52, 0.55)\)。编码器需要输出一个位于此区间内的二进制小数,并且该小数的精度足以唯一标识这个区间。例如,0.52 可以表示为二进制 0.100001...,0.55 可以表示为二进制 0.100011...。我们可以选择区间内的任意一个点,例如 0.52。将其转换为二进制小数,并输出足够多的比特,直到该前缀能够唯一确定原始区间。对于 \([0.52, 0.55)\),二进制小数 0.100001... 的前缀 0.100001 对应的区间是 \([0.515625, 0.53125)\),它包含 \([0.52, 0.55)\)。更精确地说,我们需要找到最短的二进制小数 \(0.b_1 b_2 \dots b_k\) 使得 \(0.52 \le 0.b_1 b_2 \dots b_k < 0.55\)。例如,0.10001 对应的十进制是 0.53125,位于区间内。0.100001 对应的十进制是 0.515625,不在区间内。0.100010 对应的十进制是 0.53125,位于区间内。0.100011 对应的十进制是 0.546875,位于区间内。0.1000111 对应的十进制是 0.5546875,超出区间。所以,0.100011 是一个可能的编码输出。
译码过程是编码的逆过程。译码器接收到编码的二进制小数,例如 0.53125 (对应二进制 0.10001)。
① 初始区间 \([0, 1)\)。
② 检查 0.53125 落在哪个符号的初始子区间:
⚝ A \([0, 0.2)\)
⚝ B \([0.2, 0.5)\)
⚝ C \([0.5, 1.0)\)
0.53125 落在 C 的区间 \([0.5, 1.0)\) 内,所以第一个符号是 'C'。当前区间更新为 \([0.5, 1.0)\)。
③ 在当前区间 \([0.5, 1.0)\) 内,再次根据符号概率划分子区间:
⚝ A \([0.5, 0.6)\)
⚝ B \([0.6, 0.75)\)
⚝ C \([0.75, 1.0)\)
将接收到的编码值 0.53125 映射回 \([0, 1)\) 区间:\((0.53125 - 0.5) / (1.0 - 0.5) = 0.03125 / 0.5 = 0.0625\)。
检查 0.0625 落在哪个符号的初始子区间:
⚝ A \([0, 0.2)\)
⚝ B \([0.2, 0.5)\)
⚝ C \([0.5, 1.0)\)
0.0625 落在 A 的区间 \([0, 0.2)\) 内,所以第二个符号是 'A'。当前区间更新为 \([0.5, 0.6)\)。
④ 在当前区间 \([0.5, 0.6)\) 内,再次根据符号概率划分子区间:
⚝ A \([0.5, 0.52)\)
⚝ B \([0.52, 0.55)\)
⚝ C \([0.55, 0.6)\)
将接收到的编码值 0.53125 映射回 \([0, 1)\) 区间:\((0.53125 - 0.5) / (0.6 - 0.5) = 0.03125 / 0.1 = 0.3125\)。
检查 0.3125 落在哪个符号的初始子区间:
⚝ A \([0, 0.2)\)
⚝ B \([0.2, 0.5)\)
⚝ C \([0.5, 1.0)\)
0.3125 落在 B 的区间 \([0.2, 0.5)\) 内,所以第三个符号是 'B'。当前区间更新为 \([0.52, 0.55)\)。
当译码器知道原始序列的长度(或者遇到特殊的终止符号)时,译码过程结束。
算术编码的优点:
⚝ 压缩效率高,可以逼近信源的熵,尤其对于概率分布不均匀的信源,其性能优于霍夫曼编码。
⚝ 可以方便地处理任意概率分布,包括小数概率。
⚝ 可以实现自适应编码,无需预知信源概率,通过动态更新符号频率来调整区间划分。
算术编码的缺点:
⚝ 计算复杂度较高,涉及浮点数运算(尽管实际实现中通常使用整数运算来模拟)。
⚝ 对错误敏感,编码序列中的单个比特错误可能导致后续所有符号都译码错误。
4.3.2 算术编码的实现细节 (Implementation Details of Arithmetic Coding)
直接使用浮点数进行算术编码会面临精度问题,因为计算机的浮点数表示是有限的。为了解决这个问题,实际实现中通常采用整数运算来模拟浮点运算,并使用一些技巧来避免精度损失和数值溢出。
① 整数运算模拟 (Integer Arithmetic Simulation): 使用大整数来表示区间的下界 (Lower Bound) 和范围 (Range)。例如,将 \([0, 1)\) 区间映射到 \([0, M)\) 的整数区间,其中 \(M\) 是一个足够大的整数(例如 \(2^{32}\) 或 \(2^{64}\))。区间的更新公式 \(low' = low + range \times \text{cumulative_prob_low}\) 和 \(range' = range \times \text{prob}\) 可以通过整数乘法和移位来实现。
② 区间归一化 (Interval Normalization): 随着编码的进行,区间会越来越小。当区间范围变得非常小时,后续的符号对区间的影响会越来越小,可能导致精度问题。为了保持精度,当区间范围小于某个阈值时,可以进行归一化操作。例如,如果当前区间是 \([low, high)\),且 \(high - low < 0.5\),可以将区间乘以 2,即 \([2 \times low, 2 \times high)\),同时输出一个比特(如果 \(low \ge 0.5\),输出 1 并将区间减去 0.5 后乘以 2;如果 \(low < 0.5\),输出 0 并将区间乘以 2)。这个过程可以重复进行,直到区间范围恢复到足够大。这种归一化操作使得编码器可以连续输出比特流,而不是等到整个序列编码完成后才输出一个数。
③ E3 换码 (E3 Scaling): 归一化处理了区间范围过小的问题,但如果区间 \([low, high)\) 满足 \(0.25 \le low < 0.5\) 且 \(0.5 \le high < 0.75\),即区间完全落在 \([0.25, 0.75)\) 中间区域,乘以 2 归一化后仍然可能落在 \([0.5, 1.5)\) 或 \([0, 1)\) 的中间区域,无法确定输出 0 或 1。E3 换码处理这种情况:如果区间完全落在 \([0.25, 0.75)\) 内,则将区间映射到 \([0, 1)\) 的 \([0.25, 0.75)\) 子区间,同时记录需要输出的“待定”比特数。当后续归一化操作确定了第一个比特是 0 还是 1 时,这些待定比特就可以确定并输出了。
④ 终止 (Termination): 算术编码需要一个明确的终止机制。一种方法是在编码序列末尾添加一个特殊的终止符号 (End-of-File, EOF)。另一种方法是编码完成后,输出一个位于最终区间 \([low, high)\) 内的数,例如 \(low\) 或 \(low + \epsilon\),并输出足够多的比特来区分它与相邻区间。
⑤ 概率模型 (Probability Model): 算术编码的效率高度依赖于所使用的概率模型。可以使用静态模型(预先计算好的概率)、半自适应模型(先扫描一遍计算概率,再编码)或自适应模型(在编码过程中动态更新概率)。自适应模型通常使用频率计数器,每编码一个符号就更新其频率,并可能使用平滑技术 (Smoothing) 来处理未出现的符号。
1
# 这是一个概念性的算术编码伪代码示例,不包含所有实现细节(如整数运算、归一化等)
2
3
def arithmetic_encode(symbols, probabilities):
4
"""
5
概念性算术编码函数
6
symbols: 输入符号序列
7
probabilities: 符号的概率分布字典 {symbol: probability}
8
"""
9
low = 0.0
10
high = 1.0
11
12
# 计算累积概率区间
13
cumulative_prob = {}
14
current_prob = 0.0
15
for symbol in sorted(probabilities.keys()): # 按字母顺序排序以确定区间顺序
16
cumulative_prob[symbol] = (current_prob, current_prob + probabilities[symbol])
17
current_prob += probabilities[symbol]
18
19
for symbol in symbols:
20
current_range = high - low
21
# 根据当前符号更新区间
22
low = low + current_range * cumulative_prob[symbol][0]
23
high = low + current_range * (cumulative_prob[symbol][1] - cumulative_prob[symbol][0])
24
25
# 实际实现中会在这里进行归一化和输出比特
26
27
# 编码结束,选择区间内的一个点作为最终编码值
28
# 实际实现中会输出表示这个点的二进制比特流
29
encoded_value = low # 或者 (low + high) / 2 等
30
return encoded_value, (low, high) # 返回编码值和最终区间
31
32
def arithmetic_decode(encoded_value, probabilities, sequence_length):
33
"""
34
概念性算术译码函数
35
encoded_value: 接收到的编码值
36
probabilities: 符号的概率分布字典
37
sequence_length: 原始序列长度
38
"""
39
low = 0.0
40
high = 1.0
41
decoded_symbols = []
42
43
# 计算累积概率区间
44
cumulative_prob = {}
45
current_prob = 0.0
46
for symbol in sorted(probabilities.keys()):
47
cumulative_prob[symbol] = (current_prob, current_prob + probabilities[symbol])
48
current_prob += probabilities[symbol]
49
50
for _ in range(sequence_length):
51
current_range = high - low
52
# 将编码值映射回 [0, 1) 区间
53
mapped_value = (encoded_value - low) / current_range
54
55
# 查找 mapped_value 落在哪个符号的区间
56
decoded_symbol = None
57
for symbol in sorted(probabilities.keys()):
58
if cumulative_prob[symbol][0] <= mapped_value < cumulative_prob[symbol][1]:
59
decoded_symbol = symbol
60
break
61
62
if decoded_symbol is None:
63
# 处理边界情况或错误
64
print("Error: Could not decode symbol")
65
break
66
67
decoded_symbols.append(decoded_symbol)
68
69
# 根据译码出的符号更新区间
70
low = low + current_range * cumulative_prob[decoded_symbol][0]
71
high = low + current_range * (cumulative_prob[decoded_symbol][1] - cumulative_prob[decoded_symbol][0])
72
73
# 实际实现中会在这里进行归一化和读取比特
74
75
return decoded_symbols
76
77
# 示例使用
78
# symbols_to_encode = ['C', 'A', 'B']
79
# probs = {'A': 0.2, 'B': 0.3, 'C': 0.5}
80
# encoded_val, final_interval = arithmetic_encode(symbols_to_encode, probs)
81
# print(f"Encoded value: {encoded_val}, Final interval: {final_interval}")
82
#
83
# decoded_symbols = arithmetic_decode(encoded_val, probs, len(symbols_to_encode))
84
# print(f"Decoded symbols: {decoded_symbols}")
4.4 游程编码 (Run-Length Encoding, RLE)
游程编码 (Run-Length Encoding, RLE) 是一种非常简单直观的无损编码方法,特别适用于包含大量连续重复数据的信源。它的基本思想是将连续重复出现的符号或数据序列替换为该符号和其重复次数的组合。
例如,考虑一个序列:AAAAABBBCCDAA
使用 RLE 编码后可能表示为:A5B3C2D1A2
这里的格式是 符号 + 重复次数
。如果重复次数是 1,可以省略次数或者有特定的表示方法。更常见的 RLE 实现会使用一个标记符号来指示后面的内容是游程编码,例如:
AAAAABBBCCDAA
-> (A,5)(B,3)(C,2)(D,1)(A,2)
或者在某些格式中,如果一个符号不重复,就直接写符号本身,只有重复次数大于某个阈值时才使用游程表示。
RLE 的优点:
⚝ 算法简单,易于实现。
⚝ 对于具有长游程的数据(如黑白图像、简单的图形文件格式、传真数据等)压缩效果显著。
RLE 的缺点:
⚝ 对于数据中没有或很少有连续重复的情况,RLE 可能反而会增加数据量(因为需要额外的空间存储重复次数)。
⚝ 压缩效率不如霍夫曼编码或算术编码等基于概率的方法,因为它没有利用符号之间的概率差异,只利用了符号的局部重复性。
RLE 通常作为其他更复杂的压缩算法的预处理步骤,或者用于特定类型的数据。例如,在一些位图图像格式(如 BMP)中,RLE 被用于压缩像素数据。
示例:
原始数据(像素值序列):12 12 12 12 12 34 34 56 78 78 78 78
RLE 编码:(12, 5) (34, 2) (56, 1) (78, 4)
或者更紧凑的表示,例如:5 12 2 34 1 56 4 78
RLE 的变种包括限制游程的最大长度,或者使用不同的方式来区分重复序列和非重复序列。
总结本章,我们学习了几种经典的无损信源编码算法。霍夫曼编码是一种基于符号概率构建最优前缀码的方法,适用于已知概率的离散无记忆信源,其扩展和自适应版本可以应对更复杂的情况。算术编码则是一种更强大的方法,通过将序列映射到实数区间来实现编码,可以获得接近熵的压缩效率,并且易于实现自适应编码。游程编码是一种简单的技术,适用于具有重复序列的数据。这些算法各有特点和适用范围,是无损数据压缩领域的重要基石。在下一章中,我们将探讨基于字典的无损编码算法,它们利用数据中的重复模式(不仅仅是连续重复)来进行压缩。
5. chapter 5: 基于字典的无损编码算法 (Dictionary-Based Lossless Coding Algorithms)
欢迎来到本书的第五章!在前几章中,我们深入探讨了信息论的基础,特别是熵的概念,以及无损信源编码的理论极限——信源编码定理。我们还学习了经典的无损编码方法,如霍夫曼编码和算术编码,它们通过为出现频率高的符号分配短码字来压缩数据。这些方法通常基于信源的统计特性(如符号概率)。
本章将介绍另一类重要的无损编码算法:基于字典的编码算法。这类算法的核心思想不是直接对单个符号进行编码,而是识别并替换输入数据中重复出现的字符串(或称为“短语”)为一个更短的引用(通常是字典中的索引)。随着编码过程的进行,这些算法会动态地构建或引用一个包含已出现字符串的“字典”。这种方法对于包含大量重复模式的数据(如文本文件、程序代码等)尤其有效。
我们将从字典编码的基本思想出发,然后详细解析几种最著名的基于字典的算法:LZ77、LZ78及其变体LZW。最后,我们将比较这些算法的特点、性能和适用场景。
5.1 字典编码的基本思想 (Basic Idea of Dictionary Coding)
字典编码 (Dictionary Coding) 的基本原理是利用数据中的冗余,特别是重复出现的字符串。想象一下,如果你在写一篇文章,并且某个词语或短语反复出现,你可以第一次完整地写出它,然后在后续出现时用一个简短的代号来代替,并在文章末尾提供一个“代号-短语”对照表。字典编码正是采用了类似的策略,只不过这个“对照表”——字典——通常是在编码或解码过程中动态构建的。
核心思想可以概括为:
① 识别重复出现的字符串 (Identifying Repeating Strings)。
② 将这些字符串存储在一个字典中 (Storing these Strings in a Dictionary)。
③ 在数据流中遇到已在字典中的字符串时,输出一个指向字典条目的短引用,而不是字符串本身 (Replacing occurrences of strings found in the dictionary with short references).
④ 如果遇到新的字符串(或现有字符串的扩展),将其添加到字典中,并输出相应的编码 (Adding new strings or extensions of existing strings to the dictionary and outputting their codes).
这种方法之所以能实现压缩,是因为:
⚝ 字典引用(通常是整数索引)的长度通常远小于被替换的字符串的长度。
⚝ 对于重复出现的字符串,只需要在字典中存储一次,后续出现时只需很小的开销(一个索引)。
基于字典的编码算法主要分为两大类:
① 基于滑动窗口的算法 (Sliding Window Based Algorithms):代表是LZ77。这类算法不维护一个显式的、全局增长的字典,而是使用一个有限大小的滑动窗口作为隐式字典。窗口的一部分用于查找匹配的字符串(搜索缓冲区),另一部分是待编码的数据(先行缓冲区)。
② 基于显式字典的算法 (Explicit Dictionary Based Algorithms):代表是LZ78和LZW。这类算法维护一个随着编码过程不断增长的字典,其中存储了遇到的字符串及其对应的索引。
接下来,我们将详细探讨这些具体的算法。
5.2 LZ77算法 (LZ77 Algorithm)
LZ77算法由雅各布·齐夫 (Jacob Ziv) 和亚伯拉罕·莱佩尔 (Abraham Lempel) 于1977年提出,是一种基于滑动窗口的无损压缩算法。它不构建一个显式的字典,而是利用一个有限大小的滑动窗口来查找重复的字符串。
5.2.1 LZ77算法原理与编码过程 (Principle and Encoding Process of LZ77)
LZ77算法的核心是滑动窗口 (Sliding Window)。这个窗口被分成两个部分:
① 搜索缓冲区 (Search Buffer):窗口的左侧部分,包含已经编码过的数据。它充当一个“隐式字典”,用于查找当前待编码数据中的匹配项。
② 先行缓冲区 (Lookahead Buffer):窗口的右侧部分,包含待编码的数据。
编码器的工作是:从先行缓冲区中取出数据,在搜索缓冲区中查找与先行缓冲区开头最长的匹配字符串。
如果找到匹配:
① 编码器输出一个三元组 (offset, length, next_char)。
▮▮▮▮ⓑ offset (偏移量):匹配字符串在搜索缓冲区中的起始位置相对于当前窗口起始位置的偏移量。通常是负值或从窗口末尾倒数的距离。
▮▮▮▮ⓒ length (长度):匹配字符串的长度。
▮▮▮▮ⓓ next_char (下一个字符):匹配字符串后面的第一个字符。这个字符是无法通过匹配表示的,必须显式输出。
⑤ 编码器将窗口向右滑动 length + 1 个位置,将匹配的字符串和 next_char 移出先行缓冲区并移入搜索缓冲区。
如果未找到匹配(最长匹配长度为0):
① 编码器输出一个三元组 (0, 0, current_char)。
▮▮▮▮ⓑ offset 为 0。
▮▮▮▮ⓒ length 为 0。
▮▮▮▮ⓓ current_char (当前字符):先行缓冲区的第一个字符。
⑤ 编码器将窗口向右滑动 1 个位置。
窗口的大小是固定的。当窗口滑动时,最左边的数据会从窗口中移除。
示例: 假设输入字符串为 "AABRAACADABRAA",搜索缓冲区大小为10,先行缓冲区大小为任意足够大。
初始状态:
搜索缓冲区: [空] (假设窗口起始位置为0)
先行缓冲区: AABRAACADABRAA
步骤1: 先行缓冲区开头是 'A'。搜索缓冲区为空,无匹配。
输出: (0, 0, 'A')
窗口滑动 1 位。
搜索缓冲区: A
先行缓冲区: ABRAACADABRAA
步骤2: 先行缓冲区开头是 'A'。搜索缓冲区有 'A'。最长匹配是 'A' (长度1)。
输出: (1, 1, 'B') (假设offset是相对于当前窗口末尾的距离,'A'在搜索缓冲区末尾前1位)
窗口滑动 1+1=2 位。
搜索缓冲区: AB
先行缓冲区: RAACADABRAA
步骤3: 先行缓冲区开头是 'R'. 搜索缓冲区有 'A', 'B'. 无匹配。
输出: (0, 0, 'R')
窗口滑动 1 位。
搜索缓冲区: ABR
先行缓冲区: AACADABRAA
步骤4: 先行缓冲区开头是 'A'. 搜索缓冲区有 'A', 'B', 'R'. 最长匹配是 'A' (长度1).
输出: (1, 1, 'A') (假设offset是相对于当前窗口末尾的距离)
窗口滑动 1+1=2 位。
搜索缓冲区: ABRA
先行缓冲区: ACADABRAA
步骤5: 先行缓冲区开头是 'A'. 搜索缓冲区有 'A', 'B', 'R', 'A'. 最长匹配是 'A' (长度1).
输出: (1, 1, 'C')
窗口滑动 1+1=2 位。
搜索缓冲区: ABRAA
先行缓冲区: CADABRAA
步骤6: 先行缓冲区开头是 'C'. 搜索缓冲区有 'A', 'B', 'R', 'A', 'A'. 无匹配。
输出: (0, 0, 'C')
窗口滑动 1 位。
搜索缓冲区: ABRAAC
先行缓冲区: ADABRAA
步骤7: 先行缓冲区开头是 'A'. 搜索缓冲区有 'A', 'B', 'R', 'A', 'A', 'C'. 最长匹配是 'A' (长度1).
输出: (1, 1, 'D')
窗口滑动 1+1=2 位。
搜索缓冲区: ABRAACA
先行缓冲区: DABRAA
步骤8: 先行缓冲区开头是 'D'. 搜索缓冲区有 'A', 'B', 'R', 'A', 'A', 'C', 'A'. 无匹配。
输出: (0, 0, 'D')
窗口滑动 1 位。
搜索缓冲区: ABRAACAD
先行缓冲区: ABRAA
步骤9: 先行缓冲区开头是 'A'. 搜索缓冲区有 'A', 'B', 'R', 'A', 'A', 'C', 'A', 'D'. 最长匹配是 'ABRAA' (长度5). 匹配在搜索缓冲区中的位置是 'A' (offset 8), 'B' (offset 7), 'R' (offset 6), 'A' (offset 5), 'A' (offset 4). 假设offset是相对于当前位置的倒数距离,那么 'ABRAA' 的起始位置是当前位置倒数第8个字符 'A'.
输出: (8, 5, 'A')
窗口滑动 5+1=6 位。
搜索缓冲区: ACADABRAA (窗口大小限制,左侧数据被推出)
先行缓冲区: [空]
最终输出的三元组序列为:(0,0,'A'), (1,1,'B'), (0,0,'R'), (1,1,'A'), (1,1,'C'), (0,0,'C'), (1,1,'D'), (0,0,'D'), (8,5,'A').
5.2.2 LZ77算法的译码过程 (Decoding Process of LZ77)
LZ77的译码过程相对简单。译码器接收到三元组 (offset, length, next_char) 后:
① 如果 offset 和 length 都是 0,说明这是一个未匹配的字符,直接将 next_char 添加到已译码的数据流中。
② 如果 offset 和 length 大于 0,说明这是一个匹配。译码器根据 offset 和 length,从已译码的数据流的末尾向前查找对应的字符串,并将其复制到输出流中。然后将 next_char 添加到输出流中。
已译码的数据流充当了译码器的搜索缓冲区。随着数据的译码,这个缓冲区也在增长和滑动(概念上)。
示例 (接上例):
输入三元组序列:(0,0,'A'), (1,1,'B'), (0,0,'R'), (1,1,'A'), (1,1,'C'), (0,0,'C'), (1,1,'D'), (0,0,'D'), (8,5,'A').
步骤1: (0,0,'A'). offset=0, length=0. 输出 'A'. 已译码: A
步骤2: (1,1,'B'). offset=1, length=1. 从已译码末尾向前1位是 'A'. 复制 'A' (长度1). 输出 'A'. 添加 'B'. 已译码: AB
步骤3: (0,0,'R'). offset=0, length=0. 输出 'R'. 已译码: ABR
步骤4: (1,1,'A'). offset=1, length=1. 从已译码末尾向前1位是 'R'. 复制 'R' (长度1). 输出 'R'. 添加 'A'. 已译码: ABRA
步骤5: (1,1,'C'). offset=1, length=1. 从已译码末尾向前1位是 'A'. 复制 'A' (长度1). 输出 'A'. 添加 'C'. 已译码: ABRAA
步骤6: (0,0,'C'). offset=0, length=0. 输出 'C'. 已译码: ABRAAC
步骤7: (1,1,'D'). offset=1, length=1. 从已译码末尾向前1位是 'C'. 复制 'C' (长度1). 输出 'C'. 添加 'D'. 已译码: ABRAACA
步骤8: (0,0,'D'). offset=0, length=0. 输出 'D'. 已译码: ABRAACAD
步骤9: (8,5,'A'). offset=8, length=5. 从已译码末尾向前8位是 'A'. 复制从该位置开始的5个字符 'ABRAA'. 输出 'ABRAA'. 添加 'A'. 已译码: ABRAACADABRAA
最终译码结果: AABRAACADABRAA,与原始输入一致。
5.2.3 LZ77算法的特点 (Characteristics of LZ77 Algorithm)
⚝ 优点 (Advantages):
▮▮▮▮⚝ 实现相对简单。
▮▮▮▮⚝ 译码速度快,因为译码过程只涉及复制和输出,不需要复杂的查找。
▮▮▮▮⚝ 适用于流式数据压缩,因为它只需要有限的窗口大小,不需要预先知道整个数据的大小。
▮▮▮▮⚝ 对局部重复模式敏感。
⚝ 缺点 (Disadvantages):
▮▮▮▮⚝ 编码过程需要在大搜索缓冲区中查找最长匹配,计算复杂度相对较高(尤其是在朴素实现中)。
▮▮▮▮⚝ 输出的三元组 (offset, length, next_char) 可能会占用较多空间,特别是当匹配长度较短时,每个匹配都需要额外的 offset 和 next_char 的开销。
▮▮▮▮⚝ 压缩效率受限于窗口大小。无法利用超出窗口范围的重复模式。
LZ77是许多现代压缩格式的基础,例如 DEFLATE (用于 GZIP, PKZIP, PNG) 就是 LZ77 的一个变种 (LZ77 + Huffman Coding)。
5.3 LZ78算法 (LZ78 Algorithm)
LZ78算法由雅各布·齐夫 (Jacob Ziv) 和亚伯拉罕·莱佩尔 (Abraham Lempel) 于1978年提出,与LZ77不同,它构建一个显式的字典来存储遇到的字符串。
5.3.1 LZ78算法原理与编码过程 (Principle and Encoding Process of LZ78)
LZ78算法的核心是维护一个字典,其中存储了编码过程中遇到的字符串及其对应的索引。字典通常从空开始,或者包含所有单个字符作为初始条目。
编码器的工作是:从输入数据流中读取字符,尝试找到当前读取的字符串在字典中的最长匹配前缀。
① 编码器维护一个当前正在构建的字符串 (current_string),初始为空。
② 逐个读取输入字符 (char)。将 char 添加到 current_string 的末尾,形成一个新的字符串 (new_string = current_string + char)。
③ 检查 new_string 是否在字典中。
▮▮▮▮ⓓ 如果 new_string 在字典中:更新 current_string = new_string,继续读取下一个字符。
▮▮▮▮ⓔ 如果 new_string 不在字典中:
▮▮▮▮▮▮▮▮❻ 将 new_string 添加到字典中,并分配一个新的索引。
▮▮▮▮▮▮▮▮❼ 输出一个二元组 (prefix_index, char),其中 prefix_index 是 current_string 在字典中的索引(如果 current_string 为空,则为0或特殊值),char 是导致 new_string 不在字典中的那个字符。
▮▮▮▮▮▮▮▮❽ 重置 current_string 为空,继续读取下一个字符。
⑨ 当输入数据结束时,如果 current_string 非空,则输出其在字典中的索引(或特殊结束标记)。
字典的索引通常从1开始(或0,取决于实现),0可以保留表示空字符串或特殊含义。初始字典可以包含所有单个字符,例如 ASCII 字符集,索引为 1 到 256。
示例: 假设输入字符串为 "AABRAACADABRAA"。初始字典为空,索引从1开始。
步骤1: current_string = ""
读取 'A'. new_string = "A". "A" 不在字典中。
将 "A" 添加到字典,索引1. 字典: {1: "A"}
输出: (0, 'A') (0表示空前缀)
current_string = ""
步骤2: current_string = ""
读取 'A'. new_string = "A". "A" 在字典中 (索引1).
current_string = "A"
步骤3: current_string = "A"
读取 'B'. new_string = "AB". "AB" 不在字典中。
将 "AB" 添加到字典,索引2. 字典: {1: "A", 2: "AB"}
输出: (1, 'B') (1是 "A" 的索引)
current_string = ""
步骤4: current_string = ""
读取 'R'. new_string = "R". "R" 不在字典中。
将 "R" 添加到字典,索引3. 字典: {1: "A", 2: "AB", 3: "R"}
输出: (0, 'R')
current_string = ""
步骤5: current_string = ""
读取 'A'. new_string = "A". "A" 在字典中 (索引1).
current_string = "A"
步骤6: current_string = "A"
读取 'A'. new_string = "AA". "AA" 不在字典中。
将 "AA" 添加到字典,索引4. 字典: {1: "A", 2: "AB", 3: "R", 4: "AA"}
输出: (1, 'A') (1是 "A" 的索引)
current_string = ""
步骤7: current_string = ""
读取 'C'. new_string = "C". "C" 不在字典中。
将 "C" 添加到字典,索引5. 字典: {1: "A", 2: "AB", 3: "R", 4: "AA", 5: "C"}
输出: (0, 'C')
current_string = ""
步骤8: current_string = ""
读取 'A'. new_string = "A". "A" 在字典中 (索引1).
current_string = "A"
步骤9: current_string = "A"
读取 'D'. new_string = "AD". "AD" 不在字典中。
将 "AD" 添加到字典,索引6. 字典: {1: "A", 2: "AB", 3: "R", 4: "AA", 5: "C", 6: "AD"}
输出: (1, 'D') (1是 "A" 的索引)
current_string = ""
步骤10: current_string = ""
读取 'A'. new_string = "A". "A" 在字典中 (索引1).
current_string = "A"
步骤11: current_string = "A"
读取 'B'. new_string = "AB". "AB" 在字典中 (索引2).
current_string = "AB"
步骤12: current_string = "AB"
读取 'R'. new_string = "ABR". "ABR" 不在字典中。
将 "ABR" 添加到字典,索引7. 字典: {..., 6: "AD", 7: "ABR"}
输出: (2, 'R') (2是 "AB" 的索引)
current_string = ""
步骤13: current_string = ""
读取 'A'. new_string = "A". "A" 在字典中 (索引1).
current_string = "A"
步骤14: current_string = "A"
读取 'A'. new_string = "AA". "AA" 在字典中 (索引4).
current_string = "AA"
输入结束。current_string = "AA" 非空。输出其索引。
输出: (4, null) 或特殊结束标记。
最终输出的二元组序列为:(0,'A'), (1,'B'), (0,'R'), (1,'A'), (0,'C'), (1,'D'), (2,'R'), (1,'A'), (4, null).
5.3.2 LZ78算法的译码过程 (Decoding Process of LZ78)
LZ78的译码过程也需要构建一个字典,这个字典与编码器构建的字典是同步的。
译码器接收到二元组 (prefix_index, char) 后:
① 如果 prefix_index 是 0(或表示空前缀的特殊值),说明这是一个新的单字符条目。将 char 添加到字典中,并分配下一个可用索引。输出 char。
② 如果 prefix_index 大于 0,说明这是一个已在字典中的前缀。查找字典中索引为 prefix_index 的字符串 (prefix_string)。将 prefix_string 和 char 连接起来形成新的字符串 (new_string = prefix_string + char)。将 new_string 添加到字典中,并分配下一个可用索引。输出 new_string。
示例 (接上例):
输入二元组序列:(0,'A'), (1,'B'), (0,'R'), (1,'A'), (0,'C'), (1,'D'), (2,'R'), (1,'A'), (4, null).
初始字典为空,索引从1开始。
步骤1: (0,'A'). prefix_index=0. new_string = "A". 将 "A" 添加到字典,索引1. 字典: {1: "A"}. 输出 "A". 已译码: A
步骤2: (1,'B'). prefix_index=1. 字典[1] = "A". new_string = "A" + 'B' = "AB". 将 "AB" 添加到字典,索引2. 字典: {1: "A", 2: "AB"}. 输出 "AB". 已译码: AAB
步骤3: (0,'R'). prefix_index=0. new_string = "R". 将 "R" 添加到字典,索引3. 字典: {..., 2: "AB", 3: "R"}. 输出 "R". 已译码: AABR
步骤4: (1,'A'). prefix_index=1. 字典[1] = "A". new_string = "A" + 'A' = "AA". 将 "AA" 添加到字典,索引4. 字典: {..., 3: "R", 4: "AA"}. 输出 "AA". 已译码: AABRAA
步骤5: (0,'C'). prefix_index=0. new_string = "C". 将 "C" 添加到字典,索引5. 字典: {..., 4: "AA", 5: "C"}. 输出 "C". 已译码: AABRAAC
步骤6: (1,'D'). prefix_index=1. 字典[1] = "A". new_string = "A" + 'D' = "AD". 将 "AD" 添加到字典,索引6. 字典: {..., 5: "C", 6: "AD"}. 输出 "AD". 已译码: AABRAACAD
步骤7: (2,'R'). prefix_index=2. 字典[2] = "AB". new_string = "AB" + 'R' = "ABR". 将 "ABR" 添加到字典,索引7. 字典: {..., 6: "AD", 7: "ABR"}. 输出 "ABR". 已译码: AABRAACADABR
步骤8: (1,'A'). prefix_index=1. 字典[1] = "A". new_string = "A" + 'A' = "AA". 将 "AA" 添加到字典,索引8. 字典: {..., 7: "ABR", 8: "AA"}. 输出 "AA". 已译码: AABRAACADABRAA
步骤9: (4, null). prefix_index=4. 字典[4] = "AA". 输出 "AA". 已译码: AABRAACADABRAA
最终译码结果: AABRAACADABRAA,与原始输入一致。
5.3.3 LZ78算法的特点 (Characteristics of LZ78 Algorithm)
⚝ 优点 (Advantages):
▮▮▮▮⚝ 构建显式字典,可以利用更长距离的重复模式(不受窗口大小限制)。
▮▮▮▮⚝ 译码过程相对简单,只需查字典和拼接字符串。
▮▮▮▮⚝ 字典是动态构建的,适应性较好。
⚝ 缺点 (Disadvantages):
▮▮▮▮⚝ 字典会随着输入数据的处理不断增长,可能占用大量内存。需要字典管理策略(如限制字典大小,清空或替换旧条目)。
▮▮▮▮⚝ 输出的二元组 (prefix_index, char) 中仍然包含原始字符,当匹配长度较短时,压缩效率可能不高。
▮▮▮▮⚝ 编码过程中的字典查找需要高效的数据结构(如哈希表或Trie树)。
LZ78算法本身的应用不如其变体LZW广泛,但它是理解LZW的基础。
5.4 LZW算法 (LZW Algorithm)
LZW算法由特里·韦尔奇 (Terry Welch) 于1984年提出,是LZ78算法的一个重要变体和改进。它在LZ78的基础上做了一些修改,使得输出更加紧凑,并且在某些应用中表现出色。
5.4.1 LZW算法原理与编码过程 (Principle and Construction of LZW Coding)
LZW算法的核心思想与LZ78类似,也是构建一个显式字典。主要的区别在于:
① 初始字典通常包含所有可能的单个字符。
② 编码器输出的不是 (prefix_index, char) 二元组,而是直接输出找到的最长匹配前缀在字典中的索引。
③ 字典的更新方式略有不同:将找到的最长匹配前缀加上下一个输入字符组成的新字符串添加到字典中。
编码器的工作是:
① 初始化字典,包含所有单个字符,并分配索引(例如,ASCII 字符 0-255 对应索引 0-255)。字典的下一个可用索引从 256 开始。
② current_string 初始化为输入数据的第一个字符。
③ 逐个读取输入字符 (char)。
④ 将 char 添加到 current_string 的末尾,形成一个新的字符串 (new_string = current_string + char)。
⑤ 检查 new_string 是否在字典中。
▮▮▮▮ⓕ 如果 new_string 在字典中:更新 current_string = new_string,继续读取下一个字符。
▮▮▮▮ⓖ 如果 new_string 不在字典中:
▮▮▮▮▮▮▮▮❽ 输出 current_string 在字典中的索引。
▮▮▮▮▮▮▮▮❾ 将 new_string 添加到字典中,并分配下一个可用索引。
▮▮▮▮▮▮▮▮❿ 重置 current_string = char,继续读取下一个字符。
⑪ 当输入数据结束时,输出最后一个 current_string 在字典中的索引。
示例: 假设输入字符串为 "AABRAACADABRAA"。初始字典包含 'A', 'B', 'R', 'C', 'D' 等字符,假设它们分别对应索引 65, 66, 82, 67, 68 (ASCII 值)。新条目从索引 256 开始。
步骤1: current_string = ""
读取 'A'. current_string = "A".
读取 'A'. new_string = "AA". "AA" 不在字典中。
输出 "A" 的索引 (65).
将 "AA" 添加到字典,索引 256. 字典: {..., 65:'A', ..., 256:'AA'}
current_string = "A"
步骤2: current_string = "A"
读取 'B'. new_string = "AB". "AB" 不在字典中。
输出 "A" 的索引 (65).
将 "AB" 添加到字典,索引 257. 字典: {..., 256:'AA', 257:'AB'}
current_string = "B"
步骤3: current_string = "B"
读取 'R'. new_string = "BR". "BR" 不在字典中。
输出 "B" 的索引 (66).
将 "BR" 添加到字典,索引 258. 字典: {..., 257:'AB', 258:'BR'}
current_string = "R"
步骤4: current_string = "R"
读取 'A'. new_string = "RA". "RA" 不在字典中。
输出 "R" 的索引 (82).
将 "RA" 添加到字典,索引 259. 字典: {..., 258:'BR', 259:'RA'}
current_string = "A"
步骤5: current_string = "A"
读取 'A'. new_string = "AA". "AA" 在字典中 (索引 256).
current_string = "AA"
步骤6: current_string = "AA"
读取 'C'. new_string = "AAC". "AAC" 不在字典中。
输出 "AA" 的索引 (256).
将 "AAC" 添加到字典,索引 260. 字典: {..., 259:'RA', 260:'AAC'}
current_string = "C"
步骤7: current_string = "C"
读取 'A'. new_string = "CA". "CA" 不在字典中。
输出 "C" 的索引 (67).
将 "CA" 添加到字典,索引 261. 字典: {..., 260:'AAC', 261:'CA'}
current_string = "A"
步骤8: current_string = "A"
读取 'D'. new_string = "AD". "AD" 不在字典中。
输出 "A" 的索引 (65).
将 "AD" 添加到字典,索引 262. 字典: {..., 261:'CA', 262:'AD'}
current_string = "D"
步骤9: current_string = "D"
读取 'A'. new_string = "DA". "DA" 不在字典中。
输出 "D" 的索引 (68).
将 "DA" 添加到字典,索引 263. 字典: {..., 262:'AD', 263:'DA'}
current_string = "A"
步骤10: current_string = "A"
读取 'B'. new_string = "AB". "AB" 在字典中 (索引 257).
current_string = "AB"
步骤11: current_string = "AB"
读取 'R'. new_string = "ABR". "ABR" 不在字典中。
输出 "AB" 的索引 (257).
将 "ABR" 添加到字典,索引 264. 字典: {..., 263:'DA', 264:'ABR'}
current_string = "R"
步骤12: current_string = "R"
读取 'A'. new_string = "RA". "RA" 在字典中 (索引 259).
current_string = "RA"
步骤13: current_string = "RA"
读取 'A'. new_string = "RAA". "RAA" 不在字典中。
输出 "RA" 的索引 (259).
将 "RAA" 添加到字典,索引 265. 字典: {..., 264:'ABR', 265:'RAA'}
current_string = "A"
输入结束。输出最后一个 current_string "A" 的索引 (65).
最终输出的索引序列为:65, 65, 66, 82, 256, 67, 65, 68, 65, 257, 82, 259, 65.
5.4.2 LZW算法的译码过程 (Implementation Details of LZW Coding)
LZW的译码过程也需要构建与编码器同步的字典。译码器接收到索引流。
译码器的工作是:
① 初始化字典,包含所有单个字符,并分配索引(与编码器一致)。
② 读取第一个索引 (first_code)。查找字典中对应的字符串 (current_string)。输出 current_string。prev_string = current_string。
③ 循环读取后续索引 (next_code)。
④ 查找字典中索引为 next_code 的字符串 (new_string)。
▮▮▮▮ⓔ 如果 next_code 在字典中:
▮▮▮▮▮▮▮▮❻ 输出 new_string。
▮▮▮▮▮▮▮▮❼ 将 prev_string 的第一个字符添加到 prev_string 的末尾,形成一个新的字符串 (entry_to_add = prev_string + new_string[0])。将 entry_to_add 添加到字典中,并分配下一个可用索引。
▮▮▮▮▮▮▮▮❽ prev_string = new_string。
▮▮▮▮ⓘ 如果 next_code 不在字典中 (这是一种特殊情况,称为 "code not in dictionary" 或 "KwK" case):
▮▮▮▮▮▮▮▮❿ 这意味着 next_code 对应的是 prev_string 加上 prev_string 的第一个字符。构造这个字符串 (new_string = prev_string + prev_string[0])。
▮▮▮▮▮▮▮▮❷ 输出 new_string。
▮▮▮▮▮▮▮▮❸ 将 new_string 添加到字典中,并分配下一个可用索引。
▮▮▮▮▮▮▮▮❹ prev_string = new_string。
⑭ 当输入索引流结束时,译码完成。
示例 (接上例):
输入索引序列:65, 65, 66, 82, 256, 67, 65, 68, 65, 257, 82, 259, 65.
初始字典包含 'A'(65), 'B'(66), 'R'(82), 'C'(67), 'D'(68) 等。新条目从索引 256 开始。
步骤1: 读取 65. 字典[65] = "A". 输出 "A". prev_string = "A". 已译码: A
步骤2: 读取 65. 字典[65] = "A". new_string = "A". 输出 "A". 将 prev_string ("A") + new_string[0] ('A') = "AA" 添加到字典,索引 256. 字典: {..., 256:'AA'}. prev_string = "A". 已译码: AA
步骤3: 读取 66. 字典[66] = "B". new_string = "B". 输出 "B". 将 prev_string ("A") + new_string[0] ('B') = "AB" 添加到字典,索引 257. 字典: {..., 257:'AB'}. prev_string = "B". 已译码: AAB
步骤4: 读取 82. 字典[82] = "R". new_string = "R". 输出 "R". 将 prev_string ("B") + new_string[0] ('R') = "BR" 添加到字典,索引 258. 字典: {..., 258:'BR'}. prev_string = "R". 已译码: AABR
步骤5: 读取 256. 字典[256] = "AA". new_string = "AA". 输出 "AA". 将 prev_string ("R") + new_string[0] ('A') = "RA" 添加到字典,索引 259. 字典: {..., 259:'RA'}. prev_string = "AA". 已译码: AABRAA
步骤6: 读取 67. 字典[67] = "C". new_string = "C". 输出 "C". 将 prev_string ("AA") + new_string[0] ('C') = "AAC" 添加到字典,索引 260. 字典: {..., 260:'AAC'}. prev_string = "C". 已译码: AABRAAC
步骤7: 读取 65. 字典[65] = "A". new_string = "A". 输出 "A". 将 prev_string ("C") + new_string[0] ('A') = "CA" 添加到字典,索引 261. 字典: {..., 261:'CA'}. prev_string = "A". 已译码: AABRAACA
步骤8: 读取 68. 字典[68] = "D". new_string = "D". 输出 "D". 将 prev_string ("A") + new_string[0] ('D') = "AD" 添加到字典,索引 262. 字典: {..., 262:'AD'}. prev_string = "D". 已译码: AABRAACAD
步骤9: 读取 65. 字典[65] = "A". new_string = "A". 输出 "A". 将 prev_string ("D") + new_string[0] ('A') = "DA" 添加到字典,索引 263. 字典: {..., 263:'DA'}. prev_string = "A". 已译码: AABRAACADA
步骤10: 读取 257. 字典[257] = "AB". new_string = "AB". 输出 "AB". 将 prev_string ("A") + new_string[0] ('A') = "AA" 添加到字典,索引 264. 字典: {..., 264:'AA'}. prev_string = "AB". 已译码: AABRAACADAB
步骤11: 读取 82. 字典[82] = "R". new_string = "R". 输出 "R". 将 prev_string ("AB") + new_string[0] ('A') = "ABA" 添加到字典,索引 265. 字典: {..., 265:'ABA'}. prev_string = "R". 已译码: AABRAACADABR
步骤12: 读取 259. 字典[259] = "RA". new_string = "RA". 输出 "RA". 将 prev_string ("R") + new_string[0] ('R') = "RR" 添加到字典,索引 266. 字典: {..., 266:'RR'}. prev_string = "RA". 已译码: AABRAACADABRA
步骤13: 读取 65. 字典[65] = "A". new_string = "A". 输出 "A". 将 prev_string ("RA") + new_string[0] ('R') = "RAR" 添加到字典,索引 267. 字典: {..., 267:'RAR'}. prev_string = "A". 已译码: AABRAACADABRAA
输入结束。
最终译码结果: AABRAACADABRAA,与原始输入一致。
注意:在步骤10中,译码器读取 257,字典[257]是 "AB"。它输出 "AB"。然后它将 prev_string ("A") 的第一个字符 ('A') 添加到 prev_string ("A") 的末尾,形成 "AA",并将其添加到字典。这里有一个细节,LZW 译码器在添加新条目时,是使用 上一个 输出的字符串 (prev_string) 加上 当前 输出字符串 (new_string) 的第一个字符。在 KwK (Code not in Dictionary) 情况下,当前输出字符串就是 prev_string 加上 prev_string 的第一个字符。
5.4.3 LZW算法的特点 (Characteristics of LZW Algorithm)
⚝ 优点 (Advantages):
▮▮▮▮⚝ 译码过程非常快,因为它只需要查字典和拼接字符串,不需要像 LZ77 那样进行模式匹配。
▮▮▮▮⚝ 输出只有字典索引,相比 LZ77 的三元组或 LZ78 的二元组更紧凑,通常能获得更好的压缩比。
▮▮▮▮⚝ 字典是动态构建的,适应性好。
▮▮▮▮⚝ 实现相对简单。
⚝ 缺点 (Disadvantages):
▮▮▮▮⚝ 字典会随着输入数据的处理不断增长,可能占用大量内存。需要字典管理策略。
▮▮▮▮⚝ 编码过程中的字典查找需要高效的数据结构。
▮▮▮▮⚝ 对于随机性强、重复模式少的数据,压缩效果不佳,甚至可能膨胀。
LZW算法因其高效的译码速度和不错的压缩性能而非常流行,被广泛应用于 GIF (Graphics Interchange Format)、TIFF (Tagged Image File Format) 和 PDF (Portable Document Format) 等格式中。
5.5 LZ系列算法的比较与应用 (Comparison and Applications of LZ Family Algorithms)
LZ77、LZ78和LZW算法都属于基于字典的无损压缩算法,但它们在实现细节、性能和适用场景上有所不同。
特性 (Feature) | LZ77 (1977) | LZ78 (1978) | LZW (1984) |
---|---|---|---|
字典类型 (Dictionary Type) | 隐式 (Implicit),基于滑动窗口 (Sliding Window) | 显式 (Explicit),动态增长 (Dynamic Growth) | 显式 (Explicit),动态增长 (Dynamic Growth) |
编码输出 (Encoding Output) | (offset, length, next_char) 三元组 | (prefix_index, char) 二元组 | dictionary_index 索引 |
字典更新 (Dictionary Update) | 无显式字典更新,窗口滑动即更新隐式字典 | 遇到新字符串时,将 (prefix_string + char) 加入字典 | 遇到新字符串时,将 (current_string + char) 加入字典 |
编码复杂度 (Encoding Complexity) | 较高 (搜索最长匹配) | 较高 (字典查找) | 较高 (字典查找) |
译码复杂度 (Decoding Complexity) | 较低 (复制和输出) | 较低 (查字典和拼接) | 非常低 (查字典和拼接) |
内存需求 (Memory Requirement) | 窗口大小 (Window Size) | 字典大小 (Dictionary Size) | 字典大小 (Dictionary Size) |
压缩比 (Compression Ratio) | 依赖窗口大小,对局部重复敏感 | 优于 LZ77 (理论上),对长距离重复敏感 | 通常优于 LZ78,输出更紧凑 |
流式处理 (Streaming Capability) | 良好 (有限窗口) | 较好 (需要字典管理) | 较好 (需要字典管理) |
典型应用 (Typical Applications) | DEFLATE (GZIP, PKZIP, PNG) | 不如 LZW 广泛 | GIF, TIFF, PDF, Unix compress |
比较总结 (Comparison Summary):
① 压缩效率 (Compression Efficiency): LZW 通常能获得比 LZ78 更好的压缩比,因为它输出的是纯粹的索引流,没有额外的字符开销。LZ77 的效率受限于窗口大小,但对于局部重复性强的数据表现良好。
② 速度 (Speed): 译码速度通常是 LZW > LZ78 > LZ77。编码速度则取决于字典查找或模式匹配的实现效率。
③ 内存 (Memory): LZ77 的内存需求主要取决于窗口大小,是固定的。LZ78 和 LZW 的内存需求取决于字典大小,字典会随着数据增长,可能需要字典管理机制来限制内存使用。
④ 实现复杂度 (Implementation Complexity): 基本的 LZ77、LZ78、LZW 原理都不复杂,但高效的实现(特别是编码器的字典查找或模式匹配)需要使用合适的数据结构(如哈希表、Trie树、后缀树/数组等)。
应用 (Applications):
基于LZ系列的算法及其变体在实际应用中非常广泛:
⚝ 文本压缩 (Text Compression): LZ77 的变体 DEFLATE 是 GZIP 和 PKZIP (ZIP 文件格式) 的核心算法,广泛用于文件压缩。
⚝ 图像格式 (Image Formats): LZW 用于 GIF 和 TIFF 格式的无损压缩。PNG 格式使用 DEFLATE。
⚝ 文档格式 (Document Formats): PDF 格式支持多种压缩算法,包括 DEFLATE 和 LZW。
⚝ 操作系统工具 (Operating System Tools): Unix/Linux 系统中的 compress
命令使用了 LZW 算法。
选择哪种算法取决于具体的应用需求,例如对压缩比、速度、内存限制以及数据特性的权衡。对于需要快速译码且数据有重复模式的场景,LZW 是一个不错的选择。对于流式数据或需要固定内存开销的场景,LZ77 或其变体可能更合适。
本章我们深入学习了基于字典的无损编码算法,理解了它们如何通过识别和替换重复字符串来实现压缩。这些算法构成了许多现代压缩工具和格式的基础。在下一章中,我们将转向有失真信源编码,探讨在允许一定信息损失的情况下如何实现更高的压缩率。
6. chapter 6: 有失真信源编码引论 (Introduction to Lossy Source Coding)
在前面的章节中,我们深入探讨了无损信源编码(Lossless Source Coding)的基本原理和经典算法。无损编码的目标是在不丢失任何信息的前提下,尽可能地压缩信源数据。这意味着经过编码和译码后恢复的数据与原始数据完全一致。然而,在许多实际应用中,如图像、音频和视频压缩,我们面临的数据量极其庞大,即使是最高效的无损编码算法也可能无法满足存储或传输带宽的要求。此外,人类的感知系统(视觉、听觉)对某些信息的丢失并不敏感,或者说,一定程度的失真(Distortion)是可以接受的,甚至难以察觉。正是基于这些现实需求和感知特性,有失真信源编码(Lossy Source Coding)应运而生。
有失真编码允许在压缩过程中牺牲一定的保真度(Fidelity),即允许恢复的数据与原始数据存在一定程度的差异或失真,以换取更高的压缩比(Compression Ratio)。本章将作为有失真信源编码的引论,介绍其基本概念、核心问题以及理论基础,为后续章节深入探讨具体的有失真编码技术(如量化、预测编码、变换编码等)奠定基础。
6.1 为何需要有失真编码? (Why Lossy Coding is Needed?)
正如引言所述,有失真编码的出现并非偶然,而是由实际需求和理论限制共同决定的。理解为何需要有失真编码,有助于我们认识其在现代信息技术中的重要地位。
① 数据量的爆炸式增长:现代社会产生的数据量呈指数级增长,尤其是多媒体数据(图像、音频、视频)。例如,一张高分辨率图片、一段高清视频或一段高质量音频,其原始数据量往往非常巨大。无损压缩虽然能减少数据量,但其压缩极限受限于信源的熵(Entropy),对于许多具有丰富细节和随机性的信源,无损压缩比往往有限。
② 存储空间与传输带宽的限制:尽管存储技术和网络带宽不断发展,但与数据量的增长速度相比,它们仍然是稀缺资源。为了有效地存储和传输这些海量数据,必须进行大幅度的数据压缩。无损压缩往往无法达到所需的压缩比。
③ 人类感知系统的特性:人类的视觉和听觉系统并非完美的信息接收器。它们对某些频率、细节或微小的变化不敏感。有失真编码正是利用了这些感知冗余(Perceptual Redundancy)和不可见信息,通过有选择地丢弃或近似那些对感知影响较小的数据,来实现更高的压缩比。例如,在图像中丢弃人眼难以分辨的高频细节,或在音频中移除人耳听不到的超声波成分。
④ 率失真权衡 (Rate-Distortion Trade-off):信息论告诉我们,对于一个给定的信源,存在一个理论上的压缩极限(由熵决定)来实现无损压缩。如果希望进一步降低表示数据的比特率(Bit Rate),就必然会引入失真。有失真编码的核心问题就是在表示数据的比特率(率,Rate)和由此产生的失真(失真,Distortion)之间找到一个最佳的权衡点。我们愿意接受多大的失真,以换取多低的比特率?这是有失真编码需要解决的关键问题。
⑤ 应用场景的需求:许多应用场景本身就允许甚至要求有失真压缩。例如,流媒体视频需要低比特率以适应有限的网络带宽;手机拍照为了节省存储空间通常采用有损格式(如JPEG);语音通话为了实时传输也常使用有损编码。在这些场景下,轻微的失真对用户体验的影响远小于因数据量过大导致的卡顿或延迟。
因此,有失真信源编码是应对海量数据、有限资源以及利用感知特性的一种必然选择。它不再追求完美重构,而是追求在可接受的失真水平下实现最高的压缩效率。
6.2 失真度量 (Distortion Measures)
在有失真编码中,我们允许恢复的数据 \(\hat{X}\) 与原始数据 \(X\) 之间存在差异。为了量化这种差异,我们需要定义一个失真度量(Distortion Measure)。一个失真度量是一个函数 \(d(x, \hat{x})\),它衡量当原始信源符号(或符号序列)是 \(x\) 而恢复的符号(或符号序列)是 \(\hat{x}\) 时所产生的“成本”或“误差”。
一个有效的失真度量应该满足以下性质(尽管并非所有度量都严格满足所有性质):
⚝ 非负性:\(d(x, \hat{x}) \ge 0\)。失真不能是负的。
⚝ 同一性:\(d(x, x) = 0\)。如果恢复的数据与原始数据完全相同,则失真为零。
⚝ 对称性(可选):\(d(x, \hat{x}) = d(\hat{x}, x)\)。从 \(x\) 到 \(\hat{x}\) 的失真与从 \(\hat{x}\) 到 \(x\) 的失真相同。
⚝ 三角不等式(可选):\(d(x, z) \le d(x, y) + d(y, z)\)。这通常要求失真度量是一个度量(Metric)。
在信息论和信号处理中,常用的失真度量通常是基于某种平均误差。对于一个离散信源 \(X\) 和其恢复值 \(\hat{X}\),如果考虑单个符号的失真,常用的度量包括:
① 汉明失真 (Hamming Distortion):
汉明失真通常用于衡量两个等长二进制串之间的差异,即不同位置上比特的个数。对于离散信源符号,它可以定义为:
\[ d(x, \hat{x}) = \begin{cases} 0 & \text{if } x = \hat{x} \\ 1 & \text{if } x \ne \hat{x} \end{cases} \]
这也被称为符号错误概率(Probability of Symbol Error)的度量。对于一个序列 \(x_1, x_2, \dots, x_n\) 和其恢复序列 \(\hat{x}_1, \hat{x}_2, \dots, \hat{x}_n\),总的汉明失真通常是每个位置失真的平均值:
\[ D = \frac{1}{n} \sum_{i=1}^n d(x_i, \hat{x}_i) = \frac{1}{n} \sum_{i=1}^n \mathbb{I}(x_i \ne \hat{x}_i) \]
其中 \(\mathbb{I}(\cdot)\) 是指示函数。汉明失真适用于那些所有错误都同等重要的场景,例如文本数据。
② 平方误差失真 (Squared Error Distortion):
平方误差失真通常用于衡量连续或离散数值信号的差异。对于单个符号 \(x\) 和 \(\hat{x}\),失真定义为:
\[ d(x, \hat{x}) = (x - \hat{x})^2 \]
对于一个序列 \(x_1, x_2, \dots, x_n\) 和其恢复序列 \(\hat{x}_1, \hat{x}_2, \dots, \hat{x}_n\),总的平方误差失真通常是每个位置平方误差的平均值,即均方误差(Mean Squared Error, MSE):
\[ D = \frac{1}{n} \sum_{i=1}^n (x_i - \hat{x}_i)^2 \]
均方误差是信号处理中最常用的失真度量之一,因为它具有良好的数学性质(如可微性),并且与信号的能量有关。在图像处理中,均方误差常用于计算峰值信噪比(Peak Signal-to-Noise Ratio, PSNR),PSNR是衡量图像压缩质量的常用指标,其定义为:
\[ \text{PSNR} = 10 \log_{10} \left( \frac{MAX^2}{\text{MSE}} \right) \]
其中 \(MAX\) 是信号的最大可能取值(例如,对于8位灰度图像,\(MAX=255\))。PSNR值越高,表示失真越小,图像质量越好。
③ 绝对误差失真 (Absolute Error Distortion):
绝对误差失真衡量的是原始值与恢复值之差的绝对值:
\[ d(x, \hat{x}) = |x - \hat{x}| \]
对于序列,平均绝对误差(Mean Absolute Error, MAE)为:
\[ D = \frac{1}{n} \sum_{i=1}^n |x_i - \hat{x}_i| \]
绝对误差失真在某些情况下可能比平方误差更符合人类感知,因为它对大的误差不那么敏感(平方误差会放大大的误差)。
选择哪种失真度量取决于具体的应用场景和信源特性。例如,对于文本或程序代码,任何一个比特的错误都可能导致严重后果,因此汉明失真或零失真(无损)是必需的。而对于图像或音频,平方误差或绝对误差可能更合适,因为它们与信号的能量或幅度差异相关,并且可以利用人类感知的容忍度。在实际应用中,有时也会使用更复杂的、基于感知的失真度量,以更好地反映人类对质量的主观感受。
6.3 率失真理论 (Rate-Distortion Theory)
率失真理论(Rate-Distortion Theory)是信息论中研究有失真信源编码极限的理论框架,由克劳德·香农(Claude Shannon)于1959年提出。它回答了一个根本性问题:对于一个给定的信源和允许的最大平均失真 \(D\),表示该信源所需的最小平均比特率是多少?这个最小平均比特率被称为率失真函数(Rate-Distortion Function),记为 \(R(D)\)。
率失真理论为有失真信源编码设定了理论上的性能界限。任何实际的有失真编码系统,在达到给定的平均失真 \(D\) 时,其平均比特率不可能低于 \(R(D)\)。换句话说,\(R(D)\) 是在允许平均失真不超过 \(D\) 的前提下,信源的最小可压缩率。
率失真理论的核心思想是将信源编码问题视为一个信息传输问题,但这里的“传输”不是通过噪声信道,而是通过一个“编码器-译码器”对,其中编码器将信源输出 \(X\) 映射到码字,译码器将码字映射到恢复值 \(\hat{X}\)。失真度量 \(d(x, \hat{x})\) 衡量了 \(X\) 和 \(\hat{X}\) 之间的差异。
理论上,率失真函数 \(R(D)\) 的定义是基于互信息(Mutual Information)的最小化:
\[ R(D) = \min_{p(\hat{x}|x): E[d(X, \hat{X})] \le D} I(X; \hat{X}) \]
其中,\(I(X; \hat{X})\) 是原始信源 \(X\) 和恢复信源 \(\hat{X}\) 之间的互信息。最小化是在所有满足平均失真约束 \(E[d(X, \hat{X})] \le D\) 的条件概率分布 \(p(\hat{x}|x)\) 上进行的。这里的 \(E[\cdot]\) 表示期望,即平均失真 \(E[d(X, \hat{X})] = \sum_{x, \hat{x}} p(x) p(\hat{x}|x) d(x, \hat{x})\)。
这个定义看起来有些抽象,但其核心思想是:为了在平均失真不超过 \(D\) 的情况下表示信源 \(X\),我们需要保留关于 \(X\) 的多少信息?互信息 \(I(X; \hat{X})\) 正是衡量了 \(\hat{X}\) 中包含的关于 \(X\) 的信息量。率失真函数告诉我们,为了达到平均失真 \(D\),我们至少需要保留 \(I(X; \hat{X})\) 比特/符号的信息,而这个最小值就是 \(R(D)\)。
率失真理论的重要性在于:
⚝ 提供理论极限:它为有失真编码算法的性能设定了理论上限,可以用来评估实际算法的效率,看它们距离理论最优还有多远。
⚝ 指导算法设计:虽然直接计算 \(R(D)\) 并设计达到该极限的编码器通常非常困难,但率失真理论的概念和框架可以指导我们设计更优的有失真编码算法,例如通过矢量量化(Vector Quantization)等技术来逼近理论极限。
⚝ 揭示率失真权衡的本质:它明确地量化了比特率和失真之间的基本权衡关系。
需要注意的是,率失真理论通常假设信源是独立同分布的(Independent and Identically Distributed, IID)或平稳遍历的(Stationary and Ergodic),并且编码长度趋于无穷大。在实际应用中,信源往往具有复杂的依赖结构,编码长度也有限,因此实际系统的性能可能会偏离理论值。然而,率失真理论仍然是理解有失真编码基本原理和性能界限的基石。
6.4 率失真函数 (Rate-Distortion Function)
率失真函数 \(R(D)\) 是平均失真 \(D\) 的函数,它描述了在允许平均失真不超过 \(D\) 的条件下,表示信源所需的最小平均比特率。
率失真函数具有以下重要性质:
① 单调递减性:\(R(D)\) 是 \(D\) 的非增函数。这意味着允许的失真越大,所需的比特率越低。当 \(D=0\) 时(无失真),\(R(0)\) 等于信源的熵 \(H(X)\)(对于离散无记忆信源),这与无失真信源编码定理一致。随着 \(D\) 的增加,\(R(D)\) 逐渐减小。
② 凸性:\(R(D)\) 是 \(D\) 的下凸函数(Convex Function)。这意味着通过对不同的编码方案进行“混合”(例如,对一部分数据使用一种方案,对另一部分使用另一种方案),可以获得更好的率失真性能。
③ 存在性:对于任何具有有限熵的信源和合理的失真度量,率失真函数总是存在的。
④ 可计算性:对于一些简单的信源和失真度量,率失真函数可以解析计算出来。例如:
▮▮▮▮ⓔ 离散无记忆信源 (Discrete Memoryless Source, DMS):对于具有概率分布 \(p(x)\) 的DMS,在汉明失真度量下,其率失真函数为:
\[ R(D) = H(X) - H_b(D) \]
其中 \(H(X)\) 是信源熵,\(H_b(D) = -D \log_2 D - (1-D) \log_2 (1-D)\) 是二元熵函数(Binary Entropy Function),适用于 \(0 \le D \le \min(p_i)\),其中 \(p_i\) 是信源符号的概率。对于 \(D > \min(p_i)\),\(R(D)=0\),表示可以以零比特率表示信源(例如,总是输出概率最大的符号)。
▮▮▮▮ⓑ 高斯信源 (Gaussian Source):对于均值为0、方差为 \(\sigma^2\) 的高斯信源,在平方误差失真度量下,其率失真函数为:
\[ R(D) = \frac{1}{2} \log_2 \left( \frac{\sigma^2}{D} \right) \]
适用于 \(0 \le D \le \sigma^2\)。对于 \(D > \sigma^2\),\(R(D)=0\)。这个公式被称为率失真函数中的“水填充”(Water-filling)公式的基础形式,在高斯信道容量和多维高斯信源编码中非常重要。
计算率失真函数通常需要求解一个复杂的优化问题。对于一般的信源和失真度量,解析解很难获得,但可以通过数值方法进行计算或估计。
率失真函数 \(R(D)\) 的曲线图通常被称为率失真曲线(Rate-Distortion Curve)。这条曲线描绘了理论上可达到的最佳率失真性能。任何实际的有失真编码算法的性能(由其平均比特率和平均失真组成)都可以表示为率失真平面上的一个点 \((D_{actual}, R_{actual})\)。根据率失真理论,这个点必然位于率失真曲线的上方或曲线上,即 \(R_{actual} \ge R(D_{actual})\)。编码算法设计的目标就是找到能够尽可能接近率失真曲线的点。
理解率失真函数对于评估和设计有失真编码系统至关重要。它提供了一个理论上的基准,帮助我们了解当前算法的效率以及潜在的改进空间。
6.5 有失真编码的理论极限 (Theoretical Limits of Lossy Coding)
率失真理论的核心成果是率失真定理(Rate-Distortion Theorem)。这个定理正式地阐述了率失真函数 \(R(D)\) 作为有失真信源编码理论极限的意义。
率失真定理 (Rate-Distortion Theorem):
对于一个离散无记忆信源 \(X\) 和一个单符号失真度量 \(d(x, \hat{x})\),其率失真函数为 \(R(D)\)。
① 可达性 (Achievability):对于任何允许的平均失真 \(D \ge 0\),如果一个编码系统的平均比特率 \(R\) 大于 \(R(D)\),即 \(R > R(D)\),那么存在一个编码方案,可以在编码长度趋于无穷大时,使得平均失真不超过 \(D\)。
② 不可达性 (Converse):对于任何允许的平均失真 \(D \ge 0\),如果一个编码系统的平均比特率 \(R\) 小于 \(R(D)\),即 \(R < R(D)\),那么无论采用何种编码方案,其平均失真都必然大于 \(D\)。
简单来说,率失真定理告诉我们,\(R(D)\) 是在平均失真不超过 \(D\) 的条件下,表示信源所需的最小平均比特率。我们可以在不低于 \(R(D)\) 的任何比特率下实现平均失真 \(D\),但不可能在低于 \(R(D)\) 的比特率下实现平均失真 \(D\)。
这个定理的证明通常涉及复杂的数学工具,如联合典型集(Jointly Typical Set)的概念,类似于无失真信源编码定理和信道编码定理的证明。可达性的证明通常构造一个随机编码方案,并证明对于足够大的编码长度,通过这种随机编码可以以接近 \(R(D)\) 的速率实现平均失真 \(D\)。不可达性的证明则利用互信息和失真度量的性质,证明任何速率低于 \(R(D)\) 的编码方案都无法保证平均失真不超过 \(D\)。
率失真定理为有失真编码设定了最终的理论界限。它表明,无论我们设计多么巧妙的编码算法,其性能都不可能超越率失真曲线。因此,率失真函数 \(R(D)\) 是评估有失真编码算法效率的黄金标准。一个好的有失真编码算法应该能够使其率失真性能点 \((D_{actual}, R_{actual})\) 尽可能地接近理论率失真曲线 \(R(D)\)。
然而,需要强调的是,率失真定理是在渐近意义下(即编码长度趋于无穷大)成立的。对于有限长度的编码,实际性能可能会与理论值有所偏差。此外,率失真理论通常假设信源模型已知,并且失真度量是确定的。在实际应用中,信源模型可能未知或随时间变化,失真度量也可能需要根据主观感知进行调整。这些因素都会影响实际编码系统的设计和性能。
尽管存在这些实际挑战,率失真理论仍然为我们理解有失真编码的本质提供了深刻的洞察,并为算法设计指明了方向。后续章节将介绍的各种有失真编码技术,如量化、预测编码和变换编码,都可以被视为试图在实际系统中逼近率失真理论极限的努力。
7. chapter 7: 有失真编码技术:量化 (Lossy Coding Technique: Quantization)
欢迎来到本书的第七章!在前几章中,我们深入探讨了无损信源编码(Lossless Source Coding)的理论基础和经典算法,其核心目标是在不丢失任何信息的前提下,尽可能地压缩数据。然而,在许多实际应用场景中,例如图像、音频和视频等多媒体数据,原始数据量巨大,即使采用最有效的无损压缩方法,仍然难以满足存储或传输带宽的要求。更重要的是,人类的感知系统(如视觉和听觉)对某些信息的丢失并不敏感,或者说,一定程度的失真(Distortion)是可以接受的。这为我们提供了一种新的压缩思路:有失真信源编码(Lossy Source Coding)。
有失真编码的核心思想是允许在编码过程中引入可控的失真,以换取更高的压缩率。本章将聚焦于有失真编码中最基本、也是最核心的技术之一:量化(Quantization)。量化是将连续或离散但取值范围较大的信号,映射到有限个离散值的过程。这个过程必然会引入误差,即失真,但通过精心设计量化器,我们可以控制失真的大小,并在可接受的失真水平下实现显著的数据压缩。
本章将首先介绍量化的基本概念,包括其定义、作用以及与失真度量的关系。接着,我们将详细讨论标量量化(Scalar Quantization),这是对单个样本进行量化的技术,包括均匀量化器(Uniform Quantizer)、非均匀量化器(Non-uniform Quantizer)以及最优量化器——Lloyd-Max量化器。最后,我们将介绍矢量量化(Vector Quantization, VQ),这是一种对多个样本组成的向量进行联合量化的技术,它可以利用样本之间的相关性,通常能获得比标量量化更好的性能,并介绍其核心算法LBG算法。
通过本章的学习,您将理解量化作为有失真编码基石的作用,掌握不同量化技术的原理和设计方法,并为后续学习更复杂的有失真编码技术(如预测编码和变换编码)打下坚实的基础。
7.1 量化的基本概念 (Basic Concepts of Quantization)
量化(Quantization)是将一个连续值或取值范围很大的离散值集合,映射到一个有限的、通常是较小的离散值集合的过程。在信息论和信号处理中,量化是实现数据压缩(特别是针对模拟信号或高精度数字信号)的关键步骤。
① 定义 (Definition):
量化器(Quantizer)可以被看作是一个函数 \(Q\),它将输入空间 \( \mathcal{X} \) 中的任意值 \(x\) 映射到输出空间 \( \mathcal{Y} \) 中的一个值 \(y\),其中 \( \mathcal{Y} \) 是一个有限的离散集合。
\[ y = Q(x) \]
输入空间 \( \mathcal{X} \) 可以是实数集 \( \mathbb{R} \)(对于连续信号)或一个大的离散集合(对于高精度数字信号)。输出空间 \( \mathcal{Y} = \{y_1, y_2, \dots, y_N\} \) 是一个包含 \(N\) 个离散值的集合,这些值称为量化级(Quantization Levels)或代表点(Representative Points)。
② 量化区间与决策边界 (Quantization Intervals and Decision Boundaries):
为了实现这种映射,输入空间 \( \mathcal{X} \) 被划分为 \(N\) 个区域或区间 \( R_1, R_2, \dots, R_N \),这些区域互不重叠且并集覆盖 \( \mathcal{X} \)。对于任意输入值 \(x\),如果 \(x \in R_i\),则量化器将其映射到对应的量化级 \(y_i\)。这些区域的边界称为决策边界(Decision Boundaries)。
对于标量量化,如果输入空间是实数轴,决策边界通常是一组有序的实数值 \( \{b_0, b_1, \dots, b_N\} \),其中 \(b_0 < b_1 < \dots < b_N\)。量化区间 \( R_i \) 通常定义为 \( [b_{i-1}, b_i) \) 或 \( (b_{i-1}, b_i] \)(对于 \(i=1, \dots, N-1\)),以及边界区间 \( (-\infty, b_1) \) 或 \( [b_0, b_1) \) 和 \( [b_{N-1}, \infty) \) 或 \( (b_{N-1}, b_N] \)。量化级 \(y_i\) 是与区间 \( R_i \) 关联的输出值。
③ 量化误差与失真 (Quantization Error and Distortion):
量化过程是不可逆的,因为多个不同的输入值可能被映射到同一个量化级。这种映射引入了量化误差(Quantization Error),定义为 \(e = x - Q(x)\)。量化误差是量化过程固有的失真来源。
为了评估量化引入的失真,我们需要一个失真度量(Distortion Measure)。常用的失真度量是均方误差(Mean Squared Error, MSE):
\[ D = E[(x - Q(x))^2] \]
其中 \(E[\cdot]\) 表示期望。对于离散信源,期望可以替换为对所有可能输入值及其概率的求和。对于连续信源,期望是关于概率密度函数(Probability Density Function, PDF)的积分。
其他失真度量还包括平均绝对误差(Mean Absolute Error, MAE)等,但均方误差在理论分析和实际应用中最为常见,因为它与信号的能量或功率相关,并且在数学上易于处理(例如,它与信号的方差有关)。
④ 率失真权衡 (Rate-Distortion Trade-off):
量化的输出 \(y\) 是离散的,可以用二进制码字表示。如果量化器有 \(N\) 个量化级,那么表示每个量化级至少需要 \( \lceil \log_2 N \rceil \) 比特(假设等概率)。量化级的数量 \(N\) 决定了量化输出的比特率(Rate)。
增加量化级的数量 \(N\) 可以减小量化区间的大小,从而减小量化误差,降低失真 \(D\)。但同时,表示每个量化级所需的比特数增加,导致比特率 \(R\) 增加。反之,减少 \(N\) 会增加失真但降低比特率。
因此,量化存在一个基本的率失真权衡(Rate-Distortion Trade-off):在给定的允许失真水平下,我们希望找到最小的比特率;或者在给定的比特率下,我们希望找到最小的失真。率失真理论(Rate-Distortion Theory)正是研究这种基本权衡的理论框架(详见第六章)。量化器的设计目标通常是在满足特定失真要求的前提下,最小化比特率,或者在给定比特率下,最小化失真。
⑤ 量化的类型 (Types of Quantization):
⚝ 标量量化 (Scalar Quantization):一次处理一个输入样本。
⚝ 矢量量化 (Vector Quantization, VQ):将多个输入样本组合成一个向量,然后对整个向量进行量化。矢量量化可以利用向量分量之间的相关性,通常能获得比独立的标量量化更好的性能。
本章将首先详细讨论标量量化,然后介绍矢量量化。
7.2 标量量化 (Scalar Quantization)
标量量化(Scalar Quantization)是对单个输入样本 \(x\) 进行量化的过程。一个标量量化器由 \(N\) 个决策边界 \( \{b_0, b_1, \dots, b_N\} \) 和 \(N\) 个量化级 \( \{y_1, y_2, \dots, y_N\} \) 定义。通常假设 \(b_0 < b_1 < \dots < b_N\)。输入 \(x\) 被量化为 \(y_i\) 如果 \(x\) 落在区间 \( R_i \),其中 \( R_i \) 由决策边界定义。例如,对于 \(i=1, \dots, N-1\),\( R_i = [b_{i-1}, b_i) \),而 \( R_1 = [b_0, b_1) \) 或 \( (-\infty, b_1) \),\( R_N = [b_{N-1}, b_N) \) 或 \( [b_{N-1}, \infty) \)。量化级 \(y_i\) 是与区间 \( R_i \) 关联的代表值。
标量量化器可以根据其决策边界和量化级的设置方式分为不同的类型。
7.2.1 均匀量化器 (Uniform Quantizer)
均匀量化器(Uniform Quantizer)是最简单的一种标量量化器。它的特点是量化区间(除了边界区间外)的宽度是相等的。
① 原理 (Principle):
假设输入信号的取值范围是 \( [x_{min}, x_{max}] \),并将其划分为 \(N\) 个等宽的量化区间。区间宽度(步长,Step Size)记为 \( \Delta \)。
\[ \Delta = \frac{x_{max} - x_{min}}{N} \]
决策边界可以定义为 \( b_i = x_{min} + i \Delta \) 对于 \( i=0, 1, \dots, N \)。
量化区间 \( R_i \) 为 \( [b_{i-1}, b_i) \) 对于 \( i=1, \dots, N \)。
量化级 \(y_i\) 通常取为量化区间的中心点:
\[ y_i = b_{i-1} + \frac{\Delta}{2} = x_{min} + (i - \frac{1}{2}) \Delta \]
对于 \( i=1, \dots, N \)。
② 中置量化器与中点量化器 (Mid-rise and Mid-tread Quantizers):
均匀量化器可以分为两类:
⚝ 中置量化器 (Mid-rise Quantizer):决策边界包含0。例如,对于对称输入范围 \( [-A, A] \),决策边界可以是 \( \dots, -\frac{3\Delta}{2}, -\frac{\Delta}{2}, \frac{\Delta}{2}, \frac{3\Delta}{2}, \dots \)。量化级不包含0。
⚝ 中点量化器 (Mid-tread Quantizer):量化级包含0。例如,对于对称输入范围 \( [-A, A] \),量化级可以是 \( \dots, -\Delta, 0, \Delta, 2\Delta, \dots \)。决策边界不包含0。
③ 性能分析 (Performance Analysis):
对于输入信号 \(X\),假设其概率密度函数(PDF)为 \(f_X(x)\)。量化器的均方误差(MSE)为:
\[ D = \sum_{i=1}^N \int_{R_i} (x - y_i)^2 f_X(x) dx \]
对于均匀量化器,如果输入信号在量化区间内是均匀分布的,或者如果步长 \( \Delta \) 足够小(小步长近似,Small Step Size Approximation),量化误差 \( e = x - y_i \) 在区间 \( R_i \) 内近似均匀分布在 \( [-\Delta/2, \Delta/2] \) 或 \( [0, \Delta] \)(取决于量化级的位置)。在这种近似下,量化误差的均方值(即每个区间的平均失真)近似为 \( \Delta^2/12 \)。
如果输入信号的方差为 \( \sigma_X^2 \),并且信号范围远大于 \( N\Delta \),则总的均方误差近似为:
\[ D \approx \frac{\Delta^2}{12} \]
比特率 \(R\) 通常与 \( \log_2 N \) 成正比。对于均匀量化器,\( N = (x_{max} - x_{min}) / \Delta \),所以 \( \Delta = (x_{max} - x_{min}) / N \)。
\[ D \approx \frac{(x_{max} - x_{min})^2}{12 N^2} \]
这表明失真与量化级数量 \(N\) 的平方成反比。
④ 优缺点 (Advantages and Disadvantages):
⚝ 优点 (Advantages):
▮▮▮▮ⓐ 设计简单,易于实现。
▮▮▮▮ⓑ 对于输入信号在整个范围内近似均匀分布的情况,性能较好。
⚝ 缺点 (Disadvantages):
▮▮▮▮ⓐ 对于非均匀分布的输入信号(例如,许多信号的幅度分布集中在0附近),均匀量化器不能很好地适应信号的统计特性,导致在概率密度高的区域量化误差相对较大,效率不高。
▮▮▮▮ⓑ 对输入信号的动态范围敏感,如果信号幅度变化很大,固定步长可能导致溢出或量化噪声过大。
7.2.2 非均匀量化器 (Non-uniform Quantizer)
非均匀量化器(Non-uniform Quantizer)的量化区间宽度是变化的。其设计思想是根据输入信号的概率分布,在概率密度高的区域使用较窄的量化区间,在概率密度低的区域使用较宽的量化区间。这样可以在总的量化级数量 \(N\) 一定的情况下,减小平均失真。
① 原理 (Principle):
非均匀量化器由一组决策边界 \( \{b_0, b_1, \dots, b_N\} \) 和一组量化级 \( \{y_1, y_2, \dots, y_N\} \) 定义,其中区间宽度 \( \Delta_i = b_i - b_{i-1} \) 对于不同的 \(i\) 是不相等的。
设计非均匀量化器的关键在于如何选择决策边界和量化级,以最小化给定数量 \(N\) 下的失真。
② 压扩技术 (Companding):
一种实现非均匀量化器的常用技术是压扩(Companding),它是“压缩”(Compressing)和“扩展”(Expanding)的组合词。
⚝ 原理 (Principle):在发送端,先对输入信号 \(x\) 应用一个非线性压缩函数 \(C(\cdot)\),将幅度较大的信号“压缩”,幅度较小的信号“扩展”。然后对压缩后的信号 \(C(x)\) 应用一个均匀量化器。在接收端,对量化后的信号应用一个与压缩函数对应的非线性扩展函数 \(E(\cdot) = C^{-1}(\cdot)\) 来恢复信号。
⚝ 作用 (Function):通过压缩函数,输入信号的概率分布被“展平”,使得均匀量化器在其上的性能接近最优。这相当于在原始信号域实现了非均匀量化,在信号幅度小(概率密度高)的区域,压缩函数的斜率小,对应原始信号域的量化区间窄;在信号幅度大(概率密度低)的区域,压缩函数的斜率大,对应原始信号域的量化区间宽。
⚝ 常用压扩律 (Common Companding Laws):
▮▮▮▮ⓐ μ-律 (μ-law):主要用于北美和日本的数字电话系统。
\[ y = \text{sgn}(x) \frac{\ln(1 + \mu |x|)}{\ln(1 + \mu)} \]
其中 \( \mu \ge 0 \) 是一个参数,通常取 \(\mu = 255\)。输入 \(x\) 通常归一化到 \([-1, 1]\)。
▮▮▮▮ⓑ A-律 (A-law):主要用于欧洲和世界其他地区的数字电话系统。
\[ y = \begin{cases} \frac{A|x|}{1 + \ln A} & 0 \le |x| < \frac{1}{A} \\ \frac{1 + \ln(A|x|)}{1 + \ln A} & \frac{1}{A} \le |x| \le 1 \end{cases} \]
其中 \( A \ge 1 \) 是一个参数,通常取 \(A = 87.6\)。输入 \(x\) 通常归一化到 \([-1, 1]\)。
μ-律和A-律都是对数压缩函数,它们在信号幅度较小时提供更高的量化精度,从而改善了信噪比(Signal-to-Noise Ratio, SNR),特别是对于语音信号这种幅度分布不均匀的信号。
③ 优缺点 (Advantages and Disadvantages):
⚝ 优点 (Advantages):
▮▮▮▮ⓐ 能够更好地适应输入信号的概率分布,在给定比特率下获得比均匀量化器更低的失真。
▮▮▮▮ⓑ 压扩技术实现相对简单,只需在均匀量化器前后加上非线性变换。
⚝ 缺点 (Disadvantages):
▮▮▮▮ⓐ 压扩律是基于经验或特定信号类型设计的,不一定对所有信号都是最优的。
▮▮▮▮ⓑ 最优的非均匀量化器设计需要知道输入信号的精确概率分布。
7.2.3 Lloyd-Max量化器 (Lloyd-Max Quantizer)
Lloyd-Max量化器(Lloyd-Max Quantizer)是在给定输入信号概率分布 \(f_X(x)\) 和量化级数量 \(N\) 的情况下,使均方误差(MSE)最小化的最优标量量化器。它由贝尔实验室的S. P. Lloyd和斯坦福大学的J. Max独立提出。
① 最优性条件 (Optimality Conditions):
一个量化器 \( \{b_0, \dots, b_N\} \) 和 \( \{y_1, \dots, y_N\} \) 是最优的(在MSE意义下),当且仅当满足以下两个条件:
⚝ 质心条件 (Centroid Condition):每个量化级 \(y_i\) 必须是其对应量化区间 \( R_i \) 内输入信号的条件期望(或质心)。
\[ y_i = E[X | X \in R_i] = \frac{\int_{R_i} x f_X(x) dx}{\int_{R_i} f_X(x) dx} \]
这个条件确保在给定决策边界的情况下,选择这个 \(y_i\) 可以使区间 \( R_i \) 内的均方误差最小。
⚝ 最近邻条件 (Nearest Neighbor Condition):每个决策边界 \(b_i\) 必须是其相邻量化级 \(y_i\) 和 \(y_{i+1}\) 的中点(对于连续输入)。
\[ b_i = \frac{y_i + y_{i+1}}{2} \]
这个条件确保在给定量化级的情况下,将决策边界设置在这里可以使总的均方误差最小。
② Lloyd-Max算法 (Lloyd-Max Algorithm):
由于最优性条件是相互依赖的(量化级依赖于决策边界,决策边界依赖于量化级),通常无法直接求解。Lloyd-Max算法是一个迭代算法,用于寻找满足这些条件的量化器。
⚝ 算法步骤 (Algorithm Steps):
▮▮▮▮ⓐ 初始化 (Initialization):选择一个初始的决策边界集合 \( \{b_0^{(0)}, b_1^{(0)}, \dots, b_N^{(0)}\} \)。这可以基于输入信号的范围均匀划分,或者使用其他启发式方法。
▮▮▮▮ⓑ 迭代 (Iteration):对于第 \(k\) 次迭代,执行以下两步:
▮▮▮▮▮▮▮▮❸ 更新量化级 (Update Quantization Levels):根据当前的决策边界 \( \{b_i^{(k)}\} \),计算新的量化级 \( \{y_i^{(k+1)}\} \) 使用质心条件:
\[ y_i^{(k+1)} = \frac{\int_{b_{i-1}^{(k)}}^{b_i^{(k)}} x f_X(x) dx}{\int_{b_{i-1}^{(k)}}^{b_i^{(k)}} f_X(x) dx} \]
对于 \( i=1, \dots, N \)。
▮▮▮▮▮▮▮▮❷ 更新决策边界 (Update Decision Boundaries):根据新的量化级 \( \{y_i^{(k+1)}\} \),计算新的决策边界 \( \{b_i^{(k+1)}\} \) 使用最近邻条件:
\[ b_i^{(k+1)} = \frac{y_i^{(k+1)} + y_{i+1}^{(k+1)}}{2} \]
对于 \( i=1, \dots, N-1 \)。边界 \(b_0\) 和 \(b_N\) 通常固定为输入信号的最小值和最大值(或 \(-\infty\) 和 \(+\infty\))。
▮▮▮▮ⓒ 终止 (Termination):重复步骤ⓑ,直到量化级或决策边界的变化小于预设的阈值,或者均方误差收敛。
③ 基于样本的Lloyd-Max算法 (Sample-based Lloyd-Max Algorithm):
在实际应用中,输入信号的精确概率密度函数 \(f_X(x)\) 通常是未知的。我们可以使用大量的输入样本来代替概率分布,计算经验质心和中点。这个基于样本的算法通常被称为 K-Means 算法的特例,或更具体地,LBG算法(将在矢量量化中介绍)。
⚝ 算法步骤 (Algorithm Steps):
▮▮▮▮ⓐ 初始化 (Initialization):选择 \(N\) 个初始量化级 \( \{y_1^{(0)}, \dots, y_N^{(0)}\} \)。可以从输入样本中随机选取 \(N\) 个,或者均匀选取。
▮▮▮▮ⓑ 迭代 (Iteration):对于第 \(k\) 次迭代,执行以下两步:
▮▮▮▮▮▮▮▮❸ 划分区域 (Partition Regions):根据当前的量化级 \( \{y_i^{(k)}\} \),将所有输入样本 \(x_j\) 划分到最近的量化级对应的区域 \( R_i^{(k+1)} \)。即,如果 \( |x_j - y_i^{(k)}| \le |x_j - y_l^{(k)}| \) 对于所有 \( l \ne i \),则 \( x_j \in R_i^{(k+1)} \)。这隐含地确定了决策边界 \( b_i^{(k+1)} = (y_i^{(k)} + y_{i+1}^{(k)})/2 \)。
▮▮▮▮▮▮▮▮❹ 更新量化级 (Update Quantization Levels):对于每个区域 \( R_i^{(k+1)} \),计算其中所有样本的平均值,作为新的量化级 \( y_i^{(k+1)} \)。
\[ y_i^{(k+1)} = \frac{1}{|R_i^{(k+1)}|} \sum_{x_j \in R_i^{(k+1)}} x_j \]
其中 \( |R_i^{(k+1)}| \) 是区域 \( R_i^{(k+1)} \) 中样本的数量。
▮▮▮▮ⓒ 终止 (Termination):重复步骤ⓑ,直到量化级或平均失真变化很小。
④ 优缺点 (Advantages and Disadvantages):
⚝ 优点 (Advantages):
▮▮▮▮ⓐ 在给定 \(N\) 和输入分布(或样本)的情况下,能够找到局部最优的量化器,通常性能优于均匀量化器和基于固定压扩律的非均匀量化器。
⚝ 缺点 (Disadvantages):
▮▮▮▮ⓐ 需要知道输入信号的概率分布或有足够的样本数据。
▮▮▮▮ⓑ 算法收敛到局部最优解,不同的初始化可能得到不同的结果。
▮▮▮▮ⓒ 计算复杂度相对较高,特别是基于样本的实现需要多次遍历样本集。
7.3 矢量量化 (Vector Quantization, VQ)
矢量量化(Vector Quantization, VQ)是一种将多个输入样本组合成一个向量,然后对整个向量进行联合量化的技术。与标量量化独立处理每个样本不同,矢量量化将 \(k\) 个样本 \( (x_1, x_2, \dots, x_k) \) 视为一个 \(k\) 维向量 \( \mathbf{x} \in \mathbb{R}^k \),并将其映射到 \(k\) 维空间中的一个代表向量 \( \mathbf{y} \in \mathcal{Y} \),其中 \( \mathcal{Y} = \{\mathbf{y}_1, \mathbf{y}_2, \dots, \mathbf{y}_N\} \) 是一个包含 \(N\) 个 \(k\) 维向量的有限集合,称为码本(Codebook)。码本中的向量 \( \mathbf{y}_i \) 称为码字(Codeword)。
矢量量化可以利用向量分量之间的相关性(Correlation)和依赖性(Dependency),从而在相同比特率下获得比独立的标量量化更低的失真,或者在相同失真下获得更低的比特率。这是因为许多实际信号(如图像像素、音频样本)在时间和空间上存在显著的相关性。
7.3.1 矢量量化原理 (Principle of Vector Quantization)
① 定义 (Definition):
一个 \(k\) 维矢量量化器 \(Q\) 是一个映射 \( Q: \mathbb{R}^k \to \mathcal{Y} \),其中 \( \mathcal{Y} = \{\mathbf{y}_1, \dots, \mathbf{y}_N\} \subset \mathbb{R}^k \) 是码本。
输入向量空间 \( \mathbb{R}^k \) 被划分为 \(N\) 个区域 \( R_1, R_2, \dots, R_N \),称为Voronoi区域(Voronoi Regions)。如果输入向量 \( \mathbf{x} \in R_i \),则 \( Q(\mathbf{x}) = \mathbf{y}_i \)。
② 失真度量 (Distortion Measure):
对于矢量量化,常用的失真度量是平方误差的期望:
\[ D = E[\| \mathbf{x} - Q(\mathbf{x}) \|^2] = \sum_{i=1}^N \int_{R_i} \| \mathbf{x} - \mathbf{y}_i \|^2 f_{\mathbf{X}}(\mathbf{x}) d\mathbf{x} \]
其中 \( \| \cdot \|^2 \) 是欧几里得范数的平方,\( f_{\mathbf{X}}(\mathbf{x}) \) 是输入向量 \( \mathbf{X} \) 的联合概率密度函数。
③ 最优性条件 (Optimality Conditions):
与标量量化类似,最优的 \(k\) 维矢量量化器(在MSE意义下)也必须满足两个条件:
⚝ 最近邻条件 (Nearest Neighbor Condition):对于给定的码本 \( \{\mathbf{y}_1, \dots, \mathbf{y}_N\} \),最优的划分区域 \( R_i \) 必须是所有距离 \( \mathbf{y}_i \) 比距离任何其他码字 \( \mathbf{y}_j (j \ne i) \) 都近的输入向量 \( \mathbf{x} \) 的集合。
\[ R_i = \{ \mathbf{x} \in \mathbb{R}^k : \| \mathbf{x} - \mathbf{y}_i \|^2 \le \| \mathbf{x} - \mathbf{y}_j \|^2, \forall j \ne i \} \]
这些区域 \( R_i \) 构成了由码字 \( \mathbf{y}_i \) 定义的Voronoi划分。
⚝ 质心条件 (Centroid Condition):对于给定的区域划分 \( \{R_1, \dots, R_N\} \),最优的码字 \( \mathbf{y}_i \) 必须是其对应区域 \( R_i \) 内输入向量的条件期望(质心)。
\[ \mathbf{y}_i = E[\mathbf{X} | \mathbf{X} \in R_i] = \frac{\int_{R_i} \mathbf{x} f_{\mathbf{X}}(\mathbf{x}) d\mathbf{x}}{\int_{R_i} f_{\mathbf{X}}(\mathbf{x}) d\mathbf{x}} \]
④ 码本设计 (Codebook Design):
矢量量化的核心问题是码本的设计,即如何确定最优的码字集合 \( \{\mathbf{y}_1, \dots, \mathbf{y}_N\} \) 和相应的区域划分 \( \{R_1, \dots, R_N\} \),以最小化失真。与标量Lloyd-Max算法类似,由于最优性条件相互依赖,码本设计通常采用迭代算法。
⑤ 比特率 (Bit Rate):
如果码本大小为 \(N\),表示每个码字需要 \( \lceil \log_2 N \rceil \) 比特。对于一个 \(k\) 维向量,总共有 \(k\) 个样本,因此每个样本的平均比特率为 \( R = \frac{\lceil \log_2 N \rceil}{k} \) 比特/样本。
7.3.2 LBG算法 (LBG Algorithm)
LBG算法(Linde-Buzo-Gray Algorithm)是用于设计矢量量化码本的最常用算法之一。它是基于训练样本集的迭代优化算法,是标量Lloyd-Max算法在矢量情况下的推广,有时也被称为Generalized Lloyd Algorithm (GLA)。
① 算法步骤 (Algorithm Steps):
假设我们有一个包含大量训练向量的集合 \( T = \{\mathbf{x}_1, \mathbf{x}_2, \dots, \mathbf{x}_M\} \),其中 \( \mathbf{x}_j \in \mathbb{R}^k \),\( M \gg N \)。
⚝ 初始化 (Initialization):选择 \(N\) 个初始码字 \( \{\mathbf{y}_1^{(0)}, \dots, \mathbf{y}_N^{(0)}\} \)。常用的方法包括:
▮▮▮▮ⓐ 从训练集中随机选取 \(N\) 个向量作为初始码字。
▮▮▮▮ⓑ 使用分裂(Splitting)技术:先设计一个1个码字的码本(训练集的质心),然后将这个码字分裂成两个,运行LBG算法得到2个码字的码本,再将每个码字分裂成两个,得到4个码字的码本,以此类推,直到得到 \(N\) 个码字。分裂时通常在原码字上加上一个小的随机扰动。
⚝ 迭代 (Iteration):对于第 \(l\) 次迭代,执行以下两步:
▮▮▮▮▮▮▮▮❶ 划分区域 (Partition Regions):根据当前的码本 \( \{\mathbf{y}_i^{(l)}\} \),将训练集中的每个向量 \( \mathbf{x}_j \) 归类到距离最近的码字 \( \mathbf{y}_i^{(l)} \) 对应的区域 \( R_i^{(l+1)} \)。
\[ R_i^{(l+1)} = \{ \mathbf{x}_j \in T : \| \mathbf{x}_j - \mathbf{y}_i^{(l)} \|^2 \le \| \mathbf{x}_j - \mathbf{y}_m^{(l)} \|^2, \forall m \ne i \} \]
计算当前的平均失真 \( D^{(l)} = \frac{1}{M} \sum_{j=1}^M \| \mathbf{x}_j - Q^{(l)}(\mathbf{x}_j) \|^2 \),其中 \( Q^{(l)}(\mathbf{x}_j) \) 是 \( \mathbf{x}_j \) 所属区域对应的码字。
▮▮▮▮▮▮▮▮❷ 更新码字 (Update Codewords):对于每个区域 \( R_i^{(l+1)} \),计算其中所有训练向量的平均值(质心),作为新的码字 \( \mathbf{y}_i^{(l+1)} \)。
\[ \mathbf{y}_i^{(l+1)} = \frac{1}{|R_i^{(l+1)}|} \sum_{\mathbf{x}_j \in R_i^{(l+1)}} \mathbf{x}_j \]
如果某个区域 \( R_i^{(l+1)} \) 为空,可以采用一些策略处理,例如将其对应的码字设置为训练集中失真最大的向量,或者将其删除并重新分配码字数量。
⚝ 终止 (Termination):重复步骤ⓑ,直到平均失真 \( D^{(l)} \) 的下降率小于预设的阈值,或者码字的变化很小。
② 编码与译码 (Encoding and Decoding):
⚝ 编码 (Encoding):对于一个新的输入向量 \( \mathbf{x} \),在设计好的码本 \( \{\mathbf{y}_1, \dots, \mathbf{y}_N\} \) 中找到与其距离最近的码字 \( \mathbf{y}_i \)。然后输出该码字的索引 \(i\)。这个索引就是编码后的数据。
\[ i^* = \arg \min_{i=1, \dots, N} \| \mathbf{x} - \mathbf{y}_i \|^2 \]
⚝ 译码 (Decoding):接收端接收到码字索引 \(i\) 后,直接查找码本,输出对应的码字 \( \mathbf{y}_i \) 作为恢复的向量。
③ 优缺点 (Advantages and Disadvantages):
⚝ 优点 (Advantages):
▮▮▮▮ⓐ 能够有效地利用向量分量之间的相关性,通常比独立的标量量化获得更高的压缩效率(在相同失真下)。
▮▮▮▮ⓑ LBG算法是基于训练数据进行优化的,能够适应实际信号的统计特性。
⚝ 缺点 (Disadvantages):
▮▮▮▮ⓐ 码本设计(LBG算法)的计算复杂度较高,特别是对于高维向量和大型码本。
▮▮▮▮ⓑ 编码过程需要进行最近邻搜索,计算量较大(需要计算输入向量与所有码字的距离)。可以使用一些快速搜索算法(如树结构搜索)来降低复杂度。
▮▮▮▮ⓒ LBG算法收敛到局部最优解,码本质量依赖于初始码本的选择和训练数据的代表性。
▮▮▮▮ⓓ 矢量维度 \(k\) 增加时,码本大小 \(N\) 需要指数级增长才能维持相同的量化性能,这被称为“维数灾难”(Curse of Dimensionality)。
矢量量化是许多图像、音频和语音编码标准中的重要组成部分,例如早期的语音编码标准CELP(Code-Excited Linear Prediction)就大量使用了矢量量化。
本章我们详细探讨了量化这一核心的有失真编码技术,包括标量量化和矢量量化,以及它们的设计方法和性能特点。量化是许多高级有失真编码技术的基础,理解量化原理对于深入学习后续章节内容至关重要。
8. chapter 8: 其他有失真编码技术 (Other Lossy Coding Techniques)
在前面的章节中,我们探讨了无损信源编码的理论极限和经典算法,以及有失真信源编码的理论基础——率失真理论,并详细介绍了最基本的有失真编码技术:量化。量化是许多有失真编码系统的核心组成部分,但单独使用量化往往不足以达到高效的压缩比,尤其对于具有复杂结构和相关性的信号,如图像、音频和视频。
本章将介绍两种广泛应用于多媒体信号压缩的、更为复杂的有失真编码技术:预测编码(Predictive Coding)和变换编码(Transform Coding)。这些技术通过利用信号内部的相关性或结构特性,进一步降低需要编码的信息量,从而在允许一定失真的前提下实现更高的压缩效率。
8.1 预测编码 (Predictive Coding)
预测编码是一种利用信号样本之间的相关性来减少冗余的编码技术。其核心思想是:根据信号历史样本(或已知信息)预测当前样本的值,然后只对实际值与预测值之间的差值(即预测误差)进行编码。由于信号通常具有一定的平稳性或可预测性,预测误差的方差(或能量)往往远小于原始信号的方差,因此对预测误差进行量化和编码可以获得更高的压缩效率。
预测编码系统通常包含一个预测器(Predictor),它根据过去的样本(在编码器端是原始样本,在解码器端是重建样本)来预测当前样本。预测误差经过量化器(Quantizer)处理后,被编码传输。在解码端,接收到的量化预测误差与解码器端的预测值相加,即可重建出原始信号的近似值。
8.1.1 DPCM (Differential Pulse Code Modulation)
差分脉冲编码调制(Differential Pulse Code Modulation, DPCM)是最基本的预测编码形式。它利用信号相邻样本之间的强相关性,通过编码样本之间的差值来实现压缩。
一个基本的 DPCM 编码器包含以下几个主要组成部分:
① 预测器(Predictor):根据过去的 \(N\) 个样本 \(x_{n-1}, x_{n-2}, \dots, x_{n-N}\) 来预测当前样本 \(x_n\)。最简单的预测器是使用前一个样本作为当前样本的预测值,即 \(\hat{x}_n = x_{n-1}\)。更复杂的预测器可以使用线性组合,如 \(\hat{x}_n = \sum_{i=1}^N a_i x_{n-i}\)。
② 减法器(Subtractor):计算预测误差 \(e_n = x_n - \hat{x}_n\)。
③ 量化器(Quantizer):对预测误差 \(e_n\) 进行量化,得到量化误差 \(e_n^Q\)。这是引入失真的环节。
④ 编码器(Encoder):对量化误差 \(e_n^Q\) 进行编码,生成码流传输。通常使用变长编码(Variable-Length Coding),如霍夫曼编码(Huffman Coding),因为预测误差的概率分布通常集中在零附近。
DPCM 解码器则包含:
① 解码器(Decoder):接收码流,解码得到量化误差 \(e_n^Q\)。
② 预测器(Predictor):与编码器使用相同的预测器,根据过去的重建样本来预测当前样本。注意,解码器无法获得原始样本 \(x_n\),只能使用已经重建的样本 \(\tilde{x}_{n-1}, \tilde{x}_{n-2}, \dots, \tilde{x}_{n-N}\) 来进行预测,即 \(\hat{x}_n = \sum_{i=1}^N a_i \tilde{x}_{n-i}\)。
③ 加法器(Adder):将预测值 \(\hat{x}_n\) 与量化误差 \(e_n^Q\) 相加,得到当前样本的重建值 \(\tilde{x}_n = \hat{x}_n + e_n^Q\)。
DPCM 的优点在于其简单性,并且对于具有高相关性的信号(如语音、图像的平坦区域)能有效降低预测误差的方差,从而提高压缩效率。然而,如果预测器不够准确,或者信号相关性较低,DPCM 的效果会下降。此外,量化误差会在解码端的预测环路中累积,可能导致误差扩散。
8.1.2 ADPCM (Adaptive Differential Pulse Code Modulation)
自适应差分脉冲编码调制(Adaptive Differential Pulse Code Modulation, ADPCM)是对 DPCM 的改进,它引入了自适应机制,使得预测器或量化器(或两者)的参数可以根据信号的局部特性动态调整。
自适应可以体现在以下方面:
① 自适应预测器(Adaptive Predictor):预测器的系数 \(a_i\) 不固定,而是根据信号的统计特性实时更新。例如,可以使用最小均方(Least Mean Squares, LMS)算法等自适应滤波算法来调整预测系数,使其更好地适应信号的变化。
② 自适应量化器(Adaptive Quantizer):量化器的步长(Step Size)或决策边界(Decision Boundaries)和重建电平(Reconstruction Levels)根据预测误差的统计特性或信号的局部能量进行调整。例如,如果预测误差的幅度较大,说明信号变化剧烈,可以增大步长以覆盖更大的范围;如果预测误差幅度较小,可以减小步长以提高精度。这有助于更好地匹配预测误差的动态范围,减少量化噪声。
ADPCM 系统通常比 DPCM 系统更复杂,但能更好地适应信号的变化,提供更好的压缩性能和重建质量,尤其对于非平稳信号。例如,ITU-T G.726 标准就是一种 ADPCM 算法,广泛应用于数字电话和语音通信。
8.2 变换编码概述 (Overview of Transform Coding)
变换编码是另一种重要的有失真编码技术,广泛应用于图像、音频和视频压缩。其核心思想是将信号从原始域(如时域或空域)通过一个可逆的线性变换(Linear Transform)转换到另一个域(如频域或变换域)。选择合适的变换可以使得信号的能量或信息集中在变换域的少数系数上,并且这些系数之间的相关性降低。然后,对这些变换系数进行量化和编码。由于大部分能量集中在少数系数上,可以对重要系数进行精细量化,而对不重要系数进行粗糙量化甚至直接丢弃,从而实现压缩。
变换编码的一般流程如下:
① 分块(Blocking):将原始信号(如图像)分割成较小的块。这是为了使变换更易于处理,并允许局部适应性。
② 变换(Transform):对每个块应用一个线性变换,将信号从原始域转换到变换域。例如,对于图像块,可以应用二维离散余弦变换(2D DCT)。
③ 量化(Quantization):对变换系数进行量化。这是引入失真的主要环节。通常采用非均匀量化,对能量集中的低频系数进行更精细的量化,对能量较低的高频系数进行更粗糙的量化或置零(Thresholding)。
④ 编码(Encoding):对量化后的变换系数进行编码。通常会结合使用游程编码(Run-Length Encoding, RLE)来压缩量化后产生的零系数串,再结合霍夫曼编码或算术编码对非零系数及其位置进行编码。
在解码端,接收到的码流被解码得到量化后的变换系数,然后进行反量化(Inverse Quantization),接着应用逆变换(Inverse Transform)将信号从变换域转换回原始域,最后将块组合起来重建原始信号。
变换编码的优点在于它能够有效地去除信号的空域或时域相关性,并将能量集中,便于进行基于感知的量化(Perceptual Quantization),即根据人眼的视觉或人耳的听觉特性来分配量化精度,将更多的比特分配给对感知更重要的信息。
8.2.1 离散余弦变换 (Discrete Cosine Transform, DCT) 简介
离散余弦变换(Discrete Cosine Transform, DCT)是一种实数域的线性变换,与离散傅里叶变换(Discrete Fourier Transform, DFT)密切相关。它将一个有限长度的序列表示为不同频率和幅度的余弦函数的叠加。
DCT 之所以广泛应用于图像和视频压缩(如 JPEG, MPEG 标准),主要原因在于其优异的“能量集中”(Energy Compaction)特性。对于许多自然图像块,其能量在 DCT 域中会高度集中在低频系数上,而高频系数的能量较低。这意味着大部分信息可以通过少量低频系数来表示,而大量高频系数可以被粗糙量化甚至丢弃,同时对视觉质量的影响相对较小。
DCT 有多种类型,其中最常用的是 II 型 DCT。对于一个 N 点的序列 \(x_n\),其 DCT-II 定义为:
\[ X_k = c_k \sum_{n=0}^{N-1} x_n \cos\left(\frac{\pi(2n+1)k}{2N}\right), \quad k=0, 1, \dots, N-1 \]
其中 \(c_0 = \sqrt{1/N}\),\(c_k = \sqrt{2/N}\) 对于 \(k>0\)。
二维 DCT(用于图像块)是两个一维 DCT 的组合。它将图像块转换为一个二维系数矩阵,其中左上角的系数对应于低频分量,右下角的系数对应于高频分量。
8.2.2 小波变换 (Wavelet Transform) 简介
小波变换(Wavelet Transform)是另一种强大的变换工具,它提供了一种多分辨率(Multi-resolution)或多尺度(Multi-scale)分析信号的方法。与将信号分解为不同频率的正弦波的傅里叶变换不同,小波变换将信号分解为由一个基本小波函数(Mother Wavelet)经过缩放(Scaling)和平移(Translation)得到的一系列小波函数的叠加。
小波变换的主要优点包括:
① 时频局部化(Time-Frequency Localization):小波变换能够同时提供信号在时间和频率上的局部信息。这意味着它可以很好地分析信号中的瞬态(Transients)或突变(例如图像中的边缘),这是基于全局基函数的傅里叶变换或 DCT 难以做到的。
② 多分辨率分析:小波变换可以将信号分解到不同的尺度(频率)上进行分析。高频分量通常具有较好的时间分辨率,而低频分量具有较好的频率分辨率。这与人类的视觉和听觉感知特性相符。
③ 能量集中:与 DCT 类似,小波变换也能将许多信号的能量集中到少数系数上,尤其对于具有分形(Fractal)或自相似(Self-similar)特性的信号。
离散小波变换(Discrete Wavelet Transform, DWT)通过滤波器组(Filter Bank)实现,通常将信号分解为低频子带(Approximation Coefficients)和高频子带(Detail Coefficients)。低频子带可以进一步分解,形成多级分解,从而实现多分辨率分析。
小波变换在图像压缩(如 JPEG2000 标准)、音频压缩、信号去噪、特征提取等领域有广泛应用。与 DCT 相比,小波变换在处理图像边缘和纹理方面通常表现更好,并且可以避免块效应(Blocking Artifacts)。
本章概述了预测编码和变换编码这两种重要的有失真编码技术。它们通过利用信号的相关性或结构特性,结合量化,实现了比单独量化更高的压缩效率。在实际的多媒体压缩标准中,这些技术往往被结合使用,例如在视频编码中,通常会结合帧间预测(Inter-frame Prediction, 一种预测编码)和帧内变换编码(Intra-frame Transform Coding)。
9. chapter 9: 信源编码算法的比较与选择 (Comparison and Selection of Source Coding Algorithms)
同学们,欢迎来到本书的第九章。在前几章中,我们系统地学习了信息论的基础概念,包括熵、互信息,以及无损和有失真信源编码的理论基础。我们还深入探讨了一系列经典的无损和有失真编码算法。现在,是时候将这些知识融会贯通,讨论一个非常实际且重要的问题:在面对具体的应用场景时,我们应该如何比较不同的信源编码算法,并从中选择最合适的一种?
信源编码的目标通常是在给定失真度(对于有失真编码)或无失真(对于无损编码)的前提下,最小化表示信源所需的平均比特数。然而,实际应用中的选择远不止压缩效率这么简单。我们还需要考虑算法的计算复杂度、内存需求、实时性要求、对特定数据类型的适应性以及实现的难易程度等多种因素。
本章将带领大家回顾并比较主要的信源编码算法,分析它们的优缺点,并提供一个框架,帮助大家理解如何在不同的应用需求下做出明智的选择。这不仅是对前面知识的总结,更是将理论应用于实践的关键一步。
9.1 无损算法性能比较 (Performance Comparison of Lossless Algorithms)
无损信源编码(Lossless Source Coding)的目标是在不丢失任何信息的前提下压缩数据。其理论极限由信源的熵(Entropy)决定。一个好的无损编码算法应该能够使平均码长接近信源的熵。我们在前面章节学习了几种主要的无损编码算法,现在我们来比较它们的性能。
⚝ 霍夫曼编码 (Huffman Coding)
▮▮▮▮⚝ 优点:
▮▮▮▮▮▮▮▮⚝ 对于已知信源概率分布的离散无记忆信源(Discrete Memoryless Source, DMS),霍夫曼编码能够生成最优的前缀码(Prefix Code),其平均码长最接近信源熵。
▮▮▮▮▮▮▮▮⚝ 实现相对简单。
▮▮▮▮⚝ 缺点:
▮▮▮▮▮▮▮▮⚝ 需要预先知道信源的概率分布,或者在编码过程中动态估计(自适应霍夫曼编码),这增加了复杂度。
▮▮▮▮▮▮▮▮⚝ 对于符号集较大或概率分布变化频繁的信源,效率可能下降。
▮▮▮▮▮▮▮▮⚝ 每次编码一个符号,对于相关性较强的信源,压缩效率不如考虑符号间关联的算法。
⚝ 算术编码 (Arithmetic Coding)
▮▮▮▮⚝ 优点:
▮▮▮▮▮▮▮▮⚝ 理论上可以达到比霍夫曼编码更高的压缩效率,尤其是在信源符号概率差异很大或符号集很小的情况下,它可以接近信源熵的任意精度。
▮▮▮▮▮▮▮▮⚝ 可以方便地集成高阶统计模型(考虑符号间的关联性),进一步提高压缩效率。
▮▮▮▮▮▮▮▮⚝ 编码过程不限于整数比特,可以更精细地分配码长。
▮▮▮▮⚝ 缺点:
▮▮▮▮▮▮▮▮⚝ 实现比霍夫曼编码复杂。
▮▮▮▮▮▮▮▮⚝ 对计算精度要求较高,容易引入数值问题。
▮▮▮▮▮▮▮▮⚝ 编码错误敏感,一个比特错误可能导致整个后续数据无法正确解码。
⚝ 游程编码 (Run-Length Encoding, RLE)
▮▮▮▮⚝ 优点:
▮▮▮▮▮▮▮▮⚝ 实现极其简单。
▮▮▮▮▮▮▮▮⚝ 对于包含大量连续重复符号的数据(如简单的黑白图像、传真数据),压缩效果非常好。
▮▮▮▮⚝ 缺点:
▮▮▮▮▮▮▮▮⚝ 对于没有或很少有连续重复符号的数据,压缩效果可能很差,甚至可能膨胀数据。
▮▮▮▮▮▮▮▮⚝ 是一种非常基础的算法,不适用于通用数据压缩。
⚝ LZ系列算法 (LZ Family Algorithms: LZ77, LZ78, LZW)
▮▮▮▮⚝ 优点:
▮▮▮▮▮▮▮▮⚝ 不需要预先知道信源的概率分布,是自适应的(Adaptive)。
▮▮▮▮▮▮▮▮⚝ 利用数据中的重复模式(字符串匹配或字典引用)进行压缩,对于文本、代码等具有较强局部相关性的数据效果显著。
▮▮▮▮▮▮▮▮⚝ LZ77和LZW实现相对简单且广泛应用(如gzip, PNG, GIF)。
▮▮▮▮⚝ 缺点:
▮▮▮▮▮▮▮▮⚝ 压缩效率通常不如基于概率模型的算术编码,特别是对于随机性较强的数据。
▮▮▮▮▮▮▮▮⚝ 需要维护一个字典或滑动窗口,需要一定的内存开销。
▮▮▮▮▮▮▮▮⚝ 编码和解码过程可能需要较多计算资源(字符串匹配)。
总结无损算法性能:
从理论压缩效率来看,算术编码在理想情况下最接近熵极限,优于霍夫曼编码。霍夫曼编码对于DMS是最优前缀码。LZ系列算法不依赖概率模型,而是利用数据中的重复结构,对于具有局部相关性的数据表现良好。RLE则是一种特殊情况下的高效算法。
实际应用中,通常会结合使用这些算法或其变种,例如先进行LZ类型的字典压缩,再对输出的符号或偏移量进行霍夫曼或算术编码,以达到更好的压缩效果(如DEFLATE算法,结合LZ77和霍夫曼编码)。
9.2 有损算法性能比较 (Performance Comparison of Lossy Algorithms)
有失真信源编码(Lossy Source Coding)允许在压缩过程中丢失一部分信息,以换取更高的压缩率。其理论极限由率失真函数(Rate-Distortion Function) \( R(D) \) 决定,表示在允许最大失真度 \( D \) 的情况下,表示信源所需的最小平均比特率 \( R \)。有失真编码的关键在于如何在压缩率和允许的失真之间找到最佳平衡。
⚝ 量化 (Quantization)
▮▮▮▮⚝ 标量量化 (Scalar Quantization, SQ):
▮▮▮▮▮▮▮▮⚝ 优点: 实现简单,计算量小。
▮▮▮▮▮▮▮▮⚝ 缺点: 忽略了数据样本之间的相关性,压缩效率有限。均匀量化器(Uniform Quantizer)简单但对输入分布不敏感;非均匀量化器(Non-uniform Quantizer)或Lloyd-Max量化器(Lloyd-Max Quantizer)可以根据输入分布优化,但仍是标量处理。
▮▮▮▮⚝ 矢量量化 (Vector Quantization, VQ):
▮▮▮▮▮▮▮▮⚝ 优点: 将多个样本组合成一个向量进行量化,可以有效利用样本间的相关性,通常比标量量化能达到更好的率失真性能。
▮▮▮▮▮▮▮▮⚝ 缺点: 码本(Codebook)设计复杂(如LBG算法),搜索最佳码字计算量大,内存需求高。对输入向量维度敏感,维度越高复杂度增长越快。
⚝ 预测编码 (Predictive Coding)
▮▮▮▮⚝ 优点: 利用数据样本间的时域或空域相关性,通过预测当前样本值并对预测误差进行编码,通常误差信号的方差小于原始信号,易于压缩。DPCM(Differential Pulse Code Modulation)和ADPCM(Adaptive Differential Pulse Code Modulation)是典型例子。实现相对简单。
▮▮▮▮⚝ 缺点: 预测器的性能直接影响压缩效率。对预测误差的量化引入失真,且误差可能累积。
⚝ 变换编码 (Transform Coding)
▮▮▮▮⚝ 优点: 将信号从时域或空域变换到频域或其他变换域(如离散余弦变换 DCT, 小波变换 Wavelet Transform),在变换域中,能量通常集中在少数系数上,且不同系数之间的相关性降低,便于进行量化和编码。这是现代图像、音频、视频压缩(如JPEG, MP3, H.26x)的核心技术。可以结合量化和熵编码(如霍夫曼或算术编码)实现高效压缩。
▮▮▮▮⚝ 缺点: 需要进行变换和反变换,增加了计算复杂度。选择合适的变换基和量化策略是关键。对块效应(Blocking Artifacts)敏感(如基于块的DCT)。
总结有损算法性能:
有失真编码算法的性能通常通过率失真曲线(Rate-Distortion Curve)来衡量,即在不同允许失真度 \( D \) 下达到的比特率 \( R \)。理论上,率失真函数给出了性能的下界。
⚝ 量化是最基本的有失真技术,矢量量化优于标量量化,因为它考虑了样本间的相关性。
⚝ 预测编码利用了信号的预测性,对预测误差进行编码,适用于具有强相关性的信号。
⚝ 变换编码通过能量集中和去相关性,为后续的量化和熵编码提供了便利,是目前多媒体压缩中最有效的方法之一。
实际应用中,高性能的有失真编码系统往往是多种技术的结合,例如:
① 图像压缩(JPEG):先进行块划分,然后对每个块进行DCT变换,接着对变换系数进行量化,最后对量化后的系数进行无损熵编码(霍夫曼或算术编码)。
② 音频压缩(MP3):利用感知模型(Perceptual Model)指导变换(如MDCT)和量化,丢弃人耳不敏感的信息。
③ 视频压缩(H.26x):结合帧间预测(利用时间相关性)、帧内预测(利用空间相关性)、变换(DCT)、量化和熵编码。
9.3 复杂度与实现考虑 (Complexity and Implementation Considerations)
除了压缩性能,算法的计算复杂度(Computational Complexity)和实现复杂度(Implementation Complexity)也是选择算法时必须考虑的重要因素。
⚝ 计算复杂度: 指算法在编码和解码过程中所需的计算资源,通常用浮点运算次数、整数运算次数或时钟周期来衡量。
▮▮▮▮⚝ 无损算法:
▮▮▮▮▮▮▮▮⚝ 霍夫曼编码:构造霍夫曼树需要 \( O(N \log N) \) 或 \( O(N) \) 的时间(N为符号种类数),编码和解码每个符号通常是 \( O(\text{码长}) \) 或 \( O(\log N) \)。自适应霍夫曼编码复杂度较高。
▮▮▮▮▮▮▮▮⚝ 算术编码:编码和解码每个符号需要多次乘法和加法运算,通常比霍夫曼编码慢,且对精度要求高。
▮▮▮▮▮▮▮▮⚝ LZ系列算法:编码需要进行字符串匹配,复杂度取决于匹配算法和窗口/字典大小,可能较高。解码相对简单快速(字典查找)。
▮▮▮▮▮▮▮▮⚝ RLE:编码和解码都非常快,只需要简单的计数和复制操作。
▮▮▮▮⚝ 有失真算法:
▮▮▮▮▮▮▮▮⚝ 标量量化:非常快,只需要简单的比较和查找操作。
▮▮▮▮▮▮▮▮⚝ 矢量量化:编码(码本搜索)计算量巨大,特别是对于大码本和高维度向量。解码非常快(码本查找)。
▮▮▮▮▮▮▮▮⚝ 预测编码:预测过程计算量取决于预测器阶数,对预测误差的编码/解码取决于所用方法,通常计算量适中。
▮▮▮▮▮▮▮▮⚝ 变换编码:变换(如DCT, Wavelet)和反变换需要一定的计算量(通常使用快速算法如FFT),量化和熵编码的计算量也需要考虑。
⚝ 内存需求 (Memory Requirements): 指算法在运行过程中所需的内存空间。
▮▮▮▮⚝ 无损算法:
▮▮▮▮▮▮▮▮⚝ 霍夫曼编码:需要存储霍夫曼树或码表,大小与符号种类数有关。
▮▮▮▮▮▮▮▮⚝ 算术编码:需要存储概率模型,可能需要较大的内存,特别是高阶模型。
▮▮▮▮▮▮▮▮⚝ LZ系列算法:需要维护滑动窗口(LZ77)或字典(LZ78, LZW),字典大小直接影响内存需求和压缩性能。
▮▮▮▮▮▮▮▮⚝ RLE:内存需求极低。
▮▮▮▮⚝ 有失真算法:
▮▮▮▮▮▮▮▮⚝ 标量量化:内存需求极低,只需存储量化间隔或决策边界。
▮▮▮▮▮▮▮▮⚝ 矢量量化:需要存储码本,码本大小(码字数量和维度)直接决定内存需求,可能非常大。
▮▮▮▮▮▮▮▮⚝ 预测编码:需要存储过去的样本值或预测器系数。
▮▮▮▮▮▮▮▮⚝ 变换编码:需要存储变换基或滤波器系数,以及变换后的系数。
⚝ 实现复杂度: 指算法的原理理解和代码实现的难易程度。
▮▮▮▮⚝ RLE和标量量化通常是最容易实现的。
▮▮▮▮⚝ 霍夫曼编码实现相对直接。
▮▮▮▮⚝ LZ系列算法实现需要注意滑动窗口或字典的管理。
▮▮▮▮⚝ 算术编码和矢量量化(特别是码本训练)原理和实现都比较复杂。
▮▮▮▮⚝ 变换编码需要理解相应的数学变换,并实现快速算法。
在资源受限的环境(如嵌入式系统)或需要实时处理的应用中,计算复杂度和内存需求往往是比极致压缩率更重要的考量因素。
9.4 如何根据应用选择合适的算法 (How to Choose the Right Algorithm Based on Application)
选择合适的信源编码算法是一个权衡(Trade-off)的过程,需要综合考虑应用的需求、数据的特性以及可用的计算资源。以下是一些关键的考虑因素和选择指南:
① 是否允许失真? (Is Distortion Allowed?)
▮▮▮▮⚝ 如果应用要求数据必须完全恢复,不允许任何信息丢失(如文本文件、程序代码、可执行文件、某些医疗影像),则必须使用无损编码算法。
▮▮▮▮⚝ 如果可以接受一定程度的信息丢失,且失真在可接受范围内(如图像、音频、视频、语音),则可以考虑使用有失真编码算法,以获得更高的压缩率。
② 数据类型与特性 (Data Type and Characteristics)
▮▮▮▮⚝ 文本、代码等: 通常具有较强的局部相关性和重复字符串。LZ系列算法(如LZ77, LZW)通常是首选,常与其他无损算法结合(如DEFLATE)。算术编码结合高阶模型也能取得很好的效果。
▮▮▮▮⚝ 简单图像(如传真、扫描文档中的二值图): RLE可能非常有效。
▮▮▮▮⚝ 通用图像、音频、视频: 这些数据通常在时域或空域具有较强的相关性,且人对其失真有感知特性。变换编码(如DCT, 小波)结合量化和熵编码是主流方法。预测编码和矢量量化也常作为其中的组成部分或用于特定场景。
▮▮▮▮⚝ 科学数据、传感器数据: 取决于数据的特性。如果数据中有大量重复值,RLE可能有用。如果数据是连续的且有预测性,预测编码可能适用。如果需要高精度且不允许失真,则需要无损压缩,可能需要定制算法或使用通用无损压缩工具。
③ 压缩率要求 (Compression Ratio Requirements)
▮▮▮▮⚝ 如果需要极高的压缩率,通常需要使用有失真编码,并允许较大的失真。
▮▮▮▮⚝ 在无损压缩中,算术编码理论上能达到最高效率,但实现复杂。霍夫曼编码是效率和复杂度之间的良好折衷。LZ系列算法在处理特定类型数据时效率很高。
④ 计算资源与实时性 (Computational Resources and Real-time Requirements)
▮▮▮▮⚝ 资源受限或需要实时编码/解码: 选择计算复杂度低、内存需求小的算法。RLE、简单的标量量化、基本的霍夫曼编码或简单的预测编码可能更合适。LZ77的解码速度通常很快。
▮▮▮▮⚝ 资源充足,离线处理: 可以选择计算复杂度较高但压缩效率更好的算法,如算术编码、矢量量化、复杂的变换编码或多遍压缩算法。
⑤ 实现复杂度 (Implementation Complexity)
▮▮▮▮⚝ 如果开发时间或人力有限,选择实现相对简单的算法(RLE, 标量量化, 霍夫曼编码)。
▮▮▮▮⚝ 复杂的算法(算术编码, VQ, 复杂的变换编码)需要更多的专业知识和开发投入。
⑥ 错误敏感性 (Error Sensitivity)
▮▮▮▮⚝ 算术编码对错误非常敏感,一个错误可能导致后续数据全部解码失败。
▮▮▮▮⚝ 霍夫曼编码是前缀码,一个错误通常只会影响当前码字,后续码字可以继续正确解码(除非错误导致码字边界判断失误)。
▮▮▮▮⚝ LZ系列算法的错误传播范围取决于错误发生的位置和字典/窗口机制。
▮▮▮▮⚝ 有失真编码中的一些技术(如预测编码)可能存在误差累积问题。在易出错的信道中传输时,需要考虑增加纠错码或采用对错误不那么敏感的编码结构。
案例分析:
⚝ 案例一:压缩计算机上的文本文件
▮▮▮▮⚝ 需求: 无损压缩,通用性好。
▮▮▮▮⚝ 数据特性: 文本数据,有重复字符串和词汇。
▮▮▮▮⚝ 选择: LZ系列算法(如LZ77或LZW)是很好的选择,因为它们擅长处理重复模式。结合霍夫曼编码(如DEFLATE)可以进一步提高效率。
⚝ 案例二:压缩网络视频流进行实时传输
▮▮▮▮⚝ 需求: 高压缩率,允许一定失真,实时编码和解码,对计算资源有要求。
▮▮▮▮⚝ 数据特性: 视频数据,具有强烈的时域和空域相关性。
▮▮▮▮⚝ 选择: 复杂的有失真编码标准,如H.264或H.265,它们结合了帧间/帧内预测、变换(DCT)、量化和熵编码等多种技术,并在计算复杂度和压缩效率之间做了优化。需要硬件加速或高性能处理器支持。
⚝ 案例三:压缩医疗影像用于存储
▮▮▮▮⚝ 需求: 通常要求无损或接近无损,保证诊断准确性。
▮▮▮▮⚝ 数据特性: 图像数据,可能需要保留高精度细节。
▮▮▮▮⚝ 选择: 可以使用无损图像压缩算法(如无损JPEG2000, PNG)或针对特定医疗影像模态(如DICOM)的无损压缩方法。如果允许极小的、对诊断无影响的失真,可以考虑高码率的有失真压缩。
通过本章的学习,我们了解了各种信源编码算法的优缺点,以及在实际应用中需要考虑的各种因素。选择最佳算法并非易事,往往需要在压缩效率、计算复杂度、内存需求、实时性、错误鲁棒性以及实现难度之间进行权衡。理解这些权衡,并结合具体应用的需求和数据特性,才能做出最合适的决策。
10. chapter 10: 进阶主题与应用案例 (Advanced Topics and Application Cases)
欢迎来到本书的最后一章!在前几章中,我们系统地学习了信息论中信源编码的基本原理,从信息度量(熵)到无损编码的理论极限(信源编码定理),再到经典的无损编码算法(霍夫曼编码、算术编码、LZ系列)以及有失真编码的理论基础(率失真理论)和技术(量化、预测编码、变换编码)。这些构成了信源编码的核心知识体系。
然而,信息论和信源编码是一个广阔且不断发展的领域。本章将带领大家探索一些更进阶的主题,包括在信源特性未知或部分未知情况下的编码(通用编码)、利用额外信息进行压缩(带边信息的信源编码),并深入探讨信源编码在实际应用中的强大力量,特别是在多媒体压缩领域。最后,我们将展望信源编码未来的发展方向。
本章旨在拓宽您的视野,展示信源编码理论如何应对更复杂的场景,以及它如何在现代信息技术中发挥不可或越的作用。无论您是初学者、中级学习者还是专家,希望本章都能为您带来新的启发和思考。
10.1 通用编码 (Universal Coding)
在前几章讨论的大多数信源编码算法,如霍夫曼编码和算术编码,都假设我们对信源的统计特性(例如,符号出现的概率分布)有先验知识。然而,在许多实际应用中,信源的统计特性是未知或随时间变化的。例如,压缩一个未知来源的文本文件,我们事先并不知道其中字母、单词的频率分布。在这种情况下,我们需要一种不依赖于特定信源统计模型的编码方法,这就是通用编码(Universal Coding)。
通用编码的目标是设计一种编码器,它对于某一类信源(例如,所有离散无记忆信源,或所有马尔可夫信源)都能达到接近最优的压缩性能,而无需知道信源的具体参数。这里的“接近最优”通常是指,随着待编码数据长度的增加,通用编码的平均码长能够渐近地逼近该信源的熵(对于无损编码而言),或者逼近率失真函数(对于有失真编码而言)。
① 通用编码的挑战:
▮▮▮▮⚝ 信源统计特性未知:无法直接应用依赖于概率分布的最优编码算法(如霍夫曼编码)。
▮▮▮▮⚝ 需要适应性:编码器必须能够从数据中学习或适应信源的特性。
▮▮▮▮⚝ 性能保证:需要在不知道信源具体参数的情况下,保证对一类信源的渐近最优性。
② 通用编码的分类:
▮▮▮▮⚝ 基于模型的通用编码:这类方法尝试先估计信源的模型参数,然后基于估计的模型进行编码。例如,对于离散无记忆信源,可以估计符号的概率分布;对于马尔可夫信源,可以估计转移概率。随着数据量的增加,模型估计会越来越准确,编码性能也随之提高。
▮▮▮▮⚝ 基于数据的通用编码:这类方法不显式地建立信源模型,而是直接利用数据本身的结构进行编码。LZ系列算法(LZ77, LZ78, LZW)就是典型的基于数据的通用编码算法。它们通过查找和引用数据中重复出现的模式来达到压缩目的,而这些模式的出现频率隐含地反映了信源的统计特性。
③ 重要的通用编码概念:
▮▮▮▮⚝ 渐近最优性(Asymptotic Optimality):对于长度为 \(n\) 的信源序列 \(X^n\),如果一个编码方案的平均码长 \(L_n\) 满足
\[ \lim_{n \to \infty} \frac{L_n}{n} = H \]
其中 \(H\) 是该信源的熵,则称该编码方案是渐近最优的。通用编码的目标就是实现对一类信源的渐近最优性。
▮▮▮▮⚝ 最小描述长度原则 (Minimum Description Length, MDL):MDL原则认为,最好的模型是对数据提供最短描述的模型。在编码中,这意味着选择一个模型(或编码策略),使得模型本身的描述长度加上在该模型下编码数据的长度之和最小。这与通用编码的思想紧密相关,因为它提供了一种在模型复杂度和数据拟合度之间进行权衡的框架。
④ 典型的通用编码算法:
▮▮▮▮⚝ Lempel-Ziv (LZ) 算法系列:如前所述,LZ77、LZ78、LZW是经典的无损通用编码算法。它们通过构建或引用字典来实现压缩,对未知信源具有良好的适应性。
▮▮▮▮⚝ 预测按部分匹配 (Prediction by Partial Matching, PPM):PPM是一种基于上下文模型的通用编码方法。它根据当前符号前的若干个符号(上下文)来预测下一个符号的概率分布,并结合算术编码进行压缩。PPM通常能提供比LZ系列更好的压缩比,尤其对于文本数据。
▮▮▮▮⚝ 基于上下文的自适应二进制算术编码 (Context-Adaptive Binary Arithmetic Coding, CABAC):CABAC是现代视频编码标准(如H.264/AVC, H.265/HEVC)中使用的熵编码方法。它结合了上下文建模和自适应算术编码,能够根据局部统计特性调整编码策略,从而实现高效压缩。
通用编码是信源编码理论与实践中非常重要的一部分,它使得我们能够在不完全了解信源的情况下进行有效的压缩,极大地扩展了信源编码的应用范围。
10.2 带边信息的信源编码 (Source Coding with Side Information)
在传统的信源编码场景中,编码器和译码器都只能访问待编码的信源数据。然而,在某些情况下,译码器可能拥有一些与信源相关的额外信息,这些信息被称为边信息(Side Information)。带边信息的信源编码研究如何利用译码端的边信息来实现更高效的压缩。
考虑一个场景:我们要传输一个随机变量 \(X\) 的值,而译码器已经知道另一个随机变量 \(Y\) 的值。\(X\) 和 \(Y\) 是相关的。例如,\(X\) 是一帧视频图像,而 \(Y\) 是前一帧图像(假设运动不大,两帧相关)。如果我们能利用 \(Y\) 来帮助译码 \(X\),那么编码 \(X\) 所需的比特数可能会比独立编码 \(X\) 少。
① 边信息的位置:
▮▮▮▮⚝ 编码器和译码器都有边信息:这是最简单的情况,编码器可以直接利用边信息进行更高效的编码(例如,预测编码),译码器利用边信息进行译码。
▮▮▮▮⚝ 只有译码器有边信息:这是带边信息信源编码的核心研究问题,即 Slepian-Wolf 问题。编码器不知道边信息,但需要生成一个码字,使得译码器结合其拥有的边信息能够恢复信源。
② Slepian-Wolf 定理:
Slepian-Wolf 定理是带边信息无损信源编码的基本理论结果。它考虑了两个相关的离散无记忆信源 \(X\) 和 \(Y\),它们分别由两个独立的编码器编码,但由一个联合译码器译码,且译码器拥有 \(Y\) 的序列作为边信息。
定理指出,即使编码器不知道 \(Y\),只要 \(X\) 的编码速率 \(R_X\) 满足 \(R_X \ge H(X|Y)\),并且 \(Y\) 的编码速率 \(R_Y\) 满足 \(R_Y \ge H(Y)\),且联合速率 \(R_X + R_Y \ge H(X, Y)\),则可以实现无损联合译码。
更令人惊讶的是,如果 \(Y\) 完全是边信息(即 \(Y\) 不被编码传输,只在译码端已知),那么 \(X\) 的最小可达编码速率是 \(H(X|Y)\)。这意味着,即使编码器不知道 \(Y\),理论上也能达到知道 \(Y\) 时才能达到的压缩极限 \(H(X|Y)\)。
\[ R_X \ge H(X|Y) \]
\[ R_Y \ge H(Y|X) \]
\[ R_X + R_Y \ge H(X,Y) \]
如果 \(Y\) 是边信息,不传输,则 \(R_Y=0\),此时 \(X\) 的最小速率为 \(H(X|Y)\)。
③ Slepian-Wolf 定理的意义:
▮▮▮▮⚝ 分布式信源编码(Distributed Source Coding):Slepian-Wolf 定理是分布式信源编码的基石。它表明,对于相关的信源,即使它们被独立编码,通过在译码端联合处理,也能达到与联合编码(编码器知道所有信源)相同的压缩效率。这在无线传感器网络、分布式存储等场景中有重要应用。
▮▮▮▮⚝ 理论上的可能性:定理揭示了在只有译码器拥有边信息的情况下,理论上可以达到的压缩极限。
④ 实际实现:
Slepian-Wolf 编码的实际实现通常比理论复杂。常用的方法包括:
▮▮▮▮⚝ 基于纠错码的方法:将信源 \(X\) 视为一个码字,对其进行编码(例如,使用 LDPC 码或 Turbo 码),使得 \(X\) 的码字与 \(Y\) 之间存在某种关系。译码器利用 \(Y\) 作为“校验”信息来恢复 \(X\)。
▮▮▮▮⚝ 基于格雷码(Syndrome Coding)的方法:编码器计算 \(X\) 的一个“格雷码”(Syndrome),这个格雷码是 \(X\) 经过某个线性变换的结果。译码器利用 \(Y\) 和接收到的格雷码来恢复 \(X\)。
⑤ 带边信息的有失真编码:
对于有失真编码,带边信息的场景由 Wyner-Ziv 定理描述。Wyner-Ziv 定理是 Slepian-Wolf 定理在有失真情况下的推广。它研究在译码器拥有边信息 \(Y\) 的情况下,编码器对信源 \(X\) 进行有失真编码所能达到的最小速率,以满足给定的失真度量。
Wyner-Ziv 定理表明,在允许一定失真 \(D\) 的前提下,编码器不知道边信息 \(Y\) 时对 \(X\) 进行编码的最小速率 \(R_{WZ}(D)\) 可能高于编码器知道边信息 \(Y\) 时对 \(X\) 进行编码的最小速率 \(R^*(D)\)(即率失真函数 \(R_{X|Y}(D)\))。也就是说,编码器缺乏边信息会带来一定的速率损失,除非 \(X\) 和 \(Y\) 之间满足某些特殊条件。
\[ R_{WZ}(D) \ge R^*(D) = R_{X|Y}(D) \]
这个速率损失 \(R_{WZ}(D) - R^*(D)\) 被称为 Wyner-Ziv 速率损失。
带边信息的信源编码是一个活跃的研究领域,在视频编码(帧间编码)、图像编码、传感器网络、生物信息学等领域有重要的理论和实际意义。
10.3 在图像、音频、视频压缩中的应用 (Applications in Image, Audio, and Video Compression)
信源编码理论是现代多媒体压缩技术的核心。图像、音频和视频数据通常包含大量的冗余和不相关性,通过信源编码技术可以显著减少存储空间和传输带宽。
① 图像压缩:
图像是二维信号,包含空间冗余(相邻像素相似)和视觉冗余(人眼对某些细节不敏感)。
▮▮▮▮⚝ 无损图像压缩:
▮▮▮▮⚝⚝ PNG (Portable Network Graphics):使用预测(利用相邻像素预测当前像素值,对残差进行编码)和LZ77算法的变种(Deflate)进行无损压缩。
▮▮▮▮⚝⚝ GIF (Graphics Interchange Format):使用LZW算法进行无损压缩,但颜色数限制在256色。
▮▮▮▮⚝ 有失真图像压缩:
▮▮▮▮⚝⚝ JPEG (Joint Photographic Experts Group):最常用的有失真图像压缩标准。其主要步骤包括:
▮▮▮▮▮▮▮▮⚝⚝⚝ 颜色空间转换:将RGB转换为YUV等颜色空间,分离亮度和色度信息。
▮▮▮▮▮▮▮▮⚝⚝⚝ 分块:将图像分割成8x8的小块。
▮▮▮▮▮▮▮▮⚝⚝⚝ 离散余弦变换 (DCT):对每个8x8块进行DCT变换,将空间域信号转换为频率域系数。低频系数代表图像的平滑部分,高频系数代表细节和纹理。
▮▮▮▮▮▮▮▮⚝⚝⚝ 量化:对DCT系数进行量化。这是有失真环节,通过调整量化步长控制压缩比和图像质量。人眼对高频分量不敏感,因此高频系数可以进行更粗糙的量化。
▮▮▮▮▮▮▮▮⚝⚝⚝ 熵编码:对量化后的系数进行无损编码,通常使用霍夫曼编码或算术编码(JPEG支持)。游程编码(RLE)也常用于对量化后产生的连续零值进行压缩。
▮▮▮▮⚝⚝ JPEG 2000:使用小波变换代替DCT,提供更好的压缩性能和可伸缩性。
② 音频压缩:
音频是一维时域信号,包含时域冗余、频域冗余和听觉冗余(人耳听不到或不敏感的声音)。
▮▮▮▮⚝ 无损音频压缩:
▮▮▮▮⚝⚝ FLAC (Free Lossless Audio Codec):使用线性预测(Linear Prediction)和变长编码等技术实现无损压缩。
▮▮▮▮⚝⚝ APE (Monkey's Audio):另一种流行的无损音频压缩格式。
▮▮▮▮⚝ 有失真音频压缩:
▮▮▮▮⚝⚝ MP3 (MPEG-1 Audio Layer III):经典的音频压缩格式。其核心是基于听觉心理学模型(Psychoacoustic Model)的感知编码。
▮▮▮▮▮▮▮▮⚝⚝⚝ 时频变换:将音频信号分解到不同的频率分量,通常使用改进离散余弦变换 (MDCT)。
▮▮▮▮▮▮▮▮⚝⚝⚝ 听觉心理学模型:分析信号,确定哪些频率分量是人耳听不到的(掩蔽效应),或者哪些可以进行更粗糙的量化而不被察觉。
▮▮▮▮▮▮▮▮⚝⚝⚝ 量化:根据听觉心理学模型的指导,对频率系数进行非均匀量化。
▮▮▮▮▮▮▮▮⚝⚝⚝ 熵编码:对量化后的系数和辅助信息进行霍夫曼编码或算术编码。
▮▮▮▮⚝⚝ AAC (Advanced Audio Coding):MP3的后继者,在更高压缩比下提供更好的音质,广泛应用于流媒体和移动设备。
▮▮▮▮⚝⚝ Opus:一种为互联网实时应用设计的音频编码格式,结合了语音编码和通用音频编码的特点。
③ 视频压缩:
视频是图像序列,包含空间冗余(单帧图像内的冗余)、时间冗余(相邻帧之间的相似性)和视觉冗余。视频压缩是信源编码最复杂的应用之一。
▮▮▮▮⚝ 视频编码标准(如MPEG系列、H.26x系列)通常采用混合编码框架,结合了多种技术:
▮▮▮▮⚝⚝ 帧内编码 (Intra-frame Coding):对视频帧内部进行压缩,类似于图像压缩(如使用预测、DCT、量化、熵编码)。
▮▮▮▮⚝⚝ 帧间编码 (Inter-frame Coding):利用时间冗余。通过运动估计(Motion Estimation)找到当前帧与参考帧(前一帧或后一帧)之间的运动向量,然后对运动补偿(Motion Compensation)后的残差信号进行编码。这利用了带边信息的思想,参考帧就是译码器已知的边信息。
▮▮▮▮⚝⚝ 变换:通常使用DCT或其变种对残差信号进行变换。
▮▮▮▮⚝⚝ 量化:对变换系数进行量化,控制失真和码率。
▮▮▮▮⚝⚝ 熵编码:对量化后的系数、运动向量、模式信息等进行无损编码,常用的有CABAC或CAVLC (Context-Adaptive Variable-Length Coding)。
④ 总结:
多媒体压缩是信源编码理论的集大成者。它综合运用了概率论、信息论、信号处理、感知心理学等多个领域的知识。无论是无损还是有失真压缩,其核心都是通过各种技术手段去除数据中的冗余和不相关性,并利用人类感知的特性,在可接受的失真范围内达到最大的压缩比。
10.4 信源编码的未来发展方向 (Future Directions of Source Coding)
信源编码领域一直在不断发展,以应对新的数据类型、应用场景和性能需求。以下是一些未来的发展方向:
① 面向特定应用和数据类型的编码:
▮▮▮▮⚝ 基因组数据压缩:基因序列数据量巨大,且具有独特的统计特性。开发高效的基因组数据压缩算法是生物信息学的重要需求。
▮▮▮▮⚝ 3D数据压缩:随着虚拟现实、增强现实和3D打印技术的发展,对3D模型、点云、体素数据等进行高效压缩变得越来越重要。
▮▮▮▮⚝ 传感器数据压缩:物联网设备产生海量传感器数据,通常具有时空相关性。需要在资源受限的设备上实现低复杂度、高效率的压缩。
▮▮▮▮⚝ 机器学习模型压缩:深度学习模型参数量巨大,压缩模型对于部署到边缘设备至关重要。这包括模型量化、剪枝、知识蒸馏等技术,与传统信源编码有交叉。
② 基于学习的编码 (Learning-Based Coding):
▮▮▮▮⚝ 利用深度学习技术设计端到端的压缩系统。神经网络可以学习数据的复杂分布和结构,可能超越传统手工设计的编码算法。
▮▮▮▮⚝ 例如,使用神经网络进行图像、视频的变换、量化和熵编码。这方面的研究正在快速发展,并已在一些最新的压缩标准中有所体现。
③ 分布式信源编码的进一步研究与应用:
▮▮▮▮⚝ 探索更高效、更鲁棒的 Slepian-Wolf 和 Wyner-Ziv 编码实现方法。
▮▮▮▮⚝ 将分布式编码应用于更复杂的网络场景,如多终端协作、边缘计算等。
④ 感知编码的深化:
▮▮▮▮⚝ 结合更先进的人类感知模型(视觉、听觉、触觉等),设计更符合人类感知特性的失真度量和编码策略,从而在相同感知质量下实现更高的压缩比。
▮▮▮▮⚝ 研究如何对主观质量进行更准确的评估和优化。
⑤ 安全与隐私保护的信源编码:
▮▮▮▮⚝ 在压缩数据的同时,如何保证数据的安全性和隐私性?例如,对加密数据进行压缩,或设计能够保护隐私的分布式编码方案。
⑥ 低复杂度与低延迟编码:
▮▮▮▮⚝ 对于实时通信和资源受限设备,需要开发计算复杂度低、编码/译码延迟小的压缩算法。
⑦ 通用性与适应性的提升:
▮▮▮▮⚝ 设计能够更好地适应各种信源特性,甚至能够处理信源特性剧烈变化的通用编码算法。
信源编码作为信息论的基石之一,其理论和技术将继续在数据爆炸时代发挥关键作用。未来的研究将更加注重跨学科融合,结合机器学习、感知科学、分布式系统等领域的最新进展,以应对日益多样化和复杂的压缩需求。
至此,我们已经完成了对信源编码基本原理的全面探索。希望本书能够为您构建坚实的理论基础,并激发您对信息论和数据压缩的兴趣。信源编码的世界充满挑战与机遇,期待您在这个领域取得更多的成就!