CPP-NET论文阅读

这篇具有很好参考价值的文章主要介绍了CPP-NET论文阅读。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Abstract

本文提出了一种新的细胞核分割方法,名为上下文感知多边形提议网络(Context-aware Polygon Proposal Network,CPP-Net)。该方法主要分为三部分:

  1. 点集采样:传统的距离预测方法通常只利用每个细胞核的质心像素,而忽略了周围环境的信息。因此,本文采用了点集采样的方式,即在每个细胞核内部随机采样一定数量的点作为输入,从而增强了上下文信息。

  2. 网络结构:本文提出的CPP-Net采用了简单的U-Net作为骨干网络,并在其之上添加了三个平行的一维卷积层用于预测距离。同时,为了更好地融合多个点的特征,本文还设计了一个置信度加权模块,对不同点的贡献进行加权平均。

  3. 优化策略:为了避免过拟合,本文引入了一种新颖的形状感知感知损失函数(Shape-Aware Perceptual Loss,SAP Loss),该损失函数能够约束预测出的多边形的形状与真实多边形相似。此外,本文还采用了数据增强等技术来增加训练样本的多样性,从而提高了模型的泛化能力。

实验结果表明,本文提出的方法在三个公开数据集上均取得了优异的表现,证明了其有效性和可靠性。

Related Work

相关工作的部分介绍了当前常用的几种细胞核分割方法,包括基于形状模型的方法、基于图割的方法、基于卷积神经网络的方法等。其中,基于形状模型的方法是最早被应用到细胞核分割中的方法之一,其主要思想是通过拟合形状模型来实现细胞核的分割。然而,这种方法需要预先定义形状模型,对于不同类型的细胞核难以适应。基于图割的方法则是通过对图像进行分割,然后通过优化能量函数来实现细胞核的分割。虽然这种方法能够较好地解决重叠细胞核的问题,但是计算复杂度较高,运行速度较慢。近年来,随着深度学习技术的发展,基于卷积神经网络的方法逐渐成为主流。这些方法通过训练神经网络来学习细胞核的特征,从而实现细胞核的分割。相比于传统方法,基于卷积神经网络的方法具有更高的准确性和鲁棒性。

Methods

overview

总体介绍了网络的架构,如下图:

分三步走,先对输入的图像利用以U-net为backbone的网络进行特征提取和输出,第二步利用本文提出的CEM来对特征进行处理,第三步进行后处理得到分割实例

CPP-NET论文阅读,深度学习,论文阅读,人工智能,python,深度学习

代码:

# 网络结构
import torch
import torch.nn as nn
import torch.nn.functional as F
​
# 导入 unet_parts_gn.py 中定义的网络层
from .unet_parts_gn import *
# 导入 SamplingFeatures2.py 中定义的类
from .SamplingFeatures2 import SamplingFeatures
​
# 定义 CPPNet 类,继承自 nn.Module
class CPPNet(nn.Module):
​
    # n_channels: 输入图片的通道数
    # n_rays: 光线的数量
    # erosion_factor_list: 腐蚀因子
    # return_conf: 是否返回置信度分布
    # with_seg: 是否进行语义分割
    # n_seg_cls: 语义分割的类别数
    def __init__(self, n_channels, n_rays, erosion_factor_list=[0.2, 0.4, 0.6, 0.8, 1.0], return_conf=False,
                 with_seg=True, n_seg_cls=1):
        super(CPPNet, self).__init__()
        # 定义 U-Net 的 encoder 部分
        self.inc = inconv(n_channels, 32)
        self.down1 = down(32, 64)
        self.down2 = down(64, 128)
        self.down3 = down(128, 128)
        # 定义 U-Net 的 decoder 部分
        self.up1 = up(256, 64, bilinear=True)
        self.up2 = up(128, 32, bilinear=True)
        self.up3 = up(64, 32, bilinear=True)
        # 定义特征提取层
        self.features = nn.Conv2d(32, 128, 3, padding=1)
        # 定义输出层
        self.out_prob = outconv(128, 1)  # outconv is 1*1 conv
        self.out_ray = outconv(128, n_rays) # n_rays个通道
        # 定义置信度分布的输出层
        self.conv_0_confidence = outconv(128, n_rays)
        self.conv_1_confidence = outconv(1 + len(erosion_factor_list), 1 + len(erosion_factor_list))
        nn.init.constant_(self.conv_1_confidence.conv.bias, 1.0)
