SOGM'S Data

[PaperReview]SimCSE: Simple Contrastive Learning of Sentence Embeddings (1) (작성중) 본문

About Data/AI Paper

[PaperReview]SimCSE: Simple Contrastive Learning of Sentence Embeddings (1) (작성중)

왈왈가부 2024. 1. 8. 09:06
def train_dataloader(train_dataset):
  train_sampler = RandomSampler(train_dataset)
  model_collate_fn = functools.partial(
    process_batch,
    tokenizer=tokenizer,
    max_len=args.max_seq_length
    )
  train_dataloader = DataLoader(train_dataset,
                              batch_size=args.batch_size,
                              sampler=train_sampler,
                              collate_fn=model_collate_fn)
  return train_dataloader
view raw

논문 리뷰내용은 추가예정

 

1.논문에서 학습에 구현된 wikiDataset

class wikiDataset(Dataset):
    def __init__(self, csv_path, training=True, full=False):
        dataset_df = pd.read_csv(csv_path,names=["text"])
        dataset_df.dropna(inplace=True)
        source_texts = dataset_df["text"].values
        target_texts = dataset_df["text"].values
        data = list(zip(source_texts,target_texts))
        if full:
          self.data = data
        else:
          train_data,val_data =   train_test_split(data,test_size=0.15,random_state=42,shuffle=False)
          self.data = train_data if training else val_data
    def __len__(self):
      return len(self.data)
    def __getitem__(self,idx):
      return self.data[idx]

 

2.source (anchor)와  target (positive)를 구분하는 작업 추가 

def process_batch(txt_list,tokenizer,max_len=args.max_seq_length):
  source_ls = [source for source,target in txt_list]
  target_ls = [target for source,target in txt_list]
  source_tokens = tokenizer(source_ls,truncation=True,padding="max_length",max_length=args.max_seq_length)
  target_tokens = tokenizer(target_ls,truncation=True,padding="max_length",max_length=args.max_seq_length)
  input_ids = []
  attention_mask = []
  token_type_ids = []
  for i in range(len(source_tokens["input_ids"])):
    input_ids.append(source_tokens["input_ids"][i])
    input_ids.append(target_tokens["input_ids"][i])
    attention_mask.append(source_tokens["attention_mask"][i])
    attention_mask.append(target_tokens["attention_mask"][i])
    token_type_ids.append(source_tokens["token_type_ids"][i])
    token_type_ids.append(target_tokens["token_type_ids"][i])
  return torch.tensor(input_ids),torch.tensor(attention_mask),torch.tensor(token_type_ids)

 

3.데이터 로더

데이터 로더

def train_dataloader(train_dataset):
  train_sampler = RandomSampler(train_dataset)
  model_collate_fn = functools.partial(
    process_batch,
    tokenizer=tokenizer,
    max_len=args.max_seq_length
    )
  train_dataloader = DataLoader(train_dataset,
                              batch_size=args.batch_size,
                              sampler=train_sampler,
                              collate_fn=model_collate_fn)
  return train_dataloader
view raw

 

4.옵티마이저 정의

아담 사용, warm up 으로 학습속도 초반에 빠르게 해줌 

param_optimizer = list(model.named_parameters())
no_decay = ['bias','LayerNorm.weight']
optimizer_grouped_parameters = [
    {'params': [p for n, p in param_optimizer if not any(nd in n for nd in no_decay)], 'weight_decay': args.weight_decay},
    {'params': [p for n, p in param_optimizer if any(nd in n for nd in no_decay)], 'weight_decay': 0.0}
    ]

warmup_steps = int(args.warmup_proportion * num_train_optimization_steps)
optimizer = AdamW(optimizer_grouped_parameters,lr=args.learning_rate,eps=args.adam_epsilon)
scheduler = get_linear_schedule_with_warmup(
        optimizer,
        num_warmup_steps=warmup_steps,
        num_training_steps=num_train_optimization_steps)

+) 위 코드 부연 설명 

위의 코드는 PyTorch에서 Transformers 라이브러리를 사용하여 모델 파라미터를 최적화하기 위한 Optimizer와 Scheduler를 설정하는 부분입니다. 코드를 간략히 설명 

1. param_optimizer = list(model.named_parameters()): 모델의 모든 파라미터를 가져와서 (파라미터 이름, 파라미터 값) 쌍의 리스트로 저장

2. no_decay = ['bias', 'LayerNorm.weight']: 가중치 decay 지정 ,  여기서는 바이어스(bias)와 LayerNormalization의 가중치(weight)에 대해 가중치 decay를 적용하지 않을 것으로 설정

3. optimizer_grouped_parameters: Optimizer에 전달할 파라미터 그룹 설정,  두 개의 그룹으로 나누어집니다. 첫 번째 그룹은 'no_decay'에 포함된 파라미터를 제외한 모든 파라미터에 대해 가중치 decay 를 적용, 두 번째 그룹은 'no_decay'에 해당하는 파라미터에 대해 가중치 decay 적용 x

4. warmup_steps = int(args.warmup_proportion * num_train_optimization_steps): 학습률 스케줄러에서의 웜업 단계를 설정합니다.

