API SavingModel phổ biến cho tác vụ văn bản

Trang này mô tả cách TF2 SavedModels dành cho các tác vụ liên quan đến văn bản sẽ triển khai API SavingModel có thể tái sử dụng như thế nào. (Điều này thay thế và mở rộng Chữ ký chung cho văn bản cho định dạng TF1 Hub hiện không còn được dùng nữa.)

Tổng quan

Có một số API để tính toán nhúng văn bản (còn được gọi là biểu diễn văn bản dày đặc hoặc vectơ đặc trưng văn bản).

  • API để nhúng văn bản từ dữ liệu nhập văn bản được triển khai bởi SavingModel để ánh xạ một loạt chuỗi thành một loạt vectơ nhúng. Điều này rất dễ sử dụng và nhiều mô hình trên TF Hub đã triển khai nó. Tuy nhiên, điều này không cho phép tinh chỉnh mô hình trên TPU.

  • API để nhúng văn bản với dữ liệu đầu vào được xử lý trước sẽ giải quyết cùng một nhiệm vụ nhưng được triển khai bởi hai SavingModels riêng biệt:

    • một bộ tiền xử lý có thể chạy bên trong đường dẫn đầu vào tf.data và chuyển đổi các chuỗi cũng như dữ liệu có độ dài thay đổi khác thành các Tensor số,
    • một bộ mã hóa chấp nhận kết quả của bộ tiền xử lý và thực hiện phần có thể huấn luyện được của tính toán nhúng.

    Sự phân chia này cho phép các đầu vào được xử lý trước một cách không đồng bộ trước khi được đưa vào vòng huấn luyện. Đặc biệt, nó cho phép xây dựng các bộ mã hóa có thể chạy và tinh chỉnh trên TPU .

  • API để nhúng văn bản bằng bộ mã hóa Transformer mở rộng API để nhúng văn bản từ dữ liệu đầu vào được xử lý trước cho trường hợp cụ thể của BERT và các bộ mã hóa Transformer khác.

    • Bộ tiền xử lý được mở rộng để xây dựng đầu vào bộ mã hóa từ nhiều phân đoạn văn bản đầu vào.
    • Bộ mã hóa Transformer hiển thị các phần nhúng nhận biết ngữ cảnh của từng mã thông báo.

Trong mỗi trường hợp, đầu vào văn bản là các chuỗi được mã hóa UTF-8, thường là văn bản thuần túy, trừ khi tài liệu mô hình có quy định khác.

Bất kể API nào, các mô hình khác nhau đều đã được đào tạo trước trên văn bản từ các ngôn ngữ và miền khác nhau cũng như với các nhiệm vụ khác nhau. Vì vậy, không phải mô hình nhúng văn bản nào cũng phù hợp với mọi vấn đề.

Nhúng văn bản từ kiểu nhập văn bản

Một SavingModel để nhúng văn bản từ các kiểu nhập văn bản chấp nhận một loạt đầu vào trong một chuỗi Tensor có hình dạng [batch_size] và ánh xạ chúng tới một tensor float32 có hình dạng [batch_size, dim] với các biểu diễn dày đặc (vectơ đặc trưng) của đầu vào.

Tóm tắt cách sử dụng

obj = hub.load("path/to/model")
text_input = ["A long sentence.",
              "single-word",
              "http://example.com"]
embeddings = obj(text_input)

Hãy nhớ lại API Reusable SavedModel rằng việc chạy mô hình ở chế độ đào tạo (ví dụ: đối với trường hợp bỏ học) có thể yêu cầu đối số từ khóa obj(..., training=True)obj đó cung cấp các thuộc tính .variables , .trainable_variables.regularization_losses nếu có .

Ở Keras, tất cả điều này được xử lý bởi

embeddings = hub.KerasLayer("path/to/model", trainable=...)(text_input)

Đào tạo phân tán

Nếu việc nhúng văn bản được sử dụng như một phần của mô hình được đào tạo với chiến lược phân phối, lệnh gọi tới hub.load("path/to/model") hoặc hub.KerasLayer("path/to/model", ...) , tương ứng, phải xảy ra bên trong phạm vi DistributionStrategy để tạo các biến của mô hình theo cách phân tán. Ví dụ

  with strategy.scope():
    ...
    model = hub.load("path/to/model")
    ...

Ví dụ

Nhúng văn bản với đầu vào được xử lý trước

