<jupyter_start><jupyter_text>Sparsity and L1 RegularizationOnce again, we'll work on our logistic regression model. We'll use feature columns and add a significant number of features. This model will be pretty complex. Let's see if we can keep this complexity in check.One way to reduce complexity is to use a regularization function that encourages weights to be exactly zero. For linear models such as regression, a zero weight is equivalent to not using the corresponding feature at all. In addition to avoiding overfitting, the resulting model will be more efficient.L1 regularization is a good way to increase sparsity.Run the cell below to load the data and create feature definitions.<jupyter_code>import math

from IPython import display
from matplotlib import cm
from matplotlib import gridspec
from matplotlib import pyplot as plt
import numpy as np
import pandas as pd
from sklearn import metrics
import tensorflow as tf

tf.logging.set_verbosity(tf.logging.ERROR)
pd.options.display.max_rows = 10
pd.options.display.float_format = '{:.1f}'.format

california_housing_dataframe = pd.read_csv("https://storage.googleapis.com/ml_universities/california_housing_train.csv", sep=",")

california_housing_dataframe = california_housing_dataframe.reindex(
    np.random.permutation(california_housing_dataframe.index))
def preprocess_features(california_housing_dataframe):
  """Prepares input features from California housing data set.

  Args:
    california_housing_dataframe: A Pandas DataFrame expected to contain data
      from the California housing data set.
  Returns:
    A DataFrame that contains the features to be used for the model, including
    synthetic features.
  """
  selected_features = california_housing_dataframe[
    ["latitude",
     "longitude",
     "housing_median_age",
     "total_rooms",
     "total_bedrooms",
     "population",
     "households",
     "median_income"]]
  processed_features = selected_features.copy()
  # Create a synthetic feature.
  processed_features["rooms_per_person"] = (
    california_housing_dataframe["total_rooms"] /
    california_housing_dataframe["population"])
  return processed_features

def preprocess_targets(california_housing_dataframe):
  """Prepares target features (i.e., labels) from California housing data set.

  Args:
    california_housing_dataframe: A Pandas DataFrame expected to contain data
      from the California housing data set.
  Returns:
    A DataFrame that contains the target feature.
  """
  output_targets = pd.DataFrame()
  # Create a boolean categorical feature representing whether the
  # medianHouseValue is above a set threshold.
  output_targets["median_house_value_is_high"] = (
    california_housing_dataframe["median_house_value"] > 265000).astype(float)
  return output_targets
training_examples = preprocess_features(california_housing_dataframe.head(12000))
training_targets = preprocess_targets(california_housing_dataframe.head(12000))
validation_examples = preprocess_features(california_housing_dataframe.tail(5000))
validation_targets = preprocess_targets(california_housing_dataframe.tail(5000))
def input_function(examples_df, targets_df, single_read=False):
  """Converts a pair of examples/targets `DataFrame`s to `Tensor`s.
  
  The `Tensor`s are reshaped to `(N,1)` where `N` is number of examples in the `DataFrame`s.
  
  Args:
    examples_df: A `DataFrame` that contains the input features. All its columns will be
      transformed into corresponding input feature `Tensor` objects.
    targets_df: A `DataFrame` that contains a single column, the targets corresponding to
      each example in `examples_df`.
    single_read: A `bool` that indicates whether this function should stop after reading
      through the dataset once. If `False`, the function will loop through the data set.
      This stop mechanism is user by the estimator's `predict()` to limit the number of
      values it reads.
  Returns:
    A tuple `(input_features, target_tensor)`:
      input_features: A `dict` mapping string values (the column name of the feature) to
        `Tensor`s (the actual values of the feature).
      target_tensor: A `Tensor` representing the target values.
  """
  features = {}
  for column_name in examples_df.keys():
    batch_tensor = tf.to_float(
        tf.reshape(tf.constant(examples_df[column_name].values), [-1, 1]))
    if single_read:
      features[column_name] = tf.train.limit_epochs(batch_tensor, num_epochs=1)
    else:
      features[column_name] = batch_tensor
  target_tensor = tf.to_float(
      tf.reshape(tf.constant(targets_df[targets_df.keys()[0]].values), [-1, 1]))

  return features, target_tensor
