Build custom Metric for Loss Function with Keras, with errors - customization

I'm trying to write custom metric function to set in the compile step wrote in this way:
self.model.compile(optimizer=sgd,loss='categorical_crossentropy',metrics=[self.dice_similarity_coefficient_metric,self.positive_predictive_value_metric,self.sensitivity_metric])
I wrote Dice Similarity Coefficient, Positive Predictive Value and Similarity in this way:
FP = false positive
TP = true positive
FN = false negative
def dice_similarity_coefficient_metric(self, y_true, y_pred):
y_true = np.array(K.eval(y_true))
y_pred = np.array(K.eval(y_pred))
FP = np.sum(y_pred & np.logical_not(y_true)).astype(float)
TP = np.sum(y_true & y_pred).astype(float)
FN = np.sum(np.logical_not(y_pred) &
np.logical_not(y_true)).astype(float)
return K.variable(np.array((2 * TP) / (FP + (2 * TP) + FN +
K.epsilon())))
def positive_predictive_value_metric(self, y_true, y_pred):
y_true = np.array(K.eval(y_true))
y_pred = np.array(K.eval(y_pred))
FP = np.sum(y_pred & np.logical_not(y_true)).astype(float)
TP = np.sum(y_true & y_pred).astype(float)
return K.variable(np.array(TP / (FP + TP + K.epsilon())))
def sensitivity_metric(self, y_true, y_pred):
y_true = np.array(K.eval(y_true))
y_pred = np.array(K.eval(y_pred))
TP = np.sum(y_true & y_pred).astype(float)
FN = np.sum(np.logical_not(y_pred) &
np.logical_not(y_true)).astype(float)
return K.variable(np.array(TP / (TP + FN + K.epsilon())))
when i run the code i have the following error:
InvalidArgumentError (see above for traceback): You must feed a value for placeholder tensor 'dense_3_target' with dtype float
[[Node: dense_3_target = Placeholderdtype=DT_FLOAT, shape=[], _device="/job:localhost/replica:0/task:0/cpu:0"]]
Can someone care to explain where is the problem?
Where i'm wrong?
Thank you

Probably, it's better to define metrics using backend functions. For example:
def false_negatives(Y_true, Y_pred):
return K.sum(K.round(K.clip(Y_true - Y_pred, 0, 1)))
It can be checked on an example data with 5 FN:
y_true = np.array([[1.0, 1.0, 0.0, 1.0], [1.0, 1.0, 0.0, 1.0], [1.0, 1.0, 0.0, 1.0]], dtype=np.float32)
y_pred = np.array([[0.3, 0.99, 0.99, 0.1], [0.6, 0.99, 0.99, 0.1], [0.1, 0.99, 0.99, 0.1]], dtype=np.float32)
n_fn = np.sum((y_true - y_pred) > 0.5)
Y_true = K.placeholder((None, 4), dtype=K.floatx())
Y_pred = K.placeholder((None, 4), dtype=K.floatx())
n_fn = false_negatives(Y_true, Y_pred).eval(inputs_to_values={Y_true: y_true, Y_pred: y_pred})
HTH

Related

Polynomial Coefficients from Sympy to Array

In the below code, L1 simplifies to the transfer function that I want:
import sympy as sy
z = sy.symbols('z')
L1 = sy.simplify(((z**2 - 0.5*z + 0.16) / (z-1)**2 ) - 1)
L1
After this, I manually enter the coefficients for the numerator and denominator as follow:
num = [1.5, -0.84]
den = [1., -2., 1.]
Is there a way to do this from code? I'm not sure how to convert the sympy result to something that I can work with again without manually creating the arrays num and den.
You can use as_numer_denom to get the numerator and denominator and then as_poly and coeffs to get the coefficients:
In [16]: import sympy as sy
...: z = sy.symbols('z')
...: L1 = sy.simplify(((z**2 - 0.5*z + 0.16) / (z-1)**2 ) - 1)
...: L1
Out[16]:
1.0⋅(1.5⋅z - 0.84)
────────────────────
2
1.0⋅z - 2.0⋅z + 1.0
In [17]: num, den = L1.as_numer_denom()
In [18]: num.as_poly(z).coeffs()
Out[18]: [1.5, -0.84]
In [19]: den.as_poly(z).coeffs()
Out[19]: [1.0, -2.0, 1.0]
Or to get the whole expression, you could do :
from sympy import *
z = symbols('z')
L1 = simplify(((z**2 - 0.5*z + 0.16) / (z-1)**2 ) - 1)
srepr(L1)
output:
"Mul(Float('1.0', precision=53), Add(Mul(Float('1.5', precision=53), Symbol('z')),
Float('-0.83999999999999997', precision=53)), Pow(Add(Mul(Float('1.0', precision=53),
Pow(Symbol('z'), Integer(2))), Mul(Integer(-1), Float('2.0', precision=53),
Symbol('z')), Float('1.0', precision=53)), Integer(-1)))"

