[ad_1]
Tinh chỉnh mô hình BERT trên dữ liệu truyền thông xã hội
Lấy và chuẩn bị dữ liệu
Tập dữ liệu chúng tôi sẽ sử dụng đến từ Kaggle, bạn có thể tải xuống tại đây: https://www.kaggle.com/datasets/farisdurrani/sentimentsearch (Giấy phép CC BY 4.0). Trong thử nghiệm của mình, tôi chỉ chọn bộ dữ liệu từ Fb và Twitter.
Đoạn mã sau sẽ lấy các tệp csv và lưu 3 phần tách (đào tạo, xác thực và kiểm tra) vào nơi bạn muốn. Tôi khuyên bạn nên lưu chúng vào Google Cloud Storage.
Bạn có thể chạy tập lệnh với:
python make_splits --output-dir gs://your-bucket/
import pandas as pd
import argparse
import numpy as np
from sklearn.model_selection import train_test_splitdef make_splits(output_dir):
df=pd.concat((
pd.read_csv("information/farisdurrani/twitter_filtered.csv"),
pd.read_csv("information/farisdurrani/facebook_filtered.csv")
))
df = df.dropna(subset=('sentiment'), axis=0)
df('Goal') = df('sentiment').apply(lambda x: 1 if x==0 else np.signal(x)+1).astype(int)
df_train, df_ = train_test_split(df, stratify=df('Goal'), test_size=0.2)
df_eval, df_test = train_test_split(df_, stratify=df_('Goal'), test_size=0.5)
print(f"Recordsdata will probably be saved in {output_dir}")
df_train.to_csv(output_dir + "/practice.csv", index=False)
df_eval.to_csv(output_dir + "/eval.csv", index=False)
df_test.to_csv(output_dir + "/check.csv", index=False)
print(f"Prepare : ({df_train.form}) samples")
print(f"Val : ({df_eval.form}) samples")
print(f"Take a look at : ({df_test.form}) samples")
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--output-dir')
args, _ = parser.parse_known_args()
make_splits(args.output_dir)
Dữ liệu sẽ trông gần như thế này:
Sử dụng mô hình tiền huấn luyện BERT nhỏ
Đối với mô hình của chúng tôi, chúng tôi sẽ sử dụng mô hình BERT nhẹ, BERT-Tiny. Mô hình này đã được đào tạo trước trên lượng dữ liệu khổng lồ, nhưng không nhất thiết phải có dữ liệu truyền thông xã hội và không nhất thiết phải có mục tiêu thực hiện Phân tích tình cảm. Đây là lý do tại sao chúng tôi sẽ tinh chỉnh nó.
Nó chỉ chứa 2 lớp với kích thước 128 đơn vị, có thể xem danh sách đầy đủ các mô hình đây nếu bạn muốn lấy một cái lớn hơn.
Đầu tiên chúng ta hãy tạo một principal.py
tập tin, với tất cả các mô-đun cần thiết:
import pandas as pd
import argparse
import tensorflow as tf
import tensorflow_hub as hub
import tensorflow_text as textual content
import logging
import os
os.environ("TFHUB_MODEL_LOAD_FORMAT") = "UNCOMPRESSED"def train_and_evaluate(**params):
go
# will probably be up to date as we go
Chúng ta cũng hãy viết ra những yêu cầu của chúng ta một cách chuyên dụng necessities.txt
transformers==4.40.1
torch==2.2.2
pandas==2.0.3
scikit-learn==1.3.2
gcsfs
Bây giờ chúng ta sẽ tải 2 phần để huấn luyện mô hình của mình:
- Các mã thông báothao tác này sẽ đảm nhiệm việc phân tách văn bản đầu vào thành các mã thông báo mà BERT đã được đào tạo.
- Các người mẫu chính nó.
Bạn có thể có được cả hai từ Huggingface đây. Bạn cũng có thể tải chúng xuống Cloud Storage. Đó là những gì tôi đã làm và do đó sẽ tải chúng bằng:
# Load pretrained tokenizers and bert mannequin
tokenizer = BertTokenizer.from_pretrained('fashions/bert_uncased_L-2_H-128_A-2/vocab.txt')
mannequin = BertModel.from_pretrained('fashions/bert_uncased_L-2_H-128_A-2')
Bây giờ hãy thêm phần sau vào tệp của chúng tôi:
class SentimentBERT(nn.Module):
def __init__(self, bert_model):
tremendous().__init__()
self.bert_module = bert_model
self.dropout = nn.Dropout(0.1)
self.ultimate = nn.Linear(in_features=128, out_features=3, bias=True) # Uncomment the beneath for those who solely wish to retrain sure layers.
# self.bert_module.requires_grad_(False)
# for param in self.bert_module.encoder.parameters():
# param.requires_grad = True
def ahead(self, inputs):
ids, masks, token_type_ids = inputs('ids'), inputs('masks'), inputs('token_type_ids')
# print(ids.dimension(), masks.dimension(), token_type_ids.dimension())
x = self.bert_module(ids, masks, token_type_ids)
x = self.dropout(x('pooler_output'))
out = self.ultimate(x)
return out
Nghỉ ngơi một chút ở đây. Chúng tôi có một số lựa chọn khi sử dụng lại mô hình hiện có.
- Chuyển tiếp học tập : chúng tôi đóng băng trọng số của mô hình và sử dụng nó như một “trình trích xuất đặc điểm”. Do đó, chúng tôi có thể nối thêm các lớp bổ sung ở phía dưới. Điều này thường được sử dụng trong Laptop Imaginative and prescient, nơi các mô hình như VGG, Xception, v.v. có thể được sử dụng lại để huấn luyện mô hình tùy chỉnh trên các tập dữ liệu nhỏ
- Tinh chỉnh : chúng tôi giải phóng tất cả hoặc một phần trọng số của mô hình và đào tạo lại mô hình trên tập dữ liệu tùy chỉnh. Đây là cách tiếp cận ưa thích khi đào tạo LLM tùy chỉnh.
Thông tin chi tiết hơn về Học chuyển giao và Tinh chỉnh đây:
Trong mô hình, chúng tôi đã chọn hủy đóng băng tất cả mô hình, nhưng vui lòng đóng băng một hoặc nhiều lớp của mô-đun BERT được huấn luyện trước và xem nó ảnh hưởng đến hiệu suất như thế nào.
Phần quan trọng ở đây là thêm một lớp được kết nối đầy đủ sau mô-đun BERT để “liên kết” nó với nhiệm vụ phân loại của chúng tôi, do đó lớp cuối cùng có 3 đơn vị. Điều này sẽ cho phép chúng tôi sử dụng lại các trọng số BERT đã được huấn luyện trước và điều chỉnh mô hình của chúng tôi cho phù hợp với nhiệm vụ của chúng tôi.
Tạo bộ tải dữ liệu
Để tạo bộ nạp dữ liệu, chúng ta sẽ cần nạp Tokenizer ở trên. Tokenizer lấy một chuỗi làm đầu vào và trả về một số đầu ra trong đó chúng ta có thể tìm thấy các mã thông báo (‘input_ids’ trong trường hợp của chúng ta):
Trình mã thông báo BERT hơi đặc biệt và sẽ trả về một số kết quả đầu ra, nhưng kết quả quan trọng nhất là input_ids
: chúng là những mã thông báo được sử dụng để mã hóa câu của chúng ta. Chúng có thể là từ, hoặc bộ phận hoặc từ. Ví dụ: từ “đang tìm kiếm” có thể được tạo thành từ 2 mã thông báo là “look” và “##ing”.
Bây giờ chúng ta hãy tạo một mô-đun trình tải dữ liệu sẽ xử lý các tập dữ liệu của chúng tôi:
class BertDataset(Dataset):
def __init__(self, df, tokenizer, max_length=100):
tremendous(BertDataset, self).__init__()
self.df=df
self.tokenizer=tokenizer
self.goal=self.df('Goal')
self.max_length=max_lengthdef __len__(self):
return len(self.df)
def __getitem__(self, idx):
X = self.df('bodyText').values(idx)
y = self.goal.values(idx)
inputs = self.tokenizer.encode_plus(
X,
pad_to_max_length=True,
add_special_tokens=True,
return_attention_mask=True,
max_length=self.max_length,
)
ids = inputs("input_ids")
token_type_ids = inputs("token_type_ids")
masks = inputs("attention_mask")
x = {
'ids': torch.tensor(ids, dtype=torch.lengthy).to(DEVICE),
'masks': torch.tensor(masks, dtype=torch.lengthy).to(DEVICE),
'token_type_ids': torch.tensor(token_type_ids, dtype=torch.lengthy).to(DEVICE)
}
y = torch.tensor(y, dtype=torch.lengthy).to(DEVICE)
return x, y
Viết script chính để huấn luyện mannequin
Trước hết chúng ta hãy xác định hai chức năng để xử lý các bước đào tạo và đánh giá:
def practice(epoch, mannequin, dataloader, loss_fn, optimizer, max_steps=None):
mannequin.practice()
total_acc, total_count = 0, 0
log_interval = 50
start_time = time.time()for idx, (inputs, label) in enumerate(dataloader):
optimizer.zero_grad()
predicted_label = mannequin(inputs)
loss = loss_fn(predicted_label, label)
loss.backward()
optimizer.step()
total_acc += (predicted_label.argmax(1) == label).sum().merchandise()
total_count += label.dimension(0)
if idx % log_interval == 0:
elapsed = time.time() - start_time
print(
"Epoch {:3d} | {:5d}/{:5d} batches "
"| accuracy {:8.3f} | loss {:8.3f} ({:.3f}s)".format(
epoch, idx, len(dataloader), total_acc / total_count, loss.merchandise(), elapsed
)
)
total_acc, total_count = 0, 0
start_time = time.time()
if max_steps will not be None:
if idx == max_steps:
return {'loss': loss.merchandise(), 'acc': total_acc / total_count}
return {'loss': loss.merchandise(), 'acc': total_acc / total_count}
def consider(mannequin, dataloader, loss_fn):
mannequin.eval()
total_acc, total_count = 0, 0
with torch.no_grad():
for idx, (inputs, label) in enumerate(dataloader):
predicted_label = mannequin(inputs)
loss = loss_fn(predicted_label, label)
total_acc += (predicted_label.argmax(1) == label).sum().merchandise()
total_count += label.dimension(0)
return {'loss': loss.merchandise(), 'acc': total_acc / total_count}
Chúng tôi đang tiến gần hơn đến việc hoàn thiện và chạy tập lệnh chính của mình. Hãy khâu các mảnh lại với nhau. Chúng ta có:
- MỘT
BertDataset
lớp để xử lý việc tải dữ liệu - MỘT
SentimentBERT
mô hình lấy mô hình Tiny-BERT của chúng tôi và thêm một lớp bổ sung cho trường hợp sử dụng tùy chỉnh của chúng tôi practice()
Vàeval()
chức năng để xử lý các bước đó- MỘT
train_and_eval()
chức năng gói mọi thứ
Chúng tôi sẽ sử dụng argparse
để có thể khởi chạy tập lệnh của chúng tôi bằng các đối số. Các đối số như vậy thường là các tệp đào tạo/eval/kiểm tra để chạy mô hình của chúng tôi với bất kỳ tập dữ liệu nào, đường dẫn nơi mô hình của chúng tôi sẽ được lưu trữ và các tham số liên quan đến quá trình đào tạo.
import pandas as pd
import time
import torch.nn as nn
import torch
import logging
import numpy as np
import argparsefrom torch.utils.information import Dataset, DataLoader
from transformers import BertTokenizer, BertModel
logging.basicConfig(format='%(asctime)s (%(levelname)s): %(message)s', degree=logging.DEBUG)
logging.getLogger().setLevel(logging.INFO)
# --- CONSTANTS ---
BERT_MODEL_NAME = 'small_bert/bert_en_uncased_L-2_H-128_A-2'
if torch.cuda.is_available():
logging.data(f"GPU: {torch.cuda.get_device_name(0)} is accessible.")
DEVICE = torch.gadget('cuda')
else:
logging.data("No GPU out there. Coaching will run on CPU.")
DEVICE = torch.gadget('cpu')
# --- Information preparation and tokenization ---
class BertDataset(Dataset):
def __init__(self, df, tokenizer, max_length=100):
tremendous(BertDataset, self).__init__()
self.df=df
self.tokenizer=tokenizer
self.goal=self.df('Goal')
self.max_length=max_length
def __len__(self):
return len(self.df)
def __getitem__(self, idx):
X = self.df('bodyText').values(idx)
y = self.goal.values(idx)
inputs = self.tokenizer.encode_plus(
X,
pad_to_max_length=True,
add_special_tokens=True,
return_attention_mask=True,
max_length=self.max_length,
)
ids = inputs("input_ids")
token_type_ids = inputs("token_type_ids")
masks = inputs("attention_mask")
x = {
'ids': torch.tensor(ids, dtype=torch.lengthy).to(DEVICE),
'masks': torch.tensor(masks, dtype=torch.lengthy).to(DEVICE),
'token_type_ids': torch.tensor(token_type_ids, dtype=torch.lengthy).to(DEVICE)
}
y = torch.tensor(y, dtype=torch.lengthy).to(DEVICE)
return x, y
# --- Mannequin definition ---
class SentimentBERT(nn.Module):
def __init__(self, bert_model):
tremendous().__init__()
self.bert_module = bert_model
self.dropout = nn.Dropout(0.1)
self.ultimate = nn.Linear(in_features=128, out_features=3, bias=True)
def ahead(self, inputs):
ids, masks, token_type_ids = inputs('ids'), inputs('masks'), inputs('token_type_ids')
x = self.bert_module(ids, masks, token_type_ids)
x = self.dropout(x('pooler_output'))
out = self.ultimate(x)
return out
# --- Coaching loop ---
def practice(epoch, mannequin, dataloader, loss_fn, optimizer, max_steps=None):
mannequin.practice()
total_acc, total_count = 0, 0
log_interval = 50
start_time = time.time()
for idx, (inputs, label) in enumerate(dataloader):
optimizer.zero_grad()
predicted_label = mannequin(inputs)
loss = loss_fn(predicted_label, label)
loss.backward()
optimizer.step()
total_acc += (predicted_label.argmax(1) == label).sum().merchandise()
total_count += label.dimension(0)
if idx % log_interval == 0:
elapsed = time.time() - start_time
print(
"Epoch {:3d} | {:5d}/{:5d} batches "
"| accuracy {:8.3f} | loss {:8.3f} ({:.3f}s)".format(
epoch, idx, len(dataloader), total_acc / total_count, loss.merchandise(), elapsed
)
)
total_acc, total_count = 0, 0
start_time = time.time()
if max_steps will not be None:
if idx == max_steps:
return {'loss': loss.merchandise(), 'acc': total_acc / total_count}
return {'loss': loss.merchandise(), 'acc': total_acc / total_count}
# --- Validation loop ---
def consider(mannequin, dataloader, loss_fn):
mannequin.eval()
total_acc, total_count = 0, 0
with torch.no_grad():
for idx, (inputs, label) in enumerate(dataloader):
predicted_label = mannequin(inputs)
loss = loss_fn(predicted_label, label)
total_acc += (predicted_label.argmax(1) == label).sum().merchandise()
total_count += label.dimension(0)
return {'loss': loss.merchandise(), 'acc': total_acc / total_count}
# --- Predominant operate ---
def train_and_evaluate(**params):
logging.data("operating with the next params :")
logging.data(params)
# Load pretrained tokenizers and bert mannequin
# replace the paths to whichever you're utilizing
tokenizer = BertTokenizer.from_pretrained('fashions/bert_uncased_L-2_H-128_A-2/vocab.txt')
mannequin = BertModel.from_pretrained('fashions/bert_uncased_L-2_H-128_A-2')
# Coaching parameters
epochs = int(params.get('epochs'))
batch_size = int(params.get('batch_size'))
learning_rate = float(params.get('learning_rate'))
# Load the information
df_train = pd.read_csv(params.get('training_file'))
df_eval = pd.read_csv(params.get('validation_file'))
df_test = pd.read_csv(params.get('testing_file'))
# Create dataloaders
train_ds = BertDataset(df_train, tokenizer, max_length=100)
train_loader = DataLoader(dataset=train_ds,batch_size=batch_size, shuffle=True)
eval_ds = BertDataset(df_eval, tokenizer, max_length=100)
eval_loader = DataLoader(dataset=eval_ds,batch_size=batch_size)
test_ds = BertDataset(df_test, tokenizer, max_length=100)
test_loader = DataLoader(dataset=test_ds,batch_size=batch_size)
# Create the mannequin
classifier = SentimentBERT(bert_model=mannequin).to(DEVICE)
total_parameters = sum((np.prod(p.dimension()) for p in classifier.parameters()))
model_parameters = filter(lambda p: p.requires_grad, classifier.parameters())
params = sum((np.prod(p.dimension()) for p in model_parameters))
logging.data(f"Whole params : {total_parameters} - Trainable : {params} ({params/total_parameters*100}% of complete)")
# Optimizer and loss capabilities
optimizer = torch.optim.Adam((p for p in classifier.parameters() if p.requires_grad), learning_rate)
loss_fn = nn.CrossEntropyLoss()
# If dry run we solely
logging.data(f'Coaching mannequin with {BERT_MODEL_NAME}')
if args.dry_run:
logging.data("Dry run mode")
epochs = 1
steps_per_epoch = 1
else:
steps_per_epoch = None
# Motion !
for epoch in vary(1, epochs + 1):
epoch_start_time = time.time()
train_metrics = practice(epoch, classifier, train_loader, loss_fn=loss_fn, optimizer=optimizer, max_steps=steps_per_epoch)
eval_metrics = consider(classifier, eval_loader, loss_fn=loss_fn)
print("-" * 59)
print(
"Finish of epoch {:3d} - time: {:5.2f}s - loss: {:.4f} - accuracy: {:.4f} - valid_loss: {:.4f} - legitimate accuracy {:.4f} ".format(
epoch, time.time() - epoch_start_time, train_metrics('loss'), train_metrics('acc'), eval_metrics('loss'), eval_metrics('acc')
)
)
print("-" * 59)
if args.dry_run:
# If dry run, we don't run the analysis
return None
test_metrics = consider(classifier, test_loader, loss_fn=loss_fn)
metrics = {
'practice': train_metrics,
'val': eval_metrics,
'check': test_metrics,
}
logging.data(metrics)
# save mannequin and structure to single file
if params.get('job_dir') is None:
logging.warning("No job dir supplied, mannequin is not going to be saved")
else:
logging.data("Saving mannequin to {} ".format(params.get('job_dir')))
torch.save(classifier.state_dict(), params.get('job_dir'))
logging.data("Bye bye")
if __name__ == '__main__':
# Create arguments right here
parser = argparse.ArgumentParser()
parser.add_argument('--training-file', required=True, sort=str)
parser.add_argument('--validation-file', required=True, sort=str)
parser.add_argument('--testing-file', sort=str)
parser.add_argument('--job-dir', sort=str)
parser.add_argument('--epochs', sort=float, default=2)
parser.add_argument('--batch-size', sort=float, default=1024)
parser.add_argument('--learning-rate', sort=float, default=0.01)
parser.add_argument('--dry-run', motion="store_true")
# Parse them
args, _ = parser.parse_known_args()
# Execute coaching
train_and_evaluate(**vars(args))
Điều này thật tuyệt, nhưng thật không could, mô hình này sẽ mất nhiều thời gian để đào tạo. Thật vậy, với khoảng 4,7M thông số cần huấn luyện, một bước sẽ mất khoảng 3 giây trên Macbook Professional 16Gb trang bị chip Intel.
3 giây mỗi bước có thể khá dài khi bạn còn 1238 bước cần thực hiện và 10 kỷ nguyên cần hoàn thành…
Không có GPU, không có bữa tiệc.
[ad_2]
Source link