前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用Cython编译Python源码加密加速,有这一篇就够了!

使用Cython编译Python源码加密加速,有这一篇就够了!

原创
作者头像
superhua
修改2024-06-09 13:34:17
1650
修改2024-06-09 13:34:17
举报
文章被收录于专栏:CNNCNN

0 前言

python是一门脚本语言,运行时由python虚拟机解释执行。当我们使用python设计好算法给第三方使用时只能提供源码,任何运行我们算法的人都可以看到源码以及对应的算法思路。因此,需要一定手动保护源码。

最简单的保护方式是使用代码混淆,加大阅读难度。但这只是加大阅读难度,对方只要花点时间,还是可以理解算法思路。今天介绍使用Cythonpython源码编译成库文件(Windows平台为pyd文件,Linux平台为so文件),用户拿到库文件后,无法反编译为python源码,从而保证了代码安全性。另外,还能达到代码运行加速效果。关于Cython更详细内容这里不过多介绍,本文主要介绍如何使用Cythonpython编译为库文件使用。

最近看到一个巨牛的人工智能教程,分享一下给大家。教程不仅是零基础,通俗易懂,而且非常风趣幽默,像看小说一样!觉得太牛了,所以分享给大家。平时碎片时间可以当小说看,【点这里可以去膜拜一下大神的“小说”】

1 场景实战

mobilenet v3识别ImageNet为例,项目目录如下所示:

初始算法项目结构
初始算法项目结构

核心代码在src根目录下,各个代码文件作用:

  • src/model/mobilenetv3.py:定义模型网络结构
  • src/service/model_service.py: 定义模型创建、推理
  • test_img/test.jpg: 定义测试图片,图片内容为一架飞机
  • weights/mobilenetv3_small_67.4.pth.tar: 训练好的模型参数
  • test.py: 测试代码

看看test.py具体代码,其他文件无需过于关注,感兴趣的读者可以翻阅到本文末端获取完整代码。test.py具体代码如下:

代码语言:python
代码运行次数:0
复制
from src.service.model_service import load_model, infer

model = load_model("weights/mobilenetv3_small_67.4.pth.tar")

class_idx, score = infer(model, "test_img/test.jpg")
print(class_idx, score)

运行上面代码,输出结果如下:

代码语言:python
代码运行次数:0
复制
404 0.8282396

Imagenet中,索引为404的类别为客机,可以看到整体运行没有问题。

2 算法源码加密

好了,有了上面的算法场景后,接下来我们对以上场景中的算法源码加密。

2.1 环境准备

安装Cython

执行如下命令安装Cython

代码语言:shell
复制
pip install Cython

安装c/c++编译环境

对于Linux读者,只要有gcc编译环境即可,这里不过多介绍。

对于windows读者,安装好最新的VisualStudio即可。没有安装VisualStudio的读者,可以前往https://visualstudio.microsoft.com/zh-hans/visual-cpp-build-tools/下载。

本文是在Winodws平台,使用VisualStudio 2022编译环境运行。

2.2 编写编译代码

注意,实际的编译代码由Cython实现,我们只是简单使用。主要是设置本地需要编译成pyd(或so)的python文件,无其他复杂内容, 读者可以直接拿去使用,注意修改代码根路径即可。创建文件compile.py内容如下:

代码语言:python
代码运行次数:0
复制
import os
from setuptools import setup
from distutils.extension import Extension
from distutils.command.clean import clean
from Cython.Distutils import build_ext
 
def load_all(root):
    out = []
    if os.path.isdir(root):
        out.append((root, True))
        names = os.listdir(root)
        for name in names:
            p = os.path.join(root, name)
            if os.path.isdir(p):
                out += load_all(p)
            else:
                out.append((p, False))
    else:
        out.append((root, False))
    return out
 
def load_files(root, exts=("py",)):
    out = []
    for (p, is_dir) in load_all(root):
        if not is_dir:
            ext = p.split(".")
            if len(ext) > 1 and ext[-1] in exts:
                out.append(p)
    return out
 
def get_packages(root):
    out = []
    for (p, is_dir) in load_all(root):
        if is_dir:
            out.append(p)
    return out
 

# ex_files参数可以支持不在src文件夹下的文件进行加密
def get_extensions(root):
    py_files = load_files(root, exts=("py",))
    ext_names = map(lambda x: x.replace(os.path.sep, '.')[:-3], py_files) 
    def make_extension(ext_name):
        ext_path = ext_name.replace('.', os.path.sep) + '.py'
        ext = Extension(ext_name, [ext_path], include_dirs=['.'])
        ext.cython_directives = {'language_level': "3"}
        return ext
    extensions = map(lambda x: make_extension(x), ext_names)
    return list(extensions)
 
# 对加密后的py,pyc和c文件进行清除
class CleanCode(object):

    def clean_build(self, distribution):
        clean_command = clean(distribution)
        clean_command.all = True
        clean_command.finalize_options()
        clean_command.run()
    def delete(self, root, exts):
        src_files = load_files(root, exts=exts)
        for source_file in src_files:
            if os.path.basename(source_file):
                os.remove(source_file)
        
    def clean_pro(self, root):
        self.delete(root, exts=("pyd", "pyc", "c", "so"))
         
    def delete_source_code(self, root):
        self.delete(root, exts=("py", "c")) 
        
 
MODULE_NAME = "mobilenet_test"   # 给项目名字
root = "src" 
# 继承Cython的build_ext类  
class CustomBuildExt(build_ext, CleanCode):
  
    def run(self):
        self.clean_pro(root)
        build_ext.run(self)
        self.clean_build(self.distribution)
        self.delete_source_code(root)  # 清理源代码,只保留编译后文件
 
setup(
    name=MODULE_NAME,
    packages=get_packages(root),
    ext_modules=get_extensions(root),
    cmdclass={'build_ext': CustomBuildExt}   # 自定义的CustomBuildExt
)

注意第73行代码定义了需要编译的python代码根目录。执行上面代码后,会自动清理掉原始的python源码,读者需要做好备份。如果读者想保留原始python代码,将第81行注释即可。具体执行以上代码命令为:

代码语言:shell
复制
python compile.py build_ext --inplace

执行后,项目结构如下:

编译后的算法项目结构
编译后的算法项目结构

编译后的算法代码是二进制汇编代码,已经无法反编译:

编译后的算法库文件
编译后的算法库文件

原始的算法代码无需任何修改,继续执行test.py文件:

代码语言:shell
复制
python test.py

输出如下:

代码语言:shell
复制
404 0.8282396

可以看到输出内容一模一样

3 获取源码

  1. 关注公众号:Python学习实战
  2. 公众号聊天界面回复:Cython示例,获取完整源码。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0 前言
  • 1 场景实战
  • 2 算法源码加密
    • 2.1 环境准备
      • 安装Cython
      • 安装c/c++编译环境
    • 2.2 编写编译代码
    • 3 获取源码
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档