我想进行统计模型的在线(主动)学习。
这意味着在编译时我有一个已知的初始训练数据集(x-y对)。
然而,由于其在线(主动)的性质,更多的数据会在运行时从第三方程序(一个C++仿真程序)中获取。
我在Python中使用GPytorch
进行这项工作,并通过subprocess
Python模块调用第三方程序。
我的问题属于编程类型,而不是GPytorch或统计类型,因此我在这里提问。
工作流程是:Python决定以哪些输入参数运行.cpp,创建一个基于这些参数命名的新文件夹,进入该文件夹,运行.cpp,收集文件夹中出现的数据,更新统计模型,Python决定以哪些输入参数运行.cpp,创建一个基于这些参数命名的新文件夹,进入该文件夹,运行.cpp,收集文件夹中出现的数据,更新统计模型…(例如,重复100次)。
在WSL1终端中,我通常使用以下命令运行.cpp代码:$ mpirun -n 1 smilei namelist.py
,这个命令在包含可执行文件smilei
和.py文件namelist.py
的文件夹中运行。
Python工作流程在主动学习循环的第一次迭代时返回退出码0(和必要的数据),但在第二次迭代时失败并返回退出码1。基本上,它在第一次迭代时完成了工作,但在第二次迭代时失败了。
我尝试使用subprocess.run()
和os.system()
(见下面的代码,所有的尝试都以注释开头),在括号内我输入了通常在BASH WindowsSubsytemForLinux1终端中运行第三方C++程序的命令。
我无法调试为什么第二次会失败。
我尝试打印出子进程的stdout
和stderr
,在第二次迭代的主动学习循环中查询时,它们都返回空行,似乎没有这些(没有stdout和stderr)。
我知道下面的代码可能看起来很复杂,但其实不然。它只是遵循了我上面提到的工作流程。
def SMILEI(I): os.chdir(top_folder_path)# 创建一个新文件夹,命名为a0_942.782348987103(示例值) a0 = "%.13f" % a0_from_IntensityWcm2(I) dirname = "a0_%.13f" % a0_from_IntensityWcm2(I) os.mkdir(dirname)# 进入创建的文件夹 os.chdir(top_folder_path + "/" + dirname) print("我们已更改目录并进入了新创建的文件夹!")# 将通用namelist复制到这个新创建的文件夹中 shutil.copy(top_folder_path + "/" + general_namelist_name, ".") print("我们已复制了通用namelist!")# 在通用namelist中添加a0值,即在第8行(空行)添加一行a0 = 942.782348987103。 with open(general_namelist_name, 'r+') as fd: contents = fd.readlines() contents.insert(8, "a0 = {}".format(a0)) # 新字符串应该以换行符结束 fd.seek(0) # readlines消耗了迭代器,因此我们需要重新开始 fd.writelines(contents) # 不需要截断,因为我们正在增加文件大小 print("我们已修改通用namelist以包含第8行的a0 = ...")# 重命名修改后的namelist os.rename(general_namelist_name, particular_namelist_name) print("我们已将通用namelist重命名为namelist_Xe_GPtrial_noOAM_a0included.py")# 运行仿真 print("我们现在将在文件夹内运行SMILEI命令:") print(os.getcwd()) print("根据os指示的smilei可执行文件的绝对路径是:") print(os.path.abspath("../smilei")) cp = subprocess.run(["mpirun", "-n", "1", os.path.abspath("../smilei"), particular_namelist_name], # stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE) #stdout=subprocess.PIPE, stderr=subprocess.PIPE) #capture_output=True) ) print("返回代码是:") print(cp.returncode) #os.system("mpirun -n 1 ../smilei {}".format(particular_namelist_name)) #subprocess.run("mpirun -n 1 ../smilei {}".format(particular_namelist_name), shell=True) #print(cp.stdout) # Y #print(cp.stderr) #print(cp.returncode) # 获取仿真结果 # os.chdir(top_folder_path + "/" + dirname) # print("我们再次更改了目录并再次进入了新创建的文件夹!") S = happi.Open(".") pbb = S.ParticleBinning(0).get() results_dict = dict() for z in range(len(pbb['data'][-1])): results_dict['c_%d' % z] = pbb['data'][-1][z] return np.asarray(list(results_dict.values()))if __name__ == '__main__': # 初始训练数据集: x_train = torch.from_numpy(np.array([0.1, 0.3, 0.5, 0.6, 0.8])) y_train = torch.from_numpy(np.array([0.1, 0.2, 0.3, 0.4, 0.5])) # 初始化似然和模型 likelihood = gpytorch.likelihoods.GaussianLikelihood() model = ExactGPModel(x_train, y_train, likelihood) model.train() likelihood.train() # GPs的"损失" - 边际对数似然 mll = gpytorch.mlls.ExactMarginalLogLikelihood(likelihood, model) optimizer = torch.optim.Adam(model.parameters(), lr=0.1) training_iters = 10 for i in range(training_iters): optimizer.zero_grad() output = model(x_train) loss = - mll(output, y_train) loss.backward() print('迭代 %d/%d' % (i+1, training_iters)) optimizer.step() Xn = x_train Yn = y_train ###################################################################################### # 主动学习(AL)循环: budget_value = 100 for i in range(budget_value): OldValues = lhs(1, samples=100) Xref = range_transform(OldValues, 10.0**20, 10.0**25) x_nplus1 = xnp1search(model, Xn, Xref) # x_nplus1是下一次运行SMILEI进行主动学习GP拟合的强度(W/cm2) y_nplus1 = SMILEI(x_nplus1.detach().numpy())[53] # SMILEI(x_nplus1.detach().numpy())返回一个形状为(55,)的ndarray Xn = torch.cat( ( Xn, torch.reshape(x_nplus1, (1,)) ) ) Yn = torch.cat( ( Yn, torch.reshape(torch.from_numpy(np.reshape(y_nplus1, (1,))), (1,)) ) ) model.set_train_data(Xn, Yn, strict=False) for j in range(training_iters): optimizer.zero_grad() output = model(Xn) loss = -mll(output, Yn) loss.backward() print('迭代 %d/%d' % (j+1, training_iters) + '在AL步骤号 %d/%d' % (i+1, budget_value)) optimizer.step()
为什么第二次会失败?
我实在看不出来。我也无法调试它,我没有得到任何错误消息或任何东西,它只是在第二个创建的文件夹中不运行仿真,该文件夹在Python脚本结束时只包含namelist_Xe_GPtrial_noOAM_a0included.py
,其中包含了a0值(正如它应该的那样)。
谢谢!
回答:
我自己使用plumbum
模块解决了这个问题。
我的代码保持不变,它们都很好。
然而,我修改了subprocess.run()
命令,或者我尝试过的许多变体,改为smi = local.cmd.mpirun
,然后smi("-n", "1", "../smilei", particular_namelist_name)
,我可以在循环的每次迭代中运行它!