在学习李航《统计学习方法》的逻辑斯特回归时,正好coursera上相应的线性回归和逻辑斯特回归都学习完成,在此就一起进行总结,其中图片多来自coursera课程上。
线性回归是机器学习中很好理解的一种算法。我们以常见的房屋销售为例来进行简单分析:
假设我们统计的一个房屋销售的数据如下:
在此,我们从单一变量谈起,直观上比较容易理解。训练集定义为 {(x(1),y(1)),(x(2),y(2)),…,(x(m),y(m))} ,其中 x 是输入特征, y 是输出目标, m 是样本的总数目。线性回归的最终目的如下所示,就是通过学习,得到一个拟合函数,使得通过输入特征就能预测目标输出值,本例即通过房屋大小估计房屋价格。
实际线性回归假设能够拟合各种不同的曲线,实际的房子价格可能与房间面积、房间厅室、房间朝向等多个变量有关,我们可以定义特征 x={x1,x2,…,xi} 那么我们可以定义拟合函数为:
在线性回归中,我们定义代价函数为:
梯度下降法是求解无约束最优化问题的一种最常见的方法,其实现简单,易于理解。如下图所述带有二元参数的目标函数 J(θ0,θ1) ,求解其最小值。我们可以初始化一个参数值 (θ0,θ1) ,然后求 J(θ0,θ1) 在各个方向的偏导,通过一个学习步长来改变参数,并最终求得 J(θ0,θ1) 的最小值。具体算法流程为:
end for
在线性线性回归中:
对于合适的学习率 α ,目标函数 J(θ) 在每次迭代中都会减小,因此可以通过 J(θ) 的值检测算法的正确性。在实际操作中, α 太小,算法的收敛速度会很慢,当 α 太大时,则会出现震荡,学习不到最佳参数。
梯度下降法需要不断的迭代计算,一般来说,收敛速度都会比较慢,另一种快速求解最佳解的方法是最小二乘法,具体公式为:
过拟合是机器学习中很普遍的例子,指的是训练模型在训练集上有很好的分类回归效果,但是在新的测试数据集上表现却很差,即模型的泛化能力差。
如下图所示,依旧以“大小-房价”线性回归为例来说明。房价与房屋大小可能是非线性关系,如图1所示,假设模型为 θ0+θ1x ,即线性关系,拟合效果不好,称为欠拟合;图2则是非线性拟合,假设模型为 θ0+θ1x+θ2x2 ,能够比较好的拟合两者之间的关系;图3所示的多项式 θ0+θ1x+θ2x2+θ3x3+θ4x4 则能够拟合所有的数据,即对训练样本的学习效果很好,但是这明显不是我们所期望的学习模型,存在严重过拟合。
解决过拟合问题常有以下几种方式:
正则化
- 保持所有特征,但是减小学习参数 θ 的值。
如上图3所示,通过惩罚项使最终的学习参数 θ3,θ4 极小,则最终模型与图2模型很相近。即:
当 λ 较小时,极限情况下 λ=0 ,则不考虑模型的复杂度,是原有的损失函数
回归问题一般是连续预测:如房价预测、销售额预测,即输出 y 的状态可能有无限多种;
分类问题则是离散预测:邮件分类(垃圾/正常),细胞检测(正常/癌变),输出一般对应有限状态。
一般来说,线性回归不能直接用于分类问题,因为回归是连续性模型,而且受噪音比较大,我们一般选择logistic回归来进行分类。logistic本质是线性回归,只是在特征到结果的映射中加入了一层映射函数。
对于二分类系统,我们希望学习模型的输出为0或1,对于固定的特征,我们希望学习模型预测其属于正例的概率。即: hθ(x)=P(y=1|x:θ) ,对于二分类系统, P(y=1|x:θ)+P(y=0|x:θ)=1 。logistic的假设函数为:
对于分类问题,最终就是得到一个分类边界,使样本能够被准确区分开。如下图所示的两类样本,我们假设红色为正样本,即 y=1 ,蓝色为负样本,即 y=0 。分类决策面有两个特征 x1,x2 ,因此我们定义假设模型为: hθ(x)=g(θ0+θ1x1+θ2x2) 。取 θT=[−3,1,1] ,即分类平面为 −3+x1+x2=0 ,我们可以看到:
逻辑斯特回归代价函数一般定义为:
logistic回归也可用于扩展用于多分类问题,解决办法常见的就是一对多。如下图所示有三类样本,我们可以先用一个分类器将类别一与另外两类区分开(右图1),然后用同样的办法训练两个分类器,将每个类别区分开。在得到的三个假设模型中,我们计算每个样本在每个模型中的值,即概率,通过选取最大的概率,就能确定样本所属的类别。
最后我们通过Python实现了简单的logistic二分类问题,具体代码如下:
读取txt文件中的训练数据,包含特征和标签,并给特征加上偏置项1
# load training data set
def loadData(path):
dataMat = []; labelMat = [];
f = open(path)
data= f.read().split()
for datastring in data:
dataMat.append([1,float(datastring.split(',')[0]),float(datastring.split(',')[1])])
labelMat.append(int(datastring.split(',')[2]))
return dataMat,labelMat
从txt中读取的特征值很大,进行标准归一化之后进行训练。
def featureNormalize(dataMat):
dataMatrix = mat(dataMat)
data_norm = dataMatrix;
m,n = shape(dataMatrix)
mu = mean(dataMatrix[:,1:3],axis = 0)
sigma = std(dataMatrix[:,1:3],axis = 0)
data_norm[:,1:3]= [x/y for x,y in zip((dataMatrix[:,1:3]-tile(mu,(m,1))),tile(sigma,(m,1)))]
return data_norm,mu,sigma
绘制最终分类效果图和损失函数的变化
# plot data set
def plotdata(theta,mu,sigma,dataMat,labelMat):
dataArr = array(dataMat)
positive_x =[]; positive_y = []
negtive_x =[]; negative_y = []
for i in range(len(labelMat)):
if 1 == int(labelMat[i]):
positive_x.append(dataArr[i,1]);positive_y.append(dataArr[i,2])
else:
negtive_x.append(dataArr[i,1]);negative_y.append(dataArr[i,2])
fig1 = plt.figure('fig1')
ax = fig1.add_subplot(111)
ax.scatter(positive_x,positive_y,s=30,c='red',marker='s')
ax.scatter(negtive_x,negative_y,s=30,c='green')
min_x = min(dataArr[:,1])
max_x = max(dataArr[:,1])
y_min_x = (-theta[0]-theta[1]*(min_x-mu[0,0])/sigma[0,0])*sigma[0,1]/theta[2]+mu[0,1]
y_max_x = (-theta[0]-theta[1]*(max_x-mu[0,0])/sigma[0,0])*sigma[0,1]/theta[2]+mu[0,1]
ax.plot([min_x,max_x],[y_min_x,y_max_x],'-g')
plt.xlabel('X1');plt.ylabel('X2');plt.legend();
plt.show()
# plot cost
def plotJ(J_history):
fig2 = plt.figure('fig2')
ax = fig2.add_subplot(111)
x = arange(0,len(J_history),1)
ax.plot(x,J_history)
plt.xlabel('Iter');plt.ylabel('cost');plt.legend();
plt.show()
梯度下降算法:
# sigmoid function
def sigmoid(z):
return 1.0 / (1+exp(-z))
# train
def gradientReg(dataMat,labelMat,alpha,lambda1,MaxIter):
dataMatrix = mat(dataMat)
labelMatrix = mat(labelMat).transpose()
m,n = shape(dataMatrix)
J = zeros((MaxIter,1))
theta = zeros((n,1))
for k in range(MaxIter):
h = sigmoid(dataMatrix*theta)
J[k] = 1.0/m*sum(-multiply(labelMatrix,log(h))-multiply((1-labelMatrix),log(1-h)))+\
lambda1/(2*m)*(sum(theta[2:n]**2))
error = (h-labelMatrix)
for i in range(n):
if 0 == i:
theta[i] = theta[i] - alpha*1.0/m*(error.transpose()*dataMatrix[:,i])
else:
theta[i] = theta[i] - alpha*1.0/m*(error.transpose()*dataMatrix[:,i]+lambda1*theta[i])
return theta,J
通过训练模型进行分类预测
# predict
def predict(theta,dataMat):
prob = sigmoid(dataMat*theta)
p = double(prob>0.5)
return p;
主函数
# main
if __name__=="__main__":
dataMat = []; labelMat = [];
alpha = 0.1;lambda1 = 0; MaxIter = 1000;
datapath = 'F:\Program\Python\Machine_Learning\Logistic\src\ex2data1.txt'
dataMat,labelMat=loadData(datapath)
data_norm,mu,sigma =featureNormalize(dataMat)
theta,J_history = gradientReg(data_norm,labelMat,alpha,lambda1,MaxIter)
plotdata(theta,mu,sigma,dataMat,labelMat)
plotJ(J_history)
p = predict(theta,data_norm)
print "the classify accuracy is:%.3f%%" %(mean(double(p.transpose() == labelMat)) * 100)
当 α=0.1,λ=0 时,分类效果图为:
当 α=0.1,λ=10 时,分类效果图为:
当 α=1,λ=0 时,分类效果图为:
通过对比图1和图2,可以发现当调节参数 λ 变化时,代价函数会改变,分类效果和分类结果都会变化,说明它通过引入正则项可以改变模型的复杂程度;通过对比图1和图3,可以发现,在一定范围内 α 越大,代价函数收敛越快,模型学习迭代次数越少,但是模型最终分类效果和分类结果都没变化,学习率只影响了模型训练速度,而不会影响模型的性能。
PS:
本文主要参考了李航《统计学习方法》和斯坦福的在线课程,图表也多引用自斯坦福课程,主要用于自我学习总结,代码完整示例见此处。