การสร้างข้อความสไตล์ GPT 5 บรรทัดใน Python ด้วย TensorFlow/Keras

Transformers แม้จะออกฉายในปี 2017 แต่เพิ่งเริ่มได้รับความสนใจอย่างมากในช่วงสองสามปีที่ผ่านมา ด้วยการแพร่กระจายของเทคโนโลยีผ่านแพลตฟอร์มอย่าง HuggingFace, NLP และ โมเดลภาษาขนาดใหญ่ (LLM) สามารถเข้าถึงได้มากขึ้นกว่าเดิม

ถึงกระนั้น – แม้จะมีโฆษณารอบตัวพวกเขาและด้วย หลาย คู่มือเชิงทฤษฎี มีการใช้งานแบบกำหนดเองไม่มากนักทางออนไลน์ และทรัพยากรก็ไม่พร้อมใช้งานเหมือนกับเครือข่ายประเภทอื่นๆ บางประเภทที่มีมานานแล้ว ในขณะที่คุณสามารถทำให้รอบการทำงานของคุณง่ายขึ้นโดยใช้ Transformer ที่สร้างไว้ล่วงหน้าจาก HuggingFace (หัวข้อของคู่มืออื่น) – คุณสามารถทำ รู้สึก มันทำงานอย่างไรโดยการสร้างมันขึ้นมาเองก่อนที่จะแยกมันออกไปผ่านห้องสมุด เราจะมุ่งเน้นไปที่การสร้างมากกว่าทฤษฎีและการเพิ่มประสิทธิภาพที่นี่

ในคู่มือนี้ เราจะสร้าง an แบบจำลองภาษาการถดถอยอัตโนมัติ ไปยัง สร้างข้อความ. เราจะมุ่งเน้นไปที่แง่มุมที่ใช้งานได้จริงและเรียบง่าย/กระชับของการโหลดข้อมูล การแยกข้อมูล การทำให้เป็นเวกเตอร์ การสร้างแบบจำลอง การเขียนการเรียกกลับแบบกำหนดเอง และการฝึกอบรม/การอนุมาน งานเหล่านี้แต่ละงานสามารถแยกออกเป็นคำแนะนำที่ละเอียดมากขึ้นได้ ดังนั้นเราจะคงการใช้งานเป็นงานทั่วไป ทำให้มีที่ว่างสำหรับการปรับแต่งและการปรับให้เหมาะสมตามชุดข้อมูลของคุณเอง

ประเภทของ LLM และ GPT-Fyodor

แม้ว่าการจัดหมวดหมู่จะซับซ้อนกว่านั้นมาก – คุณสามารถ แต้ จำแนกแบบจำลองภาษาที่ใช้ Transformer เป็นสามประเภท:

  • โมเดลที่ใช้ตัวเข้ารหัส – อัลเบิร์ต, เบิร์ต, ดิสทิลเบิร์ต, โรแบร์ทา
  • ตัวถอดรหัส – GPT, GPT-2, GPT-3, Transformer XL
  • รุ่น Seq2Seq – บาร์ต, mBART, T5

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

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

Seq2Seq โมเดลมีทั้งตัวเข้ารหัสและตัวถอดรหัส และเหมาะสำหรับการสร้างข้อความ การสรุป และที่สำคัญที่สุดคือการแปล

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

generate_text('the truth ultimately is')

ซึ่งภายใต้ประทุนจะป้อนข้อความแจ้งนี้ในรูปแบบคล้าย GPT และสร้าง:

'the truth ultimately is really a joy in history, this state of life through which is almost invisible, superfluous  teleological...'

อันที่จริงนี่เป็นสปอยล์เล็กๆ น้อยๆ จากตอนท้ายของคู่มือนี้! สปอยเลอร์ขนาดเล็กอีกตัวหนึ่งคือสถาปัตยกรรมที่สร้างข้อความนั้น:

inputs = layers.Input(shape=(maxlen,))
embedding_layer = keras_nlp.layers.TokenAndPositionEmbedding(vocab_size, maxlen, embed_dim)(inputs)
transformer_block = keras_nlp.layers.TransformerDecoder(embed_dim, num_heads)(embedding_layer)
outputs = layers.Dense(vocab_size, activation='softmax')(transformer_block)
    
