Пример #1
0
def siso_regression_tut(
        gpu_id: int,
        dataset: str,
        frac: float,
        validation_split: float,
        preprocessor: str,
        batch_size: int,
        epochs: int,
        optimizer: str,
        dropout: float,
        corruption_level: float,
        dae_hidden_layers: list,
        sdae_hidden_layers: list,
        cache: bool,
        regression_hidden_layers: list,
        verbose: int
):
    """Multi-floor indoor localization based on three-dimensional regression of
    location coordinates using a single-input and single-output (SISO) deep
    neural network (DNN) model and TUT datasets.

    Keyword arguments:

    """

    ### initialize numpy, random, TensorFlow, and keras
    np.random.seed()            # based on current time or OS-specific randomness source
    rn.seed()                   #  "
    tf.set_random_seed(rn.randint(0, 1000000))
    if gpu_id >= 0:
        os.environ["CUDA_VISIBLE_DEVICES"] = str(gpu_id)
    else:
        os.environ["CUDA_VISIBLE_DEVICES"] = ''
    sess = tf.Session(
        graph=tf.get_default_graph(),
        config=session_conf)
    K.set_session(sess)

    ### load datasets after scaling
    print("Loading data ...")
    if dataset == 'tut':
        from tut import TUT
        tut = TUT(
            cache=cache,
            frac=frac,
            preprocessor=preprocessor,
            classification_mode='hierarchical',
            grid_size=0)
    elif dataset == 'tut2':
        from tut import TUT2
        tut = TUT2(
            cache=cache,
            frac=frac,
            preprocessor=preprocessor,
            classification_mode='hierarchical',
            grid_size=0,
            testing_split=0.2)
    elif dataset == 'tut3':
        from tut import TUT3
        tut = TUT3(
            cache=cache,
            frac=frac,
            preprocessor=preprocessor,
            classification_mode='hierarchical',
            grid_size=0)
    else:
        print("'{0}' is not a supported data set.".format(dataset))
        sys.exit(0)
    flr_height = tut.floor_height
    training_df = tut.training_df
    training_data = tut.training_data
    testing_df = tut.testing_df
    testing_data = tut.testing_data

    ### build and train a SIMO model
    print(
        "Building and training a SISO model for three-dimensional regression ..."
    )
    rss = training_data.rss_scaled
    coord = training_data.coord_3d_scaled
    coord_scaler = training_data.coord_3d_scaler  # for inverse transform
    labels = training_data.labels
    input = Input(shape=(rss.shape[1], ), name='input')  # common input

    # (optional) build deep autoencoder or stacked denoising autoencoder
    if dae_hidden_layers != '':
        print("- Building a DAE model ...")
        model = deep_autoencoder(
            dataset=dataset,
            input_data=rss,
            preprocessor=preprocessor,
            hidden_layers=dae_hidden_layers,
            cache=cache,
            model_fname=None,
            optimizer=optimizer,
            batch_size=batch_size,
            epochs=epochs,
            validation_split=validation_split)
        x = model(input)
    elif sdae_hidden_layers != '':
        print("- Building an SDAE model ...")
        model = sdae(
            dataset=dataset,
            input_data=rss,
            preprocessor=preprocessor,
            hidden_layers=sdae_hidden_layers,
            cache=cache,
            model_fname=None,
            optimizer=optimizer,
            corruption_level=corruption_level,
            batch_size=batch_size,
            epochs=epochs,
            validation_split=validation_split)
        x = model(input)
    else:
        x = input

    # regression hidden layers
    x = BatchNormalization()(x)
    x = Activation('relu')(x)
    x = Dropout(dropout)(x)
    if regression_hidden_layers != '':
        for units in regression_hidden_layers:
            x = Dense(units)(x)
            x = BatchNormalization()(x)
            x = Activation('relu')(x)
            x = Dropout(dropout)(x)

    # coordinates regression output
    x = Dense(coord.shape[1], kernel_initializer='normal')(x)
    x = BatchNormalization()(x)
    coordinates_output = Activation(
        'linear', name='coordinates_output')(x)  # 'linear' activation

    model = Model(inputs=input, outputs=coordinates_output)
    model.compile(optimizer=optimizer, loss='mean_squared_error',
                  metrics=['mean_squared_error'])
    weights_file = os.path.expanduser("~/tmp/best_weights.h5")
    checkpoint = ModelCheckpoint(weights_file, monitor='val_loss', save_best_only=True, verbose=0)
    early_stop = EarlyStopping(monitor='val_loss', min_delta=0, patience=10, verbose=0)

    print("- Training a coordinates regressor ...", end='')
    startTime = timer()
    history = model.fit(
        x={'input': rss},
        y={'coordinates_output': coord},
        batch_size=batch_size,
        epochs=epochs,
        verbose=verbose,
        callbacks=[checkpoint, early_stop],
        validation_split=validation_split,
        shuffle=True)
    elapsedTime = timer() - startTime
    print(" completed in {0:.4e} s".format(elapsedTime))
    model.load_weights(weights_file)  # load weights from the best model

    ### evaluate the model
    print("Evaluating the model ...")
    rss = testing_data.rss_scaled
    labels = testing_data.labels
    flrs = labels.floor
    coord = testing_data.coord_3d  # original coordinates

    # calculate the classification accuracies and localization errors
    coords_scaled_pred = model.predict(rss, batch_size=batch_size)
    coord_est = coord_scaler.inverse_transform(coords_scaled_pred)  # inverse-scaling
    tmp = np.maximum(np.minimum(coord_est[:,2], 4*tut.floor_height), 0)     # clamping to [0, 4*tut.floor_height]
    flrs_pred = np.floor(tmp/tut.floor_height+0.5)  # floor number (0..4); N.B. round() behavior in Python 3 has been changed,so we cannot use it.
    flr_results = (np.equal(np.argmax(flrs, axis=1), flrs_pred)).astype(int)
    flr_acc = flr_results.mean()

    # calculate 2D localization errors
    dist_2d = norm(coord - coord_est, axis=1)
    mean_error_2d = dist_2d.mean()
    median_error_2d = np.median(dist_2d)

    # calculate 3D localization errors
    flr_diff = np.absolute(np.argmax(flrs, axis=1) - flrs_pred)
    z_diff_squared = (flr_height**2)*np.square(flr_diff)
    dist_3d = np.sqrt(np.sum(np.square(coord - coord_est), axis=1) + z_diff_squared)
    mean_error_3d = dist_3d.mean()
    median_error_3d = np.median(dist_3d)

    LocalizationResults = namedtuple('LocalizationResults', ['flr_acc',
                                                             'mean_error_2d',
                                                             'median_error_2d',
                                                             'mean_error_3d',
                                                             'median_error_3d',
                                                             'elapsedTime'])
    return LocalizationResults(flr_acc=flr_acc, mean_error_2d=mean_error_2d,
                               median_error_2d=median_error_2d,
                               mean_error_3d=mean_error_3d,
                               median_error_3d=median_error_3d,
                               elapsedTime=elapsedTime)
