在训练的初始阶段,将要构建一个逻辑回归模型来预测,某个学生是否被大学录取。设想你是大学相关部分的管理者,想通过申请学生两次测试的评分,来决定他们是否被录取。现在你拥有之前申请学生的可以用于训练逻辑回归的训练样本集。对于每一个训练样本,你有他们两次测试的评分和最后是被录取的结果。为了完成这个预测任务,我们准备构建一个可以基于两次测试评分来评估录取可能性的分类模型。
#导入需要的库
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
#将ex2data1数据导入data中
path="ex2data1.txt"
data=pd.read_table(path,header=None,names=["Exam 1","Exam 2","Admitted"],sep=',')
data.head()
Exam 1 | Exam 2 | Admitted | |
---|---|---|---|
0 | 34.623660 | 78.024693 | 0 |
1 | 30.286711 | 43.894998 | 0 |
2 | 35.847409 | 72.902198 | 0 |
3 | 60.182599 | 86.308552 | 1 |
4 | 79.032736 | 75.344376 | 1 |
创建Exam1,Exam2的散点图,并使用颜色来可视化(样本是正的(被接纳)还是负的(未被接纳)?)
#提取录取的数据和未被录取的数据
positive=data[data["Admitted"].isin([1])] #isin函数用于提取相应的行
negative=data[data["Admitted"].isin([0])]
#绘图
fig,ax=plt.subplots(figsize=(9,6))#设置图形大小
ax.scatter(positive["Exam 1"],positive["Exam 2"],s=50,c="blue",marker="o",label="Admitted") #设置点坐标、大小、颜色、图形、标签
ax.scatter(negative["Exam 1"],negative["Exam 2"],s=50,c="red",marker="x",label="Not Admitted")
ax.legend(loc=1)#图例放在右上角
ax.set_xlabel("Exam 1 Score")
ax.set_ylabel("Exam 2 Score")
plt.show()
由上图可以看到两种图形直接有一个清晰的决策边界。
接下来实现逻辑回归,训练一个模型来预测结果。
g g g代表 s i g m o i d sigmoid sigmoid型函数: g ( z ) = 1 1 + e − z g(z)=\frac{1}{1+e^{-z}} g(z)=1+e−z1
逻辑回归模型的假设函数: h θ ( x ) = g ( θ T x ) = 1 1 + e − θ T x h_{θ}(x)=g(θ^{T}x)=\frac{1}{1+e^{-θ^{T}x}} hθ(x)=g(θTx)=1+e−θTx1
#定义sigmoid函数:g(z)
def sigmoid(z):
return 1/(1+np.exp(-z))
#验证g(z)函数的功能
fig,ax=plt.subplots(figsize=(8,6))
test=np.arange(-10,10,step=0.5)
ax.plot(test,sigmoid(test),c='red')
plt.show()
sigmoid函数:g(z)验证成功,接下来编写代价函数来评估结果。
J ( θ ) = 1 m ∑ i = 1 m [ − y ( i ) l o g ( h θ ( x ( i ) ) ) − ( 1 − y ( i ) ) l o g ( 1 − h θ ( x ( i ) ) ) ] J(θ)=\frac{1}{m}\sum^{m}_{i=1}[-y^{(i)}log(h_{\theta}(x^{(i)}))-(1-y^{(i)})log(1-h_{\theta}(x^{(i)}))] J(θ)=m1∑i=1m[−y(i)log(hθ(x(i)))−(1−y(i))log(1−hθ(x(i)))]
ps:
np.multiply():数组和矩阵对应位置相乘,输出与相乘数组/矩阵的大小一致
@:对矩阵执行矩阵乘法运算
*:对数组执行对应位置相乘;对矩阵执行矩阵乘法运算
def cost(Theta,X,Y):#传入的X,Y是表格,Theta是数组
#将X,Y从表格转换成矩阵,Theta从数组转成矩阵
Theta=np.matrix(Theta)
X=np.matrix(X.values)
Y=np.matrix(Y.values)
first=np.multiply(-Y,np.log(sigmoid(X@Theta.T)))
second=np.multiply(1-Y,np.log(1-sigmoid(X@Theta.T)))
return np.sum(first-second)/len(X)
#加入向量x0(x0恒等于1)
data.insert(0,"Ones",1)
#提取数据X,Y,Theta
cols=data.shape[1]
X=data.iloc[:,0:cols-1]
Y=data.iloc[:,cols-1:cols]
Theta=np.zeros(3)
#计算初始化参数的代价函数(Theta为0)
cost(Theta, X, Y)
0.6931471805599453
设计函数来计算训练数据、标签和一些参数thata的梯度:
∂ J ( θ ) ∂ θ j = 1 m ∑ i = 1 m ( h θ ( x ( i ) ) − y ( i ) ) x j ( i ) \frac{\partial J(\theta)}{\partial \theta_{j}}=\frac{1}{m}\sum_{i=1}^{m}(h_{\theta}(x^{(i)})-y^{(i)})x_{j}^{(i)} ∂θj∂J(θ)=m1∑i=1m(hθ(x(i))−y(i))xj(i)
def gradient(Theta,X,Y):#传入的X,Y是表格,Theta是数组
#将X,Y从表格转换成矩阵,Theta从数组转成矩阵
Theta=np.matrix(Theta)
X=np.matrix(X.values)
Y=np.matrix(Y.values)
#grad记录θ向量每一个元素的梯度下降值
Theta_cnt=Theta.shape[1]
grad=np.zeros(Theta.shape[1])
#计算误差向量
error=sigmoid(X*Theta.T)-Y
for i in range(Theta_cnt):
tmp=np.multiply(error,X[:,i])
grad[i]=np.sum(tmp)/len(X)
return grad
上述函数实际上没有执行梯度下降,仅仅在计算一个梯度步长
下面是用初始数据和参数为0的梯度下降法的结果:
gradient(Theta, X, Y)
array([ -0.1 , -12.00921659, -11.26284221])
由于使用的是Python,可以用SciPy的"optimize"命名空间来计算成本和梯度参数
最常使用的参数:
返回:
#可以SciPy's truncated newton(TNC)实现寻找最优参数
import scipy.optimize as opt
result=opt.fmin_tnc(func=cost, x0=Theta,fprime=gradient,args=(X,Y))
result
(array([-25.16131863, 0.20623159, 0.20147149]), 36, 0)
这个结论下代价函数计算结果:
cost(result[0],X,Y)
0.20349770158947458
绘制决策曲线
plot_x=np.linspace(30,100,100)
plot_y=( - result[0][0] - result[0][1] * plot_x) / result[0][2]
#绘图
fig,ax=plt.subplots(figsize=(9,6))#设置图形大小
ax.plot(plot_x,plot_y,c='y',label='Prediction')
ax.scatter(positive["Exam 1"],positive["Exam 2"],s=50,c="blue",marker="o",label="Admitted") #设置点坐标、大小、颜色、图形、标签
ax.scatter(negative["Exam 1"],negative["Exam 2"],s=50,c="red",marker="x",label="Not Admitted")
ax.legend(loc=1)#图例放在右上角
ax.set_xlabel("Exam 1 Score")
ax.set_ylabel("Exam 2 Score")
plt.show()
minimize方法来拟合,minimize中method可以选择不同的算法来计算
最常使用的参数:
返回:
result=opt.minimize(fun=cost, x0=Theta,args=(X,Y),method="TNC",jac=gradient)
result
fun: 0.20349770158947458
jac: array([8.95090947e-09, 8.17143290e-08, 4.76542717e-07])
message: 'Local minimum reached (|pg| ~= 0)'
nfev: 36
nit: 17
status: 0
success: True
x: array([-25.16131863, 0.20623159, 0.20147149])
这个结论下代价函数计算结果:
cost(result["x"],X,Y)
0.20349770158947458
得到参数θ后,用这个模型预测某个学生是否能被录取。
接下来,编写一个函数,用参数θ来为数据集X输出预测。然后,使用这个函数来给分类器的训练精度打分。 逻辑回归模型的假设函数: h θ ( x ) = g ( θ T x ) = 1 1 + e − θ T x h_{θ}(x)=g(θ^{T}x)=\frac{1}{1+e^{-θ^{T}x}} hθ(x)=g(θTx)=1+e−θTx1
当 h θ > = 0.5 h_{θ}>=0.5 hθ>=0.5时,预测 y = 1 y=1 y=1;
当 h θ < 0.5 h_{θ}<0.5 hθ<0.5时,预测 y = 0 y=0 y=0.
下面开始构造预测函数predict
def predict(Theta,X):
p=sigmoid(X@Theta.T)
return [1 if x>=0.5 else 0 for x in p]
result=opt.fmin_tnc(func=cost, x0=Theta,fprime=gradient,args=(X,Y))
Y=np.matrix(Y.values)
#注意X和Y始终都是表格,需要转换成矩阵或者列表
#result[0]就是学习得到的θ
Theta_min = result[0]#如果执行了fmin_tnc之后又执行了minimize,那么result[0]就变了,需要重新执行fmin_tnc得到需要的result[0]
predictions = predict(Theta_min, X)
correct = [1 if a==b else 0 for (a, b) in zip(predictions,Y)]
accuracy = float((sum(correct) / len(correct))*100)
print ("accuracy = {:.2f}%".format(accuracy))
accuracy = 89.00%
逻辑回归分类器预测正确,如果一个学生被录取或没有录取,达到89%的精确度,这是训练集的准确性。这里没有保持住了设置或使用交叉验证得到的真实逼近,所以这个数字有可能高于其真实值。
在训练的第二部分,将要通过加入正则项提升逻辑回归算法。正则化是成本函数中的一个术语,它使算法更倾向于“更简单”的模型(在这种情况下,模型将更小的系数)。这个理论助于减少过拟合,提高模型的泛化能力。
假设您是工厂的产品经理,您有两种不同测试中某些微芯片的测试结果。从这两个测试中,您想确定微芯片是应该被接受还是被拒绝。为了帮助您做出决定,您有一个关于过去微芯片的测试结果的数据集,您可以从其中建立一个逻辑回归模型。
先进行数据提取:
#数据提取
path="ex2data2.txt"
data2=pd.read_table(path,sep=",",header=None,names=["Test 1","Test 2","Accepted"])
data2.head()
Test 1 | Test 2 | Accepted | |
---|---|---|---|
0 | 0.051267 | 0.69956 | 1 |
1 | -0.092742 | 0.68494 | 1 |
2 | -0.213710 | 0.69225 | 1 |
3 | -0.375000 | 0.50219 | 1 |
4 | -0.513250 | 0.46564 | 1 |
图形展示数据点
#分别提取Accepted=1,Accepted=0的数据点
positive=data2[data2["Accepted"].isin([1])]
negative=data2[data2["Accepted"].isin([0])]
#绘制图形:两种数据点:Accepsted,Rejected
fix,ax=plt.subplots(figsize=(8,6))
ax.scatter(positive["Test 1"],positive["Test 2"],s=20,color="blue",marker="o",label="Accepsted")
ax.scatter(negative["Test 1"],negative["Test 2"],s=20,color="red",marker="x",label="Rejected")
ax.legend(loc=1)
ax.set_xlabel("Test 1 Score")
ax.set_ylabel("Test 2 Score")
plt.show()
这个数据看起来比较复杂,无法用一条直线来良好的分开两类数据。
可以考虑像逻辑回归这样的线性技术来构造从原始特征的多项式中得到的特征。
具体可以参考pdf中给的特征:把这些特征映射到所有的x1和x2的多项式项上,直到第六次幂。
m a p F e a t u r e ( x ) = [ 1 x 1 x 2 x 1 2 x 2 2 x 1 x 2 x 1 3 . . . x 1 x 2 5 x 2 6 ] mapFeature(x)= \begin{bmatrix} 1 \\ x_{1}\\ x_{2}\\ x_{1}^{2}\\ x_2^{2}\\ x_{1}x_{2} \\ x_1^3\\.\\.\\.\\x_1x_2^5\\ x_2^6\\ \end{bmatrix} mapFeature(x)=⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡1x1x2x12x22x1x2x13...x1x25x26⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤
#创建多项式特征
#设置最高次方为6
degree=6
#提取向量x1,x2
x1=data2["Test 1"]
x2=data2["Test 2"]
#在data2中插入向量1
data2.insert(3,"Ones",1)
for i in range(1,degree+1):
for j in range(0,i+1):
data2['F'+str(i)+str(j)]=np.power(x1,i-j)*np.power(x2,j)
#data2删除Test1,Test2列
data2.drop("Test 1",axis=1,inplace=True) #删除列需要axis=1;参数inplace 默认情况下为False,表示保持原来的数据不变,True 则表示在原来的数据上改变。
data2.drop("Test 2",axis=1,inplace=True)
data2.head()
Accepted | Ones | F10 | F11 | F20 | F21 | F22 | F30 | F31 | F32 | ... | F53 | F54 | F55 | F60 | F61 | F62 | F63 | F64 | F65 | F66 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 1 | 0.051267 | 0.69956 | 0.002628 | 0.035864 | 0.489384 | 0.000135 | 0.001839 | 0.025089 | ... | 0.000900 | 0.012278 | 0.167542 | 1.815630e-08 | 2.477505e-07 | 0.000003 | 0.000046 | 0.000629 | 0.008589 | 0.117206 |
1 | 1 | 1 | -0.092742 | 0.68494 | 0.008601 | -0.063523 | 0.469143 | -0.000798 | 0.005891 | -0.043509 | ... | 0.002764 | -0.020412 | 0.150752 | 6.362953e-07 | -4.699318e-06 | 0.000035 | -0.000256 | 0.001893 | -0.013981 | 0.103256 |
2 | 1 | 1 | -0.213710 | 0.69225 | 0.045672 | -0.147941 | 0.479210 | -0.009761 | 0.031616 | -0.102412 | ... | 0.015151 | -0.049077 | 0.158970 | 9.526844e-05 | -3.085938e-04 | 0.001000 | -0.003238 | 0.010488 | -0.033973 | 0.110047 |
3 | 1 | 1 | -0.375000 | 0.50219 | 0.140625 | -0.188321 | 0.252195 | -0.052734 | 0.070620 | -0.094573 | ... | 0.017810 | -0.023851 | 0.031940 | 2.780914e-03 | -3.724126e-03 | 0.004987 | -0.006679 | 0.008944 | -0.011978 | 0.016040 |
4 | 1 | 1 | -0.513250 | 0.46564 | 0.263426 | -0.238990 | 0.216821 | -0.135203 | 0.122661 | -0.111283 | ... | 0.026596 | -0.024128 | 0.021890 | 1.827990e-02 | -1.658422e-02 | 0.015046 | -0.013650 | 0.012384 | -0.011235 | 0.010193 |
5 rows × 29 columns
接下来修改原来的成本函数cost和梯度函数gradient。
J ( θ ) = 1 m ∑ i = 1 m [ − y ( i ) l o g ( h θ ( x ( i ) ) ) − ( 1 − y ( i ) ) l o g ( 1 − h θ ( x ( i ) ) ) ] + λ 2 m ∑ j = 1 n θ j 2 J(θ)=\frac{1}{m}\sum^{m}_{i=1}[-y^{(i)}log(h_{\theta}(x^{(i)}))-(1-y^{(i)})log(1-h_{\theta}(x^{(i)}))]+\frac{\lambda}{2m}\sum_{j=1}^{n}\theta_j^2 J(θ)=m1∑i=1m[−y(i)log(hθ(x(i)))−(1−y(i))log(1−hθ(x(i)))]+2mλ∑j=1nθj2
def costReg(Theta,X,Y,LearningRate):#传入的X,Y,Theta是数组
#转换成矩阵
Theta=np.matrix(Theta)
X=np.matrix(X)
Y=np.matrix(Y)
first=np.multiply(-Y,np.log(sigmoid(X@Theta.T)))
second=np.multiply(1-Y,np.log(1-sigmoid(X@Theta.T)))
#正则项计算:
reg=(LearningRate/(2*len(X)))*np.sum(np.power(Theta[:,1:Theta.shape[1]],2))
return np.sum(first-second)/len(X)+reg
如果要使用梯度下降法令这个代价函数最小化,因为 θ 0 \theta_0 θ0未对进行正则化,所以梯度下降算法将分两种情形:
def gradientReg(Theta,X,Y,LearningRate):#传入的X,Y,Theta是数组
#将X,Y,Theta从数组转成矩阵
Theta=np.matrix(Theta)
X=np.matrix(X)
Y=np.matrix(Y)
#grad记录θ向量每一个元素的梯度下降值
Theta_cnt=Theta.shape[1]
grad=np.zeros(Theta.shape[1])
error=sigmoid(X*Theta.T)-Y
for i in range(Theta_cnt):
tmp=np.multiply(error,X[:,i])
grad[i]=np.sum(tmp)/len(X)
reg=(LearningRate/len(X))*Theta
reg[0]=0 #θ0不考虑正则化,不惩罚第0项
return grad+reg
# 不知道为什么下面这个准确度只有83.05%,上面那个准确度84.75%
#计算误差向量
# error=sigmoid(X*Theta.T)-Y
# for i in range(Theta_cnt):
# tmp=np.multiply(error,X[:,i])
# if i==0 :
# grad[i]=np.sum(tmp)/len(X)
# else:
# grad[i]=np.sum(tmp)/len(X)+(LearningRate/len(X))*Theta[:,i]
return grad
初始化变量:
cols=data2.shape[1]
#data2的列:Accepted x0 x1...
Y2=data2.iloc[:,0:1]
X2=data2.iloc[:,1:cols]
#表格转成数组
X2=np.array(X2.values)
Y2=np.array(Y2.values)
Theta2=np.zeros(X2.shape[1])
初始学习率到一个合理值
LearningRate=1
尝试调用新的默认为0的theta的正则化函数,确保计算工作正常。
costReg(Theta2, X2, Y2, LearningRate)
0.6931471805599454
gradientReg(Theta2, X2, Y2, LearningRate)
matrix([[8.47457627e-03, 1.87880932e-02, 7.77711864e-05, 5.03446395e-02,
1.15013308e-02, 3.76648474e-02, 1.83559872e-02, 7.32393391e-03,
8.19244468e-03, 2.34764889e-02, 3.93486234e-02, 2.23923907e-03,
1.28600503e-02, 3.09593720e-03, 3.93028171e-02, 1.99707467e-02,
4.32983232e-03, 3.38643902e-03, 5.83822078e-03, 4.47629067e-03,
3.10079849e-02, 3.10312442e-02, 1.09740238e-03, 6.31570797e-03,
4.08503006e-04, 7.26504316e-03, 1.37646175e-03, 3.87936363e-02]])
使用和第一部分相同的优化函数来计算优化后的结果:
result2=opt.fmin_tnc(func=costReg, x0=Theta2,fprime=gradientReg,args=(X2,Y2,LearningRate))
result2
(array([ 1.60695456, 1.1560186 , 1.96230284, -3.0506508 , -1.65702971,
-1.91905201, 0.57020964, -0.68153388, -0.71446988, 0.04581342,
-2.05403849, -0.19543701, -1.06002879, -0.50146813, -1.49394535,
0.08870346, -0.37553871, -0.1621286 , -0.47670397, -0.49928213,
-0.25753424, -1.25322562, 0.00804809, -0.51945916, -0.03978315,
-0.54273819, -0.21843762, -0.93050987]),
86,
4)
最后,使用第1部分中的预测函数来查看该方案在训练数据上的准确度:
#result2[0]就是学习得到的θ
Theta_min = result2[0]
predictions = predict(Theta_min, X2)
correct = [1 if a==b else 0 for (a, b) in zip(predictions,Y2)]
accuracy = float((sum(correct) / len(correct))*100)
print ("accuracy = {:.2f}%".format(accuracy))
accuracy = 84.75%
还可以使用高级Python库scikit-learn来解决这个问题:
from sklearn import linear_model#调用sklearn的线性回归包
model = linear_model.LogisticRegression(penalty='l2', C=1.0)#(C:正则化系数。float,默认1.0,是正则化强度的逆,必须是正浮点数,其越小,正则化越强。)
model.fit(X2, Y2.ravel())
LogisticRegression()
model.score(X2, Y2)
0.8305084745762712
之前准确度不是特别理想,可能是参数需要调整。
原因出在 创建多项式特征 ,最高次会影响结果,并且由于我代码有问题导致始终有x2,无法单独存在x1,所以准确度特别低。
绘制决策曲线
def hfun2(theta,x1,x2,degree):
temp=theta[0][0]
place=0
for i in range(1,degree+1):
for j in range(0,i+1):
temp+=np.power(x1,i-j)*np.power(x2,j)*theta[0][place+1]
place+=1
return temp
def find_decision_boundary(theta,degree):
t1 = np.linspace(-1, 1.5, 1000)
t2 = np.linspace(-1, 1.5, 1000)
cord=[(x,y)for x in t1 for y in t2]
# print(cord)
x_cord,y_cord=zip(*cord)
h_val=pd.DataFrame({'x1':x_cord,'x2':y_cord})
h_val['hval']=hfun2(theta, h_val['x1'], h_val['x2'], degree)
decision=h_val[np.abs(h_val['hval'])<2*10**-3]
return decision.x1,decision.x2
fix,ax=plt.subplots(figsize=(8,6))
x,y=find_decision_boundary(result2,6)
ax.scatter(x, y,c='y',s=10,label='Prediction')
ax.scatter(positive["Test 1"],positive["Test 2"],s=20,color="blue",marker="o",label="Accepsted")
ax.scatter(negative["Test 1"],negative["Test 2"],s=20,color="red",marker="x",label="Rejected")
ax.set_xlabel("Test 1 Score")
ax.set_ylabel("Test 2 Score")
ax.legend(loc=1)
plt.show()
λ = 0 \lambda=0 λ=0时过拟合
LearningRate=0
result3=opt.fmin_tnc(func=costReg, x0=Theta2,fprime=gradientReg,args=(X2,Y2,LearningRate))
result3
(array([ 9.11192364e+00, 1.18840465e+01, 6.30828094e+00, -8.39706468e+01,
-4.48639810e+01, -3.81221435e+01, -9.42525756e+01, -8.14257602e+01,
-4.22413355e+01, -3.52968361e+00, 2.95734207e+02, 2.51308760e+02,
3.64155830e+02, 1.61036970e+02, 5.70100234e+01, 1.71716716e+02,
2.72109672e+02, 3.12447535e+02, 1.41764016e+02, 3.22495698e+01,
-1.75836912e-01, -3.58663811e+02, -4.82161916e+02, -7.49974915e+02,
-5.03764307e+02, -4.80978435e+02, -1.85566236e+02, -3.83936243e+01]),
280,
3)
fix,ax=plt.subplots(figsize=(8,6))
x,y=find_decision_boundary(result3,6)
ax.scatter(x, y,c='y',s=10,label='Prediction')
ax.scatter(positive["Test 1"],positive["Test 2"],s=20,color="blue",marker="o",label="Accepsted")
ax.scatter(negative["Test 1"],negative["Test 2"],s=20,color="red",marker="x",label="Rejected")
ax.set_xlabel("Test 1 Score")
ax.set_ylabel("Test 2 Score")
ax.legend(loc=1)
plt.show()
λ = 100 \lambda=100 λ=100时欠拟合
LearningRate=100
result4=opt.fmin_tnc(func=costReg, x0=Theta2,fprime=gradientReg,args=(X2,Y2,LearningRate))
result4
(array([ 0.05021733, 0.03612558, 0.06132196, -0.09533284, -0.05178218,
-0.05997038, 0.01781905, -0.02129793, -0.02232718, 0.00143167,
-0.0641887 , -0.00610741, -0.0331259 , -0.01567088, -0.04668579,
0.00277198, -0.01173558, -0.00506652, -0.014897 , -0.01560257,
-0.00804795, -0.0391633 , 0.0002515 , -0.0162331 , -0.00124322,
-0.01696057, -0.00682618, -0.02907843]),
93,
4)
fix,ax=plt.subplots(figsize=(8,6))
x,y=find_decision_boundary(result4,6)
ax.scatter(x, y,c='y',s=10,label='Prediction')
ax.scatter(positive["Test 1"],positive["Test 2"],s=20,color="blue",marker="o",label="Accepsted")
ax.scatter(negative["Test 1"],negative["Test 2"],s=20,color="red",marker="x",label="Rejected")
ax.set_xlabel("Test 1 Score")
ax.set_ylabel("Test 2 Score")
ax.legend(loc=1)
plt.show()