Pytorch
TensorFlow

#
Tinh chỉnh một mô hình với Keras

Khi bạn đã hoàn thành tất cả công việc tiền xử lý dữ liệu trong phần trước, bạn chỉ còn một vài bước nữa để huấn luyện mô hình. Tuy nhiên, lưu ý rằng lệnh `model.fit()` sẽ chạy rất chậm trên CPU. Nếu bạn chưa thiết lập GPU, bạn có thể có quyền truy cập vào GPU hoặc TPU miễn phí trên Google Colab.

Các đoạn mã ví dụ bên dưới giả sử bạn đã thực thi các ví dụ trong phần trước. Dưới đây là một bản tóm tắt ngắn gọn tóm tắt lại những gì bạn cần:

    from datasets import load_dataset
    from transformers import AutoTokenizer, DataCollatorWithPadding
    import numpy as np

    raw_datasets = load_dataset("glue", "mrpc")
    checkpoint = "bert-base-uncased"
    tokenizer = AutoTokenizer.from_pretrained(checkpoint)

    def tokenize_function(example):
        return tokenizer(example["sentence1"], example["sentence2"], truncation=True)

    tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)

    data_collator = DataCollatorWithPadding(tokenizer=tokenizer, return_tensors="tf")

    tf_train_dataset = tokenized_datasets["train"].to_tf_dataset(
        columns=["attention_mask", "input_ids", "token_type_ids"],
        label_cols=["labels"],
        shuffle=True,
        collate_fn=data_collator,
        batch_size=8,
    )

    tf_validation_dataset = tokenized_datasets["validation"].to_tf_dataset(
        columns=["attention_mask", "input_ids", "token_type_ids"],
        label_cols=["labels"],
        shuffle=False,
        collate_fn=data_collator,
        batch_size=8,
    )

###
Huấn luyện

Các mô hình TensorFlow nhập từ 🤗 Transformers vốn là các mô hình Keras. Đây là phần giới thiệu ngắn về Keras.

Điều đó có nghĩa là một khi chúng tôi có dữ liệu riêng mình, chúng ta chỉ cần thao tác ít bước nữa thôi để bắt đầu huấn luyện.

Như trong chương trước, chúng ta sẽ sử dụng lớp `TFAutoModelForSequenceClassification`, với hai nhãn:

    from transformers import TFAutoModelForSequenceClassification

    model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)

Bạn sẽ nhận thấy rằng không như trong Chương 2, bạn nhận được một cảnh báo sau khi khởi tạo mô hình được huấn luyện trước này. Đây là do BERT chưa được huấn luyện trước về phân loại các cặp câu, vì vậy phần đầu của mô hình được huấn luyện trước đã bị loại bỏ và phần đầu mới phù hợp để phân loại chuỗi đã được chèn vào thay thế. Các cảnh báo chỉ ra rằng một số trọng số đã không được sử dụng (những trọng số tương ứng với đầu huấn luyện trước bị rụng) và một số trọng số khác khác được khởi tạo ngẫu nhiên (những trọng số dành cho đầu mới). Nó kết thúc bằng cách khuyến khích bạn huấn luyện mô hình, đó chính xác là những gì chúng ta sẽ làm bây giờ.

Để tinh chỉnh mô hình trên tập dữ liệu của mình, chúng ta chỉ cần `compile()` mô hình và sau đó chuyển dữ liệu của ta đến phương thức `fit()`. Thao tác này sẽ bắt đầu quá trình tinh chỉnh (sẽ mất vài phút trên GPU) và báo cáo sự mất mát ở tập huấn luyện khi nó diễn ra, cộng với mất mát ở tập kiểm định ở cuối mỗi epoch.

