人工智能基础实践教程 习题及答案 刘伟 第7-9章_第1页
人工智能基础实践教程 习题及答案 刘伟 第7-9章_第2页
人工智能基础实践教程 习题及答案 刘伟 第7-9章_第3页
人工智能基础实践教程 习题及答案 刘伟 第7-9章_第4页
人工智能基础实践教程 习题及答案 刘伟 第7-9章_第5页
已阅读5页,还剩46页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第7章习题7.1什么是激活函数?为什么神经网络必须使用非线性激活函数?答:激活函数是神经网络中引入非线性能力的关键组件,它决定神经元是否被激活以及激活强度。若不使用非线性激活函数,则无论网络有多少层,其整体仍等价于一个线性变换,无法拟合复杂模式(如异或问题)。例如,若所有层仅使用线性激活函数,则多层网络可被合并为单层线性模型,丧失表达能力。常见的非线性激活函数包括‌ReLU、‌Sigmoid‌和‌Tanh‌,它们使网络具备逼近任意复杂函数的能力,从而实现对图像、语音、文本等非线性数据的有效建模。7.2请解释反向传播算法的工作原理,并说明其在训练神经网络中的作用。答:反向传播是一种基于链式法则的梯度计算算法,用于高效计算损失函数相对于网络中每个权值的梯度。其工作流程分为两步:第一步是‌前向传播,输入数据通过网络逐层计算,得到输出与预测值;第二步是‌反向传播,从输出层开始,逐层计算损失对各层权值的偏导数,并将误差信号反向传递至前层。最终,利用梯度下降法更新权值,使损失函数最小化。反向传播是训练深度神经网络的基础,它使得在数百万参数的网络中进行端到端优化成为可能。7.3为什么单层感知机无法解决异或(XOR)问题?如何用多层神经网络解决?答:单层感知机只能学习线性可分的决策边界,而XOR问题是典型的非线性可分问题(输入(0,0)和(1,1)时输出0,输入(0,1)和(1,0)时输出1),其数据点无法被一条直线正确分类。解决方法是引入至少一个隐藏层,构建‌三层前馈神经网络。一种典型结果为“”输入层(2个神经元)→隐藏层(2个神经元,使用ReLU激活)→输出层(1个神经元)。隐藏层可以学习两个线性边界(如x₁=x₂与x₁≠x₂的组合),从而在输出层实现非线性分类。7.4什么是梯度消失问题?它在深层神经网络中为何发生?如何缓解?答:梯度消失问题是指在深层神经网络的误差反向传播中,靠近输入层的梯度趋近于0,导致这些层的权值几乎无法更新。发生原因为:使用饱和激活函数(如Sigmoid、Tanh)时,其导数最大值≤0.25。多次连乘后,梯度指数级衰减,层数越深,梯度越趋近于0。缓解方法包括:(1)使用ReLU‌及其变体作为激活函数,避免导数趋近于零;(2)采用权值初始化策略,如He初始化、Xavier初始化;(3)引入残差连接‌(ResNet)或‌批量归一化‌(BatchNormalization)。这些技术显著提升了深层网络的训练稳定性与收敛速度。7.5在神经网络训练中,如何判断模型出现过拟合?有哪些有效策略可以防止过拟合?答:过拟合表现为训练损失持续下降,但验证损失在某一阶段后开始上升,模型在训练集上表现优异而在测试集上泛化能力差。有效防止过拟合的策略包括:策略作用机制增加训练数据‌提供更多样本,降低模型对噪声的依赖‌正则化(L1/L2)‌在损失函数中加入权重惩罚项,抑制参数过大‌Dropout‌训练时随机“关闭”部分神经元,强制网络冗余学习‌提前终止‌监控验证损失,当不再改善时提前终止训练‌数据增强‌对图像/文本进行旋转、裁剪、噪声添加等变换,扩充数据多样性7.6手动构建一个单神经元感知机,训练其学习OR逻辑门的输入输出关系(输入为[[0,0],[0,1],[1,0],[1,1]],标签为[0,1,1,1]),并可视化训练过程。答:实现代码如下。importtorchimporttorch.nnasnnimportmatplotlib.pyplotasplt#数据准备X=torch.tensor([[0,0],[0,1],[1,0],[1,1]],dtype=torch.float32)y=torch.tensor([0,1,1,1],dtype=torch.float32).reshape(-1,1)#定义感知机模型classPerceptron(nn.Module):def__init__(self):super().__init__()self.linear=nn.Linear(2,1)#输入2维,输出1维self.sigmoid=nn.Sigmoid()#二分类激活函数defforward(self,x):returnself.sigmoid(self.linear(x))#训练配置model=Perceptron()criterion=nn.BCELoss()#二分类交叉熵损失optimizer=torch.optim.SGD(model.parameters(),lr=0.1)#训练循环losses=[]forepochinrange(1000):outputs=model(X)loss=criterion(outputs,y)optimizer.zero_grad()loss.backward()optimizer.step()losses.append(loss.item())#可视化损失plt.plot(losses)plt.xlabel('Epoch')plt.ylabel('Loss')plt.title('TrainingLossCurve')plt.show()#测试模型test_inputs=torch.tensor([[0,0],[1,1]],dtype=torch.float32)

