我已经为Iris数据集(仅有两个标签)建立了用于二进制分类的逻辑回归模型。该模型在所有指标上均表现良好,并且还通过了Andrew Ng给出的梯度检查。但是,当我将输出激活从“ Sigmoid”更改为“ Softmax”并使其适用于多类分类时,即使性能指标非常好,该模型也无法通过梯度检查。
深层神经网络的相同模式,我用numpy的实现通过梯度检查进行了二进制分类,但是对于多类却失败了。
Logistic回归(二进制):
我为功能选择行主要实现样式(行数,列数),但没有选择列主要样式,只是为了使它易于理解和调试。
尺寸:X =(100,4);权重=(4,1); y =(100,1)
算法实现代码(二进制):
import numpy as np
from sklearn.datasets import load_iris, load_digits
from sklearn.preprocessing import LabelBinarizer
from sklearn.metrics import log_loss
from keras.losses import CategoricalCrossentropy
from scipy.special import softmax
def sigmoid(x):
return ( (np.exp(x)) / (1 + np.exp(x) ) )
dataset = load_iris()
lb = LabelBinarizer() # Not used for binary classification
X = dataset.data
y = dataset.target
data = np.concatenate((X[:100],y[:100].reshape(-1,1)), axis = 1)
np.random.shuffle(data)
X_train = data[:, :-1]
X_b = np.c_[np.ones((X_train.shape[0] , 1)), X_train]
y_train = data[:, -1].reshape(-1,1)
num_unique_labels = len( np.unique(y_train) )
Weights = np.random.randn(X_train.shape[1]+1, num_unique_labels-1)* np.sqrt(1./ (X_train.shape[1]+1) )
m = X_b.shape[0]
yhat = sigmoid( np.dot(X_b, Weights))
loss = log_loss(y_train, yhat)
error = yhat - y_train
gradient = (1./m) * ( X_b.T.dot(error) )
梯度检查(二进制):] >>
。这证实了梯度计算是适当的。对于multi_class版本,我从上面使用了相同的梯度计算,但是将输出激活更改为Softmax(也取自scipy),并使用axis = 1来确定样本的最高概率,因为我的是一行-主要实现。grad = gradient.reshape(-1,1) Weights_delta = Weights.reshape(-1,1) num_params = Weights_delta.shape[0] JP = np.zeros((num_params,1)) JM = np.zeros((num_params,1)) J_app = np.zeros((num_params,1)) ep = float(1e-7) for i in range(num_params): Weights_add = np.copy(Weights_delta) Weights_add[i] = Weights_add[i] + ep Z_add = sigmoid(np.dot(X_b, Weights_add.reshape(X_train.shape[1]+1,num_unique_labels-1))) JP[i] = log_loss( y_train, Z_add) Weights_sub = np.copy(Weights_delta) Weights_sub[i] = Weights_sub[i] - ep Z_sub = sigmoid(np.dot(X_b, Weights_sub.reshape(X_train.shape[1]+1,num_unique_labels-1))) JM[i] = log_loss( y_train, Z_sub) J_app[i] = (JP[i] - JM[i]) / (2*ep) num = np.linalg.norm(grad - J_app) denom = np.linalg.norm(grad) + np.linalg.norm(J_app) num/denom
这将产生一个值(num / denom):8.244172628899919e-10
算法实现代码(multi_class):
] >>*Dimensions: X = (150, 4) ; Weights = (4,3) ; y = (150, 3)* import numpy as np from sklearn.datasets import load_iris, load_digits from sklearn.preprocessing import LabelBinarizer from keras.losses import CategoricalCrossentropy from scipy.special import softmax CCE = CategoricalCrossentropy() dataset = load_iris() lb = LabelBinarizer() X = dataset.data y = dataset.target lb.fit(y) data = np.concatenate((X,y.reshape(-1,1)), axis = 1) np.random.shuffle(data) X_train = data[:, :-1] X_b = np.c_[np.ones((X_train.shape[0] , 1)), X_train] y_train = lb.transform(data[:, -1]).reshape(-1,3) num_unique_labels = len( np.unique(y) ) Weights = np.random.randn(X_train.shape[1]+1, num_unique_labels) * np.sqrt(1./ (X_train.shape[1]+1) ) m = X_b.shape[0] yhat = softmax( np.dot(X_b, Weights), axis = 1) cce_loss = CCE(y_train, yhat).numpy() error = yhat - y_train gradient = (1./m) * ( X_b.T.dot(error) )
梯度检查(multi_class):
grad = gradient.reshape(-1,1) Weights_delta = Weights.reshape(-1,1) num_params = Weights_delta.shape[0] JP = np.zeros((num_params,1)) JM = np.zeros((num_params,1)) J_app = np.zeros((num_params,1)) ep = float(1e-7) for i in range(num_params): Weights_add = np.copy(Weights_delta) Weights_add[i] = Weights_add[i] + ep Z_add = softmax(np.dot(X_b, Weights_add.reshape(X_train.shape[1]+1,num_unique_labels)), axis = 1) JP[i] = CCE( y_train, Z_add).numpy() Weights_sub = np.copy(Weights_delta) Weights_sub[i] = Weights_sub[i] - ep Z_sub = softmax(np.dot(X_b, Weights_sub.reshape(X_train.shape[1]+1,num_unique_labels)), axis = 1) JM[i] = CCE( y_train, Z_sub).numpy() J_app[i] = (JP[i] - JM[i]) / (2*ep) num = np.linalg.norm(grad - J_app) denom = np.linalg.norm(grad) + np.linalg.norm(J_app) num/denom
这产生一个值:0.3345
。这显然是无法接受的差异。现在,这让我想知道我是否可以一开始就相信我的二进制标签梯度检查代码。我还在数字数据集上测试了该逻辑回归代码(具有相同的梯度计算),性能再次非常好(准确度,准确性,召回率均> 95%)。真正令我着迷的是,即使模型的性能足够好,也无法通过梯度检查。与我之前提到的神经网络相同(对binary传递,对multi_class失败)。
我什至尝试了Andrew Ng在其课程过程中提供的代码,即使该代码通过了二进制也无法通过多类。我似乎无法弄清楚我的代码在哪里有任何错误,如果它们有小错误,在第一种情况下如何传递?
我查看了这些SO,但是我觉得它们的问题与我的不同:
我正在寻找的是:
建议/修正我的用于二进制预测的梯度计算和梯度检查代码是否准确。
建议/关于多类实现可能出错的一般说明。
您将获得什么:(:P)
[由20多岁的技术专家表示感谢,他认为每个文档页面的书写都不好:)
更新:更正了一些拼写错误,并根据Alex的建议添加了更多行代码。我还意识到,在进行多类预测的情况下,我的近似梯度值(名称为J_app)非常高(1e + 2);因为我将原始梯度乘以(1./m)乘以原来的梯度(乘以名字gradient),所以我的原始梯度值大约为(1e-1至1e-2)。
近似梯度与原始梯度的值范围之间的明显差异解释了为什么我得到的最终值约为(1e + 1,0.3345)。但是,我无法弄清楚的是,如何解决这个看似很明显的错误。
我已经为虹膜数据集(仅两个标签)建立了用于二进制分类的逻辑回归模型。该模型在所有指标上均表现良好,并且还通过了给定的梯度检查...
CategoricalCrossentropy
中的keras
默认情况下以单精度运行。在脚本的开头添加以下行,您将获得num/denom
通常在1.e-9
附近:import keras
keras.backend.set_floatx('float64')