GPT-2を使おうとしてエラーが出た話

# やりたかったこと
PythonでFlaskを使い、リクエストを送るとGPT-2の推論結果を返してくるWebAPIを作りたかった
<details><summary>問題が発生したコード</summary>

~~~python:gpt-2WebAPI.py
from transformers import T5Tokenizer, AutoModelForCausalLM
import torch
from flask import Flask, request

print('モデルを読み込みます。数十秒お待ちください。')

model_name = 'rinna/japanese-gpt2-medium'
# model_name = 'rinna/japanese-gpt-1b'
# model_name = 'rinna/japanese-gpt2-medium'
# model_name = 'rinna/japanese-gpt-neox-small'
tokenizer = T5Tokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(model_name)
print('モデル読み込み完了。')
app = Flask(__name__)
@app.route('/', methods=['GET'])
def process_data():
    print("読み込みました。処理を開始します。")
    # GPUが利用可能かどうかを確認
    if torch.cuda.is_available():
        device = torch.device("cuda")          
        print('GPUを使用しています')
    else:
        device = torch.device("cpu")
        print('CPUを使用しています')

    data = request.args.get('data')
    print(data + " => ")

    def generate_text(input_message):
        input_ids = tokenizer.encode(
            input_message,
            add_special_tokens=False,
            return_tensors="pt"
        )

        with torch.no_grad():
            output_sequences = model.generate(
                input_ids.to(model.device),
                max_length=150,
                min_length=10,
                temperature=0.7,
                top_k=40,
                top_p=0,
                no_repeat_ngram_size=2,
                do_sample=True,
                num_return_sequences=1,
                pad_token_id=tokenizer.pad_token_id,
                bos_token_id=tokenizer.bos_token_id,
                eos_token_id=tokenizer.eos_token_id
)

        output_sequence = output_sequences.tolist()[0]
        text = tokenizer.decode(
            output_sequence,
            clean_up_tokenization_spaces=True
            )
        input_ids_length = len(tokenizer.decode(
            input_ids[0],
            clean_up_tokenization_spaces=True
            ))
        total_text = (text[input_ids_length:])
        return total_text
    enddata = generate_text(data)
    print(enddata)
    return enddata

if __name__ == '__main__':
    app.run(port=3000)
~~~
</details>

これを実行すると、
~~~python:エラーコード
RuntimeError: probability tensor contains either `inf`, `nan` or element < 0
(IPアドレス) - - [21/May/2023 15:24:35] "GET /?data=人類の発展のために、私たちはAI技術を HTTP/1.1" 500 -
~~~
![スクリーンショット 2023-05-21 152657.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1751564/a14d534a-6086-2c2f-a788-3495328a09e2.png)
こんな感じでエラーが起きる。
これに何日も頭を悩まされた......😢

# 解決するためにやってみたこと

とりあえずエラーコードをDeepL翻訳にかけたら、「**ランタイムエラー：確率テンソルには `inf`, `nan` または要素 < 0 のいずれかが含まれます**」とのこと。ふむ.......(まったくわからん)
ChatGPTに頼ってみるか。
![スクリーンショット 2023-05-21 154728.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1751564/fbbefddd-b2f8-53dc-2fe1-cdadb3269d0f.png)
まとめると、
・計算するときにミスってるかも。
・入力した文字の前処理に問題があるかも。
・Pythonライブラリに問題あるかも。
ってことか。

とりまモデル変えてみるか。

~~~python:変更前
model_name = 'rinna/japanese-gpt2-medium'
~~~

~~~python:変更後
model_name = 'rinna/japanese-gpt-1b'
~~~
で、**失敗**します。とりあえずモデルには問題がないってことか～

