置信传播(Belief Propagation, BP)译码算法(公式推导+代码,超详细)

置信传播(Belief Propagation, BP)译码算法(公式推导+代码,超详细)

一、理论基础

此部分参考资料:

LDPC码(一种前向纠错码):基础 & 译码算法 - 知乎

1.1 概述

置信传播(Belief Propagation,BP)算法,在编码理论中又常被称为和积算法(Sum-Product Algorithm,SPA),是一种在概率图模型上进行统计推断的消息传递机制,其核心思想是通过在Tanner图变量节点(VN)与校验节点(CN)之间迭代传递置信度信息,逐步逼近最大后验概率(MAP)。

BP算法由朱迪亚·珀尔(Judea Pearl)于1982年提出,最初用于贝叶斯网络和马尔可夫随机场的概率推断,后被引入通信领域并发展为LDPC码的核心译码方法。但BP译码算法不是只能用于LDPC码,理论上可以用于任何拥有因子图(Factor Graph)表示的码型,包括所有的线性分组码,甚至卷积码(Turbo码)。

Turbo码:Turbo码的迭代译码本质上就是在两个分量码的因子图之间交换信息,属于BP算法的一个特例。Polar码(极化码):Polar码最经典的就是SC(串行消除)和SCL(串行消除列表)译码,但Polar码完全可以使用BP算法译码。Polar码的生成矩阵GN具有递归结构,可以展开成一个递归蝶形因子图,在此图上运行BP算法不仅可以实现并行译码(降低时延),还可以通过Soft-IC等手段进一步提升性能。PAC码(极化调整卷积码):PAC码在Polar码的极化编码之前加入了卷积预变换,虽然无法再用递归蝶形因子图完整表示整个过程,但其依然有确定的校验矩阵,理论上也可以使用BP译码算法,只是其校验矩阵的密度非常高,直接运行BP算法的性能会很差。卷积码:经典的BCJR算法其实就是定义在无环图(Trellis图/网格图)上的BP算法。RA码:这是一类结构化的类LDPC码,天然适合BP译码乘积码:常用于光通信和存储,其迭代硬判决或软判决译码也是BP思想的体现。

1.2 图论基础与结构化表示

前面提到,BP算法是一个通用的框架,理论上适用于所有可以用因子图表示的概率模型。在线性分组码中,通过校验矩阵H构建的Tanner图就是因子图的一个特例。下面以LDPC码为例。

1.2.1 线性分组码的矩阵定义

LDPC码属于线性分组码的范畴。一个 (n,k) 线性分组码将 k 比特的信息序列映射为 n 比特的码字序列,从而引入 n-k 个冗余比特用于纠错。该码由一个 m×n 的奇偶校验矩阵H唯一确定,其中,m=n-k 。对于任意合法码字 x = [x1,x2,...,xn],必须满足正交约束

H\cdot x^T=0\:(mod\:2)

这意味着码字中的比特必须满足 m 个线性校验方程。LDPC码的核心特征在于校验矩阵H是“低密度”的,即矩阵中绝大多数元素为“0”,只有极少数元素为“1”。通常,每行和每列中“1”的数量远小于 n 和 m 。这种稀疏性保证了BP算法的计算复杂度随码长线性增长(O(n)),而非指数增长。

1.2.2 Tanner图:二部图模型

1981年,Tanner提出用二部图(Bipartite Graph)来表示奇偶校验矩阵,这种图后来被称为Tanner图。Tanner图由两个不同的节点集合组成:变量节点(Variable Node, VN)和校验节点(Check Node, CN),这些节点通过边相互连接,以图形化方式展示了校验矩阵H的结构。因此,Tanner图实际上就是校验矩阵的可视化表示,是定义BP算法消息传递路径的拓扑基础。

例:给定一个码长为7,信息位长度为4的LDPC码的校验矩阵H

   

H = \begin{bmatrix} 1&0&0&1&1&1&0\\ 0&1&0&0&1&0&1\\ 0&0&1&0&0&1&1 \end{bmatrix}
H_{ij}=1

 表示第 j 个变量节点和第 i 个校验节点相连。

H

 的每一列对应一个变量节点。

H

 的每一行对应一个校验节点(也可以说对应一个校验方程)。

根据该校验矩阵就可以画出对应的可视化Tanner图:

只要建立了这个Tanner图,就可以在上面跑BP算法:变量节点向校验节点发信息,校验节点处理后再发回变量节点,如此迭代。

关于Tanner图还有几个比较重要的定义:

(1)边

Tanner图中的边仅连接变量节点和校验节点,不会连接同类节点。当且仅当校验矩阵元素 

h_{ij}=1

时,校验节点 

c_j

 与变量节点 

v_i

 之间存在一条边。

(2)度分布(Degree Distribution)

校验节点度(

d_c

):与校验节点相连的边的数量,等于 

H

 对应行的汉明重。它表示该方程约束了多少个比特

变量节点度(

d_v

):与变量节点相连的边的数量,等于 

H

 对应列的汉明重。它表示该比特参与了多少个校验方程

如果图中所有变量节点的度相同,且所有校验节点的度也相同,则该码称为规则LDPC码。如果度数不统一,则称为非规则LDPC码。研究表明,设计良好的非规则LDPC码通常具有比规则码更优异的瀑布区性能,因为高度数的变量节点能更早收敛,从而辅助低度数节点译码。

(2)环(Cycles)与围长(Girth)

  • 环:从一个节点出发,经过若干条不同的边回到该节点的闭合路径。环的长度是指路径上边的数量。由于二部图的性质,环长必然是偶数(4、6、8...)。
  • 围长:图中长度最短的环的长度。

在无环图(树)中,BP算法可以精确收敛到最大后验概率。然而,为了获得良好的距离特性,实用的LDPC码必然包含环。短环(特别是长为4的环)会破坏BP算法的独立性假设,导致信息错误在局部循环并被放大,引发译码失效或震荡。因此构造LDPC码的一个重要目标就是消除短环,最大化围长。

1.3 消息传递机制

在开始公式推导之前,先回答两个问题更有利于对后面公式的理解:在Tanner图上传播的消息具体是什么含义?为什么BP算法是有效的?

(1)传播消息的具体含义

在实际工程实现中,为了简化乘法运算为加法运算,通常在对数域进行计算,传播的消息是对数似然比(Log-Likelihood Ratio, LLR),定义为:

L(x)=ln \frac{P(x_i=0|y_i)}{P(x_i=1|y_i)}
  • 如果 L(x) > 0,表示该比特更倾向为0。
  • 如果 L(x) < 0,表示该比特更倾向为1。
  • | L(x) | 的大小表示置信度(绝对值越大,越确定)。

