オススメマンガ2023、異世界じゃないやつ

オススメマンガない?って言われたのでまとめ
年末にまとめたもの異世界多めだったので、異世界じゃないやつ。ほのぼの順。語彙力すくなめ。下に行くほど殺伐としています。というか一番したが最大限殺伐としている。

お兄ちゃんはおしまい

お兄ちゃんがおしまいになってしまった話

カノジョも彼女

勢い系ラブコメ
勢いで二股をやっていくやつ。

僕の心のヤバいやつ

僕ヤバ!僕ヤバ!

2.5次元の誘惑

スポ根コスプレマンガ

瓜を破る

表紙どおり、瓜を破ることについて悩んでいく話

ヒマチの嬢王

田舎のキャバクラを盛り上げていくやつ

トリリオンゲーム

「1兆ドルゲーム」という名前のとおり、大金を稼いでいく話です。

終の退魔師

超常現象を解決していく系。
絵がめちゃうまいです。

VS EVILの続編なので、こちらから読んでもいいです。読まなくてもそこまで問題ないです。

鬼ゴロシ

人がいっぱい死ぬマンガ

ChatGPTがGoogle検索を使いものにならなくする未来

いろいろ仕組み的にChatGPTというのはGoogle検索の代替以上の働きをするなぁと思っていたのだけど、それとは別にChatGPTによって検索が使い物にならなく未来が考えられるなぁと思った。

ChatGPTが検索よりもいいのは、そのものズバリな文書がなくても、その周辺から学んだ単語の関係をもとに、答えを構築してくれることです。
たとえば検索の場合は、日本語で書かれた文書が用意されていなければ、たとえ英語や中国語の文書があったとしても日本語での検索には引っかかりません。
けど、ChatGPTの場合は、英語や中国語の文書から学んだ単語の関係や、ほかの文書から学んだ英語と日本語の関係、日本語での単語の関係などから、日本語の回答を生成してくれます。

たとえばGluonという会社について日本語で説明してる記事はおそらくないと思うのですが、ちゃんと日本語で説明してくれます。本社はベルギーですが。。。

ほかにも、わざわざブログに書くまでもないというようなスキマ情報のようなものはいくつかのブログを渡りあるいて組み合わせて結論を出すことになったりするけど、ChatGPTは自動的に組み合わせて答えを返してくれます。

そうするとなかなか検索はつらいだろうし、そこに広告はさむのも難しくなっていくかもなぁと思っていたんだけど、そもそも検索対象がChatGPTによって無限に汚されていくと、検索という行為そのものが難しくなっていくのかもなぁと思いました。
ChatGPTとWordかなんかを組み合わせて無限にブログつくるやりかたを説明する人が出てきてますね。そうやって生成されたブログがネットを埋めていくわけです。

そんで、検索した結果でてくるのがChatGPTで生成された文章なんであれば、最初からChatGPTに問い合わせればよく。 残るのは検索スキルが高くお金を落としにくい、エンジニアのような人だけになったり。
Googleは検索から売上の57%くらいを得ているので、結構つらいということに。なので、Code Redとか出してるわけですけど。

もちろん、Gluonの例もあるように、ChatGPTが生成する文章はそもそもこんな感じではあるのですが。
nowokay.hatenablog.com

まあ、ChatGPT自体は利用料が$42とか割と高くなりそうなので、そのまま使われることはない気がするけど、オープンソースでChatGPTを作ろうというプロジェクトがあったり、似たようなものは出てくるだろうし、機能限定すればデータやコンピュータリソースもそこまでかからないかもしれないし、ちょっとこの流れは止まらんのだろうなという気がする。

フィルタでうまいこと管理できないかというのも、こういったAIは、AI自身がそれっぽいと思うものを生成しているので、単純には難しいのかもしれない。
こういうAI生成文書検出ツールで検出できるものなんだろうか?

そしてChatGPTもネットから学習データをとってくる以上は、学習データがChatGPT自身に汚染されていくのであった・・・

やっぱ、ホームページをカテゴリごとに登録できるようサイトを作って審査して載せることで、ゴミのような自動生成ブログを回避してインターネットの入り口すると儲かるのかもしれない。
やまびこみたいに返事が返ってくることを願って、「やっほー」みたいな感じの名前にするといいかな。

テストコードがコードの冗長化であることについて

テストコードがコードの冗長化であるという話に、腑に落ちないという指摘がちらほらあるので、どういうことかを解説してみます。
このエントリについてです。
テストというのは、ソースコードの冗長化だと思う - きしだのHatena

※ ここでの冗長化は、英語版Wikipediaでいう「Information redundancy, such as error detection and correction methods」のことですね。

まず、「これは冗長化だな」と納得してもらいやすそうな例として、間違いが絶対に許されないような計算をするシステムの実装を考えてみます。
このような場合に、同じ仕様を3つの組織に渡して独立に実装をしてもらい、計算が必要になったとき3つのモジュールを呼び出して多数決で結果を返すようにします。
このようにして正確さを担保するのは、冗長化と呼べると思います。

