注:本文主要是自己在做项目的过程中遇到的一些问题的解决经验,不一定完全符合python开发规范,如读者有更好的方案欢迎讨论

机器学习模型与一般的代码逻辑不同,其输出结果通常不是显式地以代码的形式存在,而是基于大量第三方框架的API表示的,例如scikit-learn、tensorflow。由于机器学习模型训练时间很长,更无法在需要使用的时候立即训练得到。所以需要将模型持久化方便二次开发或调用。

在模型持久化之前,最好先将当前的工程打包成python的package,并将模型暴露成唯一的借口。这可以避免将其他业务层的代码与模型代码混在一起,增强可读性。网上的教程比较混乱,这里是我整理的一种比较简便的形式。以下是一个示例项目的目录结构组织形式。该示例项目可以在我的github上查看。

my_package是机器学习工程的包,通过__init__.py标志该目录为package。在开发my_package的时候包含了三个文件夹model/和src/,分别存放模型数据文件和主要代码。模型的类Foo2存放在foo2.py中。稍后讲如何在不修改模型代码的前提下将该工程封装成package并提供接口调用Foo2。我们先来看如果做好了这一步,会有多么方便。

foo1.py的代码如下,

class Foo1:def __init__(self, m=None, n=None):self.m = mself.n = ndef add(self):return self.m + self.nif __name__ == '__main__':foo1 = Foo1(1,2)print(foo1.add())

foo2.py的代码如下,

import pandas as pd
from foo1 import Foo1class Foo2(Foo1):def __init__(self, m=None, n=None):super(Foo2, self).__init__(m, n)def load_data(self, file='../model/test.txt'):df = pd.read_csv(file, sep = ',', header = None)self.m = df[0][0]self.n = df[1][0]def multiply(self):return self.m*self.nif __name__ == '__main__':#foo2 = Foo2(1,2)#print(foo2.multiply())foo2 = Foo2()foo2.load_data()print(foo2.multiply())

工程中各个模块之间的依赖关系通过foo1.py和foo2.py展示。可以看到foo2.py需要用到foo1.py中的类Foo1,因而需要from foo1 import Foo1。单独运行foo2.py会打印结果。然而作为一个package,这样写是不太合规范的,应当在foo2.py中使用from .foo1 import Foo1(多了一个点)。这样在my_package/__init__.py 中就不需要添加工程的路径到sys.path中了(见下文)。但是坏处是使得foo2.py无法单独运行了,因为直接运行python foo2.py时,入口在foo2.py的目录,程序只能找到foo1却无法找到 .foo1。本着不修改原始代码的原则,这里采用较不规范的from foo1 import Foo1写法。

main.py的代码如下,

from my_package import Foo2if __name__ == '__main__':foo2 = Foo2()foo2.load_data(file='my_package/model/test.txt')print(foo2.multiply())

在main.py中,我们不用关心Foo2这个类存放在my_package中哪个子目录的哪个文件中,只需要简单 from ... import ... 即可。否则需要通过 from my_package.src.foo2 import Foo2,这就要求使用my_package的人对各个类的位置十分熟悉。代码中提供了foo2.load_data方法,该方法的输入是数据文件路径。这是因为我们可能需要测试新的数据,因而通常这样的操作会将文件输入路径作为变量暴露出来,而不持久化在代码中。

现在将如何将原始的工程封装成package。首先是在各个目录(my_package/,model/和src/)下添加__init__.py文件,内容暂时为空。__init__.py文件的存在使得当前目录成为独立的package。然而作为一个整体的package,我们需要告诉程序各个包之间的关系。这就需要在__init__.py文件中添加必要的内容。需要说明的是,import的时候,__init__.py文件中的代码会先于目录中的代码文件运行,这有助于标识依赖关系。

my_package/__init__.py 中的内容如下:

# import sys
# sys.path.append('my_package/src')# import sys
# from os.path import abspath, join, dirname
# sys.path.insert(0, join(abspath(dirname(__file__)), 'src'))import sys
from os.path import abspath, join, dirname
my_path = join(abspath(dirname(__file__)), 'src')
if my_path not in sys.path:sys.path.insert(0, my_path)from .src import Foo2
#from .src import Foo1

model/__init__.py 中不需要添加代码。

src/__init__.py 中的内容如下:

from .foo2 import Foo2
#from .foo1 import Foo1

刚才提到由于不修改原始代码中import的方式,现在需要在my_package/__init__.py中修改sys.path。这里展示了三种做法,前两种注释掉了。第一种方法最简单。第二种方法的缺点是如果反复的运行main.py,会不断地将 'my_package/src' 增加sys.path中。第三种方法避免了这个问题。

再来看如何将Foo2这个类一层层地暴露出来。src/__init__.py 中首先指明了Foo2在foo2.py这个模块中。当程序运行到这里时,Foo2就存在于src的命名空间中了。因而在my_package/__init__.py 中就可以直接from .src import Foo2,进一步将Foo2放在my_package的命名空间中。如此,在main.py中就可以直接from my_package import Foo2。Foo1的暴露方法类似,不过我们这里不需要使用Foo1。