withtorch.no_grad():

predictions=(model(test_inputs).numpy()>0.5).astype(int)print("Predictions:",predictions.flatten())

#应输出[0,1]训练过程的误差变化曲线如下。答案输出:Predictions:[01]7.7构建一个3层BP网络(输入784→256→128→10),在MNIST数据集上训练,要求准确率≥97%。答:实现代码如下。importtorchimporttorch.nnasnnimporttorchvisionimporttorchvision.transformsastransforms#数据加载与预处理transform=transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.1307,),(0.3081,))#MNIST均值标准差])train_set=torchvision.datasets.MNIST(root='./data',train=True,download=True,transform=transform)train_loader=torch.utils.data.DataLoader(train_set,batch_size=64,shuffle=True)#定义MLP模型classMLP(nn.Module):def__init__(self):super().__init__()self.fc1=nn.Linear(784,256)self.fc2=nn.Linear(256,128)self.fc3=nn.Linear(128,10)self.relu=nn.ReLU()defforward(self,x):x=x.view(-1,784)#展平图像x=self.relu(self.fc1(x))x=self.relu(self.fc2(x))x=self.fc3(x)#输出层不加激活函数(配合CrossEntropyLoss)returnx#训练配置model=MLP()criterion=nn.CrossEntropyLoss()optimizer=torch.optim.Adam(model.parameters(),lr=0.001)#训练循环forepochinrange(10):forimages,labelsintrain_loader:outputs=model(images)loss=criterion(outputs,labels)optimizer.zero_grad()loss.backward()optimizer.step()print(f'Epoch{epoch+1},Loss:{loss.item():.4f}')答案输出:Epoch1,Loss:0.1292Epoch2,Loss:0.0030Epoch3,Loss:0.0374Epoch4,Loss:0.0231Epoch5,Loss:0.0144Epoch6,Loss:0.1264Epoch7,Loss:0.0560Epoch8,Loss:0.0000Epoch9,Loss:0.0044Epoch10,Loss:0.08277.8结合卷积层和全连接层,在CIFAR-10上训练一个混合模型,要求分类准确率≥70%。答:示例代码如下。importtorchimporttorch.nnasnnimporttorchvisionimporttorchvision.transformsastransforms#数据加载transform=transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))])train_set=torchvision.datasets.CIFAR10(root='./data',train=True,download=True,transform=transform)train_loader=torch.utils.data.DataLoader(train_set,batch_size=32,shuffle=True)#定义混合模型(CNN+MLP)classHybridModel(nn.Module):def__init__(self):super().__init__()#卷积部分self.conv1=nn.Conv2d(3,16,kernel_size=3,stride=1,padding=1)self.conv2=nn.Conv2d(16,32,kernel_size=3,stride=1,padding=1)self.pool=nn.MaxPool2d(2,2)#全连接部分self.fc1=nn.Linear(32*8*8,256)#CIFAR-10图像经两次池化后为8x8self.fc2=nn.Linear(256,10)self.relu=nn.ReLU()defforward(self,x):x=self.pool(self.relu(self.conv1(x)))x=self.pool(self.relu(self.conv2(x)))x=x.view(-1,32*8*8)#展平x=self.relu(self.fc1(x))x=self.fc2(x)returnx#训练配置(同MLP题,需调整epoch数)model=HybridModel()criterion=nn.CrossEntropyLoss()optimizer=torch.optim.Adam(model.parameters(),lr=0.001)#训练forepochinrange(10):

forimages,labelsintrain_loader:

outputs=model(images)

