--- library_name: transformers language: - ko metrics: - f1 base_model: - monologg/kobert pipeline_tag: text-classification --- ## 한국어 텍스트 감정분류 모델(KoBERT) ### MD(Model Description) - 위 모델은 [한국어 감정 정보가 포함된 단발성 대화 데이터셋](https://aihub.or.kr/aihubdata/data/view.do?currMenu=115&topMenu=100&dataSetSn=270)과 네이버 [블로그](https://m.blog.naver.com/PostView.naver?blogId=seolin43&logNo=223102584267&proxyReferer=https:%2F%2Fvelog.io%2F&trackingCode=external) 를 통해 7가지 감정 데이터셋을 분류하는 모델입니다. - 7가지 감정범주: ```python label2id = { "공포": 0, "놀람": 1, "분노": 2, "슬픔": 3, "중립": 4, "행복": 5, "혐오": 6, } ``` ### 메인 모델 [monologg/kobert](https://huggingface.co/monologg/kobert)모델의 사전 학습된 KoBERT 모델을 미세조정하였습니다(fine tunning) ### 구성 ```python # Tokenizer !git clone https://github.com/monologg/KoBERT-Transformers.git from kobert_transformers.tokenization_kobert import KoBertTokenizer #Default Settings tokenizer = KoBertTokenizer.from_pretrained("monologg/kobert") model = BertForSequenceClassification.from_pretrained("monologg/kobert") #Datasets Preprocessing import json with open('/content/train_datasets.json', 'r', encoding='utf-8') as files: train_dataset = json.load(files) with open('/content/test_datasets.json', 'r', encoding='utf-8') as files_t: val_dataset = json.load(files_t) from datasets import Dataset # 리스트 데이터를 Hugging Face Dataset으로 변환 train_dataset = Dataset.from_list(train_dataset) val_dataset = Dataset.from_list(val_dataset) # 토큰화 함수 정의 def tokenize_function(examples): return tokenizer( examples['Sentence'], # 텍스트 필드 이름 padding='max_length', truncation=True, max_length=128 ) train_dataset = train_dataset.map(tokenize_function, batched=True) val_dataset = val_dataset.map(tokenize_function, batched=True) # 불필요한 컬럼 제거 train_dataset = train_dataset.remove_columns(['Sentence']) val_dataset = val_dataset.remove_columns(['Sentence']) # 데이터셋 포맷 지정 (PyTorch tensors) train_dataset.set_format('torch') val_dataset.set_format('torch') ``` ### Model Config Settings ```python model.config ``` ```bash BertConfig { "_attn_implementation_autoset": true, "_name_or_path": "monologg/kobert", "architectures": [ "BertModel" ], "attention_probs_dropout_prob": 0.1, "classifier_dropout": null, "hidden_act": "gelu", "hidden_dropout_prob": 0.1, "hidden_size": 768, "id2label": { "0": "LABEL_0", "1": "LABEL_1", "2": "LABEL_2", "3": "LABEL_3", "4": "LABEL_4" }, "initializer_range": 0.02, "intermediate_size": 3072, "label2id": { "LABEL_0": 0, "LABEL_1": 1, "LABEL_2": 2, "LABEL_3": 3, "LABEL_4": 4 }, "layer_norm_eps": 1e-12, "max_position_embeddings": 512, "model_type": "bert", "num_attention_heads": 12, "num_hidden_layers": 12, "pad_token_id": 1, "position_embedding_type": "absolute", "transformers_version": "4.47.0", "type_vocab_size": 2, "use_cache": true, "vocab_size": 8002 } ``` - 라벨이 5개 밖에 없는 것을 확인할 수 있습니다. ### Configuration Settings ```python from transformers import BertConfig, BertForSequenceClassification, AutoTokenizer import torch # 감정별 라벨 매핑 label2id = { "공포": 0, "놀람": 1, "분노": 2, "슬픔": 3, "중립": 4, "행복": 5, "혐오": 6, } id2label = {v: k for k, v in label2id.items()} # BertConfig에 라벨 매핑 추가 config = BertConfig.from_pretrained( "monologg/kobert", num_labels=7, id2label=id2label, label2id=label2id ) model = BertForSequenceClassification.from_pretrained("monologg/kobert", config=config) tokenizer = KoBertTokenizer.from_pretrained("monologg/kobert") ``` - 사실은 `num_labels=7`을 할 필요가 없습니다. - 세팅 이후에 `model.config`로 세팅값을 확인해보면 7가지 레이블 값이 입력된 것을 확인 할 수 있습니다. ### Detail Configuration | 설정 항목 | 값 | |------------------|-------------------| | 학습률 | 2e-5 | | 학습 배치사이즈 | 64 | | 평가 배치사이즈 | 1 | | 평가 지표 | F1 Score (Macro) | | FP16 사용 여부 | True | ### 평가지표 | Epoch | Training Loss | Validation Loss | F1 Macro | Precision Macro | Recall Macro | |-------|---------------|-----------------|----------|-----------------|--------------| | 1 | No log | 1.273796 | 0.496522 | 0.509767 | 0.506992 | | 2 | 1.478500 | 1.216168 | 0.532564 | 0.534552 | 0.537554 | | 3 | 1.155800 | 1.216068 | 0.536442 | 0.537659 | 0.539811 | | 4 | 0.996200 | 1.235898 | 0.536210 | 0.537586 | 0.541298 | | 5 | 0.901000 | 1.248866 | 0.540000 | 0.539427 | 0.543429 | - `f1-score`기반으로 평가, → 0.54 성능이 그렇게 좋진 못합니다 - `batch_size`, `Learning_Rate`등의 하이퍼 파라미터를 조정할 필요가 보여집니다.(최소 10에포크 이상) - `T4`기준 배치사이즈는 `64`는 `VRAM 4G` 정도 차지하므로 더욱 늘려도 상관 없을듯 합니다. ### 최종 훈련코드 ```python training_args = TrainingArguments( output_dir='./results', learning_rate=2e-5, per_device_train_batch_size=16, per_device_eval_batch_size=16, num_train_epochs=10, eval_strategy="epoch", save_strategy="epoch", metric_for_best_model="f1_macro", load_best_model_at_end=True ) ``` ### Inference ```python model = BertForSequenceClassification.from_pretrained('./result/pp') tokenizer = KoBertTokenizer.from_pretrained('./result/pp') # GPU 사용 여부 확인 및 디바이스 설정 device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu") model.to(device) # 모델을 디바이스로 이동 model.eval() # 2. 입력 데이터 준비 texts = [ "오늘 정말 기분이 좋다 ㅎㅎ!", "왜 이렇게 힘든 일이 많지? ㅠㅠ", "정말 화가 난다!", "평범한 하루였어.", "너무 재밌다 ㅋㅋㅋ!", ] # 3. 토큰화 및 입력 형식 변환 def preprocess_and_tokenize(texts, tokenizer, max_length=128): inputs = tokenizer( texts, padding=True, # 배치 크기에 맞게 패딩 truncation=True, # 최대 길이 초과 시 자름 max_length=max_length, return_tensors="pt", # PyTorch 텐서 반환 ) return inputs inputs = preprocess_and_tokenize(texts, tokenizer) # 입력 데이터를 GPU로 이동 inputs = {key: val.to(device) for key, val in inputs.items()} # 4. 인퍼런스 수행 with torch.no_grad(): outputs = model(**inputs) logits = outputs.logits predictions = torch.argmax(logits, dim=-1).cpu().numpy() # 예측 결과 매핑 predicted_labels = [id2label[pred] for pred in predictions] # 6. 결과 출력 for text, label in zip(texts, predicted_labels): print(f"Input: {text}") print(f"Predicted Label: {label}\n") ```