From 954db1721b4c4a3f571dc3e7ee5951c029ca212c Mon Sep 17 00:00:00 2001 From: George Armstrong Date: Fri, 13 Dec 2019 12:28:37 -0800 Subject: [PATCH 01/13] TST: add checks for consistent results across different runs to q2 plugin --- songbird/q2/tests/test_method.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/songbird/q2/tests/test_method.py b/songbird/q2/tests/test_method.py index 484a0db..0c554cd 100644 --- a/songbird/q2/tests/test_method.py +++ b/songbird/q2/tests/test_method.py @@ -1,6 +1,7 @@ import qiime2 import unittest import numpy as np +import pandas as pd import tensorflow as tf from songbird.q2._method import multinomial from songbird.util import random_multinomial_model @@ -49,6 +50,31 @@ def test_fit(self): npt.assert_allclose(exp_beta, res_beta.T, atol=0.6, rtol=0.6) self.assertGreater(len(res_stats.to_dataframe().index), 1) + def test_fit_consistency(self): + md = self.md + + md.name = 'sampleid' + md = qiime2.Metadata(md) + + res_beta1, res_stats1, res_biplot1 = multinomial( + table=self.table, metadata=md, + min_sample_count=0, min_feature_count=0, + formula="X", epochs=1000) + + res_beta2, res_stats2, res_biplot2 = multinomial( + table=self.table, metadata=md, + min_sample_count=0, min_feature_count=0, + formula="X", epochs=1000) + + npt.assert_array_equal(res_beta1, res_beta2) + end_res_stats1 = res_stats1.to_dataframe().iloc[-1] + end_res_stats2 = res_stats2.to_dataframe().iloc[-1] + npt.assert_array_equal(end_res_stats1, end_res_stats2) + npt.assert_array_equal(res_biplot1.eigvals, res_biplot2.eigvals) + npt.assert_array_equal(res_biplot1.samples, res_biplot2.samples) + npt.assert_array_equal(res_biplot1.features, res_biplot2.features) + + if __name__ == "__main__": unittest.main() From d801861103a69234976bfccf3f74068427ccc7e2 Mon Sep 17 00:00:00 2001 From: George Armstrong Date: Fri, 13 Dec 2019 12:29:23 -0800 Subject: [PATCH 02/13] ENH: set a fixed seed for np and tf in q2 plugin --- songbird/q2/_method.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/songbird/q2/_method.py b/songbird/q2/_method.py index b98a3d6..adb01f4 100644 --- a/songbird/q2/_method.py +++ b/songbird/q2/_method.py @@ -43,7 +43,8 @@ def multinomial(table: biom.Table, # split up training and testing trainX, testX, trainY, testY = split_training( dense_table, metadata, design, - training_column, num_random_test_examples + training_column, num_random_test_examples, + seed=0, ) model = MultRegression(learning_rate=learning_rate, clipnorm=clipnorm, @@ -51,6 +52,7 @@ def multinomial(table: biom.Table, batch_size=batch_size, save_path=None) with tf.Graph().as_default(), tf.Session() as session: + tf.set_random_seed(0) model(session, trainX, trainY, testX, testY) loss, cv, its = model.fit( From 279e595f477dca7320cc7ab0b00d9c24a4c0d4ce Mon Sep 17 00:00:00 2001 From: George Armstrong Date: Fri, 13 Dec 2019 12:29:49 -0800 Subject: [PATCH 03/13] ENH: add ability to set fixed seed to train/test split --- songbird/util.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/songbird/util.py b/songbird/util.py index 5fa03ba..6ece16e 100644 --- a/songbird/util.py +++ b/songbird/util.py @@ -176,9 +176,10 @@ def design_filter(val, id_, md): def split_training(dense_table, metadata, design, training_column=None, - num_random_test_examples=10): + num_random_test_examples=10, seed=None): if training_column is None: + np.random.seed(seed) idx = np.random.random(design.shape[0]) i = np.argsort(idx)[num_random_test_examples] From c42c49cf46266c3fc3dee9290d85be31d982052b Mon Sep 17 00:00:00 2001 From: George Armstrong Date: Fri, 13 Dec 2019 12:30:26 -0800 Subject: [PATCH 04/13] ENH: add ability to set np and tf seeds in standalone --- scripts/songbird | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/scripts/songbird b/scripts/songbird index 656bfcd..5c7f60c 100644 --- a/scripts/songbird +++ b/scripts/songbird @@ -99,6 +99,18 @@ def songbird(): show_default=True, help=DESCS["summary-dir"], ) +@click.option( + "--split-seed", + default=DEFAULTS["split-seed"], + show_default=True, + help=DESCS["split-seed"] +) +@click.option( + "--tf-seed", + default=DEFAULTS["tf-seed"], + show_default=True, + help=DESCS["tf-seed"] +) def multinomial( input_biom, metadata_file, @@ -115,6 +127,8 @@ def multinomial( checkpoint_interval, summary_interval, summary_dir, + split_seed, + tf_seed, ): # load metadata and tables metadata = read_metadata(metadata_file) @@ -138,17 +152,21 @@ def multinomial( 'learning_rate': learning_rate, 'min_sample_count': min_sample_count, 'min_feature_count': min_feature_count, + 'split_seed': split_seed, + 'tf_seed': tf_seed, } + # split up training and testing trainX, testX, trainY, testY = split_training( dense_table, metadata, design, training_column, num_random_test_examples, + seed=split_seed, ) - # split up training and testing + # initialize and train the model model = MultRegression( learning_rate=learning_rate, clipnorm=clipnorm, @@ -157,6 +175,10 @@ def multinomial( save_path=summary_dir, ) with tf.Graph().as_default(), tf.Session() as session: + # set the tf random seed + if tf_seed is not None: + tf.set_random_seed(int(tf_seed)) + model(session, trainX, trainY, testX, testY) model.fit( From 76938563e127193a8b3b02a1ab2728df3a098987 Mon Sep 17 00:00:00 2001 From: George Armstrong Date: Fri, 13 Dec 2019 12:31:10 -0800 Subject: [PATCH 05/13] MAINT: update parameter info with defaults and descriptoins for seeds --- songbird/parameter_info.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/songbird/parameter_info.py b/songbird/parameter_info.py index 3441321..541aa45 100644 --- a/songbird/parameter_info.py +++ b/songbird/parameter_info.py @@ -45,6 +45,13 @@ 'to summaries that can be loaded into Tensorboard and ' 'checkpoints for recovering parameters during runtime.' ), + "split-seed": ( + 'The number to use as a seed for splitting data into training and ' + 'test sets.' + ), + "tf-seed": ( + 'The number to use as a random seed in TensorFlow.' + ), } DEFAULTS = { @@ -60,4 +67,6 @@ "checkpoint-interval": 3600, "summary-interval": 10, "summary-dir": "summarydir", + "split-seed": 0, + "tf-seed": None, } From 24d447e61d435159b53fb429999e5d3db32c32fc Mon Sep 17 00:00:00 2001 From: George Armstrong Date: Fri, 13 Dec 2019 12:31:38 -0800 Subject: [PATCH 06/13] TST: test ability to use seed parameters --- scripts/test_songbird_cli.py | 68 +++++++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/scripts/test_songbird_cli.py b/scripts/test_songbird_cli.py index 09d36a6..3219314 100644 --- a/scripts/test_songbird_cli.py +++ b/scripts/test_songbird_cli.py @@ -22,7 +22,7 @@ def setUp(self) -> None: def tearDown(self) -> None: shutil.rmtree(self.path) - def test_cli(self): + def test_cli_no_seed_set(self): runner = CliRunner() test_args = ['--input-biom', 'data/redsea/redsea.biom', '--metadata-file', 'data/redsea/redsea_metadata.txt', @@ -42,6 +42,72 @@ def test_cli(self): error = Exception('Command failed with non-zero exit code') raise error .with_traceback(ex.__traceback__) + def test_cli_set_split_seed_int(self): + runner = CliRunner() + test_args = ['--input-biom', 'data/redsea/redsea.biom', + '--metadata-file', 'data/redsea/redsea_metadata.txt', + '--formula', + 'Depth+Temperature+Salinity+Oxygen+Fluorescence' + '+Nitrate', + '--epochs', '100', + '--differential-prior', '0.5', + '--summary-interval', '1', + '--summary-dir', self.path, + '--split-seed', 42, + ] + + result = runner.invoke(songbird.multinomial, test_args) + try: + self.assertEqual(0, result.exit_code) + except AssertionError: + ex = result.exception + error = Exception('Command failed with non-zero exit code') + raise error .with_traceback(ex.__traceback__) + + def test_cli_set_split_seed_None(self): + runner = CliRunner() + test_args = ['--input-biom', 'data/redsea/redsea.biom', + '--metadata-file', 'data/redsea/redsea_metadata.txt', + '--formula', + 'Depth+Temperature+Salinity+Oxygen+Fluorescence' + '+Nitrate', + '--epochs', '100', + '--differential-prior', '0.5', + '--summary-interval', '1', + '--summary-dir', self.path, + '--split-seed', None, + ] + + result = runner.invoke(songbird.multinomial, test_args) + try: + self.assertEqual(0, result.exit_code) + except AssertionError: + ex = result.exception + error = Exception('Command failed with non-zero exit code') + raise error .with_traceback(ex.__traceback__) + + def test_cli_set_tf_seed_int(self): + runner = CliRunner() + test_args = ['--input-biom', 'data/redsea/redsea.biom', + '--metadata-file', 'data/redsea/redsea_metadata.txt', + '--formula', + 'Depth+Temperature+Salinity+Oxygen+Fluorescence' + '+Nitrate', + '--epochs', '100', + '--differential-prior', '0.5', + '--summary-interval', '1', + '--summary-dir', self.path, + '--tf-seed', 42, + ] + + result = runner.invoke(songbird.multinomial, test_args) + try: + self.assertEqual(0, result.exit_code) + except AssertionError: + ex = result.exception + error = Exception('Command failed with non-zero exit code') + raise error .with_traceback(ex.__traceback__) + if __name__ == '__main__': unittest.main() From 8f50ccdd32c3698c3103d175fbde7dee1fcc7208 Mon Sep 17 00:00:00 2001 From: George Armstrong Date: Mon, 16 Dec 2019 14:11:10 -0500 Subject: [PATCH 07/13] DEV: add message for random seeds to changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41bc182..2c01fbb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # songbird changelog ## Version 1.0.2-dev +Added ability to set random seed for CLI and sets fixed random seeds for qiime2 []() + Correcting matching between metadata and biom table and clarifying the min-feature-count parameter [#99](https://github.com/biocore/songbird/pull/99) Added Tensorboard's HParams functionality to standalone [#95](https://github.com/biocore/songbird/pull/95) From c291a6b0d3f5444838fe7b921982cc30bfa41ceb Mon Sep 17 00:00:00 2001 From: George Armstrong Date: Mon, 16 Dec 2019 14:12:18 -0500 Subject: [PATCH 08/13] MAINT: remove extra whitespace --- songbird/q2/tests/test_method.py | 1 - 1 file changed, 1 deletion(-) diff --git a/songbird/q2/tests/test_method.py b/songbird/q2/tests/test_method.py index 0c554cd..9e8cd2d 100644 --- a/songbird/q2/tests/test_method.py +++ b/songbird/q2/tests/test_method.py @@ -75,6 +75,5 @@ def test_fit_consistency(self): npt.assert_array_equal(res_biplot1.features, res_biplot2.features) - if __name__ == "__main__": unittest.main() From 290002f6905ca9829c95c9bdbe0f09a83f503d68 Mon Sep 17 00:00:00 2001 From: George Armstrong Date: Mon, 16 Dec 2019 14:13:05 -0500 Subject: [PATCH 09/13] MAINT: remove unneccesary import --- songbird/q2/tests/test_method.py | 1 - 1 file changed, 1 deletion(-) diff --git a/songbird/q2/tests/test_method.py b/songbird/q2/tests/test_method.py index 9e8cd2d..fb13407 100644 --- a/songbird/q2/tests/test_method.py +++ b/songbird/q2/tests/test_method.py @@ -1,7 +1,6 @@ import qiime2 import unittest import numpy as np -import pandas as pd import tensorflow as tf from songbird.q2._method import multinomial from songbird.util import random_multinomial_model From f976f8aad9d4dc2046685704b04acc5fcb508bf8 Mon Sep 17 00:00:00 2001 From: George Armstrong Date: Mon, 16 Dec 2019 15:00:38 -0500 Subject: [PATCH 10/13] BUG: fix error with adding None value to hparams --- scripts/songbird | 10 ++++++++-- scripts/test_songbird_cli.py | 24 ++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/scripts/songbird b/scripts/songbird index 5c7f60c..2c62962 100644 --- a/scripts/songbird +++ b/scripts/songbird @@ -152,9 +152,15 @@ def multinomial( 'learning_rate': learning_rate, 'min_sample_count': min_sample_count, 'min_feature_count': min_feature_count, - 'split_seed': split_seed, - 'tf_seed': tf_seed, } + if split_seed is not None: + hparams.update({ + 'split_seed': split_seed, + }) + if tf_seed is not None: + hparams.update({ + 'tf_seed': tf_seed, + }) # split up training and testing trainX, testX, trainY, testY = split_training( diff --git a/scripts/test_songbird_cli.py b/scripts/test_songbird_cli.py index 3219314..762b1cb 100644 --- a/scripts/test_songbird_cli.py +++ b/scripts/test_songbird_cli.py @@ -97,6 +97,7 @@ def test_cli_set_tf_seed_int(self): '--differential-prior', '0.5', '--summary-interval', '1', '--summary-dir', self.path, + '--split-seed', None, '--tf-seed', 42, ] @@ -108,6 +109,29 @@ def test_cli_set_tf_seed_int(self): error = Exception('Command failed with non-zero exit code') raise error .with_traceback(ex.__traceback__) + def test_cli_set_split_seed_tf_seed_int(self): + runner = CliRunner() + test_args = ['--input-biom', 'data/redsea/redsea.biom', + '--metadata-file', 'data/redsea/redsea_metadata.txt', + '--formula', + 'Depth+Temperature+Salinity+Oxygen+Fluorescence' + '+Nitrate', + '--epochs', '100', + '--differential-prior', '0.5', + '--summary-interval', '1', + '--summary-dir', self.path, + '--tf-seed', 42, + '--split-seed', 42, + ] + + result = runner.invoke(songbird.multinomial, test_args) + try: + self.assertEqual(0, result.exit_code) + except AssertionError: + ex = result.exception + error = Exception('Command failed with non-zero exit code') + raise error .with_traceback(ex.__traceback__) + if __name__ == '__main__': unittest.main() From fe89c5d0ced1e79afa908dc5e11164f33bb4e226 Mon Sep 17 00:00:00 2001 From: George Armstrong Date: Tue, 17 Dec 2019 12:29:32 -0500 Subject: [PATCH 11/13] MAINT: refactored to use only one seed and remove type conversion --- scripts/songbird | 30 +++++++------------- scripts/test_songbird_cli.py | 54 +++--------------------------------- songbird/parameter_info.py | 12 +++----- 3 files changed, 18 insertions(+), 78 deletions(-) diff --git a/scripts/songbird b/scripts/songbird index 2c62962..6dd5f9c 100644 --- a/scripts/songbird +++ b/scripts/songbird @@ -100,16 +100,11 @@ def songbird(): help=DESCS["summary-dir"], ) @click.option( - "--split-seed", - default=DEFAULTS["split-seed"], + "--random-seed", + default=DEFAULTS["random-seed"], show_default=True, - help=DESCS["split-seed"] -) -@click.option( - "--tf-seed", - default=DEFAULTS["tf-seed"], - show_default=True, - help=DESCS["tf-seed"] + help=DESCS["random-seed"], + type=int, ) def multinomial( input_biom, @@ -127,8 +122,7 @@ def multinomial( checkpoint_interval, summary_interval, summary_dir, - split_seed, - tf_seed, + random_seed, ): # load metadata and tables metadata = read_metadata(metadata_file) @@ -153,13 +147,9 @@ def multinomial( 'min_sample_count': min_sample_count, 'min_feature_count': min_feature_count, } - if split_seed is not None: - hparams.update({ - 'split_seed': split_seed, - }) - if tf_seed is not None: + if random_seed is not None: hparams.update({ - 'tf_seed': tf_seed, + 'random_seed': random_seed, }) # split up training and testing @@ -169,7 +159,7 @@ def multinomial( design, training_column, num_random_test_examples, - seed=split_seed, + seed=random_seed, ) # initialize and train the model @@ -182,8 +172,8 @@ def multinomial( ) with tf.Graph().as_default(), tf.Session() as session: # set the tf random seed - if tf_seed is not None: - tf.set_random_seed(int(tf_seed)) + if random_seed is not None: + tf.set_random_seed(random_seed) model(session, trainX, trainY, testX, testY) diff --git a/scripts/test_songbird_cli.py b/scripts/test_songbird_cli.py index 762b1cb..94c720c 100644 --- a/scripts/test_songbird_cli.py +++ b/scripts/test_songbird_cli.py @@ -42,7 +42,7 @@ def test_cli_no_seed_set(self): error = Exception('Command failed with non-zero exit code') raise error .with_traceback(ex.__traceback__) - def test_cli_set_split_seed_int(self): + def test_cli_set_set_seed_int(self): runner = CliRunner() test_args = ['--input-biom', 'data/redsea/redsea.biom', '--metadata-file', 'data/redsea/redsea_metadata.txt', @@ -53,7 +53,7 @@ def test_cli_set_split_seed_int(self): '--differential-prior', '0.5', '--summary-interval', '1', '--summary-dir', self.path, - '--split-seed', 42, + '--random-seed', 42, ] result = runner.invoke(songbird.multinomial, test_args) @@ -64,7 +64,7 @@ def test_cli_set_split_seed_int(self): error = Exception('Command failed with non-zero exit code') raise error .with_traceback(ex.__traceback__) - def test_cli_set_split_seed_None(self): + def test_cli_set_random_seed_None(self): runner = CliRunner() test_args = ['--input-biom', 'data/redsea/redsea.biom', '--metadata-file', 'data/redsea/redsea_metadata.txt', @@ -75,53 +75,7 @@ def test_cli_set_split_seed_None(self): '--differential-prior', '0.5', '--summary-interval', '1', '--summary-dir', self.path, - '--split-seed', None, - ] - - result = runner.invoke(songbird.multinomial, test_args) - try: - self.assertEqual(0, result.exit_code) - except AssertionError: - ex = result.exception - error = Exception('Command failed with non-zero exit code') - raise error .with_traceback(ex.__traceback__) - - def test_cli_set_tf_seed_int(self): - runner = CliRunner() - test_args = ['--input-biom', 'data/redsea/redsea.biom', - '--metadata-file', 'data/redsea/redsea_metadata.txt', - '--formula', - 'Depth+Temperature+Salinity+Oxygen+Fluorescence' - '+Nitrate', - '--epochs', '100', - '--differential-prior', '0.5', - '--summary-interval', '1', - '--summary-dir', self.path, - '--split-seed', None, - '--tf-seed', 42, - ] - - result = runner.invoke(songbird.multinomial, test_args) - try: - self.assertEqual(0, result.exit_code) - except AssertionError: - ex = result.exception - error = Exception('Command failed with non-zero exit code') - raise error .with_traceback(ex.__traceback__) - - def test_cli_set_split_seed_tf_seed_int(self): - runner = CliRunner() - test_args = ['--input-biom', 'data/redsea/redsea.biom', - '--metadata-file', 'data/redsea/redsea_metadata.txt', - '--formula', - 'Depth+Temperature+Salinity+Oxygen+Fluorescence' - '+Nitrate', - '--epochs', '100', - '--differential-prior', '0.5', - '--summary-interval', '1', - '--summary-dir', self.path, - '--tf-seed', 42, - '--split-seed', 42, + '--random-seed', None, ] result = runner.invoke(songbird.multinomial, test_args) diff --git a/songbird/parameter_info.py b/songbird/parameter_info.py index 541aa45..c55d38b 100644 --- a/songbird/parameter_info.py +++ b/songbird/parameter_info.py @@ -45,12 +45,9 @@ 'to summaries that can be loaded into Tensorboard and ' 'checkpoints for recovering parameters during runtime.' ), - "split-seed": ( - 'The number to use as a seed for splitting data into training and ' - 'test sets.' - ), - "tf-seed": ( - 'The number to use as a random seed in TensorFlow.' + "random-seed": ( + 'The number to used to receive consistent results for the random ' + 'processes in the fitting procedure.' ), } @@ -67,6 +64,5 @@ "checkpoint-interval": 3600, "summary-interval": 10, "summary-dir": "summarydir", - "split-seed": 0, - "tf-seed": None, + "random-seed": 0, } From 50e22e054504e9d1df8bac8b2ccb9332bfdf793a Mon Sep 17 00:00:00 2001 From: George Armstrong Date: Tue, 7 Jan 2020 17:16:37 -0800 Subject: [PATCH 12/13] MAINT: add PR version and link to changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c01fbb..17242af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # songbird changelog ## Version 1.0.2-dev -Added ability to set random seed for CLI and sets fixed random seeds for qiime2 []() +Added ability to set random seed for CLI and sets fixed random seeds for qiime2 [#101](https://github.com/biocore/songbird/pull/101) Correcting matching between metadata and biom table and clarifying the min-feature-count parameter [#99](https://github.com/biocore/songbird/pull/99) From fbee7e3032026dab4ed27dd8ac1a62fd33b68076 Mon Sep 17 00:00:00 2001 From: George Armstrong Date: Tue, 7 Jan 2020 17:36:47 -0800 Subject: [PATCH 13/13] ENH: expose random_seed to Q2 --- songbird/q2/_method.py | 8 +++++--- songbird/q2/plugin_setup.py | 4 +++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/songbird/q2/_method.py b/songbird/q2/_method.py index adb01f4..fcaffc8 100644 --- a/songbird/q2/_method.py +++ b/songbird/q2/_method.py @@ -25,7 +25,9 @@ def multinomial(table: biom.Table, clipnorm: float = DEFAULTS["clipnorm"], min_sample_count: int = DEFAULTS["min-sample-count"], min_feature_count: int = DEFAULTS["min-feature-count"], - summary_interval: int = DEFAULTS["summary-interval"]) -> ( + summary_interval: int = DEFAULTS["summary-interval"], + random_seed: int = DEFAULTS["random-seed"], + ) -> ( pd.DataFrame, qiime2.Metadata, skbio.OrdinationResults ): @@ -44,7 +46,7 @@ def multinomial(table: biom.Table, trainX, testX, trainY, testY = split_training( dense_table, metadata, design, training_column, num_random_test_examples, - seed=0, + seed=random_seed, ) model = MultRegression(learning_rate=learning_rate, clipnorm=clipnorm, @@ -52,7 +54,7 @@ def multinomial(table: biom.Table, batch_size=batch_size, save_path=None) with tf.Graph().as_default(), tf.Session() as session: - tf.set_random_seed(0) + tf.set_random_seed(random_seed) model(session, trainX, trainY, testX, testY) loss, cv, its = model.fit( diff --git a/songbird/q2/plugin_setup.py b/songbird/q2/plugin_setup.py index 8029dbd..589c01e 100644 --- a/songbird/q2/plugin_setup.py +++ b/songbird/q2/plugin_setup.py @@ -49,7 +49,8 @@ 'clipnorm': Float, 'min_sample_count': Int, 'min_feature_count': Int, - 'summary_interval': Int + 'summary_interval': Int, + 'random_seed': Int, }, outputs=[ ('differentials', FeatureData[Differential]), @@ -79,6 +80,7 @@ "min_sample_count": DESCS["min-sample-count"], "min_feature_count": DESCS["min-feature-count"], "summary_interval": DESCS["summary-interval"], + "random_seed": DESCS["random-seed"], }, name='Multinomial regression', description=("Performs multinomial regression and calculates "