이 글은 파이토치로 배우는 자연어처리(O'REILLY, 한빛미디어)를 공부한 내용을 바탕으로 작성하였습니다.
퍼셉트론 분류기
완전연결(FC1) 계층 하나를 가진 퍼셉트론 분류기
손실 계산에서 BCEWithLogitsLoss()를 사용하도록 sigmoid를 거치지 않은 채 출력
class ReviewClassifier(nn.Module):
""" 간단한 퍼셉트론 기반 분류기 """
def __init__(self, num_features):
"""
매개변수:
num_features (int): 입력 특성 벡터의 크기
"""
super(ReviewClassifier, self).__init__()
self.fc1 = nn.Linear(in_features=num_features,
out_features=1)
def forward(self, x_in, apply_sigmoid=False):
""" 분류기의 정방향 계산
매개변수:
x_in (torch.Tensor): 입력 데이터 텐서
x_in.shape는 (batch, num_features)입니다.
apply_sigmoid (bool): 시그모이드 활성화 함수를 위한 플래그
크로스-엔트로피 손실을 사용하려면 False로 지정합니다
반환값:
결과 텐서. tensor.shape은 (batch,)입니다.
"""
y_out = self.fc1(x_in).squeeze()
if apply_sigmoid:
y_out = torch.sigmoid(y_out)
return y_out
하이퍼파라미터와 프로그램 옵션 변수 설정
argparse의 Namespace를 사용하여 결정 요소를 관리
args = Namespace(
# 날짜와 경로 정보
frequency_cutoff=25,
model_state_file='model.pth',
review_csv='data/reviews_with_splits_lite.csv',
# review_csv='data/yelp/reviews_with_splits_full.csv',
save_dir='model_storage/ch3/yelp/',
vectorizer_file='vectorizer.json',
# 모델 하이퍼파라미터 없음
# 훈련 하이퍼파라미터
batch_size=128,
early_stopping_criteria=5,
learning_rate=0.001,
num_epochs=100,
seed=1337,
# 실행 옵션
catch_keyboard_interrupt=True,
cuda=True,
expand_filepaths_to_save_dir=True,
reload_from_files=False,
)
훈련 준비
훈련하는 동안 생성될 중요 정보(loss, accuracy)를 저장할 딕셔너리를 생성하고,
미리 만들어 둔 클래스를 호출해 dataset, vectorizer, classifier를 준비
nn.BCEWithLogitsLoss() 를 손실함수로 설정
optimizer를 Adam으로 설정
compute_accuracy() : 출력 결과와 정답을 비교해 정확도 체크하는 함수
def make_train_state(args):
return {'stop_early': False,
'early_stopping_step': 0,
'early_stopping_best_val': 1e8,
'learning_rate': args.learning_rate,
'epoch_index': 0,
'train_loss': [],
'train_acc': [],
'val_loss': [],
'val_acc': [],
'test_loss': -1,
'test_acc': -1,
'model_filename': args.model_state_file}
def update_train_state(args, model, train_state):
""" 훈련 상태를 업데이트합니다.
Components:
- 조기 종료: 과대 적합 방지
- 모델 체크포인트: 더 나은 모델을 저장합니다
:param args: 메인 매개변수
:param model: 훈련할 모델
:param train_state: 훈련 상태를 담은 딕셔너리
:returns:
새로운 훈련 상태
"""
# 적어도 한 번 모델을 저장합니다
if train_state['epoch_index'] == 0:
torch.save(model.state_dict(), train_state['model_filename'])
train_state['stop_early'] = False
# 성능이 향상되면 모델을 저장합니다
elif train_state['epoch_index'] >= 1:
loss_tm1, loss_t = train_state['val_loss'][-2:]
# 손실이 나빠지면
if loss_t >= train_state['early_stopping_best_val']:
# 조기 종료 단계 업데이트
train_state['early_stopping_step'] += 1
# 손실이 감소하면
else:
# 최상의 모델 저장
if loss_t < train_state['early_stopping_best_val']:
torch.save(model.state_dict(), train_state['model_filename'])
# 조기 종료 단계 재설정
train_state['early_stopping_step'] = 0
# 조기 종료 여부 확인
train_state['stop_early'] = \
train_state['early_stopping_step'] >= args.early_stopping_criteria
return train_state
def compute_accuracy(y_pred, y_target):
y_target = y_target.cpu()
y_pred_indices = (torch.sigmoid(y_pred)>0.5).cpu().long()#.max(dim=1)[1]
n_correct = torch.eq(y_pred_indices, y_target).sum().item()
return n_correct / len(y_pred_indices) * 100
if args.reload_from_files:
# 체크포인트에서 훈련을 다시 시작
print("데이터셋과 Vectorizer를 로드합니다")
dataset = ReviewDataset.load_dataset_and_load_vectorizer(args.review_csv,
args.vectorizer_file)
else:
print("데이터셋을 로드하고 Vectorizer를 만듭니다")
#데이터셋과 Vectorizer 만들기
dataset = ReviewDataset.load_dataset_and_make_vectorizer(args.review_csv)
dataset.save_vectorizer(args.vectorizer_file)
vectorizer = dataset.get_vectorizer()
classifier = ReviewClassifier(num_features=len(vectorizer.review_vocab))
classifier = classifier.to(args.device)
loss_func = nn.BCEWithLogitsLoss()
optimizer = optim.Adam(classifier.parameters(), lr=args.learning_rate)
scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer=optimizer,
mode='min', factor=0.5,
patience=1)
train_state = make_train_state(args)
'AI 인공지능' 카테고리의 다른 글
기계 학습(머신러닝)을 시작하는 방법: 단계별 가이드 (0) | 2023.03.31 |
---|---|
yelp 리뷰 감성 분석 (3) (0) | 2023.03.30 |
yelp 리뷰 감성 분류 (1) (0) | 2023.03.30 |
파이토치 신경망 구성하기 (0) | 2023.03.24 |
자연어처리(NLP) 기본 용어 정리 (0) | 2023.03.21 |