모델 학습 코드
!pip install transformers
!pip install torch
import pandas as pd
from sklearn.model_selection import train_test_split
import json
# JSON 데이터 불러오기
file_path = '/content/news_data.json' # 앞에서 생성한 JSON 파일 경로를 사용합니다.
with open(file_path, 'r', encoding='utf-8') as file:
data = json.load(file)
# JSON 데이터를 DataFrame으로 변환
data_df = pd.DataFrame(data)
# 'Title'과 'CategoryID' 열을 사용하여 새로운 DataFrame 생성
data_df = data_df[['Title', 'CategoryID']]
# NaN 값 제거 (이 경우 JSON 데이터에는 NaN 값이 없어야 합니다)
data_df.dropna(inplace=True)
# 레이블 인코딩 (이 경우 'CategoryID'가 이미 인코딩된 값입니다)
# 훈련, 검증 및 테스트 데이터로 분리
train_val_data, test_data = train_test_split(data_df, test_size=0.1)
train_data, val_data = train_test_split(train_val_data, test_size=0.1) # 훈련 데이터의 10%를 검증 데이터로 사용
# 데이터 세트의 크기 확인
print("전체 데이터 크기:", data_df.shape)
print("훈련 데이터 크기:", train_data.shape)
print("검증 데이터 크기:", val_data.shape)
print("테스트 데이터 크기:", test_data.shape)
from transformers import BertTokenizerFast, BertForSequenceClassification
from torch.utils.data import DataLoader, Dataset
import torch
# 데이터 준비 코드에서 생성한 train_data, val_data, test_data를 사용
# 레이블 매핑 생성
label_map = {label: idx for idx, label in enumerate(train_data['CategoryID'].unique())}
# 토크나이저 및 모델 초기화
tokenizer = BertTokenizerFast.from_pretrained("kykim/bert-kor-base")
model = BertForSequenceClassification.from_pretrained(
"kykim/bert-kor-base",
num_labels=len(label_map) # 레이블의 개수를 모델에 알림
)
# 데이터셋 클래스 정의
class NewsDataset(Dataset):
def __init__(self, dataframe, tokenizer):
self.labels = dataframe['CategoryID'].values
self.texts = [tokenizer(text, padding='max_length', max_length=36, truncation=True, return_tensors="pt") for text in dataframe['Title']]
def __len__(self):
return len(self.labels)
def __getitem__(self, idx):
item = self.texts[idx]
label = self.labels[idx]
# 토크나이저에서 반환된 텐서를 꺼내는 작업
input_ids = item['input_ids'].squeeze()
attention_mask = item['attention_mask'].squeeze()
token_type_ids = item['token_type_ids'].squeeze()
return {
'input_ids': input_ids,
'attention_mask': attention_mask,
'token_type_ids': token_type_ids,
'labels': label
}
# 데이터셋 생성 및 데이터 로더 설정
train_dataset = NewsDataset(train_data, tokenizer)
train_loader = DataLoader(train_dataset, batch_size=6, shuffle=True)
val_dataset = NewsDataset(val_data, tokenizer)
val_loader = DataLoader(val_dataset, batch_size=6, shuffle=False)
test_dataset = NewsDataset(test_data, tokenizer)
test_loader = DataLoader(test_dataset, batch_size=6, shuffle=False)
from transformers import AdamW, get_linear_schedule_with_warmup
import time
import torch
# device 설정과 모델을 GPU로 이동하는 코드는 동일하게 유지합니다.
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)
# 옵티마이저 설정
optimizer = AdamW(model.parameters(), lr=2e-5, eps=1e-8)
# 정확도 계산 함수
def calculate_accuracy(preds, labels):
# preds가 numpy 배열일 경우 torch 텐서로 변환
if not isinstance(preds, torch.Tensor):
preds = torch.tensor(preds)
# labels가 numpy 배열일 경우 torch 텐서로 변환
if not isinstance(labels, torch.Tensor):
labels = torch.tensor(labels)
# 예측값의 가장 높은 logit을 예측 레이블로 사용
_, predictions = torch.max(preds, dim=1)
# 정확한 예측의 개수 계산
correct = (predictions == labels).float()
accuracy = correct.sum() / len(correct)
return accuracy
# 에포크 수 설정
epochs = 20
# 훈련 시작 시간
start_time = time.time()
from matplotlib import pyplot as plt
train_losses = []
train_accuracies = []
val_losses = []
val_accuracies = []
# 총 훈련 스텝 = 배치 개수 * 에포크
total_steps = len(train_loader) * epochs
# 스케줄러 생성
scheduler = get_linear_schedule_with_warmup(optimizer,
num_warmup_steps=0,
num_training_steps=total_steps)
# 모델의 훈련과 평가를 위한 학습 루프
for epoch in range(epochs):
print('======== Epoch {:} / {:} ========'.format(epoch + 1, epochs))
# 훈련 모드 설정
model.train()
total_loss = 0
total_accuracy = 0
for batch in train_loader:
# 배치에서 데이터를 GPU로 이동
b_input_ids = batch['input_ids'].to(device)
b_attention_mask = batch['attention_mask'].to(device)
b_labels = batch['labels'].to(device)
# 옵티마이저 초기화
optimizer.zero_grad()
# forward pass
outputs = model(b_input_ids,
token_type_ids=None,
attention_mask=b_attention_mask,
labels=b_labels)
# loss 추출
loss = outputs.loss
total_loss += loss.item()
# backward pass를 통한 기울기 계산
loss.backward()
# 너무 큰 기울기 값을 방지하기 위한 기울기 클리핑
torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
# 옵티마이저와 스케줄러를 사용한 파라미터 업데이트
optimizer.step()
scheduler.step()
# 로그인 확률과 예측값을 계산
logits = outputs.logits
predictions = torch.argmax(logits, dim=-1)
# 배치별 정확도 계산
accuracy = (predictions == b_labels).float().mean()
total_accuracy += accuracy.item()
# 에포크별 평균 손실 및 정확도
avg_train_loss = total_loss / len(train_loader)
avg_train_accuracy = total_accuracy / len(train_loader)
# 기록
train_losses.append(avg_train_loss)
train_accuracies.append(avg_train_accuracy)
# 평가 모드 설정
model.eval()
total_val_loss = 0
total_val_accuracy = 0
with torch.no_grad():
for batch in val_loader:
# 배치에서 데이터를 GPU로 이동
b_input_ids = batch['input_ids'].to(device)
b_attention_mask = batch['attention_mask'].to(device)
b_labels = batch['labels'].to(device)
# forward pass
outputs = model(b_input_ids,
token_type_ids=None,
attention_mask=b_attention_mask,
labels=b_labels)
# loss 추출
loss = outputs.loss
total_val_loss += loss.item()
# 로그인 확률과 예측값을 계산
logits = outputs.logits
predictions = torch.argmax(logits, dim=-1)
# 배치별 정확도 계산
accuracy = (predictions == b_labels).float().mean()
total_val_accuracy += accuracy.item()
# 에포크별 평균 테스트 손실 및 정확도
avg_val_loss = total_val_loss / len(val_loader)
avg_val_accuracy = total_val_accuracy / len(val_loader)
# 기록
val_losses.append(avg_val_loss)
val_accuracies.append(avg_val_accuracy)
print(f"Train loss: {avg_train_loss}")
print(f"Train accuracy: {avg_train_accuracy}")
print(f"val loss: {avg_val_loss}")
print(f"val accuracy: {avg_val_accuracy}")
# 훈련 끝난 후 시간
end_time = time.time()
# 전체 소요 시간 계산
total_time = end_time - start_time
# 시간을 시, 분, 초로 변환
hours, remainder = divmod(total_time, 3600)
minutes, seconds = divmod(remainder, 60)
# 소요 시간 출력
print("Training completed in {:02}:{:02}:{:05.2f}".format(int(hours), int(minutes), seconds))
import matplotlib.pyplot as plt
# Epochs 범위 설정
epochs_range = range(1, epochs + 1)
# 훈련 손실 및 정확도 그래프
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(epochs_range, train_losses, label='Train Loss')
plt.plot(epochs_range, val_losses, label='val Loss')
plt.title('Training and val Loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
# 테스트 손실 및 정확도 그래프
plt.subplot(1, 2, 2)
plt.plot(epochs_range, train_accuracies, label='Train Accuracy')
plt.plot(epochs_range, val_accuracies, label='Val Accuracy')
plt.title('Training and Val Accuracy')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.legend()
plt.show()
# 테스트 모드 설정
model.eval()
total_test_loss = 0
total_test_accuracy = 0
# 기울기 계산을 비활성화
with torch.no_grad():
for batch in test_loader:
# 배치에서 데이터를 GPU로 이동
b_input_ids = batch['input_ids'].to(device)
b_attention_mask = batch['attention_mask'].to(device)
b_labels = batch['labels'].to(device)
# 모델을 통한 forward pass
outputs = model(b_input_ids,
token_type_ids=None,
attention_mask=b_attention_mask,
labels=b_labels)
# loss 추출
loss = outputs.loss
total_test_loss += loss.item()
# 로그인 확률과 예측값을 계산
logits = outputs.logits
predictions = torch.argmax(logits, dim=-1)
# 배치별 정확도 계산
accuracy = (predictions == b_labels).float().mean()
total_test_accuracy += accuracy.item()
# 평균 테스트 손실 및 정확도 계산
avg_test_loss = total_test_loss / len(test_loader)
avg_test_accuracy = total_test_accuracy / len(test_loader)
print(f"Test loss: {avg_test_loss}")
print(f"Test accuracy: {avg_test_accuracy}")
# 모델 상태 저장
model_path = "/content/model.pth" # 모델을 저장할 경로
torch.save(model.state_dict(), model_path)
저장된 모델에 다음과 같은 json 파일을 통과시켜서 Predicted_CategoryID라는 열을 새로 생성해서 예측한 결과를 저장
import pandas as pd
import torch
from transformers import BertTokenizer, BertForSequenceClassification
from torch.utils.data import DataLoader, TensorDataset
# 모델과 토크나이저 초기화
model_path = "/content/model.pth"
tokenizer = BertTokenizer.from_pretrained('monologg/kobert')
model = BertForSequenceClassification.from_pretrained('monologg/kobert', num_labels=len(set(labels)))
model.load_state_dict(torch.load(model_path))
model.eval() # 평가 모드로 설정
# JSON 데이터 불러오기
file_path = '/content/predicted_news_data.json' # 앞에서 생성한 JSON 파일 경로를 사용합니다.
with open(file_path, 'r', encoding='utf-8') as file:
data = json.load(file)
# 데이터를 DataFrame으로 변환
data_df = pd.DataFrame(data)
# 토큰화 및 텐서 변환
inputs = tokenizer(data_df['Title'].tolist(), return_tensors='pt', padding=True, truncation=True, max_length=512)
input_ids = inputs['input_ids']
attention_mask = inputs['attention_mask']
# DataLoader 생성
dataset = TensorDataset(input_ids, attention_mask)
loader = DataLoader(dataset, batch_size=32)
# 예측 수행
predicted_category_ids = []
with torch.no_grad():
for batch in loader:
b_input_ids, b_attention_mask = batch
outputs = model(b_input_ids, attention_mask=b_attention_mask)
logits = outputs.logits
predictions = torch.argmax(logits, dim=-1)
predicted_category_ids.extend(predictions.cpu().numpy())
# 예측된 카테고리 ID를 DataFrame에 추가
data_df['Predicted_CategoryID'] = predicted_category_ids
# 결과 저장
data_df.to_csv('/content/predicted_data.csv', index=False)
# 결과 확인
print(data_df.head())
새로운 데이터 투입
2020년도부터의 관련 뉴스로 학습했던 터라 그 전의 학습되지 않은 데이터를 사용해야했는데 너무 오래전이라 데이터가 동떨어질것을 감안하여 관련 주제를 주고 gpt가 직접 json 형태로 각 카테고리별 기사를 생성해보라고 했다.
[
{
"Title": "스마트 조선업계, ESG경영으로 지속 가능한 성장 추구",
"CategoryID": 0
},
{
"Title": "디지털 혁신을 통한 조선업의 ESG경영 전략",
"CategoryID": 0
},
{
"Title": "친환경 조선 기술, ESG경영의 새로운 돌파구",
"CategoryID": 0
},
{
"Title": "조선업계, ESG경영으로 글로벌 경쟁력 강화",
"CategoryID": 0
},
{
"Title": "ESG경영을 통한 조선업의 변화와 혁신",
"CategoryID": 0
},
{
"Title": "조선업계의 ESG경영, 지속 가능한 미래를 위한 전략",
"CategoryID": 0
},
{
"Title": "스마트 조선 기술과 ESG경영의 시너지 효과",
"CategoryID": 0
},
{
"Title": "조선업계, ESG경영을 통한 새로운 성장 모델 창출",
"CategoryID": 0
},
{
"Title": "스마트 조선 기술로 선도하는 ESG경영 전략",
"CategoryID": 0
},
{
"Title": "ESG경영이 조선업계에 미치는 긍정적 영향",
"CategoryID": 0
},
{
"Title": "건설업계, ESG경영을 통한 지속가능한 발전 모색",
"CategoryID": 1
},
{
"Title": "스마트 건설 기술과 ESG경영의 만남, 새로운 기회 창출",
"CategoryID": 1
},
{
"Title": "건설업계의 ESG경영 전략 및 도전",
"CategoryID": 1
},
{
"Title": "디지털 혁신과 ESG경영을 통한 건설업의 변화",
"CategoryID": 1
},
{
"Title": "스마트 건설 기술의 발전과 ESG경영의 결합",
"CategoryID": 1
},
{
"Title": "건설업계, ESG경영으로 지속 가능한 성장 추구",
"CategoryID": 1
},
{
"Title": "건설업계의 ESG경영 전략과 지속 가능성",
"CategoryID": 1
},
{
"Title": "스마트 건설 기술이 ESG경영에 미치는 영향",
"CategoryID": 1
},
{
"Title": "건설업계, ESG경영을 통한 새로운 비즈니스 모델 탐색",
"CategoryID": 1
},
{
"Title": "건설업계의 지속가능한 발전을 위한 ESG경영 전략",
"CategoryID": 1
},
{
"Title": "IT 업계의 최신 트렌드와 이슈 분석",
"CategoryID": 2
},
{
"Title": "인공지능의 발전과 IT업계의 윤리적 고민",
"CategoryID": 2
},
{
"Title": "5G 기술의 발전과 IT 업계에 미치는 영향",
"CategoryID": 2
},
{
"Title": "블록체인 기술이 IT 업계에 가져오는 변화",
"CategoryID": 2
},
{
"Title": "클라우드 컴퓨팅의 발전과 IT 업계의 전략",
"CategoryID": 2
},
{
"Title": "사이버 보안, IT 업계의 지속적인 도전과제",
"CategoryID": 2
},
{
"Title": "데이터 사이언스와 IT 업계의 미래",
"CategoryID": 2
},
{
"Title": "IT 업계에서의 인공지능의 응용과 발전",
"CategoryID": 2
},
{
"Title": "IT 업계의 지속 가능한 성장을 위한 전략",
"CategoryID": 2
},
{
"Title": "IT 기술의 혁신과 업계의 대응",
"CategoryID": 2
},
{
"Title": "중대재해처벌법 시행과 산업안전 강화 필요성",
"CategoryID": 3
},
{
"Title": "산업 현장에서의 안전 관리와 중대재해처벌법의 영향",
"CategoryID": 3
},
{
"Title": "중대재해처벌법에 따른 산업안전 규제 강화",
"CategoryID": 3
},
{
"Title": "산업안전을 위한 법적 조치, 중대재해처벌법의 중요성",
"CategoryID": 3
},
{
"Title": "중대재해처벌법 시행 이후 산업 현장의 변화",
"CategoryID": 3
},
{
"Title": "중대재해처벌법과 산업안전의 미래",
"CategoryID": 3
},
{
"Title": "산업 현장의 안전 관리 강화를 위한 법적 조치",
"CategoryID": 3
},
{
"Title": "중대재해처벌법 시행에 따른 기업의 대응 전략",
"CategoryID": 3
},
{
"Title": "중대재해처벌법과 산업안전의 새로운 기준",
"CategoryID": 3
},
{
"Title": "중대재해처벌법, 산업안전을 위한 새로운 장",
"CategoryID": 3
}
]
두근두근 모델 통과
직접 수동으로 크롤링한 무스마데이터 + 손으로 라벨링한 데이터를 취합해서 만든 최종적으로 성과가 나와야하는 데이터를 통과시켰다. 오른쪽에서 볼 수 있듯이 내가 지정한 CategoryID와 학습시킨 모델에서 예측한 Predicted_CategoryID가 일치해야하는데 최상위에 있는 5개의 데이터에 대해서는 일단 일치하는 것을 볼 수 있다.
그럼 전체 데이터에 대한 결과는..?
요약
1. 키워드 기반으로 2020년부터 총 7000개의 데이터에 대해서 학습을 시켰고 모델 자체 acc는 93.67%.
결과적으로는 musma가 이때까지 해온 경향으로 분류를 해야하므로 아래와 같은 과정으로 성능평가를 진행.
2. 성능평가에 사용한 데이터는 'musma뉴스데이터+손으로 직접 라벨링한 데이터'이고 라벨링된 카테고리랑 모델에서 예측한 카테고리의 일치도는 85.02%.
수고했다..

728x90
'Study > 소프트웨어공학&비즈니스애널리틱스 (최성철 교수님) 2023-2' 카테고리의 다른 글
Word_cloud 생성하기 (2) | 2023.12.11 |
---|---|
ANIOP 부산 지산학 네트워킹 데이 발표 (0) | 2023.12.11 |
뉴스 클리핑 코드 구현 (3) | 2023.12.09 |
AWS S3 커스텀 모델에 json 뉴스 결과 저장 성공!! (5) | 2023.11.22 |
드디어 lamda로 구운 나의 커스텀 모델 (3) | 2023.11.21 |