マテリアルズインフォマティクス(MI)入門⑧【AIは新材料を発見できるか?ー HTVSによる仮想材料探索】

マテリアルズインフォマティクス(MI)入門⑧【AIは新材料を発見できるか?ー HTVSによる仮想材料探索】

前回の第7回では、複数の物性を同時に評価する「多目的最適化」に挑み、性能の限界線である「パレートフロント」を可視化しました。その結果、私たちは既存のデータの中から最も優れた特性バランスを持つ「チャンピオン材料」を特定することができました。

しかし、材料科学者の真の目標は、既知の材料の中から最良のものを選ぶことだけではありません。まだ誰も見たことのない、未知の優れた材料を発見することこそが、MIに課せられた究極の使命です。では、どうすればチャンピオンを超える材料を見つけ出せるのでしょうか?

そこで第8回となる今回は、MIを「分析ツール」から「探索エンジン」へと進化させます。前回特定したチャンピオン材料の周辺に焦点を絞り、その近傍の組成を仮想的に多数生成・評価する「局所的」なハイスループット・バーチャルスクリーニング(HTVS)を実践します。この「一点集中型」の探索を通じて、既存のチャンピオンをわずかに、しかし確実に上回る、改良された組成を発見するプロセスを体験しましょう。

動作検証済み環境

Google Colaboratory
Python 3.11.13
matminer==0.9.3
pandas==2.2.2
scikit-learn==1.6.1
matplotlib==3.10.0
catboost==1.2.8

この記事から学べること

  • 局所探索HTVSの概念: 広大な空間を闇雲に探すのではなく、有望な候補の周辺を重点的に探索する、効率的な材料改良アプローチを理解できます。
  • 探索空間の設計: 特定の材料の周囲に、探索範囲を意図的に絞り込む方法を学べます。
  • 仮想的な材料改良の実践: 学習済みモデルを使い、既存のチャンピオン材料の組成を微調整しながら、それを超える性能を持つ候補を探索するプロセスをハンズオンで体験できます。
  • チャンピオンデータの更新: 局所探索HTVSによって、既存のチャンピオンを上回る新たな有望材料を発見し、性能の限界点をさらに押し上げるアプローチを学べます。

関連理論の解説

1. 局所探索HTVS:巨人の肩の上に乗る戦略

HTVS(ハイスループット・バーチャルスクリーニング)には様々な戦略がありますが、今回はその中でも特に実用的な局所探索(Local Search)に焦点を当てます。これは、既に発見されている最も優れた材料(チャンピオンデータ)の周辺に、さらに優れた解が存在する可能性が高い、という考え方に基づいています。

  • 仕組み: 広大な組成空間全体をランダムに探すのではなく、チャンピオン材料の組成を基準とし、その各元素の含有率をわずかに(例えば±5%)変化させた候補を大量に生成します。そして、その「少しだけ違う」候補たちの性能を予測モデルで評価します。
  • 利点: このアプローチは、「宝の山の近くには、まだ掘り出されていない宝が眠っている」という経験則に基づいた、非常に効率的な戦略です。既存の優れた発見を無駄にせず、それを足掛かりにさらなる高みを目指す「改良」や「洗練」のプロセスに適しています。
  • 役割: 新規材料の「大発見」を狙う大域的探索とは異なり、局所探索は既存の高性能材料の「微調整」や「最適化」という、より現実的で着実な成果が期待できる場面で威力を発揮します。

2. 全データ学習:探索エンジンの性能を最大化する

これまでの記事では、モデルの性能を「評価」するために、データを訓練用とテスト用に分割していました。しかし、今回の目的はモデルの評価ではなく、「探索」です。最高の探索エンジンを構築するため、私たちは手元にある全てのデータをモデルの学習に投入します。

これにより、モデルはデータセットが持つ情報を最大限に吸収し、より精度の高い予測が可能になります。この「知識を最大化したモデル」こそが、信頼性の高い仮想実験を行うための基盤となるのです。

実装方法

ワークフロー

今回は、まず全データからチャンピオン材料を特定し、その知識を最大限に詰め込んだモデルを構築。そのモデルを使って、チャンピオン周辺の局所的な探索を行います。

