diff --git a/src/stylish_tts/train/losses.py b/src/stylish_tts/train/losses.py index a9ce029..b3397d9 100644 --- a/src/stylish_tts/train/losses.py +++ b/src/stylish_tts/train/losses.py @@ -583,6 +583,8 @@ def forced_align( input_lengths: Tensor, target_lengths: Tensor, ): + import k2 + supervision_segments, token_ids, indices = self.encode_supervisions( targets, target_lengths, input_lengths ) @@ -602,7 +604,11 @@ def forced_align( best_paths = k2.shortest_path(lattices, use_double_scores=True) frame_scores = best_paths.scores[(best_paths.labels != -1)] - frame_scores = frame_scores.split(input_lengths.tolist()) + # encode_supervisions sorted the batch by length, so best_paths come back + # in that sorted order. Split the scores by the sorted per-segment lengths + # rather than the original input lengths. + sorted_lengths = supervision_segments[:, 2].tolist() + frame_scores = frame_scores.split(sorted_lengths) scores = torch.stack([p.mean() for p in frame_scores]) batch_arc_shape = best_paths.arcs.shape().remove_axis(1) @@ -612,6 +618,13 @@ def forced_align( # k2 makes an extra frame for some reasons for i in range(len(batch_frame_labels)): batch_frame_labels[i][-1] -= 1 + # Restore the original batch order so each segment's labels and score line + # up with the caller's segment. Without this every segment receives another + # segment's alignment. + inverse = torch.empty_like(indices) + inverse[indices] = torch.arange(indices.numel(), device=indices.device) + batch_frame_labels = [batch_frame_labels[j] for j in inverse.tolist()] + scores = scores[inverse] return batch_frame_labels, scores def on_train_epoch_end(self, train):