Tin Theethawat Savastham commited on
Commit
d042c14
·
1 Parent(s): a84194a

✨ Update Jupyter Notebook Visualization

Browse files
README.md CHANGED
@@ -85,6 +85,10 @@ We also provide the experimented scenario to choose and test which boolean param
85
 
86
  The script will load the data from the defined directory and use them to perform the experiment.
87
 
 
 
 
 
88
  ---
89
 
90
  © 2024, Prince of Songkla University under the Inteligent Automation Engineering Center, Faculty of Engineering.
 
85
 
86
  The script will load the data from the defined directory and use them to perform the experiment.
87
 
88
+ ## Example
89
+
90
+ For the easier experiment, we provide the Jupyter Notebook file to construct the model and training. You can visit the [Basic Model Construction]('/example/1-Basic-Model-Constructor.ipynb') and if you visit this repo from Huggingface, you can click the button **Open in Collab** to re-run this experiment. Or if you open from GitHub, you can open in GitHub Codespace to run your own Jupyter Notebook.
91
+
92
  ---
93
 
94
  © 2024, Prince of Songkla University under the Inteligent Automation Engineering Center, Faculty of Engineering.
example/.gitignore CHANGED
@@ -1,4 +1,5 @@
1
  datasets/
2
  tdce-basic/
3
  results/
4
- result/
 
 
1
  datasets/
2
  tdce-basic/
3
  results/
4
+ result/
5
+ *.pkl
example/1-Basic-Model-Constructor.ipynb CHANGED
@@ -6,6 +6,7 @@
6
  "metadata": {},
7
  "source": [
8
  "# TDCE Learning Model Basic Model Construction\n",
 
9
  "This notebook display the basic construction of Time Driven Cost Estimation Learning Model step by step without using the experiment script (experiment_script.py)"
10
  ]
11
  },
@@ -51,8 +52,8 @@
51
  "metadata": {},
52
  "outputs": [],
53
  "source": [
54
- "run_from_online = False\n",
55
- "ignore_download_dataset = True\n",
56
  "\n",
57
  "\n",
58
  "if run_from_online:\n",
@@ -78,6 +79,12 @@
78
  " import result_display as rd\n",
79
  " import mini_plot as mp\n",
80
  " # fmt:on\n",
 
 
 
 
 
 
81
  "else :\n",
82
  " # fmt:off\n",
83
  " sys.path.append('../model')\n",
@@ -731,6 +738,8 @@
731
  "metadata": {},
732
  "source": [
733
  "## Visualization\n",
 
 
734
  "Display the model training behavior"
735
  ]
736
  },
@@ -744,6 +753,80 @@
744
  "mp.plotting_learning_curve(epoch_error=error_df,element_learning_rate=element_level_lr,\n",
745
  " model_learning_rate=model_level_lr)"
746
  ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
747
  }
748
  ],
749
  "metadata": {
 
6
  "metadata": {},
7
  "source": [
8
  "# TDCE Learning Model Basic Model Construction\n",
9
+ "\n",
10
  "This notebook display the basic construction of Time Driven Cost Estimation Learning Model step by step without using the experiment script (experiment_script.py)"
11
  ]
12
  },
 
52
  "metadata": {},
53
  "outputs": [],
54
  "source": [
55
+ "run_from_online = True\n",
56
+ "ignore_download_dataset = False\n",
57
  "\n",
58
  "\n",
59
  "if run_from_online:\n",
 
79
  " import result_display as rd\n",
80
  " import mini_plot as mp\n",
81
  " # fmt:on\n",
82
+ " try:\n",
83
+ " # For online execution, in Google Colab\n",
84
+ " !pip install tensor-sensor\n",
85
+ " except:\n",
86
+ " pass\n",
87
+ "\n",
88
  "else :\n",
89
  " # fmt:off\n",
90
  " sys.path.append('../model')\n",
 
738
  "metadata": {},
739
  "source": [
740
  "## Visualization\n",
741
+ "\n",
742
+ "### Error Behavior\n",
743
  "Display the model training behavior"
744
  ]
745
  },
 
