搭建自己的AI模型应用网站:JavaScript + Flask-Python + ONNX

1. 前言

本文作者以一个前端新手视角,部署自己的神经网络模型作为后端,搭建自己的网站实现应用的实战经历。目前实现的网页应用有:

  • AI 语音服务主页
  • AI 语音识别
  • AI 语音合成
  • AI CP号码生成器

欢迎大家试用感受,本文将以博客基于GAN的序列号码预测中训练的pytorch模型为例,进行后端和前端搭建,并构建网站,以下是最终成果展示。
在这里插入图片描述
网址:http://www.funsound.cn:5002

2. 相关内容

相关知识点和工具语言如下,希望读者有一定的了解

  • 腾讯云服务器
  • Html + JavaScript 进行UI设计
  • pytorch 模型,onnx 模型导出
  • python flask 后端
  • 多进程服务实现并发访问

3. 后端工作

3.1 pytorch 模型转 onnx 模型

ONNX 模型是通用的NN格式,采用onnx格式将在服务器cpu推理上速度更快。

# 实例化生成器模型
generator = Generator(input_dim, output_dim)# 加载训练好的生成器模型权重
generator.load_state_dict(torch.load('models/generator_model.pth'))
generator.eval()  # 设置生成器为评估模式# 导出模型为 ONNX 格式
generator.export_onnx('models/generator_model.onnx', (batch_size, input_dim))

加载onnx模型进行推理

# 加载 ONNX 模型
ort_session = ort.InferenceSession('models/generator_model.onnx')
input_name = ort_session.get_inputs()[0].name
output_name = ort_session.get_outputs()[0].name
input_noise = np.random.randn(batch_size, input_dim).astype(np.float32)
generated_numbers = ort_session.run([output_name], {input_name: input_noise})[0]

基于onnx推理的CP号码生成算法封装成 【generator. LOTTO_GENERATOR】

3.2 多进程onnx服务

网站访问往往是一个多路并发访问场景,面对众多用户的请求,送入待处理,后端采用多进程进行调度。

if __name__ == "__main__":from generator import LOTTO_GENERATOR # 我们的gan网络生成算法# 初始化worker数量nj = 4backends = [LOTTO_GENERATOR() for _ in range(nj)]workers = init_workers(nj=nj, backends=backends)# 获取并打印所有worker的状态res = get_workers_state(workers)print(res)# 提交100个任务worker_dir = "demo"for _ in range(100):task_id = generate_random_string(length=11)  # 生成长度为11的随机字符串作为task_idtask_dir = f"{worker_dir}/{task_id}"  # 任务目录task_inp = generate_random_number_string(length=8)  # 生成长度为8的随机数字字符串作为任务输入task_prgs = f'{task_dir}/progress.txt'  # 任务进度文件路径task_rst = f'{task_dir}/result.txt'  # 任务结果文件路径os.system(f'mkdir -p {task_dir}')  # 创建任务目录params = {'task_id': task_id,'task_inp': task_inp,'task_prgs': task_prgs,'task_rst': task_rst}submit_task(workers=workers, params=params)  # 提交任务time.sleep(0.01)  # 等待10毫秒后提交下一个任务

注意代码中多进程服务处理用户请求采用异步方式,用户提交任务后获取task_id, 主进程不会阻塞, 用户根据task_id来追踪自己的任务进度(task_prgs)和结果(task_rst)。

其中调度方式根据子进程的忙碌情况决定,选取最闲的子进程处理用户请求

def submit_task(workers, params: dict):# 找到任务最少的workermin_task_worker = min(workers, key=lambda worker: worker.queue.qsize() + worker.working.value)min_task_worker.queue.put(params)  # 将任务提交到最少任务的worker队列中print(f'assign the task to worker-{min_task_worker.wid}'

3.3 基于Flask搭建http访问接口

我们的后端代码如下,例如我们的ip 是 100.100.123,端口试用5002,则构建了以下http访问接口:
http一般格式: 【http://IP地址:端口/路由】

  • http://100.100.123:5002/ 主页
  • http://100.100.123:5002/lotto 提交任务 【输入:用户幸运数字,输出:task_id】
  • http://100.100.123:5002/get_worker_state 子进程负载状态 【输入:task_id,输出:负载状态】
  • http://100.100.123:5002/get_task_prgs 任务完成进度 【输入:task_id,输出:任务进度】
  • http://100.100.123:5002/get_task_rst 任务结果 【输入:task_id,输出:任务结果】
