LlamaIndex v0.10.　の使い方メモ

RAGを用いる業務が増えてきたのでメモとして残しておきます。

## LlamaIndex v0.10主な変更点

:::note info
1. Llama-index-coreを作成、Integrationを個別パッケージに分離
2. LlamaHubでIntegrationを管理
3. ServiceContextが非推奨に
:::


**①Llama-index-coreに移行**
過去のバージョンでllama_indexからインポートしていたものは、基本的にllama_index.coreからインポート
```python
# from llama_index import VectorStoreIndex →
from llama_index.core import VectorStoreIndex

#  from llama_index import SimpleDirectoryReader →
from llama_index.core import SimpleDirectoryReader
```


**② サードパーティ「Integration」には主に以下のカテゴリが含まれる**
- llms
- embeddings
- multi_modal_llms
- readers
- tools
- vector_stores

**③ ServiceContextは非推奨に**
v0.10.以前ではServiceContext上で使用するLLMや埋め込みモデル、コールバックなどを指定するために使用していた。
非推奨になったのは主に
①OpenAIをデフォルトで用いることからローカルLLMを使用する場合でもapiキーが必要になること 
②カスタムモデルを用いる場合すべてのモジュールにservice_contextを渡す必要があること 
の２点が原因らしい。

**ServiceContextの代わりにSettingsを使用する**
```python
from llama_index.embedding.openai import OpneAIEmbedding
from llama_index.llm.openai import OpenAI
from llama_index.core.callbacks import CallbackManager
from llama_index.core.settings import Settings

#テキストのエンベディングモデルの指定
Settings.embed_model=OpenAIEmbedding()
#LLMモデルの指定
Settings.llm=OpenAI()
#コールバックマネージャーの指定
Settings.callback_manager=CallbackManager()
```


## LlamaIndex  v0.10.　使用例

**(1) LlamaIndexのインストール**
```python
!pip install llama-index
```

**(2) 環境変数の準備**
openaiにアクセスするために環境変数"OPENAI_API_KEY"にapiキーを設定する
```python
from os 
from google.colab import userdata
#openai api_keyを環境変数に設定
os.environ["OPENAI_API_KEY"]=userdata.get("OPENAI_API_KEY")


# Google Driveと接続
from google.colab import drive
drive.mount('/content/drive')
from google.colab import drive
drive.mount('/content/drive')
```
**(3)データの用意**
今回の場合は"data"ディレクトリ配下にデータを用意する。今回はwikipediaから参照したテキストを .txtとして保存しました。
> root/
> 　├ data/
> 　│　└starwars_ep4.txt 
> 　│　└starwars_ep5.txt
> 　│　└starwars_ep6.txt
> 　└ rag.py  

**(4) ドキュメントのLoading**
```python
from llama_index.core import SimpleDirectoryReader
#documents配下のドキュメント読み込み
documents=SimpleDirectoryReader("data").load_data()
```

**(5) Indexing**
(3)でロードしたドキュメントからインデックスの作成を行う。
今回はVectoreStoreIndexを用いて、ドキュメントをチャンクという単位に分割し、チャンク毎にベクトルに変換し保存している。

```python
from llama_index.core import VectorStoreIndex
#インデックスの作成
index = VectorStoreIndex.from_documents(documents)
```

**(6) Querying**
クエリエンジンによってユーザーの入力するクエリと関連する情報を(4)で作成したインデックスより検索する。

```python
query_engine = index.as_query_engine()

print(query_engine.query("ジャバについて教えてください。"))
```
```text:実行結果
ジャバは犯罪王であり、惑星タトゥイーンに根城を構える存在です。彼は炭素冷凍されたハン・ソロを囚われており、賞金稼ぎやレイアらを捕らえるなど、悪事を働いています。ジャバは自身の宮殿を拠点とし、様々な犯罪行為を行っています
```
一部誤りがあるものの、テキストの内容から検索して文章を生成していることがわかる。

## 精度を上げるための調整方法

LlamaIndexにおいて精度を上げるため、Indexingでは主に
 ①Embeddingモデルの変更
 ② チャンクサイズの変更
があげられる。

①Embeddingモデルの変更はSettings.embed_modelから行う
```python
from llama_index.embedding.openai import OpneAIEmbedding
from llama_index.core import Settings

Settings.embed_mdodel=OpenAIEmbedding(model="text-embedding-ada-002")
```



②チャンクサイズの変更はSentenceSplitterで行う。
```python

from llama_index.core import Settings
from llama_index.core.node_parser import SentenceSplitter

#テキストスプリッターの設定
Settings.text_splitter = SentenceSplitter(
    separator=" ",
    chunk_size=1024,
    chunk_overlap=20,
    paragraph_separator="\n\n\n",
    secondary_chunking_regex="[^,.;。]+[,.;。]?",
    tokenizer=tiktoken.encoding_for_model("gpt-3.5-turbo").encode
)
```

