<jupyter_start><jupyter_text>DO read all the comments in the python code and contents in the markdown cells  You can do this assignment in google colab itself. we have provided a notebook to use tensorboard in google colab itself.<jupyter_code>import tensorflow as tf
#enabled to get instant output. if you don't need, you can use session concept which was dicussed in lecture videos. 
#tf.enable_eager_execution()
import tensorflow as tf
tf.__version__
tf.executing_eagerly()
a = tf.random.uniform(shape=(1000,8,1,64))
b = tf.random.uniform(shape=(1000,8,1,64))
add =tf.concat([a,b],2)
print(add.shape)<jupyter_output>(1000, 8, 2, 64)<jupyter_text>1.1 Addition It is similar to numpy addition.<jupyter_code>a = tf.random.uniform(shape=(2,2))
a
b = tf.random.uniform(shape=(2,2))
b
add = a + b
add<jupyter_output><empty_output><jupyter_text>Broadcasting works similar to numpy.<jupyter_code>c = 2.0
add_2 = a + c
add_2<jupyter_output><empty_output><jupyter_text>1.2 Subtraction It is similar to numpy Subtraction.<jupyter_code>a = tf.random.uniform(shape=(2,2))
a
b = tf.random.uniform(shape=(2,2))
b
sub = a - b
sub<jupyter_output><empty_output><jupyter_text>1.3 Multiplication- For two dimensional matrices, you can multiply (m*n) and (n*p) and get (m*p)- for 3 dim matrices, you can multiply (b * n * p)  and (b * p * m)  and get (b * n * m). ( for any dim, you have to maintain last 2 dim as two dim multiplication and first n-2 dim has to be same. you will get better idea if you gothrough    all the operation which we have written below.)<jupyter_code>a = tf.random.uniform(shape=(3,2))
a
b = tf.random.uniform(shape=(2,4))
b
tf.matmul(a, b)# (3*2)@(2*4) = (3*4)<jupyter_output><empty_output><jupyter_text><jupyter_code>a = tf.random.uniform(shape=(3,2,4))
a
b = tf.random.uniform(shape=(3,4,3))
b
tf.matmul(a, b) #(3*2*4)@(3*4*3) = (3*2*3)<jupyter_output><empty_output><jupyter_text><jupyter_code>a = tf.random.uniform(shape=(3,2,2,4))
a
b = tf.random.uniform(shape=(3,2,4,3))
b
tf.matmul(a,b) # (3*2*2*4)@(3*2*4*3)=(3*2*2*3)<jupyter_output><empty_output><jupyter_text>1. 4 Transpose    - matrix transpose.<jupyter_code>a = tf.random.uniform(shape=(3,2))
a
a.shape
tf.transpose(a)<jupyter_output><empty_output><jupyter_text>We have a "perm" argument for transpose function, in that you can define your own way to change rows and column positions. as shown below<jupyter_code>a = tf.random.uniform(shape=(3,2,5,4))
a.shape
a_transpose = tf.transpose(a, perm=[0, 2, 1, 3])
a_transpose.shape<jupyter_output><empty_output><jupyter_text>My transposed matrix dim changed based on pem values( we transposed 1 and 2 axis only). you can check below example in that we chnaged all the axis<jupyter_code>a = tf.random.uniform(shape=(3,2,5,4))
a.shape
a_transpose = tf.transpose(a, perm=[3, 2, 1, 0])
a_transpose.shape<jupyter_output><empty_output><jupyter_text>1. 4 Element wise operation  1.4.1 Multiply Respective elements in two matricesEx: A = [[1 2],     [3,4]]B = [[5,6],     [7,8]]we want A*B as [A[0][0]*B[0][0], A[0][1]*B[0][1]A[1][0]*B[1][0], A[1][1]*B[1][1]]<jupyter_code>a = tf.random.uniform(shape=(3,2))
a
b = tf.random.uniform(shape=(3,2))
b
a * b
tf.multiply(a, b)<jupyter_output><empty_output><jupyter_text>You can do a*b or you can use tf.multiply  1.5 Expanding Dimensions<jupyter_code>a = tf.random.uniform(shape=(3,2))
a.shape
a_add = tf.expand_dims(a, axis=1)#we are adding an additional axis at 1st dim
a_add.shape<jupyter_output><empty_output><jupyter_text><jupyter_code>a = tf.random.uniform(shape=(3,2))
a.shape
a_add = tf.expand_dims(a, axis=2)#we are adding an additional axis at 2nd dim
a_add.shape<jupyter_output><empty_output><jupyter_text>1.6 Squeezing dimensions Note that we can squeeze along with the axis with shape 1Ex: A.shape= [3,4,1] ==> we can squeez it on axis=2 will give [3,4]A.shape= [3,1,4] ==> we can squeez it on axis=1 will give [3,4]A.shape= [1,3,4] ==> we can squeez it on axis=0 will give [3,4]A.shape= [2,3,4] ==> we can't squeez it on any of the axis<jupyter_code>a = tf.random.uniform(shape=(3,1,4))
a.shape
a_squ = tf.squeeze(a, axis=1)
a_squ<jupyter_output><empty_output><jupyter_text>1.7 Reshaping of tensors check the documentation<jupyter_code>a = tf.random.uniform(shape=(5,3,4))

