ChatGPT APIのトークン上限に合わせたテキストファイル分割



## はじめに
ChatGPT APIのトークン上限に合わせたテキストファイル分割に関しての備忘録です。
初心者です😅
間違えてる部分が多々あると思います。
もし見つけた場合、ツッコミいただけると助かります🙇
<br>
## 🦁<INS>**結論**</INS>🦁
翻訳などをする際にChatGPT APIはレスポンスのトークン数の<INS>**上限は4096**</INS>（2024年2月時点）のため翻訳などを行う際にテキストデータを送付して行う場合には、ChatGPTのレスポンストークンの上限の数値に合わせてテキストデータを分割した後、リクエストして回答を得るようにする必要がある。

:::note info
押さえておくべき点
- 英語と日本語ではトークン数は同じではない（日本語の方が多くなる）
- 翻訳の内容によってもトークン数は左右される。
- テキストデータ上限は「余裕を持ったトークン数」を設定すると良い。
- ChatGPTのレスポンスがちゃんと返ってくるかを事前にChatGPTのチャットにて作成した命令文（リクエストにあるmessage）を送ってみて確認する
:::  
***
```python:sample.py
from transformers import GPT2Tokenizer

def chatgpt_text_split_save():
    file_path = "分割したいファイルパス"
    block_size = 3000  # トークン上限を設定
    separat_part = '\n'  # 文章の区切りになる部分
    output_dir = "分割したテキストデータを入れるディレクトリ"

    # GPT2トークナイザー
    tokenizer = GPT2Tokenizer.from_pretrained('gpt2')

    with open(file_path, 'r', encoding='utf-8') as f:
        text = f.read()

    # テキストをトークン化し、トークン数をカウント
    all_tokens = tokenizer.encode(text, add_special_tokens=True)
    count = len(all_tokens)
    
    # 全テキストのトークン数
    print(f"Token count: {count}")

    # 一つ一つを区切ってるパーツによって分解
    lines = text.split(separat_part)

    # 各ボックスの初期化
    blocks = []
    block = ''
    token_count = 0

    for line in lines:
        # lineにあるトークンをカウント
        t = len(tokenizer.encode(line))

        # トークンカウントが０以上（初期ではない）かつblock_sizeを超えてしまったら新しくリストに追加する
        if token_count > 0 and block_size < (token_count + t):
            blocks.append(block)
            token_count = 0
            block = ''
        
        # トークンカウントを追加
        token_count += t

        # ブロックに区切れた部分を追加する（区切られたもの自体も追加）
        block += line + separat_part

    # 最後のblock_sizeに達してないものも追加（ここまでのものは全てblocksに追加されてるためない）
    if block:
        blocks.append(block)


        if not os.path.exists(output_dir):
            os.makedirs(output_dir)

        for i, block in enumerate(blocks):
            output_text = os.path.join(output_dir, f'text_block_{i+1}.txt')
            with open(output_text, 'w', encoding='utf-8') as output_file:
                output_file.write(block)

            print(f"{output_text} 保存完了")
```
***  

## GPT-2トークナイザーにてトークンをカウント
```python:sample.py
    tokenizer = GPT2Tokenizer.from_pretrained('gpt2')

    with open(file_path, 'r', encoding='utf-8') as f:
        text = f.read()

    # テキストをトークン化し、トークン数をカウント
    all_tokens = tokenizer.encode(text, add_special_tokens=True)
    count = len(all_tokens)
    
    # 全テキストのトークン数
    print(f"Token count: {count}")
```
トークナイザーとはトークンをカウントしてくれる。
トークナイザーは様々あり、ChatGPTによるトークン数を測るなら、GPT-2トークナイザーが正確性が高いみたい。（tiktokenと比較した際に200ぐらいの誤差がありました→GPT-2の方が多い）

***  

## トークンの分割定義
```python:sample.py
if token_count > 0 and block_size < (token_count + t):
    blocks.append(block)
    token_count = 0
    block = ''
```
### if token_count > 0 and block_size < (token_count + t):
block_sizeを超えた場合にblocksに格納して「token_count」と「block」を初期化してlineのtoken_count（t）とblockにblock_size = 3000に達するまで格納。
（separat_partによって区切られた文章が格納される）
※<INS>**3000トークンのバーを超えたら次の箱が用意されるイメージ。**</INS>

***  

## 余ったblockを追加
```python:sample.py
if block:
    blocks.append(block)
```  
完成したblock（上限に達した）は「blocks」に格納される。
そのことから最後に余った部分が「block」入ってる。
もしそれがあった場合は「blocks」に格納。
要は最後の部分を追加する。

***  

## まとめ😺
トークン、エンコーディングはまだまだ奥が深い…
勉強あるのみ。
***



## 参考にしたサイト📗
[OpenAIのAPIを利用する際のトークン数を見積もる](https://zenn.dev/kurehajime/scraps/92b57b67a14f8a)

***

[OpenAI GPTにおいて、モデルごとにトークン量が変動するのはなぜ？](https://eng-blog.iij.ad.jp/archives/18815)