# ===================================================================
# 0. 環境構築:必要なライブラリのインストール
# 1. 必要なライブラリのインポート
# 2. matminerから実データセットの読み込み
# 3. 全データの中からチャンピオン材料を特定
# 4. 全データを用いて代理モデルを訓練
# 5. チャンピオン材料周辺に探索空間を定義
# 6. HTVSによる局所的ランダム探索の実行
# 7. 探索結果の可視化とチャンピオン超え候補の特定
# ===================================================================

実行手順

  • 以下のコードブロック全体をコピーします。
  • Google Colaboratoryの新しいセルに貼り付けます。
  • セルが選択されていることを確認し、Shift キーと Enter キーを同時に押してコードを実行します。
# ===================================================================
# 0. 環境構築:必要なライブラリのインストール
# ===================================================================
!pip install matminer==0.9.3 scikit-learn==1.6.1 pandas==2.2.2
!pip install matplotlib==3.10.0 catboost==1.2.8
# ===================================================================
# 1. 必要なライブラリのインポート
# ===================================================================
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from catboost import CatBoostRegressor
from matminer.datasets import load_dataset
# ===================================================================
# 2. matminerから実データセットの読み込み
# ===================================================================
print("ステップ2: matminerから実データセットを読み込み、前処理します...")
df = load_dataset("steel_strength")
df_clean = df.dropna().reset_index(drop=True)
print(f"データ数: {len(df_clean)}")
features = [ col for col in df_clean.columns if col not in [ "composition", "formula", "yield strength", "tensile strength", "elongation", ]
]
targets = ["yield strength", "tensile strength"]
X = df_clean[features]
y = df_clean[targets]
print("データ準備完了。")
# ===================================================================
# 3. 全データの中からチャンピオン材料を特定
# ===================================================================
print("\nステップ3: 全データの中からチャンピオン材料を特定します...")
# ここでは、2つの強度の合計値が最も高いものをチャンピオンとする
y_sum = y.sum(axis=1)
champion_index = y_sum.idxmax()
champion_composition = X.loc[champion_index]
champion_properties = y.loc[champion_index]
print("\n【チャンピオン材料の組成と物性】")
print(champion_composition)
print( f"\n降伏強度: {champion_properties['yield strength']} MPa" f"\n引張強さ: {champion_properties['tensile strength']} MPa"
)
# ===================================================================
# 4. 全データを用いて代理モデルを訓練
# ===================================================================
print("\nステップ4: 全データを用いて代理モデルを学習させます...")
# X, y (全データ) を使って学習
model = CatBoostRegressor(random_seed=42, verbose=0, loss_function="MultiRMSE")
model.fit(X, y)
print("全データでのモデル学習が完了しました。")
# ===================================================================
# 5. チャンピオン材料周辺に探索空間を定義
# ===================================================================
print("\nステップ5: チャンピオン材料周辺の局所的な探索空間を定義します...")
# 各元素の組成に対して±5%の範囲を探索空間とする
search_range_ratio = 0.05
comp_min = champion_composition * (1 - search_range_ratio)
comp_max = champion_composition * (1 + search_range_ratio)
# 0のままにしたい元素は範囲を0に固定
comp_min[champion_composition == 0] = 0
comp_max[champion_composition == 0] = 0
print("\n【局所的探索空間の範囲(一部)】")
print(pd.DataFrame({"min": comp_min, "max": comp_max}).head())
# ===================================================================
# 6. HTVSによる局所的ランダム探索の実行
# ===================================================================
N_TRIALS = 500
print(f"\nステップ6: 局所的HTVSを開始します... (試行回数: {N_TRIALS})")
# 定義した狭い探索空間内でランダムな組成を生成
random_compositions = pd.DataFrame( np.random.uniform(low=comp_min, high=comp_max, size=(N_TRIALS, len(features))), columns=features,
)
# ランダム組成の物性を予測
local_search_pred = model.predict(random_compositions)
print("局所探索が完了しました。")
# ===================================================================
# 7. 探索結果の可視化とチャンピオン超え候補の特定
# ===================================================================
print("\nステップ7: 探索結果を可視化します...")
# チャンピオンの物性を超えた候補を特定
better_candidates_indices = []
for i, (ys, ts) in enumerate(local_search_pred): if ( ys >= champion_properties["yield strength"] and ts > champion_properties["tensile strength"] or ys > champion_properties["yield strength"] and ts >= champion_properties["tensile strength"] ): better_candidates_indices.append(i)
print( f"\nチャンピオンを超える新規候補を {len(better_candidates_indices)} 個発見しました。"
)
# 可視化
plt.figure(figsize=(12, 10))
# 全学習データを背景としてプロット
plt.scatter( y["tensile strength"], y["yield strength"], alpha=0.1, c="gray", label="All Training Data",
)
# HTVSの探索結果をプロット
plt.scatter( local_search_pred[:, 1], local_search_pred[:, 0], c="lightgreen", s=100, marker="o", alpha=0.5, label=f"Local Search Candidates ({N_TRIALS} trials)",
)
# チャンピオン材料の点をプロット
plt.scatter( champion_properties["tensile strength"], champion_properties["yield strength"], c="blue", s=250, marker="o", edgecolors="white", zorder=10, label="Original Champion",
)
# チャンピオンを超えた候補をプロット
if len(better_candidates_indices) > 0: better_points = local_search_pred[better_candidates_indices] plt.scatter( better_points[:, 1], better_points[:, 0], c="magenta", s=200, marker="*", edgecolors="black", zorder=11, label="Newly Discovered Superior Candidates", )
plt.xlabel("Tensile Strength (MPa)", fontsize=14)
plt.ylabel("Yield Strength (MPa)", fontsize=14)
plt.title( f"Local Random Search around Champion", fontsize=16,
)
plt.legend(fontsize=12)
plt.grid(True)
plt.show()
# 新規有望材料の組成を表示
if len(better_candidates_indices) > 0: newly_discovered_df = random_compositions.iloc[better_candidates_indices].copy() newly_discovered_df["predicted_yield_strength"] = local_search_pred[ better_candidates_indices, 0 ] newly_discovered_df["predicted_tensile_strength"] = local_search_pred[ better_candidates_indices, 1 ] print("\n【チャンピオンを超えた新規有望材料の組成】") print( newly_discovered_df.sort_values( "predicted_tensile_strength", ascending=False ).round(4) )
print("\n処理はすべて完了しました。")

