tensorflowで誤差関数を実装

一般的に利用される損失関数をtensorflowにて実装しました。

# 回帰

## L2ノルム(ユークリッド損失関数)
L2ノルムの損失関数は目的値への距離の二乗で表されます。
L2ノルム損失関数は、目的値の近くでとがった急なカーブを描いていることが特徴です。
つまりアルゴリズムは目的値にゆっくり近づきながら収束することができます。

```math
L2 = (target-predict)^2
```

```python:l2_loss.py
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(-1., 1., 500)
y = np.zeros(500)

target = tf.placeholder(tf.float32, [500])
predict = tf.placeholder(tf.float32, [500])

l2_loss = tf.square(target-predict)

with tf.Session() as sess:
  l2_output = sess.run(l2_loss, feed_dict={target: y, predict: x})

plt.plot(x, l2_output, 'b--', label='L2 LOSS')
plt.show()
```

## L1ノルム(絶対値損失関数)
L1ノルム損失関数は、距離の絶対値で表されます。
値が大きくなっても勾配が急にならないため、L1ノルムはL2ノルムよりも外れ値にうまく対応することができます。
しかし、目的値に対して滑らかではないため、うまく収束しない可能性があります。

```math
L1 = |target-predict|
```

```python:l1_loss.py
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(-1., 1., 500)
y = np.zeros(500)

target = tf.placeholder(tf.float32, [500])
predict = tf.placeholder(tf.float32, [500])

l1_loss = tf.abs(target-predict)

with tf.Session() as sess:
  l1_output = sess.run(l1_loss, feed_dict={target: y, predict: x})

plt.plot(x, l1_output, 'b--', label='L1 LOSS')
plt.show()
```

## Hurber損失関数
hurber損失では、ズレがある範囲内ならば二乗損失を、それより外なら直線上に増加する損失を与えます。
一定範囲内に対しては厳しく損失を与え、逆に外の方では損失の増加が直線的になります。
学習データに仮に外れ値があった場合に、それらに引っ張られる学習が抑えられます。

```math

L_\delta(a) = \begin{cases}
    \frac{1}{2}a^2 & for\ |a|<\delta \\
    \delta(|a|-\frac{1}{2}\delta) & (otherwise)
  \end{cases}
```

```python
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(-1., 1., 500)
y = np.zeros(500)

def huber_loss(prediction, label, delta=0.5):
    error = label - prediction
    cond = tf.abs(error) < delta
   
    squared_loss = 0.5 * tf.square(error)
    linear_loss = delta * ( tf.abs(error) - 0.5 * delta)
    return tf.where(cond, squared_loss, linear_loss)

with tf.Session() as sess:
    huber_output = sess.run(huber_loss(x, y))

plt.plot(x, huber_output, 'g--', label='Huber LOSS')
plt.show()
```

![figure_1_r.png](https://qiita-image-store.s3.amazonaws.com/0/239786/8d3dfb38-0610-f94b-1fbb-821ac0e45527.png)

# 分類

## ヒンジ損失関数
SVMにてよく使われる損失関数です。

```python:hinge_loss.py
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(-3., 5., 500)
y = np.ones(500)

target = tf.placeholder(tf.float32, [500])
predict = tf.placeholder(tf.float32, [500])

hinge_loss = tf.maximum(0., 1 - tf.multiply(target, predict))

with tf.Session() as sess:
    hinge_output = sess.run(hinge_loss, feed_dict={target: y, predict: x})

plt.plot(x, hinge_output, 'b--', label='HINGE LOSS')
plt.show()
```

## ロジスティック損失関数
ロジステック回帰はインスタンスが特定のクラスに属する確率を推定するためによく利用します。
推定された確率が50%以上なら、モデルはインスタンスがそのクラスに属する(label=1)と予測します。
そうでなければ、インスタンスはそのクラスに属さない(label=0)と予測します。

ロジスティック回帰モデルは

```math
\hat{p}=h_\theta(x) = \sigma(\theta^T \cdot x)
```
にて表され、$\sigma(t)$はシグモイド関数です。

```math
\sigma = \frac{1}{1 + exp(-t)}
```
ロジスティック回帰モデルによってインスタンスが陽性クラス(label=1)に属する確率$\hat{p}=h_\theta(x)$が推定されたら予測$\hat{y}$は以下のように得られます。

```math
\bar{y} = \begin{cases}
    0 & if\ \hat{p}<0.5 \\
    1 & if\ \hat{p}>0.5
  \end{cases}
```

訓練の目的は、モデルが陽性インスタンスに対して高い確率、陰性インスタンスには低い確率を推定できるようにするにパラメータ$\theta$を設定することです。ロジスティック損失関数は以下のようになります。

```math
J(\theta)=-y^{(i)}log(\hat{p}^{(i)})-(1-y^{(i)})log(1-\hat{p}^{(i)})
```
モデルが陽性インスタンス(label=1)に対して0に近い確率を推定するとコストが大きくなり、陰性インスタンス(label=0)に対して1に近い確率を推定するとコストが大きくなることがわかります。

```python:logistic_loss.py
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(-3., 5., 500)
y = np.ones(500)
y_ = np.zeros(500)

target = tf.placeholder(tf.float32, [500])
predict = tf.placeholder(tf.float32, [500])

entropy_loss = -tf.multiply(target, tf.log(predict)) - \
                    tf.multiply((1. - target), tf.log(1. - predict))

with tf.Session() as sess:
    entropy_output = sess.run(entropy_loss, feed_dict={target:y, predict:x})
    entropy_output_ = sess.run(entropy_loss, feed_dict={target:y_, predict:x})

plt.plot(x, entropy_output_, 'r--', label='LOGISTIC LOSS (label=0)')
plt.plot(x, entropy_output, 'g--', label='LOGISTIC LOSS (label=1)')
plt.legend(loc='lower right', prop={'size': 11})
plt.show()
```
![figure_1.png](https://qiita-image-store.s3.amazonaws.com/0/239786/cf0fb477-93b0-f826-1b9d-edd0031be639.png)

## シグモイド交差エントロピ損失関数

シグモイド関数はロジスティック損失関数と似ていますが、入力データをシグモイド関数で変換してからロジスティック損失関数に渡す違いがあります。
詳細はtensorflowのホームページを参考にしてみて下さい。

```python:sigmoid_cross_entropy_loss.py
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(-3., 5., 500)
y = np.ones(500)
y_ = np.zeros(500)

predict = tf.placeholder(tf.float32, [500])
target = tf.placeholder(tf.float32, [500])

sigmoid_cross_entropy_loss = tf.nn.sigmoid_cross_entropy_with_logits(logits=predict, labels=target)

with tf.Session() as sess:
    loss = sess.run(sigmoid_cross_entropy_loss, feed_dict={predict: x, target: y})
    loss_ = sess.run(sigmoid_cross_entropy_loss, feed_dict={predict: x, target: y_})

plt.plot(x, loss_, 'r--', label="SIGMOID CROSS ENTROPY LOSS (label=0)")
plt.plot(x, loss, 'g--', label="SIGMOID CROSS ENTROPY LOSS (label=1)")
plt.legend(loc='lower right', prop={'size': 11})
plt.show()
```
https://www.tensorflow.org/api_docs/python/tf/nn/sigmoid_cross_entropy_with_logits
![figure_1.png](https://qiita-image-store.s3.amazonaws.com/0/239786/f18c064c-be0c-24e4-314a-cb9243bc006b.png)

## ソフトマックス交差エントロピー損失関数

###ソフトマックス回帰モデル
ソフトマックス回帰モデルは、インスタンス$x$を受け取ると、まず個々のクラスkのスコア$s_k(x)$を計算し、ソフトマックス関数を適用して個々のクラスの確率を推定します。
ソフトマックス関数は、すべてのスコアの指数を計算してから、結果を正規化しています。

```math
softmax(x_i)=\frac{exp(s_i)}{\sum_{k=1}^{K}exp(s_k)}

```

###交差エントロピー(cross entropy)
訓練の目的は、ターゲットクラスを高い確率で推定するモデルをつくることです。
２つの確率分布P, Qに対して

```math
cross\ entropy=-\sum_xP(x)logQ(x)

```
を交差エントロピーと言います。

PとQが二値変数の場合、

```math
\begin{cases}
    P(x_1) = p,\ P(x_2)=1-p \\
    Q(x_1) = q,\ Q(x_2)=1-q
  \end{cases}
```
ロジスティック損失関数と等しくなることがわかります。

```math
cross\ entropy = -p\cdot log(q)-(1-p)\cdot log(1-q)
```

tensorflowにてsoftmax交差エントロピー損失関数を実装する方法は

1. マニュアルで計算グラフを構築
2. softmax_cross_entropy_with_logitsを利用
3. sparse_softmax_cross_entropy_with_logitsを利用

があります。

```python
import tensorflow as tf
import numpy as np

logits = tf.placeholder(tf.float32, [None, 3])
labels = tf.placeholder(tf.float32, [None, 3])
sparse_labels = tf.placeholder(tf.int32, [None])

## manual
softmax = tf.nn.softmax(logits)
loss_0 = -tf.reduce_sum(tf.log(softmax) * labels, axis=1)

## softmax_cross_entropy_with_logits
loss_1 = tf.nn.softmax_cross_entropy_with_logits(labels=labels, logits=logits)

## sparse_softmax_cross_entropy_with_logits
loss_2 = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=sparse_labels, logits=logits)

x = np.array([[6,5,4],
              [2,5,4],
              [3,1,6]])

y = np.array([[1,0,0],
              [0,1,0],
              [0,0,1]])

sparse_y = np.array([0,
                     1,
                     2])

with tf.Session() as sess:
    loss = sess.run([loss_0, loss_1, loss_2], 
            feed_dict={logits:x, labels:y, sparse_labels:sparse_y})

for i in range(len(loss[0])):
   print('target1: %.8f, target2: %.8f, target3: %.8f' 
           % (loss[0][i], loss[1][i], loss[2][i]))
```

# 参照
[TensorFlowのクロスエントロピー関数の動作](https://qiita.com/exy81/items/8c9ab6ba8d4a03873d7c)