そして、これ多数決というけど、1つでも異なる結果を出してくるなら他の2つも怪しいのでは?その時点でエラーにするべきでは、そもそも3つも組織を確保できん、みたいな感じで、2つのモジュールを作って異なる結果がかえってきたらエラーにしよう、というふうになったとします。
これでも冗長化よね?

で、ちょっと実行時にやってるとパフォーマンスが悪いし同じ入力には同じ出力になるので、納品前に全入力について比較確認して、片方の実装だけ納品すればいいのでは、となったとします。
この時点でも冗長化ですね?

さて、もうひとつ同じように正確性が求められる計算を実装することになったけど、別実装つくるのは難しいし、確認用の実装はすでに動作検証済み実装がのった別のコンピュータでの値を使えばいいってなったとします。けど、そのコンピュータを直接使えないので、全入力に対する出力を用意しておいて、テーブルを作って比較するとします。テーブルをひく実装というのはよくあるので、これでも実装の冗長化といえますね。

そして、これそこまでは正確性もとめられなかったので、実際に使う範囲で確認とればいいんでは、とテーブルをすこし狭くします。
これでも実装の冗長化ですね。

こうやっていくと、じゃあどこまでどのように入力・出力値の対応をせばめていくと冗長化でなくなるかという閾値は恐らくみつけることができません。

で、実装の比較を納品前にやるのであれば、それはテストと呼ばれます。

単一の入力に対して単一の出力を定義するというのは、適用範囲の狭い最小の実装ということになります。
そしてこれが、ユニットテストで多く書かれているコードです。 効率的なテストというのは、冗長化をいかに最低限にして最大限の品質を担保できるかというものになるんじゃないでしょうか。

ところで、2つの実装を比較するテストというのも、アルゴリズムの最適化のときに行ったりしますね。最適化して処理が難しくバグの入り込みやすくなった実装に対して、自明でバグの入りにくい実装との処理を比較するというのは、それなりにあるんじゃないかと。

2023/1/30 追記 自動改札の例を教えてもらいました。改札機は時間の制約やメモリの制約からテーブル化をおこなっていて、ルール通りの計算を実装した検証版と比較を行うという事例。
https://www.publickey1.jp/blog/12/_1040_2.html

Javaのインストール2023年版

ちょっとJavaのインストールについて調べてみました2023年版。
Javaにはディストリビューションがたくさんあるので、目につくインストーラーをWindowsで全部ためしてみました。
初心者が勉強するためにJavaをインストールするというときにどれを使うのが手軽か確認するというのが主な目的です。

いろいろあるので、結論を先に書いておくと次のようになります。

いまPATHの設定が必要なJDKインストーラはない。Oracle以外ではJAVA_HOMEも設定できる。
一番楽なJavaインストール手順は、「Adoptium」で検索して最初のサイトを開いてダウンロードボタンを押して取得したインストーラを起動、JAVA_HOMEの設定を指定してインストール継続、という感じになります

2023年のJavaの勉強は「プロになるJava」で!

※検索ワードを示してますが、時期や検索エンジンによっては違う結果が出る可能性があります。
※ バージョン取得がきまぐれに-versionだったり--versionだったりしますが深い意味はありません。

Oracle JDK

まずはOracleJDK
いまはOracle以外にもJDKディストリビューションを出すようになったので、Oracle社のサイトからダウンロードできるJDKを「Oracle JDK」と呼ぶようになったというレトロニムで、Oracle自身がOracle JDKと呼んでるわけではないですね。
有償化が騒がれたりもしましたが、とりあえずデスクトップ用途では無償で使えることになって、騒ぎもおさまりました。
ただし、サーバーとして外部に公開して不特定が使うという場合には、個人でも有償ライセンスが必要なので注意してください。
サーバーとして外部公開する場合にはTemurinなどのディストリビューションを使うほうがいいです。ということで、学習の際も最初からTemurinなど別のディストリビューションを使うのが無難です。

JDK Download」で検索するとトップに出ます。「Java Download」だとJava 8 JREダウンロードサイトにたどりついてしまいます。

JRE」は元々はアプレットプラグインなどブラウザ連携を含んだものだったので、そういったブラウザ連携機能の廃止と同時に公式にはリリースされなくなっています。いまはJVM+APIだけのパッケージをJREとして配布するディストリビューションがあるという感じ。
自分のプログラムを配布するときは、jlinkコマンドで必要なモジュールだけをパッケージしたカスタムランタイムを一緒に配布したり、jpackageコマンドでそういうラインタイムを含んだ実行イメージやインストーラを作って配布という方針になっていますね。

Oracle JDKダウンロードサイトはこちら。
https://www.oracle.com/jp/java/technologies/downloads/

最新版と最新LTSが選択できます。OSやアーキテクチャは自分で選ぶ必要がありますね。