A. 变量节点 -> 校验节点(v -> c)

  • 含义:根据信道给我的初始信息,以及除了你(该校验节点c)之外的其他校验节点告诉我的信息,我认为我自己是0或1的可能性是多少。

B. 校验节点 -> 变量节点(c -> v)

  • 含义:为了满足我的校验方程(即所有连接到我的变量模2和必须为0),根据除了你(该变量节点v)之外的其他变量节点目前的状态,我推断你应该取值的可能性是多少。
  • 两个维度
    • 方向(逻辑推断):如果其他变量节点的模2和是0(或1),你(该变量节点v)也应该是0(或1),才能满足整个校验方程的模2和为0,决定了LLR的正负号。
    • 强度(置信度):表示我推断你(该变量节点v)是0或1的这个消息有多确定,决定了LLR绝对值的大小。校验节点传出的置信度受限于与之相连的绝对值最小(最不可靠)的那个变量节点,这里用“木桶效应”比较好理解,因为只要有一个变量节点的消息不靠谱(例如L(x)=0.01),那么整个推断就是不靠谱的。 

(2)BP算法的有效性

  • 局部交互达成全局共识:BP译码的核心思想是将一个复杂的全局概率计算问题,分解为无数个简单的局部计算问题。每一个节点并不需要知道整个图的结构,它只与它的“邻居”进行通信。变量节点负责收集关于该比特概率的信息,校验节点负责检查这些概率是否符合奇偶校验约束。这种通信是双向迭代的。随着迭代次数增加,信息在图中扩散,原本不确定的节点(受噪声严重干扰的比特)会通过其他节点的强有力信息逐渐被纠正,最终整个网络达成共识。
  • 利用了稀疏性:BP算法之所以能成为LDPC码的核心译码方法,就是因为LDPC码的校验矩阵是非常稀疏的,这意味着每个变量只参与少数几个校验,每个校验只关联少数几个变量,围长大,消息能够有效传播,且这种稀疏性保证了复杂度随码长呈线性增长(而非最大似然译码的指数级增长)。
  • 外信息的交换:这是BP算法的精髓,当节点A向节点B传递消息时,不会把B刚才告诉的它的消息再传回B,这防止了信息在两个节点之间死循环,保证了每次迭代传递的都是“新鲜”的独立证据。

总而言之,BP译码算法可以看成是一个迭代的“投票”过程。每个比特最初只有信道的模糊观测,通过不断的变量节点(汇总意见)和校验节点(施加约束)之间的信息传播,消除不确定性,知道所有校验方程都被满足。

二、详细公式推导

此部分参考资料:

LDPC译码原理(公式推导)及其matlab代码实现(超详细)-ZEEKLOG博客

高斯白噪声信道下对数似然比的推导--- QPSK+BPSK - 哔哩哔哩

高斯白噪声信道下后验概率的计算 - 哔哩哔哩

LDPC码最小和译码优化及调度算法的研究 - 中国知网

基于深度学习的LDPC码译码算法研究 - 中国知网

卫星通信中LDPC译码器的研究与实现 - 中国知网

2.1 引理1

后面的推导需要用到引理1:给定一个长为

m

 的相互独立的二进制序列 

a = [a_1,a_2,...,a_m]

 ,第

h

 个比特为 1 的概率为

P(a_h)=p_h

 ,则二进制序列 a 中包含偶数个 1 的概率为:

 

\frac{1}{2}+\frac{1}{2}\prod^m_{h=1}(1-2p_h)

 

包含奇数个 1 的概率为:

\frac{1}{2}-\frac{1}{2}\prod^m_{h=1}(1-2p_h)

证明如下:

采用构造函数和数学归纳法进行证明。

首先构造关于

t

 的

m

 次多项式:

f(t)=\prod^m_{h=1}(1-p_h+p_ht)

通过数学归纳法证明 

t^i

 的系数为序列中包含

i

 个 1 的概率。

(1)当 

m=1

 时:

f(t)=1-p_1+p_1t

显然 

t^1

 的系数 

p_1

 为序列中包含 1 个 1 的概率,

t^0

的系数 

1-p_1

 为序列中包含 0 个 1 的概率。

(2)假设当 

m=k

 时,

t

 的 

k

 次多项式为:

f(t)=\prod^k_{h=1}(1-p_h+p_ht)=\sum^k_{h=0}B_ht^h
t^i

的系数 

B_i

 为其序列中包含 

i

 个 1 的概率。

(3)当 

m=k+1

 时,

t

 的 

k+1

 次多项式 为:

f(t)=\prod^{k+1}_{h=1}(1-p_h+p_ht)\\ \\=(1-p_{k+1}+p_{k+1}t)\prod^k_{h=1}(1-p_h+p_ht)\\ \\=(1-p_{k+1}+p_{k+1}t)\sum^k_{h=0}B_ht^h

由上式可求得 

t^i

 的系数为 

(1-p_{k+1})B_i+p_{k+1}B_{i-1}

,其中:

1-p_{k+1}

 表示第 

k+1

 个比特为 0 的概率;

B_i

 表示 

m = k

 的序列中有 

i

 个 1 的概率;

p_{k+1}

 表示第 

k+1

 个比特为 1 的概率;

B_{i-1}

 表示 

m=k

 的序列中有 

i-1

 个 1 的概率;

因此,当 

m=k+1

 时,

t^i

 的系数 

(1-p_{k+1})B_i+p_{k+1}B_{i-1}

 也表示序列中包含 

i

 个 1 的概率。综上,通过数学归纳法证明了 

t^i

 的系数为序列中包含

i

 个 1 的概率。

在此基础上,再求序列中包含偶数个 1 的概率。

即需要求 m 次多项式中所有偶数次幂项的系数之和 

B_0+B_2+B_4+...

 ,为此可以再构造一个函数 

g(t)=\prod^m_{h=1}(1-p_h-p_ht)

 ,则 

f(t)+g(t)

 会消掉所有奇数次幂项,得到所有偶数次幂项和的两倍:

f(t)+g(t)=2(B_0t^0+B_2t^2+B_4t^4+...)

再令 

t=1

 ,得到序列中包含偶数个 1 的概率 

P_{even}

 :

P_{even}=B_0+B_2+B_4+...\\ \\=\frac{f(1)+g(1)}{2}\\ \\=\frac{1}{2}+\frac{1}{2}\prod^m_{h=1}(1-2p_h)

则序列中包含奇数个 1 的概率 

P_{odd}

 :

P_{odd}=1-P_{even}=\frac{1}{2}-\frac{1}{2}\prod^m_{h=1}(1-2p_h)

2.2 前置知识

在介绍具体的译码算法之前还有一点前置知识,需要先搞清楚整个通信链路。发送端的原始编码码字为

x

 ,经过调制得到发送信号

s

 ,再通过AWGN信道传输(引入高斯白噪声 

n

 ),接收端收到的信号

