Quiz-3 (2024.10.08) // 범위: 04wk-1 까지

Author

최규빈

Published

October 8, 2024

항목 허용 여부 비고
강의노트 참고 허용 수업 중 제공된 강의노트나 본인이 정리한 자료를 참고 가능
구글 검색 허용 인터넷을 통한 자료 검색 및 정보 확인 가능
생성 모형 사용 허용 안함 인공지능 기반 도구(GPT 등) 사용 불가
import datasets
import transformers
import evaluate
import numpy as np
import pandas as pd 
import torch 
from sklearn.model_selection import train_test_split
/home/cgb3/anaconda3/envs/hf/lib/python3.12/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
  from .autonotebook import tqdm as notebook_tqdm

1. MBTI 자료분석 – 30점

Kaggle 링크: MBTI 데이터셋

Kaggle에 가입한 후, archive.zip 파일을 다운로드하고 Colab에 업로드한 뒤, 아래 명령어를 실행하여 데이터를 불러오라.

!unzip archive.zip
df = pd.read_csv("mbti_1.csv")
df_train, df_test = train_test_split(df, test_size=0.2, random_state=42)
df_train = df_train.reset_index(drop=True)
df_test = df_test.reset_index(drop=True)

이 데이터는 Myers-Briggs Type Indicator(MBTI) 성격 유형과 관련된 사람들의 텍스트 데이터를 포함하고 있다. 데이터는 사람들이 작성한 게시물의 내용과 MBTI 성격 유형 간의 관계를 분석할 수 있도록 구성되어 있다.

  • 총 데이터 수: 약 8600개의 행
    • 학습용 데이터: 6940개 행
    • 테스트용 데이터: 1735개 행
  • 각 행의 구성:
    • Type (성격 유형): MBTI 성격 유형 (예: INTP, ENFJ 등)
    • Posts (게시물): 해당 사용자가 작성한 게시물

(1) 주어진 MBTI 데이터셋을 활용하여 T(Thinking) 성향의 사람이 작성한 게시물인지 F(Feeling) 성향의 사람이 작성한 게시물인지 구분하는 모델을 학습하라.

주의

  1. df_train을 훈련자료로, df_test를 검증자료로 사용하라.
  2. 검증자료에 대한 정확도가 80%이상일 경우만 정답으로 인정한다.

(풀이)

!unzip archive.zip
df = pd.read_csv("mbti_1.csv")
df_train, df_test = train_test_split(df, test_size=0.2, random_state=42)
df_train = df_train.reset_index(drop=True)
df_test = df_test.reset_index(drop=True)
Archive:  archive.zip
  inflating: mbti_1.csv              
huggingface/tokenizers: The current process just got forked, after parallelism has already been used. Disabling parallelism to avoid deadlocks...
To disable this warning, you can either:
    - Avoid using `tokenizers` before the fork if possible
    - Explicitly set the environment variable TOKENIZERS_PARALLELISM=(true | false)
df_train['label'] = [int("F" in l) for l in df_train.type]
df_test['label'] = df_test.type.map(lambda x: int("F" in x))
train = datasets.Dataset.from_pandas(df_train)
test = datasets.Dataset.from_pandas(df_test)
데이터 = datasets.dataset_dict.DatasetDict({
    'train':train,
    'test':test
})
데이터
DatasetDict({
    train: Dataset({
        features: ['type', 'posts', 'label'],
        num_rows: 6940
    })
    test: Dataset({
        features: ['type', 'posts', 'label'],
        num_rows: 1735
    })
})
## Step1 
데이터전처리하기1 = 토크나이저 = transformers.AutoTokenizer.from_pretrained("distilbert/distilbert-base-uncased") 
def 데이터전처리하기2(examples):
    return 데이터전처리하기1(examples["posts"], truncation=True)
## Step2 
인공지능생성하기 = transformers.AutoModelForSequenceClassification.from_pretrained
## Step3 
데이터콜렉터 = transformers.DataCollatorWithPadding(tokenizer=토크나이저)
def 평가하기(eval_pred):
    predictions, labels = eval_pred
    predictions = np.argmax(predictions, axis=1)
    accuracy = evaluate.load("accuracy")
    return accuracy.compute(predictions=predictions, references=labels)