# tf.reshape(tensor, [reshape dimensions]])
b = tf.reshape(a, [a.shape[0]*a.shape[1],a.shape[2]])

print(a.shape, b.shape)<jupyter_output>(5, 3, 4) (15, 4)<jupyter_text>2. Call Backs<jupyter_code>import numpy as np
import pandas as pd
(X_train, y_train), (X_test, y_test) = tf.keras.datasets.mnist.load_data()
# if you observe the input shape its 3 dimensional vector
# for each image we have a (28*28) vector
# we will convert the (28*28) vector into single dimensional vector of 1 * 784 
X_train = X_train.reshape(X_train.shape[0], X_train.shape[1]*X_train.shape[2]) 
X_test = X_test.reshape(X_test.shape[0], X_test.shape[1]*X_test.shape[2]) 
Y_train = tf.keras.utils.to_categorical(y_train, 10) 
Y_test = tf.keras.utils.to_categorical(y_test, 10)
print(X_train.shape)
print(X_test.shape)
print(Y_train.shape)
print(Y_test.shape)
from tensorflow.keras.layers import Dense,Input,Activation
from tensorflow.keras.models import Model
import random as rn<jupyter_output><empty_output><jupyter_text>2.1 Writing custom call backs In Keras, Callback is a python class meant to be subclassed to provide specific functionality,  with a set of methods called at various stages of training (including batch/epoch start and ends),  testing, and predicting. Callbacks are useful to get a view on internal states and statistics of the model during training Writing Call Backs - You can inherit from `tf.keras.callbacks.Callback`, Copied the code for callback class from tensorflow documentation and pased in below cell, you can check that code. It has many methods like batch/epoch_begin/end. so you can do manipulate the model parameters or print the required outputs using these methods. The `logs` dict contains the loss value, and all the metrics at the end of a batch or epoch>  Do Read this Blog to understand how to inhert other classes<jupyter_code>class Callback(object):
    
    """Abstract base class used to build new callbacks.
      Attributes:
          params: dict. Training parameters
              (eg. verbosity, batch size, number of epochs...).
          model: instance of `keras.models.Model`.
              Reference of the model being trained.
          validation_data: Deprecated. Do not use.
      The `logs` dictionary that callback methods
      take as argument will contain keys for quantities relevant to
      the current batch or epoch.
      Currently, the `.fit()` method of the `Model` class
      will include the following quantities in the `logs` that
      it passes to its callbacks:
          on_epoch_end: logs include `acc` and `loss`, and
          optionally include `val_loss`
          (if validation is enabled in `fit`), and `val_acc`
          (if validation and accuracy monitoring are enabled).
          on_batch_begin: logs include `size`,
          the number of samples in the current batch.
          on_batch_end: logs include `loss`, and optionally `acc`
            (if accuracy monitoring is enabled).
      """

    def __init__(self):
        self.validation_data = None
        self.model = None
        # Whether this Callback should only run on the chief worker in a
        # Multi-Worker setting.
        # TODO(omalleyt): Make this attr public once solution is stable.
        self._chief_worker_only = None

    def set_params(self, params):
        self.params = params

    def set_model(self, model):
        self.model = model

    def on_batch_begin(self, batch, logs=None):
        """A backwards compatibility alias for `on_train_batch_begin`."""

    def on_batch_end(self, batch, logs=None):
        """A backwards compatibility alias for `on_train_batch_end`."""

    def on_epoch_begin(self, epoch, logs=None):
        """Called at the start of an epoch.
        Subclasses should override for any actions to run. This function should only
        be called during TRAIN mode.
        Arguments:
            epoch: integer, index of epoch.
            logs: dict. Currently no data is passed to this argument for this method
              but that may change in the future.
        """

    def on_epoch_end(self, epoch, logs=None):
        """Called at the end of an epoch.
        Subclasses should override for any actions to run. This function should only
        be called during TRAIN mode.
        Arguments:
            epoch: integer, index of epoch.
            logs: dict, metric results for this training epoch, and for the
              validation epoch if validation is performed. Validation result keys
              are prefixed with `val_`.
        """

    def on_train_batch_begin(self, batch, logs=None):
        """Called at the beginning of a training batch in `fit` methods.
        Subclasses should override for any actions to run.
        Arguments:
            batch: integer, index of batch within the current epoch.
            logs: dict. Has keys `batch` and `size` representing the current batch
              number and the size of the batch.
        """
        # For backwards compatibility.
        self.on_batch_begin(batch, logs=logs)

    def on_train_batch_end(self, batch, logs=None):
        """Called at the end of a training batch in `fit` methods.
        Subclasses should override for any actions to run.
        Arguments:
            batch: integer, index of batch within the current epoch.
            logs: dict. Metric results for this batch.
        """
        # For backwards compatibility.
        self.on_batch_end(batch, logs=logs)

    def on_test_batch_begin(self, batch, logs=None):
        """Called at the beginning of a batch in `evaluate` methods.
        Also called at the beginning of a validation batch in the `fit`
        methods, if validation data is provided.
        Subclasses should override for any actions to run.
        Arguments:
            batch: integer, index of batch within the current epoch.
            logs: dict. Has keys `batch` and `size` representing the current batch
                  number and the size of the batch.
        """

    def on_test_batch_end(self, batch, logs=None):
        """Called at the end of a batch in `evaluate` methods.
        Also called at the end of a validation batch in the `fit`
        methods, if validation data is provided.
        Subclasses should override for any actions to run.
        Arguments:
            batch: integer, index of batch within the current epoch.
            logs: dict. Metric results for this batch.
        """

    def on_predict_batch_begin(self, batch, logs=None):
        """Called at the beginning of a batch in `predict` methods.
        Subclasses should override for any actions to run.
        Arguments:
            batch: integer, index of batch within the current epoch.
            logs: dict. Has keys `batch` and `size` representing the current batch
                  number and the size of the batch.
        """

    def on_predict_batch_end(self, batch, logs=None):
        """Called at the end of a batch in `predict` methods.
        Subclasses should override for any actions to run.
        Arguments:
            batch: integer, index of batch within the current epoch.
            logs: dict. Metric results for this batch.
        """

    def on_train_begin(self, logs=None):
        """Called at the beginning of training.
        Subclasses should override for any actions to run.
        Arguments:
            logs: dict. Currently no data is passed to this argument for this method
                  but that may change in the future.
        """

    def on_train_end(self, logs=None):
        """Called at the end of training.
        Subclasses should override for any actions to run.
        Arguments:
            logs: dict. Currently no data is passed to this argument for this method
                  but that may change in the future.
        """

    def on_test_begin(self, logs=None):
        """Called at the beginning of evaluation or validation.
        Subclasses should override for any actions to run.
        Arguments:
            logs: dict. Currently no data is passed to this argument for this method
              but that may change in the future.
        """

    def on_test_end(self, logs=None):
        """Called at the end of evaluation or validation.
        Subclasses should override for any actions to run.
        Arguments:
            logs: dict. Currently no data is passed to this argument for this method
              but that may change in the future.
        """

    def on_predict_begin(self, logs=None):
        """Called at the beginning of prediction.
        Subclasses should override for any actions to run.
        Arguments:
            logs: dict. Currently no data is passed to this argument for this method
              but that may change in the future.
        """

    def on_predict_end(self, logs=None):
        """Called at the end of prediction.
        Subclasses should override for any actions to run.
        Arguments:
            logs: dict. Currently no data is passed to this argument for this method
              but that may change in the future.
    """