実行結果と考察

チャンピオン材料の特定

まず、全データの中から最も優れた材料(ここでは2つの強度の合計値が最大のもの)を探索の出発点となる「チャンピオン」として特定します。

【チャンピオン材料の組成と物性】
c 0.01
mn 0.05
si 0.05
cr 0.01
ni 17.50
mo 4.15
v 0.01
n 0.00
nb 0.01
co 11.50
w 0.00
al 0.62
ti 1.48
降伏強度: 2510.3 MPa, 引張強さ: 2570.0 MPa

この材料が、我々が超えるべき目標となります。

局所的HTVSによる材料改良の結果

グラフから読み解く材料改良のプロセス:

  • 灰色の点群 (All Training Data): 背景にプロットされた、学習に用いた全てのデータです。
  • 青い大きな点 (Original Champion): 我々が特定した、既存データの中で最も優れたチャンピオン材料の位置を示します。
  • 緑色の点群 (Local Search Candidates): これが局所的HTVSの探索結果です。チャンピオンの組成の±5%という非常に狭い範囲を探索したため、すべての点がチャンピオンの周辺に密集しているのが見て取れます。
  • マゼンタ色の星印 (Newly Discovered Superior Candidates): これこそが、今回の探索の最大の成果です。緑色の点群の中から、元のチャンピオン(青い点)を上回る性能を持つと予測された、2つの新たな有望候補です。

この結果は、たとえ既存のチャンピオンが非常に優れたものであっても、その周辺をMIで集中的に探索することで、さらなる改良の余地を見つけ出せることを明確に示しています。

発見された新規有望材料の組成と性能:

predicted_yield_strength (MPa)predicted_tensile_strength (MPa)c (%)mn (%)si (%)
2518.92052587.00830.00960.04920.0508
2517.97772586.44320.00980.04900.0475

新しく発見された候補は、元のチャンピオン(引張強さ: 2570.0 MPa)と比較して、約15MPa高い引張強さを持つと予測されています。これは、組成をわずかに調整することで、性能をさらに引き出せる可能性を示唆しています。

コードの詳細解説

ステップ3: 全データの中からチャンピオン材料を特定

