Giới thiệu về Quy trình Vertex

1. Tổng quan

Trong phòng thí nghiệm này, bạn sẽ tìm hiểu cách tạo và chạy quy trình học máy bằng Quy trình đỉnh cao.

Kiến thức bạn sẽ học được

Bạn sẽ tìm hiểu cách:

  • Sử dụng SDK Quy trình Kubeflow để xây dựng các quy trình học máy có thể mở rộng
  • Tạo và chạy quy trình giới thiệu 3 bước nhận thông tin nhập văn bản
  • Tạo và chạy quy trình huấn luyện, đánh giá và triển khai mô hình phân loại AutoML
  • Sử dụng các thành phần tạo sẵn để tương tác với các dịch vụ Vertex AI, được cung cấp thông qua thư viện google_cloud_pipeline_components
  • Lên lịch cho quy trình công việc bằng Cloud Scheduler

Tổng chi phí để chạy phòng thí nghiệm này trên Google Cloud là khoảng 25 USD.

2. Giới thiệu về Vertex AI

Phòng thí nghiệm này sử dụng sản phẩm AI mới nhất có trên Google Cloud. Vertex AI tích hợp các giải pháp học máy trên Google Cloud vào một trải nghiệm phát triển liền mạch. Trước đây, các mô hình được huấn luyện bằng AutoML và mô hình tuỳ chỉnh có thể truy cập được thông qua các dịch vụ riêng biệt. Dịch vụ mới kết hợp cả hai thành một API duy nhất, cùng với các sản phẩm mới khác. Bạn cũng có thể di chuyển các dự án hiện có sang Vertex AI.

Ngoài các dịch vụ huấn luyện và triển khai mô hình, Vertex AI còn có nhiều sản phẩm MLOps, trong đó có Vertex Pipelines (trọng tâm của phòng thí nghiệm này), Giám sát mô hình, Feature Store và nhiều sản phẩm khác. Bạn có thể xem tất cả sản phẩm mà Vertex AI cung cấp trong biểu đồ bên dưới.

Tổng quan về sản phẩm Vertex

Nếu bạn có ý kiến phản hồi, vui lòng xem trang hỗ trợ.

Tại sao quy trình học máy lại hữu ích?

Trước khi tìm hiểu kỹ hơn, trước tiên, hãy tìm hiểu lý do bạn nên sử dụng quy trình. Hãy tưởng tượng bạn đang xây dựng một quy trình công việc học máy bao gồm việc xử lý dữ liệu, huấn luyện mô hình, điều chỉnh siêu tham số, đánh giá và triển khai mô hình. Mỗi bước trong số này có thể có các phần phụ thuộc khác nhau và có thể trở nên khó sử dụng nếu bạn coi toàn bộ quy trình là một khối duy nhất. Khi bắt đầu mở rộng quy trình công nghệ học máy, bạn nên chia sẻ quy trình công việc với công nghệ này với những thành viên khác trong nhóm để họ có thể chạy quy trình và đóng góp mã. Nếu không có một quy trình đáng tin cậy và có thể tái lập, việc này có thể trở nên khó khăn. Với quy trình, mỗi bước trong quy trình học máy của bạn là một vùng chứa riêng. Điều này cho phép bạn phát triển các bước một cách độc lập, đồng thời theo dõi dữ liệu đầu vào và đầu ra của mỗi bước theo cách có thể tái lập. Bạn cũng có thể lên lịch hoặc kích hoạt các lần chạy quy trình dựa trên các sự kiện khác trong môi trường đám mây, chẳng hạn như bắt đầu một quy trình chạy khi có dữ liệu huấn luyện mới.

Tl;dr: quy trình giúp bạn tự động hoátái hiện quy trình công nghệ học máy.

3. Thiết lập môi trường đám mây

Bạn cần một dự án Google Cloud Platform đã bật tính năng thanh toán để chạy lớp học lập trình này. Để tạo một dự án, hãy làm theo hướng dẫn tại đây.

Bước 1: Khởi động Cloud Shell

Trong phòng thí nghiệm này, bạn sẽ thực hiện phiên Cloud Shell, đây là phiên dịch lệnh được lưu trữ bởi một máy ảo chạy trên đám mây của Google. Dù bạn có thể dễ dàng chạy phần này trên máy tính của mình, nhưng việc sử dụng Cloud Shell sẽ giúp mọi người có thể tiếp cận trải nghiệm có thể tái tạo trong một môi trường nhất quán. Sau phòng thí nghiệm này, bạn có thể thử lại phần này trên máy tính của mình.

Cho phép Cloud shell

Kích hoạt Cloud Shell

Ở góc trên cùng bên phải của Cloud Console, hãy nhấp vào nút bên dưới để Kích hoạt Cloud Shell:

Kích hoạt Cloud Shell

Nếu trước đây bạn chưa từng khởi động Cloud Shell, thì bạn sẽ thấy một màn hình trung gian (dưới màn hình đầu tiên) mô tả về ứng dụng này. Nếu trường hợp đó xảy ra, hãy nhấp vào Tiếp tục (và bạn sẽ không thấy thông báo đó nữa). Màn hình một lần đó sẽ có dạng như sau:

Thiết lập Cloud Shell

Quá trình cấp phép và kết nối với Cloud Shell chỉ mất vài phút.

Init Cloud Shell

Máy ảo này chứa tất cả các công cụ phát triển mà bạn cần. Dịch vụ này cung cấp thư mục gốc có dung lượng ổn định 5 GB và chạy trong Google Cloud, giúp nâng cao đáng kể hiệu suất và khả năng xác thực của mạng. Trong lớp học lập trình này, đa số mọi người đều có thể thực hiện chỉ bằng một trình duyệt hoặc Chromebook.

Sau khi kết nối với Cloud Shell, bạn sẽ thấy mình đã được xác thực và dự án đã được đặt thành mã dự án.