from flask import Flask, jsonify,render_template,request
from generator import LOTTO_GENERATOR
from workers import *
import datetime
import json def get_now_time():current_time = datetime.datetime.now()return current_time.strftime('%Y-%m-%d %H:%M:%S')def task_log(text,log_file="TASK.LOG"):with open(log_file,'a+') as f:print(text,file=f)app = Flask(__name__)
USER_DIR = "user_data"
TASK_MAP = {}"""
主页
"""
@app.route('/')
def index():return render_template('index.html')@app.route('/lotto', methods=['POST'])
def predict():# 获取客户端信息ip = request.remote_addrdata = request.get_json()task_id = ip + "_" + generate_random_string(20)user_id = iptask_inp = data['luck_num'] # 8位数字字符串 或者 空字符串task_dir = "%s/%s/%s" % (USER_DIR, user_id, task_id)task_prgs = f'{task_dir}/progress.txt'  # 任务进度文件路径task_rst = f'{task_dir}/result.txt'  # 任务结果文件路径task_log(f"TIME:{get_now_time()}")task_log(f"TASK_ID:{task_id}")task_log("")# 生成临时文件if not os.path.exists(task_dir): os.makedirs(task_dir)with open(task_prgs,'wt') as f:json.dump([0.0,'start'],f,indent=4)TASK_MAP[task_id] = {'task_dir': task_dir,'task_prgs': task_prgs,'task_rst': task_rst, }# 提交任务params = {'task_id': task_id,'task_inp': task_inp,'task_prgs': task_prgs,'task_rst': task_rst}submit_task(workers=workers, params=params)  # 提交任务return task_id"""
获得引擎状态
"""
@app.route('/get_worker_state', methods=['GET'])
def get_worker_state():ip = request.remote_addrres = {}for worker in workers:res[worker.wid] = worker.queue.qsize() + worker.working.valuereturn res"""
获得任务进度
"""
@app.route('/get_task_prgs', methods=['POST'])
def get_task_prgs():ip = request.remote_addrdata = request.get_json()task_id = data['task_id']if task_id not in TASK_MAP:return [-1, 'no such task id']else:task_prgs = TASK_MAP[task_id]['task_prgs']with open(task_prgs, 'rt') as f:content = json.load(f)return content"""
获得任务结果
"""
@app.route('/get_task_rst', methods=['POST'])
def get_task_rst():ip = request.remote_addrdata = request.get_json()task_id = data['task_id']if task_id not in TASK_MAP:return {}else:task_rst = TASK_MAP[task_id]['task_rst']with open(task_rst, 'rt') as f:content = json.load(f)return contentif __name__ == '__main__':# 初始化worker数量nj = 4backends = [LOTTO_GENERATOR() for _ in range(nj)]workers = init_workers(nj=nj, backends=backends)app.run(host='0.0.0.0', port=5002)

这样后端就搭建起来啦,这里有4个onnx 模型在后台监听

3.4 python客户端测试

import requests
import time
import json# 定义服务端地址
server_url = 'http://110.110.123:5002' # 你的服务器和端口
headers = {'Content-Type': 'application/json'}# 检查服务器 Worker 状态
def check_worker_status():response = requests.get(f'{server_url}/get_worker_state')if response.status_code == 200:worker_status = response.json()idle_workers = [wid for wid, status in worker_status.items() if status == 0]if idle_workers:print("Idle workers available:", idle_workers)return Trueelse:print("No idle workers available.")return Falseelse:print("Failed to get worker status.")return False# 提交任务
def submit_task(json_data):if not check_worker_status():print("No idle workers available. Task submission failed.")return Noneresponse = requests.post(f'{server_url}/lotto', json=json_data)if response.status_code == 200:task_id = response.textprint(f"Task submitted successfully. Task ID: {task_id}")return task_idelse:print("Failed to submit task.")return None# 轮询任务进度
def poll_task_progress(task_id):while True:json_data = {'task_id':task_id}response = requests.post(f'{server_url}/get_task_prgs', json=json_data)if response.status_code == 200:progress, text = response.json()print(f"Progress: {progress}, Status: {text}")if progress == 1:print("Task completed successfully.")return Trueelif progress == -1:print(f"Task failed: {text}")return Falseelse:print("Failed to get task progress.")return Falsetime.sleep(3)  # 每3秒查询一次# 获取任务结果
def get_task_result(task_id):json_data = {'task_id':task_id}response = requests.post(f'{server_url}/get_task_rst', json=json_data)if response.status_code == 200:result = response.json()print("Task result:", result)return resultelse:print("Failed to get task result.")return None# 主函数
def main():json_data = {'luck_num':""}# json_data = {'luck_num':"12345678"}# 提交TTS任务task_id = submit_task(json_data)if not task_id:return# 轮询任务进度if poll_task_progress(task_id):# 获取任务结果result = get_task_result(task_id)if __name__ == "__main__":main()

在这里插入图片描述

访问成功

4. 前端工作

4.1 JavaScript 访问 http 函数

JavaScript 调用 http端口如下:

<script>/* 提交任务 */function submitTask() {var button = document.querySelector("button");button.disabled = true;button.innerText = "正在生成...";var useLuckyNumber = document.getElementById("use_lucky_number").checked;var luckInput = document.getElementById("luck_input");var luckNum = useLuckyNumber ? luckInput.value : "";var xhr = new XMLHttpRequest();xhr.open("POST", "/lotto", true);xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");xhr.onreadystatechange = function () {if (xhr.readyState == 4 && xhr.status == 200) {var taskId = xhr.responseText;checkProgress(taskId);} else if (xhr.readyState == 4) {button.disabled = false;button.innerText = "生成";alert("任务提交失败,请重试。");}};xhr.send(JSON.stringify({luck_num: luckNum}));}/* 检查任务进度 */function checkProgress(taskId) {var xhr = new XMLHttpRequest();xhr.open("POST", "/get_task_prgs", true);xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");xhr.onreadystatechange = function () {if (xhr.readyState == 4 && xhr.status == 200) {var response = JSON.parse(xhr.responseText);var progress = response[0];var status = response[1];// document.getElementById("progress").innerText = "进度: " + progress + ", 状态: " + status;if (progress == 1) {getResult(taskId);} else if (progress == -1) {var button = document.querySelector("button");button.disabled = false;button.innerText = "生成";alert("任务失败: " + status);} else {setTimeout(function() { checkProgress(taskId); }, 3000);}}};xhr.send(JSON.stringify({task_id: taskId}));}/* 获取任务结果 */function getResult(taskId) {var xhr = new XMLHttpRequest();xhr.open("POST", "/get_task_rst", true);xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");xhr.onreadystatechange = function () {if (xhr.readyState == 4 && xhr.status == 200) {var response = JSON.parse(xhr.responseText);displayResult(response);var button = document.querySelector("button");button.disabled = false;button.innerText = "生成";}};xhr.send(JSON.stringify({task_id: taskId}));}/* 显示任务结果 */function displayResult(response) {var frontNumbers = response.front_numbers;var backNumbers = response.back_numbers;var resultContainer = document.getElementById("result");resultContainer.innerHTML = ""; // 清空之前的结果for (var i = 0; i < frontNumbers.length; i++) {var lotterySet = document.createElement("div");lotterySet.className = "lottery-set";frontNumbers[i].forEach(function(number) {var numberBall = document.createElement("div");numberBall.className = "number-ball front-ball";numberBall.innerText = number;lotterySet.appendChild(numberBall);});backNumbers[i].forEach(function(number) {var numberBall = document.createElement("div");numberBall.className = "number-ball back-ball";numberBall.innerText = number;lotterySet.appendChild(numberBall);});resultContainer.appendChild(lotterySet);}}</script>

4.2 制作网页index.html

注意到Flask提供了网页渲染功能,这样我们可以设计我们的主页

@app.route('/')
def index():return render_template('index.html')

把上述JS脚本放入index.html 就可以访问后端服务啦,具体html的UI显示,由于代码量很大这里不与展示了,感兴趣同学可以根据上述python客户端的访问逻辑试用GPT为你编写index.html,手机端访问效果如下:

在这里插入图片描述

5. 最后

上述是个人搭建自己网站部署AI应用的简单过程,完整源码后期整理上传,欢迎大家留言关注~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/27822.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

机器人运动学笔记

一、建模 参考资料&#xff1a;https://zhuanlan.zhihu.com/p/137960186 1、三维模型和连杆、关节定义 2、设置z轴 SDH和MDH会不一样&#xff0c;主要的区别在于SDH中坐标系在连杆末端&#xff0c;MDH中坐标系在连杆首端。虽然这里只是给出z轴&#xff0c;但是由于后面原点位…

【C++】Template模板

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;c系列专栏&#xff1a;C/C零基础到精通 &#x1f525; 给大…

【计算机视觉】人脸算法之图像处理基础知识(五)

图像的几何变换 3.图像的旋转 图像的旋转就是让图像按照某一点旋转到指定的角度。需要确定3个参数&#xff1a;图像的旋转中心、旋转角度和缩放因子。在openv中通过getRotationMatrix2D()函数来实现图像的旋转。 import cv2 import numpy as npimgpath "images/img1.j…

CrossOver和PD虚拟机谁更强大?CrossOver和PD虚拟机应该怎么选择

在当前的虚拟化技术和应用程序兼容性解决方案中&#xff0c;CrossOver和PD虚拟机&#xff08;Parallels Desktop&#xff09;都是备受用户喜爱的选择。对于需要在非原生系统上运行应用程序的用户而言&#xff0c;选择合适的工具尤为重要。那么&#xff0c;CrossOver和PD虚拟机谁…

Java SSTI服务端模版注入漏洞原理与利用

文章目录 前言Velocity基础语法基础示例命令执行 靶场实践漏洞代码漏洞验证检测工具 FreeMarker基础示例漏洞示例CMS案例 Thymeleaf基础示例漏洞示例安全方案 总结 前言 SSTI&#xff08;Server Side Template Injection&#xff09;全称服务端模板注入漏洞&#xff0c;在 Jav…

挑战5分钟内基于Springboot+SpringMVC+Mybatis-plus快速构建web后端三层架构

目标 在清晨的代码编辑器上&#xff0c;一场新的挑战即将开始。程序员们肃立于安静的办公室&#xff0c;眼神专注地盯着屏幕&#xff0c;等待着编译器的一声提示。 随着编译器输出的激动人心的"start!"的提示&#xff0c;战斗的序幕拉开了。Bug如潮水般涌来&#x…

python保存文件后打不开的原因是什么

引入数据集&#xff0c;奇怪的是怎么也打不开&#xff0c;显示不存在这个文件&#xff1a; 但是&#xff0c;我将文件改个名字&#xff0c;就打开了&#xff0c;难道csv的文件命名必须有一定合法性&#xff1f; import pandas users pandas.read_csv("H:\python\data an…

正运动邀您共聚2024深圳激光展,助力激光加工与智能制造!

■展会名称 2024深圳激光展 ■展会日期 2024年6月19日 - 21日 ■展馆地点 深圳国际会展中心&#xff08;新馆&#xff09; ■展位号 9H - D101 6月19至21日&#xff0c;深圳激光展将在中国深圳国际会展中心(新馆)举办。 激光加工在消费电子、光伏锂电新能源、半导体等行…

树莓派4B_OpenCv学习笔记9:图片的腐蚀与膨胀

今日继续学习树莓派4B 4G&#xff1a;&#xff08;Raspberry Pi&#xff0c;简称RPi或RasPi&#xff09; 本人所用树莓派4B 装载的系统与版本如下: 版本可用命令 (lsb_release -a) 查询: Opencv 版本是4.5.1&#xff1a; 图像的膨胀与腐蚀一般用于灰度图或者二值图,今日便来学习…

湘潭大学软件工程数据库2(题型,复习资源和计划)

文章目录 选择题关系范式事务分析E-R 图sql作业题答案链接&#xff08;仅限有官方答案的版本&#xff09;结语 现在实验全部做完了&#xff0c;实验和作业占比是百分之 40 &#xff0c;通过上图可以看出来&#xff0c;重点是 sql 语言 所以接下来主要就是学习 sql 语句怎么书写…

软考 系统架构设计师系列知识点之杂项集萃(36)

接前一篇文章&#xff1a;软考 系统架构设计师系列知识点之杂项集萃&#xff08;35&#xff09; 第58题 对软件体系结构风格的研究和实践促进了对设计的复用。Garlan和Shaw对经典体系结构风格进行了分类。其中&#xff0c;&#xff08; &#xff09;属于数据流体系结构风格&am…

孝子黄香与颍川□董超

“香九龄&#xff0c;能温席&#xff0c;孝于亲&#xff0c;所当执。”家喻户晓、妇孺皆知的《三字经》让孝子黄香名扬千秋&#xff0c;成为“二十四孝”中闻名于世的“扇枕温衾”故事的主角。 黄香&#xff08;公元68—122年&#xff09;&#xff0c;字文强&#xff0c;东汉江…

【Apache Doris】Compaction 原理 | 实践全析

【Apache Doris】Compaction 原理 | 实践全析 一、Compaction 前文概要二、Compaction 版本策略三、Compaction 类型说明四、Compaction 工程实现五、Compaction 生产实践 作者 &#xff5c; 俞剑波 一、Compaction 前文概要 LSM-Tree 简介 LSM-Tree&#xff08; Log Structu…

【Vue】Pinia管理用户数据

Pinia管理用户数据 基本思想&#xff1a;Pinia负责用户数据相关的state和action&#xff0c;组件中只负责触发action函数并传递参数 步骤1&#xff1a;创建userStore 1-创建store/userStore.js import { loginAPI } from /apis/user export const useUserStore defineStore(…

大众点评js逆向过程(未完)

1、这里mtgsig已经被拼到url中 2、进入后mtgsig已经计算完&#xff0c; ir he(this[b(4326)], !1), 就是加密函数 32 次 796 1143 ->508 -> 754 -> 1151 160 注意IC这个数组 控制流平坦化进行AST 解析 AST网址

uniapp开发微信小程序问题汇总

1. 自定义校验规则validateFunction失效 2. 微信小程序不支持<Br>换行 在 <text></text> 标签中使用\n(必须 text 标签&#xff0c;view 标签无效 ) 3. 微信小程序无法使用本地静态资源图片的解决方法 (1) 将图片上传到服务器&#xff0c;小程序访问该图片…

MySQL基础——函数和约束

目录 1函数 1.1字符串函数 1.2数值函数 1.3日期函数 1.4流程函数 2约束 2.1约束概述和演示 2.2外键约束&#xff08;表连接键&#xff09; 1函数 函数是指一段可以直接被另一段程序调用的程序或代码。 1.1字符串函数 MySQL中内置了很多字符串函数&#xff0c;常用的…

WPF界面设计

1、使用C#-WPF实现抽屉效果-炫酷漂亮的侧边栏导航菜单-SplitViewMD主题重绘原生控件的美观效果-提供源码Demo下载 码源地址&#xff1a;https://download.csdn.net/download/Prince999999/89424685 2、使用C#-WPF实现抽屉效果-菜单导航功能实现&#xff0c;常规的管理系统应该…

JVM 一些常见问题QA

GC Roots 虚拟机栈中引用的对象&#xff1b; 本地方法栈中JNI引用的对象&#xff1b; 方法区中类静态变量引用的对象&#xff1b; 方法区中常量引用的对象&#xff1b; Full GC是Minor GCMajor GC吗&#xff1f; Minor GC&#xff1a;回收年轻代&#xff1b; Major GC&…

【强化学习】gymnasium自定义环境并封装学习笔记

【强化学习】gymnasium自定义环境并封装学习笔记 gym与gymnasium简介gymgymnasium gymnasium的基本使用方法使用gymnasium封装自定义环境官方示例及代码编写环境文件__init__()方法reset()方法step()方法render()方法close()方法 注册环境创建包 Package&#xff08;最后一步&a…