K-SVD: An Algorithm for Designing Overcomplete Dictionaries for Sparse Representation

Paper:http://sites.fas.harvard.edu/~cs278/papers/ksvd.pdf
(阅读笔记)

1、写在前面

该篇论文是一篇methodology的论文,主要是解决稀疏表示的经典论文。

1.1、基本术语

  • 什么是稀疏:一个数据的所有特征中,有很多的特征与当前要解决的问题是无关的(一般会在矩阵中就直接以0表示),这样就是稀疏。
  • 优点:稀疏性很明显的具有数据开销小(0较多)的优点,学习器训练的难度也可能会很小。
  • 如果数据本身它不是稀疏的,是稠密的,那么我们想让它有稀疏的优点,所以我们就可以对数据进行稀疏表示(Sparse Representation),而这个学习的过程就是字典学习,找到合适的字典,来转换成稀疏的表示形式。

1.2、稀疏表示的过程

给定数据集X:{x1,x2,...,xm}\mathcal{X}:\{{x_1,x_2,...,x_m}\}X:{x1​,x2​,...,xm​},即有:
min⁡B,αi∑i=1m∥xi−Bαi∥22+λ∑i=1m∥αi∥1(1)\min_{B,\alpha_i} \sum_{i=1}^{m}{\|x_i - B\alpha_i\|_2^2}+\lambda\sum_{i=1}^{m}{\|\alpha_i\|_1} \tag{1} B,αi​min​i=1∑m​∥xi​−Bαi​∥22​+λi=1∑m​∥αi​∥1​(1)
其中B∈Rd×kB\in\mathbb{R}^{d\times k}B∈Rd×k是字典矩阵(转换的桥梁),kkk是字典的词汇量(样本数),ddd是样本特征数。很明显的,通过矩阵运算,上述公式目标有两点,其一:通过2范数,使Bαi=xiB\alpha_i=x_iBαi​=xi​,其二:通过1范数,αi\alpha_iαi​尽可能稀疏(0变多)。
即αi∈Rk\alpha_i\in\mathbb{R}^{k}αi​∈Rk是样本xi∈Rdx_i\in\mathbb{R}^{d}xi​∈Rd的稀疏表示,αi\alpha_iαi​和xix_ixi​都是列向量,列向量中的元素就是该样本的特征数据

1.3、如何求BBB和αi\alpha_iαi​

分成两个步骤,先固定字典矩阵BBB求αi\alpha_iαi​,再固定αi\alpha_iαi​求BBB:
min⁡αi∥xi−Bαi∥22+λ∥αi∥1(2)\min_{\alpha_i} \|x_i - B\alpha_i\|_2^2+\lambda\|\alpha_i\|_1 \tag{2} αi​min​∥xi​−Bαi​∥22​+λ∥αi​∥1​(2)
统一把αi\alpha_iαi​和xix_ixi​合并成矩阵AAA和XXX,该矩阵即包含了所有的信息,第二步固定αi\alpha_iαi​求BBB即转换成了矩阵F范数。
min⁡B∥X−BA∥F2(3)\min_{B} \|X - BA\|_F^2 \tag{3} Bmin​∥X−BA∥F2​(3)
其中X=(x1,x2,...,xm)∈Rd×mX=(x_1,x_2,...,x_m)\in \mathbb{R}^{d\times m}X=(x1​,x2​,...,xm​)∈Rd×m,A=(α1,α2,...,αm)∈Rk×mA=(\alpha_1,\alpha_2,...,\alpha_m)\in \mathbb{R}^{k\times m}A=(α1​,α2​,...,αm​)∈Rk×m,XXX和AAA就是将每一列(列向量)组合起来,直接就变成了矩阵。
F-norm: ∥X∥F=∑i∑jxi,j2(4)\text{F-norm: }\|X \|_F=\sqrt{\sum_{i}\sum_{j}x_{i,j}^2} \tag{4} F-norm: ∥X∥F​=i∑​j∑​xi,j2​​(4)
K-SVD就是求解字典矩阵BBB的方法(预先假设AAA已经知道)。

1.4、奇异值分解SVD与F范数