트레이너세부지침생성기 = transformers.TrainingArguments
트레이너생성기 = transformers.Trainer
## Step4 
강인공지능생성하기 = transformers.pipeline
#---#
## Step1 
#데이터 = 데이터불러오기('imdb')
전처리된데이터 = 데이터.map(데이터전처리하기2,batched=True)
전처리된훈련자료, 전처리된검증자료 = 전처리된데이터['train'], 전처리된데이터['test']
## Step2 
인공지능 = 인공지능생성하기("distilbert/distilbert-base-uncased", num_labels=2)
## Step3 
트레이너세부지침 = 트레이너세부지침생성기(
    output_dir="my_awesome_model",
    learning_rate=2e-5,
    per_device_train_batch_size=16,
    per_device_eval_batch_size=16,
    num_train_epochs=2, # 전체문제세트를 2번 공부하라..
    weight_decay=0.01,
    eval_strategy="epoch",
    save_strategy="epoch",
    load_best_model_at_end=True,
    push_to_hub=False,
)
트레이너 = 트레이너생성기(
    model=인공지능,
    args=트레이너세부지침,
    train_dataset=전처리된훈련자료,
    eval_dataset=전처리된검증자료,
    tokenizer=토크나이저,
    data_collator=데이터콜렉터,
    compute_metrics=평가하기,
)
트레이너.train()
Map: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 6940/6940 [00:02<00:00, 2420.93 examples/s]
Map: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1735/1735 [00:00<00:00, 2450.23 examples/s]
Some weights of DistilBertForSequenceClassification were not initialized from the model checkpoint at distilbert/distilbert-base-uncased and are newly initialized: ['classifier.bias', 'classifier.weight', 'pre_classifier.bias', 'pre_classifier.weight']
You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.
[868/868 02:53, Epoch 2/2]
Epoch Training Loss Validation Loss Accuracy
1 No log 0.516274 0.752738
2 0.526700 0.432911 0.817867

TrainOutput(global_step=868, training_loss=0.4732443638111589, metrics={'train_runtime': 173.4952, 'train_samples_per_second': 80.002, 'train_steps_per_second': 5.003, 'total_flos': 1838647493345280.0, 'train_loss': 0.4732443638111589, 'epoch': 2.0})

(2) 학습된 모형을 사용하여 아래의 Text에 대한 분류를 수행하라.

["I prefer making decisions based on logic and facts. Analyzing situations objectively is more important to me than considering emotions.", 
 "When making decisions, I think it’s most important to consider how others feel. Understanding and empathizing with the situation is always my top priority. I feel happiest when relationships are harmonious, and I try to maintain an emotional balance in everything I do."]
['I prefer making decisions based on logic and facts. Analyzing situations objectively is more important to me than considering emotions.',
 'When making decisions, I think it’s most important to consider how others feel. Understanding and empathizing with the situation is always my top priority. I feel happiest when relationships are harmonious, and I try to maintain an emotional balance in everything I do.']

(풀이)

## Step4 
강인공지능 = 강인공지능생성하기("sentiment-analysis", model="my_awesome_model/checkpoint-868")
texts = ["I prefer making decisions based on logic and facts. Analyzing situations objectively is more important to me than considering emotions.", 
 "When making decisions, I think it’s most important to consider how others feel. Understanding and empathizing with the situation is always my top priority. I feel happiest when relationships are harmonious, and I try to maintain an emotional balance in everything I do."]
강인공지능(texts)
Hardware accelerator e.g. GPU is available in the environment, but no `device` argument is passed to the `Pipeline` object. Model will be on CPU.
[{'label': 'LABEL_0', 'score': 0.7769721150398254},
 {'label': 'LABEL_1', 'score': 0.8229644298553467}]

2. sms_spam 데이터분석 – 70점

아래는 Hugging Face의 sms_spam 데이터셋을 로드하는 코드이다:

sms_spam = datasets.load_dataset('sms_spam')['train'].train_test_split(test_size=0.2, seed=42)
sms_spam
DatasetDict({
    train: Dataset({
        features: ['sms', 'label'],
        num_rows: 4459
    })
    test: Dataset({
        features: ['sms', 'label'],
        num_rows: 1115
    })
})

