☰
光学设计+深度学习:从Zemax到PyTorch的代理模型与端到端优化实战
2026/9/29 17:22:43 网站建设 项目流程

光学设计这个圈子,这几年有一个很明显的变化:以前组里招人,会Zemax、能跑Code V、懂公差分析,基本就够用了。现在不一样了,简历上如果只写"熟练使用Zemax进行镜头设计",连初筛都过不了。越来越多的课题组和公司在问同一个问题——你能不能把深度学习那套东西,接到光学设计和仿真里去?

我自己是从传统光学设计一路做过来的,zemax的优化函数写到吐,后来被逼着学PyTorch,踩了无数坑。这篇文章不是要劝所有人都去转行搞AI,而是想把"光学设计+仿真+深度学习"这条路上,哪些是真需求、哪些是噱头、具体怎么落地,掰开揉碎讲清楚。如果你是做光学设计、光学仿真、计算成像方向的硕博士,或者已经在企业里做光学系统研发,这篇内容值得你花时间看完。

1. 为什么光学设计圈开始盯上深度学习

1.1 传统光学设计的瓶颈到底卡在哪

先说清楚一件事:深度学习不是来取代Zemax的,它解决的是Zemax这类工具本身不擅长的问题。

传统光学设计流程大致是这样:确定指标→选初始结构→设置评价函数→优化→公差分析→出图。这套流程跑了几十年,非常成熟。但它有几个绕不过去的坎。

第一个坎是初始结构依赖。你设计一个七片式的手机镜头,初始结构从哪来?从专利库翻,从经验库里找,或者从双高斯、库克三片式这些经典结构改。问题是,当指标要求越来越极端——比如超广角+大光圈+超薄,经典结构根本改不出来,你得靠大量试错。这个试错过程,本质上是在一个超高维、非凸的空间里瞎撞。

第二个坎是优化速度。Zemax的局部优化算法(阻尼最小二乘法)每次迭代都要追迹大量光线,一个复杂系统优化一晚上是常事。全局优化(Global Search)更慢,而且经常给你一堆看起来能用、实际上装不出来的解。

第三个坎是评价函数的设计。这是最考验功力的地方。你要把"成像质量好"这个模糊的目标,翻译成一堆操作数(operand)的加权组合。权重给多少?MTF和畸变怎么平衡?这个问题没有标准答案,全靠经验。而且评价函数一旦定死,优化就只能在那个框架里找解,很难跳出你预设的思路。

1.2 深度学习切入的三个真实场景

深度学习之所以能进来,是因为它擅长的事情恰好对上光学设计的痛点。

场景一:用神经网络做代理模型(surrogate model)。你有一堆光学系统的结构参数和对应的性能指标,训练一个网络,输入结构参数、输出MTF或波前误差。训练好之后,评估一个结构的性能只需要毫秒级,比追迹光线快几个数量级。这就把优化问题从"每次评估都要跑仿真"变成了"用网络快速筛选候选",搜索效率完全不一样。

场景二:逆向设计。传统流程是"给结构→算性能",逆向设计是"给性能→出结构"。这个问题本质上是病态的,因为多个结构可能对应同一个性能。但生成模型(比如VAE、GAN、扩散模型)恰好擅长从分布里采样,可以给出多个候选结构,再交给传统优化去精修。

场景三:计算成像里的端到端优化。这是目前最热的方向。传统光学设计把镜头和图像处理分开做,镜头负责"成好像",算法负责"修图"。但端到端优化把光学系统和重建算法放在一起训练,让它们协同工作。典型例子是单透镜计算成像——用一个简单的单透镜,配合一个训练好的去模糊网络,成像质量能逼近复杂多片式镜头。这个思路对手机镜头、内窥镜、AR/VR光学系统冲击很大。

1.3 一个必须澄清的误区

很多人以为"深度学习做光学设计"就是拿个网络直接输出镜头参数。这个理解是错的,而且照着做基本做不出来。

真实的做法是混合架构:神经网络负责快速评估、提供初始解、或者做后处理,传统光学设计工具负责精确优化和公差分析。网络输出的是"候选"和"方向",不是"最终答案"。你最终交付的镜头,还是要过Zemax的优化和公差这一关。

我见过有师弟直接拿MLP去拟合"输入指标、输出曲率半径",训出来的网络在训练集上误差很小,一到测试集就崩。原因很简单:光学系统的性能对结构参数极其敏感,而且参数之间有强耦合,纯数据驱动的映射根本学不稳。所以后面讲的所有方法,都建立在"神经网络辅助、传统工具兜底"这个前提上。

