TowardsDataScience 2023 博客中文翻译(三百六十)

原文:TowardsDataScience

协议:CC BY-NC-SA 4.0

可视化多重共线性对多重回归模型的影响

原文:towardsdatascience.com/visualizing-the-effect-of-multicollinearity-on-multiple-regression-model-8f323ef542a9

使用 Python 进行数据可视化,解释多重共线性对多重回归的影响

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Boriharn K

·发布在Towards Data Science ·11 分钟阅读·2023 年 6 月 12 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片由Alex提供,发布在Unsplash

什么是多重共线性?

在多重回归中,多重共线性发生在预测变量(自变量)与模型中的一个或多个其他预测变量高度相关时。

为什么这很重要?

### Multiple regression equation:

Y = β₀ + β₁X₁ + β₂X₂ + ... + βᵢXᵢ + ε

理论上,如我们在方程中所见,多重回归使用多个预测变量来预测因变量的值。

顺便提一下,多重回归通过确定在保持其他预测变量不变的情况下,预测变量单位均值变化对因变量的影响来工作。

如果一个预测变量与其他变量高度相关,那么在不改变其他变量的情况下很难改变它。

多重共线性的影响

简单来说,多重共线性会影响模型系数。数据的微小变化可能会影响系数估计。因此,解释每个自变量的作用变得困难。

本文将通过数据可视化解释和展示这一现象。在开始之前,让我们讨论如何判断模型是否存在多重共线性。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

本文中的一个结果示例。图像来源:作者。

如何检测多重共线性?

### VIF (Variance Inflation Factor) equation:

VIF = 1/(1 - Rᵢ²)

我们可以使用 VIF(方差膨胀因子)来估计由于多重共线性而导致的回归系数方差的膨胀程度。

计算是通过将一个预测变量与其他预测变量回归以获得 R 平方值完成的。然后,获得的 R 平方值将用于计算 VIF 值。方程中的’i’代表预测变量。

可以使用以下标准来解释结果:

#1 : not correlated
#1–5 : moderately correlated
#> 5 : highly correlated

现在我们完成了解释部分。让我们继续到模型创建部分。

多重回归模型

开始为绘图准备多重回归模型。首先,我们将从数据集中创建两个模型;一个具有中等相关变量,另一个具有高度相关变量。

然后我们将稍微修改数据,看看哪个模型会受到更大的影响。首先导入库。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

%matplotlib inline

获取数据

例如,这篇文章将使用汽车数据数据集,该数据集具有公有领域许可。可以通过Seaborn库的seaborn.load_dataset()函数自由直接下载。

数据集包含 1970 年至 1982 年间在美国、欧洲和日本的 392 辆汽车的价格和特征。有关数据集的更多信息可以在这里找到:链接

data = sns.load_dataset('mpg')
data.dropna(inplace=True)       #remove null rows
data.head()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

进行探索性数据分析以了解数据集总是一个好主意。

data.describe()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

列中的范围(最大值 - 最小值)差异较大。因此,执行标准化有助于后续解释系数。

data = data.iloc[:,0:-2]    # select columns

from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
df_s = scaler.fit_transform(data)
df = pd.DataFrame(df_s, columns=data.columns)
df.head()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

接下来绘制一个热图以展示变量之间的相关性。

plt.figure(figsize=(9, 6))
sns.heatmap(df.corr(), cmap='coolwarm', annot=True)
plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

结果显示有些变量与其他变量高度相关。如果每个预测变量都被放入多重回归模型来预测’mpg’值,多重共线性将影响模型。

计算 VIF 值

这可以通过计算Statsmodels库中的 VIF 值来快速证明。

from statsmodels.stats.outliers_influence import variance_inflation_factor as vif

vif_data = pd.DataFrame()
vif_data["feature"] = df.columns
# calculating VIF for each feature
vif_data["VIF"] = [vif(df.values, i) for i in range(len(df.columns))]

print(vif_data)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一些 VIF 值相当高(> 5),可以解释为高度相关。如果我们直接将每个预测变量放入多重回归模型中,该模型可能会遭受多重共线性问题。

接下来,将仅选择一些预测变量。这篇文章将使用两个模型:一个包含中等相关的变量,另一个包含高度相关的变量。

第一个多重回归模型使用’气缸数’和’加速度’来预测’mpg’,而第二个模型使用’气缸数’和’位移’。让我们再次计算 VIF 值。

df1 = df[['mpg', 'cylinders', 'acceleration']]
df2 = df[['mpg', 'cylinders', 'displacement']]

# VIF dataframe1
vif_data1 = pd.DataFrame()
vif_data1['feature'] = df1.columns
vif_data1['VIF'] = [vif(df1.values, i) for i in range(len(df1.columns))]
print('model 1')
print(vif_data1)

# VIF dataframe2
vif_data2 = pd.DataFrame()
vif_data2['feature'] = df2.columns
vif_data2['VIF'] = [vif(df2.values, i) for i in range(len(df2.columns))]
print('model 2')  
print(vif_data2)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

两个模型的 VIF 值。图片由作者提供。

这两个模型之间的区别在于将变量从’加速度’改为’位移’。然而,可以注意到第一个模型中的 VIF 值都没有超过 5,而第二个模型的 VIF 值则相对较高,超过了 10。

创建多重回归模型

我们将使用OLS函数来自 statsmodels 库创建一个多重回归模型。

import statsmodels.api as sm
y1 = df1[['mpg']]
X1 = df1[['cylinders', 'acceleration']]

lm1 = sm.OLS(y1, X1)
model1 = lm1.fit()
model1.summary()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

具有中等相关变量的多重回归模型的总结表。图片由作者提供。

绘制模型

从获得的模型中,我们将定义一个函数,在网格上运行模型,以便在下一步中使用。

def run_model(v1, v2, pd_):
    mesh_size = 0.02
    x_min, x_max = pd_[[v1]].min()[0], pd_[[v1]].max()[0] 
    y_min, y_max = pd_[[v2]].min()[0], pd_[[v2]].max()[0] 
    xrange = np.arange(x_min, x_max, mesh_size)
    yrange = np.arange(y_min, y_max, mesh_size)
    xx, yy = np.meshgrid(xrange, yrange)
    return xx, yy, xrange, yrange

这部分非常有趣。让我们使用Plotly库绘制模型,它可以轻松创建交互式图表。因此,我们可以与可视化进行互动,比如缩放或旋转。

import plotly.express as px
import plotly.graph_objects as go
from sklearn.svm import SVR

#run and apply the model
xx1, yy1, xr1, yr1 = run_model('cylinders', 'acceleration', X1)
pred1 = model1.predict(np.c_[xx1.ravel(), yy1.ravel()])
pred1 = pred1.reshape(xx1.shape)

# plot the result
fig = px.scatter_3d(df1, x='cylinders', y='acceleration', z='mpg')
fig.update_traces(marker=dict(size=5))
fig.add_traces(go.Surface(x=xr1, y=yr1, z=pred1, name='pred1'))
fig.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

具有中等相关变量的多重回归模型。图片由作者提供。

我们可以对第二个存在多重共线性问题的模型执行相同的过程。

y2 = df2[['mpg']]
X2 = df2[['cylinders', 'displacement']]

lm2 = sm.OLS(y2, X2)
model2 = lm2.fit()
model2.summary()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

多重回归模型与多重共线性的总结表。图片由作者提供。

#run and apply the model
xx2, yy2, xr2, yr2 = run_model('cylinders', 'displacement', X2)
pred2 = model2.predict(np.c_[xx2.ravel(), yy2.ravel()])
pred2 = pred2.reshape(xx2.shape)

# plot the result
fig = px.scatter_3d(df2, x='cylinders', y='displacement', z='mpg')
fig.update_traces(marker=dict(size=5))
fig.add_traces(go.Surface(x=xr2, y=yr2, z=pred2, name='pred2'))
fig.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

具有高度相关变量的多重回归模型。图片由作者提供。

修改数据集

如前所述,数据中的小修改可能会影响系数估计。为了证明这一点,我们将随机选择一行并更改其值。例如,它们将乘以 1.25。

然后,我们可以进行相同的过程,并将新的和原始的多重回归模型绘制在同一个图中以查看结果。

# randomly modify a row in the dataframe 
from random import *
x = randint(1, len(df))
mod_list = [i*1.25 for i in df.iloc[x,:]]   #multiply by 1.25
df_m = df.copy()
df_m.iloc[x] = mod_list
df_m

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

计算 VIF 值

从修改后的数据集中计算 VIF 值以比较差异。

df_m1 = df_m[['mpg', 'cylinders', 'acceleration']]
df_m2 = df_m[['mpg', 'cylinders', 'displacement']]

# VIF dataframe1
vif_data1 = pd.DataFrame()
vif_data1['feature'] = df_m1.columns
vif_data1['VIF'] = [vif(df_m1.values, i) for i in range(len(df_m1.columns))]
print('model m1')
print(vif_data1)

###
# VIF dataframe2
vif_data2 = pd.DataFrame()
vif_data2['feature'] = df_m2.columns
vif_data2['VIF'] = [vif(df_m2.values, i) for i in range(len(df_m2.columns))]
print('model m2')  
print(vif_data2)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

从原始模型和修改后的模型计算的 VIF 值。图片由作者提供。

从修改后的数据集中计算的新 VIF 值略有变化。两个模型保持相同的条件:第一个模型的预测变量为中等相关,第二个模型的预测变量则为高度相关。

创建多重回归模型

为了比较模型系数的变化,让我们从新数据集中构建模型并进行绘制。

y_m1 = df_m1[['mpg']]
X_m1 = df_m1[['cylinders', 'acceleration']]

lm_m1 = sm.OLS(y_m1, X_m1)
model_m1 = lm_m1.fit()
model_m1.summary()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

中等相关变量的模型总结:原始模型和修改后的模型,分别如图所示。图像由作者提供。

接下来,对具有高度相关预测变量的第二个模型进行相同的处理。

y_m2 = df_m2[['mpg']]
X_m2 = df_m2[['cylinders', 'displacement']]

lm_m2 = sm.OLS(y_m2, X_m2)
model_m2 = lm_m2.fit()
model_m2.summary()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

高度相关变量模型的总结:原始模型和修改后的模型,分别如图所示。图像由作者提供。

从上表来看,它们的系数变化似乎很小。顺便提一下,这种情况发生是因为我们随机修改了数据集中的一行。这还不够,也过早地假设多重共线性影响模型系数是不成立的。

根据目前的概念和代码,for loop函数将在 Python 中应用于逐行修改每个值。然后,比较与原始模型的系数绝对变化。

首先定义一个函数来比较系数值。

def compare_cef(base_m, mod_m, col):
    val_base = base_m.summary().tables[1].as_html()
    ml = pd.read_html(val_base, header=0, index_col=0)[0]

    val_diff = mod_m.summary().tables[1].as_html()
    ml_m = pd.read_html(val_diff, header=0, index_col=0)[0]

    df_ = pd.DataFrame(abs(ml.iloc[:,0] - ml_m.iloc[:,0]))
    df_.rename(columns={'coef': 'r '+str(col+1)}, inplace=True)
    return df_

使用for loop函数一次修改一行。

keep_df1, keep_df2 = [], []
for n in range(len(df)):
    mod_list = [i*1.25 for i in df.iloc[n,:]]
    df_m = df.copy()
    df_m.iloc[n] = mod_list

    df_m1 = df_m[['mpg', 'cylinders', 'acceleration']]
    y_m1, X_m1 = df_m1[['mpg']], df_m1[['cylinders', 'acceleration']]
    lm_m1 = sm.OLS(y_m1, X_m1)
    mdl_m1 = lm_m1.fit()

    df_m2 = df_m[['mpg', 'cylinders', 'displacement']]
    y_m2, X_m2 = df_m2[['mpg']], df_m2[['cylinders', 'displacement']]
    lm_m2 = sm.OLS(y_m2, X_m2)
    mdl_m2 = lm_m2.fit()

    df_diff1 = compare_cef(model1, mdl_m1, n)
    df_diff2 = compare_cef(model2, mdl_m2, n)
    keep_df1.append(df_diff1)
    keep_df2.append(df_diff2)

df_t1 = pd.concat(keep_df1, axis=1)
df_t2 = pd.concat(keep_df2, axis=1)
df_t = pd.concat([df_t1, df_t2], axis=0)
df_t

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用热力图可视化获得的 DataFrame。

plt.figure(figsize=(16,2.5))
sns.heatmap(df_t, cmap='Reds')
plt.xticks([])
plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

热力图显示模型系数的绝对差异。图像由作者提供。

从热力图绘制中可以看出,前两行显示了中等相关变量模型在数据轻微变化前后的系数绝对变化。

最后两行比较了高度相关变量模型在数据轻微修改前后的系数绝对变化。

可以解释为,当数据发生变化时,具有高度相关预测变量的模型系数趋向于更加不稳定,而具有中等相关预测变量的模型则受到的影响较小。

绘制多重回归模型

为了可视化系数的变化,以一个示例,我将修改数据集中的一行,创建一个新模型,并将其与原始模型进行比较。新模型将使用’viridis’色盘(黄绿),而原始模型则用默认颜色(橙蓝)绘制。

如果你想手动选择和修改其他行,请更改下面的代码。

x = 6     #select row number
mod_list = [i*1.25 for i in df.iloc[x,:]]
df_m = df.copy()
df_m.iloc[x] = mod_list

y_m1 = df_m[['mpg']]
X_m1 = df_m[['cylinders', 'acceleration']]
lm_m1 = sm.OLS(y_m1, X_m1)
model_m1 = lm_m1.fit()

y_m2 = df_m[['mpg']]
X_m2 = df_m[['cylinders', 'displacement']]
lm_m2 = sm.OLS(y_m2, X_m2)
model_m2 = lm_m2.fit()

运行并绘制中等相关预测变量的多重回归模型。

# run the model
pred_m1 = model_m1.predict(np.c_[xx1.ravel(), yy1.ravel()])
pred_m1 = pred_m1.reshape(xx1.shape)

# plot the result
fig = px.scatter_3d(df_m, x='cylinders', y='acceleration', z='mpg')
fig.update_traces(marker=dict(size=5))
fig.add_traces(go.Surface(x=xr1, y=yr1, z=pred1, name='pred'))
fig.add_traces(go.Surface(x=xr1, y=yr1, z=pred_m1, name='pred_m1',
                          colorscale = 'viridis_r'))
fig.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

绘制中等相关预测变量的模型。图像由作者提供。

可以看到,两种模型重叠,因为两个平面的颜色在结果中混合在一起。最后,对中等相关预测变量的模型进行相同的处理。

# run the model
pred_m2 = model_m2.predict(np.c_[xx2.ravel(), yy2.ravel()])
pred_m2 = pred_m2.reshape(xx2.shape)

# plot the result
fig = px.scatter_3d(df_m, x='cylinders', y='displacement', z='mpg')
fig.update_traces(marker=dict(size=5))
fig.add_traces(go.Surface(x=xr2, y=yr2, z=pred2, name='pred'))
fig.add_traces(go.Surface(x=xr2, y=yr2, z=pred_m2, name='pred_m2',
                          colorscale = 'viridis_r'))
fig.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

绘制具有多重共线性的模型。图像由作者提供。

结果显示,两个模型并没有完全重叠。它们相互交叉,模型之间产生了一个小的间隙。

请注意,这些图表是通过随机修改数据集中的一行进行选择的。第一个模型并不完全没有多重共线性。它仍然有中等相关的预测变量。从热图中也可以看出,在某些情况下,它受到了变化的影响。然而,与第二个模型相比,这种变化造成的后果较少。

总结

本文通过比较两个模型,一个具有中等相关预测变量,另一个具有高度相关预测变量,应用数据可视化来表达多重共线性对多元回归模型的影响。同时,也进行了修改原始数据的操作,以查看哪些模型在数据的小变化中受影响更大。

结果显示,模型中具有高度相关预测变量的情况越多,模型系数在数据不稳定变化时受影响的程度越大。因此,解释每个存在多重共线性问题的模型中的预测变量可能会变得困难。

这些是我认为你可能感兴趣的数据可视化文章:

  • 8 种使用 Python 处理多个时间序列数据的可视化方式 (链接)

  • 9 种比条形图更引人注目的 Python 可视化方式 (链接)

  • 7 种使用 Python 表达排名随时间变化的可视化方式 (链接)

  • 大逃杀 — 7 种用于互动金融图表的 Python 库比较 (链接)

参考文献

直观地展示维度诅咒的真实程度

原文:towardsdatascience.com/visualizing-the-full-extent-of-the-curse-of-dimensionality-5556ff6852bb?source=collection_archive---------6-----------------------#2023-06-29

