פריסת TimesFM בארגז החול של GDC

מודל TimesFM של צוות המחקר של Google הוא מודל בסיסי ליצירת תחזיות של סדרות זמנים. הוא עבר אימון מראש על מיליארדי נקודות זמן ממערכי נתונים רבים מהעולם האמיתי, כך שאפשר להחיל אותו על מערכי נתונים חדשים של תחזיות בתחומים רבים.

במדריך הזה נסביר איך לפרוס את TimesFM ב-GDC Sandbox. המדריך כולל את המטרות הבאות.

  • יוצרים קונטיינר Docker שמריץ TimesFM,
  • פריסת הקונטיינר באמצעות מעבדי GPU שסופקו על ידי מק"ט של GDC Sandbox שמותאם ל-AI, ו
  • הפעלת פונקציות TimesFM באמצעות בקשות HTTP פשוטות.

לפני שמתחילים

ה-GPU ב-GDC Sandbox נכללים באשכול org-infra.

  • כדי להריץ פקודות מול אשכול התשתית של הארגון, צריך לוודא שיש לכם את קובץ ה-kubeconfig של אשכול org-1-infra, כמו שמתואר במאמר עבודה עם אשכולות:

    • להגדיר ולאמת באמצעות שורת הפקודה gdcloud, וגם
    • יוצרים את קובץ ה-kubeconfig עבור אשכול התשתית של הארגון, ומקצים את הנתיב שלו למשתנה הסביבה KUBECONFIG.
  • מוודאים שהוקצה למשתמש התפקיד sandbox-gpu-admin בפרויקט sandbox-gpu-project. כברירת מחדל, התפקיד מוקצה למשתמש platform-admin. כדי להקצות את התפקיד למשתמשים אחרים, צריך להיכנס לחשבון בתור platform-admin ולהריץ את הפקודה הבאה:

    kubectl --kubeconfig ${KUBECONFIG} create rolebinding ${NAME} --role=sandbox-gpu-admin \
    --user=${USER} --namespace=sandbox-gpu-project
    
  • חשוב להגדיר מאגר Artifact Registry כמו שמתואר במאמר שימוש ב-Artifact Registry ולהיכנס לחשבון כדי להעביר בדחיפה ולמשוך קובצי אימג' ל-Artifact Registry.

פריסת מודל TimesFM

הפריסה מתבצעת באמצעות קבוצה של קובצי הגדרות של Kubernetes (מניפסטים של YAML), שכל אחד מהם מגדיר רכיב או שירות ספציפיים.

  1. יוצרים סקריפט Python מבוסס-Flask app.py עם פונקציות predict לחיזוי סדרות זמן וtimeseries ליצירת תצוגה חזותית על סמך נתוני הבדיקה.

      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. יוצרים קובץ Dockerfile עם timesfm מותקן שמפעיל את האפליקציה.

     # 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.

    
    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-deployment.yaml כדי לפרוס את timesfm.

    הפריסה של בקשות השרת timesfm דורשת 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-service.yaml כדי לחשוף את שרת timesfm באופן פנימי.

    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 פועלים.

    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. מריצים את הפקודה הבאה כדי לזהות את כתובת ה-IP החיצונית של שירות TimesFM. חשוב לשמור את הערך הזה כדי להשתמש בו בשלבים הבאים, שבהם תצטרכו להחליף את הערך 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 בכתובת בפועל של השירות ובערכי הקלט שלכם למאפיינים. הפעולה הזו מפעילה את הפונקציה predict שמוגדרת ב-app.py, שתבצע מניפולציה מסוימת בנתוני הקלט ותחזיר אותם בפורמט JSON.

    curl -X POST http://TIMESFM_END_POINT/predict -H "Content-Type: application/json" -d '{"features": [1.2, 3.4, 5.6]}'
    
  2. כדי לראות דוגמה להמחשת נתונים באמצעות נתונים שנוצרו באופן אקראי, שולחים בקשת curl אל ‎ /timeseries. הפעולה הזו מפעילה את פונקציית סדרת הזמן שמוגדרת בקובץ app.py, שיוצרת סדרת זמן אקראית ומבצעת עליה ניתוח של ממוצע נע.

    curl http://TIMESFM_END_POINT/timeseries