赞
踩
最近公司的某个项目数据量比较多了,需要预测某些值,所以就有同事负责用python建立机器学习模型,因为系统本身是用Java做的后台,我来负责做对接调用模型。这整件事,通俗点来讲,就是Java传入某些参数调用python的模型,然后python输出某个结果,Java拿到结果之后就可以为所欲为了,就不多说了。但是,至于为什么不用Java直接建模呢? 答案是:可以。但是由于种种原因还是用python建模,所以Java就不得不调用python了。
这种方式就是执行python命令行,通过args可以传入多个参数,前提是已经有了python的环境,并且相关的库已经安装,假设本地命令行需要用python3来执行,第一个参数那就换成python3;第二参数是python脚本路径,第三个参数是脚本中需要执行的方法名子,假设脚本中有多个方法的情况下,只有一个可以不传。参数什么的其实也无所谓了,主要python脚本中接受的时候一一对应上就可以了。
//py脚本的路径 private static String pythonScriptPath = ""; public static String getScikitLearnValue(String... args) { try { List<String> command = new ArrayList<>(Arrays.asList("python", pythonScriptPath, "calculate_comprehensive_index")); command.addAll(Arrays.asList(args)); Process proc = Runtime.getRuntime().exec(command.toArray(new String[0])); try (BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream(), StandardCharsets.UTF_8))) { StringBuilder result = new StringBuilder(); String line; while ((line = in.readLine()) != null) { result.append(line); } proc.waitFor(); return result.toString(); } catch (IOException e) { return "0"; } } catch (IOException | InterruptedException e) { return "0"; } }
python具体参数如下。还有一点,Java调python,并非真正的调用,固化的思想是以为调用某一个函数,这个函数retrun的值就给到Java了,但是不是的,其实是Java读取IO流得到的结果,所以python中能够执行到的print,Java都能拿到,所以要尽量避免一些不必要的输出。而你想拿到的值也不能不打印只return。
import sys
if __name__ == "__main__":
if len(sys.argv) < 4:
print("Please provide function name and two numbers to sum")
else:
function_name = sys.argv[1]
if function_name == "calculate_comprehensive_index":
model_path = sys.argv[2]
num1 = int(sys.argv[3])
result = calculate_comprehensive_index(model_path, num1)
print(result)
else:
print("Function not found: " + function_name)
方式一也挺好用的,但是方式一也确实存在一些弊端,因为我这次调的python不是一小小的python,而是机器学习,对于python工程师来时,虽然把一些模型都封装了,用的时候也方便,如下,加载模型
from joblib import load
load(modelPath)
模型小则几十M,大则百兆甚至更大,load(modelPath),可想而知,从磁盘读取一个这么大的文件,而且需要频繁的计算,先优化一下,Java的思想,可以把这个加载过程放到静态块中不就可以了?没错,这个思路是对的,而且对于python来说,直接放在全局位置就可以了,就可以避免多次加载模型,但是对于方式一来所,每次调用命令,都是从磁盘找到python脚本,加载指定位置的模型,这速度相当的慢,频繁的数据计算,系统一会儿就阻塞了。所以不得不出现了这个第二种方式,简单来说就是把python做成服务,而且开放接口,供Java调用,这个速度相比第一种快的不要太多。使用的是Flask。
1、首先安装
pip install flask
2、创建一个 python 脚本,命名为 app.py,并编写以下内容:
from flask import Flask, request, jsonify from joblib import load app = Flask(__name__) # 加载模型 model_path = "path_to_your_model_file.joblib" model = load(model_path) # 定义 API 路由 @app.route('/predict', methods=['POST']) def predict(): try: # 获取 POST 请求中的数据 data = request.json # 使用模型进行预测 prediction = model.predict(data) # 返回预测结果 return jsonify({'prediction': prediction.tolist()}), 200 except Exception as e: return jsonify({'error': str(e)}), 500 if __name__ == '__main__': app.run(debug=True)
在上面的代码中,我们定义了一个路由 /predict,它接受 POST 请求并调用模型进行预测。预测结果以 JSON 格式返回给客户端
3、运行 Flask 应用程序
python app.py
4、关闭日志输出,如果你需要的话
import logging
app = Flask(__name__)
# 获取Flask应用程序的根日志记录器
log = logging.getLogger('werkzeug')
log.setLevel(logging.ERROR) # 设置日志级别为ERROR,忽略所有INFO级别的日志
# 关闭Flask应用程序的日志处理器,以禁用所有日志输出
for handler in log.handlers[:]:
log.removeHandler(handler)
# 为Flask应用程序添加一个空的日志处理器,确保没有日志会被输出
log.addHandler(logging.NullHandler())
系统 CentOS Linux release 7.9.2009 (Core)
#安装必要开发工具和库文件以便进行编译和构建需要这些依赖的软件
yum install gcc openssl openssl-devel make automake autoconf gcc-c++ bzip2-devel sqlite-devel
#通过安装 epel-release-latest-7.noarch.rpm,你可以扩展你的系统的软件源,获得更多可用的软件选择,从而更方便地满足你的软件需求
wget https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
#很多python库和框架在安装或构建过程中需要依赖OpenSSL这个库,特别是一些涉及到加密、安全通信或网络操作的库
yum install openssl11-devel
#在文件 configure 中查找所有出现的PKG_CONFIG openssl 字符串,并将其替换为PKG_CONFIG openssl11
sed -i 's/PKG_CONFIG openssl /PKG_CONFIG openssl11 /g' configure
#下载Python安装包,进入安装包之后,执行下面的配置prefix的安装位置
./configure --prefix=/usr/local/python
#把旧版本python备份
mv /usr/bin/python /usr/bin/python2.7.bak
#建立软链接
ln -s /usr/local/python/bin/python3 /usr/bin/python
ln -s /usr/local/python/bin/pip3 /usr/bin/pip
至此,已经可以完成Java很好的调用python了,但是这次的坑就是在Linux上装python等,不小心没装好,就想着肯定要删了重新装了,一删不打紧,把自带的python 2.7.5也删掉了,大家切记不要删,删完之后你会发现哇塞 ls都用不了了,最后也恢复了好才恢复好,后续如果还有性能问题应该会用WSGI来提高并发能力。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。