矩阵F范数相当于是向量L2范数的推广,而矩阵F范数的结果就是其奇异值分解的奇异值矩阵的元素的平方的和再开方(也就是矩阵XXX的XXTXX^TXXT的相似对角化矩阵的迹)。
∥X∥F=(σ12+σ22+,...,+σn2)(5)\|X \|_F=\sqrt{(\sigma_1^2+\sigma_2^2+,...,+\sigma_n^2)} \tag{5} ∥X∥F​=(σ12​+σ22​+,...,+σn2​)​(5)
其中σi\sigma_iσi​是矩阵XXX的奇异值。
因为当一个正交阵与任意矩阵相乘的F范数其实就对于其矩阵本身:
∥QX∥F2=∥Qx1,Qx2,...,Qxn∥F2=∑i=1n∥Qxi∥22=∑i=1n∥xi∥22=∥X∥F2(6)\begin{aligned} \|QX \|_F^2 & = \|Qx_1,Qx_2,...,Qx_n \|_F^2 \\ &=\sum_{i=1}^n \| Qx_i\|_2^2 \\ &=\sum_{i=1}^n \| x_i\|_2^2 \\ & = \|X \|_F^2 \\ \tag{6} \end{aligned} ∥QX∥F2​​=∥Qx1​,Qx2​,...,Qxn​∥F2​=i=1∑n​∥Qxi​∥22​=i=1∑n​∥xi​∥22​=∥X∥F2​​(6)
正交矩阵的平方是等于1的。
于是:
∥X∥F2=∥UΣVT∥F2=∥Σ∥F2=(σ12+σ22+,...,+σn2)(7)\begin{aligned} \| X \|_F^2 & = \|U \Sigma V^T \|_F^2 \\ & = \| \Sigma \|_F^2 \\ &={(\sigma_1^2+\sigma_2^2+,...,+\sigma_n^2)} \tag{7} \end{aligned} ∥X∥F2​​=∥UΣVT∥F2​=∥Σ∥F2​=(σ12​+σ22​+,...,+σn2​)​(7)

1.5、奇异值分解SVD与矩阵近似

对于矩阵的近似问题∥A−A^∥F\|A-\hat{A}\|_F∥A−A^∥F​有如下定理:
Eckart-Young-Mirsky 定理:给定一个秩为rrr的矩阵AAA,欲求其最优kkk秩的近似矩阵A^\hat{A}A^,其中k≤rk\leq rk≤r,可以用F范数表示为如下:
min⁡A^∈Rm×n∥A−A^∥Frank(A^)=k.rank(A)=r.k≤r(8)\min_{\hat{A}\in \mathbb{R}^{m\times n}} \|A - \hat{A}\|_F\\rank(\hat{A})=k.\\rank(A)=r.\\k\leq r \tag{8} A^∈Rm×nmin​∥A−A^∥F​rank(A^)=k.rank(A)=r.k≤r(8)
对矩阵AAA奇异值分解后A=Um×mΣm×nVn×nTA=U_{m\times m}\Sigma_{m\times n}V_{n\times n}^TA=Um×m​Σm×n​Vn×nT​,仅保留矩阵Σ\SigmaΣ的最大的kkk个奇异值,其他的置0,那么即得到了秩为kkk的矩阵A^k=UΣkVT\hat{A}_k=U\Sigma_kV^TA^k​=UΣk​VT,很明显地,矩阵A^\hat{A}A^即不是原来的AAA了(少了一部分奇异值矩阵的作用部分),但是却是近似原来的矩阵AAA的。同时变得稀疏了,秩低了。

2、K-SVD

