<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      NLP-情感分析 Prompting

      **NLP-情感分析 Prompting **

      注:本文是 Transformers 快速入門 Prompting 章節的學習筆記,更詳細的分析請參見原文。

      寫在前面

      Github 地址:https://github.com/Lockegogo/NLP_Tasks/tree/main/text_cls_prompt_senti

      本項目使用 Prompting 方法完成情感分析任務。Prompting 方法的核心思想是借助模板將問題轉換為與預訓練任務類似的形式來處理。

      例如要判斷標題 “American Duo Wins Opening Beach Volleyball Match” 的新聞類別,使用 Prompting 的步驟為:

      1. 構建 Prompt 模板:“This is a [MASK] News: x”
      2. 應用模板:“This is a [MASK] News: American Duo Wins Opening Beach Volleyball Match”
      3. 模型預測:從模型的輸出序列中抽取出 [MASK] token 對應的表示,然后運用 MLM head 預測 [MASK] token 對應詞表中每個 token 的分數(logits),我們只返回類別單詞對應位置的分數用于分類。

      數據預處理

      這里我們選擇中文情感分析語料庫 ChnSentiCorp 作為數據集,其包含各類網絡評論接近一萬條,可以從本倉庫下載。

      語料已經劃分好了訓練集、驗證集、測試集(分別包含 9600、1200、1200 條評論),一行是一個樣本,使用 TAB 分隔評論和對應的標簽,“0” 表示消極,“1” 表示積極。

      最常見的 Prompting 方法就是借助模板將問題轉換為 MLM 任務來解決。這里我們定義模板形式為 "總體上來說很 [MASK]。{x}",其中 x 表示評論文本,并且規定如果 [MASK] 被預測為 “好” 就判定情感為 “積極”,如果預測為 “差” 就判定為 “消極”,即 “積極” 和 “消極” 標簽對應的 label word 分別為 “好” 和 “差”。

      可以看到,MLM 任務與序列標注任務很相似,也是對 token 進行分類,并且類別是整個詞表,不同之處在于 MLM 任務只需要對文中特殊的 [MASK] token 進行標注,因此在處理數據時我們需要:

      1. 記錄下模板中所有 [MASK] 的位置,以便在模型的輸出序列中將它們的表示取出
      2. 記錄下 label word 對應的 token ID,因為我們實際上只關心模型在這些詞語上的預測結果

      首先我們編寫模板和 verbalizer 對應的函數:

      def get_prompt(x):
          prompt = f'總體上來說很[MASK]。{x}'
          return {
              'prompt': prompt,
              'mask_offset': prompt.find('[MASK]')
          }
      
      def get_verbalizer(tokenizer):
          return {
              'pos': {'token': '好', 'id': tokenizer.convert_tokens_to_ids("好")},
              'neg': {'token': '差', 'id': tokenizer.convert_tokens_to_ids("差")}
          }
      

      例如,第一個樣本轉換后的模板為:

      from transformers import AutoTokenizer
      
      checkpoint = "bert-base-chinese"
      tokenizer = AutoTokenizer.from_pretrained(checkpoint)
      
      comment = '這個賓館比較陳舊了,特價的房間也很一般。總體來說一般。'
      
      print('verbalizer:', get_verbalizer(tokenizer))
      
      prompt_data = get_prompt(comment)
      prompt, mask_offset = prompt_data['prompt'], prompt_data['mask_offset']
      
      encoding = tokenizer(prompt, truncation=True)
      tokens = encoding.tokens()
      # 將 [MASK] 從原來句子中的位置映射到 encoding 之后的位置
      mask_idx = encoding.char_to_token(mask_offset)
      
      print('prompt:', prompt)
      print('prompt tokens:', tokens)
      print('mask idx:', mask_idx)
      

      輸出如下:

      verbalizer: {'pos': {'token': '好', 'id': 1962}, 'neg': {'token': '差', 'id': 2345}}
      prompt: 總體上來說很[MASK]。這個賓館比較陳舊了,特價的房間也很一般。總體來說一般。
      prompt tokens: ['[CLS]', '總', '體', '上', '來', '說', '很', '[MASK]', '。', '這', '個', '賓', '館', '比', '較', '陳', '舊', '了', ',', '特', '價', '的', '房', '間', '也', '很', '一', '般', '。', '總', '體', '來', '說', '一', '般', '。', '[SEP]']
      mask idx: 7
      

      可以看到 BERT 分詞器正確地將 “[MASK]” 識別為一個 token,并且記錄下 [MASK] token 在序列中的索引。

      但是這種做法要求我們能夠從詞表中找到合適的 label word 來代表每一個類別,并且 label word 只能包含一個 token,而很多時候這是無法實現的。因此,另一種常見做法是為每個類別構建一個可學習的虛擬 token,然后運用類別描述來初始化虛擬 token 的表示,最后使用這些虛擬 token 來擴展模型的 MLM 頭。

      例如,這里我們可以為 “積極” 和 “消極” 構建專門的虛擬 token “[POS]” 和 “[NEG]”,并且設置對應的類別描述為 “好的、優秀的、正面的評價、積極的態度” 和 “差的、糟糕的、負面的評價、消極的態度”。下面我們擴展一下上面的 verbalizer 函數,添加一個 vtype 參數來區分兩種 verbalizer 類型:

      def get_verbalizer(tokenizer, vtype):
          assert vtype in ['base', 'virtual']
          return {
              'pos': {'token': '好', 'id': tokenizer.convert_tokens_to_ids("好")},
              'neg': {'token': '差', 'id': tokenizer.convert_tokens_to_ids("差")}
          } if vtype == 'base' else {
              'pos': {
                  'token': '[POS]', 'id': tokenizer.convert_tokens_to_ids("[POS]"),
                  'description': '好的、優秀的、正面的評價、積極的態度'
              },
              'neg': {
                  'token': '[NEG]', 'id': tokenizer.convert_tokens_to_ids("[NEG]"),
                  'description': '差的、糟糕的、負面的評價、消極的態度'
              }
          }
      
      vtype = 'virtual'
      # add label words
      if vtype == 'virtual':
          # 將新添加的 token 添加進模型的詞表
          tokenizer.add_special_tokens({'additional_special_tokens': ['[POS]', '[NEG]']})
      print('verbalizer:', get_verbalizer(tokenizer, vtype=vtype))
      

      訓練模型

      對于 MLM 任務,可以直接使用 Transformers 庫封裝好的 AutoModelForMaskedLM 類。由于 BERT 已經在 MLM 任務上進行了預訓練,因此借助模板我們甚至可以在不微調的情況下 (Zero-shot) 直接使用模板來預測情感極性。例如我們的第一個樣本:

      import torch
      from transformers import AutoModelForMaskedLM
      
      checkpoint = "bert-base-chinese"
      model = AutoModelForMaskedLM.from_pretrained(checkpoint)
      
      text = "總體上來說很[MASK]。這個賓館比較陳舊了,特價的房間也很一般。總體來說一般。"
      inputs = tokenizer(text, return_tensors="pt")
      token_logits = model(**inputs).logits
      # Find the location of [MASK] and extract its logits
      mask_token_index = torch.where(inputs["input_ids"] == tokenizer.mask_token_id)[1]
      mask_token_logits = token_logits[0, mask_token_index, :]
      # Pick the [MASK] candidates with the highest logits
      top_5_tokens = torch.topk(mask_token_logits, 5, dim=1).indices[0].tolist()
      
      for token in top_5_tokens:
          print(f"'>>> {text.replace(tokenizer.mask_token, tokenizer.decode([token]))}'")
      '>>> 總體上來說很好。這個賓館比較陳舊了,特價的房間也很一般。總體來說一般。'
      '>>> 總體上來說很棒。這個賓館比較陳舊了,特價的房間也很一般。總體來說一般。'
      '>>> 總體上來說很差。這個賓館比較陳舊了,特價的房間也很一般。總體來說一般。'
      '>>> 總體上來說很般。這個賓館比較陳舊了,特價的房間也很一般。總體來說一般。'
      '>>> 總體上來說很贊。這個賓館比較陳舊了,特價的房間也很一般。總體來說一般。'
      

      但是這種方法不夠靈活,我們還是采用繼承 Transformers 庫預訓練模型的方式手工構建模型,結構如下:

      Using cpu device
      initialize embeddings of [POS] and [NEG]
      BertForPrompt(
        (bert): BertModel()
        (cls): BertOnlyMLMHead(
          (predictions): BertLMPredictionHead(
            (transform): BertPredictionHeadTransform(
              (dense): Linear(in_features=768, out_features=768, bias=True)
              (transform_act_fn): GELUActivation()
              (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
            )
            (decoder): Linear(in_features=768, out_features=21128, bias=True)
          )
        )
      )
      

      如果采用虛擬 label word,我們除了向模型詞表中添加 “[POS]” 和 “[NEG]” token 以外,還按照我們在 verbalizer 中設置的描述來初始化這兩個 token 的嵌入。

      1. 用分詞器將描述文本轉換為對應的 token 列表 $t_1, t_2, ..., t_n $
      2. 然后初始化對應的表示為這些 token 嵌入的平均 $\frac{1}{n} \sum_{i=1}^n \boldsymbol{E}\left(t_i\right) $
      if args.vtype == "virtual":
          sp_tokens = ["[POS]", "[NEG]"]
          tokenizer.add_special_tokens({"additional_special_tokens": sp_tokens})
          model.resize_token_embeddings(len(tokenizer))
          verbalizer = get_verbalizer(tokenizer, vtype=args.vtype)
      
          with torch.no_grad():
              pos_id, neg_id = verbalizer["pos"]["id"], verbalizer["neg"]["id"]
              pos_tokenized = tokenizer(verbalizer["pos"]["description"])
              pos_tokenized_ids = tokenizer.convert_tokens_to_ids(pos_tokenized)
              neg_tokenized = tokenizer(verbalizer["neg"]["description"])
              neg_tokenized_ids = tokenizer.convert_tokens_to_ids(neg_tokenized)
              new_embedding = model.bert.embeddings.word_embeddings.weight[
                  pos_tokenized_ids
              ].mean(axis=0)
              model.bert.embeddings.word_embeddings.weight[pos_id, :] = (
                  new_embedding.clone().detach().requires_grad_(True)
              )
              new_embedding = model.bert.embeddings.word_embeddings.weight[
                  neg_tokenized_ids
              ].mean(axis=0)
              model.bert.embeddings.word_embeddings.weight[neg_id, :] = (
                  new_embedding.clone().detach().requires_grad_(True)
      

      注意,向模型詞表中添加 token 包含兩個步驟:

      1. 通過 tokenizer.add_special_tokens() 向分詞器中添加 token,這樣分詞器就能在分詞時將這些詞分為獨立的 token
      2. 通過 model.resize_token_embeddings() 擴展模型的詞表大小

      與之前相比,本次我們構建的 BertForPrompt 模型中增加了兩個特殊的函數:get_output_embeddings()set_output_embeddings(),負責調整模型的 MLM head。

      class BertForPrompt(BertPreTrainedModel):
          def __init__(self, config):
              super().__init__(config)
              self.bert = BertModel(config, add_pooling_layer=False)
              self.cls = BertOnlyMLMHead(config)
              # Initialize weights and apply final processing
              self.post_init()
      
          def get_output_embeddings(self):
              return self.cls.predictions.decoder
      
          def set_output_embeddings(self, new_embeddings):
              self.cls.predictions.decoder = new_embeddings
      
          def forward(self, batch_inputs, batch_mask_idxs, label_word_id, labels=None):
              bert_output = self.bert(**batch_inputs)
              sequence_output = bert_output.last_hidden_state
              batch_mask_reps = batched_index_select(
                  sequence_output, 1, batch_mask_idxs.unsqueeze(-1)
              ).squeeze(1)
              pred_scores = self.cls(batch_mask_reps)[:, label_word_id]
      
              loss = None
              if labels is not None:
                  loss_fn = nn.CrossEntropyLoss()
                  loss = loss_fn(pred_scores, labels)
              return loss, pred_scores
      

      如果刪除這兩個函數,那么在調用 model.resize_token_embeddings() 時,就僅僅會調整模型詞表的大小,而不會調整 MLM head,即運行上面的代碼輸出的張量維度依然是 21128。如果你不需要預測新添加 token 在 mask 位置的概率,那么即使刪除這兩個函數,代碼也能正常運行,但是對于本文這種需要預測的情況就不行了。

      為了讓模型適配我們的任務,這里首先通過 batched_index_select 函數從 BERT 的輸出序列中抽取出 [MASK] token 對應的表示,在運用 MLM head 預測出該 [MASK] token 對應詞表中每個 token 的分數之后,我們只返回類別對應 label words 的分數用于分類。

      模型對每個樣本都應該輸出 “消極” 和 “積極” 兩個類別對應 label word 的預測 logits 值。

      需要注意的是,如果采用虛擬 label word,模型是無法直接進行預測的。在擴展了詞表之后,MLM head 的參數矩陣尺寸也會進行調整,新加入的參數都是隨機初始化的,此時必須進行微調才能讓 MLM head 正常工作。

      參考資料

      1. Transformers 快速入門
      2. bert4keras 在手,baseline 我有:CLUE 基準代碼
      posted @ 2024-02-17 18:50  Lockegogo  閱讀(316)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 久久狠狠一本精品综合网| 娱乐| 狠狠色噜噜狠狠狠777米奇小说| 九九热免费在线播放视频| 亚洲中文字幕无码一区日日添| 色窝窝免费播放视频在线| 伦伦影院午夜理论片| 99精品国产综合久久久久五月天| 午夜成人鲁丝片午夜精品| 国产精品粉嫩嫩在线观看| 国内精品久久久久影院网站| 日韩欧美卡一卡二卡新区 | 色综合色综合色综合频道| 99精品热在线在线观看视| 熟女丝袜潮喷内裤视频网站| 精品亚洲无人区一区二区| 午夜在线观看成人av| 漂亮人妻被黑人久久精品| 亚洲成A人片在线观看无码不卡| 国产综合久久99久久| 国99久9在线 | 免费| 久久久久国产精品熟女影院| 久久精品国产亚洲不AV麻豆| 亚洲国产精品日韩在线 | 国产高清精品在线91| 久久国产乱子精品免费女| 欧洲亚洲成av人片天堂网| 亚洲精品午夜国产VA久久成人| 平塘县| 国产乱码日韩精品一区二区| 国产AV老师黑色丝袜美腿| 伊春市| 亚洲中文字幕无码一久久区| 亚洲中文字幕精品久久久久久动漫| 午夜国产精品福利一二| 久久av高潮av喷水av无码| 四虎影视一区二区精品| 南溪县| av无码小缝喷白浆在线观看| 中文字幕乱码人妻二区三区 | 成人性能视频在线|