Skip to main content

Azure Speech TTS性能调优的最佳实践

分类:  Azure认知服务 标签:  #Azure #人工智能 #语音服务 发布于: 2023-06-05 17:42:19

针对于Azure Speech text-to-speech性能调优,我们首先要理解官方推荐的性能指标,从官方的文档上可以看到,衡量TTS的性能指标主要有:

  • First Byte Latency: 该时间表示Synthesis任务开始到接收到音频文件的第一个设定的块之间花费的时间,在.Net SDK中使用对象SpeechSynthesisResult的属性SpeechServiceResponse_SynthesisFirstByteLatencyMs 来表示。
  • Finish Latency: 该时间表示Synthesis任务开始到整个语音合成的数据结束所花的时间。在.Net SDK中使用对象SpeechSynthesisResult的属性SpeechServiceResponse_SynthesisFinishLatencyMs来表示。

First Byte Latency和文本的长度没有什么关系。为了尽量提升用户的体验,我们希望尽量缩短第一次网络存取的时间 + 加上第一个声音数据块传到用户侧的时间。从SDK 1.6开始,我们默认的在网络层启用了标志TCP_NODELAY来减少网络上所花费的开销。为了尽量的减少First Byte Latency的时间,我们可以考虑如下的方式:

  • 在单机模式下,预先创建Synthsis客户端,并预先openConnection, 并预热,同时尽量重用该实例,例如使用单例模式。
    关于单机模式下预先创建客户端,openConnection, 预热,可以参考如下的代码:

    var speechConfig = SpeechConfig.FromSubscription(subscription, region);
    SpeechSynthesizer item =   new SpeechSynthesizer(speechConfig)
    //预热
    item.SpeakTextAsync("1").Wait();
    
  • 在服务模式下,可以采用Pool的形式预先创建多个Synthsis客户端,预先openConnectioni, 并预热,同时用完实例之后,将该实例仍然放回Pool中,同时需要注意设计idle和如何根据负载量增加更多的Synthsis客户端。 关于服务模式,可以参考代码:https://github.com/Azure-Samples/cognitive-services-speech-sdk/blob/master/samples/csharp/sharedcontent/console/speech_synthesis_server_scenario_sample.cs 以及Java的代码: https://github.com/Azure-Samples/cognitive-services-speech-sdk/blob/master/samples/java/jre/console/src/com/microsoft/cognitiveservices/speech/samples/console/SpeechSynthesisScenarioSamples.java

  • 如果使用Stream的形式取回声音数据,需要根据测试设计合理的Buffer Side。
    关于使用Stream的方式,例如:

      using (var synthesizer = new SpeechSynthesizer(config, null as AudioConfig))
      {
          using (var result = await synthesizer.StartSpeakingTextAsync(text))
          {
              using (var audioDataStream = AudioDataStream.FromResult(result))
              {
                  byte[] buffer = new byte[16000];
                  uint filledSize = 0;
                  while ((filledSize = audioDataStream.ReadData(buffer)) > 0)
                  {
                      Console.WriteLine($"{filledSize} bytes received.");
                  }
              }
          }
      }
    
  • 使用压缩格式传递语音,例如使用GStream 转换语音为压缩的音频,可以参考文档:https://docs.microsoft.com/en-us/azure/cognitive-services/speech-service/how-to-use-codec-compressed-audio-input-streams?tabs=debian&pivots=programming-language-csharp, 配置好之后,即可以使用如下的代码来使用压缩的格式:

      var audioConfig =
      AudioConfig.FromStreamInput(
          pullStream,
          audioFormat);
    
      using var recognizer = new SpeechRecognizer(speechConfig, audioConfig);
      var result = await recognizer.RecognizeOnceAsync();
    

    非常需要注意的是在Speech SDK中,客户端的实例和底层的网络协议之间是分开的,也即new一个client, 仅仅是new了一个对象,还还没有正式建立网络连接,如果需要使用网络连接,考虑:

      using (var synthesizer = new SpeechSynthesizer(uspConfig, null as AudioConfig))
      {
          using (var connection = Connection.FromSpeechSynthesizer(synthesizer))
          {
              connection.Open(true);
          }
          await synthesizer.SpeakTextAsync(text);
      }
    

    或者直接Warm up

