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

本文介绍了如何将包含机器学习模型的Python项目组织成符合标准的package,包括目录结构、模型持久化和模块间依赖关系的处理。通过创建__init__.py文件和调整sys.path,确保代码的可读性和可复用性。同时,展示了如何通过包装模型类,使其能在不修改原有代码的情况下,方便地进行接口调用和数据加载。
摘要由CSDN通过智能技术生成

注:本文主要是自己在做项目的过程中遇到的一些问题的解决经验,不一定完全符合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 = m

self.n = n

def add(self):

return self.m + self.n

if __name__ == '__main__':

foo1 = Foo1(1,2)

print(foo1.add())

foo2.py的代码如下,

import pandas as pd

from foo1 import Foo1

class 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.n

if __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 Foo2

if __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 ... 即可

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值