Пример #2
0
    )
    rss = training_data.rss_scaled
    coord = training_data.coord_scaled
    coord_scaler = training_data.coord_scaler  # for inverse transform
    labels = training_data.labels
    input = Input(shape=(rss.shape[1], ), name='input')  # common input

    # (optional) build deep autoencoder or stacked denoising autoencoder
    if dae_hidden_layers != '':
        print("\nPart 2.0: building a DAE model ...")
        model = deep_autoencoder(
            dataset=dataset,
            input_data=rss,
            preprocessor=preprocessor,
            hidden_layers=dae_hidden_layers,
            cache=cache,
            model_fname=None,
            optimizer=optimizer,
            batch_size=batch_size,
            epochs=epochs,
            validation_split=validation_split)
        x = model(input)
    elif sdae_hidden_layers != '':
        print("\nPart 2.0: building an SDAE model ...")
        model = sdae(
            dataset=dataset,
            input_data=rss,
            preprocessor=preprocessor,
            hidden_layers=sdae_hidden_layers,
            cache=cache,
            model_fname=None,
    utm = training_data.utm_scaled
    labels = training_data.labels
    input = Input(shape=(rss.shape[1], ), name='input')  # common input
    tensorboard = TensorBoard(
        log_dir="logs/{}".format(time()), write_graph=True)
    adaptive_loss_weights = AdaptiveLossWeights(
        building_weight, floor_weight, location_weight, coordinates_weight)

    # (optional) build deep autoencoder
    if dae_hidden_layers != '':
        print("\nPart 2.0: buidling a DAE model ...")
        model = deep_autoencoder(
            rss,
            preprocessor=preprocessor,
            hidden_layers=dae_hidden_layers,
            model_fname=None,
            optimizer=optimizer,
            batch_size=batch_size,
            epochs=epochs,
            validation_split=validation_split)
        x = model(input)
    else:
        x = input

    # common hidden layers
    x = BatchNormalization()(x)
    x = Activation('relu')(x)
    x = Dropout(dropout)(x)
    for units in common_hidden_layers:
        x = Dense(units)(x)
        x = BatchNormalization()(x)
def simo_hybrid_uji(
        gpu_id: int,
        dataset: str,
        frac: float,
        validation_split: float,
        preprocessor: str,
        batch_size: int,
        epochs: int,
        optimizer: str,
        dropout: float,
        corruption_level: float,
        dae_hidden_layers: list,
        sdae_hidden_layers: list,
        cache: bool,
        common_hidden_layers: list,
        floor_hidden_layers: list,
        coordinates_hidden_layers: list,
        floor_weight: float,
        coordinates_weight: float,
        verbose: int
):
    """Multi-building and multi-floor indoor localization based on hybrid
    building/floor classification and coordinates regression using a
    single-input and multi-output (SIMO) deep neural network (DNN) model and
    UJIIndoorLoc datasets.

    Keyword arguments:
    """

    ### initialize numpy, random, TensorFlow, and keras
    np.random.seed()            # based on current time or OS-specific randomness source
    rn.seed()                   #  "
    tf.set_random_seed(rn.randint(0, 1000000))
    if gpu_id >= 0:
        os.environ["CUDA_VISIBLE_DEVICES"] = str(gpu_id)
    else:
        os.environ["CUDA_VISIBLE_DEVICES"] = ''
    sess = tf.Session(
        graph=tf.get_default_graph(),
        config=session_conf)
    K.set_session(sess)

    ### load datasets after scaling
    print("Loading data ...")
    if dataset == 'uji':
        from ujiindoorloc import UJIIndoorLoc
        uji = UJIIndoorLoc(
            cache=cache,
            frac=frac,
            preprocessor=preprocessor,
            classification_mode='hierarchical')
    else:
        print("'{0}' is not a supported data set.".format(dataset))
        sys.exit(0)
    flr_height = uji.floor_height
    training_df = uji.training_df
    training_data = uji.training_data
    testing_df = uji.testing_df
    testing_data = uji.testing_data

    ### build and train a SIMO model
    print(
        "Building and training a SIMO model for hybrid classification and regression ..."
    )
    rss = training_data.rss_scaled
    coord = training_data.coord_scaled
    coord_scaler = training_data.coord_scaler  # for inverse transform
    labels = training_data.labels
    input = Input(shape=(rss.shape[1], ), name='input')  # common input

    # (optional) build deep autoencoder or stacked denoising autoencoder
    if dae_hidden_layers != '':
        print("- Building a DAE model ...")
        model = deep_autoencoder(
            dataset=dataset,
            input_data=rss,
            preprocessor=preprocessor,
            hidden_layers=dae_hidden_layers,
            cache=cache,
            model_fname=None,
            optimizer=optimizer,
            batch_size=batch_size,
            epochs=epochs,
            validation_split=validation_split)
        x = model(input)
    elif sdae_hidden_layers != '':
        print("- Building an SDAE model ...")
        model = sdae(
            dataset=dataset,
            input_data=rss,
            preprocessor=preprocessor,
            hidden_layers=sdae_hidden_layers,
            cache=cache,
            model_fname=None,
            optimizer=optimizer,
            corruption_level=corruption_level,
            batch_size=batch_size,
            epochs=epochs,
            validation_split=validation_split)
        x = model(input)
    else:
        x = input

    # common hidden layers
    # x = BatchNormalization()(x)
    # x = Activation('relu')(x)
    # x = Dropout(dropout)(x)
    # if common_hidden_layers != '':
    #     for units in common_hidden_layers:
    #         x = Dense(units)(x)
    #         x = BatchNormalization()(x)
    #         x = Activation('relu')(x)
    #         x = Dropout(dropout)(x)
    # common_hl_output = x

    # floor classification output
    # if floor_hidden_layers != '':
    #     for units in floor_hidden_layers:
    #         x = Dense(units)(x)
    #         x = BatchNormalization()(x)
    #         x = Activation('relu')(x)
    #         x = Dropout(dropout)(x)
    # x = Dense(labels.floor.shape[1])(x)
    # x = BatchNormalization()(x)
    # floor_output = Activation(
    #     'softmax', name='floor_output')(x)  # no dropout for an output layer
    #
    # x = Lambda(lambda x: K.expand_dims(x, axis=-1))(x)
    # x = Conv1D(filters=99, kernel_size=12, activation='relu')(x)
    #
    # x = MaxPooling1D(pool_size=5)(x)
    # x = Conv1D(filters=99, kernel_size=12, activation='relu')(x)
    #
    # x = MaxPooling1D(pool_size=5)(x)
    #
    # x = Conv1D(filters=99, kernel_size=12, activation='relu')(x)
    #
    # x = Dropout(dropout)(x)
    # x = Flatten()(x)
    #
    # x = Dense(labels.floor.shape[1])(x)
    # x = BatchNormalization()(x)
    # floor_output = Activation('softmax', name='floor_output')(x)
    # # coordinates regression output
    # x = common_hl_output
    # for units in coordinates_hidden_layers:
    #     x = Dense(units, kernel_initializer='normal')(x)
    #     x = BatchNormalization()(x)
    #     x = Activation('relu')(x)
    #     x = Dropout(dropout)(x)
    # x = Dense(coord.shape[1], kernel_initializer='normal')(x)
    # x = BatchNormalization()(x)
    # coordinates_output = Activation(
    #     'linear', name='coordinates_output')(x)  # 'linear' activation
    # x = common_hl_output
    # x = Lambda(lambda x:K.expand_dims(x,axis=-1))(x)
    # x = Conv1D(filters=99, kernel_size=12, activation='relu')(x)
    #
    # x = MaxPooling1D(pool_size=5)(x)
    # x = Conv1D(filters=99, kernel_size=12, activation='relu')(x)
    #
    # x = MaxPooling1D(pool_size=5)(x)
    # x = Conv1D(filters=99, kernel_size=12, activation='relu')(x)
    # x = Dropout(dropout)(x)
    # x = Flatten()(x)
    # x = Dense(coord.shape[1],kernel_initializer='normal')(x)
    # x = BatchNormalization()(x)
    # coordinates_output = Activation(
    #     'linear', name='coordinates_output')(x)



    # 1D_CNN by John

    x = Lambda(lambda x:K.expand_dims(x,axis=-1))(x)


    x = Conv1D(filters=99, kernel_size=22, activation='relu')(x)
    x = Dropout(dropout)(x)
    # x = Conv1D(filters=128, kernel_size=10, activation='relu')(x)

    # x = MaxPooling1D(pool_size=2)(x)
    x = Conv1D(filters=66, kernel_size=22, activation='relu')(x)

    # x = MaxPooling1D(pool_size=2)(x)

    x = Conv1D(filters=33, kernel_size=22, activation='relu')(x)
    x = MaxPooling1D(pool_size=2)(x)
    x = Flatten()(x)
    #1D_CNN by John
    #1DCNN by John
    n = x
    x = Dense(labels.floor.shape[1])(x)
    # x = BatchNormalization()(x)
    floor_output = Activation('softmax', name='floor_output')(x)

    common_hl_output = n
    x = common_hl_output
    x = Dense(coord.shape[1], kernel_initializer='normal')(x)
    # x = BatchNormalization()(x)
    coordinates_output = Activation(
        'linear', name='coordinates_output')(x)
    #1DCNN by John
    model = Model(
        inputs=input,
        outputs=[
            floor_output,
            coordinates_output
        ])
    model.compile(
        optimizer=optimizer,
        loss=[
            'categorical_crossentropy',
            'mean_squared_error'
        ],
        loss_weights={
            'floor_output': floor_weight,
            'coordinates_output': coordinates_weight
        },
        metrics={
            'floor_output': 'accuracy',
            'coordinates_output': 'mean_squared_error'
        })
    weights_file = os.path.expanduser("~/tmp/best_weights.h5")
    checkpoint = ModelCheckpoint(weights_file, monitor='val_loss', save_best_only=True, verbose=0)
    early_stop = EarlyStopping(monitor='val_loss', min_delta=0, patience=10, verbose=0)

    print("- Training a hybrid floor classifier and coordinates regressor ...", end='')
    startTime = timer()
    history = model.fit(
        x={'input': rss},
        y={
            'floor_output': labels.floor,
            'coordinates_output': coord
        },
        batch_size=batch_size,
        epochs=epochs,
        verbose=verbose,
        callbacks=[checkpoint, early_stop],
        validation_split=validation_split,
        shuffle=True)
    elapsedTime = timer() - startTime
    print(" completed in {0:.4e} s".format(elapsedTime))
    model.load_weights(weights_file)  # load weights from the best model

    ### evaluate the model
    print("Evaluating the model ...")
    rss = testing_data.rss_scaled
    labels = testing_data.labels
    flrs = labels.floor
    coord = testing_data.coord  # original coordinates

    # calculate the classification accuracies and localization errors
    flrs_pred, coords_scaled_pred = model.predict(rss, batch_size=batch_size)
    flr_results = (np.equal(
        np.argmax(flrs, axis=1), np.argmax(flrs_pred, axis=1))).astype(int)
    flr_acc = flr_results.mean()
    coord_est = coord_scaler.inverse_transform(coords_scaled_pred)  # inverse-scaling

    # calculate 2D localization errors
    dist_2d = norm(coord - coord_est, axis=1)
    mean_error_2d = dist_2d.mean()
    median_error_2d = np.median(dist_2d)

    # calculate 3D localization errors
    flr_diff = np.absolute(
        np.argmax(flrs, axis=1) - np.argmax(flrs_pred, axis=1))
    z_diff_squared = (flr_height**2)*np.square(flr_diff)
    dist_3d = np.sqrt(np.sum(np.square(coord - coord_est), axis=1) + z_diff_squared)
    mean_error_3d = dist_3d.mean()
    median_error_3d = np.median(dist_3d)

    LocalizationResults = namedtuple('LocalizationResults', ['flr_acc',
                                                             'mean_error_2d',
                                                             'median_error_2d',
                                                             'mean_error_3d',
                                                             'median_error_3d',
                                                             'elapsedTime'])
    return LocalizationResults(flr_acc=flr_acc, mean_error_2d=mean_error_2d,
                               median_error_2d=median_error_2d,
                               mean_error_3d=mean_error_3d,
                               median_error_3d=median_error_3d,
                               elapsedTime=elapsedTime)
Пример #5
0
def simo_swt_hybrid_tut(
        gpu_id: int,
        dataset: str,
        frac: float,
        validation_split: float,
        preprocessor: str,
        batch_size: int,
        epochs: int,
        optimizer: str,
        dropout: float,
        corruption_level: float,
        dae_hidden_layers: list,
        sdae_hidden_layers: list,
        cache: bool,
        common_hidden_layers: list,
        floor_hidden_layers: list,
        coordinates_hidden_layers: list,
        verbose: int
):
    """Multi-floor indoor localization based on hybrid floor classification and
    coordinates regression using a stage-wise trained single-input and
    multi-output (SIMO) deep neural network (DNN) model and TUT datasets.

    Keyword arguments:

    """

    ### initialize numpy, random, TensorFlow, and keras
    np.random.seed()            # based on current time or OS-specific randomness source
    rn.seed()                   # "
    tf.set_random_seed(rn.randint(0, 1000000))
    if gpu_id >= 0:
        os.environ["CUDA_VISIBLE_DEVICES"] = str(gpu_id)
    else:
        os.environ["CUDA_VISIBLE_DEVICES"] = ''
    sess = tf.Session(
        graph=tf.get_default_graph(),
        config=session_conf)
    K.set_session(sess)

    ### load datasets after scaling
    print("Loading data ...")
    if dataset == 'tut':
        from tut import TUT
        tut = TUT(
            cache=cache,
            frac=frac,
            preprocessor=preprocessor,
            classification_mode='hierarchical',
            grid_size=0)
    elif dataset == 'tut2':
        from tut import TUT2
        tut = TUT2(
            cache=cache,
            frac=frac,
            preprocessor=preprocessor,
            classification_mode='hierarchical',
            grid_size=0,
            testing_split=0.2)
    elif dataset == 'tut3':
        from tut import TUT3
        tut = TUT3(
            cache=cache,
            frac=frac,
            preprocessor=preprocessor,
            classification_mode='hierarchical',
            grid_size=0)
    else:
        print("'{0}' is not a supported data set.".format(dataset))
        sys.exit(0)
    flr_height = tut.floor_height
    training_df = tut.training_df
    training_data = tut.training_data
    testing_df = tut.testing_df
    testing_data = tut.testing_data
        
    ### build and do stage-wise training of a SIMO model
    print(
        "Building and stage-wise training a SIMO model for hybrid classification and regression ..."
    )
    rss = training_data.rss_scaled
    coord = training_data.coord_scaled
    coord_scaler = training_data.coord_scaler  # for inverse transform
    labels = training_data.labels
    input = Input(shape=(rss.shape[1], ), name='input')  # common input

    # (optional) build deep autoencoder or stacked denoising autoencoder
    if dae_hidden_layers != '':
        print("- Building a DAE model ...")
        model = deep_autoencoder(
            dataset=dataset,
            input_data=rss,
            preprocessor=preprocessor,
            hidden_layers=dae_hidden_layers,
            cache=cache,
            model_fname=None,
            optimizer=optimizer,
            batch_size=batch_size,
            epochs=epochs,
            validation_split=validation_split)
        x = model(input)
    elif sdae_hidden_layers != '':
        print("- Building an SDAE model ...")
        model = sdae(
            dataset=dataset,
            input_data=rss,
            preprocessor=preprocessor,
            hidden_layers=sdae_hidden_layers,
            cache=cache,
            model_fname=None,
            optimizer=optimizer,
            corruption_level=corruption_level,
            batch_size=batch_size,
            epochs=epochs,
            validation_split=validation_split)
        x = model(input)
    else:
        x = input

    # common hidden layers
    x = BatchNormalization()(x)
    x = Activation('relu')(x)
    x = Dropout(dropout)(x)
    if common_hidden_layers != '':
        for i in range(len(common_hidden_layers)):
            x = Dense(common_hidden_layers[i], name='common_hidden_layer_{:d}'.format(i))(x)
            x = BatchNormalization()(x)
            x = Activation('relu')(x)
            x = Dropout(dropout)(x)
    common_hl_output = x

    # floor classification output
    if floor_hidden_layers != '':
        for i in range(len(floor_hidden_layers)):
            x = Dense(floor_hidden_layers[i], name='floor_hidden_layer_{:d}'.format(i))(x)
            x = BatchNormalization()(x)
            x = Activation('relu')(x)
            x = Dropout(dropout)(x)
        i += 1
    else:
        i = 0
    x = Dense(labels.floor.shape[1], name='floor_hidden_layer_{:d}'.format(i))(x)
    x = BatchNormalization()(x)
    floor_output = Activation(
        'softmax', name='floor_output')(x)  # no dropout for an output layer

    # coordinates regression output
    x = common_hl_output
    if coordinates_hidden_layers != '':
        for i in range(len(coordinates_hidden_layers)):
            x = Dense(coordinates_hidden_layers[i], kernel_initializer='normal', name='coordinates_hidden_layer_{:d}'.format(i))(x)
            x = BatchNormalization()(x)
            x = Activation('relu')(x)
            x = Dropout(dropout)(x)
        i += 1
    else:
        i = 0
    x = Dense(coord.shape[1], kernel_initializer='normal', name='coordinates_hidden_layer_{:d}'.format(i))(x)
    x = BatchNormalization()(x)
    coordinates_output = Activation(
        'linear', name='coordinates_output')(x)  # 'linear' activation
    
    # build model
    model = Model(
        inputs=input,
        outputs=[
            floor_output,
            coordinates_output
        ])

    print("- Stage-wise training with floor information ...", end='')
    model.compile(
        optimizer=optimizer,
        loss=[
            'categorical_crossentropy',
            'mean_squared_error'
        ],
        loss_weights={
            'floor_output': 1.0,
            'coordinates_output': 0.0
        },
        metrics={
            'floor_output': 'accuracy',
            'coordinates_output': 'mean_squared_error'
        })
    weights_file = os.path.expanduser("~/tmp/best_f-weights.h5")
    checkpoint = ModelCheckpoint(weights_file, monitor='val_loss', save_best_only=True, verbose=0)
    early_stop = EarlyStopping(monitor='val_loss', patience=10, verbose=0)
    
    startTime = timer()
    f_history = model.fit(
        x={'input': rss},
        y={
            'floor_output': labels.floor,
            'coordinates_output': coord
        },
        batch_size=batch_size,
        epochs=epochs,
        verbose=verbose,
        callbacks=[checkpoint, early_stop],
        validation_split=validation_split,
        shuffle=True)
    elapsedTime = timer() - startTime
    elapsedTime_total = elapsedTime
    print(" completed in {0:.4e} s".format(elapsedTime))
    model.load_weights(weights_file)  # load weights from the best model

    print(
        "- Stage-wise training with floor-coordinates information ...", end=''
    )

    # reinitialize hidden layers based on
    # https://www.codementor.io/nitinsurya/how-to-re-initialize-keras-model-weights-et41zre2g
    # - common
    if common_hidden_layers != '':
        for i in range(len(common_hidden_layers)):
            layer = model.get_layer('common_hidden_layer_{:d}'.format(i))
            if hasattr(layer, 'kernel_initializer'):
                layer.kernel.initializer.run(session=sess)
    # # - floor
    # if floor_hidden_layers != '':
    #     for i in range(len(floor_hidden_layers)):
    #         layer = model.get_layer('floor_hidden_layer_{:d}'.format(i))
    #         if hasattr(layer, 'kernel_initializer'):
    #             layer.kernel.initializer.run(session=sess)
    #     i += 1
    # else:
    #     i = 0
    # layer = model.get_layer('floor_hidden_layer_{:d}'.format(i))
    # if hasattr(layer, 'kernel_initializer'):
    #     layer.kernel.initializer.run(session=sess)
    # - coordinats
    if coordinates_hidden_layers != '':
        for i in range(len(coordinates_hidden_layers)):
            layer = model.get_layer('coordinates_hidden_layer_{:d}'.format(i))
            if hasattr(layer, 'kernel_initializer'):
                layer.kernel.initializer.run(session=sess)
        i += 1
    else:
        i = 0
    layer = model.get_layer('coordinates_hidden_layer_{:d}'.format(i))
    if hasattr(layer, 'kernel_initializer'):
        layer.kernel.initializer.run(session=sess)
                
    model.compile(
        optimizer=optimizer,
        loss=[
            'categorical_crossentropy',
            'mean_squared_error'
        ],
        loss_weights={
            'floor_output': 1.0,
            'coordinates_output': 1.0
        },
        metrics={
            'floor_output': 'accuracy',
            'coordinates_output': 'mean_squared_error'
        })
    weights_file = os.path.expanduser("~/tmp/best_fc-weights.h5")
    checkpoint = ModelCheckpoint(weights_file, monitor='val_loss', save_best_only=True, verbose=0)
    early_stop = EarlyStopping(monitor='val_loss', patience=10, verbose=0)

    startTime = timer()
    fc_history = model.fit(
        x={'input': rss},
        y={
            'floor_output': labels.floor,
            'coordinates_output': coord
        },
        batch_size=batch_size,
        epochs=epochs,
        verbose=verbose,
        callbacks=[checkpoint, early_stop],
        validation_split=validation_split,
        shuffle=True)
    elapsedTime = timer() - startTime
    elapsedTime_total += elapsedTime
    print(" completed in {0:.4e} s".format(elapsedTime))
    model.load_weights(weights_file)  # load weights from the best model

    ### evaluate the model
    print("Evaluating the model ...")
    rss = testing_data.rss_scaled
    labels = testing_data.labels
    flrs = labels.floor
    coord = testing_data.coord  # original coordinates
    x_col_name = 'X'
    y_col_name = 'Y'

    # calculate the classification accuracies and localization errors
    flrs_pred, coords_scaled_pred = model.predict(rss, batch_size=batch_size)
    flr_results = (np.equal(
        np.argmax(flrs, axis=1), np.argmax(flrs_pred, axis=1))).astype(int)
    flr_acc = flr_results.mean()
    coord_est = coord_scaler.inverse_transform(coords_scaled_pred)  # inverse-scaling

    # calculate 2D localization errors
    dist_2d = norm(coord - coord_est, axis=1)
    mean_error_2d = dist_2d.mean()
    median_error_2d = np.median(dist_2d)

    # calculate 3D localization errors
    flr_diff = np.absolute(
        np.argmax(flrs, axis=1) - np.argmax(flrs_pred, axis=1))
    z_diff_squared = (flr_height**2)*np.square(flr_diff)
    dist_3d = np.sqrt(np.sum(np.square(coord - coord_est), axis=1) + z_diff_squared)
    mean_error_3d = dist_3d.mean()
    median_error_3d = np.median(dist_3d)

    LocalizationResults = namedtuple('LocalizationResults', ['flr_acc',
                                                             'mean_error_2d',
                                                             'median_error_2d',
                                                             'mean_error_3d',
                                                             'median_error_3d',
                                                             'elapsedTime'])
    return LocalizationResults(flr_acc=flr_acc, mean_error_2d=mean_error_2d,
                               median_error_2d=median_error_2d,
                               mean_error_3d=mean_error_3d,
                               median_error_3d=median_error_3d,
                               elapsedTime=elapsedTime)
def simo_hybrid(gpu_id, random_seed, epochs, batch_size, validation_split,
                dropout, dae_hidden_layers, common_hidden_layers,
                floor_location_hidden_layers, building_hidden_layers,
                floor_hidden_layers, location_hidden_layers, building_weight,
                floor_weight, location_weight):
    """Multi-building and multi-floor indoor localisztion based on Wi-Fi fingerprinting with a single-input and multi-output deep neural network

    Keyword arguments:
    gpu_id -- ID of GPU device to run this script; set it to a negative number for CPU (i.e., no GPU)
    random_seed -- a seed for random number generator
    epoch -- number of epochs
    batch_size -- batch size
    validation_split -- fraction of training data to be used as validation data
    dropout -- dropout rate before and after hidden layers
    dae_hidden_layers -- list of numbers of units in DAE hidden layers
    common_hidden_layers -- list of numbers of units in common hidden layers
    floor_location_hidden_layers -- list of numbers of units in floor/location hidden layers
    building_hidden_layers --list of numbers of units in building classifier hidden layers
    floor_hidden_layers --list of numbers of units in floor classifier hidden layers
    location_hidden_layers --list of numbers of units in location hidden layers
    building_weight -- loss weight for a building classifier
    floor_weight -- loss weight for a floor classifier
    location_weight -- loss weight for a location regressor
    """

    np.random.seed(random_seed)  # initialize random number generator

    #--------------------------------------------------------------------
    # import keras and its backend (e.g., tensorflow)
    #--------------------------------------------------------------------
    os.environ["CUDA_DEVICE_ORDER"] = "PCI_BUS_ID"  # see issue #152
    if gpu_id >= 0:
        os.environ["CUDA_VISIBLE_DEVICES"] = str(gpu_id)
    else:
        os.environ["CUDA_VISIBLE_DEVICES"] = ''
    os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'  # supress warning messages
    import tensorflow as tf
    from keras import backend as K
    from keras.layers import Activation, Dense, Dropout, Input
    from keras.layers.normalization import BatchNormalization
    from keras.models import Model, Sequential, load_model
    from keras.callbacks import TensorBoard
    K.clear_session()  # avoid clutter from old models / layers.

    # read both train and test dataframes for consistent label formation through one-hot encoding
    training_df = pd.read_csv(
        training_data_file,
        header=0)  # pass header=0 to be able to replace existing names
    testing_df = pd.read_csv(
        validation_data_file,
        header=0)  # turn the validation set into a testing set

    # scale numerical data (over their flattened versions for joint scaling)
    rss_scaler = StandardScaler(
    )  # the same scaling will be applied to test data later
    utm_scaler = StandardScaler()  # ditto

    col_aps = [col for col in training_df.columns if 'WAP' in col]
    num_aps = len(col_aps)
    rss = np.asarray(training_df[col_aps], dtype=np.float32)
    rss = (rss_scaler.fit_transform(rss.reshape((-1, 1)))).reshape(rss.shape)

    utm_x = np.asarray(training_df['LONGITUDE'], dtype=np.float32)
    utm_y = np.asarray(training_df['LATITUDE'], dtype=np.float32)
    utm = utm_scaler.fit_transform(np.column_stack((utm_x, utm_y)))
    num_coords = utm.shape[1]

    # # map reference points to sequential IDs per building & floor before building labels
    # training_df['REFPOINT'] = training_df.apply(lambda row: str(int(row['SPACEID'])) + str(int(row['RELATIVEPOSITION'])), axis=1) # add a new column
    # blds = np.unique(training_df[['BUILDINGID']])
    # flrs = np.unique(training_df[['FLOOR']])
    # for bld in blds:
    #     for flr in flrs:
    #         cond = (training_df['BUILDINGID']==bld) & (training_df['FLOOR']==flr)
    #         _, idx = np.unique(training_df.loc[cond, 'REFPOINT'], return_inverse=True) # refer to numpy.unique manual
    #         training_df.loc[cond, 'REFPOINT'] = idx

    # build labels for the classification of a building, a floor, and a reference point
    num_training_samples = len(training_df)
    num_testing_samples = len(testing_df)
    blds_all = np.asarray(
        pd.get_dummies(
            pd.concat([
                training_df['BUILDINGID'], testing_df['BUILDINGID']
            ])))  # for consistency in one-hot encoding for both dataframes
    num_blds = blds_all.shape[1]
    flrs_all = np.asarray(
        pd.get_dummies(pd.concat([training_df['FLOOR'],
                                  testing_df['FLOOR']])))  # ditto
    num_flrs = flrs_all.shape[1]
    blds = blds_all[:num_training_samples]
    flrs = flrs_all[:num_training_samples]
    # rfps = np.asarray(pd.get_dummies(training_df['REFPOINT']))
    # num_rfps = rfps.shape[1]
    # labels is an array of 19937 x 118
    # - 3 for BUILDINGID
    # - 5 for FLOOR,
    # - 110 for REFPOINT
    # OUTPUT_DIM = training_labels.shape[1]

    # split the training set into a training and a validation set; the original
    # validation set is used as a testing set.
    mask_training = np.random.rand(
        len(rss)) < 1.0 - validation_split  # mask index array

    rss_training = rss[mask_training]
    utm_training = utm[mask_training]
    blds_training = blds[mask_training]
    flrs_training = flrs[mask_training]
    # rfps_training = rfps[mask_training]

    rss_validation = rss[~mask_training]
    utm_validation = utm[~mask_training]
    blds_validation = blds[~mask_training]
    flrs_validation = flrs[~mask_training]
    # rfps_validation = rfps[~mask_training]

    ### build deep autoencoder model
    print("\nPart 1: buidling a DAE encoder ...")
    model = deep_autoencoder(rss_training,
                             hidden_layers=dae_hidden_layers,
                             validation_split=validation_split)

    ### build and train a complete model with the trained DAE encoder and a new classifier
    print("\nPart 2: buidling a complete model ...")

    input = Input(shape=(num_aps, ), name='input')
    x = model(input)  # denoise the input data using the DAE encoder
    x = BatchNormalization()(x)
    x = Activation('relu')(x)
    denoised_input = Dropout(dropout)(x)

    # common hidden layers for all
    if common_hidden_layers == '':
        common_input = denoised_input
    else:
        # x = Dropout(dropout)(denoised_input)
        for units in common_hidden_layers[:-1]:
            x = Dense(units)(x)
            x = BatchNormalization()(x)
            x = Activation('relu')(x)
            x = Dropout(dropout)(x)
        x = Dense(common_hidden_layers[-1])(x)
        x = BatchNormalization()(x)
        x = Activation('relu')(x)
        common_input = Dropout(dropout, name='common_input')(x)

    # building classifier output
    for units in building_hidden_layers:
        x = Dense(units)(x)
        x = BatchNormalization()(x)
        x = Activation('relu')(x)
        x = Dropout(dropout)(x)
    x = Dense(num_blds)(x)
    x = BatchNormalization()(x)
    building_output = Activation('softmax', name='building_output')(
        x)  # no dropout for an output layer

    # common hidden layers for floor and location/position
    if floor_location_hidden_layers == '':
        floor_location_input = common_input
    else:
        # x = Dropout(dropout)(common_input)
        for units in floor_location_hidden_layers[:-1]:
            x = Dense(units)(x)
            x = BatchNormalization()(x)
            x = Activation('relu')(x)
            x = Dropout(dropout)(x)
        x = Dense(floor_location_hidden_layers[-1])(x)
        x = BatchNormalization()(x)
        x = Activation('relu')(x)
        floor_location_input = Dropout(dropout, name='floor_location_input')(x)

    # floor classifier output
    for units in floor_hidden_layers:
        x = Dense(units)(x)
        x = BatchNormalization()(x)
        x = Activation('relu')(x)
        x = Dropout(dropout)(x)
    x = Dense(num_flrs)(x)
    x = BatchNormalization()(x)
    floor_output = Activation('softmax', name='floor_output')(
        x)  # no dropout for an output layer

    # location/position regressor output
    for units in location_hidden_layers:
        x = Dense(units, kernel_initializer='normal')(x)
        x = BatchNormalization()(x)
        x = Activation(REGRESSOR_ACTIVATION)(x)
        x = Dropout(dropout)(x)
    x = Dense(num_coords, kernel_initializer='normal')(x)
    x = BatchNormalization()(x)
    location_output = Activation(REGRESSOR_ACTIVATION,
                                 name='location_output')(x)

    # build and compile a SIMO model
    model = Model(inputs=[input],
                  outputs=[building_output, floor_output, location_output])
    model.compile(optimizer=OPTIMIZER,
                  loss=[
                      'categorical_crossentropy', 'categorical_crossentropy',
                      'mean_squared_error'
                  ],
                  loss_weights={
                      'building_output': building_weight,
                      'floor_output': floor_weight,
                      'location_output': location_weight
                  },
                  metrics={
                      'building_output': 'accuracy',
                      'floor_output': 'accuracy',
                      'location_output': 'mean_squared_error'
                  })

    # train the model
    tensorboard = TensorBoard(log_dir="logs/{}".format(time()),
                              write_graph=True)
    startTime = timer()
    history = model.fit(x={'input': rss_training},
                        y={
                            'building_output': blds_training,
                            'floor_output': flrs_training,
                            'location_output': utm_training
                        },
                        validation_data=({
                            'input': rss_validation
                        }, {
                            'building_output': blds_validation,
                            'floor_output': flrs_validation,
                            'location_output': utm_validation
                        }),
                        batch_size=batch_size,
                        epochs=epochs,
                        verbose=VERBOSE,
                        callbacks=[tensorboard],
                        shuffle=True)
    elapsedTime = timer() - startTime
    print("Model trained in %e s." % elapsedTime)

    ### evaluate the model
    print("\nPart 3: evaluating the model ...")

    # turn the given validation set into a testing set
    rss_testing = np.asarray(testing_df[col_aps], dtype=np.float32)
    rss_testing = (rss_scaler.transform(rss_testing.reshape(
        (-1, 1)))).reshape(rss_testing.shape)
    utm_x_testing = np.asarray(testing_df['LONGITUDE'], dtype=np.float32)
    utm_y_testing = np.asarray(testing_df['LATITUDE'], dtype=np.float32)
    utm_testing_original = np.column_stack((utm_x_testing, utm_y_testing))
    utm_testing = utm_scaler.transform(utm_testing_original)  # scaled version
    blds_testing = blds_all[num_training_samples:]
    flrs_testing = flrs_all[num_training_samples:]

    # rst = model.evaluate(
    #     x={'input': rss_testing},
    #     y={'building_output': blds_testing, 'floor_output': flrs_testing, 'location_output': utm_testing}
    # )

    # Results = namedtuple('Results', ['losses', 'metrics', 'history'])
    # Losses = namedtuple('Losses', ['overall', 'building', 'floor', 'location'])
    # Metrics = namedtuple('Metrics', ['building_acc', 'floor_acc', 'location_mse'])
    # results = Results(
    #     losses=Losses(overall=rst[0], building=rst[1], floor=rst[2], location=rst[3]),
    #     metrics=Metrics(building_acc=rst[4], floor_acc=rst[5], location_mse=rst[6]),
    #     history=history
    # )

    # calculate the classification accuracies and localization errors
    preds = model.predict(rss_testing, batch_size=batch_size
                          )  # a list of arrays (one for each output) returned
    blds_results = (np.equal(np.argmax(blds_testing, axis=1),
                             np.argmax(preds[0], axis=1))).astype(int)
    blds_acc = blds_results.mean()
    flrs_results = (np.equal(np.argmax(flrs_testing, axis=1),
                             np.argmax(preds[1], axis=1))).astype(int)
    flrs_acc = flrs_results.mean()
    bf_acc = (blds_results * flrs_results).mean()
    # rfps_results = (np.equal(np.argmax(test_labels[:, 8:118], axis=1), np.argmax(preds[:, 8:118], axis=1))).astype(int)
    # acc_rfp = rfps_results.mean()
    # acc = (blds_results*flrs_results*rfps_results).mean()
    utm_preds = utm_scaler.inverse_transform(
        preds[2])  # inverse-scaled version
    location_mse = ((utm_testing_original - utm_preds)**2).mean()

    # calculate localization errors per EvAAL/IPIN 2015 competition
    dist = norm(utm_testing_original - utm_preds, axis=1)  # Euclidean distance
    flrs_diff = np.absolute(
        np.argmax(flrs_testing, axis=1) - np.argmax(preds[1], axis=1))
    error = dist + 50 * (1 -
                         blds_results) + 4 * flrs_diff  # individual error [m]
    mean_error = error.mean()
    median_error = np.median(error)

    Results = namedtuple('Results', ['metrics', 'history'])
    Metrics = namedtuple('Metrics', [
        'building_acc', 'floor_acc', 'bf_acc', 'location_mse', 'mean_error',
        'median_error'
    ])
    results = Results(metrics=Metrics(building_acc=blds_acc,
                                      floor_acc=flrs_acc,
                                      bf_acc=bf_acc,
                                      location_mse=location_mse,
                                      mean_error=mean_error,
                                      median_error=median_error),
                      history=history)
    return results
def simo_classification_tut(
        gpu_id: int, dataset: str, frac: float, validation_split: float,
        preprocessor: str, grid_size: float, batch_size: int, epochs: int,
        optimizer: str, dropout: float, corruption_level: float,
        num_neighbors: int, scaling: float, dae_hidden_layers: list,
        sdae_hidden_layers: list, cache: bool, common_hidden_layers: list,
        floor_hidden_layers: list, location_hidden_layers: list,
        floor_weight: float, location_weight: float, verbose: int):
    """Multi-floor indoor localization based on floor and coordinates classification
    using a single-input and multi-output (SIMO) deep neural network (DNN) model
    and TUT datasets.

    Keyword arguments:

    """

    ### initialize numpy, random, TensorFlow, and keras
    np.random.seed()  # based on current time or OS-specific randomness source
    rn.seed()  #  "
    tf.set_random_seed(rn.randint(0, 1000000))
    if gpu_id >= 0:
        os.environ["CUDA_VISIBLE_DEVICES"] = str(gpu_id)
    else:
        os.environ["CUDA_VISIBLE_DEVICES"] = ''
    sess = tf.Session(graph=tf.get_default_graph(), config=session_conf)
    K.set_session(sess)

    ### load datasets after scaling
    print("Loading data ...")
    if dataset == 'tut':
        from tut import TUT
        tut = TUT(cache=cache,
                  frac=frac,
                  preprocessor=preprocessor,
                  classification_mode='hierarchical',
                  grid_size=0)
    elif dataset == 'tut2':
        from tut import TUT2
        tut = TUT2(cache=cache,
                   frac=frac,
                   preprocessor=preprocessor,
                   classification_mode='hierarchical',
                   grid_size=0,
                   testing_split=0.2)
    elif dataset == 'tut3':
        from tut import TUT3
        tut = TUT3(cache=cache,
                   frac=frac,
                   preprocessor=preprocessor,
                   classification_mode='hierarchical',
                   grid_size=0)
    else:
        print("'{0}' is not a supported data set.".format(dataset))
        sys.exit(0)
    flr_height = tut.floor_height
    training_df = tut.training_df
    training_data = tut.training_data
    testing_df = tut.testing_df
    testing_data = tut.testing_data

    ### build and train a SIMO model
    print("Building and training a SIMO model for classification ...")
    rss = training_data.rss_scaled
    coord = training_data.coord_scaled
    coord_scaler = training_data.coord_scaler  # for inverse transform
    labels = training_data.labels
    input = Input(shape=(rss.shape[1], ), name='input')  # common input

    # (optional) build deep autoencoder or stacked denoising autoencoder
    if dae_hidden_layers != '':
        print("- Building a DAE model ...")
        model = deep_autoencoder(dataset=dataset,
                                 input_data=rss,
                                 preprocessor=preprocessor,
                                 hidden_layers=dae_hidden_layers,
                                 cache=cache,
                                 model_fname=None,
                                 optimizer=optimizer,
                                 batch_size=batch_size,
                                 epochs=epochs,
                                 validation_split=validation_split)
        x = model(input)
    elif sdae_hidden_layers != '':
        print("- Building an SDAE model ...")
        model = sdae(dataset=dataset,
                     input_data=rss,
                     preprocessor=preprocessor,
                     hidden_layers=sdae_hidden_layers,
                     cache=cache,
                     model_fname=None,
                     optimizer=optimizer,
                     corruption_level=corruption_level,
                     batch_size=batch_size,
                     epochs=epochs,
                     validation_split=validation_split)
        x = model(input)
    else:
        x = input

    # common hidden layers
    x = BatchNormalization()(x)
    x = Activation('relu')(x)
    x = Dropout(dropout)(x)
    if common_hidden_layers != '':
        for units in common_hidden_layers:
            x = Dense(units)(x)
            x = BatchNormalization()(x)
            x = Activation('relu')(x)
            x = Dropout(dropout)(x)
    common_hl_output = x

    # floor classification output
    if floor_hidden_layers != '':
        for units in floor_hidden_layers:
            x = Dense(units)(x)
            x = BatchNormalization()(x)
            x = Activation('relu')(x)
            x = Dropout(dropout)(x)
    x = Dense(labels.floor.shape[1])(x)
    x = BatchNormalization()(x)
    floor_output = Activation('softmax', name='floor_output')(
        x)  # no dropout for an output layer

    # location classification output
    if location_hidden_layers != '':
        for units in location_hidden_layers:
            x = Dense(units)(x)
            x = BatchNormalization()(x)
            x = Activation('relu')(x)
            x = Dropout(dropout)(x)
    x = Dense(labels.location.shape[1])(x)
    x = BatchNormalization()(x)
    location_output = Activation('softmax', name='location_output')(
        x)  # no dropout for an output layer

    # build model
    model = Model(inputs=input, outputs=[floor_output, location_output])

    # for stage-wise training with floor information only
    model.compile(
        optimizer=optimizer,
        loss=['categorical_crossentropy', 'categorical_crossentropy'],
        loss_weights={
            'floor_output': 1.0,
            'location_output': 0.0
        },
        metrics={
            'floor_output': 'accuracy',
            'location_output': 'accuracy'
        })
    weights_file = os.path.expanduser("~/tmp/best_weights.h5")
    checkpoint = ModelCheckpoint(weights_file,
                                 monitor='val_loss',
                                 save_best_only=True,
                                 verbose=0)
    early_stop = EarlyStopping(monitor='val_loss',
                               min_delta=0,
                               patience=10,
                               verbose=0)

    print("- Stage-wise training with floor information ...", end='')
    startTime = timer()
    f_history = model.fit(x={'input': rss},
                          y={
                              'floor_output': labels.floor,
                              'location_output': labels.location
                          },
                          batch_size=batch_size,
                          epochs=epochs,
                          verbose=verbose,
                          callbacks=[checkpoint, early_stop],
                          validation_split=validation_split,
                          shuffle=True)
    elapsedTime = timer() - startTime
    print(" completed in {0:.4e} s".format(elapsedTime))
    model.load_weights(weights_file)  # load weights from the best model

    # for stage-wise training with both floor and location information
    model.compile(
        optimizer=optimizer,
        loss=['categorical_crossentropy', 'categorical_crossentropy'],
        loss_weights={
            'floor_output': 1.0,
            'location_output': 1.0
        },
        metrics={
            'floor_output': 'accuracy',
            'location_output': 'accuracy'
        })
    weights_file = os.path.expanduser("~/tmp/best_weights.h5")
    checkpoint = ModelCheckpoint(weights_file,
                                 monitor='val_loss',
                                 save_best_only=True,
                                 verbose=0)
    early_stop = EarlyStopping(monitor='val_loss',
                               min_delta=0,
                               patience=10,
                               verbose=0)

    print("- Stage-wise training with both floor and location information ...",
          end='')
    startTime = timer()
    fl_history = model.fit(x={'input': rss},
                           y={
                               'floor_output': labels.floor,
                               'location_output': labels.location
                           },
                           batch_size=batch_size,
                           epochs=epochs,
                           verbose=verbose,
                           callbacks=[checkpoint, early_stop],
                           validation_split=validation_split,
                           shuffle=True)
    elapsedTime = timer() - startTime
    print(" completed in {0:.4e} s".format(elapsedTime))
    model.load_weights(weights_file)  # load weights from the best model

    ### evaluate the model
    print("Evaluating the model ...")
    rss = testing_data.rss_scaled
    labels = testing_data.labels
    blds = labels.building
    flrs = labels.floor
    coord = testing_data.coord  # original coordinates
    x_col_name = 'X'
    y_col_name = 'Y'

    # calculate the classification accuracies and localization errors
    flrs_pred, locs_pred = model.predict(rss, batch_size=batch_size)
    flr_results = (np.equal(np.argmax(flrs, axis=1),
                            np.argmax(flrs_pred, axis=1))).astype(int)
    flr_acc = flr_results.mean()

    # calculate positioning error based on locations
    n_samples = len(flrs)
    n_locs = locs_pred.shape[1]  # number of locations (reference points)
    idxs = np.argpartition(
        locs_pred, -num_neighbors
    )[:,
      -num_neighbors:]  # (unsorted) indexes of up to num_neighbors nearest neighbors
    threshold = scaling * np.amax(locs_pred, axis=1)
    training_labels = np.concatenate(
        (training_data.labels.floor, training_data.labels.location), axis=1)
    training_coord_avg = training_data.coord_avg
    coord_est = np.zeros((n_samples, 2))
    coord_est_weighted = np.zeros((n_samples, 2))
    for i in range(n_samples):
        xs = []
        ys = []
        ws = []
        for j in idxs[i]:
            if locs_pred[i][j] >= threshold[i]:
                loc = np.zeros(n_locs)
                loc[j] = 1
                rows = np.where((training_labels == np.concatenate(
                    (flrs[i], loc))).all(axis=1))  # tuple of row indexes
                if rows[0].size > 0:
                    xs.append(training_df.loc[training_df.index[rows[0][0]],
                                              x_col_name])
                    ys.append(training_df.loc[training_df.index[rows[0][0]],
                                              y_col_name])
                    ws.append(locs_pred[i][j])
        if len(xs) > 0:
            coord_est[i] = np.array((xs, ys)).mean(axis=1)
            coord_est_weighted[i] = np.array(
                (np.average(xs, weights=ws), np.average(ys, weights=ws)))
        else:
            if rows[0].size > 0:
                key = str(np.argmax(blds[i])) + '-' + str(np.argmax(flrs[i]))
            else:
                key = str(np.argmax(blds[i]))
            coord_est[i] = coord_est_weighted[i] = training_coord_avg[key]

    # calculate 2D localization errors
    dist_2d = norm(coord - coord_est, axis=1)
    dist_weighted_2d = norm(coord - coord_est_weighted, axis=1)
    mean_error_2d = dist_2d.mean()
    mean_error_weighted_2d = dist_weighted_2d.mean()
    median_error_2d = np.median(dist_2d)
    median_error_weighted_2d = np.median(dist_weighted_2d)

    # calculate 3D localization errors
    flr_diff = np.absolute(
        np.argmax(flrs, axis=1) - np.argmax(flrs_pred, axis=1))
    z_diff_squared = (flr_height**2) * np.square(flr_diff)
    dist_3d = np.sqrt(
        np.sum(np.square(coord - coord_est), axis=1) + z_diff_squared)
    dist_weighted_3d = np.sqrt(
        np.sum(np.square(coord - coord_est_weighted), axis=1) + z_diff_squared)
    mean_error_3d = dist_3d.mean()
    mean_error_weighted_3d = dist_weighted_3d.mean()
    median_error_3d = np.median(dist_3d)
    median_error_weighted_3d = np.median(dist_weighted_3d)

    LocalizationResults = namedtuple('LocalizationResults', [
        'flr_acc', 'mean_error_2d', 'mean_error_weighted_2d',
        'median_error_2d', 'median_error_weighted_2d', 'mean_error_3d',
        'mean_error_weighted_3d', 'median_error_3d',
        'median_error_weighted_3d', 'elapsedTime'
    ])
    return LocalizationResults(
        flr_acc=flr_acc,
        mean_error_2d=mean_error_2d,
        mean_error_weighted_2d=mean_error_weighted_2d,
        median_error_2d=median_error_2d,
        median_error_weighted_2d=median_error_weighted_2d,
        mean_error_3d=mean_error_3d,
        mean_error_weighted_3d=mean_error_weighted_3d,
        median_error_3d=median_error_3d,
        median_error_weighted_3d=median_error_weighted_3d,
        elapsedTime=elapsedTime)