loss=criterion(outputs,labels)

optimizer.zero_grad()

loss.backward()

optimizer.step()

print(f'Epoch{epoch+1},Loss:{loss.item():.4f}')#到官网直接下载数据集会快很多/~kriz/cifar-10-python.tar.gz答案输出:Usingdownloadedandverifiedfile:./data\cifar-10-python.tar.gzExtracting./data\cifar-10-python.tar.gzto./dataEpoch1,Loss:1.1184Epoch2,Loss:0.9263Epoch3,Loss:0.8009Epoch4,Loss:1.0510Epoch5,Loss:1.1100Epoch6,Loss:0.3185Epoch7,Loss:0.4513Epoch8,Loss:0.7117Epoch9,Loss:0.0969Epoch10,Loss:0.58827.9加载预训练ResNet18模型,替换最后的全连接层,在自定义数据集(如猫狗分类)上微调,要求准确率≥95%。答:示例代码如下。importtorchimporttorch.nnasnnimporttorchvisionfromtorchvisionimportmodels,transforms#自定义数据集(需替换为实际路径)#假设数据目录结构:data/train/cat/,data/train/dog/data_transforms=transforms.Compose([transforms.Resize(256),transforms.CenterCrop(224),transforms.ToTensor(),transforms.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225])#ImageNet均值标准差])train_set=torchvision.datasets.ImageFolder(root='./data/train',transform=data_transforms)train_loader=torch.utils.data.DataLoader(train_set,batch_size=32,shuffle=True)#加载预训练模型并修改最后一层model=models.resnet18(pretrained=True)num_ftrs=model.fc.in_featuresmodel.fc=nn.Linear(num_ftrs,2)#猫狗二分类#冻结卷积层参数(可选)#冻结卷积层参数(可选)forparaminmodel.parameters():

param.requires_grad=False#替换全连接层(新层可训练)num_ftrs=model.fc.in_featuresmodel.fc=nn.Linear(num_ftrs,2)#训练配置criterion=nn.CrossEntropyLoss()optimizer=torch.optim.Adam(model.parameters(),lr=0.0001)

#只优化fc层(其他层无梯度)forepochinrange(10):

forimages,labelsintrain_loader:

outputs=model(images)

loss=criterion(outputs,labels)

optimizer.zero_grad()

loss.backward()

optimizer.step()