jdk-19_windows-x64_bin.msiをダウンロード。
今回はインストーラでやっていくのですが、msiとexeがある場合にはインストーラ専用形式であるmsiを選ぶほうがいいです。インストールとして許された操作以外を行わないことがある程度保証されるし、ダウンロードサイズも少し小さいので。

開くとインストールが始まります。特に設定が必要な項目はないのでNextを押していくと大丈夫です。

PATHは通ってるので、そのままjava -versionJavaのバージョンが出ます。JAVA_HOMEは設定されません。

Oracle JDKインストーラではPATHJava 15から設定されるようになっているので、いまだにPATHが必要と説明しているサイトは情報が古いです。

いまどれだけJAVA_HOMEの設定が必要なのか?っていう疑問はあるものの、インストーラJAVA_HOMEを設定できないのはOracle JDKだけなので、設定が必要なら自分で環境変数を設定するか、Oracle JDK以外のインストーラを使うかになりますね。

ちなみにjava -versionとやってjava versionが表示されるのはOracle JDKのみです。ほかはopenjdk versionなどJavaという用語を使わない表示になります。

あと、ほかのディストリビューションでも同じですが、macOSではArmかx64も正しく選ぶ必要があるので気をつけてください。Liberica JDKMicrosoft Build of OpenJDK 以外はarm版Windows用バイナリが用意されていません。

Temurin by Adoptium

特になにも条件がなくて単にJavaを動かしたいというときにおすすめしているのがAdoptiumのTemurinです。 「Adoptium」で検索すると出ます。「Temurin」でも大丈夫。

ただしくはEclipse Temurinですが、Eclipse IDE専用みたいな感じがしてしまうので、AdoptiumとかTemurinとか呼んでます。 そのためか、トップページにはEclipseと大きく書いてないですね。
https://adoptium.net/

アクセスした環境を判定して自動的に最適なLTSバイナリを選んでくれるので、目につくダウンロードボタンを押すだけでいいのも推しポイントです。

Java 19のようにLTSじゃないバージョンをインストールしたい場合には「Other platforms and versions」から飛んでバージョンなどを選択する必要がありますが、ここではOSなども設定する必要があります。デフォルトを設定してくれてればいいのに。

まあ、ここではLTSでいいという方針で行くので、トップページのダウンロードボタンを押してOpenJDK17U-jdk_x64_windows_hotspot_17.0.5_8.msiをダウンロード。

インストーラが起動します。

Nextボタンを押すとセットアップ画面が表示されて、ここでJAVA_HOMEなどの設定を指定することができます。「Will be installed...」をインストール時にJAVA_HOMEが設定されます。

PATHJAVA_HOMEも設定されてますね。

なので、いま一番楽なJavaインストール手順は、「Adoptium」で検索して最初のサイトを開いてダウンロードボタンを押して取得したインストーラを起動、JAVA_HOMEの設定を指定してインストール継続、という感じになりますね。

Amazon Corretto

Temurinの説明で「特になにも条件がなくて」と書きましたが、代表的な条件に「AWSAmazon Linux上で動かす」のようにクラウドサービスとそのサービス提供のLinuxで動かすというものがあります。その場合に、そのクラウド提供者がJDKディストリビューションを出しているのであれば、そのJDKを使うのが無難です。Oracle Cloudを使う場合にはOracle JDKのライセンスもついてたりしますね。
Amazonの場合はCorrettoというディストリビューションを出しているので、これを使うことも多いと思います。もちろん、他のディストリビューションでも動きますが、Corettoなどを使うとクラウドサービス用に動作検証や最適化がされている可能性がありますね。

「Corretto」で検索すると出てくるのだけど、rをふたつ書くのと最後のoも忘れて「corettt」としたり最後をeにして「corette」にするとぜんぜんひっかからなくなるので「Amazon Corretto」で検索しましょう。
https://aws.amazon.com/jp/corretto/

バージョンを選ぶとパッケージいちらんが表示されるので、その中から自力でみつける必要があります。
まあ、Corretto使う人はコマンドラインでインストールするやろみたいな考えなのだと思います。

ここからamazon-corretto-19.0.1.10.1-windows-x64.msiをダウンロード。

インストーラが起動します。

「Setup Environment」の中の「Setup Environment Variables」を選択しておくとPATHJAVA_HOMEが設定されます。デフォルトで設定されるので特にいじる必要もないです。

パスがとおってJAVA_HOMEも設定されています。

Azul Zulu

以前はOracle JDK以外のJDKといえばAzulという感じだったのですが他がどんどんディストリビューションを出してきたので影が薄くなりましたね・・・
「Zulu Download」で検索すると出てきます。
独自のJITを持っていたりするので面白いのですが、ここではそういった特別な機能を含まず無償で使えるZuluを使います。
https://www.azul.com/downloads/

スクロールすると「Download Now」というボタンがあるので、左端のZuluの列のものを押します。
バージョンやOSなどは自分で選ぶ必要があります。

