Implantação Local e Integração via API do Modelo de Reconhecimento de Fala Qwen3-ASR-1.7B

  1. Requisitos de Hardware e Configuração Base

O modelo Qwen3-ASR-1.7B é uma solução de reconhecimento de fala de ponta a ponta projetada para execução estritamente offline. Devido à sua arquitetura de 1,7 bilhão de parâmetros, a inferência exige recursos computacionais específicos e não é compatível com processamento via CPU ou arquiteturas de GPU defasadas.

1.1 Especificações de GPU e Memória

Para garantir a estabilidade da inferência e evitar erros de estouro de memória (OOM), o ambiente deve atender aos seguintes critérios mínimos:

  • Arquitetura da GPU: NVIDIA Ampere ou superior (ex: A10, A100, V100, RTX 3090, RTX 4090).
  • Capacidade de VRAM: Mínimo absoluto de 16GB. Recomenda-se 24GB para acomodar o carregamento dos pesos (aproximadamente 5,5GB), cache de ativação (cerca de 6GB) e processamento de arquivos de áudio mais longos.

Dispositivos com 12GB de VRAM falharão ao processar áudios superiores a 30 segundos, enquanto configurações com 16GB suportarão a carga, mas limitarão a concorrência de requisições.

1.2 Compatibilidade de Imagens e Ambiente

A imagem do modelo ins-asr-1.7b-v1 depende estritamente de uma imagem base específica para funcionar corretamente. A configuração obrigatória é:

  • Imagem Base: insbase-cuda124-pt250-dual-v7
  • Toolchain: CUDA 12.4 (incompatível com versões 11.x ou 12.2).
  • Framwork: PyTorch 2.5.0 pré-compilado para CUDA 12.4.
  • Arquitetura de Serviço: Dual (FastAPI para API REST e Gradio para interface Web).

Versões anteriores (v0) carecem de detecção automática de VAD (Voice Activity Detection) para poda de silêncio, e variantes CPU não possuem suporte para os pesos do modelo.

  1. Provisionamento e Validação do Serviço

2.1 Inicialização da Instância

Ao provisionar a instância no ambiente de containers ou nuvem, configure os seguintes parâmetros:

  1. Selecione a imagem ins-asr-1.7b-v1.
  2. Defina uma instância GPU com no mínimo 16GB de VRAM.
  3. Aloque pelo menos 100GB de disco para o sistema, pesos do modelo e logs.
  4. Desative a atribuição automática de IP público se o ambiente for estritamente privado.

Após o status da instância indicar "Em execução", o serviço não estará imediatamente disponível. O carregamento dos pesos na VRAM leva entre 15 e 20 segundos. Monitore via terminal com nvidia-smi; a alocação de memória deve estabilizar em aproximadamente 11GB antes que o serviço aceite conexões.

2.2 Acesso à Interface Web (Gradio)

A interface gráfica para testes manuais é exposta na porta 7860. O acesso pode ser realizado via:

  • Proxy HTTP reverso do painel de controle (recomendado).
  • Acesso direto: http://<IP_DA_INSTANCIA>:7860 (utilizando HTTP, não HTTPS).

Caso a página não carregue, verifique se a porta 7860 está liberada nas regras de firewall/security group e confirme a execução do processo Gradio via ps aux | grep gradio.

2.3 Validação Funcional

Para confirmar a integridade do serviço, execute os seguintes testes na interface:

  1. Seleção de Idioma: Verifique se as opções auto, zh, en, ja, ko e yue estão renderizadas corretamente.

  2. Ingestão de Áudio: Faça o upload de um arquivo WAV (16kHz, mono, <10s). O waveform deve ser exibido.

  3. Inferência: Acione o processamento. O resultado deve retornar o idioma detectado e a transcrição textual com pontuação adequada.

  4. Integração Programática via API REST


Para integração em pipelines de produção, o serviço expõe um endpoint FastAPI na porta 7861. A API exige o envio do arquivo de áudio em formato binário (multipart/form-data) e não aceita codificação base64 ou URLs remotas.

3.1 Requisição Síncrona para Arquivo Único

O script abaixo utiliza a biblioteca httpx para realizar a requisição, incluindo tipagem estática e tratamento de exceções robusto.

import httpx
from pathlib import Path
from typing import Optional, Dict

