2023/11/12

PrivateGPTの使い方メモ

はじめに


PrivateGPTを試したのでメモ。PrivateGPTのドキュメントは
https://docs.privategpt.dev/
に公開されており、少なくともLinux環境かつGPUを利用する条件では、このドキュメント通りにインストールすると使えるようになります。

使い方


インストールが完了すると、
PGPT_PROFILES=local make run
でローカル実行できます。具体的な実行コードはMakefileに記載されています。実行後、 http://localhost:8001/ にアクセスすると利用できます。

ドキュメントをPrivateGPTに取り込むには、

PGPT_PROFILES=local make ingest /path/to/docments
を実行します。8487440a6f8d135のリビジョンのコードでは、コードを改変しない限りディレクトリしか指定できません。実行すると、ディレクトリにあるファイルが解析されてPrivateGPTに追加されます。実行するたびにファイル名が同じものを除いて追加されていきます。削除方法はPrivateGPTのドキュメントに記載されています。

ドキュメントの取り込み時は単にサーバーを動かすときよりもGPUのメモリを使用するため、もしGPUのメモリが足りない場合は

CUDA_VISIBLE_DEVICES="" PGPT_PROFILES=local make ingest /path/to/docments
のようにして、GPUを見えなくすればCPUで処理してくれます。

2023/10/26

Diffusion MNIST その3

はじめに


その1で試したDiffusion MNISTについて、 ノイズの強さを表す時刻\(t\)をニューラルネットワークに伝えないとどうなるのかを見ていきます。

方法


https://github.com/MarceloGennari/diffusion_mnist のConditionalUNetの\(t\)が関連する行、つまり、TemporalEmbedding部分をコメントアウトします。具体的には
class ConditionalUNet(UNet):
    (省略)
    def forward(self, x: Tensor, t: Tensor, label: Tensor) -> Tensor:
        x0 = x #self.embedding1(x, t)
        x1 = self.block1(x0)
        x1 = self.label_emb1(x1, label)
        #x1 = self.embedding2(x1, t)
        x2 = self.block2(self.down1(x1))
        x2 = self.label_emb2(x2, label)
        #x2 = self.embedding3(x2, t)
        crossed = self.label_emb3(self.block3(self.down2(x2)), label)
        x3 = self.up1(self.attention1(crossed))
        x4 = torch.cat([x2, x3], dim=1)
        #x4 = self.embedding4(x4, t)
        x5 = self.up2(self.label_emb4(self.block4(x4), label))
        x6 = torch.cat([x5, x1], dim=1)
        x6 = self.label_emb5(x6, label)
        #x6 = self.embedding5(x6, t)
        out = self.out(self.block5(x6))
        return out
とします。

結果


その2で試した時間刻みを100にしたバージョンをベースに比較します。左側がTemporalEmbeddingありで、右側がなしに対応します。
t=50 TemporalEmbeddingあり
t=50 TemporalEmbeddingなし

t=0 TemporalEmbeddingあり
t=0 TemporalEmbeddingなし

TemporalEmbeddingなしの場合はノイズが多いように見えるので、画像の明るさをGIMPを使って上げたものが下図です。TemporalEmbeddingありではノイズが見えませんが、TemporalEmbeddingなしではノイズがはっきり見えるケースが多くなっています。
t=0 TemporalEmbeddingあり
t=0 TemporalEmbeddingなし

まとめ


時刻の埋め込みは効果があるということを確認できました。

2023/10/22

Diffusion MNIST その2

はじめに


その1で試したDiffusion MNISTについて、 ノイズを乗せるステップの細かさを粗くするとどうなるのかを見てみます。

方法


https://github.com/MarceloGennari/diffusion_mnist をいくつか変更することで粗さを変えていきます。

スケジュール変更


DiffusionProcessの引数に渡すvariance_scheduleを変えていきます。 デフォルトでは、
variance_schedule = torch.linspace(1e-4, 0.01, steps=1000)
となっています。これをパターンAでは
variance_schedule = torch.linspace(1e-4, 0.1, steps=100)
と、パターンBでは
variance_schedule = torch.linspace(1e-4, 0.999, steps=10)
とします。

それぞれのスケジュールを使ったときのalphaは

[パターン デフォルト]
[0.99990, 0.99989, 0.99988, ... , 0.99001, 0.99000]

[パターン A]
[0.99990, 0.99889, 0.99788, ... , 0.90101, 0.90000]

[パターン B]
[0.99990, 0.88891, 0.77792, ... , 0.11199, 0.00100]
となります。

alpha_barは

[パターン デフォルト]
[0.9999, 0.9998, 0.9997, ... , 0.0064, 0.0063]

[パターン A]
[0.9999, 0.9988, 0.9967, ... , 0.0062, 0.0056]

[パターン B]
[9.9990e-01, 8.8882e-01, 6.9143e-01, ... , 9.5131e-04, 9.5130e-07]
となります。ここで重要なことは、最初の時刻(ノイズが乗っていない)をt=0、最後の時刻(完全にノイズ)をt=1とするとき、alpha_barはt=0では1に近く、t=1では0に近くなるようにvariance_scheduleを決める必要があるということです。 各時刻tにおけるノイズの強さがalpha_barで決まり、t=1のときに完全にノイズになっていないと拡散プロセスの前提が崩れてしまうためです。

実際、パターンAの

variance_schedule = torch.linspace(1e-4, 0.1, steps=100)
variance_schedule = torch.linspace(1e-4, 0.01, steps=100)
に変えると、alpha_bar の値は
0.99990, 0.99970, 0.99940, ... , 0.60857, 0.60248
となりますが、この場合、数字の画像をうまく生成できません。

学習時のtの値


デフォルトではmain.py
t = torch.randint(0, 1000, (image.shape[0],))
の1000のところを、パターンAでは100に、パターンBでは10にします。

生成時のtの値


デフォルトではinference_unet.py
for t in trange(999, -1, -1):
の999のところを、パターンAでは99に、パターンBでは9にします。刻む数が少なくなると(ステップの細かさを粗くすると)、その分だけ生成時間を短くできます。

結果


デフォルトの設定ではこのようになります(その1の再掲)
t=500
t=0

時刻を100個に刻んだパターンAでも特に変わりなく生成できています。
t=50
t=0

時刻を10個に刻んだパターンBだと、多少ノイズが残ってしまいますが、生成できないというほどではありません。
t=5
t=0

まとめ


デフォルトの1000ステップではなくても、MNIST程度なら生成できることが分かりました。

2023/10/15

Diffusion MNIST

はじめに


