LLMのローカル実行を試す - GPT2
「機械学習エンジニアのためのTransformers」のサンプルコードをもとに以下のような実装をして、
import torch import pandas as pd import torch.nn.functional as F from transformers import AutoTokenizer, AutoModelForCausalLM device = "cuda" if torch.cuda.is_available() else "cpu" model_name = "gpt2-xl" tokenizer = AutoTokenizer.from_pretrained(model_name) model = AutoModelForCausalLM.from_pretrained(model_name).to(device)
生成
def generate(input_text, n_step=8, choices_per_step = 3): input_ids = tokenizer(input_text, return_tensors="pt").input_ids.to(device) iterations = [] with torch.no_grad(): for _ in range(n_step): iteration = dict() iteration["Input"] = tokenizer.decode(input_ids[0]) output= model(input_ids=input_ids) next_token_logits = output.logits[0, -1, :] next_token_probs = torch.softmax(next_token_logits, dim=-1) sorted_ids = torch.argsort(next_token_probs, dim=-1, descending=True) for choice_idx in range(choices_per_step): token_id = sorted_ids[choice_idx] token_prob = next_token_probs[token_id].cpu().numpy() token_choice = (f"{tokenizer.decode(token_id)} ({100 * token_prob:.2f}%)") iteration[f"Choice {choice_idx+1}"] = token_choice input_ids = torch.cat([input_ids, sorted_ids[None, 0, None]], dim=-1) iterations.append(iteration) print(pd.DataFrame(iterations).tail(1)["Input"].item()) ## -- 以下ビームサーチ用途として -- def log_probs_from_logits(logits, labels): logp = F.log_softmax(logits, dim=-1) logp_label = torch.gather(logp, 2, labels.unsqueeze(2)).squeeze(-1) return logp_label def sequence_logprob(model, labels, input_len=0): with torch.no_grad(): output= model(labels) log_probs = log_probs_from_logits(output.logits[:,:-1,:], labels[:,1:]) seq_log_prob = torch.sum(log_probs[:,input_len:]) return seq_log_prob.cpu().numpy() def log_probs_from_logits(logits, labels): logp = F.log_softmax(logits, dim=-1) logp_label = torch.gather(logp, 2, labels.unsqueeze(2)).squeeze(-1) return logp_label def sequence_logprob(model, labels, input_len=0): with torch.no_grad(): output= model(labels) log_probs = log_probs_from_logits(output.logits[:,:-1,:], labels[:,1:]) seq_log_prob = torch.sum(log_probs[:,input_len:]) return seq_log_prob.cpu().numpy() def generate_beam(input_text, max_length=150, num_beams = 3, num_return_sequences=3): input_ids = tokenizer(input_text, return_tensors="pt").input_ids.to(device) # サンプリング利用時: top_k = 固定閾値, top_p = 動的閾値 output_beam = model.generate(input_ids, num_beams=num_beams, num_return_sequences=num_return_sequences, no_repeat_ngram_size=2, early_stopping=True, do_sample=True, max_length=max_length, top_p=0.85) logp = sequence_logprob(model, output_beam, input_len=len(input_ids[0])) print(tokenizer.decode(output_beam[0])) print(f"\nlogprob: {logp:.2f}")
試しに、「ここに3本のバナナと、1個のリンゴがあります」の続きを生成してもらった。
まずトークンごとに生成していく最初のサンプル処理だが、これはイマイチである。
generate("""There are three bananas and one apple here.""", 50) => """'There are three bananas and one apple here. The banana is a banana. The apple is an apple. The banana is a banana. The apple is an apple. The banana is a banana. The apple is an apple. The banana is a banana. The apple is an apple. The' """
フレーズの繰り返しが生じてしまっている。
いっぽう、ビームサーチでデコードするやつはかなりそれらしい文字列を生成していた。
generate_beam("There are three bananas and one apple here.") =>"""There are three bananas and one apple here. The banana is a banana, the apple is an apple, and the banana and apple are the same banana." "I don't know what you're talking about," I said. "I've never heard of that before. What's the difference between the two?" ... "The difference is that one of the bananas has a hole in it," he said, "and the other one doesn't." "What do you mean by that?" I asked. He looked at me with a puzzled expression on his face. I thought he was going to say something, but he didn't say anything. Then he looked up at the sky and said: "Look at that. Look at""" (ここにバナナが 3 本とリンゴが 1 つあります。バナナはバナナ、リンゴはリンゴ、バナナとリンゴは同じバナナだ。」「何のことを言っているのか分かりません」と私は言いました。 「そんなこと聞いたことないです。この二つの違いは何ですか?」 ...「違いは、バナナの1本には穴があり、もう1本には穴がないことです。」と彼は言いました。 「それはどういう意味ですか?」私は尋ねた。彼は困惑した表情で私を見た。彼は何か言うだろうと思ったが、何も言わなかった。それから彼は空を見上げて言いました、「あれを見てください。見てください。」 - Google 翻訳)
村上春樹っぽさがちょっとある。「何のことを言っているのか分かりません」と私は言いました。 「そんなこと聞いたことないです。この二つの違いは何ですか?」
がかなりよかった。
このくらいの文字列生成タスクでだいたいCPUのみ(Apple M1 Max)で40秒くらいかかる。
LLMのローカル実行を試す(2) - phi 1.5
MSの公開しているphi-1.5を試してみる。ドキュメントによればこれは研究目的のモデルで、人間による調整をしておらず、学習元の偏りを受けやすく、有害なコンテンツを生成する脆弱性がある。特定目的に構成されてないので無関係のテキストを生成することがある。本番向けに利用されることは想定していない。はい。
README通りの構成。RuntimeError: "LayerNormKernelImpl" not implemented for 'Half'
と怒られたので、torch_dtype
を"auto"
ではなく torch.float32
へ変更している。あとデバイスは cuda
がノートpCに搭載されていないのでcpu
でお茶を濁しておく。
% cat phi-1_5.py import torch from transformers import AutoModelForCausalLM, AutoTokenizer device = "cuda" if torch.cuda.is_available() else "cpu" torch.set_default_device(device) model = AutoModelForCausalLM.from_pretrained( "microsoft/phi-1_5", trust_remote_code=True, torch_dtype=torch.float32) tokenizer = AutoTokenizer.from_pretrained( "microsoft/phi-1_5", trust_remote_code=True, torch_dtype=torch.float32) inputs = tokenizer('''```python def print_prime(n): """ Print all primes between 1 and n """''', return_tensors="pt", return_attention_mask=False) outputs = model.generate(**inputs, max_length=200) text = tokenizer.batch_decode(outputs)[0] print(text)
として、
% time python3 phi-1_5.py \```python def print_prime(n): """ Print all primes between 1 and n """ primes = [] for num in range(2, n+1): is_prime = True for i in range(2, int(num**0.5)+1): if num % i == 0: is_prime = False break if is_prime: primes.append(num) print(primes) print_prime(20) \``` ## Exercises 1. Write a Python function that takes a list of numbers and returns the sum of all even numbers in the list. \```python def sum_even(numbers): """ Returns the sum of all even numbers in the list """ return sum(num for num in numbers if python3 phi-1_5.py 41.46s user 4.14s system 104% cpu 43.802 total
40秒程度。エクササイズコーナーが生成されかけているのは面白い。
LLMのローカル実行を試す(3) - LINE
ここで公開されているやつ。
標準入力からテキストを読み込むようにした。
import sys import torch from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline, set_seed device = "cuda" if torch.cuda.is_available() else "cpu" model = AutoModelForCausalLM.from_pretrained( "line-corporation/japanese-large-lm-3.6b", torch_dtype=torch.float32).to(device) tokenizer = AutoTokenizer.from_pretrained( "line-corporation/japanese-large-lm-3.6b", use_fast=False) generator = pipeline("text-generation", model=model, tokenizer=tokenizer, device=device) set_seed(102) print("---") p = "" for prompt in sys.stdin: text = generator( prompt, max_length=300, pad_token_id=tokenizer.pad_token_id, num_return_sequences=1, no_repeat_ngram_size=2, do_sample=True, top_p=0.85 ) for t in text: text = t.get("generated_text").replace(p, "") print(text) print("---",)
絶妙な感じ。
--- ある日、爆弾が落ちてきて その爆弾処理班も、その班長も亡くなり、処理班長の息子も爆弾の中で。 「爆弾を あけてはいけません 爆弾の中身を あければ 爆弾は 消えて なくなります」 ある人曰く。 「この世で、一番 恐ろしいのは 爆弾です」 【写真;爆弾ゲーム】 【今日の歌】The Might --- 少子化対策として地方都市を盛り上げるアイディアはなんかないだろうか。 地方都市では、郊外に人口が流れ、中心市街地の地価が上がり続けたため、固定資産税や不動産売買税が高騰。中心市街地の家賃も上がるため、子育て中の主婦には負担になっている。 また、地方の高校は、統廃合で学校が減り、学力水準を保てなくなりつつある。大学に進学する人が少ないので、大学進学率が高い地域では、奨学金が利用できず、学力低下が生じている。 この
つらい。
--- こんにちは 私も40代前半です。 今までは20代後半から30前半のころにやっていたことを継続しています。(今はバレエ、ジム、英語など) 32歳で3人目を出産した時は、産後1年くらいは体調が戻らず、特に腰痛がひどく、また骨盤のゆがみも気になっていましたが、 4年間バレエを習いに行って、出産前よりもバレエが上手になりました。骨盤もしっかり矯正されました。産後2ヶ月からバレエに復帰し、3か月後には、発表会にも出ました。その後、半年くらいは体もきつかったですが、1年たった今、体はかなりやわらかくなり、腰痛もほとんどありません。姿勢も以前よりよくなったように思います。バレエの先生いわく、バレエを続けると、体が柔らかくなり体のバランスがよくなるとのこと。3年通いましたが体の変化を感じることができてうれしかったです!! 今は、月1回程度のクラスです、体力的にもついていけないので・・・。 体力が戻り、自分の時間が持てるようになったら、本格的に再開する予定です。体が変わるのが今から楽しみです。また、今までバレエをやっていなかった友達とも、今度一緒にやりたいね、と言っています。4人でグループレッスンをする予定です^^ バレエの後は、姿勢がよくなり背筋が伸びるので、とても気持ちがいいです! A: トピ主です アドバイスありがとうございます。私も2~3ヶ月休んでも大丈夫だと思います。今までずっと、何か習い事をするのには勇気が必要でした。でも、やってみないとわかりませんよね。子供の手が離れてきたので、習い事をするのにはいい時期なのかもしれないと思いました。
もとがLINEの学習したテキストだからかな...。内容の面白さは...。 max_lengthを大きめに設定するとだらだらと生成されがち。