​
        # 是否进行语义分割
        self.with_seg = with_seg
        self.n_seg_cls = n_seg_cls
        if self.with_seg:
            # 定义语义分割的 decoder 部分
            self.up1_seg = up(256, 64, bilinear=True)
            self.up2_seg = up(128, 32, bilinear=True)
            self.up3_seg = up(64, 32, bilinear=True)
            # 定义输出层,用于预测语义分割结果
            self.out_seg = outconv(32, n_seg_cls)
            if self.n_seg_cls == 1:
                self.final_activation_seg = nn.Sigmoid()
            else:
                self.final_activation_seg = nn.Softmax(dim=1)
​
        # 定义最终输出的激活函数
        self.final_activation_ray = nn.ReLU()
        self.final_activation_prob = nn.Sigmoid()
​
        # cem
        self.sampling_feature = SamplingFeatures(n_rays)
        self.erosion_factor_list = erosion_factor_list
        self.n_rays = n_rays
        self.return_conf = return_conf
​
    # 网络的前向传播
    # img: 输入图片
    # gt_dist: rays方向的真实值(仅在训练时用到)
    def forward(self, img, gt_dist=None):
        # step1:obtain initial D,C,P maps by u-net
        x1 = self.inc(img)
        x2 = self.down1(x1)
        x3 = self.down2(x2)
        x4 = self.down3(x3)
        x = self.up1(x4, x3)
        x = self.up2(x, x2)
        x = self.up3(x, x1)
        x = self.features(x)
        out_ray = self.out_ray(x)   # D
        out_confidence = self.conv_0_confidence(x) # c
        out_prob = self.out_prob(x) # P
​
        if gt_dist is not None:
            out_ray_for_sampling = gt_dist
        else:
            out_ray_for_sampling = out_ray
            
        ray_refined = [out_ray_for_sampling]
        # step2:CEM module
        confidence_refined = [out_confidence]
        for erosion_factor in self.erosion_factor_list:
            base_dist = (out_ray_for_sampling - 1.0) * erosion_factor
            # algorithm of sampling
            ray_sampled, _ = self.sampling_feature(out_ray_for_sampling, base_dist, 1)
            conf_sampled, _ = self.sampling_feature(out_confidence, base_dist, 1)
            
            ray_refined.append(ray_sampled + base_dist)
            confidence_refined.append(conf_sampled)
        ray_refined = torch.stack(ray_refined, dim=1)
        b, k, c, h, w = ray_refined.shape
​
        confidence_refined = torch.stack(confidence_refined, dim=1)
        # confidence_refined = torch.cat((confidence_refined, ray_refined), dim=1)
        confidence_refined = confidence_refined.permute([0, 2, 1, 3, 4]).contiguous().view(b * c, k, h, w)
        confidence_refined = self.conv_1_confidence(confidence_refined)
        confidence_refined = confidence_refined.view(b, c, k, h, w).permute([0, 2, 1, 3, 4])
        confidence_refined = F.softmax(confidence_refined, dim=1)
        if self.return_conf:
            out_conf = [out_confidence, confidence_refined]
        else:
            out_conf = None
        ray_refined = (ray_refined * confidence_refined).sum(dim=1)
​
        out_ray = self.final_activation_ray(out_ray)
        ray_refined = self.final_activation_ray(ray_refined)
        out_prob = self.final_activation_prob(out_prob)
​
        if self.with_seg:
            # step3:sap
            x_seg = self.up1_seg(x4, x3)
            x_seg = self.up2_seg(x_seg, x2)
            x_seg = self.up3_seg(x_seg, x1)
            out_seg = self.out_seg(x_seg)
            if self.n_seg_cls == 1:
                out_seg = self.final_activation_seg(out_seg)
            elif not self.training:
                out_seg = self.final_activation_seg(out_seg)
​
        else:
            out_seg = None
​
        return [out_ray, ray_refined], [out_prob], [out_seg, ], [out_conf, ]
​
    # 初始化网络的权重参数
    def init_weight(self):
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                nn.init.kaiming_normal_(m.weight, mode='fan_out')
                if hasattr(m, 'bias'):
                    nn.init.constant_(m.bias, 0)
            elif isinstance(m, nn.BatchNorm2d) or isinstance(m, nn.GroupNorm):
                nn.init.constant_(m.weight, 1.0)
                nn.init.constant_(m.bias, 0.0)
        nn.init.constant_(self.conv_1_confidence.conv.bias, 1.0)
​

context enhancement module

这一节的sampling算法使用特别关键

CPP-NET论文阅读,深度学习,论文阅读,人工智能,python,深度学习

Confidence-Based Weighting Module

对每一个通道的特征的置信度进行一个线性变换

CPP-NET论文阅读,深度学习,论文阅读,人工智能,python,深度学习

Loss Functions

公式:

CPP-NET论文阅读,深度学习,论文阅读,人工智能,python,深度学习

CPP-NET论文阅读,深度学习,论文阅读,人工智能,python,深度学习

代码:

