IA: Audio para texto

Saudações. Este é o primeiro artigo de uma série que vou nomear como “IA na caixinha”. Vou demonstrar nesse primeiro exemplo o conceito de construção de um micro-serviço para API HTTP de IA para conversão de audio para texto.

Vou usar o modelo aberto Whisper da OpenIA com um exemplo bem pontual, e caberá a você personalizar e avançar meu exemplo para suas aplicações práticas.

A API fará um trabalho muito pontual: receber um arquivo de audio (mp4, m4a, wav) e retornar o texto da transcrição do audio, ou seja, converter o audio para texto (audio2text).

Requisitos

Softwares utilizados nesse artigo

  • Linux Debian 12;
  • Docker;

1 – O modelo Whisper da OpenIA

O modelo Whisper da OpenAI é uma tecnologia avançada de reconhecimento automático de fala que utiliza técnicas de deep learning para converter áudio em texto com alta precisão.

Ele foi treinado com uma ampla variedade de idiomas, dialetos, sotaques e condições acústicas, inclusive em ambientes ruidosos, o que torna o modelo uma ferramenta poderosa para tarefas que exigem a transcrição de áudio.

Ele pode ser empregado na criação de legendas automáticas para vídeos, transcrição de reuniões, podcasts e entrevistas.

Para mim a principal aplicação do Whisper é transcrever áudios recebidos em grupos e conversas do WhatsApp e Telegram, economizando meu tempo (e também resumindo longos audios em parágrafos pontuais).

2 – Criando o container com Python3 e Whisper

Vou demonstrar a criação de um container do zero.

Esse container possuirá apenas 1 código em python com poucas linhas:

  • Bibliotecas Flask para prover um servidor HTTP para a API;
  • Biblioteca Whisper (e todas as dependências) para processar o audio recebido, converter para texto e retornar na API HTTP em formato JSON;

Crie uma pasta chamada webapi-whisper, dentro dessa pasta crie o arquivo Dockerfile com o conteúdo abaixo:

webapi-whisper/Dockerfile

Dockerfile

FROM debian:12

# Variaveis globais de ambiente
ENV \
    MAINTAINER="Patrick Brandao" \
    EMAIL=patrickbrandao@gmail.com \
    TERM=xterm \
    SHELL=/bin/bash \
    TZ=America/Sao_Paulo \
    PS1='\u@\h:\w\$ ' \
    LANG=en_US.UTF-8 \
    LANGUAGE=en_US.UTF-8

# Atualiza os repositórios e pacotes
RUN ( \
    apt-get -y update        || apt-get -y update           || exit 10; \
    apt-get -y upgrade       || apt-get -y upgrade          || exit 11; \
    apt-get -y dist-upgrade  || apt-get -y dist-upgrade     || exit 12; \
    \
    apt-get -y install procps psutils coreutils util-linux  || exit 13; \
    apt-get -y install sed gpg curl git iproute2            || exit 14; \
)

# Python base
RUN ( \
    apt-get install -y python3            || exit 31; \
    apt-get install -y python3-numpy      || exit 32; \
    apt-get install -y python3-pip        || exit 33; \
    apt-get install -y python3-flask      || exit 34; \
    apt-get install -y python3-setuptools || exit 35; \
    apt-get install -y python3-setuptools-rust || exit 36; \
)

# Python add-ons para whisper
RUN ( \
    apt-get install -y ffmpeg             || exit 41; \
    apt-get install -y python3-whisper    || exit 42; \
    apt-get install -y python3-torch      || exit 43; \
    apt-get install -y python3-tqdm       || exit 44; \
    apt-get install -y python3-numba      || exit 45; \
    apt-get install -y python3-torchaudio || exit 46; \
)

# Whisper
RUN ( \
    pip install \
        --upgrade \
        --force-reinstall \
        --break-system-packages \
        tiktoken || exit 51; \
    \
    pip install \
        --upgrade \
        --force-reinstall \
        --break-system-packages \
        git+https://github.com/openai/whisper.git || exit 52; \
)

