카테고리 없음

[ML] LSTM 모델에서 오버피팅 잡기

0hyeon의 2024. 12. 23. 17:23
반응형

1.기존 LSTM 모델

일단은 라벨링 작업 모델이다. 실시간을 데이터가 들어와서 라벨링하는 모델을 만들기전, 

 

정답지인 데이터, 그중 90%를 train.csv  나머지 10%를 test.py 로사용한다. 

 

 

 

주된 코드는 다음과같다 

import os
import json

import numpy as np

import tensorflow as tf
from keras.preprocessing.text import Tokenizer, tokenizer_from_json
from keras.preprocessing.sequence import pad_sequences
from tensorflow.python.keras.layers import Embedding, Dense, LSTM
from tensorflow.python.keras.models import Sequential
from tensorflow.python.keras.models import load_model

import pandas as pd
from konlpy.tag import Okt


def get_tokenizer(filename, tokens):
    """토큰화 도구(Tokenization)를 초기화하거나 파일에서 불러옵니다.
    토큰화란 텍스트 데이터를 숫자 시퀀스로 변환하는 작업입니다.
    tokenizer.json 파일에 저장된 기존 토크나이저를 재사용하거나, 새로 학습하여 저장합니다."""
    if os.path.exists(filename):
        with open(filename, "r", encoding="utf-8") as f:
            data = json.load(f)
            tokenizer = tokenizer_from_json(data)
    else:
        tokenizer = Tokenizer()
        tokenizer.fit_on_texts(tokens)
        tokenizer_json = tokenizer.to_json()
        with open("tokenizer.json", "w", encoding="utf-8") as f:
            f.write(json.dumps(tokenizer_json, ensure_ascii=False))

    return tokenizer


def preprocess_1(data):
    """형태소 분석:
    Okt 라이브러리를 사용하여 한국어 텍스트를 형태소 단위로 분리.
    전처리:
    특수문자 제거.
    텍스트를 tokenized라는 새로운 열에 저장."""
    okt = Okt()
    data["product"] = data["product"].str.replace("[^A-Za-z0-9가-힣]", " ", regex=True)
    data["tokenized"] = data["product"].apply(okt.morphs)

    return data


def preprocess_2(data, tokenizer, max_len):
    """숫자 시퀀스로 변환
    토크나이저를 사용해 형태소 데이터를 숫자 시퀀스로 변환.
    길이가 부족한 시퀀스는 max_len 길이로 패딩.
    """
    x = tokenizer.texts_to_sequences(data["tokenized"])
    x = pad_sequences(x, maxlen=max_len, padding="post")
    y = data["label"].tolist()

    return x, y


def get_model(vocab_size, num_label):
    """LSTM model
    Embedding Layer:
    텍스트 데이터를 고차원 벡터로 매핑.
    LSTM Layer:
    시퀀스 데이터의 문맥 정보를 학습.
    Dense Layer:
    소프트맥스 활성화 함수를 사용해 각 클래스에 대한 확률 분포를 출력."""
    model = Sequential()
    model.add(Embedding(vocab_size, 64))
    model.add(LSTM(256))
    model.add(Dense(num_label, activation="softmax"))

    print(model.summary())

    return model


def main():
    """학습 및 저장"""
    max_len = 20
    num_label = 15

    train_data = pd.read_csv("train.csv", header=0)
    test_data = pd.read_csv("test.csv", header=0)

    train_data = preprocess_1(train_data)
    test_data = preprocess_1(test_data)

    tokenizer = get_tokenizer("tokenizer.json", train_data["tokenized"])

    vocab_size = len(tokenizer.word_index) + 1

    train_x, train_y = preprocess_2(train_data, tokenizer, max_len=max_len)
    text_x, test_y = preprocess_2(test_data, tokenizer, max_len=max_len)

    model = get_model(vocab_size, num_label)
    model.compile(
        optimizer="rmsprop", loss="sparse_categorical_crossentropy", metrics=["acc"]
    )
    history = model.fit(np.array(train_x), np.array(train_y), epochs=5, batch_size=64)

    tf.saved_model.save(model, "model-store/model")


