人工智能小白日记之13 ML学习篇之9稀疏性正则化 Regularization for Sparsity

前言

老实说,这篇我看的有点抽象,就像之前的L2正则化,只知道执行它可以使得权重接近于0,越小该项的影响力越小,在避免过拟合这样的问题或者“去噪”这样的问题上非常有利,但是其原理是什么呢,一脸懵逼。不过摸石头过河的过程就是这样的,太多的不明确,走一步算一步,不然花太多时间可能效率会低,先记住结论然后应用就行了。

课程内容

1 稀疏特征的组合

这是视频里提到的,里面举了个例子,这个例子超级形象666。如果我们想训练一个模型,想找出视频库中包含某些词汇的某种视频。这里涉及到两个特征:某个特征是搜索查询中的字词, 另一个特征是需要查询的独特视频。如果有数百万个字词和数百万个视频,cross一下结果是很庞大的,会消耗很多内存。而且里面会产生一些很罕见的组合,这种组合就是之前说的噪音,没有价值,需要“去噪”,这时候就需要正则化了。

2 再聊正则化

关于上面的问题,为了达到去噪的目的,最好是噪音的权重都是0,文中引出了三种正则化:
L2正则化:执行 L2 正则化可以使权重变小,但是并不能使它们正好为 0.0
L0 正则化:尝试创建一个正则化项,减少模型中的非零系数值的计数。只有在模型能够与数据拟合时增加此计数才有意义。遗憾的是,虽然这种基于计数的方法看起来很有吸引力,但它会将我们的凸优化问题变为非凸优化问题,即 NP 困难。
L1正则化:L1 正则化这种正则化项的作用类似 L0,但它具有凸优化的优势,可有效进行计算。因此,我们可以使用 L1 正则化使模型中很多信息缺乏的系数正好为 0,从而在推理时节省 RAM。

ps:看完之后是不是一脸懵逼,所以我果断放弃了研究L0和L1这两句话里的含义。去外面papa 资料,https://blog.csdn.net/qq_29133371/article/details/54894415 ,比如这么一篇。虽然还是不太明白,至少大概有点了解了。比较重要的几段话如下:

L0范数是指向量中非0的元素的个数。如果我们用L0范数来规则化一个参数矩阵W的话,就是希望W的大部分元素都是0。换句话说,让参数W是稀疏的。

L1范数是指向量中各个元素绝对值之和,也有个美称叫“稀疏规则算子”(Lasso regularization)。

既然L0可以实现稀疏,为什么不用L0,而要用L1呢?个人理解一是因为L0范数很难优化求解(NP难问题),二是L1范数是L0范数的最优凸近似,而且它比L0范数要容易优化求解。所以大家才把目光和万千宠爱转于L1范数。“

3 L1 和 L2 正则化

L2 和 L1 采用不同的方式降低权重:

  • L2 会降低权重2。
  • L1 会降低 |权重|。

因此,L2 和 L1 具有不同的导数:

  • L2 的导数为 2 * 权重。
  • L1 的导数为 k(一个常数,其值与权重无关)。

后面强行解释了一波,为何L2正则化无法使权重刚好为0,而L1正则化可以,不过我没看明白,直接看结果吧?:
在这里插入图片描述

4 练习

本练习包含一个有些混乱的小型训练数据集。在这种情况下,过拟合问题比较令人担忧。 正则化可能会有所帮助,但采用哪种形式的正则化呢?

本练习包含 5 个相关的任务。为了简化这 5 个任务之间的比较,请在单独的标签中运行每个任务。 请注意,连接 FEATURES 和 OUTPUT 的线的厚度表示每个特征的相对权重。
在这里插入图片描述
我们先来跑下结果:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
看下问题:

1)从 L2 正则化转换到 L1 正则化对测试损失与训练损失之间的差值有何影响?
可以看出,L1的训练损失更高,但是测试损失更小了。而且测试损失与训练损失之间的差值也减小了。

2)从 L2 正则化转换到 L1 正则化对已知权重有何影响?
这题不好截图比较,最后结论是,从 L2 正则化转换到 L1 正则化之后,所有已知权重都有所减少。

3)增加 L1 正则化率 (lambda) 对已知权重有何影响?
这题需要进行测试,在L1的情况下不断加大lambda,结果就是前面lambda增加可以减小已知权重,但时候太高的时候,不能模型收敛了,直接废废了。
在这里插入图片描述

5 编程练习

进入指定练习
在这里插入图片描述
在这里插入图片描述
降低复杂性的一种方法是使用正则化函数,它会使权重正好为零。对于线性模型(例如线性回归),权重为零就相当于完全没有使用相应特征。除了可避免过拟合之外,生成的模型还会更加有效。

