<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 <a href="http
Step1:  The class <code>plot_error_surfaces</code> is just to help you visualize the data space and the parameter space during training and has nothing to do with PyTorch.
Step2:  <!--Empty Space for separating topics-->
<h2 id="Makeup_Data">Make Some Data</h2>
Import libraries and set random seed.
Step3:  Generate values from -3 to 3 that create a line with a slope of 1 and a bias of -1. This is the line that you need to estimate. Add some noise to the data
Step4:  Create a dataset object
Step5:  Plot out the data and the line.
Step6:  <!--Empty Space for separating topics-->
<h2 id="Model_Cost">Create the Model and Total Loss Function (Cost)</h2>
Create a linear regression class
Step7:  We will use PyTorch build-in functions to create a criterion function; this calculates the total loss or cost
Step8:  Create a linear regression object and optimizer object, the optimizer object will use the linear regression object.
Step9:  Remember to construct an optimizer you have to give it an iterable containing the parameters i.e. provide <code> model.parameters()</code> as an input to the object constructor 
<img src="https
Step10:  PyTorch randomly initialises your model parameters. If we use those parameters, the result will not be very insightful as convergence will be extremely fast. So we will initialise the parameters such that they will take longer to converge, i.e. look cool
Step11:  Create a plotting object, not part of PyTroch, just used to help visualize
Step12:  <!--Empty Space for separating topics-->
<h2 id="BGD">Train the Model via Batch Gradient Descent</h2>
Run 10 epochs of stochastic gradient descent
Step13:  Let's use the following diagram to help clarify the process. The model takes <code>x</code> to produce an estimate <code>yhat</code>, it will then be compared to the actual <code>y</code>  with the loss function.
<img src="https
<ASSISTANT_TASK:>
Python Code:
# These are the libraries we are going to use in the lab.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits import mplot3d
Explanation: <a href="http://cocl.us/pytorch_link_top">
    <img src="https://cocl.us/Pytorch_top" width="750" alt="IBM 10TB Storage" />
</a> 
<img src="https://ibm.box.com/shared/static/ugcqz6ohbvff804xp84y4kqnvvk3bq1g.png" width="200" alt="cognitiveclass.ai logo" />
<h1>Linear Regression 1D: Training Two Parameter Mini-Batch Gradient Descent </h1>
<h2>Table of Contents</h2>
<p>In this lab, you will create a model the PyTroch way, this will help you as models get more complicated</p>
<ul>
    <li><a href="#Makeup_Data">Make Some Data </a></li>
    <li><a href="#Model_Cost">Create the Model and Cost Function the PyTorch way </a></li>
    <li><a href="#BGD">Train the Model: Batch Gradient Descent</a></li>
</ul>
<p>Estimated Time Needed: <strong>30 min</strong></p>
<hr>
<h2>Preparation</h2>
We'll need the following libraries:
End of explanation
# class for ploting  
class plot_error_surfaces(object):
    
    # Constructor
    def __init__(self, w_range, b_range, X, Y, n_samples = 30, go = True):
        W = np.linspace(-w_range, w_range, n_samples)
        B = np.linspace(-b_range, b_range, n_samples)
        w, b = np.meshgrid(W, B)    
        Z = np.zeros((30, 30))
        count1 = 0
        self.y = Y.numpy()
        self.x = X.numpy()
        for w1, b1 in zip(w, b):
            count2 = 0
            for w2, b2 in zip(w1, b1):
                Z[count1, count2] = np.mean((self.y - w2 * self.x + b2) ** 2)
                count2 += 1
            count1 += 1
        self.Z = Z
        self.w = w
        self.b = b
        self.W = []
        self.B = []
        self.LOSS = []
        self.n = 0
        if go == True:
            plt.figure()
            plt.figure(figsize = (7.5, 5))
            plt.axes(projection = '3d').plot_surface(self.w, self.b, self.Z, rstride = 1, cstride = 1, cmap = 'viridis', edgecolor = 'none')
            plt.title('Loss Surface')
            plt.xlabel('w')
            plt.ylabel('b')
            plt.show()
            plt.figure()
            plt.title('Loss Surface Contour')
            plt.xlabel('w')
            plt.ylabel('b')
            plt.contour(self.w, self.b, self.Z)
            plt.show()
            
    # Setter
    def set_para_loss(self, model, loss):
        self.n = self.n + 1
        self.LOSS.append(loss)
        self.W.append(list(model.parameters())[0].item())
        self.B.append(list(model.parameters())[1].item())
    
    # Plot diagram
    def final_plot(self): 
        ax = plt.axes(projection = '3d')
        ax.plot_wireframe(self.w, self.b, self.Z)
        ax.scatter(self.W, self.B, self.LOSS, c = 'r', marker = 'x', s = 200, alpha = 1)
        plt.figure()
        plt.contour(self.w, self.b, self.Z)
        plt.scatter(self.W, self.B, c = 'r', marker = 'x')
        plt.xlabel('w')
        plt.ylabel('b')
        plt.show()
        
    # Plot diagram    
    def plot_ps(self):
        plt.subplot(121)
        plt.ylim()
        plt.plot(self.x, self.y, 'ro', label = "training points")
        plt.plot(self.x, self.W[-1] * self.x + self.B[-1], label = "estimated line")
        plt.xlabel('x')
        plt.ylabel('y')
        plt.ylim((-10, 15))
        plt.title('Data Space Iteration: ' + str(self.n))
        plt.show()
        plt.subplot(122)
        plt.contour(self.w, self.b, self.Z)
        plt.scatter(self.W, self.B, c = 'r', marker = 'x')
        plt.title('Loss Surface Contour Iteration' + str(self.n) )
        plt.xlabel('w')
        plt.ylabel('b')
