LED通信事業プロジェクト エンジニアブログ

ChatGPTでF#

記事更新日 2023年4月25日


はじめに

久々のF#のネタです。前回の最後で次回は「モーターのネタをやる」と締めたのですが、その間にF#に対してもプログラミング全体に対しても巨大なインパクトを与えるChatGPTが広まってしまいました。さすがに、ChatGPTを無視してプログラミングの話題を続けるわけには行かないと思い、今回は急遽F#とChatGPTを絡めた話題を書くことにしました。

で、昨今ますます話題のChatGPTですが、まずなぜプログラミングにとってChatGPTが無視できないかの説明からします。それはChatGPTにはプログラミングコードを生成してくれる機能というか能力があるからです。何か作ってほしいコードを言えば、ChatGPTが自動的に(しかも説明付きで)コードを生成してくれます。例えば、F#でPingを打つというコードをChatGPTに生成して貰います。一応説明しておくと、PingとはIPネットワークで、ある機器が接続しているかとか、遅延がどれぐらいかとかを確認するためによく使われるコマンドのことです。それを実行した結果は下の通りです。

自分: F#で、Pingを打ってその結果を返すコードを教えて。

ChatGPT: F#では、System.Net.NetworkInformation名前空間に含まれるPingクラスを使用して、Pingを送信して応答を取得することができます。 以下は、Pingを送信して応答を取得する簡単なF#コードの例です。

open System.Net.NetworkInformation let pingHost (hostNameOrAddress: string) = let pingSender = new Ping() let options = new PingOptions() options.DontFragment <- true let buffer = [|0x41; 0x42; 0x43; 0x44; 0x45; 0x46; 0x47; 0x48; 0x49; 0x4A; 0x4B; 0x4C; 0x4D; 0x4E; 0x4F; 0x50; 0x51; 0x52; 0x53; 0x54; 0x55; 0x56; 0x57; 0x58; 0x59; 0x5A|] let timeout = 5000 let reply = pingSender.Send(hostNameOrAddress, timeout, buffer, options) if reply.Status = IPStatus.Success then printfn "Ping to %s succeeded, response time: %dms" hostNameOrAddress reply.RoundtripTime else printfn "Ping to %s failed with status %s" hostNameOrAddress reply.Status.ToString() pingHost "example.com"

このコードでは、Pingクラスを使用して、指定されたホスト名またはIPアドレスにPingを送信します。(以下略)

おー、すごい!説明は更に続いていくんですが、本文とは関係無いので省略。で、このコードを実行すればF#でPingが打てちゃうんです(当たり前ですが、"example.com"は各自変えて下さい)。

ChatGPTへお願いすればコードを自動で書いてくれる。ということは・・・ あれ?これなら誰でもプログラミングできるってこと?もしかして、これで今日からプログラマーが要らなくなるのか?とか考えちゃいますね。実際、プログラマー業界では、天地がひっくり返るぐらいの大騒動となっているようですし。

ただ、同じ内容をプロの(人間の)プログラマーに書かせたら、非同期動作にするとか、結果の出し方を変えるとか、実際に使う際のインプット、アウトプットまで考慮してコードを書くはずです。そうしないと使いにくい。だから、ChatGPTが出力したコードがいうもそのまま全て使えるかといえば微妙。今のところ、プログラマーによる最終的な判断が必要でしょう。ですから、すべてのプログラマーが「今すぐ」要らなくなるかと言えばそうではありません。しかし、必要となる(プロの)プログラマーの数は日を追う毎に減っていくでしょう。それだから、大問題なわけです。

その一方で私のように本職ではないプログラマーにとっては、これは夢のような機能。これまで何か知らない機能を作ろうとするとき、これまでは一生懸命検索して、それっぽいサイトを読んで、改変して、試してみて・・・ ということの繰り返し。忘れていたコードを思い出す程度なら、ちょっと検索すればすぐですけど、あまり使われない方法だと一発で解決するってことはほとんどなくて、正解を探すまで相当な時間がかかったりします。が、ChatGPTなら、指示さえ正しければ、おおよそ間違いないコード、少なくともそれっぽいコードが一発で出てきます。使っていない人には是非使ってほしい、そう思います。まあ、今やプログラミングしててChatGPTを使わない人は、(情報漏洩対策で使えないという人以外)もはや少数だと思いますが・・・

簡単なLLM AIの説明

これから説明することの凄さを理解していただくために、素人ではありますが、ちょっとだけ今時のAIの説明をさせて頂きます。よく理解されている方は飛ばしてください。

ChatGPT※1は、ご存じの通りLLM(Large Language Model)技術を用いたAIです。LLMのAIとこれまでのAIとの違いは「生成」にあります。これまでのAI、これをディープラーニングAIとしますが、このディープラーニングAIは学習によって精度を上げるものでした。ディープラーニングとさらにそれ以前のAIを比較します。ディープラーニング以前のAIは、人間が「どこの特徴を見ろ(例えば蝶と蛾を見分けたいなら触覚を見ろとか)」とか「こういった式を使って統計を取れ」とか事前に人間が指定したアルゴリズムに従って学習をしていました。それに対し、ディープラーニングAIは、「対象物を巨大な関数を使って計算することで特定の傾向を掴む」のです。ですから、人間の関与は「特徴点を与える」」のではなく、「関数のパラメータを与える」ところにあります。その巨大な関数がどのようにできているのかというと、相互作用する関数(パーセプロトン)を組み合わせて大きな関数(ネットワーク)を作るのです。その様子が、人間の神経ネットワークに似ているため「ニューラル(神経の)」ネットワークと呼ばれます。

まあちょっと難しいので、できるだけシンプルに言い換えると、「神経細胞に模した巨大関数」に学習させると、人為的に特徴を指定して学習させるより「利口」になったといったところでしょうか?不思議ですよね。ともかく、ディープラーニングAIによって、画像認識等に画期的な性能向上が認められ、今や様々な機能に使われているのはご存じの通りです。

そんな中、AIにある一つの技術が発表されます。一般にディープラーニングが広まる少し前の2017年。Googleの研究者が、トランスフォーマーというディープラーニングの画期的アルゴリズムを発表します。具体的な技術は割愛しますが、トランスフォーマーによって自然言語処理を”高速に・高精度に”行う事ができるようになるというものでした。その成果はもう少し後に現れます。例えば、近年のGoogleやDeepLなどの機械翻訳の精度が飛躍的に上がったのは、このトランスフォーマーのおかげと言われています。

そして、トランスフォーマーを使って半ば「力業」で精度を上げようとしたのがChatGPTを初めとしたLLMです。トランスフォーマーを使ったディープラーニングAIは、学習量を増やし、パラメータを増やせば精度が上がっていることが分かっていました。それならばできる限り大量なデータを学習させてしまえばものすごいAIが作れるのではないか?ということを現実にやったのがLLMとなります。それを言うのは簡単ですが、実現するためには恐ろしいほどの計算力(サーバー)が必要とか色々ある※2わけですが、本題じゃないのでそこには触れません。

そして、LLMの成果は予想以上のものでした。なんとLLMのAIはこれまでと違って回答を「つくれる」ようになったのです。これまでのディープラーニングAIは「判断」しかできませんでした。写真を見て「これは犬」とか「これは信号」とか判断するわけです。翻訳も意味合いはだいたい同じです。与えられた情報に対するリアクションしかありません。しかし、LLMはあらかじめ与えられた選択肢から答えを出すのではなく、その場で回答をつくり出すことができます。例えば、どこかからの転載だけでなく、0からオリジナルを作り出すことができます。前述の様にコードを書けと言えば、既存のコピーを表示するのでなく自分で書くことができます。さらに、「どう思いますか?」といった意思に関する質問に答えることができます。新製品のアイデアを出せと言われれば、その「アイデア」を作り出すことができます。LLM AIはそのような「つくれる」という性能を持つため、従来のAIと区別するためにGenerative AI(生成AI)と呼ばれることがあります。例えば、最近はChatGPTだけでなく、指示に従ってオリジナルの画像を自動生成するGenerative AIも盛んです。イラストレーター瀕死で話題になったりしますが、これもありとあらゆる画像を大量に学習させた成果です。そして、LLM AIの出現より、AIは「判断」から「生成」にステージが変わり**いよいよAIの時代がきた!**と騒がれているのが、今日現在までの話です。