Lưu ý rằng 🤗 các mô hình Transformers có một khả năng đặc biệt mà hầu hết các mô hình Keras không có - chúng có thể tự động sử dụng một lượng mất mát thích hợp mà chúng tính toán bên trong. Chúng sẽ sử dụng sự mất mát này theo mặc định nếu bạn không đặt tham số mất mát bên trong `compile()`. Lưu ý rằng để sử dụng hàm mất mát trong nội bộ, bạn sẽ cần truyền các nhãn của mình như một phần của đầu vào, không phải dưới dạng nhãn riêng biệt, đây là cách thông thường để sử dụng nhãn với các mô hình Keras. Bạn sẽ thấy các ví dụ về điều này trong Phần 2 của khóa học, trong đó việc xác định hàm mất mát chính xác có thể khó khăn. Tuy nhiên, đối với phân loại chuỗi, một hàm mất mát Keras tiêu chuẩn hoạt động khá tốt, vì vậy đó là những gì chúng ta sẽ sử dụng ở đây.

    from tensorflow.keras.losses import SparseCategoricalCrossentropy

    model.compile(
        optimizer="adam",
        loss=SparseCategoricalCrossentropy(from_logits=True),
        metrics=["accuracy"],
    )
    model.fit(
        tf_train_dataset,
        validation_data=tf_validation_dataset,
    )

Lưu ý một lỗi rất phổ biến ở đây - bạn có thể chỉ cần truyền tên của hàm mất mát dưới dạng chuỗi cho Keras, nhưng theo mặc định, Keras sẽ cho rằng bạn đã áp dụng softmax cho đầu ra của mình. Tuy nhiên, nhiều mô hình xuất ra các giá trị ngay trước khi áp dụng softmax, còn được gọi là logit. Chúng ta cần nói với hàm mất mát rằng đó là những gì mô hình của chúng ta làm và cách duy nhất để làm điều đó là gọi nó trực tiếp, thay vì đặt tên bằng một chuỗi.

###
Cải thiện hiệu suất huấn luyện

Nếu bạn thử đoạn mã trên, nó chắc chắn chạy, nhưng bạn sẽ thấy rằng hàm mất mát chỉ giảm từ từ hoặc không thường xuyên. Nguyên nhân chính là do learning rate hay tốc độ học. Với hàm mất mát, khi ta truyền cho Keras tên của trình tối ưu hóa dưới dạng một chuỗi, Keras sẽ khởi tạo trình tối ưu hóa đó với các giá trị mặc định cho tất cả các tham số, bao gồm cả tốc độ học. Tuy nhiên, từ kinh nghiệm lâu năm, chúng tôi biết rằng các mô hình Transformer được hưởng lợi từ tốc độ học thấp hơn nhiều so với tỷ lệ mặc định cho Adam, là 1e-3, cũng được viết bằng 10 lũy thừa của -3, hoặc 0,001. 5e-5 (0,00005), thấp hơn khoảng hai mươi lần, là một điểm khởi đầu tốt hơn nhiều.

Ngoài việc giảm tốc độ học, chúng tôi có một mẹo thứ hai: Ta có thể từ từ giảm tốc độ học trong quá trình huấn luyện. Trong tài liệu, đôi khi bạn sẽ thấy điều này được gọi là phân rã hoặc ủ tốc độ học. Ở Keras, cách tốt nhất để làm điều này là sử dụng learning rate scheduler hay công cụ lập lịch trình tốc độ học. Một cái hay để sử dụng là `PolynomialDecay` - với cài đặt mặc định, nó chỉ đơn giản là giảm độ tuyến tính tốc độ học từ giá trị ban đầu đến giá trị cuối cùng trong quá trình huấn luyện, đó chính xác là những gì ta muốn. Tuy nhiên, để sử dụng bộ lập lịch một cách chính xác, chúng ta cần cho nó biết thời gian huấn luyện sẽ kéo dài. Chúng ta tính giá trị đó dưới dạng `num_train_steps` như sau.

    from tensorflow.keras.optimizers.schedules import PolynomialDecay

    batch_size = 8
    num_epochs = 3
    # Số bước huấn luyện là số lượng mẫu trong tập dữ liệu, chia cho kích thước lô sau đó nhân
    # với tổng số epoch. Lưu ý rằng tf_train_dataset ở đây là tf.data.Dataset theo lô,
    # không phải là Hugging Face Dataset, vì vậy len() của nó đã là num_samples // batch_size.
    num_train_steps = len(tf_train_dataset) * num_epochs
    lr_scheduler = PolynomialDecay(
        initial_learning_rate=5e-5, end_learning_rate=0.0, decay_steps=num_train_steps
    )
    from tensorflow.keras.optimizers import Adam

    opt = Adam(learning_rate=lr_scheduler)

