赞
踩
目录
近日,实践了lasso回归和岭回归的底层实现,这是一个课程作业,也是初次尝试,问题很多,就是记录一下。
基本原理:
数据集:本次数据集是一个预测房价的数据集,包括编号、交易日期、房龄、到最近地铁站距离、便利店数量、经度和纬度七个属性,预测值为每平米的价格。为了专注于实现线性回归,已经为大家处理好的数据,删除了编号信息,六个属性和预测值存于numpy数组中保存在了/data/exp02目录下,命名分别为X_train.npy和y_train.npy。测试时调用read_data函数得到特征矩阵和标签向量
岭回归:
岭回归(Ridge Regression)是一种线性回归的扩展形式,它通过在损失函数中添加L2正则化项来解决线性回归中的过拟合问题。L2正则化项会使得模型的权重参数向零收缩,从而降低模型的复杂度,避免过拟合。
在岭回归中,我们优化的目标函数为:
其中,$X$是输入特征矩阵,$y$是对应的标签,$w$是模型的权重参数,$\alpha$是正则化强度超参数。该目标函数包括两个部分,第一个部分是平方误差损失函数,它用来衡量模型的预测值与真实值之间的误差;第二个部分是L2正则化项,它用来限制模型的复杂度。
与普通的线性回归相比,岭回归在解析式中增加了一个偏置项,也就是模型权重的平方和,同时加上一个超参数 $\alpha$ 控制模型复杂度和惩罚力度,通过调整这个超参数可以得到不同的模型表现。
Lasso回归(Lasso Regression)是一种线性回归方法,它通过加入正则化项(L1正则化)来惩罚模型中的大型系数,以避免过拟合问题。Lasso回归在高维数据集中非常有用,因为它可以自动选择最相关的特征,从而简化模型。
Lasso回归的优化目标是最小化以下损失函数:
其中,$X$是输入特征矩阵,$y$是对应的标签,$w$是模型的权重参数,$\alpha$是正则化强度超参数。该目标函数包括两个部分,第一个部分是平方误差损失函数,它用来衡量模型的预测值与真实值之间的误差;第二个部分是L1正则化项,它用来限制模型的复杂度。
Lasso回归的优点是可以自动选择最相关的特征,从而降低模型的复杂度,并且可以处理具有高度相关特征的数据集。缺点是当特征数量非常大时,计算量可能会很大。
要求:只能使用numpy,禁止使用sklearn、pytorch、tensorflow等第三方库
在机器学习中,对数据进行预处理是很重要的一步。数据预处理可以帮助我们清洗数据,去除噪声和异常值,提高模型的准确性和可靠性。
数据预处理通常包括以下几个步骤:
通过对数据进行预处理,我们可以提高模型的准确性和可靠性,从而更好地解决实际问题。
在这个实现里面,因为数据集是已经处理好的,所以这个环节只进行了数据规范化。常用的数据规范化方法有以下几种
1、最大最小值归一化(Min-Max Normalization):这种方法通过将数据缩放到一个特定的范围内(通常是0到1之间)来实现规范化。具体来说,对于每个数据点,我们可以使用下面的公式进行归一化:
x' = (x - min) / (max - min)
其中,x表示原始数据点,x'表示归一化后的数据点,min和max分别表示数据集中的最小值和最大值。
2、标准化(Standardization):这种方法通过将数据转换为均值为0,标准差为1的分布来实现规范化。具体来说,对于每个数据点,我们可以使用下面的公式进行标准化:
x' = (x - mean) / std
其中,x表示原始数据点,x'表示标准化后的数据点,mean和std分别表示数据集中的均值和标准差。
3、鲁棒缩放(Robust Scaling):这种方法与最大最小值归一化类似,但是它使用数据集中的中位数和四分位数来代替最小值和最大值。这样可以使得规范化后的数据更加鲁棒,不受异常值的影响。
这里选用了标准化的方法。
- #对测试数据进行统一标准化
- def standard_st(data):
- data_mean = np.mean(data)
- # X_var=np.var(X)#方差
- data_std = np.std(data)
- data = (data - data_mean) / data_std
- return data
- #对数据进行处理(标准化操作并将构建增广矩阵)
- def data_processing_st(X):
- X = np.apply_along_axis(standard_st, 1, X)
- ones = np.ones((X.shape[0], 1))
- X = np.hstack((X, ones))
- return X
(基本思路就是先编写一个实现标准化的函数,然后再通过np.apply_along_axis()的方法实现对数据矩阵的每一行做规范化)
在初次尝试的时候犯了一个错误,在对数据进行规范化的时候,是基于对全体数据集进行规范化的(r然后就导致精度一直上不来),这种全局规范化的方法虽然可以确保数据的统一性,但实际上会产生一些问题。首先,全局规范化可能会损失数据中的一些重要信息。例如,如果我们有两个不同的数据子集,它们的数据分布可能不同,全局规范化可能会使它们的特征值相同,导致模型无法区分它们。
其次,全局规范化还可能导致模型过度拟合训练数据。这是因为全局规范化会使数据的分布变得更加集中,从而增加模型在训练数据上的准确性,但同时也可能减少了模型在未知数据上的泛化能力。
还有,在对房价进行预测时,房屋面积和房龄这两个特征的值范围可能相差很大,因此我们需要对它们进行独立的规范化,以确保它们在相同的尺度上。
本次实践直接指定使用lasso回归,所以不用选择模型。
一般来说,要从具体的问题来选择相对应的模型。本次的机器学习目标是通过进行相关计算来预测房价,常用的模型有线性回归模型、随机森林、岭回归、lasso回归等。
前面lasso回归的损失函数已经给出,即
- # 定义损失函数
- mse = np.sum(((X @ w )- y.T) @ ((X @ w) - y.T).T)/(np.shape(X)[0])
- l1 = alpha * ((np.sum(np.abs(w))))
- lassoloss = mse + l1
使用梯度下降的方法对学习准则的优化
- # 计算梯度
- dw = X.T @ ((X @ w) - y.T) + alpha * np.sign(w) # 不是很理解为什么
- loss_old = lassoloss#记录上一次的风险损失
- # 更新参数
- w = w - beta * dw
这一步直接通过for循环迭代实现就行,在循环里面我们还应该设置一个提前停止的条件,避免无意义的循环迭代。
- # 后边损失函数下降十分缓慢,设置提前停止的条件
- if (np.abs(min - loss_old) < 0.0001):
- print('提前停止!')
- break
- # 获取最小损失时候的参数w
- if (min >= lassoloss):
- min = lassoloss
- best = w
小结:基本的代码就已经完成了,整个代码我是通过手动的修改超参数,以获取到一个最优的参数(这一步可以使用网格搜索或是随机搜索的方法来进行优化的)。
基本思路:
在岭回归中我的优化目标函数为在岭回归中,我们优化的目标函数为:
因为该函数是凸函数,有解析解。极值点是导数为0的点,所以先对w进行求导,
令偏导等于0 ,然后对等式进行处理,便得到w的方程。代码中只需要找到一个合适的超参数代入该方程,便可以求得我们的目标参数。
- alpha=1#2023/4/30
- ridgeloss = 2 * (X.T @ X @ w - X.T @ y + alpha * w)
- # 对损失函数中w求偏导,令导数为0,求得w
- w = np.linalg.inv((X.T @ X + alpha * np.eye(np.shape((X.T @ X))[0]))) @ X.T @ y
1、对于本次预测房价的项目中,不适合对数据整体进行规范化,而应该是对每个特征进行规范化。
2、可以使用增广矩阵,将w、b参数合并一起,会方便一点
3、在实现Ridge回归的时候,我使用的规范方法是最大最小值规划化,将其缩放到(0,1)之间,针对这个机器学习的问题,好像这个找到的解更优。
- # 最终在main函数中传入一个维度为6的numpy数组,输出预测值
-
- import os
-
- try:
- import numpy as np
- except ImportError as e:
- os.system("sudo pip3 install numpy")
- import numpy as np
- #对测试数据进行统一标准化
- def standard_st(data):
- data_mean = np.mean(data)
- # X_var=np.var(X)#方差
- data_std = np.std(data)
- data = (data - data_mean) / data_std
- return data
- #对数据进行处理(标准化操作并将构建增广矩阵)
- def data_processing_st(X):
- X = np.apply_along_axis(standard_st, 1, X)
- ones = np.ones((X.shape[0], 1))
- X = np.hstack((X, ones))
- return X
- #使用01规范法处理数据
- def data_processing_01(X):
-
- X = np.apply_along_axis(standard_01, 1, X)
- ones = np.ones((X.shape[0], 1))
- X = np.hstack((X, ones))
- return X
- #使用01规范法处理数据
- def standard_01(data):
- min_val = np.min(data)
- max_val = np.max(data)
- scaled_data = (data - min_val) / (max_val - min_val)
- return scaled_data
-
- def ridge(data):
- data=standard_01(data)
- X, y = read_data()
- X = data_processing_01(X)
- # 选择模型并初始化参数
- w = np.zeros((7, 1))
- y_pre = X @ w # 计算预测值
- # 定义损失函数
- # alpha = 10 # 正则化系数
- alpha=1#2023/4/30
- ridgeloss = 2 * (X.T @ X @ w - X.T @ y + alpha * w)
- # 对损失函数中w求偏导,令导数为0,求得w
- w = np.linalg.inv((X.T @ X + alpha * np.eye(np.shape((X.T @ X))[0]))) @ X.T @ y
- #获取w和b
- b = w[-1]
- w = w[:-1]
- w = w.reshape(6, 1)
- return data@w+b
-
-
- def lasso(data):
- data = standard_st(data)
- X, y = read_data()
- X = data_processing_st(X)
- y=y.reshape(1,404)
- # 设置超参数
- alpha = 1000 # 正则化系数
- beta = 0.00045 # 学习率
-
- # 选择模型并初始化参数
- w = np.zeros((7, 1))
- best = w
- min = 365194055
- loss_old = 1 # 记录上一次的损失,如果两个损失变化太小,说明已经趋近最优值,提前停止
- # print(X@w)
- for i in range(100000):
- y_pre = X @ w # 计算预测值
- # 定义损失函数
- mse = np.sum(((X @ w )- y.T) @ ((X @ w) - y.T).T)/(np.shape(X)[0])
- l1 = alpha * ((np.sum(np.abs(w))))
- lassoloss = mse + l1
- # 计算梯度
- dw = X.T @ ((X @ w) - y.T) + alpha * np.sign(w)
- loss_old = lassoloss#记录上一次的风险损失
- # 更新参数
- w = w - beta * dw
- # 后边损失函数下降十分缓慢,设置提前停止的条件
- if (np.abs(min - loss_old) < 0.0001):
- print('提前停止!')
- break
- # 获取最小损失时候的参数w
- if (min >= lassoloss):
- min = lassoloss
- best = w
- # 输出损失函数的值
-
- #print(f'Iteration {i}: Loss = {lassoloss} ')
- w =best[0:6,:]
- b=best[6,0]
- print(data@w+b)
- return data@w+b
-
- def read_data(path='./data/exp02/'):
- x = np.load(path + 'X_train.npy')
- y = np.load(path + 'y_train.npy')
- return x, y

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。