参考文献: Deploying a Simple Streamlit app using Docker | Engineering Education (EngEd) Program | Section

Docker 是一个虚拟化平台,旨在通过使用容器来创建、运行和部署应用程序。我们将使用 Docker 部署一个使用 Streamlit 构建的简单机器学习应用程序。

在本教程中,我们将首先创建一个简单的机器学习模型,将其保存到 pickle 文件中以加载到我们的平台中,然后使用 Streamlit 创建其界面。创建 Streamlit 应用程序后,我们将使用 docker 来部署它。

Streamlit简介

Streamlit 是一个框架,不同的机器学习工程师和数据科学家使用它从经过训练的模型构建 UI 和强大的机器学习应用程序。

通过为用户提供交互式界面,这些应用程序可用于可视化。

它提供了一种更简单的方法来构建图表、表格和不同的图形以满足您的应用程序的需求。它还可以利用已保存或选择到应用程序中的模型进行预测。

如何安装Streamlit

使用以下命令:

pip install streamlit

让我们构建streamlit应用程序

1、文件夹结构如下:

├── Dockerfile
├── demo.xlsx
├── drive_st_test.py
├── ml.xlsx
├── requirements.txt
├── 改善v_1.xlsx

2、其中,Dockerfile文件内容如下

FROM python:3.7
WORKDIR /app
COPY requirements.txt ./requirements.txt
RUN pip install -r requirements.txt  -i https://pypi.tuna.tsinghua.edu.cn/simple
EXPOSE 8501
COPY . /app
ENTRYPOINT ["streamlit", "run"]
CMD ["drive_st_test.py"]

3.drive_st_test.py文件包含

######################
# Import libraries
######################
import pandas as pd
import numpy as np
import streamlit as st
import datetime
from datetime import datetime
from datetime import timedelta
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import seaborn as sns
from plotly import subplots
from plotly.subplots import make_subplots
import plotly.graph_objects as go
import plotly.express as px
import plotly.figure_factory as ff
#加载streamlit pyecharts
from pyecharts.charts import *
from pyecharts.faker import Faker
import streamlit_echarts
from pyecharts import options as opts
from pyecharts.globals import ThemeType
st.set_page_config(layout="wide")
######################
# Import data
######################
@st.cache(allow_output_mutation = True) #存入内存,速度变快
def load_data():
    df=pd.read_excel('./demo.xlsx')
    radar_df=pd.read_excel('./ml.xlsx')
    improve=pd.read_excel('./改善v_1.xlsx')
    return df,radar_df,improve
