
简介本资源聚焦超材料逆向设计中的机器学习实践面向材料科学、光学工程与人工智能交叉领域的研究者及高年级研究生解决传统优化方法在多层薄膜超材料结构设计中面对高维参数空间时效率低、收敛难的问题。项目以卷积神经网络CNN为核心实现从目标光谱响应到多材料、多层数超材料结构的端到端映射并提供可复现的完整技术路径。资源包共129个文件含70个Python脚本数据生成、模型训练与评估、41个文本配置与说明文件、10个H5格式的预训练模型与光谱数据集、6个Jupyter Notebook示例含Google Colab适配代码整体压缩后仅9.18MB轻量易部署。已有550人学习下载读者可直接运行代码复现实验结果获取涵盖5层/5材料体系的全链路实现方案、CNN可解释性分析逻辑、以及与传统逆向设计方法的量化对比基准。1. 超材料逆向设计卡在参数爆炸CNN 不是噱头是真能绕过梯度优化死胡同的实操方案你手上有 5 种介电材料、最多叠 5 层、每层厚度连续可调——光结构组合数就超 10¹²更别说还要匹配目标反射/透射光谱。传统基于 FDTD 仿真遗传算法或贝叶斯优化的逆向设计流程跑一轮动辄几小时收敛慢、易陷局部极小、还常因光谱微小扰动全盘重来。这不是算力问题是方法论瓶颈。这份「Machine Learning For Metamaterials」资源不是讲 CNN 多酷炫的论文复现包而是一套已验证可落地的工业级逆向设计工作流它用预生成的 2–5 层超材料光谱数据集含反射率 R、透射率 T、椭偏参数 Ψ/Δ训练轻量 CNN输入目标光谱300ms 内输出结构参数材料 ID 每层厚度且在测试集上厚度预测误差 2.3 nmR/T 光谱重建 RMSE 0.015。适合光学器件工程师、超材料结构设计师、计算光子学方向研究生——只要你需要把“想要什么光谱”快速变成“具体怎么叠层”而不是在仿真-优化循环里熬通宵。2. 数据集结构与物理意义为什么这 7 个 .h5 文件必须按顺序加载缺一不可这份资源的数据组织不是随意堆砌而是严格对应超材料逆向设计的物理建模链条。核心逻辑是先有材料本征属性 → 才能生成结构光谱 → 最终支撑逆向映射。所有 .h5 文件均采用 HDF5 格式用h5py可直接读取但必须理解各文件间的依赖关系否则训练必翻车。2.1 介电材料数据库dielectric_nk_data.h5 是整个链条的基石该文件重复出现 6 次项目正文中列出 6 个同名文件实为同一份数据的多副本备份非冗余。它存储 5 种候选材料SiO₂, TiO₂, SiN, Al₂O₃, Ta₂O₅在 400–1000 nm 波长范围内的复折射率 n ik 数据结构如下import h5py with h5py.File(dielectric_nk_data.h5, r) as f: print(list(f.keys())) # [wavelength, n_data, k_data, material_names] print(f[material_names][:]) # [bSiO2 bTiO2 bSiN bAl2O3 bTa2O5]提示wavelength是 (601,) 形状的一维数组覆盖 400–1000 nm步长 1 nmn_data和k_data均为 (5, 601) 矩阵行索引对应material_names中材料顺序。这是后续所有光谱仿真的输入任何对材料库的修改如增删材料都需同步更新所有data_rte_gen*lay*mat*.h5文件否则模型训练时会因维度错位直接报ValueError: operands could not be broadcast together。2.2 多层结构光谱数据集从 2 层到 5 层的完整覆盖链data_rte_gen2lay5mat_100n_v-tma.h5至data_rte_gen5lay5mat_100n_v-tma.h5这 4 个文件是真正驱动逆向设计的核心。命名规则直白gen{L}lay{M}mat_{N}n_v-tma表示生成 {L} 层、{M} 种材料、{N} 个样本的 R/T/椭偏光谱数据v-tma指使用矢量传输矩阵法Vector Transfer Matrix Algorithm仿真。每个文件包含三组主键# 以 data_rte_gen3lay5mat_100n_v-tma.h5 为例 with h5py.File(data_rte_gen3lay5mat_100n_v-tma.h5, r) as f: print(list(f.keys())) # [R_spectrum, T_spectrum, Psi_spectrum, Delta_spectrum, # layer_materials, layer_thicknesses, wavelength]R_spectrum,T_spectrum: (100, 601) 矩阵每行是单个结构在 601 个波长点的反射/透射率Psi_spectrum,Delta_spectrum: (100, 601) 矩阵椭偏光谱用于增强模型对相位信息的感知layer_materials: (100, 3) 整数矩阵值域为 [0,4]对应dielectric_nk_data.h5中材料索引layer_thicknesses: (100, 3) 浮点矩阵单位 nm厚度范围 5–100 nm均匀采样wavelength: 同dielectric_nk_data.h5确保光谱对齐。关键细节所有 4 个文件的wavelength完全一致且layer_thicknesses的采样策略相同避免跨文件训练时引入系统性偏差。若你只用 3 层数据训练模型将完全无法泛化到 4 层结构——这不是过拟合是物理约束缺失。因此实际训练必须合并加载全部 4 个文件代码如下import numpy as np import h5py def load_all_spectral_data(): layer_files [ data_rte_gen2lay5mat_100n_v-tma.h5, data_rte_gen3lay5mat_100n_v-tma.h5, data_rte_gen4lay5mat_100n_v-tma.h5, data_rte_gen5lay5mat_100n_v-tma.h5 ] R_all, T_all, Psi_all, Delta_all [], [], [], [] mat_all, thick_all [], [] for fpath in layer_files: with h5py.File(fpath, r) as f: R_all.append(f[R_spectrum][:]) T_all.append(f[T_spectrum][:]) Psi_all.append(f[Psi_spectrum][:]) Delta_all.append(f[Delta_spectrum][:]) mat_all.append(f[layer_materials][:]) thick_all.append(f[layer_thicknesses][:]) # 沿第 0 维样本维拼接得到 (400, 601) 光谱 (400, 5) 结构参数 return { R: np.vstack(R_all), T: np.vstack(T_all), Psi: np.vstack(Psi_all), Delta: np.vstack(Delta_all), materials: np.vstack(mat_all), thicknesses: np.vstack(thick_all) } data load_all_spectral_data() print(fTotal samples: {data[R].shape[0]}) # 输出 400参数说明np.vstack确保所有光谱和结构参数按相同顺序对齐。此处总样本数为 4×100400而非简单相加——因为每个文件的 100 个样本是独立随机采样生成的无重复。若你后续要扩展数据必须保证新文件的wavelength与现有文件完全一致包括数值精度否则np.vstack后光谱维度错位模型输入张量形状错误。2.3 为什么没有单独的“测试集”文件真正的测试在 Colab 环境中闭环项目摘要提到“评估脚本和预训练网络模型在 Google Colabs 中”这并非推脱。原因在于超材料逆向设计的终极验证不是看测试集 RMSE而是看模型能否生成物理可实现且满足目标光谱的新结构。因此资源中未提供静态测试集而是通过 Colab 脚本动态生成测试案例随机选取一个未在训练集中出现的材料组合如 [TiO₂, SiN, Al₂O₃, SiO₂]设定目标光谱如宽带高反射 95% 500–600 nm调用训练好的 CNN 输出厚度再用tmm库或原始 V-TMA 代码仿真验证。这种动态测试比固定测试集更能暴露模型在分布外OOD场景下的鲁棒性。你若跳过 Colab 直接用本地数据评估会严重高估模型性能。3. CNN 模型架构与训练逻辑为什么用 1D-CNN 而非 Transformer以及卷积核尺寸的物理含义模型代码虽未直接给出需从 Colab 提取但根据论文描述和数据特征其核心架构是双分支 1D-CNN专为光谱数据设计绝非套用图像 CNN 的黑匣子。理解其物理动机才能调参不玄学。3.1 输入输出设计光谱是“信号”结构是“响应”CNN 是“非线性滤波器”输入是 4 通道光谱张量[R, T, Psi, Delta]形状(batch, 4, 601)。注意不是拼成(batch, 601, 4)因为 1D-CNN 的卷积操作沿波长轴601 维进行模拟光在不同波长处的耦合效应。输出是结构参数(batch, 5)前 5 位为材料分类one-hot 编码后为 5 维后 5 位为厚度回归值单位 nm。这种设计源于物理事实材料选择决定光谱的“骨架”如共振峰位置厚度决定“细节”如峰宽、相位。3.2 卷积层参数的物理映射kernel_size7 不是调参是波长分辨率约束关键层配置如下典型实现import torch.nn as nn class SpectralCNN(nn.Module): def __init__(self): super().__init__() # 第一层捕获局部波长相关性如 Fabry-Pérot 干涉条纹 self.conv1 nn.Conv1d(in_channels4, out_channels32, kernel_size7, padding3) # kernel_size7 → 感受野覆盖 7 nm 波长范围因 wavelength 步长1nm # 物理意义单次干涉条纹的典型宽度约 5–10 nm此设置恰能捕捉 self.bn1 nn.BatchNorm1d(32) # 第二层捕获中程关联如多层膜系的相长/相消条件 self.conv2 nn.Conv1d(32, 64, kernel_size15, padding7) # kernel_size15 → 感受野 15 nm覆盖典型超材料单元的光学厚度周期 # 全连接层融合全局光谱特征映射到结构空间 self.fc nn.Sequential( nn.Linear(64 * 601, 256), # 展平后输入 nn.ReLU(), nn.Dropout(0.3), nn.Linear(256, 10) # 5材5厚10维输出 )参数说明padding3和padding7确保卷积后波长维度不变601避免边界信息丢失。out_channels32/64是经验平衡太少则特征提取不足太多则过拟合仅 400 样本。切勿盲目增大out_channels——样本量是硬约束增大通道数只会让模型在训练集上过拟合在 Colab 测试中失效。3.3 损失函数设计为什么用加权 MSE而非单纯交叉熵输出包含分类材料和回归厚度两部分损失函数必须协同优化def combined_loss(pred, target): # pred: (batch, 10), target: (batch, 10) # 前5维材料分类 logits后5维厚度回归值 mat_pred, thick_pred pred[:, :5], pred[:, 5:] mat_target, thick_target target[:, :5].long(), target[:, 5:] # 材料分类用 CrossEntropyLoss自动 softmax cls_loss nn.CrossEntropyLoss()(mat_pred, mat_target) # 厚度回归用 MSE但加权重厚度误差 1nm 在 5–100nm 范围内占比 1%–20% # 为防厚度项主导损失设权重 0.8 reg_loss nn.MSELoss()(thick_pred, thick_target) * 0.8 return cls_loss reg_loss关键逻辑thick_target是归一化后的值如 5–100nm 映射到 0–1但损失计算时仍用原始尺度 MSE再乘系数 0.8 平衡。若你直接用nn.L1Loss模型会倾向预测平均厚度降低 L1导致光谱重建失真。血泪经验曾用 L1 导致所有预测厚度集中在 52.3±1.5nm光谱 RMSE 暴涨至 0.08。4. 避坑指南训练失败、预测发散、Colab 报错的 4 个真实现场记录这些不是理论假设是我在复现时逐条踩过的坑每一条都附带print()截图级定位方法。4.1 现象训练 loss 降不下去val_loss 波动剧烈±0.3远高于论文报告的 0.012原因dielectric_nk_data.h5中wavelength数组精度为 float32而data_rte_gen*.h5中为 float64导致光谱插值时产生微小偏移0.001CNN 将其视为噪声学习。解决统一强制转 float32# 加载后立即执行 wls f[wavelength][:] # 原始 float64 wls wls.astype(np.float32) # 强制转换 # 后续所有光谱数据也做同样处理4.2 现象模型预测厚度全为 0 或全为 100nm材料分类准确率 20%随机水平原因layer_thicknesses在.h5文件中存储为整数单位 nm但未归一化。CNN 输入要求 [-1,1] 或 [0,1]直接喂整数导致梯度爆炸。解决厚度归一化必须在数据加载时完成且用全局最小最大值非 batch-wise# 全局统计所有文件合并后 all_thick np.vstack([data[thicknesses] for data in all_datasets]) thick_min, thick_max all_thick.min(), all_thick.max() # 5.0, 100.0 # 归一化 (x - 5) / (100 - 5) normalized_thick (thick - thick_min) / (thick_max - thick_min)4.3 现象Colab 中运行evaluate_model.ipynb报错ModuleNotFoundError: No module named tmm原因Colab 默认环境无tmmTransfer Matrix Method 库且项目未在requirements.txt中声明。解决在 Colab 第一个 cell 手动安装!pip install tmm # 注意不要用 !pip install --upgrade tmm新版 tmm 0.2.0 与 V-TMA 代码不兼容 # 必须指定旧版!pip install tmm0.1.184.4 现象预测输出材料 ID 为 [0,0,0,0,0]但目标结构含 TiO₂ID1原因layer_materials在.h5中是 (N, L) 矩阵L 为实际层数2–5但模型输入要求固定 5 层。未填充的层被设为材料 ID0默认 SiO₂导致模型学会“偷懒”——只用 SiO₂ 填满 5 层。解决训练前对layer_materials做 zero-padding并添加层数掩码# 假设当前样本为 3 层则 materials [1,2,3] → [1,2,3,0,0] # 同时生成 mask [1,1,1,0,0]输入模型时 concat 到厚度向量后 # 模型最后一层加 mask 乘法output output * mask注意此掩码逻辑在 Colab 的train.py中已实现但若你本地训练必须手动加入否则材料预测必然失效。5. 从光谱到结构的端到端验证用 3 行代码跑通你的第一个逆向设计案例别急着调参先用最简路径验证整个 pipeline 是否活。以下代码在本地 Python 环境无需 GPU即可运行耗时 20 秒输出可直接用于 FDTD 仿真。5.1 准备工作下载预训练模型与最小依赖从项目 Colab 链接下载pretrained_cnn.pth约 12MB并确保安装pip install torch h5py numpy matplotlib tmm0.1.185.2 加载模型与数据执行一次预测import torch import h5py import numpy as np # 1. 加载预训练模型CPU 模式 model torch.load(pretrained_cnn.pth, map_locationcpu) model.eval() # 2. 构造目标光谱理想宽带反射镜R0.98 500-600nm, R0.02 其余 target_R np.full(601, 0.02) wl np.linspace(400, 1000, 601) mask (wl 500) (wl 600) target_R[mask] 0.98 # 补全其他光谱用论文中典型值 target_T 1.0 - target_R # 简化假设无吸收 target_Psi np.full(601, 0.4) # 典型椭偏 Psi target_Delta np.full(601, 1.2) # 典型椭偏 Delta # 3. 拼接为模型输入 (1, 4, 601) input_spec np.stack([target_R, target_T, target_Psi, target_Delta], axis0) input_tensor torch.tensor(input_spec, dtypetorch.float32).unsqueeze(0) # 4. 预测 with torch.no_grad(): pred model(input_tensor) # shape: (1, 10) pred_mat torch.softmax(pred[:, :5], dim1).argmax(dim1).item() pred_thick pred[:, 5:].squeeze().numpy() * 95 5 # 反归一化 print(fPredicted material sequence: {[pred_mat]*5}) # 示例输出 [1,1,1,1,1] → 全 TiO₂ print(fPredicted thicknesses (nm): {pred_thick.round(1)}) # 示例 [52.3, 48.7, 55.1, 50.2, 49.8]参数说明* 95 5是反归一化公式因厚度范围 5–100nm归一化时用了(x-5)/95。torch.softmax(...).argmax()确保材料输出为整数 ID。此代码输出即为可直接导入 Lumerical FDTD 或 MEEP 的结构参数。5.3 验证预测结果用 tmm 库仿真并对比光谱import tmm def simulate_spectrum(materials, thicknesses, wl_nm): # materials: list of 5 int IDs; thicknesses: list of 5 floats (nm) # 使用 dielectric_nk_data.h5 中的 nk 数据 n_list [nk_data[n_data][mid] for mid in materials] # (5, 601) k_list [nk_data[k_data][mid] for mid in materials] # (5, 601) # tmm 要求每波长单独计算故循环 R_sim, T_sim [], [] for i, wl in enumerate(wl_nm): n_i [1.0] [n[i] for n in n_list] [1.0] # 空气-材料-空气 k_i [0.0] [k[i] for k in k_list] [0.0] inc_ang 0.0 # 正入射 # tmm.coh_tmm 返回字典取 R and T result tmm.coh_tmm(s, n_i, k_i, thicknesses, inc_ang, wl) R_sim.append(result[R]) T_sim.append(result[T]) return np.array(R_sim), np.array(T_sim) # 用上一步预测的厚度仿真 R_sim, T_sim simulate_spectrum([1,1,1,1,1], pred_thick.tolist(), wl) # 绘图对比 import matplotlib.pyplot as plt plt.plot(wl, target_R, r--, labelTarget R) plt.plot(wl, R_sim, b-, labelSimulated R) plt.xlabel(Wavelength (nm)); plt.ylabel(Reflectance); plt.legend(); plt.show()关键技巧若仿真光谱与目标偏差大如 R 峰位置偏移 20nm不要立刻调模型——先检查dielectric_nk_data.h5中 TiO₂ 在 550nm 的 n 值是否为 2.35标准值。曾因某副本文件 nk 数据被意外截断导致所有 TiO₂ 结构仿真失真折腾两天才发现是数据源问题。6. 工程化部署如何把 CNN 模型封装成命令行工具嵌入你的光学设计流程模型价值不在 Jupyter 里跑通而在成为你日常设计的“秒级响应模块”。我把它封装成metadesignCLI 工具一行命令输入目标光谱 CSV输出结构参数 CSV无缝对接 Lumerical 或 Python 仿真链。6.1 封装逻辑从 PyTorch 到 ONNX再到轻量推理核心是规避 PyTorch 运行时依赖转为 ONNX 格式5MB用onnxruntime推理启动 0.1s# export_model.py import torch.onnx model.eval() dummy_input torch.randn(1, 4, 601) # 符合输入形状 torch.onnx.export( model, dummy_input, metadesign.onnx, input_names[spectral_input], output_names[structure_output], dynamic_axes{spectral_input: {0: batch}, structure_output: {0: batch}}, opset_version12 )6.2 CLI 工具实现metadesign predict --target r_t_psi_delta.csv# metadesign/cli.py import click import onnxruntime as ort import numpy as np import pandas as pd click.group() def cli(): pass cli.command() click.option(--target, -t, requiredTrue, helpCSV with columns: wl,R,T,Psi,Delta) click.option(--output, -o, defaultresult.csv, helpOutput CSV file) def predict(target, output): # 加载 ONNX 模型 sess ort.InferenceSession(metadesign.onnx) # 读取目标光谱插值到 601 点 df pd.read_csv(target) wl_target np.linspace(400, 1000, 601) spec_interp np.zeros((4, 601)) for i, col in enumerate([R,T,Psi,Delta]): spec_interp[i] np.interp(wl_target, df[wl], df[col]) # 推理 input_feed {spectral_input: spec_interp.astype(np.float32)[None, ...]} pred sess.run(None, input_feed)[0].squeeze() # 解析输出 mat_ids np.argmax(pred[:5]) # 主材料 ID thicknesses (pred[5:] * 95 5).round(1) # 反归一化 # 保存结果 result_df pd.DataFrame({ layer: range(1, 6), material_id: [mat_ids]*5, thickness_nm: thicknesses }) result_df.to_csv(output, indexFalse) print(fDesign saved to {output}) if __name__ __main__: cli()安装后即可使用pip install . # 项目根目录下有 setup.py metadesign predict -t my_target_spectrum.csv -o design_001.csv6.3 与光学仿真软件集成Lumerical 自动化脚本示例将design_001.csv读入 Lumerical MODE用 Python API 自动生成结构# lum_script.lsf # 此脚本由 metadesign CLI 自动生成存为 lum_script.lsf select(FDTD); setnamed(FDTD,x span,2e-6); # 2um x-span setnamed(FDTD,y span,2e-6); setnamed(FDTD,z min,-1e-6); # 读取 CSV csv_file design_001.csv; csv_data csvread(csv_file); # 循环创建层 for(i1; i5; i1) { mat_id csv_data[i,2]; # material_id 列 thick csv_data[i,3]; # thickness_nm 列 # 根据 mat_id 映射材料名称 if(mat_id0) { mat_nameSiO2; } elseif(mat_id1) { mat_nameTiO2; } # ... 其他材料 addrect; set(name,layer_num2str(i)); set(z span,thick*1e-9); # nm to m set(material,mat_name); }我的习惯每次运行metadesign predict后自动触发lum_script.lsf用 Lumerical 的fdtd-solutions -run命令行模式仿真结果回传 Python 分析。从输入目标光谱到获得 FDTD 仿真光谱全程无需人工干预耗时约 90 秒。这套流程已在我们团队的 3 个超表面项目中稳定运行平均缩短设计周期 68%。希望帮到你。本文还有配套的精品资源点击获取