JDKだけではなくJREJavaFX付のパッケージを選ぶこともできます。JREに実際に何が含まれているかは要確認

zulu19.30.11-ca-jdk19.0.1-win_x64.msiをダウンロード

インストーラが起動します。

JAVA_HOMEの設定を指定する画面がでます。デフォルトでは設定されないのでTemurinと同様に指定する必要があります。

インストールが終わるとPATHJAVA_HOMEも設定されました。

Liberica JDK

Liberica JDKはBellSoftの提供するJDKです。
「Liberica」で検索すると出てきますが、「Download for Free」を押して「Download」を押すと次の画面が出てきます。
https://bell-sw.com/pages/downloads/#/java-19-current

最初はJDK 8 LTSが選択されているので、所望のバージョンを改めて選択する必要があります。 そうするとあとは「Windows」のダウンロードボタンを押すとダウンロードが始まります。
bellsoft-jdk19.0.1+11-windows-amd64.msiをダウンロードします。

起動するとインストールが始まります。

環境変数などを選ぶ画面が出ますが、デフォルトですべて選択されているので特に変更する必要はありません。

PATHJAVA_HOMEも設定されています。

Microsoft Build of OpenJDK

名前のとおり、MicrosoftがビルドしたOpenJDKです。
Azureを使うと最初から入っているかも。
Microsoft JDK」で検索すると出てきます。
https://www.microsoft.com/openjdk

独自にエスケープ解析が実装されたりもしています。
https://devblogs.microsoft.com/java/microsoft-build-of-openjdk-october-2022-psu-release/

「ダウンロード」ボタンを押すとパッケージ一覧が出ます。LTSバージョンしかリリースしないようですね。
フィルターなどはないので、自分でファイルを選びます。

microsoft-jdk-17.0.5-windows-x64.msiをダウンロードします。

インストールが始まります。

JAVA_HOMEはデフォルトでは設定されないので、指定する必要があります。

PATHJAVA_HOMEも設定されました。

SapMachine

さて、インストーラー最後はSapMachineです。名前のとおりSAPがビルドしたOpenJDKです。
「sap jdk」などでも出ると思いますが「sapmachine」で検索するのが確実
https://sap.github.io/SapMachine/

スクロールすると環境が指定された状態になっているので「Download」ボタンを押すだけです。

sapmachine-jdk-19.0.1_windows-x64_bin.msiをダウンロード、なのですが、なにやら危険なファイル認定されてしまう。署名がされてないのかな。

「・・・」を押してメニューから「保存」を選びます。

そして「詳細表示」をクリック

「保持する」を押せば、ようやくファイルが使えるようになります。

これで「ファイルを開く」とインストーラが起動するのだけど

やっぱり保護されてしまうので、「詳細情報」を押す

で、「実行」を押すとようやくインストーラが起動します。

インストーラが起動

ライセンス確認のあと環境選択が出ますが、環境変数まわりは全部デフォルトでオフです。

「Optional Features」で「Entire feature will be installed on local hard drive」を選ぶと全部が設定されます。

インストールすると、PATHJAVA_HOMEも設定されます。

しかし、さすがにこれを人にはオススメできないですね。

OpenJDK

ちなみに、LTSではない最新版を試すのであれば、OpenJDKサイトからzipなどのアーカイブをダウンロードして展開するのが楽ですね。
最新版を試したい人ならパスの設定もわかるだろうし、そもそも試すだけならjavaコマンドのフルパス指定で問題ないので。開発もIDE使うだろうから、IDEにパスの設定を追加すればいいですからね。
https://jdk.java.net/19/

SDKMAN!

さて、いろんなバージョンを使うからインストーラなんか使ってられないという人もいるんじゃないかと思います。まあだいたいはzipダウンロードで展開しておけばどうにかなると思いますが、コマンドライン作業が多いのでパスもちゃんと切り替えたいということもあるでしょう。
そういうときにはSDKMAN!でインストールするのがいいですね。
https://sdkman.io/

MacLinuxでは、サイトに書いてあるとおりcurl -s "https://get.sdkman.io" | bashを実行すればインストールされます。

Windowsの場合、コマンドプロンプトでは使えないので、CygwinなどのUNIX系ターミナルが必要です。
https://www.cygwin.com/install.html

setup-x86_64.exeをダウンロードして起動します。このexeはあとでcygwinにパッケージをインストールするときにも必要になるので、どっかに置いておきましょう。

ダウンロードサイトを選べと出ます。どこでもいいと思うのだけど、jaistとかを選んでます。

パッケージを選ぶ画面が出ますが、zipとunzipがないとSDKMAN!のインストールに失敗し、curlがないと実行に失敗します。

特にcurlを忘れるとそれっぽく動くのに怒られるのでハマります。
https://nowokay.hatenablog.com/entry/2019/06/12/035324

まあ、Cygwinがインストールできたり、LinuxMacの人はターミナル開いてSDKMAN!のサイトに書いてあるスクリプトを動かしましょう。

