008. 딥러닝 공부 4일차 [추가]
바로 전글에서 잘못 짠 부분들에 대해 수정했고, 그 결과에 대한 짧은 정리글
1. label tensor는 그대로 one-hot encode상태로 냅뒀음
:: [hate1~3, bias1~3]이 같이 있는 형태
이 상태에서 손실함수나 정확도 등 모든 부분을 앞3개 뒤3개로 나눠서 계산했음
2. 손실함수 ( CrossEntropyLoss ) 도 weights의 앞3개 뒤3개로 분리해서 만듬
crit_hate = nn.CrossEntropyLoss(weight=weights[:3])
crit_bias = nn.CrossEntropyLoss(weight=weights[3:])
3. inputs를 처리할 때 CrossEntropyLoss의 weights 크기와 안맞는다고 오류가 났었음
outputs.logits의 shape가 [8, 8, 6]이었음. 근데 이게 잘못된건줄 모르고있었다.
근데 ChatGPT로 계속 피드백하면서 하고 있으니까 사용중인 모델 GPT2ForSequenceClassification은 output.logits의 shape가 [batch_size] * [num_labels] 여야했고, 그러면 [8,8,6]이 아니라 [8,6]이여야 했음.
그래서 이 문제가 왜 발생했는지 디버그를 계속 돌려봄.
=> 디버그를 돌려본 결과 inputs 내부의 값들에 unsqueeze가 되어있었음.
그래서 input_ids나 attention_mask가 [batch_size] * [max_seq_len] 이였어야 했는데,
[batch_size] * 1 * [max_seq_len] 상태로 들어가 있었고, 이 1 때문에 가운데에 8 크기의 층이 하나 더 생긴거였음.
inputs['input_ids'] = inputs['input_ids'].squeeze(1).cuda()
inputs['attention_mask'] = inputs['attention_mask'].squeeze(1).cuda()
데이터 처리 전에 squeeze로 없애서 해결
4. 그러면 이제 CrossEntropyLoss 손실함수에 Weight를 넣어도 잘 작동하겠지?
이전에 크기 문제가 안맞는 문제 때문에 사용이 안됬었는데 ( logits는 8(위의 이유로), weight는 6이라 불일치 )
이제 해결이 됬을 거라고 생각하고 crit(outputs.logits[:, :3], labels) 와 같은 형식으로 입력
이래저래 문제가 있었고 이를 해결한 방법은
outputs.logits[:, :3]에 softmax를 돌려서 확률로 만들어주고,
labels는 0,1로 이뤄진 정수 텐서였는데 손실함수가 float 텐서만 받아서 float 텐서로 변환해줌
optimizer.zero_grad()
prob_hate = F.softmax(outputs.logits[:, :3], dim=0)
prob_bias = F.softmax(outputs.logits[:, 3:], dim=0)
loss_hate = crit_hate(prob_hate, labels[:, :3].float())
loss_bias = crit_bias(prob_bias, labels[:, 3:].float())
loss = loss_hate + loss_bias
loss.backward()
※ 이번엔 순서도 지킴... zero_grad...
5. Loss값은 누적합이 아니라 누적평균으로 진행되야함.
epoch_loss_sum += loss
count_p += 1
epoch_loss = epoch_loss_sum / count_p
pbar.set_description(f"Epochs {epoch+1}/{num_epochs} loss : {epoch_loss}")
손실값이 진행될수록 계속 쌓여서 막 2천 이렇게 쌓이니까 마음이 아팠는데
이제 1.7쯤에서 시작해서 10 Epoch 돌리면 1.5까지 떨어진다. 기분 굿
6. Eval 과정에서도 앞3개 뒤3개 나눈 변경 과정을 따라가야함 (주석 참조)
# 위에서 한 것 처럼 가운데 차원을 없애기 위해 Squeeze
inputs['input_ids'] = inputs['input_ids'].squeeze(1).cuda()
inputs['attention_mask'] = inputs['attention_mask'].squeeze(1).cuda()
labels = labels.cuda()
outputs = model(input_ids=inputs['input_ids'], attention_mask=inputs['attention_mask'])
# logits를 앞이랑 뒤를 나눠서 최적을 선택
hate_logits = outputs.logits[:, :3]
bias_logits = outputs.logits[:, 3:]
_, hate_indices_logits = torch.max(hate_logits, dim=-1)
_, bias_indices_logits = torch.max(bias_logits, dim=-1)
# Labels에서도 앞이랑 뒤를 나눠서 최적을 선택
hate_labels = labels[:, :3]
bias_labels = labels[:, 3:]
_, hate_indices_labels = torch.max(hate_labels, dim=-1)
_, bias_indices_labels = torch.max(bias_labels, dim=-1)
# Logits에서의 결과와 Labels의 결과가 같은 개수를 통해 평균 측정
correct[0] += torch.sum(torch.eq(hate_indices_labels, hate_indices_logits) & torch.eq(bias_indices_labels, bias_indices_logits))
correct[1] += (hate_indices_logits == hate_indices_labels).sum().item()
correct[2] += (bias_indices_logits == bias_indices_labels).sum().item()
total += outputs.logits.shape[0]
이렇게 10 Epoch를 한 결과
기존에는 Loss는 변화가 거의 없었고, 정확도는 오르락내리락 했던 반면 (40% -> 80% -> 50% -> ... 난리도 아니였.. )
현재는 Loss는 평균 1.73 -> 1.57로 감소,
정확도도 Global(둘 다 맞은 경우)는 선형적 상승,
한 쪽만 맞은 경우도 편차가 있지만 상승하는 방향이었음 (잘 기억이 안남..)
7. 아무튼 Fine-Tunning이 끝났으니 테스트를 해보자.
model.eval()
corpus = Korpora.load("korean_hate_speech")
hate_label = ['none', 'hate', 'offensive']
bias_label = ['none', 'others', 'gender']
for idx in range (0, 100):
test = corpus.test[idx]
text = test.text
title = test.pair
inputs = tokenizer(text, title, padding="max_length", truncation=True, max_length=128, return_tensors='pt')
outputs = model(input_ids=inputs['input_ids'], attention_mask=inputs['attention_mask'])
hate_logits = outputs.logits[:, :3]
bias_logits = outputs.logits[:, 3:]
_, hate_indices_logits = torch.max(hate_logits, dim=-1)
_, bias_indices_logits = torch.max(bias_logits, dim=-1)
hate = hate_label[hate_indices_logits.item()]
bias = bias_label[bias_indices_logits.item()]
print(f"\n[INPUT {idx+1}]\n 제목 : {title}")
print(f"내용 : {text}\n=> 이 문장의 혐오 분류는 {hate}, 차별 분류는 {bias}입니다.")
위의 Eval()과정에서 했던 것과 비슷한 느낌으로 이렇게 진행.
Korpora의 혐오 문장 test 셋의 앞의 100개에 대해 검증을 실시함.
(근데 여기에는 Label이 안달려 있던데 직접 눈으로 정답인지 확인해야 하는 게 맞는건가...)
Input 95. 갑질논란에 대해 Bias는 Other, Hate는 none이 나왔는데,
내 주관으로는 Bias는 None, Hate는 Offensive가 낫지 않을까 싶기도 하고...
Input 96은 Hate, Others가 둘다 들어맞는 듯한 느낌이다.
Input 87. 그냥 칭찬하는 댓글같은데 Offensive는 틀린 대답같음.
Input 88. Hate는 맞는데 년놈때문에 Gender가 들어간걸까? Hate만 맞고 Bias는 틀린 것 같다.
Input 89. 딱히 둘다 아닌 거 같으니 None, None이 맞는 것으로 보인다.
Input 71. '남자'배우와 '차별' 같은 워딩 때문에 Gender가 잡힌 것 같다.
대충 기억상 둘다 맞는 경우가 55%정도에 하나만 맞을 경우가 60~70%대 였던거같은데
None, None으로 들어맞는 경우가 너무 많아서 그런가 생각보다 많이 틀리는 느낌이다...
어쨋든 5시간전에 결과는 뭔가 하긴했는데 한게 맞나? 싶었는데
이번엔 제대로 성공했다는 느낌이 드니 마음이 조금 편해진다... 4일동안 헛고생 한건 아니었나 싶어서 다행쓰