※ 본 내용은 도서 '파이썬 텍스트 마이닝 완벽 가이드(박상언, 강주영, 정석찬 지음. 위키북스)'를 참고하여 만든 학교 강의 실습 자료를 바탕으로 하였습니다.
Hugging Face
- 딥러닝 모델과 자연어처리(NLP) 기술을 개발하고 공유하는 회사
- 다양한 딥러닝 프레임워크를 지원하는 라이브러리와 도구 제공
Transformers 라이브러리
- 자연어처리(NLP), 컴퓨터 비전, 오디오 및 음성 처리 작업에 대한 사전 훈련된 최첨단 모델 라이브러리
- 컴퓨터 비전 작업을 위한 현대적인 합성곱 신경망과 같은 트랜스포머가 아닌 모델도 포함
- Tokenizers, Datasets와 같은 라이브러리를 추가로 제공하여 task를 수행하기 위한 tokenizer, dataset을
손쉽게 다운로드 받아 사용
Pipeline() 함수
- Transformers 라이브러리의 가장 기본적인 객체
- 특정 모델과 동작에 필요한 전처리 및 후처리 단계를 연결하여 텍스트를 직접 입력하고 이해하기 쉬운
답변 획득 가능
Hugging Face와 Transformer 모델들을 활용하여 다음과 같이 4가지 실습을 진행하였다.
- 실습 1) BERT Pre-trained 모델을 Fine-tuning하기 (Ch.15)
- 실습 2) 한국어 문서에 대해 Fine-tuning한 BERT 모델 적용하기 (Ch.16)
- 실습 3) Transformer 모델로 문서 요약하기 (Ch. 18)
- 실습 4) Transformer 모델로 질의 응답하기 (Ch. 19)
1) BERT Pre-trained 모델을 Fine-tuning하기
- 데이터셋 전처리 및 준비
import nltk
from nltk.corpus import movie_reviews
from sklearn.model_selection import train_test_split # sklearn에서 제공하는 split 함수를 사용
import numpy as np
nltk.download('movie_reviews')
fileids = movie_reviews.fileids() # movie review data에서 file id를 가져옴
reviews = [movie_reviews.raw(fileid) for fileid in fileids] # file id를 이용해 raw text file을 가져옴
categories = [movie_reviews.categories(fileid)[0] for fileid in fileids]
# label을 0, 1의 값으로 변환
label_dict = {'pos':1, 'neg':0}
y = [label_dict[c] for c in categories]
X_train, X_test, y_train, y_test = train_test_split(reviews, y, test_size=0.2, random_state=7)
print('Train set count: ', len(X_train)) # Train set count: 1600
print('Test set count: ', len(X_test)) # Test set count: 400
- 토큰화 및 데이터셋 로드
tokenizer = BertTokenizerFast.from_pretrained('bert-base-uncased')
- BERT 모델에서 사용하는 토크나이저를 구현한 것으로, 빠른 실행 속도와 효율성이 특징
from transformers import BertTokenizerFast, BertForSequenceClassification
from torch.utils.data import DataLoader
import torch
tokenizer = BertTokenizerFast.from_pretrained('bert-base-uncased')
train_input = tokenizer(X_train, truncation=True, padding=True, return_tensors="pt")
test_input = tokenizer(X_test, truncation=True, padding=True, return_tensors="pt")
"""
truncation = True
- 입력 텍스트가 모델의 최대 길이를 초과하는 경우, 초과된 부분을 제거
- BERT 모델이 입력으로 받을 수 있는 최대 토큰 길이에 맞추기 위한 설정
padding = True
- 입력 텍스트의 길이를 동일하게 맞추기 위해 짧은 텍스트에는 패딩을 추가
- 패딩 값([PAD])이 추가되며, 이는 토큰 ID에서 0으로 변환됨.
- 기본적으로 가장 긴 텍스트의 길이에 맞춰 패딩됨.
"""
class OurDataset(torch.utils.data.Dataset):
def __init__(self, inputs, labels):
self.inputs = inputs
self.labels = labels
def __getitem__(self, idx):
item = {key: torch.tensor(val[idx]) for key, val in self.inputs.items()}
item['labels'] = torch.tensor(self.labels[idx])
return item
def __len__(self):
return len(self.labels)
train_dataset = OurDataset(train_input, y_train)
test_dataset = OurDataset(test_input, y_test)
train_loader = DataLoader(train_dataset, shuffle=True, batch_size=8)
- Fine-tuning하려는 BERT 모델 정의
- [CLS] 토큰은 사전 학습된 BERT(BertModel)모델로 생성되므로, 학습 중에 데이터셋의 레이블에 맞게 BERT와 Classifier의 파라미터가 조정됨. [CLS] 토큰은 전체 문장을 요약하는 벡터로 사용됨.
- 단순 선형 layer(torch.nn.Linear)인 Classifier을 추가하여 [CLS] 토큰의 표현을 받아 분류 결과를 반환함.
from transformers import BertModel
# BERT를 포함한 신경망 모형
class MyModel(torch.nn.Module):
def __init__(self, pretrained_model, token_size, num_labels):
super(MyModel, self).__init__()
self.token_size = token_size
self.num_labels = num_labels
self.pretrained_model = pretrained_model
# 분류기 정의
self.classifier = torch.nn.Linear(self.token_size, self.num_labels)
def forward(self, inputs):
# BERT 모형에 입력을 넣고 출력을 받음
outputs = self.pretrained_model(**inputs)
# BERT 출력에서 CLS 토큰에 해당하는 부분만 가져옴
bert_clf_token = outputs.last_hidden_state[:,0,:]
return self.classifier(bert_clf_token)
bert_model = BertModel.from_pretrained('bert-base-uncased')
# token_size는 BERT 토큰과 동일, bert_model.config.hidden_size로 알 수 있음
model = MyModel(bert_model, num_labels=2, token_size=bert_model.config.hidden_size)
- 모델 훈련
from transformers import AdamW
import torch.nn.functional as F
import time
# GPU 가속을 사용할 수 있으면 device를 cuda로 설정하고, 아니면 cpu로 설정
device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')
model.to(device) # 모형을 GPU로 복사
model.train() # 학습모드로 전환
optim = AdamW(model.parameters(), lr=5e-5) # 옵티마이저를 트랜스포머가 제공하는 AdamW로 설정
criterion = torch.nn.CrossEntropyLoss() # 멀티클래스이므로 크로스 엔트로피를 손실함수로 사용
start = time.time() # 시작시간 기록
num_epochs = 4 # 학습 epoch를 4회로 설정
for epoch in range(num_epochs):
total_epoch_loss = 0 # epoch의 총 loss 초기화
for step, batch in enumerate(train_loader):
optim.zero_grad() # 그래디언트 초기화
# 배치에서 label을 제외한 입력만 추출하여 GPU로 복사
inputs = {k: v.to(device) for k, v in batch.items() if k != 'labels'}
labels = batch['labels'].to(device) # 배치에서 라벨을 추출하여 GPU로 복사
outputs = model(inputs) # 모형으로 결과 예측
# 두 클래스에 대해 예측하고 각각 비교해야 하므로 labels에 대해 One-hot-encoding을 적용한 후에 손실을 게산
loss = criterion(outputs, F.one_hot(labels, num_classes=2).float()) # loss 계산
if (step+1) % 100 == 0: # 100 배치마다 경과한 시간과 loss를 출력
elapsed = time.time() - start
print('Epoch %d, batch %d, elapsed time: %.2f, loss: %.4f' % (epoch+1, step+1, elapsed, loss))
total_epoch_loss += loss
loss.backward() # 그래디언트 계산
optim.step() # 가중치 업데이트
avg_epoch_loss = total_epoch_loss / len(train_loader) # epoch의 평균 loss 계산
print('Average loss for epoch %d: %.4f' % (epoch+1, avg_epoch_loss))
- 모델 평가
!pip install evaluate # HuggingFace에서 제공하는 모델 평가용 라이브러리
import evaluate
metric = evaluate.load("accuracy")
test_loader = DataLoader(test_dataset, batch_size=16)
model.eval()
for batch in test_loader:
inputs = {k: v.to(device) for k, v in batch.items() if k != 'labels'}
labels = batch['labels'].to(device)
with torch.no_grad(): # 학습할 필요가 없으므로 그래디언트 계산을 끔
outputs = model(inputs)
predictions = torch.argmax(outputs, dim=-1)
metric.add_batch(predictions=predictions, references=labels)
metric.compute()
2) 한국어 문서에 대해 Fine-tuning한 BERT 모델 적용하기
- 데이터셋 전처리 및 준비
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
df = pd.read_csv('daum_movie_review.csv')
# rating이 6보다 작으면 0 즉 부정, 6 이상이면 긍정으로 라벨 생성
y = [0 if rate < 6 else 1 for rate in df.rating]
# 데이터셋을 학습, 검증, 평가의 세 데이터셋으로 분리
X_train_val, X_test, y_train_val, y_test = train_test_split(df.review.tolist(), y, random_state=0)
X_train, X_val, y_train, y_val = train_test_split(X_train_val, y_train_val, random_state=0)
print('Train set size:', len(X_train)) # Train set size: 8282
print('Validation set size:', len(X_val)) # Validation set size: 2761
print('Test set size:', len(X_test)) # Test set size: 3682
- 토큰화 및 데이터셋 로드
- tokenizer = KoBERTTokenizer.from_pretrained('skt/kobert-base-v1')
!pip install sentencepiece
!pip install 'git+https://github.com/SKTBrain/KoBERT.git#egg=kobert_tokenizer&subdirectory=kobert_hf'
!pip install evaluate # 모델 평가용 라이브러리
from kobert_tokenizer import KoBERTTokenizer
from transformers import BertModel
from torch.utils.data import DataLoader
tokenizer = KoBERTTokenizer.from_pretrained('skt/kobert-base-v1')
# 한국어 텍스트를 처리하기 위해 설계된 tokenizer로, KoBERT와 함께 사용
# 토큰화
train_input = tokenizer(X_train, truncation=True, padding=True, return_tensors="pt")
val_input = tokenizer(X_val, truncation=True, padding=True, return_tensors="pt")
test_input = tokenizer(X_test, truncation=True, padding=True, return_tensors="pt")
# Dataset 생성
train_dataset = OurDataset(train_input, y_train)
val_dataset = OurDataset(val_input, y_val)
test_dataset = OurDataset(test_input, y_test)
# DataLoader 생성
train_loader = DataLoader(train_dataset, shuffle=True, batch_size=8)
val_loader = DataLoader(val_dataset, batch_size=16)
test_loader = DataLoader(test_dataset, batch_size=16)
- Fine-tuning하려는 BERT 모델 정의
- [CLS] 토큰은 사전 학습된 KoBERT(kobert-base-v1)모델로 생성되므로, 학습 중에 데이터셋의 레이블에
맞게 BERT와 Classifier의 파라미터가 조정됨. [CLS] 토큰은 전체 문장을 요약하는 벡터로 사용됨.
- 단순 선형 layer(torch.nn.Linear)인 Classifier을 추가하여 [CLS] 토큰의 표현을 받아 분류 결과를 반환함.
# BERT를 포함한 신경망 모형
class MyModel(torch.nn.Module):
def __init__(self, pretrained_model, token_size, num_labels):
super(MyModel, self).__init__()
self.token_size = token_size
self.num_labels = num_labels
self.pretrained_model = pretrained_model
# 분류기 정의
self.classifier = torch.nn.Linear(self.token_size, self.num_labels)
def forward(self, inputs):
# BERT 모형에 입력을 넣고 출력을 받음
outputs = self.pretrained_model(**inputs)
# BERT 출력에서 CLS 토큰에 해당하는 부분만 가져옴
bert_clf_token = outputs.last_hidden_state[:,0,:]
return self.classifier(bert_clf_token)
# KoBERT 사전학습모델 로드
bert_model = BertModel.from_pretrained('skt/kobert-base-v1')
# token_size는 BERT 토큰과 동일, bert_model.config.hidden_size로 알 수 있음
model = MyModel(bert_model, num_labels=2, token_size=bert_model.config.hidden_size)
- 모델 훈련
from transformers import AdamW, get_linear_schedule_with_warmup
import torch.nn.functional as F
import time
# GPU 가속을 사용할 수 있으면 device를 cuda로 설정하고, 아니면 cpu로 설정
device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')
model.to(device) # 모형을 GPU로 복사
model.train() # 학습모드로 전환
# 옵티마이저를 트랜스포머가 제공하는 AdamW로 설정
optim = AdamW(model.parameters(), lr=5e-5, weight_decay=0.01) # 가중치 감쇠 설정
criterion = torch.nn.CrossEntropyLoss() # 멀티클래스이므로 크로스 엔트로피를 손실함수로 사용
num_epochs = 2 # 학습 epoch 설정
total_training_steps = num_epochs * len(train_loader)
# 학습 스케줄러 설정
scheduler = get_linear_schedule_with_warmup(optimizer=optim,
num_training_steps=total_training_steps,
num_warmup_steps=200)
start = time.time() # 시작시간 기록
train_loss = 0
eval_steps = 500
step = 0
for epoch in range(num_epochs):
for batch in train_loader:
model.train() # 학습모드로 전환
optim.zero_grad() # 그래디언트 초기화
# 배치에서 label을 제외한 입력만 추출하여 GPU로 복사
inputs = {k: v.to(device) for k, v in batch.items() if k != 'labels'}
labels = batch['labels'].to(device) # 배치에서 라벨을 추출하여 GPU로 복사
outputs = model(inputs) # 모형으로 결과 예측
# 두 클래스에 대해 예측하고 각각 비교해야 하므로 labels에 대해 One-hot-encoding을 적용한 후에 손실을 계산
loss = criterion(outputs, F.one_hot(labels, num_classes=2).float()) # loss 계산
train_loss += loss
loss.backward() # 그래디언트 계산
optim.step() # 가중치 업데이트
scheduler.step() # 스케줄러 업데이트
step += 1
if step % eval_steps == 0: # eval_steps 마다 경과한 시간과 loss를 출력
with torch.no_grad():
val_loss = 0
model.eval()
for batch in val_loader:
inputs = {k: v.to(device) for k, v in batch.items() if k != 'labels'}
labels = batch['labels'].to(device)
outputs = model(inputs)
loss = criterion(outputs, F.one_hot(labels, num_classes=2).float()) # loss 계산
val_loss += loss
avg_val_loss = val_loss / len(val_loader)
avg_train_loss = train_loss / eval_steps
elapsed = time.time() - start
print('Step %d, elapsed time: %.2f, train loss: %.4f, validation loss: %.4f'
% (step, elapsed, avg_train_loss, avg_val_loss))
train_loss = 0
- 모델 평가
import evaluate,torch
metric = evaluate.load("accuracy")
model.eval()
for batch in test_loader:
inputs = {k: v.to(device) for k, v in batch.items() if k != 'labels'}
labels = batch['labels'].to(device)
with torch.no_grad(): # 학습할 필요가 없으므로 그래디언트 계산을 끔
outputs = model(inputs)
print(outputs)
predictions = torch.argmax(outputs, dim=-1)
metric.add_batch(predictions=predictions, references=labels)
metric.compute()
3) Transformer 모델로 문서 요약하기
- [1] HuggingFace의 transformers.pipeline을 이용한 문서 요약
- 기본 pipeline 기능 중 ‘summarization’ 사용
from transformers import pipeline
# 문서요약을 위한 파이프라인 생성
summarizer = pipeline("summarization")
# 요약 대상 원문 - 텍스트마이닝의 정의(Wikipedia)
text = '''Text mining, also referred to as text data mining (abbr.: TDM), similar to text analytics,
is the process of deriving high-quality information from text. It involves
"the discovery by computer of new, previously unknown information,
by automatically extracting information from different written resources."
Written resources may include websites, books, emails, reviews, and articles.
High-quality information is typically obtained by devising patterns and trends
by means such as statistical pattern learning. According to Hotho et al. (2005)
we can distinguish between three different perspectives of text mining:
information extraction, data mining, and a KDD (Knowledge Discovery in Databases) process.'''
# 파이프라인으로 문서요약 수행
summary_text = summarizer(text)
print("요약문:\n", summary_text)
print("원문 길이:", len(text), "요약문 길이:", len(summary_text[0]["summary_text"]))
요약문:
[{'summary_text': ' Text mining involves deriving high-quality information from text . Written resources may include websites, books, emails, reviews, and articles . Text mining is similar to text analytics . It involves the discovery by computer of new, previously unknown information by automatically extracting information from different written resources .'}]
원문 길이: 778 요약문 길이: 341
- [2] T5 모델과 AutoClass를 이용한 문서 요약
- tokenizer = AutoTokenizer.from_pretrained("t5-small", model_max_length=512)
- AutoTokenizer: Hugging Face에서 제공하는 범용 tokenizer 클래스
- model = AutoModelForSeq2SeqLM.from_pretrained("t5-small")
- AutoModelForSeq2SeqLM: Hugging Face에서 제공하는 범용 모델 클래스
- T5: Text-to-Text Transfer Transformer
import torch
from transformers import AutoModelForSeq2SeqLM, AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("t5-small", model_max_length=512)
print("tokenizer type:", type(tokenizer))
model = AutoModelForSeq2SeqLM.from_pretrained("t5-small")
print("model type:", type(model))
# GPU 가속을 사용할 수 있으면 device를 cuda로 설정하고, 아니면 cpu로 설정
device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')
model = model.to(device)
# 원문에 필요한 전처리를 수행. 여기서는 strip()을 적용하고 \n(줄바꿈)을 제거
preprocess_text = text.strip().replace("\n","")
# 전처리 결과 앞에 summarize: 를 추가 - 모형의 task를 summarize(문서요약)로 지정
input_text = "summarize: " + preprocess_text
# 입력 원문을 토큰화
# input_text를 토큰 ID로 변환
# 텍스트는 모델의 tokenizer를 사용하여 더 작은 구성 요소(하위 단어, 단어)로 토큰화되고 tokenizer의 어휘를 기반으로 고유한 정수 ID에 매핑됨.
tokenized_text = tokenizer.encode(input_text, return_tensors="pt").to(device)
# 요약문 생성
summary_ids = model.generate(tokenized_text,
num_beams=4, # beam의 길이
no_repeat_ngram_size=3, # 동어 반복을 피하기 위해 사용
min_length=30, # 요약문의 최소 토큰 수
max_length=100, # 요약문의 최대 토큰 수
early_stopping=True) # EOS 토큰을 만나면 종료
output = tokenizer.decode(summary_ids[0], skip_special_tokens=True)
"""
summary_ids[0]의 토큰 ID를 사람이 읽을 수 있는 텍스트로 다시 변환
- summary_ids[0]: 모델은 여러 후보 시퀀스(num_beams 기준)를 생성할 수 있지만 여기서는 최상위 시퀀스([0])만 선택됨.
- skip_special_tokens=True: 디코딩된 출력에서 <pad>, <eos> 또는 <bos>와 같은 특수 토큰을 제거하고 요약 텍스트만 남김.
"""
print("Summarized text: \n",output)
print("Original text length:", len(text), "Summarized text length:", len(output))
Summarized text:
text data mining is the process of deriving high-quality information from text. it involves the discovery by computer of new, previously unknown information. a KDD (Knowledge Discovery in Databases) process is similar to text analytics.
Original text length: 778 Summarized text length: 236
- [3] AutoClass를 이용한 한글 문서 요약
1. KoBART(BartForConditionalGeneration.from_pretrained('gogamza/kobart-summarization')) 이용
2. mT5-multilingual-XLSum(AutoModelForSeq2SeqLM.from_pretrained("csebuetnlp/mT5_multilingual_XLSum") 이용
- mT5(multilingual-T5): Google T5 제품군의 일부인 transformer 기반 모델로, 다국어 데이터셋에 대해 사전 훈련되어 여러 언어 작업에 효과적
- XLSum: 45개 이상의 언어로 구성된 텍스트 요약 데이터셋
text = """디아블로는 액션 롤플레잉 핵 앤드 슬래시 비디오 게임이다.
플레이어는 주변 환경을 마우스로 사용해 영웅을 움직이게 한다.
주문을 외는 등의 다른 활동은 키보드 입력으로 이루어진다.
플레이어는 이 게임에서 장비를 획득하고, 주문을 배우고, 적을 쓰러뜨리며, NPC와 대화를 나눌 수 있다.
지하 미궁은 주어진 형식이 있고 부분적으로 반복되는 형태가 존재하나 전체적으로 보면 무작위로 생성된다.
예를 들어 지하 묘지의 경우에는 긴 복도와 닫힌 문들이 존재하고, 동굴은 좀 더 선형 형태를 띠고 있다.
플레이어에게는 몇몇 단계에서 무작위의 퀘스트를 받는다.
이 퀘스트는 선택적인 사항이나 플레이어의 영웅들을 성장시키거나 줄거리를 이해하는데 도움을 준다.
그러나 맨 뒤에 두 퀘스트는 게임을 끝내기 위해 완료시켜야 한다."""
preprocess_text = text.strip().replace("\n","")
from transformers import PreTrainedTokenizerFast
from transformers import BartForConditionalGeneration
tokenizer = PreTrainedTokenizerFast.from_pretrained('gogamza/kobart-summarization')
model = BartForConditionalGeneration.from_pretrained('gogamza/kobart-summarization')
tokenized_text = tokenizer.encode(preprocess_text, return_tensors="pt")
summary_ids = model.generate(tokenized_text,
num_beams=4, # beam의 길이
no_repeat_ngram_size=3, # 동어 반복을 피하기 위해 사용
min_length=10, # 요약문의 최소 토큰 수
max_length=150, # 요약문의 최대 토큰 수
early_stopping=True) # EOS 토큰을 만나면 종료
summary = tokenizer.decode(summary_ids[0], skip_special_tokens=True)
print(summary)
# 디아블로는 액션 롤플레잉 핵 앤드 슬래시 비디오 게임이다.
from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
tokenizer = AutoTokenizer.from_pretrained("csebuetnlp/mT5_multilingual_XLSum")
model = AutoModelForSeq2SeqLM.from_pretrained("csebuetnlp/mT5_multilingual_XLSum")
tokenized_text = tokenizer.encode(preprocess_text, return_tensors="pt")
summary_ids = model.generate(tokenized_text,
num_beams=4, # beam의 길이
no_repeat_ngram_size=2, # 동어 반복을 피하기 위해 사용
min_length=10, # 요약문의 최소 토큰 수
max_length=150, # 요약문의 최대 토큰 수
early_stopping=True) #EOS 토큰을 만나면 종료
summary = tokenizer.decode(summary_ids[0], skip_special_tokens=True)
print(summary)
# 디아블로 게임의 이야기를 들어봤다.
4) Transformer 모델로 질의 응답하기
- [1] HuggingFace의 transformers.pipeline을 이용한 질의 응답
- 기본 pipeline 기능 중 ‘question-answering’ 사용
from transformers import pipeline
question_answerer = pipeline("question-answering") # pipeline에 질의 응답에 사용할 모델(’model’) 지정 가능
context = r'''Text mining, also referred to as text data mining (abbr.: TDM), similar to text analytics,
is the process of deriving high-quality information from text. It involves
"the discovery by computer of new, previously unknown information,
by automatically extracting information from different written resources."
Written resources may include websites, books, emails, reviews, and articles.
High-quality information is typically obtained by devising patterns and trends
by means such as statistical pattern learning. According to Hotho et al. (2005)
we can distinguish between three different perspectives of text mining:
information extraction, data mining, and a KDD (Knowledge Discovery in Databases) process.'''
question = "What is text mining?"
answer = question_answerer(question=question, context=context)
print(answer)
question2 = "What are the perspectives of text mining?"
answer2 = question_answerer(question=question2, context=context)
print("질의:", question2)
print("응답:", answer2['answer'])
print("응답에 사용된 context:", context[answer2['start']:answer2['end']])
{'score': 0.42419037222862244, 'start': 103, 'end': 161, 'answer': 'the process of deriving high-quality information from text'}
질의: What are the perspectives of text mining?
응답: information extraction, data mining, and a KDD
응답에 사용된 context: information extraction, data mining, and a KDD
- [2] BERT 모델과 AutoClass를 이용한 질의 응답
- tokenizer = AutoTokenizer.from_pretrained('distilbert-base-cased-distilled-squad')
- model = AutoModelForQuestionAnswering.from_pretrained('distilbert-base-cased-distilled-squad')
from transformers import AutoTokenizer, AutoModelForQuestionAnswering
import torch
tokenizer = AutoTokenizer.from_pretrained('distilbert-base-cased-distilled-squad')
print("tokenizer type:", type(tokenizer))
model = AutoModelForQuestionAnswering.from_pretrained('distilbert-base-cased-distilled-squad')
print("model type:", type(model))
# GPU 가속을 사용할 수 있으면 device를 cuda로 설정하고, 아니면 cpu로 설정
device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')
model = model.to(device)
# 질문과 context를 함께 토큰화
# Text와 질문들에 대해, 질문을 순회하면서 현재 질문에 대한 [모델별 구분자, Token Type ID, Attention Mask를 사용해 시퀀스 구성 → 모델에 전달
inputs = tokenizer(question, context, return_tensors="pt").to(device)
with torch.no_grad():
outputs = model(**inputs)
print("output type", type(outputs))
answer_start_scores = outputs.start_logits
answer_end_scores = outputs.end_logits
# argmax를 이용해 context에서 응답의 시작일 확률이 가장 높은 토큰의 위치를 반환
answer_start = torch.argmax(answer_start_scores)
# argmax를 이용해 context에서 응답의 끝일 확률이 가장 높은 토큰의 위치를 반환
answer_end = torch.argmax(answer_end_scores) + 1
print("start:", answer_start, ", end:", answer_end)
# 토큰화 결과로부터 input_ids만 추출
input_ids = inputs["input_ids"].tolist()[0]
# input_ids에서 응답에 해당하는 id를 가져와 토큰으로 변환하고 다시 문자열로 변환
answer = tokenizer.convert_tokens_to_string(
tokenizer.convert_ids_to_tokens(input_ids[answer_start:answer_end])
)
print("질의:", question)
print("응답:", answer)
# model type: <class 'transformers.models.distilbert.modeling_distilbert.DistilBertForQuestionAnswering'>
# output type <class 'transformers.modeling_outputs.QuestionAnsweringModelOutput'>
start: tensor(35) , end: tensor(46)
질의: What is text mining?
응답: the process of deriving high - quality information from text
- [3] HuggingFace의 transformers.pipeline을 이용한 한글 문서 질의 응답
- tokenizer = ElectraTokenizer.from_pretrained("monologg/koelectra-small-v2-distilled-korquad-384")
- model = ElectraForQuestionAnswering.from_pretrained("monologg/koelectra-small-v2-distilled-korquad-384")
- KoElectra: 한국어에 특화된 Electra 모델. Electra 모델은 토큰 교체를 정확하게 분류하는 Encoder를 효율적으로 학습할 수 있는 trasnformer 기반 언어 모델
question = "수원 화성은 언제 완성되었는가?"
context = """수원 화성은 조선시대 화성유수부 시가지를 둘러싼 성곽이다.
1789년(정조 13) 수원을 팔달산 동쪽 아래로 옮기고,
1794년(정조 18) 축성을 시작해 1796년에 완성했다."""
context = context.strip().replace("\n","")
from transformers import ElectraTokenizer, ElectraForQuestionAnswering, pipeline
import torch
tokenizer = ElectraTokenizer.from_pretrained("monologg/koelectra-small-v2-distilled-korquad-384")
model = ElectraForQuestionAnswering.from_pretrained("monologg/koelectra-small-v2-distilled-korquad-384")
question_answerer = pipeline("question-answering", tokenizer=tokenizer, model=model)
answer = question_answerer({
"question": question,
"context": context,
})
print(answer)
# {'score': 0.9962994456291199, 'start': 87, 'end': 93, 'answer': '1796년에'}
import torch
inputs = tokenizer(question, context, return_tensors="pt") # 토큰화
outputs = model(**inputs)
answer_start_scores = outputs.start_logits
answer_end_scores = outputs.end_logits
# argmax를 이용해 context에서 응답의 시작일 확률이 가장 높은 토큰의 위치를 반환
answer_start = torch.argmax(answer_start_scores)
# argmax를 이용해 context에서 응답의 끝일 확률이 가장 높은 토큰의 위치를 반환
answer_end = torch.argmax(answer_end_scores) + 1
print("start:", answer_start, ", end:", answer_end)
# 토큰화 결과로부터 input_ids만 추출
input_ids = inputs["input_ids"].tolist()[0]
# input_ids에서 응답에 해당하는 id를 가져와 토큰으로 변환하고 다시 문자열로 변환
answer = tokenizer.convert_tokens_to_string(
tokenizer.convert_ids_to_tokens(input_ids[answer_start:answer_end]))
print("질의:", question)
print("응답:", answer)
"""
start: tensor(57) , end: tensor(60)
질의: 수원 화성은 언제 완성되었는가?
응답: 1796년
"""
'Research & Study Notes' 카테고리의 다른 글
처음부터 RAG pipeline 구현하기 - (2) (1) | 2024.12.17 |
---|---|
처음부터 RAG pipeline 구현하기 - (1) (2) | 2024.12.12 |
KSS 데이터셋으로 TorToise-tts 한국어 Fine-tuning (6) | 2024.11.05 |
Word representation & Feed Forward Neural Network Language Model(NNLM) (1) | 2024.03.07 |
LoRA를 이용한 LLM Fine-tuning (1) | 2024.02.12 |