2. 从Zemax到PyTorch:数据管道的搭建

2.1 为什么数据是这条路上最大的坑

做深度学习的人常说"数据和特征决定了上限",这话在光学设计里尤其成立。因为光学设计的数据不像图像识别那样有现成的ImageNet,你得自己造。

造数据的方式通常有两种。一种是用Zemax的ZOS-API批量生成:写脚本随机采样结构参数,让Zemax追迹光线,导出MTF、点列图RMS、波前误差等指标。另一种是用物理仿真自己算,比如用Python写一个简化的光线追迹或者用角谱法算衍射传播。

第一种方式数据真实,但慢。一个中等复杂度的系统,跑一次完整评价可能要几秒到几十秒,你要生成几万条数据,得跑好几天。第二种方式快,但精度和真实光学系统有差距,训出来的网络迁移到真实系统上会掉点。

我的建议是:先用快速物理仿真生成大规模预训练数据,再用Zemax生成小规模高精度数据做微调。这样兼顾了速度和精度。

2.2 ZOS-API批量采样的实操细节

Zemax的ZOS-API支持Python、MATLAB、C#等语言调用。用Python的话,通过pywin32或者官方提供的ZOSAPI模块连接。下面是一个采样脚本的骨架:

import numpy as np from ZOSAPI_NetHelper import ZOSAPI_NetHelper import clr # 连接Zemax实例 ZOSAPI_NetHelper.ZOSAPI_Initializer.Initialize() import ZOSAPI TheApplication = ZOSAPI.ZOSAPI_Connection().CreateNewApplication() TheSystem = TheApplication.PrimarySystem # 定义采样范围 r1_range = (10.0, 50.0) # 第一面曲率半径 r2_range = (-80.0, -20.0) # 第二面曲率半径 thickness_range = (2.0, 8.0) glass_catalog = ["N-BK7", "N-SF5", "F2"] def sample_system(): r1 = np.random.uniform(*r1_range) r2 = np.random.uniform(*r2_range) t = np.random.uniform(*thickness_range) glass = np.random.choice(glass_catalog) return r1, r2, t, glass def evaluate_system(r1, r2, t, glass): # 设置表面参数 surf1 = TheSystem.LDE.GetSurfaceAt(1) surf1.Radius = r1 surf1.Thickness = t surf1.Material = glass surf2 = TheSystem.LDE.GetSurfaceAt(2) surf2.Radius = r2 # 追迹并读取MTF TheSystem.Tools.OpenQuickFocus() # ... 读取评价指标 return mtf_value

这里有几个实操中容易翻车的点。

第一,Zemax实例的管理。如果你用CreateNewApplication(),每次都会开一个新的Zemax进程,跑几千次之后内存直接爆掉。正确做法是用ConnectToApplication()连接一个已经打开的Zemax实例,或者定期重启进程。

第二,异常处理。随机采样的参数很可能让系统无法追迹(比如光线全反射、面型交叉)。这时候API会抛异常或者返回无效值。你必须用try-except包住每次评价,把无效样本丢掉,否则整个脚本会中途挂掉。

第三,采样策略。纯均匀随机采样效率很低,大部分样本都是垃圾(成像质量极差)。更好的做法是围绕已知的可用结构做扰动采样,或者用拉丁超立方采样(LHS)保证参数空间的覆盖均匀性。

2.3 数据集的字段设计和归一化

采完数据,怎么组织成训练集?我的经验是每条样本至少包含这几类字段:

字段类别具体内容用途
结构参数曲率半径、厚度、材料、非球面系数网络输入
系统指标焦距、F数、视场角、波长范围网络输入(条件)
性能指标MTF各频率值、RMS波前误差、畸变网络输出(回归目标)
元信息采样时间、是否收敛、追迹光线数数据清洗

归一化这一步千万别偷懒。曲率半径可能是几十到几百,非球面系数可能是1e-6量级,如果不做归一化直接喂给网络,梯度会被大数值主导,小数值的参数根本学不动。我的做法是对每个参数单独做z-score标准化,保存均值和方差,推理时用同一套参数反归一化。

材料这种离散变量怎么处理?两种方式:一种是one-hot编码,简单但维度高;另一种是查玻璃库,把材料映射到折射率、阿贝数等连续物理量,这样网络能学到材料的光学本质。我推荐后者,泛化性更好。

3. 代理模型:让性能评估快一千倍

3.1 代理模型到底代理了什么

代理模型(surrogate model)的核心思想是:用一个训练好的神经网络,替代"结构参数→性能指标"这个映射。训练好之后,评估一个结构只需要一次前向传播,耗时从秒级降到毫秒级。

