SavedModel API ทั่วไปสำหรับงานข้อความ

หน้านี้อธิบายว่า TF2 SavedModels สำหรับงานที่เกี่ยวข้องกับข้อความควรใช้ Reusable SavedModel API อย่างไร (ซึ่งจะแทนที่และขยาย ลายเซ็นทั่วไปสำหรับข้อความ สำหรับ รูปแบบ TF1 Hub ที่เลิกใช้แล้ว)

ภาพรวม

มี API หลายตัวในการคำนวณ การฝังข้อความ (หรือที่เรียกว่า การแสดงข้อความแบบหนาแน่น หรือเวกเตอร์คุณลักษณะข้อความ)

  • API สำหรับ การฝังข้อความจากการป้อนข้อความ ถูกนำมาใช้โดย SavedModel ที่จับคู่ชุดของสตริงกับชุดของเวกเตอร์ที่ฝัง นี่ใช้งานง่ายมากและหลายรุ่นใน TF Hub ได้นำไปใช้แล้ว อย่างไรก็ตาม ไม่อนุญาตให้มีการปรับแต่งโมเดลบน TPU อย่างละเอียด

  • API สำหรับ การฝังข้อความด้วยอินพุตที่ประมวลผลล่วงหน้า จะช่วยแก้ปัญหางานเดียวกัน แต่ถูกนำไปใช้โดย SavedModels สองตัวที่แยกจากกัน:

    • ตัวประมวลผลล่วงหน้า ที่สามารถทำงานภายในไปป์ไลน์อินพุต tf.data และแปลงสตริงและข้อมูลความยาวผันแปรอื่น ๆ ให้เป็นเทนเซอร์ตัวเลข
    • ตัวเข้ารหัส ที่ยอมรับผลลัพธ์ของตัวประมวลผลล่วงหน้าและดำเนินการส่วนที่ฝึกได้ของการคำนวณแบบฝัง

    การแยกนี้ช่วยให้สามารถประมวลผลอินพุตล่วงหน้าแบบอะซิงโครนัสก่อนที่จะป้อนเข้าสู่ลูปการฝึก โดยเฉพาะอย่างยิ่ง ช่วยให้สร้างตัวเข้ารหัสที่สามารถเรียกใช้และปรับแต่งบน TPU ได้

  • API สำหรับ การฝังข้อความด้วยตัวเข้ารหัส Transformer จะขยาย API สำหรับการฝังข้อความจากอินพุตที่ประมวลผลล่วงหน้าไปยังกรณีเฉพาะของ BERT และตัวเข้ารหัส Transformer อื่นๆ

    • ตัวประมวลผลล่วงหน้า ถูกขยายเพื่อสร้างอินพุตตัวเข้ารหัสจากข้อความอินพุตมากกว่าหนึ่งส่วนของ
    • ตัวเข้ารหัส Transformer จะเปิดเผยการฝังแบบรับรู้บริบทของโทเค็นแต่ละรายการ

ในแต่ละกรณี อินพุตข้อความจะเป็นสตริงที่เข้ารหัส UTF-8 ซึ่งโดยทั่วไปจะเป็นข้อความธรรมดา เว้นแต่เอกสารประกอบของโมเดลจะระบุไว้เป็นอย่างอื่น

ไม่ว่า API จะเป็นแบบใด โมเดลที่แตกต่างกันจะได้รับการฝึกอบรมล่วงหน้าเกี่ยวกับข้อความจากภาษาและโดเมนที่แตกต่างกัน และโดยคำนึงถึงงานที่แตกต่างกัน ดังนั้นไม่ใช่ทุกโมเดลการฝังข้อความจะเหมาะกับทุกปัญหา

การฝังข้อความจากการป้อนข้อความ

SavedModel สำหรับ การฝังข้อความจากการป้อนข้อความ ยอมรับชุดของอินพุตในสตริงเทนเซอร์ของรูปร่าง [batch_size] และแมปพวกมันกับ float32 Tensor ของรูปร่าง [batch_size, dim] พร้อมการแสดงหนาแน่น (เวกเตอร์ฟีเจอร์) ของอินพุต