print(f'Epoch{epoch+1},Loss:{loss.item():.4f}')#需要建立数据目录结构:data/train/cat/,data/train/dog/下载猫狗图片7.10构建一个卷积自编码器,在MNIST上训练,输入为带噪声的图像,输出为去噪后的图像,要求PSNR≥20dB。其中,PSNR(峰值信噪比,PeakSignal-to-NoiseRatio)是衡量图像质量的客观指标,用于评估去噪后的图像与原始干净图像的相似度。答:示例代码如下。importtorchimporttorch.nnasnnimporttorchvisionimporttorchvision.transformsastransformsimportnumpyasnpfromskimage.metricsimportpeak_signal_noise_ratioaspsnr#添加噪声的函数defadd_noise(img,noise_factor=0.5):noisy_img=img+noise_factor*torch.randn_like(img)noisy_img=torch.clamp(noisy_img,0.,1.)#限制在[0,1]returnnoisy_img#数据加载transform=transforms.Compose([transforms.ToTensor()])train_set=torchvision.datasets.MNIST(root='./data',train=True,download=True,transform=transform)train_loader=torch.utils.data.DataLoader(train_set,batch_size=64,shuffle=True)#定义自编码器classAutoencoder(nn.Module):def__init__(self):super().__init__()#编码器self.encoder=nn.Sequential(nn.Conv2d(1,16,3,stride=2,padding=1),#28x28→14x14nn.ReLU(),nn.Conv2d(16,32,3,stride=2,padding=1),#14x14→7x7nn.ReLU())#解码器self.decoder=nn.Sequential(nn.ConvTranspose2d(32,16,3,stride=2,padding=1,output_padding=1),#7x7→14x14nn.ReLU(),nn.ConvTranspose2d(16,1,3,stride=2,padding=1,output_padding=1),#14x14→28x28nn.Sigmoid()#输出在[0,1])defforward(self,x):x=self.encoder(x)x=self.decoder(x)returnx#训练配置model=Autoencoder()criterion=nn.MSELoss()optimizer=torch.optim.Adam(model.parameters(),lr=0.001)#训练循环(带噪声输入)forepochinrange(10):forimages,_intrain_loader:noisy_images=add_noise(images)#添加噪声outputs=model(noisy_images)loss=criterion(outputs,images)#目标是无噪声图像optimizer.zero_grad()loss.backward()optimizer.step()print(f'Epoch{epoch+1},Loss:{loss.item():.4f}')答案输出:Epoch1,Loss:0.0160Epoch2,Loss:0.0138Epoch3,Loss:0.0118Epoch4,Loss:0.0127Epoch5,Loss:0.0129Epoch6,Loss:0.0127Epoch7,Loss:0.0113Epoch8,Loss:0.0123Epoch9,Loss:0.0132Epoch10,Loss:0.0138第8章习题8.1在深度神经网络训练中,梯度消失和梯度爆炸是常见问题。请解释其数学成因,并列举至少3种解决方案。答:成因是通过链式法则计算梯度时,深层网络的梯度是各层梯度的连乘积。若每层梯度绝对值小于1(如使用Sigmoid激活函数),则反向传播时梯度会指数级衰减,出现梯度消失;若大于1(如初始化权重过大),则梯度会指数级增长,出现梯度爆炸。解决方案包括:1)权重初始化:使用Xavier/Glorot初始化(均匀或正态分布,方差与输入维度相关)或He初始化(适配ReLU)。2)激活函数选择:用ReLU/LeakyReLU替代Sigmoid/Tanh,缓解梯度消失。3)梯度裁剪:设置阈值,当梯度超过阈值时按比例缩放。4)批归一化(BatchNormalization):规范化每层输入,稳定梯度传播。5)残差连接(ResNet):通过跳跃连接直接传递梯度,缓解深层网络梯度消失。8.2说明批归一化在深度学习中的核心作用,并写出其前向传播的数学公式。答: 批归一化的核心作用如下:1)加速训练收敛,通过规范化每层输入,减少内部协变量偏移(InternalCovariateShift)。2)缓解梯度消失/爆炸,稳定梯度传播。3)允许更高的学习率,因输入分布稳定,可增大学习率而不导致发散。4)轻微正则化效果,基于批次统计的随机性引入噪声。给定一个批次的数据X∈RB×D,B为批次大小,D1)计算批次均值:μ2)计算批次方差:σ3)规范化:X4)缩放与平移:Y=γX+β8.3解释注意力机制的数学本质,并写出缩放点积注意力的计算公式。答: 注意力机制的核心思想是:通过动态计算输入序列中各位置之间的相关性权重,分配不同注意力资源。其数学本质是加权求和,权重由输入通过线性变换后的相似度决定。 缩放点积注意力公式如下:给定查询矩阵Q∈RN×dkAttention其中,QKT计算查询与键的相似度(未归一化注意力权重);dk为缩放因子,防止点积结果过大导致softmax梯度消失;softmax8.4生成对抗网络的生成器和判别器通过博弈优化,请写出其损失函数的原始形式,并解释模式崩溃的成因与缓解方法。答: 生成对抗网络的损失函数为min其中,判别器(D)用于最大化真实样本与生成样本的区分能力;生成器(G)用于最小化生成样本被判别为假的概率。模式崩溃成因是:生成器发现某一类样本能轻松欺骗判别器后,会过度生成该类样本,忽略其他模式,如只生成数字“1”的图片而忽略其他数字。 缓解方法有:1)WassersteinGAN(WGAN):用Wasserstein距离替代JS散度,改善梯度稳定性。2)UnrolledGAN:在生成器更新时考虑判别器未来几步的反应,避免短视优化。3)多样性正则化:在生成器损失中添加惩罚项,鼓励生成多样样本(如特征匹配损失)。4)多生成器/判别器架构:如MultipleGeneratorsGAN,强制生成器覆盖不同模式。5)经验回放或记忆机制:存储生成过的样本,在训练中约束新样本不能过度偏离已生成过的模式,或显式鼓励覆盖未生成的模式。8.5预训练模型在下游任务微调时可能遭遇灾难性遗忘(CatastrophicForgetting)。请解释其含义,并列举至少2种微调策略以缓解该问题。答:灾难性遗忘是指微调时模型过度适应下游任务数据,丢失预训练阶段学到的通用知识,导致在原始任务(如语言理解)上性能下降。缓解策略包括:1)分层微调(Layer-wiseTuning):冻结底层参数(如BERT的嵌入层和前几层),仅微调高层任务相关层。底层提取通用特征,高层适配特定任务,减少知识丢失。2)弹性权重巩固(ElasticWeightConsolidation,EWC):在损失函数中添加正则化项,约束重要参数(对预训练任务敏感的参数)的变化幅度。数学形式为ℒnew=ℒ3)LoRA(Low-RankAdaptation):在预训练模型参数旁插入低秩矩阵,仅训练新增参数,保持原参数不变。显著减少可训练参数量,降低遗忘风险。8.6假设在使用长短时记忆网络对IMDB电影评论进行情感分类时,模型出现梯度爆炸问题,请实现梯度裁剪和余弦退火学习率调度策略,并考察使用后训练稳定性提升效果。答:示例代码如下。importtorchimporttorch.nnasnnfromtorch.utils.dataimportDataLoader,Datasetfromtorchtext.data.utilsimportget_tokenizerfromtorchtext.vocabimportbuild_vocab_from_iteratorimporttorch.optimasoptimfromtorch.optim.lr_schedulerimportCosineAnnealingLRimportmatplotlib.pyplotaspltimportnumpyasnpfromtqdmimporttqdmimportos#设置随机种子torch.manual_seed(42)np.random.seed(42)tokenizer=get_tokenizer('basic_english')#==================1.从手动数据集构建词汇表==================defyield_tokens_from_dir(data_dir,split='train'):