这个加速在优化里价值巨大。假设你要用遗传算法搜索10万个候选结构,用Zemax评估要跑好几天,用代理模型几分钟就筛完了。筛出来的Top候选再用Zemax精算验证,整体效率提升几个数量级。

代理模型的输入输出设计很关键。输入是结构参数,输出是什么?可以是MTF曲线(多个频率点),可以是波前误差的Zernike系数,也可以是点列图的RMS半径。输出维度的选择取决于你后续要优化什么。如果做成像质量优化,输出MTF曲线最直接;如果做波前控制,输出Zernike系数更合适。

3.2 网络结构的选择逻辑

代理模型本质是一个回归任务,输入维度通常几十到几百,输出维度几十到几百。这种规模用全连接网络(MLP)就够了,不需要上CNN或Transformer。

但有几个设计细节值得说。

残差连接。光学系统的性能对参数变化很敏感,网络需要学到精细的映射。加残差连接(ResNet那种skip connection)能让网络更容易学到"相对于某个基准的修正量",训练更稳。

多任务输出。如果你同时要预测MTF、畸变、色差,可以设计成多任务学习:共享底层特征提取,然后分几个头输出不同指标。这样网络能学到指标之间的相关性,比单独训几个网络效果好。

物理约束。纯数据驱动的网络可能预测出违反物理规律的解(比如MTF大于1)。可以在损失函数里加物理约束项,比如让预测的MTF单调递减、让波前误差满足某种平滑性。这个技巧在数据量不足时特别有用。

下面是一个代理模型的PyTorch实现骨架:

import torch import torch.nn as nn class OpticalSurrogate(nn.Module): def __init__(self, input_dim, output_dim, hidden_dim=512): super().__init__() self.input_proj = nn.Linear(input_dim, hidden_dim) self.blocks = nn.ModuleList([ self._make_block(hidden_dim) for _ in range(4) ]) self.output_head = nn.Linear(hidden_dim, output_dim) self.act = nn.GELU() def _make_block(self, dim): return nn.Sequential( nn.Linear(dim, dim), nn.LayerNorm(dim), nn.GELU(), nn.Linear(dim, dim), ) def forward(self, x): h = self.act(self.input_proj(x)) for block in self.blocks: h = h + block(h) # 残差连接 return self.output_head(h)

训练时用MSE损失,但建议对MTF这种有物理范围的输出加一个sigmoid或者softplus激活,保证输出在合理区间。

3.3 代理模型的精度验证与失效边界

代理模型训完,不能只看训练loss,必须做严格的验证。

验证集划分要小心。如果你的数据是围绕几个基准结构扰动采样的,随机划分验证集会导致训练集和验证集高度相似,验证loss虚低。正确做法是按基准结构划分:某些基准结构只出现在验证集里,测试网络的泛化能力。

失效边界要摸清。代理模型只在训练数据覆盖的参数空间内可靠。如果你拿一个训练时没见过的极端结构去问它,它可能给出完全错误的预测。所以实际使用时,要么限制搜索范围在训练分布内,要么加一个不确定性估计(比如用ensemble或者MC Dropout),对不确定的预测打问号。

我踩过的一个坑:用代理模型筛出来的"最优结构",拿去Zemax一算,MTF差了十万八千里。后来发现是那个结构落在了训练数据的稀疏区域,网络在那边基本是瞎猜。解决办法是在损失函数里对稀疏区域加权,或者干脆扩大采样范围。

4. 逆向设计与端到端优化:真正的前沿

4.1 逆向设计的两种技术路线

逆向设计要解决的是"给定目标性能,反推结构参数"。这个问题病态,因为解不唯一。技术上有两条路线。

路线一:条件生成。用条件VAE或条件GAN,输入是目标性能(比如目标MTF曲线),输出是结构参数分布。训练时让生成的结构经过代理模型评估后,性能尽量接近目标。这条路线能给出多个候选,适合探索设计空间。

路线二:直接优化。把代理模型固定,把结构参数当作可学习变量,用梯度下降直接优化结构参数,让代理模型预测的性能逼近目标。这条路线快,但容易陷入局部最优,而且优化出来的结构可能物理上不可实现。

实际中常用的是两者结合:先用生成模型给出候选,再用梯度优化精修。

4.2 端到端计算成像的架构拆解

端到端优化是目前最有落地价值的方向,我重点讲。

传统计算成像的流程是:光学系统成像→传感器采样→算法重建。光学设计和算法设计是分开的,光学工程师把镜头做到"衍射极限",算法工程师再在图像上做去卷积。但问题是,光学系统的像差和算法的重建能力之间其实可以互补。端到端优化就是让光学参数和算法参数一起训练,找到全局最优的搭配。

