หน้านี้อธิบายว่า 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
ตัวอย่าง
- บทช่วยสอน Colab จำแนกข้อความด้วย BERT
การฝังข้อความด้วย 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 สำหรับการฝังข้อความด้วยอินพุตที่ประมวลผลล่วงหน้า (ดูด้านบน)
ตัวอย่าง
- บทแนะนำ Colab แก้ปัญหางาน GLUE โดยใช้ BERT บน TPU