当前位置: 首页 > news >正文

《Python实战进阶》No.41: 使用 Streamlit 快速构建 ML 应用

Python实战进阶 No.41: 使用 Streamlit 快速构建 ML 应用

摘要

Streamlit 是一个专为数据科学家和机器学习工程师设计的开源框架,它允许您在几分钟内创建精美的交互式应用程序。本文将介绍 Streamlit 的核心功能,并通过两个实战案例展示如何构建基于 ChatGPT API 的聊天助手以及结合 EasyOCR 的 AI 家庭教师应用。无论您是想快速展示机器学习模型的效果,还是为非技术用户提供友好的交互界面,Streamlit 都是一个理想的选择。

在这里插入图片描述

核心概念和知识点

Streamlit 的基本组件与布局

Streamlit 提供了丰富的组件,使您能够轻松创建交互式界面:

  1. 文本元素st.title(), st.header(), st.subheader(), st.text(), st.markdown()
  2. 输入组件st.text_input(), st.number_input(), st.slider(), st.selectbox()
  3. 媒体元素st.image(), st.audio(), st.video()
  4. 布局组件st.sidebar, st.columns(), st.expander()
  5. 状态组件st.progress(), st.spinner(), st.success(), st.error()

Streamlit 采用自上而下的布局方式,代码的执行顺序决定了界面元素的排列顺序。

import streamlit as st

st.title("我的第一个Streamlit应用")
st.header("这是一个标题")
st.subheader("这是一个子标题")

# 侧边栏
with st.sidebar:
    st.header("侧边栏")
    option = st.selectbox("选择一个选项", ["选项1", "选项2", "选项3"])

# 列布局
col1, col2 = st.columns(2)
with col1:
    st.write("这是第一列")
    st.button("点击我")
with col2:
    st.write("这是第二列")
    st.checkbox("勾选我")

数据可视化与模型集成

Streamlit 支持多种数据可视化库,包括 Matplotlib、Plotly、Altair 等,使数据展示变得简单直观:

import streamlit as st
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import plotly.express as px

# 创建示例数据
data = pd.DataFrame({
    '日期': pd.date_range('20230101', periods=100),
    '值': np.random.randn(100).cumsum()
})

# Matplotlib 可视化
st.subheader("Matplotlib 图表")
fig, ax = plt.subplots()
ax.plot(data['日期'], data['值'])
ax.set_title("随机数据趋势")
st.pyplot(fig)

# Plotly 可视化
st.subheader("Plotly 图表")
fig = px.line(data, x='日期', y='值', title="交互式数据趋势")
st.plotly_chart(fig)

对于机器学习模型的集成,Streamlit 提供了简单的方式来加载模型并处理用户输入:

import streamlit as st
import joblib
import pandas as pd

# 加载预训练模型(示例)
@st.cache_resource
def load_model():
    return joblib.load('model.pkl')

model = load_model()

# 用户输入
st.header("预测界面")
feature1 = st.slider("特征1", 0.0, 10.0, 5.0)
feature2 = st.slider("特征2", 0.0, 10.0, 5.0)

# 预测
if st.button("预测"):
    input_data = pd.DataFrame([[feature1, feature2]], columns=['特征1', '特征2'])
    prediction = model.predict(input_data)
    st.success(f"预测结果: {prediction[0]}")

应用部署与分享

Streamlit 应用可以通过多种方式部署和分享:

  1. 本地运行streamlit run app.py
  2. Streamlit Cloud:免费托管平台,直接连接到 GitHub 仓库
  3. Docker 容器化:便于在任何环境中部署
  4. 云服务提供商:如 AWS、GCP、Azure 等

部署示例(使用 Streamlit Cloud):

  1. 将代码推送到 GitHub 仓库
  2. 访问 Streamlit Cloud
  3. 连接您的 GitHub 账户并选择仓库
  4. 点击部署

实战案例

案例一:基于 ChatGPT API 的聊天助手

让我们构建一个简单但功能完整的 ChatGPT 聊天助手:

import streamlit as st
import openai
import time

# 设置页面配置
st.set_page_config(page_title="ChatGPT 助手", page_icon="💬", layout="wide")

# 初始化会话状态
if "messages" not in st.session_state:
    st.session_state.messages = []

# 设置 OpenAI API 密钥
openai.api_key = st.secrets["openai_api_key"]  # 在Streamlit Cloud中设置密钥

# 页面标题
st.title("💬 ChatGPT 智能助手")
st.markdown("这是一个基于 ChatGPT API 的智能聊天助手,可以回答您的各种问题。")