とにかく、LLM AIの自ら「つくれる」能力により、これまでできなかった様々なことができるようになりました。さらに、その能力はF#にもちょっぴり恩恵を与えてくれるのです。

F#への福音

これまでこのブログを読んでくださっている読者の皆様ならご存じかと思いますが、残念ながらF#はマイナーな言語です。マイクロソフト開発・運営の言語なので開発環境には恵まれていますが、主力言語であるC#は当然のこと、開発終了のVB.NETと比べてもコミュニティは無きに等しいぐらい小さい・・・ そのためWeb上に情報はほとんどなく、何を調べても検索に引っかかりません。結局F#のプログラマーはC#の情報を参照しつつ、F#へ脳内変換するという作業を行う羽目になります。なんとF#を書くためにはC#の知識が必要となるのです。F#は.NET言語なのでルールやできることは基本C#と同じですから、C#のコードを見てF#に直すことはそこまで難しいことではないのですが・・・ そんなことやるなら、最初からC#で書けばいいじゃん!って話ですよね。いやいや、F#で書いた方が便利でバグも少ないんですよ、分かって下さい。

そんな遠回りしなくてもChatGPTに直接コードを作って貰えばいいじゃない?という方もいるでしょう。けど、やりたいことを直接日本語でピンポイントでAIに説明するって結構難しいんですよね。そういうのできる人をプロンプターっていうんですか?まあ、やってみれば分かりますが意外と的確に質問できない。先の例のようにPingを打ちたいとかいう誰にでも分かる明確な設定ならいいですが、通常は全体のコードの中からどうやって動かしているのかまで見ないとわからない場合も多い。そんなとき、結局Google検索してそれっぽいコードの全体を見つけてそれを参考にした方が早いんですよ。けど、F#だとコードを検索しても出てこないないから、結局C#の例を見つけて自分でF#へ変換。もとのC#が変な書き方を使っていると面倒ですし、結局言語的に遠回りしないと同じように書けなかったりして、やっぱりC#からF#への変換は結構面倒。

しかし、そんな日陰者の我々F#プログラマーに素晴らしい味方が!それがChatGPT。なんと、ChatGPTはC#で書かれたコードをいとも簡単にF#に変換しちゃうのです。変換と言えば.NETに詳しい方ならC#とVB.NETがコート変換できることは知っていると思います。なぜなら、両言語は最初から相互変換できるように作られていましたし、かつてはマイクロソフト自身が純正C#<=>VB変換ツールを出していました。しかし、F#は両言語と違い関数型言語です。.NETという共通点はあるとは言え文法もお作法も全く違う。何なら変数の考え方すら全く違う。だから、C#からF#、VBからF#みたいな変換ツールはこれまで存在していませんでした。しかし、ChatGPTは変換してしまうんです、C#からF#へ!

証拠として一つ例を載せておきます。マイクロソフト公式ページにあるC#の「ラズパイでLED点灯させる」コードです。これをF#へ変換して貰います。

自分: 次のコードをF#に変換してください。

using System; using System.Device.Gpio; using System.Threading; Console.WriteLine("Blinking LED. Press Ctrl+C to end."); int pin = 18; using var controller = new GpioController(); controller.OpenPin(pin, PinMode.Output); bool ledOn = true; while (true) { controller.Write(pin, ((ledOn) ? PinValue.High : PinValue.Low)); Thread.Sleep(1000); ledOn = !ledOn; } // 筆者注釈:マイクロソフト公式ページ(https://learn.microsoft.com/ja-jp/dotnet/iot/tutorials/blink-led)から引用