MNISTデータセットを使って拡散モデルの学習と、学習したモデルを使った画像を生成をしていきます。

これを実現するコードがすでに
https://github.com/MarceloGennari/diffusion_mnist
で公開されていましたので、こちらを利用して試します。

なお、拡散モデルについての説明は検索するとたくさん出てきますので理論的背景については論文や解説記事を参照ください。 本記事では具体的に何をすれば拡散モデルを動かせるのかを見ていきます。

元論文はarXiv:2006.11239です。

まずは動かす


ソースコードをgit cloneで取得します。 ここでは、コミットハッシュがdf15ee746aのものを使用します。 README.mdを読んで、必要なモジュールをpipでインストールしておきます。

この時点では学習も生成もCPUで実行するように設定されているため、GPUで処理するように変更した後に実行します。

学習


学習はmain.pyで実行できるのですが、この中の
device = "cpu"
と書かれている行を
device = "cuda"
に書き換えます。そして
$ python main.py
を実行すると、学習が始まります。しばらく待っていると完了し、モデルのパラメータがunet_mnist.pthに記録されます。

生成


こちらもGPUで動くように変更します。また、学習はConditionalUNetで行われるものの生成はUNetになっているため、その点も修正します。

修正するコードはinference_unet.pyです。 先ほどと同じように

- device = "cpu"
+ device = "cuda"
に書き換えます。-が書き換え対象の行で、+が書き換えたあとの行の内容です。さらに、
- from models import UNet
+ from models import UNet, ConditionalUNet
と書き換え、
- model = UNet().to(device)
+ model = ConditionalUNet().to(device)
と書き換えます。ConditionalUNetにすると、生成時にどの数字を生成するかを指定する必要があるため、
     model.eval()
+    labels = torch.randint(0, 10, (batch_size,))
+    print(labels)
+    labels_cpu = labels
+    labels = labels.to(device)
     with torch.no_grad():
         for t in trange(999, -1, -1):
             time = torch.ones(batch_size) * t
-            et = model(xt.to(device), time.to(device))  # predict noise
+            et = model(xt.to(device), time.to(device), labels)  # predict noise
             xt = process.inverse(xt, et.cpu(), t)
のように、書き換えます。ランダムに0〜9の値をラベルとして指定するようにしています。

出力部分を少し書き換えて

    labels = ["Generated Images"] * 9
-
-    for i in range(9):
-        plt.subplot(3, 3, i + 1)
-        plt.tight_layout()
-        plt.imshow(xt[i][0], cmap="gray", interpolation="none")
-        plt.title(labels[i])
-    plt.show()
+            if t % 10 == 0:
+                plt.figure(figsize=(10, 10))
+                for i in range(25):
+                    plt.subplot(5, 5, i + 1)
+                    plt.tight_layout()
+                    plt.imshow(xt[i][0], cmap="gray", interpolation="none")
+                    plt.title(f"{labels_cpu[i]}")
+                plt.savefig(f"images/generated_t{t}.png")
+    #plt.show()
とすると、逆拡散過程によって少しずつ数字が画像として浮かび上がるところを見ることができます。 ただし、このようにすると、生成した数字のpyplotでの描画処理のために生成時間が延びます。GPU使用率もあきらかに低下します。 途中結果を見る必要がなければ最初のコードのほうが良いでしょう。

生成結果


結果は次のようになります。ただし、乱数がいろいろなところで使われており、シードも固定されていないので、毎回結果は異なります。

[t=990] ノイズだらけで何も読み取れません。

[t=700] そこはかとなく数字があるように見えるような見えないような。

[t=500] 遠くからみれば(ぼかしてみれば)、数字が簡単に読み取れます。

[t=300] ざらざらしていますが、十分に読めるようになりました。

[t=0] 完全にノイズが取り除かれました。アンチエイリアスは残ったままです。

学習時の処理


学習時の処理がどのように実装されているのかを見ていきます。

上位ループ


モデルの学習をするmain.pyの主要な処理である学習のループ部分を抜き出して、疑似コードとして書き換えると、
for epoch in range(100):
    for image, label in 学習用画像とそのラベルの集合:
	# 画像に加えるノイズの強さをランダムに選択
        t = torch.randint(0, 1000, (image.shape[0],))
        
        # 画像に加えるノイズを作成
        epsilon = torch.randn(image.shape)
        
        # tとepsilonに基づいてノイズをimageに加える
        diffused_image = process.forward(image, t, epsilon)

        # modelを使って加えられたノイズを予測
        optimizer.zero_grad()
        output = model(diffused_image, t, label)
        
        # 予測したノイズがどれだけ正しいかを評価して、モデルの重みを更新
        loss = criterion(epsilon, output) # criterion = torch.nn.MSELoss()
        loss.backward()
        optimizer.step()
となります。ノイズが加えられた画像から、加えられたノイズを推定するモデルを学習しているだけです。 それ以外は通常のニューラルネットワークの学習と変わりがありません。

そしてこれは元論文のAlgorithm 1の通りの実装です。5行目の


\( \nabla_\theta \| \bm{\epsilon} - \bm{\epsilon}_\theta ( \sqrt{\bar{\alpha_t}}\bm{x}_0 + \sqrt{1-\bar{\alpha}_t}\bm{\epsilon}, t) \|^2 \)

と見比べると、\(\bm{\epsilon}\)はコード上のepsilonに、 \(\bm{\epsilon_{\theta}}\)はコード上のmodelに、 \(\sqrt{\bar{\alpha_t}}\bm{x}_0 + \sqrt{1-\bar{\alpha}_t}\bm{\epsilon}\)はdiffused_imageに相当していることが分かります。

Algorithm 1とは異なり、modelの引数にlabelが余計についていますが、これは生成する数字が0〜9のどれであるかをニューラルネットワークに指示するための値となります。

ノイズ付加処理


上位ループ内の
diffused_image = process.forward(image, t, epsilon)
の部分について、詳細を見ていきます。

まず、diffusion_model.pyDiffusionProcessの初期化処理部分である__init__にて\(\alpha_t\)の値が計算されています。

最初に\(\beta_t\) (コード上ではvariance_schedule) を

self.variance_schedule = torch.linspace(1e-4, 0.01, steps=1000)
のように計算しています。具体的には
self.variance_schedule = [1.0000e-04, 1.0991e-04, 1.1982e-04, ... , 0.009980, 0.009990, 0.01]
となっています。元論文では\(\beta_1=10^4\)、\(\beta_T=0.02\)、\(T=1000\)と書かれているので\(\beta_T\)の値のみ異なっています。

\(\alpha_t = 1-\beta_t\)であるので、

self.alpha = 1 - self.variance_schedule
にて計算され、具体的な値は
self.alpha = [0.999900, 0.999890, 0.999880 , ... , 0.990020, 0.990010, 0.990000]
となります。さらに、\(\bar{\alpha}_t=\prod_{s=1}^t \alpha_s\)であるので、
self.alpha_bar = torch.cumprod(self.alpha, dim=0)
にて計算され、具体的な値は
self.alpha_bar = [0.999900, 0.999790, 0.999670, ... , 0.006430, 0.006365, 0.006302]
となります。

これらの値を使って、forwardにてノイズを画像に付加します。 \(\sqrt{\bar{\alpha_t}}\bm{x}_0 + \sqrt{1-\bar{\alpha}_t}\bm{\epsilon}\)が計算できれば良いので、まず\(\sqrt{1-\bar{\alpha}_t}\)を

std_dev = torch.sqrt(1 - self.alpha_bar[time_step])
で計算します。次に\(\sqrt{\bar{\alpha_t}}\)を
mean_multiplier = torch.sqrt(self.alpha_bar[time_step])
で計算し、最後に
diffused_images = mean_multiplier * x_0 + std_dev * noise
のように足し合わせます。x_0が\(\bm{x}_0\)で、noiseが\(\bm{\epsilon}\)です。

ノイズを予測するモデル


ノイズを予測するモデルには小さめのUNetが使われています。コードからグラフに書き起こすと下図のようになります。
背景が水色のボックス内にはクラス名とインスタンス化時の引数を記載しています(画像をクリックすると拡大できます)。

降りる方向と登る方向では条件付けの場所が異なっていますが、このあたりはおおまかであっても十分に動くのでしょう。

以下では、Pytorchで提供されているクラス以外のものを見ていきます。

ResConvGroupNorm


このクラスは以下のような構成になっています。残差接続のある、よく見かける畳み込み演算を使ったブロックです。

LabelEmbedding


このクラスは、0〜9のラベルをベクトルに変換し、入力値\(x\)に埋め込む処理をします。画像のチャネル方向に埋め込むので、各チャネルの画像特徴が埋め込みによって全体的に明るくなったり暗くなったりすることになります。

TemporalEmbedding


このクラスは、加えるノイズの強さを表す時刻\(t\)を入力値\(x\)に埋め込む処理をします。こちらも画像のチャネル方向に埋め込むので、各チャネルの画像特徴が埋め込みによって全体的に明るくなったり暗くなったりすることになります。LabelEmbeddingと干渉しそうですが、モデルの学習時にうまく棲み分けしているのでしょう。
SinusoidalPositionEmbeddingsはarXiv:1706.03762の \[ PE_{(pos,2i)} = sin(pos/10000^{2i/d_{model}}) \] \[ PE_{(pos,2i+1)} = cos(pos/10000^{2i/d_{model}}) \] とほぼ同じ計算をしています。\(sin\)と\(cos\)の引数部分のみ取り出すと、 \[ pos/10000^{2i/d_{model}} \] となり、\(pos\)を時刻\(t\)、\(d_{model}\)を単に\(d\)と書き換えると、 \[ t/10000^{2i/d} \] となります。iは次元方向のインデックスです。もう少し書き換えて、 \[ t/10000^{i/(d/2)} \] \(i/(d/2)\)の範囲が[0,1]になるように、少し値は変わりますが、 \[ t/10000^{i/(d/2-1)} \] とします。さらに、 \[ \begin{aligned} \frac{t}{\exp\left(\log 10000^{i/(d/2-1)}\right)} &= t \cdot \exp \left(-\log 10000^{i/(d/2-1)} \right) \\ &= t \cdot \exp \left(-\frac{i}{\frac{d}{2}-1} \log 10000 \right) \end{aligned} \] と変形します。ここまで変形するとコードとの対応がとれ、
half_dim = self.dim // 2
で\(d/2\)を計算し、
embeddings = math.log(10000) / (half_dim - 1)
で、\(\frac{1}{\frac{d}{2}-1} \log 10000\)を計算し、続いて
embeddings = torch.exp(torch.arange(half_dim, device=device) * -embeddings)
で、\(\exp \left(-\frac{i}{\frac{d}{2}-1} \log 10000 \right)\)を計算していることが分かります。

なお、変形前の\(1/10000^{i/(d/2-1)}\)にしたがって

1/torch.pow(10000, torch.arange(half_dim, device=device)/(half_dim-1))
で計算しても、結果はほとんど変わりません。具体的には、最初の5つの値を出力すると、
式変形前 [1.0000000000, 0.9821373820, 0.9645937681, 0.9473634958, 0.9304410219]
式変形後 [1.0000000000, 0.9821373224, 0.9645937085, 0.9473634958, 0.9304410219]
となるので、どちらで計算してもよさそうです。

さて、時刻\(t\)をかけると

embeddings = time[:, None] * embeddings[None, :]
となり、最後に、
embeddings = torch.cat((embeddings.sin(), embeddings.cos()), dim=-1)
のように\(sin\)と\(cos\)を通し、それらを連結することで、埋め込みベクトルが作成できます。

LinearAttention


attention1のクラスであるLinearAttentionの実装のアイデアの元と思われる論文は のあたりのようですが、ぴったり当てはまるものを見つけることはできませんでした。

注意機構部分のみをコードから計算式に戻すと、 \[ d_{t'f'} = \frac{1}{\sqrt{32}}\frac{1}{7 \times 7} \sum_f{\frac{\exp(q_{t'f})}{\sum_{f''}{\exp(q_{t'f''})}} \sum_t{\frac{\exp(k_{tf})}{\sum_{t''}\exp(k_{t''f})} v_{tf'}}} \] となっているようです。普通の注意機構とは異なり、最初に\(K\)の特徴次元数×\(V\)の特徴次元数(今の場合は32×32)の行列を計算しているようです。 \(\frac{1}{\sqrt{32}}\)の32は\(K\)の特徴次元数で、arXiv:1706.03762の式(1)である \[ Attention(Q,K,V)=\mathrm{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V \] の\(d_k\)に相当するようです。

生成時の処理


生成時の処理がどのように実装されているのかを見ていきます。

上位ループ


生成処理をしているinference_unet.pyの主要部分を疑似コードとして書き換えると、
for t in trange(999, -1, -1):
    # 時刻tとラベルlabelの条件の下、ノイズを推定する
    et = model(xt, t, label)

    # 推定したノイズを使って画像からノイズを除去する
    xt = process.inverse(xt, et, t)

# xtを画像として描画する
draw(xt)
となります。モデルの学習時は時刻tをランダムに選んでいましたが、 生成するときはノイズから徐々にノイズを取り除いていきます。t=999ではノイズのみ、t=0ではノイズがなくなった画像になります。

ノイズ除去処理


上位ループ内の
xt = process.inverse(xt, et, t)
の部分について詳細を見ていきます。

元論文arXiv:2006.11239のAlgorithm 2の4行目の処理 \[ \bm{x}_{t-1}=\frac{1}{\sqrt{\alpha_t}}\left(\bm{x}_t-\frac{1-\alpha_t}{\sqrt{1-\bar{\alpha}_t}} \epsilon_\theta(\bm{x}_t,t)\right)+\sigma_t\bm{z} \] が実装されています。DiffusionProcessinverseを見ていくと、

scale = 1 / torch.sqrt(self.alpha[t])
では\(\frac{1}{\sqrt{\alpha_t}}\)の部分が計算されています。
noise_scale = (1 - self.alpha[t]) / torch.sqrt(1 - self.alpha_bar[t])
では\(\frac{1-\alpha_t}{\sqrt{1-\bar{\alpha}_t}}\)の部分が計算されています。
std_dev = torch.sqrt(self.variance_schedule[t])
では\(\sigma_t\)が計算されています。
z = torch.randn(xt.shape) if t > 1 else torch.Tensor([0])
では\(\bm{z} \sim N(0,\bm{I})\)が計算されています。ガウス分布に従うノイズを作っているだけです。

最後にすべてをつなげて

mu_t = scale * (xt - noise_scale * et)
xt = mu_t + std_dev * z  # remove noise from image  
を計算することで、Algorithm 2の4行目の処理を実現しています。

2023/09/24

Linuxのbootパーティションの空き容量を増やしたい

Linuxで /boot パーティションのサイズが足りず、さらに容量を増やすことも難しい場合は次のようにするとカーネルイメージのディスク使用量を多少減らすことができるかもしれません。

  1. /etc/initramfs-tools/initramfs.conf の COMPRESS の行を編集して圧縮手法をxzに変更します。
  2. update-initramfs -u

たとえば、Ubuntu20で、

initrd.img-5.15.0-43-generic
を使っていて、上の手順を実行すると、
217,952,335 bytes
から
155,488,300 bytes
にまで減らすことができました。

Ubuntu20からUbuntu22にアップグレードしようとしてbootパーティションの容量が足りなかったときは、この方法で回避ができました。

2023/03/12

ChatGPTとPerplexityの比較

ChatGPTとPerplexityに色々と聞いて、結果を比較してみました。

シングルクォートorダブルクォート


入力した文字列は
Pythonでは"と'のどちらが推奨されていますか?またその根拠を教えてください。
ChatGPTの出力は次の通り。
Pythonにおいて、 " "(ダブルクォーテーション)と ' '(シングルクォーテーション)の両方が文字列を表現するために使用されます。両方のクォーテーションの種類を使用することで、文字列内にクォーテーションが含まれる場合に便利になります。

しかし、PythonにはPEP 8と呼ばれるスタイルガイドがあり、その中で文字列の表現において、ダブルクォーテーションが推奨されています。PEP 8によると、ダブルクォーテーションがシングルクォーテーションよりも優れている理由は以下の通りです。

    Pythonの標準ライブラリの多くは、ダブルクォーテーションを使用しています。
    文字列内にシングルクォーテーションが含まれる場合、エスケープする必要があるため、ダブルクォーテーションの方が可読性が高くなることがあります。
    シングルクォーテーションは、文字列の代わりにシングルクォーテーションを使用することができるため、文法上の意味を区別するために混乱を招く可能性があります。

しかし、Pythonのコミュニティには、シングルクォーテーションを好む人もいます。この問題には、個人的な好みやスタイルガイドに従うなどの解決策がありますが、PEP 8に従ってダブルクォーテーションを使用することが一般的に推奨されています。
Perplexityの出力は次の通り。
Pythonでは、シングルクォーテーション(')とダブルクォーテーション(")の両方が文字列リテラルを表現するために使用されます。Pythonの公式ドキュメントによると、どちらの方法でも同じ結果が得られます[1]。
ただし、コード内で文字列を囲む引用符を一貫して使用することが推奨されています。一般的には、Pythonコミュニティではシングルクォーテーションがより一般的であると考えられています[2][3]。
これは、ダブルクォーテーションを文字列内で使用する必要性が少ないためです。また、Pep8スタイルガイドでは、シングルクォートを使用することが推奨されています[4]。
ただし、プロジェクト内で一貫性を保つことが重要です。プロジェクト内で既に採用されているスタイルに従うことが望ましいです[5]。
ちなみに[4]のリファレンスを見に行ってもどこに書いてあるのか見つけられませんでした。

https://pep8-ja.readthedocs.io/ja/latest/ を読むと、

Python では、単一引用符 ' で囲まれた文字列と、二重引用符 " で囲まれた文字列は同じです。この PEP では、どちらを推奨するかの立場は示しません。どちらを使うかのルールを決めて、守るようにして下さい。
ということなので、どちらかPEP8では推奨されているわけではないようです。ChatGPTとPerplexityのどちらも誤りですね。

きのこたけのこ


入力した文字列は
きのこたけのこ戦争について教えてください。
ChatGPTの出力は次の通り。
「きのこたけのこ戦争」とは、日本の童謡のひとつで、作詞:さとう宗幸、作曲:いずみたくによって作られた曲です。戦争を題材にした曲で、きのこ派とたけのこ派の戦いを描いています。

歌詞の冒頭には「山の手にはきのこ派 川の手にはたけのこ派」とあり、きのこ派とたけのこ派がそれぞれ勢力を持っていることが示されています。曲の進行に従い、両派の戦いが繰り広げられますが、最終的には「みんな仲良くしましょ きのこたけのこ戦争終わりました」という歌詞で、和解が成されるという内容です。

「きのこたけのこ戦争」は、子供たちに戦争を通して、相手を理解し、和解することの大切さを教えるために作られた曲で、日本の教育現場でもよく取り上げられています。
Perplexityの出力は次の通り。
「きのこたけのこ戦争」とは、日本の菓子メーカー明治製菓が販売する「きのこの山」と「たけのこの里」のどちらが美味しいかという消費者間の論争である[1][2][3][4]。この論争は1980年代から始まり、現在でもネット上で話題になっている[2]。また、明治製菓自身も「きのこたけのこ国民大調査」と称するアンケートを実施しており、その結果を公表している[5]。
ChatGPTの出力は面白いのですが、Perpelxityのほうが欲しい情報が得られています。

エディタどれがいいかな


入力した文字列は
エディタの宗教論争について説明してください。
ChatGPTの出力は次の通り。
エディタの宗教論争は、プログラマーやコンピューターエンジニアの間で長年続いている、どのエディタが最も優れているかという議論です。エディタとは、プログラムやテキストファイルを編集するためのソフトウェアのことです。

エディタの宗教論争は、個人的な好みや使い慣れている環境などによって、さまざまな立場があるため、しばしば激しい議論になります。一部のプログラマーやエンジニアは、彼らが使用するエディタについて非常に情熱的であり、そのエディタが最高であると信じて疑いません。

代表的なエディタの宗教論争としては、以下のものがあります。

    Vim vs Emacs: VimとEmacsは、両方とも高度な機能を備えたテキストエディタで、プログラマーやシステム管理者などの技術者に人気があります。両方のエディタには、熟練したユーザーによって開発されたカスタムコマンドがありますが、その開発手法や哲学に対する意見の違いにより、ユーザーは通常、どちらかを使用することになります。

    Sublime Text vs Atom vs Visual Studio Code: Sublime Text、Atom、およびVisual Studio Codeは、プログラマーの間で最も人気のあるエディタの1つです。これらのエディタには、豊富なプラグインとカスタマイズオプションがありますが、それぞれのエディタの利点と欠点についての議論が行われています。

    IDE vs エディタ: いくつかのプログラマーやエンジニアは、統合開発環境(IDE)を使用することを好む場合があります。IDEは、エディタの機能に加えて、コンパイラ、デバッガ、および他のツールを提供します。エディタを使用することを好む人は、軽量で使いやすく、シンプルなエディタを好みます。

最近では、エディタの宗教論争は、クラウドベースのエディタ(例えば、Google Docs、Office Online、CodePen)の使用に関しても続いています。これらのエディタは、ロ
Perplexityの出力は次の通り。
エディタ戦争とは、プログラミングを行う人々の間で、どのテキストエディタが最適かというテーマについての論争です。特にハッカー文化に属する人々の間で、自分たちが愛好するエディタを宗教的狂信とも言えるほど尊敬して扱う伝統があります。この論争は、vi派とEmacs派の二大陣営によって代表されます[1][2][3]。しかし、この論争は単なるジョークや冗談であることも多く、実際には多くのエディタが存在し、それぞれに優れた機能や使い勝手があるため、個人の好みや用途に合わ
ChatGPT、Perplexity共に出力途中でやめてしまいました。内容はどちらも正しそうです。

Git vs Mercurial vs Sapling


入力した文字列は
GitとMercurialとSaplingのProsとConsを教えてください。
ChatGPTの出力は次の通り。
Git、Mercurial、およびSaplingは、分散バージョン管理システムの一例です。以下に各システムのProsとConsを示します。

GitのPros:

    オープンソースであり、広く使用されている
    大規模なプロジェクトに最適で、高速で効率的なブランチとマージを提供
    GitHubなどの人気のあるプラットフォームで広く使用されているため、ツールやサポートが豊富にあります。

GitのCons:

    初心者には学習曲線が急である
    履歴の変更に対する管理が難しい場合がある

MercurialのPros:

    Gitに似た機能を提供するが、より使いやすいとされている
    Windowsにも対応しているため、開発者にとってはより利用しやすい

MercurialのCons:

    Gitほど普及していないため、サポートが少ない場合がある
    ブランチとマージの処理が遅い場合がある

SaplingのPros:

    GitとMercurialの長所を組み合わせたもので、Gitのブランチ戦略を使用できる
    ブランチとマージの速度が非常に速く、一般的なワークフローであっても効率的に動作する

SaplingのCons:

    GitやMercurialほど普及しておらず、サポートが限られている場合がある
    開発者が少ないため、コミュニティの活発性が低い場合がある

総合的に、Gitは最も広く使用されており、豊富なツールとサポートがあるため、大規模なプロジェクトに最適です。一方、MercurialはGitに似た機能を提供しながらも、より使いやすいため、初心者には適しているかもしれません。SaplingはGitやMercurialよりもブランチとマージの速度が速く、高速なワークフローが必要な場合に最適です。ただし、Saplingのサポートが限られているため、現在はGitまたはMercurialを選択することが一般的です。
Perplexityの出力は次の通り。
Git and Mercurial are both popular version control systems, each with their own pros and cons. Git is known for its speed, flexibility, and large user community[1].
Mercurial is praised for its ease of use, scalability, and support for multiple branches[2].
Sapling is a newer source control system developed by Meta that aims to be user-friendly and scalable[3].
It removes or changes some of the behavior of Mercurial commands to make them more consistent with modern expectations[4].
While Git and Mercurial are well-established systems with large communities, Sapling is a newer option that may appeal to those looking for a more user-friendly experience[5].
ChatGPTもPerplexityも正しそうな出力をしています。 Perpelxityは出力がなぜか英語になってしまいました。Prosとか書いたからかもしれません。

ChatGPTの出力の「初心者には学習曲線が急である」は誤用する多数派に引きずられて誤りを出力してしまっていますね。

歴史


入力した文字列は
1980年から10年ごとに1つずつ日本で起きた大きな出来事を教えてください。
ChatGPTの出力は次の通り。
以下は、1980年から2010年までの日本で起こった10年ごとの大きな出来事の例です。

    1980年代:バブル景気の到来
    1990年代:バブル崩壊、リーマンショックの影響による失業率上昇、そして阪神・淡路大震災
    2000年代:東京地下鉄サリン事件、小泉純一郎による構造改革、そしてリニア中央新幹線計画の発表
    2010年代:東日本大震災、福島第一原発事故、消費税率の引き上げ、そして東京オリンピック・パラリンピックの開催準備
Perplexityの出力は次の通り。
1980年代:
• 1989年1月7日:昭和天皇崩御、皇太子明仁が即位[1]。
1990年代:
• 1995年1月17日:阪神・淡路大震災発生[1]。
2000年代:
• 2003年3月20日:イラク戦争に自衛隊が派遣される[2]。
2010年代:
• 2011年3月11日:東北地方太平洋沖地震(東日本大震災)発生[1]。
2020年代:
• 2020年7月24日:東京オリンピック開催予定だったが、新型コロナウイルス感染症の影響で延期される[1]。
ChatGPTとPerplexity共にしれっと嘘をついています。

ChatGPTの出力を見てみます。まず、東京地下鉄サリン事件は
https://ja.wikipedia.org/wiki/%E5%9C%B0%E4%B8%8B%E9%89%84%E3%82%B5%E3%83%AA%E3%83%B3%E4%BA%8B%E4%BB%B6
によると、1995年(平成7年)3月20日なので、2000年代というのは嘘ですね。

リーマンショックは2008年なので、これも嘘。

リニア中央新幹線はhttps://ja.wikipedia.org/wiki/%E4%B8%AD%E5%A4%AE%E6%96%B0%E5%B9%B9%E7%B7%9Aによると、「2011年5月26日に整備計画が決定され」と書かれているので、2000年代というのも誤りのようです。

Perplexityは日付付きで列挙してくれました。

Perpexityが参照したのは、
[1] https://www3.nhk.or.jp/news/special/heisei/chronology/
[2] https://www.shugiin.go.jp/internet/itdb_kenpou.nsf/html/kenpou/chosa/shukenshi033.pdf/$File/shukenshi033.pdf
とのこと。

出力結果のうち、1989年1月7日、1995年1月17日、2011年3月11日、2020年7月24日は正解。 2003年3月20日は
https://ja.wikipedia.org/wiki/%E8%87%AA%E8%A1%9B%E9%9A%8A%E3%82%A4%E3%83%A9%E3%82%AF%E6%B4%BE%E9%81%A3
https://ja.wikipedia.org/wiki/%E3%82%A4%E3%83%A9%E3%82%AF%E6%88%A6%E4%BA%89
を読むと、軍事介入の開始日のようです。派遣日とごっちゃになってます。

正しい文章のなかに一部だけ間違えた内容を含めるとかされると、結局検証に時間がかかるので、こういったことの調査には不向きなようです。 調査の取っ掛かりには使えるのかもしれません。

いずも


入力した文字列は
「いずも」はどういった船ですか?
ChatGPTの出力は次の通り。
「いずも」とは、日本の海上自衛隊が運用するヘリコプター搭載護衛艦の名称です。正式名称は「いずも型護衛艦」で、海上自衛隊の中でも最大級の艦種となっています。

「いずも」は、航空機の発着が可能なフライトデッキと、ヘリコプターを格納できるハンガーを備えています。また、レーダーやソナーなどの最新の艦載電子機器や、対空・対水上戦闘能力を持つ艦砲やミサイルなどの装備も備えています。

「いずも」は、平時には海上自衛隊の護衛艦として、また災害時には災害派遣艦としても活躍しています。また、アメリカ海軍との共同訓練などにも参加しています。
Perplexityの出力は次の通り。
「いずも」とは、海上自衛隊が保有する護衛艦の一種であり、2019年時点で海上自衛隊史上最大の自衛艦である[1][2]。
全長248m、最大幅38m、満載排水量2万6000トンであり、軽空母に相当する大きさだ[2]。
F-35Bを搭載・運用可能とする改修工事を受けており、「いずも」と「かが」の2隻で61億円かかった[3][4]。
正しそうに見えます。

ニューロン


入力した文字列は
ニューロンのシナプスで使われているイオンの名前を列挙して
ChatGPTの出力は次の通り。
ニューロンのシナプスで使われているイオンの一部を以下に列挙します:

    ナトリウムイオン (Na+)
    カリウムイオン (K+)
    カルシウムイオン (Ca2+)
    塩化物イオン (Cl-)

これらのイオンは、神経細胞のシナプスを介して情報が伝達される際に重要な役割を果たします。具体的には、神経細胞の電気信号がシナプスを介して化学信号に変換され、シナプス前細胞から放出された神経伝達物質がシナプス後細胞の受容体に結合することで、これらのイオンが活動電位を誘発したり、神経伝達物質の放出を促進・阻害するなどの役割を担います。
Perplexityの出力は次の通り。
ニューロンのシナプスで使われているイオンの名前はカルシウムイオンです[1]。
Perplexityは1個しか挙げられませんでした。

https://ja.wikipedia.org/wiki/%E3%82%B7%E3%83%8A%E3%83%97%E3%82%B9 によるとナトリウムとカルシウムは正しいです。

https://bsd.neuroinf.jp/wiki/%E3%82%AB%E3%83%AA%E3%82%A6%E3%83%A0%E3%83%81%E3%83%A3%E3%83%8D%E3%83%AB によると、カリウムも正しいです。

https://bsd.neuroinf.jp/wiki/%E6%8A%91%E5%88%B6%E6%80%A7%E3%82%B7%E3%83%8A%E3%83%97%E3%82%B9 によると、塩化物イオンも正しそうです。

今回は嘘はなかったようです。

かな漢字変換


入力した文字列は
"kyoutonokyounokyounawodonaishiharu"を漢字かな交じり文に変換して
ChatGPTの出力は次の通り。
今日という日は誰にしはる、と書いて「きょうとなぁをどないしはる」と読みます。
Perplexityの出力は次の通り。
「きょうとの きょうな きょうを どないしはる」と書くことができます。
google IMEで変換すると
・1位候補「京都の京の京菜をどないしはる」
・2位候補「京都の今日の京菜をどないしはる」
が候補にでてくるので、そのままではIME代わりとしてはどちらも使えそうになさそうです。

ChatGPTに関して、もうすこし試してみました。Q:が入力で、A:が回答です。A:の後ろのカッコ書きの部分は著者のコメントです。

Q:
あなたはIMEです。これから入力するローマ字は日本語を表しています。最も尤もらしい漢字かな交じり文に変換してください。

A:
はい、承知しました。以下は入力されたローマ字を漢字かな交じり文に変換したものです。

あなた → あなた or 貴方

IME → IME or 入力システム

ろうまじ → ローマ字

にほんご → 日本語

もじ → 文字

かな → 仮名

こうじょう → 交じり文

に → 「に」は漢字で表記されず、仮名で表記されます。

へんかん → 変換

して → して

Q:
kyotonokyonokyonawodonaishiharu

A: (京都の今日の京菜をどないしはる、を想定しましたが入力が誤っています。kyotoではなくkyoutoでした)
ご入力いただきありがとうございます。
「きょとのきょのきょなわをどないしはる」であれば、次のように漢字かな交じり文に変換されます。

「京都の京の恋の恋縄を如何(いか)しはる」
意味:「京都と京都で恋をしている恋人同士の絆をどうされますか?」

Q:
dai23dainaikakusouridaijin

A: (第23代内閣総理大臣、を想定)
ご入力いただきありがとうございます。
「だい23だいないかくそうりだいじん」であれば、次のように漢字かな交じり文に変換されます。

「第23代内閣総理大臣」
意味:「日本の政治において、23番目の内閣総理大臣」

Q:
qawsedrftgyhujikolp

A: (変換できないことを想定)
入力された文字列「qawsedrftgyhujikolp」は、キーボードの上から左から6文字目の「q」から始まっており、左から10文字目の「k」で方向を変え、左から5文字目の「r」でまた方向転換しています。そのため、意味のある単語や文にはなっていません。

Q:
osakakarakyotomadenokyoriwonanbaishitaraosakakaratokyomadenokyorininarimasuka

A: (正しく出力されました)
「大阪から京都までの距離を何倍したら、大阪から東京までの距離になりますか?」という意味ですね。

答えは、直線距離で約2.5倍程度になりますが、実際の移動距離は異なる場合があります。また、交通手段やルートによっても異なります。

Q:
gottuumaitakoyakinomiseoshietekureyanyaroka

A: (ごっつうまいたこやきの店教えてくれやんやろか、を想定。1文字間違い)
ご入力いただきありがとうございます。
「ごっつうまいたこ焼きの店教えてくれやんなろか?」であれば、次のように解釈できます。

「とても美味しいたこ焼きを売っているお店を教えていただけませんか?」

「ごっつうまい」は「とても美味しい」を強調した方言表現です。また、「くれやんなろか?」は方言で、「くださいよね?」という意味になります。

Q:
yoruniasageba mane

A: (夜にあさげばまね、を想定。まね、が誤って変換されました。解説は間違い。「夜に歩いたら駄目だ」なら正解)
ご入力いただきありがとうございます。
「夜にあさげば真寝」という文言であれば、そのままの意味で「夜遅くに寝ると、本当に寝てしまう」という意味になります。
ただし、文脈によっては異なる解釈があるかもしれません。

Q:
ototoiyasumiyattakedo,dogenshitato

A: (一昨日休みやったけど、どげんしたと、を想定。解釈できなかった模様)
ご入力いただきありがとうございます。
「弟がやすみやったけど、道元師太と」であれば、文脈によっては意味が通じない可能性がありますが、文法的には正しい文です。
一般的には、次のように解釈できます。

「弟が休みだったけど、道元師太と一緒に過ごしました。」

ただし、「道元師太」という人物が誰かによってはわからない場合があるため、文脈によっては異なる解釈があるかもしれません。

Q:
wattihahanadearinsu

A: (わっちは花でありんす、を想定)
ご入力いただきありがとうございます。
「わっちは母なんであーりんす」という文言であれば、次のように解釈できます。

「わたしは母親なのであります(です)」

「わっち」は方言で「わたし」を表す表現で、「あーりんす」は方言で「です」を表す表現です。

Q:
wattiwahanadearinsu

A: (「は」を「わ」に変えたが、読み取られなかった)
ご入力いただきありがとうございます。
「わっちわ母なんであーりんす」という文言であれば、次のように解釈できます。

「わたしは母親なのであります(です)」

「わっち」は方言で「わたし」を表す表現で、「わ」が重ねられていることで親しみやすさが表現されています。「あーりんす」は方言で「です」を表す表現です。ただし、方言によっては異なる言葉遣いがあるため、文脈によっては異なる解釈があるかもしれません。

Q:
asunoasakarataihuugayattekimasu

A: (明日の朝から台風がやってきます、を想定)
ご入力いただきありがとうございます。
「明日の朝から体調がやってきます」という意味になります。
ただし、文脈によっては異なる解釈があるかもしれません。
やはりIMEとしてはイマイチですね。

日本の財政


入力した文字列は
日本国の貸借対照表を見せて
ChatGPTの出力は次の通り。
申し訳ありませんが、私は見ることができません。私は人工知能のプログラムであり、物理的な場所に存在するわけではありません。また、日本国の貸借対照表にアクセスする権限もありません。

しかしながら、日本国の貸借対照表は、日本の国会図書館や経済産業省など、公的な機関のウェブサイトで入手可能です。そちらを参照してください。
Perplexityの出力は次の通り。
日本国には貸借対照表が存在します。財務省のウェブサイトで公表されており、誰でも閲覧することができます[1]。
また、経済産業省のウェブサイトでも貸借対照表が掲載されています[2]。
ただし、国全体のフローとストックの財務状況を把握するためには、一般企業には存在しない国特有の仮想資産なども考慮する必要があるとされています[3]。
[1]のリンクからたどることはできました。参照URLがあるというのは検索目的では便利です。

四国で最も高い山


ChatGPTが参照したURLを出せるか試してみます。
Q:
四国で最も高い山の名前は?

A: (漢字は合っているものの読みを間違えています)
四国で最も高い山は、石鎚山(いしのぶさん)です。標高は1,982メートルです。

Q:
根拠となるWebページのURLを教えてください。

A: (出力されたURLは金峯山のページで間違い)
以下のURLが参考になります。

https://www.yamakei-online.com/yamanavi/yama.php?yama_id=230

キャッシュレス決済


入力した文字列は
銀行振込はキャッシュレス決済にあたりますか?
ChatGPTの出力は次の通り。
はい、銀行振込はキャッシュレス決済の一種です。キャッシュレス決済とは、紙幣や硬貨を使用せずに電子的な手段で支払いを行うことを指します。銀行振込は、支払いのために紙幣や硬貨を使用せずに、銀行口座を介して電子的な取引を行う方法です。そのため、銀行振込はキャッシュレス決済の一つとして位置付けられます。
Perplexityの出力は次の通り。
銀行振込はキャッシュレス決済にあたります。キャッシュレス決済とは、現金以外で支払う決済手段のことであり、クレジットカードや銀行振込などがこれにあたる[1][2][3][4]。
日本のキャッシュレス決済比率は約20%と言われていますが、銀行口座を使った支払いを含めれば約50%まで引き上がるとも言われています[3]。
どちらも正しそうです。

オセロ


入力した文字列は
オセロの初期配置を絵文字で描いて
ChatGPTの出力は次の通り。
Sure, here's a visual representation of the starting position for Othello using emojis:

⚪️⚫️⚪️⚪️⚪️⚪️⚪️⚪️
⚪️⚪️⚪️⚪️⚪️⚪️⚪️⚪️
⚪️⚪️⚪️⚪️⚪️⚪️⚪️⚪️
⚪️⚪️⚪️⚫️⚪️⚪️⚪️⚪️
⚪️⚪️⚪️⚪️⚫️⚪️⚪️⚪️
⚪️⚪️⚪️⚪️⚪️⚪️⚪️⚪️
⚪️⚪️⚪️⚪️⚪️⚪️⚪️⚪️
⚪️⚪️⚪️⚪️⚪️⚪️⚪️⚫️
Perplexityの出力は次の通り。
セロの初期配置を絵文字で描くことができます。以下は一例です。
⚪️⚫️⚫️⚫️⚫️⚫️
⚫️⚪️⚪️⚪️⚪️⚫️
⚫️⚪️⚪️⚪️⚪️⚫️
⚫️⚪️⚪️⚪️⚪️⚫️
⚫️⚪️⚪️⚪️ ⚪ ⬜
 ⬜ ⬜ ⬜ ⬜ ⬜
 (白石を「○」、黒石を「●」、「 」を空白、「■」を壁などに置き換えています。)
どちらもハズレですね。白の絵文字が環境によっては見えないかもです。

将棋の初期配置


入力した文字列は
将棋の初期配置を絵文字で書いて
ChatGPTの出力は次の通り。
将棋の初期配置を絵文字で書くと以下のようになります:

🐴🩹🦫🥋🩸🥋🦫🩹🐴
🐺🩹🩹🩹🩹🩹🩹🩹🐺
🐗🩹🦊🥋🩸🥋🦊🩹🐗
🐦🩹🩹🩹🩹🩹🩹🩹🐦
🎖️🩹🎖️🩹🎖️🩹🎖️🩹🎖️
🩸🩹🩸🩹🩸🩹🩸🩹🩸
🐭🩹🩹🩹🩹🩹🩹🩹🐭
🐱🩹🦊🥋🩸🥋🦊🩹🐱
🐵🩹🩹🩹🩹🩹🩹🩹🐵

解説:

🐴: 先手の馬
🐺: 先手のライオン(本将棋ではライオンと呼びます)
🐗: 先手の豹
🐦: 先手の鳥
🎖️: 先手の玉将
🩸: 先手の兵
🐭: 後手のねずみ
🐱: 後手の猫
🐵: 後手の猿

🦫: 「絵文字」には将棋の駒の絵文字が存在しないため、「アナグマ」を代用しています。
🥋: 同様に「道着」を表す絵文字を使用しています。
どうなっているのでしょうか。

Perplexityの出力は次の通り。

将棋の初期配置は以下のように絵文字で書くことができます。
🐴🉑🉐🉑🉐🎖️🉑🉐🉑🉐
(先手:歩兵2、香車2、桂馬2、銀将1、金将1、王将1)
(後手:歩兵2、香車2、桂馬2、銀将1、金将1、王将1)
こっちも変。絵文字というのが良くないようです。

将棋の初期配置 その2


入力した文字列は
将棋の初期配置を文字で描いて。罫線を使って枠も描いて。
ChatGPTの出力は次の通り。
はい、以下に将棋の初期配置を文字で描き、罫線で枠を描きます。

┏━┯━┯━┯━┯━┯━┯━┯━┓
┃ │香│桂│銀│金│王│金│銀│桂│香│
┠─┼─┼─┼─┼─┼─┼─┼─┨
┃歩│歩│歩│歩│歩│歩│歩│歩│歩│
┠─┼─┼─┼─┼─┼─┼─┼─┨
┃ │ │ │ │ │ │ │ │ │
┠─┼─┼─┼─┼─┼─┼─┼─┨
┃ │ │ │ │ │ │ │ │ │
┠─┼─┼─┼─┼─┼─┼─┼─┨
┃ │ │ │ │ │ │ │ │ │
┠─┼─┼─┼─┼─┼─┼─┼─┨
┃ │ │ │ │ │ │ │ │ │
┠─┼─┼─┼─┼─┼─┼─┼─┨
┃歩│歩│歩│歩│歩│歩│歩│歩│歩│
┠─┼─┼─┼─┼─┼─┼─┼─┨
┃ │飛│角│金│ │金│ │馬│飛│
┗━┷━┷━┷━┷━┷━┷━┷━┛

※棋譜や駒落ちのルール等によっては、初期配置が異なる場合があります。
Perplexityの出力は次の通り。
将棋の初期配置は以下のようになります。罫線を使って枠も描きました。
┏━┳━┳━┳━┳━┳━┳━┳━┓
┃香│桂│銀│金│玉│金│銀│桂│
┣━╋━╋━╋━╋━╋━╋━╋━┫
┃ │飛│ │ │ │ │角│ │
┣━╋━╋━╋━╋━╋━╋━╋━┫
┃歩│歩│歩│歩│歩│歩│歩│歩 │
┗━━━━━━━━━━━━━━─
※将棋盤は9×9マスです。
ChatGPTはいろいろずれています。Perplexityは8x3の出力になっています。雰囲気は出ていますが、どちらも正しくないです。

2023/03/06

subprocess.runの文字列長の制限

Pythonのsubprocess.runで長い引数を使うと、エラーが発生して実行できません。

そこで、手元のLinux環境で限界を調べてみました。

最初に引数の最大値を調べてみます。

$ getconf ARG_MAX
2097152
と表示されました。

次に、Python経由でコマンドを実行してみます。

subprocess.run(["echo", "a"*131071]) 
はエラーが発生せずに動作しました。

一方、

subprocess.run(["echo", "a"*131072])
だと、
<省略>
  File "/usr/lib/python3.9/subprocess.py", line 505, in run
    with Popen(*popenargs, **kwargs) as process:
  File "/usr/lib/python3.9/subprocess.py", line 951, in __init__
    self._execute_child(args, executable, preexec_fn, close_fds,
  File "/usr/lib/python3.9/subprocess.py", line 1823, in _execute_child
    raise child_exception_type(errno_num, err_msg, err_filename)
OSError: [Errno 7] Argument list too long: 'echo'
となり、長すぎというエラーが出ました。ARG_MAXの値よりもずいぶんと短い値でエラーになります。

複数の引数ならどうなるかを試してみます。

subprocess.run(["echo"]+["a"*131071]*15)
はエラーが発生せずに動作しました。 runに渡した文字列長はリストの要素をスペース1個でつなげるとすると1966084バイトで、ARG_MAXを下回っています。

一方、

subprocess.run(["echo"]+["a"*131071]*16)
だと先程と同じエラーが発生します。 この場合の文字列の長さは2097156でARG_MAXを超えています。

というわけで、引数の長さには2つ制限があり、

  • 引数単位での文字列長の上限
  • コマンド全体での長さの上限
の2つがあることがわかりました。