GDC Sandbox に TimesFM をデプロイする

Google Research の TimesFM モデルは、多くの実世界のデータセットから数十億のタイムポイントで事前にトレーニングされた時系列予測用の基盤モデルです。そのため、多くのドメインにわたる新しい予測データセットに適用できます。

このチュートリアル ガイドでは、GDC Sandbox に TimesFM をデプロイする方法について説明します。このガイドの目的は次のとおりです。

  • TimesFM を実行する Docker コンテナを作成します。
  • GDC Sandbox AI Optimized SKU で提供される GPU を使用してコンテナをデプロイする。
  • 単純な HTTP リクエストを使用して TimesFM 関数を呼び出します。

始める前に

GDC Sandbox AI Optimized SKU で提供される GPU は、org-infra クラスタに含まれています。

  • 組織のインフラストラクチャ クラスタに対してコマンドを実行するには、クラスタを操作するで説明されているように、org-1-infra クラスタの kubeconfig があることを確認します。

    • gdcloud コマンドラインで構成と認証を行う。
    • 組織のインフラストラクチャ クラスタの kubeconfig ファイルを生成し、そのパスを環境変数 KUBECONFIG に割り当てます。
  • ユーザーにプロジェクト sandbox-gpu-project の sandbox-gpu-admin ロールが割り当てられていることを確認します。デフォルトでは、このロールは platform-admin ユーザーに割り当てられます。platform-admin としてログインし、次のコマンドを実行して、他のユーザーにロールを割り当てることができます。

    kubectl --kubeconfig ${KUBECONFIG} create rolebinding ${NAME} --role=sandbox-gpu-admin \
    --user=${USER} --namespace=sandbox-gpu-project
    
  • Artifact Registry の使用の説明に従って Artifact Registry リポジトリを設定し、アーティファクト レジストリにイメージを push および pull できるようにログインします。

TimesFM モデルをデプロイする

デプロイは、一連の Kubernetes 構成ファイル(YAML マニフェスト)を介してオーケストレートされます。各ファイルは、特定のコンポーネントまたはサービスを定義します。

  1. 時系列予測を行う predict 関数と、テストデータに基づいて可視化を生成する timeseries 関数を含む、flask ベースの Python スクリプト app.py を作成します。

      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. timesfm がインストールされ、アプリを呼び出す Dockerfile を作成します。

     # 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. Docker イメージをビルドして、Artifact Registry リポジトリにアップロードします。

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

    次のように置き換えます。

    • REGISTRY_REPOSITORY_URL: リポジトリの URL。
  4. Docker 認証情報を保存する Secret を作成します。

    
    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
    

    次のように置き換えます。

    • DOCKER_REGISTRY_SECRET: シークレットの名前。
  5. timesfm をデプロイするファイル timesfm-deployment.yaml を作成します。

    timesfm サーバーのデプロイでは、1 つの 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
    

    次のように置き換えます。

    • REGISTRY_REPOSITORY_URL: リポジトリの URL。
    • DOCKER_REGISTRY_SECRET: Docker シークレットの名前。
  6. timesfm サーバーを内部的に公開するファイル timesfm-service.yaml を作成します。

    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. マニフェストを適用します。

    kubectl --kubeconfig ${KUBECONFIG} apply -f timesfm-deployment.yaml
    kubectl --kubeconfig ${KUBECONFIG} apply -f timesfm-service.yaml
    
  8. TimesFM Pod が実行されていることを確認します。

    kubectl --kubeconfig ${KUBECONFIG} get deployments timesfm-deployment -n sandbox-gpu-project
    kubectl --kubeconfig ${KUBECONFIG} get service timesfm-service -n sandbox-gpu-project
    
  9. 外部 IP アドレスからのインバウンド トラフィックを許可するプロジェクト ネットワーク ポリシーを作成します。

    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. 次のコマンドを実行して、TimesFM サービスの外部 IP を特定します。この値は後の手順で使用します。その際、この値を TIMESFM_END_POINT に置き換えます。

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

サービスをテストする。

  1. 予測を取得するには、curl コマンドを使用してサービスにデータを送信します。TIMESFM_END_POINT は、サービスのアドレスと特徴の入力値に置き換えます。これにより、app.py で定義された predict 関数が呼び出され、入力データに対して操作が実行され、JSON 形式で返されます。

    curl -X POST http://TIMESFM_END_POINT/predict -H "Content-Type: application/json" -d '{"features": [1.2, 3.4, 5.6]}'
    
  2. /timeseries に curl リクエストを送信して、ランダムに生成されたデータを使用したデータ可視化の例を確認します。これにより、app.py で定義された時系列関数が呼び出され、ランダムな時系列が生成され、移動平均分析が実行されます。

    curl http://TIMESFM_END_POINT/timeseries