ChatGPT: 次のようにF#に変換できます。

open System open System.Device.Gpio open System.Threading printfn "Blinking LED. Press Ctrl+C to end." let pin = 18 use controller = new GpioController() controller.OpenPin(pin, PinMode.Output) let mutable ledOn = true while true do controller.Write(pin, if ledOn then PinValue.High else PinValue.Low) Thread.Sleep(1000) ledOn <- not ledOn

変数の宣言方法や、コードの末尾に・・・ (以後説明続くので省略)

うん、なんか動きそうなコードを生成してきました。正直、ここまで変換してくれれば、多少引っかかるコードでも問題ないです。なぜなら、それでも自力で書くよりずっと早いから。ChatGPTのすごさ、分かっていただきました?もう、F#である事はハンデではありません。コードの検索はC#で行って、良い例があればそのコードをChatGPTにF#に変換させれば良いのです。すばらしい。

しかし、いとも簡単にF#へ変換していますが、いったいどうやっているんでしょうね?

なぜF#へ変換できるのか?

これまでF#に変換できるツールなどなかったのに、ChatGPTはなんで変換できるのか?どこにも載ってないのでChatGPT本人に聞いてみましょう。

自分: なぜ、あなたはC#やVB.NETのコードをF#のコードへ変換できるのですか?

ChatGPT: 私は自然言語処理技術を用いて、与えられたC#やVB.NETのコードを解析し、その意味や構造を理解することができます。その後、同等の機能を持つF#のコードを生成することができます。これは、私が機械学習や自然言語処理に関する高度なアルゴリズムとルールに基づいて、与えられたタスクを自動化するために設計されたAI技術の一種であるためです。

要は、英語や日本語に対して使う自然言語処理技術を、そのままプログラミング言語にも当てはめたってことでしょう。人間が話す言語においても自分で答えが”つくれる”のであれば、まあプログラミングコードも自分で作れるっていうのも理解できます。プログラミング言語なんて普通の言語に比べて簡単ですからね。でも、ここである一つの疑問が湧くのです。 例えば、日本語をLLMで学習する場合、それはそれはもの凄い量のテキストを読ませているはずなんですよ。プログラミングコードだってメジャーなC#やJavaScriptとかなら、そりゃ山のように存在します。しかし、F#は私が知る限りLLMが成立するほどのコンテンツが存在していません。どうしているんだろう?というわけで、それもChatGPTに聞いてみました。

自分: 一般にAIは学習量が多いほど正解率は高くなります。そしてF#はC#と比べると圧倒的に情報が少なく、あなたが学習する機会は少なくなります。だとすれば、あなたが生成するF#のコードは、あなたが生成するC#のコードに比べて間違いが多くなると思うのですが、どのように考えれば良いですか?

ChatGPT: AIによる自動コード生成において、学習量が多いほど正解率は高くなるというのは一般的な傾向ですが、それでも学習に使用するデータの質や種類、学習アルゴリズムなどが影響を与えます。F#の情報量が少ないという点は事実ですが、F#は静的型付け言語であり、関数型プログラミングの手法を用いることができるなど、C#にはない特徴を持っています。そのため、F#においても適切な学習アルゴリズムを用いたり、適切なデータを用意することで、高精度なコード生成が可能となります。 ただし、C#とF#は異なる言語であり、それぞれの言語に特有の構文や機能が存在するため、C#からF#への変換においては、適切な知識と技術が必要となります。間違いが多くなるということはあり得ますが、正確性を確保するためには、適切な手法やツールを用いて変換を行うことが重要です。

ちょっと、最後の段落が意図が伝わっていない感じがしますが、恐らく言いたいことをまとめれば「F#は超厳密な型付けをもつ関数型のルールに厳しい言語※3」なので、AI側としても正確な言語が書きやすいってことですね。つまりは、人間がコーディングしたときにバグが出にくいように厳しいルール下で作られたF#という言語は、実はAIに取ってもバグが出にくい言語だった、という事でもあるんです。まさにF#のメリットがAIにも効果があったということなのでしょう。

F#に対する学習量がどれだけかを知るよしもありませんが、ともかくC#やVB.NETからF#への変換はしてくれます。多少、「いけてない」変換もありますが、変換機能があるのとないのでは雲泥の差。皆さんも、この変換機能を使ってF#にチャレンジしてみてください。まあ、F#の場合、そもそも「学習コストがかなり高い」というハードルを越える必要があるのですが・・・

まとめ

おそらく、今回の話はF#だけではないはずです。他のマイナー言語でも、おそらく同じ状況のはず。と言うことは、これまでライブラリやサンプルがなくて困っていたマイナー言語でも、ChatGPTが媒介になってくれることにより「情報がない」というハードルを越えて、その言語の真の実力(例えばF#であればエラーの発生しにくさとか)が発揮されるかも知れません。

ChatGPTがプログラミングに多大な影響を与えるのは間違いありません。ニュースやSNSではコードの自動生成ばかりが取り上げられますが、こういったコードの変換機能も目立ちませんが素晴らしい機能です。果たしてChatGPTはマイナー言語に変革を与えることができるのか? 今後は、そういった視点でAIを見ていくのも面白いと思います。

追記

ラズパイへの.NETインストール

前回の記事で、Raspberry Pi(ラズパイ)用の.NETのインストール方法が変わりました、と説明しました。例えば、ラズパイへ長期サポート(Long Term Support: LTS)である.NET6をインストールする場合、次のコマンドを打てば良いと書きました。

curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --channel LTS

しかし、実はこのコマンドだと動かない場合があるという事が分かりました。それは、ラズパイのOSが32bitの場合です。今まではOSが32bitの場合でも上のコマンドで問題なくインストールされていたのですが、このブログ書いている現在(2023年4月)だと、このコマンドだと32bitのOSでも64bit版の.NETがインストールされることが判明しました。これは非常にたちが悪くて、32bit版のOSに64bit版が問題なく(エラーなく)インストールされてしまい、かつインストール後にどっちを入れたかどうかを見極めるのが難しいのです(ファイル名やフォルダ名からは判別できない)。

おそらく、インストールのスクリプト(dotnet-install.sh)が32bitのOSと64bitのOSを見極められていないのだと思います。なので、インストール時にインストールすべきOS(アーキテクチャ)を明示的に指定する必要があります。具体的には、先ほどのコマンドに--architecture armと追加してください。コマンドは以下の通りです。

curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --channel LTS --architecture arm

こちらのコマンドを入れれば32bit用.NET6がインストールされるはずです。最後のarmは32bit版を表します(64bit版はarm64)。そのうち修正されるかもしれませんが、dotnetはインストールされているはずなのにdotnetコマンドが動かないようでしたら、64bitがインストールされていることを疑って、こちらのコマンドを使用しインストールし直してください。

最後に、改めてパス通し含めたインストールコマンド全体を載せておきます。

# install .NET6 for 32bit arm curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --channel LTS --architecture arm echo 'export DOTNET_ROOT=$HOME/.dotnet' >> ~/.bashrc echo 'export PATH=$PATH:$HOME/.dotnet' >> ~/.bashrc source ~/.bashrc

※1; 厳密に言えばChatGPTの基となる「AI言語モデル」であるGPT-xを指しますが、ここでは言語モデルのGPTとアプリケーションのChatGPTを特に区別しません。

※2; ゆな先生のTwitter(@JapanTank)やnoteを参照。

※3; F#やFORTRANが「厳しい」、JavaScriptやVBAが「緩い」と言われている。C#やVB.NETはその中間。