import torch
import torch.nn.functional as F
​
# 定义了一个Dice Loss函数,用于计算预测结果和目标结果之间的相似度损失。
def dice_loss(pred, target, eps=1e-7):
    b = pred.shape[0]
    n_cls = pred.shape[1]
    loss = 0.0
    for ic in range(n_cls):
        ic_target = (target == ic).float().view(b, -1)
        ic_pred = pred[:, ic, :, :].view(b, -1)
        loss += (2*(ic_pred*ic_target).sum(dim=1)+eps) / (ic_pred.pow(2).sum(dim=1)+ic_target.pow(2).sum(dim=1)+eps)
    loss /= n_cls
    loss = 1.0 - loss.mean()
    return loss
​
class L1Loss_List_withSAP_withSeg(torch.nn.Module):
    def __init__(self, feature_extractor, scale=[1,1,1,1], cls_balance_mode=False):
        super(L1Loss_List_withSAP_withSeg, self).__init__()
        assert len(scale)==4
        self.scale = scale
        self.feature_extractor = feature_extractor
        if self.feature_extractor is not None:
            assert len(scale)==4
        else:
            assert len(scale)==3        
        self.cls_balance_mode = cls_balance_mode
        assert(self.cls_balance_mode in [True, False])
​
    def forward(self, prediction, target_dists, **kwargs):
​
        prob =  kwargs.get('labels', None)
        # 在前向传播过程中,首先从预测结果中提取出距离图(pred_dists)和概率图(pred_probs)。
        pred_dists = prediction[0]
        pred_probs = prediction[1]
​
        l1loss = 0.0 # the weighted L1 loss for pixel-to-boundary distance regression.
        bceloss = 0.0 # the binary cross entropy loss for centroid probability prediction
        segloss = 0.0 # the seg loss to measure the difference between gt and prediction
        # 权重L1损失通过计算每个距离图与目标距离的L1损失,并根据概率图进行加权求平均得到
        for i_dist in pred_dists:
            l1loss_map = F.l1_loss(i_dist, target_dists, reduction='none')
            l1loss += torch.mean(prob*l1loss_map)
        # 二进制交叉熵损失用于计算概率图与标签之间的损失。
        for i_prob in pred_probs:
            bceloss += F.binary_cross_entropy(i_prob, prob)
        # 分割损失根据分类平衡模式进行计算,将交叉熵损失和Dice Loss相结合。
        for i_seg in pred_segs:
            if self.cls_balance_mode:
                segloss_map = F.cross_entropy(i_seg, seg_target, reduction='none')
                cur_segloss = 0.0
                for ic in range(i_seg.shape[1]):
                    icmask = (seg_target==ic).float()
                    cur_segloss += ((icmask * segloss_map).sum()+1e-5) / (icmask.sum()+1e-5)
                segloss += cur_segloss / i_seg.shape[1]
                        + dice_loss(F.softmax(i_seg, dim=1), seg_target)
            else:
                segloss += F.cross_entropy(i_seg, seg_target) \
                        + dice_loss(F.softmax(i_seg, dim=1), seg_target)
        '''
        根据权重参数对三个损失进行加权求和得到总损失(loss)。然后,如果存在特征提取器,则计算特征相似度损失(sap_loss)并加入总损失中。最后,返回总损失和指标(metric)。
        '''
        loss = self.scale[0]*l1loss + self.scale[1]*bceloss
        metric = loss.data.clone().cpu()
        loss += self.scale[2]*segloss
​
        if self.feature_extractor is not None:
            self.feature_extractor.zero_grad()
            sap_loss = 0.0
            target_mask = (target_dists.max(dim=1, keepdim=True)[0]>0).float()
            f_target = self.feature_extractor(torch.cat((prob, target_dists), dim=1))
            for i_dist in pred_dists:
                f_pred = self.feature_extractor(torch.cat((pred_probs[-1], i_dist*target_mask), dim=1))
                sap_loss += F.l1_loss(f_pred, f_target)
            loss += self.scale[3]*sap_loss
        else:
            sap_loss = 0.0
​
        # print('loss: {:.5f}, metric: {:.5f}, l1: {:.5f}, bce: {:.5f}, seg: {:.5f}, sap: {:.5f}'\
        #     .format(loss, metric, l1loss, bceloss, segloss, sap_loss))
​
        return loss, metric
​
关于SAP

CPP-NET论文阅读,深度学习,论文阅读,人工智能,python,深度学习

SAP损失的目的是惩罚预测和真实核表示之间形状特征的差异。

本文主要考虑两种核表示策略:一是基于边界的方法中的语义分割和边界检测图;二是基于每个核的关联边界框的位置和大小。在转换模型的训练过程中,我们将每个训练图像的真值D gt和P gt c连接起来以创建输入。两种目标表示策略分别采用二值交叉熵损失和L1损失。

