Add MOSS-TTSD with XY-Tokenizer#40286
Conversation
|
cc @eustlb |
2eedcef to
0d23e64
Compare
1687d1a to
a2d4c6e
Compare
|
hey @gaoyang07 thanks a bunch for the PR! We'll try to get in an initial review by next week, we should have more bandwidth then! Thanks for your patience 🙏 |
Hey @ebezzam , hope all is well~ Just checking in on this PR — no rush if you’re tied up, but I’d appreciate your review whenever you get a chance. Thanks~ |
|
Hi @gaoyang07 thanks for your message. I started the review and hope to give you some initial comments by the end of the week! |
There was a problem hiding this comment.
Thanks for the PR, a lot of things here that are great to add to Transformers and a very nice model to add to the collection. Thanks for open-sourcing and making the effort to contribute 🙏
I've done a first review to focus on the Tokenizer since MOSS-TTSD will depend on it + iterating on the tokenizer will help you get used to some Transformer conventions for us to then iterate on MOSS-TTSD itself more efficiently!
Some general comments:
- We want to explicitly expose parameters in the configuration. So we want to avoid something like
self.params = kwargsin the configuration. I've tried to point some examples to other codecs that may be useful for you. - We have this tool known as modular to reuse code from other models. As I see from your paper that you make use of Whisper's encoder and various attention modules, we will want to use modular to import/inherit from replicated components (e.g. Whisper, Llama, DAC), and optionally add more parameters to the
initor overwrite methods likeforwardif a slight variation is needed (see this example). The modular script will be used to generatemodeling_xy_tokenizer.py. For the review process, modular also help us to clearly see the new features/architectures that your model is adding to Transformers. - Could you also add a conversion script? Essentially it's the script that will map weights from the original model to the Transformers-compatible one, and upload the model and its configuration to the hub. You can see an example here.
- I was a bit confused by your feature extractor. If your encoders follow the same architecture as Whisper, why can't you directly use Whisper's feature extractor?
- Removing unused features / options / code branch / etc
Hope that helps and let me know if you have any questions!
| return output_lengths | ||
|
|
||
|
|
||
| class XYTokenizerFeatureExtractor(WhisperFeatureExtractor): |
There was a problem hiding this comment.
I haven't dug deep into how your feature extractors differs from WhisperFeatureExtractor, but is there a reason why the WhisperFeatureExtractor can't be used as is?
From what I understand in your paper (Sec 3.1 - Encoder), the semantic and acoustic encoder are initialized to Whisper's encoder, so I would expect the same feature extractor could be used?
And a new model can used an existing feature extractor, by configuring like so.
There was a problem hiding this comment.
We implemented XYTokenizerFeatureExtractor mainly for two reasons:
-
In WhisperFeatureExtractor
, the maximum frequency is fixed at 8000, which works for 16 kHz inputs but may not generalize. Since we plan to support 24 kHz inputs in XY-Tokenizer, we needed more flexibility. -
WhisperFeatureExtractor simply truncates inputs longer than 30s. In contrast, XYTokenizerFeatureExtractor splits them into consecutive 30s chunks and adds configurable overlap (right-sided or two-sided), so we can handle inference on audio longer than 30s.
So while they are very similar, we needed these extensions for XY-Tokenizer.
| input_dim: int = 1280, | ||
| rvq_dim: Optional[int] = None, | ||
| output_dim: Optional[int] = None, | ||
| num_quantizers: int = 32, | ||
| codebook_size: int = 1024, | ||
| codebook_dim: int = 8, | ||
| quantizer_dropout: float = 0.5, | ||
| skip_rvq_ratio: float = 0.0, | ||
| vq_config: VectorQuantizerConfig = None, |
| self.input_proj = WNConv1d(input_dim, rvq_dim, 1) if input_dim != rvq_dim else nn.Identity() | ||
| self.output_proj = WNConv1d(rvq_dim, self.output_dim, 1) if rvq_dim != self.output_dim else nn.Identity() |
There was a problem hiding this comment.
Let's remove weight norm as mentioned above.
| return self.out_project(embeddings) | ||
|
|
||
|
|
||
| class ResidualVQ(nn.Module): |
There was a problem hiding this comment.
Are there similarities with DAC's RVQ? If so, we can simularly use modular to inherit/import from it.
There was a problem hiding this comment.
Thanks for pointing this out! 🙏
There are some similarities, but our quantization method is different from DAC’s. Specifically, as shown in DACVectorQuantize
, DAC normalizes both the input vectors and the codebook before the nearest-neighbor search, while in our implementation we do not apply this normalization step.
a95029f to
2cd6dc0
Compare
|
[For maintainers] Suggested jobs to run (before merge) run-slow: auto, moss_ttsd, xy_tokenizer |
|
View the CircleCI Test Summary for this PR: https://huggingface.co/spaces/transformers-community/circle-ci-viz?pr=40286&sha=8d9481 |
What does this PR do?
MOSS-TTSD 🪐
[Files and versions] [Community]
Overview
MOSS-TTSD (text to spoken dialogue) is an open-source bilingual spoken dialogue synthesis model that supports both Chinese and English. It can transform dialogue scripts between two speakers into natural, expressive conversational speech. MOSS-TTSD supports voice cloning and single-session speech generation of up to 960 seconds, making it ideal for AI podcast production.
Highlights
Before submitting
Pull Request section?
to it if that's the case.
documentation guidelines, and
here are tips on formatting docstrings.
Who can review?
Anyone in the community is free to review the PR once the tests have passed. Feel free to tag
members/contributors who may be interested in your PR.