实践中,除了用训练数据训练回归模型,使用线性回归模型做预测前,通常需要对训练结果进行测试。所谓测试指的是用另一组带有标签的数据数据集 ( x i ⊤ , y i ) , i = 1 , 2 , ⋯ , m (\boldsymbol{x}^\top_i,y_i),i=1,2,\cdots,m (xi⊤,yi),i=1,2,⋯,m,用训练所得的最优模式 w 0 \boldsymbol{w}_0 w0,得预测值 y i ′ y'_i yi′, i = 1 , 2 , ⋯ , m i=1,2,\cdots,m i=1,2,⋯,m。记 y = ( y 1 y 2 ⋮ y m ) \boldsymbol{y}=\begin{pmatrix}y_1\\y_2\\\vdots\\y_m\end{pmatrix} y= y1y2⋮ym , y t = ( y 1 ′ y 2 ′ ⋮ y m ′ ) \boldsymbol{y}_t=\begin{pmatrix}y'_1\\y'_2\\\vdots\\y'_m\end{pmatrix} yt= y1′y2′⋮ym′ 计算均方根误差
rmse = ∥ y t − y ∥ 2 m = ∥ y t − y ∥ m \text{rmse}=\sqrt{\frac{\lVert\boldsymbol{y}_{t}-\boldsymbol{y}\rVert^2}{m}}=\frac{\lVert\boldsymbol{y}_{t}-\boldsymbol{y}\rVert}{\sqrt{m}} rmse=m∥yt−y∥2=m∥yt−y∥
表示平均每个测试样本的预测值与对应实际的标签值之间的误差。显然,rmse值越小,意味着训练效果越好。下列代码是可通用于作为预测的回归模型的辅助类Regression类:
import numpy as np
class Regression(): #回归模型
def rmse(self, y, y1): #均方根误差
m = y.size
return np.linalg.norm(y - y1)/np.sqrt(m)
def test(self, x, y): #测试函数
x, y = self.pretreat(x, y, True) #标准化数据
yt = self.F(self.pattern, x) #计算预测值
return yt, self.rmse(y, yt)
class LinearRegressor(Regression, LineModel):
'''线性回归模型'''
Regression类中第3~5行的rmse函数计算向量y和y1之间的均方根误差。第6~9行定义测试函数test,对表示测试数据的x和y先进性归一化处理,然后将训练所得的模式pattern和x代入拟合函数F,计算所得向量yt,最后调用rmse计算y和yt的均方根误差。需要提醒注意的是,Regression类不可实例化,因为它自身并没有计算中所需的pretreat、F函数和pattern属性。它需要配合一定的回归模型使用,第10~11行定义的LinearRegressor类继承了Regression和LineModel(见博文《最优化方法Python计算:无约束优化应用——线性回归模型》),尽管这个类定义中什么事也没做,却是一个可实例化的类,它拥有Regression和LineModel的所有函数和属性。
综合案例
文件SeoulBikeData.csv来自UC Irvine Machine Learning Repository,包含了某城市一共享单车运营公司从2017年12月1日0时至2018年11月30日23时共8760条记录(每行表示一条记录)。数据集包含日期信息(第0列)、每小时租赁的自行车数量(第1列)、时间(第2列)和气象信息:温度、湿度、风速、能见度、体感温度、太阳辐射、降雪量、降雨量(第3~10列)以及第11~13列的季节、假日和运营三个字段,其数据需数字化:约定
下列代码读取数据,并按以上规定作数据类型的转换。此外,数据表中第0行是表示各项数据名称的表头,应予剔除。表中表示一小时单车租赁数的第1列为标签数据,应单列。表示日期的第0列数据在预测中并不适用,也应剔除。考虑到体感温度与气温和湿度相关,可忽略这一特征数据,故应将第7列剔除。
import numpy as np #导入numpy
data = np.loadtxt('SeoulBikeData.csv', delimiter=',', dtype=str)#读取数据文件
X = np.array(data) #转换为数组
X = X[1:,:] #去掉表头
Y = X[:,1].astype(float) #读取标签数据
X = X[:,[2,3,4,5,6,8,9,10,11,12,13]] #去掉日期、租赁数、体感温度
X1 = X[:,8] #读取季节列
X1 = np.array([0 if x == 'Spring' else #串转换为数值
1 if x == 'Summer' else
2 if x == 'Autumn' else
3 for x in X1])
X[:,8] = X1 #回填季节列
X1 = X[:,9] #读取假日列
X1 = np.array([0 if x == 'No Holiday' else #串转换为数值
1 for x in X1])
X[:,9] = X1 #回填假期列
X1 = X[:,10] #读取运营列
X1 = np.array([0 if x == 'No' else #串转换为数值
1 for x in X1])
X[:,10] = X1 #回填运营列
X = X.astype(float) #所有数据转换为实数型
print('共享单车特征数据:')
print(X)
print('共享单车标签数:')
print(Y)
m=X.shape[0] #读取样本个数
print('共有%d个数据样本'%m)
程序的第2行以串类型读取数据集文件SeoulBikeData.csv为data。第3行将data转换为Numpy数组X。第4行将X中表示表头的第0行去掉。第5行将表中第1列(租赁数)转存为表示标签数据的数组Y。第6行去掉原表中的第0列(日期)和第1列(租赁数,已转存),第7列体感温度,保留其他特征数据列。第7~12行将表中表示季节数据的第8列数据转换为数值数据‘Spring’转换为0,‘Summer’转换为1,‘Autumn’转换为2,‘Winter’转换为3。相仿地,第13~16行转换假日列数据,第17~20行转换运营列数据。第21行将表示样本特征数据的X中所有数据转为浮点型。运行程序,将输出
共享单车特征数据:
[[ 0. -5.2 37. ... 3. 0. 1. ]
[ 1. -5.5 38. ... 3. 0. 1. ]
[ 2. -6. 39. ... 3. 0. 1. ]
...
[21. 2.6 39. ... 2. 0. 1. ]
[22. 2.1 41. ... 2. 0. 1. ]
[23. 1.9 43. ... 2. 0. 1. ]]
共享单车标签数:
[254. 204. 173. ... 694. 712. 584.]
共有8760个数据样本
下面,我们将X、Y中的数据随机地分成两部分:Xtrain、Ytrain为训练数据中的特征及标签,Xtest、Ytest为测试数据中的特征与标签。然后用Xtrain和Ytrain训练线性回归模型,并以Xtest、Ytest作为测试数据,计算均方根误差。在上述程序后添加下列代码完成训练和测试
……
a = np.arange(m) #下标集
np.random.seed(2026) #随机种子
index = np.random.choice(a, m//3, replace = False) #随机取得三分之一作为训练集下标
index1 = np.setdiff1d(a,index) #测试集下标
Xtrain = X[index] #训练集特征数据
Ytrain = Y[index] #训练集标签数据
Xtest = X[index1] #测试集特征数据
Ytest = Y[index1] #测试集标签数据
print('随机选取%d个样本训练线性回归模型。'%(m//3))
bikeSharing = LinearRegressor() #构造线性回归模型
bikeSharing.fit(Xtrain, Ytrain) #训练模型
print('模式:')
print(bikeSharing.pattern)
_, rmse = bikeSharing.test(Xtest, Ytest) #测试数据预测
print('对其余%d个样本数据测试,均方根误差:%.4f'%(m - m//3,rmse))
程序第1行的省略号表示前段程序的代码。第2行构造序列 { 0 , 1 , ⋯ , 8759 } \{0,1,\cdots,8759\} {0,1,⋯,8759}为a。第4行从a中随机选取三分之一(m//3)数据,作为训练数据的下标index。第5行从a中除去index的数据,剩下部分作为测试数据下标index1。第6、7行构造训练数据Xtrain,Ytrain。第8、9行构造测试数据Xtest,Ytest。11行创建LinearRegressor类对象bikeSharing。第12行调用fit函数,用Xtrain,Ytrain训练bikeSharingDmand。第13~14行输出训练所得模式pattern。第15行调用bikeSharing的测试函数test对测试特征数据Xtest和Ytest进行测试,将均方根误差赋予rmse。运行程序,输出
训练中...,稍候
随机选取2920个样本训练线性回归模型。
次迭代后完成训练。
模式:
[ 0.1855 0.5034 -0.2227 -0.0213 0.0152 -0.0945
-0.4184 0.0320 -0.0213 -0.034 0.2314 -0.2268]
对其余5840个样本数据测试,均方根误差:0.1240
即模型经过75次迭代训练完毕,由所得模式:
均方根误差为0.1240,意为平均每个小时预测租赁量与实际租赁量(标准化后)误差为12.4%。