Skip to content

Critical bugs in train/preprocess.py, train/train.py and PyTorch 2.7+ compatibility issues (Python 3.12 branch) #2847

Description

@LebedevIV

Describe the bug
During the installation and execution of the latest Python 3.12 branch on Windows 10, several critical issues were encountered that completely halt the dataset preprocessing and training workflows. These include IndexError due to mismatched CLI arguments, cyclic imports, lack of mono audio fallback in data_utils.py, and NotImplementedError inside mel_processing.py caused by recent breaking changes in torch.nn.functional.pad under PyTorch 2.7+.

Bug 1: IndexError inside train/preprocess.py

Problem: The webui.py invokes train/preprocess.py with an updated or shorter array of arguments, causing sr = int(sys.argv[2]) to trigger an IndexError: list index out of range and immediately crash the дочерний процесс.
File affected: train/preprocess.py

Bug 2: Circular Import in train/train.py

Problem: When calling python training scripts as standalone tasks or processes, a cyclic dependency chain breaks initialization:
ImportError: cannot import name 'utils' from partially initialized module 'train' (most likely due to a circular import)
File affected: train/train.py / train/init.py

Bug 3: PyTorch 2.7+ 1D Reflection Padding Incompatibility

Problem: In PyTorch 2.7+, torch.nn.functional.pad with mode="reflect" explicitly rejects 1D audio tensors during multi-threaded DataLoader workers inside spectrogram_torch. It throws:
RuntimeError: Only 2D, 3D, 4D, 5D padding with non-constant padding are supported for now
File affected: train/mel_processing.py
Solution provided: We completely removed the unstable torch.nn.functional.pad(..., mode="reflect") for squeezed vectors and rewrote the reflection pad safely via manual tensor flipping and concatenation:

pad_size = int((n_fft - hop_size) / 2)
left_pad = torch.flip(y[1:pad_size + 1], dims=[0])
right_pad = torch.flip(y[-pad_size - 1:-1], dims=[0])
y = torch.cat([left_pad, y, right_pad])
y = y.unsqueeze(0)

Bug 4: Missing Mono-Audio Conversion inside DataLoader

Problem: If a user provides an inadvertently exported Stereo (2-channel) WAV file into the dataset folder, the dataloader worker crashes at the validation stage during batch collating:
RuntimeError: The expanded size of the tensor must match the existing size at non-singleton dimension 1
File affected: train/data_utils.py
Solution suggested: Implement a strict mono-channel channel selector directly into get_audio() right after load_audio(filename, self.sampling_rate):

if len(audio.shape) > 1:
    if audio.shape[0] == 2:
        audio = audio[0]
    elif audio.shape[-1] == 2:
        audio = audio[..., 0]

Bug 5: Missing Import of load_audio and naming typo in data_utils.py

Problem: NameError: name 'load_audio' is not defined occurs inside data_utils.py because it was not imported at the top. Additionally, AttributeError: 'TextAudioLoaderMultiNSFsid' object has no attribute 'target_sampling_rate' happens because the class parameter is actually named self.sampling_rate.

Bug 6: RuntimeError inside commons.slice_segments with ultra-short audio clips

Problem:
If the training dataset contains an ultra-short audio segment (e.g., less than 0.36 seconds / shorter than the framework's internal segment_size), the data iterator crashes during the matrix slicing stage inside infer/module/commons.py. The shape mismatch triggers the following error [Q17]:
RuntimeError: The expanded size of the tensor (960) must match the existing size (480) at non-singleton dimension 1. Target sizes:. Tensor sizes: [480]
File affected: infer/module/commons.py
Solution provided:
Instead of crashing the whole training process or forcing the user to manually hunt down sub-second files in the dataset, we wrapped both slice_segments and slice_segments2 core functions with a safe try-except block and an automatic zero-padding mechanism. If a chunk size is less than segment_size, it dynamically extends the audio array with silence up to the required frame size [Q17].

def slice_segments(x, ids_str, segment_size=4):
    ret = torch.zeros_like(x[:, :, :segment_size])
    for i in range(x.size(0)):
        idx_str = ids_str[i]
        idx_end = idx_str + segment_size
        try:
            chunk = x[i, :, idx_str:idx_end]
            # Safety check: pad short segments with zeros to match target size
            if chunk.size(-1) < segment_size:
                chunk = F.pad(chunk, (0, segment_size - chunk.size(-1)))
            ret[i] = chunk
        except Exception:
            pass
    return ret

def slice_segments2(x, ids_str, segment_size=4):
    ret = torch.zeros_like(x[:, :segment_size])
    for i in range(x.size(0)):
        idx_str = ids_str[i]
        idx_end = idx_str + segment_size
        try:
            chunk = x[i, idx_str:idx_end]
            # Safety check: pad short segments with zeros to match target size
            if chunk.size(-1) < segment_size:
                chunk = F.pad(chunk, (0, segment_size - chunk.size(-1)))
            ret[i] = chunk
        except Exception:
            pass
    return ret


Desktop Configuration:
OS: Windows 10 Pro x64
Python Version: 3.12.3
PyTorch Version: 2.7.1+cu118
GPU: NVIDIA GeForce GTX 1060 3GB

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions