回归方程采用线性方程。
注意结尾有常数项。这使得想将运算过程看作两矩阵的某行某列简单相乘时,需要在X矩阵(特征值矩阵)中,增加一值为1的列。
损失函数采用了算法类似求方差的函数。将回归方程带入后,可以解出损失函数值同线性回归方程参数之间的关系。而后求偏导(手算),进而得到梯度的表达式。下面的代码直接采用了这一结果。
不论是一元还是多元的问题,都可以使用下面的代码,调用gradient函数解决。
import numpy as np
def estimate(theta,datax):
theta=theta.reshape(-1,1)
esty=np.dot(datax,theta)
return esty
def returndj(theta,datax,truey):
esty=estimate(theta,datax)
N=datax.shape[0]
n=datax.shape[1]
dj=np.zeros((n,1))
for i in range(n):
dj[i,0]=2.0/N*np.dot((esty-truey).T,datax[:,i])
return dj
def returnj(theta,datax,truey):
esty=estimate(theta,datax)
temp=truey-esty
N=datax.shape[0]
j=0
for i in range(N):
j=j+temp[i,0]**2
j=j/N
return j
def gradient(datax,datay,thetainit,absjmax=1e-10,tmax=1e5,learningrate=0.01):
N=datax.shape[0]
n=datax.shape[1]
x0=np.ones((N,1))
datax=np.column_stack((x0,datax))
theta=thetainit
oldtheta=np.zeros((n,1))
t=0
while t<tmax:
dj=returndj(theta,datax,datay)
oldtheta=theta
theta=theta-learningrate*dj
absj=abs(returnj(oldtheta,datax,datay)-returnj(theta,datax,datay))
if absj<absjmax:
break
else:
t+=1
return theta
采用的回归方程是线性回归方程的引申,将线性方程所得结果带入sigmoid函数(y=1/(1+e^(-t)))以转化为(0,1)内的值,可以用来表示概率。
损失函数采用了对数结构,相比于原来类似于求方差的运算,对数结构似乎能更好地剔除与实际情况偏离较大的值,当然,这可能要以偏离较小值的更多出现为代价。
求梯度的过程也要手算(或者套结论),注意这里由于函数比较复杂,不应一次将函数彻底展开,而应保持一定的抽象程度使式子更加简洁。但要注意各部分之间的关系,如x为自变量时,sigmoid函数的导数等于sigmoid函数的平方乘上e的-x次方,设e的-x次方为t,则sigmoid函数等于1/(1+t)。
代码和上面的区别不大,因为都使用了梯度下降,而且两者损失函数值与回归方程参数之间关系的表达式只在一处不同(一个是1一个是2),挺有意思的。
import numpy as np
def sigmoid(y):
p=1.0/(1+np.exp(-y))
return p
def los(estp,truep):
f=-(truep*np.log(estp)+(1-truep)*np.log(1-estp))
return f
def estimate(theta,datax):
theta=theta.reshape(-1,1)
esty=np.dot(datax,theta)
N=esty.shape[0]
estp=np.zeros((N,1))
for i in range(N):
estp[i,0]=sigmoid(esty[i,0])
return estp
def returndj(theta,datax,truep):
estp=estimate(theta,datax)
N=datax.shape[0]
n=datax.shape[1]
dj=np.zeros((n,1))
for i in range(n):
dj[i,0]=1.0/N*np.dot((estp-truep).T,datax[:,i])
return dj
def returnj(theta,datax,truep):
estp=estimate(theta,datax)
N=estp.shape[0]
j=0
for i in range(N):
j=j+los(estp[i,0],truep[i,0])
j=j/N
return j
def gradient(datax,datay,thetainit,absjmax=1e-10,tmax=1e5,learningrate=0.01):
N=datax.shape[0]
n=datax.shape[1]
x0=np.ones((N,1))
datax=np.column_stack((x0,datax))
theta=thetainit
oldtheta=np.zeros((n,1))
t=0
while t<tmax:
dj=returndj(theta,datax,datay)
oldtheta=theta
theta=theta-learningrate*dj
absj=abs(returnj(oldtheta,datax,datay)-returnj(theta,datax,datay))
if absj<absjmax:
break
else:
t+=1
return theta
这次主要使用了梯度下降算法。解决线性回归问题还可以使用最小二乘法,与梯度下降法不同,最小二乘法通过数学的演绎直接给出了最小值点的解法,应当注意。