Custom Loss Function becomes zero when backpropagated

I am trying to write my own custom loss function that is based on the false positive and negative rates. I made a dummy code so you can check the first 2 defenitions as well. I added the rest, so you can see how it is implemented. However, still somewhere the gradient turns out to be zero. What is now the step where the gradient turns zero, or how can I check this? Please I would like to know how I can fix this :).
I tried providing you with more information so you can play around as well, but if you miss anything please do let me know!
The gradient stays True during every step. However, still during the training of the model the loss is not updated, therefore the NN does not train.
y = Variable(torch.tensor((0, 0, 0, 1, 1,1), dtype=torch.float), requires_grad = True)
y_pred = Variable(torch.tensor((0.333, 0.2, 0.01, 0.99, 0.49, 0.51), dtype=torch.float), requires_grad = True)
x = Variable(torch.tensor((0, 0, 0, 1, 1,1), dtype=torch.float), requires_grad = True)
x_pred = Variable(torch.tensor((0.55, 0.25, 0.01, 0.99, 0.65, 0.51), dtype=torch.float), requires_grad = True)
def binary_y_pred(y_pred):
y_pred.register_hook(lambda grad: print(grad))
y_pred = y_pred+torch.tensor(0.5, requires_grad=True, dtype=torch.float)
y_pred = y_pred.pow(5) # this is my way working around using torch.where()
y_pred = y_pred.pow(10)
y_pred = y_pred.pow(15)
m = nn.Sigmoid()
y_pred = m(y_pred)
y_pred = y_pred-torch.tensor(0.5, requires_grad=True, dtype=torch.float)
y_pred = y_pred*2
y_pred.register_hook(lambda grad: print(grad))
return y_pred
def confusion_matrix(y_pred, y):
TP = torch.sum(y*y_pred)
TN = torch.sum((1-y)*(1-y_pred))
FP = torch.sum((1-y)*y_pred)
FN = torch.sum(y*(1-y_pred))
k_eps = torch.tensor(1e-12, requires_grad=True, dtype=torch.float)
FN_rate = FN/(TP + FN + k_eps)
FP_rate = FP/(TN + FP + k_eps)
return FN_rate, FP_rate
def dif_rate(FN_rate_y, FN_rate_x):
dif = (FN_rate_y - FN_rate_x).pow(2)
return dif
def custom_loss_function(y_pred, y, x_pred, x):
y_pred = binary_y_pred(y_pred)
FN_rate_y, FP_rate_y = confusion_matrix(y_pred, y)
x_pred= binary_y_pred(x_pred)
FN_rate_x, FP_rate_x = confusion_matrix(x_pred, x)
FN_dif = dif_rate(FN_rate_y, FN_rate_x)
FP_dif = dif_rate(FP_rate_y, FP_rate_x)
cost = FN_dif+FP_dif
return cost
# I added the rest so you can see how it is implemented, but this peace does not fully run well! If you want this part to run as well, I can add more code.
class FeedforwardNeuralNetModel(nn.Module):
def __init__(self, input_dim, hidden_dim, output_dim):
super(FeedforwardNeuralNetModel, self).__init__()
self.fc1 = nn.Linear(input_dim, hidden_dim)
self.relu1 = nn.ReLU()
self.fc2 = nn.Linear(hidden_dim, output_dim)
self.sigmoid = nn.Sigmoid()
def forward(self, x):
out = self.fc1(x)
out = self.relu1(out)
out = self.fc2(out)
out = self.sigmoid(out)
return out
model = FeedforwardNeuralNetModel(input_dim, hidden_dim, output_dim)
optimizer = torch.optim.Adam(model.parameters(), lr=0.0001, betas=[0.9, 0.99], amsgrad=True)
criterion = torch.nn.BCELoss(weight=None, size_average=None, reduce=None, reduction='mean')
for epoch in range(num_epochs):
train_err = 0
for i, (samples, truths) in enumerate(train_loader):
samples = Variable(samples)
truths = Variable(truths)
optimizer.zero_grad() # Reset gradients
outputs = model(samples) # Do the forward pass
loss2 = criterion(outputs, truths) # Calculate loss
samples_y = Variable(samples_y)
samples_x = Variable(samples_x)
y_pred = model(samples_y)
y = Variable(y, requires_grad=True)
x_pred = model(samples_x)
x= Variable(x, requires_grad=True)
cost = custom_loss_function(y_pred, y, x_pred, x)
loss = loss2*0+cost #checking only if cost works.
loss.backward()
optimizer.step()
train_err += loss.item()
train_loss.append(train_err)
I expect the model to update during training. There is no error message.
With your definitions:TP+FN=y and TN+FP=1-y. Then you'll get FN_rate=1-y_pred and FP_rate=y_pred. Your cost is then FN_rate+FP_rate=1, the gradient of which is 0.
You can check this by hand or using a library for symbolic mathematics (e.g., SymPy):
from sympy import symbols
y, y_pred = symbols("y y_pred")
TP = y * y_pred
TN = (1-y)*(1-y_pred)
FP = (1-y)*y_pred
FN = y*(1-y_pred)
# let's ignore the eps for now
FN_rate = FN/(TP + FN)
FP_rate = FP/(TN + FP)
cost = FN_rate + FP_rate
from sympy import simplify
print(simplify(cost))
# output: 1

