toderian commited on
Commit
c111ce1
·
verified ·
1 Parent(s): 5cf3cc7

Add model.py

Browse files
Files changed (1) hide show
  1. model.py +177 -0
model.py ADDED
@@ -0,0 +1,177 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Autism Detector Model
3
+
4
+ A feedforward neural network for ASD risk classification
5
+ from structured clinical data.
6
+ """
7
+
8
+ import torch
9
+ import torch.nn as nn
10
+
11
+
12
+ class AutismDetector(nn.Module):
13
+ """
14
+ Binary classifier for autism spectrum disorder screening.
15
+
16
+ Input: 8 preprocessed clinical features
17
+ Output: 2 logits (Healthy, ASD)
18
+
19
+ Features (in order):
20
+ 1. developmental_milestones - N/G/M/C (encoded 0-3)
21
+ 2. iq_dq - numeric, normalized 0-1
22
+ 3. intellectual_disability - N/F70.0/F71/F72 (encoded 0-3)
23
+ 4. language_disorder - N/Y (encoded 0-1)
24
+ 5. language_development - N/delay/A (encoded 0-2)
25
+ 6. dysmorphism - NO/Y (encoded 0-1)
26
+ 7. behaviour_disorder - N/Y (encoded 0-1)
27
+ 8. neurological_exam - N/abnormal (encoded 0-1)
28
+ """
29
+
30
+ def __init__(self, input_size=8, hidden_sizes=None, num_classes=2, dropout=0.3):
31
+ super().__init__()
32
+
33
+ if hidden_sizes is None:
34
+ hidden_sizes = [64, 32]
35
+
36
+ layers = []
37
+ prev_size = input_size
38
+
39
+ for hidden_size in hidden_sizes:
40
+ layers.extend([
41
+ nn.Linear(prev_size, hidden_size),
42
+ nn.ReLU(),
43
+ nn.Dropout(dropout),
44
+ ])
45
+ prev_size = hidden_size
46
+
47
+ layers.append(nn.Linear(prev_size, num_classes))
48
+ self.classifier = nn.Sequential(*layers)
49
+
50
+ # Store config
51
+ self.input_size = input_size
52
+ self.hidden_sizes = hidden_sizes
53
+ self.num_classes = num_classes
54
+ self.dropout = dropout
55
+
56
+ def forward(self, x):
57
+ """
58
+ Forward pass.
59
+
60
+ Parameters
61
+ ----------
62
+ x : torch.Tensor
63
+ Input tensor of shape (batch_size, 8)
64
+
65
+ Returns
66
+ -------
67
+ torch.Tensor
68
+ Output logits of shape (batch_size, num_classes)
69
+ """
70
+ return self.classifier(x)
71
+
72
+ def predict(self, x):
73
+ """
74
+ Make predictions with probabilities.
75
+
76
+ Parameters
77
+ ----------
78
+ x : torch.Tensor
79
+ Input tensor of shape (batch_size, 8)
80
+
81
+ Returns
82
+ -------
83
+ dict with 'prediction', 'probability', 'logits'
84
+ """
85
+ self.eval()
86
+ with torch.no_grad():
87
+ logits = self.forward(x)
88
+ probs = torch.softmax(logits, dim=-1)
89
+ pred_class = torch.argmax(probs, dim=-1)
90
+
91
+ return {
92
+ 'prediction': pred_class,
93
+ 'probabilities': probs,
94
+ 'logits': logits
95
+ }
96
+
97
+
98
+ def load_model(model_path, device='cpu'):
99
+ """Load TorchScript model."""
100
+ model = torch.jit.load(model_path, map_location=device)
101
+ model.eval()
102
+ return model
103
+
104
+
105
+ def preprocess(data, config):
106
+ """
107
+ Preprocess input data using JSON config.
108
+
109
+ Parameters
110
+ ----------
111
+ data : dict
112
+ Input features as dictionary
113
+ config : dict
114
+ Preprocessor configuration from preprocessor_config.json
115
+
116
+ Returns
117
+ -------
118
+ torch.Tensor
119
+ Preprocessed features tensor of shape (1, 8)
120
+ """
121
+ features = []
122
+
123
+ for feature_name in config["feature_order"]:
124
+ if feature_name in config["categorical_features"]:
125
+ feat_config = config["categorical_features"][feature_name]
126
+
127
+ if feat_config["type"] == "text_binary":
128
+ # For neurological_exam: N -> 0, anything else -> 1
129
+ raw_value = str(data[feature_name]).strip().upper()
130
+ value = 0 if raw_value == feat_config["normal_value"] else 1
131
+ else:
132
+ # Standard categorical/binary mapping
133
+ raw_value = data[feature_name]
134
+ value = feat_config["mapping"].get(raw_value, 0)
135
+
136
+ elif feature_name in config["numeric_features"]:
137
+ feat_config = config["numeric_features"][feature_name]
138
+ raw = float(data[feature_name])
139
+ # Min-max normalization
140
+ value = (raw - feat_config["min"]) / (feat_config["max"] - feat_config["min"])
141
+ value = max(0, min(1, value)) # Clamp to [0, 1]
142
+
143
+ features.append(value)
144
+
145
+ return torch.tensor([features], dtype=torch.float32)
146
+
147
+
148
+ def get_risk_level(probability):
149
+ """
150
+ Get risk level from ASD probability.
151
+
152
+ Returns
153
+ -------
154
+ str: 'low', 'medium', or 'high'
155
+ """
156
+ if probability < 0.4:
157
+ return "low"
158
+ elif probability < 0.7:
159
+ return "medium"
160
+ else:
161
+ return "high"
162
+
163
+
164
+ if __name__ == '__main__':
165
+ # Test model creation
166
+ model = AutismDetector()
167
+ print(f"Model architecture:\n{model}")
168
+
169
+ # Test forward pass
170
+ x = torch.randn(2, 8)
171
+ output = model(x)
172
+ print(f"\nInput shape: {x.shape}")
173
+ print(f"Output shape: {output.shape}")
174
+ print(f"Output (logits): {output}")
175
+
176
+ probs = torch.softmax(output, dim=-1)
177
+ print(f"Probabilities: {probs}")