空の配列に対するmaxは何を返すか

ちょっと前に「配列中のすべての要素が条件を満たすかどうか判別する関数で、空の配列はTrueを返すべきかFalseを返すべきか」のような話が話題になってました。
まあこれは「Trueを返す」が答えなわけですが、では「配列中の最大値を返す関数で空の配列の場合は何を返すか」が気になりました。

「配列中のすべての要素が条件を満たすかどうか判別する関数」について言えば、簡単に言えばこんな感じ。
まず、配列のすべての要素が偶数であるかどうか判別する関数を考えます。

void main() {
  int[] data = { 23, 44, 12, 98, 5 };
  System.out.println(allEven(data));
}
boolean allEven(int[] data) {
  for (int n : data) {
    if (n % 2 != 0) return false;
  }
  return true;
}

これを配列の要素が0のときにfalseを返そうとすると、わざわざそのための条件を入れる必要があります。
ということで、trueを返すのが自然。

ただ、それだけだと「単に実装の都合なのに、Trueであるべきっていうのは納得いかん!」となる気がします。 実際には、「すべての要素が条件を満たすかどうか」という処理の性質としてTrueを返すのが自然なので、それについて説明してみます。

その前に、数値を全部合計する処理を考えてみましょう。

void main() {
  int[] data = { 23, 44, 12, 98, 5 };
  var result = 0;
  for (int n : data) {
    result = result + n;
  }
  System.out.println(result);
}

ちなみに今回のコードは、Java 21の試用機能として、そのまま実行できるようになる予定。
まだ試用機能なので--enable-previewが必要ですが、来年のJava 23くらいには正式機能になるはず。

で、他に、数値を倍にしたリストを得る処理を考えてみます。

import java.util.*;
import java.stream.*;
void main() {
  int[] data = { 23, 44, 12, 98, 5 };
  var result = new ArrayList<Integer>();
  for (int n : data) {
    result = Stream.concat(result.stream(), Stream.of(n * 2)).toList();
  }
  System.out.println(result);
}

この2つの処理は、同じようなパターンになってますね。
ということで、allEvenも同じようなパターンで書いてみましょう。

void main() {
  int[] data = { 23, 44, 12, 98, 5 };
  var result = true;
  for (int n : data) {
    result = result & (n % 2 == 0);
  }
  System.out.println(result);
}

これらの処理で、違っている部分をまとめると次のようになります。

処理 初期値 計算処理
sum 0 +
toList 空リスト concat
allEven true &