Việc nhúng văn bản với các đầu vào được xử lý trước được triển khai bởi hai SavingModels riêng biệt:

  • một bộ tiền xử lý ánh xạ một chuỗi Tensor có hình dạng [batch_size] thành một lệnh của Tensors số,
  • một bộ mã hóa chấp nhận một lệnh Tensors do bộ tiền xử lý trả về, thực hiện phần có thể đào tạo của tính toán nhúng và trả về một lệnh đầu ra. Đầu ra bên dưới khóa "default" là một tensor float32 có hình dạng [batch_size, dim] .

Điều này cho phép chạy bộ tiền xử lý trong đường dẫn đầu vào nhưng tinh chỉnh các phần nhúng được bộ mã hóa tính toán như một phần của mô hình lớn hơn. Đặc biệt, nó cho phép xây dựng các bộ mã hóa có thể chạy và tinh chỉnh trên TPU .

Đây là chi tiết triển khai trong đó các Tensor được chứa trong đầu ra của bộ tiền xử lý và các Tensor bổ sung (nếu có) ngoài "default" được chứa trong đầu ra của bộ mã hóa.

Tài liệu của bộ mã hóa phải chỉ định bộ tiền xử lý nào sẽ được sử dụng với nó. Thông thường, chỉ có đúng một lựa chọn đúng.

Tóm tắt cách sử dụng

text_input = tf.constant(["A long sentence.",
                          "single-word",
                          "http://example.com"])
preprocessor = hub.load("path/to/preprocessor")  # Must match `encoder`.
encoder_inputs = preprocessor(text_input)

encoder = hub.load("path/to/encoder")
encoder_outputs = encoder(encoder_inputs)
embeddings = encoder_outputs["default"]

Hãy nhớ lại API SavingModel có thể tái sử dụng rằng việc chạy bộ mã hóa ở chế độ đào tạo (ví dụ: đối với trường hợp bỏ học) có thể yêu cầu đối số từ encoder(..., training=True)encoder đó cung cấp các thuộc tính .variables , .trainable_variables.regularization_losses nếu có .

Mô hình preprocessor có thể có .variables nhưng không được đào tạo thêm. Quá trình tiền xử lý không phụ thuộc vào chế độ: nếu preprocessor() có đối số training=... thì nó không có hiệu lực.

Ở Keras, tất cả điều này được xử lý bởi

encoder_inputs = hub.KerasLayer("path/to/preprocessor")(text_input)
encoder_outputs = hub.KerasLayer("path/to/encoder", trainable=True)(encoder_inputs)
embeddings = encoder_outputs["default"]

Đào tạo phân tán

Nếu bộ mã hóa được sử dụng như một phần của mô hình được đào tạo với chiến lược phân phối, thì lệnh gọi tới hub.load("path/to/encoder") hoặc hub.KerasLayer("path/to/encoder", ...) , tương ứng, phải xảy ra bên trong

  with strategy.scope():
    ...

để tạo lại các biến mã hóa theo cách phân tán.

Tương tự, nếu bộ tiền xử lý là một phần của mô hình được đào tạo (như trong ví dụ đơn giản ở trên), thì nó cũng cần được tải trong phạm vi chiến lược phân phối. Tuy nhiên, nếu bộ tiền xử lý được sử dụng trong một đường dẫn đầu vào (ví dụ: trong một lệnh gọi được truyền tới tf.data.Dataset.map() ), việc tải nó phải diễn ra bên ngoài phạm vi chiến lược phân phối, để đặt các biến của nó (nếu có). ) trên CPU chủ.

Ví dụ

Nhúng văn bản bằng Bộ mã hóa biến áp

Bộ mã hóa biến áp cho văn bản hoạt động trên một loạt chuỗi đầu vào, mỗi chuỗi bao gồm n ≥ 1 đoạn văn bản được mã hóa, trong một số giới hạn dành riêng cho mô hình trên n . Đối với BERT và nhiều tiện ích mở rộng của nó, giới hạn đó là 2, vì vậy chúng chấp nhận các phân đoạn đơn và các cặp phân đoạn.

API để nhúng văn bản bằng bộ mã hóa Transformer mở rộng API để nhúng văn bản với dữ liệu đầu vào được xử lý trước cho cài đặt này.

Bộ tiền xử lý

Bộ tiền xử lý SavingModel để nhúng văn bản với bộ mã hóa Transformer triển khai API của bộ tiền xử lý SavingModel để nhúng văn bản với đầu vào được xử lý trước (xem bên trên), cung cấp cách ánh xạ trực tiếp các đầu vào văn bản một đoạn tới đầu vào bộ mã hóa.

