nkululeko 0.86.8__tar.gz → 0.88.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. {nkululeko-0.86.8 → nkululeko-0.88.0}/CHANGELOG.md +8 -0
  2. {nkululeko-0.86.8/nkululeko.egg-info → nkululeko-0.88.0}/PKG-INFO +22 -1
  3. {nkululeko-0.86.8 → nkululeko-0.88.0}/README.md +13 -0
  4. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/constants.py +1 -1
  5. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/data/dataset_csv.py +12 -14
  6. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/demo.py +7 -10
  7. nkululeko-0.88.0/nkululeko/ensemble.py +158 -0
  8. nkululeko-0.88.0/nkululeko/feat_extract/feats_ast.py +118 -0
  9. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/feat_extract/feats_wav2vec2.py +2 -4
  10. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/feat_extract/feats_wavlm.py +7 -4
  11. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/feature_extractor.py +5 -9
  12. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/modelrunner.py +5 -5
  13. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/models/model.py +23 -3
  14. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/models/model_cnn.py +41 -22
  15. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/models/model_mlp.py +37 -17
  16. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/models/model_mlp_regression.py +3 -1
  17. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/plots.py +25 -37
  18. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/reporting/reporter.py +69 -6
  19. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/runmanager.py +8 -11
  20. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/test_predictor.py +2 -9
  21. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/utils/stats.py +11 -7
  22. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/utils/util.py +24 -19
  23. {nkululeko-0.86.8 → nkululeko-0.88.0/nkululeko.egg-info}/PKG-INFO +22 -1
  24. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko.egg-info/SOURCES.txt +2 -0
  25. {nkululeko-0.86.8 → nkululeko-0.88.0}/LICENSE +0 -0
  26. {nkululeko-0.86.8 → nkululeko-0.88.0}/data/aesdd/process_database.py +0 -0
  27. {nkululeko-0.86.8 → nkululeko-0.88.0}/data/androids/process_database.py +0 -0
  28. {nkululeko-0.86.8 → nkululeko-0.88.0}/data/androids_orig/process_database.py +0 -0
  29. {nkululeko-0.86.8 → nkululeko-0.88.0}/data/androids_test/process_database.py +0 -0
  30. {nkululeko-0.86.8 → nkululeko-0.88.0}/data/ased/process_database.py +0 -0
  31. {nkululeko-0.86.8 → nkululeko-0.88.0}/data/asvp-esd/process_database.py +0 -0
  32. {nkululeko-0.86.8 → nkululeko-0.88.0}/data/baved/process_database.py +0 -0
  33. {nkululeko-0.86.8 → nkululeko-0.88.0}/data/cafe/process_database.py +0 -0
  34. {nkululeko-0.86.8 → nkululeko-0.88.0}/data/clac/process_database.py +0 -0
  35. {nkululeko-0.86.8 → nkululeko-0.88.0}/data/cmu-mosei/process_database.py +0 -0
  36. {nkululeko-0.86.8 → nkululeko-0.88.0}/data/demos/process_database.py +0 -0
  37. {nkululeko-0.86.8 → nkululeko-0.88.0}/data/ekorpus/process_database.py +0 -0
  38. {nkululeko-0.86.8 → nkululeko-0.88.0}/data/emns/process_database.py +0 -0
  39. {nkululeko-0.86.8 → nkululeko-0.88.0}/data/emofilm/convert_to_16k.py +0 -0
  40. {nkululeko-0.86.8 → nkululeko-0.88.0}/data/emofilm/process_database.py +0 -0
  41. {nkululeko-0.86.8 → nkululeko-0.88.0}/data/emorynlp/process_database.py +0 -0
  42. {nkululeko-0.86.8 → nkululeko-0.88.0}/data/emov-db/process_database.py +0 -0
  43. {nkululeko-0.86.8 → nkululeko-0.88.0}/data/emovo/process_database.py +0 -0
  44. {nkululeko-0.86.8 → nkululeko-0.88.0}/data/emozionalmente/create.py +0 -0
  45. {nkululeko-0.86.8 → nkululeko-0.88.0}/data/enterface/process_database.py +0 -0
  46. {nkululeko-0.86.8 → nkululeko-0.88.0}/data/esd/process_database.py +0 -0
  47. {nkululeko-0.86.8 → nkululeko-0.88.0}/data/gerparas/process_database.py +0 -0
  48. {nkululeko-0.86.8 → nkululeko-0.88.0}/data/iemocap/process_database.py +0 -0
  49. {nkululeko-0.86.8 → nkululeko-0.88.0}/data/jl/process_database.py +0 -0
  50. {nkululeko-0.86.8 → nkululeko-0.88.0}/data/jtes/process_database.py +0 -0
  51. {nkululeko-0.86.8 → nkululeko-0.88.0}/data/meld/process_database.py +0 -0
  52. {nkululeko-0.86.8 → nkululeko-0.88.0}/data/mesd/process_database.py +0 -0
  53. {nkululeko-0.86.8 → nkululeko-0.88.0}/data/mess/process_database.py +0 -0
  54. {nkululeko-0.86.8 → nkululeko-0.88.0}/data/mlendsnd/process_database.py +0 -0
  55. {nkululeko-0.86.8 → nkululeko-0.88.0}/data/msp-improv/process_database2.py +0 -0
  56. {nkululeko-0.86.8 → nkululeko-0.88.0}/data/msp-podcast/process_database.py +0 -0
  57. {nkululeko-0.86.8 → nkululeko-0.88.0}/data/oreau2/process_database.py +0 -0
  58. {nkululeko-0.86.8 → nkululeko-0.88.0}/data/portuguese/process_database.py +0 -0
  59. {nkululeko-0.86.8 → nkululeko-0.88.0}/data/ravdess/process_database.py +0 -0
  60. {nkululeko-0.86.8 → nkululeko-0.88.0}/data/ravdess/process_database_speaker.py +0 -0
  61. {nkululeko-0.86.8 → nkululeko-0.88.0}/data/savee/process_database.py +0 -0
  62. {nkululeko-0.86.8 → nkululeko-0.88.0}/data/shemo/process_database.py +0 -0
  63. {nkululeko-0.86.8 → nkululeko-0.88.0}/data/subesco/process_database.py +0 -0
  64. {nkululeko-0.86.8 → nkululeko-0.88.0}/data/tess/process_database.py +0 -0
  65. {nkululeko-0.86.8 → nkululeko-0.88.0}/data/thorsten-emotional/process_database.py +0 -0
  66. {nkululeko-0.86.8 → nkululeko-0.88.0}/data/urdu/process_database.py +0 -0
  67. {nkululeko-0.86.8 → nkululeko-0.88.0}/data/vivae/process_database.py +0 -0
  68. {nkululeko-0.86.8 → nkululeko-0.88.0}/docs/source/conf.py +0 -0
  69. {nkululeko-0.86.8 → nkululeko-0.88.0}/meta/demos/demo_best_model.py +0 -0
  70. {nkululeko-0.86.8 → nkululeko-0.88.0}/meta/demos/my_experiment.py +0 -0
  71. {nkululeko-0.86.8 → nkululeko-0.88.0}/meta/demos/my_experiment_local.py +0 -0
  72. {nkululeko-0.86.8 → nkululeko-0.88.0}/meta/demos/plot_faster_anim.py +0 -0
  73. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/__init__.py +0 -0
  74. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/aug_train.py +0 -0
  75. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/augment.py +0 -0
  76. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/augmenting/__init__.py +0 -0
  77. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/augmenting/augmenter.py +0 -0
  78. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/augmenting/randomsplicer.py +0 -0
  79. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/augmenting/randomsplicing.py +0 -0
  80. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/augmenting/resampler.py +0 -0
  81. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/autopredict/__init__.py +0 -0
  82. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/autopredict/ap_age.py +0 -0
  83. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/autopredict/ap_arousal.py +0 -0
  84. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/autopredict/ap_dominance.py +0 -0
  85. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/autopredict/ap_gender.py +0 -0
  86. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/autopredict/ap_mos.py +0 -0
  87. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/autopredict/ap_pesq.py +0 -0
  88. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/autopredict/ap_sdr.py +0 -0
  89. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/autopredict/ap_snr.py +0 -0
  90. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/autopredict/ap_stoi.py +0 -0
  91. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/autopredict/ap_valence.py +0 -0
  92. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/autopredict/estimate_snr.py +0 -0
  93. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/cacheddataset.py +0 -0
  94. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/data/__init__.py +0 -0
  95. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/data/dataset.py +0 -0
  96. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/demo_feats.py +0 -0
  97. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/demo_predictor.py +0 -0
  98. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/experiment.py +0 -0
  99. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/explore.py +0 -0
  100. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/export.py +0 -0
  101. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/feat_extract/__init__.py +0 -0
  102. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/feat_extract/feats_agender.py +0 -0
  103. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/feat_extract/feats_agender_agender.py +0 -0
  104. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/feat_extract/feats_analyser.py +0 -0
  105. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/feat_extract/feats_auddim.py +0 -0
  106. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/feat_extract/feats_audmodel.py +0 -0
  107. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/feat_extract/feats_clap.py +0 -0
  108. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/feat_extract/feats_hubert.py +0 -0
  109. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/feat_extract/feats_import.py +0 -0
  110. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/feat_extract/feats_mld.py +0 -0
  111. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/feat_extract/feats_mos.py +0 -0
  112. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/feat_extract/feats_opensmile.py +0 -0
  113. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/feat_extract/feats_oxbow.py +0 -0
  114. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/feat_extract/feats_praat.py +0 -0
  115. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/feat_extract/feats_snr.py +0 -0
  116. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/feat_extract/feats_spectra.py +0 -0
  117. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/feat_extract/feats_spkrec.py +0 -0
  118. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/feat_extract/feats_squim.py +0 -0
  119. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/feat_extract/feats_trill.py +0 -0
  120. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/feat_extract/feats_whisper.py +0 -0
  121. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/feat_extract/featureset.py +0 -0
  122. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/feat_extract/feinberg_praat.py +0 -0
  123. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/file_checker.py +0 -0
  124. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/filter_data.py +0 -0
  125. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/glob_conf.py +0 -0
  126. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/losses/__init__.py +0 -0
  127. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/losses/loss_ccc.py +0 -0
  128. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/losses/loss_softf1loss.py +0 -0
  129. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/models/__init__.py +0 -0
  130. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/models/model_bayes.py +0 -0
  131. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/models/model_gmm.py +0 -0
  132. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/models/model_knn.py +0 -0
  133. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/models/model_knn_reg.py +0 -0
  134. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/models/model_lin_reg.py +0 -0
  135. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/models/model_svm.py +0 -0
  136. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/models/model_svr.py +0 -0
  137. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/models/model_tree.py +0 -0
  138. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/models/model_tree_reg.py +0 -0
  139. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/models/model_tuned.py +0 -0
  140. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/models/model_xgb.py +0 -0
  141. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/models/model_xgr.py +0 -0
  142. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/multidb.py +0 -0
  143. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/nkuluflag.py +0 -0
  144. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/nkululeko.py +0 -0
  145. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/predict.py +0 -0
  146. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/reporting/__init__.py +0 -0
  147. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/reporting/defines.py +0 -0
  148. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/reporting/latex_writer.py +0 -0
  149. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/reporting/report.py +0 -0
  150. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/reporting/report_item.py +0 -0
  151. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/reporting/result.py +0 -0
  152. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/resample.py +0 -0
  153. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/scaler.py +0 -0
  154. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/segment.py +0 -0
  155. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/segmenting/__init__.py +0 -0
  156. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/segmenting/seg_inaspeechsegmenter.py +0 -0
  157. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/segmenting/seg_silero.py +0 -0
  158. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/syllable_nuclei.py +0 -0
  159. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/test.py +0 -0
  160. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/test_pretrain.py +0 -0
  161. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/utils/__init__.py +0 -0
  162. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko/utils/files.py +0 -0
  163. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko.egg-info/dependency_links.txt +0 -0
  164. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko.egg-info/requires.txt +0 -0
  165. {nkululeko-0.86.8 → nkululeko-0.88.0}/nkululeko.egg-info/top_level.txt +0 -0
  166. {nkululeko-0.86.8 → nkululeko-0.88.0}/pyproject.toml +0 -0
  167. {nkululeko-0.86.8 → nkululeko-0.88.0}/setup.cfg +0 -0
  168. {nkululeko-0.86.8 → nkululeko-0.88.0}/setup.py +0 -0
  169. {nkululeko-0.86.8 → nkululeko-0.88.0}/venv/bin/activate_this.py +0 -0
