Streamlit从入门到精通 系列:
st.cache_data
是缓存返回数据的计算的推荐方法:从 CSV
加载 DataFrame
、转换 NumPy
数组、查询 API
或任何其他返回可序列化数据对象的函数(str、int、float、DataFrame、array、list 等)。它会在每次函数调用时创建一个新的数据副本,使其免受突变和竞争条件的影响。st.cache_data
的行为是您想要的——所以如果您不确定,请从st.cache_data
开始,看看它是否有效!st.cache_resource
是缓存全局资源(如 ML 模型或数据库连接)的推荐方法,这些资源是您不想多次加载的不可序列化对象。使用它,您可以在应用程序的所有重新运行和会话之间共享这些资源,而无需复制或复制。请注意,对缓存返回值的任何突变都会直接改变缓存中的对象(更多详细信息见下文)。st.cache_data
是所有返回数据的函数的首选命令,无论是 DataFrames、NumPy 数组、str、int、float 还是其他可序列化类型。对于几乎所有用例来说,这都是正确的命令!
让我们看一个使用 st.cache_data
的例子。假设您的应用将优步拼车数据集(一个 50 MB 的 CSV 文件)从互联网加载到 DataFrame 中:
import streamlit as st
import time
import pandas as pd
def load_data(url):
df = pd.read_csv(url)
return df
df = load_data("https://github.com/plotly/datasets/raw/master/uber-rides-data1.csv")
st.dataframe(df)
st.button("Rerun")
运行 load_data 功能需要 2 到 30 秒,具体取决于您的互联网连接。(提示:如果您的连接速度较慢,请改用此 5 MB 数据集)。如果不进行缓存,则每次加载应用或进行用户交互时都会重新运行下载。点击我们添加的按钮自己试试吧!不是一次很棒的体验…😕
现在让我们在load_data上添加 @st.cache_data 装饰器:
import streamlit as st
import time
import pandas as pd
@st.cache_data # 👈 Add the caching decorator
def load_data(url):
df = pd.read_csv(url)
return df
df = load_data("https://github.com/plotly/datasets/raw/master/uber-rides-data1.csv")
st.dataframe(df)
st.button("Rerun")
再次运行应用。您会注意到,下载速度慢只发生在第一次运行时。随后的每次重播都应该几乎是即时的!💨
让我们一步一步地了解st.cache_data
的行为:
load_data
条目。有一个!因此,它检索缓存的对象,将其反序列化为 DataFrame
,然后返回它,而不是重新运行函数并再次下载数据。序列化和反序列化缓存对象的过程会创建原始 DataFrame 的副本。虽然这种复制行为似乎没有必要,但这是我们在缓存数据对象时想要的,因为它有效地防止了突变和并发问题。请阅读下面的“突变和并发问题”部分,以更详细地了解这一点。
Warning警告
st.cache_data
隐式使用pickle
模块,该模块已知不安全。缓存函数返回的任何内容都会被酸洗和存储,然后在检索时取消酸洗。确保缓存的函数返回受信任的值,因为可能会构造恶意pickle
数据,这些数据将在取消pickling
期间执行任意代码。切勿在不安全模式下加载可能来自不受信任的来源或可能已被篡改的数据。仅加载您信任的数据。
在上面的示例中,我们已经展示了如何缓存加载 DataFrame。缓存 DataFrame 转换(如 df.filter
或 df.sort_values
)也很有用。特别是对于大型 DataFrame,这些操作可能会很慢。
@st.cache_data
def transform(df):
df = df.filter(items=['Date/Time']) # 示例 1:筛选特定列
# df = df.apply(np.sum, axis=0)
return df
df = transform(df)
st.dataframe(df)
这样做还可以避免速率限制。
@st.cache_data
def api_call():
response = requests.get('https://jsonplaceholder.typicode.com/posts/1')
return response.json()
api_response = api_call()
print("api call", api_response)
st.write("api call", api_response)
运行复杂的机器学习模型可能会占用大量时间和内存。为避免一遍又一遍地重新运行相同的计算,请使用缓存。
@st.cache_data
def run_model(inputs):
return model(inputs)
st.cache_resource
st.cache_resource
是缓存“资源”的正确命令,这些资源应该在所有用户、会话和重新运行中全局可用。与st.cache_data
相比,它的用例更有限,尤其是在缓存数据库连接和 ML 模型方面。
作为st.cache_resource
示例,让我们看一个典型的机器学习应用。第一步,我们需要加载一个 ML 模型。我们使用 Hugging Face 的 transformers 库来做到这一点:
from transformers import pipeline
model = pipeline("sentiment-analysis") # 👈 Load the model
如果我们将此代码直接放入 Streamlit 应用程序中,则该应用程序将在每次重新运行或用户交互时加载模型。重复加载模型会带来两个问题:
相反,加载一次模型并在所有用户和会话中使用相同的对象会更有意义。这正是st.cache_resource
的用例!让我们将其添加到我们的应用程序中,并处理用户输入的一些文本:
from transformers import pipeline
@st.cache_resource # 👈 Add the caching decorator
def load_model():
return pipeline("sentiment-analysis")
model = load_model()
query = st.text_input("Your query", value="I love Streamlit! 🎈")
if query:
result = model(query)[0] # 👈 Classify the query text
st.write(result)
如果运行此应用,你将看到该应用仅调用一次 load_model - 就在应用启动时。后续运行将重用缓存中存储的相同模型,从而节省时间和内存!
使用 st.cache_resource
与使用 st.cache_data
非常相似。但是在行为上有一些重要的区别:
st.cache_data
缓存这些对象。st.cache_resource
对于连接数据库很有用。通常,您要创建一个连接对象,并希望在每个查询中全局重用该对象。每次运行时创建新的连接对象效率很低,并且可能会导致连接错误。这正是 st.cache_resource 可以做的事情,例如,对于 Postgres 数据库:
@st.cache_resource
def init_connection():
host = "hh-pgsql-public.ebi.ac.uk"
database = "pfmegrnargs"
user = "reader"
password = "NWDMCE5xdipIjRrp"
return psycopg2.connect(host=host, database=database, user=user, password=password)
conn = init_connection()
当然,您可以对任何其他数据库执行相同的操作。请参阅我们有关如何将 Streamlit 连接到数据库的指南以获取深入的示例。
上面的部分展示了每个缓存装饰器的许多常见示例。但在某些边缘情况下,决定使用哪个缓存装饰器并不那么简单。最终,这一切都归结为“数据”和“资源”之间的区别:
从上面列出的类型中,很明显,Python 中的大多数对象都是“数据”。这也是为什么 st.cache_data
是几乎所有用例的正确命令。st.cache_resource
是一个更奇特的命令,您应该只在特定情况下使用。
https://github.com/zgpeace/pets-name-langchain/tree/develop
https://docs.streamlit.io/library/advanced-features/caching#basic-usage