Explanation: The class <code>plot_error_surfaces</code> is just to help you visualize the data space and the parameter space during training and has nothing to do with PyTorch.
End of explanation
# Import libraries and set random seed
import torch
from torch.utils.data import Dataset, DataLoader
torch.manual_seed(1)
Explanation: <!--Empty Space for separating topics-->
<h2 id="Makeup_Data">Make Some Data</h2>
Import libraries and set random seed.
End of explanation
# Create Data Class
class Data(Dataset):
    
    # Constructor
    def __init__(self):
        self.x = torch.arange(-3, 3, 0.1).view(-1, 1)
        self.f = 1 * self.x - 1
        self.y = self.f + 0.1 * torch.randn(self.x.size())
        self.len = self.x.shape[0]
        
    # Getter
    def __getitem__(self,index):    
        return self.x[index],self.y[index]
    
    # Get Length
    def __len__(self):
        return self.len
Explanation: Generate values from -3 to 3 that create a line with a slope of 1 and a bias of -1. This is the line that you need to estimate. Add some noise to the data:
End of explanation
# Create dataset object
dataset = Data()
Explanation: Create a dataset object:
End of explanation
# Plot the data
plt.plot(dataset.x.numpy(), dataset.y.numpy(), 'rx', label = 'y')
plt.plot(dataset.x.numpy(), dataset.f.numpy(), label = 'f')
plt.xlabel('x')
plt.ylabel('y')
plt.legend()
Explanation: Plot out the data and the line.
End of explanation
# Create a linear regression model class
from torch import nn, optim
class linear_regression(nn.Module):
    
    # Constructor
    def __init__(self, input_size, output_size):
        super(linear_regression, self).__init__()
        self.linear = nn.Linear(input_size, output_size)
        
    # Prediction
    def forward(self, x):
        yhat = self.linear(x)
        return yhat
Explanation: <!--Empty Space for separating topics-->
<h2 id="Model_Cost">Create the Model and Total Loss Function (Cost)</h2>
Create a linear regression class
End of explanation
# Build in cost function
criterion = nn.MSELoss()
Explanation: We will use PyTorch build-in functions to create a criterion function; this calculates the total loss or cost
End of explanation
# Create optimizer
model = linear_regression(1,1)
optimizer = optim.SGD(model.parameters(), lr = 0.01)
Explanation: Create a linear regression object and optimizer object, the optimizer object will use the linear regression object.
End of explanation
# Create Dataloader object
trainloader = DataLoader(dataset = dataset, batch_size = 1)
Explanation: Remember to construct an optimizer you have to give it an iterable containing the parameters i.e. provide <code> model.parameters()</code> as an input to the object constructor 
<img src="https://ibm.box.com/shared/static/f8hskuwrnctjg21agud69ddla0jkbef5.png" width="100" alt="Model Optimizer" />
Create a <code>Dataloader</code> object:
End of explanation
# Customize the weight and bias
model.state_dict()['linear.weight'][0] = -15
model.state_dict()['linear.bias'][0] = -10
Explanation: PyTorch randomly initialises your model parameters. If we use those parameters, the result will not be very insightful as convergence will be extremely fast. So we will initialise the parameters such that they will take longer to converge, i.e. look cool
End of explanation
# Create plot surface object
get_surface = plot_error_surfaces(15, 13, dataset.x, dataset.y, 30, go = False)
Explanation: Create a plotting object, not part of PyTroch, just used to help visualize
End of explanation
# Train Model
def train_model_BGD(iter):
    for epoch in range(iter):
        for x,y in trainloader:
            yhat = model(x)
            loss = criterion(yhat, y)
            get_surface.set_para_loss(model, loss.tolist()) 
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
        get_surface.plot_ps()
train_model_BGD(10)
Explanation: <!--Empty Space for separating topics-->
<h2 id="BGD">Train the Model via Batch Gradient Descent</h2>
Run 10 epochs of stochastic gradient descent: <b>bug</b> data space is 1 iteration ahead of parameter space.
End of explanation
# Practice: Train the model via BGD using optimizer
model = linear_regression(1,1)
model.state_dict()['linear.weight'][0] = -15
model.state_dict()['linear.bias'][0] = -10
get_surface = plot_error_surfaces(15, 13, dataset.x, dataset.y, 30, go = False)
Explanation: Let's use the following diagram to help clarify the process. The model takes <code>x</code> to produce an estimate <code>yhat</code>, it will then be compared to the actual <code>y</code>  with the loss function.
<img src="https://ibm.box.com/shared/static/jo95jdvdfghxtbzwcnh0yig7choxqqxc.png" width="500" alt="Old Model Cost diagram" />
When we call <code>backward()</code> on the loss function, it will handle the differentiation. Calling the method step on the optimizer object it will update the parameters as they were inputs when we constructed the optimizer object. The connection is shown in the following figure :
<img src = "https://ibm.box.com/shared/static/oorp4w5ucfahk3vuf333nt9zrw9hefek.png" width="500" alt="Model Cost with optimizer" />
<!--Empty Space for separating topics-->
<h3>Practice</h3>
Try to train the model via BGD with <code>lr = 0.1</code>. Use <code>optimizer</code> and the following given variables.
End of explanation
<END_TASK>