# ===================================================================
# 3. 全データの中からチャンピオン材料を特定
# ===================================================================
print("\nステップ3: 全データの中からチャンピオン材料を特定します...")
# ここでは、2つの強度の合計値が最も高いものをチャンピオンとする
y_sum = y.sum(axis=1)
champion_index = y_sum.idxmax()
champion_composition = X.loc[champion_index]
champion_properties = y.loc[champion_index]
print("\n【チャンピオン材料の組成と物性】")
print(champion_composition)
print( f"\n降伏強度: {champion_properties['yield strength']} MPa" f"\n引張強さ: {champion_properties['tensile strength']} MPa"
)
  • y.sum(axis=1): ターゲットである**y**(降伏強度と引張強さの2列)の、行ごとの合計値を計算します。多目的最適化の文脈では、パレートフロント上の全ての点が「最適解」ですが、今回は局所探索の「中心点」を一つに定めるため、この合計値が最も高いものを「総合的に最も優れた材料(チャンピオン)」と定義する、シンプルな方法を採用しました。
  • .idxmax(): 合計値が最大となった行のインデックス番号を取得します。
  • .loc[champion_index]: 取得したインデックスを使い、特徴量**Xと物性値y**から、チャンピオン材料の具体的な情報を抽出します。この情報が、次のステップである探索空間設計の基準となります。

ステップ4: 全データを用いて代理モデルを訓練

# ===================================================================
# 4. 全データを用いて代理モデルを訓練
# ===================================================================
print("\nステップ4: 全データを用いて代理モデルを学習させます...")
# X, y (全データ) を使って学習
model = CatBoostRegressor(random_seed=42, verbose=0, loss_function="MultiRMSE")
model.fit(X, y)
print("全データでのモデル学習が完了しました。")
  • 戦略の転換: これまでの記事では、モデルの「汎化性能(未知のデータに対する予測精度)」を客観的に評価するため、データを訓練用とテスト用に分割していました。しかし、今回はモデルの性能評価フェーズは完了したとみなし、いざ「最高の材料を発見する」という探索フェーズに移行します。
  • 知識の最大化X_trainではなく、手元にある全ての情報(Xy)を学習に用いています。これにより、モデルはデータセットに関する最大限の知識を持つことになり、特に既知のデータ範囲内(内挿領域)における予測精度が向上します。これが、局所探索の精度を高める上で重要な役割を果たします。

ステップ5: チャンピオン材料周辺に探索空間を定義

# ===================================================================
# 5. チャンピオン材料周辺に探索空間を定義
# ===================================================================
print("\nステップ5: チャンピオン材料周辺の局所的な探索空間を定義します...")
# 各元素の組成に対して±5%の範囲を探索空間とする
search_range_ratio = 0.05
comp_min = champion_composition * (1 - search_range_ratio)
comp_max = champion_composition * (1 + search_range_ratio)
# 0のままにしたい元素は範囲を0に固定
comp_min[champion_composition == 0] = 0
comp_max[champion_composition == 0] = 0
print("\n【局所的探索空間の範囲(一部)】")
print(pd.DataFrame({"min": comp_min, "max": comp_max}).head())
  • search_range_ratio: 探索範囲の広さを定義するパラメータです。ここでは0.05、つまりチャンピオンの各元素量の**±5%の範囲**を探索します。この値を小さくすればより狭い範囲を、大きくすればより広い範囲を探索できます。
  • 探索空間の計算: **comp_mincomp_maxで、各元素の探索範囲の最小値と最大値を計算し、局所的な探索空間を定義しています。例えば、チャンピオンのNiが18.0%なら、探索範囲は18.0 * 0.95から18.0 * 1.05**となります。
  • ゼロ含有元素の処理: **comp_min[champion_composition == 0] = 0**の行は、元々含まれていなかった元素(含有量0%)が、探索中に誤って追加されないようにするための重要な処理です。

ステップ6 & 7: HTVSによる局所探索と結果の特定・可視化

