丹摩| 摩智云端深度解析:Faster R-CNN模型的训练与测试实战指南
@ 目录 文章前言1、云实例:配置选型与启动1.1 登录注册1.2 配置 SSH 密钥对1.3 创建实例1.4 登录云实例2、云存储:数据集上传与下载3、云开发:眼底血管分割案例3.1 案例背景3.2 网络搭建3.3 网络训练3.4 模型测试 文章前言 在计算机视觉的广阔领域中,图像分割技术扮演着举足轻重的角色,尤其在医学图像处理、卫星遥感分析以及众多需要精确分割的场景中,其重要性不言而喻。随着卷
@
目录
- 文章前言
- 1、云实例:配置选型与启动
- 1.1 登录注册
- 1.2 配置 SSH 密钥对
- 1.3 创建实例
- 1.4 登录云实例
- 2、云存储:数据集上传与下载
- 3、云开发:眼底血管分割案例
- 3.1 案例背景
- 3.2 网络搭建
- 3.3 网络训练
- 3.4 模型测试
文章前言
在计算机视觉的广阔领域中,图像分割技术扮演着举足轻重的角色,尤其在医学图像处理、卫星遥感分析以及众多需要精确分割的场景中,其重要性不言而喻。随着卷积神经网络(CNN)技术的飞速发展,图像分割的边界被不断拓宽,而UNet模型,作为专为图像分割任务而生的网络架构,自其诞生以来,便凭借其独特的U型结构、巧妙的跳跃连接以及卓越的性能,在学术界与工业界引发了广泛关注与热烈讨论。本文旨在全面而深入地探讨UNet模型的训练与测试过程,从理论根基到实践应用,为读者呈现一份详尽无遗的指南,助力读者在图像分割的征途中破浪前行。
UNet的设计灵感源自全卷积网络(FCN),但它在继承FCN精髓的同时,更通过引入对称的编码器-解码器结构以及创新的跨层连接,巧妙地解决了图像分割过程中常见的上下文信息丢失与细节恢复难题。这种匠心独运的设计,使得UNet在处理小样本数据集时亦能展现出惊人的泛化能力与分割精度,尤其在医学图像分割领域,如细胞、器官等精细结构的识别与分割中,UNet更是大放异彩,成为了众多研究者与工程师的首选工具。
本文的前半部分,我们将对UNet模型的发展历程进行简要回顾,深入剖析其核心设计理念,并展示其在医学图像处理、卫星遥感分析等众多领域中的成功应用案例,以此为后续深入讨论其训练与测试过程奠定坚实基础。通过本文,读者将能够全面了解到如何精心准备数据集、设计高效的损失函数、制定优化的训练策略以及准确评估模型性能等关键环节,从而掌握将UNet模型灵活应用于实际项目中的核心技能。
在训练与测试过程中,我们难免会遇到各种挑战与难题。本文的后半部分,我们将分享一些在实战中可能遇到的典型问题及其解决方案,如数据集不平衡、过拟合与欠拟合的防范、模型优化策略的调整等,帮助读者更好地理解和应用这一强大的图像分割工具。
让我们携手踏上这段充满探索与发现的旅程,共同揭开UNet模型训练与测试的神秘面纱,深入领略其在图像分割领域的无限魅力与广阔前景。通过本文的指引,相信读者定能在图像分割的征途中,披荆斩棘,勇往直前,开创属于自己的辉煌篇章。
1、云实例:配置选型与启动
1.1 登录注册
首先进入登录界面注册并登录账号
1.2 配置 SSH 密钥对
配置 SSH 密钥对的作用是后续远程登录服务器不需要密码验证,更加方便。
首先创建本地公钥,进入本地.ssh目录输入ssh-keygen -o命令,这里文件名可以设置为id_dsa,也可以是其他任意名字
之后我们可以在.ssh目录看到刚刚创建的两个文件
id_dsa
id_dsa.pub
其中id_dsa.pub就是需要的公钥文件
进入密钥对配置,创建密钥对,将id_dsa.pub的内容复制到这里就可以
1.3 创建实例
进入GPU 云实例,点击创建实例。如下图所示,按需选择需要的 GPU 型号和镜像
1.4 登录云实例
等待实例创建完成后,点击复制“访问链接”。
接着来到任意一个 SSH 连接终端进行云实例登录,我这里选择的是 VSCode,如下所示
成功后,输入:
nvidia-smi
torch.cuda.is_available()
简单验证一下功能即可,如下所示即为成功
2、云存储:数据集上传与下载
文件存储为网络共享存储,可挂载至的不同实例中。相比本地数据盘,其优势是实例间共享,可以多点读写,不受实例释放的影响;此外存储后端有多冗余副本,数据可靠性非常高;但缺陷是 IO 性能一般
考虑到以上优劣,推荐使用方式:将重要数据或代码存放于文件存储中,所有实例共享,便利的同时数据可靠性也有保障;在训练时,需要高 IO 性能的数据(如训练数据),先拷贝到实例本地数据盘,从本地盘读数据获得更好的 IO 性能。如此兼顾便利、安全和性能。
接下来,我们将训练数据上传到云实例数据盘中。使用scp工具如下
scp -rP 35740 ./DRIVE-SEG-DATA root@cn-north-b.ssh.damodel.com:/root/workspace
具体地:
35740与cn-north-b.ssh.damodel.com分别为端口号和远程地址,请参考 1.4 节替换为自己的参数
./DRIVE-SEG-DATA是本地数据集路径
/root/workspace是远程实例数据集路径
数据的下载也是类似的命令
scp -rP 35740 root@cn-north-b.ssh.damodel.com:/root/workspace ./DRIVE-SEG-DATA
本文提到的数据集可以在DRIVE 数据集中下载:链接:https://drive.grand-challenge.org/Download/
3、云开发:眼底血管分割案例
3.1 案例背景
眼底也称为眼球的内膜,包括黄斑、视网膜和视网膜中央动静脉等结构。在临床医学中,眼底图像是眼科医生对眼疾病患者进行诊断的重要依据。随着深度学习的发展,医学影像分割技术产生了深远的变化,尤其是卷积神经网络 AlexNet、VGGNet、GoogLeNet、ResNet 等,能够学习到更加抽象和高级的特征表示,从而实现更加精确的分割结果。深度学习模型在大规模数据上训练后,通常能够获得更好的泛化能力,即对未见过的数据也能做出相对准确的预测。对于医学影像分割来说,这意味着模型可以更好地适应不同类型和来源的医学图像数据,提高了分割结果的可靠性和稳定性。同时,深度学习技术支持端到端的学习方式,即从原始输入数据直接学习到最终的分割结果,无需手工设计复杂的特征提取和预处理流程。这简化了分割算法的开发流程,提高了效率和准确性。此外,医学影像数据常常包含多种模态,如 CT、MRI 等。深度学习技术能够更好地处理多模态数据,实现不同模态之间的信息融合,从而提高了医学影像分割的准确性和全面性
本次实践,我们采用 UNet 进行眼底血管医学图像分割任务。UNet 是一种被广泛应用于语义分割任务的网络结构,其编码器-解码器结构以及跳跃连接的设计,使其能够有效地捕获图像中不同尺度的特征信息,从而在眼底血管分割任务中取得较好的效果。同时,在推理阶段,UNet 采用全卷积网络结构,能够快速对新的眼底图像进行血管分割,为临床应用提供了实时性支持。
3.2 网络搭建
选用 U-Net 网络结构作为基础分割模型的原因在于其通过编解码器架构,有效地结合局部信息和全局信息,提高分割准确性;同时,U-Net 的跳跃连接结构有助于保留和恢复图像中的细节和边缘信息,且在小样本情况下表现优异,能够充分利用有限数据进行有效训练,广泛应用于医学图像分割任务中。网络架构如下
class UNet(nn.Module):
def __init__(self, n_channels, n_classes, bilinear=True):
super(UNet, self).__init__()
self.n_channels = n_channels
self.n_classes = n_classes
self.bilinear = bilinear
self.inc = DoubleConv(n_channels, 64)
self.down1 = Down(64, 128)
self.down2 = Down(128, 256)
self.down3 = Down(256, 512)
self.down4 = Down(512, 512)
self.up1 = Up(1024, 256, bilinear)
self.up2 = Up(512, 128, bilinear)
self.up3 = Up(256, 64, bilinear)
self.up4 = Up(128, 64, bilinear)
self.outc = OutConv(64, n_classes)
def forward(self, x):
x1 = self.inc(x)
x2 = self.down1(x1)
x3 = self.down2(x2)
x4 = self.down3(x3)
x5 = self.down4(x4)
x = self.up1(x5, x4)
x = self.up2(x, x3)
x = self.up3(x, x2)
x = self.up4(x, x1)
logits = self.outc(x)
return logits
3.3 网络训练
基于 PyTorch 的神经网络训练流程可以分为以下步骤(不考虑前期数据准备和模型结构):
定义损失函数 根据任务类型选择合适的损失函数(loss function),如分类任务常用的交叉熵损失(Cross-Entropy Loss)或回归任务中的均方误差(Mean Square Error)。
选择优化器 选择合适的优化器(optimizer),如随机梯度下降(SGD)、Adam 或 RMSprop,并设置初始学习率及其它优化参数。
训练模型 在训练过程中,通过迭代训练数据集来调整模型参数。每个迭代周期称为一个 epoch。对于每个 epoch,数据会被分成多个 batch,每个 batch 被输入到模型中进行前向传播、计算损失、反向传播更新梯度,并最终优化模型参数。
保存模型 当满足需求时,可以将训练好的模型保存下来,以便后续部署和使用。
根据这个步骤编写以下代码
def train_net(net, device, data_path, epochs=40, batch_size=1, lr=0.00001):
dataset = Dateset_Loader(data_path)
per_epoch_num = len(dataset) / batch_size
train_loader = torch.utils.data.DataLoader(dataset=dataset,
batch_size=batch_size,
shuffle=True)
optimizer = optim.Adam(net.parameters(),lr=lr,betas=(0.9, 0.999),eps=1e-08, weight_decay=1e-08,amsgrad=False)
criterion = nn.BCEWithLogitsLoss()
best_loss = float('inf')
loss_record = []
with tqdm(total=epochs*per_epoch_num) as pbar:
for epoch in range(epochs):
net.train()
for image, label in train_loader:
optimizer.zero_grad()
image = image.to(device=device, dtype=torch.float32)
label = label.to(device=device, dtype=torch.float32)
pred = net(image)
loss = criterion(pred, label)
pbar.set_description("Processing Epoch: {} Loss: {}".format(epoch+1, loss))
if loss < best_loss:
best_loss = loss
torch.save(net.state_dict(), 'best_model.pth')
loss.backward()
optimizer.step()
pbar.update(1)
loss_record.append(loss.item())
plt.figure()
plt.plot([i+1 for i in range(0, len(loss_record))], loss_record)
plt.title('Training Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.savefig('/root/shared-storage/results/training_loss.png')
运行这个脚本,可以在终端看到进度
训练损失函数如下,可以看到已经收敛
3.4 模型测试
测试逻辑如下所示,主要是计算 IoU 指标
def cal_miou(test_dir="/root/workspace/DRIVE-SEG-DATA/Test_Images",
pred_dir="/root/workspace/DRIVE-SEG-DATA/results", gt_dir="/root/workspace/DRIVE-SEG-DATA/Test_Labels",
model_path='best_model_drive.pth'):
name_classes = ["background", "vein"]
num_classes = len(name_classes)
if not os.path.exists(pred_dir):
os.makedirs(pred_dir)
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
net = UNet(n_channels=1, n_classes=1)
net.to(device=device)
net.load_state_dict(torch.load(model_path, map_location=device))
net.eval()
img_names = os.listdir(test_dir)
image_ids = [image_name.split(".")[0] for image_name in img_names]
time.sleep(1)
for image_id in tqdm(image_ids):
image_path = os.path.join(test_dir, image_id + ".png")
img = cv2.imread(image_path)
origin_shape = img.shape
img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
img = cv2.resize(img, (512, 512))
img = img.reshape(1, 1, img.shape[0], img.shape[1])
img_tensor = torch.from_numpy(img)
img_tensor = img_tensor.to(device=device, dtype=torch.float32)
pred = net(img_tensor)
pred = np.array(pred.data.cpu()[0])[0]
pred[pred >= 0.5] = 255
pred[pred < 0.5] = 0
pred = cv2.resize(pred, (origin_shape[1], origin_shape[0]), interpolation=cv2.INTER_NEAREST)
cv2.imwrite(os.path.join(pred_dir, image_id + ".png"), pred)
hist, IoUs, PA_Recall, Precision = compute_mIoU_gray(gt_dir, pred_dir, image_ids, num_classes, name_classes)
miou_out_path = "/root/shared-storage/results/"
show_results(miou_out_path, hist, IoUs, PA_Recall, Precision, name_classes)
模型保存的时候保存到共享存储路径/root/shared-storage,其他实例可以直接从共享存储中获取训练后的模型
更多推荐
所有评论(0)