class LossHistory(tf.keras.callbacks.Callback):
    
    def on_train_begin(self, logs={}):
        ## on begin of training, we are creating a instance varible called history
        ## it is a dict with keys [loss, acc, val_loss, val_acc]
        self.history={'loss': [],'acc': [],'val_loss': [],'val_acc': []}
        
    def on_epoch_end(self, epoch, logs={}):
        ## on end of each epoch, we will get logs and update the self.history dict
#         print(logs.keys()) # added by IQBAL
        self.history['loss'].append(logs.get('loss'))
        self.history['acc'].append(logs.get('accuracy'))
        if logs.get('val_loss', -1) != -1:
            self.history['val_loss'].append(logs.get('val_loss'))
        if logs.get('val_accuracy', -1) != -1:
            self.history['val_acc'].append(logs.get('val_accuracy'))
            
history_own=LossHistory()<jupyter_output><empty_output><jupyter_text>> in the above function we have written logs={}, which means the logs is a dictionary and the keys present will the same values that gets printed while you train your model i.e model.fit()<jupyter_code>#Input layer
input_layer = Input(shape=(784,))
#Dense hidden layer
layer1 = Dense(50,activation='sigmoid',kernel_initializer=tf.keras.initializers.glorot_normal(seed=30))(input_layer)
#output layer
output = Dense(10,activation='softmax',kernel_initializer=tf.keras.initializers.glorot_normal(seed=0))(layer1)
#Creating a model
model = Model(inputs=input_layer,outputs=output)


