初始化换发型项目:3个微服务代码 + 部署脚本
包含: - hair_service_sd: 换发型/换发色算法服务 (端口 8801) - photo_service: LoRA 训练调度服务 (端口 32678) - stable-diffusion-webui: SD WebUI 推理服务 (端口 57860) - kohya_ss_home: 训练环境代码 - meidaojia: 监控测试脚本 - setup.sh: 一键部署脚本 (conda环境恢复 + 配置生成 + 完整性检查) - start_all_services.sh: 启动3个服务 - configure.ini.template: 路径模板化 (BASE_DIR自动推导) - conda_envs/py310.yml: py310 环境定义 大文件 (weights/, models/, data/, conda_envs/*.tar.gz 等) 通过 .gitignore 排除, 由网盘单独上传。
This commit is contained in:
@@ -0,0 +1,268 @@
|
||||
import math
|
||||
import utils.weight_init as weight_init
|
||||
import torch
|
||||
import torch.nn.functional as F
|
||||
from torch import nn
|
||||
|
||||
from bodyseg.backbone.backbone import Backbone, get_norm, Conv2d
|
||||
from bodyseg.backbone.resnet import build_resnet_backbone
|
||||
|
||||
class FPN(Backbone):
|
||||
"""
|
||||
This module implements Feature Pyramid Network.
|
||||
It creates pyramid features built on top of some input feature maps.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, bottom_up, in_features, out_channels, norm="", top_block=None, fuse_type="sum"
|
||||
):
|
||||
"""
|
||||
Args:
|
||||
bottom_up (Backbone): module representing the bottom up subnetwork.
|
||||
Must be a subclass of :class:`Backbone`. The multi-scale feature
|
||||
maps generated by the bottom up network, and listed in `in_features`,
|
||||
are used to generate FPN levels.
|
||||
in_features (list[str]): names of the input feature maps coming
|
||||
from the backbone to which FPN is attached. For example, if the
|
||||
backbone produces ["res2", "res3", "res4"], any *contiguous* sublist
|
||||
of these may be used; order must be from high to low resolution.
|
||||
out_channels (int): number of channels in the output feature maps.
|
||||
norm (str): the normalization to use.
|
||||
top_block (nn.Module or None): if provided, an extra operation will
|
||||
be performed on the output of the last (smallest resolution)
|
||||
FPN output, and the result will extend the result list. The top_block
|
||||
further downsamples the feature map. It must have an attribute
|
||||
"num_levels", meaning the number of extra FPN levels added by
|
||||
this block, and "in_feature", which is a string representing
|
||||
its input feature (e.g., p5).
|
||||
fuse_type (str): types for fusing the top down features and the lateral
|
||||
ones. It can be "sum" (default), which sums up element-wise; or "avg",
|
||||
which takes the element-wise mean of the two.
|
||||
"""
|
||||
super(FPN, self).__init__()
|
||||
assert isinstance(bottom_up, Backbone)
|
||||
|
||||
# Feature map strides and channels from the bottom up network (e.g. ResNet)
|
||||
in_strides = [bottom_up._out_feature_strides[f] for f in in_features]
|
||||
in_channels = [bottom_up._out_feature_channels[f] for f in in_features]
|
||||
|
||||
_assert_strides_are_log2_contiguous(in_strides)
|
||||
lateral_convs = []
|
||||
output_convs = []
|
||||
|
||||
use_bias = norm == ""
|
||||
for idx, in_channels in enumerate(in_channels):
|
||||
lateral_norm = get_norm(norm, out_channels)
|
||||
output_norm = get_norm(norm, out_channels)
|
||||
|
||||
lateral_conv = Conv2d(
|
||||
in_channels, out_channels, kernel_size=1, bias=use_bias, norm=lateral_norm
|
||||
)
|
||||
output_conv = Conv2d(
|
||||
out_channels,
|
||||
out_channels,
|
||||
kernel_size=3,
|
||||
stride=1,
|
||||
padding=1,
|
||||
bias=use_bias,
|
||||
norm=output_norm,
|
||||
)
|
||||
weight_init.c2_xavier_fill(lateral_conv)
|
||||
weight_init.c2_xavier_fill(output_conv)
|
||||
stage = int(math.log2(in_strides[idx]))
|
||||
|
||||
lateral_convs.append(lateral_conv)
|
||||
output_convs.append(output_conv)
|
||||
# Place convs into top-down order (from low to high resolution)
|
||||
# to make the top-down computation in forward clearer.
|
||||
self.lateral_convs = nn.ModuleList(lateral_convs[::-1])
|
||||
self.output_convs = nn.ModuleList(output_convs[::-1])
|
||||
self.top_block = top_block
|
||||
self.in_features = in_features
|
||||
self.bottom_up = bottom_up
|
||||
# Return feature names are "p<stage>", like ["p2", "p3", ..., "p6"]
|
||||
self._out_feature_strides = {"p{}".format(int(math.log2(s))): s for s in in_strides}
|
||||
# top block output feature maps.
|
||||
if self.top_block is not None:
|
||||
for s in range(stage, stage + self.top_block.num_levels):
|
||||
self._out_feature_strides["p{}".format(s + 1)] = 2 ** (s + 1)
|
||||
|
||||
self._out_features = list(self._out_feature_strides.keys())
|
||||
self._out_feature_channels = {k: out_channels for k in self._out_features}
|
||||
assert fuse_type in {"avg", "sum"}
|
||||
self._fuse_type = fuse_type
|
||||
|
||||
def forward(self, x):
|
||||
"""
|
||||
Args:
|
||||
input (dict[str: Tensor]): mapping feature map name (e.g., "res5") to
|
||||
feature map tensor for each feature level in high to low resolution order.
|
||||
|
||||
Returns:
|
||||
dict[str: Tensor]:
|
||||
mapping from feature map name to FPN feature map tensor
|
||||
in high to low resolution order. Returned feature names follow the FPN
|
||||
paper convention: "p<stage>", where stage has stride = 2 ** stage e.g.,
|
||||
["p2", "p3", ..., "p6"].
|
||||
"""
|
||||
# Reverse feature maps into top-down order (from low to high resolution)
|
||||
bottom_up_features = self.bottom_up(x)
|
||||
x = [bottom_up_features[f] for f in self.in_features[::-1]]
|
||||
results = []
|
||||
prev_features = self.lateral_convs[0](x[0])
|
||||
results.append(self.output_convs[0](prev_features))
|
||||
for features, lateral_conv, output_conv in zip(
|
||||
x[1:], self.lateral_convs[1:], self.output_convs[1:]
|
||||
):
|
||||
top_down_features = F.interpolate(prev_features, scale_factor=2, mode="nearest")
|
||||
lateral_features = lateral_conv(features)
|
||||
prev_features = lateral_features + top_down_features
|
||||
if self._fuse_type == "avg":
|
||||
prev_features /= 2
|
||||
results.insert(0, output_conv(prev_features))
|
||||
|
||||
if self.top_block is not None:
|
||||
top_block_in_feature = bottom_up_features.get(self.top_block.in_feature, None)
|
||||
if top_block_in_feature is None:
|
||||
top_block_in_feature = results[self._out_features.index(self.top_block.in_feature)]
|
||||
results.extend(self.top_block(top_block_in_feature))
|
||||
assert len(self._out_features) == len(results)
|
||||
return dict(zip(self._out_features, results))
|
||||
|
||||
def _assert_strides_are_log2_contiguous(strides):
|
||||
"""
|
||||
Assert that each stride is 2x times its preceding stride, i.e. "contiguous in log2".
|
||||
"""
|
||||
for i, stride in enumerate(strides[1:], 1):
|
||||
assert stride == 2 * strides[i - 1], "Strides {} {} are not log2 contiguous".format(
|
||||
stride, strides[i - 1]
|
||||
)
|
||||
|
||||
|
||||
class LastLevelMaxPool(nn.Module):
|
||||
"""
|
||||
This module is used in the original FPN to generate a downsampled
|
||||
P6 feature from P5.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.num_levels = 1
|
||||
self.in_feature = "p5"
|
||||
|
||||
def forward(self, x):
|
||||
return [F.max_pool2d(x, kernel_size=1, stride=2, padding=0)]
|
||||
|
||||
|
||||
class LastLevelP6P7(nn.Module):
|
||||
"""
|
||||
This module is used in RetinaNet to generate extra layers, P6 and P7 from
|
||||
C5 feature.
|
||||
"""
|
||||
|
||||
def __init__(self, in_channels, out_channels):
|
||||
super().__init__()
|
||||
self.num_levels = 2
|
||||
self.in_feature = "res5"
|
||||
self.p6 = nn.Conv2d(in_channels, out_channels, 3, 2, 1)
|
||||
self.p7 = nn.Conv2d(out_channels, out_channels, 3, 2, 1)
|
||||
for module in [self.p6, self.p7]:
|
||||
weight_init.c2_xavier_fill(module)
|
||||
|
||||
def forward(self, c5):
|
||||
p6 = self.p6(c5)
|
||||
p7 = self.p7(F.relu(p6))
|
||||
return [p6, p7]
|
||||
|
||||
|
||||
def build_resnet_fpn_backbone(in_channels=3):
|
||||
"""
|
||||
Args:
|
||||
cfg: a detectron2 CfgNode
|
||||
|
||||
Returns:
|
||||
backbone (Backbone): backbone module, must be a subclass of :class:`Backbone`.
|
||||
"""
|
||||
bottom_up = build_resnet_backbone(in_channels)
|
||||
in_features = ["res2", "res3", "res4"]
|
||||
out_channels = 256
|
||||
backbone = FPN(
|
||||
bottom_up=bottom_up,
|
||||
in_features=in_features,
|
||||
out_channels=out_channels,
|
||||
norm="BN",
|
||||
# top_block=LastLevelMaxPool(),
|
||||
top_block=None,
|
||||
fuse_type="sum",
|
||||
)
|
||||
return backbone
|
||||
|
||||
def build_retinanet_resnet_fpn_backbone(cfg, in_channels=3):
|
||||
"""
|
||||
Args:
|
||||
cfg: a detectron2 CfgNode
|
||||
|
||||
Returns:
|
||||
backbone (Backbone): backbone module, must be a subclass of :class:`Backbone`.
|
||||
"""
|
||||
bottom_up = build_resnet_backbone(cfg, in_channels)
|
||||
in_features = cfg.MODEL.FPN.IN_FEATURES
|
||||
out_channels = cfg.MODEL.FPN.OUT_CHANNELS
|
||||
in_channels_p6p7 = bottom_up._out_feature_channels["res5"]
|
||||
backbone = FPN(
|
||||
bottom_up=bottom_up,
|
||||
in_features=in_features,
|
||||
out_channels=out_channels,
|
||||
norm=cfg.MODEL.FPN.NORM,
|
||||
top_block=LastLevelP6P7(in_channels_p6p7, out_channels),
|
||||
fuse_type=cfg.MODEL.FPN.FUSE_TYPE,
|
||||
)
|
||||
return backbone
|
||||
|
||||
if __name__ == "__main__":
|
||||
import argparse
|
||||
from config.default import get_cfg
|
||||
|
||||
def setup(args):
|
||||
"""
|
||||
Create configs and perform basic setups.
|
||||
"""
|
||||
cfg = get_cfg()
|
||||
cfg.merge_from_file(args.cfg)
|
||||
cfg.merge_from_list(args.opts)
|
||||
cfg.freeze()
|
||||
return cfg
|
||||
|
||||
parser = argparse.ArgumentParser(description='Train ImageNet network')
|
||||
# general
|
||||
parser.add_argument('--cfg',
|
||||
help='experiment configure file name',
|
||||
required=True,
|
||||
type=str)
|
||||
|
||||
parser.add_argument('opts',
|
||||
help="Modify config options using the command-line",
|
||||
default=None,
|
||||
nargs=argparse.REMAINDER)
|
||||
|
||||
args = parser.parse_args()
|
||||
cfg = setup(args)
|
||||
print(cfg)
|
||||
|
||||
model = build_resnet_fpn_backbone(cfg, 3)
|
||||
# model = build_retinanet_resnet_fpn_backbone(cfg, 3)
|
||||
print(model)
|
||||
# model = torch.nn.DataParallel(model, list(range(2))).cuda()
|
||||
dummy_input = torch.randn(4, 3, 512, 512)
|
||||
|
||||
out = model(dummy_input)
|
||||
|
||||
for k, v in out.items():
|
||||
print(k, v.shape)
|
||||
|
||||
# torch.onnx.export(model, dummy_input, "tmp.onnx", verbose=True,
|
||||
# input_names=['input'],
|
||||
# output_names=['output'])
|
||||
|
||||
pass
|
||||
|
||||
Reference in New Issue
Block a user