# ===================================================================
# 6. HTVSによる局所的ランダム探索の実行
# ===================================================================
N_TRIALS = 500
print(f"\nステップ6: 局所的HTVSを開始します... (試行回数: {N_TRIALS})")
# 定義した狭い探索空間内でランダムな組成を生成
random_compositions = pd.DataFrame( np.random.uniform(low=comp_min, high=comp_max, size=(N_TRIALS, len(features))), columns=features,
)
# ランダム組成の物性を予測
local_search_pred = model.predict(random_compositions)
print("局所探索が完了しました。")
# ===================================================================
# 7. 探索結果の可視化とチャンピオン超え候補の特定
# ===================================================================
print("\nステップ7: 探索結果を可視化します...")
# チャンピオンの物性を超えた候補を特定
better_candidates_indices = []
for i, (ys, ts) in enumerate(local_search_pred): if ( ys >= champion_properties["yield strength"] and ts > champion_properties["tensile strength"] or ys > champion_properties["yield strength"] and ts >= champion_properties["tensile strength"] ): better_candidates_indices.append(i)
print( f"\nチャンピオンを超える新規候補を {len(better_candidates_indices)} 個発見しました。"
)
# 可視化
plt.figure(figsize=(12, 10))
# 全学習データを背景としてプロット
plt.scatter( y["tensile strength"], y["yield strength"], alpha=0.1, c="gray", label="All Training Data",
)
# HTVSの探索結果をプロット
plt.scatter( local_search_pred[:, 1], local_search_pred[:, 0], c="lightgreen", s=100, marker="o", alpha=0.5, label=f"Local Search Candidates ({N_TRIALS} trials)",
)
# チャンピオン材料の点をプロット
plt.scatter( champion_properties["tensile strength"], champion_properties["yield strength"], c="blue", s=250, marker="o", edgecolors="white", zorder=10, label="Original Champion",
)
# チャンピオンを超えた候補をプロット
if len(better_candidates_indices) > 0: better_points = local_search_pred[better_candidates_indices] plt.scatter( better_points[:, 1], better_points[:, 0], c="magenta", s=200, marker="*", edgecolors="black", zorder=11, label="Newly Discovered Superior Candidates", )
plt.xlabel("Tensile Strength (MPa)", fontsize=14)
plt.ylabel("Yield Strength (MPa)", fontsize=14)
plt.title( f"Local Random Search around Champion", fontsize=16,
)
plt.legend(fontsize=12)
plt.grid(True)
plt.show()
# 新規有望材料の組成を表示
if len(better_candidates_indices) > 0: newly_discovered_df = random_compositions.iloc[better_candidates_indices].copy() newly_discovered_df["predicted_yield_strength"] = local_search_pred[ better_candidates_indices, 0 ] newly_discovered_df["predicted_tensile_strength"] = local_search_pred[ better_candidates_indices, 1 ] print("\n【チャンピオンを超えた新規有望材料の組成】") print( newly_discovered_df.sort_values( "predicted_tensile_strength", ascending=False ).round(4) )
  • HTVSの実行: np.random.uniformで、ステップ5で定義した狭い探索空間(comp_mincomp_max)内でランダムな組成を生成し、モデルで一括評価します。
  • 候補の特定ロジック: 探索結果**local_search_pred**を一つずつチェックし、その両方の物性値がチャンピオン材料の物性値を上回っているか(パレート支配しているか)を判定し、条件を満たす候補のインデックスを保存しています

最後に

今回は、MIを用いた材料開発の応用編として、既存の最高性能を持つ「チャンピオン材料」をさらに改良するための局所的HTVSを実践しました。チャンピオンの周辺に探索空間を絞り、集中的に仮想実験を行うことで、元の性能をわずかに上回る有望な新規候補を発見できることを示しました。

このアプローチは、全く新しい材料を一から探す「大域的探索」とは対照的に、既存の高性能材料の「最終的な磨き上げ」や「レシピの微調整」といった、極めて実用的な課題解決に貢献します。

しかし、もしチャンピオンの周辺にこれ以上優れた解が存在しなかった場合、この局所探索は行き詰まってしまいます。より賢く、より大域的な視野で有望な領域を探索するにはどうすればよいのでしょうか。

次回は、この探索プロセスをさらに進化させます。モデルの予測値とその不確かさ(自信のなさ)の両方を巧みに利用し、「次に試すべき最も有望な一手」をAIが自律的に提案する、より洗練された探索アルゴリズム「ベイズ最適化」の世界に足を踏み入れます。ぜひご期待ください。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です