Chạy lệnh sau trong Cloud Shell để xác nhận rằng bạn đã được xác thực:

gcloud auth list

Bạn sẽ thấy nội dung tương tự trong kết quả đầu ra của lệnh:

Dữ liệu đầu ra của Cloud Shell

Chạy lệnh sau trong Cloud Shell để xác nhận rằng lệnh gcloud biết về dự án của bạn:

gcloud config list project

Kết quả lệnh

[core]
project = <PROJECT_ID>

Nếu chưa, bạn có thể đặt chế độ này bằng lệnh sau:

gcloud config set project <PROJECT_ID>

Kết quả lệnh

Updated property [core/project].

Cloud Shell có một số biến môi trường, trong đó có GOOGLE_CLOUD_PROJECT chứa tên của dự án Cloud hiện tại của chúng tôi. Chúng tôi sẽ dùng dữ liệu này ở nhiều vị trí trong phòng thí nghiệm này. Bạn có thể xem chỉ số này bằng cách chạy:

echo $GOOGLE_CLOUD_PROJECT

Bước 2: Bật API

Trong các bước sau, bạn sẽ biết những dịch vụ này cần thiết ở đâu (và vì sao). Tuy nhiên, bây giờ, hãy chạy lệnh này để cấp cho dự án của bạn quyền truy cập vào các dịch vụ Compute Engine, Container Registry và Vertex AI:

gcloud services enable compute.googleapis.com         \
                       containerregistry.googleapis.com  \
                       aiplatform.googleapis.com  \
                       cloudbuild.googleapis.com \
                       cloudfunctions.googleapis.com

Thao tác này sẽ tạo ra một thông báo thành công tương tự như thông báo sau:

Operation "operations/acf.cc11852d-40af-47ad-9d59-477a12847c9e" finished successfully.

Bước 3: Tạo một bộ chứa trong Cloud Storage

Để chạy công việc huấn luyện trên Vertex AI, chúng tôi cần có một bộ chứa lưu trữ để lưu trữ những thành phần mô hình đã lưu. Bộ chứa phải là theo khu vực. Chúng tôi đang sử dụng us-central tại đây, nhưng bạn vẫn có thể sử dụng một khu vực khác (chỉ cần thay thế khu vực đó trong toàn bộ phòng thí nghiệm này). Nếu đã có bộ chứa, bạn có thể bỏ qua bước này.

Chạy các lệnh sau trong thiết bị đầu cuối Cloud Shell của bạn để tạo một bộ chứa:

BUCKET_NAME=gs://$GOOGLE_CLOUD_PROJECT-bucket
gsutil mb -l us-central1 $BUCKET_NAME

Tiếp theo, chúng ta sẽ cấp quyền truy cập vào bộ chứa này cho tài khoản dịch vụ điện toán của mình. Điều này giúp đảm bảo rằng Vertex Pipelines có các quyền cần thiết để ghi tệp vào bộ chứa này. Chạy lệnh sau để thêm quyền này:

gcloud projects describe $GOOGLE_CLOUD_PROJECT > project-info.txt
PROJECT_NUM=$(cat project-info.txt | sed -nre 's:.*projectNumber\: (.*):\1:p')
SVC_ACCOUNT="${PROJECT_NUM//\'/}-compute@developer.gserviceaccount.com"
gcloud projects add-iam-policy-binding $GOOGLE_CLOUD_PROJECT --member serviceAccount:$SVC_ACCOUNT --role roles/storage.objectAdmin

Bước 4: Tạo một thực thể Vertex AI Workbench

Trong mục Vertex AI trên Cloud Console, hãy nhấp vào Workbench:

Trình đơn Vertex AI

Tại đó, trong phần Sổ tay do người dùng quản lý, hãy nhấp vào Sổ tay mới:

Tạo sổ tay mới

Sau đó, hãy chọn loại thực thể TensorFlow Enterprise 2.3 (có LTS) không có GPU:

Thực thể TFE

Sử dụng các tuỳ chọn mặc định rồi nhấp vào Tạo.

Bước 5: Mở Sổ tay

Sau khi tạo phiên bản, hãy chọn Open JupyterLab:

Mở Sổ tay

4. Thiết lập Quy trình Vertex

Chúng tôi sẽ cần cài đặt thêm một số thư viện để sử dụng Vertex Pipelines:

  • Quy trình Kubeflow: Đây là SDK mà chúng tôi sẽ sử dụng để xây dựng quy trình của mình. Vertex Pipelines hỗ trợ các quy trình đang chạy được xây dựng bằng cả Quy trình Kubeflow hoặc TFX.
  • Thành phần quy trình của Google Cloud: Thư viện này cung cấp các thành phần được tạo sẵn giúp bạn dễ dàng tương tác với các dịch vụ Vertex AI trong các bước trong quy trình.

Bước 1: Tạo sổ tay Python và cài đặt thư viện

Trước tiên, trên trình đơn Trình chạy trong phiên bản của Sổ tay, hãy tạo một sổ tay bằng cách chọn Python 3:

Tạo sổ tay Python3

Bạn có thể truy cập trình đơn Trình chạy bằng cách nhấp vào dấu + ở trên cùng bên trái của thực thể sổ tay.

Để cài đặt cả hai dịch vụ mà chúng ta sẽ sử dụng trong phòng thí nghiệm này, trước tiên hãy đặt cờ người dùng trong một ô của sổ tay:

USER_FLAG = "--user"

Sau đó, chạy lệnh sau từ sổ tay của bạn:

!pip3 install {USER_FLAG} google-cloud-aiplatform==1.7.0 --upgrade
!pip3 install {USER_FLAG} kfp==1.8.9 google-cloud-pipeline-components==0.2.0

Sau khi cài đặt các gói này, bạn cần khởi động lại nhân:

import os

