最近小米k2社区发布了vits语音合成模型,作者基于k2社区csukuangfj的onnx模型导出代码,实现了onnx模型的流式推理代码,大大提升了VITS语音合成的响应速度,50个字的首包延迟在400ms左右。

项目地址

https://github.com/PlayVoice/vits_chinese

具体实现

我们将VITS拆解为两个模型,取名为Encoder和Decoder。
  • Encoder包括TextEncoder与DurationPredictor等;
  • Decoder包括ResidualCouplingBlock与Generator等;
  • 并且将推理逻辑也进行了切分;特别的,先验分布的采样过程:
    z_p = m_p + torch.randn_like(m_p) * torch.exp(logs_p) * noise_scale
    放在了Encoder中。
代码实现


在导出模型时,我们将sample_rate&inter_channel&shop_length 写入模型,供推理使用,具体onnx模型导出如下:

class OnnxModel_Encoder(torch.nn.Module):
    def __init__(self, model: VITS_Encoder):
        super().__init__()
        self.model = model

    def forward(self, x, x_lengths, noise_scale=1, length_scale=1):
        return self.model.infer(
            x=x,
            x_lengths=x_lengths,
            noise_scale=noise_scale,
            length_scale=length_scale,
        )


class OnnxModel_Decoder(torch.nn.Module):
    def __init__(self, model: VITS_Decoder):
        super().__init__()
        self.model = model

    def forward(self, z_p, y_mask):
        return self.model.infer(
            z_p=z_p,
            y_mask=y_mask,
        )


def add_meta_data(filename: str, meta_data: Dict[str, Any]):
    """Add meta data to an ONNX model. It is changed in-place.

    Args:
      filename:
        Filename of the ONNX model to be changed.
      meta_data:
        Key-value pairs.
    """
    model = onnx.load(filename)
    for key, value in meta_data.items():
        meta = model.metadata_props.add()
        meta.key = key
        meta.value = str(value)

    onnx.save(model, filename)


onnx流式推理


class OnnxModel_Encoder:
    def __init__(
        self,
        model: str,
    ):
        session_opts = onnxruntime.SessionOptions()
        session_opts.inter_op_num_threads = 1
        session_opts.intra_op_num_threads = 4

        self.session_opts = session_opts

        self.model = onnxruntime.InferenceSession(
            model,
            sess_options=self.session_opts,
        )
        display(self.model)

        meta = self.model.get_modelmeta().custom_metadata_map
        self.add_blank = int(meta["add_blank"])
        self.sample_rate = int(meta["sample_rate"])
        print(meta)

    def __call__(self, x: torch.Tensor):
        """
        Args:
          x:
            A int64 tensor of shape (L,)
        """
        x = x.unsqueeze(0)
        x_length = torch.tensor([x.shape[1]], dtype=torch.int64)
        noise_scale = torch.tensor([1], dtype=torch.float32)
        length_scale = torch.tensor([1], dtype=torch.float32)

        z_p, y_mask = self.model.run(
            [
                self.model.get_outputs()[0].name,
                self.model.get_outputs()[1].name,
            ],
            {
                self.model.get_inputs()[0].name: x.numpy(),
                self.model.get_inputs()[1].name: x_length.numpy(),
                self.model.get_inputs()[2].name: noise_scale.numpy(),
                self.model.get_inputs()[3].name: length_scale.numpy(),
            },
        )
        return z_p, y_mask


class OnnxModel_Decoder:
    def __init__(
        self,
        model: str,
    ):
        session_opts = onnxruntime.SessionOptions()
        session_opts.inter_op_num_threads = 1
        session_opts.intra_op_num_threads = 4

        self.session_opts = session_opts

        self.model = onnxruntime.InferenceSession(
            model,
            sess_options=self.session_opts,
        )
        display(self.model)

        meta = self.model.get_modelmeta().custom_metadata_map
        self.hop_length = int(meta["hop_length"])
        print(meta)

    def __call__(self, z_p, y_mask):
        y = self.model.run(
            [
                self.model.get_outputs()[0].name,
            ],
            {
                self.model.get_inputs()[0].name: z_p,
                self.model.get_inputs()[1].name: y_mask,
            },
        )[0]
        return y


参考资料

Microsoft's NaturalSpeech: End-to-End Text to Speech Synthesis with Human-Level Quality
https://github.com/Executedone/Chinese-FastSpeech2
https://github.com/wenet-e2e/WeTextProcessing
https://github.com/jaywalnut310/vits
https://github.com/wenet-e2e/wetts
https://github.com/csukuangfj/vits_chinese