sms_spam['train'][-3] 는 훈련 데이터의 마지막에서 세번째 항목을 출력한다. 출력된 샘플은 다음과 같다.

sms_spam['train'][-3]
{'sms': 'FREE camera phones with linerental from 4.49/month with 750 cross ntwk mins. 1/2 price txt bundle deals also avble. Call 08001950382 or call2optout/J MF\n',
 'label': 1}

출력된 샘플은 딕셔너리 형식으로, sms 항목에는 “FREE camera phones with linerental from 4.49/month…”와 같은 문장이 담겨 있다. 이 문장은 스팸(Spam) 메시지로 분류되며, label 항목에 1로 저장되어 있다.

label이 나타내는 분류는 다음과 같이 정의된다:

sms_spam['train'].features['label'].names
['ham', 'spam']

분류 레이블은 총 2가지로 나누며, 각각의 레이블은 다음과 같이 정의된다:

{0: 'ham', 1: 'spam'}
{0: 'ham', 1: 'spam'}

따라서, 문장 “FREE camera phones with linerental…”의 분류는 label이 1이므로, 스팸(Spam)에 해당한다.

(1) 아래의 코드를 참고하여 sms_spam 자료를 전처리하라. – 10점

tokenizer = transformers.AutoTokenizer.from_pretrained("distilbert/distilbert-base-uncased") 
def preprocess_function(examples):
    return tokenizer(examples["sms"], truncation=True)

전처리된 결과의 샘플은 아래와 같다.

sms_spam_preprocessed['train'][9]
{'sms': 'Headin towards busetop\n',
 'label': 0,
 'input_ids': [101, 2132, 2378, 2875, 3902, 18903, 2361, 102],
 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1]}

(풀이)

tokenizer = transformers.AutoTokenizer.from_pretrained("distilbert/distilbert-base-uncased") 
def preprocess_function(examples):
    return tokenizer(examples["sms"], truncation=True)
/home/cgb3/anaconda3/envs/hf/lib/python3.12/site-packages/transformers/tokenization_utils_base.py:1617: FutureWarning: `clean_up_tokenization_spaces` was not set. It will be set to `True` by default. This behavior will be deprecated in transformers v4.45, and will be then set to `False` by default. For more details check this issue: https://github.com/huggingface/transformers/issues/31884
  warnings.warn(
sms_spam_preprocessed = sms_spam.map(preprocess_function,batched=True)
Map: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1115/1115 [00:00<00:00, 22849.15 examples/s]
sms_spam_preprocessed['train'][9]
{'sms': 'Headin towards busetop\n',
 'label': 0,
 'input_ids': [101, 2132, 2378, 2875, 3902, 18903, 2361, 102],
 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1]}

(2) 적절한 모델을 설계하고 sms_spam 데이터를 분석하여 스팸 여부를 판별하는 코드를 작성하라. – 30점

  1. sms_spam['train']을 훈련자료로, sms_spam['test']를 검증자료로 사용하라.
  2. 검증자료에 대한 정확도가 90%이상일 경우만 정답으로 인정한다.

(풀이)

## Step1 
#데이터전처리하기1 = 토크나이저 = transformers.AutoTokenizer.from_pretrained("distilbert/distilbert-base-uncased") 
# def 데이터전처리하기2(examples):
#     return 데이터전처리하기1(examples["posts"], truncation=True)
데이터전처리하기2 = preprocess_function
데이터전처리하기1 = 토크나이저 = tokenizer
## Step2 
인공지능생성하기 = transformers.AutoModelForSequenceClassification.from_pretrained
## Step3 
데이터콜렉터 = transformers.DataCollatorWithPadding(tokenizer=토크나이저)
def 평가하기(eval_pred):
    predictions, labels = eval_pred
    predictions = np.argmax(predictions, axis=1)
    accuracy = evaluate.load("accuracy")
    return accuracy.compute(predictions=predictions, references=labels)
