最近集中学习pytorch,前两天做了个小实例突然想打包给别人耍耍,结果发现打包失败,搜索网上的方法也难以解决。整了两整天总算搞出来了,现在将整个踩坑与解决方法记录一下。当然这里同样的问题由于环境问题未必同样解法就能用,请自行决定取用。注:下述对我失败的方法网上也存在成功的情况。
本人在pycharm下terminal运行pyinstaller
环境为conda虚拟环境
python == 3.7.10
pyinstaller == 4.3
torch == 1.3.1
torchvision == 0.2.2.post3
推荐使用命令:
pyinstaller XXX.py
完成后应该会生成一个spec文件,一个dist文件夹(dist中有个文件夹XXX,下面是一系列文件),一个build文件夹,生成的可运行同名exe就在dist/XXX中。
(有人会在pyinstaller后跟-F, -w这两个命令,一个是将dist/XXX中一堆文件变为只生成一个稍大的exe,一个是去掉运行时的命令框,这两个建议先不加方便调试,不加貌似生成的快一点,搞定打包成功的话再加上重打一次包)
打包完成后注意,先将原来项目目录下要读取的文件啥的复制一份到dist/XXX中(比如txt啥的,否则可能找不到你要读取的目录)然后建议在cmd下执行exe文件,这样报错会显示(否则报错命令行一闪即逝,要靠手速截图)
前面最后会提示在site-packages\torch_init_.py出问题
https://blog.csdn.net/weixin_46767010/article/details/107280442
我发现我没缺文件,复制了也没啥用。。。绝望。。。
在生成的同名spec文件中找binaries_files,修改:
binaries_files = [ ('C://tools//Anaconda3//envs//3DP//lib//site-packages//torch//lib', '.') ]
注意,上述文件目录请改为自己的环境下的torch/lib目录(其中Anaconda后部分除3DP为环境名,其它大致相同),注意目录为双斜杠避免歧义!
然后在终端运行
pyinstaller XXX.spec
(注意是spec)
重装torch与torchvision,更换版本,建议新建一个虚拟环境搞,在新环境瞎搞,总好过搞坏原有脆弱的环境。当然torchvision可以试试直接装0.2.2,我也是按教程来的怕再出问题。
pip3 install torch===1.3.1 torchvision===0.4.2 -f https://download.pytorch.org/whl/torch_stable.html
此后产生错误“OSError: could not get source code”,
解决方法(失败):降低torchvision版本
pip uninstall torchvision
pip install torchvision==0.2.2.post3
没有解决问题,但因为已经用了不影响啥,我用了这个方法之后也没还原版本,所以降不降自己选择。如果按部就班建议操作一下。
成功的方法见坑3。
在打包后生成的同名spec文件中a = Analysis(…)下方加入:
for d in a.datas:
if '_C.cp36-win_amd64.pyd' in d[0]:
a.datas.remove(d)
break
然后运行pyinstaller XXX.spec(注意是spec)
按照1.3来。。。虽然还会报其它几个错,这个错不会报了,这我是没想到的。。。
在我踩完所有坑成功后,发现添加了-F的打包命令
pyinstaller -F XXX.py
后生成的exe运行时仍会报这个,但程序会正常运行
在打包后生成的同名spec文件中修改:
block_cipher = None
excluded_modules = ['torch.distributions'] # 加入这一行
a = Analysis(['xxx.py'],
pathex=['path'],
binaries=[],
datas=[],
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=excluded_modules, # 把=None改成=excluded_modules
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
然后运行pyinstaller XXX.spec(注意是spec)
对我没用,还是报这个错。
降低torchvision版本
pip uninstall torchvision
pip install torchvision==0.2.2.post3
虽然还是失败了,但我还是干了,之后没还原版本因为也能用,所以搞不搞自己看。
在python文件引入时,在import torch下方写:
def script_method(fn, _rcb=None):
return fn
def script(obj, optimize=True, _frames_up=0, _rcb=None):
return obj
import torch.jit
script_method1 = torch.jit.script_method
script1 = torch.jit.script
torch.jit.script_method = script_method
torch.jit.script = script
然后删除之前的spec,build,dist文件,重新应用
pyinstaller XXX.py
注意运行后依旧将项目读取文件复制一份进dist/XXX中。
然后发现,跑了一部分,有点输出!然后又报错。。。
此外,直接在pycharm运行原py代码,发现也报这个错。
报错内容为:
AttributeError: 'function' object has no attribute 'def_'
所以说没有完全成功,不过有些人就直接解决了没有报错。
追踪显示貌似是torch/jit某文件出了问题,并与自身python代码中的torch.jit.trace应用有关,解决办法见坑4。
追踪显示貌似是torch/jit某文件出了问题,并与自身python代码中的torch.jit.trace应用有关。
能遇见这个问题,一般来说是因为坑3的解决方法不完善,那咋整呢,我发现用了3.3方法后,仅在torch.jit.trace上出问题,灵机一动,在这代码之前把函数变回去不就成了吗?别说还真有用,于是这就成了解决办法。
具体如下:
在法3.3处方法中将修改的两个函数改名暂存为script_method1,script1
def script_method(fn, _rcb=None):
return fn
def script(obj, optimize=True, _frames_up=0, _rcb=None):
return obj
import torch.jit
script_method1 = torch.jit.script_method # 暂存函数
script1 = torch.jit.script # 暂存函数
torch.jit.script_method = script_method
torch.jit.script = script
然后在代码中使用torch.jit.trace(…)代码的前面将方法换回去
torch.jit.script_method = script_method1
torch.jit.script = script1
最后删除之前的spec,build,dist文件,重新打包
pyinstaller XXX.py
记得运行后依旧将项目下用到的读写文件复制一份进dist/XXX中。
至此解决,运行成功!!!