上式min⁡B∥X−BA∥F2\min_{B} \|X - BA\|_F^2minB​∥X−BA∥F2​可改写。目标即变成了使矩阵X−BAX - BAX−BA的奇异值最小的BBB(此时已经固定了矩阵AAA)。
首先对BABABA进行改写,其中下标表示列数,上标表示行数:
BA=[b11b21⋅⋅⋅bk1b12b22⋅⋅⋅bk2⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅b1db2d⋅⋅⋅bkd]d×k×[α11α21⋅⋅⋅αm1α12α22⋅⋅⋅αm2⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅α1kα2k⋅⋅⋅αmk]k×m=[∑j=1kbj1α1j∑j=1kbj1α2j⋅⋅⋅∑j=1kbj1αmj∑j=1kbj2α1j∑j=1kbj2α2j⋅⋅⋅∑j=1kbj2αmj⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅∑j=1kbjdα1j∑j=1kbjdα2j⋅⋅⋅∑j=1kbjdαmj]d×m(9)BA={\left[ \begin{matrix} b_1^1 & b_2^1 & \cdot & \cdot & \cdot & b_k^1 \\ b_1^2 & b_2^2 & \cdot & \cdot & \cdot & b_k^2 \\ \cdot & \cdot & \cdot & & & \cdot \\ \cdot & \cdot & & \cdot & & \cdot \\ \cdot & \cdot & & & \cdot & \cdot \\ b_1^d & b_2^d & \cdot & \cdot & \cdot & b_k^d \\ \end{matrix} \right] }_{d \times k} \times {\left[ \begin{matrix} \alpha_1^1 & \alpha_2^1 & \cdot & \cdot & \cdot & \alpha_m^1 \\ \alpha_1^2 & \alpha_2^2 & \cdot & \cdot & \cdot & \alpha_m^2 \\ \cdot & \cdot & \cdot & & & \cdot \\ \cdot & \cdot & & \cdot & & \cdot \\ \cdot & \cdot & & & \cdot & \cdot \\ \alpha_1^k & \alpha_2^k & \cdot & \cdot & \cdot & \alpha_m^k \\ \end{matrix} \right] }_{k \times m}\\ ={{\left[ \begin{matrix} \sum_{j=1}^{k}b_j^1\alpha_1^j & \sum_{j=1}^{k}b_j^1\alpha_2^j & \cdot & \cdot & \cdot & \sum_{j=1}^{k}b_j^1\alpha_m^j \\ \sum_{j=1}^{k}b_j^2 \alpha_1^j & \sum_{j=1}^{k}b_j^2\alpha_2^j & \cdot & \cdot & \cdot & \sum_{j=1}^{k}b_j^2\alpha_m^j \\ \cdot & \cdot & \cdot & & & \cdot \\ \cdot & \cdot & & \cdot & & \cdot \\ \cdot & \cdot & & & \cdot & \cdot \\ \sum_{j=1}^{k}b_j^d\alpha_1^j & \sum_{j=1}^{k}b_j^d \alpha_2^j & \cdot & \cdot & \cdot & \sum_{j=1}^{k}b_j^d\alpha_m^j \\ \end{matrix} \right] }}_{d \times m} \tag{9} BA=⎣⎢⎢⎢⎢⎢⎢⎡​b11​b12​⋅⋅⋅b1d​​b21​b22​⋅⋅⋅b2d​​⋅⋅⋅⋅​⋅⋅⋅⋅​⋅⋅⋅⋅​bk1​bk2​⋅⋅⋅bkd​​⎦⎥⎥⎥⎥⎥⎥⎤​d×k​×⎣⎢⎢⎢⎢⎢⎢⎡​α11​α12​⋅⋅⋅α1k​​α21​α22​⋅⋅⋅α2k​​⋅⋅⋅⋅​⋅⋅⋅⋅​⋅⋅⋅⋅​αm1​αm2​⋅⋅⋅αmk​​⎦⎥⎥⎥⎥⎥⎥⎤​k×m​=⎣⎢⎢⎢⎢⎢⎢⎢⎡​∑j=1k​bj1​α1j​∑j=1k​bj2​α1j​⋅⋅⋅∑j=1k​bjd​α1j​​∑j=1k​bj1​α2j​∑j=1k​bj2​α2j​⋅⋅⋅∑j=1k​bjd​α2j​​⋅⋅⋅⋅​⋅⋅⋅⋅​⋅⋅⋅⋅​∑j=1k​bj1​αmj​∑j=1k​bj2​αmj​⋅⋅⋅∑j=1k​bjd​αmj​​⎦⎥⎥⎥⎥⎥⎥⎥⎤​d×m​(9)
然后有bjαjb_j\alpha^jbj​αj:
bjαj=[bj1bj2⋅⋅⋅bjd]×[α1jα2j⋅⋅⋅αmj]=[bj1α1jbj1α2j⋅⋅⋅bj1αmjbj2α1jbj2α2j⋅⋅⋅bj2αmj⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅bjdα1jbjdα2j⋅⋅⋅bjdαmj]d×m(10)b_j\alpha^j={\left[ \begin{matrix} b_j^1\\ b_j^2\\ \cdot \\ \cdot \\ \cdot \\ b_j^d\\ \end{matrix} \right] } \times {\left[ \begin{matrix} \alpha_1^j & \alpha_2^j & \cdot & \cdot &\cdot & \alpha_m^j \\ \end{matrix} \right] }\\ = {{\left[ \begin{matrix} b_j^1\alpha_1^j & b_j^1\alpha_2^j & \cdot & \cdot & \cdot & b_j^1\alpha_m^j \\ b_j^2\alpha_1^j & b_j^2\alpha_2^j & \cdot & \cdot & \cdot & b_j^2\alpha_m^j \\ \cdot & \cdot & \cdot & & & \cdot \\ \cdot & \cdot & & \cdot & & \cdot \\ \cdot & \cdot & & & \cdot & \cdot \\ b_j^d\alpha_1^j & b_j^d \alpha_2^j & \cdot & \cdot & \cdot & b_j^d\alpha_m^j \\ \end{matrix} \right] }}_{d \times m} \tag{10} bj​αj=⎣⎢⎢⎢⎢⎢⎢⎡​bj1​bj2​⋅⋅⋅bjd​​⎦⎥⎥⎥⎥⎥⎥⎤​×[α1j​​α2j​​⋅​⋅​⋅​αmj​​]=⎣⎢⎢⎢⎢⎢⎢⎡​bj1​α1j​bj2​α1j​⋅⋅⋅bjd​α1j​​bj1​α2j​bj2​α2j​⋅⋅⋅bjd​α2j​​⋅⋅⋅⋅​⋅⋅⋅⋅​⋅⋅⋅⋅​bj1​αmj​bj2​αmj​⋅⋅⋅bjd​αmj​​⎦⎥⎥⎥⎥⎥⎥⎤​d×m​(10)
求和有:
∑j=1kbjαj=BA=[∑j=1kbj1α1j∑j=1kbj1α2j⋅⋅⋅∑j=1kbj1αmj∑j=1kbj2α1j∑j=1kbj2α2j⋅⋅⋅∑j=1kbj2αmj⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅∑j=1kbjdα1j∑j=1kbjdα2j⋅⋅⋅∑j=1kbjdαmj]d×m(11)\sum_{j=1}^{k}b_j\alpha^j=BA\\={{\left[ \begin{matrix} \sum_{j=1}^{k}b_j^1\alpha_1^j & \sum_{j=1}^{k}b_j^1\alpha_2^j & \cdot & \cdot & \cdot & \sum_{j=1}^{k}b_j^1\alpha_m^j \\ \sum_{j=1}^{k}b_j^2 \alpha_1^j & \sum_{j=1}^{k}b_j^2\alpha_2^j & \cdot & \cdot & \cdot & \sum_{j=1}^{k}b_j^2\alpha_m^j \\ \cdot & \cdot & \cdot & & & \cdot \\ \cdot & \cdot & & \cdot & & \cdot \\ \cdot & \cdot & & & \cdot & \cdot \\ \sum_{j=1}^{k}b_j^d\alpha_1^j & \sum_{j=1}^{k}b_j^d \alpha_2^j & \cdot & \cdot & \cdot & \sum_{j=1}^{k}b_j^d\alpha_m^j \\ \end{matrix} \right] }}_{d \times m} \tag{11} j=1∑k​bj​αj=BA=⎣⎢⎢⎢⎢⎢⎢⎢⎡​∑j=1k​bj1​α1j​∑j=1k​bj2​α1j​⋅⋅⋅∑j=1k​bjd​α1j​​∑j=1k​bj1​α2j​∑j=1k​bj2​α2j​⋅⋅⋅∑j=1k​bjd​α2j​​⋅⋅⋅⋅​⋅⋅⋅⋅​⋅⋅⋅⋅​∑j=1k​bj1​αmj​∑j=1k​bj2​αmj​⋅⋅⋅∑j=1k​bjd​αmj​​⎦⎥⎥⎥⎥⎥⎥⎥⎤​d×m​(11)
所以用∑j=1kbjαj\sum_{j=1}^{k}b_j\alpha^j∑j=1k​bj​αj来表示BABABA,即有下式,其中Ei=X−∑i≠jbjαjE_i=X-\sum _{i\neq j} b_j\alpha^jEi​=X−∑i​=j​bj​αj,即直接求解特定的bib_ibi​。
min⁡B∥X−BA∥F2=min⁡bi∥X−∑j=1kbjαj∥F2=min⁡bi∥(X−∑i≠jbjαj)−biαi∥F2=min⁡bi∥Ei−biαi∥F2(12)\min_{B} \|X - BA\|_F^2\\=\min_{b_i} \|X - \sum _{j=1}^k b_j\alpha^j\|_F^2\\=\min_{b_i} \|(X - \sum _{i\neq j} b_j\alpha^j) -b_i\alpha^i\|_F^2\\=\min_{b_i} \|E_i-b_i\alpha^i\|_F^2 \tag{12} Bmin​∥X−BA∥F2​=bi​min​∥X−j=1∑k​bj​αj∥F2​=bi​min​∥(X−i​=j∑​bj​αj)−bi​αi∥F2​=bi​min​∥Ei​−bi​αi∥F2​(12)
特别地,因为其他列都是固定的,所以在化简的时候可以简便,直接对EiE_iEi​进行化简即可。
即我们上述的式子min⁡bi∥Ei−biαi∥F2\min_{b_i} \|E_i-b_i\alpha^i\|_F^2minbi​​∥Ei​−bi​αi∥F2​,所以最后相当于只需要对EiE_iEi​进行奇异值分解得到最大奇异值对应的正交向量即可(Eckart-Young-Mirsky 定理)。
直接对EiE_iEi​进行奇异值分解来求解会同时修改bib_ibi​和αi\alpha^iαi,从而可能破坏原本的αi\alpha^iαi稀疏性。K-SVD对EiE_iEi​进行专门处理,αi\alpha^iαi仅保留非零元素,EiE_iEi​则仅保留bib_ibi​和αi\alpha^iαi的非零乘积项,得到Ei′E_i'Ei′​然后再进行奇异值分解,这样就保持了稀疏性。
于是有:
Ei′=UΣVT(13)E_i'=U\Sigma V^\mathbf{T} \tag{13} Ei′​=UΣVT(13)
选择对应列更改Σ\SigmaΣ进行更新即可。