所以首先要创建的是一个输出上述两种表示形式的unet网络,然后利用分割后的数据集对该网络进行训练,训练结束后该网络的encoder能够抽象出细胞核的高精度形状特征,取该网络的encoder参数后在训练CPP-net时使用。

Post-Processing

看算法3

主要是用在了预测阶段。

Algorithms

Algorithm1 sampling

CPP-NET论文阅读,深度学习,论文阅读,人工智能,python,深度学习

# 采样
import torch
import torch.nn.functional as F
import torch.nn as nn
import math
#import time
​
# nd_sampling采样的维度,表示每个采样点采样的特征数量。
# 当 nd_sampling 大于 0 时,表示每个采样点采样的特征数量。当 nd_sampling 等于 0 时,表示每个采样点只采样一个特征。
def feature_sampling(feature_map, coord_map, nd_sampling, sampling_mode='nearest'):
    b, c, h, w = feature_map.shape # (batch_size, channels, height, width)
    # coord_map: b, k, 2, h, w
    # 'k' for k rays in each image
    _, k, _, h, w = coord_map.shape #(batch_size, k_rays, 2, height, width)
    x_ = torch.arange(w).view(1, -1).expand(h, -1) # params are (h,w)-1 represent all
    y_ = torch.arange(h).view(-1, 1).expand(-1, w)
    grid = torch.stack([x_, y_], dim=0).float()
    # grid: b, 1, 2, h, w
    grid = grid.unsqueeze(0).expand(b, -1, -1, -1, -1).cuda()
    # sampling_coord: b, k, 2, h, w
    # 将网格采样点坐标和预定义的偏移量相加,得到最终的采样点坐标。这里的grid代表网格采样点的坐标,coord_map代表预定义的偏移量。
    sampling_coord = grid + coord_map
    # 对采样点坐标进行归一化处理,将x和y方向的坐标值都除以特征图的宽度和高度减1,以将坐标值限制在[0,1]的范围内。
    sampling_coord[:, :, 0, :, :] = sampling_coord[:, :, 0, :, :]/(w-1)
    sampling_coord[:, :, 1, :, :] = sampling_coord[:, :, 1, :, :]/(h-1)
    # 对采样点坐标进行线性变换,将坐标值从[0,1]的范围映射到[-1,1]的范围内。具体来说,该代码将采样点坐标乘以2,再减去1,从而将原始的坐标值[0,1]线性变换为[-1,1]。这个操作可以使得采样点的坐标值符合神经网络的输入要求,并且能够增强模型的表达能力。
    sampling_coord = sampling_coord*2.0-1.0
​
    assert(k*nd_sampling==c)
​
    if nd_sampling > 0:
        sampling_coord = sampling_coord.permute(1, 0, 3, 4, 2).flatten(start_dim=0, end_dim=1) # k, b, h, w, 2
        sampling_features = F.grid_sample(feature_map.view(b, k, nd_sampling, h, w).permute(1, 0, 2, 3, 4).flatten(start_dim=0, end_dim=1), sampling_coord, mode=sampling_mode) # k,b, c', h, w
        sampling_features = sampling_features.view(k, b, nd_sampling, h, w).permute(1, 0, 2, 3, 4) # b, k, c', h, w
    else:
        sampling_coord = sampling_coord.permute(0, 1, 3, 4, 2).flatten(start_dim=1, end_dim=2) # b, kh, w, 2
        sampling_features = F.grid_sample(feature_map, sampling_coord, mode=sampling_mode)
        sampling_features = sampling_features.view(b, c, k, h, w).permute(0, 2, 1, 3, 4) # b, k, c'/c, h, w
​
    sampling_features = sampling_features.flatten(start_dim=1, end_dim=2) # b, k*c', h, w
​
    return sampling_features, sampling_coord
​
class SamplingFeatures(nn.Module):
    def __init__(self, n_rays, sampling_mode='nearest'):
        super(SamplingFeatures, self).__init__()
        self.n_rays = n_rays
        self.angles = torch.arange(n_rays).float()/float(n_rays)*math.pi*2.0 # 0 - 2*pi
        self.sin_angles = torch.sin(self.angles).cuda().view(1, n_rays, 1, 1)
        self.cos_angles = torch.cos(self.angles).cuda().view(1, n_rays, 1, 1)
        self.sampling_mode = sampling_mode
    def forward(self, feature_map, dist, nd_sampling):
        # feature_map: b, c, h, w
        # dist: b, k, h, w
        # sampled_features: b, k*c, h, w
        offset_ih = self.sin_angles * dist
        offset_iw = self.cos_angles * dist
        offsets = torch.stack([offset_iw, offset_ih], dim=2)
        sampled_features, sampling_coord = feature_sampling(feature_map, offsets, nd_sampling, self.sampling_mode)
        return sampled_features, sampling_coord
