{"id":20885671,"url":"https://github.com/nripstein/bayesian-email-classifier","last_synced_at":"2026-05-01T22:32:34.199Z","repository":{"id":75062566,"uuid":"603292793","full_name":"nripstein/Bayesian-Email-Classifier","owner":"nripstein","description":"Naive Bayes spam email classifier implemented from scratch without using machine learning libraries","archived":false,"fork":false,"pushed_at":"2023-11-13T14:58:51.000Z","size":4379,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-12-30T05:15:01.155Z","etag":null,"topics":["bayesian-inference","machine-learning"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nripstein.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-02-18T03:58:41.000Z","updated_at":"2023-04-03T16:06:34.000Z","dependencies_parsed_at":"2023-11-13T15:58:12.542Z","dependency_job_id":null,"html_url":"https://github.com/nripstein/Bayesian-Email-Classifier","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/nripstein/Bayesian-Email-Classifier","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nripstein%2FBayesian-Email-Classifier","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nripstein%2FBayesian-Email-Classifier/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nripstein%2FBayesian-Email-Classifier/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nripstein%2FBayesian-Email-Classifier/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nripstein","download_url":"https://codeload.github.com/nripstein/Bayesian-Email-Classifier/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nripstein%2FBayesian-Email-Classifier/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32515838,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-30T13:12:12.517Z","status":"online","status_checked_at":"2026-05-01T02:00:05.856Z","response_time":64,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bayesian-inference","machine-learning"],"created_at":"2024-11-18T08:14:10.113Z","updated_at":"2026-05-01T22:32:34.153Z","avatar_url":"https://github.com/nripstein.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n\n\u003c!--$$\n\u003cscript src=\"https://polyfill.io/v3/polyfill.min.js?features=es6\"\u003e\u003c/script\u003e\n\u003cscript id=\"MathJax-script\" async src=\"https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-MML-AM_CHTML\"\u003e\u003c/script\u003e\n$$--\u003e\n\n# Naive Bayes Spam Email Classifier\n\n\u003cdiv align=\"center\"\u003e\n    \u003ca href=\"https://numpy.org\"\u003e\u003cimg src=\"https://img.shields.io/badge/numpy-%23013243.svg?style=for-the-badge\u0026logo=numpy\u0026logoColor=white\" /\u003e\u003c/a\u003e\n    \u003ca href=\"https://pandas.pydata.org\"\u003e\u003cimg src=\"https://img.shields.io/badge/pandas-%23150458.svg?style=for-the-badge\u0026logo=pandas\u0026logoColor=white\" /\u003e\u003c/a\u003e\n    \u003ca href=\"https://matplotlib.org\"\u003e\u003cimg src=\"https://img.shields.io/badge/Matplotlib-%23ffffff.svg?style=for-the-badge\u0026logo=Matplotlib\u0026logoColor=black\" /\u003e\u003c/a\u003e\n\u003c/div\u003e\n\u003cbr\u003e\n\n\n\n\n- [TLDR](https://github.com/nripstein/Bayesian-Email-Classifier#tldr)\n- [Why I Started This Project](https://github.com/nripstein/Bayesian-Email-Classifier#why-i-started-this-project)\n- [How to Use](https://github.com/nripstein/Bayesian-Email-Classifier#how-to-use)\n- [Have an Email? Check if it's Spam](https://github.com/nripstein/Bayesian-Email-Classifier#have-an-email-check-if-its-spam)\n- [Classifier Accuracy Analysis and Visualizations](https://github.com/nripstein/Bayesian-Email-Classifier#classifier-accuracy-analysis-and-visualizations)\n- [Mathematical Steps](https://github.com/nripstein/Bayesian-Email-Classifier#mathematical-steps)\n- [Future Directions](https://github.com/nripstein/Bayesian-Email-Classifier#classifier-accuracy-analysis-and-visualizations)\n\n## TLDR\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; This program classifies emails as spam or ham.  It uses a naïve bayes classifier algorithm, which is a machine learning algorithm.  It does not use any machine learning libraries, rather, I designed and customized the algorithm using Bayesian inference.\n\n## Why I Started This Project\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; In a Bayesian statistic class, I learned about the naive Bayes classifier and its applications in text classification. I was intrigued by the algorithm's simplicity and effectiveness, so I decided to implement it from scratch in Python without using any machine learning libraries. I wanted to challenge myself to understand the underlying math and logic of the algorithm, and to gain hands-on experience in building machine learning models from scratch.\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; Currently, I'm also learning how to create machine learning algorithms using neural networks in TensorFlow. I hope to come back to this project at some point to compare the performance of the naive Bayes classifier with a neural network classification on the same task. This project serves as a stepping stone for me to explore the world of machine learning and deepen my understanding of the algorithms behind it.\n\n## How to use:\n1. Download the repository and extract the files to your desired directory.\n2. [Download the Waterloo Public Corpus Dataset from Kaggle](https://www.kaggle.com/datasets/imdeepmind/preprocessed-trec-2007-public-corpus-dataset.)\n3. Create a folder called \"data\" in the root directory of the repository.\n4. Move the downloaded dataset file into the \"data\" folder.\n5. Run main.py to start the program.\n6. Customize use of the program according to the comments at the bottom of the main.py file.  \nNote: you will need to retrain the model, which may take some time, because the training data is stored in a csv file too large to upload to github\n\n## Have an Email? Check if it's Spam\n1. Train the model first by following the instructions in the \"How to Use\" section.\n2. Open the to_classify.txt file located in the main folder of the repository.\n3. Paste the email you want to classify into the to_classify.txt file.\n4. Save the to_classify.txt file and close it.\n5. Run main.py to classify and colour-code the email\n\nThe console will output the posterior probability of the email being spam, and will output the email with words colour coded according to their probability of coming from a spam email.  Examples below.\n\nIt is important to remember that the model was trained on a specific dataset, and as such, it may not be accurate in classifying all types of emails. The model was trained on the Waterloo Public Corpus dataset, which includes a large collection of emails from different sources. It is important to note that the model is not personalized to you or any individual, and as such, its accuracy may vary depending on the email content.  For instance, the training dataset labels almost all French emails as spam, so the words the model thinks are most likely to be spam are all in French.\n\nExamples:\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://user-images.githubusercontent.com/98430636/220826883-b6ffc540-60f0-4153-9acd-1a617d8426b5.png\" alt=\"spam\" width=\"90%\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://user-images.githubusercontent.com/98430636/220826890-44fc7199-8d15-4b77-93d0-d55be015752c.png\" alt=\"ham\" width=\"90%\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://user-images.githubusercontent.com/98430636/220827124-0b710823-ec55-441d-acaa-142dca24c2a8.png\" alt=\"prob spam\" width=\"90%\"\u003e\n\u003c/p\u003e\n\n\n\n## Classifier Accuracy Analysis and Visualizations\nTo evaluate the performance of the Naive Bayes spam email classifier, I trained it on 15,000 emails and tested it on 2,000. The overall accuracy of the model was 92.05%, which seems like a promising result. The confusion matrix below shows the number of true positives, true negatives, false positives, and false negatives for the test set.\n\n\n### Confusion Matrix\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://user-images.githubusercontent.com/98430636/220796903-e92a632e-a977-4ffc-bb74-c94f31e747b9.png\" alt=\"confusion matrix\" width=\"70%\"\u003e\n\u003c/p\u003e\n\nThe data from this confusion matrix can be used to determine the \u003cu\u003esensitivity\u003c/u\u003e and \u003cu\u003especificity\u003c/u\u003e of my algorithm. Sensitivity and specificity are two important measures used to evaluate the performance of a binary classification algorithm. Sensitivity measures the proportion of true positive cases that are correctly identified as positive, while specificity measures the proportion of true negative cases that are correctly identified as negative.\n\nFor my spam email classifier, I calculated the sensitivity and specificity based on the confusion matrix generated from testing the algorithm on 2,000 emails. We found that the most likely specificity of my algorithm was 99.8%, with a 95% confidence interval of 99.7% to 99.9%. The most likely sensitivity of my algorithm was 86.1%, with a 95% confidence interval of 84.3% to 87.9%.\n\nTo visualize the probability distribution of sensitivity and specificity, I generated probability distribution functions for each measure. These graphs  provide a way to visualize the estimattion for the a particular value being the true sensitivity or specificity. The probability distribution functions for sensitivity and specificity are shown below:\n\n### Sensitivity and Specificity Probability Distribution Functions\n\n\u003cp align=\"center\"\u003e\n    \u003cimg src=\"https://user-images.githubusercontent.com/98430636/220942351-de3ad74b-f338-4678-bacd-544e209d9e61.png\" alt=\"sens, spec\" width=\"70%\"\u003e\n\u003c/p\u003e\n\n\n\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; As you can see, the probability distribution function for sensitivity is extremley tightly distributed near 100%. This indicates that the model is very good at identifying non-spam emails, with a very low false positive rate. On the other hand, the specificity pdf shows a wider distribution with a lower mode, indicating that the model is not as good at identifying spam emails, and the degree of certainty in the sensitivity it lower. However, the 95% confidence interval for sensitivity still shows a relatively high accuracy rate, ranging from 84.3% to 87.9%. I would ideally like to improve the model so that the lower end of the sensitivity 95% confidence interval is at least 90%.\n\n## Word Clouds\n### Most Common Spam Words\n\u003cp align=\"center\"\u003e\n    \u003cimg src=\"https://user-images.githubusercontent.com/98430636/220796954-f5bfe2d8-a98b-40f3-91ae-5c2b37af3dda.png\" alt=\"spam ratio\" width=\"90%\"\u003e\n\u003c/p\u003e\n\n\n### Most Common Ham Words\n\u003cp align=\"center\"\u003e\n    \u003cimg src=\"https://user-images.githubusercontent.com/98430636/220796981-ae2880dc-37cd-4715-9413-91203022eabc.png\" alt=\"ham ratio\" width=\"90%\"\u003e\n\u003c/p\u003e\n\nThere are lots of French words which are common in the spam corpus, but very rare in the ham corpus.  This indicates that French emails are very likely spam in the training set.\n## Mathematical steps:\n\nPreparation for training:\n-\tThe body section of each email’s is converted to a set of words, which is added to a column called “word_set” using the string_to_word_set() function.\n-\tThe dataset is split into a training and testing data set.\n\nTraining the data:\n-\tA new data frame is created which contains the frequency at which each word in the training data set appears in the “spam” and “ham” category.\n\nClassifying the data:\n-\tThe Bayesian posterior probability that an email is spam is determined according to the following (basic) procedure:\n\n### Bayesian posterior calculation\n\nHypotheses:  \nH1: The email is spam\nH2: The email is ham\n\nPrior probabilities: P(category)  \nP(spam) and P(ham). We treat these as P(spam) = P(ham) = 0.5. In the strictest mathematical sense, P(spam) should be the overall frequency of spam emails, but I want to reduce bias, and the training dataset has much more spam than a normal email adress.\n\nLikelihood calculations: P(words|category)  \n$$P(\\textrm{words}|\\textrm{spam}) = \\prod_{i=1}^{n} \\frac{\\textrm{freq of word}_i \\textrm{in spam}}{\\textrm{ham words}}$$\n\n$$P(\\textrm{words}|\\textrm{ham}) = \\prod_{i=1}^{n} \\frac{\\textrm{freq of word}_i \\textrm{in ham}}{\\textrm{ham words}}$$\n\nPosteriors:\n\n$$ P(H1|D) = \\frac{P(D|H1)P(H1)}{P(D|H1)P(H1) + P(D|H2)P(H2)} $$\n\n$$ P(\\textrm{spam}|\\textrm{words}) = \\frac{P(\\textrm{words}|\\textrm{spam})P(\\textrm{spam})}{P(\\textrm{words}|\\textrm{spam})P(\\textrm{spam}) + P(\\textrm{words}|\\textrm{ham})P(\\textrm{ham})} $$\n\nDeviations from standard procedure:\n-\tIf, when doing a likelihood calculstion, a word appears in “spam” but not \"ham,\" then it is treated as if it's been in ham 0.5 times.\n-\tIf a word appears in \"ham\" but not \"spam,\" then it is treated as if it has appeared 0.01 tines\n\nThe posterior probability of each email being spam is computed.  If it's determined that there's a greater than 90% probability of the email being spam, then it is classified as spam\n\n\n\n## Future Directions\nThere are a few areas where the spam email classifier could be improved. Some of the possible areas for improvement include:\n\n1. Handling spam emails made up of one long word: Currently, the model is not equipped to handle spam emails that contain a single long word that it has not seen before. In future versions of the model, I plan to develop a mechanism to handle such words and improve the model's accuracy.\n\n2. Conducting more analysis on accuracy: While the model has shown promising results, it is clear that there is room for improvement in terms of accuracy. In order to figure out how to best improve the model, I hope to conduct more analysis on the accuracy.\n\n3. Determining the optimal probability threshold: The current version of the model classifies an email as spam or not spam based on a probability threshold of 0.95. In future versions of the model, I plan to conduct a more in-depth analysis to determine the optimal probability threshold that will provide the best classification results.\n\nBy addressing these issues, I hope to improve the accuracy and effectiveness of the spam email classifier.\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnripstein%2Fbayesian-email-classifier","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnripstein%2Fbayesian-email-classifier","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnripstein%2Fbayesian-email-classifier/lists"}