Repository: huggingface/transformers
Pull request: Fix Audio Classification Pipeline top_k Documentation Mismatch and Bug #35736

## Problem Statement
There were two issues with the audio classification pipeline's `top_k` parameter:
1. Documentation mismatch: The docs stated that when `top_k=None`, all labels should be returned, but this wasn't implemented
2. Runtime error: When `top_k=None` and a model had fewer labels than the default (5), the pipeline would crash

Fixes : #35736 

## Root Cause Analysis
After investigating the implementation, we found:
1. The `__init__` method was unconditionally setting a default `top_k=5`, even when explicitly set to `None`
2. The `_sanitize_parameters` method wasn't properly handling the `None` case, leading to potential crashes when models had fewer labels than the requested/default `top_k`

## Solution
The fix involves:
1. Properly handling `top_k=None` in initialization to respect the user's intent to get all labels
2. Updating `_sanitize_parameters` to use the model's total number of labels when `top_k=None`
4. Maintaining backward compatibility by keeping the default `top_k=5` when not explicitly specified
5. Adding comprehensive tests to verify the behavior

## Implementation Details
Changes were made to:
1. `AudioClassificationPipeline.__init__`: Modified to properly handle `None` value
2. `AudioClassificationPipeline._sanitize_parameters`: Updated to use all labels when `top_k=None`
3. Added new test file `test_audio_classification_top_k.py` with three test cases:
   - Testing `top_k=None` returns all labels
   - Testing behavior with models having fewer labels
   - Testing `top_k` greater than available labels

## Testing
Added comprehensive tests that verify:
1. When `top_k=None`, all labels are returned
2. Models with fewer labels than the default work correctly
3. Large `top_k` values are properly capped to available labels

## Test Results
<img width="598" alt="Screenshot 2025-01-19 at 2 21 30 PM" src="https://github.com/user-attachments/assets/3e698ec1-bf07-4859-a059-e93e51d4ce59" />

All test cases pass successfully, confirming the fix works as intended while maintaining backward compatibility.

**Note About Warning:**
During test execution, we see a deprecation warning: UserWarning: 

`Passing gradient_checkpointing to a config initialization is deprecated and will be removed in v5 Transformers. Using model.gradient_checkpointing_enable() instead... `

This warning is unrelated to our `top_k` changes. It comes from the model configuration initialization and is about the deprecation of setting gradient checkpointing via config. This is a broader change planned for Transformers v5 to move gradient checkpointing configuration from model initialization to explicit method calls. It doesn't affect our audio classification pipeline's functionality or the `top_k` parameter behavior.

the warning would go away by exploring a better model with multiple labels supporting gradient checkpointing even now , but i decided to leave the same model in place for originality purposes as it doesn't affect this PR in any way or form .

**cc:** @Rocketknight1 


Changed files:
filename: src/transformers/pipelines/audio_classification.py
patch: @@ -91,8 +91,11 @@ class AudioClassificationPipeline(Pipeline):
     """
 
     def __init__(self, *args, **kwargs):
-        # Default, might be overriden by the model.config.
-        kwargs["top_k"] = kwargs.get("top_k", 5)
+        # Only set default top_k if explicitly provided
+        if "top_k" in kwargs and kwargs["top_k"] is None:
+            kwargs["top_k"] = None
+        elif "top_k" not in kwargs:
+            kwargs["top_k"] = 5
         super().__init__(*args, **kwargs)
 
         if self.framework != "pt":
@@ -141,12 +144,16 @@ def __call__(
         return super().__call__(inputs, **kwargs)
 
     def _sanitize_parameters(self, top_k=None, function_to_apply=None, **kwargs):
-        # No parameters on this pipeline right now
         postprocess_params = {}
-        if top_k is not None:
+
+        # If top_k is None, use all labels
+        if top_k is None:
+            postprocess_params["top_k"] = self.model.config.num_labels
+        else:
             if top_k > self.model.config.num_labels:
                 top_k = self.model.config.num_labels
             postprocess_params["top_k"] = top_k
+
         if function_to_apply is not None:
             if function_to_apply not in ["softmax", "sigmoid", "none"]:
                 raise ValueError(

filename: tests/test_audio_classification_top_k.py
patch: @@ -0,0 +1,60 @@
+import unittest
+
+import numpy as np
+
+from transformers import pipeline
+from transformers.testing_utils import require_torch
+
+
+@require_torch
+class AudioClassificationTopKTest(unittest.TestCase):
+    def test_top_k_none_returns_all_labels(self):
+        model_name = "superb/wav2vec2-base-superb-ks"  # model with more than 5 labels
+        classification_pipeline = pipeline(
+            "audio-classification",
+            model=model_name,
+            top_k=None,
+        )
+
+        # Create dummy input
+        sampling_rate = 16000
+        signal = np.zeros((sampling_rate,), dtype=np.float32)
+
+        result = classification_pipeline(signal)
+        num_labels = classification_pipeline.model.config.num_labels
+
+        self.assertEqual(len(result), num_labels, "Should return all labels when top_k is None")
+
+    def test_top_k_none_with_few_labels(self):
+        model_name = "superb/hubert-base-superb-er"  # model with fewer labels
+        classification_pipeline = pipeline(
+            "audio-classification",
+            model=model_name,
+            top_k=None,
+        )
+
+        # Create dummy input
+        sampling_rate = 16000
+        signal = np.zeros((sampling_rate,), dtype=np.float32)
+
+        result = classification_pipeline(signal)
+        num_labels = classification_pipeline.model.config.num_labels
+
+        self.assertEqual(len(result), num_labels, "Should handle models with fewer labels correctly")
+
+    def test_top_k_greater_than_labels(self):
+        model_name = "superb/hubert-base-superb-er"
+        classification_pipeline = pipeline(
+            "audio-classification",
+            model=model_name,
+            top_k=100,  # intentionally large number
+        )
+
+        # Create dummy input
+        sampling_rate = 16000
+        signal = np.zeros((sampling_rate,), dtype=np.float32)
+
+        result = classification_pipeline(signal)
+        num_labels = classification_pipeline.model.config.num_labels
+
+        self.assertEqual(len(result), num_labels, "Should cap top_k to number of labels")