File size: 5,601 Bytes
7902c8d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
"""
models/cnn.py
CNN pour images RGB 3 canaux β€” Intel Image Classification (228Γ—228, 6 classes).

RÈGLE DE NORMALISATION :
    La normalisation est faite UNIQUEMENT dans utils/prep.py (pipeline de donnΓ©es).
    Les modèles reçoivent des images déjà normalisées — il n'y a PAS de couche
    Rescaling à l'intérieur des modèles. Cela garantit un comportement identique
    entre training, evaluation et production (Flask).
"""

# ── PyTorch ───────────────────────────────────────────────────────────────────
import torch.nn as nn
import torch.nn.functional as F


class CNN_Torch(nn.Module):
    """
    CNN PyTorch 4 blocs pour images RGB (3 canaux, 150Γ—150).
    EntrΓ©e  : (B, 3, 150, 150) β€” normalisΓ©e ImageNet (mean/std)
    Sortie  : (B, num_classes) β€” logits bruts (CrossEntropyLoss)

    Architecture :
        Block 1 : Conv(3β†’32)Γ—2   + BN + ReLU + MaxPool(2)            150β†’75
        Block 2 : Conv(32β†’64)Γ—2  + BN + ReLU + MaxPool(2) + Drop2d   75β†’37
        Block 3 : Conv(64β†’128)Γ—2 + BN + ReLU + MaxPool(2) + Drop2d   37β†’18
        Block 4 : Conv(128β†’256)Γ—2+ BN + ReLU + MaxPool(2) + Drop2d   18β†’9
        GAP     : AdaptiveAvgPool2d(1)                                  β†’(B,256)
        Head    : Linear(256β†’256) + ReLU + Dropout + Linear(256β†’C)
    """
    def __init__(self, num_classes: int = 6):
        super().__init__()

        self.features = nn.Sequential(
            # Block 1 β€” 150Γ—150 β†’ 75Γ—75
            nn.Conv2d(3, 32, kernel_size=3, padding=1, bias=False),
            nn.BatchNorm2d(32), nn.ReLU(inplace=True),
            nn.Conv2d(32, 32, kernel_size=3, padding=1, bias=False),
            nn.BatchNorm2d(32), nn.ReLU(inplace=True),
            nn.MaxPool2d(2),

            # Block 2 β€” 75Γ—75 β†’ 37Γ—37
            nn.Conv2d(32, 64, kernel_size=3, padding=1, bias=False),
            nn.BatchNorm2d(64), nn.ReLU(inplace=True),
            nn.Conv2d(64, 64, kernel_size=3, padding=1, bias=False),
            nn.BatchNorm2d(64), nn.ReLU(inplace=True),
            nn.MaxPool2d(2), nn.Dropout2d(0.10),

            # Block 3 β€” 37Γ—37 β†’ 18Γ—18
            nn.Conv2d(64, 128, kernel_size=3, padding=1, bias=False),
            nn.BatchNorm2d(128), nn.ReLU(inplace=True),
            nn.Conv2d(128, 128, kernel_size=3, padding=1, bias=False),
            nn.BatchNorm2d(128), nn.ReLU(inplace=True),
            nn.MaxPool2d(2), nn.Dropout2d(0.15),

            # Block 4 β€” 18Γ—18 β†’ 9Γ—9
            nn.Conv2d(128, 256, kernel_size=3, padding=1, bias=False),
            nn.BatchNorm2d(256), nn.ReLU(inplace=True),
            nn.Conv2d(256, 256, kernel_size=3, padding=1, bias=False),
            nn.BatchNorm2d(256), nn.ReLU(inplace=True),
            nn.MaxPool2d(2), nn.Dropout2d(0.20),
        )

        # (B,256,9,9) β†’ (B,256,1,1) β†’ (B,256)
        self.gap = nn.AdaptiveAvgPool2d(1)

        self.classifier = nn.Sequential(
            nn.Flatten(),
            nn.Linear(256, 256),
            nn.ReLU(inplace=True),
            nn.Dropout(0.30),
            nn.Linear(256, num_classes),
        )

    def forward(self, x):
        return self.classifier(self.gap(self.features(x)))


# ── TensorFlow / Keras ────────────────────────────────────────────────────────
def build_cnn_tf(num_classes: int = 6, input_shape: tuple = (228, 228, 3)):
    """
    CNN TF reproduisant l'architecture du notebook de rΓ©fΓ©rence hassanraof.
    Source : https://www.kaggle.com/code/hassanraof/intel-image-classification

    EntrΓ©e  : (B, 228, 228, 3) β€” valeurs [0, 1] normalisΓ©es par prep.py
    Sortie  : (B, num_classes) β€” softmax

    Architecture (5 blocs conv) :
        Block 1 : Conv(32, 5Γ—5) β†’ ReLU β†’ MaxPool(2,2)
        Block 2 : Conv(32, 5Γ—5) β†’ ReLU β†’ MaxPool(2,2)
        Block 3 : Conv(32, 3Γ—3) β†’ ReLU β†’ MaxPool(2,2)
        Block 4 : Conv(64, 3Γ—3) β†’ ReLU β†’ MaxPool(2,2)
        Block 5 : Conv(64, 3Γ—3) β†’ ReLU β†’ MaxPool(2,2)
        Head    : Flatten β†’ Dense(1024) β†’ Dropout(0.20)
                           β†’ Dense(124)  β†’ Dropout(0.20)
                           β†’ Dense(num_classes, softmax)

    ⚠️  PAS de couche Rescaling ici β€” la normalisation est faite dans prep.py.
        Ajouter Rescaling ici causerait une double normalisation.
    """
    from tensorflow.keras import layers, models

    return models.Sequential([
        layers.Input(shape=input_shape),
        # ← PAS de Rescaling ici

        # Block 1
        layers.Conv2D(32, kernel_size=(5, 5), activation="relu"),
        layers.MaxPooling2D(2, 2),

        # Block 2
        layers.Conv2D(32, kernel_size=(5, 5), activation="relu"),
        layers.MaxPooling2D(2, 2),

        # Block 3
        layers.Conv2D(32, kernel_size=(3, 3), activation="relu"),
        layers.MaxPooling2D(2, 2),

        # Block 4
        layers.Conv2D(64, kernel_size=(3, 3), activation="relu"),
        layers.MaxPooling2D(2, 2),

        # Block 5
        layers.Conv2D(64, kernel_size=(3, 3), activation="relu"),
        layers.MaxPooling2D(2, 2),

        # Head
        layers.Flatten(),
        layers.Dense(1024, activation="relu"),
        layers.Dropout(0.20),
        layers.Dense(124, activation="relu"),
        layers.Dropout(0.20),
        layers.Dense(num_classes, activation="softmax"),

    ], name="CNN_TF_hassanraof")