How to set parameters for lightgbm when using customized objective function for multi-class classification?

I want to test a customized objective function for lightgbm in multi-class classification.
I have specified the parameter "num_class=3".
However, an error: "
Number of classes must be 1 for non-multiclass training" is thrown
I am using python 3.6 and lightgbm version 0.2
# iris data
from sklearn import datasets
import lightgbm as lgb
import numpy as np
iris = datasets.load_iris()
X = iris['data']
y = iris['target']
# construct train-test
num_train = int(X.shape[0] / 3 * 2)
idx = np.random.permutation(X.shape[0])
x_train = X[idx[:num_train]]
x_test = X[idx[num_train:]]
y_train = y[idx[:num_train]]
y_test = y[idx[num_train:]]
# softmax function
def softmax(x):
'''
input x: an np.array of n_sample * n_class
return : an np.array of n_sample * n_class (probabilities)
'''
x = np.where(x>100, 100, x)
x = np.exp(x)
return x / np.reshape(np.sum(x, 1), [x.shape[0], 1])
# objective function
def objective(y_true, y_pred):
'''
input:
y_true: np.array of size (n_sample,)
y_pred: np.array of size (n_sample, n_class)
'''
y_pred = softmax(y_pred)
temp = np.zeros_like(y_pred)
temp[range(y_pred.shape[0]), y_true] = 1
gradient = y_pred - temp
hessian = y_pred * (1 - y_pred)
return [gradient, hessian]
# lightgbm model
model = lgb.LGBMClassifier(n_estimators=10000,
num_classes = 3,
objective = objective,
nthread=4)
model.fit(x_train, y_train,
eval_metric = 'multi_logloss',
eval_set = [(x_test, y_test), (x_train, y_train)],
eval_names = ['valid', 'train'],
early_stopping_rounds = 200, verbose = 100)
Let me answer my own question.
The arguments in the objective function should be:
y_true of size [n_sample, ]
y_pred of size [n_sample * n_class, ] instead of [n_sample, n_class]
To be more specific, y_pred should be like
y_pred = [first_class, first_class,..., second_class, second_class,..., third_class, third_class,...]
Moreover, gradient and hessian should be grouped in the same way.
def objective(y_true, y_pred):
'''
input:
y_true: np.array of size [n_sample,]
y_pred: np.array of size [n_sample * n_class, ]
return:
gradient and hessian should have exactly the same form of y_pred
'''
y_pred = np.reshape(y_pred, [num_train, 3], order = 'F')
y_pred = softmax(y_pred)
temp = np.zeros_like(y_pred)
temp[range(y_pred.shape[0]), y_true] = 1
gradient = y_pred - temp
hessian = y_pred * (1 - y_pred)
return [gradient.ravel(order = 'F'), hessian.ravel(order = 'F')]

plt.boxplot(data, vert = False) - adding one data point per boxplot - python 2.7, Matplotlib 1.5.3

Have been trying to add one single point to my boxplots. I would like just to add a point as the black ones in the image below.
data_2 = [pd.read_csv(data).values for data in os.listdir(wd)]
bp = plt.boxplot(data_2, labels = labels, vert = False, showfliers = False)
plt.show()
Any ideas for how I should go with that? You can click here to see the pic
You can just plot individual points after the boxplot is finished, just give the appropiate coordinates:
import numpy as np
import matplotlib.pyplot as plt
data = np.array( [
np.random.normal( 0.19, 0.1, 100 ),
np.random.normal( 0.17, 0.1, 100 ),
np.random.normal( 0.11, 0.1, 100 ),
np.random.normal( 0.16, 0.1, 100 ),
np.random.normal( 0.15, 0.1, 100 ) ] ).T
labels = [ 'pred2012', 'pred2007', 'pred2002', 'pred1995', 'pred1988' ]
fig, ax = plt.subplots()
ax.boxplot( data, labels=labels, vert = False, showfliers = False)
ax.plot( -0.1, 4, marker='o' )
ax.plot( 0.3, 3, marker='*', markersize=20 )
plt.savefig( 'boxplot.png' )
plt.show()