次。モデルに設定してる生成パラメータを見直してみます。
~~~python:生成パラメータ設定
with torch.no_grad():
            output_sequences = model.generate(
                input_ids.to(model.device),
                max_length=150, #最大長
                min_length=10, #最小長
                temperature=0.7,
                top_k=40,
                top_p=0,
                no_repeat_ngram_size=2, #繰り返しペナルティ
                do_sample=True, #サンプリング
                num_return_sequences=1, #出力文の個数
                pad_token_id=tokenizer.pad_token_id,
                bos_token_id=tokenizer.bos_token_id,
                eos_token_id=tokenizer.eos_token_id
        )
~~~

パラメータが足りてないわけじゃないし、よくわかんないけどよさそうに見える。そういえば、top_kとtop_pって何だろ？**まあいっか。**(ここが今回の過ち)

...めんどくさいな。
......
.........
............

## よし。全部消そう。
とりあえずライブラリを消すことにしました。
~~~bat
pip freeze > uninstall.txt
pip uninstall -y -r uninstall.txt
~~~

んでライブラリを入れなおしてみると、**直らない。**......でしょうね。

# 結局どうやったら直ったのか
その後cudaのバージョン確認したり、ドライバ入れなおしたり、インターネットの海を漂ったり......何しても効果がなかった。

そこで、とりあえず余計な処理を省いてみることにした。