if not os.getenv("IS_TESTING"):
    # Automatically restart kernel after installs
    import IPython

    app = IPython.Application.instance()
    app.kernel.do_shutdown(True)

Cuối cùng, hãy kiểm tra để đảm bảo bạn đã cài đặt các gói đúng cách. Phiên bản KFP SDK phải>=1.8:

!python3 -c "import kfp; print('KFP SDK version: {}'.format(kfp.__version__))"
!python3 -c "import google_cloud_pipeline_components; print('google_cloud_pipeline_components version: {}'.format(google_cloud_pipeline_components.__version__))"

Bước 2: Đặt mã dự án và bộ chứa

Trong phòng thí nghiệm này, bạn sẽ đề cập đến mã dự án trên Cloud của mình và bộ chứa mà bạn đã tạo trước đó. Tiếp theo, chúng ta sẽ tạo biến cho từng loại.

Nếu không biết mã dự án, bạn có thể lấy mã này bằng cách chạy lệnh sau:

import os
PROJECT_ID = ""

# Get your Google Cloud project ID from gcloud
if not os.getenv("IS_TESTING"):
    shell_output=!gcloud config list --format 'value(core.project)' 2>/dev/null
    PROJECT_ID = shell_output[0]
    print("Project ID: ", PROJECT_ID)

Nếu không, hãy đặt tại đây:

if PROJECT_ID == "" or PROJECT_ID is None:
    PROJECT_ID = "your-project-id"  # @param {type:"string"}

Sau đó, hãy tạo một biến để lưu trữ tên bộ chứa. Nếu bạn đã tạo mã trong phòng thí nghiệm này, thì các bước sau sẽ có hiệu lực. Nếu không, bạn cần đặt chế độ cài đặt này theo cách thủ công:

BUCKET_NAME="gs://" + PROJECT_ID + "-bucket"

Bước 3: Nhập thư viện

Thêm phần sau đây để nhập các thư viện mà chúng ta sẽ sử dụng trong suốt lớp học lập trình này:

import kfp

from kfp.v2 import compiler, dsl
from kfp.v2.dsl import component, pipeline, Artifact, ClassificationMetrics, Input, Output, Model, Metrics

from google.cloud import aiplatform
from google_cloud_pipeline_components import aiplatform as gcc_aip
from typing import NamedTuple

Bước 4: Định nghĩa hằng số

Điều cuối cùng chúng ta cần làm trước khi xây dựng quy trình là xác định một số biến không đổi. PIPELINE_ROOT là đường dẫn của Cloud Storage, nơi các cấu phần phần mềm do quy trình của chúng tôi tạo ra sẽ được ghi. Chúng tôi đang sử dụng us-central1 làm khu vực ở đây, nhưng nếu bạn đã sử dụng một khu vực khác khi tạo bộ chứa của mình, hãy cập nhật biến REGION trong đoạn mã dưới đây:

PATH=%env PATH
%env PATH={PATH}:/home/jupyter/.local/bin
REGION="us-central1"

PIPELINE_ROOT = f"{BUCKET_NAME}/pipeline_root/"
PIPELINE_ROOT

Sau khi chạy mã ở trên, bạn sẽ thấy thư mục gốc cho quy trình của mình được in ra. Đây là vị trí của Cloud Storage, nơi các cấu phần phần mềm trong quy trình của bạn sẽ được ghi. Mã này sẽ có định dạng gs://YOUR-BUCKET-NAME/pipeline_root/

5. Tạo quy trình đầu tiên

Để làm quen với cách hoạt động của Vertex Pipelines, trước tiên chúng ta sẽ tạo một quy trình ngắn bằng SDK KFP. Quy trình này không làm bất cứ điều gì liên quan đến máy học (đừng lo, chúng tôi sẽ giải quyết vấn đề đó!), chúng tôi sẽ sử dụng quy trình này để hướng dẫn bạn:

  • Cách tạo thành phần tuỳ chỉnh trong SDK KFP
  • Cách chạy và giám sát quy trình trong Vertex Pipelines

Chúng ta sẽ tạo một quy trình để in một câu bằng 2 kết quả đầu ra: tên sản phẩm và nội dung mô tả biểu tượng cảm xúc. Quy trình này sẽ bao gồm ba thành phần:

  • product_name: Thành phần này sẽ lấy tên sản phẩm (hoặc bất kỳ danh từ nào bạn muốn) làm dữ liệu đầu vào và trả về chuỗi đó làm dữ liệu đầu ra
  • emoji: Thành phần này sẽ lấy nội dung mô tả dạng văn bản của một biểu tượng cảm xúc và chuyển đổi nội dung đó thành biểu tượng cảm xúc. Ví dụ: mã văn bản cho ✨ là "sparkles". Thành phần này sử dụng thư viện biểu tượng cảm xúc để hướng dẫn bạn cách quản lý các phần phụ thuộc bên ngoài trong quy trình
  • build_sentence: Thành phần cuối cùng này sẽ sử dụng kết quả của hai thành phần trước để tạo một câu sử dụng biểu tượng cảm xúc. Ví dụ: kết quả có thể là "Vertex Pipelines is ✨".

Hãy bắt đầu lập trình!

Bước 1: Tạo thành phần dựa trên hàm Python

Khi sử dụng KFP SDK, chúng ta có thể tạo các thành phần dựa trên các hàm Python. Chúng ta sẽ sử dụng thuộc tính đó cho 3 thành phần trong quy trình đầu tiên. Trước tiên, chúng ta sẽ tạo thành phần product_name. Thành phần này chỉ lấy một chuỗi làm dữ liệu đầu vào và trả về chuỗi đó. Thêm các mục sau vào sổ tay của bạn:

@component(base_image="python:3.9", output_component_file="first-component.yaml")
def product_name(text: str) -> str:
    return text