How to add a time control panel to a FuncAnimation from matplotlib

I am currently using matplotlib.animation.FuncAnimation() to display an animation of my work, on a figure.
It is working very well, and I understand the arguments I am using ( interval, time range , ...) However, I was wondering if there was a way to implement (maybe directly to the figure) a panel containing the animation, a scroll-bar or whatever, which allows me to :
Move forward or backwards quickly to the time zone of interest.
Show at what point of the animation I am ( 10%, then 20%,...).
Basically, is a way to control the animation in python on the figure like the way I would control it as a video file played by a video player?
If needed, this is what looks like the code for this animation :
def init():
im1.set_data(XYslice[0, :, :])
im2.set_data(XZslice[0, Nplans/2:, :])
return([im1, im2])
def animate(t):
im1.set_data(XYslice[t, :, :])
im2.set_data(XZslice[t, Nplans/2:, :])
return [im1, im2]
anim = animation.FuncAnimation(fig, animate, np.arange(Ntime), interval=200,
blit=True, init_func=init, repeat=True)
What you are talking about is a GUI. The simplest example uses the matplotlib inbuilt widgets:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.mlab import bivariate_normal
from matplotlib.widgets import Slider, Button
#Setup figure and data
fig, ax = plt.subplots()
plt.subplots_adjust(bottom=0.25)
delta = 0.5
t = np.arange(0.0, 100.0, 0.1)
x = np.arange(-3.0, 4.001, delta)
y = np.arange(-4.0, 3.001, delta)
X, Y = np.meshgrid(x, y)
Z1 = bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
Z = (Z1 - Z2) * 5.
cmap = plt.cm.rainbow
im = ax.pcolormesh(X, Y, Z, cmap=cmap)
fig.colorbar(im)
axcolor = 'lightgoldenrodyellow'
axtime = plt.axes([0.25, 0.1, 0.65, 0.03], axisbg=axcolor)
stime = Slider(axtime, 'Time', 0.0, 100.0, valinit=50.0)
#Routines to reset and update sliding bar
def reset(event):
stime.reset()
def update(val):
time = stime.val/10.
Z = (Z1 - Z2) * time
im.set_array(Z.ravel())
fig.canvas.draw()
#Bind sliding bar and reset button
stime.on_changed(update)
resetax = plt.axes([0.8, 0.025, 0.1, 0.04])
button = Button(resetax, 'Reset', color=axcolor, hovercolor='0.975')
button.on_clicked(reset)
plt.show()
This should be a start. If you want it to look better (and add more functionality) then you need to go to a GUI framework like wxpython, check out this example.
An example which is more inline with your data-structure would go as follows:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.mlab import bivariate_normal
from matplotlib.widgets import Slider, Button
#Setup figure and data
fig, ax = plt.subplots()
plt.subplots_adjust(bottom=0.25)
delta = 0.5
t = np.linspace(0.0, 100.0, 256)
x = np.linspace(-4.0, 4.001, 512)
y = np.linspace(-4.0, 4.001, 512)
X, Y = np.meshgrid(x, y)
Z1 = bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
XZslice = np.zeros((256,512,512))
for i in range(t.shape[0]):
XZslice[i,:,:] = (Z1 - Z2) * t[i]/10.
cmap = plt.cm.rainbow
im = ax.pcolormesh(XZslice[128,:,:], cmap=cmap)
fig.colorbar(im)
axcolor = 'lightgoldenrodyellow'
axtime = plt.axes([0.25, 0.1, 0.65, 0.03], axisbg=axcolor)
stime = Slider(axtime, 'Time', 0.0, 100.0, valinit=50.0)
#Routines to reset and update sliding bar
def reset(event):
stime.reset()
def update(val):
time = int(stime.val/100.* 256)
im.set_array(XZslice[time,:,:].ravel())
fig.canvas.draw()
#Bind sliding bar and reset button
stime.on_changed(update)
resetax = plt.axes([0.8, 0.025, 0.1, 0.04])
button = Button(resetax, 'Reset', color=axcolor, hovercolor='0.975')
button.on_clicked(reset)
plt.show()