AI/정책 댓글 반응 NLP

[4] 포털 댓글 감성 분석_2. BERT_2. 모델링 및 예측

eraser 2020. 4. 21. 13:31
반응형

 

 앞 단에 이어 모델링을 진행한다. 사전학습된 BERT 모델에 transformers 라이브러리의 분류 클래스 층을 더했다. 20200420 현재, 가장 좋은 정확도를 보인 것은 15회 학습시켰을 때 0.8678 정도였다.

 


# GPU 설정

 Keras에서와 달리, PyTorch에서 GPU를 사용할 수 있는지 알아보는 과정이 필요하다. GPU를 사용할 수 있다면, GPU를 사용하도록 장치를 설정한다. (Colab Pro 기준 : Tesla P100-PCIE-16GB)

 

device_name = tf.test.gpu_device_name()

# GPU 디바이스 이름 검사
if device_name == '/device:GPU:0':
    print('Found GPU at: {}'.format(device_name))
else:
    raise SystemError('GPU가 없습니다.')

# GPU 디바이스 설정
if torch.cuda.is_available():    
    device = torch.device("cuda")
    print(f'{torch.cuda.device_count()}개의 GPU를 사용할 수 있습니다.')
    print(f'{torch.cuda.get_device_name(0)} GPU를 사용합니다.')
else:
    device = torch.device("cpu")
    print('GPU를 사용할 수 없습니다. CPU를 사용합니다.')

 


 

# 모델 설정

 

1) 모델 생성

 transformers 라이브러리에서 BertForSequenceClassification 모델을 사용한다. 토크나이저를 설정할 때와 마찬가지로 사전훈련된 모델의 이름이 필요하고, 라벨의 개수와 함께 인자로 넘기면 된다.

 

# BERT 분류 모델 생성
model = BertForSequenceClassification.from_pretrained('bert-base-multilingual-cased', num_labels=3)

# 모델 확인
model.cuda()

 

더보기

모델을 확인하면 다음과 같다.

 