"""遍历数据目录,逐条文本生成tokens"""

split_dir=os.path.join(data_dir,split)

forlabel_namein['pos','neg']:

folder=os.path.join(split_dir,label_name)

forfnameinos.listdir(folder):

iffname.endswith('.txt'):

withopen(os.path.join(folder,fname),'r',encoding='utf-8')asf:

text=f.read()

yieldtokenizer(text)print("Buildingvocabularyfrommanualdataset...")data_dir='./data/aclImdb'

#请确保路径正确vocab=build_vocab_from_iterator(

yield_tokens_from_dir(data_dir,split='train'),

specials=['<unk>'])vocab.set_default_index(vocab['<unk>'])print(f"Vocabularysize:{len(vocab)}")#==================2.自定义Dataset==================classManualIMDBDataset(Dataset):

def__init__(self,data_dir,split='train',max_samples=None):

self.samples=[]

label_map={'pos':1,'neg':0}

split_dir=os.path.join(data_dir,split)

forlabel_namein['pos','neg']:

folder=os.path.join(split_dir,label_name)

files=[fforfinos.listdir(folder)iff.endswith('.txt')]

ifmax_samples:

#正负各取一半

half=max_samples//2

files=files[:half]

forfnameintqdm(files,desc=f"Loading{split}/{label_name}",leave=False):

withopen(os.path.join(folder,fname),'r',encoding='utf-8')asf:

text=f.read()

self.samples.append((text,label_map[label_name]))

ifmax_samplesandlen(self.samples)>max_samples:

self.samples=self.samples[:max_samples]

#打乱顺序,避免正负样本连续

importrandom

random.shuffle(self.samples)

print(f"Loaded{len(self.samples)}samplesfrom{split}set")

def__len__(self):

returnlen(self.samples)

def__getitem__(self,idx):

text,label=self.samples[idx]

token_ids=vocab(tokenizer(text))

returntorch.tensor(token_ids,dtype=torch.long),label#==================3.填充函数==================defcollate_batch(batch):

texts,labels=zip(*batch)

lengths=[len(t)fortintexts]

max_len=max(lengths)

padded_texts=torch.zeros(len(texts),max_len,dtype=torch.long)

fori,textinenumerate(texts):

padded_texts[i,:len(text)]=text

labels=torch.tensor(labels,dtype=torch.float)

returnpadded_texts,labels#==================4.LSTM模型==================classLSTMClassifier(nn.Module):

def__init__(self,vocab_size,embed_dim=100,hidden_dim=128,num_layers=2):

super(LSTMClassifier,self).__init__()

self.embedding=nn.Embedding(vocab_size,embed_dim)

self.lstm=nn.LSTM(embed_dim,hidden_dim,num_layers,batch_first=True,dropout=0.3)

self.fc=nn.Linear(hidden_dim,1)

defforward(self,x):

x=self.embedding(x)

_,(hidden,_)=self.lstm(x)

out=self.fc(hidden[-1])