使用蒙特卡洛方法来直观展示具有非常多特征的观察值的行为

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Florin Andrei

·

关注 发表在 Towards Data Science ·10 min read·2023 年 6 月 29 日

想象一个数据集,由若干观察值组成,每个观察值都有 N 个特征。如果你将所有特征转换为数值表示,你可以说每个观察值是在一个 N 维空间中的一个点。

当 N 较低时,点之间的关系就是你直观上所期望的样子。但有时 N 会变得非常大——例如,如果你通过独热编码创建了很多特征等。在 N 的非常大值下,观察值的行为就像是稀疏的,或者它们之间的距离比你期望的要大。

这个现象是真实的。随着维度 N 的增加,而其他条件保持不变,包含你观察的 N-体积确实会在某种意义上增加(或者至少自由度变得更大),观察之间的欧几里得距离也会增加。点的分布实际上会变得更加稀疏。这是维度诅咒的几何基础。模型和技术在数据集上的表现会受到这些变化的影响。

如果特征数量非常大,很多事情可能会出错。特征比观察值多是模型在训练中过拟合的典型设置。在这样的空间中进行任何暴力搜索(例如 GridSearch)变得效率更低——你需要更多的试验才能覆盖相同的线性区间限制。一个微妙的效应会影响任何基于距离或邻近度的模型:随着维度的增长到非常大的值,如果你考虑观察中的任何一点,所有其他点似乎都远离,并且几乎是等距离的——由于这些模型依赖于距离来完成工作,距离差异的平衡使得它们的工作变得更加困难。例如,如果所有点似乎几乎等距离,聚类效果不会很好。

出于这些原因,还有更多原因,创建了诸如 PCA、LDA 等技术——旨在远离具有非常多维度的空间的特殊几何,并将数据集精炼到与其中实际信息更兼容的维度数。

直观上很难感知这个现象的真正规模,而超过 3 维的空间极其难以可视化,因此我们来做一些简单的 2D 可视化来帮助我们的直觉。维度如何成为问题的几何基础就是我们要在这里可视化的内容。如果你以前没有见过这些结果,可能会感到惊讶——高维空间的几何远比典型直觉所能建议的要复杂得多。

体积

考虑一个大小为 1 的正方形,位于原点。然后在正方形中内切一个圆。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一个内切于正方形的圆

这是 2 维的设置。现在考虑一般的 N 维情况。在 3 维中,你有一个内切于立方体的球体。再往上,你有一个内切于 N-立方体的 N-球体,这是最一般的描述方式。为了简化,我们将这些对象称为“球体”和“立方体”,不论它们有多少维度。

立方体的体积是固定的,总是 1。问题是:当维度 N 变化时,球体的体积会发生什么?

我们通过实验来回答这个问题,使用蒙特卡洛方法。我们将生成大量点,这些点在立方体内均匀但随机分布。对于每个点,我们计算它到原点的距离——如果该距离小于 0.5(球体的半径),那么这个点就在球体内部。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

随机点

如果我们将球体内部的点数除以总点数,这将近似球体体积和立方体体积的比例。由于立方体的体积为 1,比例将等于球体的体积。当总点数较大时,近似值会更好。

换句话说,比例inside_points / total_points将近似球体的体积。

代码相当直接。由于我们需要很多点,必须避免显式循环。我们可以使用 NumPy,但它仅支持 CPU 并且是单线程的,因此速度较慢。潜在的替代方案:CuPy(GPU)、Jax(CPU/GPU)、PyTorch(CPU/GPU)等。我们将使用 PyTorch——但 NumPy 代码看起来几乎相同。

如果你跟随嵌套的torch语句,我们生成 1 亿个随机点,计算它们到原点的距离,统计在球体内的点数,并将点数除以总点数。ratio数组将包含不同维度中球体的体积。

可调参数设置为 24 GB 内存的 GPU——如果你的硬件不同,请调整这些参数。

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
# force CPU
# device = 'cpu'

# reduce d_max if too many ratio values are 0.0
d_max = 22
# reduce n if you run out of memory
n = 10**8

ratio = np.zeros(d_max)

for d in tqdm(range(d_max, 0, -1)):
    torch.manual_seed(0)
    # combine large tensor statements for better memory allocation
    ratio[d - 1] = (
        torch.sum(
            torch.sqrt(
                torch.sum(torch.pow(torch.rand((n, d), device=device) - 0.5, 2), dim=1)
            )
            <= 0.5
        ).item()
        / n
    )

# clean up memory
torch.cuda.empty_cache()

让我们来可视化结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

球体的体积

N=1 是简单的:“球体”和“立方体”都是线段,它们是相等的,所以“球体”的“体积”是 1。对于 N=2 和 N=3,你应该记住高中几何的结果,并注意到这个模拟非常接近实际值。

但随着 N 的增加,会发生非常戏剧性的变化:球体的体积急剧下降。当 N=10 时,它已经接近 0,并且在 N 约为 20 之后,它会低于此模拟的浮点精度。超过这一点,代码计算球体的体积为 0.0(这只是一个近似值)。

>>> print(list(ratio))
[1.0, 0.78548005, 0.52364381, 0.30841056, 0.16450286, 0.08075666, 0.03688062, 0.015852, 0.00645304, 0.00249584, 0.00092725, 0.00032921, 0.00011305, 3.766e-05, 1.14e-05, 3.29e-06, 9.9e-07, 2.8e-07, 8e-08, 3e-08, 2e-08, 0.0]

一种解释是:对于较大的 N 值,几乎整个立方体的体积都集中在角落中。与之相比,包含球体的中心区域变得微不足道。在高维空间中,角落变得非常重要。大部分体积“迁移”到角落。这种变化随着 N 的增加极其迅速。

另一种看法:球体是原点的“邻域”。随着 N 的增加,点会从那个邻域中消失。原点实际上并没有特别之处,因此原点的邻域发生的情况,必须发生在所有邻域中。 “邻域”的概念经历了巨大的变化。随着 N 的增加,邻域会变得空旷。

我几年前第一次运行了这个模拟,我清楚地记得这个结果是多么震惊——当 N 增加时,球体的体积迅速下降到 0。检查代码后没有发现错误,我的反应是:保存工作,锁定屏幕,出去散步,思考刚才看到的内容。😃

线性距离

让我们计算立方体的对角线作为 N 的函数。这是很简单的,因为对角线实际上是sqrt(N),所以代码过于简单,不需要在这里展示。这些是结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

对角线长度

再次,对于 N=2 和 N=3,你应该能从几何学中识别出结果(正方形和普通三维立方体的对角线)。但随着 N 的增加,对角线也会增加,其增长是无限的。

这可能听起来非常违反直觉,但即使立方体的边长保持不变(等于 1),其对角线也可以随着 N 的增加而无限变大。已经对于 N=1000,对角线长度约为 32。如果立方体的边长是 1 米,那么在非常多维度的空间中,立方体的对角线长度将达到 1 公里。

即使沿边的距离保持不变,对角线也会无限增长,与维度数量一起增长。

每次向空间中添加一个新维度时,更多的边、面等就会出现,角落区域的配置变得更加复杂,具有更多的自由度,对角线的测量会稍微长一点。

观测之间的距离

观测点或点之间的距离怎么样?假设我们生成固定数量的随机点,然后计算任意两个点之间的平均距离,以及任何点到最近邻居的平均距离。所有点始终都包含在立方体中。随着 N 的增加,平均距离会发生什么变化?让我们再运行一次模拟。

请注意内存管理的保守方法。这很重要,因为这里使用的数据结构相当大。

n_points_d = 10**3
# how many pairs of points are there
dist_count = n_points_d * (n_points_d - 1) / 2
# we use the full pair-wise matrix of distances,
# so each distance will be counted twice
dist_count = 2 * dist_count
d_max = d_max_diag

avg_dist = np.zeros(d_max)
avg_dist_nn = np.zeros(d_max)

for d in tqdm(range(d_max, 0, -1)):
    torch.manual_seed(0)
    # generate random points
    point_coordinates = torch.rand((n_points_d, d), device=device)
    # compute differences of point coordinates on all axes
    coord_diffs = point_coordinates.unsqueeze(1) - point_coordinates
    del point_coordinates
    # square the coordinate differences
    diffs_squared = torch.pow(coord_diffs, 2)
    del coord_diffs
    # compute distances between any 2 points
    distances_full = torch.sqrt(torch.sum(diffs_squared, dim=2))
    del diffs_squared
    # compute average distance between points
    avg_dist[d - 1] = torch.sum(distances_full).item() / dist_count
    # compute distances to nearest neighbors
    distances_full[distances_full == 0.0] = np.sqrt(d) + 1
    distances_nn, _ = torch.min(distances_full, dim=0)
    del distances_full
    # compute average distance to nearest neighbors
    avg_dist_nn[d - 1] = torch.mean(distances_nn).item()
    del distances_nn

torch.cuda.empty_cache()

我们在这里使用的点数远少于此(仅 1000 个),因为主要数据结构的大小随着增加,因此如果生成太多点,我们很快就会耗尽内存。即便如此,近似结果应该足够接近实际值。

这是任何两个点之间的平均距离,基于 1000 个点,作为 N 的函数:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

点之间的平均距离

对于小的 N 值,平均距离大约是 0.5 或 1。然而,随着 N 的增加,平均距离开始增长,当 N=2000 时已经接近 18。增长是显著的。

这是到最近邻居的平均距离,作为 N 的函数:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

到最近邻居的平均距离

增加的效果更为显著,因为当 N 低于 10 时,值相当微小,点在低维空间中挤在一起。对于大的 N 值,最近邻的平均距离实际上接近任何两个点之间的平均距离——换句话说,临近空旷的情况主导了测量结果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

距离比率

这就是为什么我们一开始说在高维空间中点变得几乎等距——平均距离和最短距离变得几乎相同。这里有来自模拟的证据,以及对现象强度的直观理解。

随着维度 N 的增加,所有点彼此逐渐远离,即使它们的坐标被限制在相同的狭窄范围(-0.5, +0.5)。通过仅仅增加更多维度,点的群体变得越来越稀疏。当维度从几单位增加到几千单位时,这种增加跨度达到几个数量级。这是一个非常大的增加。

结论

维度诅咒是一个真实的现象,其基础在于 N 维空间的几何。

在其他条件相同的情况下,当你增加维度(或特征)数量时,点(或观测值)会迅速远离彼此。实际上,虽然轴上的线性间隔保持不变,但这里的空间变得更大了。所有邻域都变得空旷,每个点最终都孤立地坐在一个高维空间中。

这应该提供一些直观的理解,即一些技术(如聚类、各种模型等)在特征数量大幅变化时表现不同。少量观测值结合大量特征可能会导致次优结果——尽管维度诅咒不是唯一原因,但它可能是原因之一。

一般来说,观测值的数量应该“跟上”特征的数量——具体规则依赖于许多因素。

如果没有其他,这篇文章应该提供了一个对高维空间特性的直观概述,这些特性难以可视化。一些趋势极为强烈,现在你应该对它们的强度有一些了解。

本文中使用的所有代码都在这里:

[## misc/curse_dimensionality_article/n_sphere.ipynb 在 master · FlorinAndrei/misc

随机内容。通过在 GitHub 上创建一个账户来贡献于 FlorinAndrei/misc 的开发。

github.com](https://github.com/FlorinAndrei/misc/blob/master/curse_dimensionality_article/n_sphere.ipynb?source=post_page-----5556ff6852bb--------------------------------)

本文中使用的所有图像都是作者创作的(见上面的代码链接)。

使用 Python 地图可视化贸易流量 — 第一部分:双向贸易流量地图

原文:towardsdatascience.com/visualizing-trade-flow-in-python-maps-part-i-bi-directional-trade-flow-maps-639f39c19bba?source=collection_archive---------4-----------------------#2023-12-16

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Himalaya Bir Shrestha

·

关注 发表在 Towards Data Science ·7 分钟阅读·2023 年 12 月 16 日

商品和服务的交换以及它们相应的价值是我们日常生活中的复杂部分。同样,国家之间也会进行各种类型的贸易关系,交换产品和服务,如电力、能源商品、原材料、加工品、旅游等。理解国家之间的贸易流动(进出口)对于评估一个国家的收入和支出、经济实力、供应安全以及国家之间的关系性质至关重要。

在这个两部分系列中,我将分享如何使用 Python 将国家之间的贸易流动可视化在地图上。本系列的第一部分将重点介绍可视化双向(进口和出口)贸易流动。第二部分将重点介绍可视化净贸易流动。我将使用一个假设产品的虚拟数据集来进行这种可视化。我将以我的国家和地区(尼泊尔/南亚)作为演示的例子。让我们开始吧。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

照片由GeoJango Maps提供,发布在Unsplash上。

查找箭头的坐标

在贸易流动地图中,我的目标是表示国家之间的双向贸易关系。例如,从尼泊尔到印度的出口将由第一个箭头(A1-A2)表示,从印度到尼泊尔的进口将由第二个箭头(A3-A4)表示。这样,每对国家关系将需要四个坐标点来定义箭头的起点和终点,以分别表示出口和进口。

尽管也可以假设一个可以自动检测到的坐标(例如,国家几何形状的中心点),我打算在地图上标记这些点并逐一获取其坐标。为此,可以在类似 Google Earth 的应用程序中创建一个项目,导出 KML 文件,并通过转换器提取坐标(例如,MyGeodata Cloud网站上的 GIS 数据转换器)。

Keyhole Markup Language (KML)是一种用于在应用程序中显示地理数据的文件格式,例如 Google Earth。它使用基于标签的结构,具有嵌套的元素和属性,并基于 XML 标准(Google, 2023)。

数据

我的输入数据结构如下图所示。它包含了五种不同的邻国之间的贸易关系:尼泊尔-印度,尼泊尔-孟加拉国,尼泊尔-中国,印度-巴基斯坦,以及印度-斯里兰卡。对于每对国家,有四个坐标点用于表示两个箭头的起点和终点。Value1 表示从 Country1 到 Country2 的出口。Value2 表示 Country1 从 Country2 的进口。目标是将这种关系在 Python 地图中展示出来。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

贸易流动地图的数据输入。图片由作者提供。

我将上述数据读入 pandas dataframe df。此外,我创建了包含每对国家之间出口和进口量的字典对象,例如transfers,以及包含第一个箭头起点坐标的字典对象startarrow1_dict

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

创建必要的字典对象。图片由作者提供。

代码描述

在本节中,我将描述用于可视化贸易流图的代码。我将主要使用 matplotlib 和 cartopy 包。我还使用了相同的包来可视化全球表面温度异常中的全球表面温度异常。

  1. 导入所需包

我从导入主要的必需包和依赖项开始,如下所示:

import cartopy.crs as ccrs
import cartopy.io.shapereader as shpreader

import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib import colormaps
from matplotlib.colors import Normalize
from matplotlib.cm import ScalarMappable

import numpy as np
import pandas as pd
import os

2. 读取形状文件

作为形状文件,我使用了 Natural Earth Vector。矢量文件可以直接通过 cartopy 包的 shapereader 模块读取。

# get the country border file (10m resolution) and extract
shpfilename = shpreader.natural_earth(
                           resolution=”10m”,
                           category=”cultural”,
                           name=”admin_0_countries”,
                          )
reader = shpreader.Reader(shpfilename)
countries = reader.records()

使用名为 Fiona 的包,可以读取所有国家的列表,如下所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用 Fiona 包来打开形状文件并提取所有国家名称的列表。图片由作者提供。

3. 提取所需国家的信息

接下来,我创建了required,这是一个包含六个有贸易关系的国家的列表。我还创建了一个字典对象c,其中包含 FionaRecord,即所有相关的国家信息,可用于绘图。

# required countries
required = [“Nepal”, “India”, “Bangladesh”,”China”,”Pakistan”,”Sri Lanka”]

# extract the specific country information
c = {
     co.attributes["ADMIN"]: co
     for co in countries if co.attributes["ADMIN"] in required
    }

4. 绘制 **required** 国家及裁剪

在这一步,我首先绘制了required国家在 PlateCarree 投影中的几何形状,如下所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

绘制所需的国家。图片由作者提供。

接下来,我希望裁剪掉其余世界的几何形状,以便只查看六个国家的放大视图。我确定了能够覆盖所有六个国家的最大和最小经纬度值,设置了坐标轴的范围,并绘制了这些国家。在 for 循环中,我还添加了一个代码,显示每个国家的名称在其重心几何形状上。

matplotlib 包的zorder 属性 将决定艺术家的绘制顺序。具有较高zorder的艺术家会被绘制在顶部。

# get overall boundary box from country bounds
extents = np.array([c[cn].bounds for cn in c])
lon = [extents.min(0)[0], extents.max(0)[2]]
lat = [extents.min(0)[1], extents.max(0)[3]]

ax = plt.axes(projection=ccrs.PlateCarree())

# get country centroids
ax.set_extent([lon[0] - 1, lon[1] + 1, lat[0] - 1, lat[1] + 1])

for key, cn in zip(c.keys(),c.values()):
    ax.add_geometries(cn.geometry,
                      crs=ccrs.PlateCarree(),
                      edgecolor="gray",
                      facecolor="whitesmoke",
                     zorder = 1)

    # Add country names
    centroid = cn.geometry.centroid

    ax.text(
        centroid.x,
        centroid.y,
        key,  # Assuming 'name' is the attribute containing the country names
        horizontalalignment='center',
        verticalalignment='center',
        transform=ccrs.PlateCarree(),
        fontsize=8,  # Adjust the font size as needed
        color='black',  # Set the color of the text
        zorder = 2
        )

plt.axis("off")
plt.show()

5. 设置色彩图,添加箭头补丁和颜色条。

这是代码中最重要的部分。首先,我选择了viridis_r,即viridis的反向颜色调色板作为我的色彩图。接下来,我确定了任何国家之间的贸易值的最小和最大值,分别为tmintmax。这些值被标准化,使得tmin对应于色彩图cmap的最低端(0),tmax对应于最高端(1),并在随后的代码中相应使用。

然后,我遍历了transfers,并使用了FancyArrowPatch对象在国家之间绘制箭头。每个箭头对象都与一个独特的颜色col相关联,表示从一个国家到另一个国家的贸易流。虽然也可以使用第一个箭头坐标的偏移量来绘制第二个箭头,但我在代码中指定了第二个箭头的坐标。在代码中,mutation_scale属性用于控制箭头头部的长度,而linewidth属性用于控制主线的宽度。

最后,我在主图下方添加了水平颜色条。

ax = plt.axes(projection=ccrs.PlateCarree())

# get country centroids
ax.set_extent([lon[0] - 1, lon[1] + 1, lat[0] - 1, lat[1] + 1])

for key, cn in zip(c.keys(),c.values()):
    ax.add_geometries(cn.geometry,
                      crs=ccrs.PlateCarree(),
                      edgecolor="grey",
                      facecolor="whitesmoke",
                     zorder = 1)

    # Add country names
    centroid = cn.geometry.centroid

    ax.text(
        centroid.x,
        centroid.y,
        key,  # Assuming 'name' is the attribute containing the country names
        horizontalalignment='center',
        verticalalignment='center',
        transform=ccrs.PlateCarree(),
        fontsize=8,  # Adjust the font size as needed
        color='black',  # Set the color of the text
        zorder = 2
       )

# set up a colormap
cmap = colormaps.get("viridis_r")
tmin = np.array([v for v in transfers.values()]).min()
tmax = np.array([v for v in transfers.values()]).max()
norm = Normalize(tmin, tmax)

for tr in transfers:
    c1, c2 = tr.split(",")
    startarrow1 = startarrow1_dict[tr]
    endarrow1 = endarrow1_dict[tr]

    startarrow2 = startarrow2_dict[tr]
    endarrow2 = endarrow2_dict[tr]

    t1 = transfers[tr][0]
    col = cmap(norm(t1))

    # Use the arrow function to draw arrows
    arrow = mpatches.FancyArrowPatch(
        (startarrow1[0], startarrow1[1]),
        (endarrow1[0], endarrow1[1]),
        mutation_scale=20,    #control the length of head of arrow 
        color=col,
        arrowstyle='-|>',
        linewidth=2,  # You can adjust the linewidth to control the arrow body width
        zorder = 3
    )
    ax.add_patch(arrow)

    #OTHER WAY
    offset = 1
    t2 = transfers[tr][1]
    col = cmap(norm(t2))
    arrow = mpatches.FancyArrowPatch(
        (startarrow2[0], startarrow2[1]),
        (endarrow2[0], endarrow2[1]),
        mutation_scale=20,
        color=col,
        arrowstyle='-|>',
        linewidth=2,  # You can adjust the linewidth to control the arrow body width
        zorder = 4
    )
    ax.add_patch(arrow)

sm = ScalarMappable(norm, cmap)
fig = plt.gcf()
cbar = fig.colorbar(sm, ax=ax,
            orientation = "horizontal",
            pad = 0.05,  #distance between main plot and colorbar
            shrink = 0.8, #control length
            aspect = 20  #control width
            )
cbar.set_label("Trade flow")

plt.title("Trade flow in South Asia")
plt.axis("off")

plt.savefig("trade_flow2_with_labels.jpeg",
           dpi = 300)
plt.show()

下面展示了最终产品。在我的虚拟数据集中,贸易流最少的是从斯里兰卡到印度的出口(53 单位),用黄色表示。贸易流最多的是从孟加拉国到尼泊尔的出口(98 单位),用紫色表示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

通过箭头表示的国家间双向贸易流。图像由作者提供。

结论

在这篇文章中,我展示了如何通过使用两个箭头在 Python 地图中可视化国家之间的贸易流,包括出口和进口关系。我使用了 cartopy 和 matplotlib 包来实现这一目的。在本系列的第二部分,我将展示如何可视化“净”贸易流关系,并突出显示净出口国和净进口国。

本帖的笔记本可以在这个 GitHub 仓库中找到。感谢阅读!

参考文献

Google Developers, 2023. KML 教程 | Keyhole 标记语言 | Google 开发者。本页面的内容根据知识共享署名 4.0 国际许可证进行许可。

vLLM:PagedAttention 实现 24 倍更快的 LLM 推理

原文:towardsdatascience.com/vllm-pagedattention-for-24x-faster-llm-inference-fdfb1b80f83

在推理过程中,更高效地计算 Transformer 的注意力

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Benjamin Marie

·发表于 Towards Data Science ·6 分钟阅读·2023 年 6 月 24 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

PagedAttention 用于提示“the cat is sleeping in the kitchen and the dog is”。用于注意力计算的键值对张量存储在映射到 GPU 内存中非连续块的虚拟连续块中。— 图片由作者提供

几乎所有的大型语言模型(LLM)都依赖于 Transformer 神经架构。虽然这一架构因其高效性而受到赞誉,但也存在一些众所周知的计算瓶颈。

在解码过程中,一个瓶颈是在计算每个输入标记的键值对张量的注意力。这些张量必须全部存储在内存中。

注意:本文不会解释这些键值对的作用。这是 Transformer 架构中最复杂且最有趣的方面之一。如果你对此不了解,我强烈建议阅读 Jay Alammar 的《图解 Transformer》

随着 LLM 接受越来越长的输入,例如,LLM Claude 接受 100k 标记长的输入,这些张量消耗的内存可能变得非常大。

盲目地将所有这些张量存储在内存中会导致内存过度预留和碎片化。这种碎片化会使内存访问变得非常低效,特别是对于长序列的标记。至于过度预留,系统这样做是为了确保为张量分配足够的内存,即使它没有完全使用这些内存。

为了缓解这些问题,UC Berkeley 提出了 PagedAttention。

PagedAttention 被实现于 vLLM(Apache 2.0 许可),该项目由 LMSYS 部署,LMSYS 是一个由 UC Berkeley 的学生和教师以及 UCSD 和 CMU 的帮助下成立的开放研究组织。

在这篇文章中,我解释了什么是 PagedAttention 以及它为何显著加速解码。我将在文章的最后部分展示如何开始使用 vLLM 利用 PagedAttention 来进行推理和在你的计算机上服务 LLMs。

Transformer 的 PagedAttention

Kwon 等人 (2023) 提出了 PagedAttention。

目标是在 GPU VRAM 的不连续空间中更高效地存储键值张量。

简而言之,PagedAttention 背后的思想是创建映射到 GPU 内存中物理块的连续虚拟块。

每个块被设计用来存储预定义数量标记的键值对张量。所有块在虚拟上是连续的,并且映射到物理上不连续的块,这些块在推理过程中按需分配,在碎片化的 GPU 内存中。内存中还创建了一个简单的索引表,以将虚拟块与物理块关联起来。

PagedAttention 的内核根据需要提取这些块。这是高效的,因为系统由于块的大小限制而提取较少的键值张量。

让我们用以下提示进行说明:

猫正在厨房里睡觉,而狗则

我们为每个标记提供了键值张量。使用 PageAttention,我们可以(任意地)将块大小设置为 4。每个块包含 4 个键值张量,除了最后一个块,只包含 3 个键值张量。块在虚拟上是连续的,但在 GPU 内存中不一定是连续的,如本文引言中的图所示。

在注意力计算中,对于每个查询标记,系统逐个提取块,如下所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

说明包含最多 4 个标记的虚拟块 —— 作者提供的图像

通过按块提取键值张量,而不是整个张量序列,注意力计算要快得多。

推理的并行采样

PagedAttention 的另一个优势是虚拟块在推理时可以共享。所有通过采样或束搜索并行生成的序列可以使用相同的虚拟块,从而避免重复。

在他们的实验中,LMSYS 观察到束搜索解码的内存使用减少了 55%。

LMSYS 报告的 PagedAttention 性能

在我们亲自尝试之前,先看看作者(UC Berkeley/LMSYS)在使用 vLLM 中实现的 PagedAttention 与 Hugging Face 开发的文本生成推理库相比的性能报告。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

LLaMa 模型在输出完成任务中的性能,包括原始 Hugging Face 库 (HF)、文本生成推理库 (TGI) 和使用 PagedAttention (vLLM) 的 vLLM —— 由 UC Berkeley 和 LMSYS 绘制的图

根据这些结果,vLLM 看起来要快得多,特别是在多次输出完成的情况下。TGI 和 vLLM 之间的差距在更大的模型中会增加。这是预期中的,因为更大的模型需要更多内存,因此更容易受到内存碎片化的影响。

总体而言,vLLM 的速度比 Hugging Face Transformers 库快多达 24 倍。

注意:实际上,我对 HF 到 TGI 的改进也感到很惊讶。我还没有在我的博客上覆盖 TGI,但我可能会写一篇关于它的指南。TGI 在 Hugging Face 的生产环境中使用。虽然它似乎比 vLLM 慢很多,但 TGI 还有其他优点,比如支持更多的模型和功能。

如何在你的计算机上设置 vLLM

注意:vLLM 还不支持 CUDA 12。请使用较低版本,例如 11.8。

在本节中,我只会讲解如何在你的计算机上设置和运行 vLLM 的基本步骤。有关更高级的用法,你可以查看 vLLM 文档

当我写这篇文章时,vLLM 仅支持几种类型的模型

  • GPT-2

  • 基于 GPT-NeoX 和 Pythia

  • 基于 LLaMa

  • 基于 OPT

你可以通过遵循 这些说明 来添加对其他模型的支持。

在下面的代码中,我使用 Dolly V2(MIT 许可证)。这是一个基于 Pythia 的聊天模型,由 DataBricks 训练。

我选择了 最小的 30 亿参数版本。它可以在配备 24 GB VRAM 的消费级 GPU 上运行,例如 nVidia RTX 3080/3090。

安装 vLLM 最直接的方法是使用 pip:

pip install vllm

注意:这可能需要最多 10 分钟。

但在我的情况下,无论是在我的计算机上还是 Google Colab 上,pip 都无法安装 vllm 库。vLLM 的作者确认某些 nvcc 版本和环境存在问题。然而,对于大多数配置,pip 应该能够顺利安装 vLLM。

如果你和我处于相同的情况,解决方法就是使用 Docker 镜像。这一个对我有效:

docker run --gpus all -it --rm --shm-size=8g nvcr.io/nvidia/pytorch:22.12-py3

注意:进入 docker 后,作者建议在安装 vLLM 之前卸载 Pytorch:pip uninstall torch。然后,“pip install vllm” 应该可以正常工作。

然后,我们可以开始编写 Python 代码。

我们首先需要导入 vllm,然后用 vllm 加载模型。推理通过 llm.generate() 触发。

from vllm import LLM

prompts = ["Tell me about gravity"] #You can put several prompts in this list
llm = LLM(model="databricks/dolly-v2-3b")  # Load the model
outputs = llm.generate(prompts)  # Trigger inference

你也可以使用 vLLM 来服务 LLM。它的工作方式类似于 TGI。它也比 我在之前文章中描述的 NVIDIA Triton 推理服务器 更加简单。

你首先需要启动服务器:

 python -m vllm.entrypoints.openai.api_server --model databricks/dolly-v2-3b

注意:服务器将监听 8000 端口。确保该端口可用或在 vLLM 配置文件中更改它。

然后,你可以使用以下提示查询服务器:

curl http://localhost:8000/v1/completions \
    -H "Content-Type: application/json" \
    -d '{
        "model": "databricks/dolly-v2-3b",
        "prompt": "Tell me about gravity",
        "max_tokens": 200
    }'

就这样!你在计算机上运行了一个非常高效的 LLM 服务器。

结论

PagedAttention 显著加速了推理。这是向更实惠的 AI 迈出的另一大步。

在进一步的实验中,我确认 vLLM 在处理批量提示时特别高效。为了充分利用 vLLM,请考虑优化你的推理批处理策略。

尽管大束的束搜索可能在标准注意力计算中是不可行的,但使用 PagedAttention 的束搜索更快且内存更高效。

我的一个下一个实验是将 PagedAttention 与 QLoRa 结合,以减少内存使用。这应该很简单。它将使在消费级硬件上运行 LLM 更加高效。

如果你喜欢这篇文章并且有兴趣阅读接下来的文章,支持我工作的最佳方式是通过这个链接成为 Medium 会员:

[## 通过我的推荐链接加入 Medium - 本杰明·玛丽

作为 Medium 会员,你的一部分会员费将会转到你阅读的作家那里,而且你可以全面访问每一篇故事…

medium.com](https://medium.com/@bnjmn_marie/membership?source=post_page-----fdfb1b80f83--------------------------------)

如果你已经是会员并且想要支持这项工作, 只需在 Medium 上关注我

语音助手的可访问性

原文:towardsdatascience.com/voice-assistant-accessibility-dc737cde0394?source=collection_archive---------12-----------------------#2023-03-31

确保每个人都能被理解

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 安格斯·阿德尔斯

·

关注 发布于 Towards Data Science ·10 分钟阅读·2023 年 3 月 31 日

我多年来一直从事使用大型语言模型(如 GPT-4)的对话式 AI 工作。由于 chatGPT 的流行大爆发,令人非常兴奋,但它们如何改进呢?当然,对于这个问题有很多答案,但在这篇文章中,我将重点关注可访问性。

我们如何调整未来的机器学习模型以利用有限的数据集?我们应该如何设计我们的代理以确保每个人都能利用语音人工智能的进步?

这是对 IWSDS 2023上发表的论文的缩写。如果你想引用本文中讨论的内容,请引用标题为“语音助手的可访问性”的论文。

**Harvard**:
Addlesee, A., 2023\. Voice Assistant Accessibility. Proceedings of the 13th International Workshop on Spoken Dialogue Systems Technology (IWSDS).

**BibTeX**:
@inproceedings{addlesee2023voice,
  title={Voice Assistant Accessibility},
  author={Addlesee, Angus},
  journal={Proceedings of the 13th International Workshop on Spoken Dialogue Systems Technology (IWSDS)},
  year={2023}
}

全球有超过十亿人生活在某种形式的残疾中,而语音助手有可能改善人们的生活。例如,在我访问一个名为Leuchie House的休养院时,一位多发性硬化症(MS)患者解释了这种疾病的进展如何逐渐侵蚀了他们的独立性。一台亚马逊 Alexa 设备使这位患者能够在无需护理人员帮助的情况下关闭卧室灯。他们告诉我们,这是自诊断以来,他们第一次重新获得一些个人独立性。

像上面这样的故事激励着慈善机构推广语音助手的使用,因为它们能产生 真实的积极影响*。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我(右侧)于 2021 年 9 月与同事们访问了Leuchie House

语音助手的创作者正在获得 HIPAA 合规认证,以便在医疗保健领域进一步应用,并发布了专门针对脆弱用户群体的功能。我们同样看到早期研究者与其他学科合作,将他们的工作应用于更具体的医疗保健应用(请参见我关于语音研究趋势的 TDS 文章)。

语音助手的可及性因此至关重要,以确保未来系统在设计时考虑到最终用户的互动模式和需求。今天的语音助手在大量代表“平均”用户的数据集上进行训练和评估,但我们知道随着认知能力的下降,语音会发生变化。现有的商业系统在帮助视力障碍者方面存在巨大的隐私问题,而人们在公共场所与语音助手互动时需要公开宣布自己的残疾(下文讨论)。

随着研究和工业界推动语音助手的巨大利益超越大众市场应用,新的挑战和伦理问题也随之出现,这些问题必须被突出讨论。在本文中,我探讨了最先进的研究和当前可用的商业系统,针对多个用户群体,例如痴呆症患者、运动神经元病患者、视力丧失者、心理健康状况患者等。

我首先讨论了为认知障碍和心理健康状况人群设计语音助手,其次讨论了为身体残疾者设计系统,然后讨论了公共环境中的语音隐私,最后总结。

针对认知障碍和心理健康状况的对话系统

认知障碍影响记忆、注意力、解决问题的能力、决策、言语产生等。认知障碍的发生和进展通常与个人的年龄相关,但某些病症(如早发性痴呆)可能由中风或头部创伤引起。轻度认知障碍(MCI)具有上述症状,但不会显著干扰个人的生活。虽然 MCI 患者通常也是老年人,但另一个影响所有年龄段的脑健康子集是心理健康状况。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

下文提到的 SPRING 项目中使用的 ARI 机器人(由 PAL Robotics 生产)。

痴呆症和 MCI

随着认知能力的下降,人们在句子中途的停顿变得更频繁且时间更长。其他言语产生的变化也包括:增加重复、说话速度减慢、介词使用增多,等等。如今的语音助手常常将这些长时间的停顿误认为是用户发言的结束,迫使用户不得不挫败地重复整个陈述。

推荐痴呆症和 MCI 患者使用标准语音助手(如 Google Home 和 Amazon Alexa)进行日常使用。像 CogniHealth 这样的公司致力于策划内容,以帮助痴呆症患者及其家人——通过语音助手提供有价值的信息、建议和支持——但这些系统的语音处理和理解组件没有针对上述特定挑战的无障碍选项。

由于数据不足,该领域的研究受到限制。收集自然语言对话数据,尤其是与脆弱的老年人进行对话,在伦理上具有挑战性,特别是近年来由于 COVID,需要 定制工具 来保障数据安全。像 EU H2020 SPRING 项目 这样的研究正在医院记忆门诊候诊室中收集数据,以应对这些挑战。在这种环境下,患有痴呆症或 MCI 的患者通常会有家庭成员或看护者陪同,带来更多的多方复杂情况(剧透:这是我下一篇文章的主题)。数据收集后,可以利用 TalkBank 的子库 DementiaBank 将数据与其他研究痴呆症交流的研究人员共享。

心理健康状况

某些心理健康状况也会影响人们的语言表达和行为。焦虑症患者的语速较快,停顿时间较短,而抑郁症或创伤后应激障碍(PTSD)患者说话较慢且沉默较多。

目前似乎没有商业语音助手能够调整其语音处理以更好地理解心理健康状况的人——尽管存在针对这些用户群体的公司。像UB-OKKindspace这样的语音助手应用提供了一个安全的空间,让人们可以在没有评判的情况下分享他们的忧虑。人们,特别是年轻人,可以询问关于心理健康和其他他们可能不愿向朋友、老师或家人提问的问题。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Michael McTernan 在 2019 年苏格兰 Edge Awards 上展示 UBOK

人机交互(HRI)研究在这一领域十分丰富。系统的语音处理仍然保持标准,但机器人的交互方式有所调整。例如,可以借鉴心理学的沟通技巧来促进自我反思,并帮助缓解孤独(这与抑郁症常常伴随)。

应用范围确实非常广泛。自闭症的语言能力较弱的儿童使用了语言生成设备,长期使用后这些设备鼓励他们自发说话并使用新词汇。类似的工作有效地利用互动社交机器人进行自闭症治疗。

上述所有内容都令人兴奋,但关键是,没有研究专注于改善系统的语音处理和理解*。*

身体障碍人士设计对话系统

身体障碍会影响用户可能向语音助手提出的问题以及在收到回应后如何采取行动。例如,视力受损的人会询问他们的视觉环境,而有听力困难的人则会期待多模态的回应。

视觉障碍

盲人和视力受限的人往往会遭受似乎无关的健康问题,如营养不良。这主要是因为他们在完成日常任务(如购物、准备食物和烹饪)时遇到困难。一组视觉障碍计算机科学家在计算机视觉与模式识别会议(CVPR 2020)上讨论了在看不到时刻表、站台号、车厢字母或方向标志时,如何在火车站内导航的困难。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

类似于火车站,人们会询问有关食品包装上的文本信息。这张图片来自一篇关于厨房中这一问题的文章 (here)。

也有“人机协作”解决方案,你可以联系有视力的人来回答问题。必须同时发送照片或视频以传达视觉场景。 BeMyEyesBeSpecular 依赖视力正常的志愿者及时回答问题,而 Aira 则有一支经过培训的专业团队。

当依赖志愿者时,一个明显的问题是:视力受限的人无法知道发送的图像中是否能看到敏感信息。因此,志愿者可能会看到邮件中有姓名、地址、身份证号码或个人家中的贵重物品。Aira 通过招聘和培训专注于安全和保障的工作人员来缓解这个问题,但这需要付出代价。

也存在端到端 (E2E) 系统,如 TapTapSeeMicrosoft Seeing AI。这些系统在云端安全运行,并加密处理,因此隐私问题最小,但它们引入了一个新的问题——准确性。视力受限的人无法验证系统的回答是否正确。由于无法进行对话,用户只能依赖系统的回答。这可能导致像药物问题这样的困境。E2E 系统可能不正确,可能对用户造成伤害,但人机协作系统需要他们将药物照片发送给未知的志愿者。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这张图片展示了尝试回答视力障碍者问题的一些工作实例。目标是让用户知道系统何时不确定 (here)。

有限行动能力

家庭开放域语音助手非常方便:我们可以在手上沾满油的时候设定定时器,或者在沙发上舒适的温暖毯子下调高音乐。

这些功能不仅对行动不便的人很方便,它们对心理健康至关重要。手部、手臂或腿部有行动限制的人可以通过语音保持一定的个人独立性。

移动能力丧失本身通常不会影响语言生成,但语音助手仍可以设计得更具可及性。虽然存在提高人们舒适度和完成日常任务能力的硬件(如先进的轮椅),但这些技术目前尚未与现有的语音助手整合。在访问一个名为Leuchie House的临时护理之家时,一位居民描述了他们的沮丧,语音助手可以打开窗帘和关闭电视,但他们不得不请护理人员调整电动轮椅的靠背。这突显了包容性设计的必要性。

听力障碍

有听力困难的人对语音助手感到沮丧,从而完全放弃使用它们。对于那些在年轻时就出现听力问题的人,这种情况更加严重,因为这往往会导致语言障碍。例如,如果听不到对话,就无法学习发音。下面将讨论语言障碍的影响,但即使没有这些障碍,现有的语音助手似乎也存在局限。研究指出,有部分听力丧失的人在嘈杂环境中(如公共场所)很难跟上对话,但当设置了实时转录对话的屏幕时,他们在对话中感到更有融入感。实时说话者识别将更为有效。因此,我们应确保语音助手和社交机器人在公共多方环境中包含屏幕,以实现这一功能。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

本田的 Asimo 机器人用手语说“我爱你”(来源

许多听力障碍者懂得大约 200 种手语中的一种。研究表明,助手可以学习手语,但 NLP 社区的更多努力可以利用现有资源来改进手语处理和生成。听力障碍者渴望参与语音助手的包容性设计,以确保无障碍进展。

语音多样性

语言生成具有细微差别且因人而异,但自动语音识别(ASR)学习的是一般语音模式,因此难以理解非母语者或口音很重的人(例如苏格兰人)。然而,这一问题还进一步扩展。有口吃的人被误解,有发音困难的人(如因早期听力丧失导致)被误解,还有图雷特综合症患者被排除在对话研究之外。非标准语音也可能由影响我们用来生成语言的肌肉的疾病引起,例如肌营养不良症。

谷歌在这方面通过三个项目进行创新。项目 Euphonia项目 Relate 是谷歌的举措,旨在帮助那些说话方式不标准的人更好地被理解,项目 Understood 是他们的计划,旨在更好地理解唐氏综合症患者。谷歌甚至开设了 无障碍发现中心 来与学术界、社区和慈善/非营利组织合作,旨在“消除无障碍障碍”。

项目 Relate

言语丧失

像运动神经元病(MND)这样的某些疾病患者会逐渐失去完全说话的能力。斯蒂芬·霍金因 MND 使用了合成语音,但今天在质量和多样性方面已经有了巨大的改善。像 Cereproc 这样的公司合成具有个性、引人入胜和情感化的语音,带有不同的口音,帮助 MND 患者选择最能代表自己的声音。

语音克隆技术也变得可能,这使得面临失语风险的人能够使用语音银行技术。人们录制自己的语音以便在需要时进行克隆。像 SpeakUnique 这样的公司,甚至可以在语音在诊断后部分退化的情况下,重新构建一个人的原始声音。

隐私

个人语音助手通常仅由单一用户在私人空间中使用。因此,语音助手可以高度定制以满足该用户的需求。然而,这在公共空间中的助手并非如此。博物馆、医院、机场等公共场所的社交机器人每天会被许多人使用。一些无障碍设计的实现惠及所有人(例如,我们有时在句子中会忘记词汇),但其他则不然。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一个购物中心的 Pepper 机器人(来源)。人们在公共场所与其他人一起时,可能会感到不舒服地披露个人无障碍需求。

大多数残疾是看不见的,因此人们必须在公共场所大声描述自己的残疾,以激活某些无障碍功能。这在没有语音助手的情况下同样成问题——残疾人士通常需要在商店里宣布自己的残疾和帮助需求。

Neatebox 这样的技术正在迅速普及,以解决这一问题。残障用户可以下载应用程序并注明他们希望如何得到个人化帮助(例如,被手臂引导)。然后,当他们进入商店或机场时,客户服务团队会收到通知并提供个性化帮助。类似的技术也可以用于公共空间的社交机器人,当与需要可访问语音助手的人互动时,激活特定功能。

结论

语音助手可以超越简单的便利来改善人们的生活,这可以通过道德的数据收集和包容性设计来实现。然而,这并不简单,每个语音助手系统组件在设计针对所有人的系统时都必须被考虑在内。

我已经总结了为了使语音助手对本文讨论的用户群体更具可访问性,必须调整的具体组件:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

论文 的表格 1

你可以通过 MediumTwitterLinkedIn 联系到我。

Voronoi 网格:一种实际应用

原文:towardsdatascience.com/voronoi-grids-a-practical-application-7e6ee3b1daf0

快速成功数据科学

在墨尔本,澳大利亚,绘制学校区域

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 李·沃恩

·发表于 走向数据科学 ·10 分钟阅读·2023 年 11 月 28 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

被 Leonardo.ai DreamShaper v7 设想为彩绘玻璃窗的墨尔本

Voronoi 网格,也称为 Voronoi ,用于将平面划分为围绕一组给定种子点的离散区域。对于每个种子点,都有一个对应的区域,称为 Voronoi 单元,其中平面上的所有点都比其他点更接近种子点。

Voronoi 图在许多领域都有应用,包括计算机科学、地理学、生物学和城市规划。一个特别重要的应用是绘制需要紧急降落的飞机的最近机场。

澳大利亚墨尔本政府使用该工具制作学校学区地图。“学区”指的是那些居住在特定区域内并且被保证入读特定学校的学生。由于学生有资格就读离家最近的小学或中学——以欧几里得距离为标准——学校区域地图默认是一个 Voronoi 图。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

墨尔本学校学区地图 (维多利亚州教育部,CC-BY 4.0)

在这个快速成功数据科学项目中,我们将通过制作自己版本的墨尔本学区地图来深入了解 Voronoi 图的概念。我们将使用大都市区的小学子集,并用 SciPy 库的Voronoi类对其进行网格化。然后,我们将使用 Folium 库将 Voronoi 图叠加在墨尔本的街道地图上。

数据集

为了生成数据集,我使用了维多利亚政府的Find My School网站查找墨尔本大都市区 109 所小学的地址。然后使用LatLong.net将地址转换为十进制度数,并将结果存储在这个GitHub Gist的 CSV 文件中。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

该项目使用的墨尔本地区小学位置(由作者提供)

SciPy Voronoi 实现

Python 的SciPy科学库旨在数学、科学和工程领域,解决科学计算中的许多标准问题。它基于并补充了Numerical Python (NumPy),并提供了许多用户友好且高效的数值例程。

为了制作 Voronoi 图,SciPy 提供了scipy.spatial.voronoi()类,该类使用Qhull 库来计算 Voronoi 网格。如前所述,网格单元内的所有位置应该比其他种子点更接近生成该单元的种子点。

下图取自 SciPy 文档,种子点按正交方式排列,形成了一个简单的网格模式,其中种子点位于单元格的中心

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

正交 Voronoi 网格(来自 SciPy 文档)

种子点显示为蓝色。图中线条交汇的顶点为橙色。种子点之间的(线条)为黑色,这些脊构成了 Voronoi 单元的边界。

由于前图中的大多数种子点位于地图的边缘,只有中心种子点与有限的单元或区域相关联,因此它被有限的脊包围。其他脊为虚线,因为它们延伸到无穷大,从而界定了无限的单元,这些单元永远不会“闭合”,因为没有外部种子点来计算插入的脊。

下图也来自文档,由非正交排列的种子点构建,因此单元格更为复杂。请注意,一些脊用实线绘制,另一些则用虚线绘制。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

非正交 Voronoi 网格(来自 SciPy 文档)

实线绘制的脊在某处汇聚,形成一个封闭且有限的区域。虚线是发散的,永不相交,因此它们的单元具有无限的面积。在实际应用中,这些无限单元通常被忽略或人为地限制,比如使用地图边缘多边形。

我们可以通过 voronoi.regionsvoronoi.verticesvoronoi.ridge_verticesvoronoi.ridge_points 等属性访问区域(单元格)的列表和用于定义这些区域的坐标。我们稍后会用这些来创建可以绘制的 Voronoi 单元格多边形。

代码

下面的代码在 JupyterLab 中编写,遵循以下步骤:

  1. 加载学校名称和位置的 CSV 文件。

  2. 使用学校位置创建 Voronoi 网格。

  3. 将 Voronoi 网格转换为可以绘制的多边形。

  4. 使用方形边界框截断边缘单元格。

  5. 在墨尔本的街道地图上绘制学校和 Voronoi 网格。

导入库和准备数据

为了完成任务,我们需要 pandas、geopandas、shapely(与 geopandas 捆绑)、Folium 和 SciPy。pandas 和 geopandas 库允许数据的加载和处理,shapely 创建可绘制的多边形,Folium 允许映射,SciPy 提供构建 Voronoi 网格的算法。

如果你不熟悉,GeoPandas 是一个开源的第三方库,旨在支持 Python 中的地理空间映射。它扩展了 pandas 库使用的数据类型,使地理空间矢量数据的处理类似于表格数据的处理。它还使 Python 中的操作成为可能,而这些操作通常需要专用的地理空间数据库,如 Post GIS。

import pandas as pd
import geopandas as gpd
import folium
from shapely.geometry import Polygon
from scipy.spatial import Voronoi

# Load the school locations CSV file into a DataFrame:
df = pd.read_csv('https://bit.ly/3MYYegT')

# Create a GeoDataFrame with Point geometries:
gdf = gpd.GeoDataFrame(df, 
                       geometry=gpd.points_from_xy(df['Longitude'], 
                                                   df['Latitude']), 
                       crs='EPSG:4326')

导入必要库后,我们使用 pandas 读取 CSV 文件,并使用 geopandas 将学校坐标转换为 点几何

GeoDataFrame 是一个 pandas DataFrame,具有一个特殊的“几何”列用于位置数据。此列将几何对象类型(如点、线串、多边形等)与绘制它所需的坐标(经度和纬度)捆绑在一起。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

示例“几何”列(框选)来自 GeoDataFrame(作者的 Python Tools for Scientists)。

注意,在创建 GeoDataFrame 时,我们需要使用 crs=EPSG:4326 参数提供 坐标参考系统。“EPSG” 代表 “欧洲石油勘探组” 数据集,4326 代码由 全球定位系统GPS)使用。该系统将我们 3D 行星上的纬度和经度坐标投影到地图的平面表面上。你可以在 这里 阅读更多信息。

创建 Voronoi 网格

下一步是通过将 GeoDataFrame 的“经度”和“纬度”列传递给 SciPy 库的 Voronoi() 类来创建 Voronoi 网格。

# Create a Voronoi diagram using the GeoDataFrame.
# Use -1 to check for and exclude regions that extend to infinity:
vor = Voronoi(gdf[['Longitude', 'Latitude']])
voronoi_polygons = [Polygon(vor.vertices[region]) 
                    for region in vor.regions 
                    if region and -1 not in region]

# Create a GeoDataFrame with the Voronoi polygons:
gdf_voronoi = gpd.GeoDataFrame(geometry=voronoi_polygons, 
                               crs='EPSG:4326')

接下来,我们制作一个 Voronoi 多边形的列表,并删除空区域和延伸到无穷远的区域。这需要使用 Voronoi() 类返回的 vor 对象的 regions 属性。如果该属性以 -1 开头,那么我们知道该区域永远不会关闭。作为过程的一部分,我们将 vertices 属性传递给 shapely 的 Polygon() 类,该类生成可绘制的多边形,我们将其添加到新的 GeoDataFrame 中。

截断 Voronoi 单元

因为我们没有使用该区域内的所有小学,许多边缘单元将没有约束,可能会延伸到极其远的距离。为了处理这些情况,我们将创建一个边界框,并使用它来截断任何超出其范围的 Voronoi 单元(多边形)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

无约束的网格。天哪!(由作者提供)

# Define the bounding box lat-lon limits:
max_lat, min_lat, max_lon, min_lon = (-37.75, -37.9, 
                                      145.18, 144.84)

# Create the bounding box as a Shapely Polygon
bounding_box = Polygon.from_bounds(min_lon, min_lat, 
                                   max_lon, max_lat)

# Truncate each Voronoi polygon with the bounding box:
truncated_polygons = [polygon.intersection(bounding_box) for 
                      polygon in gdf_voronoi.geometry]

# Create a GeoDataFrame with the truncated polygons:
gdf_truncated = gpd.GeoDataFrame(geometry=truncated_polygons, 
                                 crs='EPSG:4326')

我们首先定义边界框的经纬度限制,使用比 CSV 文件中找到的值略大的值。然后再次调用 Polygon() 创建可绘制的多边形,然后使用列表推导式和 shapely 的 Polygon intersection() 方法来截断 GeoDataFrame 中的每个多边形。注意我们如何使用特殊的 geometry 列(gdf_voronoi.geometry)。最后,我们创建一个新的 GeoDataFrame 来保存截断后的多边形。

绘制地图

如果没有将其与现实世界关联的方式,Voronoi 图是无用的。因此,我们将使用 Folium 将其绘制在城市的街道地图上(通过选择 OpenStreetMap 作为 tiles 参数)。

我们还需要学校的位置和相关信息。这需要遍历 GeoDataFrame 并将每个标记添加到地图中。我们可以通过将 icon 参数设置为“家”符号来控制标记内部的图标。你可以在 Glyphicons 页面 上找到类似的图标列表。你也可以创建自定义图标,如 这篇文章 中所述。

# Create a Folium map centered on the average coordinates of the schools:
map_center = [gdf['Latitude'].mean(), gdf['Longitude'].mean()]
school_map = folium.Map(location=map_center, 
                        zoom_start=12, 
                        tiles='OpenStreetMap')

# Plot the truncated Voronoi polygons on the map:
folium.GeoJson(gdf_truncated).add_to(school_map)

# Add markers for each school:
for index, school in gdf.iterrows():
    folium.Marker(
        location=[school['Latitude'], school['Longitude']],
        popup=f"{school['School']}\n\
        {school['Street Address']}\n{school['Town']}",
        icon=folium.Icon(color='blue', icon='home')
    ).add_to(school_map)

# Save the map as an HTML file (optional):
# school_map.save('school_voronoi_map_truncated.html')

# Display the map in the notebook:
school_map

这是结果地图,允许缩放和平移。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

最终地图(由作者提供)

图示边缘的一些学校没有被着色,因为它们的“山脊”延伸到无穷远而没有收敛。这些可以通过将 Voronoi 单元与其他多边形合并来“修复”。我们稍后会讨论这个选项。

点击标记会弹出一个包含学校名称和地址的窗口。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

带有学校信息的弹出窗口(由作者提供)

要关闭多边形的蓝色填充颜色,只需在创建 folium.GeoJson 对象时修改 style_function 参数,如下所示。

folium.GeoJson(gdf_truncated, 
               style_function=lambda x: {'fillColor': 'none'}).add_to(school_map)

现在你只会看到单元边界。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

设置填充颜色为“无”的最终地图(由作者提供)

合并市政多边形

你可以在本文开头查看实际的墨尔本集水区地图,访问Find My School网站(点击学校图标以激活网格),或在mangomap.com上查看。

与我们的地图不同,真实的学校区域融合了 Voronoi 多边形与其他边界,例如 Port Philip 湾沿岸线。将所有这些附加边界纳入超出了本文的范围,但我们可以通过使用墨尔本市的市政边界多边形来了解其实现方法。

城市提供了一个shapefile格式的多边形。Shapefile 是一种地理空间矢量数据格式,用于地理信息系统(GIS)软件。尽管它的名字是“shapefile”,但它不是一个单一的文件,而是一个文件集合,位于一个文件夹中。你可以在这里了解更多信息。

要下载 shapefile,首先访问City of Melbourne’s Open Data site市政边界页面,然后导出 shapefile(作为“完整数据集”)。这些数据在CC Attribution 4.0 International license下共享。

将压缩文件夹存放在与你的 Python 脚本或笔记本相同的文件夹中,以便使用下面提供的代码。请勿解压缩文件夹。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

市政边界文件夹

这是剪裁并将现有多边形与市政边界合并的代码。它类似于用于边界框的剪裁过程,并假设你已经运行了之前的代码(针对在笔记本中运行代码的用户)。

# Read in shapefile of Melbourne City Limits as GeoDataFrame:
city_limits = gpd.read_file('municipal-boundary.zip')

# Truncate Voronoi polygons by the city_limits polygon:
truncated_polygons = [polygon.intersection(city_limits.geometry.iloc[0]) 
                      for polygon in gdf_voronoi.geometry]

# Create a GeoDataFrame with the truncated polygons:
gdf_truncated = gpd.GeoDataFrame(geometry=truncated_polygons, 
                                 crs='EPSG:4326')

# Create a Folium map centered on the average coordinates of the schools:
map_center = [gdf['Latitude'].mean(), gdf['Longitude'].mean()]
school_map = folium.Map(location=map_center, 
                        zoom_start=12, 
                        tiles='OpenStreetMap')

# Plot truncated Voronoi polygons on the map:
folium.GeoJson(gdf_truncated).add_to(school_map)

# Add markers for each school:
for index, school in gdf.iterrows():
    folium.Marker(
        location=[school['Latitude'], school['Longitude']],
        popup=f"{school['School']}\n\
        {school['Street Address']}\n{school['Town']}",
        icon=folium.Icon(color='blue', icon='home')
    ).add_to(school_map)

# Display map in notebook:
school_map

新地图,如下所示,只包含墨尔本市界内的 Voronoi 单元。许多单元边界现在符合与 Voronoi 无关的特征,如 Yarra 河、码头和主要道路。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

剪裁到墨尔本市政边界的 Voronoi 多边形(作者提供)

一些单元与其各自的学校隔离。这是因为墨尔本市政多边形实际上并未用于定义学校区域。我们在这里使用它来演示如何在你拥有所有正确的多边形集合后创建最终地图。如你所见,这是一个简单的过程。

摘要

Voronoi 图是一种最近邻映射,让你将平面用n个生成点划分为n个凸多边形。每个多边形包含一个生成点,并且每个位置在给定的多边形中距离其生成点比距离其他任何生成点都近。

在这个例子中,我们使用 Voronoi 图将澳大利亚墨尔本的地图划分为学校招生区域。每个区域的学生离关联的主要学校比其他任何城市中的主要学校都要近。由于我们没有使用所有学校或包含所有修改多边形(如河流和海岸线),因此这张地图与实际情况有所不同。

Python 的 SciPy 库包含生成 Voronoi 网格的内置功能。借助额外的第三方库,如 geopandas 和 Folium,你可以将这些网格投影到地图上,用于实际应用。

进一步阅读

如果你想了解更多关于 Voronoi 图的信息,可以查看 Francesco Bellilli 的这篇信息丰富的文章。

对 Voronoi 图的迷人世界 [## Voronoi 图的迷人世界

关于这个普遍模式及其应用的简要介绍

对 Voronoi 图的迷人世界

谢谢!

感谢阅读,请关注我,了解未来更多快速成功的数据科学项目。

Vosk:高效企业级语音识别的评估与实施指南

原文:towardsdatascience.com/vosk-for-efficient-enterprise-grade-speech-recognition-an-evaluation-and-implementation-guide-87a599217a6c

对实现和评估基于 Vosk 的语音识别系统的全面指南

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Luís Roque

·发表于 Towards Data Science ·9 分钟阅读·2023 年 5 月 15 日

引言

在我们最新的文章中,我们广泛地使用了不同的模型和方法来进行语音识别。开源社区在过去几年中一直在开发这方面的解决方案,给我们提供了许多选择。我们尝试了 Whisper、WhisperX 和 Whisper-JAX。它们都基于最近由 OpenAI 训练并开源的 Whisper 模型。它是一个在多语言和多任务上训练的自动语音识别(ASR)系统,使其在语音甚至语言任务中都具有通用性。

Whisper 的一个缺点是运行所需的资源,特别是在较长音频文件的执行时间方面。在本文中,我们将引导你通过使用 Vosk,一个开源离线语音识别工具包,来开发企业级语音识别模型。这些模型显著更快,在许多使用案例中,这是一个决定性因素。

我们深入探讨了四个 Vosk 语音识别模型的性能,突出了它们在准确性、执行时间和模型大小方面的优缺点。我们的发现揭示了有趣的见解,比如从较小的模型过渡到更大的模型时准确性提高了 20%。我们还揭示了速度和准确性之间的权衡。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1:语音转文本使我们更接近机器(来源

本文属于“大型语言模型编年史:探索 NLP 前沿”这一新周刊系列文章,将探讨如何利用大型模型的力量来处理各种 NLP 任务。通过深入研究这些前沿技术,我们旨在帮助开发者、研究人员和爱好者掌握 NLP 的潜力,开启新的可能性。

到目前为止发表的文章:

  1. 使用 ChatGPT 总结最新的 Spotify 发布内容

  2. 在大规模下掌握语义搜索:使用 FAISS 和 Sentence Transformers 索引数百万文档,实现闪电般快速的推理时间

  3. 释放音频数据的力量:使用 Whisper、WhisperX 和 PyAnnotate 进行高级转录和分段

  4. Whisper JAX 与 PyTorch:揭示 GPU 上 ASR 性能的真相

一如既往,代码可在我的 Github 上找到。

理解语音识别

语音识别是使计算机和其他设备能够理解和处理人类语音的技术,将口语转换为书面文本。这些系统学习将语音转换为文本的过程包括几个步骤。它们自动提取原始音频波形,实质上是音频信号的声学特征。然后,它们将这些特征映射到音素,并使用语言模型将音素映射到单词。更先进的系统还结合了上下文信息和语义理解,以精炼转录结果。

语音识别面临着许多挑战,因为人类语音的复杂性以及引入变异性和模糊性的因素。一些挑战包括说话人变异、背景噪声、言语流畅性问题和填充词、以及同音词和共鸣现象。说话人变异源于不同说话人的独特口音、说话风格和发音。背景噪声,如嘈杂环境中的噪音,会干扰语音信号的清晰度,对准确识别构成挑战。言语流畅性问题和填充词是另一个障碍,因为在自然语言中常出现犹豫、重复和填充词(例如“呃”和“嗯”)。最后,同音词和共鸣现象带来额外的障碍。一些单词发音相同但含义不同(同音词)。另一些单词的发音可能会根据其在句子中的位置而改变(共鸣现象)。所有这些因素都为识别任务引入了模糊性,使得我们的模型在某些任务中更难准确执行。

《构建企业级语音识别模型的实用指南》

我们利用 Vosk 的强大功能,这是一个开源的离线语音识别工具包,用于构建自定义语音识别系统。Vosk 提供了一个灵活高效的解决方案,能够在包括 Android、iOS、Windows、Linux 和 Raspberry Pi 等各种平台上实现语音识别。其关键特性包括支持多种语言、说话人识别、与小型设备兼容以及大规模服务器部署。

在接下来的部分中,我们将指导您如何使用 Vosk 创建语音识别系统。我们将讨论流行的框架和库,概述数据收集、预处理和模型训练的步骤,并提供针对特定用例的模型微调技巧。

数据收集

为了评估不同的模型,我们将使用 LibriSpeech ASR 语料库数据集(CC BY 4.0)。该数据集包括大约 1000 小时的朗读英语语音录音。LibriSpeech 数据集非常适合用来评估语音识别模型,因为它提供了多种口音、说话风格、发音和背景噪声。正如我们之前讨论的,这些因素通常会对模型的表现产生重大影响。我们希望测试 Vosk 是否能够在这些具有挑战性的环境中有效工作。

转录与单词表示

Vosk API 非常丰富;我们可以访问每个转录单词的几个属性。它返回以下属性:

  • conf:识别单词的置信度,范围从 0 到 1。

  • start:发音开始时间,以秒为单位。

  • end:发音结束时间,以秒为单位。

  • word:识别出的单词。

为了简化我们的过程,我们创建了一个 WordVosk 类,用于表示 Vosk API 返回的每个单词:

class WordVosk:
    """A class representing a word from the JSON format for Vosk speech recognition API."""

    def __init__(self, conf: float, start: float, end: float, word: str) -> None:
        """
        Initialize a Word object.

        Args:
            conf (float): Degree of confidence, from 0 to 1.
            start (float): Start time of pronouncing the word, in seconds.
            end (float): End time of pronouncing the word, in seconds.
            word (str): Recognized word.
        """
        self.conf = conf
        self.start = start
        self.end = end
        self.word = word

    def to_dict(self) -> Dict[str, Union[float, str]]:
        """Return a dictionary representation of the Word object."""
        return {
            "conf": self.conf,
            "start": self.start,
            "end": self.end,
            "word": self.word,
        }

    def to_string(self) -> str:
        """Return a string describing this instance."""
        return "{:20} from {:.2f} sec to {:.2f} sec, confidence is {:.2f}%".format(
            self.word, self.start, self.end, self.conf * 100
        )

    def to_json(self) -> str:
        """Return a JSON representation of the Word object."""
        return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True)

由于我们主要关注获取转录,我们创建了一个 Transcription 类,该类接受一个 WordVosk 对象的列表。to_raw_text 方法生成最终的原始转录:

class Transcription:
    def __init__(self, words: List[WordVosk]) -> None:
        self.words = words

    def to_dict(self) -> List[Dict[str, Union[float, str]]]:
        """Return a dictionary representation of the Transcription object."""
        return [word.to_dict() for word in self.words]

    def to_raw_text(self) -> str:
        """Generate raw transcription text from the list of WordVosk objects."""
        return " ".join(word.word for word in self.words)

这些类有助于更高效地管理 Vosk 语音识别 API 的输出,从而更方便地处理转录结果。

实现主模型类

ModelSpeechToText 类旨在使用 Vosk API 处理语音转文本转换。它接受一个音频文件和一个 Vosk 模型作为输入,并返回一个转录后的单词列表。

class ModelSpeechToText:
    def __init__(self, audio_path: str, model_path: str) -> None:
        self.audio_path = audio_path
        self.wf = wave.open(self.audio_path)
        self.model = Model(model_path)

speech_to_text 方法使用 Vosk API 将语音转录为文本。它按块读取音频文件,并使用 KaldiRecognizer 处理每块。转录结果被收集到 results 列表中。

 def speech_to_text(self) -> List[Dict[str, Any]]:
        """Transcribe speech to text using the Vosk API."""
        rec = KaldiRecognizer(self.model, self.wf.getframerate())
        rec.SetWords(True)

        results = []
        frames_per_second = 44100
        i = 0
        print("Starting transcription process...")
        while True:
            data = self.wf.readframes(4000)
            i += 4000
            if len(data) == 0:
                break
            if rec.AcceptWaveform(data):
                part_result = json.loads(rec.Result())
                results.append(part_result)
            if i % (frames_per_second * 60) == 0:
                print(f"{i / frames_per_second / 60} minutes processed")
        part_result = json.loads(rec.FinalResult())
        results.append(part_result)
        self.wf.close()
        print("Transcription process completed.")
        return results

results_to_words 方法接受转录结果,并将其转换为 WordVosk 对象的列表。这个方法使我们能够轻松地处理和处理转录后的单词。

 @staticmethod
    def results_to_words(results: List[Dict[str, Any]]) -> List[WordVosk]:
        """Convert a list of Vosk API results to a list of words."""
        list_of_words = []
        for sentence in results:
            if len(sentence) == 1:
                continue
            for ind_res in sentence["result"]:
                word = WordVosk(
                    conf=ind_res["conf"],
                    start=ind_res["start"],
                    end=ind_res["end"],
                    word=ind_res["word"],
                )

                list_of_words.append(word)
        return list_of_words

ModelSpeechToText 是我们高效使用 Vosk API 所需的最终抽象。它允许我们对音频文件进行转录,并获得转录后的单词列表,这些单词以 WordVosk 对象的形式表示。

使用字错误率评估语音识别模型

要评估语音识别模型,我们可以使用字错误率(WER)指标。它是将假设转录转换为参考转录所需的插入、删除和替换的平均数量,除以参考转录中的总单词数。WER 值越低,表示性能越好。

calculate_wer 函数计算参考转录和假设转录之间的平均 WER:

def calculate_wer(
    reference_transcriptions: List[str], hypothesis_transcriptions: List[str]
) -> float:
    """Calculate the average Word Error Rate (WER) between reference and hypothesis transcriptions.

    Args:
        reference_transcriptions: List of reference transcriptions.
        hypothesis_transcriptions: List of hypothesis transcriptions.

    Returns:
        The average Word Error Rate (WER).
    """
    assert len(reference_transcriptions) == len(
        hypothesis_transcriptions
    ), "Reference and hypothesis lists should have the same length"
    total_wer = 0
    for ref, hyp in zip(reference_transcriptions, hypothesis_transcriptions):
        total_wer += wer(ref, hyp)
    return total_wer / len(reference_transcriptions)

evaluate_models 函数使用给定的评估数据集评估多个语音识别模型:

def evaluate_models(
    models: List[ModelSpeechToText],
    evaluation_dataset: List[Tuple[str, str]],
) -> List[Tuple[float, float, float]]:
    """Evaluate multiple speech-to-text models using a given evaluation dataset.

    Args:
        models: A list of ModelSpeechToText instances.
        evaluation_dataset: A list of tuples containing the paths of the WAV files and their transcriptions.

    Returns:
        A list of tuples containing WER, execution time, and RAM usage for each model.
    """
    if not evaluation_dataset:
        print("The evaluation dataset is empty. Please check the dataset processing.")
        return []

    audio_files, reference_transcriptions = zip(*evaluation_dataset)

    metrics = []
    for model in models:
        start_time = time.time()

        hypothesis_transcriptions = transcribe_audio_files(model, audio_files)

        memory = psutil.Process().memory_info().rss
        elapsed_time = time.time() - start_time

        wer = calculate_wer(reference_transcriptions, hypothesis_transcriptions)

        metrics.append((wer, elapsed_time, memory / 1024 ** 3))

        del model
        gc.collect()

    return metrics

评估 Vosk 语音识别模型:准确性、执行时间和内存消耗

首先,让我们介绍一下我们正在测试的四个模型。这些模型在大小和复杂性上有所不同,这可能会影响它们的性能。我们从准确性、执行时间和内存消耗方面对它们进行了比较。这些模型及其各自的大小如下:

  1. vosk-model-small-en-us-0.15(大小:40M)

  2. vosk-model-en-us-0.22(大小:1.8G)

  3. vosk-model-en-us-0.22-lgraph(大小:128M)

  4. vosk-model-en-us-0.42-gigaspeech(大小:2.3G)

比较模型性能

在对含有丰富口音和复杂句子的 数据集 上评估四种 Vosk 模型后,我们观察到了它们性能的显著差异。就准确性而言,最佳模型是 0.42-gigaspeech 和 0.22,其中后者相比更小的 0.15 和 0.22-lgraph 模型的准确性提高了约 20%。这种改进可以归因于 0.42-gigaspeech 和 0.22 模型的更大尺寸,使它们能够更好地处理各种口音和复杂的语言结构。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2:每个测试模型的词错误率(WER)(图片由作者提供)

在执行时间方面,0.22 和 0.15 模型表现最佳,完成转录过程大约需要 150 秒。另一方面,0.22-lgraph 模型所需时间较长,超过 750 秒。这突显了准确性和速度之间的有趣权衡,0.22 模型作为一个吸引人的选择,在性能和准确性之间达到了良好的平衡。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3:每个测试模型的执行时间(图片由作者提供)

考虑到结果,0.22 模型提供了准确性和效率的有希望的组合,使其适用于广泛的应用场景。

结论

在这篇文章中,我们评估了四种不同的 Vosk 语音识别模型,以比较它们在准确性和执行时间方面的表现。我们观察到,0.42-gigaspeech 和 0.22 模型的准确性优越,其中 0.22 模型比较小的 0.15 和 0.22-lgraph 模型表现出 20%的改进。这一改进归因于 0.42-gigaspeech 和 0.22 模型的更大尺寸,使它们能够更好地处理不同的口音和复杂的语言结构。

在考虑执行时间时,0.22 和 0.15 模型表现最佳。特别是 0.22 模型,作为一个吸引人的选择,它在性能和准确性之间达到了良好的平衡。需要注意的是,选择最佳模型取决于每个项目的具体要求。始终考虑速度和准确性之间的权衡。

保持联系:LinkedIn

想成为更好的数据科学家吗?写编程教程!

原文:towardsdatascience.com/want-to-be-a-better-data-scientist-write-coding-tutorials-9c1312897633

提升你的技术和沟通技能,学习编写可读的代码,并开始写关于任何话题的文章。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Conor O’Sullivan

·发布于Towards Data Science ·阅读时间 5 分钟·2023 年 4 月 15 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Mimi Thian拍摄的照片,发布在Unsplash上。

当我刚开始写作时,我几乎不会编程。我的一点 Python 经验来自大学项目。与此同时,我认为自己是一个出色的写作者!现在回头看,我对早期的一些文章感到羞愧。

这使我意识到,写教程提升了我的技术沟通技能。最终,它让我成为了一个更好的数据科学家。我想分享我的经验,并讨论一些具体的好处。这包括编写可读的代码,并提供写作困难话题的第一步。

编程

技术技能对开发人员和数据科学家显然很重要。同时,我们的工作领域不断变化。这意味着我们需要跟上最新的工具。这可能既困难又耗时,因此需要真正的承诺才能不断学习。

写作激励我不断学习新的技术技能,并对这些主题进行深入了解。

写作是一种很好的激励方式。我不仅可以学习新东西,还可以将这些经验分享给世界。这种感觉令人上瘾——有人阅读和互动你的作品。不断发布新文章的愿望促使我不断学习新知识。

不仅如此,我发现写作是最好的学习方式。通常,只有在尝试解释某些东西之后,我才意识到自己实际上并不理解它。此外,我总是力求为一个主题增加价值。引入新颖性需要良好的理解。通过这种方式,写作成为了我知识的检验。这推动我真正理解我所写的技术概念。

编写可读的代码

一个相关的技能是编写可读的代码。好的代码应该自我解释。同样,基于可读代码的教程更容易编写。我花在解释代码做了什么的时间更少,而且通常你根本不需要解释。我发现这种好处扩展到了行业合作中。

任何傻瓜都可以写出计算机能理解的代码。优秀的程序员编写人类能理解的代码。

— 马丁·福勒

一个例子来自我最近的一篇文章——使用 SHAP 调试 PyTorch 图像回归模型。在教程中,我处理了一个 5 维数组中的 SHAP 值。我想用包的一个函数来可视化这个数组。为此,数组首先需要重新排序。最初,代码是这样的:

# Reshape shap values for plotting
shap_numpy = [np.swapaxes(np.swapaxes(s, 1, -1), 1, 2) for s in shap_values]

嵌套的swapaxes函数解释起来并不有趣。简而言之,代码将数组的第 3 维与第 5 维进行了转置。我发现自己不得不写一大段文字来解释代码是如何做到这一点的。因此,我重新编写了代码:

# Reshape shap values for plotting
shap_numpy = list(np.array(shap_values).transpose(0,1,3,4,2))

转置函数更直观,代码也更容易解释。当为教程编写代码时,我总是寻找这样的例子。可读的代码使得写作过程更加顺畅,教程也更易于理解。

我发现这个技能在与其他专业人士合作时特别有用。你无法逃避交接和代码审查。在这些过程中解释代码与在在线教程中解释代码没有区别。最终,编写可读的代码意味着我花在向同事解释代码上的时间更少。

写作

当我刚开始工作时,我迅速意识到非技术技能也同样重要。你不仅需要取得结果,还需要解释这些结果和你的过程。我必须每天撰写电子邮件、演示文稿或技术文档。没有办法逃避写作!

…传统上,不同的人在职业生涯中选择不同的路径——有些人更偏向技术性,有些人则更具创意和沟通能力。数据科学家必须兼具这两者。

―莫妮卡·罗加提

编写编码教程是很好的实践。写得越多,我在工作中的沟通能力就越强。复杂的分析变得更容易解释,我需要澄清的工作也少了。我迅速看到了明显的好处。然而,这只是开始。

使用教程作为写作的起点

与其他文章相比,编程教程更容易编写。没有深奥的思考或细致的论证。你只需解释代码。这意味着它们可能是进入更复杂写作的一种方式。

写作是需要练习才能变得擅长的。教程帮助我迈出了第一步。我开始尝试在说明和结论中添加吸引人的故事。通过这些磕磕绊绊的过程,我开始转向不同类型的文章。

在更加自信的步伐下,我开始撰写基于数据科学家经验的文章。这些文章基于更抽象的概念、我的个人经历和感受。我发现这些比任何编程教程都难写。然而,如果没有那些最初的教程,我不会达到现在的水平。

回顾过去,编写编程教程带来了许多好处。我提高了技术知识,学会了编写可读的代码。编写教程还让我感到自信,可以写几乎任何内容。我还没有提到对我的声誉和财务补偿的好处。

所以,如果你对写作感兴趣,编程教程是一个很好的起点。如果你在想法上挣扎,你可能会觉得这篇文章很有用:

## 永不枯竭的文章主题:5 个可靠来源

回顾过去的项目、你想学习的东西以及你的职业和个人经历

writingcooperative.com

希望你喜欢这篇文章!你可以通过成为我的推荐会员来支持我**😃**

## 通过我的推荐链接加入 Medium — Conor O’Sullivan

作为 Medium 会员,你的部分会员费会分配给你阅读的作者,并且你可以完全访问每一个故事…

conorosullyds.medium.com

| Twitter | YouTube | Newsletter — 免费注册以获得Python SHAP 课程的访问权限

想提升你的短期预测?试试需求感知

原文:towardsdatascience.com/want-to-improve-your-short-term-forecasting-try-demand-sensing-50baa4380de3

当传统的预测方法在准确性上达到瓶颈时,我们如何推动进一步的改进?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Ramkumar K

·发布在Towards Data Science ·12 分钟阅读·2023 年 6 月 20 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

照片由JJ Ying拍摄,发布在Unsplash上。

介绍

需求预测是一个估算组织在未来某一时间范围内销售情况的过程。短期需求预测通常考虑 1–3 个月的时间范围,而中期预测可以涵盖 6–18 个月。长期预测通常可以达到 3–5 年。预测帮助企业决定销售什么、何时销售以及销售多少,持有多少库存,并确定未来在容量上的投资以应对动态的客户需求。公司通常依赖于历史趋势,并结合客户的反馈,同时考虑促销活动或清仓销售来创建需求预测。

需求预测重要的原因有几个。它位于销售与运营规划(S&OP)过程的顶端,在这个阶段生成的预测会传递到其他阶段,包括供应规划、生产调度、物流规划和库存优化。需求预测的准确性至关重要,以避免由于库存过多或过少而产生的成本。预测过高可能导致过多的流动资金被锁定在库存中。另一方面,持续的预测不足可能会导致库存短缺或需要在紧急情况下使用更昂贵的原材料进行订单处理,并且需要在短时间内通过更昂贵的运输进行发货。准确的计划有助于避免这些情况,通过驱动在正确的地点和时间制造适量的产品,促进高服务水平和降低储存成本。

挑战在预测中

“预测是非常困难的,特别是当它涉及未来时。”

这句名言通常归于 20 世纪的杰出物理学家尼尔斯·玻尔(虽然是否确实是他所说还有一些争议),虽然这是对预测的轻松看法,但它突显了预测的内在挑战。除了无法预测未来,还有其他与预测方法相关的挑战。

· 商业环境的变化 — 例如,替代品可能会取代某个产品,从而导致其需求下降。或者,产品的新应用可能会导致需求相比历史趋势上升。

· 商业模式的变化 — 组织可能会改变其运营模式和商业战略。例如,一家化工公司可能选择将其业务从普通化学品转向更多的特种产品,因此历史需求模式可能不再适用。

· 数据可用性 — 历史销售数据、客户和产品层级数据以及实时订单数据可能存储在不同的系统中。

· 数据质量 — 这可能包括由于输入错误导致的不准确数据,或数据在不同数据元素之间以不同且不一致的粒度进行捕获的问题。

预测方法

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Chris Liverani 拍摄的照片,刊登在 Unsplash

预测可以基于定量或定性方法。定量方法主要是时间序列分析,我们尝试基于历史数据捕捉趋势(例如增长、季节性)。在其他定量情况下,我们可能会构建经济计量模型,将需求预测与业务相关因素相关联。采用定性方法时,我们依赖于“群体智慧”,并尝试基于专家的集体意见或调查来估计未来。网络上有许多关于预测技术的有见地的资源;以下是部分示例:

[## 需求预测方法终极指南:提升销售和优化库存 - nexocode

在我们的指南中发现需求预测的秘密!了解方法、挑战、好处以及人工智能的作用……

nexocode.com](https://nexocode.com/blog/posts/what-is-demand-forecasting/?source=post_page-----50baa4380de3--------------------------------) [## 需求预测:你需要知道的一切

对于在快速增长或动荡市场中的公司来说,需求预测至关重要,因为它帮助他们了解未来的……

www.netsuite.com](https://www.netsuite.com/portal/resource/articles/inventory-management/demand-forecasting.shtml?source=post_page-----50baa4380de3--------------------------------) [## 如何选择合适的预测技术

每位经理应了解不同类型的预测及其使用时机。

hbr.org](https://hbr.org/1971/07/how-to-choose-the-right-forecasting-technique?source=post_page-----50baa4380de3--------------------------------) [## 6 种需求预测及其预期收益

需求预测帮助企业做出更明智的库存和能力决策。我们回顾了需求的类型…

www.thefulfillmentlab.com](https://www.thefulfillmentlab.com/blog/demand-forecasting?source=post_page-----50baa4380de3--------------------------------)

短期需求感知

短期需求感知是一种利用领先指标预测产品销售的预测技术,主要在短期内进行预测。它结合了历史数据和实时信息,以每日或每周为单位进行预测。通过这种方式,它捕捉到市场中的一些关键动态,尤其是在波动时期。因此,它帮助计划人员调整生产和物流计划,使预测更精确,从而提高供应链的韧性,并减少库存和运输成本。关于需求感知的在线资源有很多,以下是一些例子:

[## 什么是需求感知 - AI/ML 解决方案与服务 | 顶级 Google Cloud 合作伙伴

参数 传统需求预测 需求感知 传统的需求预测方法依赖于历史数据…

pluto7.com](https://pluto7.com/what-is-demand-sensing/?source=post_page-----50baa4380de3--------------------------------) [## 文章 - Kearney

编辑描述

www.kearney.com](https://www.kearney.com/service/analytics/article/-/insights/modern-retail-requires-modern-demand-sensing?source=post_page-----50baa4380de3--------------------------------)

需求感知预计将具有很高的经济价值(在节省成本或避免收入损失方面),仅仅因为运输量的巨大。由于预测的粒度和刷新频率,需求感知可能在数据和计算上非常密集。但是随着计算能力的提升,预计可行性会很高。需求感知的结果通常具有较高的解释性,因为我们通常使用基于线性回归的模型进行这些应用。结果往往可以分析得很清楚,从而向利益相关者解释预测。需求感知提供的预测更新建议是可操作的,因为更改生产基于预测变化是组织的内部决策。需求感知应用在输入数据刷新方面也大多是可持续的,因为它们依赖于每日生成的实时订单数据。

一个简化的需求感知方法

需求感知依赖相关的领先指标来估计销售预测。客户订单的下达速度可能是短期需求的一个领先指标。在一些行业,如石化行业,客户通常会提前几周下订单以进行预定。在本文中,我们讨论了一种基于客户订单的需求感知方法,适用于化工公司。

在这种方法中,前提是如果订单的下达速度比历史订单下达趋势更快,最终的月需求量将会很高,反之亦然。建议的做法是通过在每个月中旬进行客户订单趋势分析,以预测下个月的产品需求,从而增强传统的预测过程。这种提前的洞察对于供应链和产品经理在调整生产和定价方面将非常有帮助。分析还会识别那些可能下订单低于预测的客户,从而使剩余的量可以提供给那些希望购买超出其预测量的客户。这将有助于主动“调换”两个客户群体之间的产品,减少订单阻塞和延迟,并提升客户体验。

为了构建一个需求感知用例的机器学习解决方案,我们遵循一系列步骤,包括数据收集、探索性数据分析、数据处理(清洗和特征工程)、模型开发和优化、可操作的洞察以及建议。主要目标是基于客户订单属性,以给定的粒度(例如产品系列、客户类别)预测当前和下个月的需求。每个步骤的详细信息列在下面:

a. 数据收集 — 根据在商品公司环境中这一用例的典型情况,我们假设每个产品(或产品系列)都有数十或数百个客户提前下订单。为了捕捉客户订单的年度和季节性趋势,我们收集至少过去 36 个月的数据。我们收集包括请求量、销售订单日期、需求预定月份、客户信息(包括客户类别和地理位置)、产品属性(包括产品系列、市场细分)的销售订单。我们从需求预定月份开始,寻找从前一个月的第一个工作日(CM-1)到需求预定月份结束(CM)期间每天(工作日,排除周末和节假日)的总产品需求量。这假设在该日期之前没有订单(前一个月的第一个工作日)。我们还需要从分析中去除取消订单或退货订单。表 1 显示了数据框中的样本数据。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

表 1. 来自历史销售订单的样本数据(数据框格式)

WD — 工作日

b. 探索性数据分析 — 我们首先了解数据的大小(行和列)以及特征的数量和类型(数值型 vs 分类型)。我们还识别每列中的空值数量。我们通过直方图和箱线图可视化数值列,以查看数据的形状(包括均值、中位数、偏度、异常值),通过条形图可视化分类数据,以确认唯一值并识别需要处理的任何异常值。

c. 数据处理 — 在此步骤中,我们去除异常值(例如,数值列中的负值或极高值)。我们还选择特征并进行特征工程。在这个用例中,我们选择的特征是在预测变量之上的更高层次的聚合。例如,如果我们预测产品系列需求,我们选择市场细分、地理位置、累计订单量和每月基线销售预测作为预测特征。给定工作日的累计订单(表 2)是通过日订单量作为特征工程的一部分得出的。图 1 显示了累计订单如何随工作日变化的示例。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

表 2. 历史订单数据框(具有特征工程属性)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1. 选择月份的工作日累计订单百分比

d. 模型开发与优化 — 推荐使用多元线性回归来处理这种情况。预计这是一个‘分段’解决方案,其中我们为每个工作日有不同的回归函数。通过 36 个月的数据,每个产品系列有数百个客户,我们将为每个产品系列拥有数千个训练数据点。我们从定义损失函数开始,以帮助我们建立提供最准确需求预测的模型。我们选择几个准确性测量指标,包括平均绝对误差(MAE)、平均绝对百分比误差(MAPE)和 R2,并测试不同模型在这些指标上的表现(我们希望 R2 高而误差低)。原始误差只是月度需求预测与产品系列历史实际值之间的差异。在数据准备方面,我们首先将数据分为训练集(80%的数据)和测试集(20%的数据)。缺失或空值在每个数据集中分别处理,以避免数据泄露。如果缺失值占特征的大部分,我们可能会完全删除该特征,因为它几乎没有预测能力。如果空值只出现在少数几行中,我们可以删除这些缺失值的行。我们也可以用中央趋势来填补缺失值,比如列的中位数(如果是数值型)或众数(如果是分类变量)。此外,我们还将分类变量转换为独热编码的数值格式。为了将数据缩放到线性回归模型中,我们将因变量(每月需求预测)除以基线销售预测,并将累计订单(自变量)除以基线销售预测。回归模型系数提供了特征在解释预测变量变化中的重要性。

e. 可操作的见解和建议 — AI 应用预测的需求可能高于或低于基线销售预测。为向利益相关者解释结果,一种方法是将历史订单曲线与实时订单进行比较(见图 2)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2. 累计订单百分比比较:历史数据与实时数据

这里仅展示了 3 个月的历史累计订单百分比曲线以作说明;在实际讨论中,我们会包含至少 12 个月的数据。

在该图中,历史累计订单百分比曲线以实线绘制,而实时累计订单(作为下个月基线销售预测的百分比)则以虚线绘制。我们可以看到,在选择的历史月份中,大约 12%的总需求在前一个月的第 10 个工作日已下单,但我们当前的趋势约为 5%的预测,表明需求比基线预测预期的要低。以此例子为例,通过更新需求预测获得利益相关者的支持后,我们可以通知决策者减少相关产品系列的生产,同时识别出下单低于其提供的预测的客户。一般来说,预测可以每天运行,以向制造部门提供生产和库存信号,并向销售团队提供需求弱点或强度的信号。人工智能/机器学习模型可以每季度重新训练一次或在业务重组时进行重新训练。

经济价值估算

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片由 Ibrahim Boran 提供,来自 Unsplash

预测不准确的货币化思路可以通过库存短缺导致的收入损失和由于过剩未售库存而增加的储存成本来考虑。虽然预测波动在较长时间内可能会平衡,但预测的一致性偏差可能会导致产品组合的不平衡。短期库存短缺的收入损失可能对业务产生长期影响,如果客户选择永久离开组织而转向竞争对手。我们通过下表中的简单例子来说明收入损失,其中一个关键假设是组织没有足够的库存来覆盖预测不准确性。该表提供了基线预测(未应用人工智能)和每月来自客户的实际需求,涵盖了 10 个产品系列:PF1 到 PF10。每个产品系列的价格也提供了。这些数字的规模代表了商品化学品。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

表 3. 假设性示例以说明由于预测不准确而导致的收入损失

我们可以看到,对于一些产品,我们的预测过高(预测 > 实际),而对于其他产品,我们的预测过低(预测 < 实际)。对于预测不足的产品,我们假设缺乏库存来弥补这一不足,并通过将价格与缺口相乘来计算损失的收入。总的损失收入估计约为 ~$58MM。通过一个能够利用每日实时订单感知未来每月需求的 AI 应用,更新预测并生产适量的产品,我们可以减少这一损失收入。即便预测误差改善 20%(这种应用中并不罕见),组织每月的损失收入也会减少$11.6MM。

总结

使用实时客户订单进行短期需求感知可能比传统预测方法更进一步,因为它利用实时信息来提高预测的准确性。不过,我们需要注意的是,这种方法可能并不适用于所有用例或业务情况。当订购模式有一定规律且订单提前下达时,这种方法效果最佳。结合市场情报来理解观察背后的商业洞察,这种技术效果更佳。由于客户通常不会提前下单超过 6-8 周,因此该方法不适用于更长期的预测。

本文中描述的方法也可以用于其他目的。例如,我们可以使用这种方法来估计每个客户/产品组合之间的预期需求和实际订单的偏差。这可以帮助识别那些下单量过少或过多的客户,这些订单量与其基线销售预测相比偏离了历史订购模式。

最后,我们希望建立机制,以便在更新的预测可用时采取行动。这可能包括增加/减少生产量或跟进那些超出预测分配量或尚未提取任何量的客户。

感谢阅读。希望你觉得这篇文章有用。欢迎将你的评论发送至 rkumar5680@gmail.com。我们可以在 LinkedIn 上联系。

注意你的束搜索超参数

原文:towardsdatascience.com/watch-out-for-your-beam-search-hyperparameters-9c4daf6668d6

默认值永远不是最佳的

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Benjamin Marie

·发表于Towards Data Science ·阅读时间 6 分钟·2023 年 1 月 11 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片由Paulius Dragunas提供,来源于Unsplash

在使用神经模型开发应用时,尝试不同的超参数来训练模型是很常见的。

例如,学习率、学习计划和丢弃率是对模型学习曲线产生重要影响的超参数。

更少见的是最佳解码超参数的搜索。如果你阅读深度学习教程或处理自然语言处理应用的科学论文,很可能用于推理的超参数甚至未被提及

大多数作者,包括我自己,不会费心寻找最佳解码超参数,而是使用默认值。

然而,这些超参数实际上可能对结果产生显著影响,无论你使用什么解码算法,总会有一些需要微调以获得更好结果的超参数

在这篇博客文章中,我展示了使用简单 Python 示例和机器翻译应用程序的解码超参数的影响。我专注于束搜索,因为这是迄今为止最受欢迎的解码算法,以及两个特定的超参数。

框架和要求

为了展示每个超参数的效果和重要性,我将展示一些使用Hugging Face Transformers 包在 Python 中生成的示例。

要安装此包,请在终端中运行以下命令(我建议在单独的 conda 环境中执行):

pip install transformers

我将使用 GPT-2(MIT 许可证)生成简单句子。

我还将使用 Marian (MIT 许可证) 运行其他机器翻译示例。我在 Ubuntu 20.04 上安装了它,按照官方说明

Beam Size 和 Length Penalty

Beam search 可能是语言生成任务中最受欢迎的解码算法。

它在每一步,即为每个新生成的标记,保持模型推断中最可能的k 个假设,其余的假设被丢弃。

最终,在解码结束时,概率最高的假设将作为输出。

k,通常称为“beam size”,是一个非常重要的超参数。

更高的k会得到更可能的假设。注意,当k=1 时,我们称之为“贪心搜索”,因为我们只保留每一步中最可能的假设。

默认情况下,在大多数应用中,k任意设置在 1 到 10 之间。这个值可能看起来非常低。

这有两个主要原因:

  • 增加 k 会增加解码时间和内存需求。换句话说,它变得更加昂贵。

  • 更高的 k 可能会产生更可能但更差的结果。这主要是由于假设的长度,但不仅仅是由于长度。较长的假设往往概率较低,因此 beam search 会倾向于促进较短的假设,这对于某些应用可能不太可能。

第一点可以通过进行更好的批量解码和投资更好的硬件来直接解决。

长度偏差可以通过另一个超参数来控制,该超参数通过每一步的长度(标记数)来标准化假设的概率。执行这种标准化的方法有很多。最常用的方程之一是由Wu et al. (2016) 提出的:

lp(Y) = (5 + |Y|)α / (5 + 1)α

其中 |Y| 是假设的长度,α 是一个通常设置在 0.5 和 1.0 之间的超参数。

然后,分数 lp(Y) 用来修改假设的概率,以便在给定 α 的情况下,偏向于解码并生成较长或较短的假设。

Hugging Face transformers 中的实现可能略有不同,但有一个α可以作为“length_penalty”传递给generate函数,如以下示例所示(改编自Transformers’ documentation):

from transformers import AutoTokenizer, AutoModelForCausalLM

#Download and load the tokenizer and model for gpt2
tokenizer = AutoTokenizer.from_pretrained("gpt2")
model = AutoModelForCausalLM.from_pretrained("gpt2")

#Prompt that will initiate the inference
prompt = "Today I believe we can finally"

#Encoding the prompt with tokenizer
input_ids = tokenizer(prompt, return_tensors="pt").input_ids

#Generate up to 30 tokens
outputs = model.generate(input_ids, length_penalty=0.5, num_beams=4, max_length=20)

#Decode the output into something readable
print(tokenizer.batch_decode(outputs, skip_special_tokens=True))

这个代码示例中的“num_beams”是我们的另一个超参数k

使用这个代码示例,提示“Today I believe we can finally”,k=4 和 α=0.5 时,我们得到:

outputs = model.generate(input_ids, length_penalty=0.5, num_beams=4, max_length=20)
Today I believe we can finally get to the point where we can make the world a better place.

当 k=50 和 α=1.0 时,我们得到:

outputs = model.generate(input_ids, length_penalty=1.0, num_beams=50, max_length=30)
Today I believe we can finally get to where we need to be," he said.\n\n"

你可以看到结果并不完全相同。

kα应在你的目标任务上独立微调**,使用一些开发数据集。

让我们在机器翻译中举一个具体的例子,看看如何进行简单的网格搜索来找到最佳超参数及其在实际应用中的影响。

机器翻译实验

在这些实验中,我使用了 Marian 和训练在 TILDE RAPID 语料库(CC-BY 4.0)上的机器翻译模型进行法语到英语的翻译。

我仅使用了数据集的前 100k 行进行训练,最后的 6k 行作为开发测试集。我将开发测试集分成两部分,每部分 3k 行:第一部分用于验证,第二部分用于评估。注意:RAPID 语料库的句子按字母顺序排列。因此,我的训练/开发测试集划分在实际使用案例中并不理想。我建议在划分语料库之前,先打乱语料库的行顺序,同时保留句子对。在本文中,我保持了字母顺序,并没有打乱,以使以下实验更具可重复性。

我使用 COMET(Apache 许可证 2.0)来评估翻译质量。

要通过网格搜索来寻找 kα 的最佳值对,我们首先必须定义每个超参数的一组值,然后尝试所有可能的组合。

由于我们在这里搜索的是解码超参数,因此这一搜索相对快速且直接,与搜索训练超参数相比。

我为此任务选择的值集如下:

  • k: {1,2,4,10,20,50,100}

  • α: {0.5,0.6,0.7,0.8,1.0,1.1,1.2}

我将机器翻译中默认使用的最常见值加粗。对于大多数自然语言生成任务,应该尝试这些值集,除了 k=100,通常这个值不容易产生最佳结果,同时解码代价较高。

我们有 7 个 k 的值和 7 个 α 的值。我们想尝试所有组合,所以我们需要对评估数据集进行 7*7=49 次解码。

我们可以通过一个简单的 bash 脚本来实现:

for k in 1 2 4 10 20 50 100 ; do
  for a in 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 ; do
    marian-decoder -m model.npz -n $a -b $k  -c model.npz.decoder.yml < test.fr > test.en
  done;
done;

然后,对每个解码输出运行 COMET 以评估翻译质量。

根据所有结果,我们可以绘制出每对值的 COMET 分数表:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者提供的表格

如你所见,使用默认超参数(下划线)获得的结果低于 26 个其他超参数值获得的结果。

实际上,所有加粗的结果在统计上都显著优于默认结果。注意:在这些实验中,我使用了测试集来计算表中展示的结果。在实际情况中,这些结果应该在另一个开发/验证集上计算,以决定在测试集上使用的值对,或用于真实世界应用。

因此,对于你的应用程序,确实值得微调解码超参数,以在付出非常小的工程努力的情况下获得更好的结果。

结论

在这篇文章中,我们只调整了 beam search 的两个超参数。还应微调更多参数。

其他解码算法,如温度采样和核采样,具有一些超参数,你可能想要查看这些参数,而不是使用默认值。

显然,随着我们增加超参数的数量进行调整,网格搜索的成本会更高。只有你在应用中的经验和实验才能告诉你是否值得对特定的超参数进行调整,以及哪些值更有可能产生令人满意的结果。

水接触时间与浓缩咖啡中的萃取:一个实验

原文:towardsdatascience.com/water-contact-time-and-extraction-in-espresso-an-experiment-9987bd24d35d?source=collection_archive---------18-----------------------#2023-02-10

咖啡数据科学

一个短期实验以隔离变量

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Robert McKeon Aloe

·

关注 发表在 Towards Data Science ·5 分钟阅读·2023 年 2 月 10 日

咖啡是一种奇妙的饮料,像茶一样,需要一定的浸泡时间来提取其精华。这段浸泡时间或水接触时间需要与从咖啡中提取的风味平衡。在浓缩咖啡中,这尤其具有挑战性,因为水在较短的浸泡时间内以压力流过咖啡,比其他冲泡方法更具挑战。

有一种理论认为,较高的水接触时间会导致浓缩咖啡中的提取率更高。虽然有人对这种观点提出了异议,但像许多事情一样,这个理论是可以测试的。关键是设计一个良好的实验(DOE),所以我们来设计一个实验并收集一些数据来开始回答这个问题。

总溶解固体 (TDS) 使用折射仪测量,这个数字与射击的输出重量和咖啡的输入重量结合使用,以确定杯中提取的咖啡百分比,称为提取产率 (EY)。这是我们试图测量的主要变量。

DOE:修改研磨颗粒大小

最简单的实验设计是修改研磨颗粒大小,这将减慢流量。然而,这会混合两个其他变量:

  1. 较小的研磨颗粒会在局部层面上更快地提取。

  2. 较小的研磨颗粒更容易受到通道效应的影响,从而在全球层面上导致提取不足。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

EY 和时间与研磨颗粒大小的关系,所有图像均由作者提供

在这两个变量之间是提取产率的火山图。因此,如果你使用了这个实验,你会看到随着研磨变小,提取产率上升直到某个峰值,之后提取产率会下降。从这样的实验中可能得出较长的水接触时间不会导致更高提取的结论,而这个结论是不准确的。

所以实验必须更好,并减少变量。

DOE:使用流量控制

使用带有流量控制的浓缩咖啡机(例如我拥有的 Decent Espresso (DE)),可以使用单一的研磨度,并进行 3 种不同的流量配置。这可以控制由于颗粒分布变化导致的提取速率变化。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

恒定流量控制的电子邮件。

然而,这仍然可能受到群头设计的影响。许多机器在水进入篮子时将水推到边缘,从而导致侧面通道效应。DE 由于水分配器的独特水分布导致水在左侧稍微比右侧流入得快。

DOE:流量控制 + 混合废咖啡

为了减少水输入变量,我们可以使用一些废咖啡。废咖啡不容易受到通道效应的影响,因为它没有 CO2 和可提取的溶解物质很少。因此,我们可以混合单一咖啡研磨度,并使用多种流量配置。

另一个改进是拉取一种萨拉米咖啡,以便更好地理解提取速率。

测试用废咖啡

对于这个测试,我制作了一些使用过的咖啡,与以前的测试一样。然而,我认为可以借此机会在更大实验前获取一些数据。这些咖啡渣来自多个使用过的咖啡饼,我将它们彻底混合在一起。然后,我打算进行 2:1 的萃取并分成两个杯子。然而,第一个萃取时间稍长,所以我对其他两个萃取保持了相同的比例。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这个结果表明,较高的流速会降低 EY,并且通常支持水接触时间更长更好的观点(即较慢的流速)。

然后我对这些咖啡饼进行了 4 ml/s 105C 的萃取,直到液体变清。我把咖啡饼晾干,并通过一个 800um 的 Kruve 筛网去除任何结块。

那么让我们进入大实验吧。

大实验

这个实验是在 Decent Espresso 机器上完成的,使用了 3 种不同的平流速率控制曲线。我在 90C 下进行这些萃取,因为我想避免过多的蒸汽,因为蒸汽对咖啡的影响仍在被发现。

然后我将新鲜研磨的咖啡与使用过的咖啡混合,并进行简单的 WDT 和自动水平压实。8 克新鲜咖啡与 13 克使用过的咖啡混合。

我在 1 ml/s 的流量下对使用过的咖啡进行了对照萃取,以去除使用过的咖啡对其他测量的影响。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

提取过程使用了 5 杯来处理三种流量曲线。对于 TDS,尽管 4 ml/s 的样品提取时间稍长,但每个样品的 TDS 趋势仍然较高。这一结果由 EY 标准化。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

4 ml/s 流量的提取结果难以得到相同的输出重量。我还去除了这些累计 EY 数字中使用过的咖啡渣的影响。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

添加了 Pump&Dump 作为参考。

这些结果显示,随着流量的增加,提取产率会下降。这在所有的萃取比例中都成立,并支持了较长水接触时间能够提取更多咖啡的观点。

咖啡的提取在浓缩咖啡中具有挑战性,因为有许多变量相互影响。我希望这个实验能帮助揭示如何将单一变量隔离并进行检查。这个实验表明,较长的水接触时间会导致更高的提取,而以前对接触时间的看法与细磨浓缩咖啡的通道效应混淆在一起。

这个实验也可以只使用新鲜咖啡进行重复,这将是一个有趣的实验。我不确定其他变量如何可能混淆测量结果。

如果你喜欢,可以在TwitterYouTubeInstagram上关注我,我会发布各种机器上的浓缩咖啡镜头和相关内容。你还可以在LinkedIn找到我,也可以在Medium订阅上关注我。

进一步阅读

我的书

我的链接

浓缩咖啡文章合集

工作与学校故事合集

我们应该早就看到 ChatGPT 了

原文:towardsdatascience.com/we-should-have-seen-chatgpt-coming-9292b4648174

观点

我们这些年来一直在为此做贡献

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Samantha Hodder

·发表在 Towards Data Science ·10 分钟阅读·2023 年 2 月 9 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片来自 Susan WilkinsonUnsplash

我的姐夫是英国一所顶尖大学的教授,教授商学院的创新研究。了解 ChatGPT 后,我丈夫问他对这件事的看法:

嗯,有一段时间,人们担心汽车发明后,所有的马会怎样

… 在汽车发明之后。

我不知道你发现 ChatGPT 时在哪里

我在海滩上度假,与我的青少年孩子们开玩笑,谈论一个看似不切实际的时尚行业商业创意。

青少年们都说,不,那是个愚蠢的想法,我的朋友说:“哦,是吗,让我们看看这个想法到底有多愚蠢吧!

他拿出手机,在搜索栏中输入一个简单的短语,然后点击纸飞机图标。

立即,闪烁的光标开始以流畅和准确的方式解释这个“愚蠢的想法”。它有示例和细节,引用了实际的人物和项目。随后,它开始概述一个相当有说服力的商业计划。

我不是投资者;我撰写创意,并将其作为项目和故事向人们和机构推介。但这个解释,加上附带的工作流程、合理性和人口统计分析,足够好到我可以将文本拆分、设计成幻灯片,稍加点缀,走进任何董事会进行推介。

这让我有种奇怪的感觉。

我们应该预料到 ChatGPT 吗?

近十年来,我一直将自己的语音采访输入 AI 转录机器……无论如何,我都不想手动完成,或按每分钟音频 1.00–2.00 美元的当时费用请别人做……一个 45 分钟的采访可能需要高达 90.00 美元的转录费用,直到 2017 年。在我的工作中,一小时只是记录片或音频项目所需转录采访时间的一小部分。直到最近,这一直是需要考虑的费用项。

我找到的最早的一个大约在 2013 年,与IBM Watson 项目有关……我记得它需要一些奇怪的登录协议,然后你必须去演示,点击 JSON 选项卡,上传 MP3 文件,然后等待转录结果。

Watson 听不清楚口音;它更喜欢标准的中西部英语,语法和发音正确。当我开始处理需要转录重澳大利亚口音的项目时,我发现转录结果既有趣又无用;整理转录文本的时间和从头转录的时间差不多。Watson 也明显无法识别英语以外的其他语言。

当我最初开始使用 Watson 时,我发现考虑这些机器学习文件会在哪里结束有些微妙的趣味,但我没有深入研究。今天,当我在谷歌上搜索这个问题时,我发现我上传的 Watson 转录采访可能在某种程度上推动了一些视频游戏的发展。

很久以前,我会随意使用“机器学习”这个术语来解释这些转录工具为何越来越好;尽管我不完全理解这意味着什么,也不清楚它会发展到哪里,尽管我已经读过并基本接受了 Donna Haraway 在Cyborg Manifesto中阐述的观点。

当我意识到这些‘机器’在完成工作方面变得越来越出色时,我短暂地自我庆贺了一下,觉得自己也在这里完成了工作;所有那些从各种来源的采访中获得的有用数据。也许这是我对这项工作的女性主义回应。

对我而言,这是一种免费的帮助。我点击了上传,然后下载。非常感谢

最近,我使用了 Otter.ai、Descript 以及较少使用的 Sonix.ai。对我来说,这些程序简直是救星:免费转录(每月“免费”账户有时间或字数限制)用于音频采访,否则需要我用当时的工具如Wreally花费数小时,或花费数百美元请别人做。

但随着 ChatGPT 和其他 AI 写作工具的出现,我恍若大悟。我现在明白了那些工作的去处;这些转录软件平台都对NLP(自然语言处理)做出了贡献。

我上传的那些访谈时长,使得“机器”能够排序发音和重音,区分辞令和方言,然后观察和计算重复的词组……它们都对建立一个庞大的数据库起到了很大帮助,这个数据库让机器能够学习人类的语言和思维方式。

而且,猜猜看,你也在做出贡献。

在最近使用过的 NLP 技术旁边给自己打个小勾:

  • Siri 语音转文本请求

  • Alexa 相关

  • 嘿 Google……给我展示一个……的食谱

  • Apple.com 技术支持聊天机器人

  • 谷歌翻译

  • Grammarly

  • 预测文本

  • Netflix 推荐

  • 遵循建议

  • 转录软件

这只是显而易见的部分。如果你想深入了解这个领域,还有很多更深层次的内容。

如果我只看转录软件,我可以说在短短几年内,我见证了准确率的巨大提升。它们从只能准确转录那些清晰且分开的单词发展到现在:可以以大约 90%的准确率转录四方对话,其中包括不同口音的说话者,包括非英语母语者。

我曾经犯过这种“头埋沙子”的错误

在这些“早期的互联网时代”,即上世纪初,公众与技术以及控制和创造技术的公司互动的方式截然不同。

以谷歌为例:在 2004 年公开上市之前,对于非硅谷内部人士来说,谷歌只是一个简洁、零广告的单页搜索引擎,你可以提出基本问题,并获得基本答案。谷歌并不是从一开始就具备从门铃到硬盘的端到端解决方案的。

Google 搜索的早期仅能提供来自网站的信息(那时的网站是个新概念)。或者是从已经上传到互联网上的现有文本,或为互联网新创建的文本(那时也不多)。再加上你必须考虑数据速率……谁还记得拨号上网的声音?互联网刚开始时是小众的。

学者们依然局限于实际的图书馆和精装书,或者在黑暗的房间里翻阅微缩胶卷做研究,这种情况持续了十年。维基百科刚刚从 1990 年代的概念起步,到 2001 年上线时才真正起步。

早期的互联网足以发现人名、已发布的内容、旅行信息和在 Mapquest 上打印出冗长的驾驶说明……它还不像今天这样存在着广泛的信息池。

还有另一点需要考虑……当时的计算机并不是“个人”的。计算机主要用于工作,通常是在工作场所,如果你家里有一台,它们通常会放在角落的桌子上,盖上防尘罩。除非你能买得起昂贵的 Macbook,否则“笔记本电脑”更像是可折叠的台式机,其价格是其他任何计算机的五倍。

在 2018 年,网站 24/7 Wall St 发布了一项分析 ,分析了根据出生年份计算计算机的成本,然后调整了通胀。如果你出生于 1978 年,著名的 IBM 5110 的价格接近 10000 美元,调整通胀后超过 38000 美元。

在 2001 年,一台 Apple Powerbook G4 的价格是 3500 美元,调整通胀后几乎达到了 5000 美元;在 2002 年,一台 Toshiba Satellite 1995 的价格是 2499 美元,调整通胀后约为 3500 美元。在 2018 年(使用这种相同方法的最后一年),一台 Huawei Matebook Pro 的价格仅为 1200 美元……而今天,如果你想订购一台新款的配备 M2 芯片的 Macbook Air,入门级价格为 1199 美元。

将计算机视为除昂贵但必要工具以外的东西需要远见;这是在 2000 年代初大多数人所不具备的。

要解释一些人的局限性,我只需照照镜子。2001 年,当我在 SXSW 互动节上展示我的前沿互动项目时,我错过了 Eric Schmidt 的主题演讲。我甚至在这个世界里工作过,那是在 2000 年首个互联网泡沫破灭后的初期计算机混乱时期,而我实际上记得曾对我的同事们大声说:

一个关于搜索引擎的演讲有什么有趣的呢?!

这是我在这个阶段如何合理化这个严重错误的方式:世界大致分为极客和非极客。极客理解计算机的力量、相关性和重要性。极客能看出计算机最终会触及每个人和一切;非极客仍在电影院里,看书,狂看《24》和《黑道家族》的 DVD 套装,或在他们的 PVR 上录制定时电视节目。像我这样的某些人甚至使用计算机创造了讲故事的新方式……

是的,我在 2001 年德克萨斯州奥斯汀的会议上去喝了免费的咖啡,而 Eric Schmidt 正在给一个小型礼堂做主题演讲。就是这样。我已经接受了这个事实。

这个故事在某种程度上揭示了我的年龄——快 50 岁了——但它也揭示了另一点:虽然我一直在媒体领域工作,这个领域与技术密不可分,但我必须也要承认,我也参与了它们的建设。

我是的,你可能也是,曾经是那些喂养聊天机器人的人之一。我甚至在某种意义上因为这样做而获得了报酬(通过获取免费的转录)。我是自愿和半知情地这样做的。

直到现在,我才看到这一切的重要性。我还看到,借助事后的洞察,基于所有的语音命令、面试交流和偏好选择,我们已经喂养了“机器”十年。而我们所诞生的正是我们应有的预期:

一种具有自然和本地语言能力的人工智能聊天机器人,包括模式和连接,具有深度和创新的想法,由丰富的客户数据支持……还有一个完整的社交媒体营销计划。

这不正好总结了现代人类/媒体/企业家的现状吗?

我们应该感到害怕吗?

我们应该担心计算和技术的这一演变吗?将其称为改变生活是激进的吗,还是只是为了引起头条的夸张说法?

回到文章开头的那个见解:

嗯,曾经有人担心所有的马会发生什么事情。

……在汽车发明之后。

你自己问问自己:你愿意为了上班而回到骑马通勤的时代吗?你下一次骑马度假会感到愉快吗?

显然,汽车的到来完全重新排序了我们对文明的所有认知:它决定了我们如何生活、住在哪里、如何工作、在哪里工作以及我们如何娱乐。汽车还帮助我们破坏了大气层,并加速了气候变化。

但我们应该害怕吗?我们应该害怕人工智能聊天机器人的到来来帮助我们完成工作吗?

这是一个更复杂的问题,但并不是完全可以忽视并当作工匠的复仇。

现在我们拥有一个多世纪的信息来分析汽车的到来对我们社会产生的影响,我坚守这个观点:

是的,我们应该意识到,甚至略微感到害怕,新的发明会取代以前或现有的系统。

但这并不意味着我们应该希望它们消失;想象没有汽车的文明基本上是荒谬的。

也许看待像 ChatGPT 这样的工具(包括它自己的 GTP-3,据说 将轻松超越 1.0 版本的能力)的方式是向前看的……而不是回头看马厩的大门。

其他的会出现,而且很快。谷歌在 2023 年 2 月 8 日直播了一场活动……这通常不是谷歌年度日历上的日期……毫无意外,我们了解到谷歌的竞争对手 ChatGPT,谷歌 Bard 即将推出。显然,他们对在人工智能聊天机器人挑战中被超越感到威胁。

人工智能聊天机器人需要一种不同的技术方法——不仅仅是兴奋地抓住然后跑掉。

也许更重要的是创建一个安全使用的文化,然后建立一套规则和行为规范来规范其使用。这些机器人将促使我们变得更加聪明,既要识别出人与机器人的区别……也要知道何时使用它们来提高效率,并以增加我们自身生产力的方式使用它们。它们还会让我们变得懒惰,就像技术已经做到的那样(我以前能记住大约 100 个电话号码……现在只剩下约五个了)。

就像汽车催生了郊区一样,AI 机器人肯定会让我们形成新的生活习惯和工作流程。AI 机器人无疑会影响教育、创业、信息和文案写作行业……仅仅是我在自己的经验领域中看到的一些例子。

不知怎么的,或许甚至违背了我们自己的判断,我们将不得不将 AI 聊天机器人视为我们所生活的知识和信息经济的显而易见的演变。

确实,考虑到这只是一个开始令人感到气馁;安全带并不是汽车最初设计的一部分。但汽车变得更快,道路变得更长更直,限速提高了,汽车的使用模式也发生了变化。车祸一直是现实;但在有人分析了数据并意识到一个小发明——安全带——可以拯救生命之后,法律被修改了,使用模式也被调整了。结果,许多生命得到了拯救。

ChatGTP(以及后续的其他机器人)的安全带测试是什么?

所以,是的。是的,我们应该早就预料到 ChatGTP,因为我们这些年来一直在帮助创建它。我们通过各种节省时间或便利的技术互动来实现这一点。

但与其试图想象它不存在,或在它已经到来后我们可以改变它,不如我们应该适应它,让它为我们服务而不是与我们对抗。并且想办法因为它而变得更聪明,或者减少工作量。

Samantha Hodder 是一位音频制作人和作家。如果你和我一样喜欢叙事播客,请订阅我的 Substack Bingeworthy

[## 想要听到更多来自 Samantha Hodder 的内容吗?

想要听到更多来自 Samantha Hodder 的内容吗?请点击我的链接关注我的个人资料,或者订阅以接收通知……

samanthahodder.medium.com](https://samanthahodder.medium.com/subscribe?source=post_page-----9292b4648174--------------------------------)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值