>・separator : 単語の区切り文字 (default:" ")
> ・chunk_size : 各チャンクのトークン数 (default:1024)
>・chunk_overlap : 分割時の各チャンクのトークンの重なり (default:20)
>・paragraph_separator : 段落間の区切り文字 (default:"\n\n\n")
>・secondary_chunking_regex : 文の区切り文字の正規表現 (default:">[^,.;。]+[,.;。]?")
>・chunking_tokenizer_fn : テキストを文章に分割する関数 (default:nltk.sent_tokenize)
>・callback_manager : コールバックマネージャ
>・tokenizer : トークナイザー

 **Embbedingモデル比較**
今回はEmbeddingモデルをtext-embedding-ada-002"と"text-embedding-3-small"とした時のQuerying結果から上位10件を表示する。
```python
#model=　を変更する
Settings.embed_mdodel=OpenAIEmbedding(model="text-embedding-ada-002")

retriever=VectorIndexRetriever(
      index=index,
      similarity_top_k=10)

nodes=retriever.retrieve("ジャバについて日本語で教えてください。")
for n in nodes:
  print("Score: {}\nText: {}".format(n.get_score(),n.get_content().replace("\n",""+"...")))
  print("len :{}".format(len(n.get_content())))

```

```text: text-embedding-ada-002
Score: 0.7947975656350409
Text: ルークにジェダイに相応しい忍耐力があるかを試していたヨーダは、年齢を重ねすぎており、辛抱強さのないルークの指導をためらうが、オビ＝ワンの説得によりやっと修行を始める。しかしジェダイとしての意識が低いル
len :1383
Score: 0.7930118632174142
...前作より1年ほどの月日が流れた。ルーク・スカイウォーカーの使者として惑星タトゥイーンに根城を構える犯罪王ジャバ・ザ・ハットの宮殿へ遣わされた2体のドロイ
len :1771
Score: 0.7858297954692173
...ヤヴィンの戦いから3年。デス・スターを反乱同盟軍に破壊された銀河帝国軍の反撃は激烈を極めた[4]。反乱軍は帝国軍の攻撃によりヤヴィン第4衛星の基地から撤
len :1697
Score: 0.778847222481473
...そんな中、銀河帝国の支配に抵抗す
len :1695
Score: 0.7656718280522846
Text: その後、R2の解析により、レイアがここに監禁され、処刑を待っていることが分かり、ルークはソロとチューバッカを説得し救出に向かう。帝国軍の猛追を受けながらも、3人はレイアの救出に成功し、ファルコンへと急
len :1153
Score: 0.7576013788521982
...激戦の果てに、
len :875
```

```text:text-embedding-3-small
Score: 0.1274102592472926
Text: ルークにジェダイに相応しい忍耐力があるかを試していたヨーダは、年齢を重ねすぎており、辛抱強さのないルークの指導をためらうが、オビ＝ワンの説得によりやっと修行を始める。しかしジェダイとしての意識が低いル
len :1383
Score: 0.11958097504967838
...前作より1年ほどの月日が流れた。ルーク・スカイウォーカーの使者として惑星タトゥイーンに根城を構える犯罪王ジャバ・ザ・ハットの宮殿へ遣わされた2体のドロイ
len :1771
Score: 0.10637537265803704
...ヤヴィンの戦いから3年。デス・スターを反乱同盟軍に破壊された銀河帝国軍の反撃は激烈を極めた[4]。反乱軍は帝国軍の攻撃によりヤヴィン第4衛星の基地から撤
len :1697
Score: 0.08226115775394001
Text: その後、R2の解析により、レイアがここに監禁され、処刑を待っていることが分かり、ルークはソロとチューバッカを説得し救出に向かう。帝国軍の猛追を受けながらも、3人はレイアの救出に成功し、ファルコンへと急
len :1153
Score: 0.07853515867038942
...そんな中、銀河帝国の支配に抵抗す
len :1695
Score: 0.07484458060546172
...激戦の果てに、
len :875
```

今回は参照テキストが3つと少なかったため検索結果の順位に大きな差はでなかったが、スコア値が変化していることがわかる。

## まとめ

今回はLlamaIndexの使い方をメモした。
次は細かな設定を学んでいきたい。


## 参考文献

こちらのサイトを参考にさせていただきました
[LlamaIndex v0.10 クイックスタートガイド - Python版](https://note.com/npaka/n/nc20c2d2068ff)