# 侧边栏配置
with st.sidebar:
    st.header("设置")
    model = st.selectbox("选择模型", ["gpt-3.5-turbo", "gpt-4"])
    temperature = st.slider("创造性 (Temperature)", min_value=0.0, max_value=1.0, value=0.7, step=0.1)
    
    st.header("会话")
    if st.button("清空会话"):
        st.session_state.messages = []
        st.experimental_rerun()

# 显示聊天历史
for message in st.session_state.messages:
    with st.chat_message(message["role"]):
        st.markdown(message["content"])

# 用户输入
prompt = st.chat_input("请输入您的问题...")

# 处理用户输入
if prompt:
    # 添加用户消息到历史
    st.session_state.messages.append({"role": "user", "content": prompt})
    
    # 显示用户消息
    with st.chat_message("user"):
        st.markdown(prompt)
    
    # 显示助手消息(带加载动画)
    with st.chat_message("assistant"):
        message_placeholder = st.empty()
        full_response = ""
        
        # 调用 OpenAI API
        try:
            response = openai.ChatCompletion.create(
                model=model,
                messages=[{"role": m["role"], "content": m["content"]} for m in st.session_state.messages],
                temperature=temperature,
                stream=True
            )
            
            # 模拟打字效果
            for chunk in response:
                if chunk.choices and chunk.choices[0].delta.get("content"):
                    content = chunk.choices[0].delta.content
                    full_response += content
                    message_placeholder.markdown(full_response + "▌")
                    time.sleep(0.01)
                    
            message_placeholder.markdown(full_response)
            
            # 添加助手回复到历史
            st.session_state.messages.append({"role": "assistant", "content": full_response})
            
        except Exception as e:
            st.error(f"发生错误: {str(e)}")

运行效果

  • 用户可以在输入框中输入问题
  • ChatGPT 会实时生成回答,并显示打字效果
  • 聊天历史会被保存,用户可以随时清空会话
  • 侧边栏允许用户调整模型和创造性参数

案例二:基于 EasyOCR 和 Streamlit 的 AI 家庭教师

这个应用将结合 EasyOCR 进行图像文字识别,并使用 ChatGPT 回答关于识别内容的问题:

import streamlit as st
import easyocr
import numpy as np
import cv2
import openai
import tempfile
import os
from PIL import Image

# 设置页面配置
st.set_page_config(page_title="AI 家庭教师", page_icon="🧠", layout="wide")

# 初始化 OpenAI API
openai.api_key = st.secrets["openai_api_key"]

# 初始化 EasyOCR
@st.cache_resource
def load_ocr_reader():
    return easyocr.Reader(['ch_sim', 'en'])  # 支持中文和英文

reader = load_ocr_reader()

# 初始化会话状态
if "chat_history" not in st.session_state:
    st.session_state.chat_history = []
if "ocr_text" not in st.session_state:
    st.session_state.ocr_text = ""
if "current_image" not in st.session_state:
    st.session_state.current_image = None

# 页面标题
st.title("🧠 AI 家庭教师")
st.markdown("上传图片或拍照,AI 将识别内容并回答您的问题!")

# 创建两列布局
col1, col2 = st.columns([1, 1])

# 第一列:图像上传和处理
with col1:
    st.header("图像输入")
    
    # 选项卡:上传图片或拍照
    tab1, tab2 = st.tabs(["上传图片", "拍照"])
    
    with tab1:
        uploaded_file = st.file_uploader("选择一张图片", type=["jpg", "jpeg", "png"])
        if uploaded_file is not None:
            # 保存上传的图片
            image = Image.open(uploaded_file)
            st.session_state.current_image = np.array(image)
            st.image(image, caption="上传的图片", use_column_width=True)
    
    with tab2:
        camera_image = st.camera_input("拍摄图片")
        if camera_image is not None:
            # 保存拍摄的图片
            image = Image.open(camera_image)
            st.session_state.current_image = np.array(image)
            st.image(image, caption="拍摄的图片", use_column_width=True)
    
    # 处理图像按钮
    if st.button("识别图像文字") and st.session_state.current_image is not None:
        with st.spinner("正在识别文字..."):
            # 使用 EasyOCR 识别文字
            results = reader.readtext(st.session_state.current_image)
            
            # 提取文本
            extracted_text = ""
            for (bbox, text, prob) in results:
                extracted_text += text + " "
            
            st.session_state.ocr_text = extracted_text
            
            # 在图像上标记识别的文字
            annotated_image = st.session_state.current_image.copy()
            for (bbox, text, prob) in results:
                # 转换坐标为整数
                top_left = tuple(map(int, bbox[0]))
                bottom_right = tuple(map(int, bbox[2]))
                
                # 在图像上绘制边界框
                cv2.rectangle(annotated_image, top_left, bottom_right, (0, 255, 0), 2)
                
                # 添加文本标签
                cv2.putText(annotated_image, text, (top_left[0], top_left[1] - 10),
                            cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2)
            
            # 显示标记后的图像
            st.image(annotated_image, caption="识别结果", use_column_width=True)
            
            # 显示识别的文字
            st.subheader("识别的文字")
            st.write(extracted_text)

