最近在研究人脸特征点检测,之前没接触过,有些论文虽然能看懂,但是细节的部分可能之前的论文都有提到过,就没有再提。所以找了一篇稍微早一点的文章开始学习起来。
MATLAB版本的代码基本上都看懂了,不过作者可能之前做过ASM,代码中有一些方法没有使用,或者使用了比较复杂的算法,其实只使用了其中一小部分结果,看起来有点难。准备找时间自己实现一下,看了代码应该不算难(又立flag了,督促自己不要偷懒)。这里先简单的实现SDM算法,不做人脸应用的代码。
简单的说SDM算法是牛顿法的一种改进,牛顿法在求解梯度的时候需要用到Hessian矩阵的逆以及Jacobian矩阵,在高维度的情况下这个计算量较大,而且有些情况下Hessian矩阵是不可逆的。这篇论文通过监督学习来学习Hessian矩阵的逆以及Jacobian矩阵的乘积,这样就可以省去复杂的计算过程。
下面代码是SDM实现的例子,完成对曲线的拟合。拟合过程中会打印误差值。R保存了要学习的参数。steps是蒙特卡洛采样的参数,采样点间隔越小,拟合效果越好。
代码简单的说就是在训练过程中已知dx,dy,然后可以通过最小二乘训练R,然后在Test过程中,已知dy可以通过R求解x的最优解。论文中提到了训练参数R和b,应该可以合到一起,这里只有参数R。
#coding:utf-8
import numpy as np
import math
import matplotlib.pyplot as plt
#采样间距
steps = [0.25,0.025,0.0025,0.00025]
y = np.mat(np.arange(-1,1,0.001))
x = np.mat(np.arcsin(y))
for i in range(len(steps)):
#train
R = [] #要学习的参数
n = 10 #迭代次数,一般要多次迭代才能达到较好的效果,减小n会看到误差增大
y0 = np.mat(np.arange(-1,1,steps[i]))
x0 = np.mat(np.arcsin(y0))
x_start = np.mat([0] * x0.shape[1])
for j in range(n):
dx = x0 - x_start
dy = y0 - np.sin(x_start)
#岭回归
r_tmp = (dy*dy.T+np.eye(1)*0.001).I*(dy*dx.T)
x_start = x_start + r_tmp*dy
R.append(r_tmp)
print np.linalg.norm(dx) #求误差,所有元素平方和开根
print '--------------------'
#test
y_pred = y
x_pred = np.mat([0]*x.shape[1])
for j in range(n):
dy = y - np.sin(x_pred)
x_pred = x_pred + R[j]*dy
# += 上面一句之前用了这个,一直有问题,有点坑,对于变量python是可以这么写的,矩阵不可以吧
plt.plot(np.asarray(x)[0], np.asarray(y)[0], 'r')
plt.plot(np.asarray(x_pred)[0], np.asarray(y_pred)[0], 'b')
plt.show()
下面是拟合的效果图,红色为真实曲线,蓝色为训练得到的曲线。