ここでどういう値が初期値になっているか見てみると、計算処理を行っても相手に影響を与えない値になっています。
つまり、a+0も0+aもaになり、aに空リストを連結しても 空リストにaを連結してもaになり、そして、a & TrueもTrue & aもaになります。 こういった、演算しても相手に影響を与えないような値を、その演算の単位元といいます。
ということで、「すべての要素が条件を満たすかどうかという処理の演算は &演算なので、空配列のときの値は &演算の単位元であるTrueを返すのが適切」となります。
ちなみに「場合による」と言ってる人が叩かれたりしてましたが、場合によるときには要件が「すべての要素が条件を満たすかどうか」ではなく、例えば「カートが出荷可能か(カートの中のすべての商品が出荷可能であり、しかしカートが空であれば出荷可能ではない」のような別のビジネス要件になるはず、という感じですね。

さて、ここで本題の「空の配列に対するmaxは何を返すか」です。
これを先ほどと同じパターンで書いてみると次のようになります。

void main() {
  int[] data = { 23, 44, 12, 98, 5 };
  var result = Integer.MIN_VALUE;
  for (int n : data) {
    result = Math.max(result, n);
  }
  System.out.println(result);
}

となると、maxの単位元は-∞ってことになるんでは、となります。 というところで、Javaのライブラリを見てみます。CollectionsmaxNoSuchElementException、StreamのmaxOptionalを返します。

jshell> Collections.max(List.<Integer>of())
|  例外java.util.NoSuchElementException
|        at ImmutableCollections$ListItr.next (ImmutableCollections.java:375)
|        at Collections.max (Collections.java:674)
|        at (#2:1)

jshell> IntStream.of().max()
$3 ==> OptionalInt.empty

これは、「オブジェクトを考えると単位元になる最小値が一意に決まるとは限らない」ということで、数値以外を考慮するならemptyや例外になる、となっています。JavaScriptのmaxは-Infinityを返すらしい。
つまり、これは「場合による」が正しいということに。

こういった話は数学だ、と言われてもあまり学校で習った数学ではなさそう。 じゃあ何かとなるのだけど、これは「離散数学」という分野を勉強するとよさそう。離散数学の中でも、代数型とか群・環・体とかいうやつ。
で、「離散数学」で勉強しようとすると難しそうな本が多かったりするので、この本くらいがおすすめ。

ということに近い話をWEB+DB PRESS Vol.135に書いていたところだったので、少し詳しく書いてみました。
「ループと状態遷移、ついでにJava 21」として、プログラムの処理中の状態遷移についてまとめています。コードサンプルにJava 21を使っているので、非常にコンパクトに完動するコードサンプルを多く載せれていて、これはいいなとなっています。興味ある人は見てみてください。

ChatGPTの登場でWeb3への興味が急速にしぼんでいる

MidjourneyやStable Diffusionのような画像生成AIが出たりChatGPTが出たりで、Web3で騒いでいたところがAIに移行した感じあります。

Google Trendsだと、生成AIは完全にWeb3を抜いています。

メタバースも抜いたところ。

ChatGPTは圧倒的です

実際にニュースどうなってるか見てみると、Web3に関するニュースは4月以降は出ていないです。

5月に一件あるのは、AI規制の話をWeb3の人が話したというもので、Web3の記事ではないです。5月はあと3日残ってるけど、そんなになさそうな気が。
3月には「Web3本格採用」のような記事が多いことを考えると、突然死のようにも見えます。

朝日新聞INTERNET WatchITmediaエンタープライズ、日経xTech IT, エレキでのWeb3記事件数は次のような感じ。

去年6月に盛り上がって漸減傾向ではあったものの、急に消えたという感じですね。

Web3はGAFAMの中央集権から逃れるというのが第一命題でもあったので、GFMによる集中的なデータや計算能力の活用で台頭してきた生成AIとは折り合いも付きにくいようにも思います。
(GAFAMのAAがLLMでは存在感がないのも面白いところではある)

追記: 5/29 メタバースが気になる人も多いようですが、こんな記事が。これ「事業化」の成功が8%であって、事業が成功かどうかではないことに注意。
メタバース事業、9割が事業化に“失敗” - ITmedia NEWS

追記: 5/30 NFTというキーワードだとどうだろうと思ったら、1月から3月まで6件程度の記事があったけど、4月に1件、そして今日ANAのNFTで1件という感じです。4月の1件はNFTが終わっているという記事でした。

Stability AIのチャットスクリプトを利用してRinnaのチャットモデルとお話する(追記あり)

Rinna社がチャットにも対応した日本語言語モデルをリリースしてました。
Rinnaの新しい3Bモデルを試してみる - きしだのHatena

そうするとちゃんとチャットとしてやりとりしたいですね。

ところで、Stable DiffusionのStability AIが言語モデルStableLMをリリースしています。
Stability AI 言語モデル「StableLM Suite」の第一弾をリリース - (英語Stability AI

で、チャットモデルもあって、Gradioを使ったWeb UIを公開しています。
Stablelm Tuned Alpha Chat - a Hugging Face Space by stabilityai

スクリプトはここのapp.pyです。
https://huggingface.co/spaces/stabilityai/stablelm-tuned-alpha-chat/tree/main

ローカルで動かすとこんな感じ。賢い。VRAMは14.5GBくらい使います。

このスクリプトで読み込んでるモデルをRinnaモデルにします。

model_name = "rinna/japanese-gpt-neox-3.6b-instruction-sft"
m = AutoModelForCausalLM.from_pretrained(
    model_name, torch_dtype=torch.float16).cuda()
tok = AutoTokenizer.from_pretrained(model_name, use_fast = False)

あと、メッセージ生成部分をRinnaモデルにあわせて変更します。

    curr_system_message = ""
    messages = curr_system_message + \
        "<NL>".join(["<NL>".join(["ユーザー: "+item[0], "システム: "+item[1]])
                for item in history])

しかし、こんな感じ。会話にならぬ・・・

返答は速いです。

※ 追記 14:54
どうやら入力が渡っていなかった模様。

tokenizerのencodeを呼び出すようにして。

    model_inputs = tok.encode(messages, return_tensors="pt", add_special_tokens=False).to("cuda")

dictでは渡さず

    generate_kwargs = dict(
#        model_inputs,

Thread呼び出しでgenerateに渡すようにすると会話できました。

    t = Thread(target=m.generate, args=[model_inputs], kwargs=generate_kwargs)

会話のコンテキストも引き継いでいますね。

コードはこちら。
https://gist.github.com/kishida/bca7a8c55ec64ee43d9caf12e4a1cf08

東北大学NLPグループの言語モデルをとりあえず動かす

東北大NLPグループからも、日本語言語モデルが新たに公開されていました。
既存のモデルのアップデートです。

今回新たに公開されたのは次の4つのモデルです。

  • cl-tohoku/bert-base-japanese-v3
  • cl-tohoku/bert-base-japanese-char-v3
  • cl-tohoku/bert-large-japanese-v2
  • cl-tohoku/bert-large-japanese-char-v2

charがついているのは文字ごとのトークナイズで、ついていないものはUnidic 2.1.2ベースのトークナイズ、かな。

しかし、CyberAgentのモデルと同じコードだと こんな感じになってしまいました。

動かすためにはfugashiとunidic_liteが必要なのでpip installしておく必要があります。unidic_liteはcharがついたモデルでは不要な気がするけど試してません。

GPUメモリは1.4GB程度の消費なので、CUDAが動けばだいたいの環境で動きそう。

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from colorama import Fore, Back, Style, init
# need fugashi, unidic_lite
init(autoreset=True)

# model_name = "cl-tohoku/bert-base-japanese-v3"
# model_name = "cl-tohoku/bert-base-japanese-char-v3"
model_name = "cl-tohoku/bert-large-japanese-char-v2"
# model_name = "cl-tohoku/bert-large-japanese-v2"

print ("model:" + model_name)
model = AutoModelForCausalLM.from_pretrained(model_name, is_decoder=True).to("cuda")
tokenizer = AutoTokenizer.from_pretrained(model_name)

# prompt = "AIによって私達の暮らしは、"
prompt = "アメリカの首都はワシントン。日本の首都は"
# prompt = "吾輩は猫で"
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
with torch.no_grad():
    tokens = model.generate(
        **inputs,
        max_new_tokens=64,
        do_sample=True,
        temperature=0.7,
        pad_token_id=tokenizer.pad_token_id,
    )
    
output = tokenizer.decode(tokens[0], skip_special_tokens=True).replace(" ", "")
print(f"{Fore.YELLOW}{prompt}{Fore.WHITE}{output[len(prompt):]}")

Rinnaの新しい3Bモデルを試してみる

CyberAgentのモデルを試したところですが、Rinna社も新しいモデルを出しました。
rinna、日本語に特化した36億パラメータのGPT言語モデルを公開|rinna株式会社のプレスリリース

ここですね。
https://huggingface.co/rinna/japanese-gpt-neox-3.6b

ということで試します。

あれーいろいろ試したときはちゃんと東京、いや大阪って答えて面白かったのだけど、ちゃんと答えてくれない・・・

GPUメモリは14.8GB使っています。

torch_dtype=torch.float16を付けても8GBちょうどくらいのGPUメモリを使うので、8GB VRAMだとつらそうです。12GB VRAMであれば大丈夫そう。

そして、今回はチャット向けにトレーニングされたモデルも用意されています。
https://huggingface.co/rinna/japanese-gpt-neox-3.6b-instruction-sft

ユーザーをユーザー、AIからの応答をシステムとして改行を<NL>にして与えればいいようなので、次のような変換を行います。

prompt = f"ユーザー:{prompt}<NL>システム:"

ちゃんと東京って答えてくれます。

あと、Rinnaモデルはなんか面白い返答を返しがち。

オススメ観光地は答えてくれませんでした。

けど、これまでのチャットモデルは3Bくらいだと続きのユーザー入力システム応答を3往復分くらい生成したり、チャットボットとしての動きが不安定だったけど、このモデルはそこはちゃんとしてそう。

オススメ観光地は答えてくれませんが、ユーザーとシステムのやりとりの枠組みが破綻するものは見かけませんでした。

チャット用スクリプトはこんな感じ
プロンプト生成以外でCyberAgentモデルのときと大きく違うのはTokenizerにuse_fast = Falseがついているくらい。

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from colorama import Fore, Back, Style, init

init(autoreset=True)

model_name = "rinna/japanese-gpt-neox-3.6b-instruction-sft"

print ("model:" + model_name)
# model = AutoModelForCausalLM.from_pretrained(model_name).to("cpu")
# model = AutoModelForCausalLM.from_pretrained(model_name, device_map='auto', torch_dtype=torch.float16)
model = AutoModelForCausalLM.from_pretrained(model_name, device_map='auto')
tokenizer = AutoTokenizer.from_pretrained(model_name, use_fast = False)

# prompt = "AIによって私達の暮らしは?"
# prompt = "日本の首都はどこですか?"
# prompt = "吾輩は猫といえば?"
prompt = "日本のおすすめの観光地を教えてください。"

prompt = f"ユーザー:{prompt}<NL>システム:"
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
with torch.no_grad():
    tokens = model.generate(
        **inputs,
        max_new_tokens=100,
        do_sample=True,
        temperature=0.8,
        pad_token_id=tokenizer.pad_token_id,
    )
prompt = prompt.replace("<NL>", "\n")
output = tokenizer.decode(tokens[0], skip_special_tokens=True).replace("<NL>", "\n")
print(f"{Fore.YELLOW}{prompt}{Fore.WHITE}{output[len(prompt):]}")

CyberAgentの日本語言語モデルを試してみる

CyberAgentが日本語LLMを公開していたので、とりあえず動かしてみました。
サイバーエージェント、最大68億パラメータの日本語LLM(大規模言語モデル)を一般公開 ―オープンなデータで学習した商用利用可能なモデルを提供― | 株式会社サイバーエージェント

モデルは次のように6サイズ提供されています。

※ Rinna社も同時に新しいモデルを出したので試しています。
Rinnaの新しい3Bモデルを試してみる - きしだのHatena

open-calm-small(160M)

まずはopen-calm-small。160Mパラメータです。

このあたりは動作確認用なので、内容は気にしない。

GPUメモリは1.3GBくらいの消費です。

open-calm-medium(400M)

次にopen-calm-medium。400Mパラメータです。

このへんも細かいことは気にしないけど、なんかまともなことを書きだしている。

GPUメモリは1.8GBくらいの消費。

open-calm-large(830M)

opem-calm-large。830Mパラメータです。
まともなことを言うようになってきていますね。

GPUメモリは2.7GBくらいの消費。

open-calm-1B(1.4B)

open-calm-1B。1.4Bパラメータです。

LLMは中途半端に大きいと京都を首都にしたがる問題w

東京といったり、広島といったり。

GPUメモリは3.7GBくらい。

open-calm-3B(2.7B)

open-calm-3B。2.7Bパラメータです。
東京と言ってるけど、なんか哲学的な話をする傾向。

繰り返しても東京と答えます。

GPUメモリは6.5GBくらい。8GBメモリのGPUでいけますね。

open-calm-7B(6.8B)

一番大きいopen-calm-7Bです。6.8Bパラメータ。

ちゃんとしたこと言ってる。

一応全部東京と答えているけど、アメリカの首都をニューヨークといったり、それより大きい首都を持つ国に東京ディズニーランドより小さいバチカンが入っていたり、正しくはないですね。

あと、毎回checkpointを読み込むのが気になる。

GPUメモリは14.1GBくらい。ということで16GBメモリを積んだGPUが必要です。

CPUで動かすと、読み込み時に最大で34GBくらいのメモリを消費したので、48GB程度のメモリを積んでないと読み込みがつらそうだけど、一旦読み込んでしまえば32GBメモリで動かせると思います。

返答はまともな気がする。CPU動作にしては返答が速い。

RuntimeError: "LayerNormKernelImpl" not implemented for 'Half'というエラーが出る場合はtorch_dtype=torch.float16を消す必要があります。

メモリ消費量(5/19追記)

パラメータ数とGPUメモリ消費量の関係はこうなっています。パラメータ数の2倍よりちょっと多い、という感じですね。

まとめ

実際は用途にあわせてFine Tuneして使うのだと思うけど、そのための基盤モデルが出たのはいいですね。
あと、7Bに関してはGPUメモリ16GBにおさまるように調整しているように思います。RTX 4080でも16GBだし、7月くらいにRTX 4060 tiの16GB版が出るという噂があるのでそれで試しやすくなることを狙っているんじゃないでしょうか。

現状ではRTX 3060の12GB版が一番コストパフォーマンスがいいですが足りない。けどautoが指定されているのでCPUとGPUで振り分けてくれるかもしれません。

うちではA4000使ってます。「遅いけどメモリたくさん」なモデルで電源などの要求が低いので、16GB GPUとしてRTX4080に比べて組み込みやすい。

今回のスクリプトは公式のサンプルに色付けなどちょっと手を加えたものです。float16の指定がされているので大きいモデルでも少ないメモリで動くのだと思います。

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from colorama import Fore, Back, Style, init

init(autoreset=True)

# model_name = "cyberagent/open-calm-small"
# model_name = "cyberagent/open-calm-medium"
# model_name = "cyberagent/open-calm-large"
model_name = "cyberagent/open-calm-1b"
# model_name = "cyberagent/open-calm-3b"
# model_name = "cyberagent/open-calm-7b"
print ("model:" + model_name)
model = AutoModelForCausalLM.from_pretrained(model_name, device_map="auto", torch_dtype=torch.float16)
tokenizer = AutoTokenizer.from_pretrained(model_name)

# prompt = "AIによって私達の暮らしは、"
prompt = "アメリカの首都はワシントン。日本の首都は"
# prompt = "吾輩は猫で"
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
with torch.no_grad():
    tokens = model.generate(
        **inputs,
        max_new_tokens=64,
        do_sample=True,
        temperature=0.7,
        pad_token_id=tokenizer.pad_token_id,
    )
    
output = tokenizer.decode(tokens[0], skip_special_tokens=True)
print(f"{Fore.YELLOW}{prompt}{Fore.WHITE}{output[len(prompt):]}")

エンジニアのためのChatGPTプラグイン3選+1

前のブログでも紹介したのだけど、ChatGPTプラグインのローリングアウトが始まって使えるようになっていて、結局みんな使うのはこの3つくらいかなーとなったので、まとめておきます。

前のブログはこれ。
Bardも世の中のサービスぜんぶGoogle製と思ってるらしい - きしだのHatena

同時に使えるのは3つまでのようだけど、他のプラグインアメリカの不動産情報など日本からは使いづらかったり、作ってみたレベルだったりなので、結局この3つに落ち着くかなーという気がします。

WebPilot

これは手放せなくなります。Web記事を読み込んでくれるプラグイン
ChatGPTには「この記事を要約して」しか入力しなくなりそう。

このエントリを要約してもらっています。
大規模言語モデルの「脳波」が反応してる部分を壊すとどうなるか試した - きしだのHatena

※ 追記 15:21 ぼくのところには来てないけど標準機能にWeb Browsingがあるので、それが使えるようになると不要かも

※ 追記 2023/5/16 標準機能のWeb Browsingが使えるようになったけど、使い分けが必要かも

プラグインとは排他的なので、Webの内容から図やグラフを作るということはできなさそう。

また、現状では指定URLの読み込みがうまくいっていないです

AskYourPDF

WebPilotはPDFを読んでくれないので、PDFを見たければこれ。
ChatGPTには「このPDFを要約して」しか入力しなくなりそう。

言語モデルのスケーリング則についての論文を要約してもらっています。
https://arxiv.org/pdf/2001.08361.pdf

※追記 2023/5/22 Web Pilot + AskYourPDFの代わりにLink Readerがあれば十分かも。

Show Me(Diagram It)

作図してくれるプラグイン
※ 追記 10:18 一覧に出てこなくなってるようです
※ 追記 22:25 出てくるようになりました。名前がShow Meに変わってます。

たとえばこんな感じです。

シーケンス図もいけます。

状態遷移も。

威力を発揮するのが、WebPilotなどと組み合わさったときです。

このエントリを図解してもらっています。
プログラマが勉強すること - きしだのHatena

こんな図が出ています。

図はMermaid.liveで編集できます。

ChatWithVideo or Video Insights(5/22追加)

WebとPDFがLink Readerだけでいけそうなので、ひとつ追加しておきます。
日本語には対応していませんが、YouTubeの要約ができます。カンファレンスなんかの動画の要約によさそう。

※ 追記 5/23 Video Insightsだと日本語もいけるという話

Wolfram

利用頻度が高いのは上記3つになると思いますが、グラフや数式処理をする人にはWolframも便利そうです。

検索もしてくれそう。

ポケモンも出せる

ニンフィアニンファがいないという指摘があったけど、これはWolframの問題ぽい。

かなりいろいろなことができるので、Wolframのサイトで確認してください。
https://www.wolfram.com/wolfram-plugin-chatgpt/