はじめに
近年、マテリアルズインフォマティクス(MI)は新材料の開発や物性予測において重要な役割を果たしています。性能の良いモデルを作るためには良質なデータを活用することが不可欠である一方、材料データはまだ十分に整備されておらず、データセットの標準化やモデル評価の難しさなどが課題として存在します。
こういった課題を解決するために開発されたのがMatbenchです。Matbenchを使うことで高品質な材料データを簡単に取得することができます。本記事では使い方をサンプルコードとともに解説します。
Matbenchとは
Matbench は、マテリアルズインフォマティクス(MI)分野における機械学習モデルの性能評価を簡単かつ客観的に行うための Python ライブラリです。公式サイトの記述によれば、「材料科学におけるImageNet」を目指して作られたとのことです。
- 物性予測や構造解析など、様々な MI タスクに対応したデータセットを統一された形式で提供
- 各データセットに対して、標準的な機械学習タスク(回帰、分類など)を定義し、モデルが取り組むべき問題を明確化
- モデルの性能を比較するための評価指標(MAE、RMSE、AUC など)を統一し、客観的な評価が可能
といった点がポイントになると思います。
リーダーボード
Matbench では、様々なモデルの性能を比較するためのリーダーボードが公開されています。リーダーボードは、各データセットとタスクにおける最高性能を達成したモデルや、その性能指標(MAE、RMSE、AUC など)を確認することができます。
リーダーボードの詳細はこちらのページで確認できます。ダウンロードなどもここから行うことができます。
Task nameと書いてあるのが各タスク名です。機械学習モデルが予測すべきターゲットごとにデータセットや評価指標が分けられています。例えば以下のようなタスクが用意されています。
- matbench_mp_e_form: 材料の形成エネルギーを予測するタスク
- matbench_mp_gap: 材料のバンドギャップを予測するタスク
- matbench_mp_is_metal: 材料が金属であるかどうかを分類するタスク
- matbench_elastic: 材料の弾性率を予測するタスク
- matbench_dielectric: 材料の誘電率を予測するタスク
リーダーボードを参考に、自分のモデルの性能を評価したり、他の研究者の成果を参考にしたりすることができます。
Matbenchの使い方
実行環境
- Windows 11
- anaconda: 24.11.3
- python: 3.9
- jupyter: 1.1
インストール方法
pipの場合
pip install matbench
condaの場合
conda install matbench
パッケージが見つからない場合は以下のコマンドを実行後に上記を実行してください。
conda config --append channels conda-forge
Pythonを使った動かし方
モジュールのインポート
インストールが正しくできていれば、以下のコードでインポートができると思います。
from matbench.bench import MatbenchBenchmark
クラスの初期化
mb = MatbenchBenchmark(autoload=False)
これを実行することで、以下のような出力が得られます。ここでタスクの一覧も確認できます。
2025-02-25 23:20:50 INFO Initialized benchmark 'matbench_v0.1' with 13 tasks:
['matbench_dielectric',
'matbench_expt_gap',
'matbench_expt_is_metal',
'matbench_glass',
'matbench_jdft2d',
'matbench_log_gvrh',
'matbench_log_kvrh',
'matbench_mp_e_form',
'matbench_mp_gap',
'matbench_mp_is_metal',
'matbench_perovskites',
'matbench_phonons',
'matbench_steels']
タスクの選択とデータ読み込み
例えば、材料の誘電率を予測するmatbench_dielectricのタスクを選んでみます。タスクは、下記のようにMatbenchBenchmarkクラスの属性として選択することができます。
selected_task = mb.matbench_dielectric #誘電率予測のタスクを選択
print(selected_task)
選択したタスクをprintしてみると、以下のように入力形式、サンプル数などデータセットの情報を確認できます。
MatbenchTask(
dataset_name=matbench_dielectric,
version=0.1,
input_type=structure,
mad=0.808534704217072,
n_samples=4764,
target=n,
task_type=regression,
unit=unitless,
)
次に、loadメソッドを使うことでデータセットの読み込みを行うことができます。
selected_task.load()
すると、このように読み込みが行われます。
2025-02-26 22:32:00 INFO Loading dataset 'matbench_dielectric'...
2025-02-26 22:32:04 INFO Dataset 'matbench_dielectric loaded.
これでデータセットの読み込みは完了です。
データの形式
取得したデータは、以下のようにpandasのデータフレームとして取得できます。
df = selected_task.df
カラムの一覧を見てみると、
print(df.columns)
"""
出力結果:
Index(['structure', 'n'], dtype='object')
"""
2つのカラムがありますが、'structure'が説明変数である材料の構造データ、'n'が目的変数である誘電率にあたります。
structureの方の中身を見てみると、
print(df["structure"])
以下のような出力が得られます。
mbid
mb-dielectric-0001 [[4.29304147 2.4785886 1.07248561] S, [4.2930...
mb-dielectric-0002 [[3.95051434 4.51121437 0.28035002] K, [4.3099...
mb-dielectric-0003 [[-1.78688104 4.79604117 1.53044621] Rb, [-1...
mb-dielectric-0004 [[4.51438064 4.51438064 0. ] Mn, [0.133...
mb-dielectric-0005 [[-4.36731958 6.8886097 0.50929706] Li, [-2...
...
mb-dielectric-4760 [[ 2.79280881 0.12499663 -1.84045389] Ca, [-2...
mb-dielectric-4761 [[-1.45848057e-16 5.50363806e+00 3.84192106e...
mb-dielectric-4762 [[0. 0. 0.] Ba, [ 0.23821924 4.32393487 -0.35...
mb-dielectric-4763 [[0. 0.18884638 0. ] K, [0. ...
mb-dielectric-4764 [[0. 0. 0.] Cs, [2.80639641 2.80639641 2.80639...
Name: structure, Length: 4764, dtype: object
データフレームの行はmbid、つまりデータセット上の材料のIDが振られています。
データの中身はこの出力だと見づらいですが、例えば最初のデータを確認してみると、
print(type(df["structure"][0])) #出力結果: <class 'pymatgen.core.structure.Structure'>
pymatgenのStructure型で得られていることがわかります。
データをprintしてみると以下のような出力が得られます。
print(df["structure"][0]))
Full Formula (K6 S6)
Reduced Formula: KS
abc : 8.586083 8.586083 5.920854
angles: 90.000000 90.000000 120.000000
Sites (12)
# SP a b c magmom
--- ---- -------- -------- -------- --------
0 S 0.666667 0.333333 0.181137 0
1 S 0.666667 0.333333 0.818863 0
2 S 0.333333 0.666667 0.818863 0
3 S 0.333333 0.666667 0.181137 0
4 S 0 0 0.67987 0
5 S 0 0 0.32013 0
6 K 0 0.642561 0.5 0
7 K 0.357439 0.357439 0.5 0
8 K 0.642561 0 0.5 0
9 K 0 0.307123 0 0
10 K 0.692877 0.692877 0 0
11 K 0.307123 0 0 0
別の記事でも紹介しましたが、このStructureオブジェクトからは格子定数と格子角、原子の種類と占有率、単位格子の体積、密度など様々な情報が取得できます。Structureオブジェクトはこのように単体ではまだ抽象化されたクラスなので、実際にモデルのトレーニングを行うにはこのオブジェクトから様々な情報を取り出して前処理を行う必要があると思われます。
データの分割
matbenchにはトレーニング、検証、テスト用にデータを分割する関数も用意されています。トレーニング、検証用にはget_train_and_val_dataメソッド、テスト用データ取得にはget_test_dataメソッドを使用します。
これらの関数にはfoldという引数を使用します。これについての具体的な説明が見当たらず推測になりますが、おそらく学習用、テスト用データの分け方の種類を表していると思われます。具体的には、
print(selected_task.folds)
の出力を見ると、
[0, 1, 2, 3, 4]
このようにリストの出力が得られ、計5種類のデータの分け方が用意されていると思われます。
例えば、fold: 0を使ってデータを読み込む場合、以下のようなコードになります。
fold = 0
train_inputs, train_outputs = selected_task.get_train_and_val_data(fold) #学習・検証用データ
test_inputs, test_outputs = selected_task.get_test_data(fold, include_target=True) #テスト用データ
get_test_dataメソッドの方は、デフォルトでは目的変数のデータは出力されないようになっていますが、include_target=Trueとすることで取得できます。
このデータを確認してみると、
print(type(train_inputs)) #出力: <class 'pandas.core.series.Series'>
print(train_inputs.shape, train_outputs.shape) #出力: (3811,) (3811,)
print(test_inputs.shape, test_outputs.shape) #出力: (953,) (953,)
データはpandasのSeries型で取得され、学習用とテストデータは80%, 20%に分割されていることがわかります。
foldの値を変えることで、データの分けられ方が変わることも確認できます。
例えばfold=0の場合、train_inputsの中身を見てみます。左のIDに注目してください。
mbid
mb-dielectric-0001 [[4.29304147 2.4785886 1.07248561] S, [4.2930...
mb-dielectric-0002 [[3.95051434 4.51121437 0.28035002] K, [4.3099...
mb-dielectric-0003 [[-1.78688104 4.79604117 1.53044621] Rb, [-1...
mb-dielectric-0004 [[4.51438064 4.51438064 0. ] Mn, [0.133...
mb-dielectric-0005 [[-4.36731958 6.8886097 0.50929706] Li, [-2...
...
mb-dielectric-4758 [[ 0.10737615 -5.39167724 -4.04533555] Mo, [ 0...
mb-dielectric-4759 [[0.06754224 4.23094828 1.68380672] Cr, [1.886...
mb-dielectric-4761 [[-1.45848057e-16 5.50363806e+00 3.84192106e...
mb-dielectric-4762 [[0. 0. 0.] Ba, [ 0.23821924 4.32393487 -0.35...
mb-dielectric-4764 [[0. 0. 0.] Cs, [2.80639641 2.80639641 2.80639...
Name: structure, Length: 3811, dtype: object
次にfold=3としてみると、左のIDの並びが変わります。
mbid
mb-dielectric-0003 [[-1.78688104 4.79604117 1.53044621] Rb, [-1...
mb-dielectric-0005 [[-4.36731958 6.8886097 0.50929706] Li, [-2...
mb-dielectric-0006 [[0.04903784 0.0347292 0.08458426] Ca, [3.740...
mb-dielectric-0007 [[5.60800905 1.10640796 1.26351442] Se, [3.762...
mb-dielectric-0008 [[ 4.5571751 2.77317895 16.01717369] O, [0.5...
...
mb-dielectric-4760 [[ 2.79280881 0.12499663 -1.84045389] Ca, [-2...
mb-dielectric-4761 [[-1.45848057e-16 5.50363806e+00 3.84192106e...
mb-dielectric-4762 [[0. 0. 0.] Ba, [ 0.23821924 4.32393487 -0.35...
mb-dielectric-4763 [[0. 0.18884638 0. ] K, [0. ...
mb-dielectric-4764 [[0. 0. 0.] Cs, [2.80639641 2.80639641 2.80639...
Name: structure, Length: 3811, dtype: object
このように、foldを変えることで学習用とテスト用のデータの分け方を変化させることができます。
以下はホームページからとってきたソースコードですが、実際は下の表のように各foldごとで別々にベンチマークがなされるようです。モデルの汎化誤差を正しく評価するため、いろいろな分け方のデータで評価をしているということでしょうか。
for fold in task.folds:
# Inputs are either chemical compositions as strings
# or crystal structures as pymatgen.Structure objects.
# Outputs are either floats (regression tasks) or bools (classification tasks)
train_inputs, train_outputs = task.get_train_and_val_data(fold)
# train and validate your model
my_model.train_and_validate(train_inputs, train_outputs)
# Get testing data
test_inputs = task.get_test_data(fold, include_target=False)
# Predict on the testing data
# Your output should be a pandas series, numpy array, or python iterable
# where the array elements are floats or bools
predictions = my_model.predict(test_inputs)
# Record your data!
task.record(fold, predictions)
ベンチマークの例(ホームページより)
まとめ
以上、Pythonを使ったMatbenchの使い方をまとめてみました。タスクごとにデータが分けられており、数行のコードでデータを持ってくることができるのでとても便利そうですね。今回はデータの取得や分割方法について紹介しましたが、次回は実際にリーダーボードで上位となっているモデルを見ていこうと思います。