UltraFeedbackの意味と使い方について:生成されたフィードバックによる大規模言語モデルの強化



- 1. UltraFeedback
- 1.1. 情報源
- 2. AIフィードバックの導入
- 2.1. UltraFeedbackの構築
- 3. UltraFeedbackの使い方
- 3.1. UltraFeedbackデータセットの構造
- 3.2. データの構造を確認
- 4. UltraFeedbackを利用して開発されたモデル
- 5. UltraRM(Ultra Reward Model)の使い方
- 5.1. 必要なライブラリのインポート
- 5.2. 報酬モデル(Reward Model)の定義
- 5.3. トークナイザーとモデルのロード
- 5.4. 評価するデータセットの準備
- 5.5. 報酬スコアの計算と比較
- 5.6. 実行結果の例
- 5.7. すべてのコード
- 6. 参考文献
1. UltraFeedback
近年、大規模言語モデル(LLM)の性能を向上させるために、人間のフィードバックを活用する手法が注目されています。こうした方法は、モデルをユーザーの好みに適応させ、応答の品質を向上させるDPOなどの手法でりようされます。しかし、人間によるフィードバック収集には多くのコストがかかり、スケーラブルではないという課題があります。この問題に対処するため、UltraFeedbackという新しいアプローチが提案されました。
1.1. 情報源
本記事は次の論文を参考にして作成しました。
UltraFeedback: Boosting Language Models with Scaled AI Feedback
written by Ganqu Cui, Lifan Yuan, Ning Ding, Guanming Yao, Bingxiang He, Wei Zhu, Yuan Ni, Guotong Xie, Ruobing Xie, Yankai Lin, Zhiyuan Liu, Maosong Sun
Submitted on 2 Oct 2023 (v1), last revised 16 Jul 2024 (this version, v2)
2. AIフィードバックの導入
人間によるフィードバックの生成はコストや時間などがかかります。そこで、LLM(具体的にはGPT-4)を利用してフィードバックを収集する手法が提案されました。このAIフィードバックは、以下のようなメリットをもたらします。
- スケーラビリティ:AIによるフィードバックは大量かつ迅速に生成可能。
- 多様性:幅広い状況をカバーするデータセットが構築可能。
- コスト削減:人間の労力や時間が不要。