Tips

奇异值分解其实与特征值分解有很紧密的相似性,我们针对的特征值分解λ\lambdaλ一般都是方阵,用来提取这个矩阵最主要的特征,而现实中方阵的要求却较为严格。那么一般矩阵就是用奇异值分解来提取特征信息。

  • 实矩阵A∈Rm×mA\in\mathbb{R}^{m\times m}A∈Rm×m,特征值分解:A=Qm×mΣQm×m−1A=Q_{m\times m}\Sigma Q_{m\times m}^{-1}A=Qm×m​ΣQm×m−1​,QQQ是矩阵AAA的正交特征向量组成的矩阵,QQQ是正交阵,所以转置和逆都是一样的(Q−1=QT)(Q^{-1}=Q^T)(Q−1=QT),另外∑\sum∑是对称阵,且只有主对角线上有元素,值是矩阵AAA的各特征值λ\lambdaλ(λ\lambdaλ的位置对应特征向量的位置)。
  • 实矩阵A∈Rm×nA\in\mathbb{R}^{m\times n}A∈Rm×n,奇异值分解:A=Um×mΣm×nVn×nTA=U_{m\times m}\Sigma_{m\times n}V_{n\times n}^TA=Um×m​Σm×n​Vn×nT​,其中∑\sum∑也同特征值分解一样,是由奇异值组成的对称阵,仅主对角线上有元素。

