# ============================================================================# CELL 1: Imports and Setup# ============================================================================importtorchimporttorch.nnasnnimporttorch.optimasoptimfromtorch.utils.dataimportDataLoader,TensorDatasetfromsklearn.datasetsimportfetch_california_housingfromsklearn.model_selectionimporttrain_test_splitfromsklearn.preprocessingimportStandardScalerimportnumpyasnpimportmatplotlib.pyplotasplt# Set random seed for reproducibilitytorch.manual_seed(42)np.random.seed(42)
InĀ [2]:
# ============================================================================# CELL 2: Load and Prepare Data# ============================================================================# Load datasethousing=fetch_california_housing()X,y=housing.data,housing.targetprint(f"Dataset shape: {X.shape}")print(f"Features: {housing.feature_names}")print(f"Target range: ${y.min():.2f} - ${y.max():.2f} (in $100,000s)")# Split into train/val/test (55%/15%/30%)X_train,X_temp,y_train,y_temp=train_test_split(X,y,test_size=0.45,random_state=42)X_val,X_test,y_val,y_test=train_test_split(X_temp,y_temp,test_size=0.67,random_state=42)print(f"\nTrain set: {X_train.shape[0]} samples")print(f"Val set: {X_val.shape[0]} samples")print(f"Test set: {X_test.shape[0]} samples")# Standardize featuresscaler=StandardScaler()X_train=scaler.fit_transform(X_train)X_val=scaler.transform(X_val)X_test=scaler.transform(X_test)# Convert to PyTorch tensorsX_train=torch.FloatTensor(X_train)y_train=torch.FloatTensor(y_train).reshape(-1,1)X_val=torch.FloatTensor(X_val)y_val=torch.FloatTensor(y_val).reshape(-1,1)X_test=torch.FloatTensor(X_test)y_test=torch.FloatTensor(y_test).reshape(-1,1)
# ============================================================================# CELL 3: Define Model Architecture# ============================================================================defcreate_model(input_size,hidden_size,dropout_rate):"""Create a simple feedforward neural network"""model=nn.Sequential(nn.Linear(input_size,hidden_size),nn.ReLU(),nn.Dropout(dropout_rate),nn.Linear(hidden_size,hidden_size//2),nn.ReLU(),nn.Dropout(dropout_rate),nn.Linear(hidden_size//2,1))returnmodel
InĀ [4]:
# ============================================================================# CELL 4: Training Function with Early Stopping# ============================================================================deftrain_with_early_stopping(model,train_loader,val_loader,lr,epochs,patience):"""Train model with early stopping"""criterion=nn.MSELoss()optimizer=optim.Adam(model.parameters(),lr=lr)train_losses=[]val_losses=[]best_val_loss=float('inf')patience_counter=0best_model_state=Noneforepochinrange(epochs):# Training phasemodel.train()train_loss=0.0forX_batch,y_batchintrain_loader:optimizer.zero_grad()outputs=model(X_batch)loss=criterion(outputs,y_batch)loss.backward()optimizer.step()train_loss+=loss.item()train_loss/=len(train_loader)train_losses.append(train_loss)# Validation phasemodel.eval()val_loss=0.0withtorch.no_grad():forX_batch,y_batchinval_loader:outputs=model(X_batch)loss=criterion(outputs,y_batch)val_loss+=loss.item()val_loss/=len(val_loader)val_losses.append(val_loss)# Print progressif(epoch+1)%10==0:print(f'Epoch {epoch+1}/{epochs} | Train Loss: {train_loss:.4f} | Val Loss: {val_loss:.4f}')# Early stopping logicifval_loss<best_val_loss:best_val_loss=val_lossbest_model_state=model.state_dict().copy()patience_counter=0else:patience_counter+=1ifpatience_counter>=patience:print(f'\nEarly stopping at epoch {epoch+1}')model.load_state_dict(best_model_state)breakreturntrain_losses,val_losses,best_val_loss
InĀ [5]:
# ============================================================================# CELL 5: Evaluation Metrics# ============================================================================defcalculate_mape(y_true,y_pred):"""Calculate Mean Absolute Percentage Error"""y_true=y_true.numpy()iftorch.is_tensor(y_true)elsey_truey_pred=y_pred.numpy()iftorch.is_tensor(y_pred)elsey_predreturnnp.mean(np.abs((y_true-y_pred)/y_true))*100defevaluate_model(model,X,y):"""Evaluate model and return metrics"""model.eval()withtorch.no_grad():predictions=model(X)mse=nn.MSELoss()(predictions,y).item()rmse=np.sqrt(mse)mape=calculate_mape(y,predictions)returnmse,rmse,mape,predictions
InĀ [6]:
# ============================================================================# CELL 6: Grid Search Parameters (STUDENTS CAN TWEAK HERE!)# ============================================================================# Define hyperparameter gridparam_grid=[{'hidden_size':64,'dropout_rate':0.2,'learning_rate':0.001},{'hidden_size':128,'dropout_rate':0.2,'learning_rate':0.001},{'hidden_size':64,'dropout_rate':0.3,'learning_rate':0.001},{'hidden_size':64,'dropout_rate':0.2,'learning_rate':0.0001},]# Training settingsBATCH_SIZE=32EPOCHS=100PATIENCE=10# Early stopping patienceprint("Grid Search Parameters:")fori,paramsinenumerate(param_grid):print(f" Config {i+1}: {params}")
# ============================================================================# CELL 9: Visualizations# ============================================================================fig,axes=plt.subplots(2,2,figsize=(14,10))# Plot 1: Training history for best modelax1=axes[0,0]ax1.plot(best_result['train_losses'],label='Training Loss',linewidth=2)ax1.plot(best_result['val_losses'],label='Validation Loss',linewidth=2)ax1.set_xlabel('Epoch',fontsize=11)ax1.set_ylabel('Loss (MSE)',fontsize=11)ax1.set_title('Training History - Best Model',fontsize=12,fontweight='bold')ax1.legend()ax1.grid(True,alpha=0.3)# Plot 2: Predictions vs Actualax2=axes[0,1]ax2.scatter(y_test.numpy(),test_pred.numpy(),alpha=0.5,s=20)ax2.plot([y_test.min(),y_test.max()],[y_test.min(),y_test.max()],'r--',lw=2,label='Perfect Prediction')ax2.set_xlabel('Actual Price ($100k)',fontsize=11)ax2.set_ylabel('Predicted Price ($100k)',fontsize=11)ax2.set_title(f'Test Set Predictions (MAPE: {test_mape:.2f}%)',fontsize=12,fontweight='bold')ax2.legend()ax2.grid(True,alpha=0.3)# Plot 3: Residualsax3=axes[1,0]residuals=(y_test.numpy()-test_pred.numpy()).flatten()ax3.scatter(test_pred.numpy(),residuals,alpha=0.5,s=20)ax3.axhline(y=0,color='r',linestyle='--',lw=2)ax3.set_xlabel('Predicted Price ($100k)',fontsize=11)ax3.set_ylabel('Residuals',fontsize=11)ax3.set_title('Residual Plot',fontsize=12,fontweight='bold')ax3.grid(True,alpha=0.3)# Plot 4: Compare all configurationsax4=axes[1,1]forresultinresults:label=f"Config {result['config']}"ax4.plot(result['val_losses'],label=label,linewidth=2)ax4.set_xlabel('Epoch',fontsize=11)ax4.set_ylabel('Validation Loss',fontsize=11)ax4.set_title('All Configurations Comparison',fontsize=12,fontweight='bold')ax4.legend()ax4.grid(True,alpha=0.3)plt.tight_layout()plt.show()print("\n"+"="*60)print("DONE!")print("="*60)
============================================================
DONE! Students can now tweak parameters in CELL 6 and rerun.
============================================================