​
​

Alorithm2 CEM

CPP-NET论文阅读,深度学习,论文阅读,人工智能,python,深度学习

# CEM
out_ray = self.out_ray(x)   # D
        out_confidence = self.conv_0_confidence(x) # c
        out_prob = self.out_prob(x) # P
​
        if gt_dist is not None:
            out_ray_for_sampling = gt_dist
        else:
            out_ray_for_sampling = out_ray
            
        ray_refined = [out_ray_for_sampling]
        # step2:CEM module
        confidence_refined = [out_confidence]
        for erosion_factor in self.erosion_factor_list:
            base_dist = (out_ray_for_sampling - 1.0) * erosion_factor
            # use of sampling
            ray_sampled, _ = self.sampling_feature(out_ray_for_sampling, base_dist, 1)
            conf_sampled, _ = self.sampling_feature(out_confidence, base_dist, 1)
            
            ray_refined.append(ray_sampled + base_dist)
            confidence_refined.append(conf_sampled)
        ray_refined = torch.stack(ray_refined, dim=1)
        b, k, c, h, w = ray_refined.shape
​
        confidence_refined = torch.stack(confidence_refined, dim=1)
        # confidence_refined = torch.cat((confidence_refined, ray_refined), dim=1)
        confidence_refined = confidence_refined.permute([0, 2, 1, 3, 4]).contiguous().view(b * c, k, h, w)
        confidence_refined = self.conv_1_confidence(confidence_refined)
        confidence_refined = confidence_refined.view(b, c, k, h, w).permute([0, 2, 1, 3, 4])
        confidence_refined = F.softmax(confidence_refined, dim=1)
        if self.return_conf:
            out_conf = [out_confidence, confidence_refined]
        else:
            out_conf = None
        ray_refined = (ray_refined * confidence_refined).sum(dim=1)
​

Algorithm3 fpp

CPP-NET论文阅读,深度学习,论文阅读,人工智能,python,深度学习

    input = torch.tensor(img)
    input = input.unsqueeze(0).unsqueeze(0)
    preds = model_dist(input.cuda())
    dist_cuda = preds[0][-1][:, :, :h, :w]
    dist = dist_cuda.data.cpu()
    prob = preds[1][-1].data.cpu()[:, :, :h, :w]
    seg = preds[2][-1].data.cpu()[:, :, :h, :w]
​
    dist_numpy = dist.numpy().squeeze()
    prob_numpy = prob.numpy().squeeze()
    seg = seg.numpy().squeeze()
    prob_numpy = prob_numpy*seg# (seg>=seg_prob_thres).astype(np.float32)
    
    dist_numpy = np.transpose(dist_numpy,(1,2,0))
    coord = dist_to_coord(dist_numpy)
    points = non_maximum_suppression(coord, prob_numpy, prob_thresh=center_prob_thres)
    star_label = polygons_to_label(coord, prob_numpy, points)
# 细粒度处理
    if FPP and sin_angles is None:
        if dist_cmp == 'cuda':
            angles = torch.arange(n_rays).float()/float(n_rays)*math.pi*2.0 # 0 - 2*pi
            sin_angles = torch.sin(angles).view(1, n_rays, 1, 1)
            cos_angles = torch.cos(angles).view(1, n_rays, 1, 1)
            sin_angles = sin_angles.cuda()
            cos_angles = cos_angles.cuda()
​
            offset_ih = sin_angles * dist_cuda
            offset_iw = cos_angles * dist_cuda
            # 1, r, h, w, 2
            offsets = torch.stack([offset_iw, offset_ih], dim=-1)
            # h, w, 2
            mean_coord = np.round(offsets.mean(dim=1).data.cpu().squeeze(dim=0).numpy()).astype(np.int16)
            
    # Offset-based Post Processing:
    if FPP:
        seg_remained = np.logical_and(seg>=seg_prob_thres, pred==0)
        while seg_remained.any():
            if seg_remained.any():
                rxs, rys = np.where(seg_remained)
                mean_coord_remained = mean_coord[seg_remained, :]
                pred_0 = pred.copy()
                rxs_a = np.clip((rxs + mean_coord_remained[:, 1]).astype(np.int16), 0, h-1)
                rys_a = np.clip((rys + mean_coord_remained[:, 0]).astype(np.int16), 0, w-1)
                pred[seg_remained] = pred[(rxs_a, rys_a)]
                if not((pred_0 != pred).any()):
                    break
            else:
                break
            seg_remained = np.logical_and(seg>=seg_prob_thres, pred==0)