returnout.squeeze()#==================5.训练函数==================deftrain_model(model,dataloader,epochs=5,lr=0.001,use_clipping=False,use_cosine=False):

device=torch.device('cuda'iftorch.cuda.is_available()else'cpu')

print(f"Usingdevice:{device}")

model=model.to(device)

criterion=nn.BCEWithLogitsLoss()

optimizer=optim.Adam(model.parameters(),lr=lr)

scheduler=CosineAnnealingLR(optimizer,T_max=epochs)ifuse_cosineelseNone

epoch_losses=[]

forepochinrange(epochs):

model.train()

total_loss=0.0

progress_bar=tqdm(dataloader,desc=f"Epoch{epoch+1}/{epochs}",leave=False)

fortexts,labelsinprogress_bar:

texts,labels=texts.to(device),labels.to(device)

optimizer.zero_grad()

outputs=model(texts)

loss=criterion(outputs,labels)

loss.backward()

ifuse_clipping:

torch.nn.utils.clip_grad_norm_(model.parameters(),max_norm=1.0)

optimizer.step()

total_loss+=loss.item()

progress_bar.set_postfix(loss=loss.item())

avg_loss=total_loss/len(dataloader)

epoch_losses.append(avg_loss)

ifscheduler:

scheduler.step()

print(f"Epoch{epoch+1},AvgLoss:{avg_loss:.4f}")

returnepoch_losses#==================6.主程序==================defmain():

#数据集路径(请根据实际情况修改)

data_dir='./data/aclImdb'

#确保该路径下存在train/和test/文件夹

max_train_samples=5000

#训练集使用前5000条(可改为None使用全部)

print("Creatingtrainingdataset...")

train_dataset=ManualIMDBDataset(data_dir,split='train',max_samples=max_train_samples)

batch_size=64

dataloader=DataLoader(train_dataset,batch_size=batch_size,shuffle=True,collate_fn=collate_batch)

vocab_size=len(vocab)

epochs=8

#配置A

print("\n===配置A:无梯度裁剪+固定学习率===")

model_a=LSTMClassifier(vocab_size)

losses_a=train_model(model_a,dataloader,epochs=epochs,lr=0.003,

use_clipping=False,use_cosine=False)

#配置B

print("\n===配置B:梯度裁剪+余弦退火学习率===")

model_b=LSTMClassifier(vocab_size)

losses_b=train_model(model_b,dataloader,epochs=epochs,lr=0.003,

use_clipping=True,use_cosine=True)

#绘图对比

plt.figure(figsize=(10,6))

plt.plot(range(1,epochs+1),losses_a,marker='o',label='NoClipping+FixedLR')

plt.plot(range(1,epochs+1),losses_b,marker='s',label='GradientClipping+CosineAnnealing')

plt.xlabel('Epoch')

plt.ylabel('AverageLoss')

plt.title('TrainingStabilityComparison')

plt.legend()

plt.grid(True)

plt.show()

print("\n===最终损失对比===")

print(f"配置A:最终损失={losses_a[-1]:.4f},最小损失={min(losses_a):.4f}")

print(f"配置B:最终损失={losses_b[-1]:.4f},最小损失={min(losses_b):.4f}")if__name__=="__main__":

main()#推荐从/~amaas/data/sentiment/提前下载好代码所需的数据集aclImdb_v1.tar.gz输出:Buildingvocabularyfrommanualdataset...Vocabularysize:100683Creatingtrainingdataset...Loaded5000samplesfromtrainset===配置A:无梯度裁剪+固定学习率===Usingdevice:cpuEpoch1,AvgLoss:0.6946Epoch2,AvgLoss:0.6933Epoch3,AvgLoss:0.6936Epoch4,AvgLoss:0.6936Epoch5,AvgLoss:0.6935Epoch6,AvgLoss:0.6932Epoch7,AvgLoss:0.6932Epoch8,AvgLoss:0.6930===配置B:梯度裁剪+余弦退火学习率===Usingdevice:cpuEpoch1,AvgLoss:0.6943Epoch2,AvgLoss:0.6935Epoch3,AvgLoss:0.6934Epoch4,AvgLoss:0.6920Epoch5,AvgLoss:0.6908Epoch6,AvgLoss:0.6883Epoch7,AvgLoss:0.6878Epoch8,AvgLoss:0.6862===最终损失对比===配置A:最终损失=0.6930,最小损失=0.6930配置B:最终损失=0.6862,最小损失=0.68628.7在医学图像分类任务中,正样本(病变)仅占5%,存在严重类别不平衡。请实现FocalLoss,并在模拟的不平衡二分类数据集上训练ResNet-18,对比其相比标准交叉熵损失的改善效果。答:示例代码如下。importtorchimporttorch.nnasnnimporttorch.optimasoptimimporttorch.nn.functionalasFfromtorch.utils.dataimportDataLoader,TensorDatasetfromtorchvision.modelsimportresnet18importnumpyasnpfromsklearn.metricsimportaccuracy_score,precision_score,recall_score,f1_score,roc_auc_scoreimportmatplotlib.pyplotaspltfromcopyimportdeepcopy#-----------------------------1.FocalLoss)-----------------------------classFocalLoss(nn.Module):