753
  "mp.plotting_learning_curve(epoch_error=error_df,element_learning_rate=element_level_lr,\n",
754
  " model_learning_rate=model_level_lr)"
755
  ]
756
+ },
757
+ {
758
+ "cell_type": "markdown",
759
+ "id": "0c101da0",
760
+ "metadata": {},
761
+ "source": [
762
+ "### Weight Adjustment Behavior\n",
763
+ "Display the Weight of Each Model Element"
764
+ ]
765
+ },
766
+ {
767
+ "cell_type": "code",
768
+ "execution_count": null,
769
+ "id": "bbd09ca5",
770
+ "metadata": {},
771
+ "outputs": [],
772
+ "source": [
773
+ "importlib.reload(mp)\n",
774
+ "mp.plot_model_level_weight(adjustment_data=sample_payload_df,epoch_error=error_df)"
775
+ ]
776
+ },
777
+ {
778
+ "cell_type": "markdown",
779
+ "id": "3a99ea9c",
780
+ "metadata": {},
781
+ "source": [
782
+ "Display the weight of each material, labor, and utility cost object."
783
+ ]
784
+ },
785
+ {
786
+ "cell_type": "code",
787
+ "execution_count": null,
788
+ "id": "283c58fe",
789
+ "metadata": {},
790
+ "outputs": [],
791
+ "source": [
792
+ "material_columns = [col for col in sample_payload_df.columns if col.startswith('material_weight_')]\n",
793
+ "employee_columns = [col for col in sample_payload_df.columns if col.startswith('employee_weight_')]\n",
794
+ "capital_columns = [col for col in sample_payload_df.columns if col.startswith('capital_cost_weight_')]\n",
795
+ "\n",
796
+ "\n",
797
+ "importlib.reload(mp)\n",
798
+ "mp.plot_element_level_weight(\n",
799
+ " adjustment_data=sample_payload_df,\n",
800
+ " material_columns=material_columns,labor_columns= employee_columns,\n",
801
+ " utility_columns= capital_columns)"
802
+ ]
803
+ },
804
+ {
805
+ "cell_type": "markdown",
806
+ "id": "d6cc2707",
807
+ "metadata": {},
808
+ "source": [
809
+ "## Export Model\n",
810
+ "Export Model to keep and use in another place"
811
+ ]
812
+ },
813
+ {
814
+ "cell_type": "code",
815
+ "execution_count": null,
816
+ "id": "74b2f7e2",
817
+ "metadata": {},
818
+ "outputs": [],
819
+ "source": [
820
+ "tdce_model.export_model(\"tdce_model.pkl\")"
821
+ ]
822
+ },
823
+ {
824
+ "cell_type": "markdown",
825
+ "id": "dce151ec",
826
+ "metadata": {},
827
+ "source": [
828
+ "© 2025, Intelligent Automation Engineering Center, Prince of Songkla University"
829
+ ]
830
  }
831
  ],