สรุปการใช้งาน

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

เรียกคืนจาก Reusable SavedModel API ที่เรียกใช้โมเดลในโหมดการฝึกอบรม (เช่น สำหรับการออกกลางคัน) อาจต้องมีอาร์กิวเมนต์ของคำหลัก obj(..., training=True) และ obj นั้นจัดเตรียมแอตทริบิวต์ .variables , .trainable_variables และ .regularization_losses ตามความเหมาะสม .

ใน Keras ทั้งหมดนี้ได้รับการดูแลโดย

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

การฝึกอบรมแบบกระจาย

หากใช้การฝังข้อความเป็นส่วนหนึ่งของโมเดลที่ได้รับการฝึกด้วยกลยุทธ์การกระจาย การเรียกไปที่ hub.load("path/to/model") หรือ hub.KerasLayer("path/to/model", ...) จะต้องเกิดขึ้นภายในขอบเขต DistributionStrategy เพื่อสร้างตัวแปรของโมเดลในลักษณะกระจาย ตัวอย่างเช่น

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

ตัวอย่าง

การฝังข้อความด้วยอินพุตที่ประมวลผลล่วงหน้า

ข้อความที่ฝังด้วยอินพุตที่ประมวลผลล่วงหน้า ถูกนำมาใช้โดย SavedModels สองตัวแยกกัน:

  • ตัวประมวลผลล่วงหน้า ที่จับคู่สตริงเทนเซอร์ของรูปร่าง [batch_size] กับคำสั่งของเทนเซอร์ตัวเลข
  • ตัวเข้ารหัส ที่ยอมรับคำสั่งของเทนเซอร์ที่ส่งคืนโดยตัวประมวลผลล่วงหน้า ดำเนินการส่วนที่ฝึกได้ของการคำนวณแบบฝัง และส่งคืนคำสั่งของเอาต์พุต เอาต์พุตภายใต้คีย์ "default" คือ float32 Tensor ของรูปร่าง [batch_size, dim]

ซึ่งช่วยให้เรียกใช้ตัวประมวลผลล่วงหน้าในไปป์ไลน์อินพุตได้ แต่ปรับแต่งการฝังที่คำนวณโดยตัวเข้ารหัสโดยเป็นส่วนหนึ่งของโมเดลที่ใหญ่กว่า โดยเฉพาะอย่างยิ่ง ช่วยให้สามารถสร้างตัวเข้ารหัสที่สามารถเรียกใช้และปรับแต่งบน TPU ได้

เป็นรายละเอียดการใช้งานซึ่งมีเทนเซอร์อยู่ในเอาต์พุตของตัวประมวลผลล่วงหน้า และเทนเซอร์เพิ่มเติมใด (ถ้ามี) นอกเหนือจาก "default" จะอยู่ในเอาต์พุตของตัวเข้ารหัส

เอกสารประกอบของตัวเข้ารหัสต้องระบุตัวประมวลผลล่วงหน้าที่จะใช้ด้วย โดยปกติแล้วจะมีตัวเลือกที่ถูกต้องเพียงตัวเลือกเดียวเท่านั้น

สรุปการใช้งาน

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"]

เรียกคืนจาก Reusable SavedModel API ที่เรียกใช้ตัวเข้ารหัสในโหมดการฝึกอบรม (เช่น สำหรับการออกกลางคัน) อาจต้องใช้อาร์กิวเมนต์ของคำหลัก encoder(..., training=True) และ encoder นั้นจัดเตรียมแอตทริบิวต์ .variables , .trainable_variables และ .regularization_losses ตามความเหมาะสม .

โมเดล preprocessor อาจมี .variables แต่ไม่ได้มีไว้สำหรับการฝึกเพิ่มเติม การประมวลผลล่วงหน้าไม่ขึ้นอยู่กับโหมด: ถ้า preprocessor() มีอาร์กิวเมนต์ training=... เลย ก็จะไม่มีผลกระทบใดๆ

ใน Keras ทั้งหมดนี้ได้รับการดูแลโดย

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

การฝึกอบรมแบบกระจาย

