//  [python]
def _train(self, patches, labels):

        assert len(patches) == len(labels)

        # randomly split data into training and validation set
        num_data = len(patches)
        indices = np.random.permutation(num_data)
        num_train_data = int(num_data / 2)  # do even split
        train_indices = indices[:num_train_data]
        validate_indices = indices[num_train_data:]
        train_posindices = list(filter(lambda idx: labels[idx] == 1, train_indices))
        train_negindices = list(filter(lambda idx: labels[idx] == 0, train_indices))

        input_image_batch, logits, y_, y_p, W_conv0, W_conv1, loss, train_step \
            = self.create_tf_variables(self.param_dict)

        init = tf.global_variables_initializer()
        sess = tf.Session()
        sess.run(init)

        saver = None

        # loop through and compute training/testing loss per epoch
        f1score_per_epoch = []
        loss_per_epoch = []
        for j in range(self.n_epochs):
            print("")

            print("******************** EPOCH %d / %d ********************" % (j, self.n_epochs))

            # train
            train_loss, train_score = self._evaluate_on_patches(
                patches, labels, input_image_batch, loss,
                sess, train_indices, y_, y_p, "train")

            print("")

            # validate
            validate_loss, validate_score = self._evaluate_on_patches(
                patches, labels, input_image_batch, loss,
                sess, validate_indices, y_, y_p, "validate")

            print("")

            print("f1 train %g, f1 validate %g, loss train %g, loss validate %g"
                  % (train_score, validate_score, train_loss, validate_loss))
            f1score_per_epoch.append([train_score, validate_score])
            loss_per_epoch.append([train_loss, validate_loss])

            if self.checkpoints_dir:
                if saver is None:
                    saver = tf.train.Saver(max_to_keep=0)
                outputfile = "%s/model_epoch_%d.ckpt" % (self.checkpoints_dir, j,)
                print("Checkpointing -> %s" % (outputfile,))
                saver.save(sess, outputfile)

            halfbatch = self.batch_size // 2

            # here, we enforce balanced training, so that if we would like to
            # use hinge loss, we will always have representatives from both
            # target classes

            n_iterations = np.min((len(train_posindices) // halfbatch,
                                   len(train_negindices) // halfbatch))

            for i in range(n_iterations):
                # must sort, since h5py needs ordered indices
                poslst = np.sort(train_posindices[i * halfbatch: (i+1) * halfbatch]).tolist()
                neglst = np.sort(train_negindices[i * halfbatch: (i+1) * halfbatch]).tolist()

                X_batch = np.vstack((
                    patches[poslst],
                    patches[neglst],
                ))
                y_batch = np.vstack((
                    as_one_hot(labels[poslst]),
                    as_one_hot(labels[neglst])
                ))

                sys.stdout.write("Training: %d / %d\r" % (i, n_iterations))
                sys.stdout.flush()

                sess.run(train_step, feed_dict={input_image_batch: X_batch, y_: y_batch})

            np.random.shuffle(train_posindices)
            np.random.shuffle(train_negindices)

            print("")

        model = {
            'sess': sess,
            'y_p': y_p,
            'input_image_batch': input_image_batch,
        }

        return model