新入社員のみんな、「ChatGPT×Python」で鬼にならないか?
ChatGPTが本当にヤバい。
断言する。新卒がこれを使いこなせば、今職場で「優秀」とされている5-6年目くらいの先輩なら余裕で出し抜ける。鬼になれる。
筆者はメーカー社員なので、メーカーの新入社員がChatGPTを使って鬼になる方法を1つ提案したい。
「ChatGPT×Python」である。
Pythonとは、ご存知のとおり物理シュミレーションからデータサイエンス、機械学習までカバーする汎用性をそなえたプログラミング言語だ。何でもできるわりには書ける人がなぜか少なく、いまだにスキルとして重宝されている。
そんなPythonにChatGPTを使おう。
ChatGPTを使えば、上司から求められるアウトプットを一瞬で出すことができる。それに対してフィードバックをもらい、それも一瞬で打ち返すことができる。
「あいつ"Python書ける"だけじゃないんだよな。こっちが言ったこと正確に理解するし、それをとんでもない早さで打ち返してくるし、なにより何度フィードバックかけても嫌な顔一つしないんだよな。だからあいつがチームに絡むと物凄くいいアウトプットが出る。」
「ChatGPT×Python」によって、こんな鬼のような新入社員にならないか?
本稿では、「ChatGPT×Python」の具体例として、二次元の熱伝導方程式の数値計算プログラムをプロンプトとともに紹介する。
ここで取り上げない題材についても、やってみてよさげなものがあれば随時ツイッターで紹介していく。
まずは温度分布を可視化
xy平面における熱伝導方程式を解くのは、Pythonによる数値計算では定番なのだが、いざ書くとなるとそこそこの知識を必要とする。これをChatGPTにお願いしてみよう。まずは温度分布を可視化する。
きわめてシンプルなプロンプトだ。ChatGPTの解答は以下。
コード全文は以下。
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
# Constants
alpha = 1e-4 # Thermal diffusivity (m²/s)
Lx, Ly = 1, 1 # Domain dimensions (m)
Nx, Ny = 100, 100 # Number of grid points
dx, dy = Lx / (Nx - 1), Ly / (Ny - 1) # Grid spacing (m)
dt = 0.25 * min(dx, dy)**2 / alpha # Time step (s)
total_time = 50 * dt # Total simulation time (s)
# Initialize the temperature field
T = np.zeros((Ny, Nx))
# Initial conditions
T[Ny // 2 - 10 : Ny // 2 + 10, Nx // 2 - 10 : Nx // 2 + 10] = 100
# Function to update the temperature field using the finite difference method
def update_temperature(T, alpha, dt, dx, dy):
T_new = T.copy()
T_new[1:-1, 1:-1] = (
T[1:-1, 1:-1]
+ alpha * dt * (
(T[1:-1, 2:] - 2 * T[1:-1, 1:-1] + T[1:-1, :-2]) / dx**2
+ (T[2:, 1:-1] - 2 * T[1:-1, 1:-1] + T[:-2, 1:-1]) / dy**2
)
)
return T_new
# Time-stepping loop
n_steps = int(total_time / dt)
for _ in range(n_steps):
T = update_temperature(T, alpha, dt, dx, dy)
# Visualize the temperature field
plt.imshow(T, cmap='hot', origin='lower', extent=[0, Lx, 0, Ly])
plt.colorbar(label='Temperature (°C)')
plt.xlabel('x (m)')
plt.ylabel('y (m)')
plt.title('2D Heat Transfer')
plt.show()
これをコピペするとふつうに欲しい結果が出てくる。意味が分からない。
温度分布が変わる様子を見たい
よくあるこんなケース。
シュミレーションする時間を4分割し、温度分布を4枚出力するコードに変更しよう。プロンプトは以下。
ChatGPTの解答とコード。
import numpy as np
import matplotlib.pyplot as plt
# Constants
alpha = 1e-4 # Thermal diffusivity (m²/s)
Lx, Ly = 1, 1 # Domain dimensions (m)
Nx, Ny = 100, 100 # Number of grid points
dx, dy = Lx / (Nx - 1), Ly / (Ny - 1) # Grid spacing (m)
dt = 0.25 * min(dx, dy)**2 / alpha # Time step (s)
total_time = 12 # Total simulation time (s)
# Initialize the temperature field
T = np.zeros((Ny, Nx))
# Initial conditions
T[Ny // 2 - 10 : Ny // 2 + 10, Nx // 2 - 10 : Nx // 2 + 10] = 100
# Function to update the temperature field using the finite difference method
def update_temperature(T, alpha, dt, dx, dy):
T_new = T.copy()
T_new[1:-1, 1:-1] = (
T[1:-1, 1:-1]
+ alpha * dt * (
(T[1:-1, 2:] - 2 * T[1:-1, 1:-1] + T[1:-1, :-2]) / dx**2
+ (T[2:, 1:-1] - 2 * T[1:-1, 1:-1] + T[:-2, 1:-1]) / dy**2
)
)
return T_new
# Time-stepping loop
n_steps = int(total_time / dt)
snapshots = [0, int(4 / dt), int(8 / dt), int(12 / dt)]
temperatures = []
for step in range(n_steps + 1):
if step in snapshots:
temperatures.append(T.copy())
T = update_temperature(T, alpha, dt, dx, dy)
# Visualize the temperature field at each snapshot
fig, axes = plt.subplots(1, 4, figsize=(16, 4), sharey=True)
for i, ax in enumerate(axes):
im = ax.imshow(temperatures[i], cmap='hot', origin='lower', extent=[0, Lx, 0, Ly])
ax.set_title(f"t = {i * 4.0} s")
axes[0].set_ylabel("y (m)")
for ax in axes:
ax.set_xlabel("x (m)")
fig.subplots_adjust(right=0.85)
cbar_ax = fig.add_axes([0.88, 0.15, 0.03, 0.7])
fig.colorbar(im, cax=cbar_ax, label="Temperature (°C)")
plt.show()
結果はこのようになる。
プログラム中に定義した変数をリファレンスとしてほしい
読むとわかるが、前項で掲載したコードは計算結果を出力する時刻(0, 4, 8, 12秒)を力業で決めている。そこで、計算時間の変数である「total_time」をリファレンスとして、計算結果を出力する時刻を自動で調整できるようにしたい。
プロンプトは以下。
ここのプロンプトはあまりスマートではない気がする。もっと呪文的なものがあるはずだ。「With "total_time" as a reference, …」も試したがうまくいかなかった。
ChatGPTの解答とコードは以下。
import numpy as np
import matplotlib.pyplot as plt
# Constants
alpha = 1e-4 # Thermal diffusivity (m²/s)
Lx, Ly = 1, 1 # Domain dimensions (m)
Nx, Ny = 100, 100 # Number of grid points
dx, dy = Lx / (Nx - 1), Ly / (Ny - 1) # Grid spacing (m)
dt = 0.25 * min(dx, dy)**2 / alpha # Time step (s)
total_time = 12 # Total simulation time (s)
num_snapshots = 4 # Number of snapshots (including the initial state)
# Initialize the temperature field
T = np.zeros((Ny, Nx))
# Initial conditions
T[Ny // 2 - 10 : Ny // 2 + 10, Nx // 2 - 10 : Nx // 2 + 10] = 100
# Calculate snapshot time steps
snapshot_interval = int((total_time / dt) / (num_snapshots - 1))
snapshots = [i * snapshot_interval for i in range(num_snapshots)]
# Function to update the temperature field using the finite difference method
def update_temperature(T, alpha, dt, dx, dy):
T_new = T.copy()
T_new[1:-1, 1:-1] = (
T[1:-1, 1:-1]
+ alpha * dt * (
(T[1:-1, 2:] - 2 * T[1:-1, 1:-1] + T[1:-1, :-2]) / dx**2
+ (T[2:, 1:-1] - 2 * T[1:-1, 1:-1] + T[:-2, 1:-1]) / dy**2
)
)
return T_new
# Time-stepping loop
n_steps = int(total_time / dt)
temperatures = []
for step in range(n_steps + 1):
if step in snapshots:
temperatures.append(T.copy())
T = update_temperature(T, alpha, dt, dx, dy)
# Visualize the temperature field at each snapshot
fig, axes = plt.subplots(1, num_snapshots, figsize=(4 * num_snapshots, 4), sharey=True)
for i, ax in enumerate(axes):
im = ax.imshow(temperatures[i], cmap='hot', origin='lower', extent=[0, Lx, 0, Ly])
ax.set_title(f"t = {i * total_time / (num_snapshots - 1):.1f} s")
axes[0].set_ylabel("y (m)")
for ax in axes:
ax.set_xlabel("x (m)")
fig.subplots_adjust(right=0.85)
cbar_ax = fig.add_axes([0.88, 0.15, 0.03, 0.7])
fig.colorbar(im, cax=cbar_ax, label="Temperature (°C)")
plt.show()
total_timeを変えれば、自動的に結果を出力する時刻が変わるはずだ。
colorbarをそれぞれのグラフに配置してほしい
まだちょっとおかしい。colorbarだ。それぞれのグラフに配置してほしい。プロンプトは以下。
import numpy as np
import matplotlib.pyplot as plt
# Constants
alpha = 1e-4 # Thermal diffusivity (m²/s)
Lx, Ly = 1, 1 # Domain dimensions (m)
Nx, Ny = 100, 100 # Number of grid points
dx, dy = Lx / (Nx - 1), Ly / (Ny - 1) # Grid spacing (m)
dt = 0.25 * min(dx, dy)**2 / alpha # Time step (s)
total_time = 12 # Total simulation time (s)
num_snapshots = 4 # Number of snapshots (including the initial state)
# Initialize the temperature field
T = np.zeros((Ny, Nx))
# Initial conditions
T[Ny // 2 - 10 : Ny // 2 + 10, Nx // 2 - 10 : Nx // 2 + 10] = 100
# Calculate snapshot time steps
snapshot_interval = int((total_time / dt) / (num_snapshots - 1))
snapshots = [i * snapshot_interval for i in range(num_snapshots)]
# Function to update the temperature field using the finite difference method
def update_temperature(T, alpha, dt, dx, dy):
T_new = T.copy()
T_new[1:-1, 1:-1] = (
T[1:-1, 1:-1]
+ alpha * dt * (
(T[1:-1, 2:] - 2 * T[1:-1, 1:-1] + T[1:-1, :-2]) / dx**2
+ (T[2:, 1:-1] - 2 * T[1:-1, 1:-1] + T[:-2, 1:-1]) / dy**2
)
)
return T_new
# Time-stepping loop
n_steps = int(total_time / dt)
temperatures = []
for step in range(n_steps + 1):
if step in snapshots:
temperatures.append(T.copy())
T = update_temperature(T, alpha, dt, dx, dy)
# Visualize the temperature field at each snapshot
fig, axes = plt.subplots(1, num_snapshots, figsize=(4 * num_snapshots, 4), sharey=True)
for i, ax in enumerate(axes):
im = ax.imshow(temperatures[i], cmap='hot', origin='lower', extent=[0, Lx, 0, Ly])
ax.set_title(f"t = {i * total_time / (num_snapshots - 1):.1f} s")
cbar = fig.colorbar(im, ax=ax, fraction=0.046, pad=0.04)
cbar.set_label("Temperature (°C)")
axes[0].set_ylabel("y (m)")
for ax in axes:
ax.set_xlabel("x (m)")
plt.show()
するとこうなる。
colorbarのスケールを合わせてほしい
スケールが合ってないので比較しづらい。スケールをそろえてほしいというプロンプトを打ち込む。
import numpy as np
import matplotlib.pyplot as plt
# Constants
alpha = 1e-4 # Thermal diffusivity (m²/s)
Lx, Ly = 1, 1 # Domain dimensions (m)
Nx, Ny = 100, 100 # Number of grid points
dx, dy = Lx / (Nx - 1), Ly / (Ny - 1) # Grid spacing (m)
dt = 0.25 * min(dx, dy)**2 / alpha # Time step (s)
total_time = 12 # Total simulation time (s)
num_snapshots = 4 # Number of snapshots (including the initial state)
# Initialize the temperature field
T = np.zeros((Ny, Nx))
# Initial conditions
T[Ny // 2 - 10 : Ny // 2 + 10, Nx // 2 - 10 : Nx // 2 + 10] = 100
# Calculate snapshot time steps
snapshot_interval = int((total_time / dt) / (num_snapshots - 1))
snapshots = [i * snapshot_interval for i in range(num_snapshots)]
# Function to update the temperature field using the finite difference method
def update_temperature(T, alpha, dt, dx, dy):
T_new = T.copy()
T_new[1:-1, 1:-1] = (
T[1:-1, 1:-1]
+ alpha * dt * (
(T[1:-1, 2:] - 2 * T[1:-1, 1:-1] + T[1:-1, :-2]) / dx**2
+ (T[2:, 1:-1] - 2 * T[1:-1, 1:-1] + T[:-2, 1:-1]) / dy**2
)
)
return T_new
# Time-stepping loop
n_steps = int(total_time / dt)
temperatures = []
for step in range(n_steps + 1):
if step in snapshots:
temperatures.append(T.copy())
T = update_temperature(T, alpha, dt, dx, dy)
# Visualize the temperature field at each snapshot
fig, axes = plt.subplots(1, num_snapshots, figsize=(4 * num_snapshots, 4), sharey=True)
# Calculate the global min and max temperature values
vmin = min([np.min(T) for T in temperatures])
vmax = max([np.max(T) for T in temperatures])
for i, ax in enumerate(axes):
im = ax.imshow(temperatures[i], cmap='hot', origin='lower', extent=[0, Lx, 0, Ly], vmin=vmin, vmax=vmax)
ax.set_title(f"t = {i * total_time / (num_snapshots - 1):.1f} s")
cbar = fig.colorbar(im, ax=ax, fraction=0.046, pad=0.04)
cbar.set_label("Temperature (°C)")
axes[0].set_ylabel("y (m)")
for ax in axes:
ax.set_xlabel("x (m)")
plt.show()
ちなみにツイッターで紹介したときはcolorbarがy軸とそろっていなかったので、こんなプロンプトを打ち込んだ。返ってきたコードをコピペすると直った。
subplotsを1×4から2×2に変更
ツイッターなどのSNSに投稿するならこのアスペクト比では見づらい。グラフを2×2に変更しよう。プロンプトは以下。
import numpy as np
import matplotlib.pyplot as plt
# Constants
alpha = 1e-4 # Thermal diffusivity (m²/s)
Lx, Ly = 1, 1 # Domain dimensions (m)
Nx, Ny = 100, 100 # Number of grid points
dx, dy = Lx / (Nx - 1), Ly / (Ny - 1) # Grid spacing (m)
dt = 0.25 * min(dx, dy)**2 / alpha # Time step (s)
total_time = 12 # Total simulation time (s)
num_snapshots = 4 # Number of snapshots (including the initial state)
# Initialize the temperature field
T = np.zeros((Ny, Nx))
# Initial conditions
T[Ny // 2 - 10 : Ny // 2 + 10, Nx // 2 - 10 : Nx // 2 + 10] = 100
# Calculate snapshot time steps
snapshot_interval = int((total_time / dt) / (num_snapshots - 1))
snapshots = [i * snapshot_interval for i in range(num_snapshots)]
# Function to update the temperature field using the finite difference method
def update_temperature(T, alpha, dt, dx, dy):
T_new = T.copy()
T_new[1:-1, 1:-1] = (
T[1:-1, 1:-1]
+ alpha * dt * (
(T[1:-1, 2:] - 2 * T[1:-1, 1:-1] + T[1:-1, :-2]) / dx**2
+ (T[2:, 1:-1] - 2 * T[1:-1, 1:-1] + T[:-2, 1:-1]) / dy**2
)
)
return T_new
# Time-stepping loop
n_steps = int(total_time / dt)
temperatures = []
for step in range(n_steps + 1):
if step in snapshots:
temperatures.append(T.copy())
T = update_temperature(T, alpha, dt, dx, dy)
# Visualize the temperature field at each snapshot
fig, axes = plt.subplots(2, 2, figsize=(8, 8), sharey=True, sharex=True)
# Calculate the global min and max temperature values
vmin = min([np.min(T) for T in temperatures])
vmax = max([np.max(T) for T in temperatures])
for i, ax in enumerate(axes.flat):
im = ax.imshow(temperatures[i], cmap='hot', origin='lower', extent=[0, Lx, 0, Ly], vmin=vmin, vmax=vmax)
ax.set_title(f"t = {i * total_time / (num_snapshots - 1):.1f} s")
cbar = fig.colorbar(im, ax=ax, fraction=0.046, pad=0.04)
cbar.set_label("Temperature (°C)")
axes[0, 0].set_ylabel("y (m)")
axes[1, 0].set_ylabel("y (m)")
for ax in axes[1]:
ax.set_xlabel("x (m)")
plt.tight_layout()
plt.show()
温度プロファイルを見たい
次に温度プロファイルを見てみよう。x方向のプロファイルを見たいとき、こう指示する。
import numpy as np
import matplotlib.pyplot as plt
# Constants
alpha = 1e-4 # Thermal diffusivity (m²/s)
Lx, Ly = 1, 1 # Domain dimensions (m)
Nx, Ny = 100, 100 # Number of grid points
dx, dy = Lx / (Nx - 1), Ly / (Ny - 1) # Grid spacing (m)
dt = 0.25 * min(dx, dy)**2 / alpha # Time step (s)
total_time = 12 # Total simulation time (s)
# Initialize the temperature field
T = np.zeros((Ny, Nx))
# Initial conditions
T[Ny // 2 - 10 : Ny // 2 + 10, Nx // 2 - 10 : Nx // 2 + 10] = 100
# Function to update the temperature field using the finite difference method
def update_temperature(T, alpha, dt, dx, dy):
T_new = T.copy()
T_new[1:-1, 1:-1] = (
T[1:-1, 1:-1]
+ alpha * dt * (
(T[1:-1, 2:] - 2 * T[1:-1, 1:-1] + T[1:-1, :-2]) / dx**2
+ (T[2:, 1:-1] - 2 * T[1:-1, 1:-1] + T[:-2, 1:-1]) / dy**2
)
)
return T_new
# Time-stepping loop
n_steps = int(total_time / dt)
for step in range(n_steps):
T = update_temperature(T, alpha, dt, dx, dy)
# Visualize the final temperature field and temperature profile at the center
fig, axes = plt.subplots(1, 2, figsize=(12, 4))
# Temperature distribution in the xy-plane
im = axes[0].imshow(T, cmap='hot', origin='lower', extent=[0, Lx, 0, Ly])
axes[0].set_title("Temperature distribution (xy-plane)")
axes[0].set_xlabel("x (m)")
axes[0].set_ylabel("y (m)")
cbar = fig.colorbar(im, ax=axes[0], fraction=0.046, pad=0.04)
cbar.set_label("Temperature (°C)")
# Temperature profile along the x-axis at the center of the domain
x_values = np.linspace(0, Lx, Nx)
axes[1].plot(x_values, T[Ny // 2, :], label="x-axis profile")
axes[1].set_title("Temperature profile at center")
axes[1].set_xlabel("x (m)")
axes[1].set_ylabel("Temperature (°C)")
axes[1].legend()
plt.tight_layout()
plt.show()
熱拡散率を変えて比較したい
これを熱拡散率αを変えて比較したい。プロンプトは以下。
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
# Constants
Lx, Ly = 1, 1 # Domain dimensions (m)
Nx, Ny = 100, 100 # Number of grid points
dx, dy = Lx / (Nx - 1), Ly / (Ny - 1) # Grid spacing (m)
total_time = 12.0 # Total simulation time (s)
thermal_diffusivities = [0.5 * 1e-4, 1e-4, 1.5 * 1e-4] # Different thermal diffusivities
def init_temperature_field(Nx, Ny):
T = np.zeros((Ny, Nx))
T[Ny // 2 - 10 : Ny // 2 + 10, Nx // 2 - 10 : Nx // 2 + 10] = 100
return T
def update_temperature(T, alpha, dt, dx, dy):
T_new = T.copy()
T_new[1:-1, 1:-1] = (
T[1:-1, 1:-1]
+ alpha * dt * (
(T[1:-1, 2:] - 2 * T[1:-1, 1:-1] + T[1:-1, :-2]) / dx**2
+ (T[2:, 1:-1] - 2 * T[1:-1, 1:-1] + T[:-2, 1:-1]) / dy**2
)
)
return T_new
fig, axes = plt.subplots(3, 2, figsize=(12, 12), sharex='col', sharey='row')
for i, alpha in enumerate(thermal_diffusivities):
dt = 0.25 * min(dx, dy)**2 / alpha # Time step (s)
T = init_temperature_field(Nx, Ny)
n_steps = int(total_time / dt)
for step in range(n_steps + 1):
T = update_temperature(T, alpha, dt, dx, dy)
# Plot the final temperature distribution in the xy-plane
im = axes[i, 0].imshow(T, cmap='hot', origin='lower', extent=[0, Nx, 0, Ny])
axes[i, 0].set_title(f'Temperature Distribution (α = {alpha:.1e})')
axes[i, 0].set_ylabel('y (m)')
# Create a custom colorbar axis with the same length as the y-axis
divider = make_axes_locatable(axes[i, 0])
cax = divider.append_axes("right", size="5%", pad=0.1)
fig.colorbar(im, cax=cax, label='Temperature (°C)')
# Plot the temperature profile along the x-axis through the center
x = np.linspace(0, Lx, Nx)
axes[i, 1].plot(x, T[Ny // 2, :], label=f'α = {alpha:.1e}')
axes[i, 1].set_title(f'Temperature Profile (α = {alpha:.1e})')
axes[i, 1].set_ylabel('Temperature (°C)')
# Set the x-axis label for the bottom subplots
for ax in axes[-1, :]:
ax.set_xlabel('x (m)')
plt.suptitle('2D Heat Transfer for Different Thermal Diffusivities')
plt.tight_layout(rect=[0.1, 0.03, 1, 0.95]) # Increase left margin to 0.1
plt.show()
所感
いかがだろうか。今回紹介した、
コードを書く
結果を出力する
結果を見てコードを手直しする
というサイクルは、数値計算プログラミングの実務そのものである。
見ての通り、ChatGPTを使うことで、このサイクルをとても効率よく回すことができるのだ。コードを書くのが問答無用でめちゃくちゃ早いし、修正も残酷なほど正確である。
そして最もおそろしいのは「何回フィードバックをかけても嫌な顔一つしない」というところだ。この点において少なくとも筆者は、プログラマーとしてChatGPTに完敗したと認めざるを得ない。
そしてこれを使いこなす新卒が現れたら・・・
想像だけで号泣できるレベルだ。今までPythonにかけた時間、アホみたいに買った「Pythonで学ぶ〇〇」、そんなこれまでの努力を全否定する。それくらいの破壊力がある。
「ChatGPT×Python」を習得すれば君も鬼になれる。新入社員のみんな、鬼にならないか?
関連記事
今回紹介したのはChatGPTのプログラムを「書く」能力だが、「読む」能力もおそろしいほど高いことがわかった。こちらでまとめているので興味がある人は読んでみてほしい。
この記事が気に入ったらサポートをしてみませんか?