def transcrever_audio_local(
    caminho_audio: str, 
    endereco_ip: str, 
    idioma_alvo: str = "auto"
) -> Optional[Dict]:
    endpoint = f"http://{endereco_ip}:7861/asr"
    arquivo = Path(caminho_audio)

    if not arquivo.exists():
        raise FileNotFoundError(f"Arquivo de áudio não localizado: {caminho_audio}")

    payload = {"language": idioma_alvo}

    with open(arquivo, "rb") as f_stream:
        arquivos = {"audio_file": (arquivo.name, f_stream, "audio/wav")}
        try:
            with httpx.Client(timeout=45.0) as cliente:
                resposta = cliente.post(endpoint, data=payload, files=arquivos)
                resposta.raise_for_status()
                return resposta.json()
        except httpx.HTTPStatusError as erro_http:
            print(f"Falha na requisição HTTP: {erro_http.response.status_code} - {erro_http.response.text}")
        except httpx.TimeoutException:
            print("A requisição excedeu o tempo limite de processamento.")
        except Exception as erro_geral:
            print(f"Erro inesperado na comunicação: {erro_geral}")

    return None

if __name__ == "__main__":
    resultado = transcrever_audio_local("audio_teste.wav", "192.168.1.50", "zh")
    if resultado:
        print(f"Idioma: {resultado.get('language')} | Texto: {resultado.get('text')}")

3.2 Processamento em Lote com Controle de Concorrência

Para processar múltiplos arquivos sem causar OOM na GPU, utiliza-se programação assíncrona com um semáforo para limitar a concorrência máxima a duas requisições simultâneas.

import asyncio
import httpx
from pathlib import Path
from typing import List, Dict

async def processar_lote_assincrono(
    lista_arquivos: List[str],
    endereco_ip: str,
    concorrencia_maxima: int = 2
) -> Dict[str, str]:
    semaforo = asyncio.Semaphore(concorrencia_maxima)
    resultados_finais = {}

    async def enviar_requisicao(caminho: str, cliente: httpx.AsyncClient):
        async with semaforo:
            url = f"http://{endereco_ip}:7861/asr"
            nome_arq = Path(caminho).name
            
            with open(caminho, "rb") as f:
                dados = {"language": "auto"}
                arquivos = {"audio_file": (nome_arq, f, "audio/wav")}
                try:
                    resp = await cliente.post(url, data=dados, files=arquivos, timeout=60.0)
                    resp.raise_for_status()
                    resultados_finais[caminho] = resp.json().get("text", "")
                except Exception as e:
                    resultados_finais[caminho] = f"Falha: {str(e)}"

    async with httpx.AsyncClient() as cliente_http:
        tarefas = [enviar_requisicao(f, cliente_http) for f in lista_arquivos]
        await asyncio.gather(*tarefas)

    return resultados_finais

# Exemplo de execução
if __name__ == "__main__":
    arquivos_wav = [str(p) for p in Path("./audios").glob("*.wav")]
    transcrições = asyncio.run(processar_lote_assincrono(arquivos_wav, "192.168.1.50"))
    for arq, texto in transcrições.items():
        print(f"{arq}: {texto[:50]}...")

  1. Solução de Problemas e Erros Frequentes

4.1 Incompatibilidade de Formato de Áudio

Sintoma: A API retorna erro ou resultado vazio ao receber arquivos MP3 ou M4A.

Causa: O pipeline de pré-processamento do modelo exige estritamente arquivos WAV com codificação PCM, mono e taxa de amostragem de 16kHz.

Correção: Utilize o ffmpeg para padronizar os arquivos antes do envio:

ffmpeg -i entrada.mp3 -ar 16000 -ac 1 -acodec pcm_s16le saida.wav

4.2 Transcrição Truncada ou Caracteres Corrompidos

Sintoma: O texto de saída contém caracteres inválidos ou é interrompido abruptamente.

Causa: Cabeçalho do arquivo WAV corrompido ou taxa de amostragem incorreta (ex: 44.1kHz).

Diagnóstico: Execute file arquivo.wav no terminal. A saída deve indicar 16 bit, mono 16000 Hz.

Correção: Force a reamostragem e reconstrução do cabeçalho:

ffmpeg -i corrompido.wav -ar 16000 -ac 1 -acodec pcm_s16le -f wav corrigido.wav

4.3 Interface Web Indisponível (502 ou Tela Branca)

Sintoma: O navegador exibe erro 502 Bad Gateway ao acessar a porta 7860.

Procedimento de Diagnóstico:

  1. Teste a conectividade interna: curl http://localhost:7860. Se retornar HTML, o problema é de rede/firewall.
  2. Verifique o status da API: curl http://localhost:7861/asr. Deve retornar {"detail":"Method Not Allowed"}.
  3. Confirme a alocação de VRAM via nvidia-smi. Se o uso estiver abaixo de 5GB, o carregamento dos pesos falhou; reinicie o container ou execute o script de inicialização manualmente.

Tags: qwen-asr speech-recognition FastAPI Gradio CUDA

Publicado em 6-22 22:12