損失関数の紹介

基本的な損失関数と関連のある活性化関数を紹介します.

目次

  1. 序章
  2. 基本的な損失関数と問題の分類
  3. 損失関数と活性化関数の組み合わせ
  4. 基本的な組み合わせのReNomでの使い方
1. 序章
損失関数はラベルと出力の誤差を計算する関数になります.
ニューラルネットワークモデルは教師データとニューラルネットワークの出力間の差を小さくする様に,学習が行われます. 損失関数はその「差」の測り方を与える役割があり、問題に対して適切に損失関数を選ぶことが重要になります.
2. 基本的な損失関数と問題の分類
損失関数には多くの種類が存在し,時には自身で新しい損失関数を定義することもあります.
2つの基本的な損失関数としてcross entropyとmean squared errorを紹介し,関連する活性関数としてsigmoid関数とsoftmax関数を紹介します.
更に基本的に問題を分類する必要があります.2値分類なのか?多値分類なのか?回帰なのか?
これらの問題に対して通常は異なる活性化関数と損失関数の組み合わせを使います.
3. 損失関数と活性化関数の組み合わせ
上で述べたような問題の種類に応じてそれぞれの組み合わせを選ぶべき理由が存在します。
上で示されるようにcross entropyはおよそ確率が出力される場合に用いられます.
Mean squared errorはたいてい回帰問題に用いられます.
学習速度が非常に遅くなることがあるために,mean squared errorとsoftmax(もしくはsigmoid)を同時に用いることはあまりお勧めできません.
4. 基本的な組み合わせのReNomでの使い方
データのリファレンスは以下になります.
Lichman, M. (2013). UCI Machine Learning Repository [ http://archive.ics.uci.edu/ml ].
Irvine, CA: University of California, School of Information and Computer Science.

Required Libraries

  • matplotlib 2.0.2
  • numpy 1.12.1
  • scikit-learn 0.18.2
  • pandas 0.20.3
In [1]:
from __future__ import division, print_function
import numpy as np
import pandas as pd
import re

import matplotlib.pyplot as plt
from sklearn.preprocessing import LabelBinarizer, OneHotEncoder
from sklearn.metrics import confusion_matrix, classification_report
from sklearn.model_selection import train_test_split
import renom as rm
from renom.optimizer import Sgd, Adam
from renom.cuda import set_cuda_active
set_cuda_active(True)
# If this is the first time running the example,
# and you need to download the data- set this to True
first_time = False

if first_time:
    import os
    os.system("wget http://archive.ics.uci.edu/ml/machine-learning-databases/credit-screening/crx.data")

def load_data(filename):
    df = pd.read_csv(filename, header=None, index_col=None)
    print("the number of {} records:{}".format(filename, len(df.index)))
    df = df.applymap(lambda d:np.nan if d=="?" else d)
    df = df.dropna(axis=0)
    print("the number of {} records after trimming:{}".format(filename, len(df.index)))
    sr_labels = df.iloc[:,-1]
    labels = sr_labels.str.replace("+","1").replace("-","0").values.astype(float)
    data = df.iloc[:,:-1].values.astype(str)
    return data, labels

数値データorカテゴリデータの識別とワンホットベクトル化

In [2]:
pattern_continuous = re.compile("^\d+\.?\d*\Z")
def onehot_vectorize(data):
    continuous_idx = {}
    for i in range(data.shape[1]):
        is_continuous = True if pattern_continuous.match(data[0][i]) else False
        if is_continuous and i==0:
            X = data[:,i].astype(float)
        elif not is_continuous and i==0:
            X = pd.get_dummies(data[:,i]).values.astype(float)
        elif is_continuous and i!=0:
            X = np.concatenate((X, data[:,i].reshape(-1,1).astype(float)), axis=1)
        elif not is_continuous and i!=0:
            X = np.concatenate((X, pd.get_dummies(data[:,i]).values.astype(float)), axis=1)
    return X

data, y = load_data("crx.data")
X = onehot_vectorize(data)
print("X:{} y:{}".format(X.shape, y.shape))
the number of crx.data records:690
the number of crx.data records after trimming:653
X:(653, 46) y:(653,)

データ作成とモデル定義

In [3]:
indices = np.arange(len(X))
X_train, X_test, y_train, y_test, indices_train, indices_test = \
train_test_split(X, y, indices, test_size=0.2)
y_train = y_train.reshape(-1, 1)
y_test = y_test.reshape(-1, 1)
print("X_train:{} y_train:{} X_test:{} y_test:{}".format(X_train.shape, y_train.shape, X_test.shape, y_test.shape))

sequential = rm.Sequential([
    rm.Dense(64),
    rm.Relu(),
    rm.Dense(32),
    rm.Relu(),
    rm.Dense(1)
])
X_train:(522, 46) y_train:(522, 1) X_test:(131, 46) y_test:(131, 1)

sigmoid activationとcross entropyを用いる際の学習ループ

まず,どのようにcross entropyを使っているかについてです.以下の部分では,sigmoidを出力層の活性化のために,cross entropyを損失関数として使っているということを示しています.

l = rm.sigmoid_cross_entropy(sequential(train_batch), response_batch)
In [4]:
batch_size = 32
epoch = 50
N = len(X_train)
optimizer = Sgd(lr=0.001)
learning_curve = []
test_learning_curve = []

for i in range(epoch):
    perm = np.random.permutation(N)
    loss = 0
    for j in range(0, N // batch_size):
        train_batch = X_train[perm[j*batch_size : (j+1)*batch_size]]
        response_batch = y_train[perm[j*batch_size : (j+1)*batch_size]]

        with sequential.train():
            l = rm.sigmoid_cross_entropy(sequential(train_batch), response_batch)

        grad = l.grad()
        grad.update(optimizer)
        loss += l.as_ndarray()
    train_loss = loss / (N // batch_size)

    test_loss = rm.sigmoid_cross_entropy(sequential(X_test), y_test).as_ndarray()
    test_learning_curve.append(test_loss)
    learning_curve.append(train_loss)
    if i%10 == 0:
        print("epoch :{}, train_loss:{}, test_loss:{}".format(i, train_loss, test_loss))

predictions = rm.sigmoid(sequential(X_test)).as_ndarray()
pred = np.array(list(map(lambda d:1 if d>0.5 else 0, predictions))).reshape(-1,1)

print(confusion_matrix(y_test, pred))
print(classification_report(y_test, pred, target_names=["-","+"]))
epoch :0, train_loss:3.272980712354183, test_loss:0.7651634812355042
epoch :10, train_loss:0.6081356462091208, test_loss:0.7066062092781067
epoch :20, train_loss:0.6118309292942286, test_loss:0.6797510385513306
epoch :30, train_loss:0.6028882898390293, test_loss:0.6095903515815735
epoch :40, train_loss:0.6093996576964855, test_loss:0.6735255718231201
[[52 10]
 [32 37]]
             precision    recall  f1-score   support

          -       0.62      0.84      0.71        62
          +       0.79      0.54      0.64        69

avg / total       0.71      0.68      0.67       131