Spaces:
Sleeping
Sleeping
| # Documentation | |
| ## Week 2: Apartment Predictor (Saved Regression Model + LLM Workflow) | |
| This file documents the apartment rent prediction app that I built, tested, and deployed on Hugging Face Spaces. | |
| --- | |
| ## 1. Project Summary | |
| The app accepts a German natural language apartment request. The user writes a sentence that includes the number of rooms, the apartment size and the town. | |
| The LLM extracts the structured values `rooms`, `area_m2` and `town` from the user text. A saved Random Forest regression model then predicts the estimated monthly rent in CHF. Finally, the LLM generates a short German explanation of the prediction and mentions that the result is only an estimate. | |
| --- | |
| ## 2. Files Used | |
| | File | Purpose | | |
| |---|---| | |
| | `app.py` | Final deployable app used by Hugging Face Spaces | | |
| | `app_student.py` | Student implementation with the completed TODOs | | |
| | `random_forest_regression.pkl` | Saved Random Forest regression model | | |
| | `bfs_municipality_and_tax_data.csv` | Municipality data used for prediction | | |
| | `requirements.txt` | Python dependencies for Hugging Face Spaces | | |
| | `README.md` | Hugging Face Space configuration and short app description | | |
| | `documentation.md` | Written documentation for the submission | | |
| | `screenshot1.png` | Screenshot of the first working app test | | |
| | `screenshot2.png` | Screenshot of the second working app test | | |
| --- | |
| ## 3. Numeric Prediction Part | |
| ### 3.1 Reused Model | |
| I used the saved model file: | |
| `random_forest_regression.pkl` | |
| The model predicts the estimated monthly apartment rent in CHF. | |
| The model uses apartment information and municipality information for the prediction. | |
| ### 3.2 Input Features | |
| The model uses these features: | |
| 1. `rooms` | |
| 2. `area` | |
| 3. `pop` | |
| 4. `pop_dens` | |
| 5. `frg_pct` | |
| 6. `emp` | |
| 7. `tax_income` | |
| In the user interface and in the LLM extraction step, the apartment size is called `area_m2`. | |
| For the model input, I map `area_m2` to the trained model feature `area`, because the saved model expects the feature name `area`. | |
| ### 3.3 Prediction Logic | |
| First, the LLM extracts the number of rooms, the apartment area and the town from the German user text. | |
| Then the app matches the extracted town to the municipality data in `bfs_municipality_and_tax_data.csv`. | |
| After that, the app combines the apartment values with the municipality values and passes them to the saved Random Forest model. | |
| The model returns a monthly rent prediction in CHF. | |
| --- | |
| ## 4. LLM Extraction Part | |
| ### 4.1 Goal | |
| The goal of the first LLM step is to transform a free German apartment request into structured JSON. | |
| The LLM extracts: | |
| - `rooms` | |
| - `area_m2` | |
| - `town` | |
| ### 4.2 Prompt Design | |
| The system prompt tells the LLM to extract apartment preferences from German text. | |
| It requires strict JSON output only, without Markdown or additional explanation. | |
| The required JSON keys are: | |
| - `rooms` | |
| - `area_m2` | |
| - `town` | |
| The prompt also tells the model to use `null` if a value is missing. | |
| ### 4.3 Expected Output Format | |
| Example: | |
| ```json | |
| { | |
| "rooms": 3.5, | |
| "area_m2": 85, | |
| "town": "Winterthur" | |
| } | |
| ``` | |
| ### 4.4 Validation | |
| The app validates the LLM response before using it. | |
| It checks whether the response is valid JSON and whether all required keys are present. | |
| It also checks that rooms, area and town are not missing. | |
| The town is matched against the BFS municipality data before the prediction is made. | |
| --- | |
| ## 5. LLM Explanation Part | |
| ### 5.1 Goal | |
| The second LLM step generates a short German explanation of the model prediction. | |
| The LLM does not calculate the rent itself. | |
| It only explains the numeric prediction returned by the Random Forest model. | |
| ### 5.2 Prompt Design | |
| The explanation prompt includes the extracted apartment preferences and the predicted monthly rent. | |
| The LLM is instructed to answer in German, mention the predicted rent in CHF and include one short uncertainty note. | |
| The response is also required to be valid JSON with the key `answer`. | |
| ### 5.3 Expected Output Format | |
| Example: | |
| ```json | |
| { | |
| "answer": "Die Wohnung mit 3.5 Zimmern und 85 m² in Winterthur hat eine geschätzte Monatsmiete von etwa 2117 CHF. Diese Schätzung basiert auf einem numerischen Modell und kommunalen Daten. Bitte beachten Sie, dass es leichte Abweichungen geben kann." | |
| } | |
| ``` | |
| --- | |
| ## 6. End-to-End Pipeline | |
| The full pipeline works like this: | |
| 1. The user enters a German apartment request. | |
| 2. The LLM extracts `rooms`, `area_m2` and `town`. | |
| 3. Python validates the extracted JSON. | |
| 4. The app matches the town to the municipality data. | |
| 5. The app builds the model input features. | |
| 6. The Random Forest model predicts the estimated monthly rent. | |
| 7. The LLM generates a short German explanation. | |
| 8. The app returns the extracted JSON, the predicted rent and the final explanation. | |
| --- | |
| ## 7. Test Cases | |
| | Test Input | Extracted Output Correct? | Prediction Returned? | Explanation Returned? | Notes | | |
| |---|---|---|---|---| | |
| | `Ich suche eine 3.5-Zimmer-Wohnung mit etwa 85 m2 in Winterthur.` | Yes | Yes | Yes | Extracted 3.5 rooms, 85 m² and Winterthur. Prediction and explanation were returned successfully. | | |
| | `Ich möchte eine 2-Zimmer-Wohnung mit ungefähr 60 m2 in Zürich mieten.` | Yes | Yes | Yes | Extracted 2 rooms, 60 m² and Zürich. Prediction and explanation were returned successfully. | | |
| | `Ich suche eine 4.5-Zimmer-Wohnung mit 110 m2 in Bern.` | Yes | Yes | Yes | This was used as an additional German test prompt to check another Swiss city. | | |
| --- | |
| ## 8. Errors and Problems | |
| ### Problem 1: Missing API key | |
| At first, the app returned an error saying that the API key was missing. | |
| The reason was that the OpenAI API key was not set as an environment variable. | |
| I fixed this by adding the API key as a Hugging Face secret named `OPENAI_API_KEY`. | |
| ### Problem 2: Wrong or invalid API key | |
| On Hugging Face, the app returned an error saying that the API key was incorrect. | |
| The reason was that the API key was copied incorrectly or an invalid key was used. | |
| I fixed this by replacing the secret with a valid key and restarting the Space. | |
| ### Problem 3: Model feature name mismatch | |
| The model returned an error because it expected the feature name `area`, but the app originally passed `area_m2`. | |
| The saved model was trained with the feature name `area`. | |
| I fixed this by mapping the extracted value `area_m2` to the model feature `area`. | |
| ### Problem 4: Model version warning | |
| A warning appeared because the Random Forest model was saved with a different scikit-learn version. | |
| This warning did not stop the app. The model still loaded and returned predictions. | |
| --- | |
| ## 9. Deployment Notes | |
| ### 9.1 Files included | |
| The following files were uploaded to Hugging Face Spaces: | |
| - `app.py` | |
| - `app_student.py` | |
| - `requirements.txt` | |
| - `README.md` | |
| - `documentation.md` | |
| - `bfs_municipality_and_tax_data.csv` | |
| - `random_forest_regression.pkl` | |
| - `screenshot1.png` | |
| - `screenshot2.png` | |
| ### 9.2 Secrets / Environment Variables | |
| The app requires these Hugging Face secrets: | |
| - `OPENAI_API_KEY` | |
| - `OPENAI_MODEL` | |
| The model name used was: | |
| `gpt-4.1-mini` | |
| ### 9.3 Deployment Result | |
| The Hugging Face Space ran successfully. | |
| The app accepted German apartment requests, extracted structured JSON, returned a rent prediction and generated a German explanation. | |
| --- | |
| ## 10. Screenshots | |
| ### Example 1 | |
|  | |
| In this example, the app extracted 3.5 rooms, 85 m² and Winterthur from the German input. It returned a monthly rent prediction and a short German explanation. | |
| ### Example 2 | |
|  | |
| In this example, the app extracted 2 rooms, 60 m² and Zürich from the German input. It returned a monthly rent prediction and explained that the result is an estimate. | |
| --- | |
| ## 11. Reflection | |
| The combination of a regression model and an LLM worked well because both parts have a clear role. | |
| The LLM is useful for understanding natural language and extracting structured values. | |
| The regression model is useful for making the numeric prediction. | |
| The system is still fragile because the LLM could extract values incorrectly or the town might not match the municipality data. | |
| I would improve the app by adding better town suggestions and more apartment features. | |
| --- | |
| ## 12. Responsible Use Note | |
| The prediction is only an estimate and should not be used as a guaranteed rental price. | |
| Real rental prices depend on many additional factors, such as location quality, building condition, floor, balcony, renovation status and market situation. | |
| The LLM may also extract values incorrectly, so the extracted JSON should always be checked. | |
| The app is useful as a learning prototype, but not as a final real estate pricing tool. |