model = keras.Model(inputs=inputs, outputs=outputs)

มีเพียง 5 บรรทัดเท่านั้นที่ใช้เพื่อสร้างโมเดลหม้อแปลงแบบถอดรหัสเท่านั้น ซึ่งจำลอง GPT ขนาดเล็ก เนื่องจากเราจะฝึกแบบจำลองในนวนิยายของฟีโอดอร์ ดอสโตเยฟสกี (ซึ่งคุณสามารถแทนที่ด้วยสิ่งอื่นใด ตั้งแต่วิกิพีเดียไปจนถึงความคิดเห็นของ Reddit) – เราจะเรียกแบบจำลองนี้คร่าวๆ GPT-ฟีโอดอร์.

เคราสNLP

เคล็ดลับสำหรับ GPT-Fyodor 5 บรรทัดอยู่ใน เคราสNLPซึ่งพัฒนาโดยทีม Keras อย่างเป็นทางการในฐานะส่วนขยายแนวนอนของ Keras ซึ่งตามแฟชั่น Keras อย่างแท้จริง มีเป้าหมายที่จะนำ NLP ที่มีความแข็งแกร่งของอุตสาหกรรมมาสู่ปลายนิ้วของคุณด้วยเลเยอร์ใหม่ (ตัวเข้ารหัส ตัวถอดรหัส การฝังโทเค็น การฝังตำแหน่ง เมตริก tokenizers เป็นต้น)

KerasNLP ไม่ใช่สวนสัตว์จำลอง. เป็นส่วนหนึ่งของ Keras (เป็นแพ็คเกจแยกต่างหาก) ที่ลดอุปสรรคในการเข้าสู่การพัฒนาโมเดล NLP เช่นเดียวกับการลดอุปสรรคในการเข้าสู่การพัฒนา Deep Learning ทั่วไปด้วยแพ็คเกจหลัก

หมายเหตุ ในขณะที่เขียน KerasNLP ยังคงถูกผลิตและอยู่ในช่วงเริ่มต้น อาจมีความแตกต่างเล็กน้อยในเวอร์ชันต่อๆ ไป การเขียนใช้เวอร์ชัน 0.3.0.

เพื่อให้สามารถใช้ KerasNLP ได้ คุณจะต้องติดตั้งผ่าน pip:

$ pip install keras_nlp

และคุณสามารถตรวจสอบเวอร์ชันด้วย:

keras_nlp.__version__

การใช้โมเดล GPT-Style กับ Keras

เริ่มต้นด้วยการนำเข้าไลบรารีที่เราจะใช้ – TensorFlow, Keras, KerasNLP และ NumPy:

import tensorflow as tf
from tensorflow import keras
import keras_nlp
import numpy as np

กำลังโหลดข้อมูล

มาโหลดนิยายของดอสโตเยฟสกีกันสักสองสามเล่มกัน เล่มหนึ่งอาจจะสั้นเกินไปสำหรับโมเดลที่จะเข้ากันได้ โดยไม่ต้องมีการแต่งมากเกินไปตั้งแต่ช่วงแรกๆ เป็นต้นไป เราจะใช้ไฟล์ข้อความดิบจาก .อย่างสง่างาม Project Gutenberg เนื่องจากความเรียบง่ายในการทำงานกับข้อมูลดังกล่าว:

crime_and_punishment_url = 'https://www.gutenberg.org/files/2554/2554-0.txt'
brothers_of_karamazov_url = 'https://www.gutenberg.org/files/28054/28054-0.txt'
the_idiot_url = 'https://www.gutenberg.org/files/2638/2638-0.txt'
the_possessed_url = 'https://www.gutenberg.org/files/8117/8117-0.txt'

paths = [crime_and_punishment_url, brothers_of_karamazov_url, the_idiot_url, the_possessed_url]
names = ['Crime and Punishment', 'Brothers of Karamazov', 'The Idiot', 'The Possessed']
texts = ''
for index, path in enumerate(paths):
    filepath = keras.utils.get_file(f'{names[index]}.txt', origin=path)
    text = ''
    with open(filepath, encoding='utf-8') as f:
        text = f.read()
        
        
        
        texts += text[10000:]