def__init__(self,alpha=0.75,gamma=2,reduction='mean'):

super(FocalLoss,self).__init__()

self.alpha=alpha

self.gamma=gamma

self.reduction=reduction

defforward(self,inputs,targets):

BCE_loss=F.binary_cross_entropy_with_logits(inputs,targets,reduction='none')

pt=torch.exp(-BCE_loss)

F_loss=self.alpha*(1-pt)**self.gamma*BCE_loss

ifself.reduction=='mean':

returnF_loss.mean()

elifself.reduction=='sum':

returnF_loss.sum()

else:

returnF_loss#--------------------2.生成模拟医学图像数据(拆分为train/val)-------------------------np.random.seed(42)torch.manual_seed(42)N=2000

#总样本数pos_ratio=0.05

#正样本比例n_pos=int(N*pos_ratio)

#100n_neg=N-n_pos

#1900#随机生成3x224x224的图像数据(实际应为真实医学图像,这里仅作演示)X=torch.randn(N,3,224,224)y=torch.zeros(N,1,dtype=torch.float)y[n_neg:]=1.0

#最后n_pos个为正样本#打乱顺序(保证正负样本随机分布)indices=np.random.permutation(N)X,y=X[indices],y[indices]#划分训练集(80%)和验证集(20%)split=int(0.8*N)X_train,y_train=X[:split],y[:split]X_val,y_val=X[split:],y[split:]#创建DataLoaderbatch_size=64train_dataset=TensorDataset(X_train,y_train)val_dataset=TensorDataset(X_val,y_val)train_loader=DataLoader(train_dataset,batch_size=batch_size,shuffle=True)val_loader=DataLoader(val_dataset,batch_size=batch_size,shuffle=False)#--------------------------3.真正的ResNet-18(修改输出层为二分类)--------------------------defget_resnet18_model():

model=resnet18(pretrained=False)

#医学图像通常随机初始化即可

model.fc=nn.Linear(model.fc.in_features,1)

#二分类输出

returnmodel#--------------------4.训练与评估函数(支持FocalLoss/CrossEntropy)------------------------deftrain_and_evaluate(model,criterion,optimizer,train_loader,val_loader,epochs=20,device='cuda'):

model=model.to(device)

train_losses,val_losses=[],[]

val_metrics={'accuracy':[],'precision':[],'recall':[],'f1':[],'auc':[]}

forepochinrange(epochs):

#训练阶段

model.train()

total_loss=0.0

forX_batch,y_batchintrain_loader:

X_batch,y_batch=X_batch.to(device),y_batch.to(device)

optimizer.zero_grad()

outputs=model(X_batch)

loss=criterion(outputs,y_batch)

loss.backward()

optimizer.step()

total_loss+=loss.item()

avg_train_loss=total_loss/len(train_loader)

train_losses.append(avg_train_loss)

#验证阶段

model.eval()

all_preds=[]

all_labels=[]

all_probs=[]

val_loss=0.0

withtorch.no_grad():

forX_batch,y_batchinval_loader:

X_batch,y_batch=X_batch.to(device),y_batch.to(device)

outputs=model(X_batch)

loss=criterion(outputs,y_batch)

val_loss+=loss.item()

probs=torch.sigmoid(outputs)

preds=(probs>=0.5).float()

all_preds.extend(preds.cpu().numpy())

all_labels.extend(y_batch.cpu().numpy())

all_probs.extend(probs.cpu().numpy())

avg_val_loss=val_loss/len(val_loader)

val_losses.append(avg_val_loss)

#计算指标(处理可能无正样本的情况)

