OpenAI EvalsをAzure OpenAI ServiceのLLMモデルで動かす方法

## 結論

[公式ドキュメント](https://github.com/openai/evals/blob/main/docs/completion-fns.md)を参考に `evals/registry/completion_fns` 配下のYAMLファイルに以下を追加する。

```yaml
langchain/llm/aoai-gpt4-chat:
  class: evals.completion_fns.langchain_llm:LangChainChatModelCompletionFn
  args:
    llm: AzureChatOpenAI
    llm_kwargs:
      openai_api_type: "azure"
      openai_api_version: "2023-05-15"
      openai_api_base: "https://XXXX.openai.azure.com"
      openai_api_key: "XXXXXXX"
      model_name: gpt-4
      deployment_name: "aoai-gpt4"
```

## OpenAI Evals とは

[OpenAI Evals](https://github.com/openai/evals) は OpenAI 社が提供するLLMコンポーネントの評価フレームワークです。

- LLMに対する入力と期待する回答の組み合わせのデータセット
- LLMによる回答と期待する回答の評価を行うEvalクラス
- LLMを呼び出すLLMコンポーネントを定義するCompletionFnクラス

この三つの要素を柔軟に組み合わせてLLMの性能評価を繰り返し定量的に行うことが出来るフレームワークとなっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2997186/e9ee355f-2581-80bb-c141-ddd75f855c68.png)


## インストール

OpenAI Evalsを使用する際はライブラリとしての呼び出しだけではなく、カスタムクラスの追加やデータセットの追加を行いたいのでリポジトリをクローンしてのインストールをお勧めします。

```shell
# リポジトリのクローン
git clone https://github.com/openai/evals.git
# クローンしたリポジトリに移動
cd evals
# リポジトリに含まれる LFS (Large File Storage) データのダウンロード
git lfs fetch --all
git lfs pull
# evalsのインストール
pip install -e .
```

## Evalsの実行

インストール完了後は oaieval コマンドで評価を実行することが出来ます。
gpt-3.5-turbo が使用するLLMコンポーネントを、test-match がデータセットとEvalクラスを指定します。
```
oaieval gpt-3.5-turbo test-match
```
※ 実行するにはChatGPTのAPIキーが必要です。

## 使用するLLM設定のカスタマイズ

[公式ドキュメント](https://github.com/openai/evals/blob/main/docs/completion-fns.md)によると `evals/registry/completion_fns` 配下のYAMLファイルに呼び出すLLMコンポーネントを定義するとのこと。

配置されているファイルの中を見てみると、先ほど指定した `gpt-3.5-turbo` が定義されていました。

```yaml:langchain_llms.yaml
langchain/llm/gpt-3.5-turbo:
  class: evals.completion_fns.langchain_llm:LangChainLLMCompletionFn
  args:
    llm: OpenAI
    llm_kwargs:
      model_name: gpt-3.5-turbo
```

class 部分で `LangChainLLMCompletionFn` が指定されているので `evals/completion_fns` ディレクトリのコードを確認してみましょう。

```python:langchain_llm.py
class LangChainLLMCompletionFn(CompletionFn):
    def __init__(self, llm: str, llm_kwargs: Optional[dict] = None, **kwargs) -> None:
        # Import and resolve self.llm to an instance of llm argument here,
        # assuming it's always a subclass of BaseLLM
        if llm_kwargs is None:
            llm_kwargs = {}
        module = importlib.import_module("langchain.llms")
        LLMClass = getattr(module, llm)

        if issubclass(LLMClass, BaseLLM):
            self.llm = LLMClass(**llm_kwargs)
        else:
            raise ValueError(f"{llm} is not a subclass of BaseLLM")
```

引数として指定した`llm`と一致するモデルのクラスを`langchain.llms`ライブラリから呼び出し、`llm_kwargs`を引数として指定しているのがわかります。
ですので、この `llm` で指定するモデルを `Azure OpenAI Service` に対応したものにして、引数で接続先等の必要なものを指定すればよいはずです。

必要なものを指定したものが初めに提示したこちらになります。
```yaml
langchain/llm/aoai-gpt4-chat:
  class: evals.completion_fns.langchain_llm:LangChainChatModelCompletionFn
  args:
    llm: AzureChatOpenAI
    llm_kwargs:
      openai_api_type: "azure"
      openai_api_version: "2023-05-15"
      openai_api_base: "https://XXXX.openai.azure.com"
      openai_api_key: "XXXXXXX"
      model_name: gpt-4
      deployment_name: "aoai-gpt4"
```

個人的にはまったポイントでもあるのですが、`LangChainLLMCompletionFn`クラスではなく`LangChainChatModelCompletionFn`クラスを指定します。
先ほど見た`gpt-3.5-turbo`のLLMコンポーネントでは`LangChainLLMCompletionFn`クラスを使用していましたが、`Completion API`は非推奨の古いAPIであり精度がいまいちでGPT4.0モデルも使用できません。そのため `ChatCompletion API`を使用する`LangChainChatModelCompletionFn`を指定します。

`llm`のモデル名や`llm_kwargs`の指定は [langChain の AzureChatOpenAI ページ](https://api.python.langchain.com/en/latest/chat_models/langchain.chat_models.azure_openai.AzureChatOpenAI.html)を参考にしました。

https://api.python.langchain.com/en/latest/chat_models/langchain.chat_models.azure_openai.AzureChatOpenAI.html

## カスタムLLMコンポーネントを使用した実行

実行時に指定するLLMコンポーネントの指定を先ほどYAMLに定義したコンポーネントに変更することで、Azure OpenAI Service を使用してEvalsを実行することが出来ます。

自分の環境ではデフォルトの並列実行数で起動すると、APIのレートリミットに引っかかってしまいました。そのため、起動時にスレッド数を1に指定して起動しています。また、実行結果のログの出力先がデフォルトでは temporaryディレクトリでわかりづらいため、実行時に出力先を指定しています。

```shell:run.sh
export EVALS_THREADS=1
oaieval langchain/llm/aoai-gpt4-chat test-match --record_path="./evalslog.jsonl"
```

## 終わりに

いかがでしたでしょうか？ 業務の都合上使用できるLLMモデルが Azure OpenAI Service に限定されている状態で OpenAI Evals を試してみたかったのですが、参考になる日本語記事が少なかったので書かせていただきました。
最終的には英語で書かれた公式のドキュメントを見てもらうのが確実ですが、同じようにLLMモデルを差し替える必要がある方の参考になれば幸いです。

## 追伸

Evals をエヴァルスとイーバルスのどちらで発音するのが正しいのかわからないため、有識者の方いらっしゃればコメントで教えていただけますと幸いです。