L1 正则化是一种增加稀疏性的好方法。

5-1 前戏

前戏跟上节中逻辑回归实战是一样的,包括了导入数据,指定特征,还有线性回归转化为逻辑回归的那个标签也在。后面定义特征列就有点不一样了:

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

def construct_feature_columns():
  """Construct the TensorFlow Feature Columns.

  Returns:
    A set of feature columns
  """

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

  long_x_lat = tf.feature_column.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])
  
  return feature_columns

ps:这里在干啥?前面见过了哈,包括了分桶操作,get_quantile_based_buckets函数则是用来计算分桶边界;还有long_x_lat = tf.feature_column.crossed_column,这里在将经纬度进行cross,所谓特征组合。

5-2 减小模型大小

您的团队需要针对 SmartRing 构建一个准确度高的逻辑回归模型,这种指环非常智能,可以感应城市街区的人口统计特征(median_incomeavg_roomshouseholds 等等),并告诉您指定城市街区的住房成本是否高昂。

由于 SmartRing 很小,因此工程团队已确定它只能处理参数数量不超过 600 个的模型。另一方面,产品管理团队也已确定,除非所保留测试集的对数损失函数低于 0.35,否则该模型不能发布。

您可以使用秘密武器“L1 正则化”调整模型,使其同时满足大小和准确率限制条件吗?
这里提供了计算模型大小的方法

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

5-3 任务 1:查找合适的正则化系数。

查找可同时满足以下两种限制条件的 L1 正则化强度参数:模型的参数数量不超过 600 个且验证集的对数损失函数低于 0.35。

以下代码可帮助您快速开始。您可以通过多种方法向您的模型应用正则化。在此练习中,我们选择使用 FtrlOptimizer 来应用正则化。FtrlOptimizer 是一种设计成使用 L1 正则化比标准梯度下降法得到更好结果的方法。

重申一次,我们会使用整个数据集来训练该模型,因此预计其运行速度会比通常要慢。

def train_linear_classifier_model(
    learning_rate,
    regularization_strength,
    steps,
    batch_size,
    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.
  my_optimizer = tf.train.FtrlOptimizer(learning_rate=learning_rate, l1_regularization_strength=regularization_strength)
  my_optimizer = tf.contrib.estimator.clip_gradients_by_norm(my_optimizer, 5.0)
  linear_classifier = tf.estimator.LinearClassifier(
      feature_columns=feature_columns,
      optimizer=my_optimizer
  )
  
  # Create input functions.
  training_input_fn = lambda: my_input_fn(training_examples, 
                                          training_targets["median_house_value_is_high"], 
                                          batch_size=batch_size)
  predict_training_input_fn = lambda: my_input_fn(training_examples, 
                                                  training_targets["median_house_value_is_high"], 
                                                  num_epochs=1, 
                                                  shuffle=False)
  predict_validation_input_fn = lambda: my_input_fn(validation_examples, 
                                                    validation_targets["median_house_value_is_high"], 
                                                    num_epochs=1, 
                                                    shuffle=False)
  
  # 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.train(
        input_fn=training_input_fn,
        steps=steps_per_period
    )
    # Take a break and compute predictions.
    training_probabilities = linear_classifier.predict(input_fn=predict_training_input_fn)
    training_probabilities = np.array([item['probabilities'] for item in training_probabilities])
    
    validation_probabilities = linear_classifier.predict(input_fn=predict_validation_input_fn)
    validation_probabilities = np.array([item['probabilities'] for item in validation_probabilities])
    
    # Compute training and validation loss.
    training_log_loss = metrics.log_loss(training_targets, training_probabilities)
    validation_log_loss = metrics.log_loss(validation_targets, validation_probabilities)
    # 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,
  # TWEAK THE REGULARIZATION VALUE BELOW
  regularization_strength=0.0,
  steps=300,
  batch_size=100,
  feature_columns=construct_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))

运行一下:
在这里插入图片描述
可以看到LogLoss已经符合要求了,就是Model Size = 788 ,不满足条件,这时候我们调整下regularization_strength,正则化强度?这应该就是之前一直见过的正则率。

linear_classifier = train_linear_classifier_model(
    learning_rate=0.1,
    regularization_strength=0.6,
    steps=300,
    batch_size=100,
    feature_columns=construct_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))

在这里插入图片描述
ps:这东西很难调,运行一次好久,所以这次正则率0.6的时候勉强达到要求,估计还要再大点,懒得调了。让我诧异的是官方答案是不是写错了,0.1?好吧,尴尬。然后logloss有点增大的赶脚,官方说过正则可能导致误差偏大是有道理的,因为大部分权重归0后,会导致某些因子没有参与训练,最终欠拟合。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值