ナード戦隊データマン

データサイエンスを用いて悪と戦うぞ

tensorflowでElasticNetを実装しscikit learnのGridSearchCVを適用する

sklearnでEstimatorを定義するためには、fitメソッドとpredictメソッドを定義する必要があります。sklearnのGridSearchCVが適切に動作するようなElasticNetクラスが作成できれば、汎用性の高いクラスであるといえます。ここでは、汎用性の高いElasticNetクラスをtensorflowで作成し、GridSearchCVによって最適な正則化パラメータをサーチします。

ElasticNetクラス

import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
from sklearn import datasets
from tensorflow.python.framework import ops

class ElasticNet:
    def __init__(self, l1=1., l2=1., step_size=1000, batch_size=50, learning_rate=0.01, random_state=13):
        ops.reset_default_graph()
        np.random.seed(random_state)
        tf.set_random_seed(random_state)
        self.l1, self.l2 = l1, l2
        self.step_size = step_size
        self.batch_size = batch_size
        self.learning_rate = learning_rate

    def fit(self, X, y):
        ops.reset_default_graph()
        with tf.name_scope("placeholders"):
            self.x_data = tf.placeholder(shape=[None, X.shape[1]], dtype=tf.float32)
            self.y_target = tf.placeholder(shape=[None, 1], dtype=tf.float32)
        
        with tf.name_scope("ElasticNet"):
            with tf.name_scope("weights"):
                self.A = tf.Variable(tf.random_normal(shape=[X.shape[1],1]))
            with tf.name_scope("biases"):
                self.b = tf.Variable(tf.random_normal(shape=[1,1]))
            with tf.name_scope("model_output"):
                self.model_output = tf.add(tf.matmul(self.x_data, self.A), self.b)
                
        with tf.name_scope("loss"):
            elastic_param1 = tf.constant(self.l1, dtype=tf.float32)
            elastic_param2 = tf.constant(self.l2, dtype=tf.float32)
            l1_a_loss = tf.reduce_mean(tf.abs(self.A))
            l2_a_loss = tf.reduce_mean(tf.square(self.A))
            e1_term = tf.multiply(elastic_param1, l1_a_loss)
            e2_term = tf.multiply(elastic_param2, l2_a_loss)
            loss = tf.expand_dims(tf.add(tf.add(tf.reduce_mean(tf.square(self.y_target - self.model_output)), e1_term), e2_term), 0)

        with tf.name_scope("optimizer"):
            my_opt = tf.train.GradientDescentOptimizer(self.learning_rate)
            train_step = my_opt.minimize(loss)

        self.sess = tf.Session()
        x_data = self.x_data
        y_target = self.y_target
        init = tf.global_variables_initializer()
        self.sess.run(init)
            
        for i in range(self.step_size):
            rand_index = np.random.choice(len(X), size=self.batch_size)
            rand_x = X[rand_index]
            rand_y = np.transpose([y[rand_index]])
            self.sess.run(train_step, feed_dict={x_data: rand_x, y_target: rand_y})
                    
        return self

    def predict(self, X):
        x_data = self.x_data
        return self.sess.run(self.model_output, feed_dict={x_data: X})
    
    def coef_(self):
        return self.sess.run([self.A, self.b])
    
    def get_params(self, deep=True):
        return {'l1':self.l1, 'l2':self.l2}
    
    def set_params(self, **parameters):
        for parameter, value in parameters.items():
            setattr(self,parameter, value)
        return self

クラスの説明

  1. fitメソッドは、訓練データをモデルに適用するメソッドです。
  2. predictメソッドは、与えられたデータから目的変数を予測するメソッドです。
  3. coef_メソッドは、係数と切片を出力します。
  4. get_params, set_paramsはGridSearchCVに必要なため、形式的に定義しておきます。

jupyter notebookで実行

irisデータを準備します。

In[1]:

from sklearn.model_selection import train_test_split
iris = datasets.load_iris()
x_vals = np.array([[x[1], x[2], x[3]] for x in iris.data])
y_vals = np.array([y[0] for y in iris.data])
X_train, X_test, y_train, y_test = train_test_split(x_vals, y_vals)

GridSearchを適用します。

In[2]:

from sklearn.pipeline import Pipeline
from sklearn.model_selection import GridSearchCV

pipe = Pipeline([("clf",ElasticNet())])
grid = GridSearchCV(pipe, param_grid={
    "clf__l1":[0.001, 0.01, 0.1, 1, 10],
    "clf__l2":[0.001, 0.01, 0.1, 1, 10]}, cv=5, scoring="r2")
grid.fit(X_train, y_train)

最適なパラメータを確認します。

In[3]:

grid.best_params_

Out[3]:

{'clf__l1': 0.1, 'clf__l2': 1}

r2スコアを確認します。

In[4]:

from sklearn.metrics import r2_score
preds = grid.predict(X_test)
r2_score(y_test, preds)

Out[4]:

0.79254313889169603

係数と切片を確認します。

In[5]:

best = grid.best_estimator_
best.named_steps["clf"].coef_()

Out[5]:

[array([[ 0.40837133],
        [ 0.45990989],
        [ 0.00617039]], dtype=float32), array([[ 2.81149912]], dtype=float32)]

参考

  1. https://github.com/nfmcclure/tensorflow_cookbook/blob/master/03_Linear_Regression/07_Implementing_Elasticnet_Regression/07_elasticnet_regression.py
  2. http://scikit-learn.org/stable/modules/pipeline.html