Implantar o TimesFM no GDC Sandbox

O modelo TimesFM do Google Research é um modelo de base para previsão de séries temporais que foi pré-treinado em bilhões de pontos de tempo de muitos conjuntos de dados do mundo real. Assim, você pode aplicá-lo a novos conjuntos de dados de previsão em vários domínios.

Este guia mostra como implantar o TimesFM no GDC Sandbox e tem os seguintes objetivos:

  • Crie um contêiner do Docker que execute o TimesFM.
  • Implante o contêiner usando as GPUs fornecidas pela GDC Sandbox AI Optimized SKU e
  • Invocar funções do TimesFM usando solicitações HTTP simples.

Antes de começar

As GPUs no GDC Sandbox estão incluídas no cluster org-infra.

  • Para executar comandos no cluster de infraestrutura da organização, verifique se você tem o kubeconfig do cluster org-1-infra, conforme descrito em Trabalhar com clusters:

    • Configure e autentique com a linha de comando gdcloud e
    • gere o arquivo kubeconfig para o cluster de infraestrutura da organização e atribua o caminho dele à variável de ambiente KUBECONFIG.
  • Verifique se o usuário tem a função sandbox-gpu-admin atribuída ao projeto sandbox-gpu-project. Por padrão, a função é atribuída ao usuário platform-admin. Você pode atribuir a função a outros usuários fazendo login como platform-admin e executando o seguinte comando:

    kubectl --kubeconfig ${KUBECONFIG} create rolebinding ${NAME} --role=sandbox-gpu-admin \
    --user=${USER} --namespace=sandbox-gpu-project
    
  • Configure o repositório do Artifact Registry conforme descrito em Usar o Artifact Registry e faça login para enviar e extrair imagens do registro de artefatos.

Implantar o modelo TimesFM

