正常的迁移学习(迁移自建网络)使用方法如paddlepaddle 6 使用迁移学习对图像进行分类_a486259的博客-****博客,但需要使用Deeplab、HRNet、UNET,FCN等知名的开源语义分割模型得要有相应的模型结构文件和模型权重文件,才可以进行迁移学习。幸好paddleseg中内置了这些模型,因此面向语义分割的迁移学习基于paddleseg实现(仅使用paddleseg实现模型加载与构建),训练部分使用paddle实现。
1、构建数据加载器
面向语义分割的数据加载器,直接参考paddlepaddle 1 数据加载器的使用_a486259的博客-****博客
2、加载预训练模型
在这里首先要明白一个前置知识,不少语义分割模型是预设backbones的,用于提取特征。
2.1 初始化模型的简单案例
#迁移学习的教程 https://aistudio.baidu.com/aistudio/projectdetail/1339458?channelType=0&channel=0
#支持的模型列表 https://github.com/PaddlePaddle/PaddleSeg/blob/release/2.4/README_CN.md
from paddleseg.models import backbones
#backbone的初始化方法 https://github.com/PaddlePaddle/PaddleSeg/blob/develop/docs/apis/backbones/backbones_cn.md
backbone=backbones.ResNet101_vd(pretrained = None)
backbone.feat_channels
from paddleseg import models
num_classes=4
deeplabv3=models.DeepLabV3P(
num_classes,
backbones.ResNet101_vd(pretrained = None),
backbone_indices = (2, 3),
)
print(deeplabv3)
加载的后模型可以看到是paddle.nn.Layer对象,跟我们自己搭建的模型是一模一样的。
2.2 初始化backbones
paddleseg中实现的backbones共有ResNet_vd、HRNet、MobileNetV3、XceptionDeeplab四个系列。
示例用法
class paddleseg.models.backbones.Resnet_vd( layers = 50, output_stride = None, multi_grid = (1, 1, 1), lr_mult_list = (0.1, 0.1, 0.2, 0.2), pretrained = None )
参数
- layers (int, optional): ResNet_vd的层数。 支持的层数有[18, 34, 50, 101, 152, 200]。 默认: 50
- output_stride (int, optional): 与输入图像相比,输出特征的缩放步长,该参数将影响下采样倍数。该参数应为 8 或 16。 默认: 8
- multi_grid (tuple|list, optional): stage 4的grid设置,用以扩大卷积的感受野。 默认: (1, 1, 1)
- pretrained (str, optional): 预训练模型的路径。
paddleseg.models.backbones.ResNet18_vd(**args) 返回 ResNet_vd 类的一个对象,其层数为 18 。 paddleseg.models.backbones.ResNet34_vd(**args) 返回 ResNet_vd 类的一个对象,其层数为 34 。 paddleseg.models.backbones.ResNet50_vd(**args) 返回 ResNet_vd 类的一个对象,其层数为 50 。 paddleseg.models.backbones.ResNet101_vd(**args) 返回 ResNet_vd 类的一个对象,其层数为 101 。 paddleseg.models.backbones.ResNet152_vd(**args) 返回 ResNet_vd 类的一个对象,其层数为 152 。 padddelseg.models.backbones.ResNet200_vd(**args) 返回 ResNet_vd 类的一个对象,其层数为 200 。
更多用法可以参考
PaddleSeg/backbones_cn.md at develop · PaddlePaddle/PaddleSeg · GitHub
2.3 初始化model
paddleseg中预训练的模型有DeepLabV3+、DeepLabV3、FCN、OCRNet、PSPNet、ANN、BiSeNetV2、DANet、FastSCNN、GCNet、GSCNN、HarDNet、UNet、U2Net、U2Net+、AttentionUNet、UNet++、DecoupledSegNet、ISANet、EMANet、DNLNet。有的模型的初始化需要先预设backbone,有的不需要。
DeepLabV3+
class paddleseg.models.DeepLabV3P( num_classes, backbone, backbone_indices = (0, 3), aspp_ratios = (1, 6, 12, 18), aspp_out_channels = 256, align_corners = False, pretrained = None )
基于 PaddlePaddle 实现的 DeepLabV3Plus。
参数
- num_classes (int): 相互独立的目标类别的数量。
- backbone (paddle.nn.Layer): 骨干网络,目前支持 Resnet50_vd/Resnet101_vd/Xception65。
-
backbone_indices (tuple, optional): 元组中的两个值指示了骨干网络输出的索引。默认:
(0, 3)
-
aspp_ratios (tuple, optional): ASSP module模块中使用的扩张率。 如果 output_stride = 16, aspp_ratios 应该设定为 (1, 6, 12, 18)。 如果 output_stride = 8, aspp_ratios 应该设定为 (1, 12, 24, 36)。 默认:
(1, 6, 12, 18)
-
aspp_out_channels (int, optional): ASPP模块的输出通道数。默认:
256
-
align_corners (bool, optional): F.interpolate 的一个参数。当特征大小为偶数时应设置为 False,例如 1024x512; 否则为 True, 例如 769x769。默认:
False
-
pretrained (str, optional): 预训练模型的url或path。 默认:
None
更多模型的用法可以参考PaddleSeg/models_cn.md at develop · PaddlePaddle/PaddleSeg · GitHub
3、训练模型
语义分割与分类的训练代码本质上是一样的,并无其他的。因此,直接复制前面章节的代码。
from tqdm import tqdm
def train_model(train_loader,model,loss_fn,optim):
model.train()
total_loss=0
total_acc=0
with tqdm(total=len(train_loader), desc=f'Epoch {epoch + 1}/{Epochs}', postfix=dict, mininterval=0.3) as pbar:
for iteration, data in enumerate(train_loader()):
x_data = data[0] # 训练数据
y_data = data[1] # 训练数据标签
predicts = model(x_data) # 预测结果
# 计算损失 等价于 prepare 中loss的设置
loss = loss_fn(predicts, y_data)
# 计算准确率 等价于 prepare 中metrics的设置
acc = paddle.metric.accuracy(predicts, y_data)
# 反向传播
loss.backward()
# 更新参数
optim.step()
# 梯度清零
optim.clear_grad()
#更新进度条
total_loss+=loss.item()
total_acc+=acc.item()
pbar.set_postfix(**{'total_loss': "%.4f"%(total_loss / (iteration + 1)),'acc':"%.4f"%(total_acc/ (iteration + 1))})
pbar.update(1)
def eval_model(test_loader,model,loss_fn):
model.eval()
total_loss=0
total_acc=0
for iteration, data in enumerate(test_loader()):
x_data = data[0] # 测试数据
y_data = data[1] # 测试数据标签
predicts = model(x_data) # 预测结果
# 计算损失与精度
loss = loss_fn(predicts, y_data)
acc = paddle.metric.accuracy(predicts, y_data)
total_loss+=loss.item()
total_acc+=acc.item()
loss,acc=total_loss / (iteration + 1), total_acc/ (iteration + 1)
print("Test result, loss is: {:.4f}, acc is: {:.4f}".format(loss,acc))
return loss,acc
#===================加载数据集==========================
train_line="ImagesSeg train.txt"
with open(train_line,"r") as f:
train_lines = f.readlines()
test_line="ImagesSeg test.txt"
with open(test_line,"r") as f:
test_lines = f.readlines()
# 测试定义的数据集
train_dataset = ImageSegDataset(train_lines,input_shape=(256,256))
test_dataset = ImageSegDataset(test_lines,input_shape=(256,256))
BATCH_SIZE=64
train_loader = paddle.io.DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True)
test_loader = paddle.io.DataLoader(test_dataset, batch_size=BATCH_SIZE, shuffle=True)
#===================构造模型和相关训练参数==========================
model = MyModel()
paddle.summary(model, (BATCH_SIZE, 3, 256, 256))
# 设置迭代次数
Epochs = 20
# 设置优化器
optim = paddle.optimizer.Adam(parameters=model.parameters())
# 设置损失函数 其中内置了softmax和onehot
loss_fn = paddle.nn.CrossEntropyLoss()
#===================训练模型==========================
for epoch in range(Epochs):
train_model(train_loader,model,loss_fn,optim)
loss,acc=eval_model(test_loader,model,loss_fn)
#paddle.save(model.state_dict(), "ep{}_acc{:4f}_loss{:.4f}model.pdparams".format(epoch,loss,acc), protocol=4)
paddle.save(model, "minist/ep{}_acc{:4f}_loss{:.4f}model".format(epoch,loss,acc), input_spec=[paddle.rand((1, 3, 256, 256))])