y=s+n

 ,其中,噪声 

n

 服从高斯分布。译码就是通过接收信号

y

 推断原始编码码字

x

 的过程,推断的依据为原始编码码字 

x

 是满足校验矩阵的,如果译码结果 

\hat x

 也满足相同的校验矩阵(即

H\cdot \hat x^T=0

 ),我们就认为找到了最可能的原始编码码字。

2.3 概率域BP译码算法

符号说明:

两个概念:

  • 外信息原则:不将节点A告诉我的信息回传给A,以防止信息自我增强(正反馈)导致的过早收敛和偏差。

后验概率:通信系统中,我们关心的是在收到信号 y 的情况下,判断发送方发送的比特是 0 或 1 的概率,可以表示成 

p(b=0|y)

 和 

p(b=1|y)

 ,一般被称为后验概率。

(1)初始化

对于每个变量节点

v_i

 ,根据信道接收值

y_i

 计算初始后验概率,这是算法的输入,代表了仅由信道观测提供的信息,也是变量节点传递给校验节点的初始信息:

\begin{cases}q^{(0)}_{ij}(0)=P_i(0)=p(x_i=0|y_i)\\q^{(0)}_{ij}(1)=P_i(1)=p(x_i=1|y_i)\end{cases}

(2)校验节点更新(校验节点 

c_j

 传递给变量节点 

v_i

 的外信息)

校验节点 

c_j

 的任务是根据连接到它的变量节点(除变量节点 

v_i

 )的信息,计算它对变量节点 

v_i

 的约束意见:

\begin{cases}r_{ji}^{(l)}(0)=\frac{1}{2}+\frac{1}{2} \displaystyle\prod_{i'\in V(j)\diagdown i}(1-2q_{i'j}^{(l-1)}(1)) \\ \\r_{ji}^{(l)}(1)=\frac{1}{2}-\frac{1}{2} \displaystyle\prod_{i'\in V(j)\diagdown i}(1-2q_{i'j}^{(l-1)}(1))\end{cases}

理解含义:

r^{(l)}_{ji}(0)

 表示在第 

l

 次迭代中,校验节点 

c_j

 推断变量节点 

v_i

 为 0 的概率,等价于除开该变量节点 

v_i

 外的其他变量节点的模2和应该为0的概率,这样才能满足连接到该校验节点 

c_j

 的所有变量节点的模2和为0的校验约束。也就等价于求其他变量节点中包含偶数个 1 的概率,即前面已经证明过的引理1。

q^{(l-1)}_{i'j}(1)

表示在第 

l

 次迭代中除 

v_i

 外的其他变量节点为 1 的概率,由第 

l-1

 次迭代时变量节点 

i'

 传递给校验节点 

j

 的外信息确定。

r^{(l)}_{ji}(1)

 的含义同理可得。

(3)变量节点更新(变量节点 

v_i

 传递给校验节点 

c_j

 的外信息)

变量节点 

v_i

 的任务是综合来自信道的初始信息和连接到它的校验节点(除校验节点 

c_j

 )传来的外信息。

\begin{cases}q_{ij}^{(l)}(0)=K_{ij}q_{ij}^{(0)}(0)\displaystyle\prod_{j'\in C(i)\diagdown j}r_{j'i}^{(l)}(0)\\ \\q_{ij}^{(l)}(1)=K_{ij}q_{ij}^{(0)}(1)\displaystyle\prod_{j'\in C(i)\diagdown j}r_{j'i}^{(l)}(1)\end{cases}

其中的 

K_{ij}

 是校正因子,使每次计算出的 

q_{ij}^{(l)}(1)+q_{ij}^{(l)}(0)=1

 。

理解含义:

q^{(l)}_{ij}(0)

 表示在第

l

 次迭代中,变量节点 

v_i

 为 0 的概率,这个概率包括初始为0的概率、除开该校验节点

c_j

 外的其他校验节点推测该变量节点 

v_i

 为 0 的概率,这些概率值相乘得到了第 

l

 次迭代中 

v_i=0

 的概率。

q^{(l)}_{ij}(1)

 的含义同理可得。

(4)译码与终止条件

在每次迭代结束时,计算每个变量节点的总后验LLR,这里包含了所有校验节点的信息:

\begin{cases}q_{i}^{(l)}(0)=K_iP_i(0)\displaystyle\prod_{j\in C(i)}r_{ji}^{(l)}(0)\\q_i^{(l)}(1)=K_iP_i(1)\displaystyle\prod_{j\in C(i)}r_{ji}^{(l)}(1)\end{cases}

其中的 

K_i

 是校正因子,目的是使 

q_i^{(l)}(0)+q_i^{(l)}(1)=1

 。

    • 若达到最大次数仍未满足校验条件,则宣布译码失败。

若 

s\neq 0

 且未达到最大迭代次数,则回到步骤(2)继续下一轮迭代。

若 

s=0

 ,说明所有校验方程满足,译码成功,提前终止。

校验:计算 

s = H\cdot \hat x^T

硬判决:若 

q_i^{(l)}(0)>q_i^{(l)}(1)

 ,则判

\hat x_i=0

 ,否则判

\hat x_i=1

 。

2.4 对数域BP译码算法

前面介绍了概率域上的BP译码算法,但在实际工程实现中,并不会使用概率域BP译码算法,因为其包含大量的连乘运算,计算复杂度非常高。常见的做法是将概率信息用对数似然比表示,得到对数域上的BP译码算法,大量的连乘运算可以转化为加法运算。

2.4.1 标准和积算法(Sum-Product Algorithm, SPA)

和积算法是BP算法的标准实现(不包含任何近似),被公认为性能最优的软判决迭代算法。

(1)初始化

同样的,第一步是初始化算法的输入。对于每个变量节点 

v_i

 ,根据信道接收值 

y_i

 计算初始信道LLR,即变量节点发送给校验节点的初始信息。对于AWGN信道、BPSK调制:

LLR(P_i)=\ln\frac{P_i(0)}{P_i(1)}=\ln\frac{P(x_i=0\mid y_i)}{P(x_i=1\mid y_i)}=\frac{2y_i}{\sigma^2}

下面对该公式进行详细推导:

  • 对数似然比(LLR)的定义

如前所述,根据接收信号

y

判断发送端发送的比特是 0 或 1 的概率可以表示为后验概率

P(x=0|y)

P(x=1|y)

 。一般情况下,我们更关心两者的比值,因为如果

P(x=0|y)

大于

P(x=1|y)

,我们就有有理由判断

x = 0

 ,否则,我们更倾向于判断

x =1

\left\{\begin{matrix} \frac{P(x=0|y)}{P(x=1|y)}>1\quad x=0 \\ \\ \frac{P(x=0)|y)}{P(x=1|y)}<1\quad x=1 \end{matrix}\right.

为了简化运算,一般还要再取对数,这就是对数似然比的定义式

LLR(y)=ln\frac{P(x=0|y)}{P(x=1|y)}

判决准则相应地变为:

\left\{\begin{matrix} ln\frac{P(x=0|y)}{P(x=1|y)}>0\quad x=0 \\ \\ ln\frac{P(x=0)|y)}{P(x=1|y)}<0\quad x=1 \end{matrix}\right.
  • 信道初始LLR的计算

得到了LLR的定义式后,却不能直接根据该定义式进行计算,原因是后验概率

P(x|y)

只是一个离散分布(即在接收端收到某一个具体的

y

值后,

x

只可能是 0 或者 1),我们无法写出它的函数解析式。在2.2节中提到过,编码码字

x

经过调制得到发送信号

s

x

s

是一一映射的,在BPSK中,0 一般映射为 +1 ,1 一般映射为 -1 ),

