wangwenguang commited on
Commit
d6001fd
·
verified ·
1 Parent(s): 5ea0d01

Delete model

Browse files
model/__init__.py DELETED
@@ -1 +0,0 @@
1
- #
 
 
model/__pycache__/__init__.cpython-310.pyc DELETED
Binary file (149 Bytes)
 
model/__pycache__/__init__.cpython-311.pyc DELETED
Binary file (150 Bytes)
 
model/__pycache__/__init__.cpython-38.pyc DELETED
Binary file (148 Bytes)
 
model/__pycache__/resnet_backbone.cpython-310.pyc DELETED
Binary file (5.77 kB)
 
model/__pycache__/resnet_backbone.cpython-311.pyc DELETED
Binary file (9.66 kB)
 
model/__pycache__/resnet_backbone.cpython-38.pyc DELETED
Binary file (5.82 kB)
 
model/__pycache__/unet_resnet.cpython-310.pyc DELETED
Binary file (3.08 kB)
 
model/__pycache__/unet_resnet.cpython-311.pyc DELETED
Binary file (5.27 kB)
 
model/__pycache__/unet_resnet.cpython-38.pyc DELETED
Binary file (3.06 kB)
 
model/__pycache__/unet_training.cpython-310.pyc DELETED
Binary file (5.41 kB)
 
model/__pycache__/unet_training.cpython-311.pyc DELETED
Binary file (9.55 kB)
 
model/__pycache__/unet_training.cpython-38.pyc DELETED
Binary file (5.37 kB)
 
