| --- |
| title: Restaurant Inspector API |
| emoji: π½οΈ |
| colorFrom: blue |
| colorTo: green |
| sdk: docker |
| pinned: false |
| --- |
| |
| # π½οΈ Restaurant Inspector |
|
|
| **Production-grade NLP annotation workflow and aspect-based sentiment analysis** for restaurant reviews. |
|
|
| Extracts structured insights across 5 dimensions using DistilBERT with a **human-in-the-loop annotation pipeline**. |
|
|
| ## π Features |
|
|
| - **Multi-Aspect Sentiment**: 4-state labeling (positive/negative/mixed/not_mentioned) for 5 aspects |
| - **Annotation Workflow**: Draft β Review β Approve with full audit trails |
| - **Database-Backed**: PostgreSQL schema with SQLAlchemy ORM + Alembic migrations |
| - **Trained Model**: DistilBERT fine-tuned on 200 professionally approved annotations |
| - **Production-Ready**: FastAPI inference server with logged metrics |
| - **Reproducible**: Version-controlled schema and training pipeline |
| |
| ## π§ Technology Stack |
| |
| - **Model**: DistilBERT-base-uncased (66M parameters, fine-tuned) |
| - **Database**: PostgreSQL (Neon hosted) with SQLAlchemy 2.0 + Alembic |
| - **ML Framework**: Hugging Face Transformers + PyTorch |
| - **API Framework**: FastAPI + Uvicorn |
| - **Data Source**: Yelp Polarity dataset (Hugging Face Datasets) |
| - **Python**: 3.11+ |
| |
| ## οΏ½ Aspect Analysis |
| |
| The model scores reviews across 5 dimensions with 4-state sentiment: |
| |
| | Aspect | States | Description | |
| |--------|--------|-------------| |
| | π **Food** | β
Positive / β Negative / βοΈ Mixed / β Not Mentioned | Quality, taste, freshness | |
| | π₯ **Service** | β
Positive / β Negative / βοΈ Mixed / β Not Mentioned | Staff, speed, attentiveness | |
| | π§Ό **Hygiene** | β
Positive / β Negative / βοΈ Mixed / β Not Mentioned | Cleanliness, sanitation | |
| | π
ΏοΈ **Parking** | β
Positive / β Negative / βοΈ Mixed / β Not Mentioned | Availability, convenience | |
| | β¨ **Cleanliness** | β
Positive / β Negative / βοΈ Mixed / β Not Mentioned | Ambiance, maintenance | |
| |
| ## π¦ Installation |
| |
| ### Prerequisites |
| |
| - Python 3.11+ |
| - PostgreSQL database (we use [Neon](https://neon.tech) for hosted Postgres) |
| - 2GB+ RAM for model training |
| |
| ### 1. Clone Repository |
| |
| ```bash |
| git clone <your-repo-url> |
| cd resturant-inspector-server |
| ``` |
| |
| ### 2. Create Virtual Environment |
| |
| ```bash |
| python -m venv venv |
| |
| # Windows PowerShell: |
| .\venv\Scripts\Activate.ps1 |
| |
| # Linux/Mac: |
| source venv/bin/activate |
| ``` |
| |
| ### 3. Install Dependencies |
| |
| ```bash |
| pip install sqlalchemy alembic psycopg2-binary datasets transformers torch scikit-learn python-dotenv fastapi uvicorn |
| ``` |
| |
| ### 4. Configure Database |
| |
| Create `.env` file: |
| |
| ```env |
| DATABASE_URL=postgresql://user:password@host/database |
| ``` |
| |
| ### 5. Run Migrations |
| |
| ```bash |
| alembic upgrade head |
| ``` |
| |
| ## π― Annotation Workflow |
| |
| ### Step 1: Bootstrap Reviews |
| |
| Load Yelp reviews into database: |
| |
| ```bash |
| $env:PYTHONPATH='.' # Windows PowerShell |
| python scripts/bootstrap_reviews.py --count 300 |
| ``` |
| |
| Result: 300 reviews in `reviews` table |
| |
| ### Step 2: Generate Draft Annotations |
| |
| Create heuristic labels using keyword rules: |
| |
| ```bash |
| $env:PYTHONPATH='.' |
| python scripts/generate_draft_annotations.py --limit 300 --annotator "data_analyst_v1" |
| ``` |
| |
| Result: 300 draft annotations with `status='draft'` |
| |
| ### Step 3: Approve Annotations |
| |
| Review and approve annotations for training: |
| |
| ```bash |
| # View current status |
| $env:PYTHONPATH='.' |
| python scripts/approve_annotations.py --summary |
|
|
| # Approve first 200 drafts |
| python scripts/approve_annotations.py --approve-count 200 --reviewer "senior_analyst_v1" |
| ``` |
| |
| Result: 200 annotations marked `status='approved'` |
| |
| ### Step 4: Train Model |
| |
| Train DistilBERT on approved annotations: |
| |
| ```bash |
| $env:PYTHONPATH='.' |
| python scripts/train.py |
| ``` |
| |
| This will: |
| 1. Load 200 approved annotations from database |
| 2. Split into 120 train / 40 val / 40 test |
| 3. Fine-tune DistilBERT (3 epochs) |
| 4. Evaluate on test set |
| 5. Save model to `models/aspect-classifier/` |
| 6. Log metrics to `training_runs` table |
|
|
| **Training time**: ~10-15 minutes (CPU) or ~2 minutes (GPU) |
|
|
| ## π Running the API Server |
|
|
| ### Start FastAPI Server |
|
|
| ```bash |
| uvicorn main:app --reload |
| ``` |
|
|
| Server runs at: http://localhost:8000 |
|
|
| ### API Documentation |
|
|
| - **Swagger UI**: http://localhost:8000/docs |
| - **ReDoc**: http://localhost:8000/redoc |
|
|
| ## π§ͺ Testing the API |
|
|
| ### Using curl |
|
|
| ```bash |
| curl -X POST "http://localhost:8000/analyze" \ |
| -H "Content-Type: application/json" \ |
| -d '{"text": "Amazing biryani but terrible parking and dirty bathrooms"}' |
| ``` |
|
|
| ### Expected Response |
|
|
| ```json |
| { |
| "food": "positive", |
| "service": "not_mentioned", |
| "hygiene": "negative", |
| "parking": "negative", |
| "cleanliness": "negative" |
| } |
| ``` |
|
|
| ## π¨ Streamlit Admin Dashboard |
|
|
| **NEW!** Visual annotation management and monitoring tool for internal use. |
|
|
| ### Quick Start |
|
|
| ```bash |
| # One-command setup and launch (Windows) |
| .\start_dashboard.ps1 |
| ``` |
|
|
| Or manually: |
|
|
| ```bash |
| # Install Streamlit dependencies |
| pip install streamlit pandas plotly |
| |
| # Apply latest migrations (includes restaurants table) |
| alembic upgrade head |
| |
| # Start dashboard |
| streamlit run streamlit_app/Home.py |
| ``` |
|
|
| Opens at: **http://localhost:8501** |
|
|
| ### Pages |
|
|
| - **π Home**: System overview with quick stats and navigation |
| - **Annotations.py**: Review and approve AI-generated labels |
| - Filter by restaurant, status, date, aspect |
| - View AI predictions for all 5 aspects |
| - Approve/reject individual annotations |
| - Real-time status updates |
| |
| - **Training.py**: Monitor model performance |
| - Training run history with metrics (F1, Precision, Recall) |
| - Performance trend charts over time |
| - Training data quality statistics |
|
|
| ### Database Structure |
|
|
| The dashboard uses a **normalized database** with proper foreign key relationships: |
|
|
| ``` |
| restaurants (master table) |
| βββ id, name, address, phone |
| βββ Referenced by reviews.restaurant_id |
| |
| reviews |
| βββ restaurant_id β restaurants.id |
| βββ Review text + metadata |
| |
| review_annotations |
| βββ review_id β reviews.id |
| βββ Aspect labels + approval status |
| ``` |
|
|
| Current restaurant: **Niloufer** (Hyderabad, India) |
|
|
| ### Workflow |
|
|
| 1. **View Annotations** β Filter and browse AI predictions |
| 2. **Approve/Reject** β Update annotation status in database |
| 3. **Train Model** β Run `python scripts/train.py` in terminal |
| 4. **View Results** β Check Training page for metrics |
|
|
| ### Use Cases |
|
|
| - **Client Demo**: Show professional annotation workflow with visual UI |
| - **Quality Control**: Manual review of model predictions |
| - **Data Curation**: Approve high-quality training data before model training |
| - **Progress Tracking**: Monitor annotation counts and training runs |
|
|
| **Note**: Streamlit runs locally for demos. FastAPI is deployed to Hugging Face Spaces for production inference. |
|
|
| See [`streamlit_app/README.md`](streamlit_app/README.md) for detailed usage guide. |
|
|
| --- |
|
|
| ## π Model Performance |
|
|
| **Current Model** (trained on 200 approved samples): |
|
|
| ``` |
| Training samples: 120 |
| Validation: 40 |
| Test: 40 |
| |
| Test Precision: 9.2% |
| Test Recall: 77.1% |
| Test F1: 16.5% |
| ``` |
|
|
| **Why low precision?** |
| - Small dataset (200 samples total) |
| - Class imbalance (most reviews don't mention all aspects) |
| - Heuristic labels contain noise |
|
|
| **Improvement roadmap**: |
| - Approve 500+ annotations β F1 > 40% |
| - Tune per-aspect decision thresholds |
| - Try RoBERTa or ALBERT |
|
|
| ## π Project Structure |
|
|
| ``` |
| resturant-inspector-server/ |
| βββ alembic/ # Database migrations |
| β βββ versions/ |
| β β βββ 20260323_0001_*.py # Initial schema |
| β β βββ 5eed963bbc03_*.py # Training runs table |
| β βββ env.py |
| βββ app/ |
| β βββ db/ |
| β β βββ models.py # Review, ReviewAnnotation, TrainingRun |
| β β βββ enums.py # AspectState, AnnotationStatus, LabelSource |
| β β βββ session.py # Database session factory |
| β β βββ base.py |
| β βββ core/ |
| β βββ labeling.py # Heuristic labeling logic |
| βββ scripts/ |
| β βββ bootstrap_reviews.py # Load Yelp data |
| β βββ generate_draft_annotations.py # Create draft labels |
| β βββ approve_annotations.py # Approve workflow |
| β βββ train.py # Train DistilBERT |
| βββ models/ |
| β βββ aspect-classifier/ # Trained model outputs |
| β βββ model.safetensors |
| β βββ config.json |
| β βββ tokenizer.json |
| β βββ metadata.json |
| βββ .env # DATABASE_URL |
| βββ alembic.ini |
| βββ PROJECT_STATUS.md # Detailed project documentation |
| βββ README.md |
| ``` |
|
|
| ## ποΈ Database Schema |
|
|
| ### `reviews` |
| Stores raw review text from external sources |
|
|
| ### `review_annotations` |
| Aspect-level annotations with audit trails |
| - **States**: draft β reviewed β approved β rejected |
| - **Sources**: heuristic, manual, heuristic_reviewed |
| - **Tracks**: annotator_name, reviewer_name, timestamps, confidence |
|
|
| ### `training_runs` |
| Logs all model training runs with metrics |
| |
| ## π οΈ Development Commands |
| |
| ### View Training History |
| |
| ```bash |
| $env:PYTHONPATH='.' |
| .\venv\Scripts\python -c "from app.db.session import SessionLocal; from app.db.models import TrainingRun; s = SessionLocal(); [print(f'Run {r.id}: F1={r.test_f1:.4f}') for r in s.query(TrainingRun).all()]; s.close()" |
| ``` |
| |
| ### Check Annotation Status |
| |
| ```bash |
| $env:PYTHONPATH='.' |
| python scripts/approve_annotations.py --summary |
| ``` |
| |
| Output: |
| ``` |
| === Annotation Status Summary === |
| approved: 200 |
| draft: 100 |
| TOTAL: 300 (66.7% approved) |
| ``` |
| |
| ### Approve More Annotations |
| |
| ```bash |
| python scripts/approve_annotations.py --approve-count 50 --reviewer "your_name" |
| ``` |
| |
| ## π Deploying to Production |
| |
| ### Option 1: Render |
| |
| 1. Push to GitHub |
| 2. Create new Web Service on Render |
| 3. Connect your repository |
| 4. Set environment variable: `DATABASE_URL` |
| 5. Build command: `pip install -r requirements.txt` |
| 6. Start command: `uvicorn main:app --host 0.0.0.0 --port $PORT` |
|
|
| ### Option 2: Docker |
|
|
| ```dockerfile |
| FROM python:3.11-slim |
| WORKDIR /app |
| COPY requirements.txt . |
| RUN pip install --no-cache-dir -r requirements.txt |
| COPY . . |
| CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] |
| ``` |
|
|
| ## π§ Troubleshooting |
|
|
| ### ModuleNotFoundError: No module named 'app' |
|
|
| Set PYTHONPATH before running scripts: |
|
|
| ```powershell |
| # Windows PowerShell |
| $env:PYTHONPATH='.' |
| |
| # Linux/Mac |
| export PYTHONPATH=. |
| ``` |
|
|
| ### Database connection fails |
|
|
| Check `.env` file exists and `DATABASE_URL` is correct: |
| ```bash |
| echo $env:DATABASE_URL # Windows |
| echo $DATABASE_URL # Linux/Mac |
| ``` |
|
|
| ### Training runs out of memory |
|
|
| Reduce batch size in `scripts/train.py`: |
| ```python |
| per_device_train_batch_size=4, # default is 8 |
| ``` |
|
|
| ## π Additional Resources |
|
|
| - **[PROJECT_STATUS.md](PROJECT_STATUS.md)** - Detailed project overview and client responses |
| - **Alembic Docs**: https://alembic.sqlalchemy.org/ |
| - **Hugging Face Transformers**: https://huggingface.co/docs/transformers |
| - **FastAPI Docs**: https://fastapi.tiangolo.com/ |
|
|
| ## π€ Contributing |
|
|
| 1. Fork the repository |
| 2. Create feature branch: `git checkout -b feature/new-aspect` |
| 3. Commit changes: `git commit -am 'Add new aspect'` |
| 4. Push: `git push origin feature/new-aspect` |
| 5. Submit Pull Request |
|
|
| ## π License |
|
|
| [Add license info] |
|
|
| ## π€ Contact |
|
|
| **Project**: Restaurant Inspector |
| **Database**: Neon Postgres |
| **Model**: DistilBERT (Hugging Face) |
|
|
| --- |
|
|
| **Built with** Python β’ PostgreSQL β’ Transformers β’ PyTorch β’ FastAPI |
| - **PARKING**: parking, no space |
| - **CLEANLINESS**: clean, messy, well-maintained |
|
|
| ## π Project Structure |
|
|
| ``` |
| resturant-inspector-server/ |
| βββ pyproject.toml # Dependencies |
| βββ train.py # Training script |
| βββ main.py # FastAPI application |
| βββ README.md # This file |
| βββ .gitignore # Git ignore rules |
| βββ venv/ # Virtual environment (not committed) |
| βββ model/ # Trained model (generated, not committed) |
| βββ config.json |
| βββ model.safetensors |
| βββ tokenizer files |
| ``` |
|
|
| ## π€ Contributing |
|
|
| 1. Fork the repository |
| 2. Create a feature branch |
| 3. Make your changes |
| 4. Run linting: `ruff check --fix .` |
| 5. Format code: `ruff format .` |
| 6. Submit a pull request |
|
|
| ## π License |
|
|
| MIT License |
|
|
| ## π Acknowledgments |
|
|
| - Hugging Face for Transformers library |
| - Yelp for the dataset |
| - FastAPI team for the framework |
|
|
| ## π Support |
|
|
| For issues or questions, please open a GitHub issue. |
|
|
| --- |
|
|
| **Built with β€οΈ using Python, FastAPI, and DistilBERT** |
|
|