2.1. UltraFeedbackの構築
この研究では、250,000件のユーザーとアシスタントの対話データを収集し、それに基づいて100万件以上のフィードバックを生成しました。このデータセットは「UltraFeedback」と名付けられ、以下の特徴を持っています。
- 高品質:GPT-4のフィードバックにより精度を確保。
- 大規模:従来のデータセットを大きく上回る量。
- 偏りの軽減:注釈の偏りを抑える技術を適用。
3. UltraFeedbackの使い方
Hugging Faceで公開されている「UltraFeedback」は、強力な報酬モデルや批評モデルの訓練に最適な、詳細で多様な好みを含むデータセットです。このデータセットは、64,000のプロンプトに対して複数のモデルから生成されたさまざまな応答が含まれており、それぞれの応答には詳しいフィードバックが付与されています。
3.1. UltraFeedbackデータセットの構造
UltraFeedbackデータセットは、おおまかに次のような構造になっています。
- プロンプト:試したい問題の説明や指示。
- 応答:モデルが生成した複数の応答。
- フィードバック:応答のクオリティを評価するためのラベル付け。指示対応性、真実性、誠実性、有用性の4角度から検討されています。
データセットのインストール
以下のコードを使用して、UltraFeedbackデータセットをインストールします。
from datasets import load_dataset
# UltraFeedbackデータセットのロード
dataset = load_dataset("openbmb/UltraFeedback")
このコードで、データセットをロードし、読み込みます。
3.2. データの構造を確認
データセットの列名を確認します。
print(dataset["train"].column_names)
データを確認するには、以下のようにします。
# データの構造を確認
print(dataset["train"][0])
これにより、一つのデータに含まれる情報が確認できます。また、以下のように応答を表示することができます。
# 複数の応答を表示
for response in dataset["train"][0]["completions"]:
print(response)
break
4. UltraFeedbackを利用して開発されたモデル
ULTRAFEEDBACKをベースに、2つのオープンソースモデルを開発された。
- UltraRM(Ultra Reward Model)
ユーザーの指示に基づいてAIの応答を評価するためのリワードモデル。
LLaMA2-13Bをベースにトレーニングされています。 - UltraCM(Ultra Critique Model)
ULTRAFEEDBACKから得られるテキストフィードバックを活用して、AIや人間のアシスタントと柔軟にやりとりできる批評モデル。
5. UltraRM(Ultra Reward Model)の使い方
以下は、UltraRMを使用してデータセットの評価を行います。
5.1. 必要なライブラリのインポート
まず、必要なライブラリをインポートします。このコードでは、transformers
ライブラリを使用してモデルやトークナイザーをロードします。また、PyTorchを用いて計算を行います。
from transformers import PreTrainedModel, LlamaConfig, LlamaModel, LlamaTokenizer
import torch.nn as nn
import torch
from typing import Optional, List
5.2. 報酬モデル(Reward Model)の定義
次に、Llamaベースの報酬モデル(LlamaRewardModel
)を定義します。このクラスは、入力を受け取り、対応する報酬スコアを生成します。
self.model
: Llamaモデルを使用してテキストデータをエンコードします。self.linear
: エンコードされた隠れ状態(hidden states)を1次元のスカラー値(報酬スコア)に変換する全結合層です。
以下がモデル定義のコードです。
class LlamaRewardModel(PreTrainedModel):
config_class = LlamaConfig
def __init__(self, config):
super().__init__(config)
self.model = LlamaModel(config)
self.regression_head = nn.Linear(self.config.hidden_size, 1, bias=False)
def forward(
self,
input_ids: torch.LongTensor = None,
attention_mask: Optional[torch.Tensor] = None,
position_ids: Optional[torch.LongTensor] = None,
past_key_values: Optional[List[torch.FloatTensor]] = None,
inputs_embeds: Optional[torch.FloatTensor] = None,
):
# モデルの出力を取得
outputs = self.model(
input_ids,
attention_mask=attention_mask,
position_ids=position_ids,
past_key_values=past_key_values,
inputs_embeds=inputs_embeds,
)
hidden_states = outputs[0] # 隠れ状態を取得
rewards = self.regression_head(hidden_states).squeeze(-1) # 報酬スコアに変換
# アテンションマスクを用いて各入力シーケンスの終了点を取得
ends = attention_mask.cumsum(dim=1).argmax(dim=1).view(-1, 1)
rewards = torch.gather(rewards, 1, ends)
return rewards
5.3. トークナイザーとモデルのロード
次に、事前学習済みのトークナイザーと報酬モデルをロードします。この例では、openbmb/UltraRM-13b
という事前学習済みモデルを使用しています。
tokenizer = LlamaTokenizer.from_pretrained("openbmb/UltraRM-13b")
model = LlamaRewardModel.from_pretrained("openbmb/UltraRM-13b")
5.4. 評価するデータセットの準備
評価には、選択された回答(chosen
)と拒否された回答(rejected
)のペアを含むデータセットを使用します。以下は、データセットの例です。
dataset = [
{"chosen": "Cheese is a dairy product made from milk.", "rejected": "Cheese comes from plants."},
{"chosen": "The sun rises in the east.", "rejected": "The sun rises in the west."},
]
5.5. 報酬スコアの計算と比較
次に、各回答ペアについて報酬スコアを計算し、その差を求めます。これにより、モデルが選択された回答と拒否された回答をどの程度うまく区別できるかを評価します。
chosen
: 選択された回答の報酬スコア。rejected
: 拒否された回答の報酬スコア。- 報酬スコアの差(
chosen_reward - rejected_reward
)を計算して出力します。
以下は具体的なコードです。
for example in dataset:
# 選択された回答のスコアを計算
inputs = tokenizer(example["chosen"], return_tensors="pt")
chosen = model(**inputs).item()
# 拒否された回答のスコアを計算
inputs = tokenizer(example["rejected"], return_tensors="pt")
rejected = model(**inputs).item()
# 報酬スコアの差を出力
print(f"Difference in rewards: {chosen - rejected}")
5.6. 実行結果の例
このコードを実行すると、以下のような出力が得られます。
5.7. すべてのコード
今までの内容をすべて一つのコードにまとめると次のようになります。
from transformers import PreTrainedModel, LlamaConfig, LlamaModel, LlamaTokenizer
import torch.nn as nn
import torch
from typing import Optional, List
class LlamaRewardModel(PreTrainedModel):
config_class = LlamaConfig
def __init__(self, config):
super().__init__(config)
self.model = LlamaModel(config)
self.regression_head = nn.Linear(self.config.hidden_size, 1, bias=False)
def forward(
self,
input_ids: torch.LongTensor = None,
attention_mask: Optional[torch.Tensor] = None,
position_ids: Optional[torch.LongTensor] = None,
past_key_values: Optional[List[torch.FloatTensor]] = None,
inputs_embeds: Optional[torch.FloatTensor] = None,
):
# モデルの出力を取得
outputs = self.model(
input_ids,
attention_mask=attention_mask,
position_ids=position_ids,
past_key_values=past_key_values,
inputs_embeds=inputs_embeds,
)
hidden_states = outputs[0] # 隠れ状態を取得
rewards = self.regression_head(hidden_states).squeeze(-1) # 報酬スコアに変換
# アテンションマスクを用いて各入力シーケンスの終了点を取得
ends = attention_mask.cumsum(dim=1).argmax(dim=1).view(-1, 1)
rewards = torch.gather(rewards, 1, ends)
return rewards
tokenizer = LlamaTokenizer.from_pretrained("openbmb/UltraRM-13b")
model = LlamaRewardModel.from_pretrained("openbmb/UltraRM-13b")
dataset = [
{"chosen": "Cheese is a dairy product made from milk.", "rejected": "Cheese comes from plants."},
{"chosen": "The sun rises in the east.", "rejected": "The sun rises in the west."},
]
for example in dataset:
# 選択された回答のスコアを計算
inputs = tokenizer(example["chosen"], return_tensors="pt")
chosen = model(**inputs).item()
# 拒否された回答のスコアを計算
inputs = tokenizer(example["rejected"], return_tensors="pt")
rejected = model(**inputs).item()
# 報酬スコアの差を出力
print(f"Difference in rewards: {chosen - rejected}")