model/resnet_backbone.py DELETED
@@ -1,216 +0,0 @@
1
- import math
2
- import torch.nn as nn
3
-
4
-
5
-
6
- def conv3x3(in_planes, out_planes, stride=1, groups=1, dilation=1):
7
- """3x3 convolution with padding
8
-
9
- Args:
10
- in_planes (int): 输入通道数
11
- out_planes (int): 输出通道数
12
- stride (int): 卷积步幅,默认为 1
13
- groups (int): 卷积分组数,默认为 1
14
- dilation (int): 卷积扩张系数,默认为 1
15
-
16
- Returns:
17
- nn.Conv2d: 定义的 3x3 卷积层
18
- """
19
- return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride,
20
- padding=dilation, groups=groups, bias=False, dilation=dilation)
21
-
22
-
23
- def conv1x1(in_planes, out_planes, stride=1):
24
- """1x1 convolution
25
- Args:
26
- in_planes (int): 输入通道数
27
- out_planes (int): 输出通道数
28
- stride (int): 步幅,默认为 1
29
-
30
- Returns:
31
- nn.Conv2d: 定义的 1x1 卷积层
32
- """
33
- return nn.Conv2d(in_planes, out_planes, kernel_size=1, stride=stride, bias=False)
34
-
35
- class Bottleneck(nn.Module):
36
- expansion = 4
37
-
38
- def __init__(self, inplanes, planes, stride=1, downsample=None, groups=1,
39
- base_width=64, dilation=1, norm_layer=None):
40
- """
41
- 初始化 Bottleneck 模块
42
-
43
- Args:
44
- inplanes (int): 输入通道数
45
- planes (int): 输出通道数(卷积层的通道数)
46
- stride (int): 卷积步幅,默认为 1
47
- downsample (nn.Module, optional): 用于调整输入尺寸的层(例如:当步幅不为 1 时,需要下采样)
48
- groups (int): 分组卷积的数量,默认为 1
49
- base_width (int): 基础宽度,影响每层的宽度
50
- dilation (int): 卷积扩张系数,默认为 1
51
- norm_layer (nn.Module, optional): 归一化层类型,默认为 `nn.BatchNorm2d`
52
- """
53
- super(Bottleneck, self).__init__()
54
-
55
- # 如果没有提供 norm_layer,则使用 BatchNorm2d
56
- if norm_layer is None:
57
- norm_layer = nn.BatchNorm2d
58
-
59
- # 计算宽度,groups 会影响每个卷积层的通道数
60
- width = int(planes * (base_width / 64.)) * groups
61
-
62
- # 第一层 1x1 卷积,作用是降维
63
- self.conv1 = conv1x1(inplanes, width)
64
- self.bn1 = norm_layer(width) # 归一化层
65
- # 第二层 3x3 卷积,作用是进行空间下采样
66
- self.conv2 = conv3x3(width, width, stride, groups, dilation)
67
- self.bn2 = norm_layer(width) # 归一化层
68
- # 第三层 1x1 卷积,作用是升维
69
- self.conv3 = conv1x1(width, planes * self.expansion)
70
- self.bn3 = norm_layer(planes * self.expansion) # 归一化层
71
-
72
- # 激活函数 ReLU
73
- self.relu = nn.ReLU(inplace=True)
74
-
75
- # 下采样层,默认为 None
76
- self.downsample = downsample
77
- # 存储步幅
78
- self.stride = stride
79
-
80
- def forward(self, x):
81
- """
82
- 正向传播
83
-
84
- Args:
85
- x (Tensor): 输入张量
86
-
87
- Returns:
88
- Tensor: 输出张量
89
- """
90
- identity = x # 存储输入张量,用于跳跃连接(residual connection)
91
-
92
- # 通过第一个 1x1 卷积层
93
- out = self.conv1(x)
94
- out = self.bn1(out)
95
- out = self.relu(out)
96
-
97
- # 通过第二个 3x3 卷积层
98
- out = self.conv2(out)
99
- out = self.bn2(out)
100
- out = self.relu(out)
101
-
102
- # 通过第三个 1x1 卷积层
103
- out = self.conv3(out)
104
- out = self.bn3(out)
105
-
106
- if self.downsample is not None:
107
- identity = self.downsample(x)
108
-
109
- # 跳跃连接:将输入(identity)与输出相加
110
- out += identity
111
-
112
- # 激活函数(ReLU)再次作用在输出上
113
- out = self.relu(out)
114
-
115
- return out
116
-
117
-
118
- class ResNet(nn.Module):
119
- def __init__(self, block, layers, num_classes=1000):
120
- super(ResNet, self).__init__()
121
-
122
- # 设置初始通道数,供残差模块使用(后续根据 expansion 自动更新)
123
- self.inplanes = 64
124
-
125
- # 输入通道数为 3,输出为 64,7x7 大卷积核,步长为 2,padding 为 3,不使用 bias,为一个CBR模块
126
- self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False)
127
- self.bn1 = nn.BatchNorm2d(64)
128
- self.relu = nn.ReLU(inplace=True)
129
-
130
- # 最大池化层,3x3 核,步长为 2,无 padding,向上取整模式
131
- self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=0, ceil_mode=True) # change
132
-
133
- # 构建四个 stage 的残差网络,每个 stage 包含 layers[i] 个残差块
134
- self.layer1 = self._make_layer(block, 64, layers[0])
135
- self.layer2 = self._make_layer(block, 128, layers[1], stride=2)
136
- self.layer3 = self._make_layer(block, 256, layers[2], stride=2)
137
- self.layer4 = self._make_layer(block, 512, layers[3], stride=2)
138
-
139
- # 平均池化,用于分类场景下整合空间信息(此实现中未启用)
140
- self.avgpool = nn.AvgPool2d(7)
141
- # 分类输出全连接层:输入通道为最后一层的输出通道(含 expansion),输出为类别数
142
- self.fc = nn.Linear(512 * block.expansion, num_classes)
143
-
144
- # 模块初始化
145
- for m in self.modules():
146
- if isinstance(m, nn.Conv2d):
147
- # He 初始化:N(0, sqrt(2/n)),适合 ReLU 激活函数
148
- n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
149
- m.weight.data.normal_(0, math.sqrt(2. / n))
150
- elif isinstance(m, nn.BatchNorm2d):
151
- # BN 权重初始化为 1,偏置初始化为 0
152
- m.weight.data.fill_(1)
153
- m.bias.data.zero_()
154
-
155
- def _make_layer(self, block, planes, blocks, stride=1):
156
- """
157
- 构建一个残差层(stage),由多个残差块 block 组成。
158
- - block: 残差块类型(如 Bottleneck 或 BasicBlock)
159
- - planes: 当前 stage 的基础输出通道数
160
- - blocks: 当前 stage 包含的残差块数量
161
- - stride: 第一个 block 的步长(用于空间下采样)
162
- """
163
- downsample = None
164
- # 如果输入通道与输出通道不一致,或 stride ≠ 1,则需要下采样对齐
165
- if stride != 1 or self.inplanes != planes * block.expansion:
166
- downsample = nn.Sequential(
167
- nn.Conv2d(self.inplanes, planes * block.expansion,
168
- kernel_size=1, stride=stride, bias=False),
169
- nn.BatchNorm2d(planes * block.expansion),
170
- )
171
- layers = []
172
- # 第一个 block:可能包含下采样和通道数调整
173
- layers.append(block(self.inplanes, planes, stride, downsample))
174
- # 更新当前输入通道数为新的输出通道数
175
- self.inplanes = planes * block.expansion
176
-
177
- # 其余 block:不下采样,通道保持一致
178
- for i in range(1, blocks):
179
- layers.append(block(self.inplanes, planes))
180
- return nn.Sequential(*layers)
181
-
182
- def forward(self, x):
183
- # print("输入数据:", x.shape)
184
- """
185
- 定义前向传播过程,输出多个阶段的特征图(适合用于下游任务如 FPN、检测、分割等)
186
- """
187
- x = self.conv1(x)
188
- x = self.bn1(x)
189
- feat1 = self.relu(x) # 输出特征图
190
- # print("feat1:", feat1.shape)
191
-
192
- x = self.maxpool(feat1)
193
- # print("池化层:", x.shape)
194
-
195
- feat2 = self.layer1(x) # stage 1 输出
196
- feat3 = self.layer2(feat2) # stage 2 输出
197
- feat4 = self.layer3(feat3) # stage 3 输出
198
- feat5 = self.layer4(feat4) # stage 4 输出
199
-
200
- # 返回所有层的中间特征图(而非分类结果),常用于特征提取任务
201
- return [feat1, feat2, feat3, feat4, feat5]
202
-
203
-
204
- def resnet50(**kwargs):
205
- # 构建一个 ResNet-50 模型实例,使用 Bottleneck 结构
206
- # 每个 stage 分别包含 3, 4, 6, 3 个残差块(符合 ResNet-50 配置)
207
- # 额外参数通过 kwargs 传入 ResNet 构造函数(如 num_classes, input_shape 等)
208
- model = ResNet(Bottleneck, [3, 4, 6, 3], **kwargs)
209
-
210
- # 删除全局平均池化层(avgpool)和全连接层(fc),以去除分类模块
211
- # 模型将仅保留主干部分(stem + stage1~stage4),适合特征提取任务
212
- del model.avgpool
213
- del model.fc
214
-
215
- # 返回去除分类头的 ResNet-50 模型,用于下游任务(如检测、分割)
216
- return model
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
model/unet_resnet.py DELETED
@@ -1,104 +0,0 @@
1
- import torch
2
- import torch.nn as nn
3
-
4
- from model.resnet_backbone import resnet50
5
-
6
- # 定义一个 U-Net 解码模块(上采样模块)
7
- class unetUp(nn.Module):
8
- def __init__(self, in_size, out_size):
9
- """
10
- 构造函数
11
- 参数:
12
- - in_size: 输入通道数(等于上采样特征图通道数 + 跳跃连接特征图通道数)
13
- - out_size: 输出通道数(经过卷积后的输出通道数)
14
- """
15
- super(unetUp, self).__init__()
16
- # 第一层卷积:输入通道为 in_size,输出通道为 out_size,卷积核大小 3x3,padding=1 保持尺寸不变
17
- self.conv1 = nn.Conv2d(in_size, out_size, kernel_size=3, padding=1)
18
- # 第二层卷积:输入通道和输出通道均为 out_size,进一步特征提取
19
- self.conv2 = nn.Conv2d(out_size, out_size, kernel_size=3, padding=1)
20
- # 上采样层:使用双线性插值将特征图放大两倍(scale_factor=2)
21
- self.up = nn.UpsamplingBilinear2d(scale_factor=2)
22
- # ReLU 激活函数,inplace=True 表示原地操作,节省内存
23
- self.relu = nn.ReLU(inplace=True)
24
-
25
- def forward(self, inputs1, inputs2):
26
- """
27
- 前向传播
28
- 参数:
29
- - inputs1: 编码器部分通过跳跃连接传来的特征图(高分辨率)
30
- - inputs2: 来自上一级解码器的输出(低分辨率,需要上采样)
31
- """
32
- # 先对 inputs2 进行上采样,然后与 inputs1 在通道维度上进行拼接
33
- # 拼接后的通道数为 in_size
34
- outputs = torch.cat([inputs1, self.up(inputs2)], 1)
35
- # 第一次卷积 + ReLU
36
- outputs = self.conv1(outputs)
37
- outputs = self.relu(outputs)
38
- # 第二次卷积 + ReLU
39
- outputs = self.conv2(outputs)
40
- outputs = self.relu(outputs)
41
- # 返回该解码模块的输出特征图
42
- return outputs
43
-
44
-
45
- # 定义 U-Net 主体结构
46
- class Unet(nn.Module):
47
- def __init__(self, num_classes=21):
48
- """
49
- 构造函数
50
- 参数:
51
- - num_classes: 最终分类的类别数(用于语义分割任务)
52
- """
53
- super(Unet, self).__init__()
54
-
55
- # 使用 ResNet50 作为编码器,提取多尺度特征
56
- self.resnet = resnet50() # 假设 resnet50() 返回 5 层特征:feat1 ~ feat5
57
- # 编码器输出的特征通道数(每层输出的特征图通道数)
58
- in_filters = [192, 512, 1024, 3072] # 通常是低到高的顺序(层级)
59
- # 解码器每一层希望恢复到的输出通道数
60
- out_filters = [64, 128, 256, 512] # 解码层输出通道数逐步降低
61
-
62
- # 定义 4 层上采样模块(从深到浅)
63
- # 每层通过双线性插值上采样 + 拼接 + 两次卷积 + ReLU
64
- self.up_concat4 = unetUp(in_filters[3], out_filters[3]) # 最深层输出
65
- self.up_concat3 = unetUp(in_filters[2], out_filters[2]) # 次深层
66
- self.up_concat2 = unetUp(in_filters[1], out_filters[1]) # 中间层
67
- self.up_concat1 = unetUp(in_filters[0], out_filters[0]) # 最浅层
68
-
69
- # 对最后一层解码器的输出再上采样一倍并做两次卷积处理,进一步提升分辨率
70
- self.up_conv = nn.Sequential(
71
- nn.UpsamplingBilinear2d(scale_factor=2), # 上采样
72
- nn.Conv2d(out_filters[0], out_filters[0], kernel_size=3, padding=1), # 卷积
73
- nn.ReLU(),
74
- nn.Conv2d(out_filters[0], out_filters[0], kernel_size=3, padding=1), # 卷积
75
- nn.ReLU(),
76
- )
77
- # 最后一层卷积,用于生成最终每类像素的得分图(通道数=num_classes)
78
- self.final = nn.Conv2d(out_filters[0], num_classes, 1)
79
-
80
- def forward(self, inputs):
81
- """
82
- 前向传播
83
- 参数:
84
- - inputs: 输入图像(通常为 RGB 图像,形状为 B×3×H×W)
85
-
86
- 返回:
87
- - final: 每个像素点在 num_classes 个类别上的预测(未Softmax)
88
- """
89
- # 编码器提取五层特征图(假设顺序为由浅到深)
90
- [feat1, feat2, feat3, feat4, feat5] = self.resnet.forward(inputs)
91
-
92
- # 解码过程:由深到浅依次上采样 + 融合跳跃连接特征
93
- up4 = self.up_concat4(feat4, feat5) # 使用 feat5 上采样后与 feat4 融合
94
- up3 = self.up_concat3(feat3, up4) # 再将上一层上采样后与 feat3 融合
95
- up2 = self.up_concat2(feat2, up3) # 同理
96
- up1 = self.up_concat1(feat1, up2) # 最浅层融合
97
-
98
- # 可选的最终进一步上采样(恢复到输入图尺寸)
99
- if self.up_conv != None:
100
- up1 = self.up_conv(up1)
101
-
102
- # 最终生成每类的预测图
103
- final = self.final(up1)
104
- return final
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
model/unet_training.py DELETED
@@ -1,199 +0,0 @@
1
- import math
2
- from functools import partial
3
-
4
- import torch
5
- import torch.nn as nn
6
- import torch.nn.functional as F
7
-
8
-
9
- def CE_Loss(inputs, target, cls_weights, num_classes=21):
10
- n, c, h, w = inputs.size() # 输入大小,n=batch_size,c=类别数,h=高度,w=宽度
11
- nt, ht, wt = target.size() # 目标大小,nt=batch_size,ht=目标图像的高度,wt=目标图像的宽度
12
-
13
- # 如果输入和目标的大小不一致,则进行插值操作
14
- if h != ht and w != wt:
15
- inputs = F.interpolate(inputs, size=(ht, wt), mode="bilinear", align_corners=True)
16
-
17
- # 将输入和目标调整为合适的形状
18
- temp_inputs = inputs.transpose(1, 2).transpose(2, 3).contiguous().view(-1, c) # 调整为 [n*h*w, c]
19
- # temp_inputs = inputs.view(-1, c) # 变为 [n*h*w, c]
20
- temp_target = target.view(-1) # 展平目标标签为 [n*h*w]
21
-
22
- # 计算交叉熵损失
23
- CE_loss = nn.CrossEntropyLoss(weight=cls_weights, ignore_index=num_classes)(temp_inputs, temp_target)
24
- return CE_loss
25
-
26
-
27
- """
28
- 这段代码实现了 Focal Loss,这是一个改进版的交叉熵损失,
29
- 专门用于处理类别不平衡问题。它通过对 困难样本(即模型不确定的样本)
30
- 施加更高的权重来增强模型对这些样本的关注,尤其是在训练过程中遇到类不平衡时。
31
- """
32
- def Focal_Loss(inputs, target, cls_weights, num_classes=21, alpha=0.5, gamma=2):
33
- n, c, h, w = inputs.size() # 输入的尺寸,n=batch_size,c=类别数,h=高度,w=宽度
34
- nt, ht, wt = target.size() # 目标标签的尺寸,nt=batch_size,ht=目标高度,wt=目标宽度
35
-
36
- # 如果输入和目标的尺寸不一致,则进行插值调整
37
- if h != ht and w != wt:
38
- inputs = F.interpolate(inputs, size=(ht, wt), mode="bilinear", align_corners=True)
39
-
40
- # 展平输入和目标,使其适应交叉熵损失函数的要求
41
- temp_inputs = inputs.transpose(1, 2).transpose(2, 3).contiguous().view(-1, c)
42
- # temp_inputs = inputs.view(-1, c) # 输入变为 [n*h*w, c]
43
- temp_target = target.view(-1) # 目标变为 [n*h*w]
44
-
45
- # 计算交叉熵损失,使用 `reduction='none'` 返回每个像素的损失
46
- logpt = -nn.CrossEntropyLoss(weight=cls_weights, ignore_index=num_classes, reduction='none')(temp_inputs,
47
- temp_target)
48
- pt = torch.exp(logpt) # 计算每个像素属于真实类别的概率
49
-
50
- # 如果 alpha 存在,则乘上 alpha 来调节正负样本的权重
51
- if alpha is not None:
52
- logpt *= alpha
53
-
54
- # 计算 Focal Loss
55
- loss = -((1 - pt) ** gamma) * logpt
56
-
57
- # 对所有像素的损失求平均
58
- loss = loss.mean()
59
- return loss
60
-
61
-
62
- """
63
- 这段代码实现了 Dice Loss,它是用于计算图像分割任务中的性能度量指标,
64
- 尤其适用于数据类别不平衡的场景。Dice 系数衡量的是预测结果和真实标签之间的重叠程度,
65
- 通常用于语义分割任务中,特别是在医学图像分割等领域。
66
- """
67
- def Dice_loss(inputs, target, beta=1, smooth=1e-5):
68
- n, c, h, w = inputs.size() # 输入的尺寸:n=batch_size, c=类别数, h=高度, w=宽度
69
- nt, ht, wt, ct = target.size() # 目标标签的尺寸:nt=batch_size, ht=高度, wt=宽度, ct=类别数
70
-
71
- # 如果输入和目标的尺寸不一致,进行插值
72
- if h != ht and w != wt:
73
- inputs = F.interpolate(inputs, size=(ht, wt), mode="bilinear", align_corners=True)
74
-
75
- # 对输入和目标进行转换,保证计算时的一致性
76
- temp_inputs = torch.softmax(inputs.transpose(1, 2).transpose(2, 3).contiguous().view(n, -1, c), -1)
77
- # temp_inputs = torch.softmax(inputs.permute(0, 2, 3, 1).contiguous().view(n, -1, c), -1)
78
- temp_target = target.view(n, -1, ct)
79
-
80
- # 计算真正例 (tp),即真实类别和预测类别的交集
81
- tp = torch.sum(temp_target[..., :-1] * temp_inputs, axis=[0, 1])
82
- # 计算假正例 (fp),即预测为类别而真实标签为背景
83
- fp = torch.sum(temp_inputs, axis=[0, 1]) - tp
84
- # 计算假负例 (fn),即真实标签为类别而预测为背景
85
- fn = torch.sum(temp_target[..., :-1], axis=[0, 1]) - tp
86
-
87
- # 计算 Dice 系数,beta 控制对假负样本的惩罚程度
88
- score = ((1 + beta ** 2) * tp + smooth) / ((1 + beta ** 2) * tp + beta ** 2 * fn + fp + smooth)
89
- # 计算 Dice Loss
90
- dice_loss = 1 - torch.mean(score)
91
- return dice_loss
92
-
93
-
94
- def weights_init(net, init_type='normal', init_gain=0.02):
95
- def init_func(m):
96
- classname = m.__class__.__name__ # 获取模块的类名
97
- if hasattr(m, 'weight') and classname.find('Conv') != -1: # 如果是卷积层
98
- if init_type == 'normal':
99
- torch.nn.init.normal_(m.weight.data, 0.0, init_gain) # 正态分布初始化
100
- elif init_type == 'xavier':
101
- torch.nn.init.xavier_normal_(m.weight.data, gain=init_gain) # Xavier初始化
102
- elif init_type == 'kaiming':
103
- torch.nn.init.kaiming_normal_(m.weight.data, a=0, mode='fan_in') # Kaiming初始化
104
- elif init_type == 'orthogonal':
105
- torch.nn.init.orthogonal_(m.weight.data, gain=init_gain) # 正交初始化
106
- else:
107
- raise NotImplementedError('initialization method [%s] is not implemented' % init_type)
108
- elif classname.find('BatchNorm2d') != -1: # 如果是批量归一化层
109
- torch.nn.init.normal_(m.weight.data, 1.0, 0.02) # 正态分布初始化,均值1,标准差0.02
110
- torch.nn.init.constant_(m.bias.data, 0.0) # 偏置初始化为0
111
-
112
- print('initialize network with %s type' % init_type) # 打印初始化方法
113
- net.apply(init_func) # 将初始化函数应用到网络的每一层
114
-
115
-
116
- def get_lr_scheduler(lr_decay_type, lr, min_lr, total_iters, warmup_iters_ratio=0.05, warmup_lr_ratio=0.1,
117
- no_aug_iter_ratio=0.05, step_num=10):
118
- # 根据学习率衰减类型(lr_decay_type)选择不同的学习率调度方式
119
- def yolox_warm_cos_lr(lr, min_lr, total_iters, warmup_total_iters, warmup_lr_start, no_aug_iter, iters):
120
- """
121
- YOLOX特定的余弦衰减带热身策略的学习率调度函数。
122
- 根据当前的训练迭代次数 (iters) 来动态调整学习率。
123
-
124
- 参数:
125
- lr: 当前的学习率
126
- min_lr: 最小学习率
127
- total_iters: 总的训练迭代次数
128
- warmup_total_iters: 热身阶段的迭代次数
129
- warmup_lr_start: 热身阶段开始时的学习率
130
- no_aug_iter: 不进行数据增强的迭代次数
131
- iters: 当前的训练迭代次数
132
-
133
- 返回:
134
- 调整后的学习率
135
- """
136
- if iters <= warmup_total_iters:
137
- # 在热身阶段,使用一个二次函数来逐步增加学习率
138
- lr = (lr - warmup_lr_start) * pow(iters / float(warmup_total_iters), 2) + warmup_lr_start
139
- elif iters >= total_iters - no_aug_iter:
140
- # 如果迭代数接近总迭代数,且接近不使用数据增强的迭代次数,则将学习率设置为最小学习率
141
- lr = min_lr
142
- else:
143
- # 在其他阶段,使用余弦衰减函数来调整学习率
144
- lr = min_lr + 0.5 * (lr - min_lr) * (
145
- 1.0 + math.cos(
146
- math.pi * (iters - warmup_total_iters) / (total_iters - warmup_total_iters - no_aug_iter))
147
- )
148
- return lr
149
-
150
- def step_lr(lr, decay_rate, step_size, iters):
151
- """
152
- 逐步衰减的学习率调度函数。
153
-
154
- 参数:
155
- lr: 当前的学习率
156
- decay_rate: 衰减率,用来控制每个step后学习率的减少比例
157
- step_size: 每次衰减发生的步数
158
- iters: 当前的训练迭代次数
159
-
160
- 返回:
161
- 调整后的学习率
162
- """
163
- if step_size < 1:
164
- raise ValueError("step_size must above 1.")
165
- # 计算经过了多少个衰减步
166
- n = iters // step_size
167
- # 按照衰减率调整学习率
168
- out_lr = lr * decay_rate ** n
169
- return out_lr
170
-
171
- # 根据学习率衰减类型选择合适的调度函数
172
- if lr_decay_type == "cos":
173
- # 如果选择的是余弦衰减(cos),计算热身阶段的迭代次数和学习率
174
- warmup_total_iters = min(max(warmup_iters_ratio * total_iters, 1), 3)
175
- warmup_lr_start = max(warmup_lr_ratio * lr, 1e-6)
176
- no_aug_iter = min(max(no_aug_iter_ratio * total_iters, 1), 15)
177
- # 使用partial函数将固定参数传给yolox_warm_cos_lr
178
- func = partial(yolox_warm_cos_lr, lr, min_lr, total_iters, warmup_total_iters, warmup_lr_start, no_aug_iter)
179
- else:
180
- # 否则,使用逐步衰减的策略
181
- # 计算每次衰减的速率
182
- decay_rate = (min_lr / lr) ** (1 / (step_num - 1))
183
- # 计算每步的衰减步长
184
- step_size = total_iters / step_num
185
- # 使用partial函数将固定参数传给step_lr
186
- func = partial(step_lr, lr, decay_rate, step_size)
187
-
188
- # 返回选择好的学习率调度函数
189
- return func
190
-
191
-
192
- def set_optimizer_lr(optimizer, lr_scheduler_func, epoch):
193
- # 使用学习率调度函数 lr_scheduler_func,根据当前的 epoch 获取对应的学习率
194
- lr = lr_scheduler_func(epoch)
195
-
196
- # 遍历优化器的所有参数组(optimizer.param_groups 是一个包含所有参数组的列表)
197
- for param_group in optimizer.param_groups:
198
- # 将当前参数组的学习率(lr)更新为调度函数计算得到的学习率
199
- param_group['lr'] = lr