Hãy cùng tìm hiểu kỹ hơn về cú pháp ở đây:

  • Trình trang trí @component biên dịch hàm này thành một thành phần khi quy trình được chạy. Bạn sẽ dùng thuộc tính này mỗi khi viết một thành phần tuỳ chỉnh.
  • Tham số base_image chỉ định hình ảnh vùng chứa mà thành phần này sẽ sử dụng.
  • Tham số output_component_file là không bắt buộc và chỉ định tệp yaml để ghi thành phần đã biên dịch vào đó. Sau khi chạy ô, bạn sẽ thấy tệp được ghi vào thực thể sổ tay của mình. Nếu muốn chia sẻ thành phần này với người khác, bạn có thể gửi cho họ tệp yaml đã tạo và yêu cầu họ tải tệp như sau:
product_name_component = kfp.components.load_component_from_file('./first-component.yaml')
  • -> str sau định nghĩa hàm sẽ chỉ định kiểu đầu ra cho thành phần này.

Bước 2: Tạo thêm 2 thành phần

Để hoàn tất quy trình, chúng ta sẽ tạo thêm 2 thành phần. Chuỗi đầu tiên chúng ta sẽ định nghĩa sẽ lấy một chuỗi làm dữ liệu đầu vào và chuyển đổi chuỗi này thành biểu tượng cảm xúc tương ứng (nếu có). Hàm này trả về một bộ dữ liệu với văn bản nhập được truyền và kết quả là biểu tượng cảm xúc:

@component(packages_to_install=["emoji"])
def emoji(
    text: str,
) -> NamedTuple(
    "Outputs",
    [
        ("emoji_text", str),  # Return parameters
        ("emoji", str),
    ],
):
    import emoji

    emoji_text = text
    emoji_str = emoji.emojize(':' + emoji_text + ':', language='alias')
    print("output one: {}; output_two: {}".format(emoji_text, emoji_str))
    return (emoji_text, emoji_str)

Thành phần này phức tạp hơn một chút so với thành phần trước đó. Hãy cùng phân tích các điểm mới:

  • Tham số packages_to_install cho thành phần biết mọi phần phụ thuộc của thư viện bên ngoài đối với vùng chứa này. Trong trường hợp này, chúng ta sẽ sử dụng một thư viện có tên là biểu tượng cảm xúc.
  • Thành phần này trả về một NamedTuple có tên là Outputs. Lưu ý rằng mỗi chuỗi trong bộ dữ liệu này có các khoá: emoji_textemoji. Chúng ta sẽ sử dụng các biến này trong thành phần tiếp theo để truy cập vào kết quả.

Thành phần cuối cùng trong quy trình này sẽ sử dụng kết quả của hai thành phần đầu tiên và kết hợp chúng để trả về một chuỗi:

@component
def build_sentence(
    product: str,
    emoji: str,
    emojitext: str
) -> str:
    print("We completed the pipeline, hooray!")
    end_str = product + " is "
    if len(emoji) > 0:
        end_str += emoji
    else:
        end_str += emojitext
    return(end_str)

Bạn có thể thắc mắc: làm thế nào để thành phần này biết sử dụng dữ liệu đầu ra từ các bước trước mà bạn đã xác định? Đây là một câu hỏi rất hay! Chúng ta sẽ kết hợp tất cả lại với nhau trong bước tiếp theo.

Bước 3: Tập hợp các thành phần lại với nhau thành một quy trình

Các định nghĩa thành phần mà chúng ta đã định nghĩa ở trên các hàm nhà máy đã tạo có thể được sử dụng trong định nghĩa quy trình để tạo các bước. Để thiết lập một quy trình, hãy sử dụng trình trang trí @pipeline, đặt tên và đặt tên và mô tả cho quy trình đó, đồng thời cung cấp đường dẫn gốc nơi cấu phần phần mềm của quy trình sẽ được viết. Cấu phần phần mềm ở đây là mọi tệp đầu ra do quy trình của bạn tạo ra. Quy trình giới thiệu này không tạo ra bất kỳ quy trình nào nhưng quy trình tiếp theo của chúng tôi sẽ tạo ra.

Trong khối mã tiếp theo, chúng ta xác định hàm intro_pipeline. Đây là nơi chúng ta chỉ định dữ liệu đầu vào cho các bước quy trình ban đầu và cách các bước kết nối với nhau:

  • product_task lấy tên sản phẩm làm thông tin đầu vào. Ở đây, chúng ta đang truyền "Quy trình đỉnh cao" nhưng bạn có thể thay đổi thành bất kỳ thứ gì bạn muốn.
  • emoji_task lấy mã văn bản cho một biểu tượng cảm xúc làm thông tin đầu vào. Bạn cũng có thể thay đổi tuỳ chọn này thành bất kỳ thông tin nào bạn muốn. Ví dụ: "party_face" đề cập đến biểu tượng cảm xúc 🥳. Xin lưu ý rằng vì cả thành phần này và thành phần product_task đều không có bước nào để nhập dữ liệu đầu vào, nên chúng ta sẽ chỉ định dữ liệu đầu vào cho các thành phần này theo cách thủ công khi xác định quy trình.
  • Bước cuối cùng trong quy trình của chúng ta – consumer_task có 3 tham số đầu vào:
    • Kết quả về product_task. Vì bước này chỉ tạo ra một đầu ra, nên chúng ta có thể tham chiếu đến bước này thông qua product_task.output.
    • Kết quả emoji của bước emoji_task. Xem thành phần emoji được xác định ở trên, nơi chúng ta đã đặt tên cho các tham số đầu ra.
    • Tương tự, đầu ra được đặt tên emoji_text từ thành phần emoji. Trong trường hợp quy trình được truyền văn bản không tương ứng với biểu tượng cảm xúc, quy trình sẽ sử dụng văn bản này để dựng một câu.
@pipeline(
    name="hello-world",
    description="An intro pipeline",
    pipeline_root=PIPELINE_ROOT,
)

# You can change the `text` and `emoji_str` parameters here to update the pipeline output
def intro_pipeline(text: str = "Vertex Pipelines", emoji_str: str = "sparkles"):
    product_task = product_name(text)
    emoji_task = emoji(emoji_str)
    consumer_task = build_sentence(
        product_task.output,
        emoji_task.outputs["emoji"],
        emoji_task.outputs["emoji_text"],
    )

Bước 4: Biên dịch và chạy quy trình

Khi đã xác định quy trình, bạn đã sẵn sàng để biên dịch. Thao tác sau đây sẽ tạo một tệp JSON mà bạn sẽ dùng để chạy quy trình:

compiler.Compiler().compile(
    pipeline_func=intro_pipeline, package_path="intro_pipeline_job.json"
)

Tiếp theo, hãy tạo biến TIMESTAMP. Chúng ta sẽ sử dụng thông tin này trong mã công việc:

from datetime import datetime

TIMESTAMP = datetime.now().strftime("%Y%m%d%H%M%S")

Sau đó, hãy xác định tác vụ quy trình của bạn:

job = aiplatform.PipelineJob(
    display_name="hello-world-pipeline",
    template_path="intro_pipeline_job.json",
    job_id="hello-world-pipeline-{0}".format(TIMESTAMP),
    enable_caching=True
)

Cuối cùng, hãy chạy công việc để tạo thực thi quy trình mới:

job.submit()

Sau khi chạy ô này, bạn sẽ thấy nhật ký chứa một đường liên kết để xem quy trình chạy trong bảng điều khiển của mình:

Nhật ký công việc của quy trình

Chuyển đến đường liên kết đó. Quy trình của bạn sẽ có dạng như sau khi hoàn tất:

Đã hoàn tất quy trình giới thiệu

Quy trình này sẽ mất 5-6 phút để chạy. Sau khi hoàn tất, bạn có thể nhấp vào thành phần build-sentence để xem kết quả cuối cùng:

Kết quả của quy trình giới thiệu

Giờ đây, khi đã quen thuộc với cách hoạt động của SDK KFP và Vertex Pipelines, bạn đã sẵn sàng xây dựng một quy trình giúp tạo và triển khai mô hình học máy bằng các dịch vụ khác của Vertex AI. Chúng ta hãy cùng tìm hiểu kỹ hơn nhé.

6. Xây dựng quy trình học máy toàn diện

Đã đến lúc xây dựng quy trình đầu tiên cho công nghệ học máy. Trong quy trình này, chúng tôi sẽ sử dụng tập dữ liệu về đậu nành khô của UCI Machine Learning, từ: KOKLU, M. và OZKAN, I.A., (2020), "Phân loại đa lớp hạt khô bằng cách sử dụng thị giác máy tính và kỹ thuật học máy." Trong Máy tính và Điện tử trong Nông nghiệp, 174, 105507. DOI (Yêu cầu quyền).

Đây là một tập dữ liệu dạng bảng và trong quy trình, chúng tôi sẽ sử dụng tập dữ liệu để huấn luyện, đánh giá và triển khai một mô hình AutoML giúp phân loại các loại đậu thành một trong 7 loại dựa trên đặc điểm của chúng.

Quy trình này sẽ:

  • Tạo một Tập dữ liệu trong
  • Huấn luyện mô hình phân loại dạng bảng bằng AutoML
  • Nhận các chỉ số đánh giá trên mô hình này
  • Dựa trên các chỉ số đánh giá, hãy quyết định xem có triển khai mô hình bằng logic có điều kiện trong Quy trình Vertex hay không
  • Triển khai mô hình cho một điểm cuối bằng công cụ Vertex Prediction

Mỗi bước được nêu sẽ là một thành phần. Hầu hết các bước trong quy trình đều sử dụng thành phần tạo sẵn cho các dịch vụ Vertex AI thông qua thư viện google_cloud_pipeline_components mà chúng tôi đã nhập trước đó trong lớp học lập trình này. Trong phần này, trước tiên, chúng ta sẽ xác định một thành phần tuỳ chỉnh. Sau đó, chúng ta sẽ xác định các bước còn lại của quy trình bằng cách sử dụng các thành phần được tạo sẵn. Các thành phần được tạo sẵn giúp bạn dễ dàng truy cập vào các dịch vụ của Vertex AI, chẳng hạn như huấn luyện và triển khai mô hình.

Bước 1: Một thành phần tuỳ chỉnh để đánh giá mô hình

Thành phần tuỳ chỉnh mà chúng ta sẽ xác định sẽ được sử dụng vào cuối quy trình sau khi quá trình huấn luyện mô hình hoàn tất. Thành phần này sẽ thực hiện một số việc:

  • Lấy chỉ số đánh giá từ mô hình phân loại AutoML đã được huấn luyện
  • Phân tích cú pháp các chỉ số và kết xuất các chỉ số đó trong giao diện người dùng Vertex Pipelines
  • So sánh các chỉ số với một ngưỡng để xác định xem có nên triển khai mô hình hay không

Trước khi định nghĩa thành phần, hãy tìm hiểu các tham số đầu vào và đầu ra của thành phần đó. Làm dữ liệu đầu vào, quy trình này sẽ lấy một số siêu dữ liệu về dự án Cloud của chúng tôi, mô hình đã huấn luyện thu được (chúng ta sẽ xác định thành phần này sau), các chỉ số đánh giá của mô hình và một thresholds_dict_str. thresholds_dict_str là nội dung chúng ta sẽ xác định khi chạy quy trình. Trong trường hợp mô hình phân loại này, đây sẽ là phần nằm dưới giá trị đường cong ROC mà chúng ta nên triển khai mô hình. Ví dụ: nếu chúng ta chuyển vào 0, 95, điều đó có nghĩa là chúng ta chỉ muốn quy trình triển khai mô hình nếu chỉ số này lớn hơn 95%.

Thành phần đánh giá của chúng ta sẽ trả về một chuỗi cho biết có triển khai mô hình hay không. Thêm nội dung sau vào một ô sổ tay để tạo thành phần tuỳ chỉnh này:

@component(
    base_image="gcr.io/deeplearning-platform-release/tf2-cpu.2-3:latest",
    output_component_file="tabular_eval_component.yaml",
    packages_to_install=["google-cloud-aiplatform"],
)
def classification_model_eval_metrics(
    project: str,
    location: str,  # "us-central1",
    api_endpoint: str,  # "us-central1-aiplatform.googleapis.com",
    thresholds_dict_str: str,
    model: Input[Artifact],
    metrics: Output[Metrics],
    metricsc: Output[ClassificationMetrics],
) -> NamedTuple("Outputs", [("dep_decision", str)]):  # Return parameter.

    import json
    import logging

    from google.cloud import aiplatform as aip

    # Fetch model eval info
    def get_eval_info(client, model_name):
        from google.protobuf.json_format import MessageToDict

        response = client.list_model_evaluations(parent=model_name)
        metrics_list = []
        metrics_string_list = []
        for evaluation in response:
            print("model_evaluation")
            print(" name:", evaluation.name)
            print(" metrics_schema_uri:", evaluation.metrics_schema_uri)
            metrics = MessageToDict(evaluation._pb.metrics)
            for metric in metrics.keys():
                logging.info("metric: %s, value: %s", metric, metrics[metric])
            metrics_str = json.dumps(metrics)
            metrics_list.append(metrics)
            metrics_string_list.append(metrics_str)

        return (
            evaluation.name,
            metrics_list,
            metrics_string_list,
        )

    # Use the given metrics threshold(s) to determine whether the model is
    # accurate enough to deploy.
    def classification_thresholds_check(metrics_dict, thresholds_dict):
        for k, v in thresholds_dict.items():
            logging.info("k {}, v {}".format(k, v))
            if k in ["auRoc", "auPrc"]:  # higher is better
                if metrics_dict[k] < v:  # if under threshold, don't deploy
                    logging.info("{} < {}; returning False".format(metrics_dict[k], v))
                    return False
        logging.info("threshold checks passed.")
        return True

    def log_metrics(metrics_list, metricsc):
        test_confusion_matrix = metrics_list[0]["confusionMatrix"]
        logging.info("rows: %s", test_confusion_matrix["rows"])

        # log the ROC curve
        fpr = []
        tpr = []
        thresholds = []
        for item in metrics_list[0]["confidenceMetrics"]:
            fpr.append(item.get("falsePositiveRate", 0.0))
            tpr.append(item.get("recall", 0.0))
            thresholds.append(item.get("confidenceThreshold", 0.0))
        print(f"fpr: {fpr}")
        print(f"tpr: {tpr}")
        print(f"thresholds: {thresholds}")
        metricsc.log_roc_curve(fpr, tpr, thresholds)

        # log the confusion matrix
        annotations = []
        for item in test_confusion_matrix["annotationSpecs"]:
            annotations.append(item["displayName"])
        logging.info("confusion matrix annotations: %s", annotations)
        metricsc.log_confusion_matrix(
            annotations,
            test_confusion_matrix["rows"],
        )

        # log textual metrics info as well
        for metric in metrics_list[0].keys():
            if metric != "confidenceMetrics":
                val_string = json.dumps(metrics_list[0][metric])
                metrics.log_metric(metric, val_string)
        # metrics.metadata["model_type"] = "AutoML Tabular classification"

    logging.getLogger().setLevel(logging.INFO)
    aip.init(project=project)
    # extract the model resource name from the input Model Artifact
    model_resource_path = model.metadata["resourceName"]
    logging.info("model path: %s", model_resource_path)

    client_options = {"api_endpoint": api_endpoint}
    # Initialize client that will be used to create and send requests.
    client = aip.gapic.ModelServiceClient(client_options=client_options)
    eval_name, metrics_list, metrics_str_list = get_eval_info(
        client, model_resource_path
    )
    logging.info("got evaluation name: %s", eval_name)
    logging.info("got metrics list: %s", metrics_list)
    log_metrics(metrics_list, metricsc)

    thresholds_dict = json.loads(thresholds_dict_str)
    deploy = classification_thresholds_check(metrics_list[0], thresholds_dict)
    if deploy:
        dep_decision = "true"
    else:
        dep_decision = "false"
    logging.info("deployment decision is %s", dep_decision)

    return (dep_decision,)

Bước 2: Thêm các thành phần tích hợp sẵn của Google Cloud

Ở bước này, chúng ta sẽ xác định phần còn lại của các thành phần quy trình và xem chúng kết hợp với nhau như thế nào. Đầu tiên, hãy xác định tên hiển thị cho lần chạy quy trình bằng cách sử dụng dấu thời gian:

import time
DISPLAY_NAME = 'automl-beans{}'.format(str(int(time.time())))
print(DISPLAY_NAME)

Sau đó, hãy sao chép mục sau vào một ô mới trong sổ tay:

@pipeline(name="automl-tab-beans-training-v2",
                  pipeline_root=PIPELINE_ROOT)