A implantação é organizada por um conjunto de arquivos de configuração do Kubernetes (manifestos YAML), cada um definindo um componente ou serviço específico.

  1. Crie um script Python baseado em Flask app.py com funções predict para fazer previsões de séries temporais e timeseries para gerar uma visualização com base nos dados de teste.

      from flask import Flask, jsonify, request
      import numpy as np
      import pandas as pd
      from sklearn.preprocessing import StandardScaler
    
      # Initialize Flask application
      app = Flask(__name__)
    
      # Sample route to display a welcome message
      @app.route('/')
      def home():
          return "Welcome to TimesFM! Use the API to interact with the app."
    
      # Example route for predictions (TimesFM might do time-series forecasting or music recommendations)
      @app.route('/predict', methods=['POST'])
      def predict():
          data = request.get_json()
    
          # Ensure the data is in the right format
          if 'features' not in data:
              return jsonify({'error': 'No features provided'}), 400
    
          # For this example, assume 'features' is a list of numbers that need to be scaled
          features = data['features']
          features = np.array(features).reshape(1, -1)
    
          # Dummy model: Apply standard scaling (you would use an actual model here)
          scaler = StandardScaler()
          scaled_features = scaler.fit_transform(features)
    
          # You would normally load your model here (e.g., using pickle or joblib)
          # For simplicity, let's just return the scaled features as a placeholder for prediction
          result = scaled_features.tolist()
    
          return jsonify({'scaled_features': result})
    
      # Example of a route for data visualization or analysis
      @app.route('/timeseries', methods=['GET'])
      def timeseries_analysis():
          # Generate a dummy time series data (replace with actual data)
          time_series_data = pd.Series(np.random.randn(100), name="Random Data")
    
          # Example analysis: compute simple moving average
          moving_avg = time_series_data.rolling(window=10).mean()
    
          return jsonify({
              'time_series': time_series_data.tolist(),
              'moving_average': moving_avg.tolist()
          })
    
      # Run the app
      if __name__ == '__main__':
          app.run(debug=True, host='0.0.0.0', port=5000)
    
  2. Crie um Dockerfile com o timesfm instalado invocando o app.

     # Use a base image with Python installed
     FROM python:3.11-slim
     # Set the working directory inside the container
     WORKDIR /app
     # Copy the requirements.txt (if any) and install dependencies
     COPY requirements.txt .
     RUN pip install --no-cache-dir numpy pandas timesfm huggingface_hub jax pytest flask scikit-learn
    
     # Copy the rest of the code into the container
     COPY . .
    
     # Expose the necessary port (default 5000 or whatever your app uses)
     EXPOSE 5000
    
     # Define the entrypoint for the container
     CMD ["python", "app.py"] # Replace with the correct entry script for TimesFM
    
  3. Crie a imagem do Docker e faça upload dela no repositório do Artifact Registry.

    docker build -t timesfm .
    docker tag timesfm "REGISTRY_REPOSITORY_URL"/timesfm:latest
    docker push "REGISTRY_REPOSITORY_URL"/timesfm:latest
    

    Substitua:

    • REGISTRY_REPOSITORY_URL: o URL do repositório.
  4. Crie um secret para salvar as credenciais do Docker.

    
    export SECRET="DOCKER_REGISTRY_SECRET"
    export DOCKER_TEST_CONFIG=~/.docker/config.json 
    kubectl --kubeconfig ${KUBECONFIG} create secret docker-registry ${SECRET} --from-file=.dockerconfigjson=${DOCKER_TEST_CONFIG} -n sandbox-gpu-project
    

    Substitua:

    • Nome do secret DOCKER_REGISTRY_SECRET.
  5. Crie um arquivo timesfm-deployment.yaml para implantar timesfm.

    A implantação do servidor timesfm solicita uma GPU.

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: timesfm-deployment
      namespace: sandbox-gpu-project
      labels:
        app: timesfm
    spec:
      replicas: 1 # You can scale up depending on your needs
      selector:
        matchLabels:
          app: timesfm
      template:
        metadata:
          labels:
            app: timesfm
        spec:
          containers:
          - name: timesfm
            image: REGISTRY_REPOSITORY_URL/timesfm:latest
            ports:
            - containerPort: 5000
            resources:
              requests:
                nvidia.com/gpu-pod-NVIDIA_H100_80GB_HBM3: 1  # Request 1 GPU
              limits:
                nvidia.com/gpu-pod-NVIDIA_H100_80GB_HBM3: 1  # Limit to 1 GPU
            env:
            - name: ENV
              value: "production"
          imagePullSecrets:
          - name: docker-registry-secret
    

    Substitua:

    • REGISTRY_REPOSITORY_URL: o URL do repositório.
    • DOCKER_REGISTRY_SECRET: nome do secret do Docker.
  6. Crie um arquivo timesfm-service.yaml para expor o servidor timesfm internamente.

    apiVersion: v1
    kind: Service
    metadata:
      name: timesfm-service
    spec:
      selector:
        app: timesfm
      ports:
        - protocol: TCP
          port: 80 # External port exposed
          targetPort: 5000 # Internal container port for Flask
      type: LoadBalancer # Use NodePort for internal access
    
  7. Aplique os manifestos.

    kubectl --kubeconfig ${KUBECONFIG} apply -f timesfm-deployment.yaml
    kubectl --kubeconfig ${KUBECONFIG} apply -f timesfm-service.yaml
    
  8. Verifique se os pods TimesFM estão em execução.

    kubectl --kubeconfig ${KUBECONFIG} get deployments timesfm-deployment -n sandbox-gpu-project
    kubectl --kubeconfig ${KUBECONFIG} get service timesfm-service -n sandbox-gpu-project
    
  9. Crie uma política de rede do projeto para permitir o tráfego de entrada de endereços IP externo.

    kubectl --kubeconfig ${KUBECONFIG} apply -f - <<EOF
    apiVersion: networking.global.gdc.goog/v1
    kind: ProjectNetworkPolicy
    metadata:
      namespace: sandbox-gpu-project
      name: allow-inbound-traffic-from-external
    spec:
      policyType: Ingress
      subject:
        subjectType: UserWorkload
      ingress:
      - from:
        - ipBlock:
            cidr: 0.0.0.0/0
    EOF
    
  10. Identifique o IP externo do serviço TimesFM executando o seguinte comando. Anote-o para usar nas etapas posteriores, em que você vai substituir esse valor por TIMESFM_END_POINT.

      kubectl --kubeconfig ${KUBECONFIG} get service timesfm-service \
            -n sandbox-gpu-project -o jsonpath='{.status.loadBalancer.ingress[*].ip}'
    

Testar o serviço.

  1. Para receber uma previsão, envie dados ao serviço usando um comando curl, substituindo TIMESFM_END_POINT pelo endereço real do serviço e pelos valores de entrada dos recursos. Isso invoca a função predict definida em app.py, que vai manipular seus dados de entrada e retorná-los no formato JSON.

    curl -X POST http://TIMESFM_END_POINT/predict -H "Content-Type: application/json" -d '{"features": [1.2, 3.4, 5.6]}'
    
  2. Envie uma solicitação curl para /timeseries para ver um exemplo de visualização de dados usando dados gerados aleatoriamente. Isso invoca a função de série temporal definida em app.py, que gera uma série temporal aleatória e realiza uma análise de média móvel nela.

    curl http://TIMESFM_END_POINT/timeseries