s

经过AWGN信道传输(加入高斯白噪声 

n

)得到接收信号

y = s+n

 ,因为噪声 

n

是服从高斯分布的,所以接收信号

y

也服从高斯分布。例如,发送端发送了

x = 0

(即

s = +1

),

y = 1+n

也依然服从高斯分布,因为给定

x

的情况下,

s

是一个常数

噪声 

n

 服从均值为 0 ,方差为 

\sigma^2

 的高斯分布,可表示为 

n \sim \mathcal{N}(0, \sigma^2)

,满足以下概率密度函数:

P(n) = \frac{1}{\sqrt{2\pi}\sigma} \exp\left( -\frac{n^2}{2\sigma^2} \right)

代入

y = s+n

,这个式子可以写成:

P(y-s) = \frac{1}{\sqrt{2\pi}\sigma} \exp\left( -\frac{(y-s)^2}{2\sigma^2} \right)

进一步,

P(y-s)

 等价于 

P(y|x)

 ,所以式子最终可以写成:

P(y|x) = \frac{1}{\sqrt{2\pi}\sigma} \exp\left( -\frac{(y-s)^2}{2\sigma^2} \right)

这里的逻辑有点绕,但其实很好理解,因为 

P(n)

 和 

P(y|x)

 描述的是同一件事情。

P(n)

 表示噪声取某个值的概率,

P(y|x)

 表示已知 

x

(等价于已知 

s

 ,因为它们是一一映射的)的条件下,

y

取某个值的概率,因为当 

x

 已知时,

s

 是一个固定的常数,

y

的值就完全由

n

决定,所以这两个表达式是完全等价的。还是前面的例子,发送端发送了

x = 0

(即

s = +1

),

y = 1+n

,这个时候求

n

取某个值的概率,也就是求

s = 1

时,

y

取某个值的概率。

P(y|x)

 被称为似然概率,根据上面的式子,它是服从高斯分布的。

还记得前面说的,后验概率 

P(x|y)

 是一个离散分布,无法写出它的函数解析式。因此,我们可以利用贝叶斯公式,将其转化为服从高斯分布的似然概率 

P(y|x)

 与先验概率 

P(x)

 的表达式:

P(x|y)=\frac{P(y|x)\cdot P(x)}{P(y)}

到这里,就可以对LLR进行计算了:

LLR(y)=ln\frac{P(x=0|y)}{P(x=1|y)}=ln\frac{P(y|x=0)\cdot P(x=0)}{P(y|x=1)\cdot P(x=1)}=ln\frac{P(y|x=0)}{P(y|x=1)}+ln\frac{P(x=0)}{P(x=1)}

这里分成了两部分:

一是信道信息 

ln\frac{P(y|x=0)}{P(y|x=1)}

 ,代入前面求的概率密度函数:

\begin{aligned} LLR(y) &= \ln \frac{\exp\left( -\frac{(y-1)^2}{2\sigma^2} \right)}{\exp\left( -\frac{(y+1)^2}{2\sigma^2} \right)} &= -\frac{(y-1)^2}{2\sigma^2} - \left( -\frac{(y+1)^2}{2\sigma^2} \right) &= \frac{2y}{\sigma^2} \end{aligned}

注意,这里

(y-s)^2

 中代入的

s

 值需要看BPSK调制的具体映射关系,这里是 0->+1,1->-1,如果是更高阶的调制,整个表达式会发生变化,求解更加复杂。具体可以看这位博主的文章:高斯白噪声信道下对数似然比的推导--- QPSK+BPSK - 哔哩哔哩

二是先验信息 

L_a = \ln \frac{P(x=0)}{P(x=1)}

 ,一般情况下发送比特是0或1的概率相等,因此 

L_a

 通常为0。

因此,信道初始LLR的计算式最终可以化简为:

LLR(y)=\frac{2y}{\sigma^2}

这是对数域译码算法中信道初始化的常用式子,基本上是作为一个结论使用的,少有详细推导。其中,

\sigma^2

 是噪声方差,它的求法也有一个推导过程,这里就不赘述了,以后有机会再补上。

(2)校验节点更新(校验节点 

c_j

 传递给变量节点 

v_i

 的外信息)

根据前面的定义,对数域BP译码算法的校验节点更新公式为:

r_{ji}=\ln\frac{r_{ji}(0)}{r_{ji}(1)} \\ \\=\ln\frac{\frac{1}{2}+\frac{1}{2}\displaystyle\prod_{i'\in V(j)\diagdown i}(1-2q_{i'j}(1))}{\frac{1}{2}-\frac{1}{2}\displaystyle\prod_{i'\in V(j)\diagdown i}(1-2q_{i'j}(1))}\\ \\=ln\frac{1+\displaystyle\prod_{i'\in V(j)\diagdown i}(1-2q_{i'j}(1))}{1-\displaystyle\prod_{i'\in V(j)\diagdown i}(1-2q_{i'j}(1))}

在数学中,双曲正切函数 

tanh

 有以下两个公式成立:

tanh(x)=\frac{e^x-e^{-x}}{e^x+e^{-x}}\quad\quad tanh^{-1}(x)=\frac{1}{2}ln\frac{1+x}{1-x}

且有以下几个关系式成立:

1-2q_{i'j}(1)=q_{i'j}(0)-q_{i'j}(1)\\ \\q_{i'j}(0)+q_{i'j}(1)=1\\ \\q_{i'j}=ln\frac{q_{ij}(0)}{q_{ij}(1)}

可以利用这几个公式对 

r_{ji}

 进行化简:

r_{ji}=2tanh^{-1}(\displaystyle\prod_{i'\in V(j)\diagdown i}(1-2q_{i'j}(1)))\\=2tanh^{-1}(\displaystyle\prod_{i'\in V(j)\diagdown i}(q_{i'j}(0)-q_{i'j}(1)))\\=2tanh^{-1}(\displaystyle\prod_{i'\in V(j)\diagdown i}(\frac{q_{i'j}(0)-q_{i'j}(1)}{q_{i'j}(0)+q_{i'j}(1)}))\\=2tanh^{-1}(\displaystyle\prod_{i'\in V(j)\diagdown i}(\frac{\frac{q_{i'j}(0)}{q_{i'j}(1)}-1}{\frac{q_{i'j}(0)}{q_{i'j}(1)}+1}))\\ \\=2tanh^{-1}(\displaystyle\prod_{i'\in V(j)\diagdown i}(\frac{e^{q_{i'j}}-1}{e^{q_{i'j}}+1}))\\ =2tanh^{-1}(\displaystyle\prod_{i'\in V(j)\diagdown i}(\frac{e^{\frac{q_{i'j}}{2}}(e^{\frac{q_{i'j}}{2}}-e^{-\frac{q_{i'j}}{2}})}{e^{\frac{q_{i'j}}{2}}(e^{\frac{q_{i'j}}{2}}+e^{-\frac{q_{i'j}}{2}})}))\\ \\=2tanh^{-1}(\displaystyle\prod_{i'\in V(j)\diagdown i}tanh\frac{q_{i'j}}{2})

故校验节点更新的公式最终可以化简为:

r_{ji}=2tanh^{-1}(\displaystyle \prod_{i'\in V(j)\setminus i}tanh\frac{q_{i'j}}{2})

 

这是SPA的标准公式。由于 tanh 的乘积计算复杂,该标准公式还有另外一种表达形式,可以使用 

\Phi

 函数将乘积转换为加法:

r_{ji}=\displaystyle\prod_{i'\in V(j)\setminus i}sign(q_{i'j})\enspace \cdot \enspace \Phi^{-1}(\displaystyle\sum_{i'\in V(j)\setminus i}\Phi(|q_{i'j}|))\\ \\ =sign(r_{ji})\: \cdot \: |r_{ji}|
r_{ji}=\displaystyle\prod_{i'\in V(j)\setminus i}sign(q_{i'j})

 是符号部分 

sign(r_{ji})

 。

\Phi^{-1}(\displaystyle\sum_{i'\in V(j)\setminus i}\Phi(|q_{i'j}|))

 是幅度部分 

|r_{ji}|

 。

符号部分表示校验节点 

c_j

 推测变量节点 

v_i

 是 0 或 1(方向),由输入消息的符号异或决定;

幅度部分表示校验节点 

c_j

 推测变量节点 

v_i

 是 0 或 1 的确定性(强度)。

\Phi

 函数定义为:

\Phi(x)=-ln(tanh(\frac{x}{2}))=ln(coth(\frac{x}{2}))=ln(\frac{e^x+1}{e^x-1})
\Phi(x)

 是自逆函数,即 

\Phi(\Phi(x))=x

 ;同时,

\Phi(x)

 是单调递减函数且为正值(当

x>0

 ),当 

x\rightarrow 0^{+}

 时,

\Phi(x)\rightarrow +\infty

 ,当 

x\rightarrow +\infty

 时,

\Phi(x)\rightarrow 0^{+}

 。

(3)变量节点更新(变量节点 

v_i

 传递给校验节点 

c_j

 的外信息)

变量节点更新的操作在LLR域即为简单的加法:

q_{ij}=ln\frac{P_i(0)\displaystyle \prod_{j' \in C(i) \setminus j}r_{j'i}(0)}{P_i(1)\displaystyle \prod_{j'\in C(i) \setminus j}r_{j'i}(1)}=LLR(P_i)+\displaystyle \sum_{j'\in C(i)\setminus j}r_{j'i}

(4)译码与终止条件

在每次迭代结束时,计算每个变量节点的总后验LLR:

q_i=LLR(P_i)+\displaystyle \sum_{j'\in C(i)} r_{j'i}

这里包含了所有校验节点的信息。

    • 若达到最大次数仍未满足校验条件,则宣布译码失败。

若 

s\neq 0

 且未达到最大迭代次数,则回到步骤(2)继续下一轮迭代。

若 

s=0

 ,说明所有校验方程满足,译码成功,提前终止。

校验:计算 

s = H\cdot \hat x^T

硬判决:若

q_i>0

 ,则判

\hat x_i=0

 ,否则判

\hat x_i=1

 。

2.4.2 最小和算法(Min-Sum Algorithm, MSA)

虽然SPA提供了最优的误码率性能,但 tanh函数(或 

\Phi

 函数)作为非线性运算在FPGA或ASIC实现中不仅占用大量面积,还容易受到定点数量化误差的影响。为了解决这一问题,工程界广泛采用基于”最小近似“的简化算法,主要改进了校验节点更新的公式,推导如下:

函数 

\Phi(x)=-ln(tanh(x/2))

 中,当

x

 较大时,

\Phi(x)

 会迅速衰减。因此,在求和 

\sum \Phi(|L|)

 时,主要的贡献来自于数值最大的 

\Phi(|L|)

 项,而 

\Phi(x)

 是单调递减函数,这意味着最大的 

\Phi(|L|)

 值对应最小的输入幅度 

|L|

 。因此,我们可以用最小值来近似整个非线性运算:

\Phi^{-1}(\sum \Phi(|L_k|))\approx min(|L_k|)

MSA校验节点更新公式

r_{ji}=\left( \displaystyle\prod_{i'\in V(j) \setminus i}sgn(q_{i'j})\right) \enspace \cdot \enspace \displaystyle \min_{i'\in V(j) \setminus i}(|q_{i'j}|)

即符号部分不变,对幅值部分进行了近似操作。

这个近似结果是偏大的。如前所述,

\Phi(x)

 是一个减函数且为正值(

x>0

 ),因为 

\Phi(|L_k|)

 都是正数,所以 

\sum \Phi(|L_k|)

 一定大于等于其中最大的那一项 

\Phi(min(|L_k|))

 ,又因为 

\Phi^{-1}(x)

 也是减函数,因此  

\Phi^{-1}(\sum \Phi(|L_k|)) < \Phi^{-1}(\Phi(min(|L_k|)))=min(|L_k|)

 。 

特性分析:

  • 硬件优势:复杂的双曲函数被简化为“比较”和“符号异或”操作。这极大地降低了逻辑门数量,使得高速并行实现成为可能。

性能损失:由于 

min(|L|)

 实际上总是大于等于真实的 

\Phi^{-1}(\sum\Phi)

 值,MSA 倾向于高估校验节点输出的可靠性(LLR幅度偏大)。这种过估计会导致性能下降,通常在高斯信道下比SPA差约0.5dB到1dB。

2.4.3 修正最小和算法

1、归一化最小和算法(Scaled Min-Sum Algorithm, NMSA)

为了修正MSA的幅度过估计问题,NMSA引入了一个归一化因子(缩放因子)

\alpha

 (通常 

0< \alpha <1

 )。

NMSA的校验节点更新公式修正为:

r_{ji}=\left( \displaystyle\prod_{i'\in V(j) \setminus i}sgn(q_{i'j})\right) \enspace \cdot \enspace \displaystyle \alpha \cdot \min_{i'\in V(j) \setminus i}(|q_{i'j}|)
  • 优势:NMSA保留了MSA的线性特性,仅增加了一个乘法(可以通过移位加法实现),却能恢复大部分因近似丢掉的性能。它是目前5G NR、DVB-T2/S2和Wi-Fi标准中LDPC译码器的主流选择。 

参数选择:

\alpha

 的最优值取决于码率、度分布和信噪比,通常通过密度进化理论计算或蒙特卡洛仿真搜索得到。对于大多数规则码,

\alpha \approx 0.75 \sim 0.8

 是常用值。

2、偏移最小和算法(Offset Min-Sum Algorithm, OMSA)

OMSA采用减法偏移来降低LLR幅度,修正过估计。

OMSA的校验节点更新公式修正为:

r_{ji}=\left( \displaystyle\prod_{i'\in V(j) \setminus i}sgn(q_{i'j})\right)\cdot max(\displaystyle\min_{i'\in V(j) \setminus i}(|q_{i'j}|)-\beta, 0)

其中,

\beta

 是非负偏移量。

  • 优势:在硬件中,减法比乘法更容易实现(尤其对于定点数)。此外,OMSA在某些高信噪比场景下对错误平层的抑制效果略优于NMSA。

三、Matlab代码

3.1 SPA

基础版本严格对照译码流程,逻辑清晰,但包含大量for循环,效率较低,适合用来理解算法。