技术点

bilinear插值

Bilinear插值是一种图像处理中常用的插值方法,用于在已知的像素值之间估算新的像素值。在上下采样等操作中,为了获得非整数坐标位置的像素值,可以使用插值方法。Bilinear插值是其中一种常见的插值方法。

具体来说,对于一个二维平面上的四个已知点(四个相邻像素),Bilinear插值计算新点的值,其计算方式如下:

设目标点为(x, y),四个已知点分别为(x1, y1)(x1, y2)(x2, y1)(x2, y2),则Bilinear插值的计算公式为:

f(x,y)=(1−α)⋅(1−β)⋅f(x1,y1)+α⋅(1−β)⋅f(x2,y1)+(1−α)⋅β⋅f(x1,y2)+α⋅β⋅f(x2,y2)

其中,α 和 β 分别表示目标点在水平和垂直方向上的相对位置(在0到1之间)。

在神经网络中,Bilinear插值通常用于上采样操作,例如在上采样层或者转置卷积层(Transpose Convolution)中。通过Bilinear插值,可以有效地增加图像的分辨率,保留一定的图像特征。在上述代码中,bilinear=True表示在上采样操作中使用Bilinear插值。

形态学操作是一组基于形状的图像处理技术,主要用于改变图像的形状和结构。这些操作通常应用于二值图像,其中图像中的像素被标记为前景(白色)或背景(黑色)。形态学操作主要包括腐蚀(Erosion)、膨胀(Dilation)、开运算(Opening)和闭运算(Closing)等。

F.grid_sample是PyTorch中的一个函数,用于在输入张量上进行二维网格采样。它接受两个输入:特征图和采样点坐标,并返回根据采样点坐标在特征图上进行插值得到的采样结果。

具体来说,F.grid_sample函数使用双线性插值方法,在输入特征图上根据给定的采样点坐标进行采样。该函数的参数和功能如下:

  • input:形状为(batch_size, channels, height, width)的输入特征图。其中,batch_size表示批量大小,channels表示通道数,heightwidth表示特征图的高度和宽度。

  • grid:形状为(batch_size, num_samples, 2)的采样点坐标。其中,num_samples表示采样点的数量,2表示采样坐标的x和y方向。

  • mode:采样模式,可以是"bilinear"或"nearest",决定了采样时使用的插值方法。

F.grid_sample函数通过对输入特征图进行插值计算,根据给定的采样点坐标在特征图上进行采样。它会根据采样点坐标的值在特征图上找到最近的四个像素点,并使用双线性插值方法计算出采样点的值。

具体的插值方法如下:

  1. 首先,根据采样点坐标的值,计算出在特征图上最近的四个像素点的坐标。这是通过向下取整和向上取整来实现的。

  2. 然后,对于每个采样点,计算其在最近的四个像素点上的权重。这是通过计算采样点与最近四个像素点的相对距离来实现的。

  3. 最后,根据权重和最近四个像素点的值,进行加权求和,得到采样点的值。

使用F.grid_sample函数可以方便地在特征图上进行二维网格采样,并得到相应的采样结果。该函数在图像处理、计算机视觉等任务中经常被用于对输入数据进行空间变换和几何变换。

形态学操作

博客:openCV:详解形态学操作(腐蚀、膨胀、开运算,闭运算等)_侵蚀和开运算-CSDN博客

以下是形态学操作的简要说明:

  1. 腐蚀(Erosion): 腐蚀操作用于缩小物体的边界。它通过在图像中滑动一个结构元素(也称为内核)并将其放在像素上,只有当内核完全覆盖物体时,该像素才被保留。

  2. 膨胀(Dilation): 膨胀操作用于扩大物体的边界。它通过在图像中滑动一个结构元素并将其放在像素上,只要内核与物体的边界相交,该像素就会被设置为前景。

  3. 开运算(Opening): 开运算是先进行腐蚀,然后进行膨胀的组合操作。它主要用于消除小的亮点或孤立的像素。

  4. 闭运算(Closing): 闭运算是先进行膨胀,然后进行腐蚀的组合操作。它主要用于填充物体内的小洞或连接相邻的物体。

这些形态学操作通常应用于二值图像,其中前景和背景用不同的颜色表示。这些操作对于图像分割、边缘检测、去噪等任务都是有用的。在细胞核分割的上下文中,形态学操作可以用于优化二值掩码,清理和改进细胞核的边界。

在代码中,你可以使用图像处理库(如OpenCV)来应用这些形态学操作。以下是一个简化的示例:

​
import cv2
import numpy as np
​
# 读取二值图像
binary_image = cv2.imread('binary_image.jpg', cv2.IMREAD_GRAYSCALE)
​
# 定义形态学内核
kernel = np.ones((5, 5), np.uint8)
​
# 腐蚀操作
erosion_result = cv2.erode(binary_image, kernel, iterations=1)
​
# 膨胀操作
dilation_result = cv2.dilate(binary_image, kernel, iterations=1)
​
# 开运算
opening_result = cv2.morphologyEx(binary_image, cv2.MORPH_OPEN, kernel)
​
# 闭运算
closing_result = cv2.morphologyEx(binary_image, cv2.MORPH_CLOSE, kernel)

本文中主要采用了腐蚀操作,详情可参照这篇博客:机器学习进阶-图像形态学操作-腐蚀操作 1.cv2.erode(进行腐蚀操作) - python我的最爱 - 博客园 (cnblogs.com)

采样

彻底分清机器学习中的上采样、下采样、过采样、欠采样【总结】_机器学习 上采样和下采样的区别-CSDN博客

文章:Point-Set Anchors for Object Detection, Instance Segmentation and Pose Estimation

学习率调度器

scheduler = ReduceLROnPlateau(optimizer, 'min', factor=0.5, verbose=True, patience=10, eps=1e-8, threshold=1e-20)

这行代码是使用 PyTorch 中的学习率调度器 ReduceLROnPlateau,并将其实例化为 scheduler 对象。

ReduceLROnPlateau 学习率调度器是一种动态调整学习率的方法。它会在训练过程中监测指定指标(如验证集上的损失函数或准确率),如果该指标不再明显地改善,则会降低学习率。具体来说,当连续 patience 个 epoch 的指标都没有明显改善时,调度器会将学习率乘以一个因子 factor

参数说明:

  • optimizer:优化器对象,即实现了梯度下降算法的优化器(如 torch.optim.SGDtorch.optim.Adam 等)。

  • 'min':指定了优化器优化的指标是最小化的。

  • factor:学习率下降的因子。默认值为 0.1,即将学习率降低为原来的 0.1 倍。

  • verbose:是否打印调度信息。默认为 True

  • patience:当连续指定数量的 epoch 没有指标改善时,降低学习率。默认值为 10。

  • eps:学习率下降的最小值。如果新学习率比旧学习率低的程度小于 eps,则不会更新学习率。默认值为 1e-8。

  • threshold:指标变化的最小值。如果指标变化的程度低于 threshold,则不会被视为明显改善。默认值为 1e-4。

示例:

import torch
from torch.optim import SGD
from torch.optim.lr_scheduler import ReduceLROnPlateau
​
# 创建一个优化器对象
optimizer = SGD(model.parameters(), lr=0.01, momentum=0.9)
​
# 创建学习率调度器对象
scheduler = ReduceLROnPlateau(optimizer, 'min', factor=0.5, verbose=True, patience=10, eps=1e-8, threshold=1e-20)
​
# 在训练过程中使用学习率调度器
for epoch in range(num_epochs):
    # 训练模型并计算损失函数和准确率
    train_loss, train_acc = train(model, train_loader, criterion, optimizer)
    valid_loss, valid_acc = validate(model, valid_loader, criterion)
​
    # 每个 epoch 结束后更新学习率
    scheduler.step(valid_loss)
​
    # 打印学习率和指标信息
    print(f"Epoch {epoch+1}, lr {optimizer.param_groups[0]['lr']:.6f}, train loss: {train_loss:.6f}, train acc: {train_acc:.6f}, valid loss: {valid_loss:.6f}, valid acc: {valid_acc:.6f}")

在这个示例中,我们首先使用 SGD 优化器创建了一个优化器对象,然后使用 ReduceLROnPlateau 学习率调度器创建了一个调度器对象。在每个 epoch 结束后,我们使用验证集上的损失函数更新学习率,并打印相关信息。

需要注意的是,在使用学习率调度器时,通常需要设置一个合适的初始学习率并进行调整。过小的学习率会导致收敛速度过慢,而过大的学习率可能会导致模型无法收敛。因此,需要根据具体任务和数据集选择一个合适的学习率,并通过训练过程中的学习率调整来优化收敛效果。

代码运行

通过os.getcwd()得到的目录中最好不含中文,否则可能会报错,目录尽量用英文。(mainshape.py)

注意训练的轮数是否为None

先进行feature_extractor的训练,得到参数文件

再进行maincppnet的训练

最后进行predict验证。

predict中修改:文章来源地址https://www.toymoban.com/news/detail-836156.html

   # 源代码
    coord = dist_to_coord(dist_numpy)
    points = non_maximum_suppression(coord, prob_numpy, prob_thresh=center_prob_thres)
    star_label = polygons_to_label(coord, prob_numpy, points)
    
    # 修改后
    points,sc,dis = non_maximum_suppression(dist_numpy, prob_numpy, prob_thresh=center_prob_thres)
    coord = dist_to_coord(dis,points)
    star_label = polygons_to_label(dis, points,img.shape)

