学生成绩预测

一、问题描述

现有两个数据集,一为训练数据集SP_train.csv,一为测试数据集SP_test.csv,训练数据集包括5000条数据,测试数据集包括1000条数据集。
训练数据集数据格式内容如下:
1
测试数据集数据格式内容如下:
在这里插入图片描述
所需解决的问题就是根据每个学生的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

但是实践证明这条路行不通,因为训练过程中电脑直接死机、计算资源利用率瞬间拉满,而且决策树生成的决策树过于复杂、庞大,电脑根本查看不了细节信息。

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 A10000
Group B01000
Group C00100
Group D00010
Group E00001

本来是一列特征,即race/ethnicity,但是经过独热码编码后,就变成了5列数字,这是独热码的特性所致——同一时刻只有一位数字有效(值为1),不同位的数字表示不同的类别,在上面的例子中,第一位表示Group A,第二位表示Group B,以此类推。所以根据编码结果,我们可以直接根据码字看出原类别。同理,训练数据集的其他类别特征也按照这样的方式编码,最终的结果是,原数据集中的5个特征类别,被编码成了17列特征数字。如下图所示:
3

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之间存在线性关系,然后试图学得一个线性模型,然后根据模型尽可能准确地预测一个与真实值相接近的数值,表达式为:
4

向量表达式为:
5

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中;

5
6
读完后的结果的为:
7

B. 然后从sklearn模型中导入独热编码OneHotEncoder,对data_features进行编码;

8
9
编码之后的结果为:
10

C. 接着就要给上面的编码结果加列名称,因为上面只是纯数据,没有索引或者名字,不方便观察。这主要用到pands模块里面的dataframe函数。

11
成帧后的数据为:
12
可以看到,现在的数据帧已经预处理好了,成功地把特征类别变成了特征数值,并且还有列属性名称,这正是我们需要的格式。

3. 训练模型

上面已经把数据集预处理好了,现在开始训练模型。

A. 首先按列属性读出全部特征数值,作为模型的输入x,读入训练集的分数,作为模型的输出y;

13

B. 然后调用并训练线性模型。

14
15
现在,经过训练,我们就得到了regr线性模型。我们可以看看这个模型的详细参数。
16
自变量系数:
17

截距
18

可以看到,虽然我们只训练了一次,但是强大的线性模型实际上是生成了3个线性函数,regr包含了这3个线性函数。如果写成显示形式,结果如下:

19

4. 预测分数

上面我们已经得到了包含3个线性函数的regr模型,现在用这个模型预测训练集中的学生成绩。

A. 首先读取训练集数据并进行预处理,过程与上面处理训练集的过程一样,不再赘述。处理好的训练集格式如下:

20

B. 然后读取该数据帧的全部列,作为模型的输入x_test,进行预测,并将结果四舍五入。

21
22

5. 提交平台,计算方差

为了提交平台,根据要求,还要把预测分数以json格式保存。

A. 预测出来的成绩是纯数组格式的数据,为了保存为json格式,需要先把数组存为字典。于是首先生成一系列的列表,作为字典的键。

23
然后生成字典格式,结果如下:
24
25

B. 然后导入json模块,进行转换。

26
27
但是会报错,提示数组类型不可json序列化,意思就是不能直接存为json文件:
28
这是因为json默认支持的类不包括ndarry,所以重写encoding方法来解决这个问题就行。
29

C. 经过上述操作,我们得到了如下的结果:

30
内容是json文件的内容,是我们想要的格式,于是将内容全部复制,新建写字本,粘贴数据,文件转存为json格式即可。
31
32

D. 提交平台,得到第一次结果:

33
由此,第一次用线性回归办法尝试预测学生成绩成功了。下附可复现代码:

五、可复现代码

// 
#加载库
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。
套索回归的优化目标是:
36
调用套索回归模型也很简单,这个模型也在sklearn的线性模型里面,直接调用就好。
37
其中alpha为与L1范数相乘的系数,代表惩罚项的大小,是可调参数,于是调整该参数寻找套索回归的最优解。

Alpha分数(方差)
022.64950330581225
0.122.715633383201094
0.323.108440016582687
0.523.600847442411894
0.824.269322199023193

七、 改进——岭回归模型

岭回归也是在线性回归模型的基础上加惩罚项目,不过惩罚项是L2范数,调用、调参与上述套索回归一样。下面直接记录调参结果。

Alpha分数(方差)
022.64950330581225
0.122.64950330581225
0.322.64950330581225
0.522.64950330581225
0.822.64950330581225

八、 改进——多项式回归模型

当我们想要创建一个适合处理非线性可分数据的模型时,我们需要使用多项式回归。 在这种回归技术中,最佳拟合线不是直线,而是一条适合数据点的曲线。 对于多项式回归,一些自变量的幂会大于1。例如:
38
训练多项式回归模型,可调的参数为degree,即最高次幂。
调用多项式模型如下:
39
40
改变degree,得到两次结果如下:

degree分数(方差)
222.956481
423.17326

九、 总结

先放上最终成绩排名截图:
41

然后再说所用到的模型,线性回归模型最简单,解释性最好,结果和训练过程一目了然,但是容易过拟合,所以结果不是很好;套索回归和岭回归分别加了L1范数和L2范数作为惩罚项,结果有所提升,多项式回归在此数据集上训练的效果也不太好,容易过拟合。
总而言之,在本训练集和测试集下,表现最好的是参数alpha=0的套索回归和参数alpha=x(0<=x<=1)的岭回归,可以达到最优结果22.649503。

终于写完了,2021元旦快乐!

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