サロゲートモデルを用いた最適化

このサンプルでは Femtet を用いて訓練データを作成し、サロゲートモデルを用いて最適化を行う方法のデモを行います。

サンプルファイル

注釈

サンプルプロジェクト と、 Femtet を用いた 訓練データ作成のための サンプルスクリプト1Femtet を用いずに 最適化を行う サンプルスクリプト2 を同じフォルダに配置してください.

このサンプルの使い方

gal_ex13_create_training_data.py をダブルクリックで実行すると、Femtet を用いたサロゲートモデルの訓練データ作成が始まります。

Femtet の解析実行数が 100 程度を超えたら gal_ex13_optimize_with_surrogate.py をダブルクリックで実行してください。(ページ下部の最適化結果は 100 解析データを元にモデルを作成しています。)

注釈

サロゲートモデルによる最適化では Femtet を使用しないので、 gal_ex13_create_training_data.py の実行中に追加の Femtet ライセンスなしで gal_ex13_optimize_with_surrogate.py を実行することができます。

Tip

サロゲートモデルとは?

PyFemtetで扱うサロゲートモデルは、既知の設計変数と目的関数のセットを学習することで未知の設計変数に対して目的関数を予測するための機械学習モデルです。

一般に質の良い学習データを作成するためには例題に記載しているような通常の最適化よりも多くの FEM 解析データが必要になりますが、一度学習データを作成できれば一度の目的関数計算を非常に高速に行うことができます。

そのため、例えば設計変数や目的関数の項目はある程度決まっており、それらの範囲や目的値が異なる課題が頻発する状況では、所望の目標値を満たす設計変数のあたり付けを非常に高速に行うことができます。

注釈

FEM 問題としての詳細は、 Femtet ヘルプ / 例題 / 応力解析 / 例題 13 をご覧ください。

設計変数

../../_images/gal_ex13_parametric.png

モデルの外観

変数名

説明

length

音叉の長さ

width

音叉の太さ

base_radius

音叉のベースの半径 (最適化では固定)

目的関数

  • 第一共振周波数 (1000 および 2000 を目標とする)

サンプルコード

gal_ex13_create_training_data.py
 1import os
 2from time import sleep
 3
 4from optuna.samplers import RandomSampler
 5
 6from pyfemtet.opt import FEMOpt, FemtetInterface, OptunaOptimizer
 7
 8
 9def get_res_freq(Femtet):
10    Galileo = Femtet.Gogh.Galileo
11    Galileo.Mode = 0
12    sleep(0.01)
13    return Galileo.GetFreq().Real
14
15
16if __name__ == '__main__':
17
18    os.chdir(os.path.dirname(__file__))
19
20    # Femtet との接続を行います。
21    fem = FemtetInterface(
22        femprj_path='gal_ex13_parametric.femprj',
23    )
24
25    # 最適化用オブジェクトの設定を行います。
26    # ただしこのスクリプトでは最適化ではなく
27    # 学習データ作成を行うので、 optuna の
28    # ランダムサンプリングクラスを用いて
29    # 設計変数の選定を行います。
30    opt = OptunaOptimizer(
31        sampler_class=RandomSampler,
32    )
33
34    # FEMOpt オブジェクトを設定します。
35    femopt = FEMOpt(
36        fem=fem,
37        opt=opt,
38    )
39
40    # 設計変数を設定します。
41    femopt.add_parameter('length', 0.1, 0.02, 0.2)
42    femopt.add_parameter('width', 0.01, 0.001, 0.02)
43    femopt.add_parameter('base_radius', 0.008, 0.006, 0.01)
44    # 目的関数を設定します。ランダムサンプリングなので
45    # direction は指定してもサンプリングに影響しません。
46    femopt.add_objective(fun=get_res_freq, name='第一共振周波数(Hz)')
47
48    # 学習データ作成を行います。
49    # 終了条件を指定しない場合、手動で停止するまで
50    # 学習データ作成を続けます。
51    # 最適化スクリプトで history_path を参照するため、
52    # わかりやすい csv ファイル名を指定します。
53    femopt.set_random_seed(42)
54    femopt.optimize(
55        history_path='training_data.csv',
56        # n_trials=100
57    )
gal_ex13_optimize_with_surrogate.py
 1import os
 2
 3from optuna.samplers import TPESampler
 4
 5from pyfemtet.opt import FEMOpt, OptunaOptimizer
 6from pyfemtet.opt.interface import PoFBoTorchInterface
 7
 8
 9def main(target):