ちゃんとインストールできると「Enjoy!」って出るのでエンジョイしましょう。

ターミナル再起動するか、メッセージにあるsource "/home/WDAGUtilityAccount/.sdkman/bin/sdkman-init.sh"を実行するとsdkコマンドが使えるようになります。WDAGUtilityAccount部分はログインアカウントになります。

sdk list javaとするとインストール可能なJava一覧が出てきます。

sdk install java 19.0.1-openとするとOpenJDKがインストールされます。この19.0.1-openの部分には先ほどの一覧のIdentifierの欄の文字列を指定します。

この時点でPATHは通ってますが、JAVA_HOMEは設定されていません。

sdk use java 19.0.1-openとするか、ターミナルを起動しなおすとJAVA_HOMEも設定されます。

LinuxMacはともかく、Windows環境でCygwinいれてまでSDKMAN!を使う必要があるかという話になりますが、結局SDKMAN!でJDKを切り替えたいのはコマンドラインでいろいろやる場合なので、JDK切り替えるツールが欲しいくらいコマンドラインでいろいろやるならCygwin使ってもいいんではないかなという気がします。

winget

ぶこめにあって素敵そうなので試そうと思ったんだけどサンドボックスで動かせなかったので保留

OpenJ9 / Semeru Runtime

IBMがJ9として開発していたJVMEclipseに寄贈されてオープンソースになっていました。愛してる人もいるだろうと思って追記
Adoptiumの前身、AdoptOpenJDKではJ9版もリリースしてたなぁと思ってAdoptium見てもHotSpot版しかなく、SDKMAN!にあったなぁと思って確認してもなく、そして、公式サイトをみてもダウンロードリンクがない、どこからもダウンロードできなさそうなプロダクトになっていました・・・
https://www.eclipse.org/openj9/

ということで、Semeru Runtimeという名前になっているようです。
「Semeru」だけだと火山が出てきたりするので「IBM Semeru」や「Semeru Download」で検索するのがいいですね。 OSは自分で選ぶ必要があります。Java 18はあるけどJava 19はない模様
https://developer.ibm.com/languages/java/semeru-runtimes/downloads/

ibm-semeru-open-jdk_x64_windows_17.0.5_8_openj9-0.35.0.msiをダウンロードします。

インストーラが起動しますね。

そして、JAVA_HOMEはデフォルトでは設定されないので指定が必要です。

PATHJAVA_HOMEも設定されていますね。そして、他のJDKとは違う表示が。

OpenJ9はJVMの名前。ふつうのJDKJVMはHotSpotと呼ばれます。
OMRはGraalVMのTruffleみたいな、スクリプト言語の開発のためのランタイムだと思います。 https://www.infoq.com/jp/news/2016/04/ibm-creates-omr/

JCLはなんだろう?Job Control Language?

Red Hat Build of OpenJDK

Red HatはOpenJDKの開発においてOracleについで多くのソースをコミットしている企業なので、当然ディストリビューションをもってます。
Red Hat Java」とかでぐぐると出てきますね。
しかし、ダウンロードにアカウントが必要なことや、Mac版がないことから、今回は見送り。
サーバーにRed HatLinuxを使ってたりアプリケーションサーバーにJBoss EAPを使ってたり、Red Hatにお世話になっている場合には使うことになると思います。

GraalVM

Oracleが開発しているもうひとつのJava、GraalVMもとりあげておきます。JITにGraalを使っていて、それを事前コンパイルに使うことでネイティブイメージを作ることで話題。 「GraalVM」で検索すれば出てきます。
https://www.graalvm.org/

ダウンロードはOSとバージョンの表になっててわかりやすいですが、インストーラはありません。

Scoop

これもWindowsのパッケージ管理として便利そうなのだけど、やっぱサンドボックスで動かせなかった・・・
https://scoop.sh/

ChatGPTのヤバさは、論理処理が必要と思ったことが確率処理でできるとわかったこと

ChatGPTのヤバいところは、論理処理が必要だと思っていたことが、じつは多数のデータを学習させた確率処理で解決可能だと示したことだと思います。

たとえば、このように正規表現にマッチする文字列を生成するには、特別に専用の論理処理が必要だと思っていました。

前のブログのときには特殊処理が必要だと考えてましたね。
ウソはウソと見抜ける人じゃないとChatGPTを使うのは難しい - きしだのHatena

けど、123_45678world.mdはマッチするのにマッチしないと言っているので、そのような誤りが入ることを考えると、どうも確率処理だけでやっているようです。

考えてみると、3層以上のニューラルネットであれば論理素子を再現できるので、ディープラーニングで論理処理を模倣することは可能なんですよね。
バックプロパゲーションでニューラルネットの学習 - きしだのHatena

そもそも論理は、多数のデータに見られる傾向を一般化してデータの意味にかかわらず適用できるようにした法則なので、もともとが確率処理でできるというのも無理はない。論理というのは「データがなくても導ける」というものなので、データがあるなら当然に導けるという話でもある。

