用pyinstaller打包LGBM模型为ELF/EXE可执行文件

1. 引入

写好的python代码和模型,如果需要做到离线部署、运行,就必须要将代码和模型打包为可独立运行的可执行文件。

使用pyinstaller就能做到这个,相同的代码,在windows上运行就能打包为exe,在linux上运行就能打包为elf。

打包的过程是怎么样?有哪些不同的打包方式?各有什么优缺点呢?

2. 打包过程:生成多个文件

假设我们的项目有3个文件组成:

  • main.py : 主入口程序
  • utils.py: 各种工具函数
  • model_rf.jl: 模型文件

打包过程分为如下步骤,在windows和linux都一样:

  1. 安装pyinstaller

    pip install pyinstaller

  2. 生成.spec文件

    pyi-makespec -w main.py

  3. 修改.spec文件

注意几点:

(1)主入口程序写在: Analysis第一个参数

(2)其他依赖程序写在:Analysis第一个参数的列表中

(3)模型文件写在: binaries中,注意要写为tuple

修改后好的.spec文件如下所示:

python 复制代码
# -*- mode: python ; coding: utf-8 -*-


block_cipher = None


a = Analysis(
    ['main.py',
	'utils.py'],
    pathex=[],
    binaries=[('model_rf.jl','.')],
    datas=[],
    hiddenimports=['scipy.special.cython_special'],
    hookspath=[],
    hooksconfig={},
    runtime_hooks=[],
    excludes=[],
    win_no_prefer_redirects=False,
    win_private_assemblies=False,
    cipher=block_cipher,
    noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)

exe = EXE(
    pyz,
    a.scripts,
    [],
    exclude_binaries=True,
    name='main',
    debug=False,
    bootloader_ignore_signals=False,
    strip=False,
    upx=True,
    console=False,
    disable_windowed_traceback=False,
    argv_emulation=False,
    target_arch=None,
    codesign_identity=None,
    entitlements_file=None,
)
coll = COLLECT(
    exe,
    a.binaries,
    a.zipfiles,
    a.datas,
    strip=False,
    upx=True,
    upx_exclude=[],
    name='main',
)

至于为什么要加入hiddenimports=['scipy.special.cython_special'],,是因为笔者在python3.8下运行,打包正常后,运行可执行文件依然报错如下:

shell 复制代码
(xxx) [aaa@bbb main]$ ./main
Traceback (most recent call last):
  File "main.py", line 1, in <module>
  File "PyInstaller/loader/pyimod02_importers.py", line 385, in exec_module
  File "sklearn/ensemble/__init__.py", line 5, in <module>
  File "PyInstaller/loader/pyimod02_importers.py", line 385, in exec_module
  File "sklearn/ensemble/_base.py", line 18, in <module>
  File "PyInstaller/loader/pyimod02_importers.py", line 385, in exec_module
  File "sklearn/tree/__init__.py", line 6, in <module>
  File "PyInstaller/loader/pyimod02_importers.py", line 385, in exec_module
  File "sklearn/tree/_classes.py", line 41, in <module>
  File "sklearn/tree/_criterion.pyx", line 1, in init sklearn.tree._criterion
ModuleNotFoundError: No module named 'scipy.special.cython_special'
[45300] Failed to execute script 'main' due to unhandled exception!

根据参考4,加入后就能修正该错误,因为pyinstaller没有加入这个必须的依赖。

  1. 运行命令进行打包

    pyinstaller main.spec

这种打包方式,会生成一个可执行文件(位于dist文件夹中),也会生成很多个运行该可执行文件所需的依赖库(dll, so),所以部署时,需要将整个文件夹拷贝到目标机。

那么,能不能只生成一个可执行文件,不生成额外的依赖文件呢?

3. 打包过程:生成单个文件

如果只有一个py文件,那么,使用一条命令就能实现生成独立的可执行文件:

复制代码
pyinstaller -F main.py

但是我们这个例子中,是有多个文件的,这就必须用下面的命令来打包:

复制代码
pyinstaller -F  -w main.py -p utils.py -p model_rf.jl --hidden-import scipy.special.cython_special

这样就能在dist文件夹中生成一个较大的可执行文件,部署时只需要部署这一个文件就可以。

4. 两种打包方式的区别

上面讲解了生成多个文件生成单个文件两种pyinstaller的打包方式。看上去生成单个文件方式更方便。

但是,实际运行打包后的可执行文件,就能发现:

(1)生成单个文件,最终只生成一个可执行文件,比较简单,但是运行很慢

(2)生成多个文件,最终生成一堆文件,但是其中的可执行文件运行会快很多;笔者实测这种方式比单个文件快5倍

为什么生成单个文件会更慢呢?从参考3可知

"one file" mode -- this mode means that it has to unpack all of the libraries to a temporary directory before the app can start

因为这个很大的单个文件,在运行主函数前,会将所有依赖都释放到临时文件中,再加载运行。这个释放文件的操作,需要占用I/O,而且每次启动程序都释放文件,自然就拖慢了运行速度。

5. 总结

pyinstaller能实现将多个.py文件,和其他模型文件,打包为可离线运行,不安装配置环境就能运行的可执行文件EXE或者ELF。

打包时,建议按照生成多个文件的方式来打包,这样程序运行起来会更快。 本文用到的所有代码和相关文件,都放到这个repo了,在linux下是正确运行的:https://github.com/ybdesire/machinelearning/tree/master/pyinstaller_model_package。

参考

  1. https://blog.csdn.net/weixin_42112050/article/details/129555170
  2. https://blog.csdn.net/LIUWENCAIJIAYOU/article/details/121470028
  3. 为什么打包后的程序运行慢: https://stackoverflow.com/questions/9469932/app-created-with-pyinstaller-has-a-slow-startup
  4. https://stackoverflow.com/questions/62581504/why-do-i-have-modulenotfounderror-no-module-named-scipy-special-cython-specia
  5. 本文所用代码。https://github.com/ybdesire/machinelearning/tree/master/pyinstaller_model_package
相关推荐
发哥来了几秒前
主流AI视频生成工具商用化能力评测:五大关键维度对比分析
大数据·人工智能·音视频
跳跳糖炒酸奶1 分钟前
基于深度学习的单目深度估计综述阅读(1)
人工智能·深度学习·数码相机·单目深度估计
曲幽7 分钟前
Django入门指南:Python Web开发的“瑞士军刀”
python·django·flask·fastapi·web·pythonweb
yangpipi-7 分钟前
第一章 语言模型基础
人工智能·语言模型·自然语言处理
m0_7482495410 分钟前
Java 语言提供了八种基本类型【文123】
java·开发语言·python
FJW02081414 分钟前
【Linux】SElinux的管理及优化
linux·运维·服务器
移幻漂流15 分钟前
Kotlin 如何解决 Java 的核心痛点:现代语言特性的深度剖析
java·python·kotlin
我的xiaodoujiao17 分钟前
使用 Python 语言 从 0 到 1 搭建完整 Web UI自动化测试学习系列 41--自定义定制化展示 Allure 测试报告内容
python·学习·测试工具·pytest
Piar1231sdafa18 分钟前
基于yolo13-C3k2-RVB的洗手步骤识别与检测系统实现_1
人工智能·算法·目标跟踪
2501_9421917718 分钟前
YOLO11-Seg-SwinTransformer榛子缺陷识别实战
python