# Clean
RUN ( mkdir -p /app; rm -rf /var/lib/apt/lists/*; )

WORKDIR /app
COPY app.py       /app/
COPY download.py  /app/

EXPOSE 5000
CMD ["python3", "app.py"]

Crie o arquivo app.py e download.py no mesmo diretório do Dockerfile, código-fonte abaixo.

webapi-whisper/app.py

Python
from flask import Flask, request, jsonify

import whisper
import tempfile

app = Flask(__name__)

# Carrega o modelo Whisper;
# você pode escolher entre
#    "tiny", "base", "small", "medium" ou "large"
model = whisper.load_model("base")

@app.route('/transcribe', methods=['POST'])
def transcribe():
    if 'audio' not in request.files:
        return jsonify({"error": "Arquivo de áudio não enviado"}), 400

    audio_file = request.files['audio']
    # Cria um arquivo temporário para salvar o áudio recebido
    with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tmp:
        audio_file.save(tmp.name)
        tmp.flush()
        # Transcreve o áudio
        result = model.transcribe(tmp.name)
    return jsonify({"transcription": result.get("text", "")})

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

webapi-whisper/download.py

Python
import sys
import whisper

if len(sys.argv) != 2:
    print(f"Uso: {sys.argv[0]} <nome_do_modelo>")
    sys.exit(1)

model_name = sys.argv[1]
print(f"Baixando o modelo '{model_name}'...")

model = whisper.load_model(model_name)
print(f"Modelo '{model_name}' baixado com sucesso!")

Criando a imagem do container

No diretório webapi-whisper/ onde está o arquivo Dockerfile, execute a construção da imagem:

Bash
docker build . -t webapi-whisper

2 – Armazenando os modelos

Por padrão, as imagens dos modelos whisper serão baixadas pelo whisper no diretório:

  • /root/.cache/whisper

Por conta disso, temos que cuidar para montar um volume que mapeie esse diretório dentro do container em um local persistente no host.

Vou armazenar no host em /storage/ia-models/whisper/ para manter o padrão com o artigo principal de IA Local.

Bash
# Criar diretorio para armazenar as imagens do modelo Whisper
mkdir -p /storage/ia-models/whisper

3 – Rodando o container webapi-whisper

Podemos rodar com ou sem GPU, vou deixar os dois exemplos para você escolher de acordo com seu ambiente.

Antes, crie uma rede no docker para esses containers de IA, conforme artigo anterior:

Bash
# Criar rede docker (ipv4 only)
# - atributo 'com.docker.network.bridge.name' define o nome
#   da interface bridge no HOST
# - atributo 'com.docker.network.bridge.enable_icc' permite
#   que constainers falem entre si, 'Inter Container Connectivity'
# - usaremos apenas ipv4, faixa 172.19.0.x
# - o HOST assume o primeiro ip dessa rede para ser o gateway
#   dos containers: 172.19.0.1

docker network create \
    -d bridge \
    --subnet 172.19.0.0/24 \
    -o "com.docker.network.bridge.name"="br-ias" \
    -o "com.docker.network.bridge.enable_icc"="true" \
    br-ias

Com a rede pronta, vamos criar o container nela, escolha uma das opções (CPU ou GPU Nvidia). A porta interna do container é 5000, e você deve escolher se vai publicá-la pelo comando “-p 5000:5000” ou se irá controla-la por proxy-reverso. Os exemplos abaixo publicam a porta 5000.

Rodando somente em CPU

Bash
# Rodando webapi-whisper para execução em CPU

# Criar diretorio para armazenar as imagens do modelo Whisper
mkdir -p /storage/ia-models/whisper

# Criando container para execução:
docker run \
    -d --restart=always \
    \
    --name webapi-whisper \
    -h webapi-whisper.intranet.br \
    \
    --network br-ias \
    --ip=172.19.0.71 \
    \
    -p 5000:5000 \
    \
    -v /storage/ia-models/whisper:/root/.cache/whisper \
    \
        webapi-whisper

Rodando em GPU Nvidia

Bash
# Rodando webapi-whisper para execução em CPU

# Criar diretorio para armazenar as imagens do modelo Whisper
mkdir -p /storage/ia-models/whisper

# Criando container para execução:
docker run \
    -d --restart=always \
    \
    --name webapi-whisper \
    -h webapi-whisper.intranet.br \
    \
    --network br-ias \
    --ip=172.19.0.71 \
    \
    -p 5000:5000 \
    \
    --gpus=all \
    \
    -v /storage/ia-models/whisper:/root/.cache/whisper \
    \
        webapi-whisper

4 – Obtendo modelos

Ao rodar o container acima (qualquer um dos métodos) o código em python do app.py acionará o uso do modelo “base” do Whisper, que na ausência do arquivo do modelo “base.pt” no diretório /root/.cache/whisper (mapeado em /storage/ia-models/whisper do host) iniciará seu download.

Esse arquivo “base” tem 140Mbyte. Enquanto o download não for concluído a API não funcionará.

Lista de modelos possíveis:

  • tiny
  • base
  • small
  • medium
  • large

O modelo “small” é um pouco maior 462M e possui resultados melhores, mas consumindo 4x mais recursos (VRAM e GPU ou RAM e CPU).

Para baixar os modelos mais avançados, rode o seguinte comando para realizar o download dentro do container webapp-whisper:

Bash
# Obter modelos do whisper:

docker exec -it webapi-whisper python3 /app/download.py tiny

docker exec -it webapi-whisper python3 /app/download.py base

docker exec -it webapi-whisper python3 /app/download.py small

docker exec -it webapi-whisper python3 /app/download.py medium

docker exec -it webapi-whisper python3 /app/download.py large

Tamanho dos modelos:

Bash
# Tamanho dos modelos whisper:
ls -lah /storage/ia-models/whisper/

total 5.0G
-rw-r--r-- 1 root root  2.9G Feb 22 22:53 large-v3.pt # 2.9 G
-rw-r--r-- 1 root root  1.5G Feb 22 22:52 medium.pt   # 1.5 G
-rw-r--r-- 1 root root  462M Feb 22 22:46 small.pt    # 462 M
-rw-r--r-- 1 root root  139M Feb 22 22:29 base.pt     # 139 M
-rw-r--r-- 1 root root   73M Feb 22 22:51 tiny.pt     #  73 M

Observe que meu exemplo roda a API HTTP na porta 5000/TCP o modelo whisper “base” de 139M, e que o melhor modelo é o “large” (large-v3).

Você pode personalizar esse container para rodar em uma porta específica e com um modelo de sua escolha.

5 – Usando a API HTTP do webapi-whisper

A API de exemplo é simples: envie para o IP do host na port 5000, HTTP, path /transcribe um arquivo de áudio. Se estiver dentro da mesma rede docker você pode usar o nome do container, se estiver no HOST use o ip do container. Endpoints:

Exemplo de envio de audio como anexo (corpo do documento HTTP, método POST):

Bash
# Enviando arquivo de audio para a API:
# -X POST : método http do envio
# -F: argumento para carregar arquivo de audio para upload
# -v: ativar modo debug do curl
curl \
    -X POST \
    -F "audio=@test/audio01.m4a" \
     -v \
    http://10.150.255.179:5000/transcribe

    #*   Trying 10.150.255.179:5000...
    #* Connected to 10.150.255.179 (10.150.255.179) port 5000 (#0)
    #> POST /transcribe HTTP/1.1
    #> Host: 10.150.255.179:5000
    #> User-Agent: curl/7.88.1
    #> Accept: */*
    #> Content-Length: 73407
    #> Content-Type: multipart/form-data; boundary=----7cfdfce0f977ed01
    #> 
    #* We are completely uploaded and fine
    #< HTTP/1.1 200 OK
    #< Server: Werkzeug/2.2.2 Python/3.11.2
    #< Date: Sun, 23 Feb 2025 10:57:45 GMT
    #< Content-Type: application/json
    #< Content-Length: 116
    #< Connection: close
    #< 
    #{ "transcription":" O rato ropa do Rei de Roma."}
    #* Closing connection 0

A resposta será do tipo JSON e a propriedade transcription contem o texto resultante.

Conclusão

Com seu container de API HTTP para audio2text você pode usar a criatividade.

Terminamos por hoje, isso deve ter te economizado muito tempo!

Patrick Brandão, patrickbrandao@gmail.com