一直對機器學習里的loss函數不太懂,這里做點筆記。
符號表示的含義,主要根據Andrew Ng的課程來的,\(m\)個樣本,第\(i\)個樣本為\(\vec x^{(i)}\),對應ground truth標簽為\(y^{(i)}\)。
線性回歸
假設函數:
損失函數:
使用MSE(mean squared error)作為loss function
有mini-SGD梯度下降來優化
邏輯回歸
邏輯回歸用於二分類,所以也叫邏輯分類。
先把線性回歸泛化為廣義線性模型:
考慮到執行二分類,要預測的\(y^{(i)} \in {0,1}\),因此使用Sigmoid函數:
得到邏輯回歸的假設函數:
獲得邏輯回歸的損失函數,則和線性模型不同。是從極大似然估計入手的,因為根據經驗風險最小化的原則,應當搜索參數\(\vec \theta\)使得loss函數取值最小,數學表達式上等價於似然函數取最大。那么首先寫出概率密度函數(p.d.f),似然函數是所有樣本的概率密度乘積,再取對數,以及乘以\(-1\),就得到邏輯回歸損失函數。
邏輯回歸的每個樣本\(\vec x^{(i)}\)對應的類別標簽\(y^{(i)}\)服從兩點分布(Bernoulli分布),其p.d.f為:
其中\(\phi\)表示\(p(y^{(i)}=1)\),也就是\(x^{(i)}\)被預測為正樣本(“1”類)的概率。
對應的似然函數數為:
則邏輯回歸的損失函數(也即負對數似然函數為):
對應的優化求解,通常也是用梯度下降來搞
Softmax回歸
考慮多類分類問題,\(y^{(i)} \in \{1,2,...,K\}\),則將邏輯回歸擴展一下可以得到想要的假設函數和損失函數。
Softmax
Sigmoid是這樣的映射:\(\sigma: \mathbb{R} \rightarrow \{0,1\}\)
Softmax則是這樣的映射:\(\sigma: \mathbb{R}^{K} \rightarrow \{0,1\}^{K}\)
也即,Softmax對一個只有一個1的one-hot編碼的類別標簽向量\(\vec t^{(i)}\)做映射,效果上是\(\vec t^{(i)}\)的每個維度都被Softmax映射到\(\{0,1\}\)內,但並不是各個維度獨立執行sigmoid,而是:
所以,看到很多網上的資料寫說softmax看作是sigmoid的泛化形式,我覺得有誤導嫌疑,從公示上看並不像,僅僅是效果上相似。
Softmax回歸的假設函數
相當於在線性回歸對於各個類別的預測的概率向量基礎上,包了一層Softmax:
Softmax回歸的loss函數
依然是用負對數似然函數作為損失函數,只不過此時的p.d.f是服從多點分布的了:
其似然函數為:
使用負對數似然函數作為損失函數:
交叉熵損失函數Cross-Entropy Loss Function
邏輯回歸是做二分類,其損失函數是2類情況下的Cross-Entropy Loss
Softmax回歸是做多類分類,其損失函數是K類情況下的Cross-Entropy Loss:
其中\(y_{gt}\)表示ground truth的y取值;\(y_{pred}\)表示分類器預測出來的y取值
Softmax Loss和Cross-Entropy Loss是一樣的嗎?
Cross-Entropy Loss,交叉熵損失函數。
嚴格說起來Cross-Entropy Loss則是規范屬術語,而Softmax Loss不是規范術語。Softmax classifier是一個線性分類器,使用到了Cross-Entropy Loss函數。也就是說,交叉熵損失函數的梯度,告訴了Softmax分類器應該如何在SGD更新公式里更新參數\(\vec \theta\)。
但是,約定俗成的說法,當人們提到SoftmaxLoss時,說的就是Cross-Entropy Loss。
(ref: https://www.quora.com/Is-the-softmax-loss-the-same-as-the-cross-entropy-loss)
此外也注意到,Softmax回歸和Logistic回歸,它們的損失函數都是交叉熵損失函數。
Caffe里的線性回歸、邏輯回歸、softmax回歸的損失函數
EuclideanLoss
EuclideanLoss作為線性回歸的損失函數
SigmoidCrossEntropyLoss
SigmoidCrossEntropyLoss是計算cross-entropy (logistic) loss,也就是multi-label並且label相互獨立,例如“民族歌曲、女聲、優雅”這樣的標簽;當然也可用於互斥的label,也即多類分類展開為one-hot編碼,但此時和SoftmaxWithLoss計算結果是不一樣的。
這個函數具體實現的時候,為了數值的穩定性,做了處理。參考:http://www.caffecn.cn/?/question/25
SoftmaxWithLoss
SoftmaxWithLoss是計算multinomial logistic loss,也就是服從多點分布的情形,單個標簽,one-hot編碼后只有一個1,其計算結果和SigmoidCrossEntropyLoss不能混為一談。
具體計算時,各個維度分別減去最大維度上的值再計算softmax(作為預測出的概率),然后套用到負對數似然損失函數中。參考shuzfan的博客:https://blog.csdn.net/shuzfan/article/details/51460895
二分類時,SigmoidCrossEntropyLoss和SoftmaxWithLoss的異同
兩者相同的地方:都是用交叉熵作為損失函數的大模樣
其中\(\hat{y^{(i)}}\)也就是\(h_{\vec \theta}(\vec x^{(i)})\)
兩者不同的地方:前者用sigmoid分別處理特征的各個維度,處理后作為預測的概率\(\hat y\);后者用softmax處理整個特征的各個維度。注意sigmoid是獨立考慮計算各個維度的,而sofmax必須知道所有維度取值后才可以分別計算各個維度。
用代碼運行結果驗證:
取x=[3,5]作為分類器/回歸器/損失函數的輸入,對應的ground truth類別標簽為1,one-hot編碼后為[0,1]。
分別以EuclideanLoss、SigmoidCrossEntropyLoss、SoftmaxWithLoss作為loss函數進行計算(這里是為了示范,實際情況下分類任務不用EuclideanLoss)。
test.py:
#!/usr/bin/env python
# coding:utf-8
from __future__ import print_function
import os, sys
pycaffe_dir = '/home/chris/work/caffe-BVLC/python'
sys.path.insert(0, pycaffe_dir)
import numpy as np
import caffe
from caffe import layers as L, params as P, to_proto
from caffe.proto import caffe_pb2
import yaml
from matplotlib import pyplot as plt
x = np.array([3,5], dtype=np.float32)
x = x[np.newaxis, :]
# single_label:把類別對應的索引作為single_label,從0開始
y1 = np.array([1], dtype=np.float32)
y1 = y1[np.newaxis, :]
# full_label:one-hot編碼格式的類別標簽向量,只有一個1,其他都是0
y2 = np.array([0, 1], dtype=np.float32)
y2 = y2[np.newaxis, :]
print('x.shape:', x.shape)
print('y1.shape:', y1.shape)
print('y2.shape:', y2.shape)
caffe.set_mode_cpu()
solver = caffe.SGDSolver('solver.pt')
solver.net.blobs['data'].data[...] = x
solver.net.blobs['single_label'].data[...] = y1
solver.net.blobs['full_label'].data[...] = y2
solver.step(1)
print('===========================')
print('x: [3,5], y:1,i.e. [0,1]')
# 0.12692806
# 也就是-math.log(math.exp(0)/(math.exp(-2)+math.exp(0)))
softmax_loss = solver.net.blobs['softmax_loss'].data
print('softmax_loss:', softmax_loss)
# 3.0553026
# 也就是-(-3-math.log(1+math.exp(-3))-math.log(1+math.exp(-5)))
sigmoid_cross_entropy_loss = solver.net.blobs['sigmoid_cross_entropy_loss'].data
print('sigmoid_cross_entropy_loss:', sigmoid_cross_entropy_loss)
# 12.5
euclidean_loss = solver.net.blobs['euclidean_loss'].data
print('euclidean_loss:', euclidean_loss)
solver.pt
:
train_net: "train.pt"
base_lr: 0.1
display: 10
max_iter: 300
lr_policy: "step"
gamma: 0.1
momentum: 0.9
weight_decay: 0.0005
stepsize: 200
snapshot: 300
snapshot_prefix: "test"
solver_mode: CPU
device_id: 0
train.pt
:
layer{
name: "data"
type: "Input"
top: "data"
top: "single_label"
top: "full_label"
input_param {
shape{
dim: 1
dim: 2
}
shape{
dim: 1
dim: 1
}
shape{
dim: 1
dim: 2
}
}
}
layer {
name: "euclidean_loss"
type: "EuclideanLoss"
bottom: "data"
bottom: "full_label"
top: "euclidean_loss"
}
layer{
name: "sigmoid_cross_entropy_loss"
type: "SigmoidCrossEntropyLoss"
bottom: "data"
bottom: "full_label"
top: "sigmoid_cross_entropy_loss"
}
layer {
name: "softmax_loss"
type: "SoftmaxWithLoss"
bottom: "data"
bottom: "single_label"
top: "softmax_loss"
}
運行結果:
x: [3,5], y:1,i.e. [0,1]
softmax_loss: 0.12692806
sigmoid_cross_entropy_loss: 3.0553026
euclidean_loss: 12.5