from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2
import csv #读 CSV
import numpy as np
import time
import matplotlib.pyplot as plt
import pandas as pd
from torch import optim
import torch.nn as nn
import torch
from torch.utils.data import Dataset,DataLoader
def get_feature_importance(feature_data, label_data, k=4,column = None):
"""
此处省略 feature_data, label_data 的生成代码。
如果是 CSV 文件,可通过 read_csv() 函数获得特征和标签。
这个函数的目的是, 找到所有的特征ZHONG, 比较有用的k个特征, 并打印这些列的名字。
"""
model = SelectKBest(chi2, k=k) #定义一个选择k个最佳特征的函数
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] # 返回选中列的特征和他们的下标。
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] # 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列。
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] #1,2,3,4, 6,7,8,9
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) # 对数据进行列归一化 0正太分布
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)) # 打印读了多少数据
def __getitem__(self, item): # getitem 需要完成读下标为item的数据
if self.mode == 'test': # 测试集没标签。 注意data要转为模型需要的float32型
return self.data[item].float()
else : # 否则要返回带标签数据
return self.data[item].float(), self.y[item].float()
def __len__(self):
return len(self.data) # 返回数据长度。
class myNet(nn.Module):
def __init__(self, inDim):
super(myNet,self).__init__()
self.fc1 = nn.Linear(inDim, 128) # 全连接
self.relu = nn.ReLU() # 激活函数 ,添加非线性
# self.fc3 = nn.Linear(128, 128)
self.fc2 = nn.Linear(128,1) # 全连接 设计模型架构。 他没有数据
def forward(self, x): #forward, 即模型前向过程
x = self.fc1(x)
x = self.relu(x)
# x = self.fc3(x)
x = self.fc2(x)
if len(x.size()) > 1:
return x.squeeze(1)
else:
return x
def train_val(model, trainloader, valloader,optimizer, loss, epoch, device, save_):
# trainloader = DataLoader(trainset,batch_size=batch,shuffle=True)
# valloader = DataLoader(valset,batch_size=batch,shuffle=True)
model = model.to(device) # 模型和数据 ,要在一个设备上。 cpu - gpu
plt_train_loss = []
plt_val_loss = []
val_rel = []
min_val_loss = 100000 # 记录训练验证loss 以及验证loss和结果
for i in range(epoch): # 训练epoch 轮
start_time = time.time() # 记录开始时间
model.train() # 模型设置为训练状态 结构
train_loss = 0.0
val_loss = 0.0
for data in trainloader: # 从训练集取一个batch的数据
optimizer.zero_grad() # 梯度清0
x, target = data[0].to(device), data[1].to(device) # 将数据放到设备上
pred = model(x) # 用模型预测数据
bat_loss = loss(pred, target) # 计算loss
bat_loss.backward() # 梯度回传, 反向传播。
optimizer.step() #用优化器更新模型。 轮到SGD出手了
train_loss += bat_loss.detach().cpu().item() #记录loss和
plt_train_loss. append(train_loss/trainloader.dataset.__len__()) #记录loss到列表。注意是平均的loss ,因此要除以数据集长度。
model.eval() # 模型设置为验证状态
with torch.no_grad(): # 模型不再计算梯度
for data in valloader: # 从验证集取一个batch的数据
val_x , val_target = data[0].to(device), data[1].to(device) # 将数据放到设备上
val_pred = model(val_x) # 用模型预测数据
val_bat_loss = loss(val_pred, val_target) # 计算loss
val_loss += val_bat_loss.detach().cpu().item() # 计算loss
val_rel.append(val_pred) #记录预测结果
if val_loss < min_val_loss:
torch.save(model, save_) #如果loss比之前的最小值小, 说明模型更优, 保存这个模型
plt_val_loss.append(val_loss/valloader.dataset.__len__()) #记录loss到列表。注意是平均的loss ,因此要除以数据集长度。
#
print('[%03d/%03d] %2.2f sec(s) TrainLoss : %.6f | valLoss: %.6f' % \
(i, epoch, time.time()-start_time, plt_train_loss[-1], plt_val_loss[-1])
) #打印训练结果。 注意python语法, %2.2f 表示小数位为2的浮点数, 后面可以对应。
# print('[%03d/%03d] %2.2f sec(s) TrainLoss : %3.6f | valLoss: %.6f' % \
# (i, epoch, time.time()-start_time, 2210.2255411, plt_val_loss[-1])
# ) #打印训练结果。 注意python语法, %2.2f 表示小数位为2的浮点数, 后面可以对应。
plt.plot(plt_train_loss) # 画图, 向图中放入训练loss数据
plt.plot(plt_val_loss) # 画图, 向图中放入训练loss数据
plt.title('loss') # 画图, 标题
plt.legend(['train', 'val']) # 画图, 图例
plt.show() # 画图, 展示
def evaluate(model_path, testset, rel_path ,device):
model = torch.load(model_path).to(device) # 模型放到设备上。 加载模型
testloader = DataLoader(testset, batch_size=1, shuffle=False) # 将验证数据放入loader 验证时, 一般batch为1
val_rel = []
model.eval() # 模型设置为验证状态
with torch.no_grad(): # 模型不再计算梯度
for data in testloader: # 从测试集取一个batch的数据
x = data.to(device) # 将数据放到设备上
pred = model(x) # 用模型预测数据
val_rel.append(pred.item()) #记录预测结果
print(val_rel) #打印预测结果
with open(rel_path, 'w') as f: #打开保存的文件
csv_writer = csv.writer(f) #初始化一个写文件器 writer
csv_writer.writerow(['id','tested_positive']) #在第一行写上 “id” 和 “tested_positive”
for i in range(len(testset)): # 把测试结果的每一行放入输出的excel表中。
csv_writer.writerow([str(i),str(val_rel[i])])
print("rel已经保存到"+ rel_path)
all_col = False #是否使用所有的列
device = 'cuda' if torch.cuda.is_available() else 'cpu' #选择使用cpu还是gpu计算。
print(device)
train_path = 'covid.train.csv' # 训练数据路径
test_path = 'covid.test.csv' # 测试数据路径
file = pd.read_csv(train_path)
file.head() # 用pandas 看看数据长啥样
if all_col == True:
feature_dim = 93
else:
feature_dim = 6 #是否使用所有的列
trainset = covidDataset(train_path,'train', feature_dim=feature_dim, all_feature=all_col)
valset = covidDataset(train_path,'val', feature_dim=feature_dim, all_feature=all_col)
testset = covidDataset(test_path,'test', feature_dim=feature_dim, all_feature=all_col) #读取训练, 验证,测试数据
# 返回损失。
#
# 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 # 返回损失。
#
# loss = mseLoss # 定义mseloss 即 平方差损失,
loss = nn.MSELoss() # 定义mseloss 即 平方差损失,
config = {
'n_epochs': 50, # maximum number of epochs
'batch_size': 32, # mini-batch size for dataloader
'optimizer': 'SGD', # optimization algorithm (optimizer in torch.optim)
'optim_hparas': { # hyper-parameters for the optimizer (depends on which optimizer you are using)
'lr': 0.0001, # learning rate of SGD
'momentum': 0.9 # momentum for SGD
},
'early_stop': 200, # early stopping epochs (the number epochs since your model's last improvement)
'save_path': 'model_save/model.pth', # your model will be saved here
}
model = myNet(feature_dim).to(device) # 实例化模型
optimizer = optim.SGD(model.parameters(), lr=0.001, momentum=0.9) # 定义优化器 动量
trainloader = DataLoader(trainset, batch_size=config['batch_size'], shuffle=True)
valloader = DataLoader(valset, batch_size=config['batch_size'], shuffle=True) # 将数据装入loader 方便取一个batch的数据
train_val(model, trainloader, valloader, optimizer, loss, config['n_epochs'], device,save_=config['save_path']) # 训练
evaluate(config['save_path'], testset, 'pred.csv', device) # 验证
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2
import csv # 读 CSV
import numpy as np
import time
import matplotlib.pyplot as plt
import pandas as pd
from torch import optim
import torch.nn as nn
import torch
from torch.utils.data import Dataset, DataLoader
sklearn.feature_selection
: 用于特征选择,SelectKBest
和 chi2
是常用的方法。csv
: 处理 CSV 文件读写。numpy
: 数值计算和数组操作。time
: 记录训练时间。matplotlib.pyplot
: 绘制训练过程中的损失曲线。pandas
: 数据处理和分析,尤其适合处理表格数据。torch
相关库: 深度学习框架 PyTorch 的核心模块,用于构建模型、优化、数据加载等。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 个最佳特征的函数
X_new = model.fit_transform(feature_data, label_data) # 用这个函数选择 k 个最佳特征
print('x_new', X_new)
scores = model.scores_ # scores 即每一列与结果的相关性
# 按重要性排序,选出最重要的 k 个
indices = np.argsort(scores)[::-1] # [::-1] 表示反转一个列表或者矩阵
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] # 返回选中列的特征和它们的下标
SelectKBest
和 chi2
统计方法来选择最佳特征。fit_transform
方法选择并转换特征数据,这是进行特征提取与数据转换的关键函数。scores_
),model.scores_
是SelectKBest
对象的一个属性,包含了每个特征的得分,得分越高,表示该特征与目标变量的相关性越强。argsort
对评分进行排序,选出得分最高的 k 个特征,注意argsort
排序得到的是相关性评分从小到大的行的下标的队列。column
),则打印选中的特征名称。chi2
适用于非负特征。k
的默认值为 4,可以根据需要调整。covidDataset
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) # 选择重要的特征列
col_indices = col_indices.tolist()
csv_data = np.array(csv_data[1:])[:, 1:].astype(float) # 转换为浮点数
if mode == 'train':
indices = [i for i in range(len(csv_data)) if i % 5 != 0] # 训练集: 选取 4/5 的数据
self.y = torch.tensor(csv_data[indices, -1])
elif mode == 'val':
indices = [i for i in range(len(csv_data)) if i % 5 == 0] # 验证集: 选取 1/5 的数据
self.y = torch.tensor(csv_data[indices, -1])
else:
indices = [i for i in range(len(csv_data))] # 测试集: 选取所有数据
data = torch.tensor(csv_data[indices, :])
self.data = data[:, col_indices] # 选取重要的特征列
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], "特征维度不匹配"
print('Finished reading the {} set of COVID19 Dataset ({} samples found, each dim = {})'
.format(mode, len(self.data), feature_dim))
def __getitem__(self, item):
if self.mode == 'test':
return self.data[item].float()
else:
return self.data[item].float(), self.y[item].float()
def __len__(self):
return len(self.data)
torch.utils.data.Dataset
,用于创建自定义数据集。__init__
:
csv.reader
读取 CSV 文件。column
存储列名。x
为特征数据,去除第一列(假设是 ID)和最后一列(标签)。y
为标签数据。all_feature
为 True
,则选取所有 93 个特征。get_feature_importance
函数选择最重要的 feature_dim
个特征。mode
划分训练集、验证集和测试集:
tensor
类型。self.data = (self.data - self.data.mean(dim=0, keepdim=True)) / self.data.std(dim=0, keepdim=True)
这个运算能够运行,原因是self.data是张量,而PyTorch 张量支持广播机制,即使 self.data
的形状是 (n_samples, n_features)
,而 mean
和 std
的形状是 (1, n_features)
,PyTorch 会自动将 mean
和 std
广播到与 self.data
相同的形状,然后进行逐元素运算。assert
确保选择的特征维度与 feature_dim
一致。__getitem__
:
__getitem__
是 PyTorch Dataset
类的必需方法。它定义了如何通过索引 item
从数据集中获取一个样本。每次调用 DataLoader
时,__getitem__
会被自动调用,用于加载一个批次的数据。__len__
:
myNet
class myNet(nn.Module):
def __init__(self, inDim):
super(myNet, self).__init__()
self.fc1 = nn.Linear(inDim, 128) # 全连接层,将输入维度映射到 128 维
self.relu = nn.ReLU() # 激活函数,增加非线性
self.fc2 = nn.Linear(128, 1) # 输出层,将 128 维映射到 1 维(回归任务)
def forward(self, x):
x = self.fc1(x) # 输入通过第一层全连接
x = self.relu(x) # 激活函数
x = self.fc2(x) # 输出层
if len(x.size()) > 1:
return x.squeeze(1) # 如果输出有多个维度,压缩第1维
else:
return x
torch.nn.Module
,用于定义神经网络模型。__init__
:
fc1
: 第一个全连接层,将输入特征维度 (inDim
) 映射到 128 维。relu
: ReLU 激活函数,增加网络的非线性能力。fc2
: 输出层,将 128 维映射到 1 维,适用于回归任务(预测连续值)。forward
:
fc1
。fc2
。squeeze
压缩第 1 维,变为 (batch_size,)。train_val
def train_val(model, trainloader, valloader, optimizer, loss, epoch, device, save_):
model = model.to(device) # 将模型移动到指定设备(CPU 或 GPU)
plt_train_loss = []
plt_val_loss = []
val_rel = []
min_val_loss = 100000 # 初始化最小验证损失
for i in range(epoch): # 训练多个轮次
start_time = time.time()
model.train() # 设置模型为训练模式
train_loss = 0.0
val_loss = 0.0
# 训练阶段
for data in trainloader:
optimizer.zero_grad() # 清空梯度
x, target = data[0].to(device), data[1].to(device) # 将数据移动到设备
pred = model(x) # 前向传播
bat_loss = loss(pred, target) # 计算损失
bat_loss.backward() # 反向传播
optimizer.step() # 更新参数
train_loss += bat_loss.detach().cpu().item() # 累加训练损失
# 记录平均训练损失
plt_train_loss.append(train_loss / len(trainloader.dataset))
# 验证阶段
model.eval() # 设置模型为评估模式
with torch.no_grad(): # 关闭梯度计算
for data in valloader:
val_x, val_target = data[0].to(device), data[1].to(device)
val_pred = model(val_x)
val_bat_loss = loss(val_pred, val_target)
val_loss += val_bat_loss.detach().cpu().item()
val_rel.append(val_pred)
# 保存最优模型
if val_loss < min_val_loss:
torch.save(model, save_) # 保存模型
min_val_loss = val_loss # 更新最小验证损失
# 记录平均验证损失
plt_val_loss.append(val_loss / len(valloader.dataset))
# 打印训练和验证损失
print('[%03d/%03d] %2.2f sec(s) TrainLoss : %.6f | valLoss: %.6f' % (
i, epoch, time.time() - start_time, plt_train_loss[-1], plt_val_loss[-1]))
# 绘制损失曲线
plt.plot(plt_train_loss)
plt.plot(plt_val_loss)
plt.title('Loss')
plt.legend(['Train', 'Val'])
plt.show()
model
: 神经网络模型。trainloader
: 训练数据的 DataLoader。valloader
: 验证数据的 DataLoader。optimizer
: 优化器,用于更新模型参数。loss
: 损失函数。epoch
: 训练的总轮次。device
: 设备类型(CPU 或 GPU)。save_
: 模型保存路径。model.train()
),启用 Dropout 等层的训练行为。optimizer.zero_grad()
)。model.eval()
),禁用 Dropout 等层的训练行为。torch.no_grad()
禁用梯度计算,节省内存和计算资源。val_rel
(虽然在代码中未被使用)。matplotlib
绘制训练和验证损失随轮次变化的曲线,帮助可视化训练过程。train_loss
和 val_loss
的计算方式需要确保正确,避免由于批次大小不同导致的损失不一致。val_rel
变量在函数中被记录但未被使用,可能需要进一步处理或删除。evaluate
def evaluate(model_path, testset, rel_path, device):
model = torch.load(model_path).to(device) # 加载并移动模型到指定设备
testloader = DataLoader(testset, batch_size=1, shuffle=False) # 测试集 DataLoader
val_rel = []
model.eval() # 设置模型为评估模式
with torch.no_grad(): # 禁用梯度计算
for data in testloader:
x = data.to(device)
pred = model(x)
val_rel.append(pred.item()) # 存储预测结果
print(val_rel) # 打印所有预测结果
# 将预测结果保存到 CSV 文件
with open(rel_path, 'w') as f:
csv_writer = csv.writer(f)
csv_writer.writerow(['id', 'tested_positive']) # 写入表头
for i in range(len(testset)):
csv_writer.writerow([str(i), str(val_rel[i])])
print("rel已经保存到" + rel_path)
model_path
: 已保存的模型文件路径。testset
: 测试数据集(covidDataset
实例)。rel_path
: 结果保存的 CSV 文件路径。device
: 设备类型(CPU 或 GPU)。torch.load
加载保存的模型,并将其移动到指定设备。DataLoader
,批次大小为 1,且不进行数据洗牌(shuffle=False
),保证预测结果的顺序与数据集一致。model.eval()
),禁用 Dropout 等层的训练行为。torch.no_grad()
禁用梯度计算,提高预测效率。val_rel
列表中。['id', 'tested_positive']
。id
对应于测试集中的样本索引,确保与实际数据匹配。all_col = False # 是否使用所有的列
device = 'cuda' if torch.cuda.is_available() else 'cpu' # 选择使用 CPU 还是 GPU
print(device)
train_path = 'covid.train.csv' # 训练数据路径
test_path = 'covid.test.csv' # 测试数据路径
file = pd.read_csv(train_path)
file.head() # 用 pandas 查看数据结构(仅用于调试,未赋值)
if all_col == True:
feature_dim = 93
else:
feature_dim = 6 # 是否使用所有的列
# 创建数据集实例
trainset = covidDataset(train_path, 'train', feature_dim=feature_dim, all_feature=all_col)
valset = covidDataset(train_path, 'val', feature_dim=feature_dim, all_feature=all_col)
testset = covidDataset(test_path, 'test', feature_dim=feature_dim, all_feature=all_col)
# 定义损失函数(此处使用均方误差损失)
loss = nn.MSELoss()
# 训练配置
config = {
'n_epochs': 50, # 最大轮次
'batch_size': 32, # 每个批次的大小
'optimizer': 'SGD', # 优化算法
'optim_hparas': { # 优化器的超参数
'lr': 0.0001, # 学习率
'momentum': 0.9 # 动量
},
'early_stop': 200, # 早停策略(未使用)
'save_path': 'model_save/model.pth', # 模型保存路径
}
# 实例化模型
model = myNet(feature_dim).to(device)
# 定义优化器
optimizer = optim.SGD(model.parameters(), lr=0.001, momentum=0.9)
# 创建数据加载器
trainloader = DataLoader(trainset, batch_size=config['batch_size'], shuffle=True)
valloader = DataLoader(valset, batch_size=config['batch_size'], shuffle=True)
# 开始训练与验证
train_val(model, trainloader, valloader, optimizer, loss, config['n_epochs'], device, save_=config['save_path'])
# 评估并保存预测结果
evaluate(config['save_path'], testset, 'pred.csv', device)
all_col
: 是否使用所有特征列,False
表示选择部分重要特征。device
: 检查是否有可用的 GPU,优先使用 GPU 加速,否则使用 CPU。train_path
: 训练数据文件路径。test_path
: 测试数据文件路径。pandas
读取训练数据并查看前几行(file.head()
),帮助理解数据结构。注意:此行代码未赋值或使用,实际运行时可能不显示输出。all_col
为 True
,则 feature_dim
为 93。feature_dim
为 6(根据特征选择函数选择的特征数)。trainset
: 训练集。valset
: 验证集。testset
: 测试集。covidDataset
类中完成。nn.MSELoss
),适用于回归任务。config
字典):
n_epochs
: 训练的总轮次,设置为 50。batch_size
: 每个批次的样本数量,设置为 32。optimizer
: 优化算法,设置为 SGD。optim_hparas
: 优化器的超参数,包括学习率和动量。early_stop
: 早停策略的参数(在当前代码中未使用)。save_path
: 模型保存路径。myNet
类的实例,输入维度为 feature_dim
,并将模型移动到指定设备。config['optim_hparas']
中定义的学习率 (0.0001
) 未被使用,实际使用的是 0.001
。trainloader
: 训练数据的 DataLoader,启用数据洗牌 (shuffle=True
)。valloader
: 验证数据的 DataLoader,启用数据洗牌 (shuffle=True
)。shuffle=False
),以保持验证结果的一致性。train_val
函数,传入模型、训练加载器、验证加载器、优化器、损失函数、轮次、设备和模型保存路径。evaluate
函数,传入模型保存路径、测试数据集、结果保存路径和设备类型。early_stop
参数在当前代码中未被实现,可以考虑在 train_val
函数中添加早停机制,以防止过拟合。正则化(Regularization)是深度学习中一种防止模型过拟合的技术。过拟合是指模型在训练集上表现很好,但在验证集或测试集上的表现较差,这通常是因为模型过于复杂,过度拟合了训练数据中的噪声或细节,导致泛化能力下降。
正则化通过在模型训练过程中引入某些约束或修改优化目标,减少模型对训练数据的过度依赖,从而提高模型的泛化能力。
在深度学习中,模型通常具有大量的参数(例如深度神经网络中的权重和偏置),这使得模型非常灵活,能够拟合复杂的数据分布。然而,这种灵活性也会导致模型容易记住训练数据中的噪声或异常值,而不是学习到数据的真实分布,最终导致模型在新数据上的表现较差。
正则化的目标是限制模型的复杂度,使模型能够更好地泛化到未见过的数据。
以下是深度学习中常见的正则化方法:
L1 和 L2 正则化通过在损失函数中添加权重参数的约束,限制模型参数的大小,从而防止模型过于复杂。
L2 正则化(权重衰减,Weight Decay)
在损失函数中添加权重的平方和作为惩罚项:
L total = L original + λ ∑ i w i 2 L_{\text{total}} = L_{\text{original}} + \lambda \sum_{i} w_i^2 Ltotal=Loriginal+λi∑wi2
其中, L original L_{\text{original}} Loriginal 是原始损失函数, w i w_i wi 是模型的权重, λ \lambda λ 是正则化强度的超参数。
L2 正则化倾向于让权重变得更小、更平滑,从而减少过拟合。
L1 正则化
在损失函数中添加权重的绝对值和作为惩罚项:
L total = L original + λ ∑ i ∣ w i ∣ L_{\text{total}} = L_{\text{original}} + \lambda \sum_{i} |w_i| Ltotal=Loriginal+λi∑∣wi∣
L1 正则化会导致一些权重变为零,从而实现特征选择(稀疏性)。
Dropout 是一种随机失活的方法,用于防止神经网络过拟合。在每次训练迭代中,随机将一部分神经元的输出置为零,模拟多个子网络的训练过程。
这种方法可以减少神经元之间的共适应性,从而提高模型的泛化能力。
数据增强通过对训练数据进行随机变换(如图像的旋转、翻转、裁剪等),生成更多样化的训练样本,从而降低模型对特定数据模式的依赖。
提前停止是一种简单但有效的正则化方法。在训练过程中,监控验证集的损失或准确率,如果验证集的性能不再提升,则提前停止训练。
Batch Normalization(批归一化)是通过对每一层的激活值进行归一化,防止激活值过大或过小,从而使训练过程更加稳定。
正则化激活函数(如 ReLU、Leaky ReLU 等)通过限制激活值的范围,间接降低模型的复杂度,从而减少过拟合。
正则化方法的选择依赖于具体任务和数据特点:
在深度学习或机器学习中,SelectKBest
是一种特征选择方法,通常用于从高维特征中选择最重要的特征,以提高模型的性能或减少计算成本。它属于特征选择(Feature Selection)的一部分,特征选择的目的是减少输入数据的维度,同时保留对模型预测最有用的信息。
SelectKBest
?SelectKBest
是 Scikit-learn 库中提供的一种特征选择工具。它按照统计指标对特征进行评分,然后选择得分最高的 k k k 个特征。其核心思想是基于某种统计方法评估每个特征与目标变量之间的相关性,从而挑选出最相关的特征。
公式化地说,给定一个数据集 X X X 和目标变量 y y y:
SelectKBest
会根据每个特征 x i x_i xi 与目标变量 y y y 的相关性得分,选择得分最高的 k k k 个特征。SelectKBest
的工作流程计算每个特征的得分:
排序:
选择前 k k k 个特征:
SelectKBest
支持多种评分函数,不同的评分函数适用于不同类型的数据。以下是一些常用的评分函数及其适用场景:
chi2
)f_classif
和 f_regression
)f_classif
:用于分类任务中的连续型特征。f_regression
:用于回归任务中的连续型特征。mutual_info_classif
和 mutual_info_regression
)mutual_info_classif
:用于分类任务。mutual_info_regression
:用于回归任务。SelectKBest
的实现在 Scikit-learn 中,SelectKBest
的使用非常简单。以下是一个具体的实现示例:
from sklearn.feature_selection import SelectKBest, chi2
from sklearn.datasets import load_iris
# 加载数据集
data = load_iris()
X, y = data.data, data.target
# 使用卡方检验选择前 2 个最重要的特征
selector = SelectKBest(score_func=chi2, k=2)
X_new = selector.fit_transform(X, y)
print("原始特征维度:", X.shape)
print("选择后的特征维度:", X_new.shape)
from sklearn.feature_selection import SelectKBest, f_regression
from sklearn.datasets import make_regression
# 生成回归数据
X, y = make_regression(n_samples=100, n_features=10, noise=0.1)
# 使用 F 检验选择前 5 个最重要的特征
selector = SelectKBest(score_func=f_regression, k=5)
X_new = selector.fit_transform(X, y)
print("原始特征维度:", X.shape)
print("选择后的特征维度:", X_new.shape)
SelectKBest
的优点和局限性SelectKBest
是一种单变量特征选择方法,它独立地评估每个特征与目标变量的相关性,而不考虑特征之间的交互作用。SelectKBest
在深度学习中的应用在深度学习中,SelectKBest
通常用于数据预处理阶段,尤其是在处理高维稀疏数据(如文本特征或基因数据)时。通过先用 SelectKBest
降维,可以减少输入特征的数量,从而加速深度学习模型的训练过程。
例如:
SelectKBest
可以用于从词袋模型(Bag-of-Words)或 TF-IDF 特征中选择最重要的词语。SelectKBest
可以用于从基因表达数据中选择与疾病分类最相关的基因。主成分分析(Principal Component Analysis,简称 PCA)是一种广泛应用于数据降维和特征提取的线性技术。它在深度学习和机器学习中经常被用来减少数据的维度,同时保留尽可能多的有用信息,从而提高模型的效率。以下是关于 PCA 的详细介绍:
主成分分析是一种线性降维方法,旨在通过寻找数据的主要变化方向,将高维数据投影到一个低维子空间,同时尽可能保留原始数据的主要信息(即数据的方差)。
给定一个数据集 X ∈ R n × m X \in \mathbb{R}^{n \times m} X∈Rn×m( n n n 是样本数, m m m 是特征数),PCA 的目标是找到一组正交向量(主成分),将数据从 m m m 维投影到 k k k 维( k < m k < m k<m),使得投影后的数据保留尽可能多的方差。
PCA 的数学原理可以分为以下几个步骤:
为了确保每个特征对结果的影响是均等的,PCA 通常要求对数据进行标准化处理,使得每个特征的均值为 0 0 0,方差为 1 1 1。公式为:
X 标准化 = X − μ σ X_{\text{标准化}} = \frac{X - \mu}{\sigma} X标准化=σX−μ
其中, μ \mu μ 是特征的均值, σ \sigma σ 是特征的标准差。
协方差矩阵衡量了不同特征之间的线性关系。对于标准化后的数据 X ∈ R n × m X \in \mathbb{R}^{n \times m} X∈Rn×m,协方差矩阵 C C C 的计算公式是:
C = 1 n − 1 X T X C = \frac{1}{n-1} X^T X C=n−11XTX
其中, C ∈ R m × m C \in \mathbb{R}^{m \times m} C∈Rm×m。
对协方差矩阵 C C C 进行特征值分解,得到特征值和特征向量:
C v i = λ i v i C v_i = \lambda_i v_i Cvi=λivi
将特征值从大到小排序,并选择前 k k k 个特征值对应的特征向量,构成投影矩阵 W ∈ R m × k W \in \mathbb{R}^{m \times k} W∈Rm×k。
将原始数据 X X X 投影到低维空间:
X 降维 = X W X_{\text{降维}} = X W X降维=XW
其中, X 降维 ∈ R n × k X_{\text{降维}} \in \mathbb{R}^{n \times k} X降维∈Rn×k 是降维后的数据。
在深度学习中,PCA 通常用于数据预处理和特征降维,以下是一些典型的应用场景:
以下是使用 Python 和 Scikit-learn 实现 PCA 的示例代码:
import numpy as np
from sklearn.decomposition import PCA
from sklearn.datasets import load_iris
# 加载数据集
data = load_iris()
X = data.data
# 初始化 PCA,保留 2 个主成分
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X)
print("原始数据维度:", X.shape)
print("降维后数据维度:", X_pca.shape)
# 查看主成分的方差解释率
explained_variance_ratio = pca.explained_variance_ratio_
print("每个主成分的方差解释率:", explained_variance_ratio)
print("总方差解释率:", np.sum(explained_variance_ratio))
# 手动实现 PCA
# 数据标准化
X_mean = np.mean(X, axis=0)
X_std = X - X_mean
# 计算协方差矩阵
cov_matrix = np.cov(X_std, rowvar=False)
# 计算特征值和特征向量
eigenvalues, eigenvectors = np.linalg.eigh(cov_matrix)
# 按特征值排序
sorted_indices = np.argsort(eigenvalues)[::-1]
eigenvalues = eigenvalues[sorted_indices]
eigenvectors = eigenvectors[:, sorted_indices]
# 选择前 2 个主成分
W = eigenvectors[:, :2]
# 数据投影
X_pca_manual = np.dot(X_std, W)
print("手动实现的降维结果:", X_pca_manual[:5])
在深度学习中,归一化(Normalization)是一种重要的预处理和优化技术,目的是对数据或中间特征进行变换,使其满足某些特定的统计特性,从而加速训练过程、提高模型的性能和稳定性。归一化可以应用于输入数据或神经网络的中间层输出,主要目的是减少梯度消失或梯度爆炸问题,增强模型的收敛性。
以下是深度学习中常见的归一化操作及其详细介绍:
输入数据的归一化是深度学习中最基本的归一化操作,目的是将数据调整到某个固定范围或分布,便于模型处理。
将数据缩放到固定范围(通常是 [ 0 , 1 ] [0, 1] [0,1] 或 [ − 1 , 1 ] [-1, 1] [−1,1])。公式如下:
x ′ = x − min ( x ) max ( x ) − min ( x ) x' = \frac{x - \text{min}(x)}{\text{max}(x) - \text{min}(x)} x′=max(x)−min(x)x−min(x)
其中 x x x 是原始数据, min ( x ) \text{min}(x) min(x) 和 max ( x ) \text{max}(x) max(x) 分别是数据的最小值和最大值。
将数据调整为均值为 0 0 0,标准差为 1 1 1 的正态分布。公式如下:
x ′ = x − μ σ x' = \frac{x - \mu}{\sigma} x′=σx−μ
其中 μ \mu μ 是数据的均值, σ \sigma σ 是数据的标准差。
对具有长尾分布的数据,使用对数变换压缩数据范围:
x ′ = log ( x + 1 ) x' = \log(x + 1) x′=log(x+1)
在深度学习中,除了对输入数据进行归一化,还可以对神经网络的中间层输出进行归一化,以加速训练和提高模型的性能。
Batch Normalization 是最常用的归一化方法之一,主要用于对每一层的激活值进行归一化。其核心思想是:在每个小批量(batch)中,将每个神经元的输出调整为均值为 0 0 0,标准差为 1 1 1,然后再通过可学习的参数进行线性变换。
公式如下:
Layer Normalization 是一种不依赖 batch 的归一化方法,主要用于序列模型(如 RNN、Transformer)。它对每个样本的所有神经元的激活值进行归一化。
公式如下:
Instance Normalization 是一种专门用于图像生成任务(如风格迁移)的归一化方法。它对每个样本的每个通道单独归一化。
公式与 Layer Normalization 类似,但归一化范围是每个样本的每个通道。
Group Normalization 是一种结合了 Batch Normalization 和 Layer Normalization 的方法。它将特征划分为多个组,然后对每一组进行归一化。
公式类似于 Layer Normalization,但归一化范围是每个组。
Weight Normalization 是一种对网络权重进行归一化的方法,目的是使权重的范数固定,以便更稳定地训练。
公式如下:
w = g ∥ v ∥ v w = \frac{g}{\|v\|} v w=∥v∥gv
其中 g g g 是可学习的标量, v v v 是原始权重向量。
归一化方法的选择取决于任务的特性和模型的架构。通过实验和调参,可以找到最适合的归一化策略。