ただ、この場合「1100円でバットとボールを買いました。バットはボールより1000円高かったのですが、それぞれいくらでしょう」に対してバットを1000円、ボールを100円と答えてしまうような間違いをするのと同じような間違いをします。

と書いたあとで試してみると、ほんとにそのような間違いをして笑いました。

この、バットとボールの問題は、次のGigazineの記事にあったものです。
直感的に「正しい」と判断した問題は実は間違っているかもしれない、認知機能を暴く「バットとボール問題」と対処法とは? - GIGAZINE

この記事では、思考のプロセスには「意識的に熟考することはほとんどなく、素早く実行する」と「熟考し、ゆっくりと実行する」の2過程があるとしています。前者の段階でバットを1000円のように間違うのは自然なので、そのあとでちゃんと考えろという話です。

前者はカーネマンのいうシステム1での処理で、後者がシステム2での処理なんだと思います。そして、ChatGPTをはじめディープラーニングでの処理はシステム1での処理を模したもので、だいたい正しそうでよくみると間違っているというような答えを出してくるのだと思います。

画像認識だとこういうのがありますね。これ、人間にも1頭の長い牛に見えてしまうので面白いのだと思います。しかし人間は、このような牛はいないとか頭としっぽが隠れることでこのように見えるということを考えて、「1頭ではない」という結論をえるわけです。このような常識を用意して前後関係を考えて結び付けていくというのを特別な処理ではなく汎用にやるのが難しい。

思えば、たとえば次のようなコードは構文の意味やメソッドの意味を考えることなく何をするかがわかります。

for (int i = 0; i < 10; ++i) {
  System.out.println("Hello!");
}

入門のとき以外で「初期処理でiに0を入れて繰り返しで++で1増やしてiが10より小さいときに繰り返すから・・」のように考えることはありませんね。
まあ、プログラミング言語というのは論理処理を感覚的に処理できるように工夫するものでもあるのでRubyなんかでは次のように書けるようになってたりしますね。

10.times do |i|
  p i
end

あと、ぼくの感覚処理にはバグがあるので、数字で始まる行は行番号に見えてしまう。

直観だけでは間違ってしまうことがあるし、たとえばCスタイルのfor文では、ちょっと条件や再計算処理が変化すると感覚的には追えずにひとつづつ確認する必要もあったり、最終的に論理処理が必要になります。
ChatGPTを見た人が「あとは論理的な確認を行うようにすれば完璧」などということがありますが、このような汎用の論理処理を論理コンピュータで実用的に計算することは計算量の爆発の問題があって不可能なので、量子コンピュータが実用化して普及するまでは正しい処理には人間が必要という状況は変わらないと思います。

2023/1/11 「予想どおりに不合理」のダン・アリエリーは「statistics is a nuiscance(統計は邪魔だ)」というような発言をする人だと思い出したので、少なくとも自身の実験は妥当とされているカーネマンの「ファスト&スロー」に差し替え。あと、大脳xxと書いてたところをシステム1, 2に変更

陽性と陰性の確率がそれぞれ50%の場合に最も検査の価値が高い

検査の結果には陽性と陰性しかないんだからそれぞれ50%、のような発言をみかけました。どうも文脈としては、検査をうけなければ100%陰性なので検査をうけないという話のようです。
けれども、もし本当に陽性と陰性がそれぞれ50%なのであれば、それは一番検査の価値が高いので、検査を受けるべきだと思います。

たとえば、極端な例として100%陽性になる検査があるとします。たとえば「血液があるかどうか」という検査は、生きている人間なら100%陽性になります。そういう検査は受けなくても結果がわかるので必要がないですね。逆に100%陰性になる検査も受ける必要がありません。

少し確率をいじって、10%が陽性で90%が陰性の場合はどうでしょうか。この場合、陽性がでれば価値が高いですが、多くの場合は陰性で、検査の結果で陰性だとしても「やっぱりね」となるので検査全体の価値はそこまで高くないです。
お年玉くじ付き年賀状をもらって「どうせはずれだから結果をみない」となるのも似たようなものです。この場合は当たる額も影響するのだけど。

で、確率をいじっていくと、50%が陽性で50%が陰性という場合が一番検査の価値が高くなります。どちらが出てもおかしくない、という場合ですね。
サッカーの試合でも、一方が圧倒的に強ければ(弱ければ)見なくても結果がわかるので試合を見ないですが、勝つか負けるかわからないとなると試合を見たくなります。

こういった場合に、その情報の価値を計算する 情報量 という考え方があります。
情報量というのは、簡単にいうと「びっくり度合い」です。その結果を見たときにどのくらいびっくりするかというものです。10%が陽性で90%が陰性の場合、陰性がでてもびっくりしないですが、陽性がでるとびっくりします。その検査の結果の陰性というのは「やっぱりね」なのでびっくりせず情報量が低いです。一方で陽性だと「なんと!」となって情報量が高いです。