典型架构是这样的:

class EndToEndImaging(nn.Module): def __init__(self, optical_params, reconstructor): super().__init__() self.optical_params = nn.Parameter(optical_params) self.reconstructor = reconstructor # 比如U-Net def forward(self, scene): # 用可微的光学模型仿真成像过程 psf = self.compute_psf(self.optical_params) captured = self.convolve(scene, psf) captured = self.add_noise(captured) # 重建 reconstructed = self.reconstructor(captured) return reconstructed, captured def compute_psf(self, params): # 用角谱法或傅里叶光学计算PSF # 必须可微,才能反传到光学参数 ...

这里的关键是光学模型必须可微。Zemax本身不可微,所以端到端训练通常用简化的可微光学模型(比如基于傅里叶光学的传播模型),或者用代理模型作为可微的光学前端。

训练时的损失函数通常是重建图像和真值的L2或L1距离,加上一些正则项。训完之后,光学参数就是优化后的镜头参数,再拿去Zemax做工程化。

4.3 端到端优化的实际收益与代价

收益很直观。文献里报道的单透镜计算成像,用一个球面单透镜配合重建网络,在特定任务上能逼近三片式镜头的成像质量。这意味着光学系统的体积、重量、成本都能大幅下降。对手机、内窥镜、AR眼镜这些对体积敏感的应用,价值巨大。

但代价也要说清楚。

第一,可微光学模型的精度问题。简化的傅里叶模型和真实光学系统有差距,优化出来的参数迁移到真实系统上会掉点。解决办法是用真实数据做微调,或者用代理模型提高仿真精度。

第二,对算法的依赖。端到端系统的成像质量高度依赖重建算法。如果算法在某个场景下失效(比如极端光照、运动模糊),整个系统就崩了。传统光学系统至少能保证"物理上成像",端到端系统没有这个保底。

第三,可解释性和可调试性差。传统光学设计,哪个面贡献了多少像差,一清二楚。端到端优化出来的系统,光学参数和算法参数耦合在一起,出了问题很难定位是光学的问题还是算法的问题。

我的建议是:端到端优化适合作为"探索性设计"的工具,用来找新的设计方向,但最终产品化还是要回到传统光学设计流程,把端到端优化出来的结构当作初始解,用Zemax做工程化和公差分析。

5. 环境搭建与工具链:少走弯路的配置方案

5.1 PyTorch环境搭建的版本匹配问题

光学方向的同学很多是从零开始学PyTorch,环境搭建是第一道坎。我见过太多人卡在版本不匹配上。

核心原则:PyTorch版本、CUDA版本、显卡驱动版本三者必须匹配。具体来说,你的显卡驱动支持的CUDA版本,必须大于等于PyTorch要求的CUDA版本。比如PyTorch 2.x通常要求CUDA 11.8或12.1,如果你的驱动太老,就得先更新驱动。

安装时不要直接pip install torch,那样装的是CPU版本。正确做法是去PyTorch官网查对应CUDA版本的安装命令,比如:

# CUDA 12.1 版本 pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121

装完一定要验证:

import torch print(torch.__version__) print(torch.cuda.is_available()) # 必须是True print(torch.cuda.get_device_name(0))

如果cuda.is_available()返回False,八成是版本不匹配或者驱动问题,别急着往下走,先把环境搞定。

5.2 用Anaconda管理多套环境

光学仿真和深度学习对Python版本、依赖库版本的要求经常冲突。比如ZOS-API可能依赖某个老版本的numpy,而PyTorch要求新版本。这时候用conda建独立环境是最省心的。

conda create -n optics_dl python=3.10 conda activate optics_dl # 在这个环境里装PyTorch和光学仿真相关的库

每个项目一个环境,互不干扰。虽然占点硬盘空间,但能省下大量排查依赖冲突的时间。

5.3 光学仿真与深度学习的工具链组合

把常用工具列一下,方便你按需选型:

环节工具说明
光学设计Zemax, Code V商业软件,精度高,有API
光学仿真LightTools, FRED照明和杂散光分析
可微光学PyTorch + 自写传播模型端到端优化必备
代理模型PyTorch, scikit-learn回归任务
数据处理NumPy, Pandas数据清洗和特征工程
可视化Matplotlib, Plotly结果展示

如果预算有限,开源方案也能用。比如用rayoptics或prysm做基础光线追迹,用diffractio做衍射计算。精度不如商业软件,但做算法验证够用。

6. 实操中的坑与经验

