Good morning! Recently, I have been dedicating myself to learning about deep learning using TensorFlow and Keras.
My case study, apparently, is quite simple (I think). I'm trying to teach my learning model how to identify leap years.
My Python code can be seen below:
import tensorflow as tf
import numpy as np
import calendar
# ------------------- UTILITY FUNCTIONS -------------------
def is_leap(year):
"""Checks if a year is a leap year."""
return calendar.isleap(year)
def generate_data(num_samples):
"""
Generates training data and labels. The generated data are random numbers starting from 1000.
Two types of data are returned, one for training and one for validation. Both are numpy arrays.
"""
inputs = []
outputs = []
for i in range(num_samples):
inputs.append(np.random.randint(10000, 20001))
if is_leap(inputs[i]):
outputs.append(1)
else:
outputs.append(0)
# Lists are converted to numpy arrays.
return np.array(inputs, dtype='float32'), np.array(outputs, dtype='float32')
# ------------------- MODEL CREATION AND TRAINING -------------------
def create_model():
"""Creates a sequential Keras model."""
model = tf.keras.models.Sequential([
tf.keras.layers.Input(shape=(1,)),
tf.keras.layers.Dense(128, activation='relu'),
tf.keras.layers.Dense(64, activation='relu'),
tf.keras.layers.Dense(1, activation='sigmoid')
])
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
return model
def train_model(model, inputs, outputs, epochs=20):
"""Trains the model."""
model.fit(inputs, outputs, epochs=epochs)
# ------------------- MAIN -------------------
if __name__ == "__main__":
# Generates data for model training.
num_samples = 10000
years, results = generate_data(num_samples)
# Model creation and training.
model = create_model()
train_model(model, years, results, 60)
# Years for testing
test_years, test_results = generate_data(100)
# Gets predictions.
predictions = model.predict(test_years)
# Prints the results
print("---------- MODEL TEST ----------")
for i in range(test_years.shape[0]):
prediction = predictions[i][0]
prediction_01 = 1 if prediction <= 0.5 else 0
result = test_results[i]
year = test_years[i]
print(f"[{year}]: Prediction: {prediction} ({prediction_01}), Real: {result}")
I have tried several different configurations, but the result always seems biased, either showing that all test years are leap years or the opposite. Here is an example of the program's output with the settings shown in the code:
---------- MODEL TEST ----------
[15782.0]: Prediction: 0.028894491493701935 (1), Real: 0.0
[12486.0]: Prediction: 0.056425176560878754 (1), Real: 0.0
[19711.0]: Prediction: 0.012782025150954723 (1), Real: 0.0
[11396.0]: Prediction: 0.07004968822002411 (1), Real: 1.0
[14363.0]: Prediction: 0.03863195702433586 (1), Real: 0.0
[17586.0]: Prediction: 0.01990177109837532 (1), Real: 0.0
[18930.0]: Prediction: 0.015046692453324795 (1), Real: 0.0
[10777.0]: Prediction: 0.07908520102500916 (1), Real: 0.0
[14515.0]: Prediction: 0.03745480626821518 (1), Real: 0.0
[12179.0]: Prediction: 0.059987716376781464 (1), Real: 0.0
[11645.0]: Prediction: 0.06669164448976517 (1), Real: 0.0
[13158.0]: Prediction: 0.049309246242046356 (1), Real: 0.0
[11396.0]: Prediction: 0.07004968822002411 (1), Real: 1.0
[12925.0]: Prediction: 0.0516749769449234 (1), Real: 0.0
[14807.0]: Prediction: 0.035287827253341675 (1), Real: 0.0
[15088.0]: Prediction: 0.03331705927848816 (1), Real: 1.0
[16337.0]: Prediction: 0.025773070752620697 (1), Real: 0.0
[19299.0]: Prediction: 0.013931039720773697 (1), Real: 0.0
[16406.0]: Prediction: 0.025408802554011345 (1), Real: 0.0
[13144.0]: Prediction: 0.0494486503303051 (1), Real: 1.0
[18829.0]: Prediction: 0.015366936102509499 (1), Real: 0.0
[15150.0]: Prediction: 0.03289690986275673 (1), Real: 0.0
[17999.0]: Prediction: 0.018265796825289726 (1), Real: 0.0
[11613.0]: Prediction: 0.06711487472057343 (1), Real: 0.0
[13565.0]: Prediction: 0.04542219638824463 (1), Real: 0.0
[10702.0]: Prediction: 0.0802496150135994 (1), Real: 0.0
[13246.0]: Prediction: 0.048442721366882324 (1), Real: 0.0
[18801.0]: Prediction: 0.015456832014024258 (1), Real: 0.0
[19585.0]: Prediction: 0.013123226352036 (1), Real: 0.0
[17522.0]: Prediction: 0.020168287679553032 (1), Real: 0.0
[15651.0]: Prediction: 0.029683079570531845 (1), Real: 0.0
[16680.0]: Prediction: 0.024010933935642242 (1), Real: 1.0
[15311.0]: Prediction: 0.03182867914438248 (1), Real: 0.0
[12538.0]: Prediction: 0.055841829627752304 (1), Real: 0.0
[10095.0]: Prediction: 0.09026535600423813 (1), Real: 0.0
[12790.0]: Prediction: 0.05309422314167023 (1), Real: 0.0
[16811.0]: Prediction: 0.023368824273347855 (1), Real: 0.0
[11217.0]: Prediction: 0.0725601464509964 (1), Real: 0.0
[14563.0]: Prediction: 0.03708960860967636 (1), Real: 0.0
[14162.0]: Prediction: 0.04024471715092659 (1), Real: 0.0
[12618.0]: Prediction: 0.05495523288846016 (1), Real: 0.0
[19699.0]: Prediction: 0.012814437039196491 (1), Real: 0.0
[16408.0]: Prediction: 0.02539803832769394 (1), Real: 1.0
[14447.0]: Prediction: 0.037976961582899094 (1), Real: 0.0
[15228.0]: Prediction: 0.03237523138523102 (1), Real: 1.0
[15779.0]: Prediction: 0.02891264297068119 (1), Real: 0.0
[11751.0]: Prediction: 0.06530816853046417 (1), Real: 0.0
[13550.0]: Prediction: 0.04556054621934891 (1), Real: 0.0
[11842.0]: Prediction: 0.06414111703634262 (1), Real: 0.0
[17247.0]: Prediction: 0.021351758390665054 (1), Real: 0.0
[17256.0]: Prediction: 0.021312057971954346 (1), Real: 1.0
[10513.0]: Prediction: 0.08325573056936264 (1), Real: 0.0
[15134.0]: Prediction: 0.03300463408231735 (1), Real: 0.0
[17974.0]: Prediction: 0.01836095005273819 (1), Real: 0.0
[10549.0]: Prediction: 0.08267498016357422 (1), Real: 0.0
[13928.0]: Prediction: 0.04220344498753548 (1), Real: 1.0
[16993.0]: Prediction: 0.022505149245262146 (1), Real: 0.0
[17120.0]: Prediction: 0.021921301260590553 (1), Real: 1.0
[14685.0]: Prediction: 0.03617788851261139 (1), Real: 0.0
[11489.0]: Prediction: 0.06877779960632324 (1), Real: 0.0
[17720.0]: Prediction: 0.01935615763068199 (1), Real: 1.0
[11165.0]: Prediction: 0.07330425828695297 (1), Real: 0.0
[13673.0]: Prediction: 0.0444408617913723 (1), Real: 0.0
[10098.0]: Prediction: 0.09021187573671341 (1), Real: 0.0
[13159.0]: Prediction: 0.049299370497465134 (1), Real: 0.0
[17958.0]: Prediction: 0.018422311171889305 (1), Real: 0.0
[15849.0]: Prediction: 0.02849932760000229 (1), Real: 0.0
[19562.0]: Prediction: 0.013186565600335598 (1), Real: 0.0
[12582.0]: Prediction: 0.05535256117582321 (1), Real: 0.0
[18768.0]: Prediction: 0.015563310123980045 (1), Real: 1.0
[16677.0]: Prediction: 0.024025622755289078 (1), Real: 0.0
[10858.0]: Prediction: 0.07784521579742432 (1), Real: 0.0
[12186.0]: Prediction: 0.05990469083189964 (1), Real: 0.0
[18558.0]: Prediction: 0.016259854659438133 (1), Real: 0.0
[19349.0]: Prediction: 0.013786446303129196 (1), Real: 0.0
[17064.0]: Prediction: 0.022176457569003105 (1), Real: 1.0
[18551.0]: Prediction: 0.016283303499221802 (1), Real: 0.0
[15677.0]: Prediction: 0.029525138437747955 (1), Real: 0.0
[14678.0]: Prediction: 0.036229733377695084 (1), Real: 0.0
[15669.0]: Prediction: 0.029574228450655937 (1), Real: 0.0
[17092.0]: Prediction: 0.022048931568861008 (1), Real: 1.0
[13242.0]: Prediction: 0.04848231002688408 (1), Real: 0.0
[11103.0]: Prediction: 0.07420048862695694 (1), Real: 0.0
[18457.0]: Prediction: 0.01660582609474659 (1), Real: 0.0
[14844.0]: Prediction: 0.035021863877773285 (1), Real: 1.0
[11382.0]: Prediction: 0.07024302333593369 (1), Real: 0.0
[18430.0]: Prediction: 0.016699181869626045 (1), Real: 0.0
[17603.0]: Prediction: 0.019832022488117218 (1), Real: 0.0
[15078.0]: Prediction: 0.03338494151830673 (1), Real: 0.0
[17369.0]: Prediction: 0.02081885002553463 (1), Real: 0.0
[14098.0]: Prediction: 0.04077168554067612 (1), Real: 0.0
[16942.0]: Prediction: 0.02274395152926445 (1), Real: 0.0
[17134.0]: Prediction: 0.021857596933841705 (1), Real: 0.0
[18702.0]: Prediction: 0.01577908731997013 (1), Real: 0.0
[18461.0]: Prediction: 0.016592128202319145 (1), Real: 0.0
[15036.0]: Prediction: 0.033673398196697235 (1), Real: 1.0
[12185.0]: Prediction: 0.059916503727436066 (1), Real: 0.0
[11637.0]: Prediction: 0.06679684668779373 (1), Real: 0.0
[16475.0]: Prediction: 0.025049572810530663 (1), Real: 0.0
[12810.0]: Prediction: 0.052881550043821335 (1), Real: 0.0
If I increase the epochs, the values become very low, something like 0.00025, and they keep repeating endlessly. If I decrease the epochs, they become too high. I can't find satisfactory results or train the model properly.
I've already tried getting help from ChatGPT, Claude, Gemini, GitCopilot, Google AI Studio, but none of them could really help me. I tried standardizing the values, but it didn’t work. I don't know what else I can do...
Please, could someone help me? I would be extremely grateful for any assistance.
The absolute value of a number is mostly irrelevant, you're feeding it nonsensical inputs.
What you could try instead would be to calculate the mod
s for a bunch of small numbers, e.g.
[n mod 2, n mod 3, n mod 4, n mod 5, ..., n mod 30]
i.e.
2 -> [0, 1, 2, 2, ... ]
3 -> [1, 0, 3, 3, ... ]
4 -> [0, 1, 0, 4, ... ]
etc.
and then use this as the input vector.
If you want to sneak in more assumptions, you could stop at 25, because 400 = 16 * 25. Or you could even take just the powers of 2s and 5s (because the predicates _ = 0 mod 4
, _ = 0 mod 100
and _ = 0 mod 400
are all representable by looking just at the remainders modulo powers of 2s and 5s, but then you would introduce so much bias that it sort-of becomes boring, so you might just as well hardcode the explicit formula.
Alternatively, you could also factorize n
into powers of primes, and then use the exponents as your feature vector, i.e.
# 2 3 5 7 11 ...
1 -> [0, 0, 0, 0, 0, ...]
2 -> [1, 0, 0, 0, 0, ...]
3 -> [0, 1, 0, 0, 0, ...]
4 -> [2, 0, 0, 0, 0, ...]
5 -> [0, 0, 1, 0, 0, ...]
etc.
Then the model would hopefully learn that whenever
you get a leap year.