main()

 

해당 train.py 실행해보면.

 

 


 

2.과적합(오버피팅) 발생

 

오버피팅 이 발생한다. 

 

 

 

과적합(overfitting)

모델이 학습 데이터셋 안에서는 일정 수준 이상의 예측 정확도를 보이지만, 새로운 데이터에 적용하면 잘 맞지 않는 것을 의미

 

입력층, 은닉층, 출력층의 노드들이 많아지게 되면 학습 데이터의 패턴뿐만 아니라 노이즈까지 학습하게 된다고 한다.

이를 "학습 편향" 이라고함. (책에서 본 bias 인듯 => 1 or 0 출력 결정짓게하는 저항)

 

모델이 복잡할수록 학습 데이터에 완벽히 맞추는 것은 쉬워지지만, 새로운 데이터에서는 일반화되지 못할 가능성이 높아짐. (못된건만배우는 나와같다)

 

 

학습? 뭔데 삐뚤어질테다

 

 

 test.csv가 990개, train.csv가 8,898개라면 데이터 양이 적다고 할 수는 없다 (1차적으로 샘플사이즈를 확보했는지 확인)

적지않다면, 모델링을 손보면된다.  어떻게? 

 

 


 

3.해결방안

크게 몇가지가 있다고 하는데, 우리는 이중 3가지를 적용해볼 예정이다. 

이유는 지극히 개인적이다. 모델 단순화 는 간지가 안나서, 교차 검증.. 최후의 보루로 하고싶어서 뺀다.

 

그리하여, 

 

  • 오버피팅 감소:
    • Dropout, L2 정규화, Early Stopping, 계층적 데이터 분리로 과적합 방지.
  • 일반화 성능 향상:
    • 더 적은 가중치와 적절한 뉴런 수로 모델이 새로운 데이터에 더 잘 대응.
  • 학습 안정성 증가:
    • Learning Rate Scheduler와 Early Stopping으로 과도한 학습 방지 및 안정적 학습.
  • 신뢰성 향상:
    • 계층적 데이터 분리로 학습과 검증 데이터 간 일관성을 유지.

 

 

진행한다.

 

 


4.적용

 

Dropout 사용 (얌생이 학습법 차단)

model.add(LSTM(128, dropout=0.3, recurrent_dropout=0.3))
  • dropout=0.3: 뉴런의 30%를 학습 중 랜덤하게 비활성화하여 모델이 특정 뉴런에 과도하게 의존하지 않도록 만듦.
  • recurrent_dropout=0.3: LSTM의 순환 연결에서도 일부 뉴런을 비활성화.
  • 과적합(오버피팅) 방지: 특정 뉴런에 의존하는 과적합 위험을 줄임.
  • 일반화 성능 향상: 다양한 데이터 패턴에 대응할 수 있도록 학습.

 

 

L2 정규화 추가 (과한학습시 취침등소등시키기 )

model.add(Dense(num_label, activation="softmax", kernel_regularizer=l2(0.01)))
  • l2(0.01): 가중치 크기에 패널티를 부여하여 가중치가 과도하게 커지지 않도록 제한.
  • 모델 복잡도를 줄임: 큰 가중치를 제한함으로써 모델의 복잡성을 낮추고, 과적합 가능성을 줄임.
  • 더 나은 일반화: 검증 데이터와 새로운 데이터에서도 성능이 향상.

 

Early Stopping (참교육. 모난싹자르기)

early_stopping = EarlyStopping(monitor="val_loss", patience=3, restore_best_weights=True)

 

  • monitor="val_loss": 검증 손실을 모니터링.
  • patience=3: 3 에포크 동안 검증 손실이 개선되지 않으면 학습 중단.
  • 과적합 방지: 검증 손실이 증가하기 시작하면 학습을 조기에 중단하여 학습 데이터에 과도하게 적합되지 않도록 함.
  • 학습 시간 절약: 불필요한 에포크를 방지하여 자원을 절약.

 

