blakelassman.dev

Writing

From even numbers to Naive Bayes

September 2, 2026·5 min read

pythonmachine-learninglearning

In May 2024, I wrote a Python program that asked for your name and a number between 1 and 100. If the number was even, you won. That was the entire game.

I had watched an hour-long YouTube video, opened a blank file, and tried to make something without following another tutorial. The result has nested if statements, no protection against someone typing a word instead of a number, and a comment where I misspelled “integer.”

I still keep the repository pinned on GitHub.

Not because the code is good. It is not. I keep it because it is an honest timestamp of what I understood when I started.

A comparison of Blake's first Python number game from May 2024 and his SMS spam classifier from April 2025.

The whole first project

The program did a few things that felt much bigger at the time:

  • Accepted a name and number from the player
  • Converted the number from text into an integer
  • Rejected numbers outside the allowed range
  • Used the modulo operator to decide whether the number was even
  • Printed a different result for a win or loss

The important part was this:

if int_player % 2 == 0:
    print("Congratulations " + name + "! You picked an even number!")
else:
    print("Sorry " + name + ", you chose an odd number...")

Today, the problems are obvious. The range checks are nested when they do not need to be. The conversion to int can crash the program. The rules say to pick a number between 1 and 100, but the code rejects 100. Everything lives at the top level of one file.

At the time, none of that mattered. I had an idea, translated it into instructions, ran it, broke it, and kept changing it until it worked. It was the first time code felt like something I could use rather than something other people made.

Eleven months later

In April 2025, I built an SMS spam classifier. It takes a text message and predicts whether it is spam or legitimate.

It is still a beginner project, but the shape of the problem is completely different. Instead of writing a rule like “if the message contains FREE, call it spam,” the program learns patterns from a labeled dataset.

The basic flow looks like this:

An SMS spam classification pipeline that loads labeled messages, splits the data, converts words into count vectors, trains a Naive Bayes model, and predicts spam or legitimate.

The script loads a tab-separated file with pandas, labels each message as spam or legitimate, and splits the data into training and testing sets. CountVectorizer turns the text into numbers the model can use. MultinomialNB learns which word patterns tend to appear in each class.

Then it drops into a small command-line loop:

while True:
    message = input("Type your SMS here: ")
    if message.strip().lower() == "exit":
        break
    else:
        predict_spam(message)

You can type a message, get a prediction, and keep testing until you enter exit.

One detail I am glad I got right is fitting the vectorizer only on the training data:

X_train_vec = vectorizer.fit_transform(X_train)
X_test_vec = vectorizer.transform(X_test)

The first line learns the vocabulary from the training messages. The second applies that existing vocabulary to messages the model has not trained on. Fitting the vectorizer again on the test set would leak information and make the evaluation less trustworthy.

I did not have language for data leakage when I wrote my first number game. By the second project, I was at least thinking about what the model should and should not be allowed to see.

About that 98% accuracy

The classifier scored above 98% accuracy on its test split. That sounds much more impressive than it is.

SMS datasets usually contain far more legitimate messages than spam. A model can achieve a strong accuracy score while still missing the cases people actually care about. Before treating this as anything more than a learning project, I would want to see the confusion matrix, precision, recall, and examples of the messages it classified incorrectly.

False positives matter too. A spam filter that blocks a real message from your bank, doctor, or employer can be worse than one that lets an annoying promotion through.

The current project does not examine any of that. It prints a single prediction and moves on. The accuracy number proves the pipeline learned something, but it does not prove the model is ready for anyone to depend on it.

What actually changed

The obvious difference is that the second project uses pandas and scikit-learn. That is not the change I care about most. Libraries can be looked up.

The bigger change was learning to split a problem into parts. Load the data. Give it a consistent shape. Separate what the model can learn from what it will be tested against. Convert language into features. Train. Evaluate. Make the result usable from the command line.

My first project was one block of instructions. The classifier was a pipeline.

That does not mean the classifier is clean. The imports are scattered through the file. The trained model is not saved. There are no automated tests. The interface is only a terminal prompt. If the process closes, everything has to train again the next time it starts.

Those are not reasons to hide it. They are the next list of things to learn.

Why both are still public

It would take about ten minutes to rewrite the first project into cleaner Python. I could add functions, handle bad input, flatten the conditionals, and fix the spelling mistakes.

I am not going to.

Cleaning it now would make the code better and the repository less useful. Its value is that it shows exactly where I started. The spam classifier means more when the even-number game is sitting beside it.

Public projects do not need to pretend every version of you had your current knowledge. A perfectly polished GitHub profile can show competence, but it can also erase the only evidence that learning happened.

The next version of the classifier should have better evaluation, saved model artifacts, tests, and a small web interface. When I build that, this version will stay where it is too.

I could clean up the trail. I would rather be able to see it.