# 第二列:聊天界面
with col2:
    st.header("AI 助教聊天")
    
    # 显示识别的文字
    if st.session_state.ocr_text:
        with st.expander("识别的文字内容", expanded=False):
            st.write(st.session_state.ocr_text)
    
    # 显示聊天历史
    chat_container = st.container()
    with chat_container:
        for message in st.session_state.chat_history:
            if message["role"] == "user":
                st.markdown(f"**你**: {message['content']}")
            else:
                st.markdown(f"**AI 助教**: {message['content']}")
    
    # 用户输入
    user_question = st.text_input("请输入您的问题...", key="user_input")
    
    if st.button("发送") and user_question:
        # 添加用户问题到历史
        st.session_state.chat_history.append({"role": "user", "content": user_question})
        
        # 构建 ChatGPT 的系统提示
        system_prompt = "你是一位专业的AI家庭教师,擅长解答学生的问题。"
        if st.session_state.ocr_text:
            system_prompt += f"以下是从图像中识别的文字内容,请基于这些内容回答问题:\n{st.session_state.ocr_text}"
        
        # 构建消息列表
        messages = [{"role": "system", "content": system_prompt}]
        for message in st.session_state.chat_history:
            messages.append({"role": message["role"], "content": message["content"]})
        
        # 调用 ChatGPT API
        with st.spinner("AI 正在思考..."):
            try:
                response = openai.ChatCompletion.create(
                    model="gpt-3.5-turbo",
                    messages=messages,
                    temperature=0.7,
                    max_tokens=1000
                )
                
                # 获取回复
                ai_response = response.choices[0].message.content
                
                # 添加回复到历史
                st.session_state.chat_history.append({"role": "assistant", "content": ai_response})
                
                # 重新加载页面以显示新消息
                st.experimental_rerun()
                
            except Exception as e:
                st.error(f"发生错误: {str(e)}")

# 侧边栏:设置和操作
with st.sidebar:
    st.header("操作")
    
    if st.button("清空聊天记录"):
        st.session_state.chat_history = []
        st.experimental_rerun()
    
    if st.button("清空图像和识别结果"):
        st.session_state.current_image = None
        st.session_state.ocr_text = ""
        st.experimental_rerun()
    
    st.header("关于")
    st.markdown("""
    **AI 家庭教师** 结合了:
    - EasyOCR 进行文字识别
    - ChatGPT 提供智能回答
    - Streamlit 构建交互界面
    
    适用于学生拍摄课本、作业等内容,获取即时解答和辅导。
    """)

运行效果

  • 用户可以上传图片或使用摄像头拍照
  • EasyOCR 识别图片中的文字,并在图像上标记
  • 用户可以基于识别的内容向 AI 助教提问
  • AI 助教(ChatGPT)会根据识别的内容和用户问题提供回答
  • 完整的聊天历史记录和清空功能

与 AI 大模型的相关性

Streamlit 与 AI 大模型的结合具有强大的协同效应:

  1. 快速原型开发:Streamlit 允许数据科学家和 AI 研究人员快速构建模型演示,无需前端开发经验。

  2. 模型可解释性:通过交互式界面,用户可以调整参数并实时观察模型输出,增强对 AI 模型的理解。

  3. 降低使用门槛:为非技术用户提供友好的界面,使他们能够轻松使用复杂的 AI 模型。

  4. API 集成简化:如我们的案例所示,Streamlit 可以轻松集成 OpenAI、Hugging Face 等 AI 服务的 API。

  5. 多模态应用:支持文本、图像、音频等多种输入形式,适合构建多模态 AI 应用。

在我们的实战案例中,我们展示了如何将 ChatGPT 和 EasyOCR 这样的 AI 模型无缝集成到 Streamlit 应用中,创建了既实用又易用的 AI 工具。

总结

Streamlit 是一个强大而简洁的工具,特别适合数据科学家和机器学习工程师快速构建交互式应用。通过本文的学习,我们掌握了:

  1. Streamlit 的基本组件和布局系统
  2. 如何在 Streamlit 中集成数据可视化和机器学习模型
  3. 应用部署与分享的方法
  4. 如何构建实用的 AI 应用,如 ChatGPT 聊天助手和 OCR 识别助教

Streamlit 的优势在于其简单性和快速开发能力,使得从想法到应用的过程变得异常高效。对于想要展示 AI 模型或数据分析结果的开发者来说,Streamlit 是一个不可多少的工具。

扩展思考