accuracy=accuracy_score(all_labels,all_preds)

precision=precision_score(all_labels,all_preds,zero_division=0)

recall=recall_score(all_labels,all_preds,zero_division=0)

f1=f1_score(all_labels,all_preds,zero_division=0)

auc=roc_auc_score(all_labels,all_probs)iflen(np.unique(all_labels))>1else0.5

val_metrics['accuracy'].append(accuracy)

val_metrics['precision'].append(precision)

val_metrics['recall'].append(recall)

val_metrics['f1'].append(f1)

val_metrics['auc'].append(auc)

print(f"Epoch{epoch+1:2d}|TrainLoss:{avg_train_loss:.4f}|ValLoss:{avg_val_loss:.4f}|"

f"Acc:{accuracy:.3f}|Recall:{recall:.3f}|F1:{f1:.3f}|AUC:{auc:.3f}")

returntrain_losses,val_losses,val_metrics#-----------------------------5.主程序:对比两种损失函数-----------------------------defmain():

device=torch.device('cuda'iftorch.cuda.is_available()else'cpu')

epochs=20

lr=0.001

#模型1:交叉熵损失

model_ce=get_resnet18_model()

criterion_ce=nn.BCEWithLogitsLoss()

optimizer_ce=optim.Adam(model_ce.parameters(),lr=lr)

print("==========TrainingwithCross-EntropyLoss==========")

train_loss_ce,val_loss_ce,metrics_ce=train_and_evaluate(

model_ce,criterion_ce,optimizer_ce,train_loader,val_loader,epochs,device

)

#模型2:FocalLoss(alpha可设为正样本比例,此处取0.95/0.05的倒数归一化,或简单用0.75)

model_fl=get_resnet18_model()

#alpha推荐设为pos_weight=n_neg/n_pos=1900/100=19,但FocalLoss中alpha是标量乘在损失上,通常取0.75~0.9

criterion_fl=FocalLoss(alpha=0.75,gamma=2)

optimizer_fl=optim.Adam(model_fl.parameters(),lr=lr)

print("\n==========TrainingwithFocalLoss==========")

train_loss_fl,val_loss_fl,metrics_fl=train_and_evaluate(

model_fl,criterion_fl,optimizer_fl,train_loader,val_loader,epochs,device

)

#-----------------------------6.结果可视化与对比-----------------------------

plt.figure(figsize=(14,5))

#损失曲线对比

plt.subplot(1,2,1)

plt.plot(range(1,epochs+1),train_loss_ce,label='CETrain',marker='o')

plt.plot(range(1,epochs+1),val_loss_ce,label='CEVal',marker='s')

plt.plot(range(1,epochs+1),train_loss_fl,label='FLTrain',marker='^')

plt.plot(range(1,epochs+1),val_loss_fl,label='FLVal',marker='d')

plt.xlabel('Epoch')

plt.ylabel('Loss')

plt.title('Training&ValidationLoss')

plt.legend()

plt.grid(True)

#F1分数对比(对不平衡任务最关键)

plt.subplot(1,2,2)

plt.plot(range(1,epochs+1),metrics_ce['f1'],label='CEF1',marker='o')

plt.plot(range(1,epochs+1),metrics_fl['f1'],label='FLF1',marker='s')

plt.xlabel('Epoch')

plt.ylabel('F1Score')

plt.title('F1ScoreonValidationSet')

plt.legend()

plt.grid(True)

plt.tight_layout()

plt.savefig('loss_f1_comparison.png')

plt.show()

#打印最终结果

print("\n==================FinalPerformance==================")

print(f"Cross-EntropyLoss:

ValLoss={val_loss_ce[-1]:.4f},F1={metrics_ce['f1'][-1]:.4f},AUC={metrics_ce['auc'][-1]:.4f}")

print(f"FocalLoss:

ValLoss={val_loss_fl[-1]:.4f},F1={metrics_fl['f1'][-1]:.4f},AUC={metrics_fl['auc'][-1]:.4f}")if__name__=="__main__":main()输出结果:==========TrainingwithFocalLoss==========Epoch1|TrainLoss:0.0579|ValLoss:0.0506|Acc:0.965|Recall:0.000|F1:0.000|AUC:0.474Epoch2|TrainLoss:0.0137|ValLoss:1.7863|Acc:0.035|Recall:1.000|F1:0.068|AUC:0.429Epoch3|TrainLoss:0.0038|ValL

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论