[DL]BinaryClassification with Sigmoid / Softmax

Posted by John on 2018-09-16
Words 851 and Reading Time 3 Minutes
Viewed Times

這篇會用我自己的理解簡介一下在做二元分類(Binary Classification or Logistic Classification)上時使用Sigmoid和Softmax的差異,以及他們應該對應哪一個crossentropy(不然你會得到錯誤訊息或是怎麼train都train不好…QQ)。

簡介Activation function

在訓練Neural Network的時候,會針對每層Layer加上Activation function,使得每層的輸入(inputs)乘上權重(weights)後會先轉換成另一個數值再傳到下一層。

比方說對於Hidden Layer我們通常會加上sigmoid, relu…作為activation function,以神經網路的意義來看就是今天當輸入給予的能量超過一個閥值(threshold)時便會刺激神經元發出訊息傳導,但如果沒有超出該閥值則訊息不會傳導。

而在分類問題上,最後一層的activation function通常會不一樣。我們常使用softmax在多元分類的問題(multi-classification),而使在二元分類(binary/ lodistic classification)的問題上則使用sigmoid。

公式介紹

公式來自這網址,我擷取部分的資料出來簡述。

Sigmoid的公式:將一個數值轉成介於[0, 1]之間的值,也可以想成二元分類時的機率

01

Softmax的公式: 可以想成多類別的時候某個類別的機率

022

然後想提個問題讓大家想想,softmax和sigmoid都可以表示機率,只是差在類別數量,那是否都能用在二元分類呢?

來做個推導一下:

032

結論是:在二元分類時,其實softmax和sigmoid是等價的,也就是說我們可以說softmax是sigmoid的一個特例(在二元分類時)。

討論

再重複一次想討論的問題:既然兩個activation function在二元分類的時候是等價的,那實際上在做二元分類(只有0和1)時,是否使用softmax和sigmoid都可以呢?

答案是可以的,但是必須要注意搭配的Loss function

  • softmax + categorical_crossentropy: 並且在Keras裡面對於categorical_crossentropy的實作有點不一樣(詳情看這篇): Keras對於categorical_crossentropy是使用one-hot的形式去計算的,所以即便是二元分類問題(output只有{0, 1}),最後一層也必須要是2顆Neuron,並且y必須是one-hot encoding後的array。
  • sigmoid + binary_crossentropy: 這是最不會有問題的方式,並且最後一層只需要1顆Neuron即可。

程式碼範例

以下給了使用VGG16作為example,講說對於二元分類問題使用softmax + categorical_crossentropy和sigmoid+binary_crossentropy的寫法差異,主要看最後一層Layer和Lossfunction、y的樣子的不同之處即可。

sigmoid+binary_crossentropy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#VGG 16

model = Sequential()
model.add(Conv2D(filters=32, kernel_size=(3, 3),
input_shape=(150, 150, 3), activation='relu'))
model.add(MaxPool2D(pool_size=(2, 2)))
model.add(Conv2D(filters=32, kernel_size=(3, 3), activation='relu'))
model.add(MaxPool2D(pool_size=(2, 2)))
model.add(Conv2D(filters=64, kernel_size=(3, 3), activation='relu'))
model.add(MaxPool2D(pool_size=(2, 2)))

model.add(Flatten())
model.add(Dense(units=64, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(units=1, activation='sigmoid'))

model.compile(loss='binary_crossentropy', optimizer='rmsprop', metrics=['accuracy'])

# X:(2000,150,150,3)
# y:(2000,)
model.fit(X, y)

softmax + categorical_crossentropy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#VGG 16

model = Sequential()
model.add(Conv2D(filters=32, kernel_size=(3, 3),
input_shape=(150, 150, 3), activation='relu'))
model.add(MaxPool2D(pool_size=(2, 2)))
model.add(Conv2D(filters=32, kernel_size=(3, 3), activation='relu'))
model.add(MaxPool2D(pool_size=(2, 2)))
model.add(Conv2D(filters=64, kernel_size=(3, 3), activation='relu'))
model.add(MaxPool2D(pool_size=(2, 2)))

model.add(Flatten())
model.add(Dense(units=64, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(units=2, activation='softmax'))

# X:(2000,150,150,3)
# y:need to ont-hot form ->(2000,2)
model.compile(loss='categorical_crossentropy', optimizer='rmsprop', metrics=['accuracy'])
from keras.utils.np_utils import to_categorical
model.fit(X, to_categorical(y))

>