Thư viện 🤗 Transformers cũng có một hàm `create_optimizer()` sẽ tạo ra một trình tối ưu hóa `AdamW` với sự giảm tốc độ học. Đây là một phím tắt thuận tiện mà bạn sẽ thấy chi tiết trong các phần sau của khóa học.

Bây giờ chúng ta đã có trình tối ưu hóa hoàn toàn mới và ta có thể thử huấn luyện với nó. Đầu tiên, hãy tải lại mô hình, để đặt lại các thay đổi đối với trọng số từ lần chạy huấn luyện mà chúng ta vừa thực hiện và sau đó ta có thể biên dịch nó bằng trình tối ưu hóa mới:

    import tensorflow as tf

    model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
    loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
    model.compile(optimizer=opt, loss=loss, metrics=["accuracy"])

Giờ ta sẽ fit lại 1 lần nữa:

    model.fit(tf_train_dataset, validation_data=tf_validation_dataset, epochs=3)

💡 Nếu bạn muốn tự động tải mô hình của mình lên Hub trong quá trình huấn luyện, bạn có thể truyền `PushToHubCallback` vào trong phương thức `model.fit()`. Chúng ta sẽ tìm hiểu thêm về điều này trong Chương 4

###
Các dự đoán của mô hình

Việc huấn luyện và theo dõi sự mất mát giảm xuống đều rất tốt, nhưng nếu chúng ta muốn thực sự có được kết quả đầu ra từ mô hình được huấn luyện, để tính toán một số chỉ số hoặc sử dụng mô hình đó trong sản xuất thì sao? Để làm điều đó, chúng ta chỉ có thể sử dụng phương thức `predict()`. Điều này sẽ trả về logit từ đầu ra của mô hình, một cho mỗi lớp.

    preds = model.predict(tf_validation_dataset)["logits"]

Chúng ta có thể chuyển đổi các logit này thành các dự đoán lớp của mô hình bằng cách sử dụng `argmax` để tìm logit cao nhất, tương ứng với lớp có nhiều khả năng nhất:

    class_preds = np.argmax(preds, axis=1)
    print(preds.shape, class_preds.shape)

    (408, 2) (408,)

Bây giờ, hãy sử dụng các `preds` đó để tính toán một số chỉ số! Chúng ta có thể tải các chỉ số được liên kết với tập dữ liệu MRPC dễ dàng như khi ta tải tập dữ liệu, lần này là với hàm `eval.load())`. Đối tượng được trả về có phương thức `compute()` mà chúng ta có thể sử dụng để thực hiện phép tính số liệu:

    import evaluate

    metric = evaluate.load("glue", "mrpc")
    metric.compute(predictions=class_preds, references=raw_datasets["validation"]["label"])

    {'accuracy': 0.8578431372549019, 'f1': 0.8996539792387542}

Kết quả chính xác bạn nhận được có thể khác nhau, vì việc khởi tạo ngẫu nhiên phần đầu mô hình có thể thay đổi các chỉ số mà nó đạt được. Ở đây, chúng ta có thể thấy mô hình có độ chính xác 85.78% trên tập kiểm định và điểm F1 là 89.97. Đó là hai chỉ số được sử dụng để đánh giá kết quả trên tập dữ liệu MRPC theo điểm chuẩn GLUE. Bảng trong bài báo BERT báo cáo điểm F1 là 88.9 cho mô hình cơ sở. Đó là mô hình `không phân biệt` viết hoa viết thường trong khi chúng ta hiện đang sử dụng mô hình `có phân biệt`, điều này giải thích kết quả tốt hơn.

Phần này kết thúc phần giới thiệu về cách tinh chỉnh bằng Keras API. Một ví dụ về cách làm này đối với hầu hết các tác vụ NLP phổ biến sẽ được đưa ra trong Chương 7. Nếu bạn muốn trau dồi kỹ năng của mình trên API Keras, hãy cố gắng tinh chỉnh một mô hình trên tập dữ liệu GLUE SST-2, bằng cách sử dụng xử lý dữ liệu bạn đã thực hiện trong phần 2.