House Price Prediction
House Price Prediction
Build an end-to-end Machine Learning regression project that predicts residential property prices from numerical and categorical features using reproducible preprocessing, baseline comparison, Linear Regression, Random Forest, error analysis, and model persistence.
Project Introduction
House price prediction is a supervised Machine Learning problem in which a model learns relationships between property attributes and observed sale prices.
A property may be represented by its living area, number of rooms, age, condition, location, parking capacity, and other characteristics. The model uses those inputs to estimate a continuous numerical target: the expected property price.
Although house price prediction is often introduced as a simple regression exercise, a reliable project requires careful attention to data quality, missing values, categorical encoding, location, changing market conditions, leakage, unusual properties, evaluation design, uncertainty, and production monitoring.
Learning Objectives
By the End of This Project
- Define house price prediction as a regression problem
- Identify numerical, categorical, and target variables
- Inspect data types, distributions, and missing values
- Recognize target leakage
- Create training and test partitions
- Build a median-value baseline
- Impute missing numerical and categorical values
- Encode categorical variables using one-hot encoding
- Use ColumnTransformer and Pipeline
- Train a Linear Regression model
- Train a Random Forest regression model
- Evaluate models using MAE, RMSE, and \(R^2\)
- Interpret residuals and large prediction errors
- Inspect model coefficients and feature importance
- Perform cross-validation correctly
- Avoid preprocessing leakage
- Save the complete fitted pipeline
- Reload the pipeline for inference
- Validate incoming prediction data
- Document limitations and responsible-use requirements
- Define monitoring for a deployed regression model
Prerequisites
Recommended Knowledge
- Python variables, functions, classes, and modules
- NumPy arrays
- pandas DataFrames
- Matplotlib and Seaborn
- Missing-value handling
- Categorical encoding
- Train-test splitting
- Feature engineering
- Linear Regression
- Regression evaluation metrics
- Overfitting and underfitting
- Basic scikit-learn pipelines
Required Packages
- pandas
- NumPy
- Matplotlib
- Seaborn
- scikit-learn
- joblib
python -m pip install pandas numpy matplotlib seaborn scikit-learn joblib
Problem Definition
Let each property contain a feature vector \(x_i\), and let its observed sale price be \(y_i\). The regression model learns a function that estimates price from the available property features.
| Project Element | Definition |
|---|---|
| Input | Property characteristics available at prediction time |
| Target | Observed sale price |
| Task Type | Supervised regression |
| Prediction | Estimated property price |
| Primary Evaluation | Errors on unseen properties |
| Unit of Analysis | One property sale or valuation record |
Expected Dataset
Save an authorized housing dataset as
housing.csv. The examples below expect the following
learning schema.
| Column | Type | Description |
|---|---|---|
price |
Numerical | Observed property price and prediction target |
area_sqft |
Numerical | Property living area in square feet |
bedrooms |
Numerical | Number of bedrooms |
bathrooms |
Numerical | Number of bathrooms |
property_age |
Numerical | Age of the property in years |
lot_area_sqft |
Numerical | Total plot or lot area |
parking_spaces |
Numerical | Number of parking spaces |
location |
Categorical | Neighborhood, district, or locality |
property_type |
Categorical | Apartment, detached house, townhouse, or another type |
condition |
Categorical | Recorded property-condition category |
Data Governance
Review Before Modeling
- Confirm that the dataset is authorized for the intended use
- Record the data source and collection period
- Remove unnecessary personal identifiers
- Restrict access to confidential transaction information
- Review retention and deletion requirements
- Document currency and measurement units
- Identify duplicate or repeated property records
- Document how missing and corrected values were produced
- Review whether geography creates fairness or legal concerns
- Preserve the dataset version used for training
Load the Dataset
from pathlib import Path
import numpy as np
import pandas as pd
data_path = Path(
"housing.csv"
)
if not data_path.exists():
raise FileNotFoundError(
"housing.csv was not found. "
"Add an authorized dataset before running the project."
)
housing = pd.read_csv(
data_path
)
print(
housing.head()
)
print(
housing.shape
)
print(
housing.dtypes
)
Validate the Required Schema
target_column = "price"
numerical_features = [
"area_sqft",
"bedrooms",
"bathrooms",
"property_age",
"lot_area_sqft",
"parking_spaces"
]
categorical_features = [
"location",
"property_type",
"condition"
]
required_columns = {
target_column,
*numerical_features,
*categorical_features
}
missing_columns = (
required_columns
- set(housing.columns)
)
if missing_columns:
raise ValueError(
"The dataset is missing required columns: "
f"{sorted(missing_columns)}"
)
housing = housing[
[
*numerical_features,
*categorical_features,
target_column
]
].copy()
housing[target_column] = pd.to_numeric(
housing[target_column],
errors="coerce"
)
housing = housing.dropna(
subset=[
target_column
]
)
if housing.empty:
raise ValueError(
"No usable rows remain after validating the target."
)
if (
housing[target_column]
<=
0
).any():
raise ValueError(
"The price target contains zero or negative values. "
"Review the source records before training."
)
Initial Data Audit
print(
housing.info()
)
print(
housing.describe(
include="all"
).transpose()
)
missing_summary = (
housing
.isna()
.sum()
.sort_values(
ascending=False
)
)
duplicate_count = housing.duplicated().sum()
print(
"Missing values:"
)
print(
missing_summary
)
print(
"Complete duplicate rows:",
duplicate_count
)
Audit Questions
- Are prices represented in one currency?
- Are all area measurements expressed in the same unit?
- Are room counts plausible?
- Are property ages valid?
- Are locations spelled consistently?
- Do repeated records represent duplicates or separate sales?
- Are missing values random or systematically concentrated?
- Are any columns created after the sale occurred?
Handle Duplicate Records Carefully
Complete duplicate rows may represent accidental duplication, but repeated properties are not automatically duplicates. The same property may be sold more than once.
complete_duplicates = housing[
housing.duplicated(
keep=False
)
]
print(
complete_duplicates.head()
)
Explore the Target Distribution
import matplotlib.pyplot as plt
import seaborn as sns
plt.figure(
figsize=(10, 5)
)
sns.histplot(
data=housing,
x=target_column,
kde=True
)
plt.title(
"House Price Distribution"
)
plt.xlabel(
"Observed Price"
)
plt.ylabel(
"Property Count"
)
plt.tight_layout()
plt.show()
Inspect the Distribution For
- Strong right skew
- Suspiciously repeated prices
- Extreme luxury properties
- Zero or negative values
- Mixed currencies
- Different market periods
- Unusual clusters caused by location
Explore Feature Relationships
plt.figure(
figsize=(9, 6)
)
sns.scatterplot(
data=housing,
x="area_sqft",
y=target_column,
hue="property_type",
alpha=0.7
)
plt.title(
"Living Area and House Price"
)
plt.xlabel(
"Living Area in Square Feet"
)
plt.ylabel(
"Observed Price"
)
plt.tight_layout()
plt.show()
numerical_analysis_columns = [
*numerical_features,
target_column
]
correlation_matrix = (
housing[
numerical_analysis_columns
]
.corr(
numeric_only=True
)
)
plt.figure(
figsize=(10, 7)
)
sns.heatmap(
correlation_matrix,
annot=True,
fmt=".2f",
cmap="coolwarm"
)
plt.title(
"Numerical Feature Correlation"
)
plt.tight_layout()
plt.show()
Prevent Target Leakage
Target leakage occurs when a feature contains information that would not be available when the prediction must be made or contains a direct transformation of the target.
| Potential Leakage Feature | Why It Is Risky |
|---|---|
| Final sale amount component | Directly reveals part of the target |
| Post-sale tax assessment | May be created after the transaction |
| Negotiated discount from final price | Depends on knowing the final price |
| Appraisal performed after sale | May incorporate the observed transaction |
| Future neighborhood statistic | Uses information unavailable at prediction time |
| Dataset-wide target encoding | Can leak validation or test targets into features |
Choose an Evaluation Split
A random split is appropriate only when it represents the intended prediction setting. If the model predicts future transactions, a chronological split may provide a more realistic evaluation.
| Split Strategy | When It May Be Appropriate |
|---|---|
| Random Split | Rows are reasonably independent and deployment resembles the same period and market |
| Chronological Split | The system predicts later sales from earlier sales |
| Grouped Split | Repeated properties, developments, buildings, or sellers must remain together |
| Geographical Holdout | The model must generalize to unseen locations |
from sklearn.model_selection import train_test_split
features = housing[
[
*numerical_features,
*categorical_features
]
]
target = housing[
target_column
]
X_train, X_test, y_train, y_test = train_test_split(
features,
target,
test_size=0.20,
random_state=42
)
print(
"Training rows:",
len(X_train)
)
print(
"Test rows:",
len(X_test)
)
Build a Median Baseline
Before training a complex model, compare it with a simple baseline. The following baseline predicts the median training price for every test property.
from sklearn.metrics import mean_absolute_error
from sklearn.metrics import mean_squared_error
from sklearn.metrics import r2_score
baseline_price = y_train.median()
baseline_predictions = np.full(
shape=len(y_test),
fill_value=baseline_price,
dtype=float
)
baseline_mae = mean_absolute_error(
y_test,
baseline_predictions
)
baseline_rmse = mean_squared_error(
y_test,
baseline_predictions
) ** 0.5
baseline_r2 = r2_score(
y_test,
baseline_predictions
)
print(
"Baseline MAE:",
baseline_mae
)
print(
"Baseline RMSE:",
baseline_rmse
)
print(
"Baseline R2:",
baseline_r2
)
Build the Preprocessing Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder
from sklearn.preprocessing import StandardScaler
numerical_pipeline = Pipeline(
steps=[
(
"imputer",
SimpleImputer(
strategy="median"
)
),
(
"scaler",
StandardScaler()
)
]
)
categorical_pipeline = Pipeline(
steps=[
(
"imputer",
SimpleImputer(
strategy="most_frequent"
)
),
(
"encoder",
OneHotEncoder(
handle_unknown="ignore",
min_frequency=2
)
)
]
)
preprocessor = ColumnTransformer(
transformers=[
(
"numerical",
numerical_pipeline,
numerical_features
),
(
"categorical",
categorical_pipeline,
categorical_features
)
],
remainder="drop"
)
Train Linear Regression
from sklearn.linear_model import LinearRegression
linear_model = Pipeline(
steps=[
(
"preprocessor",
preprocessor
),
(
"regressor",
LinearRegression()
)
]
)
linear_model.fit(
X_train,
y_train
)
linear_predictions = linear_model.predict(
X_test
)
Train Random Forest Regression
from sklearn.ensemble import RandomForestRegressor
random_forest_model = Pipeline(
steps=[
(
"preprocessor",
preprocessor
),
(
"regressor",
RandomForestRegressor(
n_estimators=300,
max_depth=None,
min_samples_leaf=2,
random_state=42,
n_jobs=-1
)
)
]
)
random_forest_model.fit(
X_train,
y_train
)
random_forest_predictions = (
random_forest_model.predict(
X_test
)
)
Linear Regression
- Provides an interpretable baseline
- Models additive linear relationships
- Can extrapolate beyond observed values
- Can be sensitive to outliers and multicollinearity
Random Forest
- Captures nonlinear relationships
- Models feature interactions
- Requires fewer linearity assumptions
- Usually does not extrapolate reliably beyond observed target patterns
Regression Metrics
| Metric | Interpretation | Important Limitation |
|---|---|---|
| MAE | Average absolute prediction error in price units | Treats all absolute errors linearly |
| RMSE | Gives greater influence to large errors | Can be dominated by extreme properties |
| \(R^2\) | Compares error with variation around the target mean | Does not directly describe error in currency units |
Reusable Evaluation Function
def evaluate_regression_model(
model_name,
actual_values,
predicted_values
):
mae = mean_absolute_error(
actual_values,
predicted_values
)
rmse = mean_squared_error(
actual_values,
predicted_values
) ** 0.5
r2 = r2_score(
actual_values,
predicted_values
)
return {
"model": model_name,
"mae": float(mae),
"rmse": float(rmse),
"r2": float(r2)
}
model_results = [
{
"model": "Median Baseline",
"mae": float(baseline_mae),
"rmse": float(baseline_rmse),
"r2": float(baseline_r2)
},
evaluate_regression_model(
model_name="Linear Regression",
actual_values=y_test,
predicted_values=linear_predictions
),
evaluate_regression_model(
model_name="Random Forest",
actual_values=y_test,
predicted_values=(
random_forest_predictions
)
)
]
results_table = pd.DataFrame(
model_results
).sort_values(
by="mae"
)
print(
results_table
)
Actual vs Predicted Values
plt.figure(
figsize=(7, 7)
)
plt.scatter(
y_test,
random_forest_predictions,
alpha=0.65
)
minimum_value = min(
y_test.min(),
random_forest_predictions.min()
)
maximum_value = max(
y_test.max(),
random_forest_predictions.max()
)
plt.plot(
[
minimum_value,
maximum_value
],
[
minimum_value,
maximum_value
],
linestyle="--"
)
plt.title(
"Actual vs Predicted House Prices"
)
plt.xlabel(
"Actual Price"
)
plt.ylabel(
"Predicted Price"
)
plt.tight_layout()
plt.show()
Predictions close to the diagonal reference line are close to the observed values. Systematic deviation from the line may reveal underprediction or overprediction in particular price ranges.
Residual Analysis
A residual is the difference between an observed target and its prediction.
residuals = (
y_test.to_numpy()
- random_forest_predictions
)
plt.figure(
figsize=(9, 5)
)
plt.scatter(
random_forest_predictions,
residuals,
alpha=0.65
)
plt.axhline(
y=0,
linestyle="--"
)
plt.title(
"Residuals vs Predicted Prices"
)
plt.xlabel(
"Predicted Price"
)
plt.ylabel(
"Residual"
)
plt.tight_layout()
plt.show()
| Residual Pattern | Possible Concern |
|---|---|
| Residuals increase with price | Error magnitude may grow for expensive properties |
| Mostly positive residuals | The model tends to underpredict |
| Mostly negative residuals | The model tends to overpredict |
| Curved residual pattern | Important nonlinear structure may remain |
| Separate residual clusters | Location or property groups may behave differently |
| Several extreme residuals | Possible outliers, unusual properties, or data-quality issues |
Inspect the Largest Errors
error_analysis = X_test.copy()
error_analysis[
"actual_price"
] = y_test
error_analysis[
"predicted_price"
] = random_forest_predictions
error_analysis[
"residual"
] = (
error_analysis["actual_price"]
- error_analysis["predicted_price"]
)
error_analysis[
"absolute_error"
] = (
error_analysis["residual"]
.abs()
)
largest_errors = (
error_analysis
.sort_values(
by="absolute_error",
ascending=False
)
.head(20)
)
print(
largest_errors
)
Review Large Errors For
- Incorrect price or area values
- Mixed measurement units
- Unusual luxury or distressed sales
- Rare locations
- Missing important amenities
- Property-condition inconsistencies
- Duplicate or repeated transactions
- Market-period changes
- Transactions with unusual circumstances
Evaluate by Location and Property Type
group_analysis = error_analysis.copy()
group_analysis[
"absolute_error"
] = (
group_analysis["actual_price"]
- group_analysis["predicted_price"]
).abs()
location_performance = (
group_analysis
.groupby(
"location",
dropna=False
)
.agg(
property_count=(
"absolute_error",
"size"
),
mean_absolute_error=(
"absolute_error",
"mean"
),
mean_actual_price=(
"actual_price",
"mean"
)
)
.sort_values(
by="mean_absolute_error",
ascending=False
)
)
print(
location_performance
)
Cross-Validation
Cross-validation evaluates a complete pipeline across several training and validation partitions. Preprocessing must be fitted independently inside each fold.
from sklearn.model_selection import KFold
from sklearn.model_selection import cross_validate
cross_validation = KFold(
n_splits=5,
shuffle=True,
random_state=42
)
scoring = {
"mae": "neg_mean_absolute_error",
"rmse": "neg_root_mean_squared_error",
"r2": "r2"
}
cross_validation_results = cross_validate(
random_forest_model,
features,
target,
cv=cross_validation,
scoring=scoring,
n_jobs=-1,
return_train_score=False
)
cross_validation_summary = {
"mean_mae": float(
-cross_validation_results[
"test_mae"
].mean()
),
"mean_rmse": float(
-cross_validation_results[
"test_rmse"
].mean()
),
"mean_r2": float(
cross_validation_results[
"test_r2"
].mean()
)
}
print(
cross_validation_summary
)
Hyperparameter Tuning
Hyperparameter tuning must use training data only. The final test set should remain untouched until the selected configuration is ready for independent evaluation.
from sklearn.model_selection import RandomizedSearchCV
parameter_distributions = {
"regressor__n_estimators": [
200,
300,
500
],
"regressor__max_depth": [
None,
8,
12,
16
],
"regressor__min_samples_split": [
2,
5,
10
],
"regressor__min_samples_leaf": [
1,
2,
4
],
"regressor__max_features": [
"sqrt",
0.7,
1.0
]
}
search = RandomizedSearchCV(
estimator=random_forest_model,
param_distributions=(
parameter_distributions
),
n_iter=20,
scoring=(
"neg_mean_absolute_error"
),
cv=cross_validation,
random_state=42,
n_jobs=-1,
refit=True
)
search.fit(
X_train,
y_train
)
best_model = search.best_estimator_
best_predictions = best_model.predict(
X_test
)
best_results = evaluate_regression_model(
model_name="Tuned Random Forest",
actual_values=y_test,
predicted_values=best_predictions
)
print(
search.best_params_
)
print(
best_results
)
Feature Importance
fitted_preprocessor = (
best_model.named_steps[
"preprocessor"
]
)
feature_names = (
fitted_preprocessor
.get_feature_names_out()
)
feature_importances = (
best_model.named_steps[
"regressor"
]
.feature_importances_
)
importance_table = (
pd.DataFrame(
{
"feature": feature_names,
"importance": feature_importances
}
)
.sort_values(
by="importance",
ascending=False
)
)
print(
importance_table.head(20)
)
Consider a Log-Transformed Target
When prices are strongly right-skewed, modeling the logarithm of the target may reduce the influence of extremely expensive properties. The final predictions must be converted back to the original price scale.
from sklearn.compose import TransformedTargetRegressor
log_target_model = Pipeline(
steps=[
(
"preprocessor",
preprocessor
),
(
"regressor",
TransformedTargetRegressor(
regressor=RandomForestRegressor(
n_estimators=300,
min_samples_leaf=2,
random_state=42,
n_jobs=-1
),
func=np.log1p,
inverse_func=np.expm1
)
)
]
)
log_target_model.fit(
X_train,
y_train
)
log_target_predictions = (
log_target_model.predict(
X_test
)
)
log_target_results = (
evaluate_regression_model(
model_name=(
"Log-Target Random Forest"
),
actual_values=y_test,
predicted_values=(
log_target_predictions
)
)
)
print(
log_target_results
)
Save the Complete Pipeline
import json
import joblib
import sklearn
model_path = (
"house_price_pipeline.joblib"
)
metadata_path = (
"house_price_metadata.json"
)
joblib.dump(
best_model,
model_path
)
metadata = {
"target_column": target_column,
"numerical_features": numerical_features,
"categorical_features": categorical_features,
"currency": (
"Replace with the dataset currency"
),
"area_unit": "square feet",
"scikit_learn_version": (
sklearn.__version__
),
"test_metrics": best_results
}
with open(
metadata_path,
"w",
encoding="utf-8"
) as metadata_file:
json.dump(
metadata,
metadata_file,
indent=4
)
print(
"Saved model:",
model_path
)
print(
"Saved metadata:",
metadata_path
)
Reload the Pipeline
loaded_model = joblib.load(
"house_price_pipeline.joblib"
)
with open(
"house_price_metadata.json",
"r",
encoding="utf-8"
) as metadata_file:
loaded_metadata = json.load(
metadata_file
)
print(
loaded_metadata
)
Predict a New Property
Replace the following illustrative values with validated application input. The example is not a valuation of a real property.
new_property = pd.DataFrame(
[
{
"area_sqft": 1800,
"bedrooms": 3,
"bathrooms": 2,
"property_age": 8,
"lot_area_sqft": 2400,
"parking_spaces": 1,
"location": "Example Location",
"property_type": "Apartment",
"condition": "Good"
}
]
)
predicted_price = loaded_model.predict(
new_property
)[0]
print(
"Estimated price:",
predicted_price
)
Validate Prediction Input
def validate_property_input(
property_data
):
expected_columns = {
*numerical_features,
*categorical_features
}
received_columns = set(
property_data.columns
)
missing_input_columns = (
expected_columns
- received_columns
)
unexpected_columns = (
received_columns
- expected_columns
)
if missing_input_columns:
raise ValueError(
"Missing prediction columns: "
f"{sorted(missing_input_columns)}"
)
if unexpected_columns:
raise ValueError(
"Unexpected prediction columns: "
f"{sorted(unexpected_columns)}"
)
if (
property_data["area_sqft"]
<=
0
).any():
raise ValueError(
"area_sqft must be greater than zero."
)
if (
property_data["bedrooms"]
<
0
).any():
raise ValueError(
"bedrooms cannot be negative."
)
if (
property_data["bathrooms"]
<
0
).any():
raise ValueError(
"bathrooms cannot be negative."
)
if (
property_data["property_age"]
<
0
).any():
raise ValueError(
"property_age cannot be negative."
)
return property_data[
[
*numerical_features,
*categorical_features
]
]
validated_property = validate_property_input(
new_property
)
validated_prediction = loaded_model.predict(
validated_property
)[0]
print(
"Validated estimated price:",
validated_prediction
)
Prediction Is Not a Formal Valuation
A model prediction is an estimate based on patterns in its training data. It does not automatically include all property details, transaction circumstances, legal conditions, structural defects, current negotiations, local regulations, or recent market events.
Common Project Mistakes
Responsible AI Considerations
Review Requirements
- Document the intended and prohibited uses
- Review whether historical prices reflect unfair practices
- Evaluate errors across relevant locations and property groups
- Avoid using protected characteristics as decision shortcuts
- Review proxy variables carefully
- Protect personal and transaction information
- Provide human review for consequential decisions
- Allow correction of inaccurate input data
- Document model limitations
- Monitor changing market conditions
Production Monitoring
| Monitoring Area | Example Measure |
|---|---|
| Input Quality | Missing, invalid, or out-of-range values |
| Category Drift | New or changing locations and property types |
| Feature Drift | Changes in area, age, room count, or property mix |
| Prediction Distribution | Changes in estimated-price range |
| Delayed Accuracy | MAE and RMSE after actual prices become available |
| Group Performance | Error by location, type, and price range |
| Latency | Time required to validate and predict |
| Model Usage | Prediction volume, failures, and manual overrides |
| Market Change | Difference between training conditions and current transactions |
Recommended Project Workflow
Define the Prediction
Specify the target value, prediction time, user, decision, currency, market, and acceptable error.
Audit and Version the Data
Review authorization, units, missing values, duplicates, geography, time coverage, and target quality.
Prevent Leakage
Remove post-sale and future-derived features before model development.
Select a Realistic Split
Use random, chronological, grouped, or geographic evaluation according to deployment.
Build a Baseline
Evaluate a median or another simple reference prediction.
Build the Pipeline
Combine imputation, scaling, encoding, and regression in one reproducible workflow.
Compare Models
Compare interpretable and nonlinear models under identical validation conditions.
Analyze Errors
Review residuals, large errors, locations, property groups, and unusual transactions.
Save and Test Artifacts
Save the complete pipeline, metadata, schema, metrics, and dependency information.
Deploy Responsibly
Validate input, communicate uncertainty, preserve human review, monitor drift, and define rollback conditions.
Hands-On Practice
Adapt the Schema
Map the columns in an authorized dataset to numerical features, categorical features, and the target.
Create Data-Quality Rules
Define valid ranges and units for price, area, rooms, age, and parking.
Compare Split Strategies
Compare random and chronological evaluation where transaction dates are available.
Compare Three Models
Compare a baseline, Linear Regression, and Random Forest using MAE, RMSE, and \(R^2\).
Analyze Residuals
Plot residuals and inspect the twenty properties with the largest absolute errors.
Evaluate Groups
Compare error by location, property type, and target-price range.
Save and Reload the Pipeline
Verify that predictions remain consistent after artifact restoration.
Create a Model Card
Document the purpose, data, metrics, intended use, limitations, risks, and monitoring requirements.
Knowledge Check
Why is house price prediction a regression problem?
Explain why the target is a continuous numerical quantity.
Why is a baseline model required?
Explain how it establishes whether Machine Learning adds useful predictive value.
What is target leakage?
Explain how future or post-sale information can create unrealistically strong evaluation results.
Why place preprocessing inside a pipeline?
Explain leakage prevention and consistent transformation during training and inference.
What does MAE measure?
Explain average absolute error in the original target unit.
Why is RMSE sensitive to large errors?
Explain the effect of squaring each residual.
Why should residuals be analyzed?
Explain systematic underprediction, overprediction, heteroscedasticity, and unusual property errors.
Why might a chronological split be preferable?
Explain how it evaluates prediction of later transactions from earlier market information.
Why save the complete fitted pipeline?
Explain the need to preserve preprocessing and prediction logic together.
Why is a model estimate not a guaranteed property valuation?
Explain data limitations, market change, missing property details, unusual transactions, uncertainty, and professional review.
Project Summary
| Project Stage | Main Outcome |
|---|---|
| Problem Definition | Defines the target, prediction time, user, and acceptable error |
| Data Audit | Reviews schema, missing values, duplicates, units, and quality |
| Leakage Review | Removes future and post-sale information |
| Evaluation Split | Creates realistic independent evaluation data |
| Baseline | Provides a simple reference prediction |
| Preprocessing | Imputes, scales, and encodes features consistently |
| Linear Regression | Provides an interpretable model baseline |
| Random Forest | Models nonlinear relationships and interactions |
| Evaluation | Measures MAE, RMSE, \(R^2\), residuals, and group errors |
| Persistence | Saves the complete fitted pipeline and metadata |
| Inference | Validates new data before producing an estimate |
| Monitoring | Tracks data quality, drift, delayed error, usage, and failures |
Key Takeaway
A reliable house price prediction project requires more than fitting a regression algorithm. You must define the valuation target, audit the data, prevent leakage, choose a split that reflects deployment, establish a baseline, preserve preprocessing inside a pipeline, compare models using currency-based errors, inspect residuals and important groups, save complete artifacts, validate inference inputs, communicate limitations, and monitor changing market conditions. The resulting prediction is an estimate supported by historical data, not a guaranteed sale price or a substitute for appropriate professional judgment.
Get Machine Learning updates by email
Short, practical lessons and course updates from Rumman Ansari. No spam.