-
Notifications
You must be signed in to change notification settings - Fork 19.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to train a multi-label Classifier #741
Comments
Don't use softmax. Use sigmoid units in the output layer and then use "binary_crossentrpy" loss. |
That works in my case. However |
Getting classes from .predict() is one line of numpy code really. |
model.predict(blabla) > 0.5 ? |
@elanmart Hi, why do you think using softmax is not a good idea? Do you use a graph model, given we have multiple outputs? |
my loss is not convergence @holderm @elanmart model.predict(Y_train[1,:]) it shows [ 0.00000000e+000 0.00000000e+000 0.00000000e+000 0.00000000e+000 from __future__ import absolute_import
from __future__ import print_function
import scipy.io
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Sequential
from keras.layers.core import Dense, Dropout, Activation, Flatten
from keras.layers.convolutional import Convolution2D, MaxPooling2D
from keras.optimizers import SGD, Adadelta, Adagrad
from keras.utils import np_utils, generic_utils
from six.moves import range
batch_size = 100
nb_classes = 5
nb_epoch = 5
data_augmentation = True
shapex, shapey = 64, 64
nb_filters = [32, 64]
nb_pool = [4, 3]
nb_conv = [5, 4]
image_dimensions = 3
mat = scipy.io.loadmat('E:\scene.mat')
X_train = mat['x_train']
Y_train = mat['y_train']
X_test = mat['x_test']
Y_test = mat['y_test']
print(X_train.shape)
print(X_test.shape)
model = Sequential()
model.add(Convolution2D(nb_filters[0], image_dimensions, nb_conv[0], nb_conv[0], border_mode='valid'))
model.add(Activation('relu'))
model.add(MaxPooling2D(poolsize=(nb_pool[0], nb_pool[0])))
model.add(Dropout(0.25))
model.add(Convolution2D(nb_filters[1], nb_filters[0], nb_conv[1], nb_conv[1], border_mode='valid'))
model.add(Activation('relu'))
model.add(MaxPooling2D(poolsize=(nb_pool[1], nb_pool[1])))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(nb_filters[-1] * (((shapex - nb_conv[0]+1)/ nb_pool[0] -nb_conv[1]+1)/ nb_pool[1]) * (((shapey -nb_conv[0]+1)/ nb_pool[0] -nb_conv[1]+1)/ nb_pool[1]), 512))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(512, nb_classes,init='uniform'))
model.add(Activation('sigmoid'))
sgd = SGD(lr=0.01, decay=1e-6, momentum=0.9, nesterov=True)
model.compile(loss='binary_crossentropy', optimizer=sgd)
if not data_augmentation:
print("Not using data augmentation or normalization")
X_train = X_train.astype("float32")
X_test = X_test.astype("float32")
X_train /= 255
X_test /= 255
model.fit(X_train, Y_train, batch_size=batch_size, nb_epoch=nb_epoch)
score = model.evaluate(X_test, Y_test, batch_size=batch_size)
print('Test score:', score)
else:
print("Using real time data augmentation")
# this will do preprocessing and realtime data augmentation
datagen = ImageDataGenerator(
featurewise_center=True, # set input mean to 0 over the dataset
samplewise_center=False, # set each sample mean to 0
featurewise_std_normalization=True, # divide inputs by std of the dataset
samplewise_std_normalization=False, # divide each input by its std
zca_whitening=False, # apply ZCA whitening
rotation_range=20, # randomly rotate images in the range (degrees, 0 to 180)
width_shift_range=0.2, # randomly shift images horizontally (fraction of total width)
height_shift_range=0.2, # randomly shift images vertically (fraction of total height)
horizontal_flip=True, # randomly flip images
vertical_flip=False) # randomly flip images
datagen.fit(X_train)
model.fit(X_train, Y_train, batch_size=batch_size, nb_epoch=nb_epoch)
score = model.evaluate(X_test, Y_test, batch_size=batch_size)
print (model.predict(X_test[1,:]))
could you help me to find out where it is wrong, thx ! |
@lemuriandezapada yeah, labels = np.zeros(preds.shape)
labels[preds>0.5] = 1 @arushi02 in softmax when increasing score for one label, all others are lowered (it's a probability distribution). You don't want that when you have multiple labels. Here's an example of one of my multilabel nets: # Build a classifier optimized for maximizing f1_score (uses class_weights)
clf = Sequential()
clf.add(Dropout(0.3))
clf.add(Dense(xt.shape[1], 1600, activation='relu'))
clf.add(Dropout(0.6))
clf.add(Dense(1600, 1200, activation='relu'))
clf.add(Dropout(0.6))
clf.add(Dense(1200, 800, activation='relu'))
clf.add(Dropout(0.6))
clf.add(Dense(800, yt.shape[1], activation='sigmoid'))
clf.compile(optimizer=Adam(), loss='binary_crossentropy')
clf.fit(xt, yt, batch_size=64, nb_epoch=300, validation_data=(xs, ys), class_weight=W, verbose=0)
preds = clf.predict(xs)
preds[preds>=0.5] = 1
preds[preds<0.5] = 0
print f1_score(ys, preds, average='macro') @xieximeng2008 What does it print during training? |
@elanmart Using real time data augmentation Epoch 0
100/1800 [>.............................] - ETA: 58s - loss: 8.1209��������������������������������������������������������������������
200/1800 [==>...........................] - ETA: 55s - loss: 6.7125��������������������������������������������������������������������
300/1800 [====>.........................] - ETA: 51s - loss: 6.2430��������������������������������������������������������������������
400/1800 [=====>........................] - ETA: 48s - loss: 6.0284��������������������������������������������������������������������
500/1800 [=======>......................] - ETA: 44s - loss: 6.1214��������������������������������������������������������������������
600/1800 [=========>....................] - ETA: 40s - loss: 5.9915��������������������������������������������������������������������
700/1800 [==========>...................] - ETA: 37s - loss: 5.8876��������������������������������������������������������������������
800/1800 [============>.................] - ETA: 33s - loss: 5.7681��������������������������������������������������������������������
900/1800 [==============>...............] - ETA: 30s - loss: 5.6844��������������������������������������������������������������������
1000/1800 [===============>..............] - ETA: 27s - loss: 5.6092��������������������������������������������������������������������
1100/1800 [=================>............] - ETA: 23s - loss: 5.5703��������������������������������������������������������������������
1200/1800 [===================>..........] - ETA: 20s - loss: 5.5240��������������������������������������������������������������������
1300/1800 [====================>.........] - ETA: 16s - loss: 5.4976��������������������������������������������������������������������
1400/1800 [======================>.......] - ETA: 13s - loss: 5.4809��������������������������������������������������������������������
1500/1800 [========================>.....] - ETA: 10s - loss: 5.4526��������������������������������������������������������������������
1600/1800 [=========================>....] - ETA: 6s - loss: 5.4486 �������������������������������������������������������������������
1700/1800 [===========================>..] - ETA: 3s - loss: 5.4596�������������������������������������������������������������������
1800/1800 [==============================] - 60s - loss: 5.4326
Epoch 1
100/1800 [>.............................] - ETA: 56s - loss: 5.1808��������������������������������������������������������������������
200/1800 [==>...........................] - ETA: 52s - loss: 5.0979��������������������������������������������������������������������
300/1800 [====>.........................] - ETA: 49s - loss: 5.1670��������������������������������������������������������������������
400/1800 [=====>........................] - ETA: 45s - loss: 5.2326��������������������������������������������������������������������
500/1800 [=======>......................] - ETA: 42s - loss: 5.2554��������������������������������������������������������������������
600/1800 [=========>....................] - ETA: 39s - loss: 5.2430��������������������������������������������������������������������
700/1800 [==========>...................] - ETA: 36s - loss: 5.2104��������������������������������������������������������������������
800/1800 [============>.................] - ETA: 33s - loss: 5.1912��������������������������������������������������������������������
900/1800 [==============>...............] - ETA: 29s - loss: 5.1716��������������������������������������������������������������������
1000/1800 [===============>..............] - ETA: 26s - loss: 5.1559��������������������������������������������������������������������
1100/1800 [=================>............] - ETA: 23s - loss: 5.1318��������������������������������������������������������������������
1200/1800 [===================>..........] - ETA: 19s - loss: 5.1532��������������������������������������������������������������������
1300/1800 [====================>.........] - ETA: 16s - loss: 5.1489��������������������������������������������������������������������
1400/1800 [======================>.......] - ETA: 13s - loss: 5.1512��������������������������������������������������������������������
1500/1800 [========================>.....] - ETA: 9s - loss: 5.1642 �������������������������������������������������������������������
1600/1800 [=========================>....] - ETA: 6s - loss: 5.1549�������������������������������������������������������������������
1700/1800 [===========================>..] - ETA: 3s - loss: 5.1418�������������������������������������������������������������������
1800/1800 [==============================] - 59s - loss: 5.1325
Epoch 2
100/1800 [>.............................] - ETA: 56s - loss: 5.2637��������������������������������������������������������������������
200/1800 [==>...........................] - ETA: 52s - loss: 5.1394��������������������������������������������������������������������
300/1800 [====>.........................] - ETA: 49s - loss: 5.1117��������������������������������������������������������������������
400/1800 [=====>........................] - ETA: 46s - loss: 5.0150��������������������������������������������������������������������
500/1800 [=======>......................] - ETA: 42s - loss: 5.0150��������������������������������������������������������������������
600/1800 [=========>....................] - ETA: 39s - loss: 4.9874��������������������������������������������������������������������
700/1800 [==========>...................] - ETA: 36s - loss: 5.0387��������������������������������������������������������������������
800/1800 [============>.................] - ETA: 32s - loss: 5.0565��������������������������������������������������������������������
900/1800 [==============>...............] - ETA: 29s - loss: 5.0565��������������������������������������������������������������������
1000/1800 [===============>..............] - ETA: 26s - loss: 5.0813��������������������������������������������������������������������
1100/1800 [=================>............] - ETA: 23s - loss: 5.0942��������������������������������������������������������������������
1200/1800 [===================>..........] - ETA: 19s - loss: 5.0876��������������������������������������������������������������������
1300/1800 [====================>.........] - ETA: 16s - loss: 5.1234��������������������������������������������������������������������
1400/1800 [======================>.......] - ETA: 13s - loss: 5.1305��������������������������������������������������������������������
1500/1800 [========================>.....] - ETA: 9s - loss: 5.1256 �������������������������������������������������������������������
1600/1800 [=========================>....] - ETA: 6s - loss: 5.1316�������������������������������������������������������������������
1700/1800 [===========================>..] - ETA: 3s - loss: 5.1296�������������������������������������������������������������������
1800/1800 [==============================] - 60s - loss: 5.1325
Epoch 3
100/1800 [>.............................] - ETA: 56s - loss: 4.7664��������������������������������������������������������������������
200/1800 [==>...........................] - ETA: 52s - loss: 5.0772��������������������������������������������������������������������
300/1800 [====>.........................] - ETA: 49s - loss: 5.1394��������������������������������������������������������������������
400/1800 [=====>........................] - ETA: 46s - loss: 5.1290��������������������������������������������������������������������
500/1800 [=======>......................] - ETA: 42s - loss: 5.1311��������������������������������������������������������������������
600/1800 [=========>....................] - ETA: 39s - loss: 5.1601��������������������������������������������������������������������
700/1800 [==========>...................] - ETA: 36s - loss: 5.1157��������������������������������������������������������������������
800/1800 [============>.................] - ETA: 33s - loss: 5.1497��������������������������������������������������������������������
900/1800 [==============>...............] - ETA: 29s - loss: 5.1716��������������������������������������������������������������������
1000/1800 [===============>..............] - ETA: 26s - loss: 5.1891��������������������������������������������������������������������
1100/1800 [=================>............] - ETA: 23s - loss: 5.1695��������������������������������������������������������������������
1200/1800 [===================>..........] - ETA: 19s - loss: 5.1705��������������������������������������������������������������������
1300/1800 [====================>.........] - ETA: 16s - loss: 5.1585��������������������������������������������������������������������
1400/1800 [======================>.......] - ETA: 13s - loss: 5.1660��������������������������������������������������������������������
1500/1800 [========================>.....] - ETA: 9s - loss: 5.1587 �������������������������������������������������������������������
1600/1800 [=========================>....] - ETA: 6s - loss: 5.1394�������������������������������������������������������������������
1700/1800 [===========================>..] - ETA: 3s - loss: 5.1394�������������������������������������������������������������������
1800/1800 [==============================] - 59s - loss: 5.1325
Epoch 4
100/1800 [>.............................] - ETA: 55s - loss: 5.1394��������������������������������������������������������������������
200/1800 [==>...........................] - ETA: 52s - loss: 5.1394��������������������������������������������������������������������
300/1800 [====>.........................] - ETA: 49s - loss: 5.1117��������������������������������������������������������������������
400/1800 [=====>........................] - ETA: 45s - loss: 5.1601��������������������������������������������������������������������
500/1800 [=======>......................] - ETA: 42s - loss: 5.1477��������������������������������������������������������������������
600/1800 [=========>....................] - ETA: 39s - loss: 5.1808��������������������������������������������������������������������
700/1800 [==========>...................] - ETA: 36s - loss: 5.1334��������������������������������������������������������������������
800/1800 [============>.................] - ETA: 32s - loss: 5.1290��������������������������������������������������������������������
900/1800 [==============>...............] - ETA: 29s - loss: 5.1163��������������������������������������������������������������������
1000/1800 [===============>..............] - ETA: 26s - loss: 5.1311��������������������������������������������������������������������
1100/1800 [=================>............] - ETA: 23s - loss: 5.1431��������������������������������������������������������������������
1200/1800 [===================>..........] - ETA: 19s - loss: 5.1394��������������������������������������������������������������������
1300/1800 [====================>.........] - ETA: 16s - loss: 5.1298��������������������������������������������������������������������
1400/1800 [======================>.......] - ETA: 13s - loss: 5.1423��������������������������������������������������������������������
1500/1800 [========================>.....] - ETA: 9s - loss: 5.1338 �������������������������������������������������������������������
1600/1800 [=========================>....] - ETA: 6s - loss: 5.1161�������������������������������������������������������������������
1700/1800 [===========================>..] - ETA: 3s - loss: 5.1174�������������������������������������������������������������������
1800/1800 [==============================] - 59s - loss: 5.1325 testing... 100/200 [==============>...............] - ETA: 1s��������������������������������������������������
200/200 [==============================] - 2s
[[ 0.00000000e+000 0.00000000e+000 0.00000000e+000 0.00000000e+000
0.00000000e+000]
[ 0.00000000e+000 0.00000000e+000 0.00000000e+000 0.00000000e+000
0.00000000e+000]
[ 0.00000000e+000 0.00000000e+000 0.00000000e+000 0.00000000e+000
0.00000000e+000]
[ 0.00000000e+000 0.00000000e+000 0.00000000e+000 0.00000000e+000
0.00000000e+000]
[ 1.22857558e-291 0.00000000e+000 3.11779756e-297 0.00000000e+000
0.00000000e+000]
.........
......... almost all outputs are zero or very very small float num |
@elanmart I used your example ,but also have above problems. dataset : X_train (1800,3,64,64), model.fit(X_train, Y_train, batch_size=batch_size, nb_epoch=nb_epoch,validation_data = (X_test,Y_test),verbose = 0)
preds = model.predict(X_test)
preds[preds>= 0.5] = 1
preds[preds<0.5] = 0
print (preds) Thanks for helping me! |
@xieximeng2008 I'd guess the problem is in your data, since the network worked well for me few days ago. |
Suppose I want to identify a house no 5436 from an image and I assume every image will have max 4 digits, so one image will be tagged with 4 one hot vectors like [(0000010000), (0000100000), (0001000000), (0000001000)] and I pass this as a 2D matrix then will it give me probabilities for each element? In this kind of tagging, I want every row to have one element which is most probable (following a probability distribution). |
Does anyone know how to replace the default the validation score by the another scoring function printed at every epoch? The scoring function for validation set should be similar to the one implemented for test set. Many thanks. clf.fit(xt, yt, batch_size=64, nb_epoch=300, validation_data=(xs, ys), class_weight=W, verbose=0) |
@elanmart |
@suraj-deshmukh ,Do you solve your problem how to load the multi-label data? How do you do it? Do you share your code? Thanks. |
@alyato , Hi I solved my problem but I lost all my codes :( due to hdd failure. But as I said in previous comment my y/target was [1,1,-1,-1,-1] and I converted it into [1,1,0,0,0] where 1 == presence and 0 == absence for all images and passed that data to ConvNet having binary crossentropy as loss function and sigmoid as activation function for output layer. |
@suraj-deshmukh ,Does i understand it like this.
So i load the train_data and train_label. The format of train_label is [0,1,2].
Then The format of train_label is [ [1,0,1],[0,1,1],[1,1,0] ] Is that right?
|
@alyato |
@suraj-deshmukh ,Thanks for your answer. But i also have some questions.
how to measure my model is better or worse? |
@elanmart I am kind of disagree with the conclusion. Maybe I am wrong. |
Hi, I'm trying to classify an image with multiple digits. Say an image with "123" to output "123". There are up to 5 digits. I'm stuck after I built the convolution layers. How do we output 5 digits each with 10 classes? Some suggested 5 independent fully connected layers after the final convolution layer. But how do we code this in Keras for the 5 independent FCs? |
@xieximeng2008 Did you ever find out why your network only returned values close to zero? I am in a similar situation where my network only returns zeroes. I am fine-tuning an InceptionV3 model. Loss function is binary_crossentropy, I am using sigmoid as activation for the final layer, and as an optimizer I use rmsprop. |
like this! modify sgd to Adam, could dec loss! thank @elanmart |
This thread is really helpful! |
@michelleowen I am in no way an expert, but could it maybe work to set the NaN values to 0.5? This might not work in general, and it might be that this value should be tweaked dependent on the problem. |
@janmatias Yes, I agree it is one workaround, but not perfect. I am thinking to modify the loss function, if the true response is NaN, then don't penalize it in the loss function. However, I am not quite sure which part of the keras code I should modify. |
Awesome! I still have a question. If the dataset is quite imbalanced, i.e. samples in some categories are much more than others, how can I adopt class_weight to solve this to get a multi-label prediction? Can anybody answer me? @suraj-deshmukh @xieximeng2008 |
@pieroit @bryan831 you could try to give more weight to positive targets in the loss function. If you use the tensorflow backend of keras you can use Would be interested to hear if this worked for you and how you set the POS_WEIGHT in relation to your number of classes! |
Hi! I am facing a bit different problem in training multi-label classifier.
Please tell me how to deal with this problem. |
@pieroit @bryan831 |
@hpnhxxwn did you try out the code I posted in the stackoverflow answer? Should be easy for you to test with copy & paste if you use the tensorflow backend. |
Instead of:
you can just write:
We threshold the probabilities to obtain a boolean vector which we in turn convert to integers. It's less imperative than two assignments. Possibly you could either keep the booleans without conversion. |
I am facing the problem with input shape of model for categorical classifier i tried this model and getting value error model=sequential() Value error: Error when checking target: expected dense_1 to have shape(None,1) but got array with shape (6000,3) i dont understand this error. help me to sort out this |
facing the same problem as @vijaycol ... but in my case, it's about image segmentation. I am passing X_train of shape (209,256,256,3). i.e. 209 images,256x256 size of each and 3 channels. It's everywhere mentioned that Y_train should be one-hot encoder but this will give error while using one-hot encoder.What to do? Suggest any solution asap. The above-discussed problem, I am facing in the following code. model=Sequential() |
@hpts23 Hey, did you solve your problem? I have exactly the same issue when using vgg16 for binary classification. |
Hi, if you are doing multi-label classification, you need to use the
multi-label binarizer instead of one hot.
ᐧ
…On Fri, Apr 27, 2018 at 1:57 AM, SpecKROELLchen ***@***.***> wrote:
@hpts23 <https://github.com/hpts23> Hey, did you solve your problem? I
have exactly the same issue when using vgg16 for binary classification.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#741 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AX71c5F1t9jWIKqzH8OQDKn_bXS1KEV6ks5tst2UgaJpZM4GE5pO>
.
--
Best Regards,
Stephen Lizcano
+49 176 4762 5344
+1 (626) 695 4868
|
That is of course what i am already doing. I use binary_crossentropy with a sigmoid activation. |
Hi, I am trying to do a multi-label classification on an image dataset of size 2.2M. I have seen people often use flow_from_directory and flow to train the network in batches. I cannot go for flow from directory as it is a multi-label problem and for using flow I need to load all my data in an array. Can someone please suggest a better way of doing it? Thanks! |
@agupt013 Why you cannot use flow or flow_from_directory for multi-label ? Can you give a valid reason for it ? |
Hi @mohapatras For each image I have 5 labels out of 20 classes. My understanding of flow_from_directory is that images are placed in a subdirectory of the respective class. I want to compute loss such that I pass all 5 labels and respective predictions to the loss function. |
I have similar but slightly different problems. I have multi-labels in one sample. Of each label, the class is mutually exclusive. So the target is more like concat( [0, 0, 1], [0, 1], [0, 0, 0, 1, 0] ) for one row. In such a case, should I train 3 separate models using |
@luoshao23 you can train a model with multiple outputs: https://keras.io/getting-started/functional-api-guide/#multi-input-and-multi-output-models. If 3 models or 1 model is a better choice probably depends on the task. |
@tobigue Thank you for your answer! So does it mean it is not a good choice to model this problem with only one output that is concatenated together in one vector? |
I need to classify attributes in a face like colour of eye, hair, skin; facial hair, lighting and so on. Each has few sub-categories in it. So should I directly apply sigmoid on all the labels or separately apply softmax on each subcategory like hair/eye colour etc? |
I wrote an explanatory blog post about multi-label classification and there is also an example with keras. https://www.depends-on-the-definition.com/guide-to-multi-label-classification-with-neural-networks/ |
@tsterbak great tutorial! Everybody in this thread should read it |
Closing as this is resolved |
I think this is still an unsolved query. And a lot of ppl are struggling to implement the multi-labeled models. Even after using sigmoid activation and binary cross-entropy the predicted probability distribution is almost near to zero for all the classes per samples. I think we really need to dig into the K.binarycross-entropy loss which in return calls nn.sigmoid_cross_entropy_with_logits(labels=target, logits=output). |
@srijandas07 have you tried to give more weight to positive targets in the loss function? If you use the tensorflow backend of keras you can use |
great! I will try to use this!! |
@tobigue Even in tf.nn.weighted_cross_entropy_with_logits sigmoid has been used. |
@srijandas07 There are probably other activations and loss functions to explore, but using the sigmoid is to my best knowledge the standard for multi-label classification. The weighted version allows you to give a higher penalty when the classifier predicted a 0 while the target was a 1, which should improve the problem of getting predictions that are all close to zero. |
@tobigue I have removed the activation and used nn.binarycrossentropy with logits. And this seems to work. After going through the function, I could infer that they implicitly use sigmoid to compute the loss. |
Can we apply different weights for different "labels" using this approach of binary cross-entropy? How is W structured here? @elanmart |
* Init commit for distributed training with JAX. * WIP * Add unit test for data parallel distribution. * Update the TODO message in the test. * Reduce he scope of the XLA flag in unit test. * Update unit test for setup/teardown * Add JAX xla backend reset for unit test * Multiple updates to the distribution. 1. Rename the distribute.py to distribution.py (same for tests). 2. Merge the global state logic to the distribution class. 3. Update all the unit tests. * Updates. * Updates * Formatting * Update test for debug setup/teardown * Further debug for unit test * lift the config logic out for testing * More debug for the unit test cleanup * Fix the unit test with warning. * Address review comments. * Address review comments.
* Init commit for distributed training with JAX. * WIP * Add unit test for data parallel distribution. * Update the TODO message in the test. * Reduce he scope of the XLA flag in unit test. * Update unit test for setup/teardown * Add JAX xla backend reset for unit test * Multiple updates to the distribution. 1. Rename the distribute.py to distribution.py (same for tests). 2. Merge the global state logic to the distribution class. 3. Update all the unit tests. * Updates. * Updates * Formatting * Update test for debug setup/teardown * Further debug for unit test * lift the config logic out for testing * More debug for the unit test cleanup * Fix the unit test with warning. * Address review comments. * Address review comments.
I need train a multi-label softmax classifier, but there is a lot of one-hot code labels in examples, so how to change code to do it?
The text was updated successfully, but these errors were encountered: