Trả lời cho sự kiện TensorFlow Everywhere tại địa phương của bạn ngay hôm nay!
Trang này được dịch bởi Cloud Translation API.
Switch to English

Xây dựng các thành phần tùy chỉnh hoàn toàn

Hướng dẫn này mô tả cách sử dụng API TFX để tạo thành phần hoàn toàn tùy chỉnh. Các thành phần hoàn toàn tùy chỉnh cho phép bạn xây dựng các thành phần bằng cách xác định đặc tả thành phần, trình thực thi và các lớp giao diện thành phần. Cách tiếp cận này cho phép bạn sử dụng lại và mở rộng một thành phần tiêu chuẩn để phù hợp với nhu cầu của bạn.

Nếu bạn chưa quen với đường ống TFX, hãy tìm hiểu thêm về các khái niệm cốt lõi của đường ống TFX .

Trình thực thi tùy chỉnh hoặc thành phần tùy chỉnh

Nếu chỉ cần logic xử lý tùy chỉnh trong khi các đầu vào, đầu ra và thuộc tính thực thi của thành phần giống như một thành phần hiện có, thì một trình thực thi tùy chỉnh là đủ. Một thành phần tùy chỉnh hoàn toàn là cần thiết khi bất kỳ đầu vào, đầu ra hoặc thuộc tính thực thi nào khác với bất kỳ thành phần TFX hiện có nào.

Làm thế nào để tạo một thành phần tùy chỉnh?

Việc phát triển một thành phần tùy chỉnh hoàn toàn yêu cầu:

  • Một tập hợp các thông số kỹ thuật tạo tác đầu vào và đầu ra được xác định cho thành phần mới. Đặc biệt, loại hiện vật đầu vào phải phù hợp với loại hiện vật đầu ra của bộ phận sản xuất hiện vật và loại hiện vật đầu ra phải phù hợp với loại hiện vật đầu vào của bộ phận tiêu thụ hiện vật nếu có.
  • Các tham số thực thi không phải cấu phần cần thiết cho thành phần mới.

ComponentSpec

Lớp ComponentSpec định nghĩa hợp đồng thành phần bằng cách xác định các tạo tác đầu vào và đầu ra cho một thành phần cũng như các tham số được sử dụng để thực thi thành phần. Có ba phần trong đó:

  • INPUTS : Từ điển các tham số đã nhập cho các tạo tác đầu vào nằm trong trình thực thi thành phần. Thông thường đầu vào tạo tác là đầu ra từ các thành phần thượng nguồn và do đó có cùng một loại.
  • OUTPUTS : Một từ điển các tham số đã nhập cho các tạo tác đầu ra mà thành phần tạo ra.
  • PARAMETERS : Một từ điển gồm các mục ExecutionParameter bổ sung sẽ được chuyển vào trình thực thi thành phần. Đây là các tham số không phải là tạo tác mà chúng ta muốn xác định một cách linh hoạt trong DSL đường ống và chuyển vào thực thi.

Đây là một ví dụ về ComponentSpec:

class HelloComponentSpec(types.ComponentSpec):
  """ComponentSpec for Custom TFX Hello World Component."""

  PARAMETERS = {
      # These are parameters that will be passed in the call to
      # create an instance of this component.
      'name': ExecutionParameter(type=Text),
  }
  INPUTS = {
      # This will be a dictionary with input artifacts, including URIs
      'input_data': ChannelParameter(type=standard_artifacts.Examples),
  }
  OUTPUTS = {
      # This will be a dictionary which this component will populate
      'output_data': ChannelParameter(type=standard_artifacts.Examples),
  }

Người thừa hành

Tiếp theo, viết mã thực thi cho thành phần mới. Về cơ bản, một lớp con mới của base_executor.BaseExecutor cần được tạo với chức năng Do của nó được ghi đè. Trong hàm Do , các đối số input_dict , output_dictexec_properties được chuyển trong ánh xạ tới INPUTS , OUTPUTSPARAMETERS được định nghĩa trong ComponentSpec tương ứng. Đối với thực exec_properties , giá trị có thể được tìm nạp trực tiếp thông qua tra cứu từ điển. Đối với hiện vật trong input_dictoutput_dict , có chức năng thuận tiện có sẵn trong artifact_utils lớp có thể được sử dụng để lấy vật dụ hoặc vật uri.

class Executor(base_executor.BaseExecutor):
  """Executor for HelloComponent."""

  def Do(self, input_dict: Dict[Text, List[types.Artifact]],
         output_dict: Dict[Text, List[types.Artifact]],
         exec_properties: Dict[Text, Any]) -> None:
    ...

    split_to_instance = {}
    for artifact in input_dict['input_data']:
      for split in json.loads(artifact.split_names):
        uri = os.path.join(artifact.uri, split)
        split_to_instance[split] = uri

    for split, instance in split_to_instance.items():
      input_dir = instance
      output_dir = artifact_utils.get_split_uri(
          output_dict['output_data'], split)
      for filename in tf.io.gfile.listdir(input_dir):
        input_uri = os.path.join(input_dir, filename)
        output_uri = os.path.join(output_dir, filename)
        io_utils.copy_file(src=input_uri, dst=output_uri, overwrite=True)

Đơn vị kiểm tra một trình thực thi tùy chỉnh

Các bài kiểm tra đơn vị cho trình thực thi tùy chỉnh có thể được tạo tương tự như bài kiểm tra này .

Giao diện thành phần

Bây giờ, phần phức tạp nhất đã hoàn thành, bước tiếp theo là lắp ráp các phần này thành một giao diện thành phần, để cho phép thành phần được sử dụng trong một đường ống. Có một số bước:

  • Đặt giao diện thành phần trở thành lớp con của base_component.BaseComponent
  • Gán một biến lớp SPEC_CLASS với lớp ComponentSpec đã được định nghĩa trước đó
  • Gán một biến lớp EXECUTOR_SPEC với lớp Executor đã được xác định trước đó
  • Xác định __init__() bằng cách sử dụng các đối số của hàm để tạo một thể hiện của lớp ComponentSpec và gọi hàm siêu với giá trị đó, cùng với tên tùy chọn

Khi một thể hiện của thành phần được tạo, logic kiểm tra kiểu trong lớp base_component.BaseComponent sẽ được gọi để đảm bảo rằng các đối số được truyền vào tương thích với thông tin kiểu được định nghĩa trong lớp ComponentSpec .

from tfx.types import standard_artifacts
from hello_component import executor

class HelloComponent(base_component.BaseComponent):
  """Custom TFX Hello World Component."""

  SPEC_CLASS = HelloComponentSpec
  EXECUTOR_SPEC = executor_spec.ExecutorClassSpec(executor.Executor)

  def __init__(self,
               input_data: types.Channel = None,
               output_data: types.Channel = None,
               name: Optional[Text] = None):
    if not output_data:
      examples_artifact = standard_artifacts.Examples()
      examples_artifact.split_names = input_data.get()[0].split_names
      output_data = channel_utils.as_channel([examples_artifact])

    spec = HelloComponentSpec(input_data=input_data,
                              output_data=output_data, name=name)
    super(HelloComponent, self).__init__(spec=spec)

Lắp ráp vào một đường ống TFX

Bước cuối cùng là cắm thành phần tùy chỉnh mới vào đường dẫn TFX. Bên cạnh việc thêm một phiên bản của thành phần mới, những điều sau đây cũng cần thiết:

  • Nối dây đúng cách các thành phần thượng nguồn và hạ lưu của thành phần mới với nó. Điều này được thực hiện bằng cách tham chiếu các đầu ra của thành phần thượng nguồn trong thành phần mới và tham chiếu các kết quả đầu ra của thành phần mới trong các thành phần hạ lưu
  • Thêm thể hiện thành phần mới vào danh sách thành phần khi xây dựng đường ống.

Ví dụ dưới đây nêu bật những thay đổi nói trên. Có thể tìm thấy đầy đủ ví dụ trong repo TFX GitHub .

def _create_pipeline():
  ...
  example_gen = CsvExampleGen(input_base=examples)
  hello = component.HelloComponent(
      input_data=example_gen.outputs['examples'], name='HelloWorld')
  statistics_gen = StatisticsGen(examples=hello.outputs['output_data'])
  ...
  return pipeline.Pipeline(
      ...
      components=[example_gen, hello, statistics_gen, ...],
      ...
  )

Triển khai một thành phần hoàn toàn tùy chỉnh

Bên cạnh các thay đổi về mã, tất cả các phần mới được thêm vào ( ComponentSpec , Executor , giao diện thành phần) cần có thể truy cập được trong môi trường chạy đường ống để chạy đường ống đúng cách.