df,radar_df,improve=load_data()
df['tid_x']=df['tid_x'].astype('str')
df['gpstime_b_date']=df['gpstime_b'].apply(lambda x: x.strftime("%Y-%m-%d"))
df['gpstime_b_date']=pd.to_datetime(df['gpstime_b_date'])
######################
# Selected sidebar variables
######################
# id_list = df['tid_x'].unique()
# select_id = st.sidebar.selectbox("选择id",list(id_list))
# select_city = st.sidebar.selectbox("选择城市",list(['全国','武汉']))
# m_cut_list=df['m_cut'].unique()
# select_status = st.sidebar.selectbox("选择载重状态",list(m_cut_list))
######################
# Write some information
######################
st.header('一、质量分析:') #车辆数 ,监控时长,重点关注车辆(规则)
st.subheader('1.1 整体质量概况:')
# Dates for date_input creation and delimitation
max_date = df['gpstime_b_date'].max()
min_date = df['gpstime_b_date'].min()
start_date = datetime(2021, 6, 1)
end_date = datetime(2021, 6, 30)
dates = st.date_input('选择时间范围', max_value=max_date, min_value=min_date, value=(start_date, end_date))
col1,col2=st.columns(2)
cond=(df['gpstime_b_date']>=pd.to_datetime(dates[0]))&(df['gpstime_b_date']<=pd.to_datetime(dates[1]))
tmp = df.loc[cond]
with col1:
    st.subheader('载重占比')
    tmp_m=tmp.groupby('m_cut')['m_cut'].agg(['count']).reset_index()
    c = (
    Pie()
    .add("", [list(z) for z in zip(tmp_m['m_cut'].tolist(), tmp_m['count'].tolist())])
    .set_global_opts(title_opts=opts.TitleOpts(title="质量分布"))
    .set_series_opts(label_opts=opts.LabelOpts(formatter="{b}: {c}"))
    d = (
    Pie()
    .add(
        [list(z) for z in zip(tmp_m['m_cut'].tolist(), tmp_m['count'].tolist())],
        radius=["40%", "75%"],
    .set_global_opts(
        #title_opts=opts.TitleOpts(title="质量分布"),
#         legend_opts=opts.LegendOpts(orient="vertical", pos_top="15%", pos_left="2%"),
    .set_series_opts(label_opts=opts.LabelOpts(formatter="{b}: {c}({d}%)"))
    streamlit_echarts.st_pyecharts(d,height=400)
with col2:
    st.subheader('质量分布')
    #密度图太卡
    # fig = ff.create_distplot(tmp['m'].values.reshape(1,-1), ['m'],bin_size=2.0)
    # st.plotly_chart(fig,use_column_width=True)
    bins=np.linspace(5,75,15)
    tmp['m_bins']=pd.cut(tmp['m'],bins)
    tmp['m_bins']=tmp['m_bins'].astype('str')
    tmp1=tmp.groupby('m_bins')['m_bins'].agg(['count']).reset_index()
    fig = px.bar(tmp1, x="m_bins",y="count")
    st.plotly_chart(fig)
#button1 =  st.button('导入车辆计算')
id_list = df['tid_x'].unique()
select_id = st.selectbox("选择底盘号",list(id_list))
tmp_m = df[df['tid_x']==select_id]
tmp_m['最终使用m'] = round(tmp_m['最终使用m'],2)
# c = (
#         Line()
#         .add_xaxis(tmp_m['gpstime_b'].tolist())
#         .add_yaxis("质量", tmp_m['最终使用m'].tolist(),is_step=True,
# #                ,is_connect_nones=True
#                       markline_opts=opts.MarkLineOpts(
#             data=[
#                 opts.MarkLineItem(type_="average", name="平均值"),
#                 opts.MarkLineItem(y=60, name="60以上")
#             ]
#         ),
#               )
#     .set_global_opts(title_opts=opts.TitleOpts(title="质量监测"))
#     )
# streamlit_echarts.st_pyecharts(c,height=500)
def time_series(tmp_m):
    plots a time series to compare pace and heart rate values over time,
    with an adjustable range for the last month, last six months, year-to-date and all time
    ts = tmp_m.copy()
    ts.sort_values(by='gpstime_b',inplace=True)
    fig = go.Figure()
    fig.add_trace(
        go.Scatter(x=ts['gpstime_b'], y=ts['最终使用m'],
                    mode='lines+markers',
                    name='lines',
                    line_shape='vh')
    fig.update_xaxes(
        title_text="日期",
        rangeselector=dict(
            buttons=list([
                dict(count=1, label="1d", step="day", stepmode="backward"),
                dict(count=7, label="7d", step="day", stepmode="backward"),
                dict(count=14, label="14d", step="day", stepmode="backward"),
                dict(count=1, label="1m", step="month", stepmode="backward"),
                dict(count=3, label="3m", step="month", stepmode="backward"),
                dict(count=6, label="6m", step="month", stepmode="backward"),
                #dict(count=1, label="YTD", step="year", stepmode="todate"),
                dict(count=1, label="1y", step="year", stepmode="backward"),
                dict(step="all")
    fig.update_yaxes(title='车重')
    m_mean = ts['最终使用m'].mean()
    fig.update_layout(
        title_text="质量监测",
        template='plotly_white',
        # yaxis=dict(
        #     title='Pace',
        #     tickformat='%M:%S'
        width=1200,height=500,
        shapes=[dict(
                type= 'line',
                line = dict(color='red', dash='dashdot'),
                yref= 'y', y0= 60, y1= 60,
                xref= 'paper', x0= 0, x1= 1,
                dict(
                type= 'line',
                line = dict(color='green', dash='dashdot'),
                yref= 'y', y0= m_mean, y1= m_mean,
                xref= 'paper', x0= 0, x1= 1,
    return fig
fig=time_series(tmp_m)
st.plotly_chart(fig)
st.header('二、驾驶行为模块:')
st.subheader('2.1 整体概况')
col1,col2=st.columns(2)
with col1:
    row_type_list=radar_df['row_type'].unique()
    select_row_type = st.selectbox("选择路况",list(row_type_list))
with col2:
    m_list=radar_df['m'].unique()
    select_m = st.selectbox("选择载重状态",list(m_list))
cond=(radar_df['row_type']==select_row_type)&(radar_df['m']==select_m)
tmp = radar_df[cond]
v1 = [tmp['val_high'].tolist()]
v2 = [tmp['val_low'].tolist()]
schema = [opts.RadarIndicatorItem(name=i, max_=1) for i in tmp['index'].values ]
c = (
    Radar()
    .add_schema(
        schema=schema
    .add("高油耗组", v1, color="red")
    .add("低油耗组", v2, color="green")
    .set_series_opts(label_opts=opts.LabelOpts(is_show=False))
    .set_global_opts(
#         legend_opts=opts.LegendOpts(selected_mode="single"),
        title_opts=opts.TitleOpts(title="油耗对比雷达图"),
#     .set_series_opts(label_opts=opts.LabelOpts(is_show=False))
#     .set_global_opts(title_opts=opts.TitleOpts(title="Radar-空气质量"))
streamlit_echarts.st_pyecharts(c,height=400)
st.subheader('2.2 高油耗车改善建议')
#单元格合并待优化
id_list = improve['底盘号'].unique()
select_id = st.selectbox("选择底盘号",list(id_list))
car_better_df = improve.loc[improve['底盘号']==select_id].drop(['Unnamed: 0'],axis=1)
# car_better_df = improve.groupby(['底盘号','路况','质量','换挡率','大油门占比','优化点','原始值','目标值','挡位','可优化幅度'])['可优化幅度'].agg(['count']).reset_index()
# car_better_df=car_better_df.set_index(['底盘号','路况','质量'])
st.table(car_better_df)

4.requirements.txt 包含.py文件中所有需要的python依赖包

pandas
numpy
streamlit
datetime
matplotlib
seaborn
plotly
pyecharts
streamlit_echarts

5.构建 Docker 镜像

  • 我们使用以下命令构建,然后是“.” 运行当前目录。
docker build -t streamlitapp:latest .
  • 您还可以使用以下命令来指定文件。
docker build -t streamlitapp:latest .f Dockerfile
  • 输出将如下所示。
Sending building context to the Docker daemon  34.90kb
Step 1/8 : FROM python:3.8
  --->d6568b1g3y4h
Step 2/8 : WORKDIR /app
  --->Using Cache
  --->25cyf5dfpbfd
Step 3/8 : COPY requirements.txt ./requirements.txt
    --->Using Cache
    --->52jdf5dffbfd
Step 4/8 : RUN pip install -r requiremts.txt
    --->Using Cache
    --->81cdf5dffedf
Step 5/8 : EXPOSE 8501
    --->Using Cache
    --->62d29afd9eb
Step 6/8 : COPY ./app
    --->9rraeb07t4d
Step 6/8 : EXPOSE 8501
    --->4b2ap4h557cc
Step 7/8 : ENTRYPOINT ["streamlit", "run"]
    --->2egaeb07tdte
Removing intermediate container 5ta3824edte
 ---> 65dv092efstfu
step 8/8 : CMD ["drive_st_test.py"]
Successfully built 65dv092efstfu
Successfully tagged streamlitapp:latest
  • 使用以下命令查看所有图像。
docker image ls
  • 输出如图所示。
REPOSITORY                   TAG                 IMAGE ID            CREATED             SIZE
streamlitapp                 latest             65dv092efstfu        2 minutes ago       1.24GB
 
docker run -p 8501:8501 streamlitapp:latest
 
gv092e0ff6btdte593a7dad8e50ef01f7t3e89fy41816624gdted7fu1h1bid1o

它还会在以下 url 中启动我们的 streamlit 应用程序:

  1. 网络网址:http://172.17.0.2.8501
  2. 外部网址:https://193.106.63.249:8501

有了这个,Streamlit 应用程序现在与 docker 一起部署。

6.最终结果

这是一个简单的图像分类Web应用程序,同时使用Streamlit和PyTorch。 对于不是Django或Flask专家的人来说,Streamlit可以是构建用于数据科学的自定义Python Web应用程序的不错选择。 我之所以选择图像分类是因为计算机视觉是受深度学习算法支持的当前AI最受欢迎的领域之一。 安装Streamlit 建立使用者介面 我选择了经过预训练的ResNet101模型来执行分类。 pip install -r requirements.txt 通常不需要设置参数。如果你需要调整对象显示的顺序,就可以先设置一个容器,然后将渲染后的内容写入容器里。如果是序列,例如[3,2,1],则将该行分为三列,各列的宽度分别为最大宽度的3/6、2/6、1/6。st.empty也是一个占位容器,不同的是,st.empty只接收“一个”对象的写入,再次写入会覆盖此前的内容。st.form也是一个容器,用法与st.container类似,可以用with语句,也可以链式调用。注意,在st.form中,只有st.form_submit_button允许有回调函数。 Python部落(python.freelycode.com)组织翻译,禁止转载,欢迎转发。欢迎回到我们第5年度版的顶级Python库列表。在这里,你会发现一些开源世界中隐藏的珍宝,它们会让你开始你的新项目,或者让你现有的项目更有趣。你会发现机器学习和非机器学习库,所以我们涵盖了所有内容。我们希望你能像我们创造它一样喜欢它,所以我们开始吧!1.HTTPX作为一个经常与API进行交互的Python死... 文章目录前言: 关于streamlitDocker 部署Streamlit项目 | Streamlit如何部署到云服务器1、安装docker2、拉取python镜像2.1、什么是DockerHub?2.2、**配置docker加速器**:2.3、拉取python镜像3. 创建python容器3.1、首先创建网段3.2、在创建的网段范围内创建python容器4、启动服务streamlit服务5、docker常用命令6、可能的报错: 前言: 关于streamlit 关于streamlit: 官网:https 怎么说呢,为了让简历更加吸引人,博主不得不踏上了Azure, Docker的学习路程,开始为自己原来课上做的project部署一个streamlit app网站。project沿用了教授随便起的名字-musical_robot, 可以帮助用户判断一段音乐属于什么类别(hiphop, rap...)。 整个网站是用streamlit 搭建的,但是只能在自己电脑上localhost运行。博主便想把它部署在Azure上,这样远程就能访问啦。 首先,这个原project用到了机械学习方面的内容,需要下载很多的音乐 文章目录前言一、安装streamlit二、使用streamlit实现前端1.引入库总结写在最后一、python安装包写入requirements.txt二、利用requirements.txt安装结束语 摸鱼时刻,前段时间项目要求做一个图像检索接口,如期做完后,开始摸鱼 在摸鱼的过程想着一个问题,就是我能不能把自己做的接口直接展示在页面上,等着前端排期太慢了- - 别说,还真有特别简单的方法,可以通过streamlit这个框架(我理解为框架类似于Flask、Django等) 一、安装stream. 这是第五届 Python 类库 Top 10 年度榜。在这里,你会发现一些隐藏在开源世界中的珍宝,它们能让你开始着手新的项目,或者让你现有的项目更加有趣。从这份榜单里,你既能找到机器学习的库,也能找到非机器学习的库,所以它在各方面均有所涉及。 HTTPX     streamlit一个非常简洁的python语言web框架,用于机器学习、数据可视化等。这里介绍在将streamlit工程部署到Heroku。系统环境:Mac电脑 + streamlit 1.3.0 1)安装Git和Heroku CLi 2)安装Git sudo apt-get install git 3)安装Heroku Cli 在Heroku官网下载 Heroku CLi,... 英文搜索:package publish python project(打包发布python项目)。搜索过程中发现官方名称是:Packaging and distributing projects How to Publish an Open-Source Python Package to PyPI 如何打包发布Python项目,让全世界的人都能用 打包Python项目并发布到PyPI 官方文档:Packaging and distributing projects 官方文档:Packaging