Skip to content

Commit 314b335

Browse files
Restore stable text style conditioning
1 parent 3afe266 commit 314b335

2 files changed

Lines changed: 83 additions & 14 deletions

File tree

src/python/piper_train/vits/lightning.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -144,12 +144,11 @@ def __init__(
144144
self.automatic_optimization = (
145145
False # Multiple optimizers require manual optimization
146146
)
147-
if style_condition_mode not in (None, "global"):
147+
if style_condition_mode not in {"global", "text"}:
148148
raise ValueError(
149-
"Only global style conditioning is supported; "
149+
"style_condition_mode must be either 'global' or 'text', "
150150
f"got {style_condition_mode!r}"
151151
)
152-
style_condition_mode = "global"
153152

154153
# Fix gin_channels BEFORE save_hyperparameters() so the correct value is saved
155154
# This fixes the bug where gin_channels=0 was saved for multi-speaker models
@@ -185,6 +184,7 @@ def __init__(
185184
prosody_dim=self.hparams.prosody_dim,
186185
style_vector_dim=self.hparams.style_vector_dim,
187186
style_condition_dropout=self.hparams.style_condition_dropout,
187+
style_condition_mode=self.hparams.style_condition_mode,
188188
)
189189
self.model_d = MultiPeriodDiscriminator(
190190
use_spectral_norm=self.hparams.use_spectral_norm
@@ -1130,11 +1130,12 @@ def add_model_specific_args(parent_parser):
11301130
)
11311131
parser.add_argument(
11321132
"--style-condition-mode",
1133-
choices=("global",),
1133+
choices=("text", "global"),
11341134
default="global",
11351135
help=(
1136-
"Style-vector injection mode. Only 'global' is supported; "
1137-
"style vectors are added to VITS global conditioning."
1136+
"Where to inject utterance-level style vectors. "
1137+
"'global' adds style to VITS global conditioning; 'text' adds "
1138+
"projected style to the scaled text encoder input."
11381139
),
11391140
)
11401141
parser.add_argument(

src/python/piper_train/vits/models.py

Lines changed: 76 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,8 @@ def __init__(
194194
kernel_size: int,
195195
p_dropout: float,
196196
gin_channels: int = 0,
197+
style_vector_dim: int = 0,
198+
style_condition_dropout: float = 0.0,
197199
):
198200
super().__init__()
199201
self.n_vocab = n_vocab
@@ -205,9 +207,19 @@ def __init__(
205207
self.kernel_size = kernel_size
206208
self.p_dropout = p_dropout
207209
self.gin_channels = gin_channels
210+
self.style_vector_dim = style_vector_dim
211+
self.style_condition_dropout = style_condition_dropout
208212

209213
self.emb = nn.Embedding(n_vocab, hidden_channels)
210214
nn.init.normal_(self.emb.weight, 0.0, hidden_channels**-0.5)
215+
self.style_proj = (
216+
nn.Linear(style_vector_dim, hidden_channels)
217+
if style_vector_dim > 0
218+
else None
219+
)
220+
if self.style_proj is not None:
221+
nn.init.zeros_(self.style_proj.weight)
222+
nn.init.zeros_(self.style_proj.bias)
211223

212224
self.encoder = attentions.Encoder(
213225
hidden_channels, filter_channels, n_heads, n_layers, kernel_size, p_dropout
@@ -217,8 +229,41 @@ def __init__(
217229
if gin_channels != 0:
218230
self.cond_layer = nn.Conv1d(gin_channels, hidden_channels, 1)
219231

220-
def forward(self, x, x_lengths, g=None):
232+
def _style_embedding(self, style_vector, batch_size: int, device, dtype):
233+
if self.style_proj is None:
234+
return None
235+
236+
if style_vector is None:
237+
style_vector = torch.zeros(
238+
batch_size,
239+
self.style_vector_dim,
240+
device=device,
241+
dtype=dtype,
242+
)
243+
244+
proj_weight = next(self.style_proj.parameters())
245+
style_emb = self.style_proj(
246+
style_vector.to(device=proj_weight.device, dtype=proj_weight.dtype)
247+
)
248+
if self.training and self.style_condition_dropout > 0.0:
249+
keep = (
250+
torch.rand(style_emb.size(0), device=style_emb.device)
251+
>= self.style_condition_dropout
252+
).to(style_emb.dtype)
253+
style_emb = style_emb * keep[:, None]
254+
255+
return style_emb.to(device=device, dtype=dtype).unsqueeze(1)
256+
257+
def forward(self, x, x_lengths, g=None, style_vector=None):
221258
x = self.emb(x) * math.sqrt(self.hidden_channels) # [b, t, h]
259+
style_emb = self._style_embedding(
260+
style_vector, batch_size=x.size(0), device=x.device, dtype=x.dtype
261+
)
262+
if style_emb is not None:
263+
# Add text-level style after token embedding scaling. PE-A emotion
264+
# vectors can be intentionally amplified at inference, and scaling
265+
# the projected style by sqrt(hidden_channels) destabilizes duration.
266+
x = x + style_emb
222267
x = torch.transpose(x, 1, -1) # [b, h, t]
223268
x_mask = torch.unsqueeze(
224269
commons.sequence_mask(x_lengths, x.size(2)), 1
@@ -776,8 +821,14 @@ def __init__(
776821
prosody_language_ids: "set[int] | None" = None,
777822
style_vector_dim: int = 0,
778823
style_condition_dropout: float = 0.0,
824+
style_condition_mode: str = "global",
779825
):
780826
super().__init__()
827+
if style_condition_mode not in {"global", "text"}:
828+
raise ValueError(
829+
"style_condition_mode must be either 'global' or 'text', "
830+
f"got {style_condition_mode!r}"
831+
)
781832
self.n_vocab = n_vocab
782833
self.spec_channels = spec_channels
783834
self.inter_channels = inter_channels
@@ -800,6 +851,7 @@ def __init__(
800851
self.prosody_dim = prosody_dim
801852
self.style_vector_dim = style_vector_dim
802853
self.style_condition_dropout = style_condition_dropout
854+
self.style_condition_mode = style_condition_mode
803855
# Language IDs with real prosody features (others are zeroed).
804856
# Default: {0} (JA only). Configurable via prosody_language_ids param.
805857
self.prosody_language_ids: set[int] = (
@@ -818,6 +870,10 @@ def __init__(
818870
kernel_size,
819871
p_dropout,
820872
gin_channels=gin_channels,
873+
style_vector_dim=(
874+
style_vector_dim if style_condition_mode == "text" else 0
875+
),
876+
style_condition_dropout=style_condition_dropout,
821877
)
822878
self.dec = Generator(
823879
inter_channels,
@@ -872,7 +928,7 @@ def __init__(
872928
self.spk_proj = None
873929

874930
self.style_proj = None
875-
if style_vector_dim > 0:
931+
if style_vector_dim > 0 and style_condition_mode == "global":
876932
if gin_channels <= 0:
877933
raise ValueError(
878934
"style_vector_dim > 0 requires gin_channels > 0 so style vectors "
@@ -1008,10 +1064,16 @@ def forward(
10081064
# training (emb_g(sid) is always used). The parameter is reserved for
10091065
# future extensions such as joint speaker-encoder fine-tuning.
10101066
g = self._get_global_conditioning(sid, lid)
1011-
g = self._add_style_condition(
1012-
g, style_vector, batch_size=x.size(0), device=x.device, dtype=x.dtype
1067+
if self.style_condition_mode == "global":
1068+
g = self._add_style_condition(
1069+
g, style_vector, batch_size=x.size(0), device=x.device, dtype=x.dtype
1070+
)
1071+
text_style_vector = None
1072+
else:
1073+
text_style_vector = style_vector
1074+
x, m_p, logs_p, x_mask = self.enc_p(
1075+
x, x_lengths, g=g, style_vector=text_style_vector
10131076
)
1014-
x, m_p, logs_p, x_mask = self.enc_p(x, x_lengths, g=g)
10151077

10161078
z, m_q, logs_q, y_mask = self.enc_q(y, y_lengths, g=g)
10171079
z_p = self.flow(z, y_mask, g=g)
@@ -1157,10 +1219,16 @@ def infer(
11571219
if self.n_speakers > 1:
11581220
assert sid is not None, "Missing speaker id"
11591221
g = self._get_global_conditioning(sid, lid)
1160-
g = self._add_style_condition(
1161-
g, style_vector, batch_size=x.size(0), device=x.device, dtype=x.dtype
1222+
if self.style_condition_mode == "global":
1223+
g = self._add_style_condition(
1224+
g, style_vector, batch_size=x.size(0), device=x.device, dtype=x.dtype
1225+
)
1226+
text_style_vector = None
1227+
else:
1228+
text_style_vector = style_vector
1229+
x, m_p, logs_p, x_mask = self.enc_p(
1230+
x, x_lengths, g=g, style_vector=text_style_vector
11621231
)
1163-
x, m_p, logs_p, x_mask = self.enc_p(x, x_lengths, g=g)
11641232

11651233
# Prepare input for duration predictor with prosody features
11661234
x_dp = self._prepare_prosody_input(x, x_mask, prosody_features, lid=lid)

0 commit comments

Comments
 (0)