6.1 数据质量比模型结构重要

我刚开始做代理模型时,花了两周调网络结构,从MLP换到ResNet再换到Transformer,效果提升有限。后来发现问题出在数据上:采样范围太宽,大量样本是无效的极端结构,网络在这些样本上浪费了容量。

后来改成围绕可用结构做局部采样,数据量减少到原来的三分之一,模型精度反而上去了。这个教训是:在光学领域,数据的物理合理性比数量重要。与其生成十万条垃圾数据,不如精心设计采样策略,生成一万条高质量数据。

6.2 别迷信端到端,混合架构更稳

端到端优化听起来很美,但实际落地时问题很多。我现在更推荐混合架构:用代理模型做快速筛选,用传统优化做精修,用神经网络做后处理。每个环节用最合适的工具,而不是强行端到端。

比如设计一个手机镜头,可以这样组合:用生成模型给出初始结构候选,用代理模型快速评估筛选,用Zemax做精确优化和公差分析,最后用轻量级网络做图像后处理。这套流程比纯端到端更可控,也更容易调试。

6.3 公差分析这一步不能省

深度学习优化出来的结构,往往对制造误差很敏感。因为优化时只考虑了理想性能,没考虑公差。我见过一个端到端优化出来的单透镜,理论上成像很好,但公差分析显示,面型误差只要超过0.1微米,性能就崩了。这种结构在实验室能做,量产根本不可能。

所以无论用什么方法优化,最后一定要过公差分析这一关。Zemax的公差分析功能很成熟,把优化结果导进去,设置合理的公差范围,看良率能不能接受。不能接受就回去重新优化,在评价函数里加公差鲁棒性约束。

6.4 学会看损失曲线判断问题

训练深度学习模型时,损失曲线是最重要的诊断工具。

训练loss下降但验证loss不降,是过拟合,加正则化或者加数据。训练loss和验证loss都不降,是欠拟合或者学习率不对,检查网络容量和学习率设置。训练loss震荡剧烈,是batch size太小或者学习率太大,调小学习率或者增大batch。

还有一个光学领域特有的问题:如果损失曲线在某个值附近卡住不动,可能是物理约束项和主损失项冲突了。比如你既要网络预测的MTF接近真值,又要它满足单调性,两个目标打架,网络就卡住了。这时候要调整损失权重,或者重新设计约束方式。

7. 这条路适合谁,以及怎么开始

7.1 判断自己该不该投入

不是所有做光学的人都必须学深度学习。如果你做的是传统成像镜头设计,指标不极端,用Zemax完全能搞定,那没必要折腾。但如果你符合下面几条中的任何一条,就值得投入:

  • 你做的系统指标极端,传统方法优化不动,需要新的搜索策略
  • 你做计算成像、超表面、衍射光学,这些领域和算法天然耦合
  • 你在企业里做研发,需要快速评估大量设计方案
  • 你想发高水平论文,纯光学设计的创新空间越来越小,交叉方向机会更多

7.2 从哪个点切入最省力

我的建议是从代理模型切入。原因有三:第一,它不改变你现有的设计流程,只是加了一个加速工具;第二,它的技术门槛相对低,会PyTorch基础就能做;第三,它的收益立竿见影,优化速度提升是实打实的。

具体路径:先用ZOS-API采一批数据,训一个MLP代理模型,验证精度,然后把它接到你的优化流程里做快速筛选。跑通这个闭环,你就理解了"光学+深度学习"的基本范式,再往端到端、逆向设计走就顺了。

7.3 学习资源的取舍

市面上的深度学习教程很多,但针对光学领域的很少。我的建议是:深度学习基础部分,找一本讲得清楚的教材过一遍,重点理解反向传播、损失函数、优化器这几个核心概念,不用一上来就啃Transformer。光学部分,你本来就是做这个的,不用额外学。

真正要花时间的是把两边接起来:怎么把光学问题形式化成深度学习任务,怎么设计损失函数,怎么处理光学数据的特殊性。这部分没有现成教材,只能靠读论文和动手做。建议找几篇计算成像、光学逆向设计的综述,跟着复现一两个简单案例,比看十篇教程都管用。

最后分享一个我自己的体会:光学设计和深度学习的结合,最难的不是技术,而是思维方式。光学工程师习惯的是"确定性"——给定参数,结果唯一。深度学习的思维是"概率性"——给定输入,输出是一个分布。这两种思维要能自由切换,才能把两边的东西真正融合起来。我花了大概半年才适应这种切换,中间走了不少弯路。但一旦跨过去,你会发现能做的事情比原来多得多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询