Softmax 函数、神经网络输出作为概率和集合分类器 Python实现
介绍:
在很多情况下,当使用神经网络模型(例如常规深度前馈网络和卷积网络对某些类别标签集合进行分类任务)时,人们会想知道是否有可能解释输出,例如y = [0.02,0,0.005,0.975 ],作为一些输入在一个类等于各自的元件值中的概率ÿ ᵢ 在输出向量。直接跳到长答案:不,除非你有一个softmax层作为你的输出层,并且用交叉熵损失函数训练网络。这一点很重要,因为它有时会在网上资源中被忽略,甚至在一些关于神经网络分类的教科书中也会被忽略。我们将看看softmax函数是如何在多项逻辑回归的背景下得出的,以及如何将它应用到集成深度神经网络模型以进行稳健分类。
派生Softmax功能:
简而言之,分类分布是伯努利分布的多类推广。伯努利分布是一个离散的概率分布,模拟单个实验的结果,或单个观察具有两个结果的随机变量(例如单个硬币翻转的结果)。分类分布自然将伯努利分布扩展到具有两个以上结果的实验。
现在,简单逻辑回归分类(即逻辑上只有两班或成果回归)假定输出Yᵢ(我是数据样本索引)空调上输入X ᵢ是伯努利分布:
将伯努利结果的对数几率与线性预测值相关联的链接函数是logit函数:
如果我们对上述等式的两边求指数并做一些重新排列,在右边(RHS)我们得到熟悉的逻辑函数:
推导多项Logistic回归的广义Logistic或Softmax函数的一种方法是首先为每个K类设置一个Logit关联线性预测器,再加上一些归一化因子以确保所有类的概率总和等于1。由此产生的K个方程组是一个对数线性概率模型的系统:
上述方程组中的ln(Z) 项 是归一化因子的(对数),Z被称为分配函数。在我们处理多项式回归时,这个方程组给出了分类分布的概率:Yᵢ| X ᵢ〜范畴(p ᵢ)。
指数化双方并施加限制:
上述方程的RHS称为吉布斯测度,并将softmax函数与统计力学相联系。接下来,解决Z给出:
最后方程组变成:
每个方程的RHS比率是softmax函数。一般来说,softmax函数的定义如下:
对于J = 1 ... ķ。我们可以看到,softmax函数将任意实数值的K维向量z归一化为其分量总和为1(换句话说,概率向量)的K维向量σ(z),并且它还提供了每个向量的加权平均值zⱼ相对于集合体zⱼ的在于夸大差异的方式(返回接近0或1的值),如果zⱼ的彼此在规模很大的不同,但返回适中值,如果zⱼ的规模相对相同。分类器模型需要学习给出前者条件而不是后者的参数(即决定性vs不决定性)。
最后,正如logit函数是简单逻辑回归的连接函数,逻辑函数是logit函数的逆函数,多项logit函数是多项逻辑回归的连接函数,softmax可以被认为是多项logit函数。通常在多项逻辑回归中,使用最大后验(MAP)估计来查找每个类别k的参数β。
交叉熵和集成神经网络分类器
现在我们已经看到了softmax函数的来源,现在是我们在神经网络分类器模型中使用它们的时候了。在softmax输出层配备的神经网络上要最小化的损失函数是交叉熵损失:
其中Ÿ是某次迭代的正确标签来我和ŷ是在迭代神经网络的输出我。实际上,这种损失函数与简单和多项逻辑回归相同。交叉熵函数的一般定义是:
之间的交叉熵p和q被定义为分布的信息熵的总和P,其中p是一些底层真实分布(在这种情况下将是真实的类别标签的类别分布)和相对熵的分布q这 是我们尝试近似p和p本身。在此函数上进行优化可以最大限度地减少p的信息熵(在p中给出更多的某些结果),同时最小化p和q之间的“距离”。
在Bridle的文章中给出了在神经网络中使用softmax作为输出层激活的理论处理。本文的要点是,使用带有神经网络隐藏层输出的softmax输出层作为每个zⱼ,使用交叉熵损失进行训练,从而给出类别标签上的后验分布(分类分布)。一般而言,深层神经网络可以大大优于简单多项式逻辑回归,代价是不能提供特征/参数的统计显着性,这是推断或找出哪些特征影响分类结果的一个非常重要的方面。完整的神经网络使用强大的优化器进行优化; RMSprop通常是一个好的开始。
因此,现在我们将使用Keras功能API调出一个深度前馈神经网络分类器,并进行一些葡萄酒分类。我们将使用几个神经网络的集合模型来给我们一个强大的分类(在实践中,这是你应该做的,由于随机初始化和随机梯度训练导致的个体神经网络预测的差异必须平均以获得好的结果)。集合模型的输出应给出一个概率向量,即某个测试示例将属于每个类别,即对类别标签的分类分布。
聚合每个单独神经网络模型结果的一种方法是在集合输出上使用softmax来给出最终概率。为了自动确定最终的softmax平均值的最佳权重,我们将在另一层上粘合每个单独神经网络在集合中的输出。下面是架构图。
每个学习模型将与最终的softmax聚合输出相反。我们可以使用Keras连接合并层将每个子网络合并在一起。连接层连接来自每个子网络的输出张量,并允许优化器对合并模型进行优化。为了简化我们的训练,每个学习模型都将在相同的数据集上进行训练。可以使用自举子集,但这使得训练更为复杂,因为我们必须单独训练每个子网,以便在其自己的输入和目标对上单独训练,同时冻结其余学习模型的训练更新。
import numpy as np
from sklearn.datasets import load_wine
from sklearn.preprocessing import MinMaxScaler, OneHotEncoder
from keras.layers import Dense, Input, concatenate, Dropout
from keras.models import Model
from keras.optimizers import rmsprop
dataset = load_wine()
ensemble_num = 10 # number of sub-networks
bootstrap_size = 0.8 # 80% size of original (training) dataset
training_size = 0.8 # 80% for training, 20% for test
num_hidden_neurons = 10 # number of neurons in hidden layer
dropout = 0.25 # percentage of weights dropped out before softmax output (this prevents overfitting)
epochs = 200 # number of epochs (complete training episodes over the training set) to run
batch = 10 # mini batch size for better convergence
# get the holdout training and test set
temp = []
scaler = MinMaxScaler()
one_hot = OneHotEncoder() # one hot encode the target classes
dataset['data'] = scaler.fit_transform(dataset['data'])
dataset['target'] = one_hot.fit_transform(np.reshape(dataset['target'], (-1,1)) ).toarray()
for i in range(len(dataset.data)):
temp.append([dataset['data'][i], np.array(dataset['target'][i])])
# shuffle the row of data and targets
temp = np.array(temp)
np.random.shuffle(temp)
# holdout training and test stop index
stop = int(training_size*len(dataset.data))
train_X = np.array([x for x in temp[:stop,0]])
train_Y = np.array([x for x in temp[:stop,1]])
test_X = np.array([x for x in temp[stop:,0]])
test_Y = np.array([x for x in temp[stop:,1]])
# now build the ensemble neural network
# first, let's build the individual sub-networks, each
# as a Keras functional model.
sub_net_outputs = []
sub_net_inputs = []
for i in range(ensemble_num):
# two hidden layers to keep it simple
# specify input shape to the shape of the training set
net_input = Input(shape = (train_X.shape[1],))
sub_net_inputs.append(net_input)
y = Dense(num_hidden_neurons)(net_input)
y = Dense(num_hidden_neurons)(y)
y = Dropout(dropout)(y)
sub_net_outputs.append(y) # sub_nets contains the output tensors
# now concatenate the output tensors
y = concatenate(sub_net_outputs)
# final softmax output layer
y = Dense(train_Y[0].shape[0], activation='softmax')(y)
# now build the whole funtional model
model = Model(inputs=sub_net_inputs, outputs=y)
model.compile(optimizer='rmsprop', loss='categorical_crossentropy')
print("Begin training...")
# train the model
model.fit( [train_X] * ensemble_num, train_Y,validation_data=[ [test_X] * ensemble_num, test_Y],
epochs=epochs, batch_size=batch)
特征选择对于我们的模型来说并不是非常重要,因为它使用所有特征很好地学习数据集。在200个历元后,训练和验证损失分别变小到10 ^ -5和10 ^ -3的数量级,这表明我们的集成神经网络模型在拟合数据和预测测试集方面做得很好。对于正确的类别,输出概率接近100%,其他类别的输出概率为0%
在本文中,我们推导了多项逻辑回归的softmax激活,并看到如何将它应用于神经网络分类器。在解释神经网络输出是可能性时记住要小心是非常重要的。然后,我们使用Keras功能API构建了一个集成神经网络分类器。