{"id":51018636,"url":"https://github.com/jpcodes44/mse446_nanoparticle_ordering","last_synced_at":"2026-06-21T14:01:28.198Z","repository":{"id":362380610,"uuid":"1258776895","full_name":"JPCodes44/MSE446_Nanoparticle_Ordering","owner":"JPCodes44","description":"🔬 MSE446 Final Machine learning project for classifying ordered vs. disordered palladium nanoparticle structures in cropped SEM micrographs using engineered image features, model comparison, and reproducible Python workflows.","archived":false,"fork":false,"pushed_at":"2026-06-04T01:08:25.000Z","size":50,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-04T02:05:28.827Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Jupyter Notebook","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/JPCodes44.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-06-03T23:00:30.000Z","updated_at":"2026-06-04T01:08:29.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/JPCodes44/MSE446_Nanoparticle_Ordering","commit_stats":null,"previous_names":["jpcodes44/mse446_nanoparticle_ordering"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/JPCodes44/MSE446_Nanoparticle_Ordering","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JPCodes44%2FMSE446_Nanoparticle_Ordering","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JPCodes44%2FMSE446_Nanoparticle_Ordering/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JPCodes44%2FMSE446_Nanoparticle_Ordering/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JPCodes44%2FMSE446_Nanoparticle_Ordering/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/JPCodes44","download_url":"https://codeload.github.com/JPCodes44/MSE446_Nanoparticle_Ordering/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JPCodes44%2FMSE446_Nanoparticle_Ordering/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34610832,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-21T02:00:05.568Z","response_time":54,"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":[],"created_at":"2026-06-21T14:01:26.998Z","updated_at":"2026-06-21T14:01:28.193Z","avatar_url":"https://github.com/JPCodes44.png","language":"Jupyter Notebook","funding_links":[],"categories":[],"sub_categories":[],"readme":"# MSE446 Palladium Nanoparticle Ordering\n\nClassical machine learning workflow for classifying cropped SEM micrographs of palladium nanoparticles on carbon as `ordered` or `disordered`.\n\nThe first pass uses engineered image features only. Filename metadata is used for parsing labels, auditing the dataset, and group-aware train/test splitting, but not as model input.\n\n## Project Structure\n\n```text\n.\n├── README.md\n├── requirements.txt\n├── src/\n│   ├── config.py\n│   ├── parse_metadata.py\n│   ├── extract_basic_features.py\n│   ├── extract_component_features.py\n│   ├── extract_hog_features.py\n│   ├── extract_features.py\n│   ├── train_dummy_baseline.py\n│   ├── train_logistic_regression.py\n│   ├── train_decision_tree.py\n│   ├── train_random_forest.py\n│   ├── train_gaussian_nb.py\n│   ├── train_knn.py\n│   ├── train_svc.py\n│   ├── train_svc_components.py\n│   ├── train_svc_hog.py\n│   └── evaluate.py\n├── notebooks/\n│   ├── 00_template.ipynb\n│   ├── 01_dataset_audit.ipynb\n│   ├── 02_feature_extraction.ipynb\n│   ├── 03_dummy_baseline.ipynb\n│   ├── 04_logistic_regression_basic_features.ipynb\n│   ├── 05_decision_tree_basic_features.ipynb\n│   ├── 06_random_forest_basic_features.ipynb\n│   ├── 07_gaussian_nb_basic_features.ipynb\n│   ├── 08_knn_basic_features.ipynb\n│   ├── 09_svc_basic_features.ipynb\n│   ├── 10_svc_hog_features.ipynb\n│   └── 11_svc_component_features.ipynb\n├── data/\n│   └── .gitkeep\n└── results/\n    ├── figures/\n    └── .gitkeep\n```\n\n## Data Layout\n\nPlace the cropped TIFF images locally at:\n\n```text\ndata/flat_with_kv_mm_filenames_cropped/\n```\n\nDo not commit raw or cropped `.tif` / `.tiff` files. They are ignored by Git.\n\nExpected filename format:\n\n```text\nkv-10p0kV__mm-6p4mm__label-disordered__sample-S5__area-A56__mag-100k__id-33__orig-33-S5-A56-100k-disordered.tif\n```\n\n## Setup\n\n```bash\npython -m venv .venv\nsource .venv/bin/activate\npip install -r requirements.txt\n```\n\n## Dummy baseline\n\nRun:\n\n```bash\npython src/parse_metadata.py\npython src/train_dummy_baseline.py\n```\n\nThis trains only the majority-class `DummyClassifier` baseline. Raw accuracy is expected to be misleading because the dataset is imbalanced, so use balanced accuracy and macro F1 when deciding whether future image-derived models improve on this baseline.\n\nThe dummy classifier ignores `X`, so the script uses a one-column placeholder feature matrix. Labels come from parsed filenames, and the train/test split uses `area_group = sample + \"__\" + area` to avoid repeated-area leakage.\n\n## Logistic Regression with basic image features\n\nRun:\n\n```bash\npython src/parse_metadata.py\npython src/train_dummy_baseline.py\npython src/extract_basic_features.py\npython src/train_logistic_regression.py\npython src/train_decision_tree.py\npython src/train_random_forest.py\npython src/train_gaussian_nb.py\npython src/train_knn.py\npython src/train_svc.py\npython src/extract_hog_features.py\npython src/train_svc_hog.py\npython src/extract_component_features.py\npython src/train_svc_components.py\n```\n\nThis is the first real supervised model. It uses only basic image-derived features from resized grayscale crops, then trains `LogisticRegression(class_weight=\"balanced\", max_iter=2000)` with `StandardScaler`. Metadata is retained for labels, grouping, and auditing, but metadata columns are not used as predictors.\n\n`train_decision_tree.py` trains an untuned `DecisionTreeClassifier(random_state=42, class_weight=\"balanced\")`, then tunes the same model family with grouped cross-validation on the training split. It prints train-vs-test metrics for both trees to check whether tuning reduces overfitting.\n\n`train_random_forest.py` trains `RandomForestClassifier(n_estimators=200, class_weight=\"balanced\", random_state=42)` on the same basic feature table and grouped split logic. It then tunes only the Random Forest model family with `GridSearchCV` and `GroupKFold` on the training split, using macro F1 as the score. It also saves impurity-based feature importance outputs for both the basic and tuned forests.\n\n`train_gaussian_nb.py` trains `GaussianNB` on the same basic image feature columns and grouped split. Gaussian Naive Bayes is a probability-based baseline that assumes numeric features are conditionally independent and approximately normally distributed within each class, which is probably weak for image features. It is included as a simple course-aligned comparison model.\n\n`train_knn.py` trains a scaled `KNeighborsClassifier` baseline and then tunes only KNN with grouped cross-validation on the training split. KNN is a similarity-based model that predicts from nearby examples in scaled feature space, so `StandardScaler` is required before distance calculations.\n\n`train_svc.py` trains a scaled `SVC(kernel=\"rbf\", class_weight=\"balanced\")` baseline and then tunes only SVC with grouped cross-validation on the training split. SVC finds a maximum-margin decision boundary; the linear kernel tests a linear boundary, while the RBF kernel can model nonlinear boundaries in the scaled feature space.\n\n`extract_hog_features.py` extracts HOG descriptors from resized grayscale crops and combines them with the existing basic image features. `train_svc_hog.py` then tunes SVC on the combined basic + HOG feature table. HOG is an engineered image feature that captures local edge direction and shape structure.\n\n`extract_component_features.py` extracts connected-component features from bright particle-like regions and combines them with the existing basic image features. `train_svc_components.py` then tunes SVC on the combined basic + component feature table. Connected-component features approximate particle/blob and aggregate structure.\n\n## Run The Workflow\n\nStart Jupyter:\n\n```bash\njupyter notebook\n```\n\nRun notebooks in order:\n\n1. `notebooks/01_dataset_audit.ipynb`\n2. `notebooks/03_dummy_baseline.ipynb`\n3. `notebooks/04_logistic_regression_basic_features.ipynb`\n4. `notebooks/05_decision_tree_basic_features.ipynb`\n5. `notebooks/06_random_forest_basic_features.ipynb`\n6. `notebooks/07_gaussian_nb_basic_features.ipynb`\n7. `notebooks/08_knn_basic_features.ipynb`\n8. `notebooks/09_svc_basic_features.ipynb`\n9. `notebooks/10_svc_hog_features.ipynb`\n10. `notebooks/11_svc_component_features.ipynb`\n\n## Outputs\n\nGenerated files are intentionally ignored:\n\n- `data/dataset_metadata.csv`\n- `data/features.csv`\n- `data/features_basic.csv`\n- `data/features_hog.csv`\n- `data/features_basic_hog.csv`\n- `data/features_components.csv`\n- `data/features_basic_components.csv`\n- `results/dataset_summary.csv`\n- `results/parameter_group_counts.csv`\n- `results/model_scores_dummy.csv`\n- `results/model_scores_logistic_basic.csv`\n- `results/model_scores_decision_tree_basic.csv`\n- `results/model_scores_decision_tree_tuned.csv`\n- `results/model_scores_random_forest_basic.csv`\n- `results/model_scores_random_forest_tuned.csv`\n- `results/model_scores_gaussian_nb_basic.csv`\n- `results/model_scores_knn_basic.csv`\n- `results/model_scores_knn_tuned.csv`\n- `results/model_scores_svc_basic.csv`\n- `results/model_scores_svc_tuned.csv`\n- `results/model_scores_svc_basic_hog.csv`\n- `results/svc_basic_hog_best_params.json`\n- `results/model_scores_svc_basic_components.csv`\n- `results/svc_basic_components_best_params.json`\n- `results/feature_importance_random_forest_basic.csv`\n- `results/feature_importance_random_forest_tuned.csv`\n- `results/figures/*.png`\n\n## Modeling Notes\n\nThe current modeling milestones are `DummyClassifier(strategy=\"most_frequent\")`, Logistic Regression on basic image features, Decision Tree on the same basic features, Random Forest on the same basic features, Gaussian Naive Bayes on the same basic features, KNN on the same basic features, SVC on the same basic features, tuned SVC on combined basic + HOG features, and tuned SVC on combined basic + connected-component features. The Decision Tree, Random Forest, KNN, and SVC experiments include simple grouped hyperparameter tuning. CNNs, graph features, and augmentation are intentionally out of scope until later project steps.\n\nThe split uses `sample + area` groups so repeated crops or magnifications from the same area do not leak across train and test sets. Metadata columns such as `kv`, `mm`, `mag`, `sample`, and `area` are excluded from model features.\n\n## Lightweight Checks\n\n```bash\npython -m compileall src\npython - \u003c\u003c'PY'\nfrom src.config import get_image_dir\nfrom src.parse_metadata import build_metadata_table\n\nmetadata = build_metadata_table(get_image_dir())\nprint(len(metadata))\nprint(metadata[\"label\"].value_counts().sort_index())\nPY\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjpcodes44%2Fmse446_nanoparticle_ordering","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjpcodes44%2Fmse446_nanoparticle_ordering","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjpcodes44%2Fmse446_nanoparticle_ordering/lists"}