def get_quantile_based_buckets(feature_values, num_buckets):
  quantiles = feature_values.quantile(
    [(i+1.)/(num_buckets + 1.) for i in xrange(num_buckets)])
  return [quantiles[q] for q in quantiles.keys()]

bucketized_households = tf.contrib.layers.bucketized_column(
  tf.contrib.layers.real_valued_column("households"),
  boundaries=get_quantile_based_buckets(training_examples["households"], 10))
bucketized_longitude = tf.contrib.layers.bucketized_column(
  tf.contrib.layers.real_valued_column("longitude"),
  boundaries=get_quantile_based_buckets(training_examples["longitude"], 50))
bucketized_latitude = tf.contrib.layers.bucketized_column(
  tf.contrib.layers.real_valued_column("latitude"),
  boundaries=get_quantile_based_buckets(training_examples["latitude"], 50))
bucketized_housing_median_age = tf.contrib.layers.bucketized_column(
  tf.contrib.layers.real_valued_column("housing_median_age"),
  boundaries=get_quantile_based_buckets(
    training_examples["housing_median_age"], 10))
bucketized_total_rooms = tf.contrib.layers.bucketized_column(
  tf.contrib.layers.real_valued_column("total_rooms"),
  boundaries=get_quantile_based_buckets(training_examples["total_rooms"], 10))
bucketized_total_bedrooms = tf.contrib.layers.bucketized_column(
  tf.contrib.layers.real_valued_column("total_bedrooms"),
  boundaries=get_quantile_based_buckets(training_examples["total_bedrooms"], 10))
bucketized_population = tf.contrib.layers.bucketized_column(
  tf.contrib.layers.real_valued_column("population"),
  boundaries=get_quantile_based_buckets(training_examples["population"], 10))
bucketized_median_income = tf.contrib.layers.bucketized_column(
  tf.contrib.layers.real_valued_column("median_income"),
  boundaries=get_quantile_based_buckets(training_examples["median_income"], 10))
bucketized_rooms_per_person = tf.contrib.layers.bucketized_column(
  tf.contrib.layers.real_valued_column("rooms_per_person"),
  boundaries=get_quantile_based_buckets(
    training_examples["rooms_per_person"], 10))

long_x_lat = tf.contrib.layers.crossed_column(
  set([bucketized_longitude, bucketized_latitude]), hash_bucket_size=1000)

