본문 바로가기
Study/소프트웨어공학&비즈니스애널리틱스 (최성철 교수님) 2023-2

클리핑된 뉴스 모델에 통과시켜서 성능확인하기

by JejuSudal 2023. 12. 10.

모델 학습 코드

!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
    }
]

 

 

두근두근 모델 통과

df.head()

직접 수동으로 크롤링한 무스마데이터 + 손으로 라벨링한 데이터를 취합해서 만든 최종적으로 성과가 나와야하는 데이터를 통과시켰다. 오른쪽에서 볼 수 있듯이 내가 지정한 CategoryID와 학습시킨 모델에서 예측한 Predicted_CategoryID가 일치해야하는데 최상위에 있는 5개의 데이터에 대해서는 일단 일치하는 것을 볼 수 있다.

그럼 전체 데이터에 대한 결과는..?

보이는가 85%
잉 내 소즁한 모델이 열일을 해주고 있군요..

 

요약

1. 키워드 기반으로 2020년부터 총 7000개의 데이터에 대해서 학습을 시켰고 모델 자체 acc는 93.67%.
결과적으로는 musma가 이때까지 해온 경향으로 분류를 해야하므로 아래와 같은 과정으로 성능평가를 진행.
2. 성능평가에 사용한 데이터는 'musma뉴스데이터+손으로 직접 라벨링한 데이터'이고 라벨링된 카테고리랑 모델에서 예측한 카테고리의 일치도는 85.02%.

 

수고했다..

728x90