Streamlit 在企业级应用中的局限性

虽然 Streamlit 非常适合原型开发和小型应用,但在企业级应用中可能面临一些挑战:

  1. 性能限制:Streamlit 应用在每次交互时会重新运行整个脚本,这可能导致大型应用的性能问题。

  2. 定制化限制:虽然 Streamlit 提供了丰富的组件,但与成熟的前端框架相比,定制化能力较弱。

  3. 并发处理:Streamlit 不是为高并发负载设计的,在多用户同时访问时可能出现性能瓶颈。

  4. 状态管理:复杂应用中的状态管理相对简单,可能不足以支持复杂的业务逻辑。

  5. 安全性考虑:企业级应用通常需要更严格的安全措施,而 Streamlit 在这方面的功能相对基础。

如何结合前端框架提升用户体验

为了克服 Streamlit 的一些限制,可以考虑以下策略:

  1. 混合架构:使用 Streamlit 作为快速原型和内部工具,而将面向客户的产品用 React、Vue 等框架开发。

  2. API 后端:将 Streamlit 应用的核心功能封装为 API(使用 FastAPI 或 Flask),然后由前端框架调用。

  3. 组件扩展:利用 Streamlit 的自定义组件功能,集成 React 组件以增强交互性。

  4. 微前端架构:将 Streamlit 应用嵌入到更大的微前端架构中,各司其职。

  5. 渐进式迁移:随着应用成熟,可以逐步将关键功能迁移到更适合生产环境的技术栈。

示例:创建 React 组件并在 Streamlit 中使用:

import streamlit as st
import streamlit.components.v1 as components

# 定义一个简单的 React 组件
react_component = """
<div id="react-root"></div>
<script src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<script>
  const e = React.createElement;
  
  class FancyButton extends React.Component {
    constructor(props) {
      super(props);
      this.state = { clicked: false };
    }
    
    render() {
      return e(
        'button',
        { 
          onClick: () => {
            this.setState({ clicked: true });
            // 与 Streamlit 通信
            if (window.Streamlit) {
              window.Streamlit.setComponentValue('按钮被点击了');
            }
          },
          style: {
            padding: '10px 20px',
            background: this.state.clicked ? '#4CAF50' : '#2196F3',
            color: 'white',
            border: 'none',
            borderRadius: '4px',
            cursor: 'pointer',
            transition: 'all 0.3s'
          }
        },
        this.state.clicked ? '已点击!' : '点击我'
      );
    }
  }
  
  ReactDOM.render(
    e(FancyButton),
    document.getElementById('react-root')
  );
</script>
"""

# 在 Streamlit 中使用 React 组件
st.title("Streamlit 与 React 集成示例")

# 显示 React 组件
component_value = components.html(react_component, height=100)

# 显示组件返回的值
if component_value:
    st.write(f"组件返回值: {component_value}")

通过这种方式,我们可以在保持 Streamlit 简单性的同时,利用前端框架的强大功能来增强用户体验。

总之,Streamlit 是一个强大的工具,特别适合快速开发和原型设计。通过了解其优势和局限性,我们可以在适当的场景中充分发挥其价值,同时在必要时结合其他技术来构建更完善的应用。

相关文章:

  • python leetcode简单练习(2)
  • 如何免费阅读Medium文章?
  • mysql JSON_ARRAYAGG联合JSON_OBJECT使用
  • 第二章:NumPy进阶与数据处理
  • 机器学习基础概括
  • windows通用网线连接ubuntu实现ssh登录、桌面控制、文件共享【实操常见问题解决思路】
  • 本地Ollama+Spring AI alibaba智能客服(飞机票预定示例Demo)Mac版
  • Redis7——进阶篇(七)
  • datetime模块
  • ECharts 中轴的分类及常见使用场景
  • 【FreeRtos】随手记录想法和DeepSeek的交流
  • anaconda学习
  • vue 路由
  • 把doi直接插入word中,然后直接生成参考文献
  • 论文阅读:Dual Anchor Graph Fuzzy Clustering for Multiview Data
  • compareTo方法示例
  • git分支和标签
  • ①EtherCAT/Ethernet/IP/Profinet/ModbusTCP协议互转工业串口网关
  • K8s存储全解析:从临时存储到持久化解决方案的完整指南
  • 探索量子世界的先驱:马克斯・普朗克与普朗克参数
  • 做业务不花钱的网站有哪些/网站设计模板
  • 网络科技有限公司网站建设策划书/查询网站服务器
  • 中小企业网站建设示范平台/南宁网站运营优化平台
  • 衡水seo_衡水网站建设-燕丰收/免费seo免费培训
  • wordpress安装点提交无法访问/seo排名优化首页
  • 推广网站建设产品介绍/百度网站推广申请