🌟 完整项目和代码

本教程是 AI 入门 30 天挑战 系列的一部分!


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:解释技术细节

思考题:

  1. 为什么训练环境和生产环境不一样?
  2. API 的作用是什么?用生活中的例子说明
  3. 为什么要做错误处理和健康检查?
  4. 如果要支持高并发,需要做什么优化?

任务 2:设计 API 接口

场景:你要为你的股票情感分析系统设计 API

要求:

  1. 设计 3-5 个接口(包括 URL、方法、参数)
  2. 定义输入输出格式
  3. 考虑错误情况
  4. 写一份简单的 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 分钟)⭐

任务:假装你在给一个完全不懂的人上第二十六堂课

要覆盖:

  1. 为什么需要部署(用至少 2 个比喻)
  2. API 是怎么工作的
  3. 演示股票分析服务
  4. 讲解实际应用场景

方式:写一篇 800 字左右的文章,或录一段 10-15 分钟的视频

第 3 步:标记卡壳点(5 分钟)

我今天卡壳的地方: □ _________________________________ □ _________________________________

第 4 步:针对性复习(5 分钟)

回到教程中卡壳的地方,重新学习,然后再次尝试解释!


📝 费曼学习笔记模板

╔═══════════════════════════════════════════════════╗
║         Day 26 费曼学习笔记                       ║
╠═══════════════════════════════════════════════════╣
║ 日期:__________                                  ║
║ 学习时长:__________                              ║
╠═══════════════════════════════════════════════════╣
║                                                   ║
║ 1. 我向小白解释了:                               ║
║ _______________________________________________  ║
║                                                   ║
║ 2. 我卡壳的地方:                                 ║
║ □ _____________________________________________  ║
║                                                   ║
║ 3. 我的通俗比喻:                                 ║
║ • 模型部署就像 ______                             ║
║ • API 就像 ______                                 ║
║ • 服务器就像 ______                               ║
║                                                   ║
╚═══════════════════════════════════════════════════╝

📊 今日总结

✅ 你今天学到了:

  1. 模型部署基础

    • 训练 vs 部署的区别
    • API 的概念和作用
    • Flask 框架入门
  2. 实战技能

    • RESTful API 设计
    • 错误处理
    • 健康检查
  3. 优化技术

    • 模型量化
    • ONNX 格式
    • 性能优化
  4. 费曼输出能力 ⭐

    • 能用比喻解释部署
    • 能向小白说明 API
    • 能完整讲解服务架构

🎁 明日预告

明天你将学习:AI 伦理和安全

内容:

  • AI 偏见和公平性
  • 隐私保护
  • 可控 AI
  • 社会责任

准备好思考 AI 的道德问题了吗?继续前进!🚀


本教程属于 AI 入门 30 天挑战 系列


🎉 恭喜你完成今天的学习!

🔗 资源汇总

💬 互动时间

思考题:今天的知识点中,哪个让你印象最深刻?为什么?

欢迎在评论区分享你的想法或疑问!👇

❤️ 如果有帮助

  • 👍 点赞:让更多人看到这篇教程
  • ⭐ Star GitHub:获取完整代码和项目
  • ➕ 关注专栏:不错过后续更新
  • 🔄 分享给朋友:一起学习进步

明天见!继续 Day 27 的学习~ 🚀

💡 学习建议

如果本篇教程对你有帮助,欢迎:

  1. Star GitHub 项目https://github.com/Lee985-cmd/AI-30Days-Challenge
  2. 留言交流你的学习困惑

一起学习,一起进步! 🤝

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