Ngoài ra, bộ tiền xử lý SavingModel cung cấp tokenize các đối tượng con có thể gọi để mã hóa (riêng biệt cho mỗi phân đoạn) và bert_pack_inputs để đóng gói n phân đoạn được mã hóa thành một chuỗi đầu vào cho bộ mã hóa. Mỗi tiểu dự án tuân theo API SavingModel có thể tái sử dụng .

Tóm tắt cách sử dụng

Để làm ví dụ cụ thể cho hai đoạn văn bản, chúng ta hãy xem xét một nhiệm vụ đòi hỏi câu hỏi liệu tiền đề (đoạn đầu tiên) có ngụ ý hay không ngụ ý một giả thuyết (đoạn thứ hai).

preprocessor = hub.load("path/to/preprocessor")

# Tokenize batches of both text inputs.
text_premises = tf.constant(["The quick brown fox jumped over the lazy dog.",
                             "Good day."])
tokenized_premises = preprocessor.tokenize(text_premises)
text_hypotheses = tf.constant(["The dog was lazy.",  # Implied.
                               "Axe handle!"])       # Not implied.
tokenized_hypotheses = preprocessor.tokenize(text_hypotheses)

# Pack input sequences for the Transformer encoder.
seq_length = 128
encoder_inputs = preprocessor.bert_pack_inputs(
    [tokenized_premises, tokenized_hypotheses],
    seq_length=seq_length)  # Optional argument.

Trong Keras, tính toán này có thể được biểu diễn dưới dạng

tokenize = hub.KerasLayer(preprocessor.tokenize)
tokenized_hypotheses = tokenize(text_hypotheses)
tokenized_premises = tokenize(text_premises)

bert_pack_inputs = hub.KerasLayer(
    preprocessor.bert_pack_inputs,
    arguments=dict(seq_length=seq_length))  # Optional argument.
encoder_inputs = bert_pack_inputs([tokenized_premises, tokenized_hypotheses])

Chi tiết về tokenize

Lệnh gọi preprocessor.tokenize() chấp nhận một chuỗi Tensor có hình dạng [batch_size] và trả về RaggedTensor có hình dạng [batch_size, ...] có giá trị là id mã thông báo int32 đại diện cho chuỗi đầu vào. Có thể có r ≥ 1 kích thước rời rạc sau batch_size nhưng không có kích thước thống nhất nào khác.

  • Nếu r =1, hình dạng là [batch_size, (tokens)] và mỗi đầu vào chỉ được mã hóa đơn giản thành một chuỗi mã thông báo phẳng.
  • Nếu r >1 thì có r -1 cấp độ nhóm bổ sung. Ví dụ: tensorflow_text.BertTokenizer sử dụng r =2 để nhóm các mã thông báo theo từ và mang lại hình dạng [batch_size, (words), (tokens_per_word)] . Mô hình hiện tại tùy thuộc vào việc tồn tại bao nhiêu cấp độ bổ sung này, nếu có, và chúng đại diện cho những nhóm nào.

Người dùng có thể (nhưng không cần) sửa đổi đầu vào được mã hóa, ví dụ: để phù hợp với giới hạn seq_length sẽ được thực thi trong đầu vào bộ mã hóa đóng gói. Các kích thước bổ sung trong đầu ra của mã thông báo có thể hữu ích ở đây (ví dụ: tôn trọng ranh giới từ) nhưng sẽ trở nên vô nghĩa trong bước tiếp theo.

Về mặt API SavingModel có thể tái sử dụng , đối tượng preprocessor.tokenize có thể có .variables nhưng không có nghĩa là phải đào tạo thêm. Mã thông báo không phụ thuộc vào chế độ: nếu preprocessor.tokenize() có đối số training=... thì nó không có hiệu lực.

Chi tiết về bert_pack_inputs

Lệnh gọi tới preprocessor.bert_pack_inputs() chấp nhận danh sách Python gồm các đầu vào được mã hóa (được phân nhóm riêng cho từng phân đoạn đầu vào) và trả về một lệnh Tensors đại diện cho một loạt chuỗi đầu vào có độ dài cố định cho mô hình bộ mã hóa Transformer.

Mỗi đầu vào được mã hóa là một RaggedTensor int32 có hình dạng [batch_size, ...] , trong đó số r của các kích thước rách rưới sau batch_size là 1 hoặc giống như trong đầu ra của preprocessor.tokenize(). (Cái sau chỉ nhằm mục đích thuận tiện; các kích thước bổ sung được làm phẳng trước khi đóng gói.)