SDK和CRL(Certificate Revocation Checks)

Azure Speech SDK连接到Azure Speech Service的时候,SDK会验证TLS Certificate, SDK会访问被Azure使用的CRL文件,如果这些文件不存在,我们可以预先下载这些文件,让SDK直接访问这些文件,从而可以大量的节省时间。

有一些基本的问题, 在SDK 1.19之前,SDK会将open ssl库静态链接,并打包在一起,但是从1.19开始,SDK不在静态打包openssl的库,因此在Linux系统上,用户需要首先在系统上安装openssl, 并设定openssl库的变量,使得SDK可以找到CRL文件,例如:

export SSL_CERT_DIR=/opt/ssl/certs

或者

export SSL_CERT_FILE=/etc/pki/tls/certs/ca-bundle.crt

也可以从这里下载CRL文件:https://docs.microsoft.com/en-us/azure/security/fundamentals/tls-certificate-changes
并指定目录。

另外有些linux的发行版没有系统变量TMP或者是TMPDIR, 默认情况下会默认导致SDK每次都会下载CRL文件。最好添加一个系统变量,可以在.bashrc或者是/etc下的profile或者bashrc文件中添加该变量。

如果CRL文件无法下载,其实可以使用配置忽略或者禁止:

例如:

config.SetProperty("OPENSSL_CONTINUE_ON_CRL_DOWNLOAD_FAILURE", "true");

或者完全disabled

config.SetProperty("OPENSSL_DISABLE_CRL_CHECK", "true");

当然如果已经下载好了,就不需要设置这些了。

语料

前面我们讨论的都是从SDK这一侧怎么优化性能,其实对于性能来说,用户使用的语料也是一个非常大的影响因素,在语料准备上,一定要符合实际的应用场景,合理的使用断语的标点符号,这包括句号(西文.), 问好, 感叹号。语料本身是有实际意义的。另外在将语料通过SDK传给Azure服务之前,最好要过滤一些特殊字符。因为某些特殊字符可能会造成Azure服务无返回,而特殊字符实际上对于用户应用场景也没有什么意义。

上线之前的Stress testing

无论什么系统上线前,总是需要对系统的承载能力进行一定的标定,标定之后,梳理必要的资源形成一个设计目标的baseline, 有了这个baseline之后,随着用户和负载的增高,有步骤的添加资源或者优化,不要打无准备的仗。

目前Azure Speech服务本身是根据定价层的不同,有一定的限制的,例如并发访问数,关于Speech的限制,详细情况您可以参考文档:
https://docs.microsoft.com/en-us/azure/cognitive-services/speech-service/speech-services-quotas-and-limits

Azure Speech服务本身提供auto-scale的特性,但是它需要时间进行缩放,如果并发连接增加的非常快,那么很容易触发429的错误,为了缓解这样的问题,我们可以要求提高限制快速的缓解这个问题,但是在这之前,还是建议自己确认确实需要更多的quota, 可以考虑使用如下的办法先缓解:

  • 在应用里实现重试的机制,例如.Net中使用Polly来帮助创建重试策略。
  • 避免突发的访问高峰,可以预先指定一些规则来添加测试的强度,例如从5TPS -> 10TPS -> 20 TPS 逐步提高,并控制时间间隔。自动重试最好也采用幂等的时间间隔进行重试,例如第一次1分钟,第二次2分钟,第三次4分钟,第四次16分钟等等。
  • 在同一个资源组,同一个区域创建多个speech资源,采用Round Robin的方式将请求分布到多个资源上。