优化一、正则化处理
正则化:loss = loss + W*W
作用:让模型更加平滑,更不容易过拟合,从而显著提升了模型效能
def mseLoss(pred, target, model):
loss = nn.MSELoss(reduction='mean')
''' Calculate loss '''
regularization_loss = 0 # 正则项
for param in model.parameters():
# TODO: you may implement L1/L2 regularization here
# 使用L2正则项
# regularization_loss += torch.sum(abs(param))
regularization_loss += torch.sum(param ** 2) # 计算所有参数平方
return loss(pred, target) + 0.00075 * regularization_loss # 返回损失。
在这段代码中:
- 首先定义了一个变量
regularization_loss
并初始化为 0,用于累积正则化损失的值。 - 然后通过循环遍历模型
model
的所有参数(使用model.parameters()
获取参数列表),在这里采用的是 L2 正则化方式,具体操作为对每个参数进行平方(param ** 2
)然后求和(torch.sum(param ** 2)
),将得到的结果累加到regularization_loss
变量中。 - 最后在返回损失时,将原本的均方误差损失(通过
loss(pred, target)
计算得到)与正则化损失(乘以系数0.00075
,即0.00075 * regularization_loss
)相加后返回,以此将正则化的影响纳入到整体的损失计算中,使得在模型训练过程中,除了考虑预测结果与真实标签的误差(均方误差部分),还会考虑模型参数的大小(通过 L2 正则化约束),避免模型过拟合等问题。
优化二、相关系数:线性相关 (SelectKBest)
原因及作用:在数据集中,有些列并不重要,起噪声干扰作用,为了挑选出哪些列重要,引入 SelectKBest,计算每一列和标签列相关系数,保留相关度高的列
1. 函数定义及目的
在代码中,SelectKBest
被用于特征选择,它通过某种评估指标(这里使用的是卡方检验chi2
)来从众多特征中挑选出最有用的k
个特征,并能够返回这些选中特征的相关信息。相关代码位于get_feature_importance
函数内,如下所示:
def get_feature_importance(feature_data, label_data, k =4,column = None):
"""
此处省略 feature_data, label_data 的生成代码。
如果是 CSV 文件,可通过 read_csv() 函数获得特征和标签。
这个函数的目的是, 找到所有的特征种, 比较有用的k个特征, 并打印这些列的名字。
"""
model = SelectKBest(chi2, k=k) #定义一个选择k个最佳特征的函数
feature_data = np.array(feature_data, dtype=np.float64)
# label_data = np.array(label_data, dtype=np.float64)
X_new = model.fit_transform(feature_data, label_data) #用这个函数选择k个最佳特征
#feature_data是特征数据,label_data是标签数据,该函数可以选择出k个特征
print('x_new', X_new)
scores = model.scores_ # scores即每一列与结果的相关性
# 按重要性排序,选出最重要的 k 个
indices = np.argsort(scores)[::-1] #[::-1]表示反转一个列表或者矩阵。
# argsort这个函数, 可以矩阵排序后的下标。 比如 indices[0]表示的是,scores中最小值的下标。
if column: # 如果需要打印选中的列
k_best_features = [column[i+1] for i in indices[0:k].tolist()] # 选中这些列 打印
print('k best features are: ',k_best_features)
return X_new, indices[0:k] # 返回选中列的特征和他们的下标。
2. 具体步骤及功能
-
创建
SelectKBest
模型对象 :
首先通过model = SelectKBest(chi2, k=k)
创建了一个SelectKBest
的实例,其中chi2
是指定的评估指标(用于衡量特征与目标变量之间的关联性),k
则是要选择的特征数量,这里传入的参数k
默认值为 4,意味着要从所有特征中选出 4 个最相关的特征。model = SelectKBest(chi2, k=k)
-
数据准备与特征选择 :
将输入的特征数据feature_data
(通过np.array
转换为合适的numpy
数组格式,并指定数据类型为np.float64
)和标签数据label_data
传入model.fit_transform
方法,该方法会根据设定的评估指标chi2
对特征进行打分,并从中挑选出得分最高的k
个特征,返回经过特征选择后的数据X_new
。X_new = model.fit_transform(feature_data, label_data)
-
获取特征得分及排序 :
通过scores = model.scores_
获取每个特征对应的得分,这些得分反映了特征与目标变量(标签数据)的相关性程度。然后使用indices = np.argsort(scores)[::-1]
对得分进行排序,得到按照相关性从高到低排序后的特征下标,[::-1]
操作是将顺序反转,使得下标按照从相关性高到低的顺序排列。scores = model.scores_
indices = np.argsort(scores)[::-1] -
打印选中的特征列名(可选操作) :
如果传入了column
参数(特征名称列表),则通过列表推导式k_best_features = [column[i+1] for i in indices[0:k].tolist()]
,根据前面得到的相关性最高的k
个特征的下标,从column
列表中取出对应的特征名称,并打印出来。if column:
k_best_features = [column[i+1] for i in indices[0:k].tolist()]
print('k best_features are: ', k_best_features) -
返回结果 :
最后函数返回经过特征选择后的数据X_new
以及相关性最高的k
个特征的下标indices[0:k]
,方便后续进一步使用这些选中的特征。return X_new, indices[0:k]
3. 在整体代码中的应用
在covidDataset
类的__init__
方法中,有调用get_feature_importance
函数来进行特征选择的相关操作,代码如下:
class covidDataset(Dataset):
def __init__(self, path, mode="train", feature_dim=5, all_feature=False):
with open(path,'r') as f:
csv_data = list(csv.reader(f))
column = csv_data[0]
x = np.array(csv_data)[1:,1:-1]
y = np.array(csv_data)[1:,-1]
if all_feature:
col_indices = np.array([i for i in range(0,93)]) # 若全选,则选中所有列。
else:
_, col_indices = get_feature_importance(x, y, feature_dim, column) # 选重要的dim列。
# X_new = get_feature_importance_with_pca(x, feature_dim, column) # 选重要的dim列。
col_indices = col_indices.tolist() # col_indices 从array 转为列表。
csv_data = np.array(csv_data[1:])[:,1:].astype(float) #取csvdata从第二行开始, 第二列开始的数据,并转为float
if mode == 'train': # 训练数据逢5选4, 记录他们的所在行
indices = [i for i in range(len(csv_data)) if i % 5!= 0]
self.y = torch.tensor(csv_data[indices,-1]) # 训练标签是csvdata的最后一列。 要转化为tensor型
elif mode == 'val': # 验证数据逢5选1, 记录他们的所在列
indices = [i for i in range(len(csv_data)) if i % 5 == 0]
# data = torch.tensor(csv_data[indices,col_indices])
self.y = torch.tensor(csv_data[indices,-1]) # 验证标签是csvdata的最后一列。 要转化为tensor型
else:
indices = [i for i in range(len(csv_data))] # 测试机只有数据
# data = torch.tensor(csv_data[indices,col_indices])
data = torch.tensor(csv_data[indices, :]) # 根据选中行取 X, 即模型的输入特征
self.data = data[:, col_indices] # col_indices 表示了重要的K列, 根据重要性, 选中k列。
self.mode = mode # 表示当前数据集的模式
self.data = (self.data - self.data.mean(dim=0,keepdim=True)) / self.data.std(dim=0,keepdim=True) # 对数据进行列归一化
assert feature_dim == self.data.shape[1] # 判断数据的列数是否为规定的dim列, 要不然就报错。
print('Finished reading the {} set of COVID19 Dataset ({} samples found, each dim = {})'
.format(mode, len(self.data), feature_dim)) # 打印读了多少数据
在这里,如果all_feature
为False
,就会调用get_feature_importance
函数,传入特征数据x
、标签数据y
以及要选择的特征维度feature_dim
和特征名称列表column
,来获取重要的特征列下标col_indices
,后续再基于这些下标从数据中选取对应的特征用于数据集的构建等操作。
优化三、主成分分析(PCA)
PCA是什么:
找到一个坐标系,使数据在保留比之前维度更少的时候信息损失是最小的(信息保留的最多,尽量分散,不要重合)。新坐标系第一个维度:主成分一、第二个维度主成分二,找到坐标系,在主成分一上的投影分布方差最大,主成分一能保留最多的信息,最好的,就是要寻找的目标
降维好处:
简化特征的复杂程度,减少训练模型计算量
PCA降维缺点:
离群点的影响较大
降维的衡量指标:
降维后,在各保留维度中的方差要最大,因为方差越大数据越散,防止了数据重叠导致信息失真。
拉伸:方差最大的方向
旋转:方差最大的方向的角度
R:协方差矩阵的特征向量
PCA计算过程:
如何找到方差最大的维度:设降维后的数据叫做"白数据",我们以"白数据"为目标。
1、手头数据矩阵D' -> 白数据矩阵D,通过拉伸和旋转;
2、问题转化为求拉伸的比例、旋转的角度;
3、设拉伸矩阵S、旋转矩阵R,问题转化为求矩阵S和R;
4、
5、到这我们知道可以用手头数据求出C',从而 求出R(旋转角度)、SST = L(降维后各维方差)
置信椭圆:
利用SVD减少计算量:
1. 函数定义及目的
定义了get_feature_importance_with_pca
函数来使用 PCA 进行特征降维,并找出对前k
个主成分影响最大的原始特征,其代码如下:
def get_feature_importance_with_pca(feature_data, k=4, column=None):
"""
使用PCA进行特征降维,并找出对前k个主成分影响最大的原始特征。
参数:
feature_data (pd.DataFrame or np.ndarray): 特征数据。
k (int): 选择的主成分数目,默认为4。
column (list, optional): 特征名称列表。如果feature_data是DataFrame,则可以省略此参数。
返回:
X_new (np.ndarray): 降维后的特征数据。
selected_features (list of lists): 对每个主成分影响最大的原始特征及其载荷。
"""
# 如果提供了列名或feature_data是DataFrame,获取列名
if column is None and hasattr(feature_data, 'columns'):
column = feature_data.columns.tolist()
elif column is None:
raise ValueError("Column names must be provided if feature_data is not a DataFrame.")
// 数据标准化
scaler = StandardScaler()
feature_data_scaled = scaler.fit_transform(feature_data)
// 应用PCA
pca = PCA(n_components=k)
X_new = pca.fit_transform(feature_data_scaled)
return X_new
该函数的主要目的是对输入的特征数据进行主成分分析,将其维度降低到指定的k
维(默认k = 4
),同时在内部的处理过程中涉及到了数据标准化以及通过 PCA 提取主成分等操作,最终返回降维后的特征数据X_new
。
2. 具体步骤及功能
-
获取特征名称(可选操作) :
首先判断是否传入了特征名称列表column
,如果没有传入且输入的feature_data
具有columns
属性(意味着feature_data
可能是pd.DataFrame
类型),则通过column = feature_data.columns.tolist()
获取特征名称列表;如果既没有传入column
,feature_data
又不是DataFrame
类型,就会抛出异常,提示必须提供列名。if column is None and hasattr(feature_data, 'columns'):
column = feature_data.columns.tolist()
elif column is None:
raise ValueError("Column names must be provided if feature_data is not a DataFrame.") -
数据标准化 :
创建StandardScaler
对象scaler
,并使用scaler.fit_transform(feature_data)
对输入的特征数据feature_data
进行标准化处理,将数据转换为均值为 0、方差为 1 的分布,这一步对于 PCA 来说是很重要的预处理步骤,因为 PCA 对数据的尺度比较敏感,标准化可以避免某些特征由于数值大小差异过大而对主成分分析产生不合理的影响。scaler = StandardScaler()
feature_data_scaled = scaler.fit_transform(feature_data) -
应用 PCA 进行降维 :
创建PCA
对象pca
,并指定主成分数量为k
(通过PCA(n_components=k)
),然后使用pca.fit_transform(feature_data_scaled)
对标准化后的特征数据feature_data_scaled
进行主成分分析并将数据降维到k
维,返回降维后的特征数据X_new
。pca = PCA(n_components=k)
X_new = pca.fit_transform(feature_data_scaled) -
返回结果 :
最后函数返回降维后的特征数据X_new
,不过按照函数定义的注释原本还计划返回对每个主成分影响最大的原始特征及其载荷相关信息(目前代码中暂未完整实现这部分返回内容,只返回了降维后的数据)。return X_new
3. 在整体代码中的应用
在covidDataset
类的__init__
方法中,有对get_feature_importance_with_pca
函数调用的相关代码(虽然目前被注释掉了,但体现了其应用的意图),如下所示:
class covidDataset(Dataset):
def __init__(self, path, mode="train", feature_dim=5, all_feature=False):
with open(path,'r') as f:
csv_data = list(csv.reader(f))
column = csv_data[0]
x = np.array(csv_data)[1:,1:-1]
y = np.array(csv_data)[1:,-1]
if all_feature:
col_indices = np.array([i for i in range(0,93)]) // 若全选,则选中所有列。
else:
_, col_indices = get_feature_importance(x, y, feature_dim, column) // 选重要的dim列。
// X_new = get_feature_importance_with_pca(x, feature_dim, column) // 选重要的dim列。
col_indices = col_indices.tolist() // col_indices 从array 转为列表。
csv_data = np.array(csv_data[1:])[:,1:].astype(float) //取csvdata从第二行开始, 第二列开始的数据,并转为float
if mode == 'train': // 训练数据逢5选4, 记录他们的所在行
indices = [i for i in range(len(csv_data)) if i % 5!= 0]
self.y = torch.tensor(csv_data[indices,-1]) // 训练标签是csvdata的最后一列。 要转化为tensor型
elif mode == 'val': // 验证数据逢5选1, 记录他们的所在列
indices = [i for i in range(len(csv_data)) if i % 5 == 0]
// data = torch.tensor(csv_data[indices,col_indices])
self.y = torch.tensor(csv_data[indices,-1]) // 验证标签是csvdata的最后一列。 要转化为tensor型
else:
indices = [i for i in range(len(csv_data))] // 测试机只有数据
// data = torch.tensor(csv_data[indices,col_indices])
data = torch.tensor(csv_data[indices, :]) // 根据选中行取 X, 即模型的输入特征
self.data = data[:, col_indices] // col_indices 表示了重要的K列, 根据重要性, 选中k列。
self.mode = mode // 表示当前数据集的模式
self.data = (self.data - self.data.mean(dim=0,keepdim=True)) / self.data.std(dim=0,keepdim=True) // 对数据进行列归一化
assert feature_dim == self.data.shape[1] // 判断数据的列数是否为规定的dim列, 要不然就报错。
print('Finished reading the {} set of COVID19 Dataset ({} samples found, each dim = {})'
.format(mode, len(self.data), feature_dim)) // 打印读了多少数据
可以看到在else
分支中(也就是不选择所有特征的情况下),原本有打算使用get_feature_importance_with_pca
函数(X_new = get_feature_importance_with_pca(x, feature_dim, column)
这行代码被注释掉了)来选取重要的feature_dim
列,和使用get_feature_importance
函数选取特征的方式形成了另一种可选的特征选择策略,都是为了从原始特征数据中提取出对后续模型训练等操作更有价值的特征子集。