ISTA与稀疏编码 both python & matlab

ISTA与稀疏编码

本人习惯

注释

我自己的想法或者例子,不必要

代码

function [...] = fun(...)
....
end

稀疏编码

稀疏编码是什么鬼

图像可以认为是 n × m × 3 n \times m \times 3 n×m×3的一个矩阵,随着图像清晰度的提升,以目前主流的1920*1800图像为例,单单一个图片就可能有 1920 × 1080 × 3 ≥ 6 × 1 0 6 1920 \times 1080 \times 3 \geq 6 \times 10^{6} 1920×1080×36×106,在假设每个位置使用256色( 2 8 2^8 28种颜色需要8bit,也就是1B),一张图片的大小已经大于6MB,未经压缩的1080P视频每分钟数据量约为 1920 × 1080 × 3 × 30 × 60 ≥ 1 × 1 0 10 1920 \times 1080 \times 3 \times 30 \times 60 \geq 1 \times 10^{10} 1920×1080×3×30×601×1010,一分钟超过一个G的存储量显然目前的存储技术无法解决,只能从无损压缩图像->无损压缩视频这个思路出发。虽然JPEG提供了一种无损压缩算法,但是速度较慢。一般情况下,对于实时性要求高或数据传输成本高的情况(例如医疗影像\卫星图像),有损压缩成为了一种必要之恶。
稀疏编码是一种依托优化思想出现的压缩方式,他从两个方向出发对图像进行压缩:

  1. 保真度
    对于原始的信号X,我们希望有一个信号Y充分逼近X,有如下关系:
    X = Y + ϵ \begin{aligned} X = Y + \epsilon \end{aligned} X=Y+ϵ
    其中 ϵ \epsilon ϵ为噪声,我们在这里希望噪声造成的影响最小,即:
    Y ∗ = arg ⁡ min ⁡ Y B ( X , Y ) \begin{aligned} Y^* = {\arg\min}_Y B(X,Y) \end{aligned} Y=argminYB(X,Y)
    其中 B ( X , Y ) = ∣ ∣ Y − X ∣ ∣ p p = ∣ ∣ ϵ ∣ ∣ p p B(X,Y)=||Y-X||_p^p = ||\epsilon||_p^p B(X,Y)=YXpp=ϵpp
    从B的定义考虑,若我们选择p=2,B(X,Y)可以认为是均方误差(MSE),我们希望其误差尽量小。 也是一种合理的解释呢!
    接下来,我们给定一个Y的表示方法
    Y = D ⋅ Z \begin{aligned} Y = D \cdot Z \end{aligned} Y=DZ
    显然,如果D是n维空间的一组标准正交基,Z就相当于是Y在D上映射的坐标,且对于指定的D,Z有唯一表示,我们希望Z有某种优秀的性质。那么我们有几个方向可以考虑
    • 推断D的结构,通过数据对D进行优化,保证得到的Z拥有好的性质,但是这太难了,对于不同的数据,很难用矩阵论的方法对D进行推断。
    • 如果上述结构不进行推断,仍可以通过机器学习算法例如BP神经网络优化来优化这个矩阵D,使得D对于一个特定场景具有较好的性质。确实是一种好的思想。但是如果D发生变化,Z将无法通过简单的矩阵乘法变回信号Y,显然如果空间的基发送变化,这组基上的坐标都将失去原来的意义。需要大量的数据才能保证在某一问题上D有良好的效果。只能考虑继续优化思路。
    • 我们先给定一组标准正交基S,再给定一些列向量,写成矩阵P,如果D = [S P], 那么P中所有向量均可由S线性表出,此时D(之后称为字典)可以称为一个超完备的集合,此时Z的表示不再唯一,为后续对于Z某种性质的优化提供了可能性。(*对于ISTA我们考虑到这里就结束了,但是仍然可以对此进行改进,如果有好的想法欢迎私信探讨)
    关于字典,我觉得是他通过一个拥有优秀性质的Z表示了蕴含复杂信息的Y
    
  2. 稀疏性
    对于一个向量Z,Z中非0元素个数为u, 则称Z为u稀疏的(u-Sparse)。对于一个充分稀疏的向量Z,使用Z{Site, Value}表示更加节省空间(类似图论算法中的两种存图结构:邻接矩阵和邻接表),是一种可行的压缩方式。
    Z = [0,0,1,0,0,0,3,0,0,0,0,0,1,0,0,2,0,5,0,0,0,0,0,0,0,0,0,0,0]
    Z{Site, Value} = [{2,1},{6,3},{12,1},{15,2},{17,5}]
    很明显sizeof(Z)>sizeof(Z{Site, Value})
    
    那么如何对通过优化保证稀疏性呢?
    Z ∗ = arg ⁡ min ⁡ Z S ( X , Y ) = ∣ ∣ Z ∣ ∣ 0 \begin{aligned} Z^* = {\arg\min}_Z S(X,Y) = ||Z||_0 \end{aligned} Z=argminZS(X,Y)=Z0
    是一种很好的想法,但是L-0范数显然是非凸的,优化难度巨大,属于NP-Hard问题,只能智取,不可强攻。
    IDEAs:
    1. 使用贪心算法(one can say it nearly always choose all the suboptimal solution)
    2. 使用群体智能算法(some said only works well on low-dimension question)
    3. 放宽条件(some called of ’松弛‘, which may be a good idea)
    根据大佬的证明,将0范数放宽到1范数是可行的,故原问题转化为:
    Z ∗ = arg ⁡ min ⁡ Z S ( Z ) = ∣ ∣ Z ∣ ∣ 1 \begin{aligned} Z^* = {\arg\min}_Z S(Z) = ||Z||_1 \end{aligned} Z=argminZS(Z)=Z1

    优秀的糅合怪LASSO格式的稀疏优化

    我们有两种思路将上述问题糅合起来,就是稀疏编码的目标:
    在有足够保真度的区域找到稀疏性最好的点:
    Z ∗ = arg ⁡ min ⁡ Z S ( Z ) \begin{aligned} Z^* = {\arg\min}_Z S(Z) \end{aligned} Z=argminZS(Z) s . t . B ( X , D ⋅ Z ) < = e \begin{aligned} s.t. B(X,D \cdot Z) <= e \end{aligned} s.t.B(X,DZ)<=e
    在稀疏性足够好的区域找到保真度最高的点: Z ∗ = arg ⁡ min ⁡ Z B ( X , D ⋅ Z ) \begin{aligned} Z^* = {\arg\min}_Z B(X,D \cdot Z) \end{aligned} Z=argminZB(X,DZ) s . t . S ( Z ) < = u \begin{aligned} s.t.S(Z) <= u \end{aligned} s.t.S(Z)<=u
    使用罚函数法把上述两个问题都可以转化为如下LASSO格式: Z ∗ = arg ⁡ min ⁡ Z B ( X , D ⋅ Z ) + λ S ( Z ) \begin{aligned} Z^* = {\arg\min}_Z B(X,D \cdot Z) + \lambda S(Z) \end{aligned} Z=argminZB(X,DZ)+λS(Z)
    这就是我们接下来要优化的问题了。

    迭代软阈值算法

    软阈值函数

    h θ ( x ) = s i g n ( x ) × m a x ( a b s ( x ) − θ , 0 ) h_\theta(x) = sign(x) \times max(abs(x)-\theta, 0) hθ(x)=sign(x)×max(abs(x)θ,0)
    在这里插入图片描述
