学生成绩预测
学生成绩预测一、问题描述二、 解决思路1.决策树办法2.回归办法三、理论基础1.独热码2.回归模型简介A.线性回归模型B.逻辑回归模型C.岭回归模型D.套索回归模型四、 动手实践1.环境及模块2.数据集预处理A.首先导入pandas模块(import pandas as pd),按属性读取数据集的特征类别,保存在data_features中;B.然后从sklearn模型中导入独热编码OneHotE
学生成绩预测
一、问题描述
现有两个数据集,一为训练数据集SP_train.csv,一为测试数据集SP_test.csv,训练数据集包括5000条数据,测试数据集包括1000条数据集。
训练数据集数据格式内容如下:
测试数据集数据格式内容如下:
所需解决的问题就是根据每个学生的gender、race/ethnicity、parental level of education、lunch、test preparation course等5种因素,预测math score、reading score、writing score。决定学生的五种因素各有其类别,比如gender包括female和male,lunch包括standard和free/reduced。
二、 解决思路
1. 决策树办法
一开始想用决策树,是因为训练集中的math score、reading score、writing score是整数数值,所以假设成绩是类别变量,类别最大不会超过100,只是类别有点多而已,于是打算用其5种特征因素训练生成决策树,根据决策树结构训练测试集从而得到相应的预测分数。
具体操作方式为:将每种特征的类别变量分别映射成数值变量,如t1={‘female’:1,‘male’:2},t2={‘group A’:1, ‘group B’:2, ‘group C’:3, ‘group D’:4, ‘group E’:5},然后根据数值变量训练生成决策树模型。
但是实践证明这条路行不通,因为训练过程中电脑直接死机、计算资源利用率瞬间拉满,而且决策树生成的决策树过于复杂、庞大,电脑根本查看不了细节信息。
2. 回归办法
预测分数问题本质是回归问题,成绩不能通过决策树预测出来。所以对于预测学生成绩问题,回归回归办法才是王道。所谓回归就是根据特征因素预测出数值,特征可以有多个,预测的数值也可以有多个。一旦确定用回归办法,就需要解决两个问题:1、如何把类别变量转换成数值变量;2、选择何种回归模型。常见的回归模型有线性回归模型、逻辑回归模型、岭回归模型、套索回归模型。
针对第一个问题,针对类别是否有序,一般有两种办法,如果类别有明显的大小关系或者顺序关系,就采用映射办法赋予类别变量相应的数值,如衣物的尺寸S、M、L就可赋予以下数值:{S: 1, M: 2, L: 3},给S赋予1,M赋予2,L赋予3,因为尺寸S、M、L本身是有大小关系的,所以赋予的值也得有这种关系。如果类别变量只是简单的并列关系,是无序的,那一般就采用独热码(onehot)对类别进行编码。就如训练数据集中的race/ethnicity(种族/民族)特征,group A、group B、group C、group D、group E谁也不比谁更好或者更好,它们只是并列关系而已,所以就可用独热码对其编码。
再看第二个问题,选择何种模型。其实这不是一个问题,这些模型,都跑一边就知道哪个模型的效果好了,哪个模型的效果好,就选哪个。实践操作中,我具体训练了多元回归模型、岭回归模型和套索模型,发现岭回归模型的效果最好。
三、理论基础
1. 独热码
在回归,分类,聚类等机器学习算法中,特征之间距离的计算或相似度的计算是非常重要的,而我们常用的距离或相似度的计算都是在欧式空间的相似度计算,计算余弦相似性,基于的就是欧式空间。
独热码最重要的地方是任意两个码字之间的距离相等,因此如果对无序的类别变量进行独热码编码,就能使不同类别的码字距离比较合理。
从效果上看,独热码的效果就是把特征数字化、扩大化,数字化顾名思义,就是把类别变成数字,扩大化则是把一个特征列变为多个特征列。例如,给测试数据集的race/ethnicity列进行独热码编码,结果如下:
类别 | One hot code |
---|---|
Group A | 10000 |
Group B | 01000 |
Group C | 00100 |
Group D | 00010 |
Group E | 00001 |
本来是一列特征,即race/ethnicity,但是经过独热码编码后,就变成了5列数字,这是独热码的特性所致——同一时刻只有一位数字有效(值为1),不同位的数字表示不同的类别,在上面的例子中,第一位表示Group A,第二位表示Group B,以此类推。所以根据编码结果,我们可以直接根据码字看出原类别。同理,训练数据集的其他类别特征也按照这样的方式编码,最终的结果是,原数据集中的5个特征类别,被编码成了17列特征数字。如下图所示:
17=2(gender)+5(race/ethnicity)+6(parental level of education)+2(lunch)+2(test preparation course)。第0列和第1列表示gender,第2~6列表示race/ethnicity,后续以此类推。
2. 回归模型简介
A. 线性回归模型
给定数据集 ,其中 ,x为特征,y为真实值。线性回归模型实际上就是求y和x的关系。它假设y和x之间存在线性关系,然后试图学得一个线性模型,然后根据模型尽可能准确地预测一个与真实值相接近的数值,表达式为:
向量表达式为:
B. 逻辑回归模型
逻辑回归虽然听起来是个回归算法,但实际上是个分类算法。它有二项式逻辑回归和多项式逻辑回归两种模式,共同点都是综合数据集给定的所有特征,得到一个或多个判定的阈值,然后将数据集中的数据分为两类或者多类。
C. 岭回归模型
这是线性模型的改良模型,线性模型求解特征值和真实值之间的线性关系时用的是最小二乘法,这是无偏的估计回归方法,存在的一个问题是,对于某些矩阵,某个元素很小的变动,会造成最后的计算结果有很大的变动,从而误差很大,岭回归模型采用的岭回归估计方法,是放弃无偏性、损失部分信息、降低精度的改良版最小二乘法。
D. 套索回归模型
岭回归是线性模型的改良版,但是也始终保留了所有的特征变量,无法降低模型的复杂度,套索回归是线性回归的另外一种改良版本,在估计模型的时候,它将一些不重要的参数直接置为0,达到变量筛选的目的。
四、 动手实践
1. 环境及模块
环境:Win10、Anaconda 3-2020.07-Windows-x86_64下的spyder(python3.8.3)
模块:pandas、sklearn、numpy、json
Pandas:用来处理数据结构和数据分析;
Sklearn:包含了一些机器学习方法,本文主要调用里面的独热编码OneHotEncoder和线性模型linear_model;
Numpy:进行大量的维度数组和矩阵运算;
Json:一种保存数据的格式,本文主要用其保存预测的分数,格式为{“key”:value}
2. 数据集预处理
处理训练集SP_train.csv和测试集SP_test.csv的过程完全相同,都是将数据集中的特征类别变为特征数值,所以下面仅叙述处理训练数据集的过程。这主要用独热编码和成帧函数。
A. 首先导入pandas模块(import pandas as pd),按属性读取数据集的特征类别,保存在data_features中;
读完后的结果的为:
B. 然后从sklearn模型中导入独热编码OneHotEncoder,对data_features进行编码;
编码之后的结果为:
C. 接着就要给上面的编码结果加列名称,因为上面只是纯数据,没有索引或者名字,不方便观察。这主要用到pands模块里面的dataframe函数。
成帧后的数据为:
可以看到,现在的数据帧已经预处理好了,成功地把特征类别变成了特征数值,并且还有列属性名称,这正是我们需要的格式。
3. 训练模型
上面已经把数据集预处理好了,现在开始训练模型。
A. 首先按列属性读出全部特征数值,作为模型的输入x,读入训练集的分数,作为模型的输出y;
B. 然后调用并训练线性模型。
现在,经过训练,我们就得到了regr线性模型。我们可以看看这个模型的详细参数。
自变量系数:
截距
可以看到,虽然我们只训练了一次,但是强大的线性模型实际上是生成了3个线性函数,regr包含了这3个线性函数。如果写成显示形式,结果如下:
4. 预测分数
上面我们已经得到了包含3个线性函数的regr模型,现在用这个模型预测训练集中的学生成绩。
A. 首先读取训练集数据并进行预处理,过程与上面处理训练集的过程一样,不再赘述。处理好的训练集格式如下:
B. 然后读取该数据帧的全部列,作为模型的输入x_test,进行预测,并将结果四舍五入。
5. 提交平台,计算方差
为了提交平台,根据要求,还要把预测分数以json格式保存。
A. 预测出来的成绩是纯数组格式的数据,为了保存为json格式,需要先把数组存为字典。于是首先生成一系列的列表,作为字典的键。
然后生成字典格式,结果如下:
B. 然后导入json模块,进行转换。
但是会报错,提示数组类型不可json序列化,意思就是不能直接存为json文件:
这是因为json默认支持的类不包括ndarry,所以重写encoding方法来解决这个问题就行。
C. 经过上述操作,我们得到了如下的结果:
内容是json文件的内容,是我们想要的格式,于是将内容全部复制,新建写字本,粘贴数据,文件转存为json格式即可。
D. 提交平台,得到第一次结果:
由此,第一次用线性回归办法尝试预测学生成绩成功了。下附可复现代码:
五、可复现代码
//
#加载库
import pandas as pd
from sklearn.preprocessing import OneHotEncoder
from sklearn import linear_model
import numpy as np
import json
# 下面特征是按照列属性读取的
data_features = pd.read_csv('.\SP_train.csv',
usecols=['gender', 'race/ethnicity',
'parental level of education',
'lunch','test preparation course'])
#调用独热编码
encoder=OneHotEncoder(sparse = False)
data_features=encoder.fit_transform(data_features)
#数据成帧
data=pd.DataFrame(data_features,
columns=['gender_1','gender_2',
'race_1','race_2',
'race_3','race_4',
'race_5','edu_1',
'edu_2','edu_3',
'edu_4','edu_5',
'edu_6','lunch_1',
'lunch_2','test_1',
'test_2'],dtype=float)
x=data[['gender_1','gender_2','race_1','race_2',
'race_3','race_4','race_5','edu_1',
'edu_2','edu_3','edu_4','edu_5',
'edu_6','lunch_1','lunch_2','test_1',
'test_2']]
y=pd.read_csv('.\SP_train.csv',
usecols=['math score','reading score','writing score'])
#调用线性回归模型
regr=linear_model.LinearRegression()
regr.fit(x,y)
#可查看模型相关信息
#打印系数
# 检验模型效果
coef = regr.coef_ # 获取自变量系数
intercept = regr.intercept_ # 获取截距
R2 = regr.score(x,y) # 2阶方差
# print(regr.coef_)
#打印常数
#print(regr.intercept_)
#预测单个学生成绩
# z=regr.predict([[1,0,0,0,1,
# 0,0,0,0,0,
# 0,0,1,0,1,
# 1,0]])
data_test_features=pd.read_csv('.\SP_test.csv',
usecols=['gender', 'race/ethnicity','parental level of education','lunch','test preparation course'])
data_test_features=encoder.fit_transform(data_test_features)
data_test=pd.DataFrame(data_test_features,
columns=['gender_1','gender_2',
'race_1','race_2',
'race_3','race_4',
'race_5','edu_1',
'edu_2','edu_3',
'edu_4','edu_5',
'edu_6','lunch_1',
'lunch_2','test_1',
'test_2'],dtype=float)
x_test=data_test[['gender_1','gender_2',
'race_1','race_2',
'race_3','race_4',
'race_5','edu_1',
'edu_2','edu_3',
'edu_4','edu_5',
'edu_6','lunch_1',
'lunch_2','test_1',
'test_2']]
#预测测试集中学生成绩
y_test_pre=regr.predict(x_test)
y_test_pre=np.round(y_test_pre)
#y_test_pre=np.array(y_test_pre,dtype=np.int32)
#print (y_test_pre)
#print(type(y_test_pre))
list=[]
for i in range(1000):
list.append(str(i))
#print(list)
#生成字典
dict1={}
dict1=dict(zip(list,y_test_pre))
#重写json类
class NumpyEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, (np.int_, np.intc, np.intp, np.int8,
np.int16, np.int32, np.int64, np.uint8,
np.uint16, np.uint32, np.uint64)):
return int(obj)
elif isinstance(obj, (np.float_, np.float16, np.float32,
np.float64)):
return float(obj)
elif isinstance(obj,(np.ndarray,)): #### This is the fix
return obj.tolist()
return json.JSONEncoder.default(self, obj)
submission1=json.dumps(dict1,cls=NumpyEncoder)
六、 改进——套索回归模型
上面用到的线性回归模型简单,十分容易理解,解释性很强,也蕴含了机器学习中的很多重要思想,但是对于非线性数据或者数据间具有相关性多项式回归难以建模,另外还有一个缺点是容易过拟合。于是考虑加上一个惩罚项,以便减少过拟合程度。套索回归就是在线性回归模型基础是加上一个L1范数,优点是可以使得某些参数值精确地收缩到0。
套索回归的优化目标是:
调用套索回归模型也很简单,这个模型也在sklearn的线性模型里面,直接调用就好。
其中alpha为与L1范数相乘的系数,代表惩罚项的大小,是可调参数,于是调整该参数寻找套索回归的最优解。
Alpha | 分数(方差) |
---|---|
0 | 22.64950330581225 |
0.1 | 22.715633383201094 |
0.3 | 23.108440016582687 |
0.5 | 23.600847442411894 |
0.8 | 24.269322199023193 |
七、 改进——岭回归模型
岭回归也是在线性回归模型的基础上加惩罚项目,不过惩罚项是L2范数,调用、调参与上述套索回归一样。下面直接记录调参结果。
Alpha | 分数(方差) |
---|---|
0 | 22.64950330581225 |
0.1 | 22.64950330581225 |
0.3 | 22.64950330581225 |
0.5 | 22.64950330581225 |
0.8 | 22.64950330581225 |
八、 改进——多项式回归模型
当我们想要创建一个适合处理非线性可分数据的模型时,我们需要使用多项式回归。 在这种回归技术中,最佳拟合线不是直线,而是一条适合数据点的曲线。 对于多项式回归,一些自变量的幂会大于1。例如:
训练多项式回归模型,可调的参数为degree,即最高次幂。
调用多项式模型如下:
改变degree,得到两次结果如下:
degree | 分数(方差) |
---|---|
2 | 22.956481 |
4 | 23.17326 |
九、 总结
先放上最终成绩排名截图:
然后再说所用到的模型,线性回归模型最简单,解释性最好,结果和训练过程一目了然,但是容易过拟合,所以结果不是很好;套索回归和岭回归分别加了L1范数和L2范数作为惩罚项,结果有所提升,多项式回归在此数据集上训练的效果也不太好,容易过拟合。
总而言之,在本训练集和测试集下,表现最好的是参数alpha=0的套索回归和参数alpha=x(0<=x<=1)的岭回归,可以达到最优结果22.649503。
终于写完了,2021元旦快乐!
更多推荐
所有评论(0)