@@ -1,6 +1,14 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
+ Version 0.88.0
5
+ --------------
6
+ * added ensemble late fusion and AST features
7
+
8
+ Version 0.87.0
9
+ --------------
10
+ * added class probability output and uncertainty analysis
11
+
4
12
  Version 0.86.8
5
13
  --------------
6
14
  * handle single feature sets as strings in the config
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: nkululeko
3
- Version: 0.86.8
3
+ Version: 0.88.0
4
4
  Summary: Machine learning audio prediction experiments based on templates
5
5
  Home-page: https://github.com/felixbur/nkululeko
6
6
  Author: Felix Burkhardt
@@ -51,6 +51,7 @@ Requires-Dist: pylatex
51
51
  - [t-SNE plots](#t-sne-plots)
52
52
  - [Data distribution](#data-distribution)
53
53
  - [Bias checking](#bias-checking)
54
+ - [Uncertainty](#uncertainty)
54
55
  - [Documentation](#documentation)
55
56
  - [Installation](#installation)
56
57
  - [Usage](#usage)
@@ -113,6 +114,13 @@ In cases you might wonder if there's bias in your data. You can try to detect th
113
114
 
114
115
  <img src="meta/images/emotion-pesq.png" width="500px"/>
115
116
 
117
+ ### Uncertainty
118
+ Nkululeko estimates uncertainty of model decision (only for classifiers) with entropy over the class-probabilities or logits per sample.
119
+
120
+ <img src="meta/images/uncertainty.png" width="500px"/>
121
+
122
+
123
+
116
124
  ## Documentation
117
125
  The documentation, along with extensions of installation, usage, INI file format, and examples, can be found [nkululeko.readthedocs.io](https://nkululeko.readthedocs.io).
118
126
 
@@ -193,6 +201,11 @@ Here is an overview of the interfaces/modules:
193
201
  All of them take *--config <my_config.ini>* as an argument.
194
202
 
195
203
  * **nkululeko.nkululeko**: do machine learning experiments combining features and learners
204
+ * **nkululeko.ensemble**: combine several nkululeko experiments and report on late fusion results
205
+ * *configurations*: which experiments to combine
206
+ * *--method* (optional): majority_voting, mean, max, sum
207
+ * *--outfile* (optional): name of CSV file for output
208
+ * *--no_labels* (optional): indicate that no ground truth is given
196
209
  * **nkululeko.multidb**: do [multiple experiments](http://blog.syntheticspeech.de/2024/01/02/nkululeko-compare-several-databases/), comparing several databases cross and in itself
197
210
  * **nkululeko.demo**: [demo the current best model](http://blog.syntheticspeech.de/2022/01/24/nkululeko-try-out-demo-a-trained-model/) on the command line
198
211
  * *--list* (optional) list of input files
@@ -343,6 +356,14 @@ F. Burkhardt, Johannes Wagner, Hagen Wierstorf, Florian Eyben and Björn Schulle
343
356
  Changelog
344
357
  =========
345
358
 
359
+ Version 0.88.0
360
+ --------------
361
+ * added ensemble late fusion and AST features
362
+
363
+ Version 0.87.0
364
+ --------------
365
+ * added class probability output and uncertainty analysis
366
+
346
367
  Version 0.86.8
347
368
  --------------
348
369
  * handle single feature sets as strings in the config
@@ -7,6 +7,7 @@
7
7
  - [t-SNE plots](#t-sne-plots)
8
8
  - [Data distribution](#data-distribution)
9
9
  - [Bias checking](#bias-checking)
10
+ - [Uncertainty](#uncertainty)
10
11
  - [Documentation](#documentation)
11
12
  - [Installation](#installation)
12
13
  - [Usage](#usage)
@@ -69,6 +70,13 @@ In cases you might wonder if there's bias in your data. You can try to detect th
69
70
 
70
71
  <img src="meta/images/emotion-pesq.png" width="500px"/>
71
72
 
73
+ ### Uncertainty
74
+ Nkululeko estimates uncertainty of model decision (only for classifiers) with entropy over the class-probabilities or logits per sample.
75
+
76
+ <img src="meta/images/uncertainty.png" width="500px"/>
77
+
78
+
79
+
72
80
  ## Documentation
73
81
  The documentation, along with extensions of installation, usage, INI file format, and examples, can be found [nkululeko.readthedocs.io](https://nkululeko.readthedocs.io).
74
82
 
@@ -149,6 +157,11 @@ Here is an overview of the interfaces/modules:
149
157
  All of them take *--config <my_config.ini>* as an argument.
150
158
 
151
159
  * **nkululeko.nkululeko**: do machine learning experiments combining features and learners
160
+ * **nkululeko.ensemble**: combine several nkululeko experiments and report on late fusion results
161
+ * *configurations*: which experiments to combine
162
+ * *--method* (optional): majority_voting, mean, max, sum
163
+ * *--outfile* (optional): name of CSV file for output
164
+ * *--no_labels* (optional): indicate that no ground truth is given
152
165
  * **nkululeko.multidb**: do [multiple experiments](http://blog.syntheticspeech.de/2024/01/02/nkululeko-compare-several-databases/), comparing several databases cross and in itself
153
166
  * **nkululeko.demo**: [demo the current best model](http://blog.syntheticspeech.de/2022/01/24/nkululeko-try-out-demo-a-trained-model/) on the command line
154
167
  * *--list* (optional) list of input files
@@ -1,2 +1,2 @@
1
- VERSION="0.86.8"
1
+ VERSION="0.88.0"
2
2
  SAMPLING_RATE = 16000
@@ -23,6 +23,9 @@ class Dataset_CSV(Dataset):
23
23
  root = os.path.dirname(data_file)
24
24
  audio_path = self.util.config_val_data(self.name, "audio_path", "./")
25
25
  df = pd.read_csv(data_file)
26
+ # trim all string values
27
+ df_obj = df.select_dtypes("object")
28
+ df[df_obj.columns] = df_obj.apply(lambda x: x.str.strip())
26
29
  # special treatment for segmented dataframes with only one column:
27
30
  if "start" in df.columns and len(df.columns) == 4:
28
31
  index = audformat.segmented_index(
@@ -49,8 +52,7 @@ class Dataset_CSV(Dataset):
49
52
  .map(lambda x: root + "/" + audio_path + "/" + x)
50
53
  .values
51
54
  )
52
- df = df.set_index(df.index.set_levels(
53
- file_index, level="file"))
55
+ df = df.set_index(df.index.set_levels(file_index, level="file"))
54
56
  else:
55
57
  if not isinstance(df, pd.DataFrame):
56
58
  df = pd.DataFrame(df)
@@ -59,27 +61,24 @@ class Dataset_CSV(Dataset):
59
61
  lambda x: root + "/" + audio_path + "/" + x
60
62
  )
61
63
  )
62
- else: # absolute path is True
64
+ else: # absolute path is True
63
65
  if audformat.index_type(df.index) == "segmented":
64
66
  file_index = (
65
- df.index.levels[0]
66
- .map(lambda x: audio_path + "/" + x)
67
- .values
67
+ df.index.levels[0].map(lambda x: audio_path + "/" + x).values
68
68
  )
69
- df = df.set_index(df.index.set_levels(
70
- file_index, level="file"))
69
+ df = df.set_index(df.index.set_levels(file_index, level="file"))
71
70
  else:
72
71
  if not isinstance(df, pd.DataFrame):
73
72
  df = pd.DataFrame(df)
74
- df = df.set_index(df.index.to_series().apply(
75
- lambda x: audio_path + "/" + x ))
73
+ df = df.set_index(
74
+ df.index.to_series().apply(lambda x: audio_path + "/" + x)
75
+ )
76
76
 
77
77
  self.df = df
78
78
  self.db = None
79
79
  self.got_target = True
80
80
  self.is_labeled = self.got_target
81
- self.start_fresh = eval(
82
- self.util.config_val("DATA", "no_reuse", "False"))
81
+ self.start_fresh = eval(self.util.config_val("DATA", "no_reuse", "False"))
83
82
  is_index = False
84
83
  try:
85
84
  if self.is_labeled and not "class_label" in self.df.columns:
@@ -106,8 +105,7 @@ class Dataset_CSV(Dataset):
106
105
  f" {self.got_gender}, got age: {self.got_age}"
107
106
  )
108
107
  self.util.debug(r_string)
109
- glob_conf.report.add_item(ReportItem(
110
- "Data", "Loaded report", r_string))
108
+ glob_conf.report.add_item(ReportItem("Data", "Loaded report", r_string))
111
109
 
112
110
  def prepare(self):
113
111
  super().prepare()
@@ -20,20 +20,19 @@ Options: \n
20
20
  import argparse
21
21
  import configparser
22
22
  import os
23
+
23
24
  import pandas as pd
25
+ from transformers import pipeline
24
26
 
27
+ import nkululeko.glob_conf as glob_conf
25
28
  from nkululeko.constants import VERSION
26
29
  from nkululeko.experiment import Experiment
27
- import nkululeko.glob_conf as glob_conf
28
30
  from nkululeko.utils.util import Util
29
- from transformers import pipeline
30
31
 
31
32
 
32
33
  def main(src_dir):
33
- parser = argparse.ArgumentParser(
34
- description="Call the nkululeko DEMO framework.")
35
- parser.add_argument("--config", default="exp.ini",
36
- help="The base configuration")
34
+ parser = argparse.ArgumentParser(description="Call the nkululeko DEMO framework.")
35
+ parser.add_argument("--config", default="exp.ini", help="The base configuration")
37
36
  parser.add_argument(
38
37
  "--file", help="A file that should be processed (16kHz mono wav)"
39
38
  )
@@ -84,8 +83,7 @@ def main(src_dir):
84
83
  )
85
84
 
86
85
  def print_pipe(files, outfile):
87
- """
88
- Prints the pipeline output for a list of files, and optionally writes the results to an output file.
86
+ """Prints the pipeline output for a list of files, and optionally writes the results to an output file.
89
87
 
90
88
  Args:
91
89
  files (list): A list of file paths to process through the pipeline.
@@ -108,8 +106,7 @@ def main(src_dir):
108
106
  f.write("\n".join(results))
109
107
 
110
108
  if util.get_model_type() == "finetune":
111
- model_path = os.path.join(
112
- util.get_exp_dir(), "models", "run_0", "torch")
109
+ model_path = os.path.join(util.get_exp_dir(), "models", "run_0", "torch")
113
110
  pipe = pipeline("audio-classification", model=model_path)
114
111
  if args.file is not None:
115
112
  print_pipe([args.file], args.outfile)
@@ -0,0 +1,158 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+
5
+ import configparser
6
+ import time
7
+ from argparse import ArgumentParser
8
+ from pathlib import Path
9
+
10
+ import pandas as pd
11
+
12
+ from nkululeko.constants import VERSION
13
+ from nkululeko.experiment import Experiment
14
+ from nkululeko.utils.util import Util
15
+
16
+
17
+ def ensemble_predictions(config_files, method, no_labels):
18
+ """
19
+ Ensemble predictions from multiple experiments.
20
+
21
+ Args:
22
+ config_files (list): List of configuration file paths.
23
+ method (str): Ensemble method to use. Options are 'majority_voting', 'mean', 'max', or 'sum'.
24
+ no_labels (bool): Flag indicating whether the predictions have labels or not.
25
+
26
+ Returns:
27
+ pandas.DataFrame: The ensemble predictions.
28
+
29
+ Raises:
30
+ ValueError: If an unknown ensemble method is provided.
31
+ AssertionError: If the number of config files is less than 2 for majority voting.
32
+
33
+ """
34
+ ensemble_preds = []
35
+ # labels = []
36
+ for config_file in config_files:
37
+ if no_labels:
38
+ # for ensembling results from Nkululeko.demo
39
+ pred = pd.read_csv(config_file)
40
+ labels = pred.columns[1:-2]
41
+ else:
42
+ # for ensembling results from Nkululeko.nkululeko
43
+ config = configparser.ConfigParser()
44
+ config.read(config_file)
45
+ expr = Experiment(config)
46
+ module = "ensemble"
47
+ expr.set_module(module)
48
+ util = Util(module, has_config=True)
49
+ util.debug(
50
+ f"running {expr.name} from config {config_file}, nkululeko version"
51
+ f" {VERSION}"
52
+ )
53
+
54
+ # get labels
55
+ labels = expr.util.get_labels()
56
+ # load the experiment
57
+ # get CSV files of predictions
58
+ pred = expr.util.get_pred_name()
59
+ print(f"Loading predictions from {pred}")
60
+ preds = pd.read_csv(pred)
61
+
62
+ ensemble_preds.append(preds)
63
+
64
+ # pd concate
65
+ ensemble_preds = pd.concat(ensemble_preds, axis=1)
66
+
67
+ if method == "majority_voting":
68
+ # majority voting, get mode, works for odd number of models
69
+ # raise error when number of configs only two:
70
+ assert (
71
+ len(config_files) > 2
72
+ ), "Majority voting only works for more than two models"
73
+ ensemble_preds["predicted"] = ensemble_preds.mode(axis=1)[0]
74
+
75
+ elif method == "mean":
76
+ for label in labels:
77
+ ensemble_preds[label] = ensemble_preds[label].mean(axis=1)
78
+
79
+ elif method == "max":
80
+ for label in labels:
81
+ ensemble_preds[label] = ensemble_preds[label].max(axis=1)
82
+ # get max value from all labels to inver that labels
83
+
84
+ elif method == "sum":
85
+ for label in labels:
86
+ ensemble_preds[label] = ensemble_preds[label].sum(axis=1)
87
+
88
+ else:
89
+ raise ValueError(f"Unknown ensemble method: {method}")
90
+
91
+ # get the highest value from all labels to inver that labels
92
+ # replace the old first predicted column
93
+ ensemble_preds["predicted"] = ensemble_preds[labels].idxmax(axis=1)
94
+
95
+ if no_labels:
96
+ return ensemble_preds
97
+
98
+ # Drop start, end columns
99
+ ensemble_preds = ensemble_preds.drop(columns=["start", "end"])
100
+
101
+ # Drop other column except until truth
102
+ ensemble_preds = ensemble_preds.iloc[:, : len(labels) + 3]
103
+
104
+ # calculate UAR from predicted and truth columns
105
+
106
+ truth = ensemble_preds["truth"]
107
+ predicted = ensemble_preds["predicted"]
108
+ uar = (truth == predicted).mean()
109
+ Util("ensemble").debug(f"UAR: {uar:.3f}")
110
+
111
+ # only return until 'predicted' column
112
+ return ensemble_preds
113
+
114
+
115
+ def main(src_dir):
116
+ parser = ArgumentParser()
117
+ parser.add_argument(
118
+ "configs",
119
+ nargs="+",
120
+ help="Paths to the configuration files of the experiments to ensemble. \
121
+ Can be INI files for Nkululeko.nkululeo or CSV files from Nkululeko.demo.",
122
+ )
123
+ parser.add_argument(
124
+ "--method",
125
+ default="majority_voting",
126
+ choices=["majority_voting", "mean", "max", "sum"],
127
+ help="Ensemble method to use (default: majority_voting)",
128
+ )
129
+ parser.add_argument(
130
+ "--outfile",
131
+ default="ensemble_result.csv",
132
+ help="Output file path for the ensemble predictions (default: ensemble_predictions.csv)",
133
+ )
134
+
135
+ # add argument if true label is not available
136
+ parser.add_argument(
137
+ "--no_labels",
138
+ action="store_true",
139
+ help="True if true labels are not available. For Nkululeko.demo results.",
140
+ )
141
+
142
+ args = parser.parse_args()
143
+
144
+ start = time.time()
145
+
146
+ ensemble_preds = ensemble_predictions(args.configs, args.method, args.no_labels)
147
+
148
+ # save to csv
149
+ ensemble_preds.to_csv(args.outfile, index=False)
150
+ print(f"Ensemble predictions saved to: {args.outfile}")
151
+ print(f"Ensemble done, used {time.time()-start:.2f} seconds")
152
+
153
+ print("DONE")
154
+
155
+
156
+ if __name__ == "__main__":
157
+ cwd = Path(__file__).parent
158
+ main(cwd)
@@ -0,0 +1,118 @@
1
+ # feats_ast.py
2
+ import os
3
+
4
+ import numpy as np
5
+ import pandas as pd
6
+ import torch
7
+ import torch.nn.functional as F
8
+ import torchaudio
9
+ from tqdm import tqdm
10
+ from transformers import AutoProcessor, ASTModel
11
+
12
+ import nkululeko.glob_conf as glob_conf
13
+ from nkululeko.feat_extract.featureset import Featureset
14
+
15
+
16
+ class Ast(Featureset):
17
+ """Class to extract AST (Audio Spectrogram Transformer) embeddings"""
18
+
19
+ def __init__(self, name, data_df, feat_type):
20
+ super().__init__(name, data_df, feat_type)
21
+ cuda = "cuda" if torch.cuda.is_available() else "cpu"
22
+ self.device = self.util.config_val("MODEL", "device", cuda)
23
+ self.model_initialized = False
24
+ self.feat_type = feat_type
25
+
26
+ def init_model(self):
27
+ self.util.debug("loading AST model...")
28
+ model_path = self.util.config_val(
29
+ "FEATS", "ast.model", "MIT/ast-finetuned-audioset-10-10-0.4593"
30
+ )
31
+ self.processor = AutoProcessor.from_pretrained(model_path)
32
+ self.model = ASTModel.from_pretrained(model_path).to(self.device)
33
+ print(f"initialized AST model on {self.device}")
34
+ self.model.eval()
35
+ self.model_initialized = True
36
+
37
+
38
+ def extract(self):
39
+ """Extract the features or load them from disk if present."""
40
+ store = self.util.get_path("store")
41
+ storage = f"{store}{self.name}.pkl"
42
+ extract = self.util.config_val("FEATS", "needs_feature_extraction", False)
43
+ no_reuse = eval(self.util.config_val("FEATS", "no_reuse", "False"))
44
+ if extract or no_reuse or not os.path.isfile(storage):
45
+ if not self.model_initialized:
46
+ self.init_model()
47
+ self.util.debug("extracting wavlm embeddings, this might take a while...")
48
+ emb_series = pd.Series(index=self.data_df.index, dtype=object)
49
+ length = len(self.data_df.index)
50
+ for idx, (file, start, end) in enumerate(
51
+ tqdm(self.data_df.index.to_list())
52
+ ):
53
+ signal, sampling_rate = torchaudio.load(
54
+ file,
55
+ frame_offset=int(start.total_seconds() * 16000),
56
+ num_frames=int((end - start).total_seconds() * 16000),
57
+ )
58
+ # make mono if stereo
59
+ if signal.shape[0] == 2:
60
+ signal = torch.mean(signal, dim=0, keepdim=True)
61
+
62
+ assert (
63
+ sampling_rate == 16000
64
+ ), f"sampling rate should be 16000 but is {sampling_rate}"
65
+ emb = self.get_embeddings(signal, sampling_rate, file)
66
+ emb_series.iloc[idx] = emb
67
+ self.df = pd.DataFrame(emb_series.values.tolist(), index=self.data_df.index)
68
+ self.df.to_pickle(storage)
69
+ try:
70
+ glob_conf.config["DATA"]["needs_feature_extraction"] = "false"
71
+ except KeyError:
72
+ pass
73
+ else:
74
+ self.util.debug(f"reusing extracted {self.feat_type} embeddings")
75
+ self.df = pd.read_pickle(storage)
76
+ if self.df.isnull().values.any():
77
+ # nanrows = self.df.columns[self.df.isna().any()].tolist()
78
+ # print(nanrows)
79
+ self.util.error(
80
+ f"got nan: {self.df.shape} {self.df.isnull().sum().sum()}"
81
+ )
82
+
83
+
84
+ def get_embeddings(self, signal, sampling_rate, file):
85
+ """Extract embeddings from raw audio signal."""
86
+ try:
87
+ inputs = self.processor(signal.numpy(), sampling_rate=sampling_rate, return_tensors="pt")
88
+
89
+ inputs = {k: v.to(self.device) for k, v in inputs.items()}
90
+
91
+ with torch.no_grad():
92
+ # Get the hidden states
93
+ outputs = self.model(**inputs)
94
+
95
+ # Get the hidden states from the last layer
96
+ last_hidden_state = outputs.last_hidden_state
97
+
98
+ # print(f"last_hidden_state shape: {last_hidden_state.shape}")
99
+ # Average pooling over the time dimension
100
+ embeddings = torch.mean(last_hidden_state, dim=1)
101
+ embeddings = embeddings.cpu().numpy()
102
+
103
+ # convert the same from (768,) to (1, 768)
104
+ # embeddings = embeddings.reshape(1, -1)
105
+ print(f"hs shape: {embeddings.shape}")
106
+
107
+
108
+ except Exception as e:
109
+ self.util.error(f"Error extracting embeddings for file {file}: {str(e)}, fill with")
110
+ return np.zeros(
111
+ self.model.config.hidden_size
112
+ ) # Return zero vector on error
113
+ return embeddings.ravel()
114
+
115
+ def extract_sample(self, signal, sr):
116
+ self.init_model()
117
+ feats = self.get_embeddings(signal, sr, "no file")
118
+ return feats
@@ -47,9 +47,7 @@ class Wav2vec2(Featureset):
47
47
  config.num_hidden_layers = layer_num - hidden_layer
48
48
  self.util.debug(f"using hidden layer #{config.num_hidden_layers}")
49
49
  self.processor = Wav2Vec2FeatureExtractor.from_pretrained(model_path)
50
- self.model = Wav2Vec2Model.from_pretrained(model_path, config=config).to(
51
- self.device
52
- )
50
+ self.model = Wav2Vec2Model.from_pretrained(model_path, config=config).to(self.device)
53
51
  print(f"intialized Wav2vec model on {self.device}")
54
52
  self.model.eval()
55
53
  self.model_initialized = True
@@ -90,7 +88,7 @@ class Wav2vec2(Featureset):
90
88
  self.util.debug("reusing extracted wav2vec2 embeddings")
91
89
  self.df = pd.read_pickle(storage)
92
90
  if self.df.isnull().values.any():
93
- nanrows = self.df.columns[self.df.isna().any()].tolist()
91
+ # nanrows = self.df.columns[self.df.isna().any()].tolist()
94
92
  # print(nanrows)
95
93
  self.util.error(
96
94
  f"got nan: {self.df.shape} {self.df.isnull().sum().sum()}"
@@ -79,8 +79,8 @@ class Wavlm(Featureset):
79
79
  self.util.debug(f"reusing extracted {self.feat_type} embeddings")
80
80
  self.df = pd.read_pickle(storage)
81
81
  if self.df.isnull().values.any():
82
- nanrows = self.df.columns[self.df.isna().any()].tolist()
83
- print(nanrows)
82
+ # nanrows = self.df.columns[self.df.isna().any()].tolist()
83
+ # print(nanrows)
84
84
  self.util.error(
85
85
  f"got nan: {self.df.shape} {self.df.isnull().sum().sum()}"
86
86
  )
@@ -104,11 +104,14 @@ class Wavlm(Featureset):
104
104
  # pool result and convert to numpy
105
105
  y = torch.mean(y, dim=1)
106
106
  y = y.detach().cpu().numpy()
107
+
108
+ # print(f"hs shape: {y.shape}")
109
+
107
110
  except RuntimeError as re:
108
111
  print(str(re))
109
- self.util.error(f"couldn't extract file: {file}")
112
+ self.util.error(f"Couldn't extract file: {file}")
110
113
 
111
- return y.flatten()
114
+ return y.ravel()
112
115
 
113
116
  def extract_sample(self, signal, sr):
114
117
  self.init_model()
@@ -39,12 +39,10 @@ class FeatureExtractor:
39
39
  self.feats = pd.DataFrame()
40
40
  for feats_type in self.feats_types:
41
41
  store_name = f"{self.data_name}_{feats_type}"
42
- self.feat_extractor = self._get_feat_extractor(
43
- store_name, feats_type)
42
+ self.feat_extractor = self._get_feat_extractor(store_name, feats_type)
44
43
  self.feat_extractor.extract()
45
44
  self.feat_extractor.filter()
46
- self.feats = pd.concat(
47
- [self.feats, self.feat_extractor.df], axis=1)
45
+ self.feats = pd.concat([self.feats, self.feat_extractor.df], axis=1)
48
46
  return self.feats
49
47
 
50
48
  def extract_sample(self, signal, sr):
@@ -77,7 +75,7 @@ class FeatureExtractor:
77
75
  return TRILLset
78
76
 
79
77
  elif feats_type.startswith(
80
- ("wav2vec2", "hubert", "wavlm", "spkrec", "whisper")
78
+ ("wav2vec2", "hubert", "wavlm", "spkrec", "whisper", "ast")
81
79
  ):
82
80
  return self._get_feat_extractor_by_prefix(feats_type)
83
81
 
@@ -107,15 +105,13 @@ class FeatureExtractor:
107
105
  prefix, _, ext = feats_type.partition("-")
108
106
  from importlib import import_module
109
107
 
110
- module = import_module(
111
- f"nkululeko.feat_extract.feats_{prefix.lower()}")
108
+ module = import_module(f"nkululeko.feat_extract.feats_{prefix.lower()}")
112
109
  class_name = f"{prefix.capitalize()}"
113
110
  return getattr(module, class_name)
114
111
 
115
112
  def _get_feat_extractor_by_name(self, feats_type):
116
113
  from importlib import import_module
117
114
 
118
- module = import_module(
119
- f"nkululeko.feat_extract.feats_{feats_type.lower()}")
115
+ module = import_module(f"nkululeko.feat_extract.feats_{feats_type.lower()}")
120
116
  class_name = f"{feats_type.capitalize()}Set"
121
117
  return getattr(module, class_name)
@@ -85,7 +85,7 @@ class Modelrunner:
85
85
  f"run: {self.run} epoch: {epoch}: result: {test_score_metric}"
86
86
  )
87
87
  # print(f"performance: {performance.split(' ')[1]}")
88
- performance = float(test_score_metric.split(' ')[1])
88
+ performance = float(test_score_metric.split(" ")[1])
89
89
  if performance > self.best_performance:
90
90
  self.best_performance = performance
91
91
  self.best_epoch = epoch
@@ -204,15 +204,15 @@ class Modelrunner:
204
204
  self.df_train, self.df_test, self.feats_train, self.feats_test
205
205
  )
206
206
  elif model_type == "cnn":
207
- from nkululeko.models.model_cnn import CNN_model
207
+ from nkululeko.models.model_cnn import CNNModel
208
208
 
209
- self.model = CNN_model(
209
+ self.model = CNNModel(
210
210
  self.df_train, self.df_test, self.feats_train, self.feats_test
211
211
  )
212
212
  elif model_type == "mlp":
213
- from nkululeko.models.model_mlp import MLP_model
213
+ from nkululeko.models.model_mlp import MLPModel
214
214
 
215
- self.model = MLP_model(
215
+ self.model = MLPModel(
216
216
  self.df_train, self.df_test, self.feats_train, self.feats_test
217
217
  )
218
218
  elif model_type == "mlp_reg":
@@ -247,8 +247,25 @@ class Model:
247
247
  self.clf.fit(feats, labels)
248
248
 
249
249
  def get_predictions(self):
250
- predictions = self.clf.predict(self.feats_test.to_numpy())
251
- return predictions
250
+ # predictions = self.clf.predict(self.feats_test.to_numpy())
251
+ if self.util.exp_is_classification():
252
+ # make a dataframe for the class probabilities
253
+ proba_d = {}
254
+ for c in self.clf.classes_:
255
+ proba_d[c] = []
256
+ # get the class probabilities
257
+ predictions = self.clf.predict_proba(self.feats_test.to_numpy())
258
+ # pred = self.clf.predict(features)
259
+ for i, c in enumerate(self.clf.classes_):
260
+ proba_d[c] = list(predictions.T[i])
261
+ probas = pd.DataFrame(proba_d)
262
+ probas = probas.set_index(self.feats_test.index)
263
+ predictions = probas.idxmax(axis=1).values
264
+ else:
265
+ predictions = self.clf.predict(self.feats_test.to_numpy())
266
+ probas = None
267
+
268
+ return predictions, probas
252
269
 
253
270
  def predict(self):
254
271
  if self.feats_test.isna().to_numpy().any():
@@ -263,13 +280,16 @@ class Model:
263
280
  )
264
281
  return report
265
282
  """Predict the whole eval feature set"""
266
- predictions = self.get_predictions()
283
+ predictions, probas = self.get_predictions()
284
+
267
285
  report = Reporter(
268
286
  self.df_test[self.target].to_numpy().astype(float),
269
287
  predictions,
270
288
  self.run,
271
289
  self.epoch,
290
+ probas=probas,
272
291
  )
292
+ report.print_probabilities()
273
293
  return report
274
294
 
275
295
  def get_type(self):