図2-1に処理の流れを示します。
以下の三つの部分から成ります。
図2-2にFDTD法の計算モデルを示します。
誘電体を含む対象領域を考え、その外部に送信アンテナと受信アンテナを置きます。
それらを含む領域がFDTD法の計算領域となり、計算領域の電界分布が計算されます。
3次元モデルではZ方向について同様に考えます。アンテナの配置については後述します。
図2-3に送信アンテナを移動させたときの電界分布を示します。
送信アンテナの数は16です。図中の赤い点が送信アンテナであり、黒い線が誘電体です。
送信アンテナを移動させると電界分布が変わります。
誘電体は電界を集める効果があります。
FDTD法では一つの送信アンテナを給電すると、
すべての受信アンテナの電界を一度に求めることができます。
したがって図2-3の計算から図2-4のようなS行列が得られます。
ここで横軸は送信アンテナ、縦軸は受信アンテナです。
S行列は誘電率分布の情報を持っています。
図2-5に順問題と逆問題の関係を示します。
上で述べた方法により誘電率分布からS行列を求めることが順問題です。
逆にS行列から誘電率分布を求めることが逆問題であり、本プログラムの目的です。
前項の方法で誘電体分布をランダムに変えて計算し、
多数の誘電体分布とS行列の組(教師データ)を求めます。
そのデータを用いて深層学習を行い、S行列から誘電率分布を推定することを考えます。
図2-6と図2-7にネットワークモデルを示します。
図2-6は通常のCNN(畳み込みニューラルネットワーク)、
図2-7はResNet[4]です。ResNetでは加算(shortcut connections)が加わります。
図2-6、図2-7において、「入力」はS行列画像のピクセル値、
「出力」は次式で計算される損失(MSELoss)です。
ここで、(Mx, My, Mz)は対象領域のセル数、
εrtrueは正解(教師データ)の比誘電率、
εrpredは比誘電率の予測値です。
以下では、"損失(loss)"は式(2-2)とします。これは比誘電率の予測誤差の平均値を表します。
(2-1)
(2-2)
リスト2-1に学習部のソースコードを示します。
ユーザーの行う作業は以下の2点です。
リスト2-1 学習部のソースコード(PyTorch)
dataset設定 ※ dataloader設定 model設定 ※ criterion = nn.MSELoss() # 損失関数: MSE optimizer = optim.Adam(model.parameters()) # 最適化関数: Adam for epoch in range(num_epochs): # エポックに関するループ for data, target in train_loader: # ミニバッチに関するループ optimizer.zero_grad() # 勾配初期化 output = model(data) # 順伝搬 loss = criterion(output, target) # 損失計算 loss.backward() # 逆伝搬 optimizer.step() # 最適化関数更新
リスト2-2にResNetのソースコードを示します。
ブロック数とチャンネル数を引数で指定することができます。
リスト2-2 ResNetのソースコード(PyTorch)
import torch from torch import nn class BasicBlock(nn.Module): def __init__(self, channels, dropout): super().__init__() self.conv1 = nn.Conv2d(channels, channels, kernel_size=3, stride=1, padding=1, bias=False) self.bn1 = nn.BatchNorm2d(channels) self.relu = nn.ReLU(inplace=True) self.conv2 = nn.Conv2d(channels, channels, kernel_size=3, stride=1, padding=1, bias=False) self.bn2 = nn.BatchNorm2d(channels) self.dropout1 = nn.Dropout(dropout) def forward(self, x: torch.Tensor) -> torch.Tensor: out = self.conv1(x) out = self.bn1(out) out = self.relu(out) out = self.conv2(out) out = self.bn2(out) out += x # ResNet out = self.relu(out) out = self.dropout1(out) return out class ResNet(nn.Module): def __init__(self, in_filters, num_blocks, channels, dropout, out_classes): super().__init__() self.conv1 = nn.Conv2d(in_filters, channels, kernel_size=3, stride=1, padding=1, bias=False) self.bn1 = nn.BatchNorm2d(channels) self.relu = nn.ReLU(inplace=True) self.maxpool = nn.MaxPool2d(2, ceil_mode=True) self.dropout1 = nn.Dropout(dropout) self.blocks = nn.Sequential(*[BasicBlock(channels, dropout) for _ in range(num_blocks)]) self.avgpool = nn.AdaptiveAvgPool2d(1) self.flatten = nn.Flatten() self.fc = nn.Linear(channels, out_classes) def forward(self, x): x = self.conv1(x) x = self.bn1(x) x = self.relu(x) x = self.maxpool(x) x = self.dropout1(x) x = self.blocks(x) x = self.avgpool(x) x = self.flatten(x) x = self.fc(x) return x