//  [python]
def train( net_obj , dataTrain, dataVal, train_op_name, n_epochs, save_addr, visualiseRateTrain=0, visualiseRateVal=0,
           save_epochs=[1000] ):
    """ Function for training a network. Updates the network weights through the training op. The function will check \
        the save address for a model checkpoint to load, otherwise it will begin training from scratch.

    Args:
        net_obj (obj): Network object.
        dataTrain (obj): Iterator object for training data.
        dataVal (obj): Iterator object for validation data.
        train_op_name (string): Name of training op created.
        n_epochs (int): Number of loops through dataset to train for.
        save_addr (str): Address of a directory to save checkpoints for desired epochs, or address of saved checkpoint. \
                        If address is for an epoch and contains a previously saved checkpoint, then the network will \
                        start training from there. Otherwise it will be trained from scratch.
        visualiseRateTrain (int): Epoch rate at which to print training loss in console.
        visualiseRateVal (int): Epoch rate at which to print validation loss in console.
        save_epochs (int list): Epochs to save checkpoints at.
    """

    if np.shape(dataTrain.dataSamples)[1] != net_obj.inputSize:
        raise Exception('the data dimensionality must match the network input size. '
                        'Data size: %d, network input size: %d'%(np.shape(dataTrain.dataSamples)[1], net_obj.inputSize))

    batchSize = dataTrain.batchSize
    numSamples = dataTrain.numSamples

    numIters = numSamples // batchSize
    if (numSamples % batchSize)>0:
        numIters+=1

    with tf.Session() as sess:
        sess.run(tf.initialize_all_variables())
        saver = tf.train.Saver()

        # check if addr has 'epoch' in name and contains a checkpoint
        if exists(join(save_addr,'checkpoint')) & ('epoch' in basename(save_addr)):
            # load a checkpoint
            saver.restore(sess,join(save_addr,'model.ckpt'))
            epoch_start = int((basename(save_addr)).split('_')[-1]) + 1
            save_addr = split(save_addr)[0]
        else:
            # save directory is empty
            epoch_start = 0
            # create network config file in directory
            save_config(net_obj,save_addr)

        for epoch_i in range(epoch_start, n_epochs+1):
            train_error = []
            for batch_i in range(numIters):
                train_batch_x , train_batch_y = dataTrain.next_batch()

                # update weights and biases
                sess.run(net_obj.train_ops['%s_train'%(train_op_name)], feed_dict={net_obj.x: train_batch_x,
                                                                                   net_obj.y_target: train_batch_y})

                # training loss
                if visualiseRateTrain > 0:
                    if epoch_i % visualiseRateTrain == 0:
                        train_error.append( net_obj.train_ops['%s_loss' % (train_op_name)].eval(
                            {net_obj.x: train_batch_x, net_obj.y_target: train_batch_y}) )

                if batch_i == numIters - 1:
                    dataTrain.reset_batch()

            # outputs average batch error
            if visualiseRateTrain > 0:
                if epoch_i % visualiseRateTrain == 0:
                    train_error = np.array(train_error)
                    print("epoch: %d, training loss: %g" % (epoch_i, np.mean(train_error)))




            # iterate over validation samples and output loss
            if visualiseRateVal > 0:
                if epoch_i % visualiseRateVal == 0:

                    val_error = []
                    for batch_i in range(dataVal.numSamples // dataVal.batchSize):
                        val_batch_x, val_batch_y = dataVal.next_batch()

                        val_error.append( net_obj.train_ops['%s_loss' % (train_op_name)].eval(
                                {net_obj.x: val_batch_x, net_obj.y_target: val_batch_y}) )

                        if batch_i == (dataVal.numSamples // dataVal.batchSize)-1:
                            dataVal.reset_batch()

                    val_error = np.array(val_error)
                    print("epoch: %d, validation loss: %g" % (epoch_i, np.mean(val_error)))

            save_model(save_addr,sess,saver,epoch_i,save_epochs)