Learning Rate Scheduler ( 기억망각곡선에 의해 잊어버린것 나머지학습, 기억력개선안되면 학습률 다운시키기)

lr_scheduler = ReduceLROnPlateau(monitor="val_loss", factor=0.5, patience=2, min_lr=0.0001)
  • monitor="val_loss": 검증 손실을 기준으로 학습률 조정.
  • factor=0.5: 학습률을 절반으로 감소.
  • patience=2: 2 에포크 동안 검증 손실이 개선되지 않으면 학습률 감소.
  • 학습 안정화: 초기 학습률이 너무 커서 최적값을 지나치는 문제를 방지.
  • 최적화 성능 개선: 학습 후반부에 작은 학습률로 세부적인 조정을 가능하게 함.

 

모델 구조 간소화(공부방 청소시키기)

model.add(LSTM(128))
  • 이전 코드의 LSTM(256) → LSTM(128)로 변경.
  • 모델 복잡도 감소: 더 적은 뉴런을 사용하여 계산량 감소 및 과적합 가능성 감소.
  • 데이터 크기에 적합한 모델: 데이터 크기(8,898개)에 비해 과도하게 복잡했던 모델을 간소화.

 

계층적 데이터 분리 (학습과 문제풀이의 밸런스를 맞추어 진행)

train_x, val_x, train_y, val_y = train_test_split(
    train_x, train_y, test_size=0.2, stratify=train_y, random_state=42
)

 

  • stratify=train_y: 학습 데이터와 검증 데이터의 클래스 비율을 동일하게 유지.
  • 클래스 불균형 방지: 검증 데이터에서도 학습 데이터와 동일한 레이블 분포를 가지게 하여 평가의 신뢰성 향상.
  • 안정적인 검증: 레이블 분포가 동일하므로 검증 데이터의 성능이 과소평가되지 않음.

 

Epochs와 학습률의 조화 (물리적인 공부시간 끌어올려)

  • Epochs: 5 → 20으로 증가.
  • Learning Rate Scheduler를 추가하여 더 많은 에포크 동안 안정적으로 학습.
  • 충분한 학습: 더 많은 에포크로 학습이 충분히 진행됨.
  • 학습률 조정: 학습 후반부에 더 세밀한 최적화가 가능.

 

 

5.결과

정말이지 위 방법대로 공부(학습)하면 점수가(신뢰도) 떨어질수가 없다

기계 

 

val_acc 75.73%

 

1. 학습 성능

  • 학습 손실 (loss):
    • 에포크 1: 2.5471 → 에포크 20: 0.4096으로 꾸준히 감소.
  • 학습 정확도 (acc):
    • 에포크 1: 19.52% → 에포크 20: 90.81%로 큰 폭으로 개선.

2. 검증 성능

  • 검증 손실 (val_loss):
    • 에포크 1: 2.3813 → 에포크 20: 0.9603으로 지속적으로 감소.
  • 검증 정확도 (val_acc):
    • 에포크 1: 20.56% → 에포크 20: 75.73%로 꾸준히 증가.

3. Learning Rate Scheduler 동작

  • 학습률 (lr):
    • 학습률이 에포크 8부터 0.001 → 0.0005로 줄어듦.
    • 이는 검증 손실의 개선 속도가 느려졌을 때 학습률을 줄이는 효과를 보여줌.

4. 오버피팅 여부

  • 학습 정확도 (acc)와 검증 정확도 (val_acc)의 차이:
    • 에포크 20 기준: 학습 정확도 90.81%, 검증 정확도 75.73%.
    • 약 15% 차이가 있지만, 이는 과도한 오버피팅이 아니라 데이터의 복잡성과 모델의 적합성 때문일 가능성이 큼.
  • 검증 손실 감소:
    • 검증 손실이 마지막 에포크까지 감소하고 있어, 아직 오버피팅 징후가 나타나지 않음.
반응형