{"id":32734771,"url":"https://github.com/mianmharoon/sentimentanalysis_coreml_emotionclassifier","last_synced_at":"2026-04-12T18:07:15.108Z","repository":{"id":321227000,"uuid":"1085013011","full_name":"MianMHaroon/SentimentAnalysis_CoreML_EmotionClassifier","owner":"MianMHaroon","description":"Emotion classification iOS app using CoreML and SwiftUI – demo for sentiment and emotion analysis, with the model converted from Scikit-learn using coremltools.","archived":false,"fork":false,"pushed_at":"2025-10-28T15:16:43.000Z","size":1342,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-28T15:24:00.464Z","etag":null,"topics":["ai","coreml","coreml-models","emotionclassification","ios","machinelearning","nlp","python3","scikit-learn","sentimentanalysis","swift","swiftui"],"latest_commit_sha":null,"homepage":"","language":"Swift","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/MianMHaroon.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-28T13:22:50.000Z","updated_at":"2025-10-28T15:16:47.000Z","dependencies_parsed_at":"2025-10-28T15:34:54.561Z","dependency_job_id":null,"html_url":"https://github.com/MianMHaroon/SentimentAnalysis_CoreML_EmotionClassifier","commit_stats":null,"previous_names":["mianmharoon/sentimentanalysis_coreml_emotionclassifier"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/MianMHaroon/SentimentAnalysis_CoreML_EmotionClassifier","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MianMHaroon%2FSentimentAnalysis_CoreML_EmotionClassifier","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MianMHaroon%2FSentimentAnalysis_CoreML_EmotionClassifier/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MianMHaroon%2FSentimentAnalysis_CoreML_EmotionClassifier/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MianMHaroon%2FSentimentAnalysis_CoreML_EmotionClassifier/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MianMHaroon","download_url":"https://codeload.github.com/MianMHaroon/SentimentAnalysis_CoreML_EmotionClassifier/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MianMHaroon%2FSentimentAnalysis_CoreML_EmotionClassifier/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":282415958,"owners_count":26665441,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-11-03T02:00:05.676Z","response_time":108,"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":["ai","coreml","coreml-models","emotionclassification","ios","machinelearning","nlp","python3","scikit-learn","sentimentanalysis","swift","swiftui"],"created_at":"2025-11-03T07:01:13.391Z","updated_at":"2025-11-03T07:02:21.843Z","avatar_url":"https://github.com/MianMHaroon.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# SentimentAnalysis_CoreMLDemo_EmotionClassifier_iOS_SwiftUI\n\nThis is an **iOS demo application** for **emotion classification** using **CoreML** and **SwiftUI**.\nThe project follows **MVVM architecture** with **async/await** and **actor-based model management**.\n\nThe **CoreML model** was converted from a **Scikit-learn pipeline** using **coremltools Python package**, including:\n\n* `DictVectorizer` for feature extraction\n* `LinearSVC` for classification\n\n\u003cp align=\"center\"\u003e\n \u003cimg src=\"https://github.com/user-attachments/assets/dbb7ede8-a229-4cfe-9a94-1a3873fcbd92\" width=\"400\" alt=\"Emotion Classifier Demo\" /\u003e\n\u003c/p\u003e\n\n\n---\n\n## Dataset\n\nThe training dataset is from [Kaggle: Emotions Dataset for NLP](https://www.kaggle.com/datasets/praveengovi/emotions-dataset-for-nlp/data). But I have added the preprocessed and formatted CSV data in the ***converter*** folder for direct use in training.\n\n### Steps:\n\n1. Original text files converted to **CSV** with two columns:\n\n   * `text` → user sentence or phrase\n   * `emotion` → label (e.g., joy, sadness, anger, fear, love, surprise)\n\n2. Python example to convert `.txt` → `.csv`:\n\n```python\nimport csv\nfrom pathlib import Path\n\n# Input and output file paths\ninput_file = Path(\"val.txt\")\noutput_file = Path(\"val.csv\")\n\n# Ensure input exists\nif not input_file.exists():\n    raise FileNotFoundError(f\"❌ Input file not found: {input_file}\")\n\n# Open the input text file and create the output CSV file\nwith input_file.open(\"r\", encoding=\"utf-8\") as infile, output_file.open(\"w\", newline=\"\", encoding=\"utf-8\") as outfile:\n    writer = csv.writer(outfile)\n    writer.writerow([\"text\", \"emotion\"])  # CSV header\n    \n    count = 0\n    for line in infile:\n        line = line.strip()\n        if not line:\n            continue\n        if \";\" in line:\n            text, emotion = line.split(\";\", 1)\n            writer.writerow([text.strip(), emotion.strip()])\n            count += 1\n        else:\n            print(f\"⚠️ Skipping malformed line: {line}\")\n    \nprint(f\"✅ Conversion complete! {count} entries saved to '{output_file.name}'\")\n\n```\n\n---\n\n## Python Setup\n\n1. Install **Python 3.9+**\n2. Install dependencies:\n\n```bash\npip install pandas numpy nltk scikit-learn coremltools\n```\n\n3. Download NLTK data:\n\nStep 1: Open Python 3 in your terminal\n\n```python\npython3\n```\n\nStep 2: Run the following commands in the Python prompt (\u003e\u003e\u003e)\n```python\n\u003e\u003e\u003e import nltk\n\u003e\u003e\u003e nltk.download('punkt')\n\u003e\u003e\u003e nltk.download('stopwords')\n```\n\nStep 3: Exit Python\n```python\n\u003e\u003e\u003e exit()\n```\n---\n\n## Training the Model\n\nRun:\n\n```bash\npython3 train_emotion_model.py\n```\n\n**Steps performed:**\n\n* Load CSV data\n* Preprocess text\n* Extract bag-of-words features\n* Train **LinearSVC** using GridSearchCV\n* Save **CoreML model** as `EmotionClassifier.mlmodel`\n\n---\n\n# How the Model Learns (Step by Step Example)\n\nThis section explains how the emotion classification model learns from training data using a step-by-step example with multiple iterations.\n\n---\n\n## Example Training Dataset\n\n| Text                         | Emotion  |\n| ---------------------------- | -------- |\n| \"I am happy today\"           | joy      |\n| \"I feel sad\"                 | sadness  |\n| \"I am really angry\"          | anger    |\n| \"I love my family\"           | love     |\n| \"I am scared of the dark\"    | fear     |\n| \"Wow, I didn’t expect that!\" | surprise |\n| \"I am ecstatic and joyful!\"  | joy      |\n| \"I am very upset and angry\"  | anger    |\n| \"I adore my friends\"         | love     |\n| \"The night makes me anxious\" | fear     |\n| \"I am shocked by the news\"   | surprise |\n| \"Feeling sad and lonely\"     | sadness  |\n\n---\n\n## Step 1: Convert Text → Features (Bag-of-Words)\n\nTake `\"I am happy today\"` → tokenize, remove stopwords:\n\n```\n['happy', 'today']\n```\n\nFeature dict (all words from dataset considered):\n\n```\n{'happy':1, 'today':1, 'sad':0, 'angry':0, 'love':0, 'scared':0, 'wow':0, 'expect':0, 'ecstatic':0, 'joyful':0, 'upset':0, 'family':0, 'friends':0, 'anxious':0, 'shocked':0, 'lonely':0}\n```\n\n---\n\n## Step 2: Initialize Weights\n\nEach emotion has a weight vector for all words (initialized to 0):\n\n```\nweights_joy = [happy=0, today=0, sad=0, angry=0, love=0, scared=0, wow=0, expect=0, ecstatic=0, joyful=0, upset=0, family=0, friends=0, anxious=0, shocked=0, lonely=0]\nweights_sadness = same...\nweights_anger = same...\nweights_love = same...\nweights_fear = same...\nweights_surprise = same...\n```\n\n---\n\n## Step 3: Training Iterations\n\n### **Iteration 1**: \"I am happy today\" → joy\n\n**Update weights:**\n* Increase weights for the true emotion for the words present.\n* Decrease weights for all other emotions for the words present.\n\n```\nweights_joy      = [happy=2, today=1, ...]\nweights_sadness  = [happy=-1, today=-0.5, ...]\nweights_anger    = [happy=-1, today=-0.5, ...]\nweights_love     = [happy=-0.5, today=-0.2, ...]\nweights_fear     = [happy=-0.5, today=-0.2, ...]\nweights_surprise = [happy=-0.5, today=-0.2, ...]\n```\n\n### **Iteration 2**: \"I feel sad\" → sadness\n\n```\nweights_sadness  = [happy=-1, today=-0.5, sad=2, ...]\nweights_joy      = [happy=2, today=1, sad=-1, ...]\nweights_anger    = [happy=-1, today=-0.5, sad=-1, ...]\nweights_love     = [happy=-0.5, today=-0.2, sad=-0.5, ...]\nweights_fear     = [happy=-0.5, today=-0.2, sad=-0.5, ...]\nweights_surprise = [happy=-0.5, today=-0.2, sad=-0.5, ...]\n```\n\n### **Iteration 3**: \"I am really angry\" → anger\n\n```\nweights_anger   = [angry=2, really=1, ...]\nweights_joy     = decrease weights for angry, really\nweights_sadness = decrease weights for angry, really\nweights_love    = decrease weights for angry, really\nweights_fear    = decrease weights for angry, really\nweights_surprise= decrease weights for angry, really\n```\n\n### **Iteration 4**: \"I love my family\" → love\n\n```\nweights_love    = [love=2, family=1, ...]\n```\n\n### **Iteration 5**: \"I am scared of the dark\" → fear\n\n```\nweights_fear    = [scared=2, dark=1, ...]\n```\n\n### **Iteration 6**: \"Wow, I didn’t expect that!\" → surprise\n\n```\nweights_surprise = [wow=2, expect=1, ...]\n```\n\n### **Iteration 7**: \"I am ecstatic and joyful!\" → joy\n\n```\nweights_joy      = [happy=2, today=1, ecstatic=1, joyful=1, ...]\nweights_other   = decrease weights for ecstatic, joyful\n```\n\n### **Iteration 8**: \"I am very upset and angry\" → anger\n\n```\nweights_anger    = [angry=3, upset=2, ...]\nweights_other    = decrease weights for angry, upset\n```\n\n### **Iteration 9**: \"I adore my friends\" → love\n\n```\nweights_love     = [love=3, family=1, friends=1, ...]\n```\n\n### **Iteration 10**: \"The night makes me anxious\" → fear\n\n```\nweights_fear     = [scared=2, dark=1, anxious=1, ...]\n```\n\n### **Iteration 11**: \"I am shocked by the news\" → surprise\n\n```\nweights_surprise = [wow=2, expect=1, shocked=1, ...]\n```\n\n### **Iteration 12**: \"Feeling sad and lonely\" → sadness\n\n```\nweights_sadness  = [sad=3, lonely=1, ...]\n```\n\n\n## Final Weights Table (Word → Emotion)\n\n| Word      | Joy | Sadness | Anger | Love | Fear | Surprise |\n| --------- | --- | ------- | ----- | ---- | ---- | -------- |\n| happy     | 2   | -1      | -1    | -0.5 | -0.5 | -0.5     |\n| today     | 1   | -0.5    | -0.5  | -0.2 | -0.2 | -0.2     |\n| sad       | -1  | 3       | -1    | -0.5 | -0.5 | -0.5     |\n| angry     | -1  | -1      | 3     | -0.5 | -0.5 | -0.5     |\n| love      | -0.5| -0.5    | -0.5  | 3    | -0.5 | -0.5     |\n| scared    | -0.5| -0.5    | -0.5  | -0.5 | 2    | -0.5     |\n| wow       | -0.5| -0.5    | -0.5  | -0.5 | -0.5 | 2        |\n| expect    | -0.5| -0.5    | -0.5  | -0.5 | -0.5 | 1        |\n| ecstatic  | 1   | -0.5    | -0.5  | -0.5 | -0.5 | -0.5     |\n| joyful    | 1   | -0.5    | -0.5  | -0.5 | -0.5 | -0.5     |\n| upset     | -0.5| -0.5    | 2     | -0.5 | -0.5 | -0.5     |\n| family    | -0.5| -0.5    | -0.5  | 1    | -0.5 | -0.5     |\n| friends   | -0.5| -0.5    | -0.5  | 1    | -0.5 | -0.5     |\n| anxious   | -0.5| -0.5    | -0.5  | -0.5 | 1    | -0.5     |\n| shocked   | -0.5| -0.5    | -0.5  | -0.5 | -0.5 | 1        |\n| lonely    | -0.5| 1       | -0.5  | -0.5 | -0.5 | -0.5     |\n\n\u003e **Note:** Positive values indicate the strength of association between a word and an emotion. Negative values reduce association with other emotions. This table is used for predicting emotions of new sentences.\n\n---\n\n## Step 4: Prediction Formula\n\nWhen a new sentence comes in, the model computes:\n\n```\nscore_emotion = sum(word_count * weight_for_that_emotion)\n```\n\n### Example: Emotion Prediction for a Sentence\n\nSentence: \"I feel so happy and excited today!\"\n\nSteps:\n\n1. Tokenize\n   ['i', 'feel', 'so', 'happy', 'and', 'excited', 'today']\n\n2. Remove stopwords\n   ['happy', 'excited', 'today']\n\n3. Create feature dictionary\n   {'happy': 1, 'excited': 1, 'today': 1}\n\n4. Compute `score_emotion` for each class\n\n| Emotion  | Score Calculation                         | Score |\n| -------- | ----------------------------------------- | ----- |\n| Joy      | 2(happy) + 1(excited) + 1(today)          | 4     |\n| Sadness  | -1(happy) + -0.5(excited) + -0.5(today)   | -2    |\n| Anger    | -1(happy) + -0.5(excited) + -0.5(today)   | -2    |\n| Love     | -0.5(happy) + -0.5(excited) + -0.5(today) | -1.5  |\n| Fear     | -0.5(happy) + -0.5(excited) + -0.5(today) | -1.5  |\n| Surprise | -0.5(happy) + -0.5(excited) + -0.5(today) | -1.5  |\n\nPredicted Emotion: Joy (highest score = 4)\n\n---\n# Evaluation Metrics Explained\n\nThis section provides detailed explanations of all the evaluation metrics used in the training report of the emotion classification model.\n\n---\n\n## 1. Precision\n\n* **Definition:** Measures how many of the predicted instances for a class are actually correct.\n* **Formula:**\n\n```math\nPrecision = True Positives / (True Positives + False Positives)\n```\n\n* **Example:**\n\n  * Model predicts 10 texts as `joy`\n  * 8 of them are actually `joy`\n  * Precision = 8 / 10 = 0.8\n* **Interpretation:** High precision means fewer **false positives**.\n\n---\n\n## 2. Recall (Sensitivity or True Positive Rate)\n\n* **Definition:** Measures how many of the actual instances of a class were correctly predicted.\n* **Formula:**\n\n```math\nRecall = True Positives / (True Positives + False Negatives)\n```\n\n* **Example:**\n\n  * There are 12 actual `joy` texts\n  * Model correctly predicted 8 of them\n  * Recall = 8 / 12 ≈ 0.667\n* **Interpretation:** High recall means fewer **false negatives**.\n\n---\n\n## 3. F1-Score\n\n* **Definition:** Harmonic mean of precision and recall, balancing both metrics.\n* **Formula:**\n\n```math\nF1 = 2 * (Precision * Recall) / (Precision + Recall)\n```\n\n* **Example:**\n\n  * Precision = 0.8, Recall = 0.667\n  * F1 = 2 * (0.8 * 0.667) / (0.8 + 0.667) ≈ 0.727\n* **Interpretation:** High F1-score → model is both accurate and complete for that class.\n\n---\n\n## 4. Support\n\n* **Definition:** Number of actual instances of each class in the dataset.\n* **Example:**\n\n  * If there are 432 `anger` texts in validation set → support = 432\n* **Interpretation:** Helps understand class distribution and performance per class.\n\n---\n\n## 5. Accuracy\n\n* **Definition:** Overall ratio of correct predictions to total predictions.\n* **Formula:**\n\n```math\nAccuracy = Total Correct Predictions / Total Predictions\n```\n\n* **Interpretation:** Overall measure of model correctness.\n\n---\n\n## 6. Macro Average (Macro Avg)\n\n* **Definition:** Average of precision, recall, and F1-score across all classes equally, ignoring class imbalance.\n* **Interpretation:** Treats all classes as equally important.\n\n---\n\n## 7. Weighted Average (Weighted Avg)\n\n* **Definition:** Average of precision, recall, and F1-score weighted by number of instances (support) per class.\n* **Interpretation:** Reflects performance considering class distribution.\n\n---\n\n## 8. Confusion Matrix\n\n* **Definition:** Table showing the counts of actual vs predicted classes.\n* **Structure:** Rows = actual classes, Columns = predicted classes\n* **Example:**\n\n| Actual \\ Predicted | joy | sadness | anger | love | fear | surprise |\n| ------------------ | --- | ------- | ----- | ---- | ---- | -------- |\n| joy                | 8   | 3       | 1     | 0    | 0    | 0        |\n\n* **Interpretation:** Helps identify which classes the model confuses. Each cell indicates how many instances of an actual class were predicted as a particular class.\n\n---\n# Actual Test Report Discussion\n\nThis section explains the training and validation results obtained from the emotion classification model.\n\n---\n\n## Training and Validation Accuracy\n\n* **Training Accuracy:** 0.9817\n\n  * Indicates that the model correctly predicted 98.17% of training examples.\n  * High accuracy shows the model has learned the patterns in the training data well.\n\n* **Validation Accuracy:** 0.8978\n\n  * Indicates 89.78% correct predictions on unseen validation data.\n  * Slightly lower than training accuracy, showing a good balance and not much overfitting.\n\n---\n\n## Classification Report\n\n| Emotion  | Precision | Recall | F1-Score | Support |\n| -------- | --------- | ------ | -------- | ------- |\n| anger    | 0.889     | 0.873  | 0.881    | 432     |\n| fear     | 0.869     | 0.889  | 0.879    | 387     |\n| joy      | 0.901     | 0.927  | 0.914    | 1072    |\n| love     | 0.804     | 0.785  | 0.795    | 261     |\n| sadness  | 0.937     | 0.929  | 0.933    | 933     |\n| surprise | 0.887     | 0.748  | 0.811    | 115     |\n\n### Observations\n\n* **Best performing classes:** `joy` and `sadness` with high precision, recall, and F1-score. The model easily recognizes these due to larger support.\n* **Lower performing class:** `surprise` with lower recall (0.748), likely due to fewer examples and overlapping features with other classes.\n* **Balanced classes:** `anger` and `fear` show balanced performance (precision ~0.87-0.89, recall ~0.87-0.89).\n* **Love:** Moderate performance, possibly due to limited vocabulary and overlap with joy/love sentiments.\n\n---\n\n## Confusion Matrix\n\n```\n[[377   7  21   3  24   0]\n [ 11 344   9   2  14   7]\n [ 18   3 994  39  15   3]\n [  2   1  50 205   3   0]\n [ 16  19  24   6 867   1]\n [  0  22   5   0   2  86]]\n```\n\nConfusion Matrix with Emotion Labels\n\n```\n| Actual \\ Predicted | joy | sadness | anger | love | fear | surprise |\n| ------------------ | --- | ------- | ----- | ---- | ---- | -------- |\n| joy                | 377 | 7       | 21    | 3    | 24   | 0        |\n| sadness            | 11  | 344     | 9     | 2    | 14   | 7        |\n| anger              | 18  | 3       | 994   | 39   | 15   | 3        |\n| love               | 2   | 1       | 50    | 205  | 3    | 0        |\n| fear               | 16  | 19      | 24    | 6    | 867  | 1        |\n| surprise           | 0   | 22      | 5     | 0    | 2    | 86       |\n\n```\n\n\n### Interpretation\n\n* **Rows** = Actual emotions\n* **Columns** = Predicted emotions\n* **Diagonal values** = Correct predictions\n* **Off-diagonal values** = Misclassifications\n\n### Example Insights\n\n* 21 `joy` texts misclassified as `anger`\n* 22 `surprise` texts misclassified as `sadness`\n* Diagonal values indicate how well each class is predicted\n\n---\n\n## Key Takeaways\n\n1. The model is **highly accurate** for common emotions like joy and sadness.\n2. Performance drops for **rare or subtle emotions** like surprise and love.\n3. Confusion matrix provides insight for **future improvements** such as adding more training examples for underrepresented classes.\n4. Overall, the model generalizes well to unseen data with 89.78% validation accuracy.\n\n---\n\n## iOS App Usage\n\n1. Open `EmotionClassifier.xcodeproj` in Xcode 14+\n2. Run on simulator/device\n3. Enter text → tap **Analyze** → shows predicted emotion \u0026 confidence\n\n---\n\n## Tech Stack\n\n* Swift 5 / SwiftUI\n* CoreML\n* MVVM (async/await, actor)\n* Python (Scikit-learn → CoreML)\n\n---\n\n## License\n\nMIT License\n\n---\n\n## Author\n\n* Muhammad Haroon\n* Email: mianmharoon72@gmail.com\n* LinkedIn: https://www.linkedin.com/in/mian-haroon\n\n---\n\n## References\n\n* [CoreML Tools Documentation](https://coremltools.readme.io/)\n* [Emotions Dataset on Kaggle](https://www.kaggle.com/datasets/praveengovi/emotions-dataset-for-nlp/data)\n* [Scikit-learn Documentation](https://scikit-learn.org/)\n* [SwiftUI Documentation](https://developer.apple.com/documentation/swiftui)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmianmharoon%2Fsentimentanalysis_coreml_emotionclassifier","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmianmharoon%2Fsentimentanalysis_coreml_emotionclassifier","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmianmharoon%2Fsentimentanalysis_coreml_emotionclassifier/lists"}