หากใช้ตัวเข้ารหัสเป็นส่วนหนึ่งของโมเดลที่ได้รับการฝึกอบรมเกี่ยวกับกลยุทธ์การกระจาย การเรียกไปที่ hub.load("path/to/encoder") หรือ hub.KerasLayer("path/to/encoder", ...) ในทางกลับกัน จะต้องเกิดขึ้นภายใน

  with strategy.scope():
    ...

เพื่อสร้างตัวแปรตัวเข้ารหัสขึ้นใหม่ในลักษณะกระจาย

ในทำนองเดียวกัน หากตัวประมวลผลล่วงหน้าเป็นส่วนหนึ่งของโมเดลที่ผ่านการฝึกอบรม (ดังตัวอย่างง่ายๆ ข้างต้น) ก็จำเป็นต้องโหลดภายใต้ขอบเขตกลยุทธ์การกระจายด้วย อย่างไรก็ตาม หากใช้ตัวประมวลผลล่วงหน้าในไปป์ไลน์อินพุต (เช่น ใน callable ที่ส่งผ่านไปยัง tf.data.Dataset.map() ) การโหลดจะต้องเกิดขึ้น นอก ขอบเขตกลยุทธ์การกระจาย เพื่อที่จะวางตัวแปร (ถ้ามี) ) บนโฮสต์ CPU

ตัวอย่าง

การฝังข้อความด้วย Transformer Encoders

ตัวเข้ารหัสหม้อแปลงสำหรับข้อความทำงานในชุดของลำดับอินพุต แต่ละลำดับประกอบด้วย n ≥ 1 ส่วนของข้อความโทเค็น ภายในบางรุ่นเฉพาะที่ผูกไว้บน n สำหรับ BERT และส่วนขยายจำนวนมาก ขอบเขตนั้นคือ 2 ดังนั้นจึงยอมรับเซ็กเมนต์เดี่ยวและคู่เซ็กเมนต์

API สำหรับ การฝังข้อความด้วยตัวเข้ารหัส Transformer จะขยาย API สำหรับการฝังข้อความด้วยอินพุตที่ประมวลผลล่วงหน้าไปยังการตั้งค่านี้

พรีโปรเซสเซอร์

SavedModel ของตัวประมวลผลล่วงหน้าสำหรับการฝังข้อความด้วยตัวเข้ารหัส Transformer จะใช้ API ของตัวประมวลผลล่วงหน้า SavedModel สำหรับการฝังข้อความด้วยอินพุตที่ประมวลผลล่วงหน้า (ดูด้านบน) ซึ่งจัดเตรียมวิธีการแมปอินพุตข้อความส่วนเดียวโดยตรงกับอินพุตตัวเข้ารหัส

นอกจากนี้ ตัวประมวลผลล่วงหน้า SavedModel จัด tokenize ย่อยที่เรียกใช้ได้สำหรับโทเค็น (แยกกันต่อเซ็กเมนต์) และ bert_pack_inputs สำหรับการบรรจุเซ็กเมนต์โทเค็น n รายการลงในลำดับอินพุตเดียวสำหรับตัวเข้ารหัส แต่ละออบเจ็กต์ย่อยเป็นไปตาม Reusable SavedModel API

สรุปการใช้งาน

เป็นตัวอย่างที่ชัดเจนสำหรับข้อความสองส่วน ให้เราดูงานที่เกี่ยวข้องกับประโยคที่ถามว่าหลักฐาน (ส่วนแรก) มีหรือไม่ได้หมายความถึงสมมติฐาน (ส่วนที่สอง)

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.

ใน Keras การคำนวณนี้สามารถแสดงเป็น

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])

รายละเอียดของ tokenize