5. scheduler = get_linear_schedule_with_warmup(optimizer, num_warmup_steps=warmup_steps, num_training_steps=num_train_optimization_steps): 웜업 및 학습률 감소 스케줄러를 설정. 초기에 빠르게 학습하는데 도움이 됨. transformer 모듈에 있음. 

 

5. Bert, Roberta 모델 init 

class BertForCL(BertPreTrainedModel):
    _keys_to_ignore_on_load_missing = [r"position_ids"]
    def __init__(self, config):
        super().__init__(config)
        #self.model_args = model_kargs["model_args"]
        self.bert = BertModel(config)
        cl_init(self, config)
    def forward(self,
        input_ids=None,
        attention_mask=None,
        token_type_ids=None,
        position_ids=None,
        head_mask=None,
        inputs_embeds=None,
        labels=None,
        output_attentions=None,
        output_hidden_states=None,
        return_dict=None,
        sent_emb=False,
    ):
        if sent_emb:
            return sentemb_forward(self, self.bert,
                input_ids=input_ids,
                attention_mask=attention_mask,
                token_type_ids=token_type_ids,
                position_ids=position_ids,
                head_mask=head_mask,
                inputs_embeds=inputs_embeds,
                labels=labels,
                output_attentions=output_attentions,
                output_hidden_states=output_hidden_states,
                return_dict=return_dict,
            )
        else:
            return cl_forward(self, self.bert,
                input_ids=input_ids,
                attention_mask=attention_mask,
                token_type_ids=token_type_ids,
                position_ids=position_ids,
                head_mask=head_mask,
                inputs_embeds=inputs_embeds,
                labels=labels,
                output_attentions=output_attentions,
                output_hidden_states=output_hidden_states,
                return_dict=return_dict,
            )

cl_init 함수에 대한 간단한 구조 : https://gist.github.com/bhuvanakundumani/dc933afece8dcebdcf841a73b84666e1

 

 

6. encoder (embedding하기)

   # Get raw embeddings
    outputs = encoder(
        input_ids,
        attention_mask=attention_mask,
        token_type_ids=token_type_ids,
        position_ids=position_ids,
        head_mask=head_mask,
        inputs_embeds=inputs_embeds,
        output_attentions=output_attentions,
        output_hidden_states=False if args.pooler_type == 'cls' else True,
        return_dict=True,
    )
    # Pooling
    pooler_output = cls.pooler(attention_mask, outputs)
    pooler_output = pooler_output.view((batch_size, args.num_sent, pooler_output.size(-1))) # (bs, num_sent, hidden)
    # If using "cls", we add an extra MLP layer
    # (same as BERT's original implementation) over the representation.
    if cls.pooler_type == "cls":
        pooler_output = cls.mlp(pooler_output)
    # Separate representation
    z1, z2 = pooler_output[:,0], pooler_output[:,1]

- cls token output을 사용할거면 MLP Layer를 한 번 통과시킴. 

- last_hidden[:, 0] as the pooler_output with a shape torch.Size([16, 768]) . (batch_size, model_dim)

- input_ids[0] has the ids for source texts and input_ids[1] has the ids for target texts.

-pooler_output = pooler_output.view((batch_size, args.num_sent, pooler_output.size(-1))) 해당  코드로인해The pooler_output with the shape torch.Size([16, 768]) is reshaped to torch.Size([8, 2, 768]) 

- 즉 , [8, 0, 768] are the outputs for the source texts ,   [8, 1, 768] are the outputs for the target texts.

- 또한 아래 코드로 인해 Source 와 target 을 구분하게 된다. 

z1, z2 = pooler_output[:,0], pooler_output[:,1]

* 참고 : bert의 Output은 아래 3가지다 .

(last_hidden_state,pooler_output,hidden_states) = model(token_ids, attention_mask = attention_mask)
last_hidden_state : 최종 인코더 계층의 모든 output   
   *last_hidden_state[:,0,:] 하면 cls 토큰만을 선택할 수 있으나,  바로 아래 소개하는 pooler_output과는 완전히 같지않음.
pooler_output : 최종 인코더 계층의[CLS] 토큰 표현. 선형 및 tanh 함수를 거쳐 계산
hidden_states : 모든 인코더 계층에서 얻은 모든 토큰의 표현.

 

7.  Loss function 

7.  Loss function 

cos_sim = cls.sim(z1.unsqueeze(1), z2.unsqueeze(0))

labels = torch.arange(cos_sim.size(0)).long().to(cls.device)

loss_fct = nn.CrossEntropyLoss()

loss = loss_fct(cos_sim, labels)

- cosine 유사도를 구하기위한 unsqueeze를 서로 반대로 해주는 모습.

 

 

참고 출처 :

1.https://github.com/princeton-nlp/SimCSE/blob/main/simcse/models.py

2. https://bhuvana-kundumani.medium.com/implementation-of-simcse-for-unsupervised-approach-in-pytorch-a3f8da756839

 

Implementation of SimCSE for unsupervised approach in Pytorch

Hi Everyone ! Welcome to my blog. In this blog, I am going to show a simple implementation of SimCSE: Simple Contrastive Learning of…

bhuvana-kundumani.medium.com

 

 

Comments