#Callbacks
history_own = LossHistory()

optimizer = tf.keras.optimizers.Adam(0.01)

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

model.fit(X_train,Y_train,epochs=3, validation_data=(X_test,Y_test), batch_size=16, callbacks=[history_own])

history_own.history<jupyter_output><empty_output><jupyter_text>Writing the call back to terminate training if loss is 'NaN'<jupyter_code>class TerminateNaN(tf.keras.callbacks.Callback):
        
    def on_epoch_end(self, epoch, logs={}):
        print(logs.keys()) # IQBAL ===> to see keys
        loss = logs.get('loss')
        if loss is not None:
            if np.isnan(loss) or np.isinf(loss):
                print("Invalid loss and terminated at epoch {}".format(epoch))
                self.model.stop_training = True<jupyter_output><empty_output><jupyter_text>2.2 Using tensorflow call backs There are some callbacks which are implemented in the TensorflowModelCheckpoint - Save the model after every epoch. You can check documentation here<jupyter_code>from tensorflow.keras.callbacks import ModelCheckpoint
#Input layer
input_layer = Input(shape=(784,))
#Dense hidden layer
layer1 = Dense(50,activation='sigmoid',kernel_initializer=tf.keras.initializers.glorot_normal(seed=30))(input_layer)
#output layer
output = Dense(10,activation='softmax',kernel_initializer=tf.keras.initializers.glorot_normal(seed=0))(layer1)
#Creating a model
model = Model(inputs=input_layer,outputs=output)


#Callbacks
#file path, it saves the model in the 'model_save' folder and we are naming model with epoch number 
#and val acc to differtiate with other models
#you have to create model_save folder before running the code.

# changed filpath for windows (my own system)
filepath="D:\Applied AI Course\Assignments\20 Callbacks\model_save\weights-{epoch:02d}-{val_accuracy:.4f}.hdf5" # val_acc ==> val_accuracy
checkpoint = ModelCheckpoint(filepath=filepath, monitor='val_loss',  verbose=1, save_best_only=True, mode='auto')

optimizer = tf.keras.optimizers.Adam(0.01)

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

model.fit(X_train,Y_train,epochs=5,validation_data=(X_test,Y_test),batch_size=16,callbacks=[checkpoint])<jupyter_output>Epoch 1/5
3748/3750 [============================>.] - ETA: 0s - loss: 1.2268 - accuracy: 0.5655<jupyter_text>If you need 4th epoch model, you can load that model as below. It saves optimizer state as well. so noo need to recompile.<jupyter_code>model.load_weights('D:\\Applied AI Course\\Assignments\\20 Callbacks\\model_save\\weights-01-0.5731.hdf5')#change this with your name which you got in above output
model.fit(X_train,Y_train)<jupyter_output>1875/1875 [==============================] - 5s 3ms/step - loss: 1.1929 - accuracy: 0.5816<jupyter_text>2.2.1 EarlyStopping:Stop training when a monitored quantity has stopped improving. You can check the documentatin here<jupyter_code>from tensorflow.keras.callbacks import EarlyStopping
#Input layer
input_layer = Input(shape=(784,))
#Dense hidden layer
layer1 = Dense(50,activation='sigmoid',kernel_initializer=tf.keras.initializers.glorot_normal(seed=30))(input_layer)
#output layer
output = Dense(10,activation='softmax',kernel_initializer=tf.keras.initializers.glorot_normal(seed=0))(layer1)
#Creating a model
model = Model(inputs=input_layer,outputs=output)

#you can monitor any quantity (here i am monitoring val_loss), you can give any number for patience based on your need. 
#i am terminating training if my validation loss incresing at once than previous loss. so maintained 1. you can give min delta
#an absolute change of less than min_delta, will count as no improvement. i maintained 0.35 because i don't want to run 
#so many epoch to see termination
earlystop = EarlyStopping(monitor='val_loss', min_delta=0.10, patience=1, verbose=1)

optimizer = tf.keras.optimizers.Adam(0.01)

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

model.fit(X_train,Y_train,epochs=10,validation_data=(X_test,Y_test),batch_size=16,callbacks=[earlystop])<jupyter_output>Epoch 1/10
3750/3750 [==============================] - 6s 2ms/step - loss: 1.5631 - accuracy: 0.4165 - val_loss: 1.4787 - val_accuracy: 0.4228
Epoch 2/10
3750/3750 [==============================] - 7s 2ms/step - loss: 1.3190 - accuracy: 0.5257 - val_loss: 1.1710 - val_accuracy: 0.6098
Epoch 3/10
3750/3750 [==============================] - 7s 2ms/step - loss: 1.1346 - accuracy: 0.5975 - val_loss: 1.0387 - val_accuracy: 0.6364
Epoch 4/10
3750/3750 [==============================] - 7s 2ms/step - loss: 0.9858 - accuracy: 0.6563 - val_loss: 1.0948 - val_accuracy: 0.5906
Epoch 00004: early stopping<jupyter_text>It stopped at 2nd epoch only. You can use this early stopping to get best model than overfitted model  2.2.2 LearningRateScheduler:You can schedule learning rate for every epoch. You can decrease or increase the learning rate based on epoch number.<jupyter_code>from tensorflow.keras.callbacks import LearningRateScheduler
def changeLearningRate(epoch):
    initial_learningrate=0.1
    changed = initial_learningrate*(1-0.1)**epoch
    return changed
changed_lr = []
for i in range(1,50):
    changed_lr.append(changeLearningRate(i))
%matplotlib inline
import matplotlib.pyplot as plt
plt.plot(changed_lr)
plt.ylabel('learning_rate')
plt.xlabel('epoch number')<jupyter_output><empty_output><jupyter_text>If you check above figure, learning rate is decreasing if my epoch number is increasing. I am using same function to schedule our learning rate based on epoch number. You can write your own function or you can write custom call back to decrease the learning rate based on val_loss/acc. In the above function, i have implemented exponential decay with decay rate 0.1, you can check that here<jupyter_code>#Input layer
input_layer = Input(shape=(784,))
#Dense hidden layer
layer1 = Dense(50,activation='sigmoid',kernel_initializer=tf.keras.initializers.glorot_normal(seed=30))(input_layer)
#output layer
output = Dense(10,activation='softmax',kernel_initializer=tf.keras.initializers.glorot_normal(seed=0))(layer1)
#Creating a model
model = Model(inputs=input_layer,outputs=output)

lrschedule = LearningRateScheduler(changeLearningRate, verbose=1)

model.compile(optimizer=tf.keras.optimizers.Adam(0.1),
              loss='categorical_crossentropy',metrics=['accuracy'])

model.fit(X_train,Y_train,epochs=3,validation_data=(X_test,Y_test),batch_size=16,callbacks=[lrschedule])<jupyter_output>Epoch 00001: LearningRateScheduler reducing learning rate to 0.1.
Epoch 1/3
3750/3750 [==============================] - 9s 2ms/step - loss: 2.4455 - accuracy: 0.1908 - val_loss: 2.7014 - val_accuracy: 0.1939 - lr: 0.1000

Epoch 00002: LearningRateScheduler reducing learning rate to 0.09000000000000001.
Epoch 2/3
3750/3750 [==============================] - 8s 2ms/step - loss: 2.6087 - accuracy: 0.1230 - val_loss: 2.5943 - val_accuracy: 0.1157 - lr: 0.0900

Epoch 00003: LearningRateScheduler reducing learning rate to 0.08100000000000002.
Epoch 3/3
3750/3750 [==============================] - 8s 2ms/step - loss: 2.3991 - accuracy: 0.1608 - val_loss: 2.2715 - val_accuracy: 0.2119 - lr: 0.0810<jupyter_text>2.2.3 ReduceLROnPlateauIt is similar to EarlyStopping, you can reduce your Learning rate by some factor if my val_loss/acc is not improving. You can check the documentation here There are many pre built call backs are available in Tensorflow, you can check those in this link.You can give as many callbacks you want while training the model as given below.<jupyter_code>#Input layer
input_layer = Input(shape=(784,))
#Dense hidden layer
layer1 = Dense(50,activation='sigmoid',kernel_initializer=tf.keras.initializers.glorot_normal(seed=30))(input_layer)
#output layer
output = Dense(10,activation='softmax',kernel_initializer=tf.keras.initializers.glorot_normal(seed=0))(layer1)
#Creating a model
model = Model(inputs=input_layer,outputs=output)

#create a call back list
lrschedule = LearningRateScheduler(changeLearningRate, verbose=0.1)
earlystop = EarlyStopping(monitor='val_loss', min_delta=0.25, patience=1, verbose=1)
filepath="model_save/weights-{epoch:02d}-{val_accuracy:.4f}.hdf5"
checkpoint = ModelCheckpoint(filepath=filepath, monitor='val_loss', verbose=1, 
                             save_best_only=True, mode='auto')


# here we are creating a list with all the callbacks we want
callback_list = [lrschedule, earlystop, checkpoint]


model.compile(optimizer=tf.keras.optimizers.Adam(0.1),
              loss='categorical_crossentropy',metrics=['accuracy'])

model.fit(X_train,Y_train,epochs=5,validation_data=(X_test,Y_test),batch_size=16,callbacks=callback_list)<jupyter_output>Epoch 00001: LearningRateScheduler reducing learning rate to 0.1.
Epoch 1/5
3734/3750 [============================>.] - ETA: 0s - loss: 2.2930 - accuracy: 0.2208
Epoch 00001: val_loss improved from inf to 2.22514, saving model to model_save/weights-01-0.2012.hdf5
3750/3750 [==============================] - 12s 3ms/step - loss: 2.2930 - accuracy: 0.2206 - val_loss: 2.2251 - val_accuracy: 0.2012 - lr: 0.1000

Epoch 00002: LearningRateScheduler reducing learning rate to 0.09000000000000001.
Epoch 2/5
3730/3750 [============================>.] - ETA: 0s - loss: 2.2689 - accuracy: 0.2256
Epoch 00002: val_loss improved from 2.22514 to 2.18152, saving model to model_save/weights-02-0.2585.hdf5
3750/3750 [==============================] - 11s 3ms/step - loss: 2.2689 - accuracy: 0.2258 - val_loss: 2.1815 - val_accuracy: 0.2585 - lr: 0.0900
Epoch 00002: early stopping
{'fraction_non_alphanumeric': 0.11719516181758745, 'fraction_numerical': 0.03734880679960771, 'mean_word_length': 3.951041877402387, 'pattern_counts': {'":': 0, '<': 94, '<?xml version=': 0, '>': 105, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 3, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '25', 'dir': 'jupyter-structured-clean-dedup', 'id': '393188', 'license': 'Apache-2.0', 'n_tokens_mistral': 8195, 'n_tokens_neox': 7630, 'n_words': 2417, 'path': 'Callbacks/Call_Backs_Reference_mdiqbalbajmi00786@gmail.com.ipynb', 'repo_name': 'iqbal786786/Machine-Learning-Assignments', 'size': '24472'}