트레이너세부지침생성기 = transformers.TrainingArguments
트레이너생성기 = transformers.Trainer
## Step4 
강인공지능생성하기 = transformers.pipeline
#---#
## Step1 
데이터 = sms_spam
#전처리된데이터 = 데이터.map(데이터전처리하기2,batched=True)
전처리된데이터 = sms_spam_preprocessed
전처리된훈련자료, 전처리된검증자료 = 전처리된데이터['train'], 전처리된데이터['test']
## Step2 
인공지능 = 인공지능생성하기("distilbert/distilbert-base-uncased", num_labels=2)
## Step3 
트레이너세부지침 = 트레이너세부지침생성기(
    output_dir="my_awesome_model",
    learning_rate=2e-5,
    per_device_train_batch_size=16,
    per_device_eval_batch_size=16,
    num_train_epochs=2, # 전체문제세트를 2번 공부하라..
    weight_decay=0.01,
    eval_strategy="epoch",
    save_strategy="epoch",
    load_best_model_at_end=True,
    push_to_hub=False,
)
트레이너 = 트레이너생성기(
    model=인공지능,
    args=트레이너세부지침,
    train_dataset=전처리된훈련자료,
    eval_dataset=전처리된검증자료,
    tokenizer=토크나이저,
    data_collator=데이터콜렉터,
    compute_metrics=평가하기,
)
트레이너.train()
Some weights of DistilBertForSequenceClassification were not initialized from the model checkpoint at distilbert/distilbert-base-uncased and are newly initialized: ['classifier.bias', 'classifier.weight', 'pre_classifier.bias', 'pre_classifier.weight']
You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.
[558/558 00:25, Epoch 2/2]
Epoch Training Loss Validation Loss Accuracy
1 No log 0.044485 0.990135
2 0.058800 0.050162 0.989238

TrainOutput(global_step=558, training_loss=0.05353698769991543, metrics={'train_runtime': 25.5284, 'train_samples_per_second': 349.336, 'train_steps_per_second': 21.858, 'total_flos': 149317665725664.0, 'train_loss': 0.05353698769991543, 'epoch': 2.0})
트레이너.predict(전처리된훈련자료)
PredictionOutput(predictions=array([[ 3.2914727, -2.9270098],
       [ 3.0869112, -2.7461848],
       [ 3.2496836, -2.9365034],
       ...,
       [-2.531946 ,  2.7342494],
       [ 2.6004195, -2.2583973],
       [ 3.1634114, -2.79446  ]], dtype=float32), label_ids=array([0, 0, 0, ..., 1, 0, 0]), metrics={'test_loss': 0.019209984689950943, 'test_accuracy': 0.9957389549226284, 'test_runtime': 3.4043, 'test_samples_per_second': 1309.824, 'test_steps_per_second': 81.956})
트레이너.predict(전처리된검증자료)
PredictionOutput(predictions=array([[-2.5481982,  2.7301414],
       [ 3.1953466, -2.7654445],
       [ 3.32271  , -2.99813  ],
       ...,
       [ 3.3674207, -3.0081215],
       [ 3.3854856, -3.0333314],
       [-2.5486443,  2.7200103]], dtype=float32), label_ids=array([1, 0, 0, ..., 0, 0, 1]), metrics={'test_loss': 0.04448513314127922, 'test_accuracy': 0.9901345291479821, 'test_runtime': 2.1984, 'test_samples_per_second': 507.183, 'test_steps_per_second': 31.841})

(3) 아래의 자료에 대한 loss를 계산하라. – 30점

Note1: 2-(2)를 풀지 않은상태에서 계산해도 정답으로 인정

Note2: loss가 출력되는 어떠한 형태의 답안도 정답으로 인정, 예를들면 아래와 같은 결과를 얻을 경우도 정답으로 인정.

