5-рядкова генерація тексту в стилі GPT у Python із TensorFlow/Keras

Трансформери, навіть випущені в 2017 році, почали набирати значну популярність лише в останні пару років. З поширенням технології через такі платформи, як HuggingFace, NLP і Великі мовні моделі (LLM) стали доступнішими, ніж будь-коли.

Тим не менш – навіть з усією ажіотажем навколо них і з ними багато теоретично орієнтованих посібників, в Інтернеті не так багато спеціальних реалізацій, а ресурси не так легкодоступні, як для деяких інших типів мереж, які існують довше. Хоча ви можете спростити свій робочий цикл, використовуючи готовий Transformer від HuggingFace (тема іншого посібника), ви можете отримати почувати як це працює, створюючи його самостійно, перш ніж абстрагувати його через бібліотеку. Тут ми зосередимося на створенні, а не на теорії та оптимізації.

У цьому посібнику ми створимо Авторегресійна модель мови до генерувати текст. Ми зосередимося на практичних і мінімалістичних/лаконічних аспектах завантаження даних, їх розділення, векторизації, побудови моделі, написання спеціального зворотного виклику та навчання/виведення. Кожне з цих завдань можна розділити на більш детальні посібники, тому ми збережемо реалізацію як загальну, залишаючи простір для налаштування та оптимізації залежно від вашого власного набору даних.

Типи LLM і GPT-Федір

Хоча категоризація може стати набагато складнішою – ви можете широко класифікувати мовні моделі на основі Transformer на три категорії:

  • Моделі на основі кодувальника – АЛЬБЕРТ, БЕРТ, ДистилБЕРТ, РоБЕРТа
  • На основі декодера – GPT, GPT-2, GPT-3, TransformerXL
  • Моделі Seq2Seq – BART, 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) – ми умовно назвемо модель ГПТ-Федір.

KerasNLP

Хитрість 5-рядкового GPT-Fyodor полягає в тому KerasNLP, який розроблено офіційною командою Keras, як горизонтальне розширення Keras, який у справжньому стилі Keras має на меті надати вам під рукою потужний у галузі НЛП із новими рівнями (кодери, декодери, вбудовування токенів, вбудовування позицій, метрики, токенізери тощо).

KerasNLP не є зразковим зоопарком. Це частина Keras (як окремий пакет), яка знижує бар’єр для входу в розробку моделі NLP так само, як він знижує бар’єр для розробки загального глибокого навчання з основним пакетом.

Примітка: На момент написання KerasNLP все ще виробляється і знаходиться на ранніх стадіях. У майбутніх версіях можуть бути незначні відмінності. Опис використовує версію 0.3.0.

Щоб мати можливість використовувати KerasNLP, вам доведеться встановити його через pip:

$ pip install keras_nlp

І ви можете перевірити версію за допомогою:

keras_nlp.__version__

Реалізація моделі у стилі GPT за допомогою Keras

Давайте почнемо з імпорту бібліотек, які ми будемо використовувати – TensorFlow, Keras, KerasNLP і NumPy:

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

Завантаження даних

Давайте завантажимо кілька романів Достоєвського – один із них був би надто коротким, щоб підходити для моделі, без трішки переобладнання з ранніх етапів і далі. Ми будемо грамотно використовувати необроблені текстові файли з Проект Гутенберг, завдяки простоті роботи з такими даними:

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

Ми просто завантажили всі файли, переглянули їх і об’єднали один над одним. Це включає в себе деяку різноманітність у використовуваній мові, зберігаючи її чітко Федор! Для кожного файлу ми пропустили перші 10 тисяч символів, що приблизно дорівнює середній довжині передмови та вступу Гутенберга, тож для кожної ітерації ми залишаємо майже недоторкану частину книги. Давайте подивимося на випадкові 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) 

У нас 69 тисяч речень. Коли ви замінюєте n символи з пробілами та порахуйте слова:

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

Примітка: Як правило, вам потрібно мати принаймні мільйон слів у наборі даних, а в ідеалі набагато більше. Ми працюємо з кількома мегабайтами даних (~5 МБ), тоді як мовні моделі зазвичай навчаються на десятках гігабайт тексту. Це, природно, спростить перевищення введеного тексту та ускладнить узагальнення (висока складність без надмірної комплектації або низька складність із великою кількістю надмірної комплектації). Сприймайте результати з недовірою.

Тим не менш, давайте розділимо їх на a навчання, тест та перевірка достовірності встановити. Спочатку давайте видалимо порожні рядки та перемішаємо речення:


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', ...

Час стандартизації та векторизації!

Векторизація тексту

Мережі розуміють не слова – вони розуміють числа. Ми захочемо позначити слова:

...
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, можна використовувати, що токенізірует та доповнює ваш вхід, дозволяючи видобувати словниковий запас і його розмір, не знаючи словника заздалегідь!

Ознайомтеся з нашим практичним практичним посібником із вивчення Git з передовими методами, прийнятими в галузі стандартами та включеною шпаргалкою. Припиніть гуглити команди Git і фактично вчитися це!

Давайте пристосуємось і підберемо a 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 з " ". Тут ви дійсно можете виконати більшу частину попередньої обробки тексту – і надати його на шар векторизації за допомогою додаткового standardize аргумент. Одного разу ви adapt() шар до тексту (масив NumPy або список текстів) – звідти можна отримати словниковий запас, а також його розмір:

vocab_size = len(vocab)
vocab_size 

Нарешті, щоб детокенізувати слова, ми створимо 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, використовуючи наш шар векторизації як засіб перетворення між словами та векторами, які можна передати в GPT-Fyodor.

Створення набору даних

Ми будемо створювати a 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)

Після створення та перемішування (знову ж таки, для хорошої міри) ми можемо застосувати функцію попередньої обробки (векторизація та поділ послідовності):

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

Поверх декодера ми маємо a 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 стекових декодерів (117 млн ​​параметрів), тоді як GPT-2 Extra Large має 48 стекових декодерів (1.5 Б параметрів). Наша модель з одним декодером із скромними параметрами 13M має працювати достатньо добре для навчальних цілей. З LLM – масштабування виявилося надзвичайно хорошою стратегією, а Transformers дозволяють добре масштабувати, що робить можливим навчання надзвичайно великих моделей.

ГПТ-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)

Кількість наших параметрів збільшиться на 400 тисяч:

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!

Трансформатори популярні та широко застосовуються для моделювання загальних послідовностей (і багато речей можна виразити як послідовності). Поки що основною перешкодою для входу була громіздка реалізація, але з KerasNLP практики глибокого навчання можуть використовувати реалізації для швидкого та легкого створення моделей.

Часова мітка:

Більше від Stackabuse