10
11    os.chdir(os.path.dirname(__file__))
12
13    # Femtet との接続の代わりに、サロゲートモデルを作成します。
14    # 学習データ作成スクリプトで作成した csv ファイルを読み込んで
15    # サロゲートモデルを作成します。
16    fem = PoFBoTorchInterface(
17        history_path='training_data.csv'
18    )
19
20    # 最適化用オブジェクトの設定を行います。
21    opt = OptunaOptimizer(
22        sampler_class=TPESampler,
23    )
24
25    # FEMOpt オブジェクトの設定を行います。
26    femopt = FEMOpt(
27        fem=fem,
28        opt=opt,
29    )
30
31    # 設計変数の設定を行います。
32    # 上下限は学習データ作成スクリプトと異なっても良いですが、
33    # 学習していない範囲は外挿となりサロゲートモデルによる
34    # 予測精度が低下することに注意してください。
35    femopt.add_parameter('length', 0.1, 0.02, 0.2)
36    femopt.add_parameter('width', 0.01, 0.001, 0.02)
37
38    # 学習時は設計変数としていたが最適化時に固定したいパラメータがある場合
39    # initial_value のみを指定して fix 引数を True にしてください。
40    # 学習時に設定しなかった設計変数を最適化時に追加することはできません。
41    femopt.add_parameter('base_radius', 0.008, fix=True)
42
43    # 学習時に設定した目的関数のうち
44    # 最適化したいものを指定します。
45    # fun 引数は与えてもいいですが、サロゲートモデル作成時に上書きされるため無視されます。
46    # 学習時に設定しなかった目的関数を最適化時に使用することはできません。
47    obj_name = '第一共振周波数(Hz)'
48    femopt.add_objective(
49        name=obj_name,
50        fun=None,
51        direction=target,
52    )
53
54    # 最適化を実行します。
55    femopt.set_random_seed(42)
56    df = femopt.optimize(
57        n_trials=50,
58        confirm_before_exit=False,
59        history_path=f'optimized_result_target_{target}.csv'
60    )
61
62    # 最適解を表示します。
63    prm_names = femopt.opt.history.prm_names
64    obj_names = femopt.opt.history.obj_names
65    prm_values = df[df['optimality'] == True][prm_names].values[0]
66    obj_values = df[df['optimality'] == True][obj_names].values[0]
67
68    message = f'''
69===== 最適化結果 =====    
70ターゲット値: {target}
71サロゲートモデルによる予測:
72'''
73    for name, value in zip(prm_names, prm_values):
74        message += f'  {name}: {value}\n'
75    for name, value in zip(obj_names, obj_values):
76        message += f'  {name}: {value}\n'
77
78    return message
79
80
81if __name__ == '__main__':
82    # 学習データから作成したサロゲートモデルで
83    # 共振周波数が 1000 になる設計を見つけます。
84    message_1000 = main(target=1000)
85
86    # 続いて、同じサロゲートモデルで
87    # 共振周波数が 2000 になる設計を見つけます。
88    message_2000 = main(target=2000)
89
90    print(message_1000)
91    print(message_2000)

サンプルコードの実行結果

../../_images/optimized_result_target_1000.png

最適化結果 (ターゲット: 1000 Hz)

../../_images/optimized_result_target_2000.png

最適化結果 (ターゲット: 2000 Hz)

第一共振周波数が 1000 または 2000 の音叉の設計変数をサロゲートモデルを用いて探索しました。得られた設計変数は図の右上に示されています。

これらの設計変数を使用して Femtet でモデルを再現し、解析を実行しました。その結果は各図の右下に示されており、サロゲートモデルと FEM 結果との比較が可能となっています。