832
  "metadata": {
functions/mini_plot.py CHANGED
@@ -1,6 +1,7 @@
1
  import pandas as pd
2
  import matplotlib.pyplot as plt
3
  import seaborn as sns
 
4
 
5
  sns.set_theme(style="whitegrid", font="Noto Sans",
6
  font_scale=1)
@@ -49,3 +50,113 @@ def plotting_learning_curve(epoch_error, element_learning_rate, model_learning_r
49
  ax[0].legend(loc="lower right")
50
  ax[1].legend(loc="lower right")
51
  fig.tight_layout(pad=3.0)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import pandas as pd
2
  import matplotlib.pyplot as plt
3
  import seaborn as sns
4
+ import numpy as np
5
 
6
  sns.set_theme(style="whitegrid", font="Noto Sans",
7
  font_scale=1)
 
50
  ax[0].legend(loc="lower right")
51
  ax[1].legend(loc="lower right")
52
  fig.tight_layout(pad=3.0)
53
+
54
+
55
+ def plot_model_level_weight(adjustment_data, epoch_error):
56
+ fig, ax = plt.subplots(1, 3, figsize=(14, 3))
57
+
58
+ ax[0].plot(
59
+ adjustment_data["model_weight_1"],
60
+ label="Material Element Weight",
61
+ )
62
+ ax[0].plot(
63
+ adjustment_data["model_weight_2"],
64
+ label="Labor Element Weight",
65
+ )
66
+ ax[0].plot(
67
+ adjustment_data["model_weight_3"],
68
+ label="Utiltiy Cost Element Weight",
69
+ )
70
+
71
+ best_train_epoch = epoch_error[
72
+ epoch_error["error_percent"]
73
+ == epoch_error["error_percent"].min()
74
+ ]
75
+ best_validate_epoch = epoch_error[
76
+ epoch_error["validate_error_percent"]
77
+ == epoch_error["validate_error_percent"].min()
78
+ ]
79
+ try:
80
+ best_train_epoch = best_train_epoch.iloc[0]
81
+ best_train_epoch = best_train_epoch["epoch"]
82
+ best_validate_epoch = best_validate_epoch.iloc[0]
83
+ best_validate_epoch = best_validate_epoch["epoch"]
84
+ except Exception as e:
85
+ best_train_epoch = np.nan
86
+ best_validate_epoch = np.nan
87
+
88
+ ax[0].set_xlabel(
89
+ f'Train Err {epoch_error.iloc[-1]["error_percent"]:.2f} , Validate Err {epoch_error.iloc[-1]["validate_error_percent"]:.2f} \n Best Epoch Train {best_train_epoch:.0f} , Validate {best_validate_epoch:.0f}'
90
+ )
91
+ ax[0].set_xticks([])
92
+ ax[0].legend(loc="lower right")
93
+ ax[0].set_title(
94
+ "All Model Level Weight",
95
+ fontdict={"fontsize": 12},
96
+ )
97
+
98
+ ax[1].plot(
99
+ adjustment_data["model_weight_1"],
100
+ label="Material Element Weight",
101
+ )
102
+ ax[1].legend(loc="lower right")
103
+ ax[1].set_title(
104
+ "Material Element Weight",
105
+ fontdict={"fontsize": 12},
106
+ )
107
+
108
+ ax[2].plot(
109
+ adjustment_data["model_weight_2"],
110
+ label="Labor Element Weight",
111
+ )
112
+ ax[2].plot(
113
+ adjustment_data["model_weight_3"],
114
+ label="Utiltiy Cost Element Weight",
115
+ )
116
+ ax[2].legend(loc="lower right")
117
+ ax[2].set_title(
118
+ "Labor and Utility Cost Element Weight",
119
+ fontdict={"fontsize": 12},
120
+ )
121
+
122
+
123
+ def plot_element_level_weight(adjustment_data, material_columns, labor_columns, utility_columns):
124
+
125
+ fig, ax = plt.subplots(1, 3, figsize=(14, 3))
126
+ for column in material_columns:
127
+ ax[0].plot(
128
+ adjustment_data[column],
129
+ label=column,
130
+ )
131
+
132
+ for column in labor_columns:
133
+ ax[1].plot(
134
+ adjustment_data[column],
135
+ label=column,
136
+ )
137
+
138
+ for column in utility_columns:
139
+ ax[2].plot(
140
+ adjustment_data[column],
141
+ label=column,
142
+ )
143
+
144
+ ax[0].legend(loc="lower right")
145
+ ax[2].legend(loc="lower right")
146
+
147
+ ax[0].set_title(
148
+ "Material Element Weight",
149
+ fontdict={"fontsize": 12},
150
+ )
151
+
152
+ ax[1].set_title(
153
+ "Labor Element Weight",
154
+ fontdict={"fontsize": 12},
155
+ )
156
+
157
+ ax[2].set_title(
158
+ "Utility Cost Element Weight",
159
+ fontdict={"fontsize": 12},
160
+ )
161
+
162
+ fig.tight_layout(pad=2.0)
model/matrix_normalization.py CHANGED
@@ -151,3 +151,150 @@ def normalize_payload(
151
  max_data,
152
  min_data,
153
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  max_data,
152
  min_data,
153
  )
154
+
155
+
156
+ def normalize_prediction_payload(
157
+ max_data,
158
+ min_data,
159
+ material_cost_matrix,
160
+ material_amount_matrix,
161
+ employee_cost_matrix,
162
+ employee_duration_matrix,
163
+ employee_day_amount_matrix,
164
+ capital_cost_matrix,
165
+ day_amount_matrix,
166
+ capital_cost_duration_matrix,
167
+ ):
168
+ # Normalized Material Matrix
169
+ max_arr, min_arr = norm.find_max_min(material_cost_matrix)
170
+ normalized_material_cost = norm.normalized(
171
+ material_cost_matrix, max_arr, min_arr)
172
+ validate_material_cost_matrix = validation_payload["validate_material_cost_matrix"]
173
+
174
+ # Normalize Validate Material Cost Matrix
175
+ normalized_validate_material_cost = norm.normalized_2d(
176
+ validate_material_cost_matrix, material_cost_matrix, max_arr, min_arr
177
+ )
178
+ max_data["material_cost"] = max_arr
179
+ min_data["material_cost"] = min_arr
180
+
181
+ # Normalized Material Amount Matrix
182
+ max_arr, min_arr = norm.find_max_min(material_amount_matrix)
183
+ normalized_material_amount = norm.normalized(
184
+ material_amount_matrix, max_arr, min_arr
185
+ )
186
+ validate_material_amount_matrix = validation_payload[
187
+ "validate_material_amount_matrix"
188
+ ]
189
+
190
+ if display_log:
191
+ print("normalized_material_amount", normalized_material_amount)
192
+ print("------------")
193
+ print("normalized_material_cost", normalized_material_cost)
194
+ print("------------")
195
+
196
+ normalized_validate_material_amount = norm.normalized_2d(
197
+ validate_material_amount_matrix, material_amount_matrix, max_arr, min_arr
198
+ )
199
+ max_data["material_amount"] = max_arr
200
+ min_data["material_amount"] = min_arr
201
+
202
+ # Normalized Employee Cost Matrix
203
+ max_arr, min_arr = norm.find_max_min(employee_cost_matrix)
204
+ normalized_employee_cost = norm.normalized(
205
+ employee_cost_matrix, max_arr, min_arr)
206
+ validate_employee_cost_matrix = validation_payload["validate_employee_cost_matrix"]
207
+ normalized_validate_employee_cost = norm.normalized(
208
+ validate_employee_cost_matrix, max_arr, min_arr
209
+ )
210
+ max_data["employee_cost"] = max_arr
211
+ min_data["employee_cost"] = min_arr
212
+
213
+ # Normalized Employee Duration Matrix
214
+ max_arr, min_arr = norm.find_max_min(employee_duration_matrix)
215
+ normalized_employee_duration = norm.normalized(
216
+ employee_duration_matrix, max_arr, min_arr
217
+ )
218
+ validate_employee_duration_matrix = validation_payload[
219
+ "validate_employee_duration_matrix"
220
+ ]
221
+ normalized_validate_employee_duration = norm.normalized(
222
+ validate_employee_duration_matrix,
223
+ max_arr,
224
+ min_arr,
225
+ )
226
+ max_data["employee_duration"] = max_arr
227
+ min_data["employee_duration"] = min_arr
228
+
229
+ # Normalized Employee Day Amount
230
+ max_arr, min_arr = norm.find_max_min(employee_day_amount_matrix)
231
+ normalized_employee_day_amount = norm.normalized(
232
+ employee_day_amount_matrix, max_arr, min_arr
233
+ )
234
+ validate_employee_day_amount_matrix = validation_payload[
235
+ "validate_employee_day_amount_matrix"
236
+ ]
237
+ normalized_validate_employee_day_amount = norm.normalized(
238
+ validate_employee_day_amount_matrix,
239
+ max_arr,
240
+ min_arr,
241
+ )
242
+ max_data["employee_day_amount"] = max_arr
243
+ min_data["employee_day_amount"] = min_arr
244
+
245
+ # Normalized Capital Cost Matrix
246
+ max_arr, min_arr = norm.find_max_min(capital_cost_matrix)
247
+ normalized_capital_cost = norm.normalized(
248
+ capital_cost_matrix, max_arr, min_arr)
249
+ validate_capital_cost_matrix = validation_payload["validate_capital_cost_matrix"]
250
+ normalized_validate_capital_cost = norm.normalized(
251
+ validate_capital_cost_matrix, max_arr, min_arr
252
+ )
253
+ max_data["capital_cost"] = max_arr
254
+ min_data["capital_cost"] = min_arr
255
+
256
+ # Normalized Day Amount Matrix
257
+ max_arr, min_arr = norm.find_max_min(day_amount_matrix)
258
+ normalized_day_amount = norm.normalized(
259
+ day_amount_matrix, max_arr, min_arr)
260
+ validate_day_amount_matrix = validation_payload["validate_day_amount_matrix"]
261
+ normalized_validate_day_amount = norm.normalized(
262
+ validate_day_amount_matrix, max_arr, min_arr
263
+ )
264
+ max_data["day_amount"] = max_arr
265
+ min_data["day_amount"] = min_arr
266
+
267
+ # Normalized Capital Cost Duration Matrix
268
+ validate_capital_duration_matrix = validation_payload[
269
+ "validate_capital_duration_matrix"
270
+ ]
271
+ max_arr, min_arr = norm.find_max_min(capital_cost_duration_matrix)
272
+ normalized_capital_cost_duration = norm.normalized(
273
+ capital_cost_duration_matrix, max_arr, min_arr
274
+ )
275
+ normalized_validate_capital_duration = norm.normalized(
276
+ validate_capital_duration_matrix, max_arr, min_arr
277
+ )
278
+ max_data["capital_cost_duration"] = max_arr
279
+ min_data["capital_cost_duration"] = min_arr
280
+
281
+ return (
282
+ normalized_material_cost,
283
+ normalized_material_amount,
284
+ normalized_validate_material_cost,
285
+ normalized_validate_material_amount,
286
+ normalized_employee_cost,
287
+ normalized_employee_duration,
288
+ normalized_employee_day_amount,
289
+ normalized_validate_employee_cost,
290
+ normalized_validate_employee_duration,
291
+ normalized_validate_employee_day_amount,
292
+ normalized_capital_cost,
293
+ normalized_capital_cost_duration,
294
+ normalized_day_amount,
295
+ normalized_validate_capital_cost,
296
+ normalized_validate_capital_duration,
297
+ normalized_validate_day_amount,
298
+ max_data,
299
+ min_data,
300
+ )
model/tdce_model.py CHANGED
@@ -3,6 +3,7 @@ import random
3
  import pandas as pd
4
  import importlib
5
  import time
 
6
 
7
  import material_network as mn
8
  import time_driven_network as tdn
@@ -539,3 +540,103 @@ class TDCEModel:
539
 
540
  def get_sample_payload(self):
541
  return self.sample_payload
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  import pandas as pd
4
  import importlib
5
  import time
6
+ import pickle
7
 
8
  import material_network as mn
9
  import time_driven_network as tdn
 
540
 
541
  def get_sample_payload(self):
542
  return self.sample_payload
543
+
544
+ def export_model(self, filename="tdce_model.pkl"):
545
+ model_data = {
546
+ "material_element": self.material_element,
547
+ "employee_element": self.employee_element,
548
+ "capital_cost_element": self.capital_cost_element,
549
+ "weights": self.weights,
550
+ "bias": self.bias,
551
+ "loss": self.loss,
552
+ "loss_prime": self.loss_prime,
553
+ "loss_percent": self.loss_percent,
554
+ "material_learning_rate": self.material_learning_rate,
555
+ "employee_learning_rate": self.employee_learning_rate,
556
+ "capital_cost_learning_rate": self.capital_cost_learning_rate,
557
+ "early_stopping": self.use_early_stopping,
558
+ "patience": self.patience,
559
+ "use_model_weight": self.use_model_weight,
560
+ "max_data": self.max_data,
561
+ "min_data": self.min_data,
562
+ "weight_list": self.weight_list,
563
+ }
564
+
565
+ pickle.dump(model_data, open(filename, "wb"))
566
+ print("Model Exported Successfully")
567
+
568
+ def load_model(self, filename="tdce_model.pkl"):
569
+ model_data = pickle.load(open(filename, "rb"))
570
+ self.material_element = model_data["material_element"]
571
+ self.employee_element = model_data["employee_element"]
572
+ self.capital_cost_element = model_data["capital_cost_element"]
573
+ self.weights = model_data["weights"]
574
+ self.bias = model_data["bias"]
575
+ self.loss = model_data["loss"]
576
+ self.loss_prime = model_data["loss_prime"]
577
+ self.loss_percent = model_data["loss_percent"]
578
+ self.material_learning_rate = model_data["material_learning_rate"]
579
+ self.employee_learning_rate = model_data["employee_learning_rate"]
580
+ self.capital_cost_learning_rate = model_data[
581
+ "capital_cost_learning_rate"
582
+ ]
583
+ self.use_early_stopping = model_data["early_stopping"]
584
+ self.patience = model_data["patience"]
585
+ self.use_model_weight = model_data["use_model_weight"]
586
+ self.max_data = model_data["max_data"]
587
+ self.min_data = model_data["min_data"]
588
+ self.weight_list = model_data["weight_list"]
589
+ print("Model Loaded Successfully")
590
+
591
+ def predict_data(self, payload):
592
+ # Normalize Payload
593
+ (
594
+ normalized_material_cost,
595
+ normalized_material_amount,
596
+ normalized_employee_cost,
597
+ normalized_employee_duration,
598
+ normalized_employee_day_amount,
599
+ normalized_capital_cost,
600
+ normalized_capital_cost_duration,
601
+ normalized_day_amount,
602
+ ) = mnorm.normalize_payload(
603
+ material_cost=payload["material_cost"],
604
+ material_amount=payload["material_amount"],
605
+ employee_cost=payload["employee_cost"],
606
+ employee_duration=payload["employee_duration"],
607
+ employee_day_amount=payload["employee_day_amount"],
608
+ capital_cost=payload["capital_cost"],
609
+ capital_cost_duration=payload["capital_cost_duration"],
610
+ day_amount=payload["day_amount"],
611
+ max_data=self.max_data,
612
+ min_data=self.min_data
613
+ )
614
+
615
+ # Predict Material Cost
616
+ predicted_mc = self.material_element.predict_sample(
617
+ cost_input=normalized_material_cost,
618
+ amount_input=normalized_material_amount
619
+ )
620
+
621
+ # Predict Employee Cost
622
+ predicted_ec = self.employee_element.predict_sample(
623
+ cost_input=normalized_employee_cost,
624
+ time_input=normalized_employee_duration,
625
+ day_amount=normalized_employee_day_amount
626
+ )
627
+
628
+ # Predict Capital Cost
629
+ predicted_cc = self.capital_cost_element.predict_sample(
630
+ cost_input=normalized_capital_cost,
631
+ time_input=normalized_capital_cost_duration,
632
+ day_amount=normalized_day_amount,
633
+ )
634
+
635
+ # Combine Result
636
+ result = (
637
+ predicted_mc * self.weights[0]
638
+ + predicted_ec * self.weights[1]
639
+ + predicted_cc * self.weights[2]
640
+ ) + self.bias
641
+
642
+ return result