constructelligence commited on
Commit
55b2f87
·
verified ·
1 Parent(s): 6dda0cf

Upload models.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. models.py +130 -0
models.py ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Model factory: shared semantic + drywall-material heads over several backbones.
2
+
3
+ The original prototype hard-coded MobileNetV3-Large/DeepLabV3. That is small and
4
+ fast but well behind modern open segmentation models. This module keeps the
5
+ two-head design (semantic coverage + independent drywall substrate) but lets you
6
+ choose the encoder:
7
+
8
+ * ``deeplabv3_mobilenet_v3_large`` - the original; edge/latency oriented.
9
+ * ``deeplabv3_resnet50`` / ``deeplabv3_resnet101`` - stronger torchvision
10
+ baselines (ASPP + ResNet), no extra dependencies.
11
+ * ``segformer_b0`` .. ``segformer_b5`` - modern transformer segmentation from
12
+ Hugging Face (requires ``transformers``); the recommended accuracy option.
13
+
14
+ All variants return ``{"semantic": (B, C, H, W), "drywall": (B, 2, H, W)}`` at
15
+ input resolution, so ``train.py`` and ``predict.py`` are architecture-agnostic.
16
+ Normalisation is shared (ImageNet mean/std), which SegFormer also expects.
17
+ """
18
+ import torch
19
+ from torch import nn
20
+ from torch.nn import functional as F
21
+ from torchvision.models import MobileNet_V3_Large_Weights, ResNet50_Weights, ResNet101_Weights
22
+ from torchvision.models.segmentation import (
23
+ deeplabv3_mobilenet_v3_large,
24
+ deeplabv3_resnet50,
25
+ deeplabv3_resnet101,
26
+ )
27
+
28
+ # torchvision DeepLabV3 variants: name -> (builder, pretrained backbone weights).
29
+ DEEPLAB_ARCHITECTURES = {
30
+ "deeplabv3_mobilenet_v3_large": (deeplabv3_mobilenet_v3_large, MobileNet_V3_Large_Weights.DEFAULT),
31
+ "deeplabv3_resnet50": (deeplabv3_resnet50, ResNet50_Weights.DEFAULT),
32
+ "deeplabv3_resnet101": (deeplabv3_resnet101, ResNet101_Weights.DEFAULT),
33
+ }
34
+ # SegFormer variants: name -> Hugging Face id of a pretrained encoder/decoder.
35
+ SEGFORMER_ARCHITECTURES = {
36
+ "segformer_b0": "nvidia/segformer-b0-finetuned-ade-512-512",
37
+ "segformer_b1": "nvidia/segformer-b1-finetuned-ade-512-512",
38
+ "segformer_b2": "nvidia/segformer-b2-finetuned-ade-512-512",
39
+ "segformer_b3": "nvidia/segformer-b3-finetuned-ade-512-512",
40
+ "segformer_b4": "nvidia/segformer-b4-finetuned-ade-512-512",
41
+ "segformer_b5": "nvidia/segformer-b5-finetuned-ade-512-512",
42
+ }
43
+ DEFAULT_ARCH = "deeplabv3_mobilenet_v3_large"
44
+
45
+
46
+ def available_architectures():
47
+ return sorted(list(DEEPLAB_ARCHITECTURES) + list(SEGFORMER_ARCHITECTURES))
48
+
49
+
50
+ def _drywall_head(channels, hidden=128, dropout=0.1):
51
+ return nn.Sequential(
52
+ nn.Conv2d(channels, hidden, kernel_size=3, padding=1, bias=False),
53
+ nn.BatchNorm2d(hidden),
54
+ nn.ReLU(inplace=True),
55
+ nn.Dropout2d(dropout),
56
+ nn.Conv2d(hidden, 2, kernel_size=1),
57
+ )
58
+
59
+
60
+ class WallPaintNet(nn.Module):
61
+ """torchvision DeepLabV3 semantic head plus an independent drywall head."""
62
+
63
+ def __init__(self, num_semantic_classes, pretrained_backbone=False, arch=DEFAULT_ARCH):
64
+ super().__init__()
65
+ if arch not in DEEPLAB_ARCHITECTURES:
66
+ raise ValueError(f"unknown DeepLab architecture {arch!r}; choose from {sorted(DEEPLAB_ARCHITECTURES)}")
67
+ builder, default_weights = DEEPLAB_ARCHITECTURES[arch]
68
+ weights = default_weights if pretrained_backbone else None
69
+ self.arch = arch
70
+ self.segmenter = builder(weights=None, weights_backbone=weights, num_classes=num_semantic_classes)
71
+ was_training = self.segmenter.training
72
+ self.segmenter.eval()
73
+ with torch.inference_mode():
74
+ features = self.segmenter.backbone(torch.zeros(1, 3, 128, 128))["out"]
75
+ self.segmenter.train(was_training)
76
+ self.drywall_head = _drywall_head(features.shape[1])
77
+
78
+ def forward(self, x):
79
+ size = x.shape[-2:]
80
+ features = self.segmenter.backbone(x)
81
+ semantic = self.segmenter.classifier(features["out"])
82
+ drywall = self.drywall_head(features["out"])
83
+ return {
84
+ "semantic": F.interpolate(semantic, size=size, mode="bilinear", align_corners=False),
85
+ "drywall": F.interpolate(drywall, size=size, mode="bilinear", align_corners=False),
86
+ }
87
+
88
+
89
+ class SegformerPaintNet(nn.Module):
90
+ """Hugging Face SegFormer semantic head plus an independent drywall head.
91
+
92
+ SegFormer predicts logits at 1/4 resolution; the last encoder hidden state
93
+ feeds the drywall head. Heavy enough to matter, but a genuine modern
94
+ transformer backbone rather than a 2021 MobileNet.
95
+ """
96
+
97
+ def __init__(self, num_semantic_classes, pretrained_backbone=False, arch="segformer_b2"):
98
+ super().__init__()
99
+ if arch not in SEGFORMER_ARCHITECTURES:
100
+ raise ValueError(f"unknown SegFormer architecture {arch!r}; choose from {sorted(SEGFORMER_ARCHITECTURES)}")
101
+ try:
102
+ from transformers import SegformerConfig, SegformerForSemanticSegmentation
103
+ except ImportError as exc: # pragma: no cover - optional dependency
104
+ raise ImportError("SegFormer architectures require `pip install transformers`") from exc
105
+ hf_id = SEGFORMER_ARCHITECTURES[arch]
106
+ self.arch = arch
107
+ if pretrained_backbone:
108
+ self.segmenter = SegformerForSemanticSegmentation.from_pretrained(
109
+ hf_id, num_labels=num_semantic_classes, ignore_mismatched_sizes=True)
110
+ else:
111
+ config = SegformerConfig.from_pretrained(hf_id, num_labels=num_semantic_classes)
112
+ self.segmenter = SegformerForSemanticSegmentation(config)
113
+ self.drywall_head = _drywall_head(self.segmenter.config.hidden_sizes[-1])
114
+
115
+ def forward(self, x):
116
+ size = x.shape[-2:]
117
+ outputs = self.segmenter(pixel_values=x, output_hidden_states=True)
118
+ semantic = F.interpolate(outputs.logits, size=size, mode="bilinear", align_corners=False)
119
+ drywall = self.drywall_head(outputs.hidden_states[-1])
120
+ drywall = F.interpolate(drywall, size=size, mode="bilinear", align_corners=False)
121
+ return {"semantic": semantic, "drywall": drywall}
122
+
123
+
124
+ def build_model(arch, num_semantic_classes, pretrained_backbone=False):
125
+ """Construct the dual-head model for ``arch`` (see :func:`available_architectures`)."""
126
+ if arch in DEEPLAB_ARCHITECTURES:
127
+ return WallPaintNet(num_semantic_classes, pretrained_backbone, arch)
128
+ if arch in SEGFORMER_ARCHITECTURES:
129
+ return SegformerPaintNet(num_semantic_classes, pretrained_backbone, arch)
130
+ raise ValueError(f"unknown architecture {arch!r}; choose from {available_architectures()}")