{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":10684,"databundleVersionId":230682,"sourceType":"competition"}],"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"### Meeting a Sayed Athar's request, I'm using the Kernel altered by Khoi Nguyen to explain how the whole code works.\n### If any part is not clear, please comment.  \n### Please upvote if it was helpful.","metadata":{"_uuid":"e154a47bf09b8770980486e87786317a1b3038e1"}},{"cell_type":"code","source":"import pandas as pd\nimport pyarrow.parquet as pq # Used to read the data\nimport os \nimport numpy as np\nfrom keras.layers import * # Keras is the most friendly Neural Network library, this Kernel use a lot of layers classes\nfrom keras.models import Model\nfrom tqdm import tqdm # Processing time measurement\nfrom sklearn.model_selection import train_test_split \nfrom keras import backend as K # The backend give us access to tensorflow operations and allow us to create the Attention class\nfrom keras import optimizers # Allow us to access the Adam class to modify some parameters\nfrom sklearn.model_selection import GridSearchCV, StratifiedKFold # Used to use Kfold to train our model\nfrom keras.callbacks import * # This object helps the model to train in a smarter way, avoiding overfitting","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-01-07T15:58:44.067972Z","iopub.execute_input":"2025-01-07T15:58:44.068354Z","iopub.status.idle":"2025-01-07T15:58:44.073442Z","shell.execute_reply.started":"2025-01-07T15:58:44.068324Z","shell.execute_reply":"2025-01-07T15:58:44.072612Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# select how many folds will be created\nN_SPLITS = 5\n# it is just a constant with the measurements data size\nsample_size = 800000","metadata":{"trusted":true,"_uuid":"6e6379386e44afc69bee8895a52da22199e888fb","execution":{"iopub.status.busy":"2025-01-07T15:58:47.035625Z","iopub.execute_input":"2025-01-07T15:58:47.035900Z","iopub.status.idle":"2025-01-07T15:58:47.039991Z","shell.execute_reply.started":"2025-01-07T15:58:47.035879Z","shell.execute_reply":"2025-01-07T15:58:47.038992Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import tensorflow.keras.backend as K\nimport tensorflow as tf\n\ndef matthews_correlation(y_true, y_pred):\n    y_pred_pos = K.round(K.clip(y_pred, 0, 1))  # Ensure predictions are rounded to 0 or 1\n    y_pred_neg = 1 - y_pred_pos\n    y_true_pos = K.cast(y_true, dtype=\"float32\")  # Cast to float32 for consistent data type\n    y_true_neg = 1 - y_true_pos\n\n    tp = K.sum(y_true_pos * y_pred_pos)\n    tn = K.sum(y_true_neg * y_pred_neg)\n    fp = K.sum(y_true_neg * y_pred_pos)\n    fn = K.sum(y_true_pos * y_pred_neg)\n\n    numerator = (tp * tn) - (fp * fn)\n    denominator = K.sqrt((tp + fp) * (tp + fn) * (tn + fp) * (tn + fn) + K.epsilon())\n    return numerator / (denominator + K.epsilon())\n","metadata":{"trusted":true,"_uuid":"c3340ee96becb5ca8f075d9c44b7df383ddba5ee","execution":{"iopub.status.busy":"2025-01-07T15:58:50.070338Z","iopub.execute_input":"2025-01-07T15:58:50.070662Z","iopub.status.idle":"2025-01-07T15:58:50.076201Z","shell.execute_reply.started":"2025-01-07T15:58:50.070634Z","shell.execute_reply":"2025-01-07T15:58:50.075187Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras.layers import Layer\nimport tensorflow.keras.backend as K\nfrom tensorflow.keras import initializers, regularizers, constraints\n\nclass Attention(Layer):\n    def __init__(self, step_dim,\n                 W_regularizer=None, b_regularizer=None,\n                 W_constraint=None, b_constraint=None,\n                 bias=True, **kwargs):\n        super(Attention, self).__init__(**kwargs)\n        self.supports_masking = True\n        self.init = initializers.get('glorot_uniform')\n\n        self.W_regularizer = regularizers.get(W_regularizer)\n        self.b_regularizer = regularizers.get(b_regularizer)\n\n        self.W_constraint = constraints.get(W_constraint)\n        self.b_constraint = constraints.get(b_constraint)\n\n        self.bias = bias\n        self.step_dim = step_dim\n        self.features_dim = 0\n\n    def build(self, input_shape):\n        assert len(input_shape) == 3\n\n        # Define weights\n        self.W = self.add_weight(\n            shape=(input_shape[-1],),\n            initializer=self.init,\n            name='{}_W'.format(self.name),\n            regularizer=self.W_regularizer,\n            constraint=self.W_constraint\n        )\n        self.features_dim = input_shape[-1]\n\n        if self.bias:\n            self.b = self.add_weight(\n                shape=(self.step_dim,),\n                initializer='zeros',\n                name='{}_b'.format(self.name),\n                regularizer=self.b_regularizer,\n                constraint=self.b_constraint\n            )\n        else:\n            self.b = None\n\n        super(Attention, self).build(input_shape)\n\n    def compute_mask(self, input, input_mask=None):\n        return None\n\n    def call(self, x, mask=None):\n        features_dim = self.features_dim\n        step_dim = self.step_dim\n\n        # Compute attention scores\n        eij = K.reshape(\n            K.dot(K.reshape(x, (-1, features_dim)),\n                  K.reshape(self.W, (features_dim, 1))),\n            (-1, step_dim)\n        )\n\n        if self.bias:\n            eij += self.b\n\n        eij = K.tanh(eij)\n\n        # Softmax over scores\n        a = K.exp(eij)\n\n        if mask is not None:\n            a *= K.cast(mask, K.floatx())\n\n        a /= K.cast(K.sum(a, axis=1, keepdims=True) + K.epsilon(), K.floatx())\n\n        a = K.expand_dims(a)\n\n        # Apply attention weights\n        weighted_input = x * a\n        return K.sum(weighted_input, axis=1)\n\n    def compute_output_shape(self, input_shape):\n        return input_shape[0], self.features_dim\n","metadata":{"trusted":true,"_uuid":"eda7ea366117d1ce8e5fce69e5bba333821d8b48","execution":{"iopub.status.busy":"2025-01-07T15:58:51.446920Z","iopub.execute_input":"2025-01-07T15:58:51.447282Z","iopub.status.idle":"2025-01-07T15:58:51.457216Z","shell.execute_reply.started":"2025-01-07T15:58:51.447249Z","shell.execute_reply":"2025-01-07T15:58:51.456275Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# just load train data\ndf_train = pd.read_csv('../input/metadata_train.csv')\n# set index, it makes the data access much faster\ndf_train = df_train.set_index(['id_measurement', 'phase'])\ndf_train.head()","metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":true,"execution":{"iopub.status.busy":"2025-01-07T15:58:54.954612Z","iopub.execute_input":"2025-01-07T15:58:54.954894Z","iopub.status.idle":"2025-01-07T15:58:54.972618Z","shell.execute_reply.started":"2025-01-07T15:58:54.954872Z","shell.execute_reply":"2025-01-07T15:58:54.971719Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# in other notebook I have extracted the min and max values from the train data, the measurements\nmax_num = 127\nmin_num = -128","metadata":{"trusted":true,"_uuid":"26df6c7fbfecd537404866faec13d1238ae3ebc6","execution":{"iopub.status.busy":"2025-01-07T15:58:59.061150Z","iopub.execute_input":"2025-01-07T15:58:59.061490Z","iopub.status.idle":"2025-01-07T15:58:59.065353Z","shell.execute_reply.started":"2025-01-07T15:58:59.061466Z","shell.execute_reply":"2025-01-07T15:58:59.064269Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# This function standardize the data from (-128 to 127) to (-1 to 1)\n# Theoretically it helps in the NN Model training, but I didn't tested without it\ndef min_max_transf(ts, min_data, max_data, range_needed=(-1,1)):\n    if min_data < 0:\n        ts_std = (ts + abs(min_data)) / (max_data + abs(min_data))\n    else:\n        ts_std = (ts - min_data) / (max_data - min_data)\n    if range_needed[0] < 0:    \n        return ts_std * (range_needed[1] + abs(range_needed[0])) + range_needed[0]\n    else:\n        return ts_std * (range_needed[1] - range_needed[0]) + range_needed[0]","metadata":{"trusted":true,"_uuid":"7b0717b14bcfcba1f48d33c8161ae51c778687af","execution":{"iopub.status.busy":"2025-01-07T15:58:59.896311Z","iopub.execute_input":"2025-01-07T15:58:59.896607Z","iopub.status.idle":"2025-01-07T15:58:59.901820Z","shell.execute_reply.started":"2025-01-07T15:58:59.896586Z","shell.execute_reply":"2025-01-07T15:58:59.900786Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# This is one of the most important peace of code of this Kernel\n# Any power line contain 3 phases of 800000 measurements, or 2.4 millions data \n# It would be praticaly impossible to build a NN with an input of that size\n# The ideia here is to reduce it each phase to a matrix of <n_dim> bins by n features\n# Each bean is a set of 5000 measurements (800000 / 160), so the features are extracted from this 5000 chunk data.\ndef transform_ts(ts, n_dim=160, min_max=(-1,1)):\n    # convert data into -1 to 1\n    ts_std = min_max_transf(ts, min_data=min_num, max_data=max_num)\n    # bucket or chunk size, 5000 in this case (800000 / 160)\n    bucket_size = int(sample_size / n_dim)\n    # new_ts will be the container of the new data\n    new_ts = []\n    # this for iteract any chunk/bucket until reach the whole sample_size (800000)\n    for i in range(0, sample_size, bucket_size):\n        # cut each bucket to ts_range\n        ts_range = ts_std[i:i + bucket_size]\n        # calculate each feature\n        mean = ts_range.mean()\n        std = ts_range.std() # standard deviation\n        std_top = mean + std # I have to test it more, but is is like a band\n        std_bot = mean - std\n        # I think that the percentiles are very important, it is like a distribuiton analysis from eath chunk\n        percentil_calc = np.percentile(ts_range, [0, 1, 25, 50, 75, 99, 100]) \n        max_range = percentil_calc[-1] - percentil_calc[0] # this is the amplitude of the chunk\n        relative_percentile = percentil_calc - mean # maybe it could heap to understand the asymmetry\n        # now, we just add all the features to new_ts and convert it to np.array\n        new_ts.append(np.concatenate([np.asarray([mean, std, std_top, std_bot, max_range]),percentil_calc, relative_percentile]))\n    return np.asarray(new_ts)","metadata":{"trusted":true,"_uuid":"c6137bbbe75c3a1509a5f98e08805dbbd492aa37","execution":{"iopub.status.busy":"2025-01-07T15:59:00.611565Z","iopub.execute_input":"2025-01-07T15:59:00.611823Z","iopub.status.idle":"2025-01-07T15:59:00.617629Z","shell.execute_reply.started":"2025-01-07T15:59:00.611802Z","shell.execute_reply":"2025-01-07T15:59:00.616626Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# this function take a piece of data and convert using transform_ts(), but it does to each of the 3 phases\n# if we would try to do in one time, could exceed the RAM Memmory\ndef prep_data(start, end):\n    # load a piece of data from file\n    praq_train = pq.read_pandas('../input/train.parquet', columns=[str(i) for i in range(start, end)]).to_pandas()\n    X = []\n    y = []\n    # using tdqm to evaluate processing time\n    # takes each index from df_train and iteract it from start to end\n    # it is divided by 3 because for each id_measurement there are 3 id_signal, and the start/end parameters are id_signal\n    for id_measurement in tqdm(df_train.index.levels[0].unique()[int(start/3):int(end/3)]):\n        X_signal = []\n        # for each phase of the signal\n        for phase in [0,1,2]:\n            # extract from df_train both signal_id and target to compose the new data sets\n            signal_id, target = df_train.loc[id_measurement].loc[phase]\n            # but just append the target one time, to not triplicate it\n            if phase == 0:\n                y.append(target)\n            # extract and transform data into sets of features\n            X_signal.append(transform_ts(praq_train[str(signal_id)]))\n        # concatenate all the 3 phases in one matrix\n        X_signal = np.concatenate(X_signal, axis=1)\n        # add the data to X\n        X.append(X_signal)\n    X = np.asarray(X)\n    y = np.asarray(y)\n    return X, y","metadata":{"trusted":true,"_uuid":"7460e718a605803f1d9e4fbec61750a0deb02a47","execution":{"iopub.status.busy":"2025-01-07T15:59:01.224960Z","iopub.execute_input":"2025-01-07T15:59:01.225418Z","iopub.status.idle":"2025-01-07T15:59:01.233310Z","shell.execute_reply.started":"2025-01-07T15:59:01.225378Z","shell.execute_reply":"2025-01-07T15:59:01.232181Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# this code is very simple, divide the total size of the df_train into two sets and process it\nX = []\ny = []\ndef load_all():\n    total_size = len(df_train)\n    for ini, end in [(0, int(total_size/2)), (int(total_size/2), total_size)]:\n        X_temp, y_temp = prep_data(ini, end)\n        X.append(X_temp)\n        y.append(y_temp)\nload_all()\nX = np.concatenate(X)\ny = np.concatenate(y)","metadata":{"trusted":true,"_uuid":"52dc826ab9ee1dd56c9fb29bd5c1b2d26b5928bf","execution":{"iopub.status.busy":"2025-01-07T15:59:03.309630Z","iopub.execute_input":"2025-01-07T15:59:03.309932Z","iopub.status.idle":"2025-01-07T16:09:06.767911Z","shell.execute_reply.started":"2025-01-07T15:59:03.309908Z","shell.execute_reply":"2025-01-07T16:09:06.767129Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# The X shape here is very important. It is also important undertand a little how a LSTM works\n# X.shape[0] is the number of id_measuremts contained in train data\n# X.shape[1] is the number of chunks resultant of the transformation, each of this date enters in the LSTM serialized\n# This way the LSTM can understand the position of a data relative with other and activate a signal that needs\n# a serie of inputs in a specifc order.\n# X.shape[3] is the number of features multiplied by the number of phases (3)\nprint(X.shape, y.shape)\n# save data into file, a numpy specific format\nnp.save(\"X.npy\",X)\nnp.save(\"y.npy\",y)","metadata":{"trusted":true,"_uuid":"51ad0e25b00536de6170168499923d82ae1d735f","execution":{"iopub.status.busy":"2025-01-07T16:09:06.768937Z","iopub.execute_input":"2025-01-07T16:09:06.769151Z","iopub.status.idle":"2025-01-07T16:09:06.958111Z","shell.execute_reply.started":"2025-01-07T16:09:06.769132Z","shell.execute_reply":"2025-01-07T16:09:06.957286Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras import initializers, regularizers, constraints\nfrom tensorflow.keras.layers import LSTM, Bidirectional\n# This is NN LSTM Model creation\ndef model_lstm(input_shape):\n    # The shape was explained above, must have this order\n    inp = Input(shape=(input_shape[1], input_shape[2],))\n    # This is the LSTM layer\n    # Bidirecional implies that the 160 chunks are calculated in both ways, 0 to 159 and 159 to zero\n    # although it appear that just 0 to 159 way matter, I have tested with and without, and tha later worked best\n    # 128 and 64 are the number of cells used, too many can overfit and too few can underfit\n    x = Bidirectional(LSTM(128, return_sequences=True))(inp)\n    # The second LSTM can give more fire power to the model, but can overfit it too\n    x = Bidirectional(LSTM(64, return_sequences=True))(x)\n    # Attention is a new tecnology that can be applyed to a Recurrent NN to give more meanings to a signal found in the middle\n    # of the data, it helps more in longs chains of data. A normal RNN give all the responsibility of detect the signal\n    # to the last cell. Google RNN Attention for more information :)\n    x = Attention(input_shape[1])(x)\n    # A intermediate full connected (Dense) can help to deal with nonlinears outputs\n    x = Dense(64, activation=\"relu\")(x)\n    # A binnary classification as this must finish with shape (1,)\n    x = Dense(1, activation=\"sigmoid\")(x)\n    model = Model(inputs=inp, outputs=x)\n    # Pay attention in the addition of matthews_correlation metric in the compilation, it is a success factor key\n    model.compile(loss='binary_crossentropy', optimizer='adam', metrics=[matthews_correlation])\n    \n    return model","metadata":{"trusted":true,"_uuid":"289bc7d1ab8048a60025801b457f8df1d848acbc","execution":{"iopub.status.busy":"2025-01-07T16:09:06.959939Z","iopub.execute_input":"2025-01-07T16:09:06.960245Z","iopub.status.idle":"2025-01-07T16:09:06.965776Z","shell.execute_reply.started":"2025-01-07T16:09:06.960221Z","shell.execute_reply":"2025-01-07T16:09:06.964740Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Here is where the training happens\ndef training(**kwargs):\n    \n    # First, create a set of indexes of the 5 folds\n    splits = list(StratifiedKFold(n_splits=N_SPLITS, shuffle=True, random_state=2019).split(X, y))\n    preds_val = []\n    y_val = []\n    # Then, iteract with each fold\n    # If you dont know, enumerate(['a', 'b', 'c']) returns [(0, 'a'), (1, 'b'), (2, 'c')]\n    for idx, (train_idx, val_idx) in enumerate(splits):\n        K.clear_session() # I dont know what it do, but I imagine that it \"clear session\" :)\n        print(\"Beginning fold {}\".format(idx+1))\n        # use the indexes to extract the folds in the train and validation data\n        train_X, train_y, val_X, val_y = X[train_idx], y[train_idx], X[val_idx], y[val_idx]\n        # instantiate the model for this fold\n        model = model_lstm(train_X.shape)\n        # This checkpoint helps to avoid overfitting. It just save the weights of the model if it delivered an\n        # validation matthews_correlation greater than the last one.\n        #ckpt = ModelCheckpoint('weights_{}.h5'.format(idx), save_best_only=True, save_weights_only=True, verbose=1, monitor='val_matthews_correlation', mode='max')\n        # Train, train, train\n        model.fit(train_X, train_y, batch_size=128, epochs=50, validation_data=[val_X, val_y],**kwargs)\n        # loads the best weights saved by the checkpoint\n        #model.load_weights('weights_{}.h5'.format(idx))\n        # Add the predictions of the validation to the list preds_val\n        preds_val.append(model.predict(val_X, batch_size=512))\n        # and the val true y\n        y_val.append(val_y)\n    \n    # concatenates all and prints the shape    \n    preds_val = np.concatenate(preds_val)[...,0]\n    y_val = np.concatenate(y_val)\n    preds_val.shape, y_val.shape\n    return model","metadata":{"trusted":true,"_uuid":"8d6f4ca319c383b1b4f671a37c5a324136e7a466","execution":{"iopub.status.busy":"2025-01-07T16:09:06.966844Z","iopub.execute_input":"2025-01-07T16:09:06.967114Z","iopub.status.idle":"2025-01-07T16:09:06.983983Z","shell.execute_reply.started":"2025-01-07T16:09:06.967094Z","shell.execute_reply":"2025-01-07T16:09:06.983237Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#model = training()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-07T16:09:06.984781Z","iopub.execute_input":"2025-01-07T16:09:06.984970Z","iopub.status.idle":"2025-01-07T16:09:06.998586Z","shell.execute_reply.started":"2025-01-07T16:09:06.984953Z","shell.execute_reply":"2025-01-07T16:09:06.997845Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import psutil\nimport cProfile\nimport pstats\nimport time\nwith tf.device('/CPU:0'):\n    \n    # Record initial memory usage\n    process = psutil.Process()\n    initial_memory = process.memory_info().rss / 1024 ** 2  # Memory in MB\n    profiler = cProfile.Profile()\n    profiler.enable()\n    \n    start_time = time.time()\n    model = training()\n    gpu_time = time.time() - start_time\n    \n    # Final memory usage\n    final_memory = process.memory_info().rss / 1024 ** 2  # Memory in MB\n    memory_usage = final_memory - initial_memory\n    \n    # Calculate throughput (samples per second)\n    throughput = len(X) / gpu_time\n\n    profiler.disable()\n\n    # Print the profiling results\n    stats = pstats.Stats(profiler)\n    stats.sort_stats('time').print_stats(10)  # Top 10 time-consuming functions\n\n    print()\n    \n    print(f'CPU Training time: {gpu_time:.4f} seconds')\n    print(f'CPU Memory usage: {memory_usage:.4f} MB')\n    print(f'CPU Throughput: {throughput:.4f} samples/second')\n\n    print('='*50)\n#-------------------------------------------------------------------------------------------\n    print('Scheduler :')\n    from tensorflow.keras.callbacks import ReduceLROnPlateau\n\n    # Reduce learning rate when validation loss stops improving\n    lr_scheduler = ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=3, verbose=1)\n\n    # Record initial memory usage\n    process = psutil.Process()\n    initial_memory = process.memory_info().rss / 1024 ** 2  # Memory in MB\n    profiler = cProfile.Profile()\n    profiler.enable()\n    \n    start_time = time.time()\n    model = training(callbacks=[lr_scheduler])\n    gpu_time = time.time() - start_time\n    \n    # Final memory usage\n    final_memory = process.memory_info().rss / 1024 ** 2  # Memory in MB\n    memory_usage = final_memory - initial_memory\n    \n    # Calculate throughput (samples per second)\n    throughput = len(X) / gpu_time\n\n    profiler.disable()\n\n    # Print the profiling results\n    stats = pstats.Stats(profiler)\n    stats.sort_stats('time').print_stats(10)  # Top 10 time-consuming functions\n\n    print()\n    \n    print(f'CPU Training time: {gpu_time:.4f} seconds')\n    print(f'CPU Memory usage: {memory_usage:.4f} MB')\n    print(f'CPU Throughput: {throughput:.4f} samples/second')\n\n    print('='*50)\n    #-------------------------------------------------------------------------------------------\n    print('Early stopping :')\n    from tensorflow.keras.callbacks import EarlyStopping\n\n    # Stop training if validation loss doesn't improve for 5 epochs\n    early_stopping = EarlyStopping(monitor='val_loss', patience=5, restore_best_weights=True, verbose=1)\n\n\n    # Record initial memory usage\n    process = psutil.Process()\n    initial_memory = process.memory_info().rss / 1024 ** 2  # Memory in MB\n    profiler = cProfile.Profile()\n    profiler.enable()\n    \n    start_time = time.time()\n    model = training(callbacks=[early_stopping])\n    gpu_time = time.time() - start_time\n    \n    # Final memory usage\n    final_memory = process.memory_info().rss / 1024 ** 2  # Memory in MB\n    memory_usage = final_memory - initial_memory\n    \n    # Calculate throughput (samples per second)\n    throughput = len(X) / gpu_time\n\n    profiler.disable()\n\n    # Print the profiling results\n    stats = pstats.Stats(profiler)\n    stats.sort_stats('time').print_stats(10)  # Top 10 time-consuming functions\n\n    print()\n    \n    print(f'CPU Training time: {gpu_time:.4f} seconds')\n    print(f'CPU Memory usage: {memory_usage:.4f} MB')\n    print(f'CPU Throughput: {throughput:.4f} samples/second')\n\n    print('='*50) ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-07T16:09:06.999409Z","iopub.execute_input":"2025-01-07T16:09:06.999671Z","iopub.status.idle":"2025-01-07T16:27:32.611313Z","shell.execute_reply.started":"2025-01-07T16:09:06.999642Z","shell.execute_reply":"2025-01-07T16:27:32.610470Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import psutil\nimport cProfile\nimport pstats\nimport time\nwith tf.device('/GPU:0'):\n    \n    # Record initial memory usage\n    process = psutil.Process()\n    initial_memory = process.memory_info().rss / 1024 ** 2  # Memory in MB\n    profiler = cProfile.Profile()\n    profiler.enable()\n    \n    start_time = time.time()\n    model = training()\n    gpu_time = time.time() - start_time\n    \n    # Final memory usage\n    final_memory = process.memory_info().rss / 1024 ** 2  # Memory in MB\n    memory_usage = final_memory - initial_memory\n    \n    # Calculate throughput (samples per second)\n    throughput = len(X) / gpu_time\n\n    profiler.disable()\n\n    # Print the profiling results\n    stats = pstats.Stats(profiler)\n    stats.sort_stats('time').print_stats(10)  # Top 10 time-consuming functions\n\n    print()\n    \n    print(f'GPU Training time: {gpu_time:.4f} seconds')\n    print(f'GPU Memory usage: {memory_usage:.4f} MB')\n    print(f'GPU Throughput: {throughput:.4f} samples/second')\n\n    print('='*50)\n#-------------------------------------------------------------------------------------------\n    print('Scheduler :')\n    from tensorflow.keras.callbacks import ReduceLROnPlateau\n\n    # Reduce learning rate when validation loss stops improving\n    lr_scheduler = ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=3, verbose=1)\n\n    # Record initial memory usage\n    process = psutil.Process()\n    initial_memory = process.memory_info().rss / 1024 ** 2  # Memory in MB\n    profiler = cProfile.Profile()\n    profiler.enable()\n    \n    start_time = time.time()\n    model = training(callbacks=[lr_scheduler])\n    gpu_time = time.time() - start_time\n    \n    # Final memory usage\n    final_memory = process.memory_info().rss / 1024 ** 2  # Memory in MB\n    memory_usage = final_memory - initial_memory\n    \n    # Calculate throughput (samples per second)\n    throughput = len(X) / gpu_time\n\n    profiler.disable()\n\n    # Print the profiling results\n    stats = pstats.Stats(profiler)\n    stats.sort_stats('time').print_stats(10)  # Top 10 time-consuming functions\n\n    print()\n    \n    print(f'GPU Training time: {gpu_time:.4f} seconds')\n    print(f'GPU Memory usage: {memory_usage:.4f} MB')\n    print(f'GPU Throughput: {throughput:.4f} samples/second')\n\n    print('='*50)\n    #-------------------------------------------------------------------------------------------\n    print('Early stopping :')\n    from tensorflow.keras.callbacks import EarlyStopping\n\n    # Stop training if validation loss doesn't improve for 5 epochs\n    early_stopping = EarlyStopping(monitor='val_loss', patience=5, restore_best_weights=True, verbose=1)\n\n\n    # Record initial memory usage\n    process = psutil.Process()\n    initial_memory = process.memory_info().rss / 1024 ** 2  # Memory in MB\n    profiler = cProfile.Profile()\n    profiler.enable()\n    \n    start_time = time.time()\n    model = training(callbacks=[early_stopping])\n    gpu_time = time.time() - start_time\n    \n    # Final memory usage\n    final_memory = process.memory_info().rss / 1024 ** 2  # Memory in MB\n    memory_usage = final_memory - initial_memory\n    \n    # Calculate throughput (samples per second)\n    throughput = len(X) / gpu_time\n\n    profiler.disable()\n\n    # Print the profiling results\n    stats = pstats.Stats(profiler)\n    stats.sort_stats('time').print_stats(10)  # Top 10 time-consuming functions\n\n    print()\n    \n    print(f'GPU Training time: {gpu_time:.4f} seconds')\n    print(f'GPU Memory usage: {memory_usage:.4f} MB')\n    print(f'GPU Throughput: {throughput:.4f} samples/second')\n\n    print('='*50)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-07T16:27:32.612299Z","iopub.execute_input":"2025-01-07T16:27:32.612630Z","iopub.status.idle":"2025-01-07T16:38:33.595102Z","shell.execute_reply.started":"2025-01-07T16:27:32.612607Z","shell.execute_reply":"2025-01-07T16:38:33.594251Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#-------------------------------------------------------------------------------------------\nprint('Mirror Strategy :')\n\n    # Define the strategy\nstrategy = tf.distribute.MirroredStrategy()\n\n# Print the number of devices\nprint(f\"Number of devices: {strategy.num_replicas_in_sync}\")\n\nwith strategy.scope():\n    # Record initial memory usage\n    process = psutil.Process()\n    initial_memory = process.memory_info().rss / 1024 ** 2  # Memory in MB\n    profiler = cProfile.Profile()\n    profiler.enable()\n    \n    start_time = time.time()\n    model = training()\n    gpu_time = time.time() - start_time\n    \n    # Final memory usage\n    final_memory = process.memory_info().rss / 1024 ** 2  # Memory in MB\n    memory_usage = final_memory - initial_memory\n    \n    # Calculate throughput (samples per second)\n    throughput = len(X) / gpu_time\n    \n    profiler.disable()\n    \n    # Print the profiling results\n    stats = pstats.Stats(profiler)\n    stats.sort_stats('time').print_stats(10)  # Top 10 time-consuming functions\n    \n    print()\n\n    print(f'GPU Training time: {gpu_time:.4f} seconds')\n    print(f'GPU Memory usage: {memory_usage:.4f} MB')\n    print(f'GPU Throughput: {throughput:.4f} samples/second')\n\nprint('='*50)\n#-------------------------------------------------------------------------------------------\nprint('Multi worker Mirror strategy :')\n\n# Define the strategy\nstrategy = tf.distribute.MultiWorkerMirroredStrategy()\n\nwith strategy.scope():\n    # Record initial memory usage\n    process = psutil.Process()\n    initial_memory = process.memory_info().rss / 1024 ** 2  # Memory in MB\n    profiler = cProfile.Profile()\n    profiler.enable()\n    \n    start_time = time.time()\n    model = training()\n    gpu_time = time.time() - start_time\n    \n    # Final memory usage\n    final_memory = process.memory_info().rss / 1024 ** 2  # Memory in MB\n    memory_usage = final_memory - initial_memory\n    \n    # Calculate throughput (samples per second)\n    throughput = len(X) / gpu_time\n    \n    profiler.disable()\n    \n    # Print the profiling results\n    stats = pstats.Stats(profiler)\n    stats.sort_stats('time').print_stats(10)  # Top 10 time-consuming functions\n    \n    print()\n\n    print(f'GPU Training time: {gpu_time:.4f} seconds')\n    print(f'GPU Memory usage: {memory_usage:.4f} MB')\n    print(f'GPU Throughput: {throughput:.4f} samples/second')\n\nprint('='*50)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-07T16:38:33.596874Z","iopub.execute_input":"2025-01-07T16:38:33.597118Z","iopub.status.idle":"2025-01-07T16:43:20.743919Z","shell.execute_reply.started":"2025-01-07T16:38:33.597097Z","shell.execute_reply":"2025-01-07T16:43:20.742759Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# The output of this kernel must be binary (0 or 1), but the output of the NN Model is float (0 to 1).\n# So, find the best threshold to convert float to binary is crucial to the result\n# this piece of code is a function that evaluates all the possible thresholds from 0 to 1 by 0.01\ndef threshold_search(y_true, y_proba):\n    best_threshold = 0\n    best_score = 0\n    for threshold in tqdm([i * 0.01 for i in range(100)]):\n        score = K.eval(matthews_correlation(y_true.astype(np.float64), (y_proba > threshold).astype(np.float64)))\n        if score > best_score:\n            best_threshold = threshold\n            best_score = score\n    search_result = {'threshold': best_threshold, 'matthews_correlation': best_score}\n    return search_result","metadata":{"trusted":true,"_uuid":"d28151fd0be9fd9762f3f55e307d82f89bfbd291"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"best_threshold = threshold_search(y_val, preds_val)['threshold']","metadata":{"trusted":true,"_uuid":"6fee7f722ed08bc1453a822a4371ed2d48e08abc"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"%%time\n# Now load the test data\n# This first part is the meta data, not the main data, the measurements\nmeta_test = pd.read_csv('../input/metadata_test.csv')","metadata":{"trusted":true,"_uuid":"ae9bd3fa9d8c0781c0708846bb7f2a9f9e6cbd3c"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"meta_test = meta_test.set_index(['signal_id'])\nmeta_test.head()","metadata":{"trusted":true,"_uuid":"3eb186d032f79c99ffba05dd1a7fabb77e13cec5"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"%%time\n# First we daclarete a series of parameters to initiate the loading of the main data\n# it is too large, it is impossible to load in one time, so we are doing it in dividing in 10 parts\nfirst_sig = meta_test.index[0]\nn_parts = 10\nmax_line = len(meta_test)\npart_size = int(max_line / n_parts)\nlast_part = max_line % n_parts\nprint(first_sig, n_parts, max_line, part_size, last_part, n_parts * part_size + last_part)\n# Here we create a list of lists with start index and end index for each of the 10 parts and one for the last partial part\nstart_end = [[x, x+part_size] for x in range(first_sig, max_line + first_sig, part_size)]\nstart_end = start_end[:-1] + [[start_end[-1][0], start_end[-1][0] + last_part]]\nprint(start_end)\nX_test = []\n# now, very like we did above with the train data, we convert the test data part by part\n# transforming the 3 phases 800000 measurement in matrix (160,57)\nfor start, end in start_end:\n    subset_test = pq.read_pandas('../input/test.parquet', columns=[str(i) for i in range(start, end)]).to_pandas()\n    for i in tqdm(subset_test.columns):\n        id_measurement, phase = meta_test.loc[int(i)]\n        subset_test_col = subset_test[i]\n        subset_trans = transform_ts(subset_test_col)\n        X_test.append([i, id_measurement, phase, subset_trans])","metadata":{"trusted":true,"_uuid":"6f8e94387f625bff0a9a6289e1ee038908bc5856"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"X_test_input = np.asarray([np.concatenate([X_test[i][3],X_test[i+1][3], X_test[i+2][3]], axis=1) for i in range(0,len(X_test), 3)])\nnp.save(\"X_test.npy\",X_test_input)\nX_test_input.shape","metadata":{"trusted":true,"_uuid":"af9aa6b2b8f8a2beda1a02ff998e3072fcad8d06"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission = pd.read_csv('../input/sample_submission.csv')\nprint(len(submission))\nsubmission.head()","metadata":{"trusted":true,"_uuid":"cfd265d3e07c4cc1679d2c4d55fe7de631c813e7"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"preds_test = []\nfor i in range(N_SPLITS):\n    model.load_weights('weights_{}.h5'.format(i))\n    pred = model.predict(X_test_input, batch_size=300, verbose=1)\n    pred_3 = []\n    for pred_scalar in pred:\n        for i in range(3):\n            pred_3.append(pred_scalar)\n    preds_test.append(pred_3)\n","metadata":{"trusted":true,"_uuid":"2f7342296138f6bfd3e9cedd029e1035de3b98fc"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"preds_test = (np.squeeze(np.mean(preds_test, axis=0)) > best_threshold).astype(np.int)\npreds_test.shape","metadata":{"trusted":true,"_uuid":"9f76c471eaf983707d446c5081ab3d50c4e40ea5"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission['target'] = preds_test\nsubmission.to_csv('submission.csv', index=False)\nsubmission.head()","metadata":{"trusted":true,"_uuid":"b35723f85d494b4b6ec630dd7c79135a110a4062"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true,"_uuid":"d7600d0093a9880003240ef9ce0a1f1303e4d982"},"outputs":[],"execution_count":null}]}