BertForSequenceClassification(
  (bert): BertModel(
    (embeddings): BertEmbeddings(
      (word_embeddings): Embedding(119547, 768, padding_idx=0)
      (position_embeddings): Embedding(512, 768)
      (token_type_embeddings): Embedding(2, 768)
      (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
      (dropout): Dropout(p=0.1, inplace=False)
    )
    (encoder): BertEncoder(
      (layer): ModuleList(
        (0): BertLayer(
          (attention): BertAttention(
            (self): BertSelfAttention(
              (query): Linear(in_features=768, out_features=768, bias=True)
              (key): Linear(in_features=768, out_features=768, bias=True)
              (value): Linear(in_features=768, out_features=768, bias=True)
              (dropout): Dropout(p=0.1, inplace=False)
            )
            (output): BertSelfOutput(
              (dense): Linear(in_features=768, out_features=768, bias=True)
              (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
              (dropout): Dropout(p=0.1, inplace=False)
            )
          )
          (intermediate): BertIntermediate(
            (dense): Linear(in_features=768, out_features=3072, bias=True)
          )
          (output): BertOutput(
            (dense): Linear(in_features=3072, out_features=768, bias=True)
            (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
            (dropout): Dropout(p=0.1, inplace=False)
          )
        )
        (1): BertLayer(
          (attention): BertAttention(
            (self): BertSelfAttention(
              (query): Linear(in_features=768, out_features=768, bias=True)
              (key): Linear(in_features=768, out_features=768, bias=True)
              (value): Linear(in_features=768, out_features=768, bias=True)
              (dropout): Dropout(p=0.1, inplace=False)
            )
            (output): BertSelfOutput(
              (dense): Linear(in_features=768, out_features=768, bias=True)
              (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
              (dropout): Dropout(p=0.1, inplace=False)
            )
          )
          (intermediate): BertIntermediate(
            (dense): Linear(in_features=768, out_features=3072, bias=True)
          )
          (output): BertOutput(
            (dense): Linear(in_features=3072, out_features=768, bias=True)
            (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
            (dropout): Dropout(p=0.1, inplace=False)
          )
        )
        (2): BertLayer(
          (attention): BertAttention(
            (self): BertSelfAttention(
              (query): Linear(in_features=768, out_features=768, bias=True)
              (key): Linear(in_features=768, out_features=768, bias=True)
              (value): Linear(in_features=768, out_features=768, bias=True)
              (dropout): Dropout(p=0.1, inplace=False)
            )
            (output): BertSelfOutput(
              (dense): Linear(in_features=768, out_features=768, bias=True)
              (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
              (dropout): Dropout(p=0.1, inplace=False)
            )
          )
          (intermediate): BertIntermediate(
            (dense): Linear(in_features=768, out_features=3072, bias=True)
          )
          (output): BertOutput(
            (dense): Linear(in_features=3072, out_features=768, bias=True)
            (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
            (dropout): Dropout(p=0.1, inplace=False)
          )
        )
        (3): BertLayer(
          (attention): BertAttention(
            (self): BertSelfAttention(
              (query): Linear(in_features=768, out_features=768, bias=True)
              (key): Linear(in_features=768, out_features=768, bias=True)
              (value): Linear(in_features=768, out_features=768, bias=True)
              (dropout): Dropout(p=0.1, inplace=False)
            )
            (output): BertSelfOutput(
              (dense): Linear(in_features=768, out_features=768, bias=True)
              (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
              (dropout): Dropout(p=0.1, inplace=False)
            )
          )
          (intermediate): BertIntermediate(
            (dense): Linear(in_features=768, out_features=3072, bias=True)
          )
          (output): BertOutput(
            (dense): Linear(in_features=3072, out_features=768, bias=True)
            (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
            (dropout): Dropout(p=0.1, inplace=False)
          )
        )
        (4): BertLayer(
          (attention): BertAttention(
            (self): BertSelfAttention(
              (query): Linear(in_features=768, out_features=768, bias=True)
              (key): Linear(in_features=768, out_features=768, bias=True)
              (value): Linear(in_features=768, out_features=768, bias=True)
              (dropout): Dropout(p=0.1, inplace=False)
            )
            (output): BertSelfOutput(
              (dense): Linear(in_features=768, out_features=768, bias=True)
              (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
              (dropout): Dropout(p=0.1, inplace=False)
            )
          )
          (intermediate): BertIntermediate(
            (dense): Linear(in_features=768, out_features=3072, bias=True)
          )
          (output): BertOutput(
            (dense): Linear(in_features=3072, out_features=768, bias=True)
            (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
            (dropout): Dropout(p=0.1, inplace=False)
          )
        )
        (5): BertLayer(
          (attention): BertAttention(
            (self): BertSelfAttention(
              (query): Linear(in_features=768, out_features=768, bias=True)
              (key): Linear(in_features=768, out_features=768, bias=True)
              (value): Linear(in_features=768, out_features=768, bias=True)
              (dropout): Dropout(p=0.1, inplace=False)
            )
            (output): BertSelfOutput(
              (dense): Linear(in_features=768, out_features=768, bias=True)
              (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
              (dropout): Dropout(p=0.1, inplace=False)
            )
          )
          (intermediate): BertIntermediate(
            (dense): Linear(in_features=768, out_features=3072, bias=True)
          )
          (output): BertOutput(
            (dense): Linear(in_features=3072, out_features=768, bias=True)
            (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
            (dropout): Dropout(p=0.1, inplace=False)
          )
        )
        (6): BertLayer(
          (attention): BertAttention(
            (self): BertSelfAttention(
              (query): Linear(in_features=768, out_features=768, bias=True)
              (key): Linear(in_features=768, out_features=768, bias=True)
              (value): Linear(in_features=768, out_features=768, bias=True)
              (dropout): Dropout(p=0.1, inplace=False)
            )
            (output): BertSelfOutput(
              (dense): Linear(in_features=768, out_features=768, bias=True)
              (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
              (dropout): Dropout(p=0.1, inplace=False)
            )
          )
          (intermediate): BertIntermediate(
            (dense): Linear(in_features=768, out_features=3072, bias=True)
          )
          (output): BertOutput(
            (dense): Linear(in_features=3072, out_features=768, bias=True)
            (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
            (dropout): Dropout(p=0.1, inplace=False)
          )
        )
        (7): BertLayer(
          (attention): BertAttention(
            (self): BertSelfAttention(
              (query): Linear(in_features=768, out_features=768, bias=True)
              (key): Linear(in_features=768, out_features=768, bias=True)
              (value): Linear(in_features=768, out_features=768, bias=True)
              (dropout): Dropout(p=0.1, inplace=False)
            )
            (output): BertSelfOutput(
              (dense): Linear(in_features=768, out_features=768, bias=True)
              (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
              (dropout): Dropout(p=0.1, inplace=False)
            )
          )
          (intermediate): BertIntermediate(
            (dense): Linear(in_features=768, out_features=3072, bias=True)
          )
          (output): BertOutput(
            (dense): Linear(in_features=3072, out_features=768, bias=True)
            (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
            (dropout): Dropout(p=0.1, inplace=False)
          )
        )
        (8): BertLayer(
          (attention): BertAttention(
            (self): BertSelfAttention(
              (query): Linear(in_features=768, out_features=768, bias=True)
              (key): Linear(in_features=768, out_features=768, bias=True)
              (value): Linear(in_features=768, out_features=768, bias=True)
              (dropout): Dropout(p=0.1, inplace=False)
            )
            (output): BertSelfOutput(
              (dense): Linear(in_features=768, out_features=768, bias=True)
              (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
              (dropout): Dropout(p=0.1, inplace=False)
            )
          )
          (intermediate): BertIntermediate(
            (dense): Linear(in_features=768, out_features=3072, bias=True)
          )
          (output): BertOutput(
            (dense): Linear(in_features=3072, out_features=768, bias=True)
            (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
            (dropout): Dropout(p=0.1, inplace=False)
          )
        )
        (9): BertLayer(
          (attention): BertAttention(
            (self): BertSelfAttention(
              (query): Linear(in_features=768, out_features=768, bias=True)
              (key): Linear(in_features=768, out_features=768, bias=True)
              (value): Linear(in_features=768, out_features=768, bias=True)
              (dropout): Dropout(p=0.1, inplace=False)
            )
            (output): BertSelfOutput(
              (dense): Linear(in_features=768, out_features=768, bias=True)
              (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
              (dropout): Dropout(p=0.1, inplace=False)
            )
          )
          (intermediate): BertIntermediate(
            (dense): Linear(in_features=768, out_features=3072, bias=True)
          )
          (output): BertOutput(
            (dense): Linear(in_features=3072, out_features=768, bias=True)
            (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
            (dropout): Dropout(p=0.1, inplace=False)
          )
        )
        (10): BertLayer(
          (attention): BertAttention(
            (self): BertSelfAttention(
              (query): Linear(in_features=768, out_features=768, bias=True)
              (key): Linear(in_features=768, out_features=768, bias=True)
              (value): Linear(in_features=768, out_features=768, bias=True)
              (dropout): Dropout(p=0.1, inplace=False)
            )
            (output): BertSelfOutput(
              (dense): Linear(in_features=768, out_features=768, bias=True)
              (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
              (dropout): Dropout(p=0.1, inplace=False)
            )
          )
          (intermediate): BertIntermediate(
            (dense): Linear(in_features=768, out_features=3072, bias=True)
          )
          (output): BertOutput(
            (dense): Linear(in_features=3072, out_features=768, bias=True)
            (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
            (dropout): Dropout(p=0.1, inplace=False)
          )
        )
        (11): BertLayer(
          (attention): BertAttention(
            (self): BertSelfAttention(
              (query): Linear(in_features=768, out_features=768, bias=True)
              (key): Linear(in_features=768, out_features=768, bias=True)
              (value): Linear(in_features=768, out_features=768, bias=True)
              (dropout): Dropout(p=0.1, inplace=False)
            )
            (output): BertSelfOutput(
              (dense): Linear(in_features=768, out_features=768, bias=True)
              (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
              (dropout): Dropout(p=0.1, inplace=False)
            )
          )
          (intermediate): BertIntermediate(
            (dense): Linear(in_features=768, out_features=3072, bias=True)
          )
          (output): BertOutput(
            (dense): Linear(in_features=3072, out_features=768, bias=True)
            (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
            (dropout): Dropout(p=0.1, inplace=False)
          )
        )
      )
    )
    (pooler): BertPooler(
      (dense): Linear(in_features=768, out_features=768, bias=True)
      (activation): Tanh()
    )
  )
  (dropout): Dropout(p=0.1, inplace=False)
  (classifier): Linear(in_features=768, out_features=3, bias=True)
)

 

 pooler 층에 도달하기까지 총 12개의 BertLayer가 있다. 셀프 어텐션 모델을 기반으로 구현해 놓은 일련의 임베딩 과정이라고 이해하자. 각각의 층은 attention, intermediate, output으로 구성되어 있다.

 pooler 층에서는 tanh 함수를 사용하며, 0.1 만큼의 드롭아웃이 적용되었다. 여러 과정을 거치고 거쳐, 768차원의 임베딩 벡터들이 나오게 된다.

 

 마지막 classifier층이 분류층이다. Linear함수를 사용하며, 768차원의 임베딩 벡터를 입력 받아 3개의 라벨을 돌려 준다.

 

2) 옵티마이저 설정 등

 Adam 옵티마이저를 사용한다. transformers 라이브러리에서는 AdamW로 사용한다.

 모델 파라미터를 변경하지 않고 그대로 사용하며, 학습률과 eps는 Chris McCornick의 구현 코드에서 조금씩 변경하였다. 학습률을 조금씩 감소시킬 수 있도록 스케쥴러도 생성한다.

 

# 옵티마이저 설정
optimizer = AdamW(model.parameters(), lr=2e-5, eps=1e-8)

# 학습 횟수
epochs = 10

# 총 훈련과정에서 반복하게 될 스텝
total_steps = len(train_dataloader)*epochs

# 스케쥴러 생성
scheduler = get_linear_schedule_with_warmup(optimizer, num_warmup_steps=0, num_training_steps=total_steps)

 

3) 기타 설정

 정확도를 계산하고, 시간을 표시하는 함수를 만든다. 또한 시드를 42로 고정한다.

 

# 정확도 계산
def get_accuracy(preds, labels):
    pred_flat = np.argmax(preds, axis=1).flatten()
    labels_flat = labels.flatten()
    return np.sum(pred_flat == labels_flat)/len(labels_flat)

# 시간 표시
def format_time(elapsed):
    elapsed_rounded = int(round(elapsed))
    return str(datetime.timedelta(seconds=elapsed_rounded))

# 시드 고정
SEED = 42
random.seed(SEED)
np.random.seed(SEED)
torch.manual_seed(SEED)
torch.cuda.manual_seed_all(SEED)

 


 

# 학습

 

 모델을 학습한다. Keras, Tensorflow에서와 달리 PyTorch에서는 훈련 모드, 평가 모드가 따로 있기 때문에, 모델 훈련 시 훈련 모드로 설정하는 것에 유의한다. 또한 검증 및 평가 시, 그래디언트를 계산하지 않도록 설정해야 한다.

 그래디언트를 계산할 때도 forward, backward, clip 등 함수를 적용하는 부분, 데이터 로더를 활용해 GPU 장치에 데이터를 넣는 부분 등 생소한 부분은 documentation과 기존의 코드를 참고하며 따라 갔다.

 

 출력 로짓에 소프트맥스를 적용하지 않았음에 주의한다.

 

# 그래디언트 초기화
model.zero_grad()

# 학습 횟수만큼 학습
for epoch_i in range(0, epochs):    
    total_loss = 0 # loss 초기화
    model.train() # 훈련 모드 변경
    
    # 데이터 로더에서 배치만큼 반복하여 가져옴
    for step, batch in enumerate(train_dataloader):
        batch = tuple(t.to(device) for t in batch) # 배치를 GPU에 넣음.
        b_input_ids, b_input_mask, b_labels = batch # 배치에서 데이터 추출
        
        # forward
        outputs = model(b_input_ids, token_type_ids=None, attention_mask=b_input_mask, labels=b_labels)
        
        # 총 loss 갱신
        loss = outputs[0]
        total_loss += loss
        
        # backward
        loss.backward()
        
        # 그래디언트 클리핑
        torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
        
        # 가중치 파라미터 업데이트
        optimizer.step()
        
        # 학습률 감소
        scheduler.step()
        
        # 그래디언트 초기화
        model.zero_grad()
   
    # 학습 완료 후 평균 loss 계산
    avg_train_loss = total_loss/len(train_data_loader)
    print(f"평균 학습 loss : {avg_train_loss}")
    
    # 학습 완료 후 검증 수행
    model.eval() # 평가 모드로 변경
    
    eval_loss, eval_accuracy = 0, 0
    nb_eval_steps = 0
    
    for batch in validation_dataloader:
        batch = tuple(t.to(device) for t in batch)
    
    	b_input_ids, b_input_mask, b_labels = batch
    
    	# 그래디언트 계산 해제
        with torch.no_grad():
            outputs = model(b_input_ids, token_type_ids=None, attention_mask=b_input_mask)
        
        # 로짓
        logits = outputs[0]
        
        # CPU로 데이터 이동하여 정확도 계산
        logits = logits.detach().cpu().numpy()
        label_ids = b_labels.to('cpu').numpy()
        
        tmp_acc = get_accuracy(logits, label_ids)
        eval_accuracy += tmp_acc
        nb_eval_steps += 1
        
    print(f"검증 정확도 : {eval_accuracy/nb_eval_steps}")

 

 학습 과정에서 loss의 변화 및 검증 정확도를 나타내면 다음과 같다.

>>> Epoch 1
평균 학습 loss : 0.6024325862583899
검증 정확도 : 0.8180415560115887

>>> Epoch 2
평균 학습 loss : 0.4202223287149979
검증 정확도 : 0.852347145098986

>>> Epoch 3
평균 학습 loss : 0.3315888200106289
검증 정확도 : 0.8496951955577017

>>> Epoch 4
평균 학습 loss : 0.26685928835803624
검증 정확도 : 0.8533505854659585

>>> Epoch 5
평균 학습 loss : 0.21310586419580727
검증 정확도 : 0.8637433606953163

>>> Epoch 6
평균 학습 loss : 0.175708380857712
검증 정확도 : 0.8596579249154999

>>> Epoch 7
평균 학습 loss : 0.13869329651086587
검증 정확도 : 0.8584885019314341

>>> Epoch 8
평균 학습 loss : 0.11233711548936258
검증 정확도 : 0.864890149686142

>>> Epoch 9
평균 학습 loss : 0.09370306544518918
검증 정확도 : 0.8571266900048286

>>> Epoch 10
평균 학습 loss : 0.07878449447267755
검증 정확도 : 0.8641507725736359

 

 학습에 총 1시간 40분 정도가 소요되었다. 평균 학습 loss는 계속해서 낮아지지만, 검증 정확도가 epoch 5 이후로 크게 향상되지 않는 것으로 보아, epoch을 4~6 정도로만 설정해도 될 것 같다. 학습 시간이 오래 걸린다는 점을 고려해 본다면, 더욱 그렇다.

 


# 테스트셋 정확도 

 

 모델을 평가 모드로 변경하여 테스트셋에 대한 정확도를 구한다. 위와 동일한 과정이다.

 

더보기

테스트셋 정확도를 구하는 코드는 다음과 같다.

 

model.eval()

eval_loss, eval_accuracy = 0
nb_eval_steps = 0

for step, batch in enumerate(test_dataloader):
    batch = tuple(t.to(device) for t in batch)
    b_input_ids, b_input_mask, b_labels = batch
    
    with torch.no_grad():
        outputs = model(b_input_ids, token_type_ids=None, attention_mask=b_input_mask)
    
    logits = outputs[0]
    
    logits = logits.detach().cpu().numpy()
    label_ids = b_labels.to('cpu').numpy()
    
    tmp_acc = get_accuracy(logits, label_ids)
    eval_accuracy += tmp_acc
    nb_eval_steps += 1
    
print(f"테스트 정확도 : {eval_accuracy/nb_eval_steps}")

 

 테스트에는 1분 24초가 소요되며, 검증 정확도와 비슷하게 0.8576이 나온다.

 


# 예측

 

 순환신경망 모델에서와 마찬가지로 각 문장에 대해 예측을 진행해 보았다.

 

def convert_data(sent):
    # tokenize and pad sentence
    tokenized_sent = BertTokenize(list(sent))
    padded_sent = pad_sentences(tokenized_sent)
    
    # create attention mask
    masks_sent = create_masks(padded_sent)
    
    # torch tensor
    inputs = torch.tensor(padded_sent)
    masks = torch.tensor(masks_sent)    
    
    return inputs, masks

def test_sentences(sent):
	# model to evaluation mode
    model.eval()
    
    # predict data
    inputs, masks = convert_data(sent)
    b_input_ids = inputs.to(device)
    b_input_mask = masks.to(device)
    
    with torch.no_grad():
    	outputs = model(b_input_ids, token_type_ids=None, attention_mask=b_input_mask)
    
    # get prediction
    logits = outputs[0]
    pred = np.argmax(logits)
    if pred == 1:
        result = "긍정"
    elif pred == 0:
        result = "중립"
    else:
        result = "부정"
    
    return result

 

 결과는 다음과 같다.

 

문장 결과
주52시간으로 삶이 쾌적해졌어요. 긍정
워라밸이좋아졌 네 요. 긍정
이건 재앙이야ㅡㅡ 중립
돈을 더 못 벌자나 부정
주52시간 조금만 더 보완하면 되겠네요. 부정
결국 다 망하자는 거죠. 부정
아이들이랑놀아줄수있는데왜그러는지모르겠네요. 부정
사무직은 좋지만 생산직은? 중립
배부른 소리하고있네. 부정
적게 일하고 많이 법시다. 중립
이 밤을 우린 어떻게 할까요.... 중립
배고파 중립
배불러 중립

 

 순환신경망 모델에 비해 부정으로 예측된 예시 문장이 많아졌다. BERT 모델의 경우 문맥 정보를 바탕으로 임베딩된 결과를 활용하기 때문에, 감성어 사전을 기반으로 하던 때에 비해 "돈을 더 못 벌자나" , "주52시간 조금만 더 보완하면 되겠네요."  등의 문장이 부정으로 분류되었다.

 다만 "이건 재앙이야ㅡㅡ"가 중립으로 분류되고,  "아이들이랑놀아줄수있는데왜그러는지모르겠네요." 의 문장이 부정으로 분류된 것은 아쉽다.

 


 이를 기반으로 포털 댓글 데이터셋에 대한 분류를 진행했다. 순환신경망 기반 모델과 달리 threshold 등은 설정하지 않았다.

 

더보기

코드가 효율적이지는 않다.

 

from tqdm import tqdm_notebook as tn_

X_pred = load_data(PRED_PATH, is_train=False)
bert_preds = []

for text in tn_(texts):
    _, label = test_sentences(text)
    bert_preds.append(label)
반응형