計算としては、その結果がでる確率をpとすると情報量は log \frac{1}{p}となります。簡単にいえば、確率が低いほど情報量が高くなって0に近いとかなり大きい値になり1に近いと0になる計算です。

そして、結果すべての情報量の平均を平均情報量とかエントロピーといいます。
陽性の確率をp1、陰性の確率をp2にしたとき、平均情報量は p_1 log \frac{1}{p_1} + p_2 log \frac{1}{p_2}になります。
10%が陽性で90%が陰性であればp1=0.1, p2=0.9で、logの底を10としたとき、 \frac{1}{10} log 10 +  \frac{9}{10} log \frac{10}{9} \risingdotseq 0.14になります。50%が陽性で50%が陰性であれば \frac{1}{2} log 2 +  \frac{1}{2} log 2 = log 2 \risingdotseq 0.3となります。結果が半々のほうが平均情報量が高い、エントロピーが高いということになります。つまり検査の価値が高いということですね。

実際に検査体制がととのってないときには、検査の価値を高めるために、肺に影があるなどcoivd19の可能性が高い場合にはPCR検査をすることなく陽性とみなしたり、症状がない場合には検査をしないのような対策をして、検査をする場合の平均情報量が高くなるような工夫をしていましたね。

ところで、エントロピーが高いというのは全体的にびっくり度合いが高いということです。そして片付いた部屋はエントロピーが低く、散らかった部屋はエントロピーが高いということになっています。
これは、片付いた部屋で何かを手にとっても予測の範囲であることが多いのでびっくりしないですが、ちらかった部屋で何かを手にとったとき「おー、こんなものがある!」のようにびっくりすることが多いことからもわかりますね。
検査のエントロピーは高くするべきですが、部屋のエントロピーは低くしましょう。

Jakarta NoSQLを試す(動かない)

なんかJakarta NoSQLというのが出ていて素敵そうだったので試してみた。
サポートしているデータベースがこんな感じで、このあたりを統一的に扱えると便利そう。
http://www.jnosql.org/docs/supported_dbs.html

NoSQLといっても色々あって単一APIでは難しいんでは、というのはそのとおりで、Key Value、Column、Document、Graphの4分類で扱う感じっぽい。

このGetting Startedに従ってやってみる。
http://www.jnosql.org/docs/document.html

結果としては、動かなかった。
けど、準備としては必要なことをやってるはずなので、メモとして残しておいて、今後正式リリースされてちゃんと動きそうになったらまた試してみる。

MongoDBを起動する

今回はMongoDBを試すけど、Dockerで動かします。

> docker run --name testmongo -d -p 27017:27017 mongo

試しに普通にJavaから接続する

まずは普通にJavaコードで。
dependencyにこういうの追加します。

<dependency>
    <groupId>org.mongodb</groupId>
    <artifactId>mongodb-driver-sync</artifactId>
    <version>4.8.1</version>
</dependency>

データつっこむやつ。

import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import org.bson.Document;

public class TryInsert {

    public static void main(String[] args) {
        
        var uri = "mongodb://localhost";
        try (MongoClient client = MongoClients.create(uri)) {
            var db = client.getDatabase("mydb");
            var coll = db.getCollection("mycol");
            var doc = Document.parse("""
                           {
                             id: 123,
                             name: "kishida",
                             phone: ["234", "123"],
                           }
                           """);
            coll.insertOne(doc);
        }
    }
}

データ読み込むやつ

import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;

public class TryRead {
    public static void main(String[] args){
        var uri = "mongodb://localhost";
        try (MongoClient client = MongoClients.create(uri)) {
            var db = client.getDatabase("mydb");
            var coll = db.getCollection("mycol");
            var ite = coll.find();
            for (var d : ite) {
                System.out.println(d);
            }
        }
    }
}

成功。よかった。

Document{{_id=63b68bfe6414fd518da18292, id=123, name=kishida, phone=[234, 123]}}

Jakarta NoSQLのための依存ライブラリ

ということでJNoSQLを試す。 まずはMongoDBはdocumentデータベースなので、次のようなmappingを追加

<dependency>
    <groupId>org.eclipse.jnosql.mapping</groupId>
    <artifactId>mapping-document</artifactId>
    <version>1.0.0-b4</version>
</dependency>

そしてMongoDBのためのドライバを追加

<dependency>
    <groupId>org.eclipse.jnosql.communication</groupId>
    <artifactId>mongodb-driver</artifactId>
    <version>1.0.0-b4</version>
</dependency>

CDIが必要なのでWeld SEを入れる

<dependency>
    <groupId>org.jboss.weld.se</groupId>
    <artifactId>weld-se-shaded</artifactId>
    <version>5.1.0.Final</version>
</dependency>
<dependency>
    <groupId>org.jboss.weld.se</groupId>
    <artifactId>weld-se</artifactId>
    <version>2.4.8.Final</version>
</dependency>

MicroProfile Configが必要なので入れる