def pipeline(
    bq_source: str = "bq://aju-dev-demos.beans.beans1",
    display_name: str = DISPLAY_NAME,
    project: str = PROJECT_ID,
    gcp_region: str = "us-central1",
    api_endpoint: str = "us-central1-aiplatform.googleapis.com",
    thresholds_dict_str: str = '{"auRoc": 0.95}',
):
    dataset_create_op = gcc_aip.TabularDatasetCreateOp(
        project=project, display_name=display_name, bq_source=bq_source
    )

    training_op = gcc_aip.AutoMLTabularTrainingJobRunOp(
        project=project,
        display_name=display_name,
        optimization_prediction_type="classification",
        budget_milli_node_hours=1000,
        column_transformations=[
            {"numeric": {"column_name": "Area"}},
            {"numeric": {"column_name": "Perimeter"}},
            {"numeric": {"column_name": "MajorAxisLength"}},
            {"numeric": {"column_name": "MinorAxisLength"}},
            {"numeric": {"column_name": "AspectRation"}},
            {"numeric": {"column_name": "Eccentricity"}},
            {"numeric": {"column_name": "ConvexArea"}},
            {"numeric": {"column_name": "EquivDiameter"}},
            {"numeric": {"column_name": "Extent"}},
            {"numeric": {"column_name": "Solidity"}},
            {"numeric": {"column_name": "roundness"}},
            {"numeric": {"column_name": "Compactness"}},
            {"numeric": {"column_name": "ShapeFactor1"}},
            {"numeric": {"column_name": "ShapeFactor2"}},
            {"numeric": {"column_name": "ShapeFactor3"}},
            {"numeric": {"column_name": "ShapeFactor4"}},
            {"categorical": {"column_name": "Class"}},
        ],
        dataset=dataset_create_op.outputs["dataset"],
        target_column="Class",
    )
    model_eval_task = classification_model_eval_metrics(
        project,
        gcp_region,
        api_endpoint,
        thresholds_dict_str,
        training_op.outputs["model"],
    )

    with dsl.Condition(
        model_eval_task.outputs["dep_decision"] == "true",
        name="deploy_decision",
    ):

        endpoint_op = gcc_aip.EndpointCreateOp(
            project=project,
            location=gcp_region,
            display_name="train-automl-beans",
        )

        gcc_aip.ModelDeployOp(
            model=training_op.outputs["model"],
            endpoint=endpoint_op.outputs["endpoint"],
            dedicated_resources_min_replica_count=1,
            dedicated_resources_max_replica_count=1,
            dedicated_resources_machine_type="n1-standard-4",
        )

Hãy xem điều gì đang xảy ra trong mã này:

  • Trước tiên, giống như trong quy trình trước, chúng ta xác định các tham số đầu vào mà quy trình này nhận. Chúng ta cần đặt các giá trị này theo cách thủ công vì chúng không phụ thuộc vào kết quả của các bước khác trong quy trình.
  • Phần còn lại của quy trình sử dụng một số thành phần tạo sẵn để tương tác với các dịch vụ Vertex AI:
    • TabularDatasetCreateOp tạo một tập dữ liệu dạng bảng trong Vertex AI dựa trên một nguồn tập dữ liệu trong Cloud Storage hoặc BigQuery. Trong quy trình này, chúng tôi sẽ chuyển dữ liệu thông qua URL của bảng BigQuery
    • AutoMLTabularTrainingJobRunOp bắt đầu công việc huấn luyện AutoML cho một tập dữ liệu dạng bảng. Chúng ta truyền một vài tham số cấu hình cho thành phần này, bao gồm loại mô hình (trong trường hợp này là phân loại), một số dữ liệu trên các cột, khoảng thời gian chúng ta muốn chạy huấn luyện và con trỏ đến tập dữ liệu. Lưu ý để truyền tập dữ liệu vào thành phần này, chúng ta đang cung cấp kết quả của thành phần trước đó thông qua dataset_create_op.outputs["dataset"]
    • EndpointCreateOp tạo một điểm cuối trong Vertex AI. Điểm cuối được tạo ở bước này sẽ được chuyển dưới dạng dữ liệu đầu vào cho thành phần tiếp theo
    • ModelDeployOp triển khai một mô hình nhất định cho một điểm cuối trong Vertex AI. Trong trường hợp này, chúng ta sẽ sử dụng điểm cuối đã tạo ở bước trước. Có các lựa chọn khác về cấu hình. Tuy nhiên, ở đây chúng tôi chỉ cung cấp loại máy điểm cuối và mô hình mà chúng tôi muốn triển khai. Chúng ta truyền mô hình bằng cách truy cập kết quả của bước huấn luyện trong quy trình
  • Quy trình này cũng sử dụng logic có điều kiện, một tính năng của Quy trình Vertex cho phép bạn xác định một điều kiện cùng với nhiều nhánh dựa trên kết quả của điều kiện đó. Hãy nhớ rằng khi xác định quy trình, chúng ta đã truyền tham số thresholds_dict_str. Đây là ngưỡng chính xác mà chúng tôi đang sử dụng để xác định xem có triển khai mô hình cho một điểm cuối hay không. Để triển khai việc này, chúng ta sẽ sử dụng lớp Condition trong SDK KFP. Điều kiện chúng ta truyền vào là kết quả của thành phần eval tuỳ chỉnh mà chúng ta đã xác định trước đó trong lớp học lập trình này. Nếu điều kiện này đúng, quy trình sẽ tiếp tục thực thi thành phần deploy_op. Nếu độ chính xác không đáp ứng ngưỡng đã xác định trước của chúng tôi, thì quy trình sẽ dừng ở đây và sẽ không triển khai mô hình.

Bước 3: Biên dịch và chạy quy trình học máy toàn diện

Sau khi quy trình đầy đủ đã được xác định, đã đến lúc biên dịch:

compiler.Compiler().compile(
    pipeline_func=pipeline, package_path="tab_classif_pipeline.json"
)

Tiếp theo, hãy xác định công việc:

ml_pipeline_job = aiplatform.PipelineJob(
    display_name="automl-tab-beans-training",
    template_path="tab_classif_pipeline.json",
    pipeline_root=PIPELINE_ROOT,
    parameter_values={"project": PROJECT_ID, "display_name": DISPLAY_NAME},
    enable_caching=True
)

Cuối cùng, hãy thực hiện tác vụ:

ml_pipeline_job.submit()