การเรียกไปยัง preprocessor.tokenize() ยอมรับสตริง Tensor ของรูปร่าง [batch_size] และส่งคืน RaggedTensor ของรูปร่าง [batch_size, ...] ซึ่งมีค่าเป็น int32 token ids ที่แสดงถึงสตริงอินพุต อาจมีมิติที่ขาดหาย r ≥ 1 รายการหลังจาก batch_size แต่ไม่มีมิติที่สม่ำเสมออื่นๆ

  • ถ้า r =1 รูปร่างจะเป็น [batch_size, (tokens)] และแต่ละอินพุตจะถูกโทเค็นให้เป็นลำดับโทเค็นแบบเรียบๆ
  • ถ้า r >1 แสดงว่า r -1 มีการจัดกลุ่มเพิ่มเติม ตัวอย่างเช่น tensorflow_text.BertTokenizer ใช้ r =2 เพื่อจัดกลุ่มโทเค็นตามคำและให้ค่ารูปร่าง [batch_size, (words), (tokens_per_word)] ขึ้นอยู่กับโมเดลที่มีอยู่ว่ามีระดับพิเศษเหล่านี้อยู่กี่ระดับ (ถ้ามี) และกลุ่มใดที่ระดับพิเศษเหล่านี้เป็นตัวแทน

ผู้ใช้สามารถ (แต่ไม่จำเป็นต้อง) แก้ไขอินพุตโทเค็น เช่น เพื่อรองรับขีดจำกัด seq_length ที่จะบังคับใช้ในอินพุตตัวเข้ารหัสแบบแพ็ก มิติพิเศษในเอาต์พุตโทเค็นสามารถช่วยได้ที่นี่ (เช่น การเคารพขอบเขตของคำ) แต่จะไร้ความหมายในขั้นตอนถัดไป

ในแง่ของ Reusable SavedModel API ออบเจ็กต์ preprocessor.tokenize อาจมี .variables แต่ไม่ได้มีไว้สำหรับการฝึกอบรมเพิ่มเติม โทเค็นไนเซชันไม่ขึ้นอยู่กับโหมด: หาก preprocessor.tokenize() มีอาร์กิวเมนต์ training=... เลย ก็จะไม่มีผลใดๆ

รายละเอียดของ bert_pack_inputs

การเรียกไปยัง preprocessor.bert_pack_inputs() ยอมรับรายการ Python ของอินพุตโทเค็น (แยกกันเป็นชุดสำหรับแต่ละส่วนของอินพุต) และส่งคืน dict ของ Tensors ที่แสดงถึงชุดของลำดับอินพุตที่มีความยาวคงที่สำหรับโมเดลตัวเข้ารหัส Transformer

อินพุตโทเค็นแต่ละรายการคือ int32 RaggedTensor ของรูปร่าง [batch_size, ...] โดยที่หมายเลข r ของมิติที่ขาดหายไปหลังจากbatch_sizeเป็น 1 หรือเหมือนกับในเอาต์พุตของ preprocessor.tokenize(). (อย่างหลังมีไว้เพื่อความสะดวกเท่านั้น ขนาดพิเศษจะถูกทำให้เรียบก่อนบรรจุ)

การบรรจุจะเพิ่มโทเค็นพิเศษรอบๆ ส่วนอินพุตตามที่ตัวเข้ารหัสคาดหวัง การเรียก bert_pack_inputs() นำไปใช้ทุกประการกับรูปแบบการบรรจุที่ใช้โดยโมเดล BERT ดั้งเดิมและส่วนขยายจำนวนมาก: ลำดับที่แพ็กเริ่มต้นด้วยโทเค็นเริ่มต้นของลำดับหนึ่งโทเค็น ตามด้วยเซ็กเมนต์โทเค็น ซึ่งแต่ละเซ็กเมนต์สิ้นสุดด้วยปลายด้านหนึ่งของเซกเมนต์ โทเค็น ตำแหน่งที่เหลืออยู่จนถึงความยาว seq_length (ถ้ามี) จะถูกเติมด้วยโทเค็นการเสริม

หากลำดับที่แพ็กเกิน seq_length bert_pack_inputs() จะตัดเซ็กเมนต์ให้เป็นคำนำหน้าที่มีขนาดเท่ากันโดยประมาณ เพื่อให้ลำดับที่แพ็กพอดีภายใน seq_length ทุกประการ