Việc đóng gói sẽ thêm các mã thông báo đặc biệt xung quanh các phân đoạn đầu vào như bộ mã hóa mong đợi. Lệnh gọi bert_pack_inputs() thực hiện chính xác sơ đồ đóng gói được sử dụng bởi các mô hình BERT ban đầu và nhiều tiện ích mở rộng của chúng: chuỗi được đóng gói bắt đầu bằng một mã thông báo bắt đầu chuỗi, theo sau là các phân đoạn được mã hóa, mỗi phân đoạn được kết thúc bởi một phần cuối của phân đoạn mã thông báo. Các vị trí còn lại tối đa seq_length, nếu có, sẽ được lấp đầy bằng các mã thông báo đệm.

Nếu một chuỗi được đóng gói vượt quá seq_length, bert_pack_inputs() sẽ cắt các phân đoạn của nó thành các tiền tố có kích thước xấp xỉ bằng nhau để chuỗi được đóng gói khớp chính xác trong seq_length.

Việc đóng gói không phụ thuộc vào chế độ: nếu preprocessor.bert_pack_inputs() có đối số training=... thì nó không có hiệu lực. Ngoài ra, preprocessor.bert_pack_inputs dự kiến ​​sẽ không có biến hoặc hỗ trợ tinh chỉnh.

Bộ mã hóa

Bộ mã hóa được gọi theo lệnh của encoder_inputs theo cách tương tự như trong API để nhúng văn bản với đầu vào được xử lý trước (xem ở trên), bao gồm các điều khoản từ API SavingModel có thể tái sử dụng .

Tóm tắt cách sử dụng

encoder = hub.load("path/to/encoder")
encoder_outputs = encoder(encoder_inputs)

hoặc tương đương trong Keras:

encoder = hub.KerasLayer("path/to/encoder", trainable=True)
encoder_outputs = encoder(encoder_inputs)

Chi tiết

encoder_outputs là một lệnh của Tensors với các khóa sau.

  • "sequence_output" : một tensor float32 có hình dạng [batch_size, seq_length, dim] với khả năng nhúng nhận biết ngữ cảnh của từng mã thông báo của mỗi chuỗi đầu vào được đóng gói.
  • "pooled_output" : một tensor float32 có hình dạng [batch_size, dim] với việc nhúng toàn bộ từng chuỗi đầu vào, xuất phát từ Sequence_output theo một cách nào đó có thể huấn luyện được.
  • "default" , theo yêu cầu của API để nhúng văn bản với dữ liệu đầu vào được xử lý trước: một tensor float32 có hình dạng [batch_size, dim] với việc nhúng từng chuỗi đầu vào. (Đây có thể chỉ là bí danh của pooled_output.)

Nội dung của encoder_inputs không được định nghĩa API này yêu cầu nghiêm ngặt. Tuy nhiên, đối với các bộ mã hóa sử dụng đầu vào kiểu BERT, nên sử dụng các tên sau (từ Bộ công cụ tạo mô hình NLP của TensorFlow Model Garden ) để giảm thiểu ma sát trong việc hoán đổi bộ mã hóa và tái sử dụng các mô hình tiền xử lý:

  • "input_word_ids" : một Tensor int32 có hình dạng [batch_size, seq_length] với các id mã thông báo của chuỗi đầu vào được đóng gói (nghĩa là bao gồm mã thông báo bắt đầu chuỗi, mã thông báo cuối phân đoạn và phần đệm).
  • "input_mask" : một Tensor int32 có hình dạng [batch_size, seq_length] có giá trị 1 tại vị trí của tất cả các mã thông báo đầu vào có trước phần đệm và giá trị 0 cho mã thông báo phần đệm.
  • "input_type_ids" : một Tensor int32 có hình dạng [batch_size, seq_length] với chỉ mục của phân đoạn đầu vào đã tạo ra mã thông báo đầu vào ở vị trí tương ứng. Phân đoạn đầu vào đầu tiên (chỉ số 0) bao gồm mã thông báo bắt đầu chuỗi và mã thông báo cuối phân đoạn của nó. Phân đoạn thứ hai trở lên (nếu có) bao gồm mã thông báo cuối phân đoạn tương ứng của chúng. Mã thông báo đệm nhận lại chỉ mục 0.

Đào tạo phân tán

Để tải các đối tượng bộ tiền xử lý và bộ mã hóa bên trong hoặc bên ngoài phạm vi chiến lược phân phối, các quy tắc tương tự sẽ được áp dụng như trong API cho phần nhúng văn bản với đầu vào được xử lý trước (xem ở trên).

Ví dụ