K-SVD: An Algorithm for Designing Overcomplete Dictionaries for Sparse Representation相关推荐

  1. k Nearest Neighbor Algorithm

    k Nearest Neighbor Algorithm k Nearest Neighbor(kNN) algorithm算法和k-Means算法一样,都是简单理解,但是实际效果出人意料的算法之一. ...

  2. 图像处理与计算机视觉:基础,经典以及最近发展(3)计算机视觉中的信号处理与模式识别

    从本章开始,进入本文的核心章节.一共分三章,分别讲述信号处理与模式识别,图像处理与分析以及计算机视觉.与其说是讲述,不如说是一些经典文章的罗列以及自己的简单点评.与前一个版本不同的是,这次把所有的文章 ...

  3. 图像处理与计算机视觉基础、经典以及最近发展

    图像处理与计算机视觉基础,经典以及最近发展 By xdyang(杨晓冬xdyang.ustc@gmail.com) 一. 绪论 1. 为什么要写这篇文章 从2002年到现在,接触图像快十年了.虽然没有 ...

  4. 图像处理与计算机视觉基础相关领域的经典书籍以及论文

    原文的链接是http://www.iask.sina.com.cn/u/2252291285/ish. 我非常感谢原作者杨晓冬辛勤地编写本文章,并愿意共享出来.我也希望转载本文的各位朋友,要注明原作者 ...

  5. 【转】【转】 图像处理与计算机视觉的经典书籍

    转载转自http://blog.csdn.net/a4079/article/details/23617423 ******************************************** ...

  6. 小白理解k-svd算法

    小白理解k-svd字典学习 一.字典学习 字典学习也可简单称之为稀疏编码,字典学习偏向于学习字典D.从矩阵分解角度,看字典学习过程:给定样本数据集Y,Y的每一列表示一个样本:字典学习的目标是把Y矩阵分 ...

  7. IEEE Trans 2006 使用K-SVD构造超完备字典以进行稀疏表示(稀疏分解)

    K-SVD可以看做K-means的一种泛化形式,K-means算法总每个信号量只能用一个原子来近似表示,而K-SVD中每个信号是用多个原子的线性组合来表示的.    K-SVD算法总体来说可以分成两步 ...

  8. Dictionary Learning详解(附带K-SVD算法)

    Dictionary Learning详解 第四十五次写博客,本人数学基础不是太好,如果有幸能得到读者指正,感激不尽,希望能借此机会向大家学习.本文主要对字典学习(Dictionary Learnin ...

  9. (转...)图像处理与计算机视觉 基础、经典以及最近发展

    [-] 图像处理与计算机视觉基础经典以及最近发展 一 绪论 为什么要写这篇文章 图像处理和计算机视觉的分类 图像处理和计算机视觉开源库以及编程语言选择 本文的特点和结构以及适合的对象 二 图像处理与计 ...

最新文章

  1. chocolatey的使用
  2. leetcode - Same Tree
  3. java大会主题曲_网易未来大会主题曲发布,从创作到演唱都由AI包办
  4. 这些Java代码优化细节,你需要注意!
  5. php自己总结的一些经典的实例
  6. python3扫盲系列-(3)
  7. springmvcdate类型接收不到_无线电小课堂:如何在市区环境有效接收短波信号,选什么天线?...
  8. Notable magic numbers
  9. nginx应用总结(1)--基础认识和应用配置
  10. extract 模板 php,PHP自定义函数实现assign()数组分配到模板及extract()变量分配到模板功能示例...
  11. UI设计灵感|移动应用的数据表盘都是怎么设计的?
  12. Opencv图像数据结构剖析
  13. win7系统网络计算机,Win7系统打开局域网没看到其他计算机的修复方法
  14. c++做界面_一看就会做系列 SmartLink远程诊断发布需求指南简单版
  15. FasterRCNN调试笔记
  16. magisk卸载内置软件_【教程篇】安卓手机卡刷Magisk(面具)获取Root教程
  17. love2d引擎开发资源合集
  18. 移动开发福利:GMTC全球移动技术大会解决方案专场免费报名!
  19. ppt文字磨砂玻璃效果制作教程
  20. 51单片机 c语言 汇编,51单片机之时钟(C语言和汇编两种方式实现)

热门文章

  1. java 防篡改_用JAVA写一个简易图片防篡改
  2. Redis集群——去中心化模式
  3. 什么是多尺度密集网络 - MSDNet ?
  4. openstack出错The server is currently unavailable. Please try again at a later time.(HTTP 503)
  5. 用好“亲和图”带你拨开云雾见月明
  6. 评论:后MWC2012的一些感悟
  7. 2017.12.5 八周第二次课
  8. 汉字转拼音 java_Java汉字转拼音工具类完整代码实例
  9. 在vue项目中使用html2canvas实现保存网页为图片
  10. 《论文写作》课堂收获