การบรรจุไม่ขึ้นอยู่กับโหมด: หาก preprocessor.bert_pack_inputs() มีอาร์กิวเมนต์ training=... เลย ก็จะไม่มีผลกระทบใดๆ นอกจากนี้ preprocessor.bert_pack_inputs ไม่คาดว่าจะมีตัวแปร หรือสนับสนุนการปรับแต่งอย่างละเอียด

ตัวเข้ารหัส

ตัวเข้ารหัสถูกเรียกใช้บน dict ของ encoder_inputs ในลักษณะเดียวกับใน API สำหรับการฝังข้อความด้วยอินพุตที่ประมวลผลล่วงหน้า (ดูด้านบน) รวมถึงข้อกำหนดจาก Reusable SavedModel API

สรุปการใช้งาน

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

หรือเทียบเท่าใน Keras:

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

รายละเอียด

encoder_outputs เป็น dict ของ Tensors พร้อมด้วยคีย์ต่อไปนี้

  • "sequence_output" : float32 Tensor ของรูปร่าง [batch_size, seq_length, dim] พร้อมการฝังการรับรู้บริบทของแต่ละโทเค็นของทุกลำดับอินพุตที่อัดแน่น
  • "pooled_output" : float32 Tensor ของรูปร่าง [batch_size, dim] พร้อมการฝังของแต่ละลำดับอินพุตโดยรวม ซึ่งได้มาจาก sequence_output ในลักษณะที่ฝึกได้บางอย่าง
  • "default" ตามที่ API กำหนดสำหรับการฝังข้อความด้วยอินพุตที่ประมวลผลล่วงหน้า: float32 Tensor ของรูปร่าง [batch_size, dim] พร้อมการฝังของลำดับอินพุตแต่ละลำดับ (นี่อาจเป็นเพียงนามแฝงของ pooled_output)

เนื้อหาของ encoder_inputs ไม่จำเป็นอย่างเคร่งครัดตามคำจำกัดความ API นี้ อย่างไรก็ตาม สำหรับตัวเข้ารหัสที่ใช้อินพุตสไตล์ BERT ขอแนะนำให้ใช้ชื่อต่อไปนี้ (จาก ชุดเครื่องมือการสร้างแบบจำลอง NLP ของ TensorFlow Model Garden ) เพื่อลดแรงเสียดทานในตัวเข้ารหัสที่สับเปลี่ยนกันและการนำโมเดลพรีโปรเซสเซอร์กลับมาใช้ใหม่:

  • "input_word_ids" : int32 Tensor ของรูปร่าง [batch_size, seq_length] พร้อมด้วยรหัสโทเค็นของลำดับอินพุตที่อัดแน่น (นั่นคือรวมถึงโทเค็นเริ่มต้นของลำดับ โทเค็นจุดสิ้นสุดของเซกเมนต์ และช่องว่างภายใน)
  • "input_mask" : int32 Tensor ของรูปร่าง [batch_size, seq_length] โดยมีค่า 1 ที่ตำแหน่งของโทเค็นอินพุตทั้งหมดที่อยู่ก่อนการเติม และค่า 0 สำหรับโทเค็นการเติม
  • "input_type_ids" : int32 Tensor ของรูปร่าง [batch_size, seq_length] พร้อมด้วยดัชนีของส่วนอินพุตที่ก่อให้เกิดโทเค็นอินพุตในตำแหน่งที่เกี่ยวข้อง ส่วนอินพุตแรก (ดัชนี 0) รวมถึงโทเค็นเริ่มต้นของลำดับและโทเค็นสิ้นสุดของเซ็กเมนต์ ส่วนที่สองและส่วนหลัง (ถ้ามี) จะรวมโทเค็นส่วนท้ายของส่วนตามลำดับ โทเค็นการขยายรับดัชนี 0 อีกครั้ง

การฝึกอบรมแบบกระจาย

สำหรับการโหลดออบเจ็กต์ตัวประมวลผลล่วงหน้าและตัวเข้ารหัสภายในหรือภายนอกขอบเขตกลยุทธ์การกระจาย กฎเดียวกันกับใน API สำหรับการฝังข้อความด้วยอินพุตที่ประมวลผลล่วงหน้า (ดูด้านบน)

ตัวอย่าง