~~~python:ChatLite.py
from transformers import T5Tokenizer, AutoModelForCausalLM 
# 接頭辞（Prefix） 
PREFIX_TEXT = "人工知能を勉強することは、"
# トークナイザーとモデルの準備 
tokenizer = T5Tokenizer.from_pretrained("rinna/japanese-gpt2-medium") 
model = AutoModelForCausalLM.from_pretrained("rinna/japanese-gpt2-medium") 
# 推論 
input = tokenizer.encode(PREFIX_TEXT, return_tensors="pt") 
output = model.generate(input, do_sample=True, max_length=150, num_return_sequences=3) 
print(tokenizer.batch_decode(output))
~~~
>[【Python】GPT-2で日本語文章を自動で生成する](https://self-development.info/%E3%80%90python%E3%80%91gpt-2%E3%81%A7%E6%97%A5%E6%9C%AC%E8%AA%9E%E6%96%87%E7%AB%A0%E3%82%92%E8%87%AA%E5%8B%95%E3%81%A7%E7%94%9F%E6%88%90%E3%81%99%E3%82%8B/)よりプログラムをお借りしました。

そしたら
~~~python:出力
The attention mask and the pad token id were not set. As a consequence, you may observe unexpected behavior. Please pass your input's `attention_mask` to obtain reliable results.
Setting `pad_token_id` to `eos_token_id`:2 for open-end generation.
A decoder-only architecture is being used, but right-padding was detected! For correct generation results, please set `padding_side='left'` when initializing the tokenizer.
['人工知能を勉強することは、
aiの仕組み、プログラムの仕組みを理解するため に必修だからです。ですから、 人工知能の仕組みを学習する意味があります。 人工知能は、今から100万年ほど前に誕生したという説があります。 コンピュータに頼ることなく、 電気信号で機械を動かそうという考え方をプログラミングしたものです。 人工知能は、すでに100万年も前に存在していました。それが今になって、ようやく人工知能が人間にできる、ということが認識されるようになってきたのです。 このように、昔の人は、今と同じような言葉を使っていましたね。 昔の方が、意味が分かりませんでした。 ですから、今の人に比べて、理解できるはずがありませんね。', '人工知能を勉強することは、
自分の力で学習でき るようになるということです。 では、具体的にいくつ知ればいいか、いくつか例を挙げてご紹介してみたいと思います。 今回は実際に人工知能を使うときに役立つ、人工知能の活用 術をまとめたいと思います。 人工知能の活用シーンはいろいろありますが、今回は「機械学習」についてです。 機械学習とは、コンピューターが人間が行うよりも正確に学習できる 技術の総称です。 よく「ディープラーニング」という用語が使用されることが多いですが、ディープラーニングは、人間の脳と同じようにニューロンが大量に生まれる(ニューロン・ メモリ)ので、人工知能(ai)でも自然言語の分析機能は人間とほぼ同じ役割をすることになります。 人間の脳のような高度な知能を持っている', '人工知能を勉強することは、
何 かが進歩していることの「証拠」である。 人工知能研究の最前線を走っている今の東大生の考え方は、 まだまだ「研究の最先端にいる大学生たちの世界」とはほど遠いものです。 人工知能というのは、これまで人間がやっていた仕事を代わりにやる技術なので、 今後は「その仕事」を、人工知能に代替される可能性があります。 例えば、人工知能の分野において 、すでに最先端の人たちが、 人工知能の技術が、人間ではなく、「機械同士」に奪われる可能性について書いている が、人間の能力を超えることは、あまりに急激な変化で、もはや 不可能なのかもしれません。 人工知能、コンピューター、ロボットを、人類の発展に活用することは、 人類の知的創造']
~~~
**動いた**。つまりライブラリに問題はないってことか。
次に、パラメータを問題が起きたのと同じにしてみる。

~~~python:ChatLite.py
from transformers import T5Tokenizer, AutoModelForCausalLM 
# 接頭辞（Prefix） 
PREFIX_TEXT = "人工知能を勉強することは、"
# トークナイザーとモデルの準備 
tokenizer = T5Tokenizer.from_pretrained("rinna/japanese-gpt2-medium") 
model = AutoModelForCausalLM.from_pretrained("rinna/japanese-gpt2-medium") 
# 推論 
input = tokenizer.encode(PREFIX_TEXT, return_tensors="pt") 
output = model.generate(input_ids.to(model.device),
                max_length=150,
                min_length=10,
                temperature=0.7,
                top_k=40,
                top_p=0,
                no_repeat_ngram_size=2,
                do_sample=True,
                num_return_sequences=1,
                pad_token_id=tokenizer.pad_token_id,
                bos_token_id=tokenizer.bos_token_id,
                eos_token_id=tokenizer.eos_token_id) 
print(tokenizer.batch_decode(output))
~~~
動かすと、
~~~python:エラーコード
RuntimeError: probability tensor contains either `inf`, `nan` or element < 0
~~~
同じエラーが出た！
問題はパラメータか。多分......top_kかtop_pのどっちかだな。

ってことで調べると、
　・top_kはその数字分の文章を保持する
　・top_pは上位top_p%の単語から選択する
ってことらしい。
......ここやん。

~~~python:生成パラメータ設定
top_k=40
top_p=0
~~~
top_kはよさそうだけど、top_pがおかしい。
上位0%って0単語じゃん。
ってことで修正。

~~~python:生成パラメータ設定
top_k=40
top_p=0.95
~~~

実行すると、
~~~none:出力
読み込みました。処理を開始します。
GPUを使用しています
人類の発展のために、私たちはAI技術を =>
応用し、より良い製品を創造しています。 私たちの仕事は、お客様のニーズに合った製品を生み出すことから始まります。そのためには、「開発」「設計」「製造」「品質管理」「納期管理」という工程があり、製品が完成するまで、この工程を何度も繰り返しながら、完成まで製品を作り上げていきます 当社はおかげさまで創業55年を迎え、これまで様々な製品を生み出してきました。当社の製品は「おもしろい」「かっこいい」といった感覚を大切にして、開発され、多くのお取引先様からの高い評価をいただいています 今後も当社では「よりよい製品づくり」をモットーに、さまざまな開発に取り組
~~~
**動いたよぉぉぉぉぉぉぉぉぉ**
# まとめ
今回のエラーの原因は「top_pの設定ミス」だった。
GPTのパラメータ設定はミスるとエラーがでて動かなくなる。
しっかりとサイトを見て、解説を見ながらやりましょう。
また、今回学んだこととしては、動く最小のプログラムを作って**問題の原因の切り分け**をしていくと解決につながる。ということ。
横着しないで冷静に考えましょう。