In this notebook you will compare different regression models in order to assess which model fits best. We will be using polynomial regression as a means to examine this topic. In particular you will:
We will continue to use the House data from previous notebooks.
In [1]:
import graphlab
Next we're going to write a polynomial function that takes an SArray and a maximal degree and returns an SFrame with columns containing the SArray to all the powers up to the maximal degree.
The easiest way to apply a power to an SArray is to use the .apply() and lambda x: functions. For example to take the example array and compute the third power we can do as follows: (note running this cell the first time may take longer than expected since it loads graphlab)
In [2]:
tmp = graphlab.SArray([1., 2., 3.])
tmp_cubed = tmp.apply(lambda x: x**3)
print tmp
print tmp_cubed
We can create an empty SFrame using graphlab.SFrame() and then add any columns to it with ex_sframe['column_name'] = value. For example we create an empty SFrame and make the column 'power_1' to be the first power of tmp (i.e. tmp itself).
In [3]:
ex_sframe = graphlab.SFrame()
ex_sframe['power_1'] = tmp
print ex_sframe
Using the hints above complete the following function to create an SFrame consisting of the powers of an SArray up to a specific degree:
In [15]:
def polynomial_sframe(feature, degree):
# assume that degree >= 1
# initialize the SFrame:
poly_sframe = graphlab.SFrame()
# and set poly_sframe['power_1'] equal to the passed feature
poly_sframe['power_1'] = feature
# first check if degree > 1
if degree > 1:
# then loop over the remaining degrees:
# range usually starts at 0 and stops at the endpoint-1. We want it to start at 2 and stop at degree
for power in range(2, degree + 1):
# first we'll give the column a name:
name = 'power_' + str(power)
# then assign poly_sframe[name] to the appropriate power of feature
poly_sframe[name] = feature.apply(lambda x: x**power)
return poly_sframe
To test your function consider the smaller tmp variable and what you would expect the outcome of the following call:
In [16]:
print polynomial_sframe(tmp, 3)
Let's use matplotlib to visualize what a polynomial regression looks like on some real data.
In [17]:
sales = graphlab.SFrame('kc_house_data.gl/')
As in Week 3, we will use the sqft_living variable. For plotting purposes (connecting the dots), you'll need to sort by the values of sqft_living. For houses with identical square footage, we break the tie by their prices.
In [18]:
sales = sales.sort(['sqft_living', 'price'])
Let's start with a degree 1 polynomial using 'sqft_living' (i.e. a line) to predict 'price' and plot what it looks like.
In [20]:
poly1_data = polynomial_sframe(sales['sqft_living'], 1)
poly1_data['price'] = sales['price'] # add price to the data since it's the target
NOTE: for all the models in this notebook use validation_set = None to ensure that all results are consistent across users.
In [21]:
model1 = graphlab.linear_regression.create(poly1_data, target = 'price', features = ['power_1'], validation_set = None)
In [22]:
#let's take a look at the weights before we plot
model1.get("coefficients")
Out[22]:
In [23]:
import matplotlib.pyplot as plt
%matplotlib inline
In [25]:
plt.plot(poly1_data['power_1'], poly1_data['price'], '.', poly1_data['power_1'], model1.predict(poly1_data), '-')
Out[25]:
Let's unpack that plt.plot() command. The first pair of SArrays we passed are the 1st power of sqft and the actual price we then ask it to print these as dots '.'. The next pair we pass is the 1st power of sqft and the predicted values from the linear model. We ask these to be plotted as a line '-'.
We can see, not surprisingly, that the predicted values all fall on a line, specifically the one with slope 280 and intercept -43579. What if we wanted to plot a second degree polynomial?
In [26]:
poly2_data = polynomial_sframe(sales['sqft_living'], 2)
my_features = poly2_data.column_names() # get the name of the features
poly2_data['price'] = sales['price'] # add price to the data since it's the target
model2 = graphlab.linear_regression.create(poly2_data, target = 'price', features = my_features, validation_set = None)
In [27]:
model2.get("coefficients")
Out[27]:
In [28]:
plt.plot(poly2_data['power_1'], poly2_data['price'], '.', poly2_data['power_1'], model2.predict(poly2_data), '-')
Out[28]:
The resulting model looks like half a parabola. Try on your own to see what the cubic looks like:
In [29]:
poly3_data = polynomial_sframe(sales['sqft_living'], 3)
my_features = poly3_data.column_names()
poly3_data['price'] = sales['price']
model3 = graphlab.linear_regression.create(poly3_data, target = 'price', features = my_features, validation_set = None)
In [30]:
plt.plot(poly3_data['power_1'], poly3_data['price'], '.', poly3_data['power_1'], model3.predict(poly3_data), '-')
Out[30]:
Now try a 15th degree polynomial:
In [35]:
poly15_data = polynomial_sframe(sales['sqft_living'], 15)
my_features = poly15_data.column_names()
poly15_data['price'] = sales['price']
model15 = graphlab.linear_regression.create(poly15_data, target = 'price', features = my_features, validation_set = None)
In [36]:
model15.get("coefficients")
Out[36]:
In [ ]:
plt.plot(poly15_data['power_1'], poly15_data['price'], '.', poly15_data['power_1'], model5.predict(poly15_data), '-')
What do you think of the 15th degree polynomial? Do you think this is appropriate? If we were to change the data do you think you'd get pretty much the same curve? Let's take a look.
We're going to split the sales data into four subsets of roughly equal size. Then you will estimate a 15th degree polynomial model on all four subsets of the data. Print the coefficients (you should use .print_rows(num_rows = 16) to view all of them) and plot the resulting fit (as we did above). The quiz will ask you some questions about these results.
To split the sales data into four subsets, we perform the following steps:
.random_split(0.5, seed=0)
. .random_split(0.5, seed=0)
.We set seed=0
in these steps so that different users get consistent results.
You should end up with 4 subsets (set_1
, set_2
, set_3
, set_4
) of approximately equal size.
In [32]:
dtmp0,dtmp1 = sales.random_split(.5,seed=0)
set_1,set_2 = dtmp0.random_split(.5,seed=0)
set_3,set_4 = dtmp1.random_split(.5,seed=0)
Fit a 15th degree polynomial on set_1, set_2, set_3, and set_4 using sqft_living to predict prices. Print the coefficients and make a plot of the resulting model.
In [47]:
plt.plot(poly15_set_1_data['power_1'], poly15_set_1_data['price'], '.', poly15_set_1_data['power_1'], set_1_model15.predict(poly15_set_1_data), '-')
Out[47]:
In [49]:
poly15_set_2_data = polynomial_sframe(set_2['sqft_living'], 15)
my_features_set_2 = poly15_set_2_data.column_names()
poly15_set_2_data['price'] = set_2['price']
set_2_model15 = graphlab.linear_regression.create(poly15_set_2_data, target = 'price', features = my_features_set_2, validation_set = None)
set_2_model15.get("coefficients").print_rows(num_rows = 16)
In [50]:
plt.plot(poly15_set_2_data['power_1'], poly15_set_2_data['price'], '.', poly15_set_2_data['power_1'], set_2_model15.predict(poly15_set_2_data), '-')
Out[50]:
In [52]:
poly15_set_3_data = polynomial_sframe(set_3['sqft_living'], 15)
my_features_set_3 = poly15_set_3_data.column_names()
poly15_set_3_data['price'] = set_3['price']
set_3_model15 = graphlab.linear_regression.create(poly15_set_3_data, target = 'price', features = my_features_set_3, validation_set = None)
set_3_model15.get("coefficients").print_rows(num_rows = 16)
In [51]:
plt.plot(poly15_set_3_data['power_1'], poly15_set_3_data['price'], '.', poly15_set_3_data['power_1'], set_3_model15.predict(poly15_set_3_data), '-')
Out[51]:
In [53]:
poly15_set_4_data = polynomial_sframe(set_4['sqft_living'], 15)
my_features_set_4 = poly15_set_4_data.column_names()
poly15_set_4_data['price'] = set_4['price']
set_4_model15 = graphlab.linear_regression.create(poly15_set_4_data, target = 'price', features = my_features_set_4, validation_set = None)
set_4_model15.get("coefficients").print_rows(num_rows = 16)
In [54]:
plt.plot(poly15_set_4_data['power_1'], poly15_set_4_data['price'], '.', poly15_set_4_data['power_1'], set_4_model15.predict(poly15_set_4_data), '-')
Out[54]:
Some questions you will be asked on your quiz:
Quiz Question: Is the sign (positive or negative) for power_15 the same in all four models?
Quiz Question: (True/False) the plotted fitted lines look the same in all four plots
Whenever we have a "magic" parameter like the degree of the polynomial there is one well-known way to select these parameters: validation set. (We will explore another approach in week 4).
We split the sales dataset 3-way into training set, test set, and validation set as follows:
training_and_validation
and testing
. Use random_split(0.9, seed=1)
.training
and validation
. Use random_split(0.5, seed=1)
.Again, we set seed=1
to obtain consistent results for different users.
In [120]:
training_and_validation, test_data = sales.random_split(0.9, seed=1)
train_data, validation_data = training_and_validation.random_split(0.5, seed=1)
Next you should write a loop that does the following:
(Note you can turn off the print out of linear_regression.create() with verbose = False)
In [117]:
result = dict()
for degree in range(1, 15 + 1):
ps = polynomial_sframe(train_data['sqft_living'], degree)
my_features = ps.column_names()
ps['price'] = train_data['price']
model = graphlab.linear_regression.create(ps, target = 'price', features = my_features, validation_set = None, verbose = False)
validation_pol = polynomial_sframe(validation_data['sqft_living'], degree)
predictions = model.predict(validation_pol)
residuals = validation_data['price'] - predictions
result[degree] = sum(residuals**2)
print "Deegree of polynomial:", min(result, key=result.get)
Quiz Question: Which degree (1, 2, …, 15) had the lowest RSS on Validation data?
Now that you have chosen the degree of your polynomial using validation data, compute the RSS of this model on TEST data. Report the RSS on your quiz.
In [123]:
ps = polynomial_sframe(train_data['sqft_living'], 6)
my_features = ps.column_names()
ps['price'] = train_data['price']
model = graphlab.linear_regression.create(ps, target = 'price', features = my_features, validation_set = None, verbose = False)
test_pol = polynomial_sframe(test_data['sqft_living'], degree)
predictions = model.predict(test_pol)
residuals = test_data['price'] - predictions
RSS = sum(residuals**2)
Quiz Question: what is the RSS on TEST data for the model with the degree selected from Validation data? (Make sure you got the correct degree from the previous question)
In [124]:
print RSS
In [ ]: