lit_module.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. import itertools
  2. from dataclasses import dataclass
  3. from typing import Any, Callable, Literal, Optional
  4. import lightning as L
  5. import torch
  6. import torch.nn.functional as F
  7. import wandb
  8. from lightning.pytorch.loggers import TensorBoardLogger, WandbLogger
  9. from matplotlib import pyplot as plt
  10. from torch import nn
  11. from torch.utils.checkpoint import checkpoint as gradient_checkpoint
  12. from fish_speech.models.vqgan.losses import (
  13. MultiResolutionSTFTLoss,
  14. discriminator_loss,
  15. feature_loss,
  16. generator_loss,
  17. )
  18. from fish_speech.models.vqgan.modules.convnext import ConvNeXt
  19. from fish_speech.models.vqgan.modules.encoders import VQEncoder
  20. from fish_speech.models.vqgan.utils import plot_mel, sequence_mask
  21. @dataclass
  22. class VQEncodeResult:
  23. features: torch.Tensor
  24. indices: torch.Tensor
  25. loss: torch.Tensor
  26. feature_lengths: torch.Tensor
  27. @dataclass
  28. class VQDecodeResult:
  29. mels: torch.Tensor
  30. class VQGAN(L.LightningModule):
  31. def __init__(
  32. self,
  33. optimizer: Callable,
  34. lr_scheduler: Callable,
  35. encoder: ConvNeXt,
  36. vq: VQEncoder,
  37. decoder: ConvNeXt,
  38. generator: nn.Module,
  39. discriminator: ConvNeXt,
  40. mel_transform: nn.Module,
  41. hop_length: int = 640,
  42. sample_rate: int = 32000,
  43. freeze_discriminator: bool = False,
  44. ):
  45. super().__init__()
  46. # pretrain: vq use gt mel as target, hifigan use gt mel as input
  47. # finetune: end-to-end training, use gt mel as hifi gan target but freeze vq
  48. # Model parameters
  49. self.optimizer_builder = optimizer
  50. self.lr_scheduler_builder = lr_scheduler
  51. # Generator and discriminator
  52. self.encoder = encoder
  53. self.vq = vq
  54. self.decoder = decoder
  55. self.generator = generator
  56. self.discriminator = discriminator
  57. self.mel_transform = mel_transform
  58. self.freeze_discriminator = freeze_discriminator
  59. # Crop length for saving memory
  60. self.hop_length = hop_length
  61. self.sampling_rate = sample_rate
  62. # Disable automatic optimization
  63. self.automatic_optimization = False
  64. if self.freeze_discriminator:
  65. for p in self.discriminator.parameters():
  66. p.requires_grad = False
  67. # Freeze generator
  68. for p in self.generator.parameters():
  69. p.requires_grad = False
  70. def configure_optimizers(self):
  71. # Need two optimizers and two schedulers
  72. optimizer_generator = self.optimizer_builder(
  73. itertools.chain(
  74. self.encoder.parameters(),
  75. self.vq.parameters(),
  76. self.decoder.parameters(),
  77. )
  78. )
  79. optimizer_discriminator = self.optimizer_builder(
  80. self.discriminator.parameters()
  81. )
  82. lr_scheduler_generator = self.lr_scheduler_builder(optimizer_generator)
  83. lr_scheduler_discriminator = self.lr_scheduler_builder(optimizer_discriminator)
  84. return (
  85. {
  86. "optimizer": optimizer_generator,
  87. "lr_scheduler": {
  88. "scheduler": lr_scheduler_generator,
  89. "interval": "step",
  90. "name": "optimizer/generator",
  91. },
  92. },
  93. {
  94. "optimizer": optimizer_discriminator,
  95. "lr_scheduler": {
  96. "scheduler": lr_scheduler_discriminator,
  97. "interval": "step",
  98. "name": "optimizer/discriminator",
  99. },
  100. },
  101. )
  102. def training_step(self, batch, batch_idx):
  103. optim_g, optim_d = self.optimizers()
  104. audios, audio_lengths = batch["audios"], batch["audio_lengths"]
  105. audios = audios.float()
  106. audios = audios[:, None, :]
  107. with torch.no_grad():
  108. gt_mels = self.mel_transform(audios, sample_rate=self.sampling_rate)
  109. mel_lengths = audio_lengths // self.hop_length
  110. mel_masks = torch.unsqueeze(sequence_mask(mel_lengths, gt_mels.shape[2]), 1).to(
  111. gt_mels.dtype
  112. )
  113. vq_result = self.encode(audios, audio_lengths)
  114. loss_vq = vq_result.loss
  115. if loss_vq.ndim > 1:
  116. loss_vq = loss_vq.mean()
  117. decoded_mels = self.decode(
  118. indices=None,
  119. features=vq_result.features,
  120. audio_lengths=audio_lengths,
  121. ).mels
  122. with torch.no_grad():
  123. with torch.autocast(device_type=audios.device.type, enabled=False):
  124. fake_audios = self.generator(decoded_mels.float())
  125. assert (
  126. audios.shape == fake_audios.shape
  127. ), f"{audios.shape} != {fake_audios.shape}"
  128. # Discriminator
  129. if self.freeze_discriminator is False:
  130. scores = self.discriminator(gt_mels)
  131. score_fakes = self.discriminator(decoded_mels.detach())
  132. with torch.autocast(device_type=audios.device.type, enabled=False):
  133. loss_disc, _, _ = discriminator_loss([scores], [score_fakes])
  134. self.log(
  135. f"train/discriminator/loss",
  136. loss_disc,
  137. on_step=True,
  138. on_epoch=False,
  139. prog_bar=False,
  140. logger=True,
  141. sync_dist=True,
  142. )
  143. optim_d.zero_grad()
  144. self.manual_backward(loss_disc)
  145. self.clip_gradients(
  146. optim_d, gradient_clip_val=1000.0, gradient_clip_algorithm="norm"
  147. )
  148. optim_d.step()
  149. # Adv Loss
  150. score_fakes = self.discriminator(decoded_mels)
  151. # Adversarial Loss
  152. with torch.autocast(device_type=audios.device.type, enabled=False):
  153. loss_adv, _ = generator_loss([score_fakes])
  154. self.log(
  155. f"train/generator/adv",
  156. loss_adv,
  157. on_step=True,
  158. on_epoch=False,
  159. prog_bar=False,
  160. logger=True,
  161. sync_dist=True,
  162. )
  163. # Feature Matching Loss
  164. score_gts = self.discriminator(gt_mels)
  165. with torch.autocast(device_type=audios.device.type, enabled=False):
  166. loss_fm = feature_loss([score_gts], [score_fakes])
  167. self.log(
  168. f"train/generator/adv_fm",
  169. loss_fm,
  170. on_step=True,
  171. on_epoch=False,
  172. prog_bar=False,
  173. logger=True,
  174. sync_dist=True,
  175. )
  176. with torch.autocast(device_type=audios.device.type, enabled=False):
  177. loss_mel = F.l1_loss(gt_mels * mel_masks, decoded_mels * mel_masks)
  178. self.log(
  179. "train/generator/loss_mel",
  180. loss_mel,
  181. on_step=True,
  182. on_epoch=False,
  183. prog_bar=False,
  184. logger=True,
  185. sync_dist=True,
  186. )
  187. self.log(
  188. "train/generator/loss_vq",
  189. loss_vq,
  190. on_step=True,
  191. on_epoch=False,
  192. prog_bar=False,
  193. logger=True,
  194. sync_dist=True,
  195. )
  196. loss = loss_mel * 20 + loss_vq + loss_adv + loss_fm
  197. self.log(
  198. "train/generator/loss",
  199. loss,
  200. on_step=True,
  201. on_epoch=False,
  202. prog_bar=False,
  203. logger=True,
  204. sync_dist=True,
  205. )
  206. optim_g.zero_grad()
  207. self.manual_backward(loss)
  208. self.clip_gradients(
  209. optim_g, gradient_clip_val=1000.0, gradient_clip_algorithm="norm"
  210. )
  211. optim_g.step()
  212. # Manual LR Scheduler
  213. scheduler_g, scheduler_d = self.lr_schedulers()
  214. scheduler_g.step()
  215. scheduler_d.step()
  216. def validation_step(self, batch: Any, batch_idx: int):
  217. audios, audio_lengths = batch["audios"], batch["audio_lengths"]
  218. audios = audios.float()
  219. audios = audios[:, None, :]
  220. gt_mels = self.mel_transform(audios, sample_rate=self.sampling_rate)
  221. mel_lengths = audio_lengths // self.hop_length
  222. mel_masks = torch.unsqueeze(sequence_mask(mel_lengths, gt_mels.shape[2]), 1).to(
  223. gt_mels.dtype
  224. )
  225. vq_result = self.encode(audios, audio_lengths)
  226. decoded_mels = self.decode(
  227. indices=vq_result.indices,
  228. audio_lengths=audio_lengths,
  229. ).mels
  230. fake_audios = self.generator(decoded_mels)
  231. fake_mels = self.mel_transform(fake_audios.squeeze(1))
  232. min_mel_length = min(
  233. decoded_mels.shape[-1], gt_mels.shape[-1], fake_mels.shape[-1]
  234. )
  235. decoded_mels = decoded_mels[:, :, :min_mel_length]
  236. gt_mels = gt_mels[:, :, :min_mel_length]
  237. fake_mels = fake_mels[:, :, :min_mel_length]
  238. mel_loss = F.l1_loss(gt_mels * mel_masks, fake_mels * mel_masks)
  239. self.log(
  240. "val/mel_loss",
  241. mel_loss,
  242. on_step=False,
  243. on_epoch=True,
  244. prog_bar=True,
  245. logger=True,
  246. sync_dist=True,
  247. )
  248. for idx, (
  249. mel,
  250. gen_mel,
  251. decode_mel,
  252. audio,
  253. gen_audio,
  254. audio_len,
  255. ) in enumerate(
  256. zip(
  257. gt_mels,
  258. fake_mels,
  259. decoded_mels,
  260. audios.detach().float(),
  261. fake_audios.detach().float(),
  262. audio_lengths,
  263. )
  264. ):
  265. mel_len = audio_len // self.hop_length
  266. image_mels = plot_mel(
  267. [
  268. gen_mel[:, :mel_len],
  269. decode_mel[:, :mel_len],
  270. mel[:, :mel_len],
  271. ],
  272. [
  273. "Generated",
  274. "Decoded",
  275. "Ground-Truth",
  276. ],
  277. )
  278. if isinstance(self.logger, WandbLogger):
  279. self.logger.experiment.log(
  280. {
  281. "reconstruction_mel": wandb.Image(image_mels, caption="mels"),
  282. "wavs": [
  283. wandb.Audio(
  284. audio[0, :audio_len],
  285. sample_rate=self.sampling_rate,
  286. caption="gt",
  287. ),
  288. wandb.Audio(
  289. gen_audio[0, :audio_len],
  290. sample_rate=self.sampling_rate,
  291. caption="prediction",
  292. ),
  293. ],
  294. },
  295. )
  296. if isinstance(self.logger, TensorBoardLogger):
  297. self.logger.experiment.add_figure(
  298. f"sample-{idx}/mels",
  299. image_mels,
  300. global_step=self.global_step,
  301. )
  302. self.logger.experiment.add_audio(
  303. f"sample-{idx}/wavs/gt",
  304. audio[0, :audio_len],
  305. self.global_step,
  306. sample_rate=self.sampling_rate,
  307. )
  308. self.logger.experiment.add_audio(
  309. f"sample-{idx}/wavs/prediction",
  310. gen_audio[0, :audio_len],
  311. self.global_step,
  312. sample_rate=self.sampling_rate,
  313. )
  314. plt.close(image_mels)
  315. def encode(self, audios, audio_lengths=None):
  316. if audio_lengths is None:
  317. audio_lengths = torch.tensor(
  318. [audios.shape[-1]] * audios.shape[0],
  319. device=audios.device,
  320. dtype=torch.long,
  321. )
  322. with torch.no_grad():
  323. features = self.mel_transform(audios, sample_rate=self.sampling_rate)
  324. feature_lengths = (
  325. audio_lengths
  326. / self.hop_length
  327. # / self.vq.downsample
  328. ).long()
  329. # print(features.shape, feature_lengths.shape, torch.max(feature_lengths))
  330. feature_masks = torch.unsqueeze(
  331. sequence_mask(feature_lengths, features.shape[2]), 1
  332. ).to(features.dtype)
  333. features = (
  334. gradient_checkpoint(
  335. self.encoder, features, feature_masks, use_reentrant=False
  336. )
  337. * feature_masks
  338. )
  339. vq_features, indices, loss = self.vq(features, feature_masks)
  340. return VQEncodeResult(
  341. features=vq_features,
  342. indices=indices,
  343. loss=loss,
  344. feature_lengths=feature_lengths,
  345. )
  346. def calculate_audio_lengths(self, feature_lengths):
  347. return feature_lengths * self.hop_length * self.vq.downsample
  348. def decode(
  349. self,
  350. indices=None,
  351. features=None,
  352. audio_lengths=None,
  353. feature_lengths=None,
  354. ):
  355. assert (
  356. indices is not None or features is not None
  357. ), "indices or features must be provided"
  358. assert (
  359. feature_lengths is not None or audio_lengths is not None
  360. ), "feature_lengths or audio_lengths must be provided"
  361. if audio_lengths is None:
  362. audio_lengths = self.calculate_audio_lengths(feature_lengths)
  363. mel_lengths = audio_lengths // self.hop_length
  364. mel_masks = torch.unsqueeze(
  365. sequence_mask(mel_lengths, torch.max(mel_lengths)), 1
  366. ).float()
  367. if indices is not None:
  368. features = self.vq.decode(indices)
  369. # Sample mels
  370. decoded = gradient_checkpoint(self.decoder, features, use_reentrant=False)
  371. return VQDecodeResult(
  372. mels=decoded,
  373. )