function [decoded_bits, iter] = SPA_BP(H, llr, max_iterations) % 实现SPA-BP译码算法 % 输入: % H - 校验矩阵 % llr - 接收到的LLR值 % max_iterations - 最大迭代次数 % 输出: % decoded_bits - 译码后的比特序列 % 获取校验矩阵尺寸 [m, n] = size(H); % 初始化变量节点到校验节点的消息 % v2c[i,j]表示第i个变量节点到第j个校验节点的消息 v2c = zeros(n, m); % 初始化校验节点到变量节点的消息 % c2v[j,i]表示第j个校验节点到第i个变量节点的消息 c2v = zeros(m, n); % 初始化变量节点的后验LLR值 posterior_llr = llr; % 创建校验节点和变量节点的连接列表,避免遍历整个矩阵 check_nodes = cell(m, 1); variable_nodes = cell(n, 1); for i = 1:m check_nodes{i} = find(H(i, :) == 1); end for j = 1:n variable_nodes{j} = find(H(:, j) == 1); end % BP迭代过程 for iter = 1:max_iterations % 校验节点到变量节点的消息更新 for i = 1:m connected_vars = check_nodes{i}; for j_idx = 1:length(connected_vars) j = connected_vars(j_idx); % 校验节点i到变量节点j的消息更新使用tanh-product规则 prod_tanh = 1; for k_idx = 1:length(connected_vars) k = connected_vars(k_idx); if k ~= j % 防止数值不稳定 v2c_clamped = max(min(v2c(k, i), 30), -30); prod_tanh = prod_tanh * tanh(v2c_clamped / 2); end end % 防止数值不稳定(避免log(0)) if abs(prod_tanh) > 0.9999 prod_tanh = sign(prod_tanh) * 0.9999; end c2v(i, j) = 2 * atanh(prod_tanh); % 限制消息的数值范围,防止数值溢出 c2v(i, j) = max(min(c2v(i, j), 30), -30); end end % 变量节点到校验节点的消息更新 for j = 1:n connected_checks = variable_nodes{j}; for i_idx = 1:length(connected_checks) i = connected_checks(i_idx); % 变量节点j到校验节点i的消息 = % 原始LLR + 所有其他连接的校验节点到该变量节点的消息 v2c(j, i) = llr(j); for k_idx = 1:length(connected_checks) k = connected_checks(k_idx); if k ~= i v2c(j, i) = v2c(j, i) + c2v(k, j); end end end end % 计算每个变量节点的后验LLR值 for j = 1:n posterior_llr(j) = llr(j); connected_checks = variable_nodes{j}; for i_idx = 1:length(connected_checks) i = connected_checks(i_idx); posterior_llr(j) = posterior_llr(j) + c2v(i, j); end end % 硬判决 decoded_bits = double(posterior_llr < 0); % 校验是否满足所有校验方程 syndromes = mod(H * decoded_bits', 2); if sum(syndromes) == 0 % 所有校验方程都满足,提前结束迭代 break; end end end

优化版本利用了Matlab擅长矩阵运算的特点,去掉繁琐的for循环,运行速度可以提升10倍左右

function [decoded_bits, iter] = SPA_BP_Optimized(H, llr, max_iterations) % SPA_BP_Optimized 实现矢量化BP译码算法 [m, n] = size(H); % 预处理:将H转换为逻辑矩阵,用于快速掩膜(Masking) H_mask = logical(H); % 初始化消息矩阵 (全部使用 m x n 维度) % V2C: 变量节点到校验节点 (初始为LLR值,仅在H_mask位置有效) V2C = repmat(llr, m, 1) .* H_mask; % C2V: 校验节点到变量节点 C2V = zeros(m, n); % 数值稳定性常数 SMALL_VAL = 1e-15; % 防止log(0) LARGE_VAL = 30; % LLR截断阈值,防止溢出 for iter = 1:max_iterations % ============================== % 1. 校验节点更新 (Horizontal Step) % ============================== % 目标:计算 2 * atanh( prod( tanh(V2C/2) \ i ) ) % 优化策略: 使用 φ 函数技巧,将乘积转为求和,并将符号和数值分离处理 % A. 处理符号 (Sign) Sign_V2C = sign(V2C); Sign_V2C(V2C == 0) = 1; % 避免0符号问题 Sign_V2C(~H_mask) = 1; % 非连接处设为1,不影响乘积 % 计算每行的总奇偶校验位 Total_Sign = prod(Sign_V2C, 2); % 排除自身的符号 (通过除法或异或,这里用乘法因为是1/-1) C2V_Sign = Total_Sign .* Sign_V2C; % B. 处理数值 (Magnitude) - 使用 -log(tanh(|x|/2)) 变换 Abs_V2C = abs(V2C); % 避免数值计算错误 log(tanh(0))=Inf,对极小值进行裁剪 Abs_V2C(Abs_V2C < SMALL_VAL) = SMALL_VAL; % 核心变换函数 phi(x) Phi_V2C = -log(tanh(Abs_V2C / 2)); Phi_V2C(~H_mask) = 0; % 非连接处设为0,不影响求和 % 行求和 Total_Phi = sum(Phi_V2C, 2); % 排除自身贡献 Phi_External = Total_Phi - Phi_V2C; % 逆变换回去 (函数形式相同) % C2V_Mag = -log(tanh(Phi_External / 2)); % 同样对极小值进行裁剪 Phi_External(Phi_External < SMALL_VAL) = SMALL_VAL; C2V_Mag = -log(tanh(Phi_External / 2)); % C. 组合符号和数值,并进行范围截断 C2V = C2V_Sign .* C2V_Mag; C2V = max(min(C2V, LARGE_VAL), -LARGE_VAL); % 应用掩膜,确保非连接处为0 C2V = C2V .* H_mask; % ============================== % 2. 变量节点更新 (Vertical Step) % ============================== % 目标: LLR_post = LLR_intrinsic + sum(C2V) % V2C_new = LLR_post - C2V_old % 计算后验 LLR (列求和) Sum_C2V = sum(C2V, 1); Posterior_LLR = llr + Sum_C2V; % 硬判决 decoded_bits = double(Posterior_LLR < 0); % 校验是否满足 H * x = 0 % 注意:Matlab中 dense matrix * dense vector 很快 syndrome = mod(H * decoded_bits', 2); if all(syndrome == 0) return; end % 更新 V2C 消息 (总和 - 来自该校验节点的消息) V2C = repmat(Posterior_LLR, m, 1) - C2V; % 再次截断并掩膜 V2C = max(min(V2C, LARGE_VAL), -LARGE_VAL); V2C = V2C .* H_mask; end end

3.2 MSA、NMSA、OMSA

MSA基础版本,将校验节点更新的部分相应改为:

% 校验节点到变量节点的消息更新 for i = 1:m connected_vars = check_nodes{i}; for j_idx = 1:length(connected_vars) j = connected_vars(j_idx); % 校验节点i到变量节点j的消息更新使用Min-sum规则 min_val = Inf; sign_prod = 1; for k_idx = 1:length(connected_vars) k = connected_vars(k_idx); if k ~= j sign_prod = sign_prod * sign(v2c(k, i)); min_val = min(min_val, abs(v2c(k, i))); % 遍历找到最小值 end end c2v(i, j) = sign_prod * min_val; % 限制消息的数值范围,防止数值溢出 c2v(i, j) = max(min(c2v(i, j), 20), -20); end end

NMSA基础版本,将校验节点更新的部分相应改为:

% 校验节点到变量节点的消息更新 for i = 1:m connected_vars = check_nodes{i}; for j_idx = 1:length(connected_vars) j = connected_vars(j_idx); % 校验节点i到变量节点j的消息更新使用Min-sum规则 min_val = Inf; sign_prod = 1; for k_idx = 1:length(connected_vars) k = connected_vars(k_idx); if k ~= j sign_prod = sign_prod * sign(v2c(k, i)); min_val = min(min_val, abs(v2c(k, i))); end end % 应用归一化因子alpha c2v(i, j) = sign_prod * alpha * min_val; % 限制消息的数值范围,防止数值溢出 c2v(i, j) = max(min(c2v(i, j), 20), -20); end end

OMSA基础版本,将校验节点更新的部分相应改为:

% 校验节点到变量节点的消息更新 for i = 1:m connected_vars = check_nodes{i}; for j_idx = 1:length(connected_vars) j = connected_vars(j_idx); % 校验节点i到变量节点j的消息更新使用Mins_sum规则 min_val = Inf; sign_prod = 1; for k_idx = 1:length(connected_vars) k = connected_vars(k_idx); if k ~= j sign_prod = sign_prod * sign(v2c(k, i)); min_val = min(min_val, abs(v2c(k, i))); end end % 应用偏移操作 offset_val = max(min_val - beta, 0); c2v(i, j) = sign_prod * offset_val; % 限制消息的数值范围,防止数值溢出 c2v(i, j) = max(min(c2v(i, j), 20), -20); end end

优化版本,将以上四种算法整合了一下,直接一步到位:

function [decoded_bits, iter] = BP_Decoder(H, llr, max_iterations, algo_type, param) % 实现统一的 BP 译码 % 输入: % H: 校验矩阵 % llr: 初始对数似然比 % max_iterations: 最大迭代次数 % algo_type: 'SPA', 'MSA', 'NMSA', 'OMSA' % param: 对于 NMSA 是缩放因子 alpha (如 0.75); 对于 OMSA 是偏移量 beta (如 0.15) if nargin < 4 algo_type = 'SPA'; end if nargin < 5 % 默认参数设置 if strcmp(algo_type, 'NMSA') param = 0.75; elseif strcmp(algo_type, 'OMSA') param = 0.15; else param = 0; end end [m, n] = size(H); H_mask = logical(H); % 初始化消息矩阵 V2C = repmat(llr, m, 1) .* H_mask; C2V = zeros(m, n); % 常数定义 LARGE_VAL = 30; % 截断阈值 SMALL_VAL = 1e-15; % 仅用于 SPA,防止 log(0)=Inf for iter = 1:max_iterations % ============================== % 1. 校验节点更新 (Horizontal Step) % ============================== % --- A. 符号处理 (所有算法通用) --- Sign_V2C = sign(V2C); Sign_V2C(V2C == 0) = 1; Sign_V2C(~H_mask) = 1; % 计算总奇偶校验 (行乘积) Total_Sign = prod(Sign_V2C, 2); C2V_Sign = Total_Sign .* Sign_V2C; % 排除自身 % --- B. 数值处理 (根据算法区分) --- Abs_V2C = abs(V2C); if strcmp(algo_type, 'SPA') % === SPA 算法 === Abs_V2C(Abs_V2C < SMALL_VAL) = SMALL_VAL; Phi_V2C = -log(tanh(Abs_V2C / 2)); Phi_V2C(~H_mask) = 0; Total_Phi = sum(Phi_V2C, 2); Phi_External = Total_Phi - Phi_V2C; % 防止数值问题 Phi_External(Phi_External < SMALL_VAL) = SMALL_VAL; C2V_Mag = -log(tanh(Phi_External / 2)); else % === MSA / NMSA / OMSA === % 1. 预处理:将非连接处(0)设为无穷大,避免影响求最小值 Abs_V2C_Temp = Abs_V2C; Abs_V2C_Temp(~H_mask) = inf; % 2. 寻找每一行的 最小值(min1) 和 次小值(min2) % [val, idx] = min(...) [min1_val, min1_idx] = min(Abs_V2C_Temp, [], 2); % 为了找次小值,先将最小值的位置设为无穷大 Abs_V2C_Temp_NoMin1 = Abs_V2C_Temp; % 使用线性索引快速替换 linear_indices = sub2ind([m, n], (1:m)', min1_idx); Abs_V2C_Temp_NoMin1(linear_indices) = inf; [min2_val, ~] = min(Abs_V2C_Temp_NoMin1, [], 2); % 3. 构建 C2V_Mag 矩阵 % 逻辑:如果所在位置正好是最小值 min1 则输出 min2,否则是 min1 % 先假设所有位置输出的都是全局最小值 C2V_Mag = repmat(min1_val, 1, n); % 将原本是 min1 的位置用 min2 代替 C2V_Mag(linear_indices) = min2_val; % 4. 针对变种进行修正 if strcmp(algo_type, 'NMSA') % Normalized MSA: 乘以归一化因子 alpha (0 < alpha < 1) alpha = param; C2V_Mag = C2V_Mag * alpha; elseif strcmp(algo_type, 'OMSA') % Offset MSA: 减去偏移量 beta, 并保证不小于0 beta = param; C2V_Mag = max(C2V_Mag - beta, 0); end end % --- C. 组合与截断 (所有算法通用) --- C2V = C2V_Sign .* C2V_Mag; C2V = max(min(C2V, LARGE_VAL), -LARGE_VAL); C2V = C2V .* H_mask; % ============================== % 2. 变量节点更新 (Vertical Step) % ============================== Sum_C2V = sum(C2V, 1); Posterior_LLR = llr + Sum_C2V; decoded_bits = double(Posterior_LLR < 0); % 校验 H*x^T = 0 syndrome = mod(H * decoded_bits', 2); if all(syndrome == 0) return; end V2C = repmat(Posterior_LLR, m, 1) - C2V; V2C = max(min(V2C, LARGE_VAL), -LARGE_VAL); V2C = V2C .* H_mask; end end

Read more

第二章-AIGC入门-AIGC工具全解析:技术控的效率神器,DeepSeek国产大模型的骄傲(8/36)

第二章-AIGC入门-AIGC工具全解析:技术控的效率神器,DeepSeek国产大模型的骄傲(8/36)

一、引言:AIGC 时代的浪潮 在数字化时代的浪潮中,人工智能生成内容(AIGC)技术正以迅猛之势席卷而来,深刻地改变着我们的生活和工作方式。从日常的社交媒体互动,到专业的内容创作、设计、教育、医疗等领域,AIGC 工具无处不在,展现出强大的影响力和无限的潜力。 AIGC 技术的核心在于利用人工智能算法,通过对海量数据的学习和分析,自动生成各种形式的内容,包括文本、图像、音频、视频等 。这一技术的突破,打破了传统内容创作的边界,使得内容生产变得更加高效、智能和多样化。无论是创作一篇新闻报道、设计一幅精美的海报,还是制作一段引人入胜的视频,AIGC 工具都能提供有力的支持,帮助创作者节省时间和精力,激发更多的创意灵感。 如今,AIGC 工具已经广泛应用于各个行业。在新闻媒体领域,自动化新闻写作工具能够快速生成体育赛事、财经新闻等报道,大大提高了新闻的时效性;在广告营销行业,AIGC 可以根据产品特点和目标受众,生成极具吸引力的广告文案和创意设计,提升营销效果;在影视游戏制作中,AIGC

By Ne0inhk
彻底解决 Stable Diffusion WebUI 启动报错:AttributeError: ‘MessageFactory’ object has no attribute ‘GetProto

彻底解决 Stable Diffusion WebUI 启动报错:AttributeError: ‘MessageFactory’ object has no attribute ‘GetProto

彻底解决 Stable Diffusion WebUI 启动报错:AttributeError: ‘MessageFactory’ object has no attribute ‘GetPrototype’ Stable Diffusion web UI sd-webui-roop 【技术分享】ComfyUI中protobuf版本兼容性问题的优雅解决方案:猴子补丁实战 告别环境崩溃:ONNX 与 Protobuf 版本兼容性指南 一、问题背景 最近在使用 Stable Diffusion WebUI(v1.10.1)时,安装了 sd-webui-roop 插件后,启动时出现了一个非常典型的 Protobuf 版本冲突错误: AttributeError: 'MessageFactory' object has no attribute '

By Ne0inhk

揭秘VSCode Copilot无法登录原因:5步快速恢复访问权限

第一章:VSCode Copilot无法登录问题概述 Visual Studio Code(VSCode)中的GitHub Copilot作为一款智能代码补全工具,极大提升了开发者的编码效率。然而,在实际使用过程中,部分用户频繁遭遇Copilot无法正常登录的问题,导致功能受限或完全不可用。该问题可能由多种因素引发,包括网络连接异常、身份验证失效、插件配置错误或系统环境限制等。 常见表现形式 * 点击“Sign in to GitHub”后无响应或弹窗无法加载 * 登录完成后仍提示“GitHub authentication failed” * Copilot状态始终显示为“Not signed in” 基础排查步骤 1. 确认网络可正常访问GitHub服务,必要时配置代理 2. 检查VSCode是否已更新至最新版本 3. 重新安装GitHub Copilot及GitHub Authentication扩展 验证身份认证状态 可通过开发者工具查看认证请求是否成功发出。在VSCode中按 F1,输入 Developer: Open

By Ne0inhk
豆包Seedream 4.0多图融合实力派:田园犬+三花猫多场景创作,AI绘画新时代来了!

豆包Seedream 4.0多图融合实力派:田园犬+三花猫多场景创作,AI绘画新时代来了!

豆包Seedream 4.0多图融合实力派:田园犬+三花猫多场景创作,AI绘画新时代来了! 🌟 Hello,我是摘星! 🌈 在彩虹般绚烂的技术栈中,我是那个永不停歇的色彩收集者。 🦋 每一个优化都是我培育的花朵,每一个特性都是我放飞的蝴蝶。 🔬 每一次代码审查都是我的显微镜观察,每一次重构都是我的化学实验。 🎵 在编程的交响乐中,我既是指挥家也是演奏者。让我们一起,在技术的音乐厅里,奏响属于程序员的华美乐章。 摘要 作为一名长期关注AI技术发展的开发者,我见证了从GAN到DALL-E,再到Stable Diffusion的图像生成技术演进历程。而今天,当我深入体验字节跳动最新发布的豆包Seedream 4.0时,我被这项技术的突破性表现深深震撼了。这不仅仅是一次简单的版本迭代,而是AI绘画领域的一次革命性跃进。 通过我使用中华田园犬和三花猫素材进行的深度测评,Seedream 4.0展现出了前所未有的多图融合能力和主体一致性保持水平。从真实场景的动物追逐图,到充满想象力的卡通探险绘本,再到创意十足的布偶挂件设计,每一个生成结果都让我感受到了AI创作的无限可能。这款模

By Ne0inhk