最后总结一下步骤:

1、在工程目录及各个子目录中增加__init__.py

2、my_package/__init__.py 中增加修改sys.path的代码

3、按照目录关系,将需要暴露的类或函数由内至外逐级写在各目录的__init__.py中

4、在需要调用的时候直接from my_package import ... 即可

mariadb不支持load data_不修改代码打包python机器学习工程相关推荐

  1. python如何修改代码_不修改代码打包python机器学习工程

    注:本文主要是自己在做项目的过程中遇到的一些问题的解决经验,不一定完全符合python开发规范,如读者有更好的方案欢迎讨论 机器学习模型与一般的代码逻辑不同,其输出结果通常不是显式地以代码的形式存在, ...

  2. 使用vite和Element Plus,实现部署后不修改代码/打包,新增主题/皮肤包

    Web前端界面切换主题/皮肤,是一个常见的需求.如果希望在打包部署后实现皮肤的修改甚至增加皮肤,不需要修改源码或者重新打包,类似于我们常见的皮肤包扩展,又该如何实现呢? 我使用类似上一期多语言包功能中 ...

  3. 朴素贝叶斯代码实现python

    P(B)称为"先验概率",即在A事件发生之前,对B事件概率的一个判断. P(B|A)称为"后验概率",即在A事件发生之后,对B事件概率的重新评估. P(A|B) ...

  4. python机器学习开源代码_Python简化代码机器学习库PyCaret 2.0发布

    字幕组双语原文:Python简化代码机器学习库PyCaret 2.0发布 英语原文:Announcing PyCaret 2.0 翻译:雷锋字幕组(Shangru) 我们激动的宣布,PyCaret第二 ...

  5. jz2440开发板移植U-boot之修改代码支持DM9000网卡

    今天我们来移植U-boot到jz2440开发板,修改代码支持DM9000网卡.查看之前写的移植记录请点击链接:点击查看之前的移植记录 现在大多数开发板都支持DM9000网卡.我们的U-boot源码里面 ...

  6. 移植uboot之修改代码支持NorFlash记录续集

    接着上一篇文章写的内容(上一篇文章链接:移植uboot之修改代码支持NORFLASH),上一篇结尾测试flash的擦除读写功能,结果无法写flash,卡在了这里: 前面已经擦除成功,这里写内容写不进去 ...

  7. am335x linux修改ip,Linux 修改代码以支持LED 控制(board-am335xevm)

    Linux 修改代码以支持LED 控制(board-am335xevm) Note: All the codes are changed at the file board-am335xevm.c d ...

  8. 五、移植u-boot-2016.03到Jz2440之修改代码支持NOR Flash

    5. 移植u-boot-2016.03修改代码支持NOR Flash 从上一节把uboot烧写到NAND启动后,最后打印出Flash: 0 Bytes,如下图所示.我们的Jz2440开发板的NOR F ...

  9. 【计算机系统结构】Self-modifying code 自修改代码

    Self-modifying code 在计算机科学中,自修改代码是一种代码,当代码执行时修改它自身的指令,通常用于减少指令路径长度以提高性能或简单地减少额外的,重复的,相似的代码,以此来简化维护.自 ...

最新文章

  1. SHELL中的三神器:管道、重定向、backtick
  2. ARM汇编:加载和存储指令集(六大类)---LDR(ADR)、LDRB、LDRH、STR、STRB、STRH
  3. 快速构建Windows 8风格应用14-ShareContract概述及原理
  4. 用基本信号画出如下的信号_股市入门基本知识丨下跌时期可以抄底的安全信号有哪些...
  5. spring boot测试_测试Spring Boot有条件的合理方式
  6. 使用bootstrap的相关配置
  7. osi七层模型 与Linux的一些常用命令和权限管理 继承上篇
  8. react小书没读完的记录
  9. 计算机转换外界信息原理,高级文秘及办公自动化教程-计算机基础
  10. 爬虫入门(1)——requests(1)
  11. Vue中 $event not defined
  12. div上下切换(新增、删除、上下div切换)
  13. 澳拳击袋鼠体型巨大 身高超2米体重近200斤
  14. java-从date中获取年份
  15. 使用IIS部署网站步骤
  16. ssm微信小程序美容理发店预约系统app——计算机毕业设计
  17. nopCommerce3.9简体中文语言包(nopCommerce3.9 language)
  18. 那些辉煌的背后, 不知装载了多少苦涩
  19. OpenLayers结合Turf实现空间运算
  20. python画xy轴_python中的坐标轴该如何画?好画吗?

热门文章

  1. 用macport安装nginx
  2. angular 生命周期钩子 ngOnInit() 和 ngAfterViewInit() 的区别
  3. Oracle hint手动优化
  4. 20155303 2016-2017-2 《Java程序设计》第一周学习总结
  5. PostgreSQL条件语句
  6. webhook机器人php源码,webhook机器人
  7. SpringBoot2.0应用(五):SpringBoot2.0整合MyBatis
  8. Cocos creator之javascript闭包
  9. 基于神念TGAM的脑波小车(1)
  10. 在此激励自己,记录自己的学习历程