字典学习(Dictionary Learning, KSVD)详解
参考资料1、字典学习思想字典学习的思想应该源来实际生活中的字典的概念。字典是前辈们学习总结的精华,当我们需要学习新的知识的时候,不必与先辈们一样去学习先辈们所有学习过的知识,我们可以参考先辈们给我们总结的字典,通过查阅这些字典,我们可以大致学会到这些知识。为了将上述过程用准确的数学语言描述出来,我们需要将“总结字典”、“查阅字典”做出一个更为准确的描述。就从我们的常识出发:我们通常会要求的我们的字
1、字典学习思想
字典学习的思想应该源来实际生活中的字典的概念。字典是前辈们学习总结的精华,当我们需要学习新的知识的时候,不必与先辈们一样去学习先辈们所有学习过的知识,我们可以参考先辈们给我们总结的字典,通过查阅这些字典,我们可以大致学会到这些知识。
为了将上述过程用准确的数学语言描述出来,我们需要将“总结字典”、“查阅字典”做出一个更为准确的描述。就从我们的常识出发:
- 我们通常会要求的我们的字典尽可能全面,也就是说总结出的字典不能漏下关键的知识点。
- 查字典的时候,我们想要我们查字典的过程尽可能简洁,迅速,准确。即,查字典要快、准、狠。
- 查到的结果,要尽可能地还原出原来知识。当然,如果要完全还原出来,那么这个字典和查字典的方法会变得非常复杂,所以我们只需要尽可能地还原出原知识点即可。
注: 以上内容,完全是自己的理解,如有不当之处,欢迎各位拍砖。
下面,我们要讨论的就是如何将上述问题抽象成一个数学问题,并解决这个问题。
2、字典学习数学模型
2.1 数学描述
我们将上面的所提到的关键点用几个数学符号表示一下:
- “以前的知识”,更专业一点,我们称之为原始样本,用矩阵 Y \mathbf{Y} Y 表示;
- “字典”,我们称之为字典矩阵,用 D \mathbf{D} D 表示, “字典"中的词条,我们称之为原子 (atom),用列向量 d k \mathrm{d}_{k} dk 表示;
- “查字典的方法”,我们称为稀疏矩阵,用 X \mathbf{X} X;
- “查字典的过程”,我们可以用矩阵的乘法来表示,即 D X \mathbf{D X} DX 。
用数学语言描述,字典学习的主要思想是,利用包含 K K K 个原子 d k \mathbf{d}_{k} dk 的字典矩阵 D ∈ R m × K \mathbf{D} \in \mathbf{R}^{m \times K} D∈Rm×K ,稀疏线性表示原始样本 Y ∈ R m × n \mathbf{Y} \in \mathbf{R}^{m \times n} Y∈Rm×n (其中 m m m 表示样本数, n n n 表示样本的属性) ,即有 Y = D X \mathbf{Y}=\mathbf{D} \mathbf{X} Y=DX (这只是我们理想的情况),其中 X ∈ R K × n \mathbf{X} \in \mathbf{R}^{K \times n} X∈RK×n 为稀疏矩阵,可以将上 述问题用数学语言描述为如下优化问题
min D , X ∥ Y − D X ∥ F 2 , s.t. ∀ i , ∥ x i ∥ 0 ≤ T 0 (2-1) \min _{\mathbf{D}, \mathbf{X}}\|\mathbf{Y}-\mathbf{D} \mathbf{X}\|_{F}^{2}, \quad \text { s.t. } \forall i,\left\|\mathbf{x}_{i}\right\|_{0} \leq T_{0} \tag{2-1} D,Xmin∥Y−DX∥F2, s.t. ∀i,∥xi∥0≤T0(2-1)
或者
min D , X ∑ i ∥ x i ∥ 0 , s.t. min D , X ∥ Y − D X ∥ F 2 ≤ ϵ , (2-2) \min _{\mathbf{D}, \mathbf{X}} \sum_{i}\left\|\mathbf{x}_{i}\right\|_{0} \text {, s.t. } \min _{\mathbf{D}, \mathbf{X}}\|\mathbf{Y}-\mathbf{D} \mathbf{X}\|_{F}^{2} \leq \epsilon,\tag{2-2} D,Xmini∑∥xi∥0, s.t. D,Xmin∥Y−DX∥F2≤ϵ,(2-2)
上式中 X \mathbf{X} X 为稀疏编码的矩阵, x i ( i = 1 , 2 , ⋯ , K ) \mathbf{x}_{i}(i=1,2, \cdots, K) xi(i=1,2,⋯,K) 为该矩阵中的行向量,代表字典矩阵的系数。
注: ∥ x i ∥ 0 \left\|\mathrm{x}_{i}\right\|_{0} ∥xi∥0 表示零阶范数,它表示向量中不为 0 的数的个数。
2.2 求解问题
式 (2-1) 的目标函数表示,我们要最小化查完的字典与原始样本的误差,即要尽可能还原出原始样本;它的限的制条件 ∥ x i ∥ 0 ≤ T 0 \left\|\mathbf{x}_{i}\right\|_{0} \leq T_{0} ∥xi∥0≤T0 ,表示查字典的方式要尽可能简单,即 X \mathbf{X} X 要尽可能稀疏。式 (2-2) 同理。
式 (2-1) 或式 (2-2) 是一个带有约束的优化问题,可以利用拉格朗日乘子法将其转化为无约束优化问题
min D , X ∥ Y − D X ∥ F 2 + λ ∥ x i ∥ 1 (2-3) \min _{\mathbf{D}, \mathbf{X}}\|\mathbf{Y}-\mathbf{D} \mathbf{X}\|_{F}^{2}+\lambda\left\|\mathbf{x}_{i}\right\|_{1}\tag{2-3} D,Xmin∥Y−DX∥F2+λ∥xi∥1(2-3)
注: 我们将 ∥ x i ∥ 0 \left\|\mathbf{x}_{i}\right\|_{0} ∥xi∥0 用 ∥ x i ∥ 1 \left\|\mathbf{x}_{i}\right\|_{1} ∥xi∥1 代替,主要是 ∥ x i ∥ 1 \left\|\mathbf{x}_{i}\right\|_{1} ∥xi∥1 更加便于求解。
这里有两个优化变量 D , X \mathbf{D}, \mathbf{X} D,X ,为解决这个优化问题,一般是固定其中一个优化变量,优化另一个变量,如此交替进行。式 (2-3) 中 的稀疏矩阵 X \mathbf{X} X 可以利用已有经典算法求解,如 Lasso (Least Absolute Shrinkage and Selection Operator) 、 OMP (Orthogonal Matching Pursuit),这里我重点讲述如何更新字典 D \mathbf{D} D ,对更新 X \mathbf{X} X 不多做讨论。
假设 X \mathbf{X} X 是已知的,我们逐列更新字典。下面我们仅更新字典的第 k k k 列,记 d k \mathbf{d}_{k} dk 为字典 D \mathbf{D} D 的第 k k k 列向量,记 x T k \mathbf{x}_{T}^{k} xTk 为稀疏矩阵 X \mathbf{X} X 的第 k k k 行向 量,那么对式 ( 2 − 1 ) (2-1) (2−1) ,我们有
∥ Y − D X ∥ F 2 = ∥ Y − ∑ j = 1 K d j x T j ∥ F 2 = ∥ ( Y − ∑ j ≠ k d j x T j ) − d k x T k ∥ F 2 = ∥ E k − d k x T k ∥ F 2 (2-4) \begin{aligned} \|\mathbf{Y}-\mathbf{D} \mathbf{X}\|_{F}^{2} &=\left\|\mathbf{Y}-\sum_{j=1}^{K} \mathbf{d}_{j} \mathbf{x}_{T}^{j}\right\|_{F}^{2} \\ &=\left\|\left(\mathbf{Y}-\sum_{j \neq k} \mathbf{d}_{j} \mathbf{x}_{T}^{j}\right)-\mathbf{d}_{k} \mathbf{x}_{T}^{k}\right\|_{F}^{2} \\ &=\left\|\mathbf{E}_{k}-\mathbf{d}_{k} \mathbf{x}_{T}^{k}\right\|_{F}^{2} \end{aligned}\tag{2-4} ∥Y−DX∥F2=∥∥∥∥∥Y−j=1∑KdjxTj∥∥∥∥∥F2=∥∥∥∥∥∥⎝⎛Y−j=k∑djxTj⎠⎞−dkxTk∥∥∥∥∥∥F2=∥∥Ek−dkxTk∥∥F2(2-4)
上式中残差 E k = Y − ∑ j ≠ k d j x T j \mathbf{E}_{k}=\mathbf{Y}-\sum_{j \neq k} \mathbf{d}_{j} \mathbf{x}_{T}^{j} Ek=Y−∑j=kdjxTj ,此时优化问题可描述为
min d k , x T k ∥ E k − d k x T k ∥ F 2 \min _{\mathbf{d}_{k}, \mathbf{x}_{T}^{k}}\left\|\mathbf{E}_{k}-\mathbf{d}_{k} \mathbf{x}_{T}^{k}\right\|_{F}^{2} dk,xTkmin∥∥Ek−dkxTk∥∥F2
因此我们需要求出最优的 d k , x T k \mathbf{d}_{k}, \mathbf{x}_{T}^{k} dk,xTk ,这是一个最小二乘问题,可以利用最小二乘的方法求解,或者可以利用SVD进行求解,这里利用 SVD的方式求解出两个优化变量。
但是,在这里我人需要注意的是,不能直接利用 E k \mathbf{E}_{k} Ek 进行求解,否则求得的新的 x k T \mathbf{x}_{k}^{T} xkT 不稀疏。因此我们需要将 E k \mathbf{E}_{k} Ek 中对应的 x T k \mathbf{x}_{T}^{k} xTk 不为 0 的 位置提取出来,得到新的 E k ′ \mathbf{E}_{k}^{\prime} Ek′ ,这个过程如图2-1所示,这样描述更加清晰。
图 2 − 1 提 取 部 分 残 差 图2-1 提取部分残差 图2−1提取部分残差
如上图,假设我们要更新第0列原子,我们将 x T k \mathbf{x}_{T}^{k} xTk 中为零的位置找出来,然后把 E k \mathbf{E}_{k} Ek 对应的位置删除,得到 E k ′ , \mathbf{E}_{k}^{\prime} , Ek′, 此时优化问题可描述为
min d k , x T k ∥ E k ′ − d k x T ′ k ∥ F 2 (2-5) \min _{\mathbf{d}_{k}, \mathbf{x}_{T}^{k}}\left\|\mathbf{E}_{k}^{\prime}-\mathbf{d}_{k} \mathbf{x}_{T}^{\prime k}\right\|_{F}^{2} \tag{2-5} dk,xTkmin∥∥Ek′−dkxT′k∥∥F2(2-5)
因此我们需要求出最优的 d k , x ′ T k \mathbf{d}_{k}, \mathbf{x}^{\prime}{ }_{T}^{k} dk,x′Tk
E k ′ = U Σ V T (2-6) \mathbf{E}_{k}^{\prime}=U \Sigma V^{T}\tag{2-6} Ek′=UΣVT(2-6)
取左奇异矩阵 U U U 的第1个列向量 U 1 = U ( ⋅ , 1 ) \mathbf{U}_{1}=U(\cdot, 1) U1=U(⋅,1) 作为 d k \mathbf{d}_{k} dk ,即 d k = u 1 \mathbf{d}_{k}=\mathbf{u}_{1} dk=u1 ,取右奇异矩阵的第1个行向量与第1个奇异值的乘积作为 x ′ T k \mathbf{x}^{\prime}{ }_{T} k x′Tk , 即 x T ′ k = Σ ( 1 , 1 ) V T ( 1 , ⋅ ) \mathbf{x}_{T}^{\prime k}=\Sigma(1,1) V^{T}(1, \cdot) xT′k=Σ(1,1)VT(1,⋅) 。得到 x T ′ k \mathbf{x}_{T}^{\prime k} xT′k 后,将其对应地更新到原 x T k \mathbf{x}_{T}^{ k} xTk 。
注: 式 (2-6) 所求得的奇异值矩阵 Σ \Sigma Σ 中的奇异值应从大到小排列;同样也有 x ′ T k = Σ ( 1 , 1 ) V ( ⋅ , 1 ) T \mathbf{x}^{\prime}{ }_{T}^{k}=\Sigma(1,1) V(\cdot, 1)^{T} x′Tk=Σ(1,1)V(⋅,1)T ,这与上面 x T ′ k \mathbf{x}_{T}^{\prime k} xT′k 的求法是 相等的。
2.3 字典学习算法实现
据2.2小节,利用稀疏算法求解得到稀疏矩阵 X \mathbf{X} X 后,逐列更新字典,有如下算法1.1。
算法1.1:字典学习(K-SVD)
输入: 原始样本,字典,稀疏矩阵
输出: 字典,稀疏矩阵
- 初始化: 从原始样本 Y ∈ R m × n Y \in \mathbf{R}^{m \times n} Y∈Rm×n 随机取 K K K 个列向量或者取它的左奇异矩阵的前 K K K 个列向量 { d 1 , d 2 , ⋯ , d K } \left\{\mathbf{d}_{1}, \mathbf{d}_{2}, \cdots, \mathbf{d}_{K}\right\} {d1,d2,⋯,dK} 作为初始字典 的原子,得到字典 D ( 0 ) ∈ R m × K \mathbf{D}^{(0)} \in \mathbf{R}^{m \times K} D(0)∈Rm×K 。令 j = 0 j=0 j=0 ,重复下面步骤2-3,直到达到指定的迭代步数,或收敛到指定的误差:
- 稀疏编码:利用字典上一步得到的字典 D ( j ) \mathbf{D}^{(j)} D(j) ,稀疏编码,得到 X ( j ) ∈ R K × n \mathbf{X}^{(j)} \in \mathbf{R}^{K \times n} X(j)∈RK×n 。
- 字典更新: 逐列更新字典 D ( j ) \mathbf{D}^{(j)} D(j) ,字典的列 d k ∈ { d 1 , d 2 , ⋯ , d K } \mathbf{d}_{k} \in\left\{\mathbf{d}_{1}, \mathbf{d}_{2}, \cdots, \mathbf{d}_{K}\right\} dk∈{d1,d2,⋯,dK}
- 当更新 d k \mathbf{d}_{k} dk 时,计算误差矩阵 E k \mathbf{E}_{k} Ek
E k = Y − ∑ j ≠ k d j x T j \mathbf{E}_{k}=\mathbf{Y}-\sum_{j \neq k} \mathbf{d}_{j} \mathbf{x}_{T}^{j} Ek=Y−j=k∑djxTj - 取出稀 疏矩阵第 k k k 个行向量 x T k \mathbf{x}_{T}^{k} xTk 不为 0 的索引的集合 ω k = { i ∣ 1 ≤ i ≤ n , x T k ( i ) ≠ 0 } \omega_{k}=\left\{i \mid 1 \leq i \leq n, \mathbf{x}_{T}^{k}(i) \neq 0\right\} ωk={i∣1≤i≤n,xTk(i)=0} x T ′ T k = { x T k ( i ) ∣ 1 ≤ i ≤ n , x T k ( i ) ≠ 0 } \mathbf{x}_{T}^{\prime}{ }_{T}^{k}=\left\{\mathbf{x}_{T}^{k}(i) \mid \mathbf{1} \leq i \leq n, \mathbf{x}_{T}^{k}(i) \neq 0\right\} xT′Tk={xTk(i)∣1≤i≤n,xTk(i)=0}
- 从 E k \mathbf{E}_{k} Ek 取出对应 ω k \omega_{k} ωk 不为 0 的列,得到 E k ′ \mathbf{E}_{k}^{\prime} Ek′.
- 对 E k ′ \mathbf{E}_{k}^{\prime} Ek′ 作奇异值分解 E k = U Σ V T \mathbf{E}_{k}=U \Sigma V^{T} Ek=UΣVT ,取 U U U 的第1列更新字典的第 k k k 列,即 d k = U ( ⋅ , 1 ) \mathbf{d}_{k}=U(\cdot, 1) dk=U(⋅,1) ;令 x T ′ = Σ ( 1 , 1 ) V ( ⋅ , 1 ) T \mathbf{x}_{T}^{\prime}=\Sigma(1,1) V(\cdot, 1)^{T} xT′=Σ(1,1)V(⋅,1)T ,得 到 x T ′ k \mathbf{x}_{T}^{\prime k} xT′k 后,将其对应地更新到原 x T k \mathbf{x}_{T}^{k} xTk 。
- j = j + 1 j=j+1 j=j+1
3、字典学习Python实现
以下实验的运行环境为python3.6+jupyter5.4。
载入数据
import numpy as np
import pandas as pd
from scipy.io import loadmat
train_data_mat = loadmat("../data/train_data2.mat")
train_data = train_data_mat["Data"]
train_label = train_data_mat["Label"]
print(train_data.shape, train_label.shape)
注: 上面的数据集,可以随便使用一个,也可以随便找一个张图片。
初始化字典
u, s, v = np.linalg.svd(train_data)
n_comp = 50
dict_data = u[:, :n_comp]
字典更新
def dict_update(y, d, x, n_components):
"""
使用KSVD更新字典的过程
"""
for i in range(n_components):
index = np.nonzero(x[i, :])[0]
if len(index) == 0:
continue
# 更新第i列
d[:, i] = 0
# 计算误差矩阵
r = (y - np.dot(d, x))[:, index]
# 利用svd的方法,来求解更新字典和稀疏系数矩阵
u, s, v = np.linalg.svd(r, full_matrices=False)
# 使用左奇异矩阵的第0列更新字典
d[:, i] = u[:, 0]
# 使用第0个奇异值和右奇异矩阵的第0行的乘积更新稀疏系数矩阵
for j,k in enumerate(index):
x[i, k] = s[0] * v[0, j]
return d, x
注: 上面代码的16~17需要注意python的numpy中的普通索引和花式索引的区别,花式索引会产生一个原数组的副本,所以对花式索引的操作并不会改变原数据,因此不能像第10行一样,需利用直接索引更新x。
迭代更新求解
可以指定迭代更新的次数,或者指定收敛的误差。
from sklearn import linear_model
max_iter = 10
dictionary = dict_data
y = train_data
tolerance = 1e-6
for i in range(max_iter):
# 稀疏编码
x = linear_model.orthogonal_mp(dictionary, y)
e = np.linalg.norm(y - np.dot(dictionary, x))
if e < tolerance:
break
dict_update(y, dictionary, x, n_comp)
sparsecode = linear_model.orthogonal_mp(dictionary, y)
train_restruct = dictionary.dot(sparsecode)
更多推荐
所有评论(0)