Chuyển đến đường liên kết xuất hiện trong nhật ký sau khi chạy ô ở trên để xem quy trình của bạn trong bảng điều khiển. Quy trình này sẽ mất hơn một giờ để chạy. Phần lớn thời gian đều dành cho bước huấn luyện AutoML. Quy trình hoàn chỉnh sẽ có dạng như sau:

Quy trình AutoML đã hoàn tất

Nếu bạn bật/tắt nút "Mở rộng cấu phần phần mềm" ở trên cùng, bạn có thể xem thông tin chi tiết về nhiều cấu phần phần mềm được tạo qua quy trình. Ví dụ: nếu nhấp vào cấu phần phần mềm dataset, bạn sẽ thấy thông tin chi tiết về tập dữ liệu Vertex AI đã tạo. Bạn có thể nhấp vào đường liên kết tại đây để chuyển đến trang chứa tập dữ liệu đó:

Tập dữ liệu quy trình

Tương tự, để xem hình ảnh trực quan của các chỉ số thu được từ thành phần đánh giá tuỳ chỉnh, hãy nhấp vào cấu phần phần mềm có tên là chisoc. Ở bên phải trang tổng quan, bạn sẽ có thể thấy ma trận nhầm lẫn cho mô hình sau:

Hình ảnh hoá chỉ số

Để xem mô hình và điểm cuối được tạo trong lần chạy quy trình này, hãy chuyển đến phần mô hình rồi nhấp vào mô hình có tên automl-beans. Tại đó, bạn sẽ thấy mô hình này được triển khai cho một điểm cuối:

Điểm cuối của mô hình

Bạn cũng có thể truy cập vào trang này bằng cách nhấp vào cấu phần phần mềm điểm cuối trong biểu đồ quy trình.

Ngoài việc xem biểu đồ quy trình trong bảng điều khiển, bạn cũng có thể sử dụng Quy trình Vertex cho tính năng Theo dõi dòng. Theo dõi nối tiếp, chúng tôi muốn nói đến việc theo dõi các cấu phần phần mềm được tạo trong toàn bộ quy trình của bạn. Điều này có thể giúp chúng tôi nắm được vị trí tạo cấu phần phần mềm và cách sử dụng các cấu phần phần mềm đó trong suốt quy trình hoạt động của công nghệ học máy. Ví dụ: để xem hoạt động theo dõi nối tiếp cho tập dữ liệu được tạo trong quy trình này, hãy nhấp vào cấu phần phần mềm tập dữ liệu, rồi nhấp vào Xem dữ liệu dòng thừa:

Xem dòng dõi

Thông tin này cho chúng tôi biết tất cả các địa điểm mà cấu phần phần mềm này đang được sử dụng:

Thông tin chi tiết về dòng dõi

Bước 4: So sánh các chỉ số trong các lần chạy quy trình

Nếu chạy quy trình này nhiều lần, bạn nên so sánh các chỉ số giữa các lần chạy. Bạn có thể dùng phương thức aiplatform.get_pipeline_df() để truy cập vào siêu dữ liệu đang chạy. Tại đây, chúng ta sẽ nhận được siêu dữ liệu cho tất cả các lần chạy của quy trình này và tải siêu dữ liệu vào một Pandas DataFrame:

pipeline_df = aiplatform.get_pipeline_df(pipeline="automl-tab-beans-training-v2")
small_pipeline_df = pipeline_df.head(2)
small_pipeline_df

Vậy là bạn đã hoàn thành phòng thí nghiệm!

🎉 Xin chúc mừng! 🎉

Bạn đã tìm hiểu cách sử dụng Vertex AI để:

  • Sử dụng SDK Kubeflow Pipelines để xây dựng quy trình toàn diện với các thành phần tuỳ chỉnh
  • Chạy quy trình của bạn trên Vertex Pipelines và bắt đầu chạy quy trình bằng SDK
  • Xem và phân tích biểu đồ Vertex Pipelines trong bảng điều khiển
  • Sử dụng các thành phần tạo sẵn của quy trình để thêm các dịch vụ Vertex AI vào quy trình của bạn
  • Lên lịch cho các công việc trong quy trình định kỳ

Để tìm hiểu thêm về các phần khác nhau của Vertex, hãy xem tài liệu này.

7. Dọn dẹp

Để không bị tính phí, bạn nên xoá các tài nguyên đã tạo trong toàn bộ phòng thí nghiệm này.

Bước 1: Dừng hoặc xoá phiên bản Sổ tay

Nếu muốn tiếp tục sử dụng sổ tay mà bạn đã tạo trong phòng thí nghiệm này, bạn nên tắt sổ tay đó khi không sử dụng. Trên giao diện người dùng của Sổ tay trong Cloud Console, hãy chọn sổ tay đó rồi chọn Dừng. Nếu bạn muốn xoá hoàn toàn phiên bản, hãy chọn Xoá:

Dừng thực thể

Bước 2: Xoá điểm cuối

Để xoá điểm cuối bạn đã triển khai, hãy chuyển đến phần Điểm cuối trong bảng điều khiển Vertex AI rồi nhấp vào biểu tượng xoá:

Xoá điểm cuối

Sau đó, nhấp vào UnDeploy (Huỷ triển khai) trên lời nhắc sau:

Huỷ triển khai mô hình

Cuối cùng, hãy chuyển đến phần Kiểu máy của bảng điều khiển, tìm kiểu máy đó rồi nhấp vào Xoá mô hình trong trình đơn có biểu tượng ba dấu chấm ở bên phải:

Xoá mô hình

Bước 3: Xoá bộ chứa trong Cloud Storage

Để xoá Bộ chứa Storage, hãy sử dụng trình đơn Điều hướng trong Cloud Console, duyệt đến Bộ nhớ, chọn bộ chứa rồi nhấp vào Xoá:

Xoá bộ nhớ