学完了吴恩达机器学习的Logistic回归一章,事不宜迟,马上实战。我选择拿来练习的数据会尽量找手边的真实数据,这样才能真确地增加处理问题的能力。
简介
我手上有一份某985大学某专业2018年考研复试结果的excel表格数据是真实的,是从该学校一个师兄的手上拿到的:
复试结果
整个表格共有82个数据,也就是共有82名考生参加了复试,其中有56个考生被录取。我希望构建一个Logistic回归模型,以初试的四门课为自变量——政治、英语、数学和专业课,基于这四个变量预测考生是否会被录取。
我在excel对数据进行了简单的处理,单独选取了政治、英语、数学、专业课和拟录取结果五列出来,并且删除了拟“少干”计划录取和拟“退役大学生士兵”专项计划录取两个特殊数据,将“拟录取”的值设为1,“拟候补录取”和“拟不录取”的值设为0,保存为csv文件。
代码
sklearn库有专门的logistic回归类,但是吴恩达的作业要求同学自己实现一遍logistic回归——求解全局最小代价的参数,而且我还不是很熟悉sklearn库,因此我在Python重复造一次轮子。首先导入相关的库:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.metrics import classification_report # 分类评价报告
import scipy.optimize as opt # 寻找最优解
data = pd.read_csv('c:/users/msi/desktop/reexam.csv', header='infer') # 导入数据
预览一下数据:
In [5]: data
Out[5]:
politics english math pro result
0 70 85 135 142 1
1 70 87 129 150 1
2 77 84 120 142 1
3 67 82 120 141 1
4 76 83 108 139 1
.. ... ... ... ... ...
75 64 75 101 115 0
76 64 63 98 141 0
77 59 71 110 128 0
78 65 78 94 121 0
79 66 65 98 134 0
[80 rows x 5 columns]
然后看一下数据的散点图,看看有没有哪个单一变量可以用一条直线或者曲线对结果进行分类的:
sns.set(context="notebook", style="darkgrid")
sns.pairplot(data, hue='result')
plt.show()
根据录取结果上色的两两散点图
可以看出,并没有哪个单一变量可以简单画一条线就能很好地得到分类结果。因此我们建立四个变量的Logistic回归模型,实质上是利用S型函数预测被录取的概率。我们用一个函数h表示预测的录取概率,如果预测概率h大于0.5则预测为被录取,h小于0.5则预测为被刷掉:
类比与线性回归的最小二乘法,线性回归我们通过寻找使得预测值和真实值的离差平方和最小的参数值作为OLS估计值,在logistics回归中,我们同样引入一个代价函数,这个代价函数的特点是,预测得越准确函数值越小,预测偏差越大代价函数越大。我们的目标是寻找一组参数
,使得以下代价函数越小:
在此案例中利用了scipy的optimize类,来寻找全局最优解。我们先处理一下数据,用函数get_x(df)来获取设计矩阵,用函数get_y(df)来获取响应变量向量,并且定义S型函数、代价函数和梯度函数:
def get_x(df): # 读取特征
ones = pd.DataFrame({'ones': np.ones(len(df))})
data = pd.concat([ones, df], axis=1) # 在最左边添加一列1
return data.iloc[:, :-1].values # 返回ndarray
def get_y(df): # 读取标签
return np.array(df.iloc[:, -1])
def sigmoid(z):
return 1 / (1 + np.exp(-z))
def cost(theta, x, y):
return np.mean(-y * np.log(sigmoid(x @ theta)) - (1 - y) * np.log(1-sigmoid(x @ theta)))
def gradient(theta, x, y):
return (1 / len(x)) * x.T @ (sigmoid(x @ theta) - y)
x = get_x(data)
y = get_y(data)
在variable explorer大致看一下设计矩阵X和响应变量向量Y的样子:
预测变量X和响应变量Y
利用scipy的optimize类的minimize方法来寻找全局最小值,并且输出结果:
theta = np.zeros(x.shape[1])
res = opt.minimize(fun=cost, x0=theta, args=(x, y), method='Newton-CG', jac=gradient)
print(res)
In[10]: print(res)
fun: 0.38340711774034997
jac: array([ 0.00568961, -0.04087682, -0.06336895, -0.08010658, -0.08244267])
message: 'Optimization terminated successfully.'
nfev: 26
nhev: 0
nit: 12
njev: 103
status: 0
success: True
x: array([-22.52111266, 0.08576881, 0.09176291, 0.03154884,
0.05450403])
预测
上面最后一行array就是要求解的参数,我们赶紧来评估一下预测结果吧。首先定义预测函数,将预测变量X和参数的估计值theta输入之后,输出预测值,是1和0组成的矩阵:
def predict(x, theta):
# 获取预测值
prob = sigmoid(x @ theta)
return (prob >=0.5).astype(int) # 概率大于0.5则输出1,否则输出0
利用sklearn的classfication report来评估一下预测结果:
final_theta = res.x
y_pred = predict(x, final_theta)
print(classification_report(y, y_pred))
In[12]: print(classification_report(y, y_pred))
precision recall f1-score support
0 1.00 0.69 0.82 26
1 0.87 1.00 0.93 54
accuracy 0.90 80
macro avg 0.94 0.85 0.87 80
weighted avg 0.91 0.90 0.89 80
可以看出,此次建模没有做特征映射,也没有做正则化处理,预测结果很不错,精确度较高,F1值打分也在90%以上。因此最终结论是,建立logistic回归模型,输入初试的四门课的成绩来预测最终是否能被录取,预测结果比较准确。