Other`s Description about Soft Thresholding: https://blog.csdn.net/jbb0523/article/details/52103257

ISTA

由LASSO问题的解形式可以得到( Z 0 = 0 ⃗ Z_0=\vec 0 Z0=0 ):
Z t + 1 = Z t − 1 L W d T ( W d Z t − X ) = 1 L W d T X + ( I − 1 L W d T W d ) Z t \begin{aligned} Z_{t+1} = Z_t - \frac{1}{L}W_d^T(W_dZ_t-X) \\ = \frac{1}{L}W_d^TX+(I-\frac{1}{L}W_d^TW_d)Z_t \end{aligned} Zt+1=ZtL1WdT(WdZtX)=L1WdTX+(IL1WdTWd)Zt
若取
S = I − 1 L W d T W d W e = 1 L W d T L ≥ r a n k ( W d T W d ) S = I - \frac{1}{L}W_d^TW_d\\ W_e = \frac{1}{L}W_d^T\\ L \ge rank(W_d^TW_d) S=IL1WdTWdWe=L1WdTLrank(WdTWd)
则迭代公式可化为:
Z t + 1 = W e X + S Z t \begin{aligned} Z_{t+1} = W_eX+SZ_t \end{aligned} Zt+1=WeX+SZt
若每一次迭代后经过一次激活则有:
Z t + 1 = h θ ( W e X + S Z t ) \begin{aligned} Z_{t+1} = h_\theta(W_eX+SZ_t) \end{aligned} Zt+1=hθ(WeX+SZt)
则有如下算法模型(RNN):

终止条件
其他情况
X
We
+
Soft Thresholding
Z
S

python 实现:
in ista.py

from utils import sh
import numpy as np


class ISTA:
    _w_d = np.asmatrix([])
    _lambda = 0
    _l = 0
    _alpha = 0

    '''
        init(wd, lam, l)
        wd      字典      wd      
        lam     系数      lambda  
        l       常数      l       
    '''

    def __init__(self, wd, lam, lip, alpha):
        self._w_d = np.asmatrix(wd)
        self._lambda = lam
        eig, eig_vs = np.linalg.eig(np.dot(wd.T, wd))
        r = np.max(eig)
        if r > lip:
            print('L is not suitable where l = %f but eig_max(wd) = %f' % (lip, r))
            print('now L is changed to eig_max(wd) + 1 which larger than eig_max(wd)')
            lip = r + 1
        del eig, eig_vs
        self._l = lip
        self._alpha = alpha
        self.we = self._w_d.T / self._l
        self.beta = self._alpha / self._l

    '''
        y,e = do_train(x, eps, itm)
        x       原向量         x                
        eps     误差限         epsilon          
        itm     最大迭代次数    iterate_times_max
    '''

    def _work(self, x, eps):
        z = np.zeros((self._w_d.shape[1], 1))
        it = 0
        err_t = []
        while True:
            it += 1
            t = np.dot(self._w_d, z) - x
            z1 = sh(z - self.we * t, self.beta)
            err = np.linalg.norm(z - z1)
            err_t.append(err)
            if err < eps:
                break
            z = z1

        return z, it, np.asarray(err_t)

    def do_work(self, args):
        assert args.__len__() == 2
        return self._work(args[0], args[1])

in utils.py

import numpy as np


def sh(x, theta):
    return np.multiply(np.sign(x), np.maximum(np.abs(x) - theta, 0))


def model_work(model, *args):
    return model.do_work(args);

in entrance.py

from utils import model_train, model_work
from ista import ISTA
import numpy as np
import tensorflow as tf
from scipy.linalg import orth


def get_wd(m, n):
    psi = np.eye(m)
    phi = np.random.randn(n, m)
    phi = np.transpose(orth(np.transpose(phi)))
    return np.dot(phi, psi)


def get_x_z(m, k, wd):
    z = np.zeros([m, 1])
    ind = np.random.choice(a=m, size=k, replace=False, p=None)
    z[ind, 0] = np.random.randn(1, k) * 10
    return np.dot(wd, z), z


def call_ista(wd, lam, lip, alpha, x, eps):
    model = ISTA(wd, lam, lip, alpha)
    return model_work(model, x, eps)


def test_ista(m, n, k, wd, x, z, lam, lip, alpha, eps):
    zr, it, err = call_ista(wd, lam, lip, alpha, x, eps)
    with open('ista.txt', 'w') as f:
        for i in range(m):
            f.write('%03.3f %03.3f\n' % (z[i, 0], zr[i, 0]))
    print(it)
    print(err)


def train_lista(m, n, k, wd, lam, L, alpha, eps, times):
    # model = Lista(...)
    for i in range(times):
        x, z = get_x_z(m, k, wd)
        # train_model(model,x,z,T)
    pass


def main():
    m, n, k = 1024, 256, 8
    wd = get_wd(m, n)
    x, z = get_x_z(m, k, wd)
    L = 2
    lam = 0.1
    alpha = 0.1
    eps = 0.001
    test_ista(m, n, k, wd, x, z, lam, L, alpha, eps)


if __name__ == '__main__':
    main()

matlab代码

% CSDN不支持matlab代码,这里使用lua近似替代,可能有显示问题
% author sunwei
% at 2020.10.11
function z = sh(beta, theta)
% sh为软阈值函数简写
% 传入一个向量beta,一个阈值theta
% z 为软阈值变换的结果
z = sign(beta).*max(abs(beta)-theta,0);
end

function [x,z] = get_x_z(m,k,wd)
% 随机信号生成
% z为`稀疏信号`
% x为`原始信号`
% 为方便验证,实际上原始信号通过稀疏信号生成
z = zeros(m,1);
ind = randperm(m);
ind = ind(1:k);
z(ind) = randn(1,k) * 10;
x = wd*z;
end

function [zr, it] = ista(wd, L, alpha, x, eps)
% wd 字典
% alpha sh参数的一部分
% L 李普希斯常数(一个大于wd'*wd)的常数
% x 得到的信号
% eps 误差
    [~,n] = size(wd);
    z = zeros(n,1);
    it = 0;
    while true
        t = z - 1/L*wd'*(wd*z-x);
        zr = sh(t, alpha/L);
        it = it + 1;
        if((z-zr)'*(z-zr) < eps) break; end
        z = zr;
    end
end

function test_ista()
    m = 1024; n = 256; k = 6;
    wd = randn(n,m);
    wd = orth(wd')';
    [x, z] = get_x_z(m,k,wd);
    eig_val = eig(wd'*wd);
    L = max(eig_val) + 1;
    lam = 0.1;
    alpha = 0.1;
    eps = 0.001;
    [zr, it] = ista(wd, L, alpha, x, eps);
    t = 1:m;
    plot(t,z,t,zr)
    fprintf('it = %d, err = %d', it, (z-zr)'*(z-zr));
end

补充:

  1. 下一篇:LISTA 基于学习的迭代压缩软阈值算法(Paper: Learning Fast Approximations of Sparse Coding)
  2. 关于稀疏表示可以看一看Coursera上的https://www.coursera.org/learn/image-processing,Duke大学的Professor还是厉害的。
  3. python fprintf('it = %d, err = %d', it, (z-zr)'*(z-zr));一句有问题,err应该是 稀疏性提供的误差+保真度提供的误差

猜你喜欢

转载自blog.csdn.net/goes_on/article/details/109008377