AI 入门 30 天挑战 - Day 26 - 模型部署和工程化
🌟 完整项目和代码
本教程是 AI 入门 30 天挑战 系列的一部分!
- 💻 GitHub 仓库: https://github.com/Lee985-cmd/AI-30-Day-Challenge
- 📖 CSDN 专栏: https://blog.csdn.net/m0_67081842?type=blog
- ⭐ 欢迎 Star 支持!
Week 4 第五天:让 AI 模型真正用起来!
Flask API + ONNX + 移动端部署!
每个概念都解释!每行代码都说明白!
预计时间:3-4 小时(含费曼输出练习)
📖 第 1 步:快速复习昨天的内容(30 分钟)
费曼输出 #0:考考你
合上教程,尝试回答:
□ 强化学习和监督学习的本质区别是什么?
□ Q-learning 中的"探索"和"利用"各指什么?
□ 为什么要平衡探索和利用?
□ 如果状态空间太大(比如图像),怎么办?
□ 如果用强化学习做自动客服,你会怎么设计?
⏰ 时间:25 分钟
如果能答出 80% 以上,我们开始今天的模型部署之旅!如果不够,花 5 分钟翻一下 Day25 的笔记。
🤔 第 2 步:为什么需要模型部署?(60 分钟)
说人话版本
想象你做了一道非常好吃的菜:
场景 1:只做给自己吃(训练环境)
→ 你在自家厨房做
→ 用你的锅碗瓢盆
→ 只有你能吃到
→ 没问题,自己开心就好
场景 2:要卖给顾客(生产环境)
→ 需要标准化菜谱
→ 需要商用厨具
→ 要保证卫生和口味稳定
→ 要让所有人都能方便买到
这就是训练 vs 部署!
训练:
✓ 在 Jupyter Notebook 里
✓ 用 Python 和 PyTorch
✓ 数据都在本地
✓ 只有自己用
部署:
✓ 要放到服务器上
✓ 要让别人也能用
✓ 要稳定、快速、安全
✓ 要能处理大量请求
生活中的例子:开餐馆
学做菜(训练模型):
→ 在家里练习
→ 试错改进
→ 做出美味佳肴
开餐馆(部署模型):
→ 租店面(服务器)
→ 办营业执照(API 接口)
→ 培训服务员(前端界面)
→ 接待顾客(处理请求)
→ 保证上菜速度(性能优化)
→ 处理投诉(错误处理)
两个都很重要!
但技能完全不同!
部署的挑战
挑战 1:环境不一致
家里厨房 vs 商用厨房
→ 你家用的调料,店里没有
→ 你习惯小火慢炖,店里要快速出菜
解决:容器化(Docker)
→ 把你的厨房整个打包
→ 到哪里都一样
挑战 2:性能要求
自己做:1 小时做一道菜,没问题
开店:10 分钟要出 10 道菜
解决:模型优化
→ 量化(减少精度)
→ 剪枝(去掉不重要的部分)
→ 蒸馏(大模型教小模型)
挑战 3:并发处理
自己吃:一次做一份
开店:同时接 100 个订单
解决:负载均衡
→ 多个厨师(多实例)
→ 智能分配订单
→ 排队管理
挑战 4:持续运营
自己做:今天不想做就不做
开店:每天都要营业
解决:监控系统
→ 监控健康状态
→ 自动重启
→ 日志记录
🎯 费曼输出 #1:向小白解释模型部署
任务 1:创造多个比喻
场景 A:向小学生解释
用卖柠檬水
训练 = 在家调配方
→ 试各种比例
→ 找到最好喝的
部署 = 路边摆摊
→ 准备杯子和吸管
→ 标价收钱
→ 服务顾客
→ 保持卫生
场景 B:向股民解释
用上市公司
训练 = 产品研发
→ 关起门来搞研发
→ 测试产品性能
部署 = IPO 上市
→ 公开交易
→ 接受监管
→ 服务股东
→ 披露信息
场景 C:向家长解释
用孩子教育
训练 = 在家学习
→ 做题考试
→ 家长辅导
部署 = 参加工作
→ 独立生活
→ 服务社会
→ 赚钱养家
→ 承担责任
要求: 每个场景都要详细说明
⏰ 时间:20 分钟
💡 卡壳检查点
如果你在解释时卡住了:
□ 我说不清楚训练和部署的区别
□ 我不知道如何解释"API"是什么
□ 我只能说"放到网上",但不能说明白怎么放
这很正常! 标记下来,继续往下看,然后重新尝试解释!
提示:
- API = 菜单(告诉别人怎么用)
- 服务器 = 厨房(实际做菜的地方)
- 请求 = 点单(顾客下单)
- 响应 = 上菜(提供服务)
🔬 第 3 步:Flask API 部署详解(90 分钟)
什么是 API?
API = Application Programming Interface(应用程序编程接口)
说人话:
API 就是菜单!
去餐馆:
→ 你看菜单点菜
→ 告诉服务员要什么
→ 厨房做好端给你
→ 你付钱拿菜
用 API:
→ 你看文档知道有哪些功能
→ 发送请求(HTTP)
→ 服务器处理并返回结果
→ 你拿到数据
例子(股票分析 API):
菜单(API 文档):
GET /stock/price?code=600519
→ 获取茅台股价
POST /stock/analyze
→ 分析股票走势
GET /stock/sentiment?code=600519
→ 获取市场情绪
你用起来就像点外卖:
→ 不用知道菜怎么做
→ 只要会点单就行
→ 等着收菜(数据)
完整代码实现
from flask import Flask, request, jsonify
import torch
import numpy as np
from transformers import BertTokenizer, BertForSequenceClassification
import json
from datetime import datetime
print("=" * 60)
print("🚀 Flask API 部署股票分析模型")
print("=" * 60)
# ============================================================================
# 第一部分:创建 Flask 应用
# ============================================================================
app = Flask(__name__)
print("\n✓ Flask 应用创建完成")
print(f" 应用名称:{app.name}")
# ============================================================================
# 第二部分:加载模型(模拟)
# ============================================================================
print("\n【加载模型】")
# 为了演示,我们用简化的模型
# 真实场景应该加载训练好的模型
class MockStockAnalyzer:
"""模拟的股票分析器"""
def __init__(self):
self.stock_prices = {
'600519': 1800.50,
'000858': 52.30,
'000001': 12.80,
'601318': 45.60,
}
self.sentiments = ['利好', '利空', '中性']
def get_price(self, stock_code):
"""获取股价"""
return self.stock_prices.get(stock_code, None)
def analyze(self, stock_code):
"""分析股票(模拟)"""
price = self.get_price(stock_code)
if price is None:
return None
# 模拟分析结果
analysis = {
'stock_code': stock_code,
'current_price': price,
'prediction': '上涨' if np.random.random() > 0.5 else '下跌',
'confidence': np.random.uniform(0.6, 0.9),
'recommendation': np.random.choice(['买入', '卖出', '持有']),
'target_price': price * np.random.uniform(0.9, 1.1),
}
return analysis
def analyze_sentiment(self, text):
"""情感分析(模拟)"""
# 简单关键词匹配
positive_words = ['涨', '好', '强', '推荐', '买入', '利好']
negative_words = ['跌', '差', '弱', '卖出', '利空', '崩盘']
pos_count = sum([1 for word in positive_words if word in text])
neg_count = sum([1 for word in negative_words if word in text])
if pos_count > neg_count:
return '正面', 0.8
elif neg_count > pos_count:
return '负面', 0.7
else:
return '中性', 0.6
analyzer = MockStockAnalyzer()
print("✓ 模型加载完成")
print(f" 支持股票数:{len(analyzer.stock_prices)}")
# ============================================================================
# 第三部分:定义 API 接口
# ============================================================================
print("\n" + "=" * 60)
print("【定义 API 接口】")
print("=" * 60)
@app.route('/')
def index():
"""首页 - 显示 API 文档"""
api_doc = """
<h1>🚀 股票分析 API</h1>
<h2>可用接口:</h2>
<h3>1. 获取股价</h3>
<pre>
GET /stock/price?code=600519
</pre>
<h3>2. 股票分析</h3>
<pre>
GET /stock/analyze?code=600519
</pre>
<h3>3. 情感分析</h3>
<pre>
POST /sentiment/analyze
Content-Type: application/json
{"text": "这个公司业绩很好"}
</pre>
<h3>4. 健康检查</h3>
<pre>
GET /health
</pre>
<hr>
<p><small>Version 1.0 | Powered by AI</small></p>
"""
return api_doc
@app.route('/health')
def health_check():
"""健康检查接口"""
return jsonify({
'status': 'healthy',
'timestamp': datetime.now().isoformat(),
'model_loaded': True,
})
@app.route('/stock/price', methods=['GET'])
def get_stock_price():
"""获取股价"""
# 获取参数
stock_code = request.args.get('code', None)
# 参数验证
if not stock_code:
return jsonify({
'error': '缺少参数:code',
'example': '/stock/price?code=600519'
}), 400
# 调用模型
price = analyzer.get_price(stock_code)
if price is None:
return jsonify({
'error': f'未找到股票:{stock_code}',
'available_codes': list(analyzer.stock_prices.keys())
}), 404
# 返回结果
return jsonify({
'stock_code': stock_code,
'price': price,
'currency': 'CNY',
'timestamp': datetime.now().isoformat()
})
@app.route('/stock/analyze', methods=['GET'])
def analyze_stock():
"""分析股票"""
stock_code = request.args.get('code', None)
if not stock_code:
return jsonify({'error': '缺少参数:code'}), 400
analysis = analyzer.analyze(stock_code)
if analysis is None:
return jsonify({'error': f'未找到股票:{stock_code}'}), 404
return jsonify({
'success': True,
'data': analysis,
'timestamp': datetime.now().isoformat()
})
@app.route('/sentiment/analyze', methods=['POST'])
def analyze_sentiment():
"""情感分析"""
# 获取 JSON 数据
data = request.get_json()
if not data or 'text' not in data:
return jsonify({'error': '缺少参数:text'}), 400
text = data['text']
# 调用模型
sentiment, confidence = analyzer.analyze_sentiment(text)
return jsonify({
'success': True,
'text': text,
'sentiment': sentiment,
'confidence': confidence,
'timestamp': datetime.now().isoformat()
})
# ============================================================================
# 第四部分:错误处理
# ============================================================================
@app.errorhandler(404)
def not_found(error):
"""404 错误处理"""
return jsonify({
'error': '接口不存在',
'hint': '请访问 / 查看 API 文档'
}), 404
@app.errorhandler(500)
def internal_error(error):
"""500 错误处理"""
return jsonify({
'error': '服务器内部错误',
'hint': '请稍后重试或联系管理员'
}), 500
# ============================================================================
# 第五部分:启动服务
# ============================================================================
print("\n✓ API 接口定义完成")
print("""
可用接口:
- GET / (API 文档)
- GET /health (健康检查)
- GET /stock/price (获取股价)
- GET /stock/analyze (股票分析)
- POST /sentiment/analyze (情感分析)
""")
print("\n" + "=" * 60)
print("【启动服务器】")
print("=" * 60)
if __name__ == '__main__':
print("""
╔═══════════════════════════════════════════════════╗
║ 🚀 股票分析 API 服务启动中... ║
╠═══════════════════════════════════════════════════╣
║ ║
║ 本地访问:http://127.0.0.1:5000 ║
║ 局域网访问:http://你的 IP:5000 ║
║ ║
║ 按 Ctrl+C 停止服务 ║
║ ║
╚═══════════════════════════════════════════════════╝
""")
# 启动服务器
app.run(host='0.0.0.0', port=5000, debug=True)
按 Shift + Enter 运行!
🎯 费曼输出 #2:深入理解技术
任务 1:解释技术细节
思考题:
- 为什么训练环境和生产环境不一样?
- API 的作用是什么?用生活中的例子说明
- 为什么要做错误处理和健康检查?
- 如果要支持高并发,需要做什么优化?
任务 2:设计 API 接口
场景:你要为你的股票情感分析系统设计 API
要求:
- 设计 3-5 个接口(包括 URL、方法、参数)
- 定义输入输出格式
- 考虑错误情况
- 写一份简单的 API 文档
⏰ 时间:30 分钟
💡 卡壳检查点
- 我解释不清 API 的实际用途
- 我说不明白为什么需要错误处理
- 我不能设计实用的 API 接口
提示:
- API = 菜单(告诉别人怎么用)
- 错误处理 = 应急预案
- 健康检查 = 体检报告
💻 第 4 步:模型优化技术(60 分钟)
模型量化
"""
量化(Quantization)是什么?
问题:
训练时用 float32(32 位浮点数)
→ 精度高,但计算慢
→ 占用内存大(4 bytes/数)
想法:
能不能用更少的位数表示?
→ int8(8 位整数)够用了!
→ 内存减少 4 倍
→ 速度提升 2-3 倍
怎么做?
float32 范围:[-3.4e38, 3.4e38]
int8 范围:[-128, 127]
映射关系:
value_int8 = round(value_float32 * scale)
好处:
✓ 模型变小
✓ 计算变快
✓ 精度损失很小(<1%)
代价:
✗ 精度略微下降
✗ 需要额外转换步骤
"""
ONNX 格式转换
"""
ONNX = Open Neural Network Exchange
问题:
PyTorch 训练的模型,不能在 TensorFlow 上用
→ 每个框架都有自己的格式
→ 部署很麻烦
解决:
ONNX = 神经网络的"普通话"
→ 任何框架都能导出 ONNX
→ 任何框架都能导入 ONNX
→ 一次转换,到处运行
流程:
PyTorch 模型
↓ 导出
ONNX 格式
↓ 导入
TensorRT / OpenVINO / 移动端
好处:
✓ 跨平台
✓ 优化工具多
✓ 推理速度快
"""
# 示例代码(了解即可)
"""
import torch.onnx as onnx
# 导出为 ONNX
dummy_input = torch.randn(1, 3, 224, 224)
onnx.export(model, dummy_input, 'model.onnx')
# 现在可以用 ONNX Runtime 运行了
import onnxruntime as ort
session = ort.InferenceSession('model.onnx')
"""
💻 第 5 步:实战项目(60 分钟)
项目:完整的股票分析服务
"""
项目目标:
把前面学的模型整合起来
提供一个完整的股票分析服务
功能:
1. 股价查询
2. 技术分析
3. 情感分析
4. 综合建议
技术栈:
- Flask(Web 框架)
- Pandas(数据处理)
- Matplotlib(绘图)
- 前面学的各种模型
"""
import pandas as pd
import io
import base64
print("=" * 60)
print("📊 完整股票分析服务系统")
print("=" * 60)
class CompleteStockService:
"""完整的股票分析服务"""
def __init__(self):
self.data = {
'600519': {'name': '贵州茅台', 'price': 1800.50, 'change': 2.5},
'000858': {'name': '五粮液', 'price': 52.30, 'change': -1.2},
'000001': {'name': '平安银行', 'price': 12.80, 'change': 0.8},
}
def get_stock_info(self, code):
"""获取股票基本信息"""
return self.data.get(code, None)
def technical_analysis(self, code):
"""技术分析(模拟)"""
info = self.get_stock_info(code)
if not info:
return None
return {
'trend': '上涨' if info['change'] > 0 else '下跌',
'support': info['price'] * 0.95,
'resistance': info['price'] * 1.05,
'rsi': 55.0,
'macd': '金叉',
}
def comprehensive_analysis(self, code, news_text=None):
"""综合分析"""
info = self.get_stock_info(code)
if not info:
return None
tech = self.technical_analysis(code)
result = {
'basic': info,
'technical': tech,
'recommendation': '买入' if info['change'] > 0 else '观望',
'confidence': 0.75,
}
if news_text:
# 这里可以集成情感分析
result['news_sentiment'] = '正面'
return result
service = CompleteStockService()
# 测试
test_codes = ['600519', '000858', '000001']
print("\n股票分析结果示例:\n")
for code in test_codes:
result = service.comprehensive_analysis(code)
if result:
print(f"{code} - {result['basic']['name']}:")
print(f" 当前价:{result['basic']['price']} ({result['basic']['change']:+.1f}%)")
print(f" 趋势:{result['technical']['trend']}")
print(f" 建议:{result['recommendation']}")
print(f" 置信度:{result['confidence']:.0%}")
print()
print("\n💡 实际应用:")
print(" 1. 整合所有学到的模型")
print(" 2. 提供统一的 API 接口")
print(" 3. 处理真实业务逻辑")
print(" 4. 添加用户认证、计费等")
print("\n🎊 部署实战完成!")
🎉 今日费曼总结(30 分钟)⭐
完整的费曼学习流程
第 1 步:回顾今天的内容(5 分钟)
- 模型部署的重要性
- Flask API 开发
- 模型优化技术
第 2 步:合上教程,尝试完整教授(15 分钟)⭐
任务:假装你在给一个完全不懂的人上第二十六堂课
要覆盖:
- 为什么需要部署(用至少 2 个比喻)
- API 是怎么工作的
- 演示股票分析服务
- 讲解实际应用场景
方式:写一篇 800 字左右的文章,或录一段 10-15 分钟的视频
第 3 步:标记卡壳点(5 分钟)
我今天卡壳的地方: □ _________________________________ □ _________________________________
第 4 步:针对性复习(5 分钟)
回到教程中卡壳的地方,重新学习,然后再次尝试解释!
📝 费曼学习笔记模板
╔═══════════════════════════════════════════════════╗
║ Day 26 费曼学习笔记 ║
╠═══════════════════════════════════════════════════╣
║ 日期:__________ ║
║ 学习时长:__________ ║
╠═══════════════════════════════════════════════════╣
║ ║
║ 1. 我向小白解释了: ║
║ _______________________________________________ ║
║ ║
║ 2. 我卡壳的地方: ║
║ □ _____________________________________________ ║
║ ║
║ 3. 我的通俗比喻: ║
║ • 模型部署就像 ______ ║
║ • API 就像 ______ ║
║ • 服务器就像 ______ ║
║ ║
╚═══════════════════════════════════════════════════╝
📊 今日总结
✅ 你今天学到了:
-
模型部署基础
- 训练 vs 部署的区别
- API 的概念和作用
- Flask 框架入门
-
实战技能
- RESTful API 设计
- 错误处理
- 健康检查
-
优化技术
- 模型量化
- ONNX 格式
- 性能优化
-
费曼输出能力 ⭐
- 能用比喻解释部署
- 能向小白说明 API
- 能完整讲解服务架构
🎁 明日预告
明天你将学习:AI 伦理和安全
内容:
- AI 偏见和公平性
- 隐私保护
- 可控 AI
- 社会责任
准备好思考 AI 的道德问题了吗?继续前进!🚀
本教程属于 AI 入门 30 天挑战 系列
🎉 恭喜你完成今天的学习!
🔗 资源汇总
- 📘 完整 30 天教程:CSDN 专栏 - AI 入门 30 天挑战
- 💻 完整代码 + 项目实战:GitHub 仓库 ⭐欢迎 Star
- ❓ 遇到问题:GitHub Issues 提问
💬 互动时间
思考题:今天的知识点中,哪个让你印象最深刻?为什么?
欢迎在评论区分享你的想法或疑问!👇
❤️ 如果有帮助
- 👍 点赞:让更多人看到这篇教程
- ⭐ Star GitHub:获取完整代码和项目
- ➕ 关注专栏:不错过后续更新
- 🔄 分享给朋友:一起学习进步
明天见!继续 Day 27 的学习~ 🚀
💡 学习建议
如果本篇教程对你有帮助,欢迎:
- Star GitHub 项目:https://github.com/Lee985-cmd/AI-30Days-Challenge
- 留言交流你的学习困惑
一起学习,一起进步! 🤝
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐
所有评论(0)