PredictionOutput(predictions=array([[-2.3364387,  2.866232 ],
       [ 3.2778776, -2.9869947],
       [ 3.0951078, -2.8594062],
       [ 2.7923744, -2.6019576],
       [ 3.460438 , -3.1327899],
       [ 3.395659 , -3.091136 ],
       [ 3.403764 , -3.1232061],
       [ 3.2923138, -2.9569058],
       [ 3.1802518, -2.8368335],
       [ 3.3302088, -2.9617522],
       [ 3.394341 , -3.0325623],
       [ 3.1710148, -2.9316459]], dtype=float32),
label_ids=array([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
metrics={'test_loss': 0.0024105568882077932, 'test_accuracy': 1.0, 'test_runtime': 1.1632, 'test_samples_per_second': 10.317, 'test_steps_per_second': 0.86})

(풀이)

트레이너.predict(
    datasets.Dataset.from_dict(sms_spam_preprocessed['test'][::100]) # 데이터 
)
PredictionOutput(predictions=array([[-2.5481987,  2.7301414],
       [ 3.1816514, -2.7806158],
       [ 3.0223804, -2.736825 ],
       [ 2.9823759, -2.5838423],
       [ 3.3489246, -3.040609 ],
       [ 3.3441825, -3.0298705],
       [ 3.3488343, -2.9992719],
       [ 3.2199192, -2.915003 ],
       [ 3.0861435, -2.7881389],
       [ 3.23049  , -2.9884613],
       [ 3.2536855, -2.9340549],
       [ 3.1646938, -2.8317883]], dtype=float32), label_ids=array([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), metrics={'test_loss': 0.0026043050456792116, 'test_accuracy': 1.0, 'test_runtime': 1.4644, 'test_samples_per_second': 8.194, 'test_steps_per_second': 0.683})

(어려운풀이1)

dct = sms_spam_preprocessed['test'][::100]
#데이터콜렉터(sms_spam_preprocessed['test'][::100])

인공지능(**데이터콜렉터(
    [
        dict(label=dct['label'][i], input_ids=dct['input_ids'][i],attention_mask=dct['attention_mask'][i]) 
        for i in range(12)
    ]
).to("cuda:0"))
SequenceClassifierOutput(loss=tensor(0.0026, device='cuda:0', grad_fn=<NllLossBackward0>), logits=tensor([[-2.5482,  2.7301],
        [ 3.1817, -2.7806],
        [ 3.0224, -2.7368],
        [ 2.9824, -2.5838],
        [ 3.3489, -3.0406],
        [ 3.3442, -3.0299],
        [ 3.3488, -2.9993],
        [ 3.2199, -2.9150],
        [ 3.0861, -2.7881],
        [ 3.2305, -2.9885],
        [ 3.2537, -2.9341],
        [ 3.1647, -2.8318]], device='cuda:0', grad_fn=<AddmmBackward0>), hidden_states=None, attentions=None)

(어려운풀이2)

dct2 = dct.copy()
del dct2['sms']
인공지능(**데이터콜렉터(dct2).to("cuda:0"))
SequenceClassifierOutput(loss=tensor(0.0026, device='cuda:0', grad_fn=<NllLossBackward0>), logits=tensor([[-2.5482,  2.7301],
        [ 3.1817, -2.7806],
        [ 3.0224, -2.7368],
        [ 2.9824, -2.5838],
        [ 3.3489, -3.0406],
        [ 3.3442, -3.0299],
        [ 3.3488, -2.9993],
        [ 3.2199, -2.9150],
        [ 3.0861, -2.7881],
        [ 3.2305, -2.9885],
        [ 3.2537, -2.9341],
        [ 3.1647, -2.8318]], device='cuda:0', grad_fn=<AddmmBackward0>), hidden_states=None, attentions=None)

(어려운풀이3)

입력정보들_원시텍스트 = 데이터['test'][::100]['sms']
정리된숫자들_토큰화된자료 = 토크나이저(입력정보들_원시텍스트,truncation=True,return_tensors='pt',padding=True)
정리된숫자들_토큰화된자료['labels'] = torch.tensor(데이터['test'][::100]['label']) # 이거 해설할때는 이 line 깜빡하고 빼먹었습니다...
인공지능(**정리된숫자들_토큰화된자료.to("cuda:0"))
SequenceClassifierOutput(loss=tensor(0.0026, device='cuda:0', grad_fn=<NllLossBackward0>), logits=tensor([[-2.5482,  2.7301],
        [ 3.1817, -2.7806],
        [ 3.0224, -2.7368],
        [ 2.9824, -2.5838],
        [ 3.3489, -3.0406],
        [ 3.3442, -3.0299],
        [ 3.3488, -2.9993],
        [ 3.2199, -2.9150],
        [ 3.0861, -2.7881],
        [ 3.2305, -2.9885],
        [ 3.2537, -2.9341],
        [ 3.1647, -2.8318]], device='cuda:0', grad_fn=<AddmmBackward0>), hidden_states=None, attentions=None)