feature_columns = set([
  long_x_lat,
  bucketized_longitude,
  bucketized_latitude,
  bucketized_housing_median_age,
  bucketized_total_rooms,
  bucketized_total_bedrooms,
  bucketized_population,
  bucketized_households,
  bucketized_median_income,
  bucketized_rooms_per_person])<jupyter_output><empty_output><jupyter_text>Calculate the model sizeTo calculate the model size, we simply count the number of parameters that are non-zero. We provide a helper function below to do that. The function uses intimate knowledge of the Estimators API - don't worry about understanding how it works.<jupyter_code>def model_size(estimator):
  variables = estimator.get_variable_names()
  size = 0
  for variable in variables:
    if not any(x in variable 
               for x in ['global_step',
                         'centered_bias_weight',
                         'bias_weight',
                         'Ftrl']
              ):
      size += np.count_nonzero(estimator.get_variable_value(variable))
  return size<jupyter_output><empty_output><jupyter_text>Reduce the model sizeYour team needs to build a highly accurate Logistic Regression model on the *SmartRing*, a ring that is so smart it can sense the demographics of a city block ('median_income', 'avg_rooms', 'households', ..., etc.) and tell you whether the given city block is high cost city block or not.Since the SmartRing is small, the engineering team has determined that it can only handle a model that has **no more than 600 parameters**. On the other hand, the product management team has determined that the model is not launchable unless the **LogLoss is less than 0.35** on the holdout test set.Can you use your secret weapon — L1 regularization — to tune the model to satisfy both the size and accuracy constraints?  Task 1: Find a good regularization coefficient.**Find an L1 regularization strength parameter which satisfies both constraints — model size is less than 600 and log-loss is less than 0.35 on validation set.**The following code will help you get started. There are many ways to apply regularization to your model. Here, we chose to do it using `FtrlOptimizer`, which is designed to give better results with L1 regularization than standard gradient descent.Again, the model will train on the entire data set, so expect it to run slower than normal.<jupyter_code>def train_linear_classifier_model(
    learning_rate,
    regularization_strength,
    steps,
    feature_columns,
    training_examples,
    training_targets,
    validation_examples,
    validation_targets):
  """Trains a linear regression model.
  
  In addition to training, this function also prints training progress information,
  as well as a plot of the training and validation loss over time.
  
  Args:
    learning_rate: A `float`, the learning rate.
    regularization_strength: A `float` that indicates the strength of the L1
       regularization. A value of `0.0` means no regularization.
    steps: A non-zero `int`, the total number of training steps. A training step
      consists of a forward and backward pass using a single batch.
    feature_columns: A `set` specifying the input feature columns to use.
    training_examples: A `DataFrame` containing one or more columns from
      `california_housing_dataframe` to use as input features for training.
    training_targets: A `DataFrame` containing exactly one column from
      `california_housing_dataframe` to use as target for training.
    validation_examples: A `DataFrame` containing one or more columns from
      `california_housing_dataframe` to use as input features for validation.
    validation_targets: A `DataFrame` containing exactly one column from
      `california_housing_dataframe` to use as target for validation.
      
  Returns:
    A `LinearClassifier` object trained on the training data.
  """

  periods = 7
  steps_per_period = steps / periods

  # Create a linear classifier object.
  linear_classifier = tf.contrib.learn.LinearClassifier(
      feature_columns=feature_columns,
      optimizer=tf.train.FtrlOptimizer(
          learning_rate=learning_rate,
          l1_regularization_strength=regularization_strength),
      gradient_clip_norm=5.0
  )

  training_input_function = lambda: input_function(
      training_examples, training_targets)
  training_input_function_for_predict = lambda: input_function(
      training_examples, training_targets, single_read=True)
  validation_input_function_for_predict = lambda: input_function(
      validation_examples, validation_targets, single_read=True)
  
  # Train the model, but do so inside a loop so that we can periodically assess
  # loss metrics.
  print "Training model..."
  print "LogLoss (on validation data):"
  training_log_losses = []
  validation_log_losses = []
  for period in range (0, periods):
    # Train the model, starting from the prior state.
    linear_classifier.fit(
        input_fn=training_input_function,
        steps=steps_per_period
    )
    # Take a break and compute predictions.
    training_probabilities = np.array(list(linear_classifier.predict_proba(
        input_fn=training_input_function_for_predict)))
    validation_probabilities = np.array(list(linear_classifier.predict_proba(
          input_fn=validation_input_function_for_predict)))
    # Compute training and validation loss.
    training_log_loss = metrics.log_loss(training_targets, training_probabilities[:, 1])
    validation_log_loss = metrics.log_loss(validation_targets, validation_probabilities[:, 1])
    # Occasionally print the current loss.
    print "  period %02d : %0.2f" % (period, validation_log_loss)
    # Add the loss metrics from this period to our list.
    training_log_losses.append(training_log_loss)
    validation_log_losses.append(validation_log_loss)
  print "Model training finished."

  # Output a graph of loss metrics over periods.
  plt.ylabel("LogLoss")
  plt.xlabel("Periods")
  plt.title("LogLoss vs. Periods")
  plt.tight_layout()
  plt.plot(training_log_losses, label="training")
  plt.plot(validation_log_losses, label="validation")
  plt.legend()

  return linear_classifier
linear_classifier = train_linear_classifier_model(
    learning_rate=0.1,
    regularization_strength=0.0,
    steps=300,
    feature_columns=feature_columns,
    training_examples=training_examples,
    training_targets=training_targets,
    validation_examples=validation_examples,
    validation_targets=validation_targets)
print "Model size:", model_size(linear_classifier)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.07433495691270138, 'fraction_numerical': 0.007343574372424129, 'mean_word_length': 4.910540301151461, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 14, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '3', 'dir': 'jupyter-structured-clean-dedup', 'id': '402848', 'license': 'MIT', 'n_tokens_mistral': 3934, 'n_tokens_neox': 3722, 'n_words': 1136, 'path': 'ml-exercises/sparsity_and_l1_regularization.ipynb', 'repo_name': 'StevenPZChan/machine-learning', 'size': '13345'}