import pandas as pd
import numpy as np
import matplotlib.pylab as plt
import scipy.optimize as opt
百度网盘地址如下:
链接:https://pan.baidu.com/s/12iVi93AQStYRmgUhhYR0Zg 提取码:9kgk
# 根据自己存放的路径进行修改
path='ML-homework-main\ex2-logistic regression\ex2data1.txt'
data=pd.read_csv(path,header=None,names=['Exam1','Exam2','Admitted']
# 查看效果图【这种查看方式只能再jupyter中使用】
data.head()
# 将Admitted为1的部分提出来,组成positive
positive = data[data['Admitted'].isin([1])]
negative = data[data['Admitted'].isin([0])]
plt.figure(figsize=(12, 8))
# 头两个参数是X轴与Y轴数据,c是颜色,marker是散点图形状
plt.scatter(positive['Exam1'], positive['Exam2'],c='b',marker='o',label='Admitted')
plt.scatter(negative['Exam1'], negative['Exam2'],c='r',marker='x',label='Not Admitted')
plt.xlabel('Exam1')
plt.ylabel('Exam2')
plt.legend(loc=True)
plt.show()
在二分类问题中,Y
取值只可能是0
或1
,激活函数很好满足了这个特点,当X
越大,Y
越接近1
,换一种说法即X
越大越可能取1
,因此我们选其作为分类问题中的拟合函数
# g(z),z越大越靠近1,z越小越靠近0
def sigmoid(z):
return 1/(1+np.exp(-z))
# 取[-10,10]之间的数作为x轴,步距为0.5
num=np.arange(-10,10,step=0.5)
plt.plot(num,sigmoid(num),'r')
plt.show()
代价函数的作用是使预测结果出错时给予惩罚,正确时给予奖励
如Y
现实中应该为1
,但是预测结果却为0
,此时代价函数值应该很大,起惩罚作用,让操作者知道预测出现问题;反之,若预测正确,代价函数值应该很小,起奖励作用,让操作者知道预测问题不大
总之代价函数值反映了预测结果的准确程度,我们希望它越小越好
为何此处代价函数不选用常见的平方损失函数,而要选这么复杂的形式?
代价函数的选择与拟合函数有关,此处拟合函数为激活函数,代入平方损失函数中得到的代价函数无法确定是否为凸函数,因此不一定能找到最优解,同时由于激活函数中有指数项,若后期需要对代价函数进行求导等操作时不好处理
根据我们的计算习惯,通常有指数项的都需要用一个对数对其进行处理,方便操作。同时我们也可以发现,上述代价函数在Y
取0
或1
时是截然不同的结果,实则是将两段函数整合为一个函数。
由于激活函数取值h
必定处于(0,1)
,因此对于-ln(h)
以及-(ln(1-h))
也仅仅关注其(0,1)
范围上的取值情况,为了便于观察函数取值情况,先暂时忽略与取值情况无关的累加符以及i
上标
当预测值Y==1
时,J(θ)=-ln(h)
,此时h
越靠近1
,J(θ)
越靠近0
,即预测正确时,代价很小
反之,当h
越靠近0
,J(θ)
越靠近+∞
,即预测错误时,代价很大
当预测值Y==0
时,J(θ)=-ln(1-h)
,此时h
越靠近1
,J(θ)
越靠近+∞
,即预测错误时,代价很大
反之,当h
越靠近0
,J(θ)
越靠近0
,即预测正确时,代价很小
由此可以得出结论,此代价函数是一个凸函数【即可用梯度下降法迭代θ
】,表达式方便求导等运算,也保证了在预测正确时给予奖励,预测错误时给予惩罚,由此我们选择其作为代价函数。
# 代价函数
def cost(theta, X, Y):
# [email protected]即当前的预测值|
first = Y*np.log(sigmoid(X@theta.T))
second = (1-Y)*np.log(1-sigmoid(X@theta.T))
return -1*np.mean(first+second)
其中向量计算是梯度下降的矩阵表示,1/m二者相同不多做解释,主要解释后面部分为何一致,解释如下【最好辅助画图理解】:
注意⚠!!!这仅仅是求导的处理,并没有真正实现梯度下降,若要实现真正梯度下降还需要确定迭代次数、学习率等
# 梯度下降的向量计算表示
def gradient(theta, X, Y):
# 结果返回J(θ)对不同θj的的求导结果
return 1/len(X)*X.T@(sigmoid(X@theta.T)-Y)
# 由于激活函数参数仍然采用线性表示,因此需要填充一列1,使线性表达式能用矩阵向量表示
data.insert(0,'Ones',1)
# 行全取,列只有最后一列不取
X=data.iloc[:,0:-1].values
# 行全取,列只取最后一列,保证Y是一维的,否则cost函数会出错
Y = data.iloc[:, -1].values
# 在当前数据下只需要3个θ,theta初始值全0,是行向量
theta=np.zeros(3)
# 观察初始θ值对应的预测函数
# 发现代价函数为0.69,说明准确率还不赖!【瞎猫碰上死耗子】
cost(theta,X,Y)
# J(θ)对不同θ进行求导后的取值
gradient(theta,X,Y)
此处实现梯度下降我们使用scipy.optimize
包中内置方法进行实现,不再像刚学梯度下降时手动编写函数进行迭代,若有需求可看此文章
机器学习之梯度下降
# func是要最小化的函数【代价函数】
# x0是最小化函数的自变量【θ值】
# fprime是最小化的方法【梯度下降】
result = opt.fmin_tnc(func=cost, x0=theta, fprime=gradient, args=(X, Y))
# 看看result是啥,发现result[0]是你和后的θ
result
# 此时result[0]中存放的就是根据目前算法得出的θ最优解
# 更新θ值
theta=result[0]
# 看看当前cost值,发现代价值相对于前面的0.68更小了,说明预测准确率进一步上升
cost(result[0],X,Y)
此时我们已经得到了 θ \theta θ值,只需要带回h
函数即可得到预测情况
根据 θ \theta θ进行预测: h θ ( x ) = 1 1 + e − θ T X {h_\theta(x)=\frac{1}{1+e^{-\theta^TX}}} hθ(x)=1+e−θTX1
h θ ≥ 0.5 h_\theta\ge0.5 hθ≥0.5, 预测 y = 1 y=1 y=1
h θ < 0.5 h_\theta<0.5 hθ<0.5, 预测 y = 0 y=0 y=0
# 预测函数
def predict(theta, X):
# 得到m个预测值
probability = sigmoid(X @ theta.T)
# 如果预测值大于等于0.5,则预测为1;反之预测为0
return [1 if x >= 0.5 else 0 for x in probability]
# predictions存放当前X对应的预测值
predictions = predict(np.matrix(theta), X)
# ^是异或符号,二者同说明预测成功,此时为异或值0,在correct中标记为1
# zip函数起打包作用,即同时从predictions,Y中取对应元素a,b
correct = [1 if a^b == 0 else 0 for (a,b) in zip(predictions, Y)]
# 预测正确的长度除总长度即预测准确率
accuracy = (sum(correct) / len(correct))
print(f'accuracy = {accuracy*100}%')
我们知道,在激活函数 h ( x ) h(x) h(x)中,如果 x > 0 x>0 x>0,则 y y y被预测为 1 1 1,反之预测为 0 0 0,因此 x = 0 x=0 x=0就是一个分界线
而在我们的模型中,激活函数的参数是由 θ T X \theta^TX θTX来表示的,因此如果我们令 θ T X = 0 \theta^TX=0 θTX=0并画出这条直线,则会在散点图中呈现出分界线的效果,分界效果的好坏与预测准确率相关
θ T X = 0 \theta^TX=0 θTX=0
θ 0 x 0 + θ 1 x 1 + θ 2 x 2 = 0 \theta_{0} x_0+\theta_{1} x_1+\theta_{2} x_2=0 θ0x0+θ1x1+θ2x2=0
x 2 = − ( θ 0 θ 2 x 0 + θ 1 θ 2 x 1 ) x_2=-(\dfrac{\theta_0}{\theta_2}x_0+\dfrac{\theta_1}{\theta_2}x_1) x2=−(θ2θ0x0+θ2θ1x1)
其中 x 0 x_0 x0是我们为了便于矩阵计算所加上去的,其值恒为 1 1 1
∵ \because ∵我们画图以 x 2 x_2 x2为纵坐标, x 1 x_1 x1为横坐标
∴ x 2 \therefore x2 ∴x2对应我们认知中的 y y y轴, x 1 x_1 x1对应我们认知中的 x x x轴
# 结果为三个θ都除θ2再乘-1,结果由列表保存
coef=-(theta/theta[2])
# x取值为[30,100],步幅为0.5
x=np.arange(30,100,0.5)
# θ0对应参数为x0,而x0恒为1;θ1对应参数为x1
y=coef[0]+coef[1]*x
终于来到最后一步了!!!
# 头两个参数是X轴与Y轴数据,c是颜色,marker是散点图形状
plt.scatter(positive['Exam1'], positive['Exam2'],c='b',marker='o',label='Admitted')
plt.scatter(negative['Exam1'], negative['Exam2'],c='r',marker='x',label='Not Admitted')
# 传入x与y数据绘制折线图
plt.plot(x,y,c='g')
plt.xlabel('Exam1')
plt.ylabel('Exam2')
plt.legend(loc=True)
plt.show()
本文代码主体来自于这位大佬的笔记吴恩达机器学习笔记,在此基础上加上了自己对于一些代码的注释从而更加方便理解,本人是新手入门,有错误在所难免,望各位不吝赐教。若本文对于你理解逻辑回归有些许帮助,也不妨给个赞鼓励一下,你们的支持是我创作最大的动力!