<jupyter_start><jupyter_text>Exercise 1 Can you write a that fits a 3rd order polynomial to data?    $y = a x^3 + b x^2 + c x + d$- Start by looking up what a PyTorch `Parameter`. This [list of PyTorch building blocks](https://pytorch.org/docs/stable/nn.htmlcontainers) is a good reference.- Decide what your forward function will be -- it should map your inputs to a prediction.- Implement a mean squared error loss function. It should compare your prediction to truth.Bonus: Rewrite your loss function to use an L1 loss. Search  for it [here](https://pytorch.org/docs/stable/nn.functional.html) and include it in your code.Bonus 2:  Add regularization! You've only got 4 parameters -- `a, b, c, d` -- but you can still regularize them!  Boilerplate This sets up a synthetic dataset<jupyter_code>from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
import numpy as np

n = 10000
# X is just a uniformly distributed 1D dataset
X = np.linspace(-50, 100, n)
# The prediction is the image of the cubic transformation
Y = 0.1 * X**3.0 - 9.3 * X**2.0 + 0.1 * X - 0.2 + np.random.normal(size=n) * 2000.0
Y = Y.astype(np.float32)

X_train, X_test, Y_train, Y_test = train_test_split(X, Y)
print(X_train.shape, X_test.shape, Y_train.shape, Y_test.shape)
plt.scatter(X, Y, s = 0.1)<jupyter_output>(7500,) (2500,) (7500,) (2500,)<jupyter_text>Model definition Fill out the code within `__init__` and `forward`.<jupyter_code>import torch
from torch import nn
from torch.nn import functional as F
import pytorch_lightning as pl


class Cubic(pl.LightningModule):
    def __init__(self):
        super(Cubic, self).__init__()
        # **** FILL ME IN *****
    
    def forward(self, x):
        # **** FILL IN LINES BELOW **** 
        return prediction

    def training_step(self, batch, batch_nb):
        x, y = batch
        prediction = self.forward(x)
        # **** FILL IN LINES BELOW **** 
        mse = 0.0 
        return {'loss': mse, 'log': {'mse': mse}}
    
    def configure_optimizers(self):
        return torch.optim.Adam(self.parameters(), lr=1e-2)<jupyter_output><empty_output><jupyter_text>Model instantiation<jupyter_code>from pytorch_lightning.logging import WandbLogger

model = Cubic()

# add a logger
logger = WandbLogger(name="00C_exercise", log_model=True, project="simple_mf")

trainer = pl.Trainer(max_epochs=100, progress_bar_refresh_rate=1,
                     reload_dataloaders_every_epoch=True,
                     logger=logger)<jupyter_output>GPU available: True, used: False
TPU available: False, using: 0 TPU cores<jupyter_text>Fit the model<jupyter_code>model.a, model.b, model.c, model.d<jupyter_output><empty_output><jupyter_text>Review model before it has been fit. As expected, the model and data do not agree at all.<jupyter_code>Y_pred = (model.a.detach().numpy() * X**3.0 
          + model.b.detach().numpy() * X**2.0 
          + model.c.detach().numpy() * X 
          + model.d.detach().numpy())

plt.scatter(X, Y, s=1., label="data")
plt.scatter(X, Y_pred, s=1., c='k', label="prediction")
plt.legend()
import numpy as np
from random import shuffle
from torch import from_numpy
from torch.utils.data import DataLoader
from torch.utils.data import TensorDataset
from torch.utils.data import BatchSampler
from torch.utils.data import RandomSampler

dataset = TensorDataset(torch.from_numpy(X_train), torch.from_numpy(Y_train))
bs = BatchSampler(RandomSampler(dataset), 
                  batch_size=128, drop_last=False)
train_loader = DataLoader(dataset, batch_sampler=bs, num_workers=8)
dataset = TensorDataset(torch.from_numpy(X_test), torch.from_numpy(Y_test))
bs = BatchSampler(RandomSampler(dataset), 
                  batch_size=128, drop_last=False)
test_loader = DataLoader(dataset, batch_sampler=bs, num_workers=8)
trainer.fit(model, train_loader)
model.a, model.b, model.c, model.d<jupyter_output><empty_output><jupyter_text>Plot the model predictions against the training data<jupyter_code>Y_pred = (model.a.detach().numpy() * X**3.0 
          + model.b.detach().numpy() * X**2.0 
          + model.c.detach().numpy() * X 
          + model.d.detach().numpy())

plt.scatter(X, Y, s=0.1)
plt.scatter(X, Y_pred, s=0.1, c='k')<jupyter_output><empty_output><jupyter_text>SOLUTION  Spoiler alert! Don't look here until you're ready..........................<jupyter_code>import torch
from torch import nn
from torch.nn import functional as F
import pytorch_lightning as pl


class Cubic(pl.LightningModule):
    def __init__(self):
        super(Cubic, self).__init__()
        # **** FILL ME IN *****
        self.a = nn.Parameter(torch.rand(1))
        self.b = nn.Parameter(torch.rand(1))
        self.c = nn.Parameter(torch.rand(1))
        self.d = nn.Parameter(torch.rand(1))
    
    def forward(self, x):
        # **** FILL ME IN **** 
        prediction = self.a * x**3.0 + self.b * x**2.0 + self.c * x + self.d
        return prediction

    def training_step(self, batch, batch_nb):
        x, y = batch
        prediction = self.forward(x)
        mse = ((prediction - y)**2.0).mean()
        # And for L1 loss
        mae = F.l1_loss(prediction, y)
        return {'loss': mse, 'log': {'mse': mse}}
    
    def configure_optimizers(self):
        return torch.optim.Adam(self.parameters(), lr=1e-2)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.13196921344096113, 'fraction_numerical': 0.01914773793880233, 'mean_word_length': 3.5538461538461537, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 27, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '7', 'dir': 'jupyter-structured-clean-dedup', 'id': '628949', 'license': 'MIT', 'n_tokens_mistral': 1911, 'n_tokens_neox': 1821, 'n_words': 475, 'path': 'notebooks/00C Exercise.ipynb', 'repo_name': 'cemoody/simple_mf', 'size': '5327'}