<dependency>
    <groupId>org.eclipse.microprofile.config</groupId>
    <artifactId>microprofile-config-api</artifactId>
    <version>3.0.2</version>
</dependency>

あと、標準アノテーションも入れておく

<dependency>
    <groupId>javax.annotation</groupId>
    <artifactId>javax.annotation-api</artifactId>
    <version>1.3.2</version>
</dependency>

試してみる

CDIを使うので、resources/META-INF/beans.xmlに空のファイルを作っておく。

マッピング用のクラス。JPAっぽいけど違うアノテーションを使う。共通にしてほしい。

import jakarta.nosql.mapping.Column;
import jakarta.nosql.mapping.Entity;
import jakarta.nosql.mapping.Id;
import java.util.List;

@Entity("Person")
public class Person {
    @Id("id")
    long id;
    
    @Column
    String name;
    
    @Column("phone")
    List<String> phones;
}

こういうDocumentCollectionManagerをとってくるためのProducerを用意。getting startedではクラス名にミスがありそう。

import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Produces;
import jakarta.nosql.Settings;
import jakarta.nosql.document.DocumentCollectionManager;
import jakarta.nosql.document.DocumentCollectionManagerFactory;
import jakarta.nosql.document.DocumentConfiguration;
import java.util.Map;
import org.eclipse.jnosql.communication.mongodb.document.MongoDBDocumentConfiguration;

@ApplicationScoped
public class DocumentCollectionManagerProducer {
    private static final String COLLECTION = "mycol";
    private DocumentConfiguration config;
    private DocumentCollectionManagerFactory manFact;
    
    @PostConstruct
    public void init() {
        config = new MongoDBDocumentConfiguration();
        Map<String, Object> settings = Map.of("mongodb-server-host-1", "localhost:27017");
        manFact = config.get(Settings.of(settings));
    }
    
    @Produces
    public DocumentCollectionManager getManager() {
        return manFact.get(COLLECTION);
    }
}

こんな感じらしい。

import jakarta.enterprise.inject.se.SeContainer;
import jakarta.enterprise.inject.se.SeContainerInitializer;
import jakarta.nosql.document.DocumentQuery;
import jakarta.nosql.mapping.document.DocumentTemplate;
import java.util.List;
import java.util.Optional;
import static jakarta.nosql.document.DocumentQuery.*;

public class App {
    public static void main(String[] args) {
        try (SeContainer cont = SeContainerInitializer.newInstance().initialize()) {
            Person p = new Person();
            p.name = "yamamoto";
            p.phones = List.of("321", "543");
            p.id = 918;
            
            var temp = cont.select(DocumentTemplate.class).get();
            temp.insert(p);
            
            DocumentQuery q = select().from("Person").where("id").eq(918).build();
            Optional<Person> result = temp.singleResult(q);
            System.out.println(result);
        }
    }
}

では動かしてみよう。

なんかDocumentTemplateがふたつあるといって怒られた・・・

Exception in thread "main" org.jboss.weld.exceptions.AmbiguousResolutionException: WELD-001335: Ambiguous dependencies for type DocumentTemplate with qualifiers 
 Possible dependencies: 
  - Managed Bean [class org.eclipse.jnosql.mapping.document.DefaultDocumentTemplate] with qualifiers [@Any @Default],
  - Managed Bean [class org.eclipse.jnosql.mapping.document.DefaultDocumentTemplateProducer$ProducerDocumentTemplate] with qualifiers [@Any @Default]

それでは、とリポジトリ使うやつを試す。 こんな感じにインタフェース作ると実装を勝手につくってくれる。

import jakarta.inject.Named;
import jakarta.nosql.mapping.Repository;
import java.util.List;
import java.util.stream.Stream;

public interface PersonRepository extends Repository<Person, Long> {
    List<Person> findByName(String name);
    Stream<Person> findByPhones(String phone);
}

そして動かす。

import jakarta.enterprise.inject.se.SeContainer;
import jakarta.enterprise.inject.se.SeContainerInitializer;
import java.util.List;
import org.eclipse.jnosql.mapping.DatabaseQualifier;

public class App {
    public static void main(String[] args) {
        try (SeContainer cont = SeContainerInitializer.newInstance().initialize()) {
            Person p = new Person();
            p.name = "yamamoto";
            p.phones = List.of("321", "543");
            p.id = 918;

            PersonRepository repo = cont.select(PersonRepository.class)
                    .select(DatabaseQualifier.ofDocument("mycol")).get();
            repo.save(p);
        }
    }
}

@DatabaseはQualifierではないと怒られた。

Exception in thread "main" org.jboss.weld.exceptions.IllegalArgumentException: WELD-000814: Annotation @jakarta.nosql.mapping.Database(value=DOCUMENT, provider="mycol") is not a qualifier

なんか設定でどうにかなる問題じゃなさそうなので、今日はあきらめ。
ちゃんと使えるなら使いやすそうなAPIなので、正式化は楽しみ。
しかしJava / Jakarta EEは難しい・・・