网上关于机器学习算法的讲解很多,不过推导过程可能比较繁琐,结论比较隐蔽。为了能够让c/c++的初学者通过代码简单的实现、加深理解,所以本文章简单的总结出结论,尽量减少推导过程。并且将各个变量的范围、意义做出解释。
我目前大学本科在读,非人工智能专业,第一次接触机器学习,本文章仅是我的学习记录和总结,所有的处理方式都不一定是最正确的。但是致力于让只学完c\c++基本语法的人也能实现该算法。
不知道怎么处理数据、得到“输入”的请看我的文章:《机器学习数据的预处理》(如果找不到说明我还没写)。
另外,我会尽可能多打括号,防止各位对到底对什么求和感到困惑,变量也用x[a]而非
表示,便于直接写成代码。
首先:
该算法的输入可以是连续的。另外,可以在预处理就使所有样本的同一个输入平均值为0,建议直接采用“标准化”。
矩阵类:
在c\c++编写pca和lda前,最好自己事先编写一个矩阵类,或者在网上找别人的代码实现矩阵运算。下面举例一个简单矩阵类,介绍所有需要的功能及变量:
图中标出了所有变量,其意义如下:
1. width,矩阵宽度。显然是整型
2. depth,矩阵高度。显然是整型
3. m[a][b],矩阵第b行,第a个的值。是浮点型
需要的功能:
1. 矩阵乘矩阵,注意a*b矩阵乘以c*a矩阵将得到c*b矩阵
2. 矩阵乘\除常数,每项都乘上该常数
3. 矩阵加\减矩阵,各项相减
4. 矩阵转置,注意a*b会变成b*a
5. 矩阵求逆矩阵,尽量用高效的算法,否则高纬度的样本会使这一步非常非常慢。这里提供一种可行的方法,具体实现过于简单,不再赘述。将原矩阵M与一个单位矩阵变为增广矩阵
↓
经过初等变换,从左往右每一列减去左边几列对应值的x倍(图中及该x不表示某个具体值,仅表示该位置不为0,图中及该x与任一其他x不相等)
↓
经过初等变换,从右往左每一列减去右边几列对应值的x倍
↓
截取下半部分作为矩阵N。N为M的逆矩阵。
PCA:
在了解下方的变量前,请确认:
1. 你有非常多的样本(即图中表示的非常多的点)
2. 你的每个样本有多个的输入的值(即每个点处于二维平面、三维或多维空间中)。
3. 如果已经过“标准化”则所有样本输入点的同个输入值平均值为0,换而言之,如果一个点以表示的话,则样本里所有点的
与
与
......都为0。如果没有经过标准化,请将所有点变成
(很简单不说明方法)。
该算法将根据所有点的位置找到合适的投影方式为所有点降维(如图,将所有点降到1维似乎对点的分类没有帮助,可以将极高维的样本点降到合适(具体多少合适请因情况而异,具体下方会说明)的维度后,用lda实现分类,这也是为什么将pca与lda放在一起讲的原因)
所有变量,意义如下:
1. K,你期望通过pca降到的维度。大于1但是小于等于输入样本点的维度,与lda结合使用时请多次尝试改变这个数值找到最优值。是整型
2. Mi,全部的输入样本。其中包含一个width(样本数量,即点的数量),一个depth(每个样本输入值的数量,即点的维度),in[a][b],第a个样本的第b维的值。是矩阵类(见上方矩阵类)
3. Ms,根据下方公式简单相乘得到的协方差矩阵,其width与depth符合矩阵相乘所得的宽与高。是矩阵类
4. Mtemp,输出的用于降维的矩阵。是矩阵类
5. Mo,输出的全部样本。其width与Mi的width相同,depth等于K。是矩阵类
学习:
1. 拷贝所有样本点到Mi中的in[a][b],并赋予Mi正确的width与depth。
2. 求协方差矩阵
(转置见上方矩阵类)
3. 用下方讲的 “qr分解” 方法求出Ms的所有特征值与特征向量,找出特征值最大的K个特征向量,纵向排列到Mtemp的in[a][b]中,请确认Mtemp的width等于K,Mtemp的depth等于Mi的depth
4. 得到输出的样本,显然样本的数量没有改变,还是width个,而样本的维度由depth维变成了K维
得到结果:
因为是与LDA一起写,所以这里得到的结果有两个:
其一,是输出的Mo,作为lda的学习样本。
其二,是用于降维的矩阵Mtemp,在学习结束后用测试样本测试或投入使用时,直接用乘以原始样本来降维原始样本。
LDA:
lda也是降维,我是这么理解的:pca的降维,是使所有点尽可能分散。lda的降维,是使不同类点尽可能分散,同类点尽可能集中。
另外,以下所有变量与pca里出现的变量并无关系,即使是重名也不是同一个变量,请注意。
所有变量,意义如下:
1. K,你期望通过lda降到的维度。小于等于输入样本点的维度,且可以等于1。是整型
2. Mi,全部的输入样本,拷贝自pca的Mo。是矩阵类
3. num_o,答案的种数。显然是整型
4. o[a],第a个样本所对应的答案。是整型
5. num_x[a],答案为a的样本的个数。显然是整型
6. x[a][b],答案为a的第b个样本。其width等于1,其depth等于Mi的depth。是矩阵类
7. μ[a],答案为a的所有样本各个值的平均值。其width等于1,depth等于Mi的depth。是矩阵类
8. Msw,类内协方差矩阵。是矩阵类
9. Msb,类间协方差矩阵。是矩阵类
10. Mtemp,输出的用于降维或得到答案的矩阵。是矩阵类
学习:
1. 拷贝pca的Mo到这里的Mi,拷贝输入样本的所有答案到o[a],显然经过pca的变换不会影响样本的顺序,所以答案也按原来的顺序拷贝即可。
2. 将Mi中所有答案不同的样本分别拷贝到Mi[a][b]中。并计算μ[a]:
3. 计算类内协方差矩阵。
(公式中是 还是
取决于你的x[a][b]是竖着的还是躺着的。如果严格按照上文方法定义该矩阵就应该使用
)
3. 计算类间协方差矩阵
(P{o=k}即o[a]=k的概率,计算非常简单,不多介绍)
4. 用上方讲的 “qr分解” 方法求出矩阵 “ ” (如果程序卡住可能是矩阵求逆过慢)的所有特征值与特征向量,找出特征值最大的K个特征向量,纵向排列到Mtemp的in[a][b]中,请确认Mtemp的width等于K,Mtemp的depth等于Mi的depth
输出的结果:
注意pca的Mtemp与lda的Mtemp需要作为学习结果保存。
1. 如果K=1,则用乘以样本可以使所有点最终降到1维:
,每一类点都可以得到一个
,这些
可以作为学习结果保存。则测试或投入使用时,得到样本点Input,可以通过计算:
再将 x 与每一类的 ,比较大小,最接近的一项作为答案输出。
2. 如果K>1,则可以得到:
像数据经过pca处理再传递到lda一样,lda可以再将处理完的样本传递至下一步处理。
QR分解求特征值及特征向量:
为什么放到下面讲,因为就针对我自己而言,这个东西是最劝退的,当然如果网上找到的矩阵类中包含求特征值与特征向量的方法,则可以跳过这个直接结束。
需要的变量:
1. N,迭代次数,越大越准确(我做测试时取的是50,可供参考)。显然是整型
2. A,原矩阵,即需要求特征值与特征向量的矩阵。是矩阵类
3. B[a],第a次迭代准备被分解的矩阵。是矩阵类
4. Q[a],第a次迭代分解出的Q矩阵。是矩阵类
5. R[a],第a次迭代分解出的R矩阵。是矩阵类
6. λ[a],输出的所有特征值。是浮点型
7. O,输出的矩阵,每一列是A的一个特征向量。是矩阵类
qr分解及迭代:
1. 拷贝原矩阵即A到B[0]。
2. 如下方式分解B[0]得到Q、R。先申请几个临时变量
1. q[a]。是矩阵类
2. r[a]。是矩阵类
3. temp[a],用于将向量单位化。是浮点型
先将B[0]分解为width个宽度为1,高度为depth的矩阵(向量)q。
↓
(如图所示,一个3*3矩阵被分为3个1*3矩阵:q[0]、q[1]、q[2])
将一个单位矩阵分解为width个宽度为1,高度为depth的矩阵(向量)r。
↓
(如图所示,一个3*3矩阵被分为3个1*3矩阵:r[0]、r[1]、r[2])
从0到a按顺序求出每个q[a]、r[a](注意这不是严谨的数学公式),每个
(a>0)
此时,计算出所有temp[a]。
按顺序合并并处理Q[0]、R[0]:
3. 按照分解B[0]的方式,如下迭代N次,第a次迭代时:
最终得到B[N],形如:
其对角线上各值即为特征向量。拷贝到λ[a]中。
4. 对于每个λ[a],得到A-λ[a],形如:
最右增加一列0,形如:
将任意一行,比如第一行变成{1,0,0,......,1}(原因不解释),形如:
最后做行初等变换使之形如:
取最后一列即为该λ[a]对应的特征向量,将所有这些向量按顺序填入到O中,与λ[a]一起作为输出。
最后:
写的比较匆忙,很多地方没有检查,我本人学习这些算法的时间也很短,如果存在任何问题,有任何意见或建议欢迎指出,我会马上进行更正。
另外,要深入理解一种算法,其具体原理和推导过程也是需要学习的。网上有太多这方面的文章,在这就不做详细解释。
谢谢。