到了这里,关于CPP-NET论文阅读的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 人工智能之深度学习

    第一章 人工智能概述 1.1人工智能的概念和历史 1.2人工智能的发展趋势和挑战 1.3人工智能的伦理和社会问题 第二章 数学基础 1.1线性代数 1.2概率与统计 1.3微积分 第三章 监督学习 1.1无监督学习 1.2半监督学习 1.3增强学习 第四章 深度学习 1.1神经网络的基本原理 1.2深度学习的

    2024年02月09日
    浏览(51)
  • 人工智能深度学习

    目录 人工智能 深度学习 机器学习 神经网络 机器学习的范围 模式识别 数据挖掘 统计学习 计算机视觉 语音识别 自然语言处理 机器学习的方法 回归算法 神经网络 SVM(支持向量机) 聚类算法 降维算法 推荐算法 其他 机器学习的分类 机器学习模型的评估 机器学习的应用 机

    2024年02月22日
    浏览(54)
  • 人工智能、机器学习、深度学习的区别

    人工智能涵盖范围最广,它包含了机器学习;而机器学习是人工智能的重要研究内容,它又包含了深度学习。 人工智能是一门以计算机科学为基础,融合了数学、神经学、心理学、控制学等多个科目的交叉学科。 人工智能是一门致力于使计算机能够模拟、模仿人类智能的学

    2024年02月08日
    浏览(51)
  • 机器学习入门教学——人工智能、机器学习、深度学习

    1、人工智能 人工智能相当于人类的代理人,我们现在所接触到的人工智能基本上都是弱AI,主要作用是正确解释从外部获得的数据,并对这些数据加以学习和利用,以便灵活的实现特定目标和任务。 例如: 阿尔法狗、智能汽车 简单来说: 人工智能使机器像人类一样进行感

    2024年02月09日
    浏览(84)
  • 探索人工智能:深度学习、人工智能安全和人工智能编程(文末送书)

    人工智能知识对于当今的互联网技术人来说已经是刚需。但人工智能的概念、流派、技术纷繁复杂,选择哪本书入门最适合呢? 这部被誉为人工智能“百科全书”的《人工智能(第3版)》,可以作为每个技术人进入 AI 世界的第一本书。 购书链接,限时特惠5折 这本书是美国

    2024年02月03日
    浏览(113)
  • 机器学习、人工智能、深度学习三者的区别

    目录 1、三者的关系 2、能做些什么 3、阶段性目标 机器学习、人工智能(AI)和深度学习之间有密切的关系,它们可以被看作是一种从不同层面理解和实现智能的方法。 人工智能(AI):人工智能是一门研究如何使计算机能够模仿人类智能的学科。它涵盖了各种技术和方法,

    2024年02月14日
    浏览(57)
  • 12、人工智能、机器学习、深度学习的关系

    很多年前听一个机器学习的公开课,在QA环节,一个同学问了老师一个问题“ 机器学习和深度学习是什么关系 ”? 老师先没回答,而是反问了在场的同学,结果问了2-3个,没有人可以回答的很到位,我当时也是初学一脸懵,会场准备的小礼品也没有拿到。 后来老师解释“机

    2024年02月05日
    浏览(65)
  • 一探究竟:人工智能、机器学习、深度学习

    1.1 人工智能是什么?          1956年在美国Dartmounth 大学举办的一场研讨会中提出了人工智能这一概念。人工智能(Artificial Intelligence),简称AI,是计算机科学的一个分支,它企图了解智能的实质,并生产出一种新的能以人类智能相似的方式做出反应的智能机器,该领域的

    2024年02月17日
    浏览(47)
  • 深度学习:探索人工智能的前沿

    人工智能(Artificial Intelligence,简称AI)是一门研究如何使计算机能够执行通常需要人类智能的任务的领域。从早期的符号推理到现代的深度学习,人工智能经历了漫长的发展过程。 20世纪50年代,AI的奠基性工作开始,研究者们试图通过符号推理来模拟人类思维过程。然而,

    2024年01月19日
    浏览(69)
  • 人工智能的深度学习如何入门

    人工智能深度学习近年来成为热门的技术领域,被广泛应用于许多领域,如自然语言处理、图像识别、机器翻译等。学习人工智能深度学习需要具备一定的数学和编程基础,但对于初学者来说,并不需要过于复杂的数学和编程知识。本文将介绍人工智能深度学习的基本概念和

    2024年03月27日
    浏览(58)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包