เราเพียงแค่ดาวน์โหลดไฟล์ทั้งหมด ผ่านไฟล์เหล่านั้น และต่อไฟล์เหล่านั้นเข้าด้วยกัน ซึ่งรวมถึงความหลากหลายในภาษาที่ใช้ ในขณะที่ยังคงไว้ซึ่งความชัดเจนของฟีโอดอร์! สำหรับแต่ละไฟล์ เราได้ข้ามอักขระ 10k ตัวแรก ซึ่งมีความยาวเฉลี่ยของคำนำและบทนำของ Gutenberg ดังนั้นเราจึงเหลือเนื้อหาส่วนใหญ่ของหนังสือสำหรับการทำซ้ำแต่ละครั้ง มาดูอักขระสุ่ม 500 ตัวใน texts สตริงตอนนี้:


texts[25000:25500]
'nd that was whynI addressed you at once. For in unfolding to you the story of my life, Indo not wish to make myself a laughing-stock before these idle listeners,nwho indeed know all about it already, but I am looking for a mannof feeling and education. Know then that my wife was educated in anhigh-class school for the daughters of noblemen, and on leaving shendanced the shawl dance before the governor and other personages fornwhich she was presented with a gold medal and a certificate of merit.n'

แยกสตริงออกเป็นประโยคก่อนทำการประมวลผลอื่น ๆ :

text_list = texts.split('.')
len(text_list) 

เรามี 69k ประโยค เมื่อคุณเปลี่ยน n อักขระที่มีช่องว่างและนับคำ:

len(texts.replace('n', ' ').split(' ')) 

หมายเหตุ โดยทั่วไป คุณจะต้องมีคำอย่างน้อยหนึ่งล้านคำในชุดข้อมูล และควรมากกว่านั้นมาก เรากำลังดำเนินการกับข้อมูลไม่กี่เมกะไบต์ (~5MB) ในขณะที่โมเดลภาษามักได้รับการฝึกฝนในข้อความหลายสิบกิกะไบต์ ซึ่งจะทำให้ง่ายต่อการใส่ข้อความมากเกินไปและยากที่จะสรุปได้ (ความฉงนสนเท่ห์สูงโดยไม่ต้องใส่มากเกินไป หรือความฉงนสนเท่ห์ต่ำที่มีการใส่มากเกินไป) ใช้ผลลัพธ์ด้วยเม็ดเกลือ

อย่างไรก็ตาม ขอแยกสิ่งเหล่านี้เป็น การอบรม, ทดสอบ และ การตรวจสอบ ชุด. ขั้นแรก ให้ลบสตริงว่างและสับเปลี่ยนประโยค:


text_list = list(filter(None, text_list))

import random
random.shuffle(text_list)

จากนั้น เราจะแบ่ง 70/15/15:

length = len(text_list)
text_train = text_list[:int(0.7*length)]
text_test = text_list[int(0.7*length):int(0.85*length)]
text_valid = text_list[int(0.85*length):]

นี่เป็นวิธีที่ง่ายแต่มีประสิทธิภาพในการแยกการตรวจสอบการทดสอบรถไฟ มาดูกันเลย text_train:

[' It was a dull morning, but the snow had ceased',
 'nn"Pierre, you who know so much of what goes on here, can you really havenknown nothing of this business and have heard nothing about it?"nn"What? What a set! So it's not enough to be a child in your old age,nyou must be a spiteful child too! Varvara Petrovna, did you hear what hensaid?"nnThere was a general outcry; but then suddenly an incident took placenwhich no one could have anticipated', ...

เวลาสำหรับมาตรฐานและ vectorization!

การแปลงข้อความเป็นเวกเตอร์

เครือข่ายไม่เข้าใจคำศัพท์ พวกเขาเข้าใจตัวเลข เราต้องการแปลงเป็นคำ:

...
sequence = ['I', 'am', 'Wall-E']
sequence = tokenize(sequence)
print(sequence) # [4, 26, 472]
...

นอกจากนี้ เนื่องจากประโยคมีความยาวต่างกัน - โดยทั่วไปจะมีการเติมช่องว่างภายในทางด้านซ้ายหรือขวาเพื่อให้แน่ใจว่ามีรูปร่างเหมือนกันในทุกประโยคที่ป้อน สมมติว่าประโยคที่ยาวที่สุดของเราคือ 5 คำ (โทเค็น) ยาว ในกรณีนั้น ประโยค Wall-E จะถูกเสริมด้วยศูนย์สองตัว ดังนั้นเราจึงมั่นใจว่ารูปร่างอินพุตเหมือนกัน:

sequence = pad_sequence(sequence)
print(sequence) # [4, 26, 472, 0, 0]

ตามเนื้อผ้า ทำได้โดยใช้ TensorFlow Tokenizer และเคราส์ pad_sequences() วิธีการ – อย่างไรก็ตาม เลเยอร์ที่สะดวกกว่ามาก TextVectorization, สามารถใช้ได้ ซึ่ง tokenizes และ แทรกข้อมูลของคุณ ช่วยให้คุณแยกคำศัพท์และขนาดของคำศัพท์โดยไม่ต้องรู้คำศัพท์ล่วงหน้า!

ดูคู่มือเชิงปฏิบัติสำหรับการเรียนรู้ Git ที่มีแนวทางปฏิบัติที่ดีที่สุด มาตรฐานที่ยอมรับในอุตสาหกรรม และเอกสารสรุปรวม หยุดคำสั่ง Googling Git และจริงๆ แล้ว เรียน มัน!

มาปรับตัวให้เข้ากับกันเถอะ TextVectorization ชั้น:

from tensorflow.keras.layers import TextVectorization

def custom_standardization(input_string):
    sentence = tf.strings.lower(input_string)
    sentence = tf.strings.regex_replace(sentence, "n", " ")
    return sentence

maxlen = 50



vectorize_layer = TextVectorization(
    standardize = custom_standardization,
    output_mode="int",
    output_sequence_length=maxlen + 1,
)

vectorize_layer.adapt(text_list)
vocab = vectorize_layer.get_vocabulary()

พื้นที่ custom_standardization() วิธีการอาจใช้เวลานานกว่านี้มาก เราได้ย่ออินพุตทั้งหมดแล้วแทนที่ n กับ " ". นี่คือที่ที่คุณสามารถใส่การประมวลผลล่วงหน้าสำหรับข้อความส่วนใหญ่ได้จริงๆ และใส่ลงในเลเยอร์ vectorization ผ่านตัวเลือก standardize การโต้แย้ง. เมื่อคุณ adapt() เลเยอร์ของข้อความ (อาร์เรย์ NumPy หรือรายการข้อความ) - คุณสามารถรับคำศัพท์รวมถึงขนาดได้จากที่นั่น:

vocab_size = len(vocab)
vocab_size 

สุดท้าย ในการถอดรหัสคำ เราจะสร้าง an index_lookup พจนานุกรม:

index_lookup = dict(zip(range(len(vocab)), vocab))    
index_lookup[5] 

มันแมปโทเค็นทั้งหมด ([1, 2, 3, 4, ...]) ถึงคำในคำศัพท์ (['a', 'the', 'i', ...]). โดยการส่งผ่านคีย์ (ดัชนีโทเค็น) เราสามารถรับคำกลับได้อย่างง่ายดาย ตอนนี้คุณสามารถเรียกใช้ vectorize_layer() ในการป้อนข้อมูลใด ๆ และสังเกตประโยคเวกเตอร์:

vectorize_layer(['hello world!'])

ซึ่งส่งผลให้:

<tf.Tensor: shape=(1, 51), dtype=int64, numpy=
array([[   1, 7509,    0,    0,    0,    0,    0,    0,    0,    0,    0,
           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
           0,    0,    0,    0,    0,    0,    0]], dtype=int64)>

สวัสดีมีดัชนีของ 1 ในขณะที่โลกมีดัชนีของ 7509! ที่เหลือคือการเติมให้กับ maxlen เราได้คำนวณ

เรามีวิธีการทำให้ข้อความเป็นเวกเตอร์ – ตอนนี้ มาสร้างชุดข้อมูลจาก text_train, text_test และ text_validโดยใช้เลเยอร์ vectorization ของเราเป็นสื่อในการแปลงระหว่างคำและเวกเตอร์ที่สามารถป้อนลงใน GPT-Fyodor

การสร้างชุดข้อมูล

เราจะสร้าง tf.data.Dataset สำหรับแต่ละชุดของเราโดยใช้ from_tensor_slices() และให้รายชื่อของเทนเซอร์สไลซ์ (ประโยค):

batch_size = 64

train_dataset = tf.data.Dataset.from_tensor_slices(text_train)
train_dataset = train_dataset.shuffle(buffer_size=256)
train_dataset = train_dataset.batch(batch_size)

test_dataset = tf.data.Dataset.from_tensor_slices(text_test)
test_dataset = test_dataset.shuffle(buffer_size=256)
test_dataset = test_dataset.batch(batch_size)

valid_dataset = tf.data.Dataset.from_tensor_slices(text_valid)
valid_dataset = valid_dataset.shuffle(buffer_size=256)
valid_dataset = valid_dataset.batch(batch_size)

เมื่อสร้างและสับเปลี่ยนแล้ว (อีกครั้งเพื่อการวัดที่ดี) – เราสามารถใช้ฟังก์ชัน preprocessing (vectorization and sequence splitting) ได้:

def preprocess_text(text):
    text = tf.expand_dims(text, -1)
    tokenized_sentences = vectorize_layer(text)
    x = tokenized_sentences[:, :-1]
    y = tokenized_sentences[:, 1:]
    return x, y


train_dataset = train_dataset.map(preprocess_text)
train_dataset = train_dataset.prefetch(tf.data.AUTOTUNE)

test_dataset = test_dataset.map(preprocess_text)
test_dataset = test_dataset.prefetch(tf.data.AUTOTUNE)

valid_dataset = valid_dataset.map(preprocess_text)
valid_dataset = valid_dataset.prefetch(tf.data.AUTOTUNE)

พื้นที่ preprocess_text() ฟังก์ชันจะขยายตามมิติสุดท้าย แปลงข้อความให้เป็นเวกเตอร์โดยใช้ของเรา vectorize_layer และสร้างอินพุตและเป้าหมาย โดยชดเชยด้วยโทเค็นเดียว นางแบบจะใช้ [0..n] ที่จะอนุมาน n+1โดยให้ผลการทำนายสำหรับแต่ละคำโดยพิจารณาจากทุกคำก่อนหน้านั้น ลองดูรายการเดียวในชุดข้อมูลใด ๆ :

for entry in train_dataset.take(1):
    print(entry)

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

(<tf.Tensor: shape=(64, 50), dtype=int64, numpy=
array([[17018,   851,     2, ...,     0,     0,     0],
       [  330,    74,     4, ...,     0,     0,     0],
       [   68,   752, 30273, ...,     0,     0,     0],
       ...,
       [    7,    73,  2004, ...,     0,     0,     0],
       [   44,    42,    67, ...,     0,     0,     0],
       [  195,   252,   102, ...,     0,     0,     0]], dtype=int64)>, <tf.Tensor: shape=(64, 50), dtype=int64, numpy=
array([[  851,     2,  8289, ...,     0,     0,     0],
       [   74,     4,    34, ...,     0,     0,     0],
       [  752, 30273,  7514, ...,     0,     0,     0],
       ...,
       [   73,  2004,    31, ...,     0,     0,     0],
       [   42,    67,    76, ...,     0,     0,     0],
       [  252,   102,  8596, ...,     0,     0,     0]], dtype=int64)>)

ในที่สุด – ได้เวลาสร้างแบบจำลองแล้ว!

นิยามโมเดล

เราจะใช้ประโยชน์จากเลเยอร์ KerasNLP ที่นี่ หลังจาก Inputเราจะเข้ารหัสอินพุตผ่าน a TokenAndPositionEmbedding ชั้นผ่านใน .ของเรา vocab_size, maxlen และ embed_dim. เหมือน embed_dim ที่เลเยอร์นี้ส่งออกและป้อนข้อมูลลงใน TransformerDecoder จะ เก็บไว้ในตัวถอดรหัส. ขณะที่เขียน ตัวถอดรหัสจะรักษามิติข้อมูลเข้าโดยอัตโนมัติ และไม่อนุญาตให้คุณฉายภาพไปยังเอาต์พุตอื่น แต่จะให้คุณกำหนดมิติที่แฝงผ่าน intermediate_dim ข้อโต้แย้ง.

เราจะคูณมิติข้อมูลการฝังด้วยสองสำหรับการแทนค่าแฝง แต่คุณสามารถคงขนาดเดิมหรือใช้ตัวเลขแยกจากการฝังตัวหรี่ได้:

embed_dim = 128
num_heads = 4

def create_model():
    inputs = keras.layers.Input(shape=(maxlen,), dtype=tf.int32)
    embedding_layer = keras_nlp.layers.TokenAndPositionEmbedding(vocab_size, maxlen, embed_dim)(inputs)
    decoder = keras_nlp.layers.TransformerDecoder(intermediate_dim=embed_dim, 
                                                            num_heads=num_heads, 
                                                            dropout=0.5)(embedding_layer)
    
    outputs = keras.layers.Dense(vocab_size, activation='softmax')(decoder)
    
    model = keras.Model(inputs=inputs, outputs=outputs)
    
    model.compile(
        optimizer="adam", 
        loss='sparse_categorical_crossentropy',
        metrics=[keras_nlp.metrics.Perplexity(), 'accuracy']
    )
    return model

model = create_model()
model.summary()

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

Model: "model_5"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 input_6 (InputLayer)        [(None, 30)]              0         
                                                                 
 token_and_position_embeddin  (None, 30, 128)          6365824   
 g_5 (TokenAndPositionEmbedd                                     
 ing)                                                            
                                                                 
 transformer_decoder_5 (Tran  (None, 30, 128)          132480    
 sformerDecoder)                                                 
                                                                 
 dense_5 (Dense)             (None, 30, 49703)         6411687   
                                                                 
=================================================================
Total params: 13,234,315
Trainable params: 13,234,315
Non-trainable params: 0
_________________________________________________________________

GPT-2 วางตัวถอดรหัสจำนวนมาก - GPT-2 Small มีตัวถอดรหัสแบบเรียงซ้อน 12 ตัว (พารามิเตอร์ 117M) ในขณะที่ GPT-2 Extra Large มีตัวถอดรหัสแบบเรียงซ้อน 48 ตัว (1.5B พารามิเตอร์) โมเดลตัวถอดรหัสเดียวของเราที่มีพารามิเตอร์ 13M ที่ต่ำต้อยควรทำงานได้ดีเพียงพอสำหรับวัตถุประสงค์ด้านการศึกษา ด้วย LLM การขยายขนาดได้พิสูจน์แล้วว่าเป็นกลยุทธ์ที่ดีอย่างยิ่ง และ Transformers อนุญาตให้มีการปรับขนาดที่ดี ทำให้สามารถฝึกโมเดลที่มีขนาดใหญ่มากได้

GPT-3 มี “น้อย” พารามิเตอร์ 175B ทีมงานของ Google Brain ได้ฝึกฝนโมเดลพารามิเตอร์ 1.6T เพื่อทำการวิจัยแบบเบาบาง ในขณะที่ยังคงการคำนวณในระดับเดียวกับโมเดลที่เล็กกว่ามาก

ตามจริงแล้ว หากเราเพิ่มจำนวนตัวถอดรหัสจาก 1 เป็น 3:

def create_model():
    inputs = keras.layers.Input(shape=(maxlen,), dtype=tf.int32)
    x = keras_nlp.layers.TokenAndPositionEmbedding(vocab_size, maxlen, embed_dim)(inputs)
    for i in range(4):
        x = keras_nlp.layers.TransformerDecoder(intermediate_dim=embed_dim*2, num_heads=num_heads,                                                             dropout=0.5)(x)
    do = keras.layers.Dropout(0.4)(x)
    outputs = keras.layers.Dense(vocab_size, activation='softmax')(do)
    
    model = keras.Model(inputs=inputs, outputs=outputs)

จำนวนพารามิเตอร์ของเราจะเพิ่มขึ้น 400k:

Total params: 13,631,755
Trainable params: 13,631,755
Non-trainable params: 0

พารามิเตอร์ส่วนใหญ่ในเครือข่ายของเรามาจาก TokenAndPositionEmbedding และ Dense ชั้น!

ลองใช้ตัวถอดรหัสระดับความลึกต่างๆ ตั้งแต่ 1 ไปจนถึงทุกวิธีที่เครื่องของคุณสามารถจัดการและจดบันทึกผลลัพธ์ได้ ไม่ว่าในกรณีใด เราเกือบจะพร้อมฝึกโมเดลแล้ว! มาสร้างคอลแบ็กแบบกำหนดเองที่จะสร้างตัวอย่างข้อความในแต่ละยุค เพื่อให้เราสามารถดูว่าโมเดลเรียนรู้การสร้างประโยคผ่านการฝึกอบรมได้อย่างไร

โทรกลับแบบกำหนดเอง

class TextSampler(keras.callbacks.Callback):
    def __init__(self, start_prompt, max_tokens):
        self.start_prompt = start_prompt
        self.max_tokens = max_tokens
        
    
    
    def sample_token(self, logits):
        logits, indices = tf.math.top_k(logits, k=5, sorted=True)
        indices = np.asarray(indices).astype("int32")
        preds = keras.activations.softmax(tf.expand_dims(logits, 0))[0]
        preds = np.asarray(preds).astype("float32")
        return np.random.choice(indices, p=preds)

    def on_epoch_end(self, epoch, logs=None):
        decoded_sample = self.start_prompt
        
        for i in range(self.max_tokens-1):
            tokenized_prompt = vectorize_layer([decoded_sample])[:, :-1]
            predictions = self.model.predict([tokenized_prompt], verbose=0)
            
            
            
            
            sample_index = len(decoded_sample.strip().split())-1
            
            sampled_token = self.sample_token(predictions[0][sample_index])
            sampled_token = index_lookup[sampled_token]
            decoded_sample += " " + sampled_token
            
        print(f"nSample text:n{decoded_sample}...n")


random_sentence = ' '.join(random.choice(text_valid).replace('n', ' ').split(' ')[:4])
sampler = TextSampler(random_sentence, 30)
reducelr = keras.callbacks.ReduceLROnPlateau(patience=10, monitor='val_loss')

การฝึกโมเดล

ในที่สุดก็ถึงเวลาฝึก! โยนในของเรา train_dataset และ validation_dataset ด้วยการโทรกลับในสถานที่:

model = create_model()
history = model.fit(train_dataset, 
                    validation_data=valid_dataset,
                    epochs=10, 
                    callbacks=[sampler, reducelr])

ตัวอย่างเลือกประโยคที่โชคร้ายที่เริ่มต้นด้วยเครื่องหมายคำพูดสิ้นสุดและคำพูดเริ่มต้น แต่ยังคงให้ผลลัพธ์ที่น่าสนใจในขณะฝึกอบรม:

# Epoch training
Epoch 1/10
658/658 [==============================] - ETA: 0s - loss: 2.7480 - perplexity: 15.6119 - accuracy: 0.6711
# on_epoch_end() sample generation
Sample text:
”  “What do you had not been i had been the same man was not be the same eyes to been a whole man and he did a whole man to the own...
# Validation
658/658 [==============================] - 158s 236ms/step - loss: 2.7480 - perplexity: 15.6119 - accuracy: 0.6711 - val_loss: 2.2130 - val_perplexity: 9.1434 - val_accuracy: 0.6864 - lr: 0.0010
...
Sample text:
”  “What do you know it is it all this very much as i should not have a great impression  in the room to be  able of it in my heart...

658/658 [==============================] - 149s 227ms/step - loss: 1.7753 - perplexity: 5.9019 - accuracy: 0.7183 - val_loss: 2.0039 - val_perplexity: 7.4178 - val_accuracy: 0.7057 - lr: 0.0010

มันเริ่มต้นด้วย:

“อะไรที่คุณไม่เคยเป็น ฉันก็เหมือนเดิม”...

ซึ่งไม่สมเหตุสมผลเลยจริงๆ เมื่อสิ้นสุดยุคสั้นสิบยุค มันจะสร้างบางสิ่งตามแนวของ:

“หมายความว่ายังไงที่เป็นคนธรรมดาที่สุดของผู้ชายกันแน่”…

แม้ว่าประโยคที่สองจะยังไม่สมเหตุสมผลมากนัก แต่ก็มีเหตุผลมากกว่าประโยคแรกมาก การฝึกอบรมที่ยาวขึ้นเกี่ยวกับข้อมูลที่มากขึ้น (ด้วยขั้นตอนก่อนการประมวลผลที่สลับซับซ้อนมากขึ้น) จะให้ผลลัพธ์ที่ดีกว่า เราได้ฝึกฝนมันในช่วง 10 ยุคที่มีการออกกลางคันสูงเพื่อต่อสู้กับขนาดชุดข้อมูลขนาดเล็ก ถ้าปล่อยให้ฝึกนานขึ้น มันจะสร้างข้อความที่เหมือนฟีโอดอร์มาก เพราะมันคงจะจำส่วนใหญ่ของมันได้

หมายเหตุ เนื่องจากเอาต์พุตค่อนข้างละเอียด คุณจึงปรับแต่ง verbose อาร์กิวเมนต์ขณะปรับโมเดลเพื่อลดจำนวนข้อความบนหน้าจอ

การอนุมานแบบจำลอง

ในการอนุมาน เราจะต้องการจำลองอินเทอร์เฟซของ TextSampler – วิธีการที่รับเมล็ดและ a response_length (max_tokens). เราจะใช้วิธีการเดียวกันกับในตัวอย่าง:

def sample_token(logits):
        logits, indices = tf.math.top_k(logits, k=5, sorted=True)
        indices = np.asarray(indices).astype("int32")
        preds = keras.activations.softmax(tf.expand_dims(logits, 0))[0]
        preds = np.asarray(preds).astype("float32")
        return np.random.choice(indices, p=preds)

def generate_text(prompt, response_length=20):
    decoded_sample = prompt
    for i in range(response_length-1):
        tokenized_prompt = vectorize_layer([decoded_sample])[:, :-1]
        predictions = model.predict([tokenized_prompt], verbose=0)
        sample_index = len(decoded_sample.strip().split())-1

        sampled_token = sample_token(predictions[0][sample_index])
        sampled_token = index_lookup[sampled_token]
        decoded_sample += " " + sampled_token
    return decoded_sample

ตอนนี้คุณสามารถเรียกใช้เมธอดกับตัวอย่างใหม่ได้:

generate_text('the truth ultimately is')


generate_text('the truth ultimately is')

ปรับปรุงผลลัพธ์?

ดังนั้นคุณจะปรับปรุงผลลัพธ์ได้อย่างไร? มีบางสิ่งที่สามารถนำไปปฏิบัติได้จริง:

  • การล้างข้อมูล (ล้างข้อมูลอินพุตให้ละเอียดยิ่งขึ้น เราเพิ่งตัดตัวเลขโดยประมาณตั้งแต่เริ่มต้นและลบอักขระขึ้นบรรทัดใหม่)
  • รับข้อมูลเพิ่มเติม (เราทำงานกับข้อมูลข้อความเพียงไม่กี่เมกะไบต์)
  • ปรับขนาดโมเดลควบคู่ไปกับข้อมูล (ตัวถอดรหัสซ้อนไม่ยาก!)

สรุป

ในขณะที่ไปป์ไลน์การประมวลผลล่วงหน้านั้นเรียบง่ายและสามารถปรับปรุงได้ - ไปป์ไลน์ที่ระบุไว้ในคู่มือนี้สร้างโมเดลสไตล์ GPT ที่เหมาะสม โดยต้องใช้โค้ดเพียง 5 บรรทัดเพื่อสร้างหม้อแปลงตัวถอดรหัสแบบกำหนดเองเท่านั้น โดยใช้ Keras!

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

ประทับเวลา:

เพิ่มเติมจาก สแต็ค