def train_with_hdf5(data, num_epochs, train_fn, test_fn,
        tr_transform = lambda x:x,
        te_transform = lambda x:x,
        verbose=True, train_shuffle=True,
        save_best_params_to=None,
        last_layer=None,
        max_per_epoch=-1):
    tr_stream = data.streamer(training=True, shuffled=train_shuffle)
    te_stream = data.streamer(training=False, shuffled=False)
    from tqdm import tqdm
    ret = []
    mve_params = None
    mve = None
    best_epoch = 0;
    for epoch in range(num_epochs):
        start = time.time()
        tr_err, tr_batches = 0,0
        for imb in tqdm(tr_stream.get_epoch_iterator(), total=data.ntrain/data.batch_size):
            if imb[0].shape[0] != data.batch_size:
                continue
            imb = tr_transform(imb)
            if not isinstance(imb, tuple):
                imb = (imb,)
            tr_err += train_fn(*imb)
            tr_batches += 1
            if max_per_epoch > 0 and tr_batches > max_per_epoch:
                break
        tr_err /= tr_batches

        val_err, val_batches = 0,0
        if te_stream is not None:
            for imb in tqdm(te_stream.get_epoch_iterator(), total=data.ntest/data.batch_size):
                if imb[0].shape[0] != data.batch_size:
                    continue
                imb = te_transform(imb)
                if not isinstance(imb, tuple):
                    imb = (imb,)
                val_err += test_fn(*imb)
                val_batches += 1
                if max_per_epoch > 0 and val_batches > max_per_epoch:
                    break
            val_err /= val_batches
            if save_best_params_to is not None:
                if mve is None or val_err < mve:
                    mve = val_err
                    mve_params = [np.copy(p) for p in (nn.layers.get_all_param_values(last_layer))]
                    best_epoch = epoch;
        if verbose:
            print('ep {}/{} - tl {:.5f} - vl {:.5f} - t {:.3f}s'.format(
                epoch, num_epochs, tr_err, val_err, time.time()-start))
        ret.append((tr_err, val_err))

    if mve_params is None:
        best_epoch = epoch;
        mve_params = [np.copy(p) for p in (nn.layers.get_all_param_values(last_layer))]
    if save_best_params_to is not None:
        print("save best epoch: {:d}".format(best_epoch));
        save_params(mve_params, save_best_params_to)
    return ret,best_epoch

# goes from raw image array (usually uint8) to floatX, square=True crops to
# size of the short edge, center=True crops